build: Refactor to DSL to bump ReVanced Patcher

This commit is contained in:
oSumAtrIX 2024-06-16 16:02:39 +02:00
parent 40279e0369
commit 51773f30bc
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
6 changed files with 111 additions and 55 deletions

View File

@ -3,7 +3,7 @@ package app.revanced.cli.command
import app.revanced.library.PackageName
import app.revanced.library.PatchUtils
import app.revanced.library.VersionMap
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.patch.loadPatchesFromJar
import picocli.CommandLine
import java.io.File
import java.util.logging.Logger
@ -22,7 +22,7 @@ internal class ListCompatibleVersions : Runnable {
description = ["Paths to patch bundles."],
arity = "1..*",
)
private lateinit var patchBundles: Array<File>
private lateinit var patchBundles: Set<File>
@CommandLine.Option(
names = ["-f", "--filter-package-names"],
@ -38,8 +38,6 @@ internal class ListCompatibleVersions : Runnable {
private var countUnusedPatches: Boolean = false
override fun run() {
val patches = PatchBundleLoader.Jar(*patchBundles)
fun VersionMap.buildVersionsString(): String {
if (isEmpty()) return "Any"
@ -58,6 +56,8 @@ internal class ListCompatibleVersions : Runnable {
appendLine(versions.buildVersionsString().prependIndent("\t"))
}
val patches = loadPatchesFromJar(patchBundles)
PatchUtils.getMostCommonCompatibleVersions(
patches,
packageNames,

View File

@ -1,12 +1,13 @@
package app.revanced.cli.command
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.patch.Package
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.options.PatchOption
import app.revanced.patcher.patch.loadPatchesFromJar
import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.io.File
import java.util.logging.Logger
import app.revanced.patcher.patch.Option as PatchOption
@Command(
name = "list-patches",
@ -19,7 +20,7 @@ internal object ListPatchesCommand : Runnable {
description = ["Paths to patch bundles."],
arity = "1..*",
)
private lateinit var patchBundles: Array<File>
private lateinit var patchBundles: Set<File>
@Option(
names = ["-d", "--with-descriptions"],
@ -70,16 +71,19 @@ internal object ListPatchesCommand : Runnable {
private var packageName: String? = null
override fun run() {
fun Patch.CompatiblePackage.buildString() =
buildString {
fun Package.buildString(): String {
val (name, versions) = this
return buildString {
if (withVersions && versions != null) {
appendLine("Package name: $name")
appendLine("Compatible versions:")
append(versions!!.joinToString("\n") { version -> version }.prependIndent("\t"))
append(versions.joinToString("\n") { version -> version }.prependIndent("\t"))
} else {
append("Package name: $name")
}
}
}
fun PatchOption<*>.buildString() =
buildString {
@ -126,10 +130,10 @@ internal object ListPatchesCommand : Runnable {
}
fun Patch<*>.filterCompatiblePackages(name: String) =
compatiblePackages?.any { it.name == name }
compatiblePackages?.any { (compatiblePackageName, _) -> compatiblePackageName == name }
?: withUniversalPatches
val patches = PatchBundleLoader.Jar(*patchBundles).withIndex().toList()
val patches = loadPatchesFromJar(patchBundles).withIndex().toList()
val filtered =
packageName?.let { patches.filter { (_, patch) -> patch.filterCompatiblePackages(it) } } ?: patches

View File

@ -2,7 +2,7 @@ package app.revanced.cli.command
import app.revanced.library.Options
import app.revanced.library.Options.setOptions
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.patch.loadPatchesFromJar
import picocli.CommandLine
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.io.File
@ -19,7 +19,7 @@ internal object OptionsCommand : Runnable {
description = ["Paths to patch bundles."],
arity = "1..*",
)
private lateinit var patchBundles: Array<File>
private lateinit var patchBundles: Set<File>
@CommandLine.Option(
names = ["-p", "--path"],
@ -44,7 +44,7 @@ internal object OptionsCommand : Runnable {
override fun run() =
try {
PatchBundleLoader.Jar(*patchBundles).let { patches ->
loadPatchesFromJar(patchBundles).let { patches ->
val exists = filePath.exists()
if (!exists || overwrite) {
if (exists && update) patches.setOptions(filePath)

View File

@ -4,11 +4,11 @@ import app.revanced.library.ApkUtils
import app.revanced.library.ApkUtils.applyTo
import app.revanced.library.Options
import app.revanced.library.Options.setOptions
import app.revanced.library.adb.AdbManager
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.PatchSet
import app.revanced.library.installation.installer.*
import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherConfig
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.loadPatchesFromJar
import kotlinx.coroutines.runBlocking
import picocli.CommandLine
import picocli.CommandLine.Help.Visibility.ALWAYS
@ -31,7 +31,7 @@ internal object PatchCommand : Runnable {
private lateinit var apk: File
private var integrations = setOf<File>()
private var integrations = emptySet<File>()
private var patchBundles = emptySet<File>()
@ -193,7 +193,7 @@ internal object PatchCommand : Runnable {
description = ["One or more DEX files or containers to merge into the APK."],
)
@Suppress("unused")
private fun setIntegrations(integrations: Array<File>) {
private fun setIntegrations(integrations: Set<File>) {
integrations.firstOrNull { !it.exists() }?.let {
throw CommandLine.ParameterException(spec.commandLine(), "Integrations file ${it.path} does not exist.")
}
@ -256,7 +256,7 @@ internal object PatchCommand : Runnable {
logger.info("Loading patches")
val patches = PatchBundleLoader.Jar(*patchBundles.toTypedArray())
val patches = loadPatchesFromJar(patchBundles)
// Warn if a patch can not be found in the supplied patch bundles.
if (warn) {
@ -271,6 +271,7 @@ internal object PatchCommand : Runnable {
}
// endregion
val patcherTemporaryFilesPath = temporaryFilesPath.resolve("patcher")
val (packageName, patcherResult) = Patcher(
PatcherConfig(
@ -295,8 +296,7 @@ internal object PatchCommand : Runnable {
// region Patch
patcher.context.packageMetadata.packageName to patcher.apply {
acceptIntegrations(integrations)
acceptPatches(filteredPatches)
accept(filteredPatches, integrations)
// Execute patches.
runBlocking {
@ -304,16 +304,18 @@ internal object PatchCommand : Runnable {
patchResult.exception?.let {
StringWriter().use { writer ->
it.printStackTrace(PrintWriter(writer))
logger.severe("${patchResult.patch.name} failed:\n$writer")
logger.severe("\"${patchResult.patch.name}\" failed:\n$writer")
}
} ?: logger.info("${patchResult.patch.name} succeeded")
} ?: logger.info("\"${patchResult.patch.name}\" succeeded")
}
}
}.get()
// endregion
}
// region Save
apk.copyTo(temporaryFilesPath.resolve(apk.name), overwrite = true).apply {
patcherResult.applyTo(this)
}.let { patchedApkFile ->
@ -340,9 +342,23 @@ internal object PatchCommand : Runnable {
// region Install
deviceSerial?.let { serial ->
AdbManager.getAdbManager(deviceSerial = serial.ifEmpty { null }, mount)
}?.install(AdbManager.Apk(outputFilePath, packageName))
deviceSerial?.let { it ->
val deviceSerial = it.ifEmpty { null }
runBlocking {
val result = if (mount) {
AdbRootInstaller(deviceSerial)
} else {
AdbInstaller(deviceSerial)
}.install(Installer.Apk(outputFilePath, packageName))
when (result) {
RootInstallerResult.FAILURE -> logger.severe("Failed to mount the patched APK file")
is AdbInstallerResult.Failure -> logger.severe(result.exception.toString())
else -> logger.info("Installed the patched APK file")
}
}
}
// endregion
@ -358,7 +374,7 @@ internal object PatchCommand : Runnable {
* @param patches The patches to filter.
* @return The filtered patches.
*/
private fun Patcher.filterPatchSelection(patches: PatchSet): PatchSet =
private fun Patcher.filterPatchSelection(patches: Set<Patch<*>>): Set<Patch<*>> =
buildSet {
val packageName = context.packageMetadata.packageName
val packageVersion = context.packageMetadata.packageVersion
@ -367,33 +383,32 @@ internal object PatchCommand : Runnable {
val patchName = patch.name!!
val explicitlyExcluded = excludedPatches.contains(patchName) || excludedPatchesByIndex.contains(i)
if (explicitlyExcluded) return@patch logger.info("Excluding $patchName")
if (explicitlyExcluded) return@patch logger.info("Excluding \"$patchName\"")
// Make sure the patch is compatible with the supplied APK files package name and version.
patch.compatiblePackages?.let { packages ->
packages.singleOrNull { it.name == packageName }?.let { `package` ->
val matchesVersion =
force || `package`.versions?.let {
it.any { version -> version == packageVersion }
} ?: true
packages.singleOrNull { (name, _) -> name == packageName }?.let { (_, versions) ->
val matchesVersion = force ||
versions?.let { it.any { version -> version == packageVersion } }
?: true
if (!matchesVersion) {
return@patch logger.warning(
"$patchName is incompatible with version $packageVersion. " +
"The patch \"$patchName\" is incompatible with version $packageVersion. " +
"This patch is only compatible with version " +
packages.joinToString(";") { pkg ->
pkg.versions!!.joinToString(", ")
packages.joinToString(";") { (_, versions) ->
versions!!.joinToString(", ")
},
)
}
} ?: return@patch logger.fine(
"$patchName is incompatible with $packageName. " +
"The patch \"$patchName\" is incompatible with $packageName. " +
"This patch is only compatible with " +
packages.joinToString(", ") { `package` -> `package`.name },
packages.joinToString(", ") { (name, _) -> name },
)
return@let
} ?: logger.fine("$patchName has no constraint on packages.")
} ?: logger.fine("\"$patchName\" has no constraint on packages.")
// If the patch is implicitly used, it will be only included if [exclusive] is false.
val implicitlyIncluded = !exclusive && patch.use
@ -401,9 +416,9 @@ internal object PatchCommand : Runnable {
val explicitlyIncluded = includedPatches.contains(patchName) || includedPatchesByIndex.contains(i)
val included = implicitlyIncluded || explicitlyIncluded
if (!included) return@patch logger.info("$patchName excluded") // Case 1.
if (!included) return@patch logger.info("\"$patchName\" excluded") // Case 1.
logger.fine("Adding $patchName")
logger.fine("Adding \"$patchName\"")
add(patch)
}

View File

@ -1,6 +1,9 @@
package app.revanced.cli.command.utility
import app.revanced.library.adb.AdbManager
import app.revanced.library.installation.installer.*
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import picocli.CommandLine.*
import java.io.File
import java.util.logging.Logger
@ -32,13 +35,29 @@ internal object InstallCommand : Runnable {
private var packageName: String? = null
override fun run() {
fun install(deviceSerial: String? = null) =
try {
AdbManager.getAdbManager(deviceSerial, packageName != null).install(AdbManager.Apk(apk, packageName))
} catch (e: AdbManager.DeviceNotFoundException) {
suspend fun install(deviceSerial: String? = null) {
val result = try {
if (packageName != null) {
AdbRootInstaller(deviceSerial)
} else {
AdbInstaller(deviceSerial)
}.install(Installer.Apk(apk, packageName))
} catch (e: Exception) {
logger.severe(e.toString())
}
deviceSerials?.forEach(::install) ?: install()
when (result) {
RootInstallerResult.FAILURE ->
logger.severe("Failed to mount the APK file")
is AdbInstallerResult.Failure ->
logger.severe(result.exception.toString())
else ->
logger.info("Installed the APK file")
}
}
runBlocking {
deviceSerials?.map { async { install(it) } }?.awaitAll() ?: install()
}
}
}

View File

@ -1,6 +1,9 @@
package app.revanced.cli.command.utility
import app.revanced.library.adb.AdbManager
import app.revanced.library.installation.installer.*
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import picocli.CommandLine.*
import picocli.CommandLine.Help.Visibility.ALWAYS
import java.util.logging.Logger
@ -33,13 +36,28 @@ internal object UninstallCommand : Runnable {
private var unmount: Boolean = false
override fun run() {
fun uninstall(deviceSerial: String? = null) =
try {
AdbManager.getAdbManager(deviceSerial, unmount).uninstall(packageName)
} catch (e: AdbManager.DeviceNotFoundException) {
suspend fun uninstall(deviceSerial: String? = null) {
val result = try {
if (unmount) {
AdbRootInstaller(deviceSerial)
} else {
AdbInstaller(deviceSerial)
}.uninstall(packageName)
} catch (e: Exception) {
logger.severe(e.toString())
}
deviceSerials?.forEach { uninstall(it) } ?: uninstall()
when (result) {
RootInstallerResult.FAILURE ->
logger.severe("Failed to unmount the patched APK file")
is AdbInstallerResult.Failure ->
logger.severe(result.exception.toString())
else -> logger.info("Uninstalled the patched APK file")
}
}
runBlocking {
deviceSerials?.map { async { uninstall(it) } }?.awaitAll() ?: uninstall()
}
}
}