196 lines
6.5 KiB
Kotlin
196 lines
6.5 KiB
Kotlin
import java.util.Properties
|
|
import org.gradle.internal.os.OperatingSystem
|
|
|
|
plugins {
|
|
id("com.android.application")
|
|
id("org.jetbrains.kotlin.plugin.compose")
|
|
}
|
|
|
|
val appMinSdk = 28
|
|
|
|
android {
|
|
namespace = "net.bluemangoo.indpjsk"
|
|
compileSdk {
|
|
version = release(37)
|
|
}
|
|
|
|
defaultConfig {
|
|
applicationId = "net.bluemangoo.indpjsk"
|
|
minSdk = appMinSdk
|
|
targetSdk = 37
|
|
versionCode = 4
|
|
versionName = "1.3"
|
|
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
isMinifyEnabled = false
|
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
|
}
|
|
}
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_11
|
|
targetCompatibility = JavaVersion.VERSION_11
|
|
}
|
|
buildFeatures {
|
|
compose = true
|
|
buildConfig = true
|
|
}
|
|
|
|
sourceSets.getByName("main").assets.directories.add(
|
|
layout.buildDirectory.dir("generated/readme").get().asFile.absolutePath
|
|
)
|
|
}
|
|
|
|
val copyReadmeToAssets = tasks.register<Copy>("copyReadmeToAssets") {
|
|
group = "build"
|
|
description = "Pack README.md"
|
|
from(rootProject.file("README.md"))
|
|
into(layout.buildDirectory.dir("generated/readme").get().asFile)
|
|
rename { "README.md" }
|
|
inputs.file(rootProject.file("README.md")).withPropertyName("readme")
|
|
outputs.dir(layout.buildDirectory.dir("generated/readme")).withPropertyName("readmeAssets")
|
|
}
|
|
|
|
tasks.named("preBuild") {
|
|
dependsOn(copyReadmeToAssets)
|
|
}
|
|
tasks.matching { it.name.startsWith("merge") && it.name.contains("Assets") }.configureEach {
|
|
dependsOn(copyReadmeToAssets)
|
|
}
|
|
|
|
dependencies {
|
|
implementation("androidx.core:core-ktx:1.19.1")
|
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.11.0")
|
|
implementation("androidx.activity:activity-compose:1.13.0")
|
|
implementation(platform("androidx.compose:compose-bom:2026.09.00"))
|
|
implementation("androidx.compose.ui:ui")
|
|
implementation("androidx.compose.ui:ui-graphics")
|
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
|
implementation("androidx.compose.material3:material3")
|
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
|
}
|
|
|
|
val cargoTarget = "aarch64-linux-android"
|
|
val cargoSoName = "libcore_vpn.so"
|
|
val cargoDir = rootProject.file("core_vpn")
|
|
val cargoSo = File(cargoDir, "target/$cargoTarget/release/$cargoSoName")
|
|
val jniLibsArm64 = file("src/main/jniLibs/arm64-v8a")
|
|
val cargoBin: String = providers.gradleProperty("cargoBin").getOrElse("cargo")
|
|
|
|
fun resolveNdkDirectory(project: Project): File {
|
|
val localPropertiesFile = project.rootProject.file("local.properties")
|
|
val properties = Properties()
|
|
if (localPropertiesFile.exists()) {
|
|
localPropertiesFile.inputStream().use { properties.load(it) }
|
|
}
|
|
|
|
properties.getProperty("ndk.dir")?.let { path ->
|
|
val file = File(path)
|
|
if (file.exists()) return file
|
|
}
|
|
|
|
val sdkDirPath = properties.getProperty("sdk.dir")
|
|
?: System.getenv("ANDROID_SDK_ROOT")
|
|
?: System.getenv("ANDROID_HOME")
|
|
?: throw GradleException("Failed to resolve Android SDK path")
|
|
|
|
val ndkParentDir = File(sdkDirPath, "ndk")
|
|
if (!ndkParentDir.exists() || !ndkParentDir.isDirectory) {
|
|
throw GradleException("Failed to resolve NDK directory: ${ndkParentDir.absolutePath}")
|
|
}
|
|
|
|
val installedVersions = ndkParentDir.listFiles { f -> f.isDirectory }
|
|
?.sortedWith(compareBy { it.name })
|
|
?: emptyList()
|
|
|
|
if (installedVersions.isEmpty()) {
|
|
throw GradleException("Failed to find any installed NDK versions in ${ndkParentDir.absolutePath}")
|
|
}
|
|
|
|
return installedVersions.last()
|
|
}
|
|
|
|
fun resolveLlvmBinDirectory(ndkDir: File): File {
|
|
val prebuiltDir = File(ndkDir, "toolchains/llvm/prebuilt")
|
|
val hostDir = prebuiltDir.listFiles { file -> file.isDirectory }?.firstOrNull()
|
|
?: throw IllegalStateException("Ndk llvm not found: ${prebuiltDir.absolutePath}")
|
|
|
|
val binDir = File(hostDir, "bin")
|
|
if (!binDir.exists()) {
|
|
throw IllegalStateException("Ndk llvm not found: ${binDir.absolutePath}")
|
|
}
|
|
return binDir
|
|
}
|
|
|
|
val cargoBuildVpnCore = tasks.register<Exec>("cargoBuildVpnCore") {
|
|
group = "build"
|
|
description = "Rust build core_vpn"
|
|
|
|
workingDir = cargoDir
|
|
doFirst {
|
|
try {
|
|
val ndkDir = resolveNdkDirectory(project)
|
|
val ndkHomePath = ndkDir.absolutePath
|
|
val ndkBin = resolveLlvmBinDirectory(ndkDir)
|
|
val scriptExt = if (OperatingSystem.current().isWindows) ".cmd" else ""
|
|
val execExt = if (OperatingSystem.current().isWindows) ".exe" else ""
|
|
|
|
environment("NDK_HOME", ndkHomePath)
|
|
environment("PATH", "${ndkBin.absolutePath}${File.pathSeparator}${System.getenv("PATH")}")
|
|
environment(
|
|
"CC_aarch64-linux-android",
|
|
ndkBin.resolve("aarch64-linux-android$appMinSdk-clang$scriptExt").absolutePath
|
|
)
|
|
environment(
|
|
"CXX_aarch64-linux-android",
|
|
ndkBin.resolve("aarch64-linux-android$appMinSdk-clang++$scriptExt").absolutePath
|
|
)
|
|
environment("AR_aarch64-linux-android", ndkBin.resolve("llvm-ar$execExt").absolutePath)
|
|
environment(
|
|
"CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER",
|
|
ndkBin.resolve("aarch64-linux-android$appMinSdk-clang$scriptExt").absolutePath
|
|
)
|
|
} catch (e: Exception) {
|
|
logger.warn("[cargoBuildVpnCore] resolveNdkDirectory failed: ${e.message}")
|
|
}
|
|
}
|
|
commandLine(cargoBin, "build", "--release", "--target", cargoTarget)
|
|
|
|
inputs.files(
|
|
File(cargoDir, "Cargo.toml"),
|
|
File(cargoDir, "Cargo.lock"),
|
|
File(cargoDir, "build.rs"),
|
|
).withPropertyName("rustConfig")
|
|
inputs.dir(File(cargoDir, "src")).withPropertyName("rustSrc")
|
|
|
|
outputs.file(cargoSo).withPropertyName("rustSo")
|
|
}
|
|
|
|
val copyVpnSo = tasks.register("copyVpnSo") {
|
|
group = "build"
|
|
description = "Sync core_vpn.so"
|
|
|
|
dependsOn(cargoBuildVpnCore)
|
|
|
|
inputs.file(cargoSo).withPropertyName("rustSo")
|
|
outputs.dir(jniLibsArm64).withPropertyName("jniLibsArm64")
|
|
|
|
doLast {
|
|
check(cargoSo.isFile) {
|
|
"File $cargoSo not exist"
|
|
}
|
|
if (jniLibsArm64.exists()) {
|
|
jniLibsArm64.deleteRecursively()
|
|
}
|
|
jniLibsArm64.mkdirs()
|
|
val dest = File(jniLibsArm64, cargoSoName)
|
|
cargoSo.copyTo(dest, overwrite = true)
|
|
logger.lifecycle("[copyVpnSo] ${cargoSo.absolutePath} -> ${dest.absolutePath}")
|
|
}
|
|
}
|
|
|
|
tasks.named("preBuild") {
|
|
dependsOn(copyVpnSo)
|
|
} |