Kotlin Multiplatform bindings for ONNX Runtime (CPU only).
A single common Kotlin API in front of the two official ONNX Runtime distributions:
| Platform | Backend |
|---|---|
| JVM (desktop) | com.microsoft.onnxruntime:onnxruntime |
| Android | com.microsoft.onnxruntime:onnxruntime-android |
| macOS arm64 | official prebuilt libonnxruntime 1.28.0 (osx-arm64), linked via cinterop |
| macOS x64 | official prebuilt libonnxruntime 1.23.2 (osx-x86_64, the last Intel build Microsoft shipped) |
| Linux x64 / arm64 | official prebuilt libonnxruntime 1.28.0, linked via cinterop |
| Windows x64 | official prebuilt onnxruntime.dll 1.28.0, linked via cinterop |
The Kotlin/Native klibs embed the link configuration (@loader_path / $ORIGIN
rpath). Since a dynamic library cannot be embedded into a klib, the shared
libraries are published as standalone per-platform artifacts
(onnxruntime-lib-<platform>); see Installation.
Published to Maven Central since 1.0.0; current version 1.0.2 (bundles ONNX Runtime 1.28.0).
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
// module build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation("cn.enaium.onnxruntime:onnxruntime-kmp:1.0.2")
}
}
}Add the matching native runtime artifact, and pass its extracted directory to the Kotlin/Native linker:
// e.g. for linuxX64
val libDir = project.layout.buildDirectory.dir("onnxruntime-lib") // extracted location
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().configureEach {
binaries.all {
linkerOpts("-L${libDir.get().asFile.absolutePath}", "-lonnxruntime")
}
}
}At run time, ship the shared library next to the final binary — the klib embeds an
@loader_path (macOS) / $ORIGIN (Linux) rpath that finds it there.
| Platform | Artifact to resolve | Runtime file to ship |
|---|---|---|
| macOS arm64 | cn.enaium.onnxruntime:onnxruntime-lib-macosarm64:1.0.2 |
libonnxruntime.1.dylib |
| macOS x64 | cn.enaium.onnxruntime:onnxruntime-lib-macosx64:1.0.2 |
libonnxruntime.1.23.2.dylib |
| Linux x64 | cn.enaium.onnxruntime:onnxruntime-lib-linuxx64:1.0.2 |
libonnxruntime.so.1 |
| Linux arm64 | cn.enaium.onnxruntime:onnxruntime-lib-linuxarm64:1.0.2 |
libonnxruntime.so.1 |
| Windows x64 | cn.enaium.onnxruntime:onnxruntime-lib-mingwx64:1.0.2 |
onnxruntime.dll |
Note: the klib cannot embed a dynamic library, and the build machine's library path baked into the published klib does not exist on consumer machines — it is only a linker warning, but the
-lonnxruntimesearch still needs the-Lflag above.
// module build.gradle.kts
dependencies {
implementation("cn.enaium.onnxruntime:onnxruntime-kmp-jvm:1.0.0")
}or keep onnxruntime-kmp in a KMP module and consume the jvm() target variant.
The official com.microsoft.onnxruntime:onnxruntime package (with its bundled
per-platform natives) is pulled in automatically.
// module build.gradle.kts
dependencies {
implementation("cn.enaium.onnxruntime:onnxruntime-kmp-android:1.0.0")
}The .so files are bundled in the official onnxruntime-android AAR and loaded via
System.loadLibrary.
import cn.enaium.onnxruntime.*
fun classify(modelPath: String, pixels: FloatArray): Int {
val env = createEnv()
val session = createSession(env, modelPath)
val input = pixels.toTensor(longArrayOf(1, 1, 28, 28))
val result = session.run("Input3" to input)
val logits = result.getValue("Plus214_Output_0") as FloatTensor
session.close()
env.close()
return logits.argMax()
}createEnv(logLevel, logId): Env— environment (owns sessions; on JVM it wraps the process-globalOrtEnvironment).createSessionOptions(): SessionOptions— threads, graph optimization level, log level, session config entries.createRunOptions(): RunOptions— per-run termination control.createSession(env, modelPath | modelBytes, options?): Session— loads an.onnx/.ortmodel from a file or from memory.Session.inputNames/outputNames,Session.run(inputs, outputs?, runOptions?)returningMap<String, Tensor>.- Tensors:
FloatTensor,DoubleTensor,LongTensor,IntTensor,StringTensorwithshape,size,data; convert withFloatArray.toTensor(shape)etc. - Errors:
OnnxRuntimeException(code, message).
- Operator overloading on tensors:
+,-,*,/(element-wise, tensor or scalar),unaryMinus,get/setwith multi-dimensional indices,iterator(). - Member extensions:
FloatTensor.dot(other)(2-D matrix multiply),normalizeInPlace(),argMax(). - Extension factories:
FloatArray.toTensor(shape),LongArray.toTensor(shape), ... Tensor.run(vararg inputs: Pair<String, Tensor>)andSession.run(inputName, tensor)conveniences.
onnxruntime-kmp– the multiplatform library (common API + JVM/Android/cinteropimplementations). The prebuilt shared libraries are downloaded from the official GitHub releases by the build intonative/(git-ignored).onnxruntime-lib-<platform>– per-platform artifacts that package the prebuilt shared library for publication (see the table above).example– desktop example + tests: JVM and Kotlin/Native executables running real MNIST inference with the bundledmodel/mnist-8.onnx(committed).example-android– Android example app + instrumented test..github/workflows/test.yml/publish.yml– CI and Maven Central publishing.
./gradlew publishToMavenLocal # publishes :onnxruntime-kmp to ~/.m2
./gradlew :example:run # JVM MNIST example
./gradlew :example:runMacosArm64 # native MNIST example (per host target)
./gradlew :example:macosArm64Test # per-platform tests (jvmTest, linuxX64Test, ...)
./gradlew :example-android:assembleDebug- Only the CPU execution provider is wired in; GPU/CUDA packages are deliberately not included.
- The JVM backend wraps the process-global
OrtEnvironment(closing it is a no-op); the native backend creates and releases a realOrtEnv*. - Native consumers must provide the matching shared library: macOS targets look for
libonnxruntime.1.dylib(arm64) /libonnxruntime.1.23.2.dylib(x64) via the embedded@loader_pathrpath, Linux forlibonnxruntime.so.1via$ORIGIN, and Windows foronnxruntime.dllnext to the executable.
MIT, see LICENSE.