What are Gradle plugin marker artifacts, and why are they needed for resolving plugins via the plugins {} block from a custom repository?
answer
- <id>:<id>.gradle.plugin:<version>
- marker = empty module depending on real jar
- id → coordinates bridge
- auto-generated with java-gradle-plugin + maven-publish
- resolved from pluginManagement.repositories
basics
~20 sA plugin marker is a tiny published module whose coordinates are derived from the plugin id (id:id.gradle.plugin) and whose only job is to declare a dependency on the real plugin jar. It lets plugins {} resolve a plugin by id rather than by Maven coordinates.
solid answer
~40 sWhen you apply a plugin with `plugins { id("com.acme.greeting") version "1.0" }`, Gradle has only the **id**, not Maven coordinates. To bridge that gap, each plugin publishes a **marker artifact**: a minimal POM/module at coordinates `com.acme.greeting:com.acme.greeting.gradle.plugin:1.0`. The marker has no code — it simply declares a runtime dependency on the actual implementation module (e.g. `com.acme:greeting-plugin:1.0`). Resolution flow: 1. Gradle maps the id to the marker coordinates `<id>:<id>.gradle.plugin`. 2. It looks the marker up in the repositories declared in `pluginManagement { repositories { … } }`. 3. The marker pulls in the real implementation jar transitively. When you use `java-gradle-plugin` together with `maven-publish`, the marker publications are generated automatically (one per declared plugin). The Plugin Portal works the same way; for a custom repo you must publish both the implementation and the markers.
code
kotlin · 19 linesplugins {
`java-gradle-plugin`
`maven-publish`
}
group = "com.acme"
version = "1.0"
gradlePlugin {
plugins {
create("greeting") {
id = "com.acme.greeting"
implementationClass = "com.acme.GreetingPlugin"
}
}
}
// Publishing now produces:
// com.acme:greeting:1.0 (implementation)
// com.acme.greeting:com.acme.greeting.gradle.plugin:1.0 (marker)go deeper
Know that plugins {} needs a marker to find a plugin by id from a repository.
State the marker coordinate pattern and that java-gradle-plugin + maven-publish generates them automatically.
Walk the full id→marker→implementation resolution chain and contrast with the legacy buildscript classpath path.
Reason about repository topology for an internal plugin ecosystem: where markers live, mirroring, and consumption guarantees across teams.
## The id-to-coordinates problem The `plugins {}` DSL resolves plugins by **id**, e.g. `id("com.acme.greeting")`. But artifact repositories are keyed by Maven/Ivy **coordinates** (group:name:version). Something must translate an id into coordinates. That something is the **plugin marker artifact**. ## Marker coordinates For a plugin id `com.acme.greeting`, Gradle expects a marker module at: ``` group = com.acme.greeting name = com.acme.greeting.gradle.plugin version = <the version requested in plugins {}> ``` i.e. `<id>:<id>.gradle.plugin:<version>`. The marker is essentially an empty module: its POM declares a single dependency on the real implementation module that contains the compiled `Plugin<Project>` and the descriptor. ## Resolution sequence 1. `plugins { id("com.acme.greeting") version "1.0" }` is encountered. 2. Gradle searches `pluginManagement.repositories` for the marker `com.acme.greeting:com.acme.greeting.gradle.plugin:1.0`. 3. The marker's dependency resolves the real jar onto the build's plugin classpath. 4. Gradle reads `META-INF/gradle-plugins/com.acme.greeting.properties` from that jar to find `implementation-class` and applies it. ## Generating markers With `java-gradle-plugin` + `maven-publish` applied, Gradle automatically creates a `pluginMaven` publication for the implementation and one `<name>PluginMarkerMaven` publication per declared plugin. You then publish all of them: ```kotlin plugins { `java-gradle-plugin` `maven-publish` } ``` ## Why this matters in practice - A consumer who only knows the id can resolve the plugin without knowing internal coordinates. - One implementation jar can back several ids — each gets its own marker. - Forgetting to publish markers is the classic reason `plugins {}` fails with "Plugin not found" even though the implementation jar is in the repo (it would still work via `buildscript {}` classpath + `apply plugin`, which bypasses markers).
- Why can apply plugin (legacy) resolve a plugin from buildscript classpath without a marker, but plugins {} cannot?The legacy path puts the jar directly on the buildscript classpath, so Gradle just needs the descriptor. The plugins {} block resolves by id from a repository, which requires the marker to translate the id into coordinates.
- If one implementation jar declares two plugin ids, how many markers are published?Two — one marker module per declared id, each pointing at the same implementation module.
saying these in an interview costs you the question
- Saying the marker contains the plugin code (it is empty; it only declares a dependency).
- Forgetting that markers are needed specifically for plugins {} repository resolution, not for direct buildscript classpath use.
- Inventing marker coordinates other than <id>:<id>.gradle.plugin.