diff --git a/skainet-backends/skainet-backend-api/build.gradle.kts b/skainet-backends/skainet-backend-api/build.gradle.kts index e37a2ea34..5a0fcadc5 100644 --- a/skainet-backends/skainet-backend-api/build.gradle.kts +++ b/skainet-backends/skainet-backend-api/build.gradle.kts @@ -25,6 +25,13 @@ kotlin { linuxX64() linuxArm64() + // Android-native targets, for on-device consumers that link the engine directly + // (SKaiNET-transformers builds gemma-iree for androidNativeArm32). Unlike + // skainet-io-core this module has no posix-typed code, so arm32's Int-width + // ssize_t/size_t needs no separate 64-bit source set here. + androidNativeArm64() + androidNativeArm32() + jvm() js { @@ -102,5 +109,16 @@ kotlin { val linuxArm64Main by getting { dependsOn(linuxMain) } + + // Straight onto nativeMain rather than linuxMain: Android's native targets are + // Linux-based, but linuxMain is free to grow glibc-specific code that bionic would + // not carry. nativeMain holds what actually applies to both. + val androidNativeArm64Main by getting { + dependsOn(nativeMain) + } + + val androidNativeArm32Main by getting { + dependsOn(nativeMain) + } } } diff --git a/skainet-backends/skainet-backend-cpu/build.gradle.kts b/skainet-backends/skainet-backend-cpu/build.gradle.kts index d3309296f..7b56fbdec 100644 --- a/skainet-backends/skainet-backend-cpu/build.gradle.kts +++ b/skainet-backends/skainet-backend-cpu/build.gradle.kts @@ -44,6 +44,12 @@ kotlin { linuxX64Main { dependsOn(linuxMain.get()) } linuxArm64Main { dependsOn(linuxMain.get()) } + // See PlatformCpuOpsFactory.androidNative.kt for why these hang off nativeMain and + // not linuxMain. + androidNativeMain { dependsOn(nativeMain.get()) } + androidNativeArm32Main { dependsOn(androidNativeMain.get()) } + androidNativeArm64Main { dependsOn(androidNativeMain.get()) } + // Golden (bit-identical) regression tests for the packed encodings — SKEEP-003 gate. // Shared by the JVM and Kotlin/Native test targets only: JS/Wasm compute Float in // double precision and are not expected to be bit-identical (they are covered by the diff --git a/skainet-backends/skainet-backend-cpu/gradle.properties b/skainet-backends/skainet-backend-cpu/gradle.properties index 8380ea84c..2c148ba07 100644 --- a/skainet-backends/skainet-backend-cpu/gradle.properties +++ b/skainet-backends/skainet-backend-cpu/gradle.properties @@ -1,4 +1,8 @@ POM_ARTIFACT_ID=skainet-backend-cpu POM_NAME=skainet neural network scripting API -kotlin.mpp.applyDefaultHierarchyTemplate=false \ No newline at end of file +kotlin.mpp.applyDefaultHierarchyTemplate=false + +# Adds androidNativeArm32/Arm64. On-device consumers link the eager CPU backend directly, +# so it has to publish the same targets skainet-lang-core and skainet-io-core already do. +skainet.targets=jvm,js,wasmJs,wasmWasi,apple,linux,androidNative \ No newline at end of file diff --git a/skainet-backends/skainet-backend-cpu/src/androidNativeMain/kotlin/sk/ainet/exec/tensor/ops/PlatformCpuOpsFactory.androidNative.kt b/skainet-backends/skainet-backend-cpu/src/androidNativeMain/kotlin/sk/ainet/exec/tensor/ops/PlatformCpuOpsFactory.androidNative.kt new file mode 100644 index 000000000..0d2b0fb1b --- /dev/null +++ b/skainet-backends/skainet-backend-cpu/src/androidNativeMain/kotlin/sk/ainet/exec/tensor/ops/PlatformCpuOpsFactory.androidNative.kt @@ -0,0 +1,22 @@ +package sk.ainet.exec.tensor.ops + +import sk.ainet.backend.api.kernel.KernelRegistry +import sk.ainet.exec.kernel.ScalarKernelProvider +import sk.ainet.lang.tensor.data.TensorDataFactory +import sk.ainet.lang.tensor.ops.TensorOps + +/** + * Android's native targets get the same portable factory the Linux one uses, and for the same + * reason: `DirectCpuExecutionContext` is pure Kotlin here, with no cinterop of its own. + * + * It is a copy rather than a `dependsOn(linuxMain)` on purpose. Android native is Linux-based, + * but it is bionic, not glibc — `linuxMain` is free to grow code that assumes the latter, and + * inheriting that silently is worse than the six duplicated lines below. The accelerated + * kernels for these targets live in `skainet-backend-native-cpu`, not here. + */ +internal actual fun platformDefaultCpuOpsFactory(): (TensorDataFactory) -> TensorOps { + // Non-JVM has no ServiceLoader; register the scalar packed-quant kernels + // (Q4_K/Q6_K/Q5_1/Q5_0/Q8_0/Q4_0) so DefaultCpuOpsBase can dispatch them. + KernelRegistry.register(ScalarKernelProvider) + return { factory -> DefaultCpuOps(factory) } +} diff --git a/skainet-compile/skainet-compile-dag/build.gradle.kts b/skainet-compile/skainet-compile-dag/build.gradle.kts index 4281ec714..0562de4c5 100644 --- a/skainet-compile/skainet-compile-dag/build.gradle.kts +++ b/skainet-compile/skainet-compile-dag/build.gradle.kts @@ -27,6 +27,13 @@ kotlin { linuxX64 () linuxArm64 () + // Android-native targets, for on-device consumers that link the engine directly + // (SKaiNET-transformers builds gemma-iree for androidNativeArm32). Unlike + // skainet-io-core this module has no posix-typed code, so arm32's Int-width + // ssize_t/size_t needs no separate 64-bit source set here. + androidNativeArm64() + androidNativeArm32() + jvm() js { diff --git a/skainet-compile/skainet-compile-json/build.gradle.kts b/skainet-compile/skainet-compile-json/build.gradle.kts index 08d01b7d6..26c46f722 100644 --- a/skainet-compile/skainet-compile-json/build.gradle.kts +++ b/skainet-compile/skainet-compile-json/build.gradle.kts @@ -27,6 +27,11 @@ kotlin { linuxX64() linuxArm64() + // Android-native targets, to match the modules this one is compiled against: skainet-backend-cpu + // publishes them, and its commonTest depends on this module, so the target set has to close. + androidNativeArm64() + androidNativeArm32() + jvm() js { diff --git a/skainet-compile/skainet-compile-json/src/androidNativeMain/kotlin/sk/ainet/compile/json/Serialization.androidNative.kt b/skainet-compile/skainet-compile-json/src/androidNativeMain/kotlin/sk/ainet/compile/json/Serialization.androidNative.kt new file mode 100644 index 000000000..ac3ab828b --- /dev/null +++ b/skainet-compile/skainet-compile-json/src/androidNativeMain/kotlin/sk/ainet/compile/json/Serialization.androidNative.kt @@ -0,0 +1,12 @@ +package sk.ainet.compile.json + +import sk.ainet.compile.json.model.SkJsonExport + +/** + * One file for both `androidNativeArm32` and `androidNativeArm64` — the default hierarchy + * template groups them under `androidNativeMain`, so this does not need the per-target + * duplication the other native actuals here carry. + */ +public actual suspend fun writeExportToFile(export: SkJsonExport, path: String, pretty: Boolean) { + throw NotImplementedError("writeExportToFile is not implemented for Android native in this module. Use toJsonString() and handle file I/O in the host app.") +} diff --git a/skainet-compile/skainet-compile-opt/build.gradle.kts b/skainet-compile/skainet-compile-opt/build.gradle.kts index 0315845fa..2b4893464 100644 --- a/skainet-compile/skainet-compile-opt/build.gradle.kts +++ b/skainet-compile/skainet-compile-opt/build.gradle.kts @@ -26,6 +26,13 @@ kotlin { linuxX64 () linuxArm64 () + // Android-native targets, for on-device consumers that link the engine directly + // (SKaiNET-transformers builds gemma-iree for androidNativeArm32). Unlike + // skainet-io-core this module has no posix-typed code, so arm32's Int-width + // ssize_t/size_t needs no separate 64-bit source set here. + androidNativeArm64() + androidNativeArm32() + jvm() js { diff --git a/skainet-io/skainet-io-gguf/build.gradle.kts b/skainet-io/skainet-io-gguf/build.gradle.kts index 854c04052..c7819e045 100644 --- a/skainet-io/skainet-io-gguf/build.gradle.kts +++ b/skainet-io/skainet-io-gguf/build.gradle.kts @@ -36,6 +36,13 @@ kotlin { linuxX64 () linuxArm64 () + // Android-native targets, for on-device consumers that link the engine directly + // (SKaiNET-transformers builds gemma-iree for androidNativeArm32). Unlike + // skainet-io-core this module has no posix-typed code, so arm32's Int-width + // ssize_t/size_t needs no separate 64-bit source set here. + androidNativeArm64() + androidNativeArm32() + js { browser() } diff --git a/skainet-lang/skainet-lang-dag/build.gradle.kts b/skainet-lang/skainet-lang-dag/build.gradle.kts index 448bc18c4..d00d95f42 100644 --- a/skainet-lang/skainet-lang-dag/build.gradle.kts +++ b/skainet-lang/skainet-lang-dag/build.gradle.kts @@ -28,6 +28,13 @@ kotlin { linuxX64() linuxArm64() + // Android-native targets, for on-device consumers that link the engine directly + // (SKaiNET-transformers builds gemma-iree for androidNativeArm32). Unlike + // skainet-io-core this module has no posix-typed code, so arm32's Int-width + // ssize_t/size_t needs no separate 64-bit source set here. + androidNativeArm64() + androidNativeArm32() + jvm() js { diff --git a/skainet-lang/skainet-lang-models/build.gradle.kts b/skainet-lang/skainet-lang-models/build.gradle.kts index 0021c82a8..7d3211bd0 100644 --- a/skainet-lang/skainet-lang-models/build.gradle.kts +++ b/skainet-lang/skainet-lang-models/build.gradle.kts @@ -28,6 +28,11 @@ kotlin { linuxX64() linuxArm64() + // Android-native targets, to match the modules this one is compiled against: skainet-backend-cpu + // publishes them, and its commonTest depends on this module, so the target set has to close. + androidNativeArm64() + androidNativeArm32() + jvm() js {