Selection Layers¶
The selection is global — one switch narrows the whole build. Its sources form three layers in increasing priority:
| Layer | Sources | Who sets it |
|---|---|---|
| Committed default | kmp-targets.properties (and legacy root gradle.properties) |
the team, in git |
| Personal override | kmp-targets.local.properties (and legacy local.properties) — git-ignored |
each developer, per machine |
| Per-invocation | -Pkmptargets.targets=… CLI flag, ORG_GRADLE_PROJECT_kmptargets.targets env |
this one build — a terminal run, a CI job |
kmptargets.targets is the single canonical key across every layer — the string you commit, the string you override locally, and the string a CI matrix passes per job are the same vocabulary.
Exact precedence (highest first)¶
-Pkmptargets.targets=...on the CLIORG_GRADLE_PROJECT_kmptargets.targetsenvironment variablekmp-targets.local.properties(per-developer, git-ignored)kmp-targets.properties(committed, team-shared)- the root
gradle.properties(a subproject'sgradle.propertiesis not a source — Gradle only reads root-level project properties) local.properties(per-developer, git-ignored)
When no source provides a value, two fallbacks (not overrides — anything above beats them) apply: a project-wide defaultSelection set from build-logic, then the plugin default — every target the plugin knows about.
The rarer -Dorg.gradle.project.kmptargets.targets and ~/.gradle/gradle.properties forms resolve at the gradle.properties layer, below the dedicated files. Why the dedicated files outrank gradle.properties: Design.
With the opt-in kmptargets.xcodeEnv flag on, an extra layer sits between 2 and 3 — Xcode's own SDK_NAME/ARCHS when it invokes Gradle — so an Xcode build needs no -P. The flag off, it does not exist.
The config files¶
# kmp-targets.properties — committed, team-shared
kmptargets.targets=jvm,iosArm64
kmptargets.hierarchyTemplate=true
# kmptargets.hierarchyCollapse=false # see "Hierarchy"
# kmptargets.strict=true # see "Advisories & Strict Mode"
# kmptargets.umbrellaTasks=true # see "CI"
# kmptargets.framework.buildTypes=debug # see "Apple Framework"
kmp-targets.local.properties (git-ignored) is optional: absent it's ignored; present, its keys override the committed file's. Both files accept only known kmptargets.* keys — an unknown key fails the build with a "did you mean …?" suggestion. Both are tracked configuration-cache inputs: editing one invalidates the cache, leaving them untouched keeps cache hits.
Selection grammar¶
kmptargets.targets=android,iosArm64 # explicit list
kmptargets.targets=appleMobile # preset (iosArm64 + iosSimulatorArm64 + iosX64)
kmptargets.targets=appleMobile,-iosArm64 # preset minus a leaf
kmptargets.targets=apple,+android # preset plus an addition
kmptargets.targets=ANDROID, ios-arm64 # aliases + case-insensitive
Unknown tokens fail the build at configuration time with a "did you mean …?" suggestion (why the parser is strict). Bare Apple sub-family names (ios, macos, watchos, tvos) are rejected with a hint pointing at the relevant leaf or appleX preset.
The full preset and leaf vocabulary lives in the Targets Reference.
Diagnosing a surprising selection¶
kmpTargetsInfo prints the resolved selection and the winning source by name — command line (-Pkmptargets.targets), kmp-targets.local.properties, etc. Values from -Dorg.gradle.project.kmptargets.targets and ~/.gradle/gradle.properties are indistinguishable from root gradle.properties; all three report as the fused gradle.properties (...) layer.