blob: 7f6ec2f27cd5b46e40afdcfbfbbf338303809f38 [file] [log] [blame]
package com.android.build.gradle.tasks.factory;
import static com.android.builder.core.VariantType.LIBRARY;
import static com.android.builder.core.VariantType.UNIT_TEST;
import static com.google.common.base.Preconditions.checkNotNull;
import com.android.annotations.NonNull;
import com.android.build.gradle.AndroidGradleOptions;
import com.android.build.gradle.internal.CompileOptions;
import com.android.build.gradle.internal.LoggerWrapper;
import com.android.build.gradle.internal.dsl.CoreAnnotationProcessorOptions;
import com.android.build.gradle.internal.scope.ConventionMappingHelper;
import com.android.build.gradle.internal.scope.GlobalScope;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.builder.model.AndroidLibrary;
import com.android.builder.model.SyncIssue;
import com.android.utils.FileUtils;
import com.android.utils.ILogger;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.file.ConfigurableFileTree;
import org.gradle.api.file.FileCollection;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* Configuration Action for a JavaCompile task.
*/
public class JavaCompileConfigAction implements TaskConfigAction<AndroidJavaCompile> {
private static final ILogger LOG = LoggerWrapper.getLogger(JavaCompileConfigAction.class);
private VariantScope scope;
public JavaCompileConfigAction(VariantScope scope) {
this.scope = scope;
}
@NonNull
@Override
public String getName() {
return scope.getTaskName("compile", "JavaWithJavac");
}
@NonNull
@Override
public Class<AndroidJavaCompile> getType() {
return AndroidJavaCompile.class;
}
@Override
public void execute(@NonNull final AndroidJavaCompile javacTask) {
final BaseVariantData testedVariantData = scope.getTestedVariantData();
scope.getVariantData().javacTask = javacTask;
javacTask.compileSdkVersion = scope.getGlobalScope().getExtension().getCompileSdkVersion();
javacTask.mBuildContext = scope.getInstantRunBuildContext();
// We can't just pass the collection directly, as the instanceof check in the incremental
// compile doesn't work recursively currently, so every ConfigurableFileTree needs to be
// directly in the source array.
for (ConfigurableFileTree fileTree: scope.getVariantData().getJavaSources()) {
javacTask.source(fileTree);
}
// javac 1.8 may generate code that uses class not available in android.jar. This is fine
// if jack is used to compile code for the app and this compile task is created only for
// unit test. In which case, we want to keep the default bootstrap classpath.
final boolean keepDefaultBootstrap =
scope.getVariantConfiguration().getJackOptions().isEnabled()
&& JavaVersion.current().isJava8Compatible();
if (!keepDefaultBootstrap) {
// Set boot classpath if we don't need to keep the default. Otherwise, this is added as
// normal classpath.
javacTask.getOptions().setBootClasspath(
Joiner.on(File.pathSeparator).join(
scope.getGlobalScope().getAndroidBuilder()
.getBootClasspathAsStrings(false)));
}
ConventionMappingHelper.map(javacTask, "classpath", new Callable<FileCollection>() {
@Override
public FileCollection call() {
FileCollection classpath = scope.getJavaClasspath();
Project project = scope.getGlobalScope().getProject();
if (keepDefaultBootstrap) {
classpath = classpath.plus(project.files(
scope.getGlobalScope().getAndroidBuilder().getBootClasspath(false)));
}
if (testedVariantData != null) {
// For libraries, the classpath from androidBuilder includes the library
// output (bundle/classes.jar) as a normal dependency. In unit tests we
// don't want to package the jar at every run, so we use the *.class
// files instead.
if (!testedVariantData.getType().equals(LIBRARY)
|| scope.getVariantData().getType().equals(UNIT_TEST)) {
classpath = classpath.plus(project.files(
testedVariantData.getScope().getJavaClasspath(),
testedVariantData.getScope().getJavaOutputDir(),
testedVariantData.getScope().getJavaDependencyCache()));
}
if (scope.getVariantData().getType().equals(UNIT_TEST)
&& testedVariantData.getType().equals(LIBRARY)) {
// The bundled classes.jar may exist, but it's probably old. Don't
// use it, we already have the *.class files in the classpath.
AndroidLibrary testedLibrary =
testedVariantData.getVariantConfiguration().getOutput();
if (testedLibrary != null) {
File jarFile = testedLibrary.getJarFile();
classpath = classpath.minus(project.files(jarFile));
}
}
}
return classpath;
}
});
javacTask.setDestinationDir(scope.getJavaOutputDir());
javacTask.setDependencyCacheDir(scope.getJavaDependencyCache());
CompileOptions compileOptions = scope.getGlobalScope().getExtension().getCompileOptions();
AbstractCompilesUtil.configureLanguageLevel(
javacTask,
compileOptions,
scope.getGlobalScope().getExtension().getCompileSdkVersion(),
scope.getVariantConfiguration().getJackOptions().isEnabled());
javacTask.getOptions().setEncoding(compileOptions.getEncoding());
GlobalScope globalScope = scope.getGlobalScope();
Project project = scope.getGlobalScope().getProject();
boolean incremental;
CoreAnnotationProcessorOptions annotationProcessorOptions =
scope.getVariantConfiguration().getJavaCompileOptions()
.getAnnotationProcessorOptions();
checkNotNull(annotationProcessorOptions.getIncludeCompileClasspath());
Collection<File> processorPath =
Lists.newArrayList(
scope.getVariantData().getVariantDependency()
.resolveAndGetAnnotationProcessorClassPath(
annotationProcessorOptions.getIncludeCompileClasspath(),
scope.getGlobalScope().getAndroidBuilder().getErrorReporter()));
if (compileOptions.getIncremental() != null) {
incremental = compileOptions.getIncremental();
LOG.info("Incremental flag set to %1$b in DSL", incremental);
} else {
if (globalScope.getExtension().getDataBinding().isEnabled()
|| !processorPath.isEmpty()
|| project.getPlugins().hasPlugin("com.neenbedankt.android-apt")
|| project.getPlugins().hasPlugin("me.tatarka.retrolambda")) {
incremental = false;
LOG.info("Incremental Java compilation disabled in variant %1$s "
+ "as you are using an incompatible plugin",
scope.getVariantConfiguration().getFullName());
} else if (scope.getTestedVariantData() != null) {
// Incremental javac is currently (Gradle 2.14-2.14.1) broken for invocations
// that have directories on their classpath.
incremental = false;
} else {
// For now, default to true, unless the use uses several source folders,
// in that case, we cannot guarantee that the incremental java works fine.
// some source folders may be configured but do not exist, in that case, don't
// use as valid source folders to determine whether or not we should turn on
// incremental compilation.
List<File> sourceFolders = new ArrayList<File>();
for (ConfigurableFileTree sourceFolder
: scope.getVariantData().getUserJavaSources()) {
if (sourceFolder.getDir().exists()) {
sourceFolders.add(sourceFolder.getDir());
}
}
incremental = sourceFolders.size() == 1;
if (sourceFolders.size() > 1) {
LOG.info("Incremental Java compilation disabled in variant %1$s "
+ "as you are using %2$d source folders : %3$s",
scope.getVariantConfiguration().getFullName(),
sourceFolders.size(), Joiner.on(',').join(sourceFolders));
}
}
}
if (AndroidGradleOptions.isJavaCompileIncrementalPropertySet(project)) {
scope.getGlobalScope().getAndroidBuilder().getErrorReporter().handleSyncError(
null,
SyncIssue.TYPE_GENERIC,
String.format(
"The %s property has been replaced by a DSL property. Please add the "
+ "following to your build.gradle instead:\n"
+ "android {\n"
+ " compileOptions.incremental = false\n"
+ "}",
AndroidGradleOptions.PROPERTY_INCREMENTAL_JAVA_COMPILE));
}
if (incremental) {
LOG.info("Using incremental javac compilation.");
javacTask.getOptions().setIncremental(true);
} else {
LOG.info("Not using incremental javac compilation.");
}
if (!processorPath.isEmpty()) {
if (Boolean.TRUE.equals(annotationProcessorOptions.getIncludeCompileClasspath())) {
processorPath.addAll(javacTask.getClasspath().getFiles());
}
javacTask.getOptions().getCompilerArgs().add("-processorpath");
javacTask.getOptions().getCompilerArgs().add(FileUtils.joinFilePaths(processorPath));
}
if (!annotationProcessorOptions.getClassNames().isEmpty()) {
javacTask.getOptions().getCompilerArgs().add("-processor");
javacTask.getOptions().getCompilerArgs().add(
Joiner.on(',').join(annotationProcessorOptions.getClassNames()));
}
if (!annotationProcessorOptions.getArguments().isEmpty()) {
for (Map.Entry<String, String> arg :
annotationProcessorOptions.getArguments().entrySet()) {
javacTask.getOptions().getCompilerArgs().add(
"-A" + arg.getKey() + "=" + arg.getValue());
}
}
// Create directory for output of annotation processor.
javacTask.doFirst(task -> {
FileUtils.mkdirs(scope.getAnnotationProcessorOutputDir());
});
javacTask.getOptions().getCompilerArgs().add("-s");
javacTask.getOptions().getCompilerArgs().add(
scope.getAnnotationProcessorOutputDir().getAbsolutePath());
}
}