Reputation: 973
So, I want to add an external library to my project. The library itself is quite small, around 300 methods. But it is configured to be very liberal with it's proguard configuration. I ran a simple test with/without the library and with/without proguard on a barebones project and this is what I came up with
Proguard Lib Method Count
N N 15631
Y N 6370
N Y 15945
Y Y 15573
As you can see, with proguard enabled, the count is ~6000. But the moment I add the lib, count shoots up to ~15000 despite the library itself being only ~300 methods.
So my question is, how do I ignore the proguard configuration of this particular library?
UPDATE:
It is not possible with android gradle plugin now. I found android bug which doesn't have priority at all. Please avoid answers with mentioning "it is not possible" and keep question opened until a workaround or an official decision is possible. Otherwise, you will collect half of bounty without adding value. Thanks!
Upvotes: 18
Views: 4612
Reputation: 339
Edit: In Gradle 7.3 there is a method where you can specify for which dependencies to ignore keep rules: https://developer.android.com/reference/tools/gradle-api/7.3/com/android/build/api/dsl/KeepRules#ignoreExternalDependencies(kotlin.Array)
For example:
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("androidx.lifecycle:lifecycle-runtime")
}
}
}
Old answer: Inspired by Jonas' answer, modified for Kotlin DSL and confirmed working on Android Gradle plugin 7.2.1:
import com.android.build.gradle.internal.tasks.ProguardConfigurableTask
afterEvaluate {
// Get each ProguardConfigurableTask
tasks.withType(ProguardConfigurableTask::class.java).forEach { task ->
// Remove proguard rules from lifecycle-runtime library
val filteredConfigurationFiles = task.configurationFiles.filter { file ->
!file.path.contains("lifecycle-runtime")
}
task.configurationFiles.setFrom(filteredConfigurationFiles)
}
}
Upvotes: 3
Reputation: 1256
In case you're using R8 (which replaced ProGuard since Android Gradle plugin 3.4.0) - you can filter-out specific consumer rule files by adding the following work-around to your module's build.gradle
:
tasks.whenTaskAdded { Task task ->
// Once 'minifyEnabled' is set to 'true' for a certain build type/variant -
// a 'minify<variantName>WithR8' task will be created for each such variant
//
// - This task is implemented by com.android.build.gradle.internal.tasks.R8Task
// - R8Task extends from ProguardConfigurableTask
// - ProguardConfigurableTask exposes property 'configurationFiles'
// - configurationFiles contains all files that will be contributing R8 rules
// - configurationFiles is mutable (its type is ConfigurableFileCollection)
//
// Thus - we can overwrite the list of files and filter them out as we please
//
// More details: https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/R8Task.kt
if (task.name.startsWith("minify") && task.name.endsWith("WithR8")) {
afterEvaluate {
def filteredList = task.configurationFiles.filter {
// Example paths in this collection:
// /Users/me/MyProject/myModule/proguard-rules.pro
// (for library dependencies) /Users/me/.gradle/caches/<...>/okhttp3.pro
// The below filter condition will, for example, exclude consumer ProGuard rules file from the AndroidX RecyclerView library
!it.path.contains("recyclerview-1.1.0")
}
task.configurationFiles.setFrom(filteredList.files)
}
}
}
The above work-around was confirmed to be working on Android Gradle plugin 4.2.2
If one decides to rely on such a work-around - it might be a good idea to also add some sort of automated checks and/or tests to make sure this filtering is working. Given that the solution is quite fragile and can break with future updates of Android Gradle plugin.
Upvotes: 3
Reputation: 6200
In this specific case you have a few options:
Add the following to your build.gradle:
afterEvaluate {
// All proguard tasks shall depend on our filter task
def proguardTasks = tasks.findAll { task ->
task.name.startsWith('transformClassesAndResourcesWithProguardFor') }
proguardTasks.each { task -> task.dependsOn filterConsumerRules }
}
// Let's define our custom task that filters some unwanted
// consumer proguard rules
task(filterConsumerRules) << {
// Collect all consumer rules first
FileTree allConsumerRules = fileTree(dir: 'build/intermediates/exploded-aar',
include: '**/proguard.txt')
// Now filter the ones we want to exclude:
// Change it to fit your needs, replace library with
// the name of the aar you want to filter.
FileTree excludeRules = allConsumerRules.matching {
include '**/library/**'
}
// Print some info and delete the file, so ProGuard
// does not pick it up. We could also just rename it.
excludeRules.each { File file ->
println 'Deleting ProGuard consumer rule ' + file
file.delete()
}
}
When using DexGuard (7.2.02+), you can add the following snippet to your build.gradle:
dexguard {
// Replace library with the name of the aar you want to filter
// The ** at the end will include every other rule.
consumerRuleFilter '!**/library/**,**'
}
Mind that the logic is inverted to the ProGuard example above, the consumerRuleFilter will only include consumer rules that match the pattern.
Upvotes: 11