1 ---
2 name: sdk-kotlin-lib
3 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.
4 allowed-tools: Read, Write, Edit, Glob, Grep, Bash
5 user-invocable: false
6 ---
7
8 # Skill: Onde Kotlin SDK (Android Library)
9
10 ## What This Skill Covers
11
12 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:
13
14 - Pre-compiled `.so` native libraries for all four Android ABIs (`arm64-v8a`, `armeabi-v7a`, `x86_64`, `x86`) under `jniLibs/`
15 - Auto-generated Kotlin source at `sdk/kotlin/onde/src/main/kotlin/com/ondeinference/onde/onde.kt`
16
17 ## Crate & Tooling Layout
18
19 ```
20 onde/
21 ├── Cargo.toml # crate-type = ["lib", "cdylib", "staticlib"]
22 ├── uniffi.toml # [bindings.kotlin] package_name = "com.ondeinference.onde"
23 ├── build-kotlin.sh # main build script — cross-compiles + runs bindgen
24 ├── uniffi-bindgen/
25 │ ├── Cargo.toml # bin: uniffi-bindgen, dep: uniffi = "=0.31.0", features = ["cli"]
26 │ └── uniffi-bindgen.rs # fn main() { uniffi::uniffi_bindgen_main() }
27 └── sdk/kotlin/onde/
28 └── src/main/
29 ├── jniLibs/<ABI>/libonde.so
30 └── kotlin/com/ondeinference/onde/onde.kt
31 ```
32
33 ## Key Constraints
34
35 - **UniFFI is pinned to `=0.31.0`** in both `Cargo.toml` (lib) and `uniffi-bindgen/Cargo.toml`. Never change this independently — both must stay in sync.
36 - **`cdylib` crate-type** is required for the `.so` output consumed by the JVM at runtime.
37 - **`uniffi::setup_scaffolding!()`** in `src/lib.rs` is the proc-macro approach — no `.udl` file is needed.
38 - **Android NDK minimum API = 24** (`MIN_API=24` in `build-kotlin.sh`) to match the library's `minSdk`.
39 - **Package name is `com.ondeinference.onde`** — set in `uniffi.toml`, never override in bindgen flags.
40
41 ## Build Command
42
43 ```bash
44 # Full release build for all four Android ABIs
45 ./build-kotlin.sh
46
47 # Debug build, arm64 only
48 ./build-kotlin.sh --debug --target aarch64-linux-android
49
50 # Regenerate Kotlin source from an existing .so (skip Rust compilation)
51 ./build-kotlin.sh --generate-only
52 ```
53
54 The script:
55 1. Builds the `onde-uniffi-bindgen` CLI binary (host).
56 2. Cross-compiles `libonde.so` for each selected Android target using the NDK clang toolchain.
57 3. Copies `.so` files into `sdk/kotlin/onde/src/main/jniLibs/<ABI>/`.
58 4. Runs `uniffi-bindgen generate --library <.so> --language kotlin --out-dir <src_root> --config uniffi.toml`.
59
60 ## NDK Setup
61
62 The script auto-detects the NDK in order:
63 1. `$ANDROID_NDK_HOME`
64 2. `$ANDROID_HOME/ndk/` (picks newest version)
65 3. `~/Library/Android/sdk/ndk/` (macOS default)
66 4. `~/Android/Sdk/ndk/` (Linux default)
67
68 Or pass explicitly: `./build-kotlin.sh --ndk /path/to/ndk`
69
70 Required Rust targets:
71 ```bash
72 rustup target add \
73 aarch64-linux-android \
74 armv7-linux-androideabi \
75 x86_64-linux-android \
76 i686-linux-android
77 ```
78
79 ## UniFFI Binding Rules
80
81 All types that cross the FFI boundary must carry the appropriate UniFFI derive:
82
83 | Rust trait/derive | Kotlin output |
84 |---------------------------|---------------------------|
85 | `#[derive(uniffi::Record)]` | `data class` |
86 | `#[derive(uniffi::Enum)]` | `sealed class` / `enum` |
87 | `#[derive(uniffi::Error)]` | `sealed class` (throwable)|
88 | `#[derive(uniffi::Object)]` | `class` (Arc-backed) |
89 | `#[uniffi::export(callback_interface)]` | `interface` |
90
91 **Forbidden in UniFFI-exported types:**
92 - `usize` / `isize` — use `u64` / `i64` instead
93 - `impl Into<String>` — use concrete `String`
94 - `tokio::sync::mpsc::Receiver` — use `callback_interface` + free function
95 - Callback interfaces as parameters of `#[derive(uniffi::Object)]` methods — must be free functions
96
97 ## Adding a New Exported Function or Type
98
99 1. Add the type/function in the relevant `src/` file with the correct UniFFI derive or `#[uniffi::export]`.
100 2. Verify `uniffi::setup_scaffolding!()` is still the only scaffolding call in `src/lib.rs`.
101 3. Re-run `./build-kotlin.sh --generate-only` (if a compiled `.so` already exists) or the full build.
102 4. The new class/function will appear in `sdk/kotlin/onde/src/main/kotlin/com/ondeinference/onde/onde.kt`.
103 5. Never hand-edit `onde.kt` — it is always fully regenerated.
104
105 ## Common Pitfalls
106
107 - **Version mismatch between `uniffi` in `Cargo.toml` and `uniffi-bindgen/Cargo.toml`** — bindgen will refuse to process the library and emit a schema version error. Both must be `=0.31.0`.
108 - **Missing `cdylib` in `crate-type`** — `cargo build` will not produce a `.so`. The `Cargo.toml` must keep `["lib", "cdylib", "staticlib"]`.
109 - **`___chkstk_darwin` linker error** — only occurs on tvOS, not Android. Ignore on Android builds.
110 - **Android sandbox has no `HOME`** — `dirs::home_dir()` returns `None` inside the Android sandbox. On Android, `hf-hub` is a direct dependency (not optional) so that the HF cache path can be set explicitly via `GLOBAL_HF_CACHE` before any model download.
111 - **`libonde.so` not found after build** — the script searches multiple candidate paths including workspace `target/`. If the workspace `target/` is above the crate directory, the fallback path logic in `build-kotlin.sh` will find it.