What compilation targets does KMP support, and what artifact does each produce?
answer
- Targets: JVM/Android, Native (LLVM), JS(IR), Wasm
- JVM→bytecode, Native→framework/binary, JS→ES modules, Wasm→.wasm
- Apple slices bundled into XCFramework
- Native is AOT, no JVM at runtime
- Gradle metadata picks the right variant for consumers
basics
~10 sA target is a platform you build for. JVM produces bytecode, Native produces machine-code binaries (like an iOS framework), JS produces JavaScript, and Wasm produces WebAssembly. You declare targets in Gradle.
solid answer
~40 sIn the `kotlin { }` block you declare **targets**: `jvm()`/`androidTarget()`, native ones (`iosArm64()`, `iosSimulatorArm64()`, `macosArm64()`, `linuxX64()`, `mingwX64()`), `js(IR)`, and `wasmJs()`/`wasmWasi()`. Each emits a different artifact: JVM → `.class`/`.jar` bytecode; Kotlin/Native → static/dynamic libraries or an Apple **`.framework`** (often packaged as an **XCFramework** for Swift); JS → ES modules consumable by npm/browser; Wasm → a `.wasm` binary. Native targets compile ahead-of-time via **LLVM**, so there's no JVM at runtime. Android isn't a separate compiler back-end — it reuses the JVM back-end via `androidTarget()`. Each target has its own dependencies and can be published as a Gradle artifact with platform-specific metadata so consumers pick the right variant automatically.
go deeper
Names a few targets and that each builds a different output.
Maps each target family to its artifact and knows native uses LLVM/AOT.
Configures targets in Gradle, handles XCFramework/simulator-vs-device, and explains metadata-based variant selection.
Plans the target matrix for a product, weighs build cost, publishing strategy, and runtime-model differences across targets.
## Targets = platforms you compile for A **target** tells the Kotlin compiler which platform to produce output for. You declare them inside the `kotlin { }` Gradle block; each target gets its own source sets and dependency scope. ## The target families and their artifacts - **JVM** — `jvm()` for server/desktop, `androidTarget()` for Android. Output: **JVM bytecode** (`.class` files in a `.jar` or an Android library/`.aar`). Android shares the JVM back-end; it's not a distinct compiler back-end. - **Kotlin/Native** — `iosArm64()`, `iosSimulatorArm64()`, `iosX64()`, `macosArm64()`, `linuxX64()`, `mingwX64()` (Windows), `watchosArm64()`, etc. Compiled **ahead-of-time through LLVM** into machine code. Output: a static/shared library, or on Apple an **`.framework`**. Multiple architectures are bundled into an **XCFramework** so an iOS app links one artifact. - **JavaScript** — `js(IR)` produces **JavaScript** (ES modules) for browser/Node, consumable as an npm package. - **WebAssembly** — `wasmJs()` (browser, JS interop) and `wasmWasi()` (WASI runtimes) produce a **`.wasm`** binary. Wasm is the newest family and powers Compose for Web. ## Runtime model differs by target - JVM targets need a JVM at runtime and get JIT/GC from it. - Native targets are **self-contained binaries** — no JVM. Kotlin/Native ships its own memory manager and GC. - JS/Wasm run in the browser/JS engine or a Wasm runtime. ```kotlin kotlin { androidTarget() iosArm64(); iosSimulatorArm64() jvm("desktop") js(IR) { browser() } wasmJs { browser() } // bundle iOS slices into one XCFramework // (configured via the xcframework() helper in cocoapods/framework setup) } ``` ## Publishing and variant selection When you publish a KMP library, Gradle attaches **Kotlin/Gradle metadata** describing each variant. A consumer that depends on your library automatically receives the correct artifact for its own target — JVM consumers get the JVM jar, iOS consumers get the framework slice — without manual classifier juggling. The root `*-kotlin-multiplatform` artifact carries this metadata. ## Practical notes - iOS needs **both** a device target (`iosArm64`) and a simulator target (`iosSimulatorArm64`) for development. - Apple-silicon vs Intel matters for simulator targets (`iosSimulatorArm64` vs `iosX64`). - `js(IR)` is the modern JS back-end (the legacy back-end is removed).
- Why declare both iosArm64 and iosSimulatorArm64?Device builds use the arm64 device target; running in the simulator on Apple silicon needs the simulator-arm64 target — they are separate binaries.
- Is Android a separate compiler back-end?No — androidTarget() reuses the JVM back-end; the difference is Android packaging (.aar) and the Android-specific dependency scope.
saying these in an interview costs you the question
- Claiming native targets run on a JVM at runtime
- Thinking there is one universal binary instead of per-target artifacts
- Not knowing iOS needs separate device and simulator targets
- Confusing js(IR) with the removed legacy JS back-end
- Believing consumers must manually pick artifact classifiers