How do you configure Kotlin compiler flags in modern KGP via the `compilerOptions { }` block, and how does that differ from the old kotlinOptions DSL?
answer
- compilerOptions { } replaces kotlinOptions { }
- Values are lazy Property<T> set with .set(...)
- Type-safe enums: JvmTarget, KotlinVersion
- languageVersion (syntax) vs apiVersion (libs) vs jvmTarget (bytecode)
- freeCompilerArgs.add(...) for raw -X flags
basics
~20 sUse the kotlin { compilerOptions { } } block to set things like the language version, JVM target, and extra compiler args. It replaces the older kotlinOptions block and uses lazy Gradle Property values you set with .set(...).
solid answer
~40 sModern KGP exposes **`compilerOptions { }`**, available both project-wide under the `kotlin { }` extension and per-task on `KotlinCompile`. Values are **lazy Gradle `Property<T>`** objects, so you assign with `.set(...)` (or `=` with the assignment plugin). Typical settings: `jvmTarget.set(JvmTarget.JVM_21)`, `languageVersion.set(KotlinVersion.KOTLIN_2_1)`, `apiVersion.set(...)`, `allWarningsAsErrors.set(true)`, and `freeCompilerArgs.add("-Xjsr305=strict")`. These use **type-safe enums** (`JvmTarget`, `KotlinVersion`) instead of raw strings. The legacy **`kotlinOptions { }`** DSL used eager `String` properties (e.g. `jvmTarget = "21"`) and is **deprecated** in Kotlin 2.x in favor of `compilerOptions`. Configuring at the `kotlin { compilerOptions { } }` level applies to all Kotlin compilations; configuring `tasks.withType<KotlinCompile>().configureEach { compilerOptions { } }` lets you target specific tasks.
code
kotlin · 11 linesimport org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
languageVersion.set(KotlinVersion.KOTLIN_2_1)
allWarningsAsErrors.set(true)
freeCompilerArgs.addAll("-Xjsr305=strict", "-Xjvm-default=all")
}
}go deeper
Knows compiler flags go in a kotlin block and that jvmTarget can be set there.
Uses compilerOptions with enums and .set(...), and knows kotlinOptions is the deprecated predecessor.
Distinguishes languageVersion/apiVersion/jvmTarget, applies per-task vs project-wide config, and explains the lazy Property migration.
Reasons about staged compiler upgrades (new kotlinc, pinned languageVersion), freeCompilerArgs governance, and configuration-avoidance implications across many modules.
## Where compiler flags live now KGP 2.x gives you a single, lazy, type-safe API: **`compilerOptions { }`**. Project-wide (applies to every Kotlin compilation): ```kotlin kotlin { compilerOptions { jvmTarget.set(JvmTarget.JVM_21) languageVersion.set(KotlinVersion.KOTLIN_2_1) apiVersion.set(KotlinVersion.KOTLIN_2_1) allWarningsAsErrors.set(true) freeCompilerArgs.add("-Xjsr305=strict") } } ``` Per-task (override or add only on some tasks): ```kotlin tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach { compilerOptions { freeCompilerArgs.add("-Xcontext-receivers") } } ``` ## Key properties - **`jvmTarget`** — emitted bytecode version, a `JvmTarget` enum (e.g. `JVM_21`). - **`languageVersion`** — the *source* language level the compiler accepts (a `KotlinVersion` enum). Lets you adopt a new compiler but keep older language semantics. - **`apiVersion`** — the maximum stdlib/library API version your code may use; must be ≤ languageVersion. - **`allWarningsAsErrors`** — fail the build on any warning. - **`freeCompilerArgs`** — a `ListProperty<String>` for raw `-X`/`-P` flags; use `.add(...)`/`.addAll(...)`. ## Lazy Property vs the old eager DSL The old DSL: ```kotlin kotlinOptions { // deprecated in 2.x jvmTarget = "21" // a plain String freeCompilerArgs += "-Xjsr305=strict" } ``` Problems it had: - **String-typed** target (`"21"`) — easy to mistype, no enum safety. - **Eager** — values resolved immediately, working against Gradle's lazy configuration/avoidance model. The new DSL fixes both: properties are **`Property<T>`** (`org.gradle.api.provider.Property`), evaluated lazily, and targets are **enums** (`JvmTarget`, `KotlinVersion`). You set them with **`.set(...)`** (or `=` if the Gradle assignment overload is enabled). `kotlinOptions` is kept as a thin deprecated bridge for migration. ## languageVersion vs apiVersion vs jvmTarget — three different things - `jvmTarget` = bytecode level (JVM concept). - `languageVersion` = which Kotlin *syntax/semantics* the compiler honors. - `apiVersion` = which Kotlin *library APIs* you may call. They are orthogonal: you might run kotlinc 2.1 with `languageVersion = 2.0` to keep old semantics while still upgrading the compiler binary.
- What is the difference between languageVersion and apiVersion?languageVersion sets which Kotlin syntax/semantics the compiler accepts; apiVersion caps which stdlib/library APIs you may call. apiVersion must be ≤ languageVersion.
- Why prefer compilerOptions over kotlinOptions?compilerOptions uses lazy Gradle Property values and type-safe enums, fits configuration avoidance, and kotlinOptions is deprecated in Kotlin 2.x.
saying these in an interview costs you the question
- Setting jvmTarget to a raw string in the new DSL and expecting enum safety
- Confusing languageVersion with jvmTarget
- Assigning to a Property with plain = without the assignment overload, then surprised it fails
- Thinking kotlinOptions is the current recommended API