How do you apply and configure other plugins inside a precompiled convention plugin, and how do you get type-safe accessors for them?
answer
- plugins {} block inside the convention plugin
- kotlin-dsl generates type-safe accessors
- community plugin → add to build-logic classpath
- marker coordinates as implementation dep
- no version in convention plugins {}
basics
~20 sRequest them in the convention plugin's own plugins {} block. The kotlin-dsl plugin then generates type-safe accessors so you can configure them with java { }, kotlin { }, etc. Their plugin coordinates must be on the build-logic project's classpath.
solid answer
~50 sInside a precompiled convention plugin you use a real **`plugins { }` block** — exactly like a normal build script — to apply core plugins (`` `java-library` ``) or community plugins (`id("org.jetbrains.kotlin.jvm")`). Because the `kotlin-dsl` plugin compiles the script, it can **generate type-safe accessors** for the extensions and configurations those plugins contribute, so `java { }`, `kotlin { }`, `tasks.test { }`, and custom extension blocks resolve with full completion. The catch: for a **community** plugin you can request by id, that plugin's implementation must be on the **build-logic project's classpath** — you add it as an `implementation` dependency in `build-logic/build.gradle.kts` using its **marker artifact coordinates** (`gradlePlugin.dependencies` form: `"group:artifact:version"` of the plugin's Gradle marker). Without that dependency, `plugins { id(...) }` fails to resolve at compile time. You cannot use `version` in the convention plugin's `plugins {}` block — versions come from the classpath dependency instead.
code
kotlin · 12 lines// build-logic/build.gradle.kts — put the plugin on the classpath
plugins { `kotlin-dsl` }
repositories { gradlePluginPortal(); mavenCentral() }
dependencies {
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.24")
}
// build-logic/src/main/kotlin/com.acme.kotlin-conventions.gradle.kts
plugins {
id("org.jetbrains.kotlin.jvm") // NO version here
}
kotlin { jvmToolchain(21) } // type-safe accessorgo deeper
Know you use a plugins {} block inside the convention plugin to apply other plugins.
Explain the classpath dependency for community plugins and the no-version rule, plus how type-safe accessors arise.
Discuss layering convention plugins, version-catalog plugin markers, and managing plugin versions centrally.
Standardize plugin versions org-wide via catalogs and a base convention so every repo resolves the same plugin baseline.
## Applying plugins from inside a convention plugin A precompiled convention plugin is itself a build script, so it can carry a `plugins { }` block: ```kotlin // build-logic/src/main/kotlin/com.acme.kotlin-conventions.gradle.kts plugins { `java-library` id("org.jetbrains.kotlin.jvm") } kotlin { jvmToolchain(21) } ``` ### Type-safe accessors The magic of `kotlin-dsl` is that, after a plugin is requested in the `plugins {}` block, Gradle **generates type-safe accessors** for the model objects that plugin contributes: the `kotlin { }` and `java { }` extension blocks, named tasks like `tasks.named<Test>("test")`, configurations like `implementation(...)`, and so on. This is why writing convention plugins feels exactly like writing a regular build script — completion and type-checking work. ### The classpath requirement for community plugins Core plugins (java, java-library, application, jvm-test-suite…) ship with Gradle and need nothing extra. **Community plugins** are different: to *resolve* `id("org.jetbrains.kotlin.jvm")` at the convention plugin's compile time, the plugin's implementation must be **on the build-logic project's compile classpath**. You add it as a normal dependency in `build-logic/build.gradle.kts`, using the plugin's **marker coordinates**: ```kotlin // build-logic/build.gradle.kts plugins { `kotlin-dsl` } repositories { gradlePluginPortal() mavenCentral() } dependencies { implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.24") // or via marker: implementation("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.9.24") } ``` A common, cleaner idiom is to map the plugin marker through a **version catalog** and reference it: `implementation(libs.plugins.kotlin.jvm.asProvider())` style via `pluginToImplementation` patterns, but the essence is the same — the plugin must be on the classpath. ### No version in the convention plugin's plugins block In the convention plugin's own `plugins {}` block you must **not** specify a `version` — `id("...") version "x"` is illegal there because the version is fixed by the classpath dependency you declared in `build-logic/build.gradle.kts`. Specifying a version yields a build error. ### Order matters If your convention configuration relies on another convention plugin (e.g. `com.acme.kotlin-conventions` builds on `com.acme.base-conventions`), apply the base one in the dependent's `plugins {}` block by id — Gradle resolves the dependency order.
- Why does `id("org.jetbrains.kotlin.jvm") version "1.9.24"` fail inside a convention plugin?Inside a precompiled script plugin the plugin version is fixed by the build-logic project's classpath dependency, so declaring a `version` there is disallowed and produces a build error. Set the version in `build-logic/build.gradle.kts` instead.
- What happens if you request a community plugin by id but forget to add it to the build-logic classpath?Compilation of the convention plugin fails because the plugin id cannot be resolved — Gradle can't find the plugin marker/implementation on the classpath.
saying these in an interview costs you the question
- Putting `version` in the convention plugin's `plugins {}` block.
- Forgetting that community plugins need a classpath dependency in build-logic.
- Believing type-safe accessors require manual imports of extension types.