skip to content

What does applyDefaultHierarchyTemplate() do, and when do you still need to define intermediate source sets manually?

level: middleimportance: should knowfreq 45%

answer

  1. Auto-creates standard intermediate sets + dependsOn
  2. Only materializes sets you have targets for
  3. Implicit when using standard targets only
  4. Manual for jvmAndAndroidMain custom groupings
  5. Don't half-mix template + manual on same set

basics

~20 s

It auto-creates the standard tree of intermediate source sets (like nativeMain, appleMain, iosMain) and wires them with dependsOn, based on the targets you declared. You only hand-write a set when you need a non-standard grouping the template does not provide.

solid answer

~40 s

`applyDefaultHierarchyTemplate()` applies Kotlin's built-in **default hierarchy template**: given your declared targets it generates the conventional intermediate source sets — e.g. `nativeMain` for all native targets, `appleMain`, `iosMain`, `tvosMain`, etc. — and wires every `dependsOn` automatically. In recent Kotlin it is applied implicitly when you use only standard targets and don't define your own hierarchy, so calling it is often optional but explicit-is-clearer. You still wire sets manually for groupings the template doesn't model, the canonical case being `jvmAndAndroidMain` shared by JVM and Android (the template keeps those separate). If you call it and *also* manually create a set the template owns, you risk conflicting wiring, so customize via the template DSL or accept the manual route consistently. The template only materializes a set if at least one declared target uses it.

code

kotlin · 13 lines
kotlin
kotlin {
    applyDefaultHierarchyTemplate()
    jvm()
    androidTarget()
    iosArm64()
    iosX64()
    sourceSets {
        // iosMain auto-created; we add a custom JVM+Android group manually
        val jvmAndAndroidMain by creating { dependsOn(commonMain.get()) }
        jvmMain.get().dependsOn(jvmAndAndroidMain)
        androidMain.get().dependsOn(jvmAndAndroidMain)
    }
}

go deeper

for a junior

Knows the template saves manual dependsOn boilerplate.

for a middle

Explains which sets it creates, the implicit-application rule, and the jvmAndAndroidMain exception.

for a senior

Discusses conflict pitfalls and customizing via the template DSL lambda.

for a principal

Weighs declarative template customization vs. fully manual hierarchies for large multi-target codebases.

## The problem it solves Before the template, every project hand-wrote `dependsOn` chains: `iosArm64Main.dependsOn(iosMain)`, `iosMain.dependsOn(nativeMain)`, and so on — verbose and error-prone. ## What `applyDefaultHierarchyTemplate()` does It applies Kotlin's **default hierarchy template**, a predefined tree of intermediate source sets. Based on the targets you declared, it: - **Creates** the conventional intermediate sets that apply, e.g. `nativeMain`, `appleMain`, `iosMain`, `tvosMain`, `watchosMain`, `macosMain`, `linuxMain`, `mingwMain` — and their `*Test` counterparts. - **Wires** all `dependsOn` edges (leaf → group → … → `commonMain`). - Only **materializes a set when relevant**: `iosMain` appears only if you declared at least one iOS target. ```kotlin kotlin { applyDefaultHierarchyTemplate() iosArm64(); iosX64(); macosArm64() // auto: iosMain, appleMain, nativeMain created & wired sourceSets { appleMain.dependencies { /* shared by iOS + macOS */ } } } ``` ## Implicit application With recent Kotlin Gradle plugin versions, the template is **applied automatically** when you use only standard targets and have **not** declared your own hierarchy or manually called `dependsOn`. Calling it explicitly is still recommended for clarity and to silence warnings when mixing. ## When you still go manual The template models *platform-family* groupings, not arbitrary ones. You wire `dependsOn` by hand for non-standard sets: - **`jvmAndAndroidMain`** — the classic: share `java.*`-based code across JVM and Android (the template keeps `jvmMain` and `androidMain` separate). - Any bespoke subset like 'all targets that have a filesystem'. ## Conflict pitfall If you call the template **and** manually create/own a set it already manages, you can get duplicate `dependsOn` edges or compiler warnings. Either customize through the template DSL (`applyDefaultHierarchyTemplate { ... }`) or define a fully custom hierarchy — don't half-mix. ## Customizing the template You can pass a lambda to extend it (e.g. add a `jvmAndAndroidMain` group) rather than wiring raw `dependsOn`, keeping everything declarative.

  • Will applyDefaultHierarchyTemplate() create iosMain if you declared no iOS targets?
    No. It materializes only the sets relevant to your declared targets, so iosMain appears only when an iOS target exists.
  • Why is jvmAndAndroidMain not created automatically?
    The template models platform-family groupings; JVM and Android stay separate, so a shared java.* set is a custom grouping you must wire or add via the template lambda.

saying these in an interview costs you the question

  • Thinking it creates every possible set regardless of targets
  • Manually re-creating a set the template already owns
  • Believing it can produce jvmAndAndroidMain out of the box
  • Not knowing it can be applied implicitly

context