diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts new file mode 100644 index 0000000..2906157 --- /dev/null +++ b/build-logic/convention/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + `kotlin-dsl` +} + +dependencies { + compileOnly(libs.androidx.gradle) + compileOnly(libs.kotlin.gradle) + compileOnly(libs.compose.compiler.gradle) + implementation(libs.spotless.gradle) +} + +gradlePlugin { + plugins { + register("androidApplication") { + id = "cfw.android.application" + implementationClass = "AndroidApplicationConventionPlugin" + } + register("androidTests") { + id = "cfw.android.tests" + implementationClass = "AndroidTestsConventionPlugin" + } + register("codeLint") { + id = "cfw.code.lint" + implementationClass = "CodeLintConventionPlugin" + } + register("androidLibrary") { + id = "cfw.library" + implementationClass = "AndroidLibraryConventionPlugin" + } + register("androidLibraryTests") { + id = "cfw.library.tests" + implementationClass = "AndroidLibraryTestsConventionPlugin" + } + register("sampleCommonDeps") { + id = "cfw.sample.common.deps" + implementationClass = "SampleCommonDepsConventionPlugin" + } + } +} diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt new file mode 100644 index 0000000..c60188f --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt @@ -0,0 +1,41 @@ +import com.android.build.api.dsl.ApplicationExtension +import cfw.buildlogic.AndroidConfig +import cfw.buildlogic.configureAndroid +import cfw.buildlogic.configureCommonKotlinCompileOptions +import cfw.buildlogic.libs +import cfw.buildlogic.pluginId +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +@Suppress("unused") +class AndroidApplicationConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.application") + apply(libs.pluginId("compose-compiler")) + apply("cfw.code.lint") + } + + extensions.configure { + defaultConfig { + targetSdk = AndroidConfig.TARGET_SDK + } + configureAndroid(this) + + buildFeatures { + compose = true + } + + packaging { + resources { + excludes.add("/META-INF/{AL2.0,LGPL2.1}") + } + } + } + + configureCommonKotlinCompileOptions() + } + } +} diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt new file mode 100644 index 0000000..353ad49 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -0,0 +1,41 @@ +import com.android.build.api.dsl.LibraryExtension +import cfw.buildlogic.AndroidConfig +import cfw.buildlogic.configureAndroid +import cfw.buildlogic.configureCommonKotlinCompileOptions +import cfw.buildlogic.libs +import cfw.buildlogic.pluginId +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +@Suppress("unused") +class AndroidLibraryConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.library") + apply(libs.pluginId("compose-compiler")) + apply("cfw.code.lint") + } + + extensions.configure { + defaultConfig { + lint.targetSdk = AndroidConfig.TARGET_SDK + } + configureAndroid(this) + + buildFeatures { + compose = true + } + + packaging { + resources { + excludes.add("/META-INF/{AL2.0,LGPL2.1}") + } + } + } + + configureCommonKotlinCompileOptions() + } + } +} diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryTestsConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryTestsConventionPlugin.kt new file mode 100644 index 0000000..3473390 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryTestsConventionPlugin.kt @@ -0,0 +1,35 @@ +import com.android.build.api.dsl.LibraryExtension +import cfw.buildlogic.library +import cfw.buildlogic.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies + +@Suppress("unused") +class AndroidLibraryTestsConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.android.library") + + extensions.configure { + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + testOptions { + unitTests.all { + it.useJUnitPlatform() + } + } + } + + dependencies { + add("testImplementation", libs.library("junit")) + add("androidTestImplementation", libs.library("androidx-test-junit")) + add("androidTestImplementation", libs.library("androidx-test-espresso")) + add("androidTestImplementation", platform(libs.library("compose-bom"))) + add("androidTestImplementation", libs.library("compose-ui-test-junit4")) + } + } + } +} diff --git a/build-logic/convention/src/main/kotlin/AndroidTestsConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidTestsConventionPlugin.kt new file mode 100644 index 0000000..effff3d --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidTestsConventionPlugin.kt @@ -0,0 +1,30 @@ +import com.android.build.api.dsl.ApplicationExtension +import cfw.buildlogic.library +import cfw.buildlogic.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies + +@Suppress("unused") +class AndroidTestsConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.android.application") + + extensions.configure { + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + } + + dependencies { + add("testImplementation", libs.library("junit")) + add("androidTestImplementation", libs.library("androidx-test-junit")) + add("androidTestImplementation", libs.library("androidx-test-espresso")) + add("androidTestImplementation", platform(libs.library("compose-bom"))) + add("androidTestImplementation", libs.library("compose-ui-test-junit4")) + } + } + } +} diff --git a/build-logic/convention/src/main/kotlin/CodeLintConventionPlugin.kt b/build-logic/convention/src/main/kotlin/CodeLintConventionPlugin.kt new file mode 100644 index 0000000..5c3a460 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/CodeLintConventionPlugin.kt @@ -0,0 +1,33 @@ +import com.diffplug.gradle.spotless.SpotlessExtension +import cfw.buildlogic.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +@Suppress("unused") +class CodeLintConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.diffplug.spotless") + + extensions.configure { + kotlin { + target("**/*.kt", "**/*.kts") + targetExclude("**/build/**/*.kt") + ktlint( + libs.findLibrary("ktlint-core").get().get().version!!, + ).editorConfigOverride( + mapOf("ktlint_standard_annotation" to "disabled"), + ) + trimTrailingWhitespace() + endWithNewline() + } + format("xml") { + target("**/*.xml") + trimTrailingWhitespace() + endWithNewline() + } + } + } + } +} diff --git a/build-logic/convention/src/main/kotlin/SampleCommonDepsConventionPlugin.kt b/build-logic/convention/src/main/kotlin/SampleCommonDepsConventionPlugin.kt new file mode 100644 index 0000000..6562fc5 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/SampleCommonDepsConventionPlugin.kt @@ -0,0 +1,24 @@ +import cfw.buildlogic.library +import cfw.buildlogic.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies + +@Suppress("unused") +class SampleCommonDepsConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.android.application") + + dependencies { + add("implementation", libs.library("appcompat")) + add("implementation", libs.library("androidx-core-ktx")) + add("implementation", libs.library("androidx-lifecycle-runtime-ktx")) + add("implementation", libs.library("androidx-lifecycle-viewmodel-compose")) + add("implementation", libs.library("activity-compose")) + add("implementation", platform(libs.library("compose-bom"))) + add("implementation", libs.findBundle("compose-ui").get()) + } + } + } +} diff --git a/buildSrc/src/main/kotlin/buildLogic/AndroidConfig.kt b/build-logic/convention/src/main/kotlin/cfw/buildlogic/AndroidConfig.kt similarity index 94% rename from buildSrc/src/main/kotlin/buildLogic/AndroidConfig.kt rename to build-logic/convention/src/main/kotlin/cfw/buildlogic/AndroidConfig.kt index 667dd73..526dc16 100644 --- a/buildSrc/src/main/kotlin/buildLogic/AndroidConfig.kt +++ b/build-logic/convention/src/main/kotlin/cfw/buildlogic/AndroidConfig.kt @@ -1,4 +1,4 @@ -package buildlogic +package cfw.buildlogic import org.gradle.api.JavaVersion as GradleJavaVersion import org.jetbrains.kotlin.gradle.dsl.JvmTarget as KotlinJvmTarget @@ -11,4 +11,4 @@ object AndroidConfig { // https://youtrack.jetbrains.com/issue/KT-66995/JvmTarget-and-JavaVersion-compatibility-for-easier-JVM-version-setup val JavaVersion = GradleJavaVersion.VERSION_17 val JvmTarget = KotlinJvmTarget.JVM_17 -} \ No newline at end of file +} diff --git a/build-logic/convention/src/main/kotlin/cfw/buildlogic/Libs.kt b/build-logic/convention/src/main/kotlin/cfw/buildlogic/Libs.kt new file mode 100644 index 0000000..b4e203d --- /dev/null +++ b/build-logic/convention/src/main/kotlin/cfw/buildlogic/Libs.kt @@ -0,0 +1,20 @@ +package cfw.buildlogic + +import org.gradle.api.Project +import org.gradle.api.artifacts.MinimalExternalModuleDependency +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.getByType + +val Project.libs: VersionCatalog + get() = extensions.getByType().named("libs") + +fun VersionCatalog.library(name: String): Provider = + findLibrary(name).get() + +fun VersionCatalog.pluginId(name: String): String = + findPlugin(name).get().get().pluginId + +fun VersionCatalog.version(name: String): String = + findVersion(name).get().requiredVersion diff --git a/buildSrc/src/main/kotlin/buildLogic/ProjectExtensions.kt b/build-logic/convention/src/main/kotlin/cfw/buildlogic/ProjectExtensions.kt similarity index 59% rename from buildSrc/src/main/kotlin/buildLogic/ProjectExtensions.kt rename to build-logic/convention/src/main/kotlin/cfw/buildlogic/ProjectExtensions.kt index dc69f1d..b8caa39 100644 --- a/buildSrc/src/main/kotlin/buildLogic/ProjectExtensions.kt +++ b/build-logic/convention/src/main/kotlin/cfw/buildlogic/ProjectExtensions.kt @@ -1,11 +1,9 @@ -package buildlogic +package cfw.buildlogic import com.android.build.api.dsl.CommonExtension -import org.gradle.accessors.dm.LibrariesForLibs import org.gradle.api.Project -import org.gradle.kotlin.dsl.the - -val Project.libs get() = the() +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile internal fun Project.configureAndroid(commonExtension: CommonExtension) { commonExtension.apply { @@ -21,3 +19,11 @@ internal fun Project.configureAndroid(commonExtension: CommonExtension) { } } } + +internal fun Project.configureCommonKotlinCompileOptions() { + tasks.withType().configureEach { + compilerOptions { + jvmTarget.set(AndroidConfig.JvmTarget) + } + } +} diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties new file mode 100644 index 0000000..1c9073e --- /dev/null +++ b/build-logic/gradle.properties @@ -0,0 +1,4 @@ +# Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534 +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configureondemand=true diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000..f4f70e7 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,21 @@ +dependencyResolutionManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "build-logic" +include(":convention") diff --git a/build.gradle.kts b/build.gradle.kts index 22f8bda..5da5194 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,16 +1,10 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { + alias(libs.plugins.agp) apply false + alias(libs.plugins.android.library) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.compose.compiler) apply false + alias(libs.plugins.spotless) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.hilt.android) apply false } - -subprojects { - tasks.withType().configureEach { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_17) - } - } -} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index b729296..0000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - `kotlin-dsl` -} - -repositories { - gradlePluginPortal() - mavenCentral() - google() -} - -dependencies { - implementation(libs.androidx.gradle) - implementation(libs.kotlin.gradle) - implementation(libs.compose.compiler.gradle) - implementation(libs.spotless.gradle) - implementation(gradleApi()) - - // workaround to enable version catalogs (libs) in buildSrc - implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) -} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts deleted file mode 100644 index d6e07ba..0000000 --- a/buildSrc/settings.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -dependencyResolutionManagement { - versionCatalogs { - create("libs") { - from(files("../gradle/libs.versions.toml")) - } - } -} - -rootProject.name = "ComposeFloatingWindow-buildSrc" diff --git a/buildSrc/src/main/kotlin/android.application.gradle.kts b/buildSrc/src/main/kotlin/android.application.gradle.kts deleted file mode 100644 index d9d2576..0000000 --- a/buildSrc/src/main/kotlin/android.application.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -import buildlogic.AndroidConfig -import buildlogic.configureAndroid - -plugins { - id("com.android.application") - id("org.jetbrains.kotlin.plugin.compose") - id("code.lint") -} - -android { - defaultConfig { - targetSdk = AndroidConfig.TARGET_SDK - } - configureAndroid(this) - - buildFeatures { - compose = true - } - - packaging { - resources { - excludes.add("/META-INF/{AL2.0,LGPL2.1}") - } - } -} diff --git a/buildSrc/src/main/kotlin/android.tests.gradle.kts b/buildSrc/src/main/kotlin/android.tests.gradle.kts deleted file mode 100644 index cf44a8f..0000000 --- a/buildSrc/src/main/kotlin/android.tests.gradle.kts +++ /dev/null @@ -1,21 +0,0 @@ -import org.gradle.accessors.dm.LibrariesForLibs - -plugins { - id("com.android.application") -} - -android { - defaultConfig { - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } -} - -val libs = the() - -dependencies { - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.test.junit) - androidTestImplementation(libs.androidx.test.espresso) - androidTestImplementation(platform(libs.compose.bom)) - androidTestImplementation(libs.compose.ui.test.junit4) -} diff --git a/buildSrc/src/main/kotlin/code.lint.gradle.kts b/buildSrc/src/main/kotlin/code.lint.gradle.kts deleted file mode 100644 index 1b6539e..0000000 --- a/buildSrc/src/main/kotlin/code.lint.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -import org.gradle.accessors.dm.LibrariesForLibs - -plugins { - id("com.diffplug.spotless") -} - -val libs = the() - - -spotless { - kotlin { - target("**/*.kt", "**/*.kts") - targetExclude("**/build/**/*.kt") - ktlint(libs.ktlint.core.get().version).editorConfigOverride( - mapOf("ktlint_standard_annotation" to "disabled") - ) - trimTrailingWhitespace() - endWithNewline() - } - format("xml") { - target("**/*.xml") - trimTrailingWhitespace() - endWithNewline() - } -} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/library.gradle.kts b/buildSrc/src/main/kotlin/library.gradle.kts deleted file mode 100644 index 60ff369..0000000 --- a/buildSrc/src/main/kotlin/library.gradle.kts +++ /dev/null @@ -1,25 +0,0 @@ -import buildlogic.AndroidConfig -import buildlogic.configureAndroid - -plugins { - id("com.android.library") - id("org.jetbrains.kotlin.plugin.compose") - id("code.lint") -} - -android { - defaultConfig { - lint.targetSdk = AndroidConfig.TARGET_SDK - } - configureAndroid(this) - - buildFeatures { - compose = true - } - - packaging { - resources { - excludes.add("/META-INF/{AL2.0,LGPL2.1}") - } - } -} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/library.tests.gradle.kts b/buildSrc/src/main/kotlin/library.tests.gradle.kts deleted file mode 100644 index 6f2acda..0000000 --- a/buildSrc/src/main/kotlin/library.tests.gradle.kts +++ /dev/null @@ -1,27 +0,0 @@ -import org.gradle.accessors.dm.LibrariesForLibs - -plugins { - id("com.android.library") -} - -android { - defaultConfig { - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - } - testOptions { - unitTests.all { - it.useJUnitPlatform() - } - } -} - -val libs = the() - -dependencies { - testImplementation(libs.junit) - - androidTestImplementation(libs.androidx.test.junit) - androidTestImplementation(libs.androidx.test.espresso) - androidTestImplementation(platform(libs.compose.bom)) - androidTestImplementation(libs.compose.ui.test.junit4) -} diff --git a/buildSrc/src/main/kotlin/sample.common.deps.gradle.kts b/buildSrc/src/main/kotlin/sample.common.deps.gradle.kts deleted file mode 100644 index 3bce02e..0000000 --- a/buildSrc/src/main/kotlin/sample.common.deps.gradle.kts +++ /dev/null @@ -1,17 +0,0 @@ -import org.gradle.accessors.dm.LibrariesForLibs - -plugins { - id("com.android.application") -} - -val libs = the() - -dependencies { - implementation(libs.appcompat) - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.lifecycle.runtime.ktx) - implementation(libs.androidx.lifecycle.viewmodel.compose) - implementation(libs.activity.compose) - implementation(platform(libs.compose.bom)) - implementation(libs.bundles.compose.ui) -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0894dac..e2a416e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,4 @@ [versions] -compile-sdk = "36" -target-sdk = "36" -min-sdk = "24" - agp = "9.4.0" kotlin = "2.4.10" diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 90669ed..140acfb 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -1,7 +1,7 @@ plugins { `maven-publish` - id("library") - id("library.tests") + id("cfw.library") + id("cfw.library.tests") } android { diff --git a/plans/plan-buildsrc-to-build-logic-migration.md b/plans/plan-buildsrc-to-build-logic-migration.md new file mode 100644 index 0000000..300ef38 --- /dev/null +++ b/plans/plan-buildsrc-to-build-logic-migration.md @@ -0,0 +1,139 @@ +# Implementation Plan — Migrate buildSrc to build-logic + +**Branch:** `chore/gradle/switch-to-build-logic` +**Status:** Implemented (working tree, pending commit) +**Date:** 2026-09-05 + +## Problem statement + +The project keeps its convention plugins in `buildSrc/`, the legacy mechanism for sharing build +logic. `buildSrc` is compiled as part of every build, invalidates caches frequently, and needs a +well-known workaround (`implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))`) +to make the version catalog accessible to precompiled script plugins. The sibling repository +[generic-datastore](../generic-datastore) already uses the modern `build-logic` included-build +pattern; this migration ports that structure here (`gd.*` → `cfw.*`). + +## Current state (buildSrc) + +- Six precompiled script plugins: `android.application`, `android.tests`, `code.lint`, `library`, + `library.tests`, `sample.common.deps`; `code.lint` is applied transitively by + `android.application` and `library`. +- Helpers in package `buildlogic`: `AndroidConfig` (COMPILE_SDK 37, TARGET_SDK 37, MIN_SDK 24, + Java/JVM 17) and `ProjectExtensions` (`configureAndroid`). +- AGP 9.4.0, Kotlin 2.4.10, Spotless 8.10.2 are put on the buildSrc classpath via `implementation`; + plugins are applied by literal ID with the version coming from that classpath. +- Modules consume the plugins by ID; module scripts otherwise only use the root `libs` catalog. +- Gradle 9.7.1, AGP 9 built-in Kotlin (`org.jetbrains.kotlin.android` is never applied), + configuration cache enabled, JDK 17, JitPack builds only `:library`. +- Root `build.gradle.kts` sets `jvmTarget = JVM_17` on every subproject's `KotlinCompile` tasks. + +## Target structure (mirroring generic-datastore) + +A `build-logic` included build with a single `:convention` subproject; class-based convention +plugins in the default package registered in a `gradlePlugin` block; catalog access via a +`VersionCatalog` helper (`Libs.kt`); `compileOnly` for AGP/KGP/compose-compiler; the root build +pins every consumed plugin `apply false` so versions resolve on the main build classpath. + +``` +build-logic/ +├── gradle.properties # gradle#2534 comment; parallel/caching/configureondemand +├── settings.gradle.kts # dependencyResolutionManagement (google regex-filtered, +│ # mavenCentral, gradlePluginPortal) + versionCatalogs +│ # create("libs") from files("../gradle/libs.versions.toml") +│ # rootProject.name = "build-logic"; include(":convention") +└── convention/ + ├── build.gradle.kts # `kotlin-dsl`; compileOnly(libs.androidx.gradle / libs.kotlin.gradle / + │ # libs.compose.compiler.gradle); implementation(libs.spotless.gradle); + │ # gradlePlugin { plugins { register(...) } } for the 6 plugins + └── src/main/kotlin/ + ├── AndroidApplicationConventionPlugin.kt + ├── AndroidTestsConventionPlugin.kt + ├── CodeLintConventionPlugin.kt + ├── AndroidLibraryConventionPlugin.kt + ├── AndroidLibraryTestsConventionPlugin.kt + ├── SampleCommonDepsConventionPlugin.kt + └── cfw/buildlogic/ + ├── AndroidConfig.kt # ported unchanged (37/37/24, Java 17) + ├── ProjectExtensions.kt # configureAndroid() ported; + configureCommonKotlinCompileOptions() + └── Libs.kt # copied from generic-datastore (libs accessor + library/pluginId/version helpers) +``` + +## Plugin ID mapping (chosen: `cfw.*` namespace) + +| buildSrc (old) | build-logic (new) | Class (default package) | +|---------------------|----------------------------|------------------------------------| +| `android.application` | `cfw.android.application` | `AndroidApplicationConventionPlugin` | +| `android.tests` | `cfw.android.tests` | `AndroidTestsConventionPlugin` | +| `code.lint` | `cfw.code.lint` | `CodeLintConventionPlugin` | +| `library` | `cfw.library` | `AndroidLibraryConventionPlugin` | +| `library.tests` | `cfw.library.tests` | `AndroidLibraryTestsConventionPlugin`| +| `sample.common.deps` | `cfw.sample.common.deps` | `SampleCommonDepsConventionPlugin` | + +Helpers move from package `buildlogic` to `cfw.buildlogic` (mirroring `gd.buildlogic`). + +## Convention plugin bodies (1:1 ports of existing logic) + +- **CodeLintConventionPlugin** — same shape as generic-datastore's `SpotlessConventionPlugin`: + `pluginManager.apply("com.diffplug.spotless")`; ktlint version via + `libs.findLibrary("ktlint-core")`, `editorConfigOverride(ktlint_standard_annotation = disabled)`, + `trimTrailingWhitespace()`, `endWithNewline()`, xml `format("xml")` block. +- **AndroidApplicationConventionPlugin** — applies `com.android.application`, + `libs.pluginId("compose-compiler")`, chained `cfw.code.lint`; configures `ApplicationExtension`: + `defaultConfig.targetSdk`, `configureAndroid(this)`, `buildFeatures.compose = true`, packaging + excludes `/META-INF/{AL2.0,LGPL2.1}`; then `configureCommonKotlinCompileOptions()`. +- **AndroidTestsConventionPlugin** — applies `com.android.application`; `testInstrumentationRunner`; + deps via catalog helpers: `testImplementation` junit; `androidTestImplementation` + androidx-test-junit, androidx-test-espresso, `platform(compose-bom)`, compose-ui-test-junit4. +- **AndroidLibraryConventionPlugin** — applies `com.android.library`, compose-compiler, chained + `cfw.code.lint`; configures `LibraryExtension`: `defaultConfig.lint.targetSdk`, + `configureAndroid`, `buildFeatures.compose`, packaging excludes; then + `configureCommonKotlinCompileOptions()`. +- **AndroidLibraryTestsConventionPlugin** — applies `com.android.library`; testInstrumentationRunner; + `testOptions.unitTests.all { useJUnitPlatform() }`; same test-dep set as AndroidTests (plus junit). +- **SampleCommonDepsConventionPlugin** — applies `com.android.application`; adds appcompat, + androidx-core-ktx, lifecycle-runtime-ktx, lifecycle-viewmodel-compose, activity-compose, + `platform(compose-bom)`, bundle `compose-ui`. +- **`configureCommonKotlinCompileOptions()`** — + `tasks.withType().configureEach { compilerOptions { jvmTarget.set(JVM_17) } }` + only (no opt-ins — preserves current behavior). + +## Existing file changes + +1. **`settings.gradle.kts`** — add `includeBuild("build-logic")` as the first statement inside + `pluginManagement`. +2. **`build.gradle.kts`** (root) — becomes a plugins-only block (basis pattern), pinning with + `apply false`: `agp`, `android-library`, `compose-compiler`, `kotlin-android` (all already in + the catalog, currently unused), `spotless`, plus the existing `ksp` and `hilt-android`. Remove + the `subprojects { KotlinCompile … }` block and imports — that jvmTarget=17 config moves into + the app/library convention plugins (`configureCommonKotlinCompileOptions`), covering exactly the + same modules. The `kotlin-android` pin is what puts KGP on the main build classpath so those + AGP/KGP types resolve at runtime (build-logic deps are `compileOnly`, per the basis). +3. **Module scripts** — swap to `cfw.*` IDs: `library` (`cfw.library`, `cfw.library.tests`), and + the 4 samples (`cfw.android.application`, `cfw.android.tests`, `cfw.sample.common.deps`). + Nothing else in them changes. +4. **Delete `buildSrc/`** entirely. +5. **Catalog cleanup** — remove the stale, unused `[versions]` `compile-sdk = "36"` / + `target-sdk = "36"` / `min-sdk = "24"` (they contradict `AndroidConfig`'s actual 37/37/24 and + nothing references them; the basis catalog carries no SDK entries). + +## Behavior preserved + +- `code.lint` remains applied transitively by the app/library conventions. +- AGP 9 built-in Kotlin: `org.jetbrains.kotlin.android` still never applied to projects (only a + classpath pin). +- All SDK/JVM constants, dependency sets, packaging excludes, and spotless config stay + byte-for-byte equivalent. +- JitPack (`JITPACK=true`) still configures only root + `:library`; the included build works there + since it's wired via `pluginManagement`. + +## Verification + +1. `./gradlew help` — full configuration with the included build. +2. `./gradlew :library:assembleRelease` — library convention + spotless + publishing setup. +3. `./gradlew :samples:app-activity:assembleDebug :samples:service-hilt:assembleDebug` — app + conventions, sample deps, hilt/ksp path. +4. `./gradlew spotlessCheck` — lint config still wired (spotless lints module sources only, + exactly as before; build-logic sources are not linted, same as the basis). +5. `JITPACK=true ./gradlew help` — library-only JitPack path. +6. Spot-check configuration cache still works (enabled in `gradle.properties`; the included build + is CC-compatible). diff --git a/samples/app-activity/build.gradle.kts b/samples/app-activity/build.gradle.kts index 885ea2d..3e63b1d 100644 --- a/samples/app-activity/build.gradle.kts +++ b/samples/app-activity/build.gradle.kts @@ -1,7 +1,7 @@ plugins { - id("android.application") - id("android.tests") - id("sample.common.deps") + id("cfw.android.application") + id("cfw.android.tests") + id("cfw.sample.common.deps") } android { diff --git a/samples/fullscreen-dialog/build.gradle.kts b/samples/fullscreen-dialog/build.gradle.kts index 31749f8..d4b15e6 100644 --- a/samples/fullscreen-dialog/build.gradle.kts +++ b/samples/fullscreen-dialog/build.gradle.kts @@ -1,7 +1,7 @@ plugins { - id("android.application") - id("android.tests") - id("sample.common.deps") + id("cfw.android.application") + id("cfw.android.tests") + id("cfw.sample.common.deps") } android { diff --git a/samples/keyboard-usage/build.gradle.kts b/samples/keyboard-usage/build.gradle.kts index 8897bcd..c596684 100644 --- a/samples/keyboard-usage/build.gradle.kts +++ b/samples/keyboard-usage/build.gradle.kts @@ -1,7 +1,7 @@ plugins { - id("android.application") - id("android.tests") - id("sample.common.deps") + id("cfw.android.application") + id("cfw.android.tests") + id("cfw.sample.common.deps") } android { diff --git a/samples/service-hilt/build.gradle.kts b/samples/service-hilt/build.gradle.kts index bf9c887..2e2e955 100644 --- a/samples/service-hilt/build.gradle.kts +++ b/samples/service-hilt/build.gradle.kts @@ -1,7 +1,7 @@ plugins { - id("android.application") - id("android.tests") - id("sample.common.deps") + id("cfw.android.application") + id("cfw.android.tests") + id("cfw.sample.common.deps") alias(libs.plugins.hilt.android) alias(libs.plugins.ksp) diff --git a/settings.gradle.kts b/settings.gradle.kts index e9cc461..2316e32 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,5 @@ pluginManagement { + includeBuild("build-logic") repositories { google { content {