What problem does versionMapping{} solve in a MavenPublication, and how does it change the generated POM?
answer
- declared vs resolved versions
- fixes POM, not Module Metadata
- dynamic versions / BOM / constraints
- usage java-api / java-runtime
- fromResolutionResult / fromResolutionOf
basics
~10 sversionMapping{} writes the actual resolved dependency versions into the published POM instead of the declared (possibly dynamic or constraint-driven) versions, so consumers using plain Maven see concrete, reproducible versions.
solid answer
~40 sBy default Gradle copies the **declared** dependency versions into the POM. That breaks down when you declare dynamic versions (`1.+`), rely on a platform/BOM with no explicit version, or use dependency constraints — a plain Maven consumer reading that POM gets blank or non-deterministic versions. `versionMapping {}` tells Gradle to substitute the versions that **were actually resolved** for a chosen configuration into the POM's dependency entries. You map by usage: `usage("java-api") { fromResolutionOf("runtimeClasspath") }` and `usage("java-runtime") { fromResolutionResult() }` are common, or `allVariants { fromResolutionResult() }`. The Gradle Module Metadata still carries the rich variant info; versionMapping only fixes the **POM** so Maven consumers and tools that only read POMs get concrete versions. It's strongly recommended whenever you publish with constraints, platforms, or dynamic versions.
code
kotlin · 7 linescreate<MavenPublication>("lib") {
from(components["java"])
versionMapping {
usage("java-api") { fromResolutionOf("runtimeClasspath") }
usage("java-runtime") { fromResolutionResult() }
}
}go deeper
Likely unfamiliar; at most know POMs can contain declared dependency versions.
Recognize that dynamic/BOM/constraint versions can produce bad POMs and that a feature exists to fix them.
Explain versionMapping usage blocks, fromResolutionResult vs fromResolutionOf, and that only the POM (not Module Metadata) is rewritten.
Make versionMapping a standard for all internal libraries to guarantee Maven-consumer reproducibility; weigh Module Metadata vs POM-only ecosystems org-wide.
## The problem When Gradle generates a Maven POM, each dependency's `<version>` comes — by default — from what you **declared** in the build, not from what Gradle actually **resolved**. That is fine when you write `implementation("com.acme:lib:1.4.0")`. It breaks in several modern patterns: - **Dynamic versions**: `implementation("com.acme:lib:1.+")` would publish `1.+` in the POM — meaningless and non-reproducible for a Maven consumer. - **Platforms / BOMs**: `implementation(platform("com.acme:bom:2.0"))` plus `implementation("com.acme:lib")` (no version) — the POM would have an **empty** version because you never declared one. - **Dependency constraints**: a version forced via `constraints { }` isn't on the dependency declaration, so it wouldn't appear in the POM. Gradle Module Metadata (the `.module` file) handles all of this correctly because it records the full resolution. But consumers using plain Maven, or tools that only parse the POM, see the broken versions. ## What versionMapping does `versionMapping {}` instructs the publication to write the **resolved** version of each dependency into the POM. You declare, per usage (API vs runtime), which configuration's resolution result to read from: ```kotlin publishing { publications { create<MavenPublication>("lib") { from(components["java"]) versionMapping { usage("java-api") { fromResolutionOf("runtimeClasspath") } usage("java-runtime") { fromResolutionResult() } } } } } ``` - `fromResolutionResult()` uses the resolution of the configuration that matches that usage (e.g. runtimeClasspath for runtime). - `fromResolutionOf("runtimeClasspath")` pins API versions to what the runtime classpath resolved, a common choice so api and runtime agree. - `allVariants { fromResolutionResult() }` applies one rule to everything. ## What changes in the output Only the **POM's** `<version>` entries change — they become the concrete resolved versions. The Gradle Module Metadata is unaffected (it already had the rich info). So Gradle consumers are unchanged; Maven consumers now get deterministic, buildable POMs. ## When to use it Use versionMapping whenever your published module relies on dynamic versions, platforms/BOMs, or constraints. It's effectively a best practice for any library that wants clean interop with Maven. Without it, downstream Maven builds may fail to resolve or pull unexpected versions. ## Relationship to coordinates This is about the **versions of your dependencies in the POM**, not your own module's GAV. Your own version still comes from the publication/project as usual; versionMapping fixes the dependency `<version>` fields.
- Does versionMapping change the Gradle Module Metadata too?No. The .module metadata already records resolved variants and versions; versionMapping only rewrites the POM's <version> fields for Maven/POM-only consumers.
- Give a concrete case where the POM would otherwise have an empty version.When you use a platform/BOM and declare a dependency without an explicit version — the declared version is blank, so without versionMapping the POM emits an empty <version>.
- What does fromResolutionOf("runtimeClasspath") accomplish for the java-api usage?It pins the API dependencies' published versions to whatever the runtime classpath resolved, keeping api and runtime versions consistent.
saying these in an interview costs you the question
- Saying versionMapping affects your module's own GAV version — it only touches dependency versions in the POM.
- Claiming it modifies the Gradle Module Metadata.
- Thinking it is unnecessary when using BOMs/constraints — that's exactly when it matters.