skip to content

What are the rules and gotchas around file placement, packages, and ids for precompiled convention plugins?

level: middleimportance: should knowfreq 35%

answer

  1. src/main/kotlin + kotlin-dsl
  2. id = filename minus .gradle.kts
  3. dotted filename → dotted id
  4. package is prepended to id
  5. includeBuild in pluginManagement

basics

~20 s

Place *.gradle.kts files in src/main/kotlin of a kotlin-dsl project. The plugin id is the filename minus .gradle.kts. A package declaration namespaces the generated class but does not change the id; the dotted filename itself forms the id.

solid answer

~40 s

Precompiled Kotlin convention scripts must live in **`src/main/kotlin`** (Groovy ones in `src/main/groovy`) of a project applying **`kotlin-dsl`** (or `groovy-gradle-plugin`). The **plugin id equals the filename with `.gradle.kts` stripped**, so `com.acme.java-conventions.gradle.kts` → id `com.acme.java-conventions`. To namespace, you can put the file in a package directory and add a `package com.acme;` declaration *at the top of the script* — but be careful: in Gradle the recommended way to namespace is simply to use a **dotted filename** (`com.acme.foo.gradle.kts`). If you use a package directory plus `package` statement, the package name is **prepended** to the plugin id. Filenames can't contain characters illegal in plugin ids, and the id must be a valid plugin id (dotted, lowercase-friendly). The enclosing build (`build-logic`) is wired via `includeBuild` in `pluginManagement` so consumers can apply by id.

code

bash · 6 lines
bash
# Both produce plugin id `com.acme.java-conventions`:
# (A) dotted filename, no package directory
build-logic/src/main/kotlin/com.acme.java-conventions.gradle.kts

# (B) package directory + `package com.acme` at top of file
build-logic/src/main/kotlin/com/acme/java-conventions.gradle.kts

go deeper

for a junior

Know files go in src/main/kotlin and the id is the filename.

for a middle

Explain the package-prepended-to-id rule and the dotted-filename alternative, plus the includeBuild wiring.

for a senior

Advise a single naming convention org-wide and explain the common doubled-prefix mistake.

for a principal

Set a naming standard (namespace prefix per org/team) enforced across repos for discoverability and collision-avoidance.

## Where files go - Kotlin convention plugins: `build-logic/src/main/kotlin/*.gradle.kts` - Groovy convention plugins: `build-logic/src/main/groovy/*.gradle` - The project must apply **`kotlin-dsl`** (Kotlin) or **`groovy-gradle-plugin`** (Groovy). These plugins scan those source roots and compile each script into a `Plugin<Project>`. ## Id derivation The generated plugin's id is the **filename minus the build-script suffix**: | Filename | Plugin id | |---|---| | `java-conventions.gradle.kts` | `java-conventions` | | `com.acme.java-conventions.gradle.kts` | `com.acme.java-conventions` | The dotted prefix in the filename is the simplest, recommended way to give the plugin a namespaced id. ## Packages If you place the file in a package directory, e.g. `src/main/kotlin/com/acme/java-conventions.gradle.kts`, and add `package com.acme` at the top of the script, the **package is prepended** to the id: the plugin id becomes `com.acme.java-conventions`. So two routes lead to the same id — a dotted *filename* or a *package* + plain filename. Mixing them (dotted filename **and** a package) compounds the prefix and is a common confusion; pick one convention. ## Valid ids A plugin id must be a valid Gradle plugin id: dot-separated segments of alphanumerics, `-`, and `_`. Filenames that would produce an illegal id won't compile. Stick to lowercase, dotted, hyphen-separated names like `com.acme.kotlin-library`. ## Wiring for consumers For the ids to be visible to the main build, `build-logic` is usually included in `settings.gradle.kts`: ```kotlin pluginManagement { includeBuild("build-logic") } ``` (With `buildSrc`, the conventions are visible automatically — but the buildSrc-vs-build-logic decision is owned by a separate topic.) ## Gotchas 1. Wrong source root (`src/main/java` or root) → script ignored, plugin not generated. 2. Forgetting `kotlin-dsl` → no compilation, no plugin. 3. Expecting the package to be ignored — it's prepended to the id. 4. Using characters illegal in plugin ids in the filename. 5. Forgetting `includeBuild` in `pluginManagement`, so consumers can't resolve the id.

  • If you put a file in `src/main/kotlin/com/acme/` with `package com.acme` AND name it `com.acme.foo.gradle.kts`, what id results?
    The package and the dotted filename both contribute, so you get a doubled prefix like `com.acme.com.acme.foo` — a classic mistake. Use either a dotted filename or a package, not both.
  • What happens if the script is in `src/main/java` instead of `src/main/kotlin`?
    The `kotlin-dsl` plugin won't pick it up, so no plugin is generated and applying its expected id fails with 'plugin not found'.

saying these in an interview costs you the question

  • Believing the package declaration is ignored for the id.
  • Putting scripts in the wrong source root.
  • Using filenames that aren't valid plugin ids.

context