Fix build script

This commit is contained in:
LoveSy 2023-03-02 21:16:25 +08:00 committed by John Wu
parent ca31412c05
commit 307cf87215
4 changed files with 162 additions and 84 deletions

View File

@ -1,3 +1,7 @@
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@ -72,23 +76,41 @@ fun genKeyData(keysDir: File, outSrc: File) {
} }
} }
fun genStubManifest(srcDir: File, outDir: File): String { @CacheableTask
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level)) abstract class ManifestUpdater: DefaultTask() {
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val mergedManifest: RegularFileProperty
val cmpList = mutableListOf<String>() @get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val factoryClassDir: DirectoryProperty
cmpList.add( @get:InputFiles
""" @get:PathSensitive(PathSensitivity.RELATIVE)
abstract val appClassDir: DirectoryProperty
@get:OutputFile
abstract val outputManifest: RegularFileProperty
@TaskAction
fun taskAction() {
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
val cmpList = mutableListOf<String>()
cmpList.add(
"""
|<provider |<provider
| android:name="x.COMPONENT_PLACEHOLDER_0" | android:name="x.COMPONENT_PLACEHOLDER_0"
| android:authorities="${'$'}{applicationId}.provider" | android:authorities="${'$'}{applicationId}.provider"
| android:directBootAware="true" | android:directBootAware="true"
| android:exported="false" | android:exported="false"
| android:grantUriPermissions="true" />""".ind(2) | android:grantUriPermissions="true" />""".ind(2)
) )
cmpList.add( cmpList.add(
""" """
|<receiver |<receiver
| android:name="x.COMPONENT_PLACEHOLDER_1" | android:name="x.COMPONENT_PLACEHOLDER_1"
| android:exported="false"> | android:exported="false">
@ -104,10 +126,10 @@ fun genStubManifest(srcDir: File, outDir: File): String {
| <data android:scheme="package" /> | <data android:scheme="package" />
| </intent-filter> | </intent-filter>
|</receiver>""".ind(2) |</receiver>""".ind(2)
) )
cmpList.add( cmpList.add(
""" """
|<activity |<activity
| android:name="x.COMPONENT_PLACEHOLDER_2" | android:name="x.COMPONENT_PLACEHOLDER_2"
| android:exported="true"> | android:exported="true">
@ -116,37 +138,55 @@ fun genStubManifest(srcDir: File, outDir: File): String {
| <category android:name="android.intent.category.LAUNCHER" /> | <category android:name="android.intent.category.LAUNCHER" />
| </intent-filter> | </intent-filter>
|</activity>""".ind(2) |</activity>""".ind(2)
) )
cmpList.add( cmpList.add(
""" """
|<activity |<activity
| android:name="x.COMPONENT_PLACEHOLDER_3" | android:name="x.COMPONENT_PLACEHOLDER_3"
| android:directBootAware="true" | android:directBootAware="true"
| android:exported="false" | android:exported="false"
| android:taskAffinity="" | android:taskAffinity="">
| tools:ignore="AppLinkUrlError">
| <intent-filter> | <intent-filter>
| <action android:name="android.intent.action.VIEW"/> | <action android:name="android.intent.action.VIEW"/>
| <category android:name="android.intent.category.DEFAULT"/> | <category android:name="android.intent.category.DEFAULT"/>
| </intent-filter> | </intent-filter>
|</activity>""".ind(2) |</activity>""".ind(2)
) )
cmpList.add( cmpList.add(
""" """
|<service |<service
| android:name="x.COMPONENT_PLACEHOLDER_4" | android:name="x.COMPONENT_PLACEHOLDER_4"
| android:exported="false" />""".ind(2) | android:exported="false" />""".ind(2)
) )
cmpList.add( cmpList.add(
""" """
|<service |<service
| android:name="x.COMPONENT_PLACEHOLDER_5" | android:name="x.COMPONENT_PLACEHOLDER_5"
| android:exported="false" | android:exported="false"
| android:permission="android.permission.BIND_JOB_SERVICE" />""".ind(2) | android:permission="android.permission.BIND_JOB_SERVICE" />""".ind(2)
) )
// Shuffle the order of the components
cmpList.shuffle(RANDOM)
val (factoryPkg, factoryClass) = factoryClassDir.asFileTree.first().let {
it.parentFile.name to it.name.removeSuffix(".java")
}
val (appPkg, appClass) = appClassDir.asFileTree.first().let {
it.parentFile.name to it.name.removeSuffix(".java")
}
var manifest = mergedManifest.asFile.get().readText()
manifest = manifest.replace("<application", """<application android:appComponentFactory="$factoryPkg.$factoryClass" android:name="$appPkg.$appClass"""")
manifest = manifest.replace("</application", "${cmpList.joinToString("\n\n")}\n</application")
outputManifest.get().asFile.writeText(manifest)
}
}
fun genStubClass(factoryOutDir: File, appOutDir: File) {
fun String.ind(level: Int) = replaceIndentByMargin(" ".repeat(level))
val classNameGenerator = sequence { val classNameGenerator = sequence {
fun notJavaKeyword(name: String) = when (name) { fun notJavaKeyword(name: String) = when (name) {
@ -176,7 +216,7 @@ fun genStubManifest(srcDir: File, outDir: File): String {
} }
}.distinct().iterator() }.distinct().iterator()
fun genClass(type: String): String { fun genClass(type: String, outDir: File) {
val clzName = classNameGenerator.next() val clzName = classNameGenerator.next()
val (pkg, name) = clzName.split('.') val (pkg, name) = clzName.split('.')
val pkgDir = File(outDir, pkg) val pkgDir = File(outDir, pkg)
@ -185,18 +225,10 @@ fun genStubManifest(srcDir: File, outDir: File): String {
it.println("package $pkg;") it.println("package $pkg;")
it.println("public class $name extends com.topjohnwu.magisk.$type {}") it.println("public class $name extends com.topjohnwu.magisk.$type {}")
} }
return clzName
} }
// Generate 2 non redirect-able classes genClass("DelegateComponentFactory", factoryOutDir)
val factory = genClass("DelegateComponentFactory") genClass("DelegateApplication", appOutDir)
val app = genClass("DelegateApplication")
// Shuffle the order of the components
cmpList.shuffle(RANDOM)
val xml = File(srcDir, "AndroidManifest.xml").readText()
return xml.format(factory, app, cmpList.joinToString("\n\n"))
} }
fun genEncryptedResources(res: InputStream, outDir: File) { fun genEncryptedResources(res: InputStream, outDir: File) {

View File

@ -1,26 +1,29 @@
import com.android.build.api.artifact.ArtifactTransformationRequest
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.dsl.ApkSigningConfig
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
import com.android.build.gradle.BaseExtension import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.android.builder.internal.packaging.IncrementalPackager import com.android.builder.internal.packaging.IncrementalPackager
import com.android.builder.model.SigningConfig
import com.android.tools.build.apkzlib.sign.SigningExtension import com.android.tools.build.apkzlib.sign.SigningExtension
import com.android.tools.build.apkzlib.sign.SigningOptions import com.android.tools.build.apkzlib.sign.SigningOptions
import com.android.tools.build.apkzlib.zfile.ZFiles import com.android.tools.build.apkzlib.zfile.ZFiles
import com.android.tools.build.apkzlib.zip.ZFileOptions import com.android.tools.build.apkzlib.zip.ZFileOptions
import org.apache.tools.ant.filters.FixCrLfFilter import org.apache.tools.ant.filters.FixCrLfFilter
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.JavaVersion import org.gradle.api.JavaVersion
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.plugins.ExtensionAware import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.tasks.Delete import org.gradle.api.provider.Property
import org.gradle.api.tasks.StopExecutionException import org.gradle.api.tasks.*
import org.gradle.api.tasks.Sync
import org.gradle.kotlin.dsl.* import org.gradle.kotlin.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.io.PrintStream
import java.security.KeyStore import java.security.KeyStore
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
@ -46,6 +49,9 @@ private fun BaseExtension.kotlin(configure: Action<KotlinAndroidProjectExtension
private val Project.android: BaseAppModuleExtension private val Project.android: BaseAppModuleExtension
get() = extensions["android"] as BaseAppModuleExtension get() = extensions["android"] as BaseAppModuleExtension
private val Project.androidComponents
get() = extensions.getByType(ApplicationAndroidComponentsExtension::class.java)
fun Project.setupCommon() { fun Project.setupCommon() {
androidBase { androidBase {
compileSdkVersion(33) compileSdkVersion(33)
@ -72,7 +78,7 @@ fun Project.setupCommon() {
} }
} }
private fun SigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry { private fun ApkSigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType()) val keyStore = KeyStore.getInstance(storeType ?: KeyStore.getDefaultType())
storeFile!!.inputStream().use { storeFile!!.inputStream().use {
keyStore.load(it, storePassword!!.toCharArray()) keyStore.load(it, storePassword!!.toCharArray())
@ -82,7 +88,7 @@ private fun SigningConfig.getPrivateKey(): KeyStore.PrivateKeyEntry {
return entry as KeyStore.PrivateKeyEntry return entry as KeyStore.PrivateKeyEntry
} }
private fun addComment(apkPath: File, signConfig: SigningConfig, minSdk: Int, eocdComment: String) { private fun addComment(inFile: File, outFile: File, signConfig: ApkSigningConfig, minSdk: Int, eocdComment: String) {
val privateKey = signConfig.getPrivateKey() val privateKey = signConfig.getPrivateKey()
val signingOptions = SigningOptions.builder() val signingOptions = SigningOptions.builder()
.setMinSdkVersion(minSdk) .setMinSdkVersion(minSdk)
@ -96,7 +102,9 @@ private fun addComment(apkPath: File, signConfig: SigningConfig, minSdk: Int, eo
noTimestamps = true noTimestamps = true
autoSortFiles = true autoSortFiles = true
} }
ZFiles.apk(apkPath, options).use { outFile.parentFile.mkdirs()
inFile.copyTo(outFile, overwrite = true)
ZFiles.apk(outFile, options).use {
SigningExtension(signingOptions).register(it) SigningExtension(signingOptions).register(it)
it.eocdComment = eocdComment.toByteArray() it.eocdComment = eocdComment.toByteArray()
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete() it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
@ -104,6 +112,31 @@ private fun addComment(apkPath: File, signConfig: SigningConfig, minSdk: Int, eo
} }
} }
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
addComment(inFile, outFile, signingConfig.get(), 0, comment.get())
outFile
}
}
private fun Project.setupAppCommon() { private fun Project.setupAppCommon() {
setupCommon() setupCommon()
@ -146,15 +179,22 @@ private fun Project.setupAppCommon() {
} }
} }
android.applicationVariants.all { androidComponents.onVariants { variant ->
val projectName = project.name.lowercase() val commentTask = tasks.register(
val variantCapped = name.replaceFirstChar { it.uppercase() } "comment${variant.name.replaceFirstChar { it.uppercase() }}",
tasks.getByPath(":$projectName:package$variantCapped").doLast { AddCommentTask::class.java
val apk = outputs.files.asFileTree.filter { it.name.endsWith(".apk") }.singleFile )
val comment = "version=${Config.version}\n" + val transformationRequest = variant.artifacts.use(commentTask)
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
.toTransformMany(SingleArtifact.APK)
val signingConfig = android.buildTypes.getByName(variant.buildType!!).signingConfig
commentTask.configure {
this.transformationRequest.set(transformationRequest)
this.signingConfig.set(signingConfig)
this.comment.set("version=${Config.version}\n" +
"versionCode=${Config.versionCode}\n" + "versionCode=${Config.versionCode}\n" +
"stubVersion=${Config.stubVersion}\n" "stubVersion=${Config.stubVersion}\n")
addComment(apk, signingConfig, android.defaultConfig.minSdk!!, comment) this.outFolder.set(File(buildDir, "outputs/apk/${variant.name}"))
} }
} }
} }
@ -265,33 +305,45 @@ fun Project.setupApp() {
fun Project.setupStub() { fun Project.setupStub() {
setupAppCommon() setupAppCommon()
androidComponents.onVariants { variant ->
val manifestUpdater =
project.tasks.register(
"${variant.name}ManifestProducer",
ManifestUpdater::class.java
) {
dependsOn("generate${variant.name.replaceFirstChar { it.uppercase() }}ObfuscatedClass")
appClassDir.set(File(buildDir, "generated/source/app/${variant.name}"))
factoryClassDir.set(File(buildDir, "generated/source/factory/${variant.name}"))
}
variant.artifacts.use(manifestUpdater)
.wiredWithFiles(
ManifestUpdater::mergedManifest,
ManifestUpdater::outputManifest)
.toTransform(SingleArtifact.MERGED_MANIFEST)
}
android.applicationVariants.all { android.applicationVariants.all {
val variantCapped = name.replaceFirstChar { it.uppercase() } val variantCapped = name.replaceFirstChar { it.uppercase() }
val variantLowered = name.lowercase() val variantLowered = name.lowercase()
val manifest = file("src/${variantLowered}/AndroidManifest.xml") val outFactoryClassDir = File(buildDir, "generated/source/factory/${variantLowered}")
val outManifestDir = File(buildDir, "generated/source/manifest/${variantLowered}") val outAppClassDir = File(buildDir, "generated/source/app/${variantLowered}")
val outResDir = File(buildDir, "generated/source/res/${variantLowered}") val outResDir = File(buildDir, "generated/source/res/${variantLowered}")
val templateDir = file("template")
val aapt = File(android.sdkDirectory, "build-tools/${android.buildToolsVersion}/aapt2") val aapt = File(android.sdkDirectory, "build-tools/${android.buildToolsVersion}/aapt2")
val apk = File(buildDir, "intermediates/processed_res/" + val apk = File(buildDir, "intermediates/processed_res/" +
"${variantLowered}/out/resources-${variantLowered}.ap_") "${variantLowered}/out/resources-${variantLowered}.ap_")
val apkTmp = File("${apk}.tmp") val apkTmp = File("${apk}.tmp")
val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedManifest") { val genManifestTask = tasks.register("generate${variantCapped}ObfuscatedClass") {
inputs.property("seed", RAND_SEED) inputs.property("seed", RAND_SEED)
inputs.dir(templateDir) outputs.dirs(outFactoryClassDir, outAppClassDir)
outputs.dir(outManifestDir)
outputs.file(manifest)
doLast { doLast {
val xml = genStubManifest(templateDir, outManifestDir) outFactoryClassDir.mkdirs()
manifest.parentFile.mkdirs() outAppClassDir.mkdirs()
PrintStream(manifest).use { genStubClass(outFactoryClassDir, outAppClassDir)
it.print(xml)
}
} }
} }
preBuildProvider.configure { dependsOn(genManifestTask) } registerJavaGeneratingTask(genManifestTask, outFactoryClassDir, outAppClassDir)
registerJavaGeneratingTask(genManifestTask, outManifestDir)
val processResourcesTask = tasks.getByPath(":stub:process${variantCapped}Resources") val processResourcesTask = tasks.getByPath(":stub:process${variantCapped}Resources")
processResourcesTask.doLast { processResourcesTask.doLast {

View File

@ -1 +1,14 @@
<manifest/> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<application
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon,UnusedAttribute">
</application>
</manifest>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<application
android:appComponentFactory="%s"
android:name="%s"
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon,UnusedAttribute">
%s
</application>
</manifest>