com.android.build.gradle.tasks.AndroidProGuardTask Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-core Show documentation
Show all versions of gradle-core Show documentation
Amazon Core library to build Android Gradle plugin.
The newest version!
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.tasks;
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES;
import static com.android.builder.model.AndroidProject.FD_OUTPUTS;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.internal.DependencyManager;
import com.android.build.gradle.internal.PostCompilationData;
import com.android.build.gradle.internal.TaskManager;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.FileSupplier;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.build.gradle.internal.variant.BaseVariantOutputData;
import com.android.build.gradle.internal.variant.LibraryVariantData;
import com.android.builder.core.VariantConfiguration;
import com.android.builder.tasks.Job;
import com.android.builder.tasks.JobContext;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import org.gradle.api.Action;
import org.gradle.api.Task;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskAction;
import org.gradle.tooling.BuildException;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import groovy.lang.Closure;
import proguard.ParseException;
import proguard.gradle.ProGuardTask;
/**
* Decoration for the {@link ProGuardTask} so it implements shared interfaces with our custom
* tasks.
*/
public class AndroidProGuardTask extends ProGuardTask implements FileSupplier {
/**
* resulting obfuscation mapping file.
*/
@Nullable
@InputFile
@Optional
File mappingFile;
/**
* if this is a test related proguard task, this will point to tested application mapping file
* which can be absent in case the tested application did not request obfuscation.
*/
@Nullable
@InputFile
@Optional
File testedAppMappingFile;
@Override
public void printmapping(Object printMapping) throws ParseException {
mappingFile = (File) printMapping;
super.printmapping(printMapping);
}
@Override
public void applymapping(Object applyMapping) throws ParseException {
testedAppMappingFile = (File) applyMapping;
}
@Override
public File get() {
return mappingFile;
}
@NonNull
@Override
public Task getTask() {
return this;
}
@Override
@TaskAction
public void proguard() throws IOException, ParseException {
final Job job = new Job(getName(),
new com.android.builder.tasks.Task() {
@Override
public void run(@NonNull Job job,
@NonNull JobContext context) throws IOException {
try {
AndroidProGuardTask.this.doMinification();
} catch (ParseException e) {
throw new IOException(e);
}
}
});
try {
SimpleWorkQueue.push(job);
// wait for the task completion.
job.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
public void doMinification() throws ParseException, IOException {
// only set the tested application mapping file if it exists (it must at this point or that
// means the tested application did not request obfuscation).
if (testedAppMappingFile != null && testedAppMappingFile.exists()) {
super.applymapping(testedAppMappingFile);
}
super.proguard();
}
public static class ConfigAction implements TaskConfigAction {
private VariantScope scope;
private Callable inputDir;
private Callable javaResourcesInputDir;
private Callable> inputLibraries;
public ConfigAction(VariantScope scope, final PostCompilationData pcData) {
this.scope = scope;
inputDir = pcData.getInputDirCallable();
javaResourcesInputDir = pcData.getJavaResourcesInputDirCallable();
inputLibraries = pcData.getInputLibrariesCallable();
}
@Override
public String getName() {
return scope.getTaskName("proguard");
}
@Override
public Class getType() {
return AndroidProGuardTask.class;
}
@Override
public void execute(final AndroidProGuardTask proguardTask) {
final BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
final VariantConfiguration variantConfig = scope.getVariantData().getVariantConfiguration();
final BaseVariantData testedVariantData = scope.getTestedVariantData();
// use single output for now.
final BaseVariantOutputData variantOutputData = scope.getVariantData().getOutputs().get(0);
if (testedVariantData != null) {
proguardTask.dependsOn(testedVariantData.getScope().getObfuscationTask().getName());
}
variantData.obfuscationTask = proguardTask;
variantData.mappingFileProviderTask = proguardTask;
// --- Output File ---
final File outFile = variantData instanceof LibraryVariantData ? new File(
String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_INTERMEDIATES + "/"
+ TaskManager.DIR_BUNDLES + "/" + variantData.getVariantConfiguration()
.getDirName() + "/classes.jar") : new File(
String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_INTERMEDIATES
+ "/classes-proguard/" + variantData.getVariantConfiguration().getDirName()
+ "/classes.jar");
variantData.obfuscatedClassesJar = outFile;
// --- Proguard Config ---
try {
if (testedVariantData != null) {
// Don't remove any code in tested app.
proguardTask.dontshrink();
proguardTask.dontoptimize();
// We can't call dontobfuscate, since that would make ProGuard ignore the mapping file.
proguardTask.keep("class * {*;}");
proguardTask.keep("interface * {*;}");
proguardTask.keep("enum * {*;}");
proguardTask.keepattributes();
// Input the mapping from the tested app so that we can deal with obfuscated code.
proguardTask.applymapping(testedVariantData.getMappingFile());
// All -dontwarn rules for test dependencies should go in here:
proguardTask.configuration(
testedVariantData.getVariantConfiguration().getTestProguardFiles());
} else {
if (variantConfig.isTestCoverageEnabled()) {
// when collecting coverage, don't remove the JaCoCo runtime
proguardTask.keep("class com.vladium.** {*;}");
proguardTask.keep("class org.jacoco.** {*;}");
proguardTask.keep("interface org.jacoco.** {*;}");
proguardTask.dontwarn("org.jacoco.**");
}
proguardTask.configuration(new Callable>() {
@Override
public Collection call() throws Exception {
List proguardFiles = variantConfig.getProguardFiles(true,
Collections.singletonList(getDefaultProguardFile(
TaskManager.DEFAULT_PROGUARD_CONFIG_FILE)));
proguardFiles.add(
variantOutputData.processResourcesTask.getProguardOutputFile());
return proguardFiles;
}
});
}
// --- 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 (!scope.getGlobalScope().getExtension().getPackageBuildConfig()) {
exclude += (", !" + packageName + "/Manifest.class");
exclude += (", !" + packageName + "/Manifest$*.class");
exclude += (", !" + packageName + "/BuildConfig.class");
}
proguardTask.injars(ImmutableMap.of("filter", exclude), inputDir);
// include R files and such for compilation
String include = exclude.replace("!", "");
LinkedHashMap map1 = new LinkedHashMap(1);
map1.put("filter", include);
proguardTask.libraryjars(map1, inputDir);
// injar: the local dependencies
Callable inJars = new Callable>() {
@Override
public List call() throws Exception {
return DependencyManager
.getPackagedLocalJarFileList(variantData.getVariantDependency());
}
};
proguardTask.injars(ImmutableMap.of("filter", "!META-INF/MANIFEST.MF"), inJars);
// libjar: the library dependencies. In this case we take all the compile-scope
// dependencies
Callable libJars = new Callable>() {
@Override
public Iterable call() throws Exception {
// get all the compiled jar.
Set compiledJars = scope.getGlobalScope().getAndroidBuilder()
.getCompileClasspath(variantConfig);
// and remove local jar that are also packaged
final List localJars = DependencyManager
.getPackagedLocalJarFileList(variantData.getVariantDependency());
return Iterables.filter(compiledJars, new Predicate() {
@Override
public boolean apply(File file) {
return !localJars.contains(file);
}
});
}
};
proguardTask.libraryjars(ImmutableMap.of("filter", "!META-INF/MANIFEST.MF"), libJars);
// ensure local jars keep their package names
proguardTask.keeppackagenames();
} else {
// injar: the compilation output
proguardTask.injars(inputDir);
if (javaResourcesInputDir != null) {
proguardTask.injars(javaResourcesInputDir);
}
// injar: the packaged dependencies
LinkedHashMap map = new LinkedHashMap(1);
map.put("filter", "!META-INF/MANIFEST.MF");
proguardTask.injars(map, inputLibraries);
// the provided-only jars as libraries.
Callable> libJars = new Callable>() {
@Override
public List call() throws Exception {
return variantData.getVariantConfiguration().getProvidedOnlyJars();
}
};
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(new Action() {
@Override
public void execute(Task proguardTask) {
for (String runtimeJar : scope.getGlobalScope().getAndroidBuilder()
.getBootClasspathAsStrings()) {
try {
((AndroidProGuardTask)proguardTask).libraryjars(runtimeJar);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
});
if (testedVariantData != null) {
// input the tested app as library
proguardTask.libraryjars(testedVariantData.javacTask.getDestinationDir());
// including its dependencies
Callable testedPackagedJars = new Callable>() {
@Override
public Set call() throws Exception {
return scope.getGlobalScope().getAndroidBuilder()
.getPackagedJars(testedVariantData.getVariantConfiguration());
}
};
LinkedHashMap map = new LinkedHashMap(1);
map.put("filter", "!META-INF/MANIFEST.MF");
proguardTask.libraryjars(map, testedPackagedJars);
}
// --- Out files ---
proguardTask.outjars(outFile);
final File proguardOut = new File(
String.valueOf(scope.getGlobalScope().getBuildDir()) + "/" + FD_OUTPUTS
+ "/mapping/" + variantData.getVariantConfiguration().getDirName());
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(new Closure(this, this) {
public Boolean doCall(Task it) {
return proguardOut.mkdirs();
}
public Boolean doCall() {
return doCall(null);
}
});
} catch (ParseException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private File getDefaultProguardFile(String name) {
File sdkDir = scope.getGlobalScope().getSdkHandler().getAndCheckSdkFolder();
return new File(sdkDir,
SdkConstants.FD_TOOLS + File.separatorChar
+ SdkConstants.FD_PROGUARD + File.separatorChar
+ name);
}
}
}