blob: 34664b92196817d80d2a0917b668089f7dc3ab49 [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 com.android.SdkConstants
import com.android.annotations.NonNull
import com.android.annotations.Nullable
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.internal.core.GradleVariantConfiguration
import com.android.build.gradle.internal.profile.SpanRecorders
import com.android.build.gradle.internal.scope.AndroidTask
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.core.DefaultBuildType
import com.android.builder.core.VariantType
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 org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.internal.ConventionMapping
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.tooling.BuildException
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import static com.android.SdkConstants.FN_ANNOTATIONS_ZIP
import static com.android.SdkConstants.LIBS_FOLDER
import static com.android.builder.model.AndroidProject.FD_INTERMEDIATES
import static com.android.builder.model.AndroidProject.FD_OUTPUTS
/**
* TaskManager for creating tasks in an Android library project.
*/
class LibraryTaskManager extends TaskManager {
private static final String ANNOTATIONS = "annotations"
private Task assembleDefault;
public LibraryTaskManager (
Project project,
AndroidBuilder androidBuilder,
BaseExtension extension,
SdkHandler sdkHandler,
DependencyManager dependencyManager,
ToolingModelBuilderRegistry toolingRegistry) {
super(project, androidBuilder, extension, sdkHandler, dependencyManager, toolingRegistry)
}
@Override
public void createTasksForVariantData(
@NonNull TaskFactory tasks,
@NonNull BaseVariantData<? extends BaseVariantOutputData> variantData) {
LibraryVariantData libVariantData = variantData as LibraryVariantData
GradleVariantConfiguration variantConfig = variantData.variantConfiguration
DefaultBuildType buildType = variantConfig.buildType
VariantScope variantScope = variantData.getScope()
String fullName = variantConfig.fullName
String dirName = variantConfig.dirName
createAnchorTasks(tasks, variantScope)
createCheckManifestTask(tasks, variantScope)
// Add a task to create the res values
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_GENERATE_RES_VALUES_TASK) {
createGenerateResValuesTask(tasks, variantScope)
}
// Add a task to process the manifest(s)
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_MANIFEST_TASK) {
createMergeLibManifestsTask(tasks, variantScope)
}
// Add a task to compile renderscript files.
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_CREATE_RENDERSCRIPT_TASK) {
createRenderscriptTask(tasks, variantScope)
}
AndroidTask<MergeResources> packageRes = SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_RESOURCES_TASK) {
// 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.variantConfiguration.dirName/res"),
false /*includeDependencies*/,
false /*process9Patch*/);
if (!variantData.variantDependency.androidDependencies.isEmpty()) {
// 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) { MergeResources task ->
task.conventionMapping.publicFile = {
new File(variantScope.globalScope.intermediatesDir,
"$DIR_BUNDLES/${dirName}/${SdkConstants.FN_PUBLIC_TXT}")
}
}
return mergeResourceTask;
}
// Add a task to merge the assets folders
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_ASSETS_TASK) {
createMergeAssetsTask(tasks, variantScope)
}
// Add a task to create the BuildConfig class
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_BUILD_CONFIG_TASK) {
createBuildConfigTask(tasks, variantScope)
}
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_BACKPORT_RESOURCES_TASK) {
createPreprocessResourcesTask(tasks, variantScope)
}
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROCESS_RES_TASK) {
// 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("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"),
false /*generateResourcePackage*/,
)
// process java resources
createProcessJavaResTask(tasks, variantScope)
}
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_AIDL_TASK) {
createAidlTask(tasks, variantScope)
}
// Add a compile task
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_COMPILE_TASK) {
createJavaCompileTask(tasks, variantScope);
}
// package the prebuilt native libs into the bundle folder
Sync packageJniLibs = project.tasks.create(
"package${fullName.capitalize()}JniLibs",
Sync)
// Add dependencies on NDK tasks if NDK plugin is applied.
if (isNdkTaskNeeded) {
// Add NDK tasks
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_NDK_TASK) {
createNdkTasks(variantData);
packageJniLibs.dependsOn variantData.ndkCompileTask
packageJniLibs.from(variantData.ndkCompileTask.soFolder).include("**/*.so")
}
}
Sync packageRenderscript = SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGING_TASK) {
// package from 2 sources.
packageJniLibs.from(variantConfig.jniLibsList).include("**/*.so")
packageJniLibs.into(project.file(
"$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/jni"))
// package the renderscript header files files into the bundle folder
Sync packageRenderscript = project.tasks.create(
"package${fullName.capitalize()}Renderscript",
Sync)
// package from 3 sources. the order is important to make sure the override works well.
packageRenderscript.from(variantConfig.renderscriptSourceList).include("**/*.rsh")
packageRenderscript.into(project.file(
"$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/$SdkConstants.FD_RENDERSCRIPT"))
return packageRenderscript;
}
// merge consumer proguard files from different build types and flavors
MergeFileTask mergeProGuardFileTask = SpanRecorders.record(
ExecutionType.LIB_TASK_MANAGER_CREATE_MERGE_PROGUARD_FILE_TASK) {
MergeFileTask mergeProGuardFileTask = project.tasks.create(
"merge${fullName.capitalize()}ProguardFiles",
MergeFileTask)
mergeProGuardFileTask.inputFiles =
project.files(variantConfig.getConsumerProguardFiles()).files
mergeProGuardFileTask.outputFile = project.file(
"$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}/$LibraryBundle.FN_PROGUARD_TXT")
return mergeProGuardFileTask
}
// copy lint.jar into the bundle folder
Copy lintCopy = project.tasks.create(
"copy${fullName.capitalize()}Lint",
Copy)
lintCopy.dependsOn LINT_COMPILE
lintCopy.from("$project.buildDir/${FD_INTERMEDIATES}/lint/lint.jar")
lintCopy.into("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/$dirName")
Zip bundle = project.tasks.create(
"bundle${fullName.capitalize()}",
Zip)
libVariantData.generateAnnotationsTask = variantData.variantDependency.annotationsPresent ? createExtractAnnotations(
fullName, project, variantData) : null
if (libVariantData.generateAnnotationsTask != null) {
bundle.dependsOn(libVariantData.generateAnnotationsTask)
}
final boolean instrumented = variantConfig.buildType.isTestCoverageEnabled()
// data holding dependencies and input for the dex. This gets updated as new
// post-compilation steps are inserted between the compilation and dx.
PostCompilationData pcData = new PostCompilationData()
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_POST_COMPILATION_TASK) {
pcData.classGeneratingTask = [variantScope.javaCompileTask.name]
pcData.libraryGeneratingTask = Collections.singletonList(
variantData.variantDependency.packageConfiguration.buildDependencies)
pcData.inputFiles = {
return variantData.javaCompileTask.outputs.files.files
}
pcData.inputDir = {
return variantScope.javaOutputDir
}
pcData.inputLibraries = {
return Collections.emptyList()
}
// if needed, instrument the code
if (instrumented) {
pcData = createJacocoTask(tasks, variantScope, pcData);
}
}
if (buildType.isMinifyEnabled()) {
// run proguard on output of compile task
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PROGUARD_TASK) {
File outFile = maybeCreateProguardTasks(tasks, variantScope, pcData);
pcData.inputFiles = { [outFile] }
pcData.inputDir = null
pcData.inputLibraries = { [] }
}
} else {
// package the local jar in libs/
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_PACKAGE_LOCAL_JAR) {
Sync packageLocalJar = project.tasks.create(
"package${fullName.capitalize()}LocalJar",
Sync)
packageLocalJar.from(
DependencyManager.getPackagedLocalJarFileList(
variantData.variantDependency).toArray())
packageLocalJar.into(project.file(
"$project.buildDir/${FD_INTERMEDIATES}/$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.inputLibraries)
TaskManager.optionalDependsOn(packageLocalJar, pcData.libraryGeneratingTask)
pcData.libraryGeneratingTask = Collections.singletonList(packageLocalJar)
// jar the classes.
Jar jar = project.tasks.create("package${fullName.capitalize()}Jar", Jar);
jar.dependsOn variantScope.processJavaResourcesTask.name
// add the class files (whether they are instrumented or not.
jar.from(pcData.inputDir)
TaskManager.optionalDependsOn(jar, pcData.classGeneratingTask)
pcData.classGeneratingTask = Collections.singletonList(jar)
jar.from(variantScope.getJavaResourcesDestinationDir())
jar.destinationDir = project.file(
"$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}")
jar.archiveName = "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 (!((LibraryExtension) extension).packageBuildConfig) {
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
}
}
}
bundle.dependsOn packageRes.name, packageRenderscript, lintCopy, packageJniLibs, mergeProGuardFileTask
TaskManager.optionalDependsOn(bundle, pcData.classGeneratingTask)
TaskManager.optionalDependsOn(bundle, pcData.libraryGeneratingTask)
bundle.setDescription("Assembles a bundle containing the library in ${fullName.capitalize()}.");
bundle.destinationDir = project.file("$project.buildDir/${FD_OUTPUTS}/aar")
bundle.setArchiveName("${project.name}-${variantConfig.baseName}.${BuilderConstants.EXT_LIB_ARCHIVE}")
bundle.extension = BuilderConstants.EXT_LIB_ARCHIVE
bundle.from(project.file("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"))
bundle.from(project.file("$project.buildDir/${FD_INTERMEDIATES}/$ANNOTATIONS/${dirName}"))
// get the single output for now, though that may always be the case for a library.
LibVariantOutputData variantOutputData = libVariantData.outputs.get(0)
variantOutputData.packageLibTask = bundle
variantData.assembleVariantTask.dependsOn bundle
variantOutputData.assembleTask = variantData.assembleVariantTask
if (extension.defaultPublishConfig.equals(fullName)) {
VariantHelper.setupDefaultConfig(project,
variantData.variantDependency.packageConfiguration)
// add the artifact that will be published
project.artifacts.add("default", bundle)
getAssembleDefault().dependsOn variantData.assembleVariantTask
}
// also publish the artifact with its full config name
if (extension.publishNonDefault) {
project.artifacts.add(variantData.variantDependency.publishConfiguration.name, bundle)
bundle.classifier = variantData.variantDependency.publishConfiguration.name
}
// configure the variant to be testable.
variantConfig.output = new LibraryBundle(
bundle.archivePath,
project.file("$project.buildDir/${FD_INTERMEDIATES}/$DIR_BUNDLES/${dirName}"),
variantData.getName(),
project.getPath()) {
@Override
@Nullable
String getProjectVariant() {
return variantData.getName()
}
@NonNull
@Override
List<LibraryDependency> getDependencies() {
return variantConfig.directLibraries
}
@NonNull
@Override
List<? extends AndroidLibrary> getLibraryDependencies() {
return variantConfig.directLibraries
}
@NonNull
@Override
List<ManifestDependency> getManifestDependencies() {
return variantConfig.directLibraries
}
@Override
@Nullable
MavenCoordinates getRequestedCoordinates() {
return null
}
@Override
@Nullable
MavenCoordinates getResolvedCoordinates() {
return null
}
@Override
@NonNull
protected File getJarsRootFolder() {
return getFolder();
}
};
SpanRecorders.record(ExecutionType.LIB_TASK_MANAGER_CREATE_LINT_TASK) {
createLintTasks(tasks, variantScope);
}
}
public ExtractAnnotations createExtractAnnotations(
String fullName, Project project, BaseVariantData variantData) {
GradleVariantConfiguration config = variantData.variantConfiguration
String dirName = config.dirName
ExtractAnnotations task = project.tasks.create(
"extract${fullName.capitalize()}Annotations",
ExtractAnnotations)
task.description =
"Extracts Android annotations for the ${fullName} variant into the archive file"
task.group = BasePlugin.BUILD_GROUP
task.variant = variantData
task.destinationDir = project.file("$project.buildDir/${FD_INTERMEDIATES}/$ANNOTATIONS/${dirName}")
task.output = new File(task.destinationDir, FN_ANNOTATIONS_ZIP)
task.classDir = project.file("$project.buildDir/${FD_INTERMEDIATES}/classes/${variantData.variantConfiguration.dirName}")
task.source = variantData.getJavaSources()
task.encoding = extension.compileOptions.encoding
task.sourceCompatibility = extension.compileOptions.sourceCompatibility
conventionMapping(task).map("classpath") {
project.files(androidBuilder.getCompileClasspath(config))
}
task.dependsOn variantData.javaCompileTask
// 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 {
task.bootClasspath = androidBuilder.getBootClasspathAsStrings()
}
return task
}
private Task getAssembleDefault() {
if (assembleDefault == null) {
assembleDefault = project.tasks.findByName("assembleDefault");
}
return assembleDefault
}
private static ConventionMapping conventionMapping(Task task) {
task.conventionMapping
}
}