What does `kotlin { jvmToolchain(21) }` do, and how is it different from setting jvmTarget?
answer
- Toolchain = which JDK runs the build
- jvmTarget = which bytecode version kotlinc emits
- jvmToolchain aligns Kotlin+Java+test JDK
- Reproducible, independent of JAVA_HOME
- Foojay resolver enables auto-download
basics
~20 sjvmToolchain(21) tells Gradle to compile and run with a JDK 21, downloading one if needed. jvmTarget only sets the bytecode version. The toolchain controls which JDK runs the build; the target controls what class-file version is produced.
solid answer
~40 s`kotlin { jvmToolchain(21) }` configures the **Gradle Java toolchain** for both Kotlin and Java compilation: Gradle locates (or provisions, via a download repository) a JDK whose major version is 21 and uses it for `compileKotlin`, `compileJava`, and the JDK that runs tests. It also implicitly sets the Kotlin `jvmTarget` and Java `release` to 21, so the produced bytecode matches. By contrast, setting only `compilerOptions { jvmTarget = JvmTarget.JVM_21 }` changes *only* the target bytecode version emitted by kotlinc; it does not control which JDK actually runs the compiler. Using a toolchain makes the build **reproducible across machines** regardless of the developer's local `JAVA_HOME`. You can still override the bytecode target separately from the toolchain JDK (e.g. toolchain 21 but jvmTarget 17) if you must run on a newer JDK but emit older bytecode.
code
kotlin · 7 lines// Build on JDK 21, but emit bytecode compatible with JVM 17
kotlin {
jvmToolchain(21)
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
}go deeper
Knows jvmToolchain(21) picks JDK 21 and is configured in the kotlin block.
Clearly separates toolchain (runtime JDK) from jvmTarget (bytecode), and knows the toolchain aligns Java and tests too.
Explains reproducibility vs JAVA_HOME, the Foojay auto-download mechanism, and valid toolchain-21/target-17 splits.
Reasons about org-wide toolchain policy, vendor pinning, provisioning in CI, and emitting older bytecode while building on a current secure JDK.
## The two independent knobs There are two distinct concepts people confuse: - **Toolchain** = *which JDK runs the build* (the `javac`/`kotlinc` launcher and the test JVM). - **jvmTarget** = *which bytecode version kotlinc emits* (the `version` field in the produced `.class` files). ## jvmToolchain ```kotlin kotlin { jvmToolchain(21) } ``` This is sugar over Gradle's **Java Toolchains** feature. It: - Asks Gradle for a JDK with **language version 21**. If none is installed, and a toolchain **download repository** (the Foojay resolver plugin) is configured in `settings.gradle.kts`, Gradle downloads one automatically. - Applies that JDK to **Kotlin compilation, Java compilation, and the test runtime** consistently. - Implicitly aligns the **Kotlin `jvmTarget`** and the **Java `release`** to 21. The big win is **reproducibility**: the build no longer depends on whatever `JAVA_HOME` a developer happens to have. For finer control you can configure the underlying spec: ```kotlin kotlin { jvmToolchain { languageVersion.set(JavaLanguageVersion.of(21)) vendor.set(JvmVendorSpec.ADOPTIUM) } } ``` ## jvmTarget ```kotlin kotlin { compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } } ``` This only sets the **bytecode version**. It does **not** choose the JDK. So `jvmTarget = JVM_17` with no toolchain means: compile with whatever JDK is running Gradle, but emit JDK 17-compatible class files. ## When they diverge A legitimate combination is **toolchain 21 + jvmTarget 17**: build on a modern, secure JDK 21 but ship bytecode that runs on a JVM 17 production runtime. The toolchain decides the compiler/runtime; jvmTarget decides the emitted class-file level. If you only set the toolchain, jvmTarget follows it automatically. ## Settings needed for auto-download ```kotlin // settings.gradle.kts plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } ``` Without that resolver, an uninstalled toolchain fails the build instead of being downloaded.
- Your CI machine only has JDK 17 but the build needs toolchain 21 — what happens?Gradle tries to locate a JDK 21. If the Foojay toolchain resolver is configured it auto-downloads one; otherwise the build fails with a 'No matching toolchains' error.
- If you set jvmToolchain(21) and nothing else, what is the jvmTarget?It is implicitly 21 — the toolchain aligns the Kotlin jvmTarget (and Java release) to the toolchain's language version.
The toolchain is the oven you bake in; jvmTarget is the 'best before' label on the cake — different ovens can still print the same label.
saying these in an interview costs you the question
- Saying jvmTarget controls which JDK runs the build
- Claiming the toolchain only affects Kotlin and not Java/tests
- Believing toolchain JDK and bytecode target must always be identical
- Not knowing JAVA_HOME is bypassed when a toolchain is configured