How does the TargetJvmVersion attribute drive variant selection for libraries that support multiple JVM targets?
answer
- org.gradle.jvm.version integer
- compatible when P <= C
- disambiguation: highest compatible
- consumer value from toolchain/release
- too-low request -> no matching variant
basics
~20 sEach variant declares the JVM version it targets via TargetJvmVersion. A variant built for an older JVM is compatible with a consumer on a newer one, and Gradle picks the highest compatible version for your build.
solid answer
~50 s`org.gradle.jvm.version` (`TargetJvmVersion`) records the minimum JVM a variant's bytecode runs on. Its **compatibility rule** says: a producer variant targeting version P is compatible with a consumer requesting version C when P <= C — because a newer JVM can execute older bytecode, but not vice versa. Its **disambiguation rule** then prefers the **highest** compatible P, giving you the most modern artifact your JVM can still run. The Java plugin derives the consumer's requested value from your toolchain / `release` / source-target compatibility. So a library that publishes JVM-8 and JVM-17 variants will resolve to the 8 variant for a JVM-8 build and the 17 variant for a JVM-17 build, from the *same* dependency coordinate. If you request a JVM lower than any published variant, you get a 'no matching variant' failure pointing at the JVM-version attribute.
code
kotlin · 7 linesjava {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
// Compile/runtime classpaths now request TargetJvmVersion 17;
// a multi-target library resolves to its highest compatible (<= 17) variant.go deeper
Know that libraries can target different JVMs and Gradle picks one matching your Java version.
State the compatibility direction (P <= C) and that disambiguation prefers the highest, and that the value comes from the toolchain.
Explain how a toolchain bump can silently change selected variants and transitive graphs, and how to verify with dependencyInsight.
Set org policy on JVM baselines and toolchains so variant selection stays consistent across many modules and CI agents.
## What TargetJvmVersion encodes `TargetJvmVersion` (attribute id `org.gradle.jvm.version`) is an integer attribute on a variant stating the **minimum JVM major version** that variant supports — e.g. 8, 11, 17, 21. It exists because one library can ship multiple builds (multi-release jars or distinct variants) compiled for different JVM baselines. ## The built-in rules Gradle registers standard compatibility and disambiguation rules for this attribute in the `attributesSchema`: - **Compatibility**: producer value `P` is compatible with consumer request `C` iff `P <= C`. Rationale: a JVM at version C can run bytecode targeting any `P <= C`. A variant needing JVM 21 is *incompatible* with a consumer on 17. - **Disambiguation**: among compatible variants, choose the **highest** `P`. This gives the most up-to-date artifact runnable on the consumer's JVM. ## Where the consumer value comes from The Java/Java-Library plugin computes the consumer's requested `TargetJvmVersion` from your build's effective target: the **Java toolchain** language version, or `release`, or `sourceCompatibility`/`targetCompatibility`. So you rarely set it by hand — configuring the toolchain is enough. ## End-to-end example A library publishes (via Gradle Module Metadata) `runtimeElements` variants tagged JVM 8 and JVM 17. - Consumer toolchain = 8 -> only the JVM-8 variant is compatible -> selected. - Consumer toolchain = 17 -> both compatible -> disambiguation picks JVM 17. - Consumer toolchain = 7 -> neither compatible -> **no matching variant**, error names `org.gradle.jvm.version`. ```kotlin java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } // Gradle now requests TargetJvmVersion 17 on compile/runtime classpaths, // so multi-target libraries resolve to their JVM-17 variant when available. ``` ## Why it matters This is how Gradle delivers *automatic, correct* artifact selection for libraries that straddle JVM versions, without classifiers or manual switches — and why bumping a toolchain can change which variant (and transitive deps) you pull.
- Why is requesting a lower JVM than the producer offers a failure, but requesting a higher one fine?Compatibility is P <= C: a variant built for JVM 21 cannot run on a consumer at 17 (higher P, lower C) so it's rejected; a variant for 8 runs on 17 so it's accepted.
- How does Gradle decide the consumer's requested TargetJvmVersion?The Java plugin derives it from the effective target — the Java toolchain language version, or `release`, or source/target compatibility — and applies it to the resolvable classpath configurations.
Like choosing a video file your player supports: an older codec plays everywhere, but among supported ones you pick the highest-quality the device can still handle.
saying these in an interview costs you the question
- Saying a higher-JVM variant is acceptable for a lower-JVM consumer (gets the direction backwards).
- Claiming disambiguation prefers the lowest JVM version.