Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.android.build.gradle.internal.TaskManager Maven / Gradle / Ivy
/*
* Copyright (C) 2014 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.build.OutputFile.DENSITY;
import static com.android.builder.core.BuilderConstants.CONNECTED;
import static com.android.builder.core.BuilderConstants.DEVICE;
import static com.android.builder.core.VariantType.ANDROID_TEST;
import static com.android.builder.core.VariantType.LIBRARY;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import android.databinding.tool.DataBindingBuilder;
import android.databinding.tool.DataBindingCompilerArgs;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.api.transform.QualifiedContent;
import com.android.build.api.transform.QualifiedContent.ContentType;
import com.android.build.api.transform.QualifiedContent.DefaultContentType;
import com.android.build.api.transform.QualifiedContent.Scope;
import com.android.build.api.transform.Transform;
import com.android.build.gradle.AndroidConfig;
import com.android.build.gradle.AndroidGradleOptions;
import com.android.build.gradle.ProguardFiles;
import com.android.build.gradle.internal.core.Abi;
import com.android.build.gradle.internal.core.GradleVariantConfiguration;
import com.android.build.gradle.internal.coverage.JacocoReportTask;
import com.android.build.gradle.internal.dependency.VariantDependencies;
import com.android.build.gradle.internal.dsl.AbiSplitOptions;
import com.android.build.gradle.internal.dsl.AnnotationProcessorOptions;
import com.android.build.gradle.internal.dsl.CoreAnnotationProcessorOptions;
import com.android.build.gradle.internal.dsl.CoreBuildType;
import com.android.build.gradle.internal.dsl.CoreJackOptions;
import com.android.build.gradle.internal.dsl.CoreJavaCompileOptions;
import com.android.build.gradle.internal.dsl.CoreSigningConfig;
import com.android.build.gradle.internal.dsl.PackagingOptions;
import com.android.build.gradle.internal.incremental.BuildInfoLoaderTask;
import com.android.build.gradle.internal.incremental.BuildInfoWriterTask;
import com.android.build.gradle.internal.incremental.InstantRunAnchorTaskConfigAction;
import com.android.build.gradle.internal.incremental.InstantRunPatchingPolicy;
import com.android.build.gradle.internal.model.CoreExternalNativeBuild;
import com.android.build.gradle.internal.ndk.NdkHandler;
import com.android.build.gradle.internal.pipeline.ExtendedContentType;
import com.android.build.gradle.internal.pipeline.OriginalStream;
import com.android.build.gradle.internal.pipeline.StreamFilter;
import com.android.build.gradle.internal.pipeline.TransformManager;
import com.android.build.gradle.internal.pipeline.TransformStream;
import com.android.build.gradle.internal.pipeline.TransformTask;
import com.android.build.gradle.internal.publishing.AndroidArtifacts;
import com.android.build.gradle.internal.scope.AndroidTask;
import com.android.build.gradle.internal.scope.AndroidTaskRegistry;
import com.android.build.gradle.internal.scope.ConventionMappingHelper;
import com.android.build.gradle.internal.scope.DefaultGradlePackagingScope;
import com.android.build.gradle.internal.scope.GlobalScope;
import com.android.build.gradle.internal.scope.PackagingScope;
import com.android.build.gradle.internal.scope.SupplierTask;
import com.android.build.gradle.internal.scope.TaskConfigAction;
import com.android.build.gradle.internal.scope.VariantOutputScope;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.tasks.AndroidReportTask;
import com.android.build.gradle.internal.tasks.CheckManifest;
import com.android.build.gradle.internal.tasks.DependencyReportTask;
import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask;
import com.android.build.gradle.internal.tasks.ExtractProguardFiles;
import com.android.build.gradle.internal.tasks.FileSupplier;
import com.android.build.gradle.internal.tasks.GenerateApkDataTask;
import com.android.build.gradle.internal.tasks.InstallVariantTask;
import com.android.build.gradle.internal.tasks.JackJacocoReportTask;
import com.android.build.gradle.internal.tasks.LintCompile;
import com.android.build.gradle.internal.tasks.MockableAndroidJarTask;
import com.android.build.gradle.internal.tasks.PrepareDependenciesTask;
import com.android.build.gradle.internal.tasks.ResolveDependenciesTask;
import com.android.build.gradle.internal.tasks.SigningReportTask;
import com.android.build.gradle.internal.tasks.SourceSetsTask;
import com.android.build.gradle.internal.tasks.TestServerTask;
import com.android.build.gradle.internal.tasks.UninstallTask;
import com.android.build.gradle.internal.tasks.ValidateSigningTask;
import com.android.build.gradle.internal.tasks.databinding.DataBindingExportBuildInfoTask;
import com.android.build.gradle.internal.tasks.databinding.DataBindingMergeArtifactsTransform;
import com.android.build.gradle.internal.tasks.databinding.DataBindingProcessLayoutsTask;
import com.android.build.gradle.internal.test.AbstractTestDataImpl;
import com.android.build.gradle.internal.test.TestDataImpl;
import com.android.build.gradle.internal.transforms.DexTransform;
import com.android.build.gradle.internal.transforms.ExtractJarsTransform;
import com.android.build.gradle.internal.transforms.JackTransform;
import com.android.build.gradle.internal.transforms.JacocoTransform;
import com.android.build.gradle.internal.transforms.JarMergingTransform;
import com.android.build.gradle.internal.transforms.MergeJavaResourcesTransform;
import com.android.build.gradle.internal.transforms.MultiDexTransform;
import com.android.build.gradle.internal.transforms.NewShrinkerTransform;
import com.android.build.gradle.internal.transforms.ProGuardTransform;
import com.android.build.gradle.internal.transforms.ProguardConfigurable;
import com.android.build.gradle.internal.transforms.ShrinkResourcesTransform;
import com.android.build.gradle.internal.transforms.StripDebugSymbolTransform;
import com.android.build.gradle.internal.variant.AndroidArtifactVariantData;
import com.android.build.gradle.internal.variant.ApkVariantData;
import com.android.build.gradle.internal.variant.ApkVariantOutputData;
import com.android.build.gradle.internal.variant.ApplicationVariantData;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.build.gradle.internal.variant.BaseVariantOutputData;
import com.android.build.gradle.internal.variant.SplitHandlingPolicy;
import com.android.build.gradle.internal.variant.TestVariantData;
import com.android.build.gradle.tasks.AidlCompile;
import com.android.build.gradle.tasks.CleanBuildCache;
import com.android.build.gradle.tasks.CompatibleScreensManifest;
import com.android.build.gradle.tasks.ExternalNativeBuildJsonTask;
import com.android.build.gradle.tasks.ExternalNativeBuildTask;
import com.android.build.gradle.tasks.ExternalNativeBuildTaskUtils;
import com.android.build.gradle.tasks.ExternalNativeCleanTask;
import com.android.build.gradle.tasks.ExternalNativeJsonGenerator;
import com.android.build.gradle.tasks.GenerateBuildConfig;
import com.android.build.gradle.tasks.GenerateResValues;
import com.android.build.gradle.tasks.GenerateSplitAbiRes;
import com.android.build.gradle.tasks.JackPreDexTransform;
import com.android.build.gradle.tasks.JavaPreCompileTask;
import com.android.build.gradle.tasks.Lint;
import com.android.build.gradle.tasks.ManifestProcessorTask;
import com.android.build.gradle.tasks.MergeManifests;
import com.android.build.gradle.tasks.MergeResources;
import com.android.build.gradle.tasks.MergeSourceSetFolders;
import com.android.build.gradle.tasks.NdkCompile;
import com.android.build.gradle.tasks.PackageApplication;
import com.android.build.gradle.tasks.PackageSplitAbi;
import com.android.build.gradle.tasks.PackageSplitRes;
import com.android.build.gradle.tasks.PreColdSwapTask;
import com.android.build.gradle.tasks.ProcessAndroidResources;
import com.android.build.gradle.tasks.ProcessManifest;
import com.android.build.gradle.tasks.ProcessTestManifest;
import com.android.build.gradle.tasks.RenderscriptCompile;
import com.android.build.gradle.tasks.ShaderCompile;
import com.android.build.gradle.tasks.SplitZipAlign;
import com.android.build.gradle.tasks.ZipAlign;
import com.android.build.gradle.tasks.factory.AndroidUnitTest;
import com.android.build.gradle.tasks.factory.IncrementalSafeguard;
import com.android.build.gradle.tasks.factory.JacocoAgentConfigAction;
import com.android.build.gradle.tasks.factory.JavaCompileConfigAction;
import com.android.build.gradle.tasks.factory.PackageJarArtifactConfigAction;
import com.android.build.gradle.tasks.factory.ProcessJavaResConfigAction;
import com.android.build.gradle.tasks.factory.TestServerTaskConfigAction;
import com.android.builder.core.AndroidBuilder;
import com.android.builder.core.DefaultDexOptions;
import com.android.builder.core.VariantConfiguration;
import com.android.builder.core.VariantType;
import com.android.builder.dependency.level2.AndroidDependency;
import com.android.builder.model.DataBindingOptions;
import com.android.builder.model.OptionalCompilationStep;
import com.android.builder.model.SyncIssue;
import com.android.builder.profile.Recorder;
import com.android.builder.testing.ConnectedDeviceProvider;
import com.android.builder.testing.api.DeviceProvider;
import com.android.builder.testing.api.TestServer;
import com.android.manifmerger.ManifestMerger2;
import com.android.sdklib.AndroidVersion;
import com.android.utils.StringHelper;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import groovy.lang.Closure;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.execution.TaskExecutionGraph;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.tasks.Copy;
import org.gradle.api.tasks.Sync;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry;
/**
* Manages tasks creation.
*/
public abstract class TaskManager {
public static final String DEFAULT_PROGUARD_CONFIG_FILE = "proguard-android.txt";
public static final String DIR_BUNDLES = "bundles";
public static final String DIR_ATOMBUNDLES = "atombundles";
public static final String INSTALL_GROUP = "Install";
public static final String BUILD_GROUP = BasePlugin.BUILD_GROUP;
public static final String ANDROID_GROUP = "Android";
protected Project project;
protected AndroidBuilder androidBuilder;
protected DataBindingBuilder dataBindingBuilder;
private DependencyManager dependencyManager;
protected SdkHandler sdkHandler;
protected AndroidConfig extension;
protected ToolingModelBuilderRegistry toolingRegistry;
private final GlobalScope globalScope;
private final AndroidTaskRegistry androidTasks = new AndroidTaskRegistry();
private Logger logger;
protected final Recorder recorder;
protected boolean isComponentModelPlugin = false;
// Task names
// These cannot be AndroidTasks as in the component model world there is nothing to force
// generateTasksBeforeEvaluate to happen before the variant tasks are created.
private static final String MAIN_PREBUILD = "preBuild";
private static final String UNINSTALL_ALL = "uninstallAll";
private static final String DEVICE_CHECK = "deviceCheck";
private static final String DEVICE_ANDROID_TEST = DEVICE + ANDROID_TEST.getSuffix();
protected static final String CONNECTED_CHECK = "connectedCheck";
private static final String CONNECTED_ANDROID_TEST = CONNECTED + ANDROID_TEST.getSuffix();
private static final String ASSEMBLE_ANDROID_TEST = "assembleAndroidTest";
private static final String SOURCE_SETS = "sourceSets";
public static final String LINT = "lint";
protected static final String LINT_COMPILE = "compileLint";
private static final String EXTRACT_PROGUARD_FILES = "extractProguardFiles";
public static final String ATOM_SUFFIX = "Atom";
// Tasks
private AndroidTask jacocoAgentTask;
public AndroidTask createMockableJar;
public TaskManager(
@NonNull Project project,
@NonNull AndroidBuilder androidBuilder,
@NonNull DataBindingBuilder dataBindingBuilder,
@NonNull AndroidConfig extension,
@NonNull SdkHandler sdkHandler,
@NonNull NdkHandler ndkHandler,
@NonNull DependencyManager dependencyManager,
@NonNull ToolingModelBuilderRegistry toolingRegistry,
@NonNull Recorder recorder) {
this.project = project;
this.androidBuilder = androidBuilder;
this.dataBindingBuilder = dataBindingBuilder;
this.sdkHandler = sdkHandler;
this.extension = extension;
this.toolingRegistry = toolingRegistry;
this.dependencyManager = dependencyManager;
this.recorder = recorder;
logger = Logging.getLogger(this.getClass());
globalScope = new GlobalScope(
project,
androidBuilder,
extension,
sdkHandler,
ndkHandler,
toolingRegistry);
}
private boolean isDebugLog() {
return project.getLogger().isEnabled(LogLevel.DEBUG);
}
private boolean isInfoLog() {
return project.getLogger().isEnabled(LogLevel.INFO);
}
public DataBindingBuilder getDataBindingBuilder() {
return dataBindingBuilder;
}
public DependencyManager getDependencyManager() {
return dependencyManager;
}
/**
* Creates the tasks for a given BaseVariantData.
*/
public abstract void createTasksForVariantData(
@NonNull TaskFactory tasks,
@NonNull BaseVariantData extends BaseVariantOutputData> variantData);
public GlobalScope getGlobalScope() {
return globalScope;
}
/**
* Returns a collection of buildables that creates native object.
*
* A buildable is considered to be any object that can be used as the argument to
* Task.dependsOn. This could be a Task or a BuildableModelElement (e.g. BinarySpec).
*/
protected Collection getNdkBuildable(BaseVariantData variantData) {
if (variantData.ndkCompileTask== null) {
return Collections.emptyList();
}
return Collections.singleton(variantData.ndkCompileTask);
}
/**
* Override to configure NDK data in the scope.
*/
public void configureScopeForNdk(@NonNull VariantScope scope) {
final BaseVariantData variantData = scope.getVariantData();
scope.setNdkSoFolder(Collections.singleton(new File(
scope.getGlobalScope().getIntermediatesDir(),
"ndk/" + variantData.getVariantConfiguration().getDirName() + "/lib")));
File objFolder = new File(scope.getGlobalScope().getIntermediatesDir(),
"ndk/" + variantData.getVariantConfiguration().getDirName() + "/obj");
scope.setNdkObjFolder(objFolder);
for (Abi abi : NdkHandler.getAbiList()) {
scope.addNdkDebuggableLibraryFolders(abi,
new File(objFolder, "local/" + abi.getName()));
}
}
protected AndroidConfig getExtension() {
return extension;
}
public void resolveDependencies(
@NonNull VariantDependencies variantDeps,
@Nullable String testedProjectPath) {
Set libsToExplode =
dependencyManager.resolveDependencies(variantDeps, testedProjectPath);
dependencyManager.processLibraries(libsToExplode);
}
/**
* Create tasks before the evaluation (on plugin apply). This is useful for tasks that
* could be referenced by custom build logic.
*/
public void createTasksBeforeEvaluate(@NonNull TaskFactory tasks) {
androidTasks.create(tasks, UNINSTALL_ALL, uninstallAllTask -> {
uninstallAllTask.setDescription("Uninstall all applications.");
uninstallAllTask.setGroup(INSTALL_GROUP);
});
androidTasks.create(tasks, DEVICE_CHECK, deviceCheckTask -> {
deviceCheckTask.setDescription(
"Runs all device checks using Device Providers and Test Servers.");
deviceCheckTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
});
androidTasks.create(tasks, CONNECTED_CHECK, connectedCheckTask -> {
connectedCheckTask.setDescription(
"Runs all device checks on currently connected devices.");
connectedCheckTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
});
androidTasks.create(tasks, MAIN_PREBUILD, task -> {});
AndroidTask extractProguardFiles =
androidTasks.create(
tasks, EXTRACT_PROGUARD_FILES, ExtractProguardFiles.class, task -> {});
// Make sure MAIN_PREBUILD runs first:
extractProguardFiles.dependsOn(tasks, MAIN_PREBUILD);
androidTasks.create(tasks, new SourceSetsTask.ConfigAction(extension));
androidTasks.create(tasks, ASSEMBLE_ANDROID_TEST,
assembleAndroidTestTask -> {
assembleAndroidTestTask.setGroup(BasePlugin.BUILD_GROUP);
assembleAndroidTestTask.setDescription("Assembles all the Test applications.");
});
AndroidTask globalLintTask = androidTasks.create(tasks,
new Lint.GlobalConfigAction(globalScope));
tasks.named(JavaBasePlugin.CHECK_TASK_NAME, it -> it.dependsOn(LINT));
androidTasks.create(tasks, new LintCompile.ConfigAction(globalScope));
if (AndroidGradleOptions.isBuildCacheEnabled(project)) {
androidTasks.create(tasks, new CleanBuildCache.ConfigAction(globalScope));
}
}
public void createMockableJarTask(TaskFactory tasks) {
createMockableJar = androidTasks.create(tasks, new MockableAndroidJarTask.ConfigAction(globalScope));
}
protected void createDependencyStreams(
@NonNull TaskFactory tasks,
@NonNull final VariantScope variantScope) {
BaseVariantData extends BaseVariantOutputData> variantData = variantScope.getVariantData();
final GradleVariantConfiguration config = variantData.getVariantConfiguration();
TransformManager transformManager = variantScope.getTransformManager();
// content that can be found in a jar:
Set fullJar = ImmutableSet.of(
DefaultContentType.CLASSES, DefaultContentType.RESOURCES, ExtendedContentType.NATIVE_LIBS);
transformManager.addStream(OriginalStream.builder()
.addContentTypes(fullJar)
.addScope(Scope.PROJECT_LOCAL_DEPS)
.setJars(config::getLocalPackagedJars)
.build());
ImmutableList dependencies =
ImmutableList.of(
variantScope.getResolveDependenciesTask() != null
? variantScope.getResolveDependenciesTask().getName()
: variantScope.getPrepareDependenciesTask().getName(),
variantData
.getVariantDependency()
.getPackageConfiguration()
.getBuildDependencies());
transformManager.addStream(OriginalStream.builder()
.addContentTypes(TransformManager.CONTENT_JARS)
.addScope(Scope.EXTERNAL_LIBRARIES)
.setJars(() -> Stream.concat(
config.getExternalPackagedJars().stream(),
variantScope.getGlobalScope().getAndroidBuilder()
.getAdditionalPackagedJars(config).stream())
.collect(Collectors.toSet()))
.setDependencies(dependencies)
.build());
// data binding related artifacts for external libs
if (extension.getDataBinding().isEnabled()) {
transformManager.addStream(OriginalStream.builder()
.addContentTypes(TransformManager.DATA_BINDING_ARTIFACT)
.addScope(Scope.EXTERNAL_LIBRARIES)
.setFolders(config::getSubProjectDataBindingArtifactFolders)
.setDependencies(dependencies)
.build()
);
}
transformManager.addStream(OriginalStream.builder()
.addContentTypes(TransformManager.CONTENT_NATIVE_LIBS)
.addScope(Scope.EXTERNAL_LIBRARIES)
.setJars(() -> Stream.concat(
config.getExternalPackagedJniJars().stream(),
variantScope.getGlobalScope().getAndroidBuilder()
.getAdditionalPackagedJars(config).stream())
.collect(Collectors.toSet()))
.setFolders(config::getExternalAarJniLibFolders)
.setDependencies(dependencies)
.build());
// for the sub modules, only the main jar has resources.
transformManager.addStream(OriginalStream.builder()
.addContentTypes(TransformManager.CONTENT_JARS)
.addScope(Scope.SUB_PROJECTS)
.setJars(config::getSubProjectPackagedJars)
.setDependencies(dependencies)
.build());
// the local deps don't have resources (been merged into the main jar)
transformManager.addStream(OriginalStream.builder()
.addContentTypes(TransformManager.CONTENT_CLASS)
.addScope(Scope.SUB_PROJECTS_LOCAL_DEPS)
.setJars(config::getSubProjectLocalPackagedJars)
.setDependencies(dependencies)
.build());
// and the native libs of the libraries are in a separate folder.
transformManager.addStream(OriginalStream.builder()
.addContentTypes(TransformManager.CONTENT_NATIVE_LIBS)
.addScope(Scope.SUB_PROJECTS)
.setFolders(config::getSubProjectJniLibFolders)
.setJars(config::getSubProjectPackagedJniJars)
.setDependencies(dependencies)
.build());
// provided only scopes.
transformManager.addStream(OriginalStream.builder()
.addContentTypes(fullJar)
.addScope(Scope.PROVIDED_ONLY)
.setJars(config::getProvidedOnlyJars)
.build());
if (variantScope.getTestedVariantData() != null) {
final BaseVariantData testedVariantData = variantScope.getTestedVariantData();
VariantScope testedVariantScope = testedVariantData.getScope();
// create two streams of different types.
transformManager.addStream(OriginalStream.builder()
.addContentTypes(DefaultContentType.CLASSES)
.addScope(Scope.TESTED_CODE)
.setFolders(Suppliers.ofInstance(
(Collection) ImmutableList.of(testedVariantScope.getJavaOutputDir())))
.setDependency(testedVariantScope.getJavacTask().getName())
.build());
transformManager.addStream(OriginalStream.builder()
.addContentTypes(DefaultContentType.CLASSES)
.addScope(Scope.TESTED_CODE)
.setJars(() -> variantScope.getGlobalScope().getAndroidBuilder()
.getAllPackagedJars(testedVariantData.getVariantConfiguration()))
.setDependency(ImmutableList.of(
testedVariantScope.getPrepareDependenciesTask().getName(),
testedVariantData.getVariantDependency().getPackageConfiguration()
.getBuildDependencies()))
.build());
}
handleJacocoDependencies(tasks, variantScope);
}
public void createMergeAppManifestsTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope variantScope) {
AndroidArtifactVariantData> androidArtifactVariantData =
(AndroidArtifactVariantData) variantScope.getVariantData();
Set screenSizes = androidArtifactVariantData.getCompatibleScreens();
// loop on all outputs. The only difference will be the name of the task, and location
// of the generated manifest
for (final BaseVariantOutputData vod : androidArtifactVariantData.getOutputs()) {
VariantOutputScope scope = vod.getScope();
AndroidTask csmTask = null;
if (vod.getMainOutputFile().getFilter(DENSITY) != null) {
csmTask = androidTasks.create(tasks,
new CompatibleScreensManifest.ConfigAction(scope, screenSizes));
scope.setCompatibleScreensManifestTask(csmTask);
}
ImmutableList.Builder optionalFeatures =
ImmutableList.builder();
if (getIncrementalMode(
variantScope.getVariantConfiguration()) != IncrementalMode.NONE) {
optionalFeatures.add(ManifestMerger2.Invoker.Feature.INSTANT_RUN_REPLACEMENT);
}
if (AndroidGradleOptions.getTestOnly(project)) {
optionalFeatures.add(ManifestMerger2.Invoker.Feature.TEST_ONLY);
}
AndroidTask extends ManifestProcessorTask> processManifestTask =
androidTasks.create(
tasks,
getMergeManifestConfig(scope, optionalFeatures.build()));
scope.setManifestProcessorTask(processManifestTask);
processManifestTask.dependsOn(tasks, variantScope.getPrepareDependenciesTask());
if (variantScope.getMicroApkTask() != null) {
processManifestTask.dependsOn(tasks, variantScope.getMicroApkTask());
}
if (csmTask != null) {
processManifestTask.dependsOn(tasks, csmTask);
}
addManifestArtifact(tasks, scope.getVariantScope().getVariantData());
}
}
/** Creates configuration action for the merge manifests task. */
@NonNull
protected TaskConfigAction extends ManifestProcessorTask> getMergeManifestConfig(
@NonNull VariantOutputScope scope,
@NonNull List optionalFeatures) {
return new MergeManifests.ConfigAction(scope, optionalFeatures);
}
/**
* Adds the manifest artifact for the variant.
*
* This artifact is added if the publishNonDefault option is {@code true}. See variant
* dependencies evaluation in {@link VariantDependencies} for more details.
*/
private void addManifestArtifact(
@NonNull TaskFactory tasks,
@NonNull BaseVariantData extends BaseVariantOutputData> variantData) {
if (variantData.getVariantDependency().getManifestConfiguration() != null) {
ManifestProcessorTask mergeManifestsTask =
variantData.getMainOutput().getScope().getManifestProcessorTask().get(tasks);
project.getArtifacts().add(
variantData.getVariantDependency().getManifestConfiguration().getName(),
AndroidArtifacts.buildManifestArtifact(globalScope.getProjectBaseName(),
new FileSupplier() {
@NonNull
@Override
public Task getTask() {
return mergeManifestsTask;
}
@Override
public File get() {
return mergeManifestsTask.getManifestOutputFile();
}
}));
}
}
public void createMergeLibManifestsTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
AndroidTask processManifest = androidTasks.create(tasks,
new ProcessManifest.ConfigAction(scope));
processManifest.dependsOn(tasks, scope.getPrepareDependenciesTask());
BaseVariantOutputData variantOutputData = scope.getVariantData().getMainOutput();
variantOutputData.getScope().setManifestProcessorTask(processManifest);
}
protected void createProcessTestManifestTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
AndroidTask processTestManifestTask = androidTasks.create(tasks,
new ProcessTestManifest.ConfigAction(scope));
processTestManifestTask.dependsOn(tasks, scope.getPrepareDependenciesTask());
BaseVariantOutputData variantOutputData = scope.getVariantData().getMainOutput();
variantOutputData.getScope().setManifestProcessorTask(processTestManifestTask);
}
public void createRenderscriptTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
scope.setRenderscriptCompileTask(
androidTasks.create(tasks, new RenderscriptCompile.ConfigAction(scope)));
BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
GradleVariantConfiguration config = variantData.getVariantConfiguration();
// get single output for now.
BaseVariantOutputData variantOutputData = variantData.getMainOutput();
scope.getRenderscriptCompileTask().dependsOn(tasks, scope.getPrepareDependenciesTask());
if (config.getType().isForTesting()) {
scope.getRenderscriptCompileTask().dependsOn(tasks,
variantOutputData.getScope().getManifestProcessorTask());
} else {
scope.getRenderscriptCompileTask().dependsOn(tasks, scope.getCheckManifestTask());
}
scope.getResourceGenTask().dependsOn(tasks, scope.getRenderscriptCompileTask());
// only put this dependency if rs will generate Java code
if (!config.getRenderscriptNdkModeEnabled()) {
scope.getSourceGenTask().dependsOn(tasks, scope.getRenderscriptCompileTask());
}
}
public AndroidTask createMergeResourcesTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
return createMergeResourcesTask(tasks, scope, true /*processResources*/);
}
public AndroidTask createMergeResourcesTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope,
boolean processResources) {
return basicCreateMergeResourcesTask(
tasks,
scope,
"merge",
null /*outputLocation*/,
true /*includeDependencies*/,
processResources);
}
public AndroidTask basicCreateMergeResourcesTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope,
@NonNull String taskNamePrefix,
@Nullable File outputLocation,
final boolean includeDependencies,
final boolean processResources) {
AndroidTask mergeResourcesTask = androidTasks.create(tasks,
new MergeResources.ConfigAction(
scope,
taskNamePrefix,
outputLocation,
includeDependencies,
processResources));
mergeResourcesTask.dependsOn(
tasks,
scope.getPrepareDependenciesTask(),
scope.getResourceGenTask());
scope.setMergeResourcesTask(mergeResourcesTask);
scope.setResourceOutputDir(
Objects.firstNonNull(outputLocation, scope.getDefaultMergeResourcesOutputDir()));
scope.setMergeResourceOutputDir(outputLocation);
return scope.getMergeResourcesTask();
}
public void createMergeAssetsTask(TaskFactory tasks, VariantScope scope) {
AndroidTask mergeAssetsTask = androidTasks.create(tasks,
new MergeSourceSetFolders.MergeAssetConfigAction(scope));
mergeAssetsTask.dependsOn(tasks,
scope.getPrepareDependenciesTask().getName(),
scope.getAssetGenTask());
scope.setMergeAssetsTask(mergeAssetsTask);
}
public void createMergeJniLibFoldersTasks(
@NonNull TaskFactory tasks,
@NonNull final VariantScope variantScope) {
// merge the source folders together using the proper priority.
AndroidTask mergeJniLibFoldersTask = androidTasks.create(tasks,
new MergeSourceSetFolders.MergeJniLibFoldersConfigAction(variantScope));
mergeJniLibFoldersTask.dependsOn(tasks,
variantScope.getPrepareDependenciesTask().getName(),
variantScope.getAssetGenTask());
variantScope.setMergeJniLibFoldersTask(mergeJniLibFoldersTask);
// create the stream generated from this task
variantScope.getTransformManager().addStream(OriginalStream.builder()
.addContentType(ExtendedContentType.NATIVE_LIBS)
.addScope(Scope.PROJECT)
.setFolder(variantScope.getMergeNativeLibsOutputDir())
.setDependency(mergeJniLibFoldersTask.getName())
.build());
// create a stream that contains the content of the local NDK build
variantScope.getTransformManager().addStream(OriginalStream.builder()
.addContentType(ExtendedContentType.NATIVE_LIBS)
.addScope(Scope.PROJECT)
.setFolders(Suppliers.ofInstance(variantScope.getNdkSoFolder()))
.setDependency(getNdkBuildable(variantScope.getVariantData()))
.build());
// create a stream that contains the content of the local external native build
if (variantScope.getExternalNativeJsonGenerator() != null) {
variantScope.getTransformManager().addStream(OriginalStream.builder()
.addContentType(ExtendedContentType.NATIVE_LIBS)
.addScope(Scope.PROJECT)
.setFolder(variantScope.getExternalNativeJsonGenerator().getObjFolder())
.setDependency(variantScope.getExternalNativeBuildTask().getName())
.build());
}
// create a stream containing the content of the renderscript compilation output
// if support mode is enabled.
if (variantScope.getVariantConfiguration().getRenderscriptSupportModeEnabled()) {
variantScope.getTransformManager().addStream(OriginalStream.builder()
.addContentType(ExtendedContentType.NATIVE_LIBS)
.addScope(Scope.PROJECT)
.setFolders(() -> {
ImmutableList.Builder builder = ImmutableList.builder();
if (variantScope.getRenderscriptLibOutputDir().isDirectory()) {
builder.add(variantScope.getRenderscriptLibOutputDir());
}
File rsLibs = variantScope.getGlobalScope().getAndroidBuilder()
.getSupportNativeLibFolder();
if (rsLibs != null && rsLibs.isDirectory()) {
builder.add(rsLibs);
}
if (variantScope.getVariantConfiguration()
.getRenderscriptSupportModeBlasEnabled()) {
File rsBlasLib = variantScope.getGlobalScope().getAndroidBuilder()
.getSupportBlasLibFolder();
if (rsBlasLib == null || !rsBlasLib.isDirectory()) {
throw new GradleException(
"Renderscript BLAS support mode is not supported "
+ "in BuildTools" + rsBlasLib);
} else {
builder.add(rsBlasLib);
}
}
return builder.build();
})
.setDependency(variantScope.getRenderscriptCompileTask().getName())
.build());
}
// compute the scopes that need to be merged.
Set mergeScopes = getResMergingScopes(variantScope);
// Create the merge transform
MergeJavaResourcesTransform mergeTransform = new MergeJavaResourcesTransform(
variantScope.getGlobalScope().getExtension().getPackagingOptions(),
mergeScopes, ExtendedContentType.NATIVE_LIBS, "mergeJniLibs");
variantScope.getTransformManager().addTransform(tasks, variantScope, mergeTransform);
}
public void createBuildConfigTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
AndroidTask generateBuildConfigTask =
androidTasks.create(tasks, new GenerateBuildConfig.ConfigAction(scope));
scope.setGenerateBuildConfigTask(generateBuildConfigTask);
scope.getSourceGenTask().dependsOn(tasks, generateBuildConfigTask.getName());
if (scope.getVariantConfiguration().getType().isForTesting()) {
// in case of a test project, the manifest is generated so we need to depend
// on its creation.
// For test apps there should be a single output, so we get it.
BaseVariantOutputData variantOutputData = scope.getVariantData().getMainOutput();
generateBuildConfigTask.dependsOn(
tasks, variantOutputData.getScope().getManifestProcessorTask());
} else {
generateBuildConfigTask.dependsOn(tasks, scope.getCheckManifestTask());
}
}
public void createGenerateResValuesTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
AndroidTask generateResValuesTask = androidTasks.create(
tasks, new GenerateResValues.ConfigAction(scope));
scope.getResourceGenTask().dependsOn(tasks, generateResValuesTask);
}
public void createApkProcessResTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
createProcessResTask(
tasks,
scope,
new File(globalScope.getIntermediatesDir(),
"symbols/" + scope.getVariantData().getVariantConfiguration().getDirName()),
true);
}
public void createProcessResTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope,
@Nullable File symbolLocation,
boolean generateResourcePackage) {
BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
variantData.calculateFilters(scope.getGlobalScope().getExtension().getSplits());
boolean useAaptToGenerateLegacyMultidexMainDexProguardRules =
isLegacyMultidexMode(scope);
// loop on all outputs. The only difference will be the name of the task, and location
// of the generated data.
for (BaseVariantOutputData vod : variantData.getOutputs()) {
final VariantOutputScope variantOutputScope = vod.getScope();
variantOutputScope.setProcessResourcesTask(androidTasks.create(tasks,
new ProcessAndroidResources.ConfigAction(variantOutputScope, symbolLocation,
generateResourcePackage,
useAaptToGenerateLegacyMultidexMainDexProguardRules)));
// always depend on merge res,
variantOutputScope.getProcessResourcesTask().dependsOn(tasks,
scope.getMergeResourcesTask());
if (scope.getDataBindingProcessLayoutsTask() != null) {
variantOutputScope.getProcessResourcesTask().dependsOn(tasks,
scope.getDataBindingProcessLayoutsTask().getName());
}
variantOutputScope
.getProcessResourcesTask()
.dependsOn(tasks, variantOutputScope.getManifestProcessorTask());
if (vod.getMainOutputFile().getFilter(DENSITY) == null) {
scope.setGenerateRClassTask(variantOutputScope.getProcessResourcesTask());
scope.getSourceGenTask().optionalDependsOn(
tasks,
variantOutputScope.getProcessResourcesTask());
}
}
}
/**
* Creates the split resources packages task if necessary. AAPT will produce split packages for
* all --split provided parameters. These split packages should be signed and moved unchanged to
* the APK build output directory.
*/
@NonNull
public AndroidTask createSplitResourcesTasks(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope,
@NonNull PackagingScope packagingScope) {
BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
checkState(variantData.getSplitHandlingPolicy().equals(
SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY),
"Can only create split resources tasks for pure splits.");
List extends BaseVariantOutputData> outputs = variantData.getOutputs();
final BaseVariantOutputData variantOutputData = variantData.getMainOutput();
if (outputs.size() != 1) {
throw new RuntimeException(
"In release 21 and later, there can be only one main APK, " +
"found " + outputs.size());
}
VariantOutputScope variantOutputScope = variantOutputData.getScope();
AndroidTask packageSplitRes =
androidTasks.create(tasks, new PackageSplitRes.ConfigAction(scope));
packageSplitRes.dependsOn(tasks, variantOutputScope.getProcessResourcesTask().getName());
if (packagingScope.getSigningConfig() != null) {
packageSplitRes.dependsOn(tasks, getValidateSigningTask(tasks, packagingScope));
}
return packageSplitRes;
}
@Nullable
public AndroidTask createSplitAbiTasks(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope,
@NonNull PackagingScope packagingScope) {
ApplicationVariantData variantData = (ApplicationVariantData) scope.getVariantData();
checkState(variantData.getSplitHandlingPolicy().equals(
SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY),
"split ABI tasks are only compatible with pure splits.");
Set filters = AbiSplitOptions.getAbiFilters(
getExtension().getSplits().getAbiFilters());
if (filters.isEmpty()) {
return null;
}
List outputs = variantData.getOutputs();
if (outputs.size() != 1) {
throw new RuntimeException(
"In release 21 and later, there can be only one main APK, " +
"found " + outputs.size());
}
BaseVariantOutputData variantOutputData = variantData.getMainOutput();
// first create the split APK resources.
AndroidTask generateSplitAbiRes =
androidTasks.create(tasks, new GenerateSplitAbiRes.ConfigAction(scope));
generateSplitAbiRes.dependsOn(tasks,
variantOutputData.getScope().getProcessResourcesTask().getName());
// then package those resources with the appropriate JNI libraries.
AndroidTask packageSplitAbiTask =
androidTasks.create(tasks, new PackageSplitAbi.ConfigAction(scope));
packageSplitAbiTask.dependsOn(tasks, generateSplitAbiRes);
packageSplitAbiTask.dependsOn(tasks, scope.getNdkBuildable());
if (packagingScope.getSigningConfig() != null) {
packageSplitAbiTask.dependsOn(tasks, getValidateSigningTask(tasks, packagingScope));
}
if (scope.getExternalNativeBuildTask() != null) {
packageSplitAbiTask.dependsOn(tasks, scope.getExternalNativeBuildTask());
}
// set up dependency on the jni merger.
for (TransformStream stream : scope.getTransformManager().getStreams(
StreamFilter.NATIVE_LIBS)) {
packageSplitAbiTask.dependsOn(tasks, stream.getDependencies());
}
return packageSplitAbiTask;
}
public void createSplitTasks(@NonNull TaskFactory tasks, @NonNull VariantScope variantScope) {
VariantOutputScope outputScope = variantScope.getVariantData().getMainOutput().getScope();
PackagingScope packagingScope = new DefaultGradlePackagingScope(outputScope);
AndroidTask packageSplitResourcesTask =
createSplitResourcesTasks(tasks, variantScope, packagingScope);
final AndroidTask packageSplitAbiTask =
createSplitAbiTasks(tasks, variantScope, packagingScope);
AndroidTask zipAlign =
androidTasks.create(tasks, new SplitZipAlign.ConfigAction(variantScope));
//noinspection VariableNotUsedInsideIf - only need to check if task exist.
if (packageSplitAbiTask != null) {
zipAlign.configure(
tasks,
task ->
task.getAbiInputFiles()
.addAll(variantScope.getPackageSplitAbiOutputFiles()));
}
zipAlign.dependsOn(tasks, packageSplitResourcesTask);
zipAlign.optionalDependsOn(tasks, packageSplitAbiTask);
outputScope.setSplitZipAlignTask(zipAlign);
}
/**
* Returns the scopes for which the java resources should be merged.
* @param variantScope the scope of the variant being processed.
* @return the list of scopes for which to merge the java resources.
*/
@NonNull
protected abstract Set getResMergingScopes(@NonNull VariantScope variantScope);
/**
* Creates the java resources processing tasks.
*
* The java processing will happen in two steps:
*
* {@link Sync} task configured with {@link ProcessJavaResConfigAction} will sync all source
* folders into a single folder identified by
* {@link VariantScope#getSourceFoldersJavaResDestinationDir()}
* {@link MergeJavaResourcesTransform} will take the output of this merge plus the
* dependencies and will create a single merge with the {@link PackagingOptions} settings
* applied.
*
*
* @param tasks tasks factory to create tasks.
* @param variantScope the variant scope we are operating under.
*/
public void createProcessJavaResTasks(
@NonNull TaskFactory tasks,
@NonNull VariantScope variantScope) {
TransformManager transformManager = variantScope.getTransformManager();
// now copy the source folders java resources into the temporary location, mainly to
// maintain the PluginDsl COPY semantics.
AndroidTask processJavaResourcesTask =
androidTasks.create(tasks, new ProcessJavaResConfigAction(variantScope));
variantScope.setProcessJavaResourcesTask(processJavaResourcesTask);
// create the stream generated from this task
variantScope.getTransformManager().addStream(OriginalStream.builder()
.addContentType(DefaultContentType.RESOURCES)
.addScope(Scope.PROJECT)
.setFolder(variantScope.getSourceFoldersJavaResDestinationDir())
.setDependency(processJavaResourcesTask.getName())
.build());
// compute the scopes that need to be merged.
Set mergeScopes = getResMergingScopes(variantScope);
// Create the merge transform
MergeJavaResourcesTransform mergeTransform = new MergeJavaResourcesTransform(
variantScope.getGlobalScope().getExtension().getPackagingOptions(),
mergeScopes, DefaultContentType.RESOURCES, "mergeJavaRes");
variantScope.setMergeJavaResourcesTask(
transformManager.addTransform(tasks, variantScope, mergeTransform).orElse(null));
}
public void createAidlTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
scope.setAidlCompileTask(androidTasks.create(tasks, new AidlCompile.ConfigAction(scope)));
scope.getSourceGenTask().dependsOn(tasks, scope.getAidlCompileTask());
scope.getAidlCompileTask().dependsOn(tasks, scope.getPrepareDependenciesTask());
}
public void createShaderTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
// merge the shader folders together using the proper priority.
AndroidTask mergeShadersTask = androidTasks.create(tasks,
new MergeSourceSetFolders.MergeShaderSourceFoldersConfigAction(scope));
// TODO do we support non compiled shaders in aars?
//mergeShadersTask.dependsOn(tasks, scope.getVariantData().prepareDependenciesTask);
// compile the shaders
AndroidTask shaderCompileTask = androidTasks.create(
tasks, new ShaderCompile.ConfigAction(scope));
scope.setShaderCompileTask(shaderCompileTask);
shaderCompileTask.dependsOn(tasks, mergeShadersTask);
scope.getAssetGenTask().dependsOn(tasks, shaderCompileTask);
}
/**
* Creates the task for creating *.class files using javac. These tasks are created regardless
* of whether Jack is used or not, but assemble will not depend on them if it is. They are
* always used when running unit tests.
*/
public AndroidTask extends JavaCompile> createJavacTask(
@NonNull final TaskFactory tasks,
@NonNull final VariantScope scope) {
final BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
AndroidTask javacIncrementalSafeguard = androidTasks.create(tasks,
new IncrementalSafeguard.ConfigAction(scope));
AndroidTask preCompileTask = androidTasks.create(tasks,
new JavaPreCompileTask.ConfigAction(scope));
final AndroidTask extends JavaCompile> javacTask =
androidTasks.create(
tasks,
new JavaCompileConfigAction(scope, androidBuilder.getErrorReporter()));
scope.setJavacTask(javacTask);
javacTask.dependsOn(tasks, javacIncrementalSafeguard, preCompileTask);
setupCompileTaskDependencies(tasks, scope, javacTask);
// Create jar task for uses by external modules.
if (variantData.getVariantDependency().getClassesConfiguration() != null) {
AndroidTask packageJarArtifact =
androidTasks.create(tasks, new PackageJarArtifactConfigAction(scope));
packageJarArtifact.dependsOn(tasks, javacTask);
}
return javacTask;
}
/**
* Add stream of classes compiled by javac to transform manager.
*
* This should not be called for classes that will also be compiled from source by jack.
*/
public static void addJavacClassesStream(VariantScope scope) {
checkNotNull(scope.getJavacTask());
// create the output stream from this task
scope.getTransformManager().addStream(OriginalStream.builder()
.addContentType(DefaultContentType.CLASSES)
.addScope(Scope.PROJECT)
.setFolder(scope.getJavaOutputDir())
.setDependency(scope.getJavacTask().getName())
.build());
}
private static void setupCompileTaskDependencies(
@NonNull TaskFactory tasks, @NonNull VariantScope scope, AndroidTask> compileTask) {
compileTask.optionalDependsOn(tasks, scope.getSourceGenTask());
compileTask.dependsOn(tasks, scope.getPrepareDependenciesTask());
// TODO - dependency information for the compile classpath is being lost.
// Add a temporary approximation
compileTask.dependsOn(
tasks,
scope.getVariantData()
.getVariantDependency()
.getCompileConfiguration()
.getBuildDependencies());
compileTask.dependsOn(
tasks,
scope.getVariantData()
.getVariantDependency()
.getAnnotationProcessorConfiguration()
.getBuildDependencies());
}
/**
* Makes the given task the one used by top-level "compile" task.
*/
public static void setJavaCompilerTask(
@NonNull AndroidTask extends Task> javaCompilerTask,
@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
scope.getCompileTask().dependsOn(tasks, javaCompilerTask);
scope.setJavaCompilerTask(javaCompilerTask);
// TODO: Get rid of it once we stop keeping tasks in variant data.
//noinspection VariableNotUsedInsideIf
if (scope.getVariantData().javacTask != null) {
// This is not the experimental plugin, let's update variant data, so Variants API
// keeps working.
scope.getVariantData().javaCompilerTask = tasks.named(javaCompilerTask.getName());
}
}
/**
* Creates the task that will handle micro apk.
*
* New in 2.2, it now supports the unbundled mode, in which the apk is not bundled
* anymore, but we still have an XML resource packaged, and a custom entry in the manifest.
* This is triggered by passing a null {@link Configuration} object.
*
* @param tasks the task factory
* @param scope the variant scope
* @param config an optional Configuration object. if non null, this will embed the micro apk,
* if null this will trigger the unbundled mode.
*/
public void createGenerateMicroApkDataTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope,
@Nullable Configuration config) {
AndroidTask generateMicroApkTask = androidTasks.create(tasks,
new GenerateApkDataTask.ConfigAction(scope, config));
scope.setMicroApkTask(generateMicroApkTask);
generateMicroApkTask.optionalDependsOn(tasks, config);
// the merge res task will need to run after this one.
scope.getResourceGenTask().dependsOn(tasks, generateMicroApkTask);
}
public void createExternalNativeBuildJsonGenerators(@NonNull VariantScope scope) {
CoreExternalNativeBuild externalNativeBuild = extension.getExternalNativeBuild();
ExternalNativeBuildTaskUtils.ExternalNativeBuildProjectPathResolution pathResolution =
ExternalNativeBuildTaskUtils.getProjectPath(externalNativeBuild);
if (pathResolution.errorText != null) {
androidBuilder.getErrorReporter().handleSyncError(
scope.getVariantConfiguration().getFullName(),
SyncIssue.TYPE_EXTERNAL_NATIVE_BUILD_CONFIGURATION,
pathResolution.errorText);
return;
}
if (pathResolution.makeFile == null) {
// No project
return;
}
scope.setExternalNativeJsonGenerator(ExternalNativeJsonGenerator.create(
project.getProjectDir(),
pathResolution.buildSystem,
pathResolution.makeFile,
androidBuilder,
sdkHandler,
scope
));
}
public void createExternalNativeBuildTasks(TaskFactory tasks, @NonNull VariantScope scope) {
ExternalNativeJsonGenerator generator = scope.getExternalNativeJsonGenerator();
if (generator == null) {
return;
}
// Set up JSON generation tasks
AndroidTask> generateTask = androidTasks.create(tasks,
ExternalNativeBuildJsonTask.createTaskConfigAction(
generator, scope));
generateTask.dependsOn(tasks, scope.getPreBuildTask());
boolean buildOnlyTargetAbi = AndroidGradleOptions.isBuildOnlyTargetAbiEnabled(project);
// Set up build tasks
AndroidTask buildTask = androidTasks.create(
tasks,
new ExternalNativeBuildTask.ConfigAction(
buildOnlyTargetAbi ? AndroidGradleOptions.getBuildTargetAbi(project) : null,
generator, scope, androidBuilder));
buildTask.dependsOn(tasks, generateTask);
scope.setExternalNativeBuildTask(buildTask);
scope.getCompileTask().dependsOn(tasks, buildTask);
// Set up clean tasks
Task cleanTask = checkNotNull(tasks.named("clean"));
cleanTask.dependsOn(androidTasks.create(tasks, new ExternalNativeCleanTask.ConfigAction(
generator, scope, androidBuilder)).getName());
}
public void createNdkTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
if (ExternalNativeBuildTaskUtils.isExternalNativeBuildEnabled(
extension.getExternalNativeBuild())) {
return;
}
AndroidTask ndkCompileTask =
androidTasks.create(tasks, new NdkCompile.ConfigAction(scope));
ndkCompileTask.dependsOn(tasks, scope.getPreBuildTask());
if (Boolean.TRUE.equals(
scope.getVariantData()
.getVariantConfiguration()
.getMergedFlavor()
.getRenderscriptNdkModeEnabled())) {
ndkCompileTask.dependsOn(tasks, scope.getRenderscriptCompileTask());
}
scope.getCompileTask().dependsOn(tasks, ndkCompileTask);
}
/**
* Create transform for stripping debug symbols from native libraries before deploying.
*/
public static void createStripNativeLibraryTask(
@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
if (!scope.getGlobalScope().getNdkHandler().isConfigured()) {
// We don't know where the NDK is, so we won't be stripping the debug symbols from
// native libraries.
return;
}
TransformManager transformManager = scope.getTransformManager();
GlobalScope globalScope = scope.getGlobalScope();
transformManager.addTransform(
tasks,
scope,
new StripDebugSymbolTransform(
globalScope.getProject(),
globalScope.getNdkHandler(),
globalScope.getExtension().getPackagingOptions().getDoNotStrip()));
}
/**
* Creates the tasks to build unit tests.
*/
public void createUnitTestVariantTasks(
@NonNull TaskFactory tasks,
@NonNull TestVariantData variantData) {
VariantScope variantScope = variantData.getScope();
BaseVariantData testedVariantData = variantScope.getTestedVariantData();
checkState(testedVariantData != null);
createPreBuildTasks(tasks, variantScope);
// Create all current streams (dependencies mostly at this point)
createDependencyStreams(tasks, variantScope);
createProcessJavaResTasks(tasks, variantScope);
createCompileAnchorTask(tasks, variantScope);
// :app:compileDebugUnitTestSources should be enough for running tests from AS, so add an
// explicit dependency on resource copying tasks.
variantScope.getCompileTask().dependsOn(
tasks,
variantScope.getProcessJavaResourcesTask(),
testedVariantData.getScope().getProcessJavaResourcesTask());
AndroidTask extends JavaCompile> javacTask = createJavacTask(tasks, variantScope);
addJavacClassesStream(variantScope);
setJavaCompilerTask(javacTask, tasks, variantScope);
javacTask.dependsOn(tasks, testedVariantData.getScope().getJavacTask());
createRunUnitTestTask(tasks, variantScope);
variantScope.getAssembleTask().dependsOn(tasks, createMockableJar);
// This hides the assemble unit test task from the task list.
variantScope.getAssembleTask().configure(tasks, task -> task.setGroup(null));
}
/**
* Creates the tasks to build android tests.
*/
public void createAndroidTestVariantTasks(@NonNull TaskFactory tasks,
@NonNull TestVariantData variantData) {
VariantScope variantScope = variantData.getScope();
// get single output for now (though this may always be the case for tests).
final BaseVariantOutputData variantOutputData = variantData.getMainOutput();
final BaseVariantData testedVariantData =
(BaseVariantData) variantData.getTestedVariantData();
final BaseVariantOutputData testedVariantOutputData = testedVariantData.getMainOutput();
createAnchorTasks(tasks, variantScope);
// Create all current streams (dependencies mostly at this point)
createDependencyStreams(tasks, variantScope);
// Add a task to process the manifest
createProcessTestManifestTask(tasks, variantScope);
// Add a task to create the res values
createGenerateResValuesTask(tasks, variantScope);
// Add a task to compile renderscript files.
createRenderscriptTask(tasks, variantScope);
// Add a task to merge the resource folders
createMergeResourcesTask(tasks, variantScope);
// Add a task to merge the assets folders
createMergeAssetsTask(tasks, variantScope);
if (variantData.getTestedVariantData().getVariantConfiguration().getType().equals(
VariantType.LIBRARY)) {
// in this case the tested library must be fully built before test can be built!
if (testedVariantOutputData.getScope().getAssembleTask() != null) {
String bundle =
testedVariantOutputData.getScope().getVariantScope().getTaskName("bundle");
variantOutputData.getScope().getManifestProcessorTask().dependsOn(tasks, bundle);
variantScope.getMergeResourcesTask().dependsOn(tasks, bundle);
}
}
// Add a task to create the BuildConfig class
createBuildConfigTask(tasks, variantScope);
// Add a task to generate resource source files
createApkProcessResTask(tasks, variantScope);
// process java resources
createProcessJavaResTasks(tasks, variantScope);
createAidlTask(tasks, variantScope);
createShaderTask(tasks, variantScope);
// Add NDK tasks
if (!isComponentModelPlugin) {
createNdkTasks(tasks, variantScope);
}
variantScope.setNdkBuildable(getNdkBuildable(variantData));
// add tasks to merge jni libs.
createMergeJniLibFoldersTasks(tasks, variantScope);
// create data binding merge task before the javac task so that it can
// parse jars before any consumer
createDataBindingMergeArtifactsTaskIfNecessary(tasks, variantScope);
// Add a task to compile the test application
CoreJackOptions jackOptions = variantData.getVariantConfiguration().getJackOptions();
if (jackOptions.isEnabled()) {
AndroidTask jackTask =
createJackTask(tasks, variantScope, true /*compileJavaSources*/);
setJavaCompilerTask(jackTask, tasks, variantScope);
} else {
AndroidTask extends JavaCompile> javacTask = createJavacTask(tasks, variantScope);
addJavacClassesStream(variantScope);
setJavaCompilerTask(javacTask, tasks, variantScope);
createPostCompilationTasks(tasks, variantScope);
}
checkNotNull(variantScope.getJavaCompilerTask());
variantScope.getJavaCompilerTask().dependsOn(
tasks,
testedVariantData.getScope().getJavaCompilerTask());
// Add data binding tasks if enabled
createDataBindingTasksIfNecessary(tasks, variantScope);
createPackagingTask(tasks, variantScope, false /*publishApk*/,
null /* buildInfoGeneratorTask */);
tasks.named(ASSEMBLE_ANDROID_TEST, assembleTest ->
assembleTest.dependsOn(variantOutputData.getScope().getAssembleTask().getName()));
createConnectedTestForVariant(tasks, variantScope);
}
protected enum IncrementalMode {
NONE,
FULL,
}
/**
* Returns the incremental mode for this variant.
* @param config the variant's configuration
* @return the {@link IncrementalMode} for this variant.
*/
protected IncrementalMode getIncrementalMode(@NonNull GradleVariantConfiguration config) {
if (config.isInstantRunSupported()
&& targetDeviceSupportsInstantRun(config, project)
&& globalScope.isActive(OptionalCompilationStep.INSTANT_DEV)) {
return IncrementalMode.FULL;
}
return IncrementalMode.NONE;
}
private static boolean targetDeviceSupportsInstantRun(
@NonNull GradleVariantConfiguration config,
@NonNull Project project) {
if (config.isLegacyMultiDexMode()) {
// We don't support legacy multi-dex on Dalvik.
return AndroidGradleOptions.getTargetFeatureLevel(project) >=
AndroidVersion.ART_RUNTIME.getFeatureLevel();
}
return true;
}
/**
* Is the given variant relevant for lint?
*/
private static boolean isLintVariant(
@NonNull BaseVariantData extends BaseVariantOutputData> baseVariantData) {
// Only create lint targets for variants like debug and release, not debugTest
VariantConfiguration config = baseVariantData.getVariantConfiguration();
return !config.getType().isForTesting();
}
/**
* Add tasks for running lint on individual variants. We've already added a
* lint task earlier which runs on all variants.
*/
public void createLintTasks(TaskFactory tasks, final VariantScope scope) {
final BaseVariantData extends BaseVariantOutputData> baseVariantData =
scope.getVariantData();
if (!isLintVariant(baseVariantData)) {
return;
}
// wire the main lint task dependency.
tasks.named(LINT, lint -> lint.dependsOn(scope.getJavacTask().getName()));
AndroidTask variantLintCheck = androidTasks.create(
tasks, new Lint.ConfigAction(scope));
variantLintCheck.dependsOn(tasks, LINT_COMPILE, scope.getJavacTask());
}
private void createLintVitalTask(
@NonNull TaskFactory tasks,
@NonNull ApkVariantData variantData) {
checkState(getExtension().getLintOptions().isCheckReleaseBuilds());
// TODO: re-enable with Jack when possible
if (!variantData.getVariantConfiguration().getBuildType().isDebuggable() &&
!variantData.getVariantConfiguration().getJackOptions().isEnabled()) {
final AndroidTask lintReleaseCheck = androidTasks.create(
tasks,
new Lint.VitalConfigAction(variantData.getScope()));
lintReleaseCheck.optionalDependsOn(tasks, variantData.javacTask);
variantData.getScope().getAssembleTask().dependsOn(tasks, lintReleaseCheck);
// If lint is being run, we do not need to run lint vital.
// TODO: Find a better way to do this.
project.getGradle().getTaskGraph().whenReady(new Closure(this, this) {
public void doCall(TaskExecutionGraph taskGraph) {
if (taskGraph.hasTask(LINT)) {
project.getTasks().getByName(lintReleaseCheck.getName()).setEnabled(false);
}
}
});
}
}
private void createRunUnitTestTask(
@NonNull TaskFactory tasks,
@NonNull final VariantScope variantScope) {
final AndroidTask runTestsTask =
androidTasks.create(tasks, new AndroidUnitTest.ConfigAction(variantScope));
runTestsTask.dependsOn(tasks, variantScope.getAssembleTask());
tasks.named(JavaPlugin.TEST_TASK_NAME, test -> test.dependsOn(runTestsTask.getName()));
}
public void createTopLevelTestTasks(final TaskFactory tasks, boolean hasFlavors) {
createMockableJarTask(tasks);
final List reportTasks = Lists.newArrayListWithExpectedSize(2);
List providers = getExtension().getDeviceProviders();
// If more than one flavor, create a report aggregator task and make this the parent
// task for all new connected tasks. Otherwise, create a top level connectedAndroidTest
// DefaultTask.
AndroidTask extends DefaultTask> connectedAndroidTestTask;
if (hasFlavors) {
connectedAndroidTestTask = androidTasks.create(tasks,
new AndroidReportTask.ConfigAction(
globalScope,
AndroidReportTask.ConfigAction.TaskKind.CONNECTED));
reportTasks.add(connectedAndroidTestTask.getName());
} else {
connectedAndroidTestTask = androidTasks.create(tasks,
CONNECTED_ANDROID_TEST,
connectedTask -> {
connectedTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
connectedTask.setDescription("Installs and runs instrumentation tests "
+ "for all flavors on connected devices.");
});
}
tasks.named(CONNECTED_CHECK, check -> check.dependsOn(connectedAndroidTestTask.getName()));
AndroidTask extends DefaultTask> deviceAndroidTestTask;
// if more than one provider tasks, either because of several flavors, or because of
// more than one providers, then create an aggregate report tasks for all of them.
if (providers.size() > 1 || hasFlavors) {
deviceAndroidTestTask = androidTasks.create(tasks,
new AndroidReportTask.ConfigAction(
globalScope,
AndroidReportTask.ConfigAction.TaskKind.DEVICE_PROVIDER));
reportTasks.add(deviceAndroidTestTask.getName());
} else {
deviceAndroidTestTask = androidTasks.create(tasks,
DEVICE_ANDROID_TEST,
providerTask -> {
providerTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
providerTask.setDescription("Installs and runs instrumentation tests "
+ "using all Device Providers.");
});
}
tasks.named(DEVICE_CHECK, check -> check.dependsOn(deviceAndroidTestTask.getName()));
// Create top level unit test tasks.
androidTasks.create(tasks, JavaPlugin.TEST_TASK_NAME, unitTestTask -> {
unitTestTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
unitTestTask.setDescription("Run unit tests for all variants.");
});
tasks.named(JavaBasePlugin.CHECK_TASK_NAME,
check -> check.dependsOn(JavaPlugin.TEST_TASK_NAME));
// If gradle is launched with --continue, we want to run all tests and generate an
// aggregate report (to help with the fact that we may have several build variants, or
// or several device providers).
// To do that, the report tasks must run even if one of their dependent tasks (flavor
// or specific provider tasks) fails, when --continue is used, and the report task is
// meant to run (== is in the task graph).
// To do this, we make the children tasks ignore their errors (ie they won't fail and
// stop the build).
//TODO: move to mustRunAfter once is stable.
if (!reportTasks.isEmpty() && project.getGradle().getStartParameter()
.isContinueOnFailure()) {
project.getGradle().getTaskGraph().whenReady(new Closure(this, this) {
public void doCall(TaskExecutionGraph taskGraph) {
for (String reportTask : reportTasks) {
if (taskGraph.hasTask(reportTask)) {
tasks.named(reportTask, new Action() {
@Override
public void execute(Task task) {
((AndroidReportTask) task).setWillRun();
}
});
}
}
}
});
}
}
protected void createConnectedTestForVariant(
@NonNull TaskFactory tasks,
@NonNull final VariantScope variantScope) {
final BaseVariantData extends BaseVariantOutputData> baseVariantData =
variantScope.getTestedVariantData();
final TestVariantData testVariantData = (TestVariantData) variantScope.getVariantData();
// get single output for now
final BaseVariantOutputData variantOutputData = baseVariantData.getMainOutput();
final BaseVariantOutputData testVariantOutputData = testVariantData.getMainOutput();
TestDataImpl testData = new TestDataImpl(testVariantData);
testData.setExtraInstrumentationTestRunnerArgs(
AndroidGradleOptions.getExtraInstrumentationTestRunnerArgs(project));
configureTestData(testData);
// create the check tasks for this test
// first the connected one.
ImmutableList> artifactsTasks =
ImmutableList.of(
testVariantData.getMainOutput().getScope().getAssembleTask(),
baseVariantData.getScope().getAssembleTask());
final AndroidTask connectedTask = androidTasks.create(
tasks,
new DeviceProviderInstrumentTestTask.ConfigAction(
testVariantData.getScope(),
new ConnectedDeviceProvider(sdkHandler.getSdkInfo().getAdb(),
globalScope.getExtension().getAdbOptions().getTimeOutInMs(),
new LoggerWrapper(logger)), testData));
connectedTask.dependsOn(tasks, artifactsTasks.toArray());
tasks.named(CONNECTED_ANDROID_TEST,
connectedAndroidTest -> connectedAndroidTest.dependsOn(connectedTask.getName()));
if (baseVariantData.getVariantConfiguration().getBuildType().isTestCoverageEnabled()) {
final AndroidTask reportTask;
if (baseVariantData.getVariantConfiguration().getJackOptions().isEnabled()) {
reportTask = androidTasks.create(
tasks,
new JackJacocoReportTask.ConfigAction(variantScope));
} else {
reportTask = androidTasks.create(
tasks,
new JacocoReportTask.ConfigAction(variantScope));
}
reportTask.dependsOn(tasks, connectedTask.getName());
variantScope.setCoverageReportTask(reportTask);
baseVariantData.getScope().getCoverageReportTask().dependsOn(tasks, reportTask);
tasks.named(CONNECTED_ANDROID_TEST,
connectedAndroidTest -> connectedAndroidTest.dependsOn(reportTask.getName()));
}
List providers = getExtension().getDeviceProviders();
boolean hasFlavors = baseVariantData.getVariantConfiguration().hasFlavors();
// now the providers.
for (DeviceProvider deviceProvider : providers) {
final AndroidTask providerTask = androidTasks
.create(tasks, new DeviceProviderInstrumentTestTask.ConfigAction(
testVariantData.getScope(), deviceProvider, testData));
providerTask.dependsOn(tasks, artifactsTasks.toArray());
tasks.named(DEVICE_ANDROID_TEST,
deviceAndroidTest -> deviceAndroidTest.dependsOn(providerTask.getName()));
}
// now the test servers
List servers = getExtension().getTestServers();
for (final TestServer testServer : servers) {
final AndroidTask serverTask = androidTasks.create(
tasks,
new TestServerTaskConfigAction(variantScope, testServer));
serverTask.dependsOn(
tasks,
testVariantOutputData.getScope().getAssembleTask(),
variantOutputData.getScope().getAssembleTask());
tasks.named(DEVICE_CHECK,
deviceAndroidTest -> deviceAndroidTest.dependsOn(serverTask.getName()));
}
}
/**
* Creates the post-compilation tasks for the given Variant.
*
* These tasks create the dex file from the .class files, plus optional intermediary steps like
* proguard and jacoco
*
*/
public void createPostCompilationTasks(
@NonNull TaskFactory tasks,
@NonNull final VariantScope variantScope) {
checkNotNull(variantScope.getJavacTask());
variantScope.getInstantRunBuildContext().setInstantRunMode(
getIncrementalMode(variantScope.getVariantConfiguration()) != IncrementalMode.NONE);
final BaseVariantData extends BaseVariantOutputData> variantData = variantScope.getVariantData();
final GradleVariantConfiguration config = variantData.getVariantConfiguration();
TransformManager transformManager = variantScope.getTransformManager();
// ---- Code Coverage first -----
boolean isTestCoverageEnabled = config.getBuildType().isTestCoverageEnabled() &&
!config.getType().isForTesting() &&
getIncrementalMode(variantScope.getVariantConfiguration()) == IncrementalMode.NONE;
if (isTestCoverageEnabled) {
createJacocoTransform(tasks, variantScope);
}
boolean isMinifyEnabled = isMinifyEnabled(variantScope);
boolean isMultiDexEnabled = config.isMultiDexEnabled();
// Switch to native multidex if possible when using instant run.
boolean isLegacyMultiDexMode = isLegacyMultidexMode(variantScope);
AndroidConfig extension = variantScope.getGlobalScope().getExtension();
// ----- External Transforms -----
// apply all the external transforms.
List customTransforms = extension.getTransforms();
List> customTransformsDependencies = extension.getTransformsDependencies();
for (int i = 0, count = customTransforms.size() ; i < count ; i++) {
Transform transform = customTransforms.get(i);
List deps = customTransformsDependencies.get(i);
transformManager
.addTransform(tasks, variantScope, transform)
.ifPresent(t -> {
if (!deps.isEmpty()) {
t.dependsOn(tasks, deps);
}
// if the task is a no-op then we make assemble task depend on it.
if (transform.getScopes().isEmpty()) {
variantScope.getAssembleTask().dependsOn(tasks, t);
}
});
}
// ----- Minify next -----
if (isMinifyEnabled) {
boolean outputToJarFile = isMultiDexEnabled && isLegacyMultiDexMode;
createMinifyTransform(tasks, variantScope, outputToJarFile);
}
// ----- 10x support
AndroidTask preColdSwapTask = null;
if (variantScope.getInstantRunBuildContext().isInInstantRunMode()) {
AndroidTask allActionsAnchorTask =
createInstantRunAllActionsTasks(tasks, variantScope);
assert variantScope.getInstantRunTaskManager() != null;
preColdSwapTask = variantScope.getInstantRunTaskManager()
.createPreColdswapTask(project);
preColdSwapTask.dependsOn(tasks, allActionsAnchorTask);
// when dealing with platforms that can handle multi dexes natively, automatically
// turn on multi dexing so shards are packaged as individual dex files.
if (InstantRunPatchingPolicy.PRE_LOLLIPOP !=
variantScope.getInstantRunBuildContext().getPatchingPolicy()) {
isMultiDexEnabled = true;
// force pre-dexing to be true as we rely on individual slices to be packaged
// separately.
extension.getDexOptions().setPreDexLibraries(true);
variantScope.getInstantRunTaskManager().createSlicerTask();
}
extension.getDexOptions().setJumboMode(true);
}
// ----- Multi-Dex support
Optional> multiDexClassListTask;
// non Library test are running as native multi-dex
if (isMultiDexEnabled && isLegacyMultiDexMode) {
if (!variantData.getVariantConfiguration().getBuildType().isUseProguard()) {
throw new IllegalStateException(
"Build-in class shrinker and multidex are not supported yet.");
}
// ----------
// create a transform to jar the inputs into a single jar.
if (!isMinifyEnabled) {
// merge the classes only, no need to package the resources since they are
// not used during the computation.
JarMergingTransform jarMergingTransform = new JarMergingTransform(
TransformManager.SCOPE_FULL_PROJECT);
transformManager
.addTransform(tasks, variantScope, jarMergingTransform)
.ifPresent(variantScope::addColdSwapBuildTask);
}
// ---------
// create the transform that's going to take the code and the proguard keep list
// from above and compute the main class list.
MultiDexTransform multiDexTransform = new MultiDexTransform(
variantScope,
extension.getDexOptions(),
null);
multiDexClassListTask =
transformManager.addTransform(tasks, variantScope, multiDexTransform);
multiDexClassListTask.ifPresent(variantScope::addColdSwapBuildTask);
} else {
multiDexClassListTask = Optional.empty();
}
// create dex transform
DefaultDexOptions dexOptions = DefaultDexOptions.copyOf(extension.getDexOptions());
if (variantData.getType().isForTesting()) {
// Don't use custom dx flags when compiling the test APK. They can break the test APK,
// like --minimal-main-dex.
dexOptions.setAdditionalParameters(ImmutableList.of());
}
DexTransform dexTransform = new DexTransform(
dexOptions,
config.getBuildType().isDebuggable(),
isMultiDexEnabled,
isMultiDexEnabled && isLegacyMultiDexMode ? variantScope.getMainDexListFile() : null,
variantScope.getPreDexOutputDir(),
variantScope.getGlobalScope().getAndroidBuilder(),
getLogger(),
variantScope.getInstantRunBuildContext(),
AndroidGradleOptions.getBuildCache(variantScope.getGlobalScope().getProject()));
Optional> dexTask =
transformManager.addTransform(tasks, variantScope, dexTransform);
// need to manually make dex task depend on MultiDexTransform since there's no stream
// consumption making this automatic
dexTask.ifPresent(t -> {
t.optionalDependsOn(tasks, multiDexClassListTask.orElse(null));
variantScope.addColdSwapBuildTask(t);
});
if (preColdSwapTask != null) {
for (AndroidTask extends DefaultTask> task : variantScope.getColdSwapBuildTasks()) {
task.dependsOn(tasks, preColdSwapTask);
}
}
}
private boolean isLegacyMultidexMode(@NonNull VariantScope variantScope) {
return variantScope.getVariantData().getVariantConfiguration().isLegacyMultiDexMode() &&
(getIncrementalMode(variantScope.getVariantConfiguration()) == IncrementalMode.NONE
|| variantScope.getInstantRunBuildContext().getPatchingPolicy() ==
InstantRunPatchingPolicy.PRE_LOLLIPOP);
}
private boolean isMinifyEnabled(VariantScope variantScope) {
return variantScope.getVariantConfiguration().isMinifyEnabled()
|| isTestedAppMinified(variantScope);
}
/**
* Default values if {@code false}, only {@link TestApplicationTaskManager}
* overrides this, because tested applications might be minified.
* @return if the tested application is minified
*/
protected boolean isTestedAppMinified(@NonNull VariantScope variantScope){
return false;
}
/**
* Create InstantRun related tasks that should be ran right after the java compilation task.
*/
@NonNull
private AndroidTask createInstantRunAllActionsTasks(
@NonNull TaskFactory tasks, @NonNull VariantScope variantScope) {
AndroidTask allActionAnchorTask = getAndroidTasks().create(tasks,
new InstantRunAnchorTaskConfigAction(variantScope));
TransformManager transformManager = variantScope.getTransformManager();
ExtractJarsTransform extractJarsTransform = new ExtractJarsTransform(
ImmutableSet.of(QualifiedContent.DefaultContentType.CLASSES),
ImmutableSet.of(Scope.SUB_PROJECTS));
Optional> extractJarsTask =
transformManager
.addTransform(tasks, variantScope, extractJarsTransform);
InstantRunTaskManager instantRunTaskManager =
new InstantRunTaskManager(
getLogger(),
variantScope,
variantScope.getTransformManager(),
androidTasks,
tasks,
recorder);
variantScope.setInstantRunTaskManager(instantRunTaskManager);
AndroidTask buildInfoLoaderTask =
instantRunTaskManager.createInstantRunAllTasks(
variantScope.getGlobalScope().getExtension().getDexOptions(),
androidBuilder::getDexByteCodeConverter,
extractJarsTask.orElse(null),
allActionAnchorTask,
getResMergingScopes(variantScope),
new SupplierTask() {
private final VariantOutputScope variantOutputScope =
variantScope.getVariantData().getMainOutput().getScope();
@Nullable
@Override
public AndroidTask> getBuilderTask() {
return variantOutputScope.getManifestProcessorTask();
}
@Override
public File get() {
return variantOutputScope
.getVariantScope()
.getInstantRunManifestOutputFile();
}
},
new SupplierTask() {
private final VariantOutputScope variantOutputScope =
variantScope.getVariantData().getMainOutput().getScope();
@Nullable
@Override
public AndroidTask> getBuilderTask() {
return variantOutputScope.getProcessResourcesTask();
}
@Override
public File get() {
return variantOutputScope.getProcessResourcePackageOutputFile();
}
},
true /* addResourceVerifier */);
if (variantScope.getSourceGenTask() != null) {
variantScope.getSourceGenTask().dependsOn(tasks, buildInfoLoaderTask);
}
return allActionAnchorTask;
}
protected void handleJacocoDependencies(
@NonNull TaskFactory tasks,
@NonNull VariantScope variantScope) {
GradleVariantConfiguration config = variantScope.getVariantConfiguration();
// we add the jacoco jar if coverage is enabled, but we don't add it
// for test apps as it's already part of the tested app.
// For library project, since we cannot use the local jars of the library,
// we add it as well.
boolean isTestCoverageEnabled = config.getBuildType().isTestCoverageEnabled() &&
getIncrementalMode(variantScope.getVariantConfiguration()) == IncrementalMode.NONE &&
(!config.getType().isForTesting() ||
(config.getTestedConfig() != null &&
config.getTestedConfig().getType() == VariantType.LIBRARY));
if (isTestCoverageEnabled) {
AndroidTask agentTask = getJacocoAgentTask(tasks);
// also add a new stream for the jacoco agent Jar
variantScope.getTransformManager().addStream(OriginalStream.builder()
.addContentTypes(TransformManager.CONTENT_JARS)
.addScope(Scope.EXTERNAL_LIBRARIES)
.setJar(globalScope.getJacocoAgent())
.setDependency(agentTask.getName())
.build());
}
}
public void createJacocoTransform(
@NonNull TaskFactory taskFactory,
@NonNull final VariantScope variantScope) {
JacocoTransform jacocoTransform = new JacocoTransform(project.getConfigurations());
Optional> task =
variantScope
.getTransformManager()
.addTransform(taskFactory, variantScope, jacocoTransform);
AndroidTask agentTask = getJacocoAgentTask(taskFactory);
task.ifPresent(t -> t.dependsOn(taskFactory, agentTask));
}
@Nullable
public AndroidTask createJackTask(
@NonNull final TaskFactory tasks,
@NonNull final VariantScope scope,
final boolean compileJavaSources) {
if (scope.getTestedVariantData() != null) {
scope.getTransformManager().addStream(
OriginalStream.builder()
.addContentType(ExtendedContentType.JACK)
.addScope(Scope.TESTED_CODE)
.setJar(scope.getTestedVariantData().getScope().getJackClassesZip())
.setDependency(scope.getTestedVariantData().getScope().getJavaCompilerTask().getName())
.build());
}
// ----- Create PreDex tasks for libraries -----
JackPreDexTransform preDexPackagedTransform =
JackPreDexTransform.builder()
.buildToolInfo(androidBuilder::getBuildToolInfo)
.errorReporter(androidBuilder.getErrorReporter())
.javaProcessExecutor(androidBuilder.getJavaProcessExecutor())
.javaMaxHeapSize(
globalScope.getExtension().getDexOptions().getJavaMaxHeapSize())
.coreJackOptions(scope.getVariantConfiguration().getJackOptions())
.minApiVersion(scope.getMinSdkVersion())
.forPackagedLibs()
.debugJackInternals(isDebugLog())
.verboseProcessing(isInfoLog())
.debuggable(scope.getVariantConfiguration().getBuildType().isDebuggable())
.create();
Optional> packageTask =
scope.getTransformManager().addTransform(tasks, scope, preDexPackagedTransform);
if (scope.getVariantConfiguration().isTestCoverageEnabled()) {
AndroidTask jacocoTask = getJacocoAgentTask(tasks);
packageTask.ifPresent(t ->
t.optionalDependsOn(
tasks,
scope.getVariantData()
.getVariantDependency()
.getPackageConfiguration()
.getBuildDependencies(),
jacocoTask));
}
JackPreDexTransform preDexRuntimeTransform =
JackPreDexTransform.builder()
.bootClasspath(() -> androidBuilder.getBootClasspath(true))
.buildToolInfo(androidBuilder::getBuildToolInfo)
.errorReporter(androidBuilder.getErrorReporter())
.javaProcessExecutor(androidBuilder.getJavaProcessExecutor())
.javaMaxHeapSize(
globalScope.getExtension().getDexOptions().getJavaMaxHeapSize())
.coreJackOptions(scope.getVariantConfiguration().getJackOptions())
.minApiVersion(scope.getMinSdkVersion())
.forClasspathLibs()
.debugJackInternals(isDebugLog())
.verboseProcessing(isInfoLog())
.debuggable(scope.getVariantConfiguration().getBuildType().isDebuggable())
.create();
scope.getTransformManager().addTransform(tasks, scope, preDexRuntimeTransform);
// ----- Create Jack Task -----
JackTransform jackTransform = new JackTransform(scope, compileJavaSources);
scope.getVariantData().jackTransform = jackTransform;
TransformTask.ConfigActionCallback jackTransformCallback =
(transform, task) -> {
scope.getVariantData().javaCompilerTask = task;
scope.getVariantData().mappingFileProviderTask =
new FileSupplier() {
@NonNull
@Override
public Task getTask() {
return task;
}
@Override
public File get() {
return transform.getMappingFile();
}
};
};
final AndroidTask jackTask =
scope.getTransformManager()
.addTransform(
tasks,
scope,
jackTransform,
jackTransformCallback)
.orElse(null);
if (jackTask == null) {
// Error adding JackTransform. A SyncIssue was already emitted at this point.
getLogger().error("Could not create jack transform.", new Throwable());
return null;
}
// Jack is compiling and also providing the binary and mapping files.
setJavaCompilerTask(jackTask, tasks, scope);
setupCompileTaskDependencies(tasks, scope, jackTask);
jackTask.optionalDependsOn(tasks, scope.getMergeJavaResourcesTask());
jackTask.dependsOn(tasks, scope.getSourceGenTask());
return jackTask;
}
/**
* Must be called before the javac task is created so that we it can be earlier in the transform
* pipeline.
*/
protected void createDataBindingMergeArtifactsTaskIfNecessary(
@NonNull TaskFactory tasks, @NonNull VariantScope variantScope) {
if (!extension.getDataBinding().isEnabled()) {
return;
}
VariantType type = variantScope.getVariantData().getType();
boolean isTest = type == VariantType.ANDROID_TEST || type == VariantType.UNIT_TEST;
if (isTest && !extension.getDataBinding().isEnabledForTests()) {
BaseVariantData testedVariantData = variantScope.getTestedVariantData();
if (testedVariantData.getType() != LIBRARY) {
return;
}
}
setDataBindingAnnotationProcessorParams(variantScope);
AndroidTask existing = variantScope
.getDataBindingMergeArtifactsTask();
if (existing != null) {
return;
}
Optional> dataBindingMergeTask;
dataBindingMergeTask = variantScope
.getTransformManager()
.addTransform(tasks, variantScope,
new DataBindingMergeArtifactsTransform(getLogger(), variantScope));
variantScope.setDataBindingMergeArtifactsTask(dataBindingMergeTask.get());
}
protected void createDataBindingTasksIfNecessary(@NonNull TaskFactory tasks,
@NonNull VariantScope scope) {
if (!extension.getDataBinding().isEnabled()) {
return;
}
VariantType type = scope.getVariantData().getType();
boolean isTest = type == VariantType.ANDROID_TEST || type == VariantType.UNIT_TEST;
if (isTest && !extension.getDataBinding().isEnabledForTests()) {
BaseVariantData testedVariantData = scope.getTestedVariantData();
if (testedVariantData.getType() != LIBRARY) {
return;
}
}
boolean isJack = Boolean.TRUE.equals(
scope.getVariantConfiguration().getJackOptions().isEnabled());
if (isJack) {
getLogger().warn("Using Data Binding with Jack compiler is an experimental feature.");
}
dataBindingBuilder.setDebugLogEnabled(getLogger().isDebugEnabled());
AndroidTask processLayoutsTask = androidTasks
.create(tasks, new DataBindingProcessLayoutsTask.ConfigAction(scope));
scope.setDataBindingProcessLayoutsTask(processLayoutsTask);
scope.getGenerateRClassTask().dependsOn(tasks, processLayoutsTask);
processLayoutsTask.dependsOn(tasks, scope.getMergeResourcesTask());
AndroidTask exportBuildInfo = androidTasks
.create(tasks, new DataBindingExportBuildInfoTask.ConfigAction(scope));
exportBuildInfo.dependsOn(tasks, processLayoutsTask);
exportBuildInfo.dependsOn(tasks, scope.getSourceGenTask());
AndroidTask extends Task> javaCompilerTask = scope.getJavaCompilerTask();
if (javaCompilerTask != null) {
javaCompilerTask.dependsOn(tasks, exportBuildInfo);
javaCompilerTask.dependsOn(tasks, scope.getDataBindingMergeArtifactsTask());
}
// support for split apk
for (BaseVariantOutputData baseVariantOutputData : scope.getVariantData().getOutputs()) {
final ProcessAndroidResources processResTask =
baseVariantOutputData.processResourcesTask;
if (processResTask != null) {
processResTask.dependsOn(processLayoutsTask.getName());
}
}
}
private void setDataBindingAnnotationProcessorParams(@NonNull VariantScope scope) {
BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
GradleVariantConfiguration variantConfiguration = variantData.getVariantConfiguration();
CoreJavaCompileOptions javaCompileOptions = variantConfiguration
.getJavaCompileOptions();
CoreAnnotationProcessorOptions processorOptions = javaCompileOptions
.getAnnotationProcessorOptions();
if (processorOptions instanceof AnnotationProcessorOptions) {
AnnotationProcessorOptions ots = (AnnotationProcessorOptions) processorOptions;
// Specify data binding only if another class is specified. Doing so disables discovery
// so we must explicitly list data binding.
if (!ots.getClassNames().isEmpty()
&& !ots.getClassNames().contains(DataBindingBuilder.PROCESSOR_NAME)) {
ots.className(DataBindingBuilder.PROCESSOR_NAME);
}
String packageName = variantConfiguration.getOriginalApplicationId();
final DataBindingCompilerArgs.Type type;
final BaseVariantData artifactVariantData;
final boolean isTest;
if (variantData.getType() == VariantType.ANDROID_TEST) {
artifactVariantData = scope.getTestedVariantData();
isTest = true;
} else {
artifactVariantData = variantData;
isTest = false;
}
if (artifactVariantData.getType() == VariantType.LIBRARY) {
type = DataBindingCompilerArgs.Type.LIBRARY;
} else {
type = DataBindingCompilerArgs.Type.APPLICATION;
}
int minApi = variantConfiguration.getMinSdkVersion().getApiLevel();
DataBindingCompilerArgs args = DataBindingCompilerArgs.builder()
.bundleFolder(scope.getBundleFolderForDataBinding())
.enabledForTests(extension.getDataBinding().isEnabledForTests())
.enableDebugLogs(getLogger().isDebugEnabled())
.buildFolder(scope.getBuildFolderForDataBindingCompiler())
.sdkDir(scope.getGlobalScope().getSdkHandler().getSdkFolder())
.xmlOutDir(scope.getLayoutInfoOutputForDataBinding())
.exportClassListTo(variantData.getType().isExportDataBindingClassList() ?
scope.getGeneratedClassListOutputFileForDataBinding() : null)
.printEncodedErrorLogs(dataBindingBuilder.getPrintMachineReadableOutput())
.modulePackage(packageName)
.minApi(minApi)
.testVariant(isTest)
.type(type)
.build();
ots.arguments(args.toMap());
} else {
getLogger().error("Cannot setup data binding for %s because java compiler options"
+ " is not an instance of AnnotationProcessorOptions", processorOptions);
}
}
/**
* Creates the final packaging task, and optionally the zipalign task (if the variant is signed)
*
* @param publishApk if true the generated APK gets published.
* @param fullBuildInfoGeneratorTask task that generates the build-info.xml for full build.
*/
public void createPackagingTask(@NonNull TaskFactory tasks,
@NonNull VariantScope variantScope,
boolean publishApk,
@Nullable AndroidTask fullBuildInfoGeneratorTask) {
GlobalScope globalScope = variantScope.getGlobalScope();
ApkVariantData variantData = (ApkVariantData) variantScope.getVariantData();
boolean signedApk = variantData.isSigned();
boolean multiOutput = variantData.getOutputs().size() > 1;
boolean abiSpecified =
!Strings.isNullOrEmpty(AndroidGradleOptions.getBuildTargetAbi(project));
GradleVariantConfiguration variantConfiguration = variantScope.getVariantConfiguration();
/**
* PrePackaging step class that will look if the packaging of the main APK split is
* necessary when running in InstantRun mode. In InstantRun mode targeting an api 23 or
* above device, resources are packaged in the main split APK. However when a warm swap is
* possible, it is not necessary to produce immediately the new main SPLIT since the runtime
* use the resources.ap_ file directly. However, as soon as an incompatible change forcing a
* cold swap is triggered, the main APK must be rebuilt (even if the resources were changed
* in a previous build).
*/
IncrementalMode incrementalMode = getIncrementalMode(variantConfiguration);
List outputDataList = variantData.getOutputs();
// loop on all outputs. The only difference will be the name of the task, and location
// of the generated data.
for (final ApkVariantOutputData variantOutputData : outputDataList) {
final VariantOutputScope variantOutputScope = variantOutputData.getScope();
final String outputName = variantOutputData.getFullName();
InstantRunPatchingPolicy patchingPolicy =
variantScope.getInstantRunBuildContext().getPatchingPolicy();
DefaultGradlePackagingScope packagingScope =
new DefaultGradlePackagingScope(variantOutputScope);
AndroidTask packageApp =
androidTasks.create(
tasks,
new PackageApplication.StandardConfigAction(
packagingScope, patchingPolicy));
AndroidTask packageInstantRunResources = null;
if (variantScope.getInstantRunBuildContext().isInInstantRunMode()) {
packageInstantRunResources = androidTasks.create(
tasks,
new PackageApplication.InstantRunResourcesConfigAction(
variantScope.getInstantRunResourcesFile(),
packagingScope,
patchingPolicy));
// Make sure the MAIN artifact is registered after the RESOURCES one.
packageApp.dependsOn(tasks, packageInstantRunResources);
}
packageApp.configure(
tasks,
task -> variantOutputData.packageAndroidArtifactTask = task);
TransformManager transformManager = variantScope.getTransformManager();
// Common code for both packaging tasks.
Consumer> configureResourcesAndAssetsDependencies =
task -> {
task.dependsOn(tasks, variantScope.getMergeAssetsTask());
task.dependsOn(tasks, variantOutputScope.getProcessResourcesTask());
transformManager
.getStreams(StreamFilter.RESOURCES)
.forEach(stream -> task.dependsOn(tasks, stream.getDependencies()));
};
configureResourcesAndAssetsDependencies.accept(packageApp);
if (packageInstantRunResources != null) {
configureResourcesAndAssetsDependencies.accept(packageInstantRunResources);
}
CoreSigningConfig signingConfig = packagingScope.getSigningConfig();
//noinspection VariableNotUsedInsideIf - we use the whole packaging scope below.
if (signingConfig != null) {
packageApp.dependsOn(tasks, getValidateSigningTask(tasks, packagingScope));
}
packageApp.optionalDependsOn(
tasks,
variantOutputScope.getShrinkResourcesTask(),
// TODO: When Jack is converted, add activeDexTask to VariantScope.
variantOutputScope.getVariantScope().getJavaCompilerTask(),
// TODO: Remove when Jack is converted to AndroidTask.
variantData.javaCompilerTask,
variantOutputData.packageSplitResourcesTask,
variantOutputData.packageSplitAbiTask);
for (TransformStream stream : transformManager.getStreams(StreamFilter.DEX)) {
// TODO Optimize to avoid creating too many actions
packageApp.dependsOn(tasks, stream.getDependencies());
}
for (TransformStream stream : transformManager.getStreams(StreamFilter.NATIVE_LIBS)) {
// TODO Optimize to avoid creating too many actions
packageApp.dependsOn(tasks, stream.getDependencies());
}
variantScope.setPackageApplicationTask(packageApp);
AndroidTask> appTask = packageApp;
if (signedApk) {
/*
* There may be a zip align task in the variant output scope, even if we don't
* need one for this.
*/
if (variantData.getZipAlignEnabled()
&& variantOutputScope.getSplitZipAlignTask() != null) {
appTask.dependsOn(tasks, variantOutputScope.getSplitZipAlignTask());
}
}
checkState(variantScope.getAssembleTask() != null);
if (fullBuildInfoGeneratorTask != null) {
AndroidTask finalPackageInstantRunResources =
packageInstantRunResources;
AndroidTask> finalAppTask = appTask;
fullBuildInfoGeneratorTask.configure(tasks, task -> {
task.mustRunAfter(
finalAppTask.getName(),
finalPackageInstantRunResources.getName());
});
variantScope.getAssembleTask().dependsOn(
tasks, fullBuildInfoGeneratorTask.getName());
}
// Add an assemble task
if (multiOutput) {
// create a task for this output
variantOutputScope.setAssembleTask(createAssembleTask(tasks, variantOutputData));
// If ABI is specified, the variant assemble task depends on the output assemble
// task for the given ABI. Otherwise, it depends on all output assemble tasks.
if (!abiSpecified
|| abiSpecified && variantOutputData == variantData.getMainOutput()) {
variantScope
.getAssembleTask()
.dependsOn(tasks, variantOutputScope.getAssembleTask());
}
} else {
// single output
variantOutputScope.setAssembleTask(variantScope.getAssembleTask());
variantOutputData.assembleTask = variantData.assembleVariantTask;
}
if (!signedApk && variantOutputData.packageSplitResourcesTask != null) {
// in case we are not signing the resulting APKs and we have some pure splits
// we should manually copy them from the intermediate location to the final
// apk location unmodified.
final String appTaskName = appTask.getName();
AndroidTask copySplitTask = androidTasks.create(
tasks,
variantOutputScope.getTaskName("copySplit"),
Copy.class,
copyTask -> {
copyTask.setDestinationDir(getGlobalScope().getApkLocation());
copyTask.from(
variantOutputData
.packageSplitResourcesTask
.getOutputDirectory());
copyTask.mustRunAfter(appTaskName);
});
variantOutputScope.getAssembleTask().dependsOn(tasks, copySplitTask);
}
variantOutputScope.getAssembleTask().dependsOn(tasks, appTask);
if (publishApk) {
final String projectBaseName = globalScope.getProjectBaseName();
// if this variant is the default publish config or we also should publish non
// defaults, proceed with declaring our artifacts.
if (getExtension().getDefaultPublishConfig().equals(outputName)) {
appTask.configure(tasks, packageTask -> project.getArtifacts().add("default",
AndroidArtifacts.buildApkArtifact(
projectBaseName, null, (FileSupplier) packageTask)
));
for (FileSupplier outputFileProvider :
variantOutputData.getSplitOutputFileSuppliers()) {
project.getArtifacts().add("default",
AndroidArtifacts.buildApkArtifact(
projectBaseName, null, outputFileProvider));
}
try {
if (variantOutputData.getMetadataFile() != null) {
project.getArtifacts().add(
"default" + VariantDependencies.CONFIGURATION_METADATA,
AndroidArtifacts.buildMetadataArtifact(
projectBaseName, variantOutputData.getMetadataFile()));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
if (variantData.getMappingFileProvider() != null) {
project.getArtifacts().add(
"default" + VariantDependencies.CONFIGURATION_MAPPING,
AndroidArtifacts.buildMappingArtifact(
projectBaseName, variantData.getMappingFileProvider()));
}
}
if (getExtension().getPublishNonDefault()) {
appTask.configure(tasks, packageTask -> project.getArtifacts().add(
variantData.getVariantDependency().getPublishConfiguration().getName(),
AndroidArtifacts.buildApkArtifact(
projectBaseName, null, (FileSupplier) packageTask)));
for (FileSupplier outputFileProvider :
variantOutputData.getSplitOutputFileSuppliers()) {
project.getArtifacts().add(
variantData.getVariantDependency().getPublishConfiguration().getName(),
AndroidArtifacts.buildApkArtifact(
projectBaseName, null, outputFileProvider));
}
try {
if (variantOutputData.getMetadataFile() != null) {
project.getArtifacts().add(
variantData.getVariantDependency().getMetadataConfiguration().getName(),
AndroidArtifacts.buildMetadataArtifact(
projectBaseName, variantOutputData.getMetadataFile()));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
if (variantData.getMappingFileProvider() != null) {
project.getArtifacts().add(
variantData.getVariantDependency().getMappingConfiguration().getName(),
AndroidArtifacts.buildMappingArtifact(
projectBaseName, variantData.getMappingFileProvider()));
}
if (variantData.classesJarTask != null) {
project.getArtifacts().add(
variantData.getVariantDependency().getClassesConfiguration().getName(),
variantData.classesJarTask);
}
}
}
}
// create install task for the variant Data. This will deal with finding the
// right output if there are more than one.
// Add a task to install the application package
if (signedApk) {
AndroidTask installTask = androidTasks.create(
tasks, new InstallVariantTask.ConfigAction(variantScope));
installTask.dependsOn(tasks, variantScope.getAssembleTask());
}
if (getExtension().getLintOptions().isCheckReleaseBuilds()
&& (incrementalMode == IncrementalMode.NONE)) {
createLintVitalTask(tasks, variantData);
}
// add an uninstall task
final AndroidTask uninstallTask = androidTasks.create(
tasks, new UninstallTask.ConfigAction(variantScope));
tasks.named(UNINSTALL_ALL, uninstallAll -> uninstallAll.dependsOn(uninstallTask.getName()));
}
protected AndroidTask> getValidateSigningTask(
@NonNull TaskFactory tasks, @NonNull PackagingScope packagingScope) {
ValidateSigningTask.ConfigAction configAction =
new ValidateSigningTask.ConfigAction(packagingScope);
AndroidTask> validateSigningTask = androidTasks.get(configAction.getName());
if (validateSigningTask == null) {
validateSigningTask = androidTasks.create(tasks, configAction);
}
return validateSigningTask;
}
public AndroidTask createAssembleTask(
@NonNull TaskFactory tasks,
@NonNull final BaseVariantOutputData variantOutputData) {
return androidTasks.create(
tasks,
variantOutputData.getScope().getTaskName("assemble"),
task -> {
variantOutputData.assembleTask = task;
});
}
public AndroidTask createAssembleTask(
@NonNull TaskFactory tasks,
@NonNull final BaseVariantData extends BaseVariantOutputData> variantData) {
return androidTasks.create(
tasks,
variantData.getScope().getTaskName("assemble"),
task -> {
variantData.assembleVariantTask = task;
});
}
@NonNull
public AndroidTask createAssembleTask(
@NonNull TaskFactory tasks,
@NonNull VariantDimensionData dimensionData) {
final String sourceSetName =
StringHelper.capitalize(dimensionData.getSourceSet().getName());
return androidTasks.create(
tasks,
"assemble" + sourceSetName,
assembleTask -> {
assembleTask.setDescription("Assembles all " + sourceSetName + " builds.");
assembleTask.setGroup(BasePlugin.BUILD_GROUP);
});
}
public AndroidTask getJacocoAgentTask(TaskFactory tasks) {
if (jacocoAgentTask == null) {
jacocoAgentTask = androidTasks.create(tasks, new JacocoAgentConfigAction(globalScope));
}
return jacocoAgentTask;
}
/**
* Creates a zip align task that aligns a provided APK.
*
* @param name the name of the task
* @param inputFile the file to align
* @param outputFile where to put the aligned file
* @param variantOutputScope the variant output scope required to find build tools
*/
@NonNull
public ZipAlign createZipAlignTask(
@NonNull String name,
@NonNull File inputFile,
@NonNull File outputFile,
@NonNull VariantOutputScope variantOutputScope) {
// Add a task to zip align application package
ZipAlign zipAlignTask =
project
.getTasks()
.create(
name,
ZipAlign.class,
new ZipAlign.ConfigAction(variantOutputScope));
ConventionMappingHelper.map(zipAlignTask, "inputFile", () -> inputFile);
ConventionMappingHelper.map(zipAlignTask, "outputFile", () -> outputFile);
/*
* We need to make sure we have the manifest available so, we need a dependency.
*/
zipAlignTask.dependsOn(
variantOutputScope.getVariantScope().getPackageApplicationTask().getName());
return zipAlignTask;
}
protected void createMinifyTransform(
@NonNull TaskFactory taskFactory,
@NonNull final VariantScope variantScope,
boolean createJarFile) {
doCreateMinifyTransform(taskFactory,
variantScope,
null /*mappingConfiguration*/, // No mapping in non-test modules.
createJarFile);
}
/**
* Actually creates the minify transform, using the given mapping configuration. The mapping is
* only used by test-only modules.
*/
protected final void doCreateMinifyTransform(
@NonNull TaskFactory taskFactory,
@NonNull final VariantScope variantScope,
@Nullable Configuration mappingConfiguration,
boolean createJarFile) {
if (variantScope.getVariantData().getVariantConfiguration().getBuildType().isUseProguard()) {
createProguardTransform(taskFactory, variantScope, mappingConfiguration, createJarFile);
createShrinkResourcesTransform(taskFactory, variantScope);
} else {
// Since the built-in class shrinker does not obfuscate, there's no point running
// it on the test APK (it also doesn't have a -dontshrink mode).
if (variantScope.getTestedVariantData() == null) {
createNewShrinkerTransform(variantScope, taskFactory);
createShrinkResourcesTransform(taskFactory, variantScope);
}
}
}
private void createNewShrinkerTransform(VariantScope scope, TaskFactory taskFactory) {
NewShrinkerTransform transform = new NewShrinkerTransform(scope);
applyProguardConfig(transform, scope.getVariantData());
if (getIncrementalMode(scope.getVariantConfiguration()) != IncrementalMode.NONE) {
//TODO: This is currently overly broad, as finding the actual application class
// requires manually parsing the manifest, see
// aapt -D (getMainDexListProguardOutputFile)
transform.keep("class ** extends android.app.Application {*;}");
transform.keep("class com.android.tools.fd.** {*;}");
}
scope.getTransformManager().addTransform(taskFactory, scope, transform);
}
private void createProguardTransform(
@NonNull TaskFactory taskFactory,
@NonNull VariantScope variantScope,
@Nullable Configuration mappingConfiguration,
boolean createJarFile) {
if (getIncrementalMode(variantScope.getVariantConfiguration()) != IncrementalMode.NONE) {
logger.warn("Instant Run: Proguard is not compatible with instant run. "
+ "It has been disabled for {}",
variantScope.getVariantConfiguration().getFullName());
return;
}
final BaseVariantData extends BaseVariantOutputData> variantData = variantScope
.getVariantData();
final GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
final BaseVariantData testedVariantData = variantScope.getTestedVariantData();
ProGuardTransform transform = new ProGuardTransform(variantScope, createJarFile);
if (testedVariantData != null) {
applyProguardDefaultsForTest(transform);
// All -dontwarn rules for test dependencies should go in here:
transform.setConfigurationFiles(
testedVariantData.getVariantConfiguration()::getTestProguardFiles);
// register the mapping file which may or may not exists (only exist if obfuscation)
// is enabled.
transform.applyTestedMapping(testedVariantData.getMappingFile());
} else if (isTestedAppMinified(variantScope)){
applyProguardDefaultsForTest(transform);
// All -dontwarn rules for test dependencies should go in here:
transform.setConfigurationFiles(variantConfig::getTestProguardFiles);
transform.applyTestedMapping(mappingConfiguration);
} else {
applyProguardConfig(transform, variantData);
if (mappingConfiguration != null) {
transform.applyTestedMapping(mappingConfiguration);
}
}
TransformTask.ConfigActionCallback proGuardTransformCallback =
(proGuardTransform, proGuardTask) ->
variantData.mappingFileProviderTask =
new FileSupplier() {
@NonNull
@Override
public Task getTask() {
return proGuardTask;
}
@Override
public File get() {
return proGuardTransform.getMappingFile();
}
};
Optional> task =
variantScope
.getTransformManager()
.addTransform(
taskFactory,
variantScope,
transform,
proGuardTransformCallback);
task.ifPresent(t -> t.optionalDependsOn(taskFactory, mappingConfiguration));
}
private static void applyProguardDefaultsForTest(ProGuardTransform transform) {
// Don't remove any code in tested app.
transform.dontshrink();
transform.dontoptimize();
// We can't call dontobfuscate, since that would make ProGuard ignore the mapping file.
transform.keep("class * {*;}");
transform.keep("interface * {*;}");
transform.keep("enum * {*;}");
transform.keepattributes();
}
private void createShrinkResourcesTransform(
@NonNull TaskFactory taskFactory,
@NonNull VariantScope scope) {
CoreBuildType buildType = scope.getVariantConfiguration().getBuildType();
if (!buildType.isShrinkResources()) {
// The user didn't enable resource shrinking, silently move on.
return;
}
if (!scope.useResourceShrinker()) {
// The user enabled resource shrinking, but we disabled it for some reason. Try to
// explain.
if (getIncrementalMode(scope.getVariantConfiguration()) != IncrementalMode.NONE) {
logger.warn("Instant Run: Resource shrinker automatically disabled for {}",
scope.getVariantConfiguration().getFullName());
return;
}
if (buildType.isMinifyEnabled() && !buildType.isUseProguard()) {
androidBuilder.getErrorReporter().handleSyncError(
null,
SyncIssue.TYPE_GENERIC,
"Built-in class shrinker and resource shrinking are not supported yet.");
return;
}
return;
}
// if resources are shrink, insert a no-op transform per variant output
// to transform the res package into a stripped res package
for (final BaseVariantOutputData variantOutputData : scope.getVariantData().getOutputs()) {
VariantOutputScope variantOutputScope = variantOutputData.getScope();
ShrinkResourcesTransform shrinkResTransform = new ShrinkResourcesTransform(
variantOutputData,
variantOutputScope.getProcessResourcePackageOutputFile(),
variantOutputScope.getShrinkedResourcesFile(),
androidBuilder,
logger);
AndroidTask shrinkTask =
scope.getTransformManager()
.addTransform(taskFactory, variantOutputScope, shrinkResTransform)
.orElse(null);
// need to record this task since the package task will not depend
// on it through the transform manager.
variantOutputScope.setShrinkResourcesTask(shrinkTask);
}
}
private void applyProguardConfig(
ProguardConfigurable transform,
final BaseVariantData extends BaseVariantOutputData> variantData) {
final GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration();
transform.setConfigurationFiles(
() -> {
Set proguardFiles =
variantConfig.getProguardFiles(
true,
Collections.singletonList(
ProguardFiles.getDefaultProguardFile(
TaskManager.DEFAULT_PROGUARD_CONFIG_FILE,
project)));
// use the first output when looking for the proguard rule output of
// the aapt task. The different outputs are not different in a way that
// makes this rule file different per output.
BaseVariantOutputData outputData = variantData.getMainOutput();
proguardFiles.add(outputData.processResourcesTask.getProguardOutputFile());
return proguardFiles;
});
if (variantData.getType() == LIBRARY) {
transform.keep("class **.R");
transform.keep("class **.R$*");
}
if (variantData.getVariantConfiguration().isTestCoverageEnabled()) {
// when collecting coverage, don't remove the JaCoCo runtime
transform.keep("class com.vladium.** {*;}");
transform.keep("class org.jacoco.** {*;}");
transform.keep("interface org.jacoco.** {*;}");
transform.dontwarn("org.jacoco.**");
}
}
public void createReportTasks(
TaskFactory tasks,
final List> variantDataList) {
AndroidTask dependencyReportTask = androidTasks.create(
tasks,
"androidDependencies",
DependencyReportTask.class,
task -> {
task.setDescription("Displays the Android dependencies of the project.");
task.setVariants(variantDataList);
task.setGroup(ANDROID_GROUP);
});
if (AndroidGradleOptions.isImprovedDependencyResolutionEnabled(project)) {
for (BaseVariantData extends BaseVariantOutputData> variantData : variantDataList) {
dependencyReportTask.dependsOn(
tasks,
variantData.getScope().getResolveDependenciesTask());
}
}
androidTasks.create(tasks, "signingReport", SigningReportTask.class,
task -> {
task.setDescription("Displays the signing info for each variant.");
task.setVariants(variantDataList);
task.setGroup(ANDROID_GROUP);
});
}
public void createAnchorTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
createPreBuildTasks(tasks, scope);
// also create sourceGenTask
final BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
scope.setSourceGenTask(androidTasks.create(tasks,
scope.getTaskName("generate", "Sources"),
Task.class,
task -> {
variantData.sourceGenTask = task;
}));
// and resGenTask
scope.setResourceGenTask(androidTasks.create(tasks,
scope.getTaskName("generate", "Resources"),
Task.class,
task -> {
variantData.resourceGenTask = task;
}));
scope.setAssetGenTask(androidTasks.create(tasks,
scope.getTaskName("generate", "Assets"),
Task.class,
task -> {
variantData.assetGenTask = task;
}));
if (!variantData.getType().isForTesting()
&& variantData.getVariantConfiguration().getBuildType().isTestCoverageEnabled()) {
scope.setCoverageReportTask(androidTasks.create(tasks,
scope.getTaskName("create", "CoverageReport"),
Task.class,
task -> {
task.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
task.setDescription(String.format(
"Creates test coverage reports for the %s variant.",
variantData.getName()));
}));
}
// and compile task
createCompileAnchorTask(tasks, scope);
}
private void createPreBuildTasks(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
final BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
scope.setPreBuildTask(androidTasks.create(tasks,
scope.getTaskName("pre", "Build"), task -> {
variantData.preBuildTask = task;
}));
scope.getPreBuildTask().dependsOn(tasks, MAIN_PREBUILD);
if (isMinifyEnabled(scope)) {
scope.getPreBuildTask().dependsOn(tasks, EXTRACT_PROGUARD_FILES);
}
// for all libraries required by the configurations of this variant, make this task
// depend on all the tasks preparing these libraries.
VariantDependencies variantDependencies = variantData.getVariantDependency();
AndroidTask prepareDependenciesTask = androidTasks.create(tasks,
new PrepareDependenciesTask.ConfigAction(scope, variantDependencies));
scope.setPrepareDependenciesTask(prepareDependenciesTask);
prepareDependenciesTask.dependsOn(tasks, scope.getPreBuildTask());
prepareDependenciesTask.dependsOn(tasks, variantDependencies.getCompileConfiguration());
prepareDependenciesTask.dependsOn(tasks, variantDependencies.getPackageConfiguration());
if (AndroidGradleOptions.isImprovedDependencyResolutionEnabled(project)) {
AndroidTask resolveDependenciesTask =
androidTasks.create(
tasks,
new ResolveDependenciesTask.ConfigAction(scope, dependencyManager));
scope.setResolveDependenciesTask(resolveDependenciesTask);
scope.getPreBuildTask().dependsOn(tasks, resolveDependenciesTask);
resolveDependenciesTask.dependsOn(
tasks,
variantDependencies.getCompileConfiguration(),
variantDependencies.getPackageConfiguration());
// Dependency of the tested variant must be resolved before test variant.
if (variantData instanceof TestVariantData) {
BaseVariantData testedVariantData =
(BaseVariantData) ((TestVariantData) variantData).getTestedVariantData();
VariantScope testedScope = testedVariantData.getScope();
resolveDependenciesTask.dependsOn(tasks, testedScope.getResolveDependenciesTask());
}
} else {
dependencyManager.addDependenciesToPrepareTask(
tasks,
variantData,
prepareDependenciesTask);
}
}
private void createCompileAnchorTask(@NonNull TaskFactory tasks, @NonNull final VariantScope scope) {
final BaseVariantData extends BaseVariantOutputData> variantData = scope.getVariantData();
scope.setCompileTask(androidTasks.create(tasks, new TaskConfigAction() {
@NonNull
@Override
public String getName() {
return scope.getTaskName("compile", "Sources");
}
@NonNull
@Override
public Class getType() {
return Task.class;
}
@Override
public void execute(@NonNull Task task) {
variantData.compileTask = task;
variantData.compileTask.setGroup(BUILD_GROUP);
}
}));
scope.getAssembleTask().dependsOn(tasks, scope.getCompileTask());
}
public void createCheckManifestTask(@NonNull TaskFactory tasks, @NonNull VariantScope scope) {
scope.setCheckManifestTask(
androidTasks.create(tasks, getCheckManifestConfig(scope)));
scope.getCheckManifestTask().dependsOn(tasks, scope.getPreBuildTask());
scope.getPrepareDependenciesTask().dependsOn(tasks, scope.getCheckManifestTask());
}
protected CheckManifest.ConfigAction getCheckManifestConfig(@NonNull VariantScope scope){
return new CheckManifest.ConfigAction(scope, false);
}
@NonNull
protected Logger getLogger() {
return logger;
}
@NonNull
public AndroidTaskRegistry getAndroidTasks() {
return androidTasks;
}
public void addDataBindingDependenciesIfNecessary(DataBindingOptions options) {
if (!options.isEnabled()) {
return;
}
String version = MoreObjects.firstNonNull(options.getVersion(),
dataBindingBuilder.getCompilerVersion());
project.getDependencies().add("compile", SdkConstants.DATA_BINDING_LIB_ARTIFACT + ":"
+ dataBindingBuilder.getLibraryVersion(version));
project.getDependencies().add("compile", SdkConstants.DATA_BINDING_BASELIB_ARTIFACT + ":"
+ dataBindingBuilder.getBaseLibraryVersion(version));
// TODO load config name from source sets
project.getDependencies().add("annotationProcessor",
SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT + ":" +
version);
if (options.isEnabledForTests()) {
project.getDependencies().add("androidTestAnnotationProcessor",
SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT + ":" +
version);
}
if (options.getAddDefaultAdapters()) {
project.getDependencies()
.add("compile", SdkConstants.DATA_BINDING_ADAPTER_LIB_ARTIFACT + ":" +
dataBindingBuilder.getBaseAdaptersVersion(version));
}
}
protected void configureTestData(AbstractTestDataImpl testData) {
testData.setAnimationsDisabled(
getGlobalScope().getExtension().getTestOptions().getAnimationsDisabled());
}
}