blob: 2f2c7a30b921d1c645c7cfe2d54cad9cf76cfa33 [file] [log] [blame]
/*
* 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.internal;
import static com.android.SdkConstants.FN_ANNOTATIONS_ZIP;
import static com.android.SdkConstants.LIBS_FOLDER;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.dsl.CoreBuildType;
import com.android.build.gradle.internal.scope.AndroidTask;
import com.android.build.gradle.internal.scope.ConventionMappingHelper;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.MergeFileTask;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.build.gradle.internal.variant.BaseVariantOutputData;
import com.android.build.gradle.internal.variant.LibVariantOutputData;
import com.android.build.gradle.internal.variant.LibraryVariantData;
import com.android.build.gradle.internal.variant.VariantHelper;
import com.android.build.gradle.tasks.ExtractAnnotations;
import com.android.build.gradle.tasks.MergeResources;
import com.android.builder.core.AndroidBuilder;
import com.android.builder.core.BuilderConstants;
import com.android.builder.dependency.LibraryBundle;
import com.android.builder.dependency.LibraryDependency;
import com.android.builder.dependency.ManifestDependency;
import com.android.builder.model.AndroidLibrary;
import com.android.builder.model.MavenCoordinates;
import com.android.builder.profile.ExecutionType;
import com.android.builder.profile.Recorder;
import com.android.builder.profile.ThreadRecorder;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.tasks.Copy;
import org.gradle.api.tasks.Sync;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.bundling.Zip;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.tooling.BuildException;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
/**
* TaskManager for creating tasks in an Android library project.
*/
public class LibraryTaskManager extends TaskManager {
private static final String ANNOTATIONS = "annotations";
private Task assembleDefault;
public LibraryTaskManager (
Project project,
AndroidBuilder androidBuilder,
AndroidConfig extension,
SdkHandler sdkHandler,
DependencyManager dependencyManager,
ToolingModelBuilderRegistry toolingRegistry) {
super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry);
}
@Override
public void createTasksForVariantData(
@NonNull final TaskFactory tasks,
@NonNull final BaseVariantData<? extends BaseVariantOutputData> variantData) {
final LibraryVariantData libVariantData = (LibraryVariantData) variantData;
final GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
CoreBuildType buildType = variantConfig.getBuildType();
final VariantScope variantScope = variantData.getScope();
final String dirName = variantConfig.getDirName();
createAnchorTasks(tasks, variantScope);
createCheckManifestTask(tasks, variantScope);
// Add a task to create the res values
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
createGenerateResValuesTask(tasks, variantScope);
return null;
}
});
// Add a task to process the manifest(s)
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
createMergeLibManifestsTask(tasks, variantScope);
return null;
}
});
// Add a task to compile renderscript files.
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
createRenderscriptTask(tasks, variantScope);
return null;
}
});
AndroidTask<MergeResources> packageRes = ThreadRecorder.get().record(
ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK,
new Recorder.Block<AndroidTask<MergeResources>>() {
@Override
public AndroidTask<MergeResources> call() throws Exception {
// Create a merge task to only merge the resources from this library and not
// the dependencies. This is what gets packaged in the aar.
AndroidTask<MergeResources> mergeResourceTask =
basicCreateMergeResourcesTask(
tasks,
variantScope,
"package",
new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + variantScope
.getVariantConfiguration().getDirName() +
"/res"),
false /*includeDependencies*/,
false /*process9Patch*/);
if (variantData.getVariantDependency().hasNonOptionalLibraries()) {
// Add a task to merge the resource folders, including the libraries, in order to
// generate the R.txt file with all the symbols, including the ones from
// the dependencies.
createMergeResourcesTask(tasks, variantScope);
}
mergeResourceTask.configure(tasks,
new Action<Task>() {
@Override
public void execute(Task task) {
MergeResources mergeResourcesTask = (MergeResources) task;
mergeResourcesTask.setPublicFile(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName + "/" +
SdkConstants.FN_PUBLIC_TXT));
}
});
return mergeResourceTask;
}
});
// Add a task to merge the assets folders
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() {
createMergeAssetsTask(tasks, variantScope);
return null;
}
});
// Add a task to create the BuildConfig class
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
createBuildConfigTask(tasks, variantScope);
return null;
}
});
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROCESS_RES_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
// Add a task to generate resource source files, directing the location
// of the r.txt file to be directly in the bundle.
createProcessResTask(tasks, variantScope,
new File(variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName),
false /*generateResourcePackage*/);
// process java resources
createProcessJavaResTasks(tasks, variantScope);
return null;
}
});
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_AIDL_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
createAidlTask(tasks, variantScope);
return null;
}
});
// Add a compile task
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_COMPILE_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
AndroidTask<JavaCompile> javacTask = createJavacTask(tasks, variantScope);
TaskManager.setJavaCompilerTask(javacTask, tasks, variantScope);
return null;
}
});
// package the prebuilt native libs into the bundle folder
final Sync packageJniLibs = project.getTasks().create(
variantScope.getTaskName("package", "JniLibs"),
Sync.class);
// Add dependencies on NDK tasks if NDK plugin is applied.
if (isNdkTaskNeeded) {
// Add NDK tasks
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_NDK_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
createNdkTasks(variantScope);
packageJniLibs.dependsOn(variantData.ndkCompileTask);
packageJniLibs.from(variantData.ndkCompileTask.getSoFolder())
.include("**/*.so");
return null;
}
});
} else {
if (variantData.compileTask != null) {
variantData.compileTask.dependsOn(getNdkBuildable(variantData));
} else {
variantScope.getCompileTask().dependsOn(tasks, getNdkBuildable(variantData));
}
}
Sync packageRenderscript = ThreadRecorder.get().record(
ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGING_TASK,
new Recorder.Block<Sync>() {
@Override
public Sync call() throws Exception {
// package from 2 sources.
packageJniLibs.from(variantConfig.getJniLibsList())
.include("**/*.so");
packageJniLibs.into(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName + "/jni"));
// package the renderscript header files files into the bundle folder
Sync packageRenderscript = project.getTasks().create(
variantScope.getTaskName("package", "Renderscript"), Sync.class);
// package from 3 sources. the order is important to make sure the override works well.
packageRenderscript.from(variantConfig.getRenderscriptSourceList())
.include("**/*.rsh");
packageRenderscript.into(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName + "/" + SdkConstants.FD_RENDERSCRIPT));
return packageRenderscript;
}
});
// merge consumer proguard files from different build types and flavors
MergeFileTask mergeProGuardFileTask = ThreadRecorder.get().record(
ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_PROGUARD_FILE_TASK,
new Recorder.Block<MergeFileTask>() {
@Override
public MergeFileTask call() throws Exception {
MergeFileTask mergeProGuardFileTask = project.getTasks().create(
variantScope.getTaskName("merge", "ProguardFiles"),
MergeFileTask.class);
mergeProGuardFileTask.setVariantName(variantConfig.getFullName());
mergeProGuardFileTask.setInputFiles(
project.files(variantConfig.getConsumerProguardFiles())
.getFiles());
mergeProGuardFileTask.setOutputFile(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName + "/" + LibraryBundle.FN_PROGUARD_TXT));
return mergeProGuardFileTask;
}
});
// copy lint.jar into the bundle folder
Copy lintCopy = project.getTasks().create(
variantScope.getTaskName("copy", "Lint"), Copy.class);
lintCopy.dependsOn(LINT_COMPILE);
lintCopy.from(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
"lint/lint.jar"));
lintCopy.into(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName));
final Zip bundle = project.getTasks().create(variantScope.getTaskName("bundle"), Zip.class);
if (variantData.getVariantDependency().isAnnotationsPresent()) {
libVariantData.generateAnnotationsTask =
createExtractAnnotations(project, variantData);
}
if (libVariantData.generateAnnotationsTask != null) {
bundle.dependsOn(libVariantData.generateAnnotationsTask);
}
final boolean instrumented = variantConfig.getBuildType().isTestCoverageEnabled();
// data holding dependencies and input for the dex. This gets updated as new
// post-compilation steps are inserted between the compilation and dx.
final PostCompilationData pcDataTemp = new PostCompilationData();
final PostCompilationData pcData = ThreadRecorder.get().record(
ExecutionType.LIB_TASK_MANAGER_CREATE_POST_COMPILATION_TASK,
new Recorder.Block<PostCompilationData>() {
@Override
public PostCompilationData call() throws Exception {
pcDataTemp.setClassGeneratingTasks(Collections.singletonList(
variantScope.getJavacTask().getName()));
pcDataTemp.setLibraryGeneratingTasks(Collections.singletonList(
variantData.getVariantDependency().getPackageConfiguration()
.getBuildDependencies()));
pcDataTemp.setInputFilesCallable(new Callable<List<File>>() {
@Override
public List<File> call() throws Exception {
return new ArrayList<File>(
variantData.javacTask.getOutputs().getFiles().getFiles());
}
});
pcDataTemp.setInputDir(variantScope.getJavaOutputDir());
pcDataTemp.setInputLibraries(Collections.<File>emptyList());
// if needed, instrument the code
if (instrumented) {
return createJacocoTask(tasks, variantScope, pcDataTemp);
}
return pcDataTemp;
}
});
checkState(pcData != null);
if (buildType.isMinifyEnabled()) {
// run proguard on output of compile task
ThreadRecorder.get().record(
ExecutionType.LIB_TASK_MANAGER_CREATE_PROGUARD_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
File outFile = maybeCreateProguardTasks(tasks, variantScope,
pcData);
checkNotNull(outFile);
pcData.setInputFiles(Collections.singletonList(outFile));
pcData.setInputDirCallable(null);
pcData.setInputLibraries(Collections.<File>emptyList());
return null;
}
});
} else {
// package the local jar in libs/
ThreadRecorder.get().record(
ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGE_LOCAL_JAR,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
Sync packageLocalJar = project.getTasks().create(
variantScope.getTaskName("package", "LocalJar"), Sync.class);
packageLocalJar.from(
DependencyManager
.getPackagedLocalJarFileList(
variantData.getVariantDependency())
.toArray());
packageLocalJar.into(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName + "/" + LIBS_FOLDER));
// add the input libraries. This is only going to be the agent jar if applicable
// due to how inputLibraries is initialized.
// TODO: clean this.
packageLocalJar.from(pcData.getInputLibrariesCallable());
TaskManager.optionalDependsOn(
packageLocalJar,
pcData.getLibraryGeneratingTasks());
pcData.setLibraryGeneratingTasks(
Collections.singletonList(packageLocalJar));
// jar the classes.
Jar jar = project.getTasks().create(
variantScope.getTaskName("package", "Jar"), Jar.class);
jar.dependsOn(variantScope.getMergeJavaResourcesTask().getName());
// add the class files (whether they are instrumented or not.
jar.from(pcData.getInputDirCallable());
TaskManager.optionalDependsOn(jar, pcData.getClassGeneratingTasks());
pcData.setClassGeneratingTasks(Collections.singletonList(jar));
jar.from(variantScope.getJavaResourcesDestinationDir());
jar.setDestinationDir(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName));
jar.setArchiveName("classes.jar");
String packageName = variantConfig.getPackageFromManifest();
if (packageName == null) {
throw new BuildException("Failed to read manifest", null);
}
packageName = packageName.replace(".", "/");
jar.exclude(packageName + "/R.class");
jar.exclude(packageName + "/R$*.class");
if (!getExtension().getPackageBuildConfig()) {
jar.exclude(packageName + "/Manifest.class");
jar.exclude(packageName + "/Manifest$*.class");
jar.exclude(packageName + "/BuildConfig.class");
}
if (libVariantData.generateAnnotationsTask != null) {
// In case extract annotations strips out private typedef annotation classes
jar.dependsOn(libVariantData.generateAnnotationsTask);
}
return null;
}
});
}
bundle.dependsOn(packageRes.getName(), packageRenderscript, lintCopy, packageJniLibs,
mergeProGuardFileTask);
TaskManager.optionalDependsOn(bundle, pcData.getClassGeneratingTasks());
TaskManager.optionalDependsOn(bundle, pcData.getLibraryGeneratingTasks());
bundle.setDescription("Assembles a bundle containing the library in " +
variantConfig.getFullName() + ".");
bundle.setDestinationDir(new File(variantScope.getGlobalScope().getOutputsDir(), "aar"));
bundle.setArchiveName(project.getName() + "-" + variantConfig.getBaseName() + "."
+ BuilderConstants.EXT_LIB_ARCHIVE);
bundle.setExtension(BuilderConstants.EXT_LIB_ARCHIVE);
bundle.from(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName));
bundle.from(new File(
variantScope.getGlobalScope().getIntermediatesDir(),
ANNOTATIONS + "/" + dirName));
// get the single output for now, though that may always be the case for a library.
LibVariantOutputData variantOutputData = libVariantData.getOutputs().get(0);
variantOutputData.packageLibTask = bundle;
variantData.assembleVariantTask.dependsOn(bundle);
variantOutputData.assembleTask = variantData.assembleVariantTask;
if (getExtension().getDefaultPublishConfig().equals(variantConfig.getFullName())) {
VariantHelper.setupDefaultConfig(project,
variantData.getVariantDependency().getPackageConfiguration());
// add the artifact that will be published
project.getArtifacts().add("default", bundle);
getAssembleDefault().dependsOn(variantData.assembleVariantTask);
}
// also publish the artifact with its full config name
if (getExtension().getPublishNonDefault()) {
project.getArtifacts().add(
variantData.getVariantDependency().getPublishConfiguration().getName(), bundle);
bundle.setClassifier(
variantData.getVariantDependency().getPublishConfiguration().getName());
}
// configure the variant to be testable.
variantConfig.setOutput(new LibraryBundle(
bundle.getArchivePath(),
new File(variantScope.getGlobalScope().getIntermediatesDir(),
DIR_BUNDLES + "/" + dirName),
variantData.getName(),
project.getPath()) {
@Override
@Nullable
public String getProjectVariant() {
return variantData.getName();
}
@NonNull
@Override
public List<LibraryDependency> getDependencies() {
return variantConfig.getDirectLibraries();
}
@NonNull
@Override
public List<? extends AndroidLibrary> getLibraryDependencies() {
return variantConfig.getDirectLibraries();
}
@NonNull
@Override
public List<? extends ManifestDependency> getManifestDependencies() {
return variantConfig.getDirectLibraries();
}
@Override
@Nullable
public MavenCoordinates getRequestedCoordinates() {
return null;
}
@Override
@Nullable
public MavenCoordinates getResolvedCoordinates() {
return null;
}
@Override
@NonNull
protected File getJarsRootFolder() {
return getFolder();
}
@Override
public boolean isOptional() {
return false;
}
});
ThreadRecorder.get().record(ExecutionType.LIB_TASK_MANAGER_CREATE_LINT_TASK,
new Recorder.Block<Void>() {
@Override
public Void call() throws Exception {
createLintTasks(tasks, variantScope);
return null;
}
});
}
public ExtractAnnotations createExtractAnnotations(
final Project project,
final BaseVariantData variantData) {
final GradleVariantConfiguration config = variantData.getVariantConfiguration();
final ExtractAnnotations task = project.getTasks().create(
variantData.getScope().getTaskName("extract", "Annotations"),
ExtractAnnotations.class);
task.setDescription(
"Extracts Android annotations for the " + variantData.getVariantConfiguration()
.getFullName()
+ " variant into the archive file");
task.setGroup(BasePlugin.BUILD_GROUP);
task.variant = variantData;
task.setDestinationDir(new File(
variantData.getScope().getGlobalScope().getIntermediatesDir(),
ANNOTATIONS + "/" + config.getDirName()));
task.output = new File(task.getDestinationDir(), FN_ANNOTATIONS_ZIP);
task.classDir = new File(variantData.getScope().getGlobalScope().getIntermediatesDir(),
"classes/" + variantData.getVariantConfiguration().getDirName());
task.setSource(variantData.getJavaSources());
task.encoding = getExtension().getCompileOptions().getEncoding();
task.setSourceCompatibility(
getExtension().getCompileOptions().getSourceCompatibility().toString());
ConventionMappingHelper.map(task, "classpath", new Callable<ConfigurableFileCollection>() {
@Override
public ConfigurableFileCollection call() throws Exception {
return project.files(androidBuilder.getCompileClasspath(config));
}
});
task.dependsOn(variantData.getScope().getJavacTask().getName());
// Setup the boot classpath just before the task actually runs since this will
// force the sdk to be parsed. (Same as in compileTask)
task.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
if (task instanceof ExtractAnnotations) {
ExtractAnnotations extractAnnotations = (ExtractAnnotations) task;
extractAnnotations.bootClasspath = androidBuilder.getBootClasspathAsStrings();
}
}
});
return task;
}
private Task getAssembleDefault() {
if (assembleDefault == null) {
assembleDefault = project.getTasks().findByName("assembleDefault");
}
return assembleDefault;
}
}