Kmp
Skill SKaiNET-developers/SKaiNET-coding-skills/skainet-contributor-skills/skills/kmp
Use ONLY when configuring KMP targets, source-set hierarchies, or `expect`/`actual` placement in a module INSIDE the SKaiNET repository. Trigger tokens include `kotlin { ... }`, `iosArm64()`, `commonMain`, `jvmMain`, `expect fun`, `actual fun`, `androidNative`, `wasmJs`, source-set dependency edits in `SKaiNET/skainet-*/build.gradle.kts`. Do NOT fire on KMP setup in a CONSUMER project (an app that depends on SKaiNET) — those concerns are simpler and live in `skainet-consumer-setup`.From its SKILL.md
npx -y skills add SKaiNET-developers/SKaiNET-coding-skills --skill kmpAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.3 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it
kmp
Rules for configuring Kotlin Multiplatform targets and source-set hierarchies in SKaiNET. Pure-logic code lives in commonMain and runs on every target; platform-specific implementations are confined to the narrowest source-set that needs them.
When to use
- Adding a new KMP target to a module (e.g. enabling iosArm64 for a previously JVM-only module).
- Deciding whether a file belongs in
commonMain/jvmMain/ a native target / a JS or WASM target. - Adding
expect/actualdeclarations. - Configuring
dependencies { }blocks per source set. - Reviewing the KSP-generated source-set layout for a multiplatform module.
When NOT to use
- Selecting which catalog accessor to use or registering the module — that's
gradle-multimodule. - Writing the actual Kotlin code — that's
kotlin. - Building Java-facing facades (those always live in
jvmMain/sk.ainet.java) — coordinate withskainet-java-interop.
Hard rules
- Canonical target list for a published library (mirror
skainet-lang-core's set unless there's a documented reason not to):jvm()android { namespace = "sk.ainet.<area>"; compileSdk = libs.versions.android.compileSdk.get().toInt(); minSdk = libs.versions.android.minSdk.get().toInt(); compilerOptions { jvmTarget.set(JvmTarget.JVM_11) } }iosArm64(),iosSimulatorArm64()macosArm64()linuxX64(),linuxArm64()androidNativeArm32(),androidNativeArm64()(vendor-native backends)js { browser() }@OptIn(ExperimentalWasmDsl::class) wasmJs { browser() },@OptIn(ExperimentalWasmDsl::class) wasmWasi { nodejs() }
expectlives incommonMain.actuallives in the narrowest source set that needs the platform API —jvmMainforjava.io,iosArm64Mainfor an iOS-specific call, etc.commonMainis the default home for Kotlin code. Move a file out only when it imports a platform API thatcommonMaincannot resolve.commonTestuseskotlin.test.jvmTestmay add Kotest. Kotest's runner is JVM-only — never import it from acommonTestsource set.- Source-set dependencies use the per-set DSL (
commonMain.dependencies { },jvmMain.dependencies { }), or thesourceSets { commonMain { dependencies { ... } } }form. Don't add dependencies to the baredependencies { }block at the top level of a KMP module — that's a JVM-only Gradle pattern. api(...)only when the dependency's types appear in the public Kotlin signatures of the consuming source set. Otherwiseimplementation(...). The KSP annotations module is a deliberateapi(...)because KSP-generated code references those annotations fromcommonMain.- KSP-generated sources for
commonMainare added explicitly tocommonMain.kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin"). Thetasks.configureEach { … dependsOn("kspCommonMainKotlinMetadata") }block at the bottom of the build script is required — copy it verbatim fromskainet-lang-core/build.gradle.kts:72-77when KSP is involved. - Don't introduce target-specific intermediate source sets without a reason. Use the default hierarchy template (
iosMainaggregatingiosArm64Main+iosSimulatorArm64Main;nativeMainaggregating all native targets) only when at least two siblings actually share code.
Workflow
- Open the closest sibling module's
build.gradle.ktsand copy its target list — divergence from the canonical set MUST be justified in the change description. - Decide the source set for the new code:
- Pure Kotlin, no platform API →
commonMain. - Platform API needed → narrowest source set (
jvmMain,iosArm64Main,wasmJsMain). - Cross-platform behaviour with platform-specific implementation →
expectincommonMain,actualin each platform source set that the module targets.
- Pure Kotlin, no platform API →
- Wire dependencies in the matching source-set block (
commonMain.dependencies { }, etc.). - Add tests in the matching test source set (
commonTestfor cross-target,jvmTestfor JVM-only tooling like Kotest). - Run
./gradlew :module:assembleto validate every target compiles. If a Native target fails, the source set probably leaked a JVM API — move it tojvmMain.
Canonical examples
Full target list with explicit-API mode:
kotlin {
explicitApi()
android {
namespace = "sk.ainet.lang.core"
compileSdk = libs.versions.android.compileSdk.get().toInt()
minSdk = libs.versions.android.minSdk.get().toInt()
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
iosArm64()
iosSimulatorArm64()
macosArm64()
linuxX64()
linuxArm64()
androidNativeArm32()
androidNativeArm64()
jvm()
js {
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmWasi {
nodejs()
}
// ... source sets ...
}
// from: SKaiNET/skainet-lang/skainet-lang-core/build.gradle.kts:14-50
Per-source-set dependencies and KSP-generated source folder:
sourceSets {
commonMain {
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
dependencies {
api(project(":skainet-lang:skainet-lang-ksp-annotations"))
}
}
jvmMain.dependencies {
implementation(libs.kotlinx.benchmark.runtime)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
}
// from: SKaiNET/skainet-lang/skainet-lang-core/build.gradle.kts:52-68
KSP wiring required when commonMain consumes generated code:
tasks.configureEach {
if (name != "kspCommonMainKotlinMetadata" &&
(name.startsWith("compileKotlin") || name.startsWith("ksp") || name.contains("ourcesJar"))) {
dependsOn("kspCommonMainKotlinMetadata")
}
}
dependencies {
add("kspCommonMainMetadata", project(":skainet-lang:skainet-lang-ksp-processor"))
}
// from: SKaiNET/skainet-lang/skainet-lang-core/build.gradle.kts:72-83
Related skills
- Catalog accessors (
libs.plugins.kotlinMultiplatform,libs.kotlinx.io.core) and module registration — see../gradle-multimodule/SKILL.md. - File-level Kotlin idioms once you've placed a file in the right source set — see
../kotlin/SKILL.md. - The Java-friendly facade naturally lives in
jvmMainundersk/ainet/java/— see../skainet-java-interop/SKILL.md.
Anti-patterns
// WRONG — top-level dependencies block on a KMP module
dependencies {
implementation(libs.kotlinx.io.core)
}
// RIGHT — per-source-set
sourceSets {
commonMain.dependencies { implementation(libs.kotlinx.io.core) }
}
// WRONG — `actual` in commonMain
// commonMain/.../FileLoader.kt
public actual fun loadModelFile(path: String): ByteArray = TODO() // commonMain has no platform API
// RIGHT — expect in commonMain, actual in jvmMain
// commonMain/.../FileLoader.kt
public expect fun loadModelFile(path: String): ByteArray
// jvmMain/.../FileLoader.kt
public actual fun loadModelFile(path: String): ByteArray = java.io.File(path).readBytes()
// WRONG — Kotest in commonTest
// commonTest/.../FooSpec.kt
import io.kotest.core.spec.style.StringSpec // Kotest runner is JVM-only
// RIGHT — Kotest in jvmTest, kotlin.test in commonTest
// commonTest/.../FooTest.kt
import kotlin.test.Test
// jvmTest/.../FooSpec.kt
import io.kotest.core.spec.style.StringSpec
References
references/target-matrix.md— every KMP target SKaiNET ships, what it's used for, and the per-target source-set name.references/sourceset-rules.md— thecommonMain→ platform source-set hierarchy andexpect/actualplacement decision tree.
What ships with it: 3 files
9.3 KB alongside SKILL.md
evals/
- evals.json2.2 KB
references/
- sourceset-rules.md4.0 KB
- target-matrix.md3.0 KB