When you declare multiple targets in a KMP module, what tasks does the plugin generate and how do you build or test a single platform?
answer
- per-target tasks: jvmTest, compileKotlinJvm, jvmJar
- native: compileKotlinIosArm64 + link tasks
- allTests aggregates target test tasks
- run target-prefixed task to scope
- apple targets need a macOS host
basics
~10 sEach target gets its own tasks — e.g. compileKotlinJvm, jvmJar, jvmTest, compileKotlinIosArm64, iosX64Test. To build or test just one platform you run the target-prefixed task, like ./gradlew jvmTest instead of ./gradlew check.
solid answer
~40 sDeclaring a target makes the KMP plugin register a *family* of per-target tasks. For `jvm()` you get `compileKotlinJvm`, `jvmJar`, `jvmTest`, and the lifecycle `jvmMainClasses`. Native targets give `compileKotlinIosArm64`, `linkDebug…`, and so on; JS gives `compileKotlinJs`, `jsBrowserTest`, etc. The standard `build` and `check` lifecycle tasks fan out to all of these, which can be slow, so to iterate you run the target-prefixed task — `./gradlew jvmTest` or `./gradlew compileKotlinJvm`. There is also `allTests` (an aggregated test task) and `metadataMain` for the common compilation. Importantly, native targets can only be *built on a compatible host* — iOS targets require macOS — so CI usually splits the target matrix across runners and uses target-prefixed tasks to skip targets the host can't build.
code
bash · 10 lines# full gate (slow): every target's compile + test
./gradlew check
# scope to one platform while iterating
./gradlew jvmTest
./gradlew compileKotlinJvm
# CI sharding: linux runner builds jvm/js, mac runner builds ios
./gradlew jvmTest jsNodeTest # on ubuntu
./gradlew iosSimulatorArm64Test # on macosgo deeper
Know that each platform has its own test task (e.g. jvmTest) and that you can run just that.
List the per-target task families, the allTests aggregator, and how to scope a build/test to one platform.
Explain host constraints for native targets and how to shard the CI matrix across OS runners using target-prefixed tasks.
Discuss build-time/cost trade-offs of a wide target matrix, remote build cache + configuration cache benefits, and a multi-runner CI topology for KMP.
## Per-target task generation The KMP plugin's central idea on the Gradle side is that **each declared target owns a slice of the task graph**. When you call `jvm()`, the plugin registers (via `tasks.register`, lazily) tasks named with the target as a prefix or suffix: - `compileKotlinJvm` — compiles `jvmMain` (+ the common code it consumes). - `jvmMainClasses`, `jvmJar` — assembly. - `jvmTest` — runs the `jvmTest` source set. For `iosArm64()` you get `compileKotlinIosArm64`, link tasks, and (for runnable simulator targets) test tasks like `iosSimulatorArm64Test`. For `js(IR)` you get `compileKotlinJs`, `jsBrowserTest`, `jsNodeTest`. The common (metadata) compilation has `compileCommonMainKotlinMetadata`. ## Aggregate / lifecycle tasks - `build` / `assemble` — produce all artifacts for all targets. - `check` — run every verification, including every target's tests. - `allTests` — an aggregating test task that depends on each target's test task and merges reports. Because these fan out across *every* target, a full `./gradlew check` in a wide KMP module is expensive. ## Building / testing one platform To iterate quickly, invoke the target-prefixed task directly: ```bash ./gradlew jvmTest # only the JVM tests ./gradlew compileKotlinJvm # only JVM compilation ./gradlew iosSimulatorArm64Test # only iOS simulator tests (needs macOS) ./gradlew jsNodeTest # only JS-on-Node tests ``` This is also the lever for **CI**: run `jvmTest` on a Linux runner and the iOS tasks on a macOS runner. ## Host constraints Native compilation is **host-dependent**. Apple targets (`iosArm64`, `macosArm64`, …) can only be linked on macOS; `mingwX64` favors Windows; `linuxX64` favors Linux. The Kotlin/Native toolchain enforces this, which is why a CI matrix typically splits targets by OS and uses target-prefixed tasks to build only what the current host supports. The `kotlin.native.enableKlibsCrossCompilation` and host-presence checks influence what's runnable. ## Configuration cache / laziness KGP registers these tasks lazily and they are compatible with Gradle's configuration cache in recent versions, so the large task count doesn't force eager configuration. From a build-engineering standpoint, the headline is: *targets multiply the task graph; use target-prefixed tasks to scope work and to shard CI.*
- Why does running `./gradlew iosArm64Test` fail on a Linux CI runner?Kotlin/Native links Apple targets only on macOS hosts. The Kotlin/Native toolchain can't produce or run iOS binaries on Linux, so iOS tasks must run on a macOS runner. CI typically shards the target matrix by OS for this reason.
- What does the `allTests` task do?It is an aggregating lifecycle task that depends on each declared target's test task and merges their results into a combined report, so one invocation runs the whole multiplatform test suite for the host's buildable targets.
saying these in an interview costs you the question
- Claiming iOS targets can be built on Linux CI.
- Saying there is a single `test` task for all platforms (it is per-target plus the `allTests` aggregator).
- Assuming `./gradlew check` is cheap in a wide KMP module.