mirror of
https://github.com/MetaCubeX/ClashMetaForAndroid.git
synced 2025-02-19 16:23:14 +03:00
Improve: improve clash core build
This commit is contained in:
parent
7381b787f3
commit
e2383df06b
@ -28,8 +28,8 @@ dependencies {
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("golang") {
|
||||
id = "library-golang"
|
||||
implementationClass = "LibraryGolangPlugin"
|
||||
id = "clash-build"
|
||||
implementationClass = "com.github.kr328.clash.tools.ClashBuildPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,167 +0,0 @@
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleScriptException
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.SetProperty
|
||||
import org.gradle.api.tasks.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
abstract class GolangBuildTask : DefaultTask() {
|
||||
abstract val debug: Property<Boolean>
|
||||
@Input get
|
||||
|
||||
abstract val premium: Property<Boolean>
|
||||
@Input get
|
||||
|
||||
abstract val nativeAbis: SetProperty<String>
|
||||
@Input get
|
||||
|
||||
abstract val minSdkVersion: Property<Int>
|
||||
@Input get
|
||||
|
||||
abstract val ndkDirectory: DirectoryProperty
|
||||
@InputDirectory get
|
||||
|
||||
abstract val cmakeDirectory: DirectoryProperty
|
||||
@InputDirectory get
|
||||
|
||||
abstract val inputDirectory: DirectoryProperty
|
||||
@InputDirectory get
|
||||
|
||||
abstract val outputDirectory: DirectoryProperty
|
||||
@OutputDirectory get
|
||||
|
||||
@TaskAction
|
||||
fun build() {
|
||||
val src = inputDirectory.get().asFile
|
||||
|
||||
val generateCmd = """go run make/make.go bridge native build android %s"""
|
||||
|
||||
val buildCmd = if (debug.get()) {
|
||||
"""go build --buildmode=c-shared -trimpath -o "%s" -tags "without_gvisor,without_system,debug${if (premium.get()) ",premium" else ""}"""
|
||||
} else {
|
||||
"""go build --buildmode=c-shared -trimpath -o "%s" -tags "without_gvisor,without_system${if (premium.get()) ",premium" else ""}" -ldflags "-s -w""""
|
||||
}
|
||||
|
||||
"go mod tidy".exec(pwd = src)
|
||||
|
||||
nativeAbis.get().parallelStream().forEach {
|
||||
val out = outputDirectory.get().file("$it/libclash.so")
|
||||
|
||||
generateCmd.format(it.toGoArch()).exec(pwd = src.resolve("tun2socket/bridge"), env = generateGolangGenerateEnvironment(it))
|
||||
buildCmd.format(out).exec(pwd = src, env = generateGolangBuildEnvironment(it))
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateGolangGenerateEnvironment(abi: String): Map<String, String> {
|
||||
val path = cmakeDirectory.get().asFile.absolutePath + File.pathSeparator + System.getenv("PATH")
|
||||
|
||||
return mapOf(
|
||||
"PATH" to path,
|
||||
"CMAKE_SYSTEM_NAME" to "Android",
|
||||
"CMAKE_ANDROID_NDK" to ndkDirectory.get().asFile.absolutePath,
|
||||
"CMAKE_ANDROID_ARCH_ABI" to abi,
|
||||
"CMAKE_SYSTEM_VERSION" to minSdkVersion.get().toString()
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateGolangBuildEnvironment(abi: String): Map<String, String> {
|
||||
val (goArch, goArm) = when (abi) {
|
||||
"arm64-v8a" -> "arm64" to ""
|
||||
"armeabi-v7a" -> "arm" to "7"
|
||||
"x86" -> "386" to ""
|
||||
"x86_64" -> "amd64" to ""
|
||||
else -> throw UnsupportedOperationException("unsupported abi: $abi")
|
||||
}
|
||||
|
||||
val compiler = when (abi) {
|
||||
"armeabi-v7a" ->
|
||||
"armv7a-linux-androideabi${minSdkVersion.get()}-clang"
|
||||
"arm64-v8a" ->
|
||||
"aarch64-linux-android${minSdkVersion.get()}-clang"
|
||||
"x86" ->
|
||||
"i686-linux-android${minSdkVersion.get()}-clang"
|
||||
"x86_64" ->
|
||||
"x86_64-linux-android${minSdkVersion.get()}-clang"
|
||||
else ->
|
||||
throw GradleScriptException(
|
||||
"Unsupported abi $abi",
|
||||
FileNotFoundException("Unsupported abi $abi")
|
||||
)
|
||||
}
|
||||
|
||||
return mapOf(
|
||||
"CC" to compilerBasePath.resolve(compiler).absolutePath,
|
||||
"GOOS" to "android",
|
||||
"GOARCH" to goArch,
|
||||
"GOARM" to goArm,
|
||||
"CGO_ENABLED" to "1",
|
||||
"CFLAGS" to "-O3 -Werror",
|
||||
"CMAKE_ARGS" to "-DCMAKE_TOOLCHAIN_FILE=${ndkDirectory.get().asFile.absolutePath}/build/cmake/android.toolchain.cmake -DANDROID_ABI=$abi -DANDROID_PLATFORM=android-${minSdkVersion.get()} -DCMAKE_BUILD_TYPE=Release",
|
||||
"PATH" to cmakeDirectory.get().asFile.absolutePath + File.pathSeparator + System.getenv("PATH")
|
||||
)
|
||||
}
|
||||
|
||||
private fun String.toGoArch(): String {
|
||||
return when (this) {
|
||||
"arm64-v8a" -> "arm64"
|
||||
"armeabi-v7a" -> "arm"
|
||||
"x86" -> "386"
|
||||
"x86_64" -> "amd64"
|
||||
else -> throw UnsupportedOperationException("unsupported abi: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.exec(
|
||||
pwd: File,
|
||||
env: Map<String, String> = System.getenv()
|
||||
): String {
|
||||
val process = ProcessBuilder().run {
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS))
|
||||
command("cmd.exe", "/c", this@exec)
|
||||
else
|
||||
command("bash", "-c", this@exec)
|
||||
|
||||
environment().putAll(env)
|
||||
directory(pwd)
|
||||
|
||||
redirectErrorStream(true)
|
||||
|
||||
start()
|
||||
}
|
||||
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
process.inputStream.copyTo(outputStream)
|
||||
|
||||
if (process.waitFor() != 0) {
|
||||
println(outputStream.toString("utf-8"))
|
||||
throw GradleScriptException("Exec $this failure", IOException())
|
||||
}
|
||||
|
||||
return outputStream.toString("utf-8")
|
||||
}
|
||||
|
||||
private val compilerBasePath: File
|
||||
get() {
|
||||
val host = when {
|
||||
Os.isFamily(Os.FAMILY_WINDOWS) ->
|
||||
"windows"
|
||||
Os.isFamily(Os.FAMILY_MAC) ->
|
||||
"darwin"
|
||||
Os.isFamily(Os.FAMILY_UNIX) ->
|
||||
"linux"
|
||||
else ->
|
||||
throw GradleScriptException(
|
||||
"Unsupported host",
|
||||
FileNotFoundException("Unsupported host")
|
||||
)
|
||||
}
|
||||
|
||||
return ndkDirectory.get().asFile.resolve("toolchains/llvm/prebuilt/$host-x86_64/bin")
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
import com.android.build.gradle.LibraryExtension
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import org.gradle.api.GradleScriptException
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.util.*
|
||||
|
||||
class LibraryGolangPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
target.extensions.getByType(LibraryExtension::class.java).apply {
|
||||
target.afterEvaluate {
|
||||
val properties = Properties().apply {
|
||||
target.rootProject.file("local.properties").inputStream().use(this::load)
|
||||
}
|
||||
val cmakeDirectory = target.rootProject.file(properties.getProperty("cmake.dir")!!)
|
||||
|
||||
libraryVariants.forEach { variant ->
|
||||
val abis = defaultConfig.externalNativeBuild.cmake.abiFilters +
|
||||
defaultConfig.externalNativeBuild.ndkBuild.abiFilters
|
||||
|
||||
val nameCapitalize = variant.name.capitalize(Locale.getDefault())
|
||||
val golangBuildDir = target.golangBuild.resolve(variant.name)
|
||||
|
||||
val task = target.tasks.register(
|
||||
"externalGolangBuild$nameCapitalize",
|
||||
GolangBuildTask::class.java
|
||||
) {
|
||||
it.premium.set(variant.flavorName == "premium")
|
||||
it.debug.set(variant.name == "debug")
|
||||
it.nativeAbis.set(abis)
|
||||
it.minSdkVersion.set(defaultConfig.minSdk!!)
|
||||
it.ndkDirectory.set(ndkDirectory)
|
||||
it.cmakeDirectory.set(cmakeDirectory)
|
||||
it.inputDirectory.set(target.golangSource)
|
||||
it.outputDirectory.set(golangBuildDir)
|
||||
}
|
||||
|
||||
sourceSets.named(variant.name) {
|
||||
it.jniLibs {
|
||||
srcDir(golangBuildDir)
|
||||
}
|
||||
}
|
||||
|
||||
variant.externalNativeBuildProviders.forEach {
|
||||
it.get().dependsOn(task)
|
||||
}
|
||||
target.tasks.filter { it.name.startsWith("buildCMake") }.forEach {
|
||||
it.mustRunAfter(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.api.BaseVariant
|
||||
import java.io.Serializable
|
||||
|
||||
data class BuildConfig(
|
||||
val debug: Boolean,
|
||||
val premium: Boolean,
|
||||
val abis: List<NativeAbi>,
|
||||
val minSdkVersion: Int,
|
||||
) : Serializable {
|
||||
companion object {
|
||||
fun of(extension: BaseExtension, variant: BaseVariant): BuildConfig {
|
||||
return BuildConfig(
|
||||
debug = variant.buildType.isDebuggable,
|
||||
premium = variant.flavorName == "premium",
|
||||
abis = extension.defaultConfig.externalNativeBuild.cmake.abiFilters
|
||||
.map { NativeAbi.parse(it) }
|
||||
.distinct(),
|
||||
minSdkVersion = extension.defaultConfig.minSdkVersion!!.apiLevel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import com.android.build.gradle.LibraryExtension
|
||||
import golangBuild
|
||||
import golangSource
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Delete
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class ClashBuildPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
target.afterEvaluate {
|
||||
val cmakeDirectory = resolveCmakeDir(target)
|
||||
|
||||
target.extensions.getByType(LibraryExtension::class.java).apply {
|
||||
libraryVariants.forEach { variant ->
|
||||
val config = BuildConfig.of(this, variant)
|
||||
val buildDir = target.golangBuild.resolve(variant.name)
|
||||
val capitalize = variant.name.capitalize(Locale.getDefault())
|
||||
|
||||
val task = target.tasks.register(
|
||||
"externalGolangBuild$capitalize",
|
||||
ClashBuildTask::class.java
|
||||
) {
|
||||
it.config.set(config)
|
||||
it.ndkDirectory.set(ndkDirectory)
|
||||
it.cmakeDirectory.set(cmakeDirectory)
|
||||
it.inputDirectory.set(target.golangSource)
|
||||
it.outputDirectory.set(buildDir)
|
||||
}
|
||||
|
||||
sourceSets.named(variant.name) {
|
||||
it.jniLibs {
|
||||
srcDir(buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
variant.externalNativeBuildProviders.forEach {
|
||||
it.get().dependsOn(task)
|
||||
}
|
||||
target.tasks.filter { it.name.startsWith("buildCMake") }.forEach {
|
||||
it.mustRunAfter(task)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveCmakeDir(project: Project): File {
|
||||
val properties = Properties().apply {
|
||||
project.rootProject.file("local.properties").inputStream().use(this::load)
|
||||
}
|
||||
|
||||
return project.rootProject.file(
|
||||
properties.getProperty("cmake.dir") ?: throw GradleException("cmake.dir not found")
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
|
||||
abstract class ClashBuildTask : DefaultTask() {
|
||||
abstract val config: Property<BuildConfig>
|
||||
@Input get
|
||||
|
||||
abstract val ndkDirectory: DirectoryProperty
|
||||
@InputDirectory get
|
||||
|
||||
abstract val cmakeDirectory: DirectoryProperty
|
||||
@InputDirectory get
|
||||
|
||||
abstract val inputDirectory: DirectoryProperty
|
||||
@InputDirectory get
|
||||
|
||||
abstract val outputDirectory: DirectoryProperty
|
||||
@OutputDirectory get
|
||||
|
||||
@TaskAction
|
||||
fun build() {
|
||||
val input = inputDirectory.file
|
||||
val output = outputDirectory.file
|
||||
|
||||
val config = config.get()
|
||||
val environment = Environment(ndkDirectory.file, cmakeDirectory.file, config.minSdkVersion)
|
||||
|
||||
val tags = listOf("without_gvisor", "without_system") +
|
||||
(if (config.debug) listOf("debug") else emptyList()) +
|
||||
(if (config.premium) listOf("premium") else emptyList())
|
||||
|
||||
Command.ofGoModuleTidy(input).exec()
|
||||
|
||||
config.abis.forEach {
|
||||
Command.ofGoRun(
|
||||
"make/make.go",
|
||||
listOf("bridge", "native", "build", "android", it.goArch),
|
||||
input.resolve("tun2socket/bridge"),
|
||||
environment.ofLwipBuild(it)
|
||||
).exec()
|
||||
|
||||
Command.ofGoBuild(
|
||||
"c-shared",
|
||||
output.resolve("${it.value}/libclash.so"),
|
||||
tags,
|
||||
!config.debug,
|
||||
input,
|
||||
environment.ofCoreBuild(it)
|
||||
).exec()
|
||||
}
|
||||
}
|
||||
|
||||
private val DirectoryProperty.file: File
|
||||
get() = get().asFile
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import java.io.File
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class Command(
|
||||
private val command: Array<String>,
|
||||
workingDir: File,
|
||||
environments: Map<String, String>
|
||||
) {
|
||||
private val processBuilder: ProcessBuilder = ProcessBuilder(*command)
|
||||
.redirectErrorStream(true)
|
||||
.directory(workingDir)
|
||||
.apply { environment().putAll(environments) }
|
||||
|
||||
fun exec() {
|
||||
val process = processBuilder.start()
|
||||
|
||||
thread {
|
||||
process.inputStream.copyTo(System.out)
|
||||
}
|
||||
|
||||
val result = process.waitFor()
|
||||
|
||||
if (result != 0) {
|
||||
throw GradleException("exec ${command.joinToString(" ")}: exit with $result")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun ofGoModuleTidy(workingDir: File): Command {
|
||||
return Command(arrayOf("go", "mod", "tidy"), workingDir, System.getenv())
|
||||
}
|
||||
|
||||
fun ofGoBuild(
|
||||
mode: String,
|
||||
output: File,
|
||||
tags: List<String>,
|
||||
strip: Boolean,
|
||||
workingDir: File,
|
||||
environments: Map<String, String>
|
||||
): Command {
|
||||
val command = mutableListOf("go", "build")
|
||||
|
||||
// go build mode
|
||||
command += "-buildmode"
|
||||
command += mode
|
||||
|
||||
// output file
|
||||
command += "-o"
|
||||
command += output.absolutePath
|
||||
|
||||
// trim path prefix
|
||||
command += "-trimpath"
|
||||
|
||||
if (tags.isNotEmpty()) {
|
||||
command += "-tags"
|
||||
command += tags.joinToString(",")
|
||||
}
|
||||
|
||||
if (strip) {
|
||||
command += "-ldflags"
|
||||
command += "-s -w"
|
||||
}
|
||||
|
||||
return Command(command.toTypedArray(), workingDir, environments)
|
||||
}
|
||||
|
||||
fun ofGoRun(
|
||||
file: String,
|
||||
args: List<String>,
|
||||
workingDir: File,
|
||||
environments: Map<String, String>
|
||||
): Command {
|
||||
val command = mutableListOf("go", "run")
|
||||
|
||||
command += file
|
||||
command += args
|
||||
|
||||
return Command(command.toTypedArray(), workingDir, environments)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import org.gradle.api.GradleException
|
||||
import java.io.File
|
||||
|
||||
class Environment(
|
||||
private val ndkDirectory: File,
|
||||
private val cmakeDirectory: File,
|
||||
private val minSdkVersion: Int,
|
||||
) {
|
||||
fun ofCoreBuild(abi: NativeAbi): Map<String, String> {
|
||||
val host = when {
|
||||
Os.isFamily(Os.FAMILY_WINDOWS) ->
|
||||
"windows"
|
||||
Os.isFamily(Os.FAMILY_MAC) ->
|
||||
"darwin"
|
||||
Os.isFamily(Os.FAMILY_UNIX) ->
|
||||
"linux"
|
||||
else ->
|
||||
throw GradleException("Unsupported host: ${System.getProperty("os.name")}")
|
||||
}
|
||||
|
||||
val compiler = ndkDirectory.resolve("toolchains/llvm/prebuilt/$host-x86_64/bin")
|
||||
.resolve("${abi.compiler}${minSdkVersion}-clang")
|
||||
|
||||
return mapOf(
|
||||
"CC" to compiler.absolutePath,
|
||||
"GOOS" to "android",
|
||||
"GOARCH" to abi.goArch,
|
||||
"GOARM" to abi.goArm,
|
||||
"CGO_ENABLED" to "1",
|
||||
"CFLAGS" to "-O3 -Werror",
|
||||
)
|
||||
}
|
||||
|
||||
fun ofLwipBuild(abi: NativeAbi): Map<String, String> {
|
||||
val path = "${cmakeDirectory.absolutePath}${File.pathSeparator}${System.getenv("PATH")}"
|
||||
|
||||
return mapOf(
|
||||
"PATH" to path,
|
||||
"CMAKE_SYSTEM_NAME" to "Android",
|
||||
"CMAKE_ANDROID_NDK" to ndkDirectory.absolutePath,
|
||||
"CMAKE_ANDROID_ARCH_ABI" to abi.value,
|
||||
"CMAKE_SYSTEM_VERSION" to minSdkVersion.toString()
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.github.kr328.clash.tools
|
||||
|
||||
enum class NativeAbi(
|
||||
val value: String,
|
||||
val compiler: String,
|
||||
val goArch: String,
|
||||
val goArm: String
|
||||
) {
|
||||
ArmeabiV7a("armeabi-v7a", "armv7a-linux-androideabi", "arm", "7"),
|
||||
Arm64V8a("arm64-v8a", "aarch64-linux-android", "arm64", ""),
|
||||
X86("x86", "i686-linux-android", "386", ""),
|
||||
X64("x86_64", "x86_64-linux-android", "amd64", "");
|
||||
|
||||
companion object {
|
||||
fun parse(value: String): NativeAbi {
|
||||
return when (value) {
|
||||
ArmeabiV7a.value -> ArmeabiV7a
|
||||
Arm64V8a.value -> Arm64V8a
|
||||
X86.value -> X86
|
||||
X64.value -> X64
|
||||
else -> throw IllegalArgumentException("unsupported abi $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
id("kotlinx-serialization")
|
||||
id("library-golang")
|
||||
id("clash-build")
|
||||
}
|
||||
|
||||
val geoipDatabaseUrl =
|
||||
|
@ -16,6 +16,7 @@ endif ()
|
||||
|
||||
include_directories("${GO_OUTPUT_BASE}/${CMAKE_ANDROID_ARCH_ABI}")
|
||||
include_directories("${GO_SOURCE}")
|
||||
include_directories("${GO_SOURCE}/tun2socket/bridge/native")
|
||||
|
||||
link_directories("${GO_OUTPUT_BASE}/${CMAKE_ANDROID_ARCH_ABI}")
|
||||
|
||||
|
@ -5,8 +5,8 @@ go 1.16
|
||||
require (
|
||||
cfa/blob v0.0.0 // local generated
|
||||
github.com/Dreamacro/clash v0.0.0 // local
|
||||
github.com/kr328/tun2socket v0.0.0 // local
|
||||
github.com/dlclark/regexp2 v1.4.0
|
||||
github.com/kr328/tun2socket v0.0.0 // local
|
||||
github.com/miekg/dns v1.1.42
|
||||
github.com/oschwald/geoip2-golang v1.5.0
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
|
Loading…
x
Reference in New Issue
Block a user