All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.build.gradle.internal.TaskManager Maven / Gradle / Ivy

The newest version!
/*
 * 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 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 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 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 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 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 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 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 variantData = scope.getVariantData(); checkState(variantData.getSplitHandlingPolicy().equals( SplitHandlingPolicy.RELEASE_21_AND_AFTER_POLICY), "Can only create split resources tasks for pure splits."); List 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 createJavacTask( @NonNull final TaskFactory tasks, @NonNull final VariantScope scope) { final BaseVariantData variantData = scope.getVariantData(); AndroidTask javacIncrementalSafeguard = androidTasks.create(tasks, new IncrementalSafeguard.ConfigAction(scope)); AndroidTask preCompileTask = androidTasks.create(tasks, new JavaPreCompileTask.ConfigAction(scope)); final AndroidTask 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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()); } }