$npx -y skills add rcosteira79/android-skills --skill android-gradle-logicUse when setting up or refactoring Android Gradle build logic — convention plugins, composite builds, version catalogs, and shared build configuration across modules.
| 1 | # Android Gradle Build Logic |
| 2 | |
| 3 | Centralise build configuration in reusable **Convention Plugins** inside a `build-logic/` composite build, so each module's `build.gradle.kts` collapses to `plugins { alias(libs.plugins.myapp.android.library) }` plus a `namespace`. |
| 4 | |
| 5 | The canonical worked example is **[nowinandroid's `build-logic/`](https://github.com/android/nowinandroid/tree/main/build-logic)** — start from it rather than hand-rolling. This skill covers the three wiring details that are easy to get wrong, plus the AGP 9 deltas. |
| 6 | |
| 7 | ## The wiring gotchas |
| 8 | |
| 9 | **1. `build-logic` does NOT inherit the root version catalog — recreate it.** A composite build has its own `settings.gradle.kts`; the root `libs` catalog is invisible inside `build-logic` until you declare it. Without this the convention plugins can't reference `libs.*` and won't compile: |
| 10 | |
| 11 | ```kotlin |
| 12 | // build-logic/settings.gradle.kts |
| 13 | dependencyResolutionManagement { |
| 14 | versionCatalogs { |
| 15 | create("libs") { from(files("../gradle/libs.versions.toml")) } |
| 16 | } |
| 17 | } |
| 18 | ``` |
| 19 | |
| 20 | **2. Declare the convention-plugin ids in `[plugins]` with `version = "unspecified"`** — otherwise `alias(libs.plugins.myapp.android.library)` in a module file doesn't resolve (Gradle treats it as a versioned external plugin and fails to find it). The `id` here must match the one you `register(...)`: |
| 21 | |
| 22 | ```toml |
| 23 | # gradle/libs.versions.toml |
| 24 | [plugins] |
| 25 | myapp-android-application = { id = "myapp.android.application", version = "unspecified" } |
| 26 | myapp-android-library = { id = "myapp.android.library", version = "unspecified" } |
| 27 | myapp-android-compose = { id = "myapp.android.compose", version = "unspecified" } |
| 28 | ``` |
| 29 | |
| 30 | **3. Set the JVM toolchain via Kotlin's extension, not `JavaPluginExtension`.** AGP's `com.android.application` / `com.android.library` plugins do **not** apply Gradle's `java` plugin, so `configure<JavaPluginExtension> { ... }` throws *"Extension of type JavaPluginExtension does not exist"* and fails configuration in every module. Use the Kotlin extension inside the convention plugin: |
| 31 | |
| 32 | ```kotlin |
| 33 | extensions.configure<org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension> { |
| 34 | jvmToolchain(21) |
| 35 | } |
| 36 | // equivalently: kotlin { jvmToolchain(21) } |
| 37 | ``` |
| 38 | |
| 39 | ## AGP 9 Implications |
| 40 | |
| 41 | The convention plugin pattern above targets AGP 8. AGP 9 changes several things that hit build logic directly: it drops the standalone `org.jetbrains.kotlin.android` plugin (Kotlin is built into `com.android.application` / `com.android.library`), removes `BaseExtension` and the old variant APIs (`applicationVariants` → `androidComponents { onVariants { … } }`), moves `kotlinOptions {}` to a top-level `kotlin { compilerOptions { … } }`, and makes `kapt` incompatible (migrate to KSP). Any convention plugin that touches these needs updating. |
| 42 | |
| 43 | Defer to the dedicated migration skills for the mechanics rather than duplicating the steps here: Google's [`agp-9-upgrade`](https://github.com/android/skills/tree/main/agp-9-upgrade) for pure-Android projects, JetBrains' [`kotlin-tooling-agp9-migration`](https://github.com/Kotlin/kotlin-agent-skills/tree/main/skills/kotlin-tooling-agp9-migration) for KMP, and this repo's `gradle-build-performance` skill for the kapt → KSP step. |
| 44 | |
| 45 | ## Checklist |
| 46 | |
| 47 | - [ ] `build-logic` included as a composite build (`includeBuild("build-logic")`) in the root `settings.gradle.kts` |
| 48 | - [ ] `build-logic/settings.gradle.kts` recreates the `libs` catalog via `from(files("../gradle/libs.versions.toml"))` |
| 49 | - [ ] Convention plugins `register`-ed with stable ids **and** declared in `[plugins]` with `version = "unspecified"` |
| 50 | - [ ] JVM toolchain set via `KotlinAndroidProjectExtension` / `kotlin { jvmToolchain() }`, never `JavaPluginExtension` |
| 51 | - [ ] `compileSdk` / `minSdk` / Compose set once in the plugins, not per module |