From efd665afac9a7bbf5d9808b757c9cebba1963155 Mon Sep 17 00:00:00 2001 From: James Hamilton Date: Mon, 14 Jun 2021 14:35:12 +0200 Subject: [PATCH] Fix collection of AAPT rules --- docs/md/manual/setup/upgrading.md | 5 + .../gradle/plugin/android/AndroidPlugin.kt | 16 ++ .../plugin/android/ProGuardTransform.kt | 9 +- .../proguard/gradle/ProGuardPluginTest.kt | 44 ++++++ .../plugin/android/dsl/AaptRulesTest.kt | 141 ++++++++++++++++++ .../android/dsl/FlavorsConfigurationTest.kt | 9 ++ 6 files changed, 222 insertions(+), 2 deletions(-) diff --git a/docs/md/manual/setup/upgrading.md b/docs/md/manual/setup/upgrading.md index b295c1d7..f360ab59 100644 --- a/docs/md/manual/setup/upgrading.md +++ b/docs/md/manual/setup/upgrading.md @@ -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" diff --git a/gradle-plugin/src/main/kotlin/proguard/gradle/plugin/android/AndroidPlugin.kt b/gradle-plugin/src/main/kotlin/proguard/gradle/plugin/android/AndroidPlugin.kt index e0aebfb7..b1af4ab9 100644 --- a/gradle-plugin/src/main/kotlin/proguard/gradle/plugin/android/AndroidPlugin.kt +++ b/gradle-plugin/src/main/kotlin/proguard/gradle/plugin/android/AndroidPlugin.kt @@ -121,6 +121,7 @@ class AndroidPlugin(private val androidExtension: BaseExtension) : Plugin= 7) return diff --git a/gradle-plugin/src/main/kotlin/proguard/gradle/plugin/android/ProGuardTransform.kt b/gradle-plugin/src/main/kotlin/proguard/gradle/plugin/android/ProGuardTransform.kt index 0c15fe7a..bba54164 100644 --- a/gradle-plugin/src/main/kotlin/proguard/gradle/plugin/android/ProGuardTransform.kt +++ b/gradle-plugin/src/main/kotlin/proguard/gradle/plugin/android/ProGuardTransform.kt @@ -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 diff --git a/gradle-plugin/src/test/kotlin/proguard/gradle/ProGuardPluginTest.kt b/gradle-plugin/src/test/kotlin/proguard/gradle/ProGuardPluginTest.kt index 0ed69658..2ddbf4cf 100644 --- a/gradle-plugin/src/test/kotlin/proguard/gradle/ProGuardPluginTest.kt +++ b/gradle-plugin/src/test/kotlin/proguard/gradle/ProGuardPluginTest.kt @@ -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 + } + } + } }) diff --git a/gradle-plugin/src/test/kotlin/proguard/gradle/plugin/android/dsl/AaptRulesTest.kt b/gradle-plugin/src/test/kotlin/proguard/gradle/plugin/android/dsl/AaptRulesTest.kt index 9a051ca3..2537f3af 100644 --- a/gradle-plugin/src/test/kotlin/proguard/gradle/plugin/android/dsl/AaptRulesTest.kt +++ b/gradle-plugin/src/test/kotlin/proguard/gradle/plugin/android/dsl/AaptRulesTest.kt @@ -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 { (); }" + } + } + + "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 { (); }" + } + } + } + + "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 { (); }" + } + } + } }) diff --git a/gradle-plugin/src/test/kotlin/proguard/gradle/plugin/android/dsl/FlavorsConfigurationTest.kt b/gradle-plugin/src/test/kotlin/proguard/gradle/plugin/android/dsl/FlavorsConfigurationTest.kt index 8b454216..cb93fbec 100644 --- a/gradle-plugin/src/test/kotlin/proguard/gradle/plugin/android/dsl/FlavorsConfigurationTest.kt +++ b/gradle-plugin/src/test/kotlin/proguard/gradle/plugin/android/dsl/FlavorsConfigurationTest.kt @@ -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 { (); }" + } } } })