Walk me through exactly what Gradle does, step by step, to resolve `id("com.gradle.develocity")` from the Plugin Portal into runnable plugin classes.
answer
- resolution at settings/config time, before tasks
- marker POM → impl dependency → jar
- META-INF/gradle-plugins/<id>.properties → implementation-class
- pluginManagement repos, default gradlePluginPortal()
- cached in Gradle user home, --offline works
basics
~10 sGradle builds the marker coordinate com.gradle.develocity:com.gradle.develocity.gradle.plugin:<v>, fetches that POM from the pluginManagement repos (Portal by default), reads its single dependency, downloads the implementation jar, and loads the plugin class onto the build's classpath.
solid answer
~50 sResolution runs at **settings/configuration time**, before the project is configured: 1. Gradle reads the requested `id` and `version` from the `plugins {}` block. 2. It synthesizes the **marker coordinate**: `com.gradle.develocity:com.gradle.develocity.gradle.plugin:<version>`. 3. It searches the repositories declared in `pluginManagement { repositories { … } }` (default: `gradlePluginPortal()`) for that marker **POM**. 4. The marker POM declares one **dependency** on the real implementation module; Gradle resolves and downloads that **implementation jar** (and its transitive deps) into the buildscript/plugin classpath. 5. Gradle reads the jar's plugin descriptor under `META-INF/gradle-plugins/<id>.properties`, which names the `implementation-class`. 6. It instantiates that class and calls `apply(target)`. If no version is given (typical when it comes from a version catalog or `pluginManagement.plugins`), step 2 uses the resolved version. Markers and implementation jars are cached in the Gradle user home, so subsequent builds skip the network.
code
toml · 2 lines# META-INF/gradle-plugins/com.gradle.develocity.properties (inside the impl jar)
implementation-class=com.gradle.develocity.agent.gradle.DevelocityPlugingo deeper
Recall the broad flow: id becomes a marker, marker points to a jar, jar is loaded.
Give the ordered pipeline including the META-INF descriptor and that pluginManagement (not project repos) supplies the lookup repositories.
Add timing (settings/config time), caching/offline behavior, and conflict resolution across plugins sharing a classpath.
Discuss how to standardize pluginManagement across an org (init scripts, settings convention plugins) so marker lookup is governed centrally.
## When resolution happens Plugin resolution is an **early** phase. The `plugins {}` block is evaluated when the build/settings script is compiled, *before* the project's own configuration runs and before any task graph exists. That is why the block is restricted (no arbitrary logic, only `id`, `version`, `apply`) — Gradle must resolve it deterministically up front. ## The full pipeline 1. **Read request.** From `id("com.gradle.develocity") version "3.x"`, Gradle has an id + version. 2. **Build the marker coordinate.** `group = id`, `artifact = id + ".gradle.plugin"`, `version = version` → `com.gradle.develocity:com.gradle.develocity.gradle.plugin:3.x`. 3. **Locate repositories.** Gradle looks only at `pluginManagement { repositories { … } }` in `settings.gradle(.kts)`. If you never configure it, the implicit default is `gradlePluginPortal()` → `https://plugins.gradle.org/m2/`. (Note: project-level `repositories { }` are for *dependencies*, not plugin markers.) 4. **Fetch the marker POM.** Gradle requests the marker module from each repo in order until found. 5. **Follow the redirect.** The marker POM's single `<dependency>` names the implementation module (e.g. `com.gradle:develocity-gradle-plugin:3.x`). Gradle resolves it through normal dependency resolution, applying conflict resolution against other plugins on the same classpath. 6. **Download + cache.** The implementation jar and transitive dependencies land in the Gradle user home cache (`~/.gradle/caches/`), so later builds are offline-capable for that version. 7. **Read the descriptor.** Inside the jar, `META-INF/gradle-plugins/com.gradle.develocity.properties` contains `implementation-class=...`. This is how an id maps to a concrete class. 8. **Instantiate + apply.** Gradle constructs the plugin object and invokes `Plugin.apply(target)`. ## Caching and offline Because both the marker and the implementation jar are real modules, they obey Gradle's dependency cache. `--offline` works once a version is cached. Changing the version forces a fresh marker lookup. ```kotlin // settings.gradle.kts — controls WHERE markers are looked up pluginManagement { repositories { gradlePluginPortal() // default; serves marker POMs + impl jars maven("https://nexus.acme.io/plugins") // corporate mirror, searched in order } } ``` ## Common confusion The Plugin Portal serves *both* the marker POM and (by proxying Maven Central / the publisher's repo) the implementation jar, so people think it is one artifact. It is two: a redirect POM and a code jar.
- Which file inside the implementation jar tells Gradle which class to apply for a given id?`META-INF/gradle-plugins/<id>.properties`, containing an `implementation-class=` entry. Gradle reads it to map the id to a concrete `Plugin<?>` class.
- Why can't you put repositories for plugin markers in the project's `repositories {}` block?Project `repositories {}` resolve *dependencies*. Plugin markers are resolved earlier, from `pluginManagement { repositories {} }` in settings — a separate, earlier resolution scope.
saying these in an interview costs you the question
- Saying project-level repositories {} resolve plugin markers — they don't; pluginManagement does.
- Claiming resolution happens at task-execution time — it happens far earlier, at script evaluation.
- Forgetting the descriptor step (META-INF/gradle-plugins) that maps id → class.