Explain plugin marker artifacts and when you must use useModule instead of useVersion.
answer
- marker = id:id.gradle.plugin:version
- tiny POM redirecting to impl jar
- no marker -> useModule
- useVersion needs marker, useModule replaces coords
- add repo in pluginManagement.repositories
basics
~20 sA plugin marker is a tiny POM at id:id.gradle.plugin:version that redirects an id to its real jar. When a plugin has no marker, useVersion can't find it, so you call useModule to point at the real artifact coordinates.
solid answer
~40 sWhen you request `id 'com.example.foo' version '1.2.3'`, Gradle looks up a **plugin marker artifact** at `com.example.foo:com.example.foo.gradle.plugin:1.2.3` — a generated POM whose only job is to declare a dependency on the real implementation jar. The Gradle Plugin Portal and the `java-gradle-plugin` publishing both produce these automatically. But plugins published only to a plain Maven repo as ordinary libraries often have **no marker**. Then `useVersion` is useless because there's nothing at the marker coordinates to resolve. The fix is `useModule("group:artifact:version")` in `eachPlugin`, which tells Gradle to resolve the implementation directly from those coordinates and treat it as the provider of that plugin id. This is the canonical bridge for legacy or internal plugins that predate the marker convention.
code
groovy · 14 linespluginManagement {
repositories {
gradlePluginPortal()
maven { url 'https://repo.acme.internal/maven' } // where the real jar lives
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == 'com.acme.tools') {
// no marker published -> point at the actual artifact
useModule("com.acme:acme-gradle-plugin:${requested.version}")
}
}
}
}go deeper
Awareness that plugin ids map to artifacts via a marker is enough.
Explain the marker coordinate pattern and that useModule is for no-marker plugins.
Walk through the full resolution flow, the repository requirement, and contrast useVersion vs useModule precisely.
Advise teams to publish markers via java-gradle-plugin so consumers never need useModule; treat useModule as a controlled interop bridge for legacy artifacts.
## What a plugin marker is The `plugins {}` DSL resolves plugins by **id**, but artifact repositories are addressed by **coordinates** (group:artifact:version). Gradle bridges the two with a *plugin marker*: a synthetic module at coordinates derived from the id — ``` <plugin-id>:<plugin-id>.gradle.plugin:<version> ``` For id `com.example.foo` and version `1.2.3` that is `com.example.foo:com.example.foo.gradle.plugin:1.2.3`. The marker is a tiny POM with no code; it just declares a runtime dependency on the **real** implementation jar (e.g. `com.example:foo-impl:1.2.3`). When you apply the plugin, Gradle resolves the marker, follows it to the impl jar, and loads the plugin class. ## Who publishes markers - The **Gradle Plugin Portal** publishes markers for everything hosted there. - The `java-gradle-plugin` plugin, when you publish your own plugin with `gradlePlugin { plugins { ... } }`, auto-generates and publishes markers. ## When there is no marker Many internal/enterprise plugins, or older third-party ones, are published to a corporate Maven repo as a plain library — `com.acme:acme-gradle-plugin:1.0` — with **no marker** at `com.acme.tools:com.acme.tools.gradle.plugin:1.0`. A bare `id 'com.acme.tools'` request then fails: Gradle can't find the marker. ## The useModule fix ``` pluginManagement { resolutionStrategy { eachPlugin { if (requested.id.id == 'com.acme.tools') { useModule("com.acme:acme-gradle-plugin:${requested.version}") } } } } ``` `useModule` overrides the *target coordinates* entirely, so Gradle skips the marker lookup and resolves the implementation directly. `requested.version` lets you keep the version coming from the build script. ## useVersion vs useModule — the distinction - `useVersion` changes only the **version** of the marker resolution; the marker must still exist. - `useModule` changes the **whole coordinate** (group, artifact, version), bypassing the marker. Use it precisely when no marker is published. ## Repository requirement Whatever coordinates `useModule` targets must be reachable from a repository declared in `pluginManagement { repositories { ... } }` — adding `mavenCentral()` or your corporate repo there is usually required, since by default only the Plugin Portal is searched.
- What coordinates does Gradle derive for the marker of plugin id 'org.foo.bar' at version 2.0?org.foo.bar:org.foo.bar.gradle.plugin:2.0 — the group and artifact both equal the id (with .gradle.plugin appended to the artifact).
- Besides the eachPlugin hook, what else must be in place for useModule to succeed?The repository hosting the real artifact must be declared in pluginManagement.repositories; otherwise Gradle, which only searches the Plugin Portal by default, cannot find the coordinates.
- How do you normally avoid needing useModule when publishing your own plugin?Use the java-gradle-plugin plugin with a gradlePlugin { plugins { ... } } block, which auto-generates and publishes the marker artifact alongside the implementation.
saying these in an interview costs you the question
- Saying useVersion works when no marker exists — it cannot resolve a nonexistent marker.
- Forgetting that the target repository must be added to pluginManagement.repositories.
- Confusing the marker artifact with the implementation jar; they are distinct modules.