mirror of
https://github.com/Guardsquare/proguard.git
synced 2026-03-13 09:50:34 +08:00
Fix collection of AAPT rules
This commit is contained in:
@@ -197,6 +197,11 @@ deprecated:
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning "Known issue with library projects and AAPT rules"
|
||||
In library projects, the ProGuard Gradle plugin will not apply any keep rules that would have been generated by
|
||||
AAPT when using the AGP integration. Therefore, you may need to apply some extra keep rules for classes referenced from
|
||||
resources in your own ProGuard configuration.
|
||||
|
||||
7. You can then build your application as usual and ProGuard will be automatically executed on the configured variants as before:
|
||||
|
||||
=== "Linux/macOS"
|
||||
|
||||
@@ -121,6 +121,7 @@ class AndroidPlugin(private val androidExtension: BaseExtension) : Plugin<Projec
|
||||
val matchingConfiguration = proguardBlock.configurations.findVariantConfiguration(variant.name)
|
||||
if (matchingConfiguration != null) {
|
||||
verifyNotMinified(variant)
|
||||
disableAaptOutputCaching(project, variant)
|
||||
|
||||
collectConsumerRulesTask.dependsOn(createCollectConsumerRulesTask(
|
||||
project,
|
||||
@@ -191,6 +192,21 @@ class AndroidPlugin(private val androidExtension: BaseExtension) : Plugin<Projec
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: improve loading AAPT rules so that we don't rely on this
|
||||
private fun disableAaptOutputCaching(project: Project, variant: BaseVariant) {
|
||||
val cachingEnabled = project.hasProperty("org.gradle.caching") &&
|
||||
(project.findProperty("org.gradle.caching") as String).toBoolean()
|
||||
|
||||
if (cachingEnabled) {
|
||||
// ensure that the aapt_rules.pro has been generated, so ProGuard can use it
|
||||
val processResourcesTask = project.tasks.findByName("process${variant.name.capitalize()}Resources")
|
||||
processResourcesTask?.outputs?.doNotCacheIf("We need to regenerate the aapt_rules.pro file, sorry!") {
|
||||
project.logger.debug("Disabling AAPT caching for ${variant.name}")
|
||||
!File("${project.buildDir.absolutePath}/intermediates/proguard/configs/aapt_rules.pro").exists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun warnOldProguardVersion(project: Project) {
|
||||
if (agpVersion.major >= 7) return
|
||||
|
||||
|
||||
@@ -76,7 +76,12 @@ class ProGuardTransform(
|
||||
proguardTask.configuration(project.tasks.getByPath(COLLECT_CONSUMER_RULES_TASK_NAME + variantName.capitalize()).outputs.files)
|
||||
proguardTask.configuration(variantBlock.configurations.map { project.file(it.path) })
|
||||
|
||||
getAaptRulesFile()?.let { proguardTask.configuration(it) }
|
||||
val aaptRulesFile = getAaptRulesFile()
|
||||
if (aaptRulesFile != null && File(aaptRulesFile).exists()) {
|
||||
proguardTask.configuration(aaptRulesFile)
|
||||
} else {
|
||||
project.logger.warn("AAPT rules file not found: you may need to apply some extra keep rules for classes referenced from resources in your own ProGuard configuration.")
|
||||
}
|
||||
|
||||
val mappingDir = File("${project.buildDir.absolutePath}/outputs/proguard/$variantName/mapping")
|
||||
if (!mappingDir.exists()) mappingDir.mkdirs()
|
||||
@@ -147,7 +152,7 @@ class ProGuardTransform(
|
||||
private fun getAaptRulesFile() = androidExtension.aaptAdditionalParameters
|
||||
.zipWithNext { cmd, param -> if (cmd == "--proguard") param else null }
|
||||
.filterNotNull()
|
||||
.firstOrNull()
|
||||
.firstOrNull { File(it).exists() }
|
||||
}
|
||||
|
||||
typealias ProGuardIOEntry = Pair<File, File>
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
package proguard.gradle
|
||||
|
||||
import io.kotest.core.spec.style.FreeSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import testutils.AndroidProject
|
||||
import testutils.SourceFile
|
||||
import testutils.applicationModule
|
||||
import testutils.createGradleRunner
|
||||
import testutils.createTestKitDir
|
||||
import testutils.libraryModule
|
||||
|
||||
class ProGuardPluginTest : FreeSpec({
|
||||
val testKitDir = createTestKitDir()
|
||||
@@ -88,4 +92,44 @@ class ProGuardPluginTest : FreeSpec({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"Given a library project" - {
|
||||
val project = autoClose(AndroidProject().apply {
|
||||
addModule(libraryModule("lib", buildDotGradle = """
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'com.guardsquare.proguard'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
debug {}
|
||||
}
|
||||
}
|
||||
|
||||
proguard {
|
||||
configurations {
|
||||
release {
|
||||
defaultConfiguration 'proguard-android.txt'
|
||||
configuration 'proguard-project.txt'
|
||||
}
|
||||
}
|
||||
}
|
||||
""".trimIndent(),
|
||||
additionalFiles = listOf(SourceFile("proguard-project.txt", "-keep class **"))))
|
||||
}.create())
|
||||
|
||||
"When the project is assembled" - {
|
||||
val result = createGradleRunner(project.rootDir, testKitDir, "assembleRelease").build()
|
||||
|
||||
"Then the build should succeed" {
|
||||
result.task(":lib:assembleRelease")?.outcome shouldBe TaskOutcome.SUCCESS
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -118,4 +118,145 @@ class AaptRulesTest : FreeSpec({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"Given a project with a configuration for a variant in a project that has caching enabled" - {
|
||||
val project = autoClose(AndroidProject(gradleDotProperties = "org.gradle.caching=true").apply {
|
||||
addModule(applicationModule("app", buildDotGradle = """
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'com.guardsquare.proguard'
|
||||
}
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
versionCode 1
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proguard {
|
||||
configurations {
|
||||
debug {
|
||||
defaultConfiguration 'proguard-android-debug.txt'
|
||||
}
|
||||
}
|
||||
}""".trimIndent()))
|
||||
}.create())
|
||||
|
||||
"When assembleDebug is executed" - {
|
||||
val aaptRules = File("${project.moduleBuildDir("app")}/intermediates/proguard/configs/aapt_rules.pro")
|
||||
// run once, then delete the aapt_rules file
|
||||
val result0 = createGradleRunner(project.rootDir, testKitDir, "assembleDebug").build()
|
||||
aaptRules.delete()
|
||||
// running again should re-generate the rules file
|
||||
val result = createGradleRunner(project.rootDir, testKitDir, "clean", "assembleDebug", "--info").build()
|
||||
|
||||
"The build should succeed" {
|
||||
result0.task(":app:assembleDebug")?.outcome shouldBe TaskOutcome.SUCCESS
|
||||
result.task(":app:assembleDebug")?.outcome shouldBe TaskOutcome.SUCCESS
|
||||
}
|
||||
|
||||
"The rules file should be passed to ProGuard" {
|
||||
result.output shouldContain "Loading configuration file ${aaptRules.absolutePath}"
|
||||
}
|
||||
|
||||
"The the AAPT rules should be generated" {
|
||||
aaptRules.shouldExist()
|
||||
aaptRules.readText() shouldContain "-keep class com.example.app.MainActivity { <init>(); }"
|
||||
}
|
||||
}
|
||||
|
||||
"When bundleDebug is executed" - {
|
||||
val aaptRules = File("${project.moduleBuildDir("app")}/intermediates/proguard/configs/aapt_rules.pro")
|
||||
// run once, then delete the aapt_rules file
|
||||
val result0 = createGradleRunner(project.rootDir, testKitDir, "clean", "bundleDebug").build()
|
||||
aaptRules.delete()
|
||||
// running again should re-generate the rules file
|
||||
val result = createGradleRunner(project.rootDir, testKitDir, "clean", "bundleDebug", "--info").build()
|
||||
|
||||
"The build should succeed" {
|
||||
result0.task(":app:bundleDebug")?.outcome shouldBe TaskOutcome.SUCCESS
|
||||
result.task(":app:bundleDebug")?.outcome shouldBe TaskOutcome.SUCCESS
|
||||
}
|
||||
|
||||
"The rules file should be passed to ProGuard" {
|
||||
result.output shouldContain "Loading configuration file ${aaptRules.absolutePath}"
|
||||
}
|
||||
|
||||
"The the AAPT rules should be generated" {
|
||||
aaptRules.shouldExist()
|
||||
aaptRules.readText() shouldContain "-keep class com.example.app.MainActivity { <init>(); }"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"Given a project with a flavor configuration for a variant in a project that has caching enabled" - {
|
||||
val project = autoClose(AndroidProject(gradleDotProperties = "org.gradle.caching=true").apply {
|
||||
addModule(applicationModule("app", buildDotGradle = """
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'com.guardsquare.proguard'
|
||||
}
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "version"
|
||||
productFlavors {
|
||||
demo {
|
||||
dimension "version"
|
||||
applicationIdSuffix ".demo"
|
||||
versionNameSuffix "-demo"
|
||||
}
|
||||
full {
|
||||
dimension "version"
|
||||
applicationIdSuffix ".full"
|
||||
versionNameSuffix "-full"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proguard {
|
||||
configurations {
|
||||
demoDebug {
|
||||
defaultConfiguration 'proguard-android-debug.txt'
|
||||
}
|
||||
}
|
||||
}""".trimIndent()))
|
||||
}.create())
|
||||
|
||||
"When the project is evaluated" - {
|
||||
val aaptRules = File("${project.moduleBuildDir("app")}/intermediates/proguard/configs/aapt_rules.pro")
|
||||
// run once, then delete the aapt_rules file
|
||||
val result0 = createGradleRunner(project.rootDir, testKitDir, "assembleDebug").build()
|
||||
aaptRules.delete()
|
||||
// running again should re-generate the rules file
|
||||
val result = createGradleRunner(project.rootDir, testKitDir, "clean", "assembleDebug", "--info").build()
|
||||
|
||||
"The build should succeed" {
|
||||
result0.task(":app:assembleDebug")?.outcome shouldBe TaskOutcome.SUCCESS
|
||||
result.task(":app:assembleDebug")?.outcome shouldBe TaskOutcome.SUCCESS
|
||||
}
|
||||
|
||||
"The rules file should be passed to ProGuard" {
|
||||
result.output shouldContain "Loading configuration file ${aaptRules.absolutePath}"
|
||||
}
|
||||
|
||||
"The the AAPT rules should be generated" {
|
||||
aaptRules.shouldExist()
|
||||
aaptRules.readText() shouldContain "-keep class com.example.app.MainActivity { <init>(); }"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
package proguard.gradle.plugin.android.dsl
|
||||
|
||||
import io.kotest.core.spec.style.FreeSpec
|
||||
import io.kotest.matchers.file.shouldExist
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import java.io.File
|
||||
import org.gradle.testkit.runner.TaskOutcome.SUCCESS
|
||||
import testutils.AndroidProject
|
||||
import testutils.applicationModule
|
||||
@@ -82,6 +85,12 @@ class FlavorsConfigurationTest : FreeSpec({
|
||||
"ProGuard should not be executed for non-matching build variants" {
|
||||
buildResult.task(":app:transformClassesAndResourcesWithProguardTransformForFullDebug")?.outcome shouldBe null
|
||||
}
|
||||
|
||||
"AAPT rules should be generated" - {
|
||||
val aaptRules = File("${project.moduleBuildDir("app")}/intermediates/proguard/configs/aapt_rules.pro")
|
||||
aaptRules.shouldExist()
|
||||
aaptRules.readText() shouldContain "-keep class com.example.app.MainActivity { <init>(); }"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user