onionwire/android/sdk/build.gradle.kts

164 lines
5.8 KiB
Text
Raw Permalink Normal View History

import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
// ---------------------------------------------------------------------------
// Rust: build crates/onionwire-sdk for the requested Android ABIs and generate
// the UniFFI Kotlin bindings from the host cdylib.
//
// Nothing here touches the Linux TUI crate's feature graph: the SDK crate is
// its own Cargo workspace that selects Arti's rustls backend.
// ---------------------------------------------------------------------------
val repoRoot: File = rootProject.projectDir.parentFile
val sdkCrateDir: File = repoRoot.resolve("crates/onionwire-sdk")
/** NDK version used for the Rust link step. Must match an installed NDK. */
val onionwireNdkVersion: String = (findProperty("onionwire.ndk") as String?)
?: "28.2.13676358"
/** Compile against this API level. Keep in sync with `minSdk`. */
val onionwireApiLevel: Int = ((findProperty("onionwire.api") as String?) ?: "26").toInt()
val androidSdkDir: File = sequenceOf(
System.getenv("ANDROID_HOME"),
System.getenv("ANDROID_SDK_ROOT"),
).filterNotNull().map(::File).firstOrNull()
?: rootProject.file("local.properties")
.takeIf { it.exists() }
?.readLines()
?.firstOrNull { it.startsWith("sdk.dir=") }
?.substringAfter('=')
?.trim()
?.let(::File)
?: error("ANDROID_HOME / ANDROID_SDK_ROOT unset and no sdk.dir in android/local.properties")
val ndkDir: File = androidSdkDir.resolve("ndk/$onionwireNdkVersion")
require(ndkDir.isDirectory) {
"NDK $onionwireNdkVersion not found at $ndkDir — run: sdkmanager --install \"ndk;$onionwireNdkVersion\""
}
val cargoExe: String = (findProperty("onionwire.cargo") as String?)
?: System.getenv("CARGO")
?: File(System.getProperty("user.home"), ".cargo/bin/cargo").takeIf { it.canExecute() }?.absolutePath
?: "cargo"
/** `-Ponionwire.abis=arm64-v8a,x86_64` to build more than one ABI. */
val onionwireAbis: List<String> = (findProperty("onionwire.abis") as String?)
?.split(',')?.map(String::trim)?.filter(String::isNotEmpty)
?: listOf("arm64-v8a")
val jniLibsDir = layout.buildDirectory.dir("rustJniLibs")
val uniffiKotlinDir = layout.buildDirectory.dir("generated/uniffi")
/** Host cdylib. UniFFI's `--library` mode dlopens it, so it must be host-arch. */
val cargoHostLib by tasks.registering(Exec::class) {
group = "onionwire"
description = "Builds libonionwire_sdk.so for the host (used only to emit bindings)."
workingDir = sdkCrateDir
environment("ANDROID_HOME", androidSdkDir.absolutePath)
commandLine(cargoExe, "build", "--release", "--lib")
}
val uniffiBindgen by tasks.registering(Exec::class) {
group = "onionwire"
description = "Generates the Kotlin UniFFI bindings into build/generated/uniffi."
dependsOn(cargoHostLib)
workingDir = sdkCrateDir
inputs.file(sdkCrateDir.resolve("src/lib.rs"))
outputs.dir(uniffiKotlinDir)
environment("ANDROID_HOME", androidSdkDir.absolutePath)
doFirst { uniffiKotlinDir.get().asFile.mkdirs() }
commandLine(
cargoExe, "run", "--release", "--bin", "uniffi-bindgen", "--",
"generate",
"--library", "target/release/libonionwire_sdk.so",
"--language", "kotlin",
"--no-format",
"--config", "uniffi.toml",
"--out-dir", uniffiKotlinDir.get().asFile.absolutePath,
)
}
val cargoBuildAndroid by tasks.registering(Exec::class) {
group = "onionwire"
description = "Cross-compiles crates/onionwire-sdk for ${onionwireAbis.joinToString()}."
workingDir = sdkCrateDir
inputs.dir(sdkCrateDir.resolve("src"))
inputs.file(sdkCrateDir.resolve("Cargo.toml"))
inputs.file(sdkCrateDir.resolve("Cargo.lock"))
inputs.dir(repoRoot.resolve("src"))
inputs.file(repoRoot.resolve("Cargo.toml"))
outputs.dir(jniLibsDir)
environment("ANDROID_HOME", androidSdkDir.absolutePath)
environment("ANDROID_NDK_HOME", ndkDir.absolutePath)
environment("NDK_HOME", ndkDir.absolutePath)
commandLine(
buildList {
add(cargoExe); add("ndk")
onionwireAbis.forEach { add("-t"); add(it) }
add("--platform"); add(onionwireApiLevel.toString())
add("-o"); add(jniLibsDir.get().asFile.absolutePath)
add("build"); add("--release"); add("--lib")
}
)
}
// ---------------------------------------------------------------------------
android {
namespace = "com.siriusdevops.onionwire.sdk"
compileSdk = 36
ndkVersion = onionwireNdkVersion
defaultConfig {
minSdk = onionwireApiLevel
consumerProguardFiles("consumer-rules.pro")
ndk {
abiFilters += onionwireAbis
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
sourceSets["main"].jniLibs.srcDir(jniLibsDir)
sourceSets["main"].kotlin.srcDir(uniffiKotlinDir)
buildTypes {
release {
isMinifyEnabled = false
}
}
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
dependencies {
// UniFFI's Kotlin backend talks to the Rust cdylib through JNA direct
// mapping, and the async bindings need coroutines. Both are part of the
// public API surface, so they are `api` — a consumer of the AAR needs them
// on its own compile classpath. JNA's `aar` variant is what carries
// libjnidispatch.so for each ABI.
api(variantOf(libs.jna) { artifactType("aar") })
api(libs.kotlinx.coroutines.core)
}
tasks.withType<KotlinCompile>().configureEach {
dependsOn(uniffiBindgen)
}
tasks.matching { it.name == "preBuild" }.configureEach {
dependsOn(cargoBuildAndroid, uniffiBindgen)
}