Targets

The plugin looks at the Kotlin targets you declare in kotlin { } and builds the Rust crate for each of them. You don't have to configure targets separately for the plugin.

Kotlin target Rust target(s) Library Delivered as
jvm() host (debug), all desktop targets (release) dynamic JVM resources, loaded by JNA
android { } see Android dynamic jniLibs, loaded by JNA
iosArm64() aarch64-apple-ios static cinterop
iosSimulatorArm64() aarch64-apple-ios-sim static cinterop
iosX64() x86_64-apple-ios static cinterop
macosArm64() aarch64-apple-darwin static cinterop
linuxX64() / linuxArm64() x86_64-unknown-linux-gnu / aarch64-unknown-linux-gnu static cinterop
mingwX64() x86_64-pc-windows-gnu static cinterop

A Kotlin target that is not in this table fails the configuration with Unhandled target.

Debug and release builds

Everything is built with Cargo's dev profile by default. To build with --release, pass the Gradle property releaseBuild:

./gradlew publish -PreleaseBuild=true

The property also changes which Rust targets are built for JVM and Android:

Debug Release
JVM the host only aarch64/x86_64 for macOS and Linux, x86_64 for Windows
Android the host architecture's ABI(s) arm64-v8a, armeabi-v7a, x86_64

So a debug JVM artifact only works on the machine that built it, and a release build needs toolchains for every desktop platform.

JVM

kotlin {
    jvm()
}

The dynamic libraries are copied into the jvmMain resources under JNA's platform prefix (for example darwin-aarch64/libfoo.dylib), so JNA finds them on the classpath at runtime. To load a different library file instead, set the system property uniffi.component.<namespace>.libraryOverride to its name or path.

For a release build, Cargo needs a C toolchain and linker for each foreign platform. On macOS, the CI uses messense/macos-cross-toolchains and mingw-w64 and sets the usual Cargo variables, for example:

CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc
AR_x86_64_unknown_linux_gnu=x86_64-linux-gnu-ar
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc

See .github/workflows/publish.yml for the full set. Alternatively, a target can be built with cross:

cargo {
    compilations.linuxX64 { useCross = true }
}

For a JVM-only project, see examples/jvm-only.

Android

Android support is built on the Android Kotlin Multiplatform library plugin (AGP 9). Apply it next to the Kotlin Multiplatform plugin and configure Android inside kotlin { }:

plugins {
    kotlin("multiplatform")
    id("com.android.kotlin.multiplatform.library") version "9.3.1"
    id("ch.ubique.uniffi.plugin") version "1.3.0"
}

kotlin {
    android {
        namespace = "com.example.quickstart"
        compileSdk = 36
        minSdk = 21
    }
}

The libraries are handed to AGP as generated jniLibs. If you enable Android host tests with withHostTest { }, the plugin also builds the crate for your machine and adds it to the host test resources, so unit tests can call Rust without a device.

Migrating from 1.0.x

Older versions used com.android.library with androidTarget { } and a top-level android { } block. Both are replaced by the setup above. minSdk and compileSdk are now set directly in kotlin { android { } } instead of in a defaultConfig { } block.

NDK

The Android targets are compiled with the NDK's clang. By default the plugin picks the newest NDK in $ANDROID_HOME/ndk, and falls back to $ANDROID_NDK_ROOT. To pin a version:

cargo {
    ndkVersion = "28.1.13356709"
}

Debug ABIs

In debug builds the plugin compiles for the ABI(s) matching your machine: arm64-v8a on ARM hosts, and x86_64 plus arm64-v8a on x86 hosts. To compile only what your device needs:

./gradlew :app:assembleDebug -PandroidAbis=arm64-v8a

or, permanently:

cargo {
    androidDebugAbis.add("arm64-v8a")
}

Both accept arm64-v8a, armeabi-v7a and x86_64. Release builds always include all three.

Kotlin/Native

Each native target links a static library through cinterop. The plugin generates a .def file per target that points at the library and the generated C headers, and adds a cinterop named uniffi-cinterop to the target's main compilation.

Remember to set kotlin.mpp.enableCInteropCommonization=true, see Getting started.

Apple targets can only be built on macOS. If the same build script also runs on Linux or Windows CI, guard them:

import ch.ubique.uniffi.plugin.model.RustHost

kotlin {
    if (RustHost.Platform.MacOS.isCurrent) {
        iosArm64()
        iosSimulatorArm64()
        macosArm64()
    }
}

Linking with Rust's linker

The linker bundled with Kotlin/Native is sometimes older than the LLVM your Rust toolchain uses, and fails to link the Rust objects. useRustUpLinker() makes a compilation link with the lld that ships with your active Rust toolchain instead:

import ch.ubique.uniffi.plugin.extensions.useRustUpLinker

kotlin {
    mingwX64 {
        compilations.getByName("test") {
            useRustUpLinker()
        }
    }
}

The examples and tests in this repository use it for mingwX64 test binaries and for the Apple targets.