Skip to content

Instantly share code, notes, and snippets.

@tophyr
Last active August 29, 2015 14:07
Show Gist options
  • Save tophyr/6ca3a5eaa68655357565 to your computer and use it in GitHub Desktop.
Save tophyr/6ca3a5eaa68655357565 to your computer and use it in GitHub Desktop.
ButterKnifeProcessor + Optimizing Proguard workaround
// from <aosp_root>/tools/base/build-system/gradle/src/main/groovy/com/android/build/gradle/BasePlugin.groovy
// here just for context, unmodified
/**
* Creates the proguarding task for the given Variant.
* @param variantData the variant data.
* @param testedVariantData optional. variant data representing the tested variant, null if the
* variant is not a test variant
* @return outFile file outputted by proguard
*/
@NonNull
public File createProguardTasks(@NonNull BaseVariantData variantData,
@Nullable BaseVariantData testedVariantData) {
VariantConfiguration variantConfig = variantData.variantConfiguration
def proguardTask = project.tasks.create(
"proguard${variantData.variantConfiguration.fullName.capitalize()}",
ProGuardTask)
proguardTask.dependsOn variantData.javaCompileTask, variantData.variantDependency.packageConfiguration.buildDependencies
if (testedVariantData != null) {
proguardTask.dependsOn testedVariantData.obfuscationTask
}
variantData.obfuscationTask = proguardTask
// --- Output File ---
File outFile;
if (variantData instanceof LibraryVariantData) {
outFile = project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/$DIR_BUNDLES/${variantData.variantConfiguration.dirName}/classes.jar")
} else {
outFile = project.file(
"${project.buildDir}/${FD_INTERMEDIATES}/classes-proguard/${variantData.variantConfiguration.dirName}/classes.jar")
}
// --- Proguard Config ---
if (testedVariantData != null) {
// don't remove any code in tested app
proguardTask.dontshrink()
proguardTask.keepnames("class * extends junit.framework.TestCase")
proguardTask.keepclassmembers("class * extends junit.framework.TestCase {\n" +
" void test*(...);\n" +
"}")
// input the mapping from the tested app so that we can deal with obfuscated code
proguardTask.applymapping("${project.buildDir}/${FD_OUTPUTS}/proguard/${testedVariantData.variantConfiguration.dirName}/mapping.txt")
// for tested app, we only care about their aapt config since the base
// configs are the same files anyway.
proguardTask.configuration(testedVariantData.processResourcesTask.proguardOutputFile)
}
// all the config files coming from build type, product flavors.
List<Object> proguardFiles = variantConfig.getProguardFiles(true /*includeLibs*/)
for (Object proguardFile : proguardFiles) {
proguardTask.configuration(proguardFile)
}
// also the config file output by aapt
proguardTask.configuration(variantData.processResourcesTask.proguardOutputFile)
// --- InJars / LibraryJars ---
if (variantData instanceof LibraryVariantData) {
String packageName = variantConfig.getPackageFromManifest()
if (packageName == null) {
throw new BuildException("Failed to read manifest", null)
}
packageName = packageName.replace('.', '/');
// injar: the compilation output
// exclude R files and such from output
String exclude = '!' + packageName + "/R.class"
exclude += (', !' + packageName + "/R\$*.class")
if (!((LibraryExtension)extension).packageBuildConfig) {
exclude += (', !' + packageName + "/Manifest.class")
exclude += (', !' + packageName + "/Manifest\$*.class")
exclude += (', !' + packageName + "/BuildConfig.class")
}
proguardTask.injars(variantData.javaCompileTask.destinationDir, filter: exclude)
// include R files and such for compilation
String include = exclude.replace('!', '')
proguardTask.libraryjars(variantData.javaCompileTask.destinationDir, filter: include)
// injar: the local dependencies
Closure inJars = {
Arrays.asList(getLocalJarFileList(variantData.variantDependency))
}
proguardTask.injars(inJars, filter: '!META-INF/MANIFEST.MF')
// libjar: the library dependencies. In this case we take all the compile-scope
// dependencies
Closure libJars = {
Set<File> compiledJars = androidBuilder.getCompileClasspath(variantConfig)
Object[] localJars = getLocalJarFileList(variantData.variantDependency)
compiledJars.findAll({ !localJars.contains(it) })
}
proguardTask.libraryjars(libJars, filter: '!META-INF/MANIFEST.MF')
// ensure local jars keep their package names
proguardTask.keeppackagenames()
} else {
// injar: the compilation output
proguardTask.injars(variantData.javaCompileTask.destinationDir)
// injar: the packaged dependencies
Closure inJars = {
androidBuilder.getPackagedJars(variantConfig)
}
proguardTask.injars(inJars, filter: '!META-INF/MANIFEST.MF')
// the provided-only jars as libraries.
Closure libJars = {
variantData.variantConfiguration.providedOnlyJars
}
proguardTask.libraryjars(libJars)
}
// libraryJars: the runtime jars. Do this in doFirst since the boot classpath isn't
// available until the SDK is loaded in the prebuild task
proguardTask.doFirst {
for (String runtimeJar : androidBuilder.getBootClasspath()) {
proguardTask.libraryjars(runtimeJar)
}
}
if (testedVariantData != null) {
// input the tested app as library
proguardTask.libraryjars(testedVariantData.javaCompileTask.destinationDir)
// including its dependencies
Closure testedPackagedJars = {
androidBuilder.getPackagedJars(testedVariantData.variantConfiguration)
}
proguardTask.libraryjars(testedPackagedJars, filter: '!META-INF/MANIFEST.MF')
}
// --- Out files ---
proguardTask.outjars(outFile)
final File proguardOut = project.file(
"${project.buildDir}/${FD_OUTPUTS}/proguard/${variantData.variantConfiguration.dirName}")
proguardTask.dump(new File(proguardOut, "dump.txt"))
proguardTask.printseeds(new File(proguardOut, "seeds.txt"))
proguardTask.printusage(new File(proguardOut, "usage.txt"))
proguardTask.printmapping(new File(proguardOut, "mapping.txt"))
// proguard doesn't verify that the seed/mapping/usage folders exist and will fail
// if they don't so create them.
proguardTask.doFirst {
proguardOut.mkdirs()
}
return outFile
}
afterEvaluate {
android.applicationVariants.all { variant ->
def proguardTask = project.tasks.findByName("proguard${variant.name.capitalize()}")
if (proguardTask != null) {
def inJarField = proguardTask.class.superclass.getDeclaredField('inJarFilters')
inJarField.setAccessible(true)
filters = inJarField.get(proguardTask)
filters.get(1).put('filter', filters.get(1).get('filter') + ',!**ButterKnifeProcessor**')
print filters
}
}
}
// from http://grepcode.com/file/repo1.maven.org/maven2/net.sf.proguard/proguard-gradle/4.10/proguard/gradle/ProGuardTask.java
// here just for context, unmodified
/**
* This Task allows to configure and run ProGuard from Gradle.
*
* @author Eric Lafortune
*/
public class ProGuardTask extends DefaultTask
{
// Accumulated input and output, for the sake of Gradle's lazy file
// resolution and lazy task execution.
private final List inJarFiles = new ArrayList();
private final List inJarFilters = new ArrayList();
private final List outJarFiles = new ArrayList();
private final List outJarFilters = new ArrayList();
private final List inJarCounts = new ArrayList();
private final List libraryJarFiles = new ArrayList();
private final List libraryJarFilters = new ArrayList();
private final List configurationFiles = new ArrayList();
// Accumulated configuration.
private final Configuration configuration = new Configuration();
// Field acting as a parameter for the class member specification methods.
private ClassSpecification classSpecification;
// Gradle task inputs and outputs, because annotations on the List fields
// (private or not) don't seem to work. Private methods don't work either,
// but package visible or protected methods are ok.
@InputFiles
protected FileCollection getInJarFiles() throws ParseException
{
return getProject().files(inJarFiles);
}
@Optional @OutputFiles
protected FileCollection getOutJarFiles() throws ParseException
{
return getProject().files(outJarFiles);
}
@InputFiles
protected FileCollection getLibraryJarFiles() throws ParseException
{
return getProject().files(libraryJarFiles);
}
@InputFiles
protected FileCollection getConfigurationFiles() throws ParseException
{
return getProject().files(configurationFiles);
}
// Gradle task settings corresponding to all ProGuard options.
public void configuration(Object configurationFiles)
throws ParseException, IOException
{
// Just collect the arguments, so they can be resolved lazily.
this.configurationFiles.add(configurationFiles);
}
public void injars(Object inJarFiles)
throws ParseException
{
injars(null, inJarFiles);
}
public void injars(Map filterArgs, Object inJarFiles)
throws ParseException
{
// Just collect the arguments, so they can be resolved lazily.
this.inJarFiles.add(inJarFiles);
this.inJarFilters.add(filterArgs);
}
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment