skip to content

How do AGP's `compileOptions.sourceCompatibility`/`targetCompatibility` relate to a JVM toolchain, and which one should you set?

level: middleimportance: must knowfreq 50%

answer

  1. toolchain = which JDK
  2. compileOptions = which bytecode
  3. AGP defaults source/target to toolchain
  4. override only to go lower
  5. keep jvmTarget in sync

basics

~10 s

The toolchain selects which JDK compiles; compileOptions sets the Java bytecode level (source/target). With a toolchain set, source/target default to the toolchain version, so you usually only need the toolchain.

solid answer

~40 s

`compileOptions.sourceCompatibility` and `targetCompatibility` declare the Java *language level* and *bytecode target* (e.g. Java 17 features and class-file version). The JVM **toolchain** declares which *JDK* performs the compilation. They answer different questions: 'what JDK do I compile with' (toolchain) vs 'what bytecode do I emit' (compileOptions). When you set a toolchain via `jvmToolchain(17)`, AGP defaults `source/targetCompatibility` to that language version, so a single toolchain declaration is usually enough. You'd set `compileOptions` explicitly only when you need to emit *lower* bytecode than the toolchain JDK — e.g. compile with JDK 17 but target Java 11 for broader desugaring/library compatibility. In Kotlin, the parallel knob is `compilerOptions.jvmTarget`, which should be kept consistent with `targetCompatibility`.

code

kotlin · 10 lines
kotlin
android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions { jvmTarget = "11" }
}

// Compile with JDK 17, but emit Java 11 bytecode:
kotlin { jvmToolchain(17) }

go deeper

for a junior

Know that compileOptions sets source/target bytecode and the toolchain selects the JDK.

for a middle

Explain that AGP defaults source/target to the toolchain and that you override only to emit lower bytecode.

for a senior

Reason about library-consumer compatibility, keeping Kotlin jvmTarget aligned, and the invalid 'target higher than toolchain' case.

for a principal

Define org policy: standardize the toolchain JDK while allowing modules to lower targetCompatibility for binary-compatibility guarantees.

## Two orthogonal settings | Setting | Question it answers | Example value | |---|---|---| | JVM toolchain (`jvmToolchain(n)` / `java.toolchain.languageVersion`) | Which **JDK** compiles the code? | JDK 17 install | | `compileOptions.sourceCompatibility` | Which Java **language features** are allowed? | `JavaVersion.VERSION_17` | | `compileOptions.targetCompatibility` | Which **bytecode/class-file version** is emitted? | `JavaVersion.VERSION_17` | | Kotlin `compilerOptions.jvmTarget` | Bytecode target for **Kotlin** | `JvmTarget.JVM_17` | ## How they interact under AGP When a toolchain is declared, AGP and the Java plugin **default** `sourceCompatibility`/`targetCompatibility` to the toolchain's language version. So: ```kotlin kotlin { jvmToolchain(17) } // => source/target compatibility default to 17, jvmTarget aligns to 17 ``` is enough for most apps. ## When to set compileOptions explicitly You override `compileOptions` only to emit **lower** bytecode than the compiling JDK — a common case is compiling with JDK 17 but targeting Java 11 class files so the app runs on more devices/runtimes or so a library stays consumable by older toolchains: ```kotlin android { compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } kotlinOptions { jvmTarget = "11" } // keep Kotlin in sync } kotlin { jvmToolchain(17) } // still compile on JDK 17 ``` ## Pitfalls - Setting `compileOptions` to a level *higher* than the toolchain JDK fails — you cannot emit Java 21 bytecode from a JDK 17 toolchain. - Forgetting to bump Kotlin's `jvmTarget` alongside `targetCompatibility` causes a mismatch warning/error. - Android desugaring (`isCoreLibraryDesugaringEnabled`) lets you use newer APIs at lower targets, but that's separate from the toolchain.

  • Can you set targetCompatibility to 21 with a JDK 17 toolchain?
    No. The compiling JDK must support the requested bytecode level; targeting a version above the toolchain JDK fails. You'd need a JDK 21 toolchain.
  • If you only set jvmToolchain(17) and nothing else, what bytecode is emitted?
    Java 17 class files — source/targetCompatibility default to the toolchain language version, and Kotlin's jvmTarget aligns to 17.
  • Why might a library module set targetCompatibility lower than its toolchain?
    So consumers on older JDKs/toolchains can still read its class files, while the library authors still build on a newer, supported JDK.

saying these in an interview costs you the question

  • Saying compileOptions and the toolchain are the same thing.
  • Trying to target a higher bytecode version than the toolchain JDK provides.
  • Changing targetCompatibility but leaving Kotlin's jvmTarget mismatched.

context