feature/diffusion-vision
name: sdk-kotlin-lib description: Build the Onde Kotlin Android library from Rust source using UniFFI bindings. Use when cross-compiling libonde.so for Android ABIs, generating Kotlin bindings, or modifying the build-kotlin.sh pipeline. allowed-tools: Read, Write, Edit, Glob, Grep, Bash
user-invocable: false
Skill: Onde Kotlin SDK (Android Library)
What This Skill Covers
Building the onde Kotlin Android library from the Rust crate using UniFFI bindings. The output is a Kotlin/JVM library module (sdk/kotlin/onde/) with:
- Pre-compiled
.sonative libraries for all four Android ABIs (arm64-v8a,armeabi-v7a,x86_64,x86) underjniLibs/ - Auto-generated Kotlin source at
sdk/kotlin/onde/src/main/kotlin/com/ondeinference/onde/onde.kt
Crate & Tooling Layout
onde/
├── Cargo.toml # crate-type = ["lib", "cdylib", "staticlib"]
├── uniffi.toml # [bindings.kotlin] package_name = "com.ondeinference.onde"
├── build-kotlin.sh # main build script — cross-compiles + runs bindgen
├── uniffi-bindgen/
│ ├── Cargo.toml # bin: uniffi-bindgen, dep: uniffi = "=0.31.0", features = ["cli"]
│ └── uniffi-bindgen.rs # fn main() { uniffi::uniffi_bindgen_main() }
└── sdk/kotlin/onde/
└── src/main/
├── jniLibs/<ABI>/libonde.so
└── kotlin/com/ondeinference/onde/onde.kt
Key Constraints
- UniFFI is pinned to
=0.31.0in bothCargo.toml(lib) anduniffi-bindgen/Cargo.toml. Never change this independently — both must stay in sync. cdylibcrate-type is required for the.sooutput consumed by the JVM at runtime.uniffi::setup_scaffolding!()insrc/lib.rsis the proc-macro approach — no.udlfile is needed.- Android NDK minimum API = 24 (
MIN_API=24inbuild-kotlin.sh) to match the library'sminSdk. - Package name is
com.ondeinference.onde— set inuniffi.toml, never override in bindgen flags.
Build Command
# Full release build for all four Android ABIs
./build-kotlin.sh
# Debug build, arm64 only
./build-kotlin.sh --debug --target aarch64-linux-android
# Regenerate Kotlin source from an existing .so (skip Rust compilation)
./build-kotlin.sh --generate-only
The script:
1. Builds the onde-uniffi-bindgen CLI binary (host).
2. Cross-compiles libonde.so for each selected Android target using the NDK clang toolchain.
3. Copies .so files into sdk/kotlin/onde/src/main/jniLibs/<ABI>/.
4. Runs uniffi-bindgen generate --library <.so> --language kotlin --out-dir <src_root> --config uniffi.toml.
NDK Setup
The script auto-detects the NDK in order:
1. $ANDROID_NDK_HOME
2. $ANDROID_HOME/ndk/ (picks newest version)
3. ~/Library/Android/sdk/ndk/ (macOS default)
4. ~/Android/Sdk/ndk/ (Linux default)
Or pass explicitly: ./build-kotlin.sh --ndk /path/to/ndk
Required Rust targets:
bash
rustup target add \
aarch64-linux-android \
armv7-linux-androideabi \
x86_64-linux-android \
i686-linux-android
UniFFI Binding Rules
All types that cross the FFI boundary must carry the appropriate UniFFI derive:
| Rust trait/derive | Kotlin output |
|---|---|
#[derive(uniffi::Record)] |
data class |
#[derive(uniffi::Enum)] |
sealed class / enum |
#[derive(uniffi::Error)] |
sealed class (throwable) |
#[derive(uniffi::Object)] |
class (Arc-backed) |
#[uniffi::export(callback_interface)] |
interface |
Forbidden in UniFFI-exported types:
- usize / isize — use u64 / i64 instead
- impl Into<String> — use concrete String
- tokio::sync::mpsc::Receiver — use callback_interface + free function
- Callback interfaces as parameters of #[derive(uniffi::Object)] methods — must be free functions
Adding a New Exported Function or Type
- Add the type/function in the relevant
src/file with the correct UniFFI derive or#[uniffi::export]. - Verify
uniffi::setup_scaffolding!()is still the only scaffolding call insrc/lib.rs. - Re-run
./build-kotlin.sh --generate-only(if a compiled.soalready exists) or the full build. - The new class/function will appear in
sdk/kotlin/onde/src/main/kotlin/com/ondeinference/onde/onde.kt. - Never hand-edit
onde.kt— it is always fully regenerated.
Common Pitfalls
- Version mismatch between
uniffiinCargo.tomlanduniffi-bindgen/Cargo.toml— bindgen will refuse to process the library and emit a schema version error. Both must be=0.31.0. - Missing
cdylibincrate-type—cargo buildwill not produce a.so. TheCargo.tomlmust keep["lib", "cdylib", "staticlib"]. ___chkstk_darwinlinker error — only occurs on tvOS, not Android. Ignore on Android builds.- Android sandbox has no
HOME—dirs::home_dir()returnsNoneinside the Android sandbox. On Android,hf-hubis a direct dependency (not optional) so that the HF cache path can be set explicitly viaGLOBAL_HF_CACHEbefore any model download. libonde.sonot found after build — the script searches multiple candidate paths including workspacetarget/. If the workspacetarget/is above the crate directory, the fallback path logic inbuild-kotlin.shwill find it.