From ebad3991f3dc9823f4057d810f6250d26cc205dd Mon Sep 17 00:00:00 2001 From: Michal Harakal Date: Thu, 30 Apr 2026 10:13:51 +0200 Subject: [PATCH] docs(native-cpu): document JVM-only consumption + shadow-jar caveat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a "Consuming this module" section to NativeKernelProvider's class kdoc covering the two gotchas a downstream consumer hits when wiring skainet-backend-native-cpu into a KMP project for the first time: 1. The module ships only a `jvm()` Kotlin target — no klib variants for Native / JS / Wasm. KMP consumers MUST put the dep in `jvmMain.dependencies`, never in `commonMain`. The wrong placement triggers "Couldn't resolve dependency 'sk.ainet.core:skainet-backend-native-cpu' in 'commonMain' for all target platforms" warnings on every non-JVM target. Plain `kotlin("jvm")` modules use the regular `dependencies` block. 2. Shadow-jar consumers using `mergeServiceFiles()` on com.gradleup.shadow:9.4.x silently lose the NativeKernelProviderFactory entry from the merged META-INF/services/...KernelProvider file. The fat JAR ends up running the priority-50 Panama path even though the native classes + .so live in the JAR. Cross-references the doLast workaround in `kllama-cli` (SKaiNET-transformers) that rebuilds the union. Also re-words the staged-rollout cursor: previously called PR 5 "this commit" — accurate when first written, but stale now that all 5 rollout PRs have shipped. Pure documentation; no code changes; tests still 27/27 green on jvmTest. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../ainet/exec/kernel/NativeKernelProvider.kt | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/skainet-backends/skainet-backend-native-cpu/src/jvmMain/kotlin/sk/ainet/exec/kernel/NativeKernelProvider.kt b/skainet-backends/skainet-backend-native-cpu/src/jvmMain/kotlin/sk/ainet/exec/kernel/NativeKernelProvider.kt index f1c3f118..c9aa3543 100644 --- a/skainet-backends/skainet-backend-native-cpu/src/jvmMain/kotlin/sk/ainet/exec/kernel/NativeKernelProvider.kt +++ b/skainet-backends/skainet-backend-native-cpu/src/jvmMain/kotlin/sk/ainet/exec/kernel/NativeKernelProvider.kt @@ -24,10 +24,51 @@ import sk.ainet.backend.api.kernel.Q4KMemSegMatmulKernel * callers stick with [matmulQ4K]; both wrap the same C symbol so * outputs are bit-for-bit identical. * + * ## Consuming this module + * + * The whole `skainet-backend-native-cpu` module is **JVM-only**: it + * depends on `java.lang.foreign.*`, which exists on Java SE only — + * not on Kotlin/Native, JS, Wasm, or Android Runtime. The module + * declares only a `jvm()` Kotlin target; no klib variants are + * published. + * + * KMP consumers MUST add the dependency to `jvmMain` only, never to + * `commonMain`: + * + * ```kotlin + * sourceSets { + * val jvmMain by getting { + * dependencies { + * implementation("sk.ainet.core:skainet-backend-native-cpu:") + * } + * } + * } + * ``` + * + * Putting it in `commonMain.dependencies` causes Gradle to fail + * resolution on every non-JVM target with a long string of "Couldn't + * resolve dependency 'sk.ainet.core:skainet-backend-native-cpu' in + * 'commonMain' for all target platforms" warnings. JVM-only consumers + * (plain `kotlin("jvm")` modules) can use the regular `dependencies` + * block. + * + * Auto-discovery: `KernelServiceLoader.installAll()` scans + * `META-INF/services/sk.ainet.backend.api.kernel.KernelProvider` on + * the classpath and registers everything it finds. With the JAR on + * the classpath there is nothing else to wire — no manual + * `KernelRegistry.register()` call. + * + * Shadow-jar consumers: `mergeServiceFiles()` on shadow plugin + * 9.4.x has a known bug that silently drops one of the two co- + * located service files when both `skainet-backend-cpu` and + * `skainet-backend-native-cpu` are on the classpath — see the + * `kllama-cli` build script in `SKaiNET-transformers` for a working + * `doLast` workaround that rebuilds the union. + * * Staged rollout cursor (see `native-ffm-plan` asciidoc): * - PR 2: real Q4_K matmul wired into the heap SPI. * - PR 3: MemSeg-input zero-copy sibling. - * - PR 5 (this commit): native FP32 matmul wired into [matmulFp32]. + * - PR 5: native FP32 matmul wired into [matmulFp32]. * - Later: native `matmulQ6K`, `matmulQ8_0` (need new SPI accessors). */ public object NativeKernelProvider : KernelProvider, MemSegKernelProvider {