Files
PurrfectSnap/native/build.gradle.kts
2025-10-03 17:10:09 +05:30

153 lines
5.4 KiB
Kotlin

import java.io.File
import java.util.Locale
import java.util.Properties
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.Sync
data class CargoTarget(
val triple: String,
val abi: String,
val toolchainPrefix: String,
val apiLevel: Int
) {
val envSuffix: String = triple.replace('-', '_')
val envSuffixUpper: String = envSuffix.uppercase(Locale.ROOT)
val taskSuffix: String = abi.split('-', '_').joinToString("") { fragment ->
fragment.replaceFirstChar { ch -> ch.titlecase(Locale.ROOT) }
}
}
plugins {
alias(libs.plugins.androidLibrary)
}
val nativeBuildHash = rootProject.ext.get("buildHash").toString()
val nativeLibFileName = "lib${nativeBuildHash}.so"
val localProperties = Properties().apply {
val file = rootProject.file("local.properties")
if (file.exists()) {
file.inputStream().use { load(it) }
}
}
val ndkHomePath = (System.getenv("ANDROID_NDK_HOME")?.trimEnd('/', '\\')
?: localProperties.getProperty("ndk.dir")?.trimEnd('/', '\\')
?: error("Unable to locate the Android NDK. Set ANDROID_NDK_HOME or define ndk.dir in local.properties"))
.replace("\\:", ":")
val ndkHome = File(ndkHomePath)
require(ndkHome.exists()) { "Configured NDK directory $ndkHome does not exist" }
val osName = System.getProperty("os.name").lowercase(Locale.ROOT)
val osArch = System.getProperty("os.arch").lowercase(Locale.ROOT)
val hostTag = when {
osName.contains("windows") -> "windows-x86_64"
osName.contains("mac") && osArch.contains("arm") -> "darwin-arm64"
osName.contains("mac") -> "darwin-x86_64"
else -> "linux-x86_64"
}
val toolchainBin = File(ndkHome, "toolchains/llvm/prebuilt/$hostTag/bin")
require(toolchainBin.exists()) { "Unable to locate NDK toolchain under $toolchainBin" }
val isWindowsHost = hostTag.startsWith("windows")
val clangSuffix = if (isWindowsHost) ".cmd" else ""
val llvmArExecutable = File(toolchainBin, if (isWindowsHost) "llvm-ar.exe" else "llvm-ar")
require(llvmArExecutable.exists()) { "Unable to find llvm-ar executable at ${llvmArExecutable.absolutePath}" }
val toolchainPath = listOf(toolchainBin.absolutePath, System.getenv("PATH") ?: "")
.filter { it.isNotBlank() }
.joinToString(File.pathSeparator)
val cargoTargets = listOf(
CargoTarget(
triple = "aarch64-linux-android",
abi = "arm64-v8a",
toolchainPrefix = "aarch64-linux-android",
apiLevel = 28,
),
CargoTarget(
triple = "armv7-linux-androideabi",
abi = "armeabi-v7a",
toolchainPrefix = "armv7a-linux-androideabi",
apiLevel = 28,
),
)
fun clangExecutableFor(target: CargoTarget): File {
val executable = File(toolchainBin, "${target.toolchainPrefix}${target.apiLevel}-clang$clangSuffix")
require(executable.exists()) { "Unable to find clang executable at ${executable.absolutePath}" }
return executable
}
fun clangPlusPlusExecutableFor(target: CargoTarget): File {
val executable = File(toolchainBin, "${target.toolchainPrefix}${target.apiLevel}-clang++$clangSuffix")
require(executable.exists()) { "Unable to find clang++ executable at ${executable.absolutePath}" }
return executable
}
android {
namespace = rootProject.ext["applicationId"].toString() + ".nativelib"
compileSdk = 36
ndkVersion = ndkHome.name
buildFeatures {
buildConfig = true
}
defaultConfig {
buildConfigField("String", "NATIVE_NAME", "\"$nativeBuildHash\".toString()")
minSdk = 28
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
sourceSets["main"].jniLibs.srcDir("build/rustJniLibs/android")
}
val syncTasks = cargoTargets.map { target ->
val rustupTask = tasks.register<Exec>("rustup${target.taskSuffix}") {
workingDir = file("rust")
commandLine("rustup", "target", "add", target.triple)
}
val cargoTask = tasks.register<Exec>("cargoBuild${target.taskSuffix}") {
group = "build"
dependsOn(rustupTask)
workingDir = file("rust")
commandLine("cargo", "build", "--release", "--target", target.triple)
val clang = clangExecutableFor(target).absolutePath
val clangxx = clangPlusPlusExecutableFor(target).absolutePath
environment("CC", clang)
environment("CXX", clangxx)
environment("CC_${target.envSuffix}", clang)
environment("CXX_${target.envSuffix}", clangxx)
environment("CC_${target.envSuffixUpper}", clang)
environment("CXX_${target.envSuffixUpper}", clangxx)
environment("CARGO_TARGET_${target.envSuffixUpper}_LINKER", clang)
environment("AR", llvmArExecutable.absolutePath)
environment("AR_${target.envSuffix}", llvmArExecutable.absolutePath)
environment("AR_${target.envSuffixUpper}", llvmArExecutable.absolutePath)
environment("CARGO_TARGET_${target.envSuffixUpper}_AR", llvmArExecutable.absolutePath)
environment("PATH", toolchainPath)
}
tasks.register<Sync>("syncNative${target.taskSuffix}") {
dependsOn(cargoTask)
val outputLibName = nativeLibFileName
from(layout.projectDirectory.file("rust/target/${target.triple}/release/libsnapenhance.so")) {
rename { outputLibName }
}
into(layout.buildDirectory.dir("rustJniLibs/android/${target.abi}"))
}
}
tasks.named("preBuild").configure {
syncTasks.forEach { dependsOn(it) }
}