In a Kotlin Multiplatform project, what does it mean that platform source sets `dependsOn` commonMain, and why is this relationship special compared to a regular library dependency?
answer
- dependsOn = compiler-level edge, not implementation()
- Enables expect/actual matching by signature
- Directional: platform sees common, never reverse
- Transitive up the chain to commonMain
- Shares internal visibility + same package
basics
~10 sPlatform code like jvmMain depends on commonMain. This lets shared code declare a feature and each platform fill in the missing parts. It is a special link, not a normal library dependency.
solid answer
~40 s`dependsOn` is the Kotlin Multiplatform (KMP) edge that builds the source-set hierarchy: a platform source set such as `jvmMain` or `iosMain` `dependsOn` `commonMain`. It is NOT a regular library/`implementation` dependency. The special part is that it enables `expect`/`actual`: `commonMain` declares an `expect fun`/`expect class`, and each leaf source set that `dependsOn` it must supply the matching `actual`. The edge is one-directional (platform sees common, not the reverse) and transitive (an intermediate set like `appleMain` can sit between leaves and `commonMain`). The compiler treats depended-on sources as visible internal code, so `internal` declarations and same-package access work across the edge, which a normal dependency would not allow.
code
kotlin · 8 lines// commonMain
expect fun platformName(): String
// jvmMain (dependsOn commonMain)
actual fun platformName(): String = "JVM " + System.getProperty("java.version")
// iosMain (dependsOn commonMain via the default hierarchy)
actual fun platformName(): String = "iOS"go deeper
Knows platform sets depend on commonMain and that this enables shared code with platform implementations.
Explains it is a compiler-level edge enabling expect/actual and shared internal visibility, and that it is directional.
Adds transitivity through intermediate sets, contrasts cleanly with Gradle artifact dependencies, and discusses per-target compilation gathering common+platform sources.
Reasons about how the edge shapes API visibility/encapsulation strategy across modules and why common must stay agnostic for build/portability guarantees.
## What `dependsOn` is A Kotlin Multiplatform (KMP) project organizes code into **source sets** — named buckets of `.kt` files such as `commonMain`, `jvmMain`, `iosMain`, `androidMain`. The **`dependsOn`** relationship connects them into a directed graph: a more specific (platform) source set `dependsOn` a more general one (ultimately `commonMain`). ```kotlin // build.gradle.kts (manual wiring; usually the template does this for you) kotlin { sourceSets { val commonMain by getting val jvmMain by getting { dependsOn(commonMain) // jvmMain SEES commonMain } } } ``` ## Why it is NOT a normal dependency A normal Gradle dependency (`implementation("group:artifact")` or `api(project(":x"))`) wires **compiled artifacts** together. `dependsOn` wires **source sets at the compiler level**: - **`expect`/`actual` works across it.** `commonMain` may declare `expect fun currentTimeMillis(): Long`. Every leaf platform source set that (transitively) `dependsOn` `commonMain` is REQUIRED to provide an `actual fun currentTimeMillis(): Long`. The compiler matches them by signature. - **Visibility is shared.** `internal` declarations in `commonMain` are visible in `jvmMain`, and both can live in the same package — they compile together as one unit per target. - **It is directional.** `jvmMain` sees `commonMain`; `commonMain` can NEVER see `jvmMain`. Common code must stay platform-agnostic. - **It is transitive.** If `iosArm64Main dependsOn appleMain dependsOn nativeMain dependsOn commonMain`, then `iosArm64Main` sees everything up the chain. ## The hierarchy in practice When the JVM target is compiled, the compiler gathers `commonMain` + `jvmMain` together and resolves each `expect` to its `actual`. When the iOS target is compiled, it gathers `commonMain` + (intermediate sets) + `iosX64Main`. So each **leaf** sees exactly the right `expect`/`actual` set for its platform. ## Key terms - **Source set**: a named group of sources sharing dependencies and a compile target. - **`expect`**: a declaration in shared code with no body — a promise. - **`actual`**: the platform implementation fulfilling that promise. - **Leaf source set**: a platform-specific terminal set (e.g. `iosArm64Main`) that actually compiles to a target.
- Can commonMain reference a class declared only in jvmMain?No. The edge is directional — common code cannot see platform code, so it must use an `expect` declaration (or a common interface) instead.
- What happens if a leaf source set forgets to provide an actual?Compilation fails for that target with an error that the `expect` declaration has no `actual` for the platform.
commonMain writes a job description (expect); each platform source set is a hired worker who must do that exact job (actual).
saying these in an interview costs you the question
- Calling dependsOn the same as implementation()/api()
- Thinking commonMain can see platform code
- Believing expect/actual works without the dependsOn edge
- Saying the edge is bidirectional
- Confusing source sets with Gradle modules