Skip to content
Snippets Groups Projects
Unverified Commit 11c4e19e authored by Kr328's avatar Kr328 Committed by GitHub
Browse files

Refactor: refactor project structure & update clash core (#1174)

* Refactor: refactor project structure

* Chore: remove apply

* Chore: update clash core

* Fix: fix versionName & versionCode patch

* Chore: update golang plugin

* Fix: fix workflow build
parent fa23be76
No related branches found
No related tags found
No related merge requests found
Showing
with 173 additions and 632 deletions
name: Build Unsigned
on:
workflow_dispatch:
push:
branches:
- main
paths-ignore:
- '.github/**'
- '.idea/**'
......@@ -13,8 +10,6 @@ on:
- 'LICENSE'
- 'NOTICE'
pull_request:
branches:
- main
paths-ignore:
- '.github/**'
- '.idea/**'
......@@ -38,10 +33,8 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.16
go-version: 1.17
- name: Setup Android SDK
uses: android-actions/setup-android@v2
- name: Setup Cmake & Ninja
uses: lukka/get-cmake@latest
- name: Build
run: ./gradlew --no-daemon app:assembleRelease
\ No newline at end of file
run: ./gradlew --no-daemon app:assembleFossRelease
\ No newline at end of file
......@@ -19,9 +19,11 @@ gradle-app.setting
# Ignore IDEA config
*.iml
/.idea/*
/core/src/main/golang/.idea/*
!/.idea/codeStyles
!/core/src/main/golang/.idea/codeStyles
/core/src/foss/go/.idea/*
!/core/src/foss/go/.idea/codeStyles
/core/src/premium/go/.idea/*
!/core/src/premium/go/.idea/codeStyles
# KeyStore
*.keystore
......
[submodule "core/src/main/golang/clash"]
path = core/src/main/golang/clash
[submodule "clash-foss"]
path = core/src/foss/go/clash
url = https://github.com/Kr328/clash.git
[submodule "kaidl"]
path = kaidl
url = https://github.com/Kr328/kaidl.git
[submodule "core/src/main/golang/tun2socket"]
path = core/src/main/golang/tun2socket
url = https://github.com/Kr328/tun2socket-lwip.git
import java.util.*
plugins {
id("com.android.application")
kotlin("android")
kotlin("kapt")
}
android {
compileSdk = buildTargetSdkVersion
flavorDimensions(buildFlavor)
defaultConfig {
applicationId = "com.github.kr328.clash"
minSdk = buildMinSdkVersion
targetSdk = buildTargetSdkVersion
versionCode = buildVersionCode
versionName = buildVersionName
resConfigs("zh-rCN", "zh-rHK", "zh-rTW")
resValue("string", "release_name", "v$buildVersionName")
resValue("integer", "release_code", "$buildVersionCode")
}
packagingOptions {
exclude("DebugProbesKt.bin")
}
buildTypes {
named("release") {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
productFlavors {
create("foss") {
dimension = "foss"
versionNameSuffix = ".foss"
applicationIdSuffix = ".foss"
}
create("premium") {
dimension = "premium"
versionNameSuffix = ".premium"
if (buildFlavor == "premium") {
val localFile = rootProject.file("local.properties")
if (localFile.exists()) {
val appCenterKey = localFile.inputStream()
.use { Properties().apply { load(it) } }
.getProperty("appcenter.key", null)
if (appCenterKey != null) {
buildConfigField("String", "APP_CENTER_KEY", "\"$appCenterKey\"")
} else {
buildConfigField("String", "APP_CENTER_KEY", "null")
}
} else {
buildConfigField("String", "APP_CENTER_KEY", "null")
}
}
}
}
val signingFile = rootProject.file("keystore.properties")
if (signingFile.exists()) {
val properties = Properties().apply {
signingFile.inputStream().use {
load(it)
}
}
signingConfigs {
create("release") {
storeFile = rootProject.file(properties.getProperty("storeFile")!!)
storePassword = properties.getProperty("storePassword")!!
keyAlias = properties.getProperty("keyAlias")!!
keyPassword = properties.getProperty("keyPassword")!!
}
}
buildTypes {
named("release") {
signingConfig = signingConfigs["release"]
}
}
}
buildFeatures {
dataBinding = true
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
splits {
abi {
isEnable = true
isUniversalApk = true
}
}
id("com.android.application")
}
dependencies {
val premiumImplementation by configurations
implementation(project(":core"))
implementation(project(":service"))
implementation(project(":design"))
implementation(project(":common"))
api(project(":core"))
api(project(":service"))
api(project(":design"))
api(project(":common"))
implementation(kotlin("stdlib-jdk7"))
implementation(deps.kotlin.coroutine)
implementation(deps.androidx.core)
implementation(deps.androidx.activity)
implementation(deps.androidx.fragment)
implementation(deps.androidx.appcompat)
implementation(deps.androidx.coordinator)
implementation(deps.androidx.recyclerview)
implementation(deps.google.material)
premiumImplementation("com.microsoft.appcenter:appcenter-analytics:$appcenterVersion")
premiumImplementation("com.microsoft.appcenter:appcenter-crashes:$appcenterVersion")
val premiumImplementation by configurations
implementation(kotlin("stdlib-jdk7"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion")
implementation("androidx.core:core-ktx:$coreVersion")
implementation("androidx.activity:activity:$activityVersion")
implementation("androidx.appcompat:appcompat:$appcompatVersion")
implementation("androidx.coordinatorlayout:coordinatorlayout:$coordinatorlayoutVersion")
implementation("androidx.recyclerview:recyclerview:$recyclerviewVersion")
implementation("androidx.fragment:fragment:$fragmentVersion")
implementation("com.google.android.material:material:$materialVersion")
premiumImplementation(deps.appcenter.analytics)
premiumImplementation(deps.appcenter.crashes)
}
task("cleanRelease", type = Delete::class) {
tasks.getByName("clean", type = Delete::class) {
delete(file("release"))
}
afterEvaluate {
tasks["clean"].dependsOn(tasks["cleanRelease"])
}
\ No newline at end of file
@file:Suppress("UNUSED_VARIABLE")
import com.android.build.gradle.BaseExtension
import java.net.URL
buildscript {
repositories {
mavenCentral()
mavenLocal()
google()
maven("https://maven.kr328.app/releases")
}
dependencies {
classpath(deps.build.android)
classpath(deps.build.kotlin.common)
classpath(deps.build.kotlin.serialization)
classpath(deps.build.ksp)
classpath(deps.build.golang)
}
}
allprojects {
repositories {
google()
mavenCentral()
maven("https://maven.kr328.app/releases")
}
}
subprojects {
val isApp = name == "app"
apply(plugin = if (isApp) "com.android.application" else "com.android.library")
extensions.configure(BaseExtension::class) {
val minSdkVersion = 21
val targetSdkVersion = 30
val buildVersionCode = 204009
val buildVersionName = "2.4.9"
val defaultDimension = "feature"
ndkVersion = "23.0.7599858"
compileSdkVersion(targetSdkVersion)
defaultConfig {
if (isApp) {
applicationId = "com.github.kr328.clash"
}
minSdk = minSdkVersion
targetSdk = targetSdkVersion
versionName = buildVersionName
versionCode = buildVersionCode
if (!isApp) {
consumerProguardFiles("consumer-rules.pro")
}
resValue("string", "release_name", "v$buildVersionName")
resValue("integer", "release_code", "$buildVersionCode")
externalNativeBuild {
cmake {
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
}
}
}
externalNativeBuild {
cmake {
version = "3.18.1"
}
}
if (isApp) {
packagingOptions {
excludes.add("DebugProbesKt.bin")
}
}
buildTypes {
named("release") {
isMinifyEnabled = isApp
isShrinkResources = isApp
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
productFlavors {
flavorDimensions(defaultDimension)
create("foss") {
dimension = defaultDimension
versionNameSuffix = ".foss"
if (isApp) {
applicationIdSuffix = ".foss"
}
}
create("premium") {
dimension = defaultDimension
versionNameSuffix = ".premium"
}
}
buildFeatures.apply {
dataBinding {
isEnabled = name != "hideapi"
}
}
if (isApp) {
splits {
abi {
isEnable = true
isUniversalApk = true
}
}
}
}
}
......
plugins {
kotlin("jvm") version "1.5.10"
`java-gradle-plugin`
}
repositories {
mavenCentral()
google()
}
dependencies {
implementation(kotlin("gradle-plugin"))
implementation(kotlin("serialization"))
implementation("com.android.tools.build:gradle:4.2.1")
implementation("com.google.devtools.ksp:symbol-processing-gradle-plugin:1.5.10-1.0.0-beta01")
}
gradlePlugin {
plugins {
create("golang") {
id = "clash-build"
implementationClass = "com.github.kr328.clash.tools.ClashBuildPlugin"
}
}
}
import org.gradle.api.Project
const val buildVersionCode = 204009
const val buildVersionName = "2.4.9"
const val buildMinSdkVersion = 21
const val buildTargetSdkVersion = 30
const val buildNdkVersion = "22.1.7171670"
val Project.buildFlavor: String
get() {
return if (project(":core").file("src/main/golang/clash/script/script.go").exists())
"premium"
else
"foss"
}
\ No newline at end of file
const val activityVersion = "1.2.3"
const val coroutineVersion = "1.5.0"
const val roomVersion = "2.3.0"
const val coreVersion = "1.5.0"
const val appcompatVersion = "1.3.0"
const val muiltprocessVersion = "1.0.0"
const val appcenterVersion = "4.2.0"
const val serializationVersion = "1.2.1"
const val materialVersion = "1.3.0"
const val coordinatorlayoutVersion = "1.1.0"
const val recyclerviewVersion = "1.2.1"
const val fragmentVersion = "1.3.4"
const val viewpagerVersion = "1.0.0"
import org.gradle.api.Project
import java.io.File
val Project.golangSource: File
get() = file("src/main/golang")
val Project.golangBuild: File
get() = buildDir.resolve("intermediates/golang")
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(abis: List<NativeAbi>, minSdkVersion: Int, variant: BaseVariant): BuildConfig {
return BuildConfig(
debug = variant.buildType.isDebuggable,
premium = variant.flavorName == "premium",
abis = abis,
minSdkVersion = minSdkVersion
)
}
}
}
package com.github.kr328.clash.tools
import com.android.build.gradle.LibraryExtension
import golangBuild
import golangSource
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.util.*
class ClashBuildPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.afterEvaluate {
target.extensions.getByType(LibraryExtension::class.java).apply {
val abis = defaultConfig.externalNativeBuild.cmake.abiFilters
.map { NativeAbi.parse(it) }
.distinct()
val minSdkVersion = defaultConfig.minSdkVersion!!.apiLevel
target.tasks.register("cleanGolang", ClashCleanTask::class.java) {
it.applyFrom(target, abis)
target.tasks.getByName("clean").dependsOn(it)
}
libraryVariants.forEach { variant ->
val config = BuildConfig.of(abis, minSdkVersion, 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.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)
}
}
}
}
}
}
\ No newline at end of file
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 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, 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("tun2socket", ".", "android", it.goArch),
input.resolve("tun2socket"),
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
}
\ No newline at end of file
package com.github.kr328.clash.tools
import org.gradle.api.Project
import org.gradle.api.tasks.Delete
import golangSource
abstract class ClashCleanTask : Delete() {
fun applyFrom(project: Project, abis: List<NativeAbi>) {
val bridge = project.golangSource.resolve("tun2socket")
delete(bridge.resolve("build"))
abis.forEach {
delete(bridge.resolve("build_android_${it.goArch}.go"))
}
}
}
\ No newline at end of file
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)
}
}
}
\ No newline at end of file
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 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 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")
val ar = ndkDirectory.resolve("toolchains/llvm/prebuilt/$host-x86_64/bin")
.resolve("${abi.archiver}-ar")
return mapOf(
"CC" to compiler.absolutePath,
"AR" to ar.absolutePath,
)
}
}
\ No newline at end of file
package com.github.kr328.clash.tools
enum class NativeAbi(
val value: String,
val compiler: String,
val archiver: String,
val goArch: String,
val goArm: String
) {
ArmeabiV7a("armeabi-v7a", "armv7a-linux-androideabi", "arm-linux-androideabi", "arm", "7"),
Arm64V8a("arm64-v8a", "aarch64-linux-android", "aarch64-linux-android", "arm64", ""),
X86("x86", "i686-linux-android", "i686-linux-android", "386", ""),
X64("x86_64", "x86_64-linux-android", "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")
}
}
}
}
plugins {
id("com.android.library")
kotlin("android")
}
android {
compileSdk = buildTargetSdkVersion
defaultConfig {
minSdk = buildMinSdkVersion
targetSdk = buildTargetSdkVersion
versionCode = buildVersionCode
versionName = buildVersionName
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
named("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
id("com.android.library")
}
dependencies {
compileOnly(project(":hideapi"))
implementation(kotlin("stdlib-jdk7"))
implementation("androidx.core:core-ktx:$coreVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion")
}
repositories {
mavenCentral()
google()
implementation(deps.kotlin.coroutine)
implementation(deps.androidx.core)
}
import com.github.kr328.golang.GolangPlugin
import java.io.FileOutputStream
import java.net.URL
import java.time.Duration
plugins {
id("com.android.library")
kotlin("android")
id("com.android.library")
id("kotlinx-serialization")
id("clash-build")
id("golang-android")
}
val geoipDatabaseUrl =
......@@ -14,60 +15,31 @@ val geoipDatabaseUrl =
val geoipInvalidate = Duration.ofDays(7)!!
val geoipOutput = buildDir.resolve("intermediates/golang_blob")
android {
compileSdk = buildTargetSdkVersion
ndkVersion = buildNdkVersion
flavorDimensions(buildFlavor)
defaultConfig {
minSdk = buildMinSdkVersion
targetSdk = buildTargetSdkVersion
versionCode = buildVersionCode
versionName = buildVersionName
consumerProguardFiles("consumer-rules.pro")
externalNativeBuild {
cmake {
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
arguments(
"-DGO_SOURCE:STRING=$golangSource",
"-DGO_OUTPUT:STRING=$golangBuild",
"-DFLAVOR_NAME=$buildFlavor",
)
}
}
}
buildTypes {
named("release") {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
productFlavors {
golang {
sourceSets {
create("foss") {
dimension = "foss"
srcDir.set(file("src/foss/go"))
}
create("premium") {
dimension = "premium"
srcDir.set(file("src/premium/go"))
}
all {
fileName.set("libclash.so")
}
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
android {
productFlavors {
all {
externalNativeBuild {
cmake {
arguments("-DGO_SOURCE:STRING=${golang.sourceSets.getByName(name).srcDir.asFile.get()}")
arguments("-DGO_OUTPUT:STRING=${GolangPlugin.outputDirOf(project, null, null)}")
arguments("-DFLAVOR_NAME:STRING=$name")
}
}
}
}
externalNativeBuild {
......@@ -78,12 +50,12 @@ android {
}
dependencies {
api(project(":common"))
implementation(project(":common"))
implementation(kotlin("stdlib-jdk7"))
implementation("androidx.core:core-ktx:$coreVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
implementation(deps.androidx.core)
implementation(deps.kotlin.coroutine)
implementation(deps.kotlin.serialization.json)
}
repositories {
......@@ -108,6 +80,8 @@ task("downloadGeoipDatabase") {
var GeoipDatabase []byte
""".trimIndent()
outputs.dir(geoipOutput)
onlyIf {
System.currentTimeMillis() - databaseFile.lastModified() > geoipInvalidate.toMillis()
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment