Ferrus Nexus KMP is a Kotlin Multiplatform library for bridging Kotlin code with native libraries (focusing on library authors). It provides a shared API for calling native functions, creating foreign instances, and working with Wire message types across JVM and native targets.
- Kotlin Multiplatform support for JVM and native platforms
- Helpers for calling native functions and methods
- Support for input and output protobuf-style messages via Wire
- Native pointer and foreign instance lifecycle management
- Ready to publish as a Maven artifact
- JVM (thought to be used along jextract)
- Linux x64
- Linux arm64
- Windows x64
Add the dependency to your project:
implementation("io.github.ignaciodelatorrearias:ferrus-nexus:0.0.1")import io.github.ignaciodelatorrearias.ferrusnexus.FerrusNexus
val nexus = FerrusNexus(
library = "my_native_library",
errorAdapter = YourErrorMessage.ADAPTER,
errorManagerFunction = { status: int, error: YourErrorMessage ->
// handle errors
},
freeBufferFunction = my_native_library_h::free_buffer
)
nexus.function(my_native_library_h::some_function)import io.github.ignaciodelatorrearias.ferrusnexus.executablePath
import io.github.ignaciodelatorrearias.ferrusnexus.FerrusNexus
val name = when (Platform.osFamily) {
OsFamily.UNKNOWN -> throw Error("Unknown Platform")
OsFamily.MACOSX -> throw Error("Unsupported Platform")
OsFamily.IOS -> throw Error("Unsupported Platform")
OsFamily.LINUX -> "libmy_native_library.so"
OsFamily.WINDOWS -> "my_native_library.dll"
OsFamily.ANDROID -> throw Error("Unsupported Platform")
OsFamily.WASM -> throw Error("Unsupported Platform")
OsFamily.TVOS -> throw Error("Unsupported Platform")
OsFamily.WATCHOS -> throw Error("Unsupported Platform")
}
val nexus = FerrusNexus(
executablePath / name,
errorAdapter = YourErrorMessage.ADAPTER,
errorManagerFunction = { status: int, error: YourErrorMessage ->
// handle errors
},
"free_buffer"
)
nexus.function("some_function")
// Optionally if you consider using implicit dynamic linking and cinterops functionality.
val nexus = FerrusNexus(
errorAdapter = YourErrorMessage.ADAPTER,
errorManagerFunction = { status: int, error: YourErrorMessage ->
// handle errors
},
::free_buffer
)
nexus.function(::some_function)Kotlin/native doesn't implement a way to use dynamic libraries, but it does implement a way to use static ones.
We could use some trickery to pass the native library files to the linker, forcing implicit linking.
linkerOpts.mingw_x64 = -Lnatives/x86_64-windows/ -lmy_native_library.dll
linkerOpts.linux_x64 = -Lnatives/x86_64-linux-gnu/ -lmy_native_library -rpath "$ORIGIN:."
However for library authors this creates a problem, because users need to be aware of how cinterops works and what files would they need configure to pass to the linker.
Ferrus Nexus offers a way to use explicit linking and do some non-configuration-intensive trickery.
A typical .def file:
headers = my_native_library.h
package = com.example.my_native_library
compilerOpts = -Imy_native_library/
staticLibraries.mingw_x64 = my_native_library.dll
libraryPaths.mingw_x64 = natives/x86_64-windows/
staticLibraries.linux = libmy_native_library.so
libraryPaths.linux_x64 = natives/x86_64-linux-gnu/
libraryPaths.linux_arm64 = natives/aarch64-linux-gnu/
This only helps to include the .dll/.so inside the .klib.
The client of the library only needs to add:
plugins {
...
id("io.github.ignaciodelatorrearias.ferrus-nexus-extract") version "0.0.1"
}This plugin only extracts the dynamic libraries from the .klib and places it alongside the executable for the corresponding platform.
This project is licensed under the Apache License 2.0.