How does dependsOn create an intermediate source set, and how does it differ from a regular Gradle dependency declaration?
answer
- dependsOn = hierarchy wiring, not classpath
- Inherits sources + carries expect/actual
- implementation/api = library artifacts only
- Default template auto-wires dependsOn
- Manual dependsOn only for custom sets
basics
~20 sdependsOn links one source set to another so it inherits its code and can see its expect declarations. A normal dependency just adds a library on the classpath; dependsOn builds the multiplatform source-set hierarchy itself.
solid answer
~40 s`dependsOn` is the KMP-specific wiring that places one source set under another in the hierarchy. When `iosArm64Main.dependsOn(iosMain)` and `iosMain.dependsOn(commonMain)`, the leaf set inherits all sources of its parents, can implement their `expect` declarations with `actual`, and can refine visibility. This is structural: it defines the compilation graph and which `actual` belongs to which `expect`. A regular Gradle dependency (`implementation`, `api`) just puts a compiled artifact on the classpath — it cannot share `expect/actual` or merge source visibility. You rarely call `dependsOn` by hand now: `applyDefaultHierarchyTemplate()` sets up the standard tree. You only wire it manually for non-standard groupings like `jvmAndAndroidMain`. Crucially, a source set must depend on at most one parent chain consistently, and `actual` declarations live in the source set whose target needs them.
code
kotlin · 14 lineskotlin {
jvm()
androidTarget()
sourceSets {
val commonMain by getting
// custom intermediate set the template does NOT create
val jvmAndAndroidMain by creating { dependsOn(commonMain) }
val jvmMain by getting { dependsOn(jvmAndAndroidMain) }
val androidMain by getting { dependsOn(jvmAndAndroidMain) }
jvmAndAndroidMain.dependencies {
implementation("org.slf4j:slf4j-api:2.0.13") // library dep, different mechanism
}
}
}go deeper
Knows dependsOn links source sets but may blur it with library dependencies.
Clearly separates dependsOn (hierarchy + expect/actual) from implementation/api (classpath).
Explains expect/actual resolution along the dependsOn graph and diamond ambiguity errors.
Reasons about when to introduce custom intermediate sets vs. relying on the template, and the maintenance trade-offs.
## Two different kinds of 'depends' KMP has two unrelated mechanisms that both sound like dependencies: ### 1. `dependsOn` — source-set hierarchy wiring `dependsOn` is a method on a Kotlin source set that declares its **parent** in the hierarchy. It is *structural*: - The child **inherits the parent's sources** (they compile together for the child's target). - The child can provide **`actual`** implementations for the parent's **`expect`** declarations. - Visibility is refined downward: code in the parent sees only its own + ancestors' APIs. ```kotlin kotlin { val commonMain by sourceSets.getting val iosMain by sourceSets.creating { dependsOn(commonMain) } val iosArm64Main by sourceSets.getting { dependsOn(iosMain) } val iosX64Main by sourceSets.getting { dependsOn(iosMain) } } ``` ### 2. Library dependencies — `implementation` / `api` Inside `sourceSet.dependencies { }` you add **library artifacts** with `implementation(...)`, `api(...)`, etc. These add compiled code to the classpath but **do not** build the source-set hierarchy and **cannot** carry `expect/actual`. ## Why the distinction matters - `expect/actual` resolution follows the **`dependsOn`** graph, not the library graph. An `expect fun` in `commonMain` is satisfied by an `actual` in a source set reachable down the `dependsOn` chain for that target. - A library dependency can never satisfy an `expect` — only a source set in the same module's hierarchy can. ## Modern practice `applyDefaultHierarchyTemplate()` auto-creates the standard tree (`commonMain` → `nativeMain` → `appleMain` → `iosMain` → leaves) and wires all the `dependsOn` calls. You hand-write `dependsOn` only for **custom** intermediate sets the template does not provide (e.g. `jvmAndAndroidMain`). ## Gotchas - A leaf may have **multiple** `dependsOn` parents (diamond), but every `expect` must resolve to exactly one `actual` per target — ambiguity is a compile error. - Don't mix manual `dependsOn` for a set the template already manages; you can get duplicate or conflicting wiring.
- Can a library dependency satisfy an expect declaration?No. expect/actual is resolved only within the module's source-set hierarchy via dependsOn; a classpath artifact cannot provide an actual.
- When must you still write dependsOn by hand?For custom intermediate sets the default hierarchy template does not generate, such as jvmAndAndroidMain grouping JVM and Android.
saying these in an interview costs you the question
- Treating dependsOn as just another implementation() call
- Claiming a library can supply an actual for an expect
- Manually re-wiring sets the template already manages
- Not knowing actuals resolve via the dependsOn graph