Files
Magisk/app/buildSrc/src/main/java/Setup.kt

495 lines
18 KiB
Kotlin
Raw Normal View History

2023-03-02 21:16:25 +08:00
import com.android.build.api.artifact.ArtifactTransformationRequest
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.dsl.ApkSigningConfig
2024-11-09 20:07:53 -08:00
import com.android.build.api.instrumentation.FramesComputationMode.COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS
import com.android.build.api.instrumentation.InstrumentationScope
2023-03-02 21:16:25 +08:00
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
2021-09-08 00:45:15 +08:00
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
2021-09-09 20:19:49 -07:00
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
2022-06-18 02:08:44 +08:00
import com.android.builder.internal.packaging.IncrementalPackager
import com.android.tools.build.apkzlib.sign.SigningExtension
import com.android.tools.build.apkzlib.sign.SigningOptions
import com.android.tools.build.apkzlib.zfile.ZFiles
import com.android.tools.build.apkzlib.zip.ZFileOptions
2021-09-09 20:19:49 -07:00
import org.apache.tools.ant.filters.FixCrLfFilter
2021-09-08 00:45:15 +08:00
import org.gradle.api.Action
2023-03-02 21:16:25 +08:00
import org.gradle.api.DefaultTask
2021-09-08 00:45:15 +08:00
import org.gradle.api.JavaVersion
import org.gradle.api.Project
2023-03-02 21:16:25 +08:00
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Copy
2023-03-02 20:32:13 -08:00
import org.gradle.api.tasks.Delete
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.StopExecutionException
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.TaskAction
2024-07-10 22:32:38 +08:00
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.exclude
2023-03-02 20:32:13 -08:00
import org.gradle.kotlin.dsl.filter
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.register
import org.gradle.kotlin.dsl.registering
2024-07-10 22:09:05 +08:00
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2024-07-10 22:09:05 +08:00
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.ByteArrayOutputStream
import java.io.File
import java.net.URI
2022-06-18 02:08:44 +08:00
import java.security.KeyStore
import java.security.MessageDigest
2022-06-18 02:08:44 +08:00
import java.security.cert.X509Certificate
import java.util.HexFormat
2022-06-20 02:51:58 +08:00
import java.util.jar.JarFile
2023-03-02 20:32:13 -08:00
import java.util.zip.Deflater
import java.util.zip.DeflaterOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
2021-12-14 21:30:15 +08:00
private fun Project.androidBase(configure: Action<BaseExtension>) =
extensions.configure("android", configure)
2021-09-09 20:19:49 -07:00
2021-12-14 21:30:15 +08:00
private fun Project.android(configure: Action<BaseAppModuleExtension>) =
2021-09-09 20:19:49 -07:00
extensions.configure("android", configure)
2021-09-08 00:45:15 +08:00
private val Project.androidApp: BaseAppModuleExtension
2022-10-31 22:31:15 +08:00
get() = extensions["android"] as BaseAppModuleExtension
2021-09-08 00:45:15 +08:00
private val Project.androidLib: LibraryExtension
get() = extensions["android"] as LibraryExtension
2023-03-02 21:16:25 +08:00
private val Project.androidComponents
get() = extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
2021-09-08 00:45:15 +08:00
fun Project.setupCommon() {
2021-12-14 21:30:15 +08:00
androidBase {
2025-03-23 12:25:52 +08:00
compileSdkVersion(36)
buildToolsVersion = "36.0.0"
2022-01-27 01:46:00 -08:00
ndkPath = "$sdkDirectory/ndk/magisk"
2025-05-30 00:24:24 -07:00
ndkVersion = "28.1.13356709"
2021-09-08 00:45:15 +08:00
defaultConfig {
2023-03-17 04:24:16 -07:00
minSdk = 23
2021-09-08 00:45:15 +08:00
}
compileOptions {
2024-08-24 02:46:47 +08:00
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
2021-09-08 00:45:15 +08:00
}
2022-06-03 01:13:29 -07:00
packagingOptions {
resources {
2024-07-10 22:09:05 +08:00
excludes += arrayOf(
"/META-INF/*",
2025-05-02 13:22:16 +00:00
"/META-INF/androidx/**",
2024-07-10 22:09:05 +08:00
"/META-INF/versions/**",
"/org/bouncycastle/**",
"/org/apache/commons/**",
"/kotlin/**",
"/kotlinx/**",
"/okhttp3/**",
"/*.txt",
"/*.bin",
"/*.json",
)
}
2023-02-20 01:03:35 -08:00
}
2022-05-27 00:44:20 -07:00
}
configurations.all {
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk7")
exclude("org.jetbrains.kotlin", "kotlin-stdlib-jdk8")
}
2024-07-10 22:09:05 +08:00
tasks.withType<KotlinCompile> {
compilerOptions {
2024-08-24 02:46:47 +08:00
jvmTarget = JvmTarget.JVM_21
2024-07-10 22:09:05 +08:00
}
}
}
private fun Project.downloadFile(url: String, checksum: String): File {
val file = layout.buildDirectory.file(checksum).get().asFile
if (file.exists()) {
val md = MessageDigest.getInstance("SHA-256")
file.inputStream().use { md.update(it.readAllBytes()) }
val hash = HexFormat.of().formatHex(md.digest())
if (hash != checksum) {
file.delete()
}
}
if (!file.exists()) {
file.parentFile.mkdirs()
URI(url).toURL().openStream().use { dl ->
file.outputStream().use {
dl.copyTo(it)
}
}
}
return file
}
const val BUSYBOX_DOWNLOAD_URL =
2024-10-06 01:47:13 -07:00
"https://github.com/topjohnwu/magisk-files/releases/download/files/busybox-1.36.1.1.zip"
const val BUSYBOX_ZIP_CHECKSUM =
2024-10-06 01:47:13 -07:00
"b4d0551feabaf314e53c79316c980e8f66432e9fb91a69dbbf10a93564b40951"
fun Project.setupCoreLib() {
setupCommon()
val abiList = Config.abiList
val syncLibs by tasks.registering(Sync::class) {
into("src/main/jniLibs")
for (abi in abiList) {
into(abi) {
2025-05-14 15:14:34 -07:00
from(rootFile("native/out/$abi")) {
2024-07-24 22:49:48 -07:00
include("magiskboot", "magiskinit", "magiskpolicy", "magisk", "libinit-ld.so")
rename { if (it.endsWith(".so")) it else "lib$it.so" }
}
}
}
onlyIf {
if (inputs.sourceFiles.files.size != abiList.size * 5)
throw StopExecutionException("Please build binaries first! (./build.py binary)")
true
}
}
val downloadBusybox by tasks.registering(Copy::class) {
dependsOn(syncLibs)
from(zipTree(downloadFile(BUSYBOX_DOWNLOAD_URL, BUSYBOX_ZIP_CHECKSUM)))
include(abiList.map { "$it/libbusybox.so" })
into("src/main/jniLibs")
}
val syncResources by tasks.registering(Sync::class) {
into("src/main/resources/META-INF/com/google/android")
2025-05-14 15:14:34 -07:00
from(rootFile("scripts/update_binary.sh")) {
rename { "update-binary" }
}
2025-05-14 15:14:34 -07:00
from(rootFile("scripts/flash_script.sh")) {
rename { "updater-script" }
}
}
androidLib.libraryVariants.all {
val variantCapped = name.replaceFirstChar { it.uppercase() }
tasks.getByPath("merge${variantCapped}JniLibFolders").dependsOn(downloadBusybox)
processJavaResourcesProvider.configure { dependsOn(syncResources) }
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
val stubApk = stubTask.outputs.files.asFileTree.filter {
it.name.endsWith(".apk")
}
val syncAssets = tasks.register("sync${variantCapped}Assets", Sync::class) {
dependsOn(stubTask)
inputs.property("version", Config.version)
inputs.property("versionCode", Config.versionCode)
into("src/${this@all.name}/assets")
2025-05-14 15:14:34 -07:00
from(rootFile("scripts")) {
include("util_functions.sh", "boot_patch.sh", "addon.d.sh",
"app_functions.sh", "uninstaller.sh", "module_installer.sh")
}
2025-05-14 15:14:34 -07:00
from(rootFile("tools/bootctl"))
into("chromeos") {
2025-05-14 15:14:34 -07:00
from(rootFile("tools/futility"))
from(rootFile("tools/keys")) {
include("kernel_data_key.vbprivk", "kernel.keyblock")
}
}
from(stubApk) {
rename { "stub.apk" }
}
filesMatching("**/util_functions.sh") {
filter {
it.replace(
"#MAGISK_VERSION_STUB",
"MAGISK_VER='${Config.version}'\nMAGISK_VER_CODE=${Config.versionCode}"
)
}
filter<FixCrLfFilter>("eol" to FixCrLfFilter.CrLf.newInstance("lf"))
}
}
mergeAssetsProvider.configure { dependsOn(syncAssets) }
}
tasks.named<Delete>("clean") {
delete.addAll(listOf("src/main/jniLibs", "src/main/resources", "src/debug", "src/release"))
}
2021-09-08 00:45:15 +08:00
}
2023-03-02 21:16:25 +08:00
private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
2022-06-18 02:08:44 +08:00
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
storeFile!!.inputStream().use {
keyStore.load(it, storePassword!!.toCharArray())
}
val keyPwdArray = keyPassword!!.toCharArray()
val entry = keyStore.getEntry(keyAlias!!, KeyStore.PasswordProtection(keyPwdArray))
return entry as KeyStore.PrivateKeyEntry
}
2023-03-02 21:16:25 +08:00
abstract class AddCommentTask: DefaultTask() {
@get:Input
abstract val comment: Property<String>
@get:Input
abstract val signingConfig: Property<ApkSigningConfig>
@get:InputFiles
abstract val apkFolder: DirectoryProperty
@get:OutputDirectory
abstract val outFolder: DirectoryProperty
@get:Internal
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
@TaskAction
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
val inFile = File(artifact.outputFile)
val outFile = outFolder.file(inFile.name).get().asFile
2023-03-02 20:32:13 -08:00
val privateKey = signingConfig.get().getPrivateKey()
val signingOptions = SigningOptions.builder()
.setMinSdkVersion(0)
.setV1SigningEnabled(true)
.setV2SigningEnabled(true)
.setKey(privateKey.privateKey)
.setCertificates(privateKey.certificate as X509Certificate)
.setValidation(SigningOptions.Validation.ASSUME_INVALID)
.build()
val options = ZFileOptions().apply {
noTimestamps = true
autoSortFiles = true
}
2024-07-13 00:50:24 +08:00
outFile.parentFile?.mkdirs()
2023-03-02 20:32:13 -08:00
inFile.copyTo(outFile, overwrite = true)
ZFiles.apk(outFile, options).use {
SigningExtension(signingOptions).register(it)
it.eocdComment = comment.get().toByteArray()
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
it.get(IncrementalPackager.VERSION_CONTROL_INFO_ENTRY_PATH)?.delete()
2023-03-02 20:32:13 -08:00
it.get(JarFile.MANIFEST_NAME)?.delete()
}
2023-03-02 21:16:25 +08:00
outFile
}
}
fun Project.setupAppCommon() {
2021-09-08 00:45:15 +08:00
setupCommon()
2021-09-09 20:19:49 -07:00
2021-09-08 00:45:15 +08:00
android {
signingConfigs {
2025-05-14 15:14:34 -07:00
Config["keyStore"]?.also {
create("config") {
storeFile = rootFile(it)
2021-09-08 00:45:15 +08:00
storePassword = Config["keyStorePass"]
keyAlias = Config["keyAlias"]
keyPassword = Config["keyPass"]
}
}
}
2024-03-08 17:09:54 -08:00
defaultConfig {
2025-03-23 12:25:52 +08:00
targetSdk = 36
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt")
)
2024-03-08 17:09:54 -08:00
}
2021-09-08 00:45:15 +08:00
buildTypes {
2025-05-14 15:14:34 -07:00
val config = signingConfigs.findByName("config") ?: signingConfigs["debug"]
debug {
signingConfig = config
}
release {
signingConfig = config
2021-09-08 00:45:15 +08:00
}
}
2021-12-14 21:30:15 +08:00
lint {
2021-09-08 00:45:15 +08:00
disable += "MissingTranslation"
2023-02-20 01:23:56 -08:00
checkReleaseBuilds = false
2021-09-08 00:45:15 +08:00
}
2021-12-14 21:30:15 +08:00
dependenciesInfo {
includeInApk = false
}
2023-02-20 01:03:35 -08:00
2023-04-01 01:54:10 -07:00
packaging {
jniLibs {
useLegacyPackaging = true
}
}
2021-09-08 00:45:15 +08:00
}
2022-06-18 02:08:44 +08:00
2023-03-02 21:16:25 +08:00
androidComponents.onVariants { variant ->
val commentTask = tasks.register(
"comment${variant.name.replaceFirstChar { it.uppercase() }}",
AddCommentTask::class.java
)
val transformationRequest = variant.artifacts.use(commentTask)
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
.toTransformMany(SingleArtifact.APK)
val signingConfig = androidApp.buildTypes.getByName(variant.buildType!!).signingConfig
2023-03-02 21:16:25 +08:00
commentTask.configure {
2024-07-10 22:32:38 +08:00
this.transformationRequest = transformationRequest
this.signingConfig = signingConfig
this.comment = "version=${Config.version}\n" +
2023-02-12 17:40:28 +08:00
"versionCode=${Config.versionCode}\n" +
2024-07-10 22:32:38 +08:00
"stubVersion=${Config.stubVersion}\n"
this.outFolder.set(layout.buildDirectory.dir("outputs/apk/${variant.name}"))
2022-06-18 02:08:44 +08:00
}
}
2021-09-08 00:45:15 +08:00
}
2021-09-09 20:19:49 -07:00
2024-11-09 20:07:53 -08:00
fun Project.setupMainApk() {
setupAppCommon()
android {
namespace = "com.topjohnwu.magisk"
defaultConfig {
applicationId = "com.topjohnwu.magisk"
vectorDrawables.useSupportLibrary = true
versionName = Config.version
versionCode = Config.versionCode
ndk {
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64", "riscv64")
debugSymbolLevel = "FULL"
}
}
androidComponents.onVariants { variant ->
variant.instrumentation.apply {
setAsmFramesComputationMode(COMPUTE_FRAMES_FOR_INSTRUMENTED_METHODS)
transformClassesWith(
DesugarClassVisitorFactory::class.java, InstrumentationScope.ALL) {}
}
}
}
}
fun Project.setupStubApk() {
2021-09-09 20:19:49 -07:00
setupAppCommon()
2023-03-02 21:16:25 +08:00
androidComponents.onVariants { variant ->
2023-03-02 20:32:13 -08:00
val variantName = variant.name
val variantCapped = variantName.replaceFirstChar { it.uppercase() }
2023-03-02 21:16:25 +08:00
val manifestUpdater =
2023-03-02 20:32:13 -08:00
project.tasks.register("${variantName}ManifestProducer", ManifestUpdater::class.java) {
dependsOn("generate${variantCapped}ObfuscatedClass")
2024-07-10 22:32:38 +08:00
applicationId = variant.applicationId
appClassDir.set(layout.buildDirectory.dir("generated/source/app/$variantName"))
factoryClassDir.set(layout.buildDirectory.dir("generated/source/factory/$variantName"))
2023-03-02 21:16:25 +08:00
}
variant.artifacts.use(manifestUpdater)
.wiredWithFiles(
ManifestUpdater::mergedManifest,
ManifestUpdater::outputManifest)
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
androidApp.applicationVariants.all {
2023-02-20 14:55:27 +08:00
val variantCapped = name.replaceFirstChar { it.uppercase() }
val variantLowered = name.lowercase()
2023-09-13 17:39:56 +08:00
val outFactoryClassDir = layout.buildDirectory.file("generated/source/factory/${variantLowered}").get().asFile
val outAppClassDir = layout.buildDirectory.file("generated/source/app/${variantLowered}").get().asFile
val outResDir = layout.buildDirectory.dir("generated/source/res/${variantLowered}").get().asFile
val aapt = File(androidApp.sdkDirectory, "build-tools/${androidApp.buildToolsVersion}/aapt2")
2024-09-04 22:37:23 +08:00
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"${variantLowered}/process${variantCapped}Resources/linked-resources-binary-format-${variantLowered}.ap_").get().asFile
2021-09-09 20:19:49 -07:00
2023-03-02 21:16:25 +08:00
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
2023-02-20 01:23:56 -08:00
inputs.property("seed", RAND_SEED)
2023-03-02 21:16:25 +08:00
outputs.dirs(outFactoryClassDir, outAppClassDir)
2021-09-09 20:19:49 -07:00
doLast {
2023-03-02 21:16:25 +08:00
outFactoryClassDir.mkdirs()
outAppClassDir.mkdirs()
2023-03-02 20:32:13 -08:00
genStubClasses(outFactoryClassDir, outAppClassDir)
2021-12-14 21:30:15 +08:00
}
}
2023-03-02 21:16:25 +08:00
registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
2021-09-09 20:19:49 -07:00
2023-03-02 20:32:13 -08:00
val processResourcesTask = tasks.named("process${variantCapped}Resources") {
2023-03-03 15:50:25 +08:00
outputs.dir(outResDir)
2023-03-02 20:32:13 -08:00
doLast {
2023-03-03 15:50:25 +08:00
val apkTmp = File("${apk}.tmp")
2023-03-02 20:32:13 -08:00
exec {
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
2023-02-20 01:23:56 -08:00
}
2023-03-02 20:32:13 -08:00
val bos = ByteArrayOutputStream()
ZipFile(apkTmp).use { src ->
ZipOutputStream(apk.outputStream()).use {
it.setLevel(Deflater.BEST_COMPRESSION)
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
it.closeEntry()
}
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
}
2021-09-09 20:19:49 -07:00
}
2023-03-02 20:32:13 -08:00
apkTmp.delete()
genEncryptedResources(bos.toByteArray(), outResDir)
2021-09-09 20:19:49 -07:00
}
}
2023-03-02 20:32:13 -08:00
2023-02-20 01:23:56 -08:00
registerJavaGeneratingTask(processResourcesTask, outResDir)
2021-09-09 20:19:49 -07:00
}
2021-12-14 21:30:15 +08:00
// Override optimizeReleaseResources task
2024-09-04 22:37:23 +08:00
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
2023-09-13 17:39:56 +08:00
val optRes = layout.buildDirectory.file("intermediates/optimized_processed_res/" +
2024-03-21 14:10:28 +08:00
"release/optimizeReleaseResources/resources-release-optimize.ap_").get().asFile
2023-03-31 11:07:17 +08:00
afterEvaluate {
tasks.named("optimizeReleaseResources") {
2021-12-14 21:30:15 +08:00
doLast { apk.copyTo(optRes, true) }
}
}
2022-01-09 02:22:34 +08:00
tasks.named<Delete>("clean") {
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
}
2021-09-09 20:19:49 -07:00
}
const val LSPOSED_DOWNLOAD_URL =
"https://github.com/LSPosed/LSPosed/releases/download/v1.9.2/LSPosed-v1.9.2-7024-zygisk-release.zip"
const val LSPOSED_CHECKSUM =
"0ebc6bcb465d1c4b44b7220ab5f0252e6b4eb7fe43da74650476d2798bb29622"
2025-02-12 22:24:33 +08:00
const val SHAMIKO_DOWNLOAD_URL =
"https://github.com/LSPosed/LSPosed.github.io/releases/download/shamiko-383/Shamiko-v1.2.1-383-release.zip"
const val SHAMIKO_CHECKSUM =
"93754a038c2d8f0e985bad45c7303b96f70a93d8335060e50146f028d3a9b13f"
fun Project.setupTestApk() {
setupAppCommon()
androidApp.applicationVariants.all {
val variantCapped = name.replaceFirstChar { it.uppercase() }
val dlTask by tasks.register("download${variantCapped}Lsposed", Sync::class) {
from(downloadFile(LSPOSED_DOWNLOAD_URL, LSPOSED_CHECKSUM)) {
rename { "lsposed.zip" }
}
2025-02-12 22:24:33 +08:00
from(downloadFile(SHAMIKO_DOWNLOAD_URL, SHAMIKO_CHECKSUM)) {
rename { "shamiko.zip" }
}
into("src/${this@all.name}/assets")
}
mergeAssetsProvider.configure { dependsOn(dlTask) }
}
}