This repository was archived by the owner on Aug 20, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 44
Add R8/ProGuard rules to the codegen artifact #143
Open
JakeWharton
wants to merge
2
commits into
master
Choose a base branch
from
jakew/rules/2019-07-14
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,118 @@ | ||
| import org.gradle.internal.jvm.Jvm | ||
|
|
||
| apply plugin: 'java-library' | ||
|
|
||
| sourceCompatibility = JavaVersion.VERSION_1_8 | ||
| targetCompatibility = JavaVersion.VERSION_1_8 | ||
|
|
||
| configurations { | ||
| r8 | ||
| proGuard | ||
| } | ||
|
|
||
| // TODO Remove when R8 is updated to 1.5 or newer which pulls kotlinx.metadata from Maven Central. | ||
| repositories { | ||
| maven { | ||
| url 'https://kotlin.bintray.com/kotlinx' | ||
| } | ||
| } | ||
|
|
||
| dependencies { | ||
| testImplementation deps.junit | ||
| testImplementation deps.truth | ||
| testImplementation deps.dagger.runtime | ||
| testAnnotationProcessor deps.dagger.compiler | ||
|
|
||
| r8 'com.android.tools:r8:1.4.93' | ||
| proGuard 'net.sf.proguard:proguard-base:6.1.1' | ||
| } | ||
|
|
||
| def r8Classes = new File(buildDir, 'classes/java/testR8') | ||
| task runR8(type: JavaExec, dependsOn: 'testClasses') { | ||
| classpath = configurations.r8 | ||
| main = 'com.android.tools.r8.R8' | ||
|
|
||
| doFirst { | ||
| if (r8Classes.exists()) { | ||
| r8Classes.deleteDir() | ||
| } | ||
| r8Classes.mkdirs() | ||
|
|
||
| def arguments = [ | ||
| '--release', | ||
| '--classfile', | ||
| '--output', r8Classes.absolutePath, | ||
| '--pg-conf', file('src/test/rules.pro').absolutePath, | ||
| '--pg-conf', file('src/main/resources/META-INF/proguard/dagger-reflect.pro').absolutePath, | ||
| '--lib', new File(Jvm.current().getJavaHome(), "jre/lib/rt.jar").absolutePath | ||
| ] | ||
| sourceSets.test.compileClasspath.each { | ||
| if (it.exists()) { | ||
| arguments.add('--lib') | ||
| arguments.add(it.absolutePath) | ||
| } | ||
| } | ||
| arguments.addAll(sourceSets.test.output.classesDirs.asFileTree) | ||
|
|
||
| args = arguments | ||
| } | ||
| } | ||
|
|
||
| task testR8(type: Test, dependsOn: 'runR8') { | ||
| description = 'Run tests after optimizing with R8' | ||
| group = 'verification' | ||
|
|
||
| testClassesDirs = files(r8Classes) | ||
| classpath = files(r8Classes, sourceSets.test.compileClasspath) | ||
|
|
||
| inputs.dir(r8Classes) | ||
| } | ||
| check.dependsOn('testR8') | ||
|
|
||
| def proguardClasses = new File(buildDir, 'classes/java/testProGuard') | ||
| task runProGuard(type: JavaExec, dependsOn: 'testClasses') { | ||
| classpath = configurations.proGuard | ||
| main = 'proguard.ProGuard' | ||
|
|
||
| doFirst { | ||
| if (proguardClasses.exists()) { | ||
| proguardClasses.deleteDir() | ||
| } | ||
| proguardClasses.mkdirs() | ||
|
|
||
| def arguments = [ | ||
| '-include', file('src/test/rules.pro').absolutePath, | ||
| '-include', file('src/main/resources/META-INF/proguard/dagger-reflect.pro').absolutePath, | ||
| '-libraryjars', new File(Jvm.current().getJavaHome(), "jre/lib/rt.jar").absolutePath, | ||
| ] | ||
| sourceSets.test.compileClasspath.each { | ||
| if (it.exists()) { | ||
| arguments.add('-libraryjars') | ||
| arguments.add(it.absolutePath) | ||
| } | ||
| } | ||
| sourceSets.test.output.classesDirs.each { | ||
| if (it.exists()) { | ||
| arguments.add('-injars') | ||
| arguments.add(it.absolutePath) | ||
| } | ||
| } | ||
| arguments.add('-outjars') | ||
| arguments.add(proguardClasses.absolutePath) | ||
|
|
||
| args = arguments | ||
| } | ||
| } | ||
|
|
||
| task testProGuard(type: Test, dependsOn: 'runProGuard') { | ||
| description = 'Run tests after optimizing with ProGuard' | ||
| group = 'verification' | ||
|
|
||
| testClassesDirs = files(proguardClasses) | ||
| classpath = files(proguardClasses, sourceSets.test.compileClasspath) | ||
|
|
||
| inputs.dir(proguardClasses) | ||
| } | ||
| check.dependsOn('testProGuard') | ||
|
|
||
| apply from: rootProject.file('gradle/gradle-mvn-push.gradle') |
49 changes: 49 additions & 0 deletions
49
codegen/src/main/resources/META-INF/proguard/dagger-reflect.pro
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # When using factories or builders, the enclosing class is used to determine the component type. | ||
| -keepattributes InnerClasses | ||
| -keepattributes EnclosingMethod # Required by InnerClasses | ||
|
|
||
| # JSR 330 and Dagger annotations like @Inject, @Provide, @Component, etc. are obviously needed. | ||
| -keepattributes RuntimeVisibleAnnotations | ||
|
|
||
| # Annotation defaults are required for @MapKey, @Component, @Subcomponent, etc. | ||
| -keepattributes AnnotationDefault | ||
|
|
||
| # Generic signatures are needed for properly parsing types of module methods, component methods, | ||
| # and injected fields/methods. | ||
| -keepattributes Signature | ||
|
|
||
| # The names of component and subcomponent types must be kept so the Dagger-generated type can be | ||
| # resolved by prepending a prefix to the name. This should also prevent vertical class merging. | ||
| -keepnames @dagger.Component class * | ||
| -keepnames @dagger.Subcomponent class * | ||
|
|
||
| # For each component type, keep the corresponding Dagger-generated type. We also keep the create | ||
| # method, in case there is no builder or factory. | ||
| -if @dagger.Component class **.* | ||
| -keep class <1>.Dagger<2> { | ||
| static ** create(); | ||
| } | ||
|
|
||
| # For each component builder type that is kept, keep the name of that type which will ensure it | ||
| # remains nested inside its corresponding component type. | ||
| -if @dagger.Component$Builder class **.*$* | ||
| -keep class <1>.<2> | ||
|
|
||
| # For each component builder type that is kept, keep the corresponding Dagger-generated component | ||
| # type and the factory method for the builder. | ||
| -if @dagger.Component$Builder class **.*$* | ||
| -keep class <1>.Dagger<2> { | ||
| static ** builder(); | ||
| } | ||
|
|
||
| # For each component factory type that is kept, keep the name of that type which will ensure it | ||
| # remains nested inside its corresponding component type. | ||
| -if @dagger.Component$Factory class **.*$* | ||
| -keep class <1>.<2> | ||
|
|
||
| # For each component factory type, keep the corresponding Dagger-generated component type and the | ||
| # factory method for the factory. | ||
| -if @dagger.Component$Factory class **.*$* | ||
| -keep class <1>.Dagger<2> { | ||
| static ** factory(); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package dagger; | ||
|
|
||
| import static java.lang.annotation.RetentionPolicy.CLASS; | ||
|
|
||
| import java.lang.annotation.Retention; | ||
|
|
||
| @Retention(CLASS) | ||
| public @interface Keep {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # @org.junit.Test | ||
| -keepattributes RuntimeVisibleAnnotations | ||
|
|
||
| # Test methods are invoked reflectively. | ||
| -keepclasseswithmembers class * { | ||
| @org.junit.Test <methods>; | ||
| } | ||
|
|
||
| # Test classes are constructed reflectively. | ||
| -keepclassmembers class **.*Test { | ||
| <init>(...); | ||
| } | ||
|
|
||
| # Annotation to preserve conditions required for tests which otherwise aren't automatically kept. | ||
| -keep @dagger.Keep class * | ||
|
|
||
| # For Mac OS and its case-insensitive (by default) filesystem. | ||
| -dontusemixedcaseclassnames |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a mention of
@Bindswhich I think may be a special case as it's not referenced by generated Dagger component code. Is it covered by one of these rules?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These rules are for the codegen artifact reflection, not the reflection in the reflect artifact.
I'm not convinced anyone will be using shrinking with the reflect artifact. I'd be curious what their use case for this was.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good point, sorry.
Re shrinking with reflect: we sometimes minifyEnabled=true on debug flavor so we can test shrinking-related issues. If dagger-reflect was set up it with
if (idea)this quick-change-for-debugging would fail the build unexpectedly because of reflect.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, though, having dagger-reflect in your APK would already produce results that were not representative of what happens when shrinking in release. In release builds a large portion of Dagger-generated types are vertically merge and/or inlined. I've even written some non-trivial graphs that have been completely eliminated by shrinking leaving only a bunch of chained calls to constructors.
I'm not opposed to adding rules to the reflect artifact. They're probably pretty basic. But I think the concept of shrinking and using reflect are mutually exclusive in their goals so it's not a high priority.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I was referring to here is adding
minifyEnabled=trueto debug so that we can verify things not related to Dagger, or dagger-reflect. But at the moment if the debug build is set up with dagger-reflect, it prevents debugging other problems., because injection crashes. I see why it's not high priority though, as it shouldn't be hard to turn off dagger-reflect, it's just inconvenient.