| /* |
| * 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.SdkConstants.FD_ASSETS; |
| import static com.android.SdkConstants.FD_RES; |
| import static com.android.SdkConstants.FN_ANDROID_MANIFEST_XML; |
| import static com.android.SdkConstants.FN_LINT_JAR; |
| import static com.android.SdkConstants.FN_RESOURCE_TEXT; |
| import static com.android.SdkConstants.FN_SPLIT_LIST; |
| import static com.android.build.gradle.internal.dependency.VariantDependencies.CONFIG_NAME_LINTCHECKS; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.ALL; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.EXTERNAL; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope.MODULE; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.CLASSES; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.DATA_BINDING_ARTIFACT; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.DATA_BINDING_BASE_CLASS_LOG_ARTIFACT; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.JAVA_RES; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.JNI; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.PROGUARD_RULES; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH; |
| import static com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.AAPT_FRIENDLY_MERGED_MANIFESTS; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.ANNOTATION_PROCESSOR_LIST; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.APK_MAPPING; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.DATA_BINDING_BASE_CLASS_LOGS_DEPENDENCY_ARTIFACTS; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.DATA_BINDING_BASE_CLASS_SOURCE_OUT; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.DATA_BINDING_DEPENDENCY_ARTIFACTS; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.INSTANT_RUN_MAIN_APK_RESOURCES; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.INSTANT_RUN_MERGED_MANIFESTS; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.JAVAC; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.LIBRARY_MANIFEST; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.LINKED_RES_FOR_BUNDLE; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.LINT_JAR; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.MANIFEST_MERGE_REPORT; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.MERGED_ASSETS; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.MERGED_MANIFESTS; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.MERGED_NOT_COMPILED_RES; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.MOCKABLE_JAR; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.PLATFORM_R_TXT; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.PUBLISHED_DEX; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.PUBLISHED_JAVA_RES; |
| import static com.android.build.gradle.internal.scope.TaskOutputHolder.TaskOutputType.PUBLISHED_NATIVE_LIBS; |
| 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.FEATURE; |
| 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.OutputFile; |
| 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.api.AnnotationProcessorOptions; |
| import com.android.build.gradle.api.JavaCompileOptions; |
| import com.android.build.gradle.internal.aapt.AaptGeneration; |
| import com.android.build.gradle.internal.core.Abi; |
| import com.android.build.gradle.internal.core.GradleVariantConfiguration; |
| import com.android.build.gradle.internal.coverage.JacocoConfigurations; |
| import com.android.build.gradle.internal.coverage.JacocoReportTask; |
| import com.android.build.gradle.internal.dsl.AbiSplitOptions; |
| 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.model.CoreExternalNativeBuild; |
| import com.android.build.gradle.internal.ndk.NdkHandler; |
| import com.android.build.gradle.internal.packaging.GradleKeystoreHelper; |
| 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.TransformTask; |
| import com.android.build.gradle.internal.publishing.AndroidArtifacts; |
| import com.android.build.gradle.internal.publishing.VariantPublishingSpec; |
| import com.android.build.gradle.internal.res.GenerateLibraryRFileTask; |
| import com.android.build.gradle.internal.res.LinkAndroidResForBundleTask; |
| import com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask; |
| import com.android.build.gradle.internal.res.namespaced.NamespacedResourcesTaskManager; |
| import com.android.build.gradle.internal.scope.CodeShrinker; |
| import com.android.build.gradle.internal.scope.ExistingBuildElements; |
| import com.android.build.gradle.internal.scope.GlobalScope; |
| import com.android.build.gradle.internal.scope.TaskConfigAction; |
| import com.android.build.gradle.internal.scope.TaskOutputHolder; |
| import com.android.build.gradle.internal.scope.VariantScope; |
| import com.android.build.gradle.internal.scope.VariantScope.Java8LangSupport; |
| import com.android.build.gradle.internal.tasks.AndroidReportTask; |
| import com.android.build.gradle.internal.tasks.CheckManifest; |
| import com.android.build.gradle.internal.tasks.CheckProguardFiles; |
| 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.ExtractTryWithResourcesSupportJar; |
| import com.android.build.gradle.internal.tasks.GenerateApkDataTask; |
| import com.android.build.gradle.internal.tasks.InstallVariantTask; |
| import com.android.build.gradle.internal.tasks.LintCompile; |
| import com.android.build.gradle.internal.tasks.MockableAndroidJarTask; |
| import com.android.build.gradle.internal.tasks.PipelineToPublicationTask; |
| import com.android.build.gradle.internal.tasks.PlatformAttrExtractorTask; |
| import com.android.build.gradle.internal.tasks.PrepareLintJar; |
| import com.android.build.gradle.internal.tasks.SigningReportTask; |
| import com.android.build.gradle.internal.tasks.SourceSetsTask; |
| import com.android.build.gradle.internal.tasks.TaskInputHelper; |
| 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.DataBindingGenBaseClassesTask; |
| import com.android.build.gradle.internal.tasks.databinding.DataBindingMergeArtifactsTransform; |
| import com.android.build.gradle.internal.tasks.databinding.DataBindingMergeGenClassLogTransform; |
| import com.android.build.gradle.internal.test.AbstractTestDataImpl; |
| import com.android.build.gradle.internal.test.TestDataImpl; |
| import com.android.build.gradle.internal.transforms.BuiltInShrinkerTransform; |
| import com.android.build.gradle.internal.transforms.CustomClassTransform; |
| import com.android.build.gradle.internal.transforms.D8MainDexListTransform; |
| import com.android.build.gradle.internal.transforms.DesugarTransform; |
| import com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform; |
| import com.android.build.gradle.internal.transforms.DexArchiveBuilderTransformBuilder; |
| import com.android.build.gradle.internal.transforms.DexMergerTransform; |
| import com.android.build.gradle.internal.transforms.DexMergerTransformCallable; |
| import com.android.build.gradle.internal.transforms.DexTransform; |
| import com.android.build.gradle.internal.transforms.ExternalLibsMergerTransform; |
| import com.android.build.gradle.internal.transforms.ExtractJarsTransform; |
| import com.android.build.gradle.internal.transforms.FixStackFramesTransform; |
| import com.android.build.gradle.internal.transforms.JacocoTransform; |
| import com.android.build.gradle.internal.transforms.JarMergingTransform; |
| import com.android.build.gradle.internal.transforms.MainDexListTransform; |
| import com.android.build.gradle.internal.transforms.MergeJavaResourcesTransform; |
| import com.android.build.gradle.internal.transforms.MultiDexTransform; |
| import com.android.build.gradle.internal.transforms.PreDexTransform; |
| 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.BaseVariantData; |
| import com.android.build.gradle.internal.variant.MultiOutputPolicy; |
| import com.android.build.gradle.internal.variant.TaskContainer; |
| import com.android.build.gradle.internal.variant.TestVariantData; |
| import com.android.build.gradle.options.BooleanOption; |
| import com.android.build.gradle.options.IntegerOption; |
| import com.android.build.gradle.options.ProjectOptions; |
| import com.android.build.gradle.options.StringOption; |
| 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.CopyOutputs; |
| 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.GenerateTestConfig; |
| import com.android.build.gradle.tasks.InstantRunResourcesApkBuilder; |
| import com.android.build.gradle.tasks.JavaPreCompileTask; |
| import com.android.build.gradle.tasks.LintGlobalTask; |
| import com.android.build.gradle.tasks.LintPerVariantTask; |
| import com.android.build.gradle.tasks.MainApkListPersistence; |
| 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.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.SplitsDiscovery; |
| import com.android.build.gradle.tasks.factory.AndroidUnitTest; |
| import com.android.build.gradle.tasks.factory.JavaCompileConfigAction; |
| import com.android.build.gradle.tasks.factory.ProcessJavaResConfigAction; |
| import com.android.builder.core.AndroidBuilder; |
| import com.android.builder.core.DefaultDexOptions; |
| import com.android.builder.core.DesugarProcessArgs; |
| import com.android.builder.core.VariantType; |
| import com.android.builder.dexing.DexerTool; |
| import com.android.builder.dexing.DexingType; |
| import com.android.builder.errors.EvalIssueReporter.Type; |
| import com.android.builder.model.DataBindingOptions; |
| 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.builder.utils.FileCache; |
| import com.android.ide.common.build.ApkData; |
| import com.android.manifmerger.ManifestMerger2; |
| import com.android.sdklib.AndroidVersion; |
| import com.android.utils.FileUtils; |
| import com.android.utils.StringHelper; |
| import com.google.common.base.Joiner; |
| import com.google.common.base.MoreObjects; |
| import com.google.common.base.Splitter; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| import java.io.File; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Objects; |
| import java.util.Optional; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import java.util.function.Supplier; |
| import java.util.stream.Collectors; |
| 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.artifacts.ConfigurationVariant; |
| import org.gradle.api.artifacts.Dependency; |
| import org.gradle.api.artifacts.DependencySet; |
| import org.gradle.api.artifacts.PublishArtifact; |
| import org.gradle.api.attributes.Attribute; |
| import org.gradle.api.attributes.AttributeContainer; |
| import org.gradle.api.file.FileCollection; |
| 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.PathSensitivity; |
| import org.gradle.api.tasks.Sync; |
| import org.gradle.api.tasks.TaskAction; |
| 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 DIR_BUNDLES = "bundles"; |
| public static final String INSTALL_GROUP = "Install"; |
| public static final String BUILD_GROUP = BasePlugin.BUILD_GROUP; |
| public static final String ANDROID_GROUP = "Android"; |
| public static final String FEATURE_SUFFIX = "Feature"; |
| |
| // 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. |
| public static final String MAIN_PREBUILD = "preBuild"; |
| public static final String UNINSTALL_ALL = "uninstallAll"; |
| public static final String DEVICE_CHECK = "deviceCheck"; |
| public static final String DEVICE_ANDROID_TEST = DEVICE + ANDROID_TEST.getSuffix(); |
| public static final String CONNECTED_CHECK = "connectedCheck"; |
| public static final String CONNECTED_ANDROID_TEST = CONNECTED + ANDROID_TEST.getSuffix(); |
| public static final String ASSEMBLE_ANDROID_TEST = "assembleAndroidTest"; |
| public static final String LINT = "lint"; |
| public static final String EXTRACT_PROGUARD_FILES = "extractProguardFiles"; |
| |
| @NonNull protected final Project project; |
| @NonNull protected final ProjectOptions projectOptions; |
| @NonNull protected final AndroidBuilder androidBuilder; |
| @NonNull protected final DataBindingBuilder dataBindingBuilder; |
| @NonNull protected final SdkHandler sdkHandler; |
| @NonNull protected final AndroidConfig extension; |
| @NonNull protected final ToolingModelBuilderRegistry toolingRegistry; |
| @NonNull protected final GlobalScope globalScope; |
| @NonNull protected final Recorder recorder; |
| @NonNull private final Logger logger; |
| @Nullable private final FileCache buildCache; |
| @NonNull protected final TaskFactory taskFactory; |
| |
| // Tasks. TODO: remove the mutable state from here. |
| public MockableAndroidJarTask createMockableJar; |
| |
| public TaskManager( |
| @NonNull GlobalScope globalScope, |
| @NonNull Project project, |
| @NonNull ProjectOptions projectOptions, |
| @NonNull AndroidBuilder androidBuilder, |
| @NonNull DataBindingBuilder dataBindingBuilder, |
| @NonNull AndroidConfig extension, |
| @NonNull SdkHandler sdkHandler, |
| @NonNull ToolingModelBuilderRegistry toolingRegistry, |
| @NonNull Recorder recorder) { |
| this.globalScope = globalScope; |
| this.project = project; |
| this.projectOptions = projectOptions; |
| this.androidBuilder = androidBuilder; |
| this.dataBindingBuilder = dataBindingBuilder; |
| this.sdkHandler = sdkHandler; |
| this.extension = extension; |
| this.toolingRegistry = toolingRegistry; |
| this.recorder = recorder; |
| this.logger = Logging.getLogger(this.getClass()); |
| |
| // It's too early to materialize the project-level cache, we'll need to get it from |
| // globalScope later on. |
| this.buildCache = globalScope.getBuildCache(); |
| |
| taskFactory = new TaskFactoryImpl(project.getTasks()); |
| } |
| |
| public boolean isComponentModelPlugin() { |
| return false; |
| } |
| |
| @NonNull |
| public TaskFactory getTaskFactory() { |
| return taskFactory; |
| } |
| |
| @NonNull |
| public DataBindingBuilder getDataBindingBuilder() { |
| return dataBindingBuilder; |
| } |
| |
| /** Creates the tasks for a given BaseVariantData. */ |
| public abstract void createTasksForVariantScope(@NonNull VariantScope variantScope); |
| |
| /** |
| * 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<Object> 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())); |
| } |
| |
| } |
| |
| /** |
| * Create tasks before the evaluation (on plugin apply). This is useful for tasks that could be |
| * referenced by custom build logic. |
| */ |
| public void createTasksBeforeEvaluate() { |
| taskFactory.create( |
| UNINSTALL_ALL, |
| uninstallAllTask -> { |
| uninstallAllTask.setDescription("Uninstall all applications."); |
| uninstallAllTask.setGroup(INSTALL_GROUP); |
| }); |
| |
| taskFactory.create( |
| DEVICE_CHECK, |
| deviceCheckTask -> { |
| deviceCheckTask.setDescription( |
| "Runs all device checks using Device Providers and Test Servers."); |
| deviceCheckTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP); |
| }); |
| |
| taskFactory.create( |
| CONNECTED_CHECK, |
| connectedCheckTask -> { |
| connectedCheckTask.setDescription( |
| "Runs all device checks on currently connected devices."); |
| connectedCheckTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP); |
| }); |
| |
| taskFactory.create(MAIN_PREBUILD, task -> {}); |
| |
| ExtractProguardFiles extractProguardFiles = |
| taskFactory.create(EXTRACT_PROGUARD_FILES, ExtractProguardFiles.class, task -> {}); |
| // Make sure MAIN_PREBUILD runs first: |
| extractProguardFiles.dependsOn(MAIN_PREBUILD); |
| |
| taskFactory.create(new SourceSetsTask.ConfigAction(extension)); |
| |
| taskFactory.create( |
| ASSEMBLE_ANDROID_TEST, |
| assembleAndroidTestTask -> { |
| assembleAndroidTestTask.setGroup(BasePlugin.BUILD_GROUP); |
| assembleAndroidTestTask.setDescription("Assembles all the Test applications."); |
| }); |
| |
| taskFactory.create(new LintCompile.ConfigAction(globalScope)); |
| |
| // Lint task is configured in afterEvaluate, but created upfront as it is used as an |
| // anchor task. |
| createGlobalLintTask(); |
| configureCustomLintChecksConfig(); |
| |
| if (buildCache != null) { |
| taskFactory.create(new CleanBuildCache.ConfigAction(globalScope)); |
| } |
| |
| // for testing only. |
| taskFactory.create( |
| new TaskConfigAction<ConfigAttrTask>() { |
| @NonNull |
| @Override |
| public String getName() { |
| return "resolveConfigAttr"; |
| } |
| |
| @NonNull |
| @Override |
| public Class<ConfigAttrTask> getType() { |
| return ConfigAttrTask.class; |
| } |
| |
| @Override |
| public void execute(@NonNull ConfigAttrTask task) { |
| task.resolvable = true; |
| } |
| }); |
| |
| taskFactory.create( |
| new TaskConfigAction<ConfigAttrTask>() { |
| @NonNull |
| @Override |
| public String getName() { |
| return "consumeConfigAttr"; |
| } |
| |
| @NonNull |
| @Override |
| public Class<ConfigAttrTask> getType() { |
| return ConfigAttrTask.class; |
| } |
| |
| @Override |
| public void execute(@NonNull ConfigAttrTask task) { |
| task.consumable = true; |
| } |
| }); |
| } |
| |
| private void configureCustomLintChecksConfig() { |
| // create a single configuration to point to a project or a local file that contains |
| // the lint.jar for this project. |
| // This is not the configuration that consumes lint.jar artifacts from normal dependencies, |
| // or publishes lint.jar to consumers. These are handled at the variant level. |
| globalScope.setLintChecks(createCustomLintChecksConfig(project)); |
| } |
| |
| @NonNull |
| public static Configuration createCustomLintChecksConfig(@NonNull Project project) { |
| Configuration lintChecks = project.getConfigurations().maybeCreate(CONFIG_NAME_LINTCHECKS); |
| lintChecks.setVisible(false); |
| lintChecks.setDescription("Configuration to apply external lint check jar"); |
| lintChecks.setCanBeConsumed(false); |
| return lintChecks; |
| } |
| |
| // this is call before all the variants are created since they are all going to depend |
| // on the global LINT_JAR task output |
| public void configureCustomLintChecks() { |
| // setup the task that reads the config and put the lint jar in the intermediate folder |
| // so that the bundle tasks can copy it, and the inter-project publishing can publish it |
| File lintJar = FileUtils.join(globalScope.getIntermediatesDir(), "lint", FN_LINT_JAR); |
| |
| PrepareLintJar copyLintTask = |
| taskFactory.create(new PrepareLintJar.ConfigAction(globalScope, lintJar)); |
| |
| // publish the lint intermediate file to the global tasks |
| globalScope.addTaskOutput(LINT_JAR, lintJar, copyLintTask.getName()); |
| } |
| |
| public void createGlobalLintTask() { |
| taskFactory.create(LINT, LintGlobalTask.class, task -> {}); |
| taskFactory.configure(JavaBasePlugin.CHECK_TASK_NAME, it -> it.dependsOn(LINT)); |
| } |
| |
| // this is run after all the variants are created. |
| public void configureGlobalLintTask(@NonNull final Collection<VariantScope> variants) { |
| // we only care about non testing and non feature variants |
| List<VariantScope> filteredVariants = |
| variants.stream().filter(TaskManager::isLintVariant).collect(Collectors.toList()); |
| |
| if (filteredVariants.isEmpty()) { |
| return; |
| } |
| |
| // configure the global lint task. |
| new LintGlobalTask.GlobalConfigAction(globalScope, filteredVariants) |
| .execute((LintGlobalTask) taskFactory.findByName(LINT)); |
| |
| // publish the local lint.jar to all the variants. This is not for the task output itself |
| // but for the artifact publishing. |
| FileCollection lintJarCollection = globalScope.getOutput(LINT_JAR); |
| |
| // FIXME we don't to do this during config, but there's no way right now as we want to publish it. |
| // A work around would be to keep hold of the file in the global scope as well as the |
| // file collection in TaskOutput but then this is dangerous as one could use it without |
| // the task dependency. |
| File lintJar = lintJarCollection.getSingleFile(); |
| for (VariantScope scope : variants) { |
| scope.addTaskOutput(LINT_JAR, lintJar, PrepareLintJar.NAME); |
| } |
| } |
| |
| // This is for config attribute debugging |
| public static class ConfigAttrTask extends DefaultTask { |
| boolean consumable = false; |
| boolean resolvable = false; |
| @TaskAction |
| public void run() { |
| for (Configuration config : getProject().getConfigurations()) { |
| AttributeContainer attributes = config.getAttributes(); |
| if ((consumable && config.isCanBeConsumed()) |
| || (resolvable && config.isCanBeResolved())) { |
| System.out.println(config.getName()); |
| System.out.println("\tcanBeResolved: " + config.isCanBeResolved()); |
| System.out.println("\tcanBeConsumed: " + config.isCanBeConsumed()); |
| for (Attribute<?> attr : attributes.keySet()) { |
| System.out.println( |
| "\t" + attr.getName() + ": " + attributes.getAttribute(attr)); |
| } |
| if (consumable && config.isCanBeConsumed()) { |
| for (PublishArtifact artifact : config.getArtifacts()) { |
| System.out.println("\tArtifact: " + artifact.getName() + " (" + artifact.getFile().getName() + ")"); |
| } |
| for (ConfigurationVariant cv : config.getOutgoing().getVariants()) { |
| System.out.println("\tConfigurationVariant: " + cv.getName()); |
| for (PublishArtifact pa : cv.getArtifacts()) { |
| System.out.println("\t\tArtifact: " + pa.getFile()); |
| System.out.println("\t\tType:" + pa.getType()); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| public void createMockableJarTask() { |
| File mockableJar = globalScope.getMockableAndroidJarFile(); |
| createMockableJar = |
| taskFactory.create( |
| new MockableAndroidJarTask.ConfigAction(globalScope, mockableJar)); |
| |
| globalScope.addTaskOutput(MOCKABLE_JAR, mockableJar, createMockableJar.getName()); |
| } |
| |
| public void createAttrFromAndroidJarTask() { |
| File platformRtxt = FileUtils.join(globalScope.getIntermediatesDir(), "attr", "R.txt"); |
| |
| PlatformAttrExtractorTask task = |
| taskFactory.create( |
| new PlatformAttrExtractorTask.ConfigAction(globalScope, platformRtxt)); |
| |
| globalScope.addTaskOutput(PLATFORM_R_TXT, platformRtxt, task.getName()); |
| } |
| |
| public void createBuildArtifactReportTask(@NonNull final VariantScope scope) {} |
| |
| protected void createDependencyStreams(@NonNull final VariantScope variantScope) { |
| // Since it's going to chance the configurations, we need to do it before |
| // we start doing queries to fill the streams. |
| handleJacocoDependencies(variantScope); |
| |
| TransformManager transformManager = variantScope.getTransformManager(); |
| |
| transformManager.addStream( |
| OriginalStream.builder(project, "ext-libs-classes") |
| .addContentTypes(TransformManager.CONTENT_CLASS) |
| .addScope(Scope.EXTERNAL_LIBRARIES) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| RUNTIME_CLASSPATH, EXTERNAL, CLASSES)) |
| .build()); |
| |
| transformManager.addStream( |
| OriginalStream.builder(project, "ext-libs-res-plus-native") |
| .addContentTypes( |
| DefaultContentType.RESOURCES, ExtendedContentType.NATIVE_LIBS) |
| .addScope(Scope.EXTERNAL_LIBRARIES) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| RUNTIME_CLASSPATH, EXTERNAL, JAVA_RES)) |
| .build()); |
| |
| // and the android AAR also have a specific jni folder |
| transformManager.addStream( |
| OriginalStream.builder(project, "ext-libs-native") |
| .addContentTypes(TransformManager.CONTENT_NATIVE_LIBS) |
| .addScope(Scope.EXTERNAL_LIBRARIES) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| RUNTIME_CLASSPATH, EXTERNAL, JNI)) |
| .build()); |
| |
| // data binding related artifacts for external libs |
| if (extension.getDataBinding().isEnabled()) { |
| transformManager.addStream( |
| OriginalStream.builder(project, "sub-project-data-binding") |
| .addContentTypes(TransformManager.DATA_BINDING_ARTIFACT) |
| .addScope(Scope.SUB_PROJECTS) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| COMPILE_CLASSPATH, MODULE, DATA_BINDING_ARTIFACT)) |
| .build()); |
| transformManager.addStream( |
| OriginalStream.builder(project, "ext-libs-data-binding") |
| .addContentTypes(TransformManager.DATA_BINDING_ARTIFACT) |
| .addScope(Scope.EXTERNAL_LIBRARIES) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| COMPILE_CLASSPATH, EXTERNAL, DATA_BINDING_ARTIFACT)) |
| .build()); |
| transformManager.addStream( |
| OriginalStream.builder(project, "sub-project-data-binding-base-classes") |
| .addContentTypes(TransformManager.DATA_BINDING_BASE_CLASS_LOG_ARTIFACT) |
| .addScope(Scope.SUB_PROJECTS) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| COMPILE_CLASSPATH, |
| MODULE, |
| DATA_BINDING_BASE_CLASS_LOG_ARTIFACT)) |
| .build()); |
| transformManager.addStream( |
| OriginalStream.builder(project, "ext-libs-data-binding-base-classes") |
| .addContentTypes(TransformManager.DATA_BINDING_BASE_CLASS_LOG_ARTIFACT) |
| .addScope(Scope.EXTERNAL_LIBRARIES) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| COMPILE_CLASSPATH, |
| EXTERNAL, |
| DATA_BINDING_BASE_CLASS_LOG_ARTIFACT)) |
| .build()); |
| } |
| |
| // for the sub modules, new intermediary classes artifact has its own stream |
| transformManager.addStream( |
| OriginalStream.builder(project, "sub-projects-classes") |
| .addContentTypes(TransformManager.CONTENT_CLASS) |
| .addScope(Scope.SUB_PROJECTS) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| RUNTIME_CLASSPATH, MODULE, CLASSES)) |
| .build()); |
| |
| // same for the resources which can be java-res or jni |
| transformManager.addStream( |
| OriginalStream.builder(project, "sub-projects-res-plus-native") |
| .addContentTypes( |
| DefaultContentType.RESOURCES, ExtendedContentType.NATIVE_LIBS) |
| .addScope(Scope.SUB_PROJECTS) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection( |
| RUNTIME_CLASSPATH, MODULE, JAVA_RES)) |
| .build()); |
| |
| // and the android library sub-modules also have a specific jni folder |
| transformManager.addStream( |
| OriginalStream.builder(project, "sub-projects-native") |
| .addContentTypes(TransformManager.CONTENT_NATIVE_LIBS) |
| .addScope(Scope.SUB_PROJECTS) |
| .setArtifactCollection( |
| variantScope.getArtifactCollection(RUNTIME_CLASSPATH, MODULE, JNI)) |
| .build()); |
| |
| // provided only scopes. |
| transformManager.addStream( |
| OriginalStream.builder(project, "provided-classes") |
| .addContentTypes(TransformManager.CONTENT_CLASS) |
| .addScope(Scope.PROVIDED_ONLY) |
| .setFileCollection(variantScope.getProvidedOnlyClasspath()) |
| .build()); |
| |
| if (variantScope.getTestedVariantData() != null) { |
| final BaseVariantData testedVariantData = variantScope.getTestedVariantData(); |
| |
| VariantScope testedVariantScope = testedVariantData.getScope(); |
| |
| VariantPublishingSpec testedSpec = |
| testedVariantScope |
| .getPublishingSpec() |
| .getTestingSpec(variantScope.getVariantConfiguration().getType()); |
| |
| // get the OutputPublishingSpec from the ArtifactType for this particular variant spec |
| VariantPublishingSpec.OutputPublishingSpec taskOutputSpec = |
| testedSpec.getSpec(AndroidArtifacts.ArtifactType.CLASSES); |
| // now get the output type |
| TaskOutputHolder.OutputType testedOutputType = taskOutputSpec.getOutputType(); |
| |
| // create two streams of different types. |
| transformManager.addStream( |
| OriginalStream.builder(project, "tested-code-classes") |
| .addContentTypes(DefaultContentType.CLASSES) |
| .addScope(Scope.TESTED_CODE) |
| .setFileCollection(testedVariantScope.getOutput(testedOutputType)) |
| .build()); |
| |
| transformManager.addStream( |
| OriginalStream.builder(project, "tested-code-deps") |
| .addContentTypes(DefaultContentType.CLASSES) |
| .addScope(Scope.TESTED_CODE) |
| .setArtifactCollection( |
| testedVariantScope.getArtifactCollection( |
| RUNTIME_CLASSPATH, ALL, CLASSES)) |
| .build()); |
| } |
| } |
| |
| public void createMergeApkManifestsTask(@NonNull VariantScope variantScope) { |
| AndroidArtifactVariantData androidArtifactVariantData = |
| (AndroidArtifactVariantData) variantScope.getVariantData(); |
| Set<String> screenSizes = androidArtifactVariantData.getCompatibleScreens(); |
| |
| CompatibleScreensManifest csmTask = |
| taskFactory.create( |
| new CompatibleScreensManifest.ConfigAction(variantScope, screenSizes)); |
| variantScope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST, |
| variantScope.getCompatibleScreensManifestDirectory(), |
| csmTask.getName()); |
| |
| ImmutableList.Builder<ManifestMerger2.Invoker.Feature> optionalFeatures = |
| ImmutableList.builder(); |
| |
| if (variantScope.isTestOnly()) { |
| optionalFeatures.add(ManifestMerger2.Invoker.Feature.TEST_ONLY); |
| } |
| |
| if (variantScope.getVariantConfiguration().getDexingType() == DexingType.LEGACY_MULTIDEX) { |
| optionalFeatures.add( |
| ManifestMerger2.Invoker.Feature.ADD_MULTIDEX_APPLICATION_IF_NO_NAME); |
| } |
| |
| if (variantScope.getVariantConfiguration().getBuildType().isDebuggable()) { |
| optionalFeatures.add(ManifestMerger2.Invoker.Feature.DEBUGGABLE); |
| } |
| |
| if (!getAdvancedProfilingTransforms(projectOptions).isEmpty() |
| && variantScope.getVariantConfiguration().getBuildType().isDebuggable()) { |
| optionalFeatures.add(ManifestMerger2.Invoker.Feature.ADVANCED_PROFILING); |
| } |
| |
| ManifestProcessorTask processManifestTask = |
| createMergeManifestTask(variantScope, optionalFeatures); |
| |
| variantScope.addTaskOutput( |
| MERGED_MANIFESTS, |
| variantScope.getManifestOutputDirectory(), |
| processManifestTask.getName()); |
| |
| variantScope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.MANIFEST_METADATA, |
| ExistingBuildElements.getMetadataFile(variantScope.getManifestOutputDirectory()), |
| processManifestTask.getName()); |
| |
| // TODO: use FileCollection |
| variantScope.setManifestProcessorTask(processManifestTask); |
| |
| processManifestTask.dependsOn(variantScope.getCheckManifestTask()); |
| |
| if (variantScope.getMicroApkTask() != null) { |
| processManifestTask.dependsOn(variantScope.getMicroApkTask()); |
| } |
| } |
| |
| @NonNull |
| private static List<String> getAdvancedProfilingTransforms(@NonNull ProjectOptions options) { |
| String string = options.get(StringOption.IDE_ANDROID_CUSTOM_CLASS_TRANSFORMS); |
| if (string == null) { |
| return ImmutableList.of(); |
| } |
| return Splitter.on(',').splitToList(string); |
| } |
| |
| @NonNull |
| private static File computeManifestReportFile(@NonNull VariantScope variantScope) { |
| return FileUtils.join( |
| variantScope.getGlobalScope().getOutputsDir(), |
| "logs", |
| "manifest-merger-" |
| + variantScope.getVariantConfiguration().getBaseName() |
| + "-report.txt"); |
| } |
| |
| /** Creates the merge manifests task. */ |
| @NonNull |
| protected ManifestProcessorTask createMergeManifestTask( |
| @NonNull VariantScope variantScope, |
| @NonNull ImmutableList.Builder<ManifestMerger2.Invoker.Feature> optionalFeatures) { |
| if (variantScope.getVariantConfiguration().isInstantRunBuild(globalScope)) { |
| optionalFeatures.add(ManifestMerger2.Invoker.Feature.INSTANT_RUN_REPLACEMENT); |
| } |
| |
| final File reportFile = computeManifestReportFile(variantScope); |
| MergeManifests mergeManifestsAndroidTask = |
| taskFactory.create( |
| new MergeManifests.ConfigAction( |
| variantScope, optionalFeatures.build(), reportFile)); |
| |
| final String name = mergeManifestsAndroidTask.getName(); |
| |
| variantScope.addTaskOutput( |
| INSTANT_RUN_MERGED_MANIFESTS, |
| variantScope.getInstantRunManifestOutputDirectory(), |
| name); |
| |
| variantScope.addTaskOutput(MANIFEST_MERGE_REPORT, reportFile, name); |
| |
| return mergeManifestsAndroidTask; |
| } |
| |
| public ProcessManifest createMergeLibManifestsTask(@NonNull VariantScope scope) { |
| |
| // for library, there is only one manifest (no split). |
| File libraryProcessedManifest = |
| new File(scope.getManifestOutputDirectory(), FN_ANDROID_MANIFEST_XML); |
| |
| final File reportFile = computeManifestReportFile(scope); |
| ProcessManifest processManifest = |
| taskFactory.create( |
| new ProcessManifest.ConfigAction( |
| scope, libraryProcessedManifest, reportFile)); |
| |
| final String taskName = processManifest.getName(); |
| |
| scope.addTaskOutput(MERGED_MANIFESTS, scope.getManifestOutputDirectory(), taskName); |
| |
| scope.addTaskOutput( |
| AAPT_FRIENDLY_MERGED_MANIFESTS, |
| scope.getAaptFriendlyManifestOutputDirectory(), |
| taskName); |
| |
| scope.addTaskOutput(LIBRARY_MANIFEST, libraryProcessedManifest, taskName); |
| scope.addTaskOutput(MANIFEST_MERGE_REPORT, reportFile, taskName); |
| |
| processManifest.dependsOn(scope.getCheckManifestTask()); |
| |
| scope.setManifestProcessorTask(processManifest); |
| |
| return processManifest; |
| } |
| |
| protected void createProcessTestManifestTask( |
| @NonNull VariantScope scope, |
| @NonNull VariantScope testedScope) { |
| |
| ProcessTestManifest processTestManifestTask = |
| taskFactory.create( |
| new ProcessTestManifest.ConfigAction( |
| scope, testedScope.getOutput(MERGED_MANIFESTS))); |
| |
| scope.addTaskOutput( |
| MERGED_MANIFESTS, |
| scope.getManifestOutputDirectory(), |
| processTestManifestTask.getName()); |
| |
| if (scope.getCheckManifestTask() != null) { |
| processTestManifestTask.dependsOn(scope.getCheckManifestTask()); |
| } |
| |
| scope.setManifestProcessorTask(processTestManifestTask); |
| } |
| |
| public void createRenderscriptTask( |
| @NonNull VariantScope scope) { |
| scope.setRenderscriptCompileTask( |
| taskFactory.create(new RenderscriptCompile.ConfigAction(scope))); |
| |
| GradleVariantConfiguration config = scope.getVariantConfiguration(); |
| |
| if (config.getType().isForTesting()) { |
| scope.getRenderscriptCompileTask().dependsOn(scope.getManifestProcessorTask()); |
| } else { |
| scope.getRenderscriptCompileTask().dependsOn(scope.getPreBuildTask()); |
| } |
| |
| scope.getResourceGenTask().dependsOn(scope.getRenderscriptCompileTask()); |
| // only put this dependency if rs will generate Java code |
| if (!config.getRenderscriptNdkModeEnabled()) { |
| scope.getSourceGenTask().dependsOn(scope.getRenderscriptCompileTask()); |
| } |
| |
| } |
| |
| public MergeResources createMergeResourcesTask( |
| @NonNull VariantScope scope, boolean processResources) { |
| |
| boolean alsoOutputNotCompiledResources = |
| scope.useResourceShrinker() |
| || (scope.getTestedVariantData() == null |
| && globalScope |
| .getExtension() |
| .getTestOptions() |
| .getUnitTests() |
| .isIncludeAndroidResources()); |
| |
| return basicCreateMergeResourcesTask( |
| scope, |
| MergeType.MERGE, |
| null /*outputLocation*/, |
| true /*includeDependencies*/, |
| processResources, |
| alsoOutputNotCompiledResources, |
| ImmutableSet.of()); |
| } |
| |
| /** Defines the merge type for {@link #basicCreateMergeResourcesTask} */ |
| public enum MergeType { |
| /** |
| * Merge all resources with all the dependencies resources. |
| */ |
| MERGE { |
| @Override |
| public VariantScope.TaskOutputType getOutputType() { |
| return VariantScope.TaskOutputType.MERGED_RES; |
| } |
| }, |
| /** |
| * Merge all resources without the dependencies resources for an aar. |
| */ |
| PACKAGE { |
| @Override |
| public VariantScope.TaskOutputType getOutputType() { |
| return VariantScope.TaskOutputType.PACKAGED_RES; |
| } |
| }; |
| |
| public abstract VariantScope.TaskOutputType getOutputType(); |
| } |
| |
| public MergeResources basicCreateMergeResourcesTask( |
| @NonNull VariantScope scope, |
| @NonNull MergeType mergeType, |
| @Nullable File outputLocation, |
| final boolean includeDependencies, |
| final boolean processResources, |
| boolean alsoOutputNotCompiledResources, |
| @NonNull ImmutableSet<MergeResources.Flag> flags) { |
| |
| File mergedOutputDir = MoreObjects |
| .firstNonNull(outputLocation, scope.getDefaultMergeResourcesOutputDir()); |
| |
| String taskNamePrefix = mergeType.name().toLowerCase(Locale.ENGLISH); |
| |
| File mergedNotCompiledDir = |
| alsoOutputNotCompiledResources |
| ? new File( |
| globalScope.getIntermediatesDir() |
| + "/merged-not-compiled-resources/" |
| + scope.getVariantConfiguration().getDirName()) |
| : null; |
| |
| MergeResources mergeResourcesTask = |
| taskFactory.create( |
| new MergeResources.ConfigAction( |
| scope, |
| taskNamePrefix, |
| mergedOutputDir, |
| mergedNotCompiledDir, |
| includeDependencies, |
| processResources, |
| true, |
| flags)); |
| |
| scope.addTaskOutput( |
| mergeType.getOutputType(), mergedOutputDir, mergeResourcesTask.getName()); |
| |
| if (alsoOutputNotCompiledResources) { |
| scope.addTaskOutput( |
| MERGED_NOT_COMPILED_RES, mergedNotCompiledDir, mergeResourcesTask.getName()); |
| } |
| |
| mergeResourcesTask.dependsOn( |
| |
| scope.getResourceGenTask()); |
| |
| if (extension.getTestOptions().getUnitTests().isIncludeAndroidResources()) { |
| scope.getCompileTask().dependsOn(mergeResourcesTask); |
| } |
| |
| scope.setResourceOutputDir(mergedOutputDir); |
| scope.setMergeResourceOutputDir(outputLocation); |
| return mergeResourcesTask; |
| } |
| |
| public MergeSourceSetFolders createMergeAssetsTask(@NonNull VariantScope scope) { |
| final GradleVariantConfiguration variantConfiguration = scope.getVariantConfiguration(); |
| File outputDir = |
| FileUtils.join( |
| globalScope.getIntermediatesDir(), |
| FD_ASSETS, |
| variantConfiguration.getDirName()); |
| |
| MergeSourceSetFolders mergeAssetsTask = |
| taskFactory.create( |
| new MergeSourceSetFolders.MergeAppAssetConfigAction(scope, outputDir)); |
| |
| // register the output |
| scope.addTaskOutput(MERGED_ASSETS, outputDir, mergeAssetsTask.getName()); |
| |
| mergeAssetsTask.dependsOn(scope.getAssetGenTask()); |
| scope.setMergeAssetsTask(mergeAssetsTask); |
| |
| return mergeAssetsTask; |
| } |
| |
| @NonNull |
| public Optional<TransformTask> createMergeJniLibFoldersTasks( |
| @NonNull final VariantScope variantScope) { |
| // merge the source folders together using the proper priority. |
| MergeSourceSetFolders mergeJniLibFoldersTask = |
| taskFactory.create( |
| new MergeSourceSetFolders.MergeJniLibFoldersConfigAction(variantScope)); |
| mergeJniLibFoldersTask.dependsOn(variantScope.getAssetGenTask()); |
| |
| // create the stream generated from this task |
| variantScope |
| .getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "mergedJniFolder") |
| .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(project, "local-ndk-build") |
| .addContentType(ExtendedContentType.NATIVE_LIBS) |
| .addScope(Scope.PROJECT) |
| .setFolders(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(project, "external-native-build") |
| .addContentType(ExtendedContentType.NATIVE_LIBS) |
| .addScope(Scope.PROJECT) |
| .setFolder( |
| variantScope |
| .getExternalNativeJsonGenerator() |
| .getObjFolder()) |
| .setDependency( |
| checkNotNull(variantScope.getExternalNativeBuildTask()) |
| .getName()) |
| .build()); |
| } |
| |
| // create a stream containing the content of the renderscript compilation output |
| // if support mode is enabled. |
| if (variantScope.getVariantConfiguration().getRenderscriptSupportModeEnabled()) { |
| final Supplier<Collection<File>> supplier = |
| () -> { |
| ImmutableList.Builder<File> 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(); |
| }; |
| |
| variantScope |
| .getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "rs-support-mode-output") |
| .addContentType(ExtendedContentType.NATIVE_LIBS) |
| .addScope(Scope.PROJECT) |
| .setFolders(supplier) |
| .setDependency( |
| variantScope.getRenderscriptCompileTask().getName()) |
| .build()); |
| } |
| |
| // compute the scopes that need to be merged. |
| Set<? super Scope> mergeScopes = getResMergingScopes(variantScope); |
| // Create the merge transform |
| MergeJavaResourcesTransform mergeTransform = new MergeJavaResourcesTransform( |
| variantScope.getGlobalScope().getExtension().getPackagingOptions(), |
| mergeScopes, ExtendedContentType.NATIVE_LIBS, "mergeJniLibs", variantScope); |
| |
| return variantScope |
| .getTransformManager() |
| .addTransform(taskFactory, variantScope, mergeTransform); |
| } |
| |
| public void createBuildConfigTask(@NonNull VariantScope scope) { |
| GenerateBuildConfig generateBuildConfigTask = |
| taskFactory.create(new GenerateBuildConfig.ConfigAction(scope)); |
| scope.setGenerateBuildConfigTask(generateBuildConfigTask); |
| scope.getSourceGenTask().dependsOn(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. |
| |
| generateBuildConfigTask.dependsOn(scope.getManifestProcessorTask()); |
| } else { |
| generateBuildConfigTask.dependsOn(scope.getCheckManifestTask()); |
| } |
| } |
| |
| public void createGenerateResValuesTask( |
| @NonNull VariantScope scope) { |
| GenerateResValues generateResValuesTask = |
| taskFactory.create(new GenerateResValues.ConfigAction(scope)); |
| scope.getResourceGenTask().dependsOn(generateResValuesTask); |
| } |
| |
| public void createApkProcessResTask( |
| |
| @NonNull VariantScope scope) { |
| |
| createProcessResTask( |
| |
| scope, |
| new File( |
| globalScope.getIntermediatesDir(), |
| "symbols/" |
| + scope.getVariantData() |
| .getVariantConfiguration() |
| .getDirName()), |
| scope.getProcessResourcePackageOutputDirectory(), |
| null, |
| MergeType.MERGE, |
| scope.getGlobalScope().getProjectBaseName()); |
| } |
| |
| protected boolean isLibrary() { |
| return false; |
| } |
| |
| public void createProcessResTask( |
| @NonNull VariantScope scope, |
| @NonNull File symbolLocation, |
| @NonNull File resPackageOutputFolder, |
| @Nullable TaskOutputHolder.TaskOutputType packageOutputType, |
| @NonNull MergeType mergeType, |
| @NonNull String baseName) { |
| BaseVariantData variantData = scope.getVariantData(); |
| |
| variantData.calculateFilters(scope.getGlobalScope().getExtension().getSplits()); |
| |
| createSplitsDiscovery(scope); |
| |
| boolean useAaptToGenerateLegacyMultidexMainDexProguardRules = |
| scope.getDexingType() == DexingType.LEGACY_MULTIDEX; |
| |
| if (Boolean.TRUE.equals( |
| scope.getGlobalScope().getExtension().getAaptOptions().getNamespaced())) { |
| new NamespacedResourcesTaskManager(globalScope, taskFactory, scope) |
| .createNamespacedResourceTasks( |
| resPackageOutputFolder, |
| packageOutputType, |
| baseName, |
| useAaptToGenerateLegacyMultidexMainDexProguardRules); |
| return; |
| } |
| createNonNamespacedResourceTasks( |
| scope, |
| symbolLocation, |
| resPackageOutputFolder, |
| packageOutputType, |
| mergeType, |
| baseName, |
| useAaptToGenerateLegacyMultidexMainDexProguardRules); |
| } |
| |
| private void createNonNamespacedResourceTasks( |
| @NonNull VariantScope scope, |
| @NonNull File symbolDirectory, |
| @NonNull File resPackageOutputFolder, |
| TaskOutputHolder.TaskOutputType packageOutputType, |
| @NonNull MergeType mergeType, |
| @NonNull String baseName, |
| boolean useAaptToGenerateLegacyMultidexMainDexProguardRules) { |
| File symbolTableWithPackageName = |
| FileUtils.join( |
| globalScope.getIntermediatesDir(), |
| FD_RES, |
| "symbol-table-with-package", |
| scope.getVariantConfiguration().getDirName(), |
| "package-aware-r.txt"); |
| final String taskName; |
| |
| File symbolFile = new File(symbolDirectory, FN_RESOURCE_TEXT); |
| |
| if (mergeType == MergeType.PACKAGE) { |
| // Simply generate the R class for a library |
| GenerateLibraryRFileTask task = |
| taskFactory.create( |
| new GenerateLibraryRFileTask.ConfigAction( |
| scope, symbolFile, symbolTableWithPackageName)); |
| taskName = task.getName(); |
| scope.setProcessResourcesTask(task); |
| |
| } else { |
| LinkApplicationAndroidResourcesTask processAndroidResources = |
| taskFactory.create( |
| createProcessAndroidResourcesConfigAction( |
| scope, |
| () -> symbolDirectory, |
| symbolTableWithPackageName, |
| resPackageOutputFolder, |
| useAaptToGenerateLegacyMultidexMainDexProguardRules, |
| mergeType, |
| baseName)); |
| |
| taskName = processAndroidResources.getName(); |
| scope.addTaskOutput( |
| VariantScope.TaskOutputType.PROCESSED_RES, resPackageOutputFolder, taskName); |
| if (packageOutputType != null) { |
| scope.addTaskOutput( |
| packageOutputType, |
| resPackageOutputFolder, |
| processAndroidResources.getName()); |
| } |
| |
| scope.setProcessResourcesTask(processAndroidResources); |
| |
| // create the task that creates the aapt output for the bundle. |
| File resourcesAsProtosFile = |
| FileUtils.join( |
| globalScope.getIntermediatesDir(), |
| "res-bundle", |
| scope.getVariantConfiguration().getDirName(), |
| "bundled-res.ap_"); |
| |
| LinkAndroidResForBundleTask linkResForBundle = |
| taskFactory.create( |
| new LinkAndroidResForBundleTask.ConfigAction( |
| scope, resourcesAsProtosFile)); |
| |
| // publish it. |
| scope.addTaskOutput( |
| LINKED_RES_FOR_BUNDLE, resourcesAsProtosFile, linkResForBundle.getName()); |
| } |
| scope.addTaskOutput(VariantScope.TaskOutputType.SYMBOL_LIST, symbolFile, taskName); |
| |
| // Needed for the IDE |
| scope.getSourceGenTask().dependsOn(taskName); |
| |
| // Synthetic output for AARs (see SymbolTableWithPackageNameTransform), and created in |
| // process resources for local subprojects. |
| scope.addTaskOutput( |
| VariantScope.TaskOutputType.SYMBOL_LIST_WITH_PACKAGE_NAME, |
| symbolTableWithPackageName, |
| taskName); |
| |
| // Needed for the IDE |
| Task t = project.getTasks().findByName(taskName); |
| if (t != null) { |
| scope.getSourceGenTask().dependsOn(taskName); |
| } |
| } |
| |
| protected TaskConfigAction<LinkApplicationAndroidResourcesTask> |
| createProcessAndroidResourcesConfigAction( |
| @NonNull VariantScope scope, |
| @NonNull Supplier<File> symbolLocation, |
| @NonNull File symbolWithPackageName, |
| @NonNull File resPackageOutputFolder, |
| boolean useAaptToGenerateLegacyMultidexMainDexProguardRules, |
| @NonNull MergeType sourceTaskOutputType, |
| @NonNull String baseName) { |
| return new LinkApplicationAndroidResourcesTask.ConfigAction( |
| scope, |
| symbolLocation, |
| symbolWithPackageName, |
| resPackageOutputFolder, |
| useAaptToGenerateLegacyMultidexMainDexProguardRules, |
| sourceTaskOutputType, |
| baseName, |
| isLibrary()); |
| } |
| |
| /** |
| * 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 FULL_APK build output directory. |
| */ |
| @NonNull |
| public PackageSplitRes createSplitResourcesTasks(@NonNull VariantScope scope) { |
| BaseVariantData variantData = scope.getVariantData(); |
| |
| checkState( |
| variantData.getMultiOutputPolicy().equals(MultiOutputPolicy.SPLITS), |
| "Can only create split resources tasks for pure splits."); |
| |
| File densityOrLanguagesPackages = scope.getSplitDensityOrLanguagesPackagesOutputDirectory(); |
| PackageSplitRes packageSplitRes = |
| taskFactory.create( |
| new PackageSplitRes.ConfigAction(scope, densityOrLanguagesPackages)); |
| variantData.packageSplitResourcesTask = packageSplitRes; |
| scope.addTaskOutput( |
| VariantScope.TaskOutputType.DENSITY_OR_LANGUAGE_PACKAGED_SPLIT, |
| densityOrLanguagesPackages, |
| packageSplitRes.getName()); |
| |
| if (scope.getVariantConfiguration().getSigningConfig() != null) { |
| packageSplitRes.dependsOn(getValidateSigningTask(scope)); |
| } |
| |
| return packageSplitRes; |
| } |
| |
| @Nullable |
| public PackageSplitAbi createSplitAbiTasks(@NonNull VariantScope scope) { |
| BaseVariantData variantData = scope.getVariantData(); |
| |
| checkState( |
| variantData.getMultiOutputPolicy().equals(MultiOutputPolicy.SPLITS), |
| "split ABI tasks are only compatible with pure splits."); |
| |
| Set<String> filters = AbiSplitOptions.getAbiFilters(extension.getSplits().getAbiFilters()); |
| if (filters.isEmpty()) { |
| return null; |
| } |
| |
| List<ApkData> fullApkDatas = |
| variantData.getOutputScope().getSplitsByType(OutputFile.OutputType.FULL_SPLIT); |
| if (!fullApkDatas.isEmpty()) { |
| throw new RuntimeException( |
| "In release 21 and later, there cannot be full splits and pure splits, " |
| + "found " |
| + Joiner.on(",").join(fullApkDatas) |
| + " and abi filters " |
| + Joiner.on(",").join(filters)); |
| } |
| |
| File generateSplitAbiResOutputDirectory = scope.getGenerateSplitAbiResOutputDirectory(); |
| // first create the ABI specific split FULL_APK resources. |
| GenerateSplitAbiRes generateSplitAbiRes = |
| taskFactory.create( |
| new GenerateSplitAbiRes.ConfigAction( |
| scope, generateSplitAbiResOutputDirectory)); |
| scope.addTaskOutput( |
| VariantScope.TaskOutputType.ABI_PROCESSED_SPLIT_RES, |
| generateSplitAbiResOutputDirectory, |
| generateSplitAbiRes.getName()); |
| |
| // then package those resources with the appropriate JNI libraries. |
| File generateSplitAbiPackagesOutputDirectory = scope.getSplitAbiPackagesOutputDirectory(); |
| PackageSplitAbi packageSplitAbiTask = |
| taskFactory.create( |
| new PackageSplitAbi.ConfigAction( |
| scope, |
| generateSplitAbiPackagesOutputDirectory, |
| scope.getOutput( |
| VariantScope.TaskOutputType.ABI_PROCESSED_SPLIT_RES))); |
| scope.addTaskOutput( |
| VariantScope.TaskOutputType.ABI_PACKAGED_SPLIT, |
| generateSplitAbiPackagesOutputDirectory, |
| packageSplitAbiTask.getName()); |
| variantData.packageSplitAbiTask = packageSplitAbiTask; |
| |
| packageSplitAbiTask.dependsOn(scope.getNdkBuildable()); |
| |
| if (variantData.getVariantConfiguration().getSigningConfig() != null) { |
| packageSplitAbiTask.dependsOn(getValidateSigningTask(variantData.getScope())); |
| } |
| |
| if (scope.getExternalNativeBuildTask() != null) { |
| packageSplitAbiTask.dependsOn(scope.getExternalNativeBuildTask()); |
| } |
| |
| return packageSplitAbiTask; |
| } |
| |
| public void createSplitTasks(@NonNull VariantScope variantScope) { |
| createSplitResourcesTasks(variantScope); |
| createSplitAbiTasks(variantScope); |
| } |
| |
| /** |
| * 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<? super Scope> getResMergingScopes(@NonNull VariantScope variantScope); |
| |
| /** |
| * Creates the java resources processing tasks. |
| * |
| * <p>The java processing will happen in two steps: |
| * |
| * <ul> |
| * <li>{@link Sync} task configured with {@link ProcessJavaResConfigAction} will sync all |
| * source folders into a single folder identified by {@link |
| * VariantScope#getSourceFoldersJavaResDestinationDir()} |
| * <li>{@link MergeJavaResourcesTransform} will take the output of this merge plus the |
| * dependencies and will create a single merge with the {@link PackagingOptions} settings |
| * applied. |
| * </ul> |
| * |
| * This sets up only the Sync part. The transform is setup via {@link |
| * #createMergeJavaResTransform(VariantScope)} |
| * |
| * @param variantScope the variant scope we are operating under. |
| */ |
| public void createProcessJavaResTask(@NonNull VariantScope variantScope) { |
| // Copy the source folders java resources into the temporary location, mainly to |
| // maintain the PluginDsl COPY semantics. |
| |
| // TODO: move this file computation completely out of VariantScope. |
| File destinationDir = variantScope.getSourceFoldersJavaResDestinationDir(); |
| |
| Sync processJavaResourcesTask = |
| taskFactory.create(new ProcessJavaResConfigAction(variantScope, destinationDir)); |
| variantScope.setProcessJavaResourcesTask(processJavaResourcesTask); |
| variantScope.getVariantData().processJavaResourcesTask = processJavaResourcesTask; |
| |
| processJavaResourcesTask.dependsOn(variantScope.getPreBuildTask()); |
| |
| // create the task outputs for others to consume |
| FileCollection collection = |
| variantScope.addTaskOutput( |
| VariantScope.TaskOutputType.JAVA_RES, |
| destinationDir, |
| processJavaResourcesTask.getName()); |
| |
| // create the stream generated from this task |
| variantScope |
| .getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "processed-java-res") |
| .addContentType(DefaultContentType.RESOURCES) |
| .addScope(Scope.PROJECT) |
| .setFileCollection(collection) |
| .build()); |
| } |
| |
| /** |
| * Sets up the Merge Java Res transform. |
| * |
| * @param variantScope the variant scope we are operating under. |
| * @see #createProcessJavaResTask(VariantScope) |
| */ |
| public void createMergeJavaResTransform(@NonNull VariantScope variantScope) { |
| TransformManager transformManager = variantScope.getTransformManager(); |
| |
| // Compute the scopes that need to be merged. |
| Set<? super Scope> mergeScopes = getResMergingScopes(variantScope); |
| |
| // Create the merge transform. |
| MergeJavaResourcesTransform mergeTransform = |
| new MergeJavaResourcesTransform( |
| variantScope.getGlobalScope().getExtension().getPackagingOptions(), |
| mergeScopes, |
| DefaultContentType.RESOURCES, |
| "mergeJavaRes", |
| variantScope); |
| variantScope.setMergeJavaResourcesTask( |
| transformManager |
| .addTransform(taskFactory, variantScope, mergeTransform) |
| .orElse(null)); |
| } |
| |
| public AidlCompile createAidlTask(@NonNull VariantScope scope) { |
| AidlCompile aidlCompileTask = taskFactory.create(new AidlCompile.ConfigAction(scope)); |
| scope.setAidlCompileTask(aidlCompileTask); |
| scope.getSourceGenTask().dependsOn(aidlCompileTask); |
| aidlCompileTask.dependsOn(scope.getPreBuildTask()); |
| |
| return aidlCompileTask; |
| } |
| |
| public void createShaderTask(@NonNull VariantScope scope) { |
| // merge the shader folders together using the proper priority. |
| MergeSourceSetFolders mergeShadersTask = |
| taskFactory.create( |
| new MergeSourceSetFolders.MergeShaderSourceFoldersConfigAction(scope)); |
| // TODO do we support non compiled shaders in aars? |
| //mergeShadersTask.dependsOn( scope.getVariantData().prepareDependenciesTask); |
| |
| File outputDir = scope.getGeneratedAssetsDir("shaders"); |
| |
| // compile the shaders |
| ShaderCompile shaderCompileTask = |
| taskFactory.create(new ShaderCompile.ConfigAction(scope, outputDir)); |
| scope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.SHADER_ASSETS, |
| outputDir, |
| shaderCompileTask.getName()); |
| shaderCompileTask.dependsOn(mergeShadersTask); |
| |
| scope.getAssetGenTask().dependsOn(shaderCompileTask); |
| } |
| |
| protected abstract void postJavacCreation(@NonNull final VariantScope scope); |
| |
| /** |
| * 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 JavaCompile createJavacTask(@NonNull final VariantScope scope) { |
| File processorListFile = |
| FileUtils.join( |
| globalScope.getIntermediatesDir(), |
| "javaPrecompile", |
| scope.getDirName(), |
| "annotationProcessors.json"); |
| |
| JavaPreCompileTask preCompileTask = |
| taskFactory.create(new JavaPreCompileTask.ConfigAction(scope, processorListFile)); |
| preCompileTask.dependsOn(scope.getPreBuildTask()); |
| scope.addTaskOutput(ANNOTATION_PROCESSOR_LIST, processorListFile, preCompileTask.getName()); |
| |
| // create the output folder |
| File outputFolder = |
| new File( |
| globalScope.getIntermediatesDir(), |
| "/classes/" + scope.getVariantConfiguration().getDirName()); |
| |
| final JavaCompile javacTask = |
| taskFactory.create(new JavaCompileConfigAction(scope, outputFolder)); |
| scope.setJavacTask(javacTask); |
| |
| setupCompileTaskDependencies(scope, javacTask); |
| |
| scope.addTaskOutput(JAVAC, outputFolder, javacTask.getName()); |
| |
| postJavacCreation(scope); |
| |
| 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 void addJavacClassesStream(VariantScope scope) { |
| // create separate streams for the output of JAVAC and for the pre/post javac |
| // bytecode hooks |
| scope.getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "javac-output") |
| // Need both classes and resources because some annotation |
| // processors generate resources |
| .addContentTypes( |
| DefaultContentType.CLASSES, DefaultContentType.RESOURCES) |
| .addScope(Scope.PROJECT) |
| .setFileCollection(scope.getOutput(JAVAC)) |
| .build()); |
| |
| scope.getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "pre-javac-generated-bytecode") |
| .addContentTypes( |
| DefaultContentType.CLASSES, DefaultContentType.RESOURCES) |
| .addScope(Scope.PROJECT) |
| .setFileCollection( |
| scope.getVariantData().getAllPreJavacGeneratedBytecode()) |
| .build()); |
| |
| scope.getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "post-javac-generated-bytecode") |
| .addContentTypes( |
| DefaultContentType.CLASSES, DefaultContentType.RESOURCES) |
| .addScope(Scope.PROJECT) |
| .setFileCollection( |
| scope.getVariantData().getAllPostJavacGeneratedBytecode()) |
| .build()); |
| |
| if (scope.hasOutput(TaskOutputHolder.TaskOutputType.RUNTIME_R_CLASS_CLASSES)) { |
| scope.getTransformManager() |
| .addStream( |
| OriginalStream.builder(project, "final-r-classes") |
| .addContentTypes( |
| DefaultContentType.CLASSES, |
| DefaultContentType.RESOURCES) |
| .addScope(Scope.PROJECT) |
| .setFileCollection( |
| scope.getOutput( |
| TaskOutputHolder.TaskOutputType |
| .RUNTIME_R_CLASS_CLASSES)) |
| .build()); |
| } |
| } |
| |
| private static void setupCompileTaskDependencies( |
| @NonNull VariantScope scope, @NonNull Task compileTask) { |
| |
| if (scope.getSourceGenTask() != null) { |
| compileTask.dependsOn(scope.getSourceGenTask()); |
| } |
| } |
| |
| /** Makes the given task the one used by top-level "compile" task. */ |
| public static void setJavaCompilerTask( |
| @NonNull Task javaCompilerTask, @NonNull VariantScope scope) { |
| |
| scope.getCompileTask().dependsOn(javaCompilerTask); |
| } |
| |
| /** |
| * 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 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 VariantScope scope, |
| @Nullable FileCollection config) { |
| GenerateApkDataTask generateMicroApkTask = |
| taskFactory.create(new GenerateApkDataTask.ConfigAction(scope, config)); |
| scope.setMicroApkTask(generateMicroApkTask); |
| |
| // the merge res task will need to run after this one. |
| scope.getResourceGenTask().dependsOn(generateMicroApkTask); |
| } |
| |
| public void createExternalNativeBuildJsonGenerators(@NonNull VariantScope scope) { |
| |
| CoreExternalNativeBuild externalNativeBuild = extension.getExternalNativeBuild(); |
| ExternalNativeBuildTaskUtils.ExternalNativeBuildProjectPathResolution pathResolution = |
| ExternalNativeBuildTaskUtils.getProjectPath(externalNativeBuild); |
| if (pathResolution.errorText != null) { |
| androidBuilder |
| .getIssueReporter() |
| .reportError( |
| Type.EXTERNAL_NATIVE_BUILD_CONFIGURATION, |
| pathResolution.errorText, |
| scope.getVariantConfiguration().getFullName()); |
| return; |
| } |
| |
| if (pathResolution.makeFile == null) { |
| // No project |
| return; |
| } |
| |
| scope.setExternalNativeJsonGenerator( |
| ExternalNativeJsonGenerator.create( |
| project.getPath(), |
| project.getProjectDir(), |
| project.getBuildDir(), |
| pathResolution.externalNativeBuildDir, |
| checkNotNull(pathResolution.buildSystem), |
| pathResolution.makeFile, |
| androidBuilder, |
| sdkHandler, |
| scope)); |
| } |
| |
| public void createExternalNativeBuildTasks(@NonNull VariantScope scope) { |
| ExternalNativeJsonGenerator generator = scope.getExternalNativeJsonGenerator(); |
| if (generator == null) { |
| return; |
| } |
| |
| // Set up JSON generation tasks |
| Task generateTask = |
| taskFactory.create( |
| ExternalNativeBuildJsonTask.createTaskConfigAction(generator, scope)); |
| |
| generateTask.dependsOn(scope.getPreBuildTask()); |
| |
| ProjectOptions projectOptions = globalScope.getProjectOptions(); |
| |
| String targetAbi = |
| projectOptions.get(BooleanOption.BUILD_ONLY_TARGET_ABI) |
| ? projectOptions.get(StringOption.IDE_BUILD_TARGET_ABI) |
| : null; |
| |
| // Set up build tasks |
| ExternalNativeBuildTask buildTask = |
| taskFactory.create( |
| new ExternalNativeBuildTask.ConfigAction( |
| targetAbi, generator, scope, androidBuilder)); |
| |
| buildTask.dependsOn(generateTask); |
| scope.setExternalNativeBuildTask(buildTask); |
| scope.getCompileTask().dependsOn(buildTask); |
| |
| // Set up clean tasks |
| Task cleanTask = checkNotNull(taskFactory.findByName("clean")); |
| cleanTask.dependsOn( |
| taskFactory |
| .create( |
| new ExternalNativeCleanTask.ConfigAction( |
| generator, scope, androidBuilder)) |
| .getName()); |
| } |
| |
| public void createNdkTasks(@NonNull VariantScope scope) { |
| if (ExternalNativeBuildTaskUtils.isExternalNativeBuildEnabled( |
| extension.getExternalNativeBuild())) { |
| return; |
| } |
| |
| NdkCompile ndkCompileTask = taskFactory.create(new NdkCompile.ConfigAction(scope)); |
| |
| ndkCompileTask.dependsOn(scope.getPreBuildTask()); |
| if (Boolean.TRUE.equals( |
| scope.getVariantData() |
| .getVariantConfiguration() |
| .getMergedFlavor() |
| .getRenderscriptNdkModeEnabled())) { |
| ndkCompileTask.dependsOn(scope.getRenderscriptCompileTask()); |
| } |
| scope.getCompileTask().dependsOn(ndkCompileTask); |
| } |
| |
| /** Create transform for stripping debug symbols from native libraries before deploying. */ |
| public static void createStripNativeLibraryTask( |
| @NonNull TaskFactory taskFactory, @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( |
| taskFactory, |
| scope, |
| new StripDebugSymbolTransform( |
| globalScope.getProject(), |
| globalScope.getNdkHandler(), |
| globalScope.getExtension().getPackagingOptions().getDoNotStrip(), |
| scope.getVariantConfiguration().getType() == VariantType.LIBRARY)); |
| } |
| |
| /** Creates the tasks to build unit tests. */ |
| public void createUnitTestVariantTasks(@NonNull TestVariantData variantData) { |
| VariantScope variantScope = variantData.getScope(); |
| BaseVariantData testedVariantData = |
| checkNotNull(variantScope.getTestedVariantData(), "Not a unit test variant"); |
| VariantScope testedVariantScope = testedVariantData.getScope(); |
| |
| createPreBuildTasks(variantScope); |
| |
| // Create all current streams (dependencies mostly at this point) |
| createDependencyStreams(variantScope); |
| |
| createProcessJavaResTask(variantScope); |
| createCompileAnchorTask(variantScope); |
| |
| if (extension.getTestOptions().getUnitTests().isIncludeAndroidResources()) { |
| File unitTestConfigDir = |
| new File( |
| globalScope.getIntermediatesDir(), |
| "unitTestConfig/" + variantData.getVariantConfiguration().getDirName()); |
| GenerateTestConfig generateTestConfig = |
| taskFactory.create( |
| new GenerateTestConfig.ConfigAction(variantScope, unitTestConfigDir)); |
| variantScope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.UNIT_TEST_CONFIG_DIRECTORY, |
| unitTestConfigDir, |
| generateTestConfig.getName()); |
| variantScope.getCompileTask().dependsOn(generateTestConfig); |
| } |
| |
| // :app:compileDebugUnitTestSources should be enough for running tests from AS, so add |
| // dependencies on tasks that prepare necessary data files. |
| Task compileTask = variantScope.getCompileTask(); |
| compileTask.dependsOn( |
| |
| variantScope.getProcessJavaResourcesTask(), |
| testedVariantScope.getProcessJavaResourcesTask()); |
| if (extension.getTestOptions().getUnitTests().isIncludeAndroidResources()) { |
| compileTask.dependsOn(testedVariantScope.getMergeAssetsTask()); |
| compileTask.dependsOn(testedVariantScope.getManifestProcessorTask()); |
| } |
| |
| // Empty R class jar. TODO: Resources support for unit tests? |
| variantScope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.COMPILE_ONLY_NAMESPACED_R_CLASS_JAR, |
| project.files(), |
| null); |
| |
| JavaCompile javacTask = createJavacTask(variantScope); |
| addJavacClassesStream(variantScope); |
| setJavaCompilerTask(javacTask, variantScope); |
| javacTask.dependsOn(testedVariantScope.getJavacTask()); |
| |
| createMergeJavaResTransform(variantScope); |
| |
| createRunUnitTestTask(variantScope); |
| |
| DefaultTask assembleUnitTests = variantScope.getAssembleTask(); |
| assembleUnitTests.dependsOn(createMockableJar); |
| |
| // This hides the assemble unit test task from the task list. |
| assembleUnitTests.setGroup(null); |
| } |
| |
| protected void createSplitsDiscovery(VariantScope variantScope) { |
| if (variantScope.getVariantData().getType().getCanHaveSplits()) { |
| // split list calculation and save to this file. |
| File splitListOutputFile = |
| new File( |
| new File(variantScope.getSplitSupportDirectory(), "split-list"), |
| FN_SPLIT_LIST); |
| SplitsDiscovery splitsDiscoveryAndroidTask = |
| taskFactory.create( |
| new SplitsDiscovery.ConfigAction(variantScope, splitListOutputFile)); |
| |
| variantScope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.SPLIT_LIST, |
| splitListOutputFile, |
| splitsDiscoveryAndroidTask.getName()); |
| } |
| } |
| |
| /** Creates the tasks to build android tests. */ |
| public void createAndroidTestVariantTasks(@NonNull TestVariantData variantData) { |
| VariantScope variantScope = variantData.getScope(); |
| |
| createAnchorTasks(variantScope); |
| |
| // Create all current streams (dependencies mostly at this point) |
| createDependencyStreams(variantScope); |
| |
| // persist variant's output |
| taskFactory.create(new MainApkListPersistence.ConfigAction(variantScope)); |
| |
| // Add a task to process the manifest |
| createProcessTestManifestTask( |
| variantScope, checkNotNull(variantScope.getTestedVariantData()).getScope()); |
| |
| // Add a task to create the res values |
| createGenerateResValuesTask(variantScope); |
| |
| // Add a task to compile renderscript files. |
| createRenderscriptTask(variantScope); |
| |
| // Add a task to merge the resource folders |
| createMergeResourcesTask(variantScope, true); |
| |
| // Add tasks to compile shader |
| createShaderTask(variantScope); |
| |
| // Add a task to merge the assets folders |
| createMergeAssetsTask(variantScope); |
| |
| // Add a task to create the BuildConfig class |
| createBuildConfigTask(variantScope); |
| |
| // Add a task to generate resource source files |
| createApkProcessResTask(variantScope); |
| |
| // process java resources |
| createProcessJavaResTask(variantScope); |
| |
| createAidlTask(variantScope); |
| |
| // Add NDK tasks |
| if (!isComponentModelPlugin()) { |
| createNdkTasks(variantScope); |
| } |
| variantScope.setNdkBuildable(getNdkBuildable(variantData)); |
| |
| // add tasks to merge jni libs. |
| createMergeJniLibFoldersTasks(variantScope); |
| |
| // Add data binding tasks if enabled |
| createDataBindingTasksIfNecessary(variantScope, MergeType.MERGE); |
| |
| // Add a task to compile the test application |
| JavaCompile javacTask = createJavacTask(variantScope); |
| addJavacClassesStream(variantScope); |
| setJavaCompilerTask(javacTask, variantScope); |
| createPostCompilationTasks(variantScope); |
| |
| createPackagingTask(variantScope, null /* buildInfoGeneratorTask */); |
| |
| taskFactory.configure( |
| ASSEMBLE_ANDROID_TEST, |
| assembleTest -> |
| assembleTest.dependsOn(variantData.getScope().getAssembleTask().getName())); |
| |
| createConnectedTestForVariant(variantScope); |
| } |
| |
| /** Is the given variant relevant for lint? */ |
| private static boolean isLintVariant(@NonNull VariantScope variantScope) { |
| // Only create lint targets for variants like debug and release, not debugTest |
| final VariantType variantType = variantScope.getVariantConfiguration().getType(); |
| return !variantType.isForTesting() && variantType != FEATURE; |
| } |
| |
| /** |
| * Add tasks for running lint on individual variants. We've already added a lint task earlier |
| * which runs on all variants. |
| */ |
| public void createLintTasks(final VariantScope scope) { |
| if (!isLintVariant(scope)) { |
| return; |
| } |
| |
| taskFactory.create(new LintPerVariantTask.ConfigAction(scope)); |
| } |
| |
| /** Returns the full path of a task given its name. */ |
| private String getTaskPath(String taskName) { |
| return project.getRootProject() == project |
| ? ':' + taskName |
| : project.getPath() + ':' + taskName; |
| } |
| |
| private void maybeCreateLintVitalTask(@NonNull ApkVariantData variantData) { |
| VariantScope variantScope = variantData.getScope(); |
| GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration(); |
| |
| if (!isLintVariant(variantScope) |
| || variantScope.getInstantRunBuildContext().isInInstantRunMode() |
| || variantConfig.getBuildType().isDebuggable() |
| || !extension.getLintOptions().isCheckReleaseBuilds()) { |
| return; |
| } |
| |
| LintPerVariantTask lintReleaseCheck = |
| taskFactory.create(new LintPerVariantTask.VitalConfigAction(variantScope)); |
| if (variantData.javacTask != null) { |
| lintReleaseCheck.dependsOn(variantData.javacTask); |
| } |
| |
| variantScope.getAssembleTask().dependsOn(lintReleaseCheck); |
| |
| // If lint is being run, we do not need to run lint vital. |
| project.getGradle() |
| .getTaskGraph() |
| .whenReady( |
| taskGraph -> { |
| if (taskGraph.hasTask(getTaskPath(LINT))) { |
| project.getTasks() |
| .getByName(lintReleaseCheck.getName()) |
| .setEnabled(false); |
| } |
| }); |
| } |
| |
| private void createRunUnitTestTask( |
| @NonNull final VariantScope variantScope) { |
| final AndroidUnitTest runTestsTask = |
| taskFactory.create(new AndroidUnitTest.ConfigAction(variantScope)); |
| |
| taskFactory.configure( |
| JavaPlugin.TEST_TASK_NAME, test -> test.dependsOn(runTestsTask.getName())); |
| } |
| |
| public void createTopLevelTestTasks(boolean hasFlavors) { |
| createMockableJarTask(); |
| createAttrFromAndroidJarTask(); |
| |
| final List<String> reportTasks = Lists.newArrayListWithExpectedSize(2); |
| |
| List<DeviceProvider> providers = extension.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. |
| |
| DefaultTask connectedAndroidTestTask; |
| if (hasFlavors) { |
| connectedAndroidTestTask = |
| taskFactory.create( |
| new AndroidReportTask.ConfigAction( |
| globalScope, |
| AndroidReportTask.ConfigAction.TaskKind.CONNECTED)); |
| reportTasks.add(connectedAndroidTestTask.getName()); |
| } else { |
| connectedAndroidTestTask = |
| taskFactory.create( |
| CONNECTED_ANDROID_TEST, |
| connectedTask -> { |
| connectedTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP); |
| connectedTask.setDescription( |
| "Installs and runs instrumentation tests " |
| + "for all flavors on connected devices."); |
| }); |
| } |
| |
| taskFactory.configure( |
| CONNECTED_CHECK, check -> check.dependsOn(connectedAndroidTestTask.getName())); |
| |
| DefaultTask deviceAndroidTestTask; |
| // if more than one provider tasks, either because of several flavors, or because of |
| // more than one providers, then create an aggregate report tasks for all of them. |
| if (providers.size() > 1 || hasFlavors) { |
| deviceAndroidTestTask = |
| taskFactory.create( |
| new AndroidReportTask.ConfigAction( |
| globalScope, |
| AndroidReportTask.ConfigAction.TaskKind.DEVICE_PROVIDER)); |
| reportTasks.add(deviceAndroidTestTask.getName()); |
| } else { |
| deviceAndroidTestTask = |
| taskFactory.create( |
| DEVICE_ANDROID_TEST, |
| providerTask -> { |
| providerTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP); |
| providerTask.setDescription( |
| "Installs and runs instrumentation tests " |
| + "using all Device Providers."); |
| }); |
| } |
| |
| taskFactory.configure( |
| DEVICE_CHECK, check -> check.dependsOn(deviceAndroidTestTask.getName())); |
| |
| // Create top level unit test tasks. |
| |
| taskFactory.create( |
| JavaPlugin.TEST_TASK_NAME, |
| unitTestTask -> { |
| unitTestTask.setGroup(JavaBasePlugin.VERIFICATION_GROUP); |
| unitTestTask.setDescription("Run unit tests for all variants."); |
| }); |
| taskFactory.configure( |
| 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( |
| taskGraph -> { |
| for (String reportTask : reportTasks) { |
| if (taskGraph.hasTask(getTaskPath(reportTask))) { |
| taskFactory.configure( |
| reportTask, |
| task -> ((AndroidReportTask) task).setWillRun()); |
| } |
| } |
| }); |
| } |
| } |
| |
| protected void createConnectedTestForVariant( |
| @NonNull final VariantScope variantScope) { |
| final BaseVariantData baseVariantData = checkNotNull(variantScope.getTestedVariantData()); |
| final TestVariantData testVariantData = (TestVariantData) variantScope.getVariantData(); |
| |
| boolean isLibrary = |
| baseVariantData.getVariantConfiguration().getType() == VariantType.LIBRARY; |
| |
| TestDataImpl testData = |
| new TestDataImpl( |
| testVariantData, |
| variantScope.getOutput(VariantScope.TaskOutputType.APK), |
| isLibrary |
| ? null |
| : testVariantData |
| .getTestedVariantData() |
| .getScope() |
| .getOutput(VariantScope.TaskOutputType.APK)); |
| testData.setExtraInstrumentationTestRunnerArgs( |
| projectOptions.getExtraInstrumentationTestRunnerArgs()); |
| |
| configureTestData(testData); |
| |
| // create the check tasks for this test |
| // first the connected one. |
| ImmutableList<DefaultTask> artifactsTasks = |
| ImmutableList.of( |
| variantScope.getAssembleTask(), |
| testVariantData.getTestedVariantData().getScope().getAssembleTask()); |
| |
| DeviceProviderInstrumentTestTask connectedTask = |
| taskFactory.create( |
| new DeviceProviderInstrumentTestTask.ConfigAction( |
| testVariantData.getScope(), |
| new ConnectedDeviceProvider( |
| sdkHandler.getSdkInfo().getAdb(), |
| extension.getAdbOptions().getTimeOutInMs(), |
| new LoggerWrapper(logger)), |
| testData, |
| project.files() /* testTargetMetadata */)); |
| |
| connectedTask.dependsOn(artifactsTasks.toArray()); |
| |
| variantScope.setConnectedTask(connectedTask); |
| |
| taskFactory.configure( |
| CONNECTED_ANDROID_TEST, |
| connectedAndroidTest -> connectedAndroidTest.dependsOn(connectedTask.getName())); |
| |
| if (baseVariantData.getVariantConfiguration().getBuildType().isTestCoverageEnabled()) { |
| |
| Configuration jacocoAntConfiguration = |
| JacocoConfigurations.getJacocoAntTaskConfiguration( |
| project, getJacocoVersion(variantScope)); |
| JacocoReportTask reportTask = |
| taskFactory.create( |
| new JacocoReportTask.ConfigAction( |
| variantScope, jacocoAntConfiguration)); |
| |
| reportTask.dependsOn(connectedTask.getName()); |
| |
| variantScope.setCoverageReportTask(reportTask); |
| baseVariantData.getScope().getCoverageReportTask().dependsOn(reportTask); |
| |
| taskFactory.configure( |
| CONNECTED_ANDROID_TEST, |
| connectedAndroidTest -> connectedAndroidTest.dependsOn(reportTask.getName())); |
| } |
| |
| List<DeviceProvider> providers = extension.getDeviceProviders(); |
| |
| // now the providers. |
| for (DeviceProvider deviceProvider : providers) { |
| |
| final DeviceProviderInstrumentTestTask providerTask = |
| taskFactory.create( |
| new DeviceProviderInstrumentTestTask.ConfigAction( |
| testVariantData.getScope(), |
| deviceProvider, |
| testData, |
| project.files() /* testTargetMetadata */)); |
| |
| providerTask.dependsOn(artifactsTasks.toArray()); |
| taskFactory.configure( |
| DEVICE_ANDROID_TEST, |
| deviceAndroidTest -> deviceAndroidTest.dependsOn(providerTask.getName())); |
| } |
| |
| // now the test servers |
| List<TestServer> servers = extension.getTestServers(); |
| for (final TestServer testServer : servers) { |
| final TestServerTask serverTask = |
| taskFactory.create( |
| new TestServerTask.TestServerTaskConfigAction( |
| variantScope, testServer)); |
| serverTask.dependsOn(variantScope.getAssembleTask()); |
| |
| taskFactory.configure( |
| 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 final VariantScope variantScope) { |
| |
| checkNotNull(variantScope.getJavacTask()); |
| |
| 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() |
| && !variantScope.getInstantRunBuildContext().isInInstantRunMode(); |
| if (isTestCoverageEnabled) { |
| createJacocoTransform(variantScope); |
| } |
| |
| maybeCreateDesugarTask(variantScope, config.getMinSdkVersion(), transformManager); |
| |
| AndroidConfig extension = variantScope.getGlobalScope().getExtension(); |
| |
| // Merge Java Resources. |
| createMergeJavaResTransform(variantScope); |
| |
| // ----- External Transforms ----- |
| // apply all the external transforms. |
| List<Transform> customTransforms = extension.getTransforms(); |
| List<List<Object>> customTransformsDependencies = extension.getTransformsDependencies(); |
| |
| for (int i = 0, count = customTransforms.size(); i < count; i++) { |
| Transform transform = customTransforms.get(i); |
| |
| List<Object> deps = customTransformsDependencies.get(i); |
| transformManager |
| .addTransform(taskFactory, variantScope, transform) |
| .ifPresent( |
| t -> { |
| if (!deps.isEmpty()) { |
| t.dependsOn(deps); |
| } |
| |
| // if the task is a no-op then we make assemble task depend on it. |
| if (transform.getScopes().isEmpty()) { |
| variantScope.getAssembleTask().dependsOn(t); |
| } |
| }); |
| } |
| |
| // ----- Android studio profiling transforms |
| for (String jar : getAdvancedProfilingTransforms(projectOptions)) { |
| if (variantScope.getVariantConfiguration().getBuildType().isDebuggable() |
| && variantData.getType().equals(VariantType.DEFAULT) |
| && jar != null) { |
| transformManager.addTransform( |
| taskFactory, variantScope, new CustomClassTransform(jar)); |
| } |
| } |
| |
| // ----- Minify next ----- |
| maybeCreateJavaCodeShrinkerTransform(variantScope); |
| |
| maybeCreateResourcesShrinkerTransform(variantScope); |
| |
| // ----- 10x support |
| |
| PreColdSwapTask preColdSwapTask = null; |
| if (variantScope.getInstantRunBuildContext().isInInstantRunMode()) { |
| |
| DefaultTask allActionsAnchorTask = createInstantRunAllActionsTasks(variantScope); |
| assert variantScope.getInstantRunTaskManager() != null; |
| preColdSwapTask = |
| variantScope.getInstantRunTaskManager().createPreColdswapTask(projectOptions); |
| preColdSwapTask.dependsOn(allActionsAnchorTask); |
| |
| // 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 |
| |
| DexingType dexingType = variantScope.getDexingType(); |
| |
| // Upgrade from legacy multi-dex to native multi-dex if possible when using with a device |
| if (dexingType == DexingType.LEGACY_MULTIDEX) { |
| if (variantScope.getVariantConfiguration().isMultiDexEnabled() |
| && variantScope |
| .getVariantConfiguration() |
| .getMinSdkVersionWithTargetDeviceApi() |
| .getFeatureLevel() |
| >= 21) { |
| dexingType = DexingType.NATIVE_MULTIDEX; |
| } |
| } |
| |
| Optional<TransformTask> multiDexClassListTask; |
| |
| if (dexingType == DexingType.LEGACY_MULTIDEX) { |
| boolean proguardInPipeline = variantScope.getCodeShrinker() == CodeShrinker.PROGUARD; |
| |
| // If ProGuard will be used, we'll end up with a "fat" jar anyway. If we're using the |
| // new dexing pipeline, we'll use the new MainDexListTransform below, so there's no need |
| // for merging all classes into a single jar. |
| if (!proguardInPipeline && !usingIncrementalDexing(variantScope)) { |
| // Create a transform to jar the inputs into a single jar. 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(taskFactory, 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. |
| Transform multiDexTransform; |
| if (usingIncrementalDexing(variantScope)) { |
| if (projectOptions.get(BooleanOption.ENABLE_D8_MAIN_DEX_LIST)) { |
| multiDexTransform = new D8MainDexListTransform(variantScope); |
| } else { |
| multiDexTransform = |
| new MainDexListTransform(variantScope, extension.getDexOptions()); |
| } |
| } else { |
| multiDexTransform = new MultiDexTransform(variantScope, extension.getDexOptions()); |
| } |
| multiDexClassListTask = |
| transformManager.addTransform(taskFactory, variantScope, multiDexTransform); |
| multiDexClassListTask.ifPresent(variantScope::addColdSwapBuildTask); |
| } else { |
| multiDexClassListTask = Optional.empty(); |
| } |
| |
| |
| if (usingIncrementalDexing(variantScope)) { |
| createNewDexTasks(variantScope, multiDexClassListTask.orElse(null), dexingType); |
| } else { |
| createDexTasks(variantScope, multiDexClassListTask.orElse(null), dexingType); |
| } |
| |
| if (preColdSwapTask != null) { |
| for (DefaultTask task : variantScope.getColdSwapBuildTasks()) { |
| task.dependsOn(preColdSwapTask); |
| } |
| } |
| |
| // ---- Create tasks to publish the pipeline output as needed. |
| |
| final File intermediatesDir = variantScope.getGlobalScope().getIntermediatesDir(); |
| createPipelineToPublishTask( |
| variantScope, |
| transformManager.getPipelineOutputAsFileCollection(StreamFilter.DEX), |
| FileUtils.join(intermediatesDir, "bundling", "dex"), |
| PUBLISHED_DEX); |
| |
| createPipelineToPublishTask( |
| variantScope, |
| transformManager.getPipelineOutputAsFileCollection(StreamFilter.RESOURCES), |
| FileUtils.join(intermediatesDir, "bundling", "java-res"), |
| PUBLISHED_JAVA_RES); |
| |
| createPipelineToPublishTask( |
| variantScope, |
| transformManager.getPipelineOutputAsFileCollection(StreamFilter.NATIVE_LIBS), |
| FileUtils.join(intermediatesDir, "bundling", "native-libs"), |
| PUBLISHED_NATIVE_LIBS); |
| } |
| |
| private void createPipelineToPublishTask( |
| @NonNull VariantScope variantScope, |
| @NonNull FileCollection fileCollection, |
| @NonNull File outputFile, |
| @NonNull TaskOutputHolder.TaskOutputType outputType) { |
| PipelineToPublicationTask task = |
| taskFactory.create( |
| new PipelineToPublicationTask.ConfigAction( |
| variantScope, fileCollection, outputFile, outputType)); |
| |
| variantScope.addTaskOutput(outputType, outputFile, task.getName()); |
| } |
| |
| private void maybeCreateDesugarTask( |
| @NonNull VariantScope variantScope, |
| @NonNull AndroidVersion minSdk, |
| @NonNull TransformManager transformManager) { |
| if (variantScope.getJava8LangSupportType() == Java8LangSupport.DESUGAR) { |
| FileCache userCache = getUserIntermediatesCache(); |
| |
| FixStackFramesTransform fixFrames = |
| new FixStackFramesTransform( |
| () -> androidBuilder.getBootClasspath(true), |
| System.getProperty("sun.boot.class.path"), |
| userCache); |
| transformManager.addTransform(taskFactory, variantScope, fixFrames); |
| |
| DesugarTransform desugarTransform = |
| new DesugarTransform( |
| () -> androidBuilder.getBootClasspath(true), |
| System.getProperty("sun.boot.class.path"), |
| userCache, |
| minSdk.getFeatureLevel(), |
| androidBuilder.getJavaProcessExecutor(), |
| project.getLogger().isEnabled(LogLevel.INFO), |
| projectOptions.get(BooleanOption.ENABLE_GRADLE_WORKERS), |
| variantScope.getGlobalScope().getTmpFolder().toPath(), |
| getProjectVariantId(variantScope), |
| projectOptions.get(BooleanOption.ENABLE_INCREMENTAL_DESUGARING)); |
| transformManager.addTransform(taskFactory, variantScope, desugarTransform); |
| |
| if (minSdk.getFeatureLevel() |
| >= DesugarProcessArgs.MIN_SUPPORTED_API_TRY_WITH_RESOURCES) { |
| return; |
| } |
| |
| if (variantScope.getVariantConfiguration().getType().isForTesting()) { |
| BaseVariantData testedVariant = |
| Objects.requireNonNull(variantScope.getTestedVariantData()); |
| if (testedVariant.getType() != VariantType.LIBRARY) { |
| // test variants, except for library, should not package try-with-resources jar |
| // as the tested variant already contains it |
| return; |
| } |
| } |
| |
| // add runtime classes for try-with-resources support |
| String taskName = variantScope.getTaskName(ExtractTryWithResourcesSupportJar.TASK_NAME); |
| ExtractTryWithResourcesSupportJar extractTryWithResources = |
| taskFactory.create( |
| new ExtractTryWithResourcesSupportJar.ConfigAction( |
| variantScope.getTryWithResourceRuntimeSupportJar(), |
| taskName, |
| variantScope.getFullVariantName())); |
| variantScope.getTryWithResourceRuntimeSupportJar().builtBy(extractTryWithResources); |
| transformManager.addStream( |
| OriginalStream.builder(project, "runtime-deps-try-with-resources") |
| .addContentTypes(TransformManager.CONTENT_CLASS) |
| .addScope(Scope.EXTERNAL_LIBRARIES) |
| .setFileCollection(variantScope.getTryWithResourceRuntimeSupportJar()) |
| .build()); |
| } |
| } |
| |
| /** |
| * Creates tasks used for DEX generation. This will use a new pipeline that uses dex archives in |
| * order to enable incremental dexing support. |
| */ |
| private void createNewDexTasks( |
| @NonNull VariantScope variantScope, |
| @Nullable TransformTask multiDexClassListTask, |
| @NonNull DexingType dexingType) { |
| TransformManager transformManager = variantScope.getTransformManager(); |
| |
| DefaultDexOptions dexOptions; |
| if (variantScope.getVariantData().getType().isForTesting()) { |
| // Don't use custom dx flags when compiling the test FULL_APK. They can break the test FULL_APK, |
| // like --minimal-main-dex. |
| dexOptions = DefaultDexOptions.copyOf(extension.getDexOptions()); |
| dexOptions.setAdditionalParameters(ImmutableList.of()); |
| } else { |
| dexOptions = extension.getDexOptions(); |
| } |
| |
| boolean minified = runJavaCodeShrinker(variantScope); |
| FileCache userLevelCache = getUserDexCache(minified, dexOptions.getPreDexLibraries()); |
| DexArchiveBuilderTransform preDexTransform = |
| new DexArchiveBuilderTransformBuilder() |
| .setAndroidJarClasspath( |
| () -> |
| variantScope |
| .getGlobalScope() |
| .getAndroidBuilder() |
| .getBootClasspath(false)) |
| .setDexOptions(dexOptions) |
| .setMessageReceiver(variantScope.getGlobalScope().getMessageReceiver()) |
| .setUserLevelCache(userLevelCache) |
| .setMinSdkVersion(variantScope.getMinSdkVersion().getFeatureLevel()) |
| .setDexer(variantScope.getDexer()) |
| .setUseGradleWorkers( |
| projectOptions.get(BooleanOption.ENABLE_GRADLE_WORKERS)) |
| .setInBufferSize(projectOptions.get(IntegerOption.DEXING_READ_BUFFER_SIZE)) |
| .setOutBufferSize( |
| projectOptions.get(IntegerOption.DEXING_WRITE_BUFFER_SIZE)) |
| .setIsDebuggable( |
| variantScope |
| .getVariantConfiguration() |
| .getBuildType() |
| .isDebuggable()) |
| .setJava8LangSupportType(variantScope.getJava8LangSupportType()) |
| .setEnableIncrementalDesugaring( |
| projectOptions.get(BooleanOption.ENABLE_INCREMENTAL_DESUGARING)) |
| .setProjectVariant(getProjectVariantId(variantScope)) |
| .createDexArchiveBuilderTransform(); |
| transformManager |
| .addTransform(taskFactory, variantScope, preDexTransform) |
| .ifPresent(variantScope::addColdSwapBuildTask); |
| |
| boolean isDebuggable = variantScope.getVariantConfiguration().getBuildType().isDebuggable(); |
| if (dexingType != DexingType.LEGACY_MULTIDEX |
| && variantScope.getCodeShrinker() == null |
| && extension.getTransforms().isEmpty()) { |
| ExternalLibsMergerTransform externalLibsMergerTransform = |
| new ExternalLibsMergerTransform( |
| dexingType, |
| variantScope.getDexMerger(), |
| variantScope.getMinSdkVersion().getFeatureLevel(), |
| isDebuggable, |
| variantScope.getGlobalScope().getMessageReceiver(), |
| DexMergerTransformCallable::new); |
| |
| transformManager.addTransform(taskFactory, variantScope, externalLibsMergerTransform); |
| } |
| |
| DexMergerTransform dexTransform = |
| new DexMergerTransform( |
| dexingType, |
| dexingType == DexingType.LEGACY_MULTIDEX |
| ? project.files(variantScope.getMainDexListFile()) |
| : null, |
| variantScope.getGlobalScope().getMessageReceiver(), |
| variantScope.getDexMerger(), |
| variantScope.getMinSdkVersion().getFeatureLevel(), |
| isDebuggable); |
| Optional<TransformTask> dexTask = |
| transformManager.addTransform(taskFactory, variantScope, dexTransform); |
| // need to manually make dex task depend on MultiDexTransform since there's no stream |
| // consumption making this automatic |
| dexTask.ifPresent( |
| t -> { |
| if (multiDexClassListTask != null) { |
| t.dependsOn(multiDexClassListTask); |
| } |
| variantScope.addColdSwapBuildTask(t); |
| }); |
| } |
| |
| @NonNull |
| private static String getProjectVariantId(@NonNull VariantScope variantScope) { |
| return variantScope.getGlobalScope().getProject().getName() |
| + ":" |
| + variantScope.getFullVariantName(); |
| } |
| |
| private boolean usingIncrementalDexing(@NonNull VariantScope variantScope) { |
| if (!projectOptions.get(BooleanOption.ENABLE_DEX_ARCHIVE)) { |
| return false; |
| } |
| if (variantScope.getVariantConfiguration().getBuildType().isDebuggable()) { |
| return true; |
| } |
| |
| // In release builds only D8 can be used. See b/37140568 for details. |
| return projectOptions.get(BooleanOption.ENABLE_D8); |
| } |
| |
| @Nullable |
| private FileCache getUserDexCache(boolean isMinifiedEnabled, boolean preDexLibraries) { |
| if (!preDexLibraries || isMinifiedEnabled) { |
| return null; |
| } |
| |
| return getUserIntermediatesCache(); |
| } |
| |
| @Nullable |
| private FileCache getUserIntermediatesCache() { |
| if (globalScope |
| .getProjectOptions() |
| .get(BooleanOption.ENABLE_INTERMEDIATE_ARTIFACTS_CACHE)) { |
| return globalScope.getBuildCache(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** Creates the pre-dexing task if needed, and task for producing the final DEX file(s). */ |
| private void createDexTasks( |
| @NonNull VariantScope variantScope, |
| @Nullable TransformTask multiDexClassListTask, |
| @NonNull DexingType dexingType) { |
| TransformManager transformManager = variantScope.getTransformManager(); |
| AndroidBuilder androidBuilder = variantScope.getGlobalScope().getAndroidBuilder(); |
| |
| DefaultDexOptions dexOptions; |
| if (variantScope.getVariantData().getType().isForTesting()) { |
| // Don't use custom dx flags when compiling the test FULL_APK. They can break the test FULL_APK, |
| // like --minimal-main-dex. |
| dexOptions = DefaultDexOptions.copyOf(extension.getDexOptions()); |
| dexOptions.setAdditionalParameters(ImmutableList.of()); |
| } else { |
| dexOptions = extension.getDexOptions(); |
| } |
| |
| boolean cachePreDex = |
| dexingType.isPreDex() |
| && dexOptions.getPreDexLibraries() |
| && !runJavaCodeShrinker(variantScope); |
| boolean preDexEnabled = |
| variantScope.getInstantRunBuildContext().isInInstantRunMode() || cachePreDex; |
| if (preDexEnabled) { |
| FileCache buildCache; |
| if (cachePreDex |
| && projectOptions.get(BooleanOption.ENABLE_INTERMEDIATE_ARTIFACTS_CACHE)) { |
| buildCache = this.buildCache; |
| } else { |
| buildCache = null; |
| } |
| |
| PreDexTransform preDexTransform = |
| new PreDexTransform( |
| dexOptions, |
| androidBuilder, |
| buildCache, |
| dexingType, |
| variantScope.getMinSdkVersion().getFeatureLevel()); |
| transformManager |
| .addTransform(taskFactory, variantScope, preDexTransform) |
| .ifPresent(variantScope::addColdSwapBuildTask); |
| } |
| |
| if (!preDexEnabled || dexingType != DexingType.NATIVE_MULTIDEX) { |
| // run if non native multidex or no pre-dexing |
| DexTransform dexTransform = |
| new DexTransform( |
| dexOptions, |
| dexingType, |
| preDexEnabled, |
| project.files(variantScope.getMainDexListFile()), |
| checkNotNull(androidBuilder.getTargetInfo(), "Target Info not set."), |
| androidBuilder.getDexByteCodeConverter(), |
| variantScope.getGlobalScope().getMessageReceiver(), |
| variantScope.getMinSdkVersion().getFeatureLevel()); |
| Optional<TransformTask> dexTask = |
| transformManager.addTransform(taskFactory, variantScope, dexTransform); |
| // need to manually make dex task depend on MultiDexTransform since there's no stream |
| // consumption making this automatic |
| dexTask.ifPresent( |
| t -> { |
| if (multiDexClassListTask != null) { |
| t.dependsOn(multiDexClassListTask); |
| } |
| variantScope.addColdSwapBuildTask(t); |
| }); |
| } |
| } |
| |
| private boolean runJavaCodeShrinker(VariantScope variantScope) { |
| return variantScope.getCodeShrinker() != null || isTestedAppObfuscated(variantScope); |
| } |
| |
| /** |
| * Default values if {@code false}, only {@link TestApplicationTaskManager} overrides this, |
| * because tested applications might be obfuscated. |
| * |
| * @return if the tested application is obfuscated |
| */ |
| protected boolean isTestedAppObfuscated(@NonNull VariantScope variantScope) { |
| return false; |
| } |
| |
| /** Create InstantRun related tasks that should be ran right after the java compilation task. */ |
| @NonNull |
| private DefaultTask createInstantRunAllActionsTasks(@NonNull VariantScope variantScope) { |
| |
| DefaultTask allActionAnchorTask = |
| taskFactory.create(new InstantRunAnchorTaskConfigAction(variantScope)); |
| |
| TransformManager transformManager = variantScope.getTransformManager(); |
| |
| ExtractJarsTransform extractJarsTransform = |
| new ExtractJarsTransform( |
| ImmutableSet.of(DefaultContentType.CLASSES), |
| ImmutableSet.of(Scope.SUB_PROJECTS)); |
| Optional<TransformTask> extractJarsTask = |
| transformManager.addTransform(taskFactory, variantScope, extractJarsTransform); |
| |
| InstantRunTaskManager instantRunTaskManager = |
| new InstantRunTaskManager( |
| getLogger(), |
| variantScope, |
| variantScope.getTransformManager(), |
| taskFactory, |
| recorder); |
| |
| FileCollection instantRunMergedManifests = |
| variantScope.getOutput(INSTANT_RUN_MERGED_MANIFESTS); |
| |
| variantScope.setInstantRunTaskManager(instantRunTaskManager); |
| AndroidVersion minSdkForDx = variantScope.getMinSdkVersion(); |
| BuildInfoLoaderTask buildInfoLoaderTask = |
| instantRunTaskManager.createInstantRunAllTasks( |
| variantScope.getGlobalScope().getExtension().getDexOptions(), |
| androidBuilder::getDexByteCodeConverter, |
| extractJarsTask.orElse(null), |
| allActionAnchorTask, |
| getResMergingScopes(variantScope), |
| instantRunMergedManifests, |
| true /* addResourceVerifier */, |
| minSdkForDx.getFeatureLevel()); |
| |
| if (variantScope.getSourceGenTask() != null) { |
| variantScope.getSourceGenTask().dependsOn(buildInfoLoaderTask); |
| } |
| |
| return allActionAnchorTask; |
| } |
| |
| protected void handleJacocoDependencies(@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() |
| && !variantScope.getInstantRunBuildContext().isInInstantRunMode() |
| && (!config.getType().isForTesting() |
| || (config.getTestedConfig() != null |
| && config.getTestedConfig().getType() |
| == VariantType.LIBRARY)); |
| if (isTestCoverageEnabled) { |
| if (variantScope.getDexer() == DexerTool.DX) { |
| androidBuilder |
| .getIssueReporter() |
| .reportWarning( |
| Type.GENERIC, |
| String.format( |
| "Jacoco version is downgraded to %s because dx is used. " |
| + "This is due to -P%s=false flag. See " |
| + "https://issuetracker.google.com/37116789 for " |
| + "more details.", |
| JacocoConfigurations.VERSION_FOR_DX, |
| BooleanOption.ENABLE_D8.getPropertyName())); |
| } |
| |
| String jacocoAgentRuntimeDependency = |
| JacocoConfigurations.getAgentRuntimeDependency(getJacocoVersion(variantScope)); |
| project.getDependencies() |
| .add( |
| variantScope.getVariantDependencies().getRuntimeClasspath().getName(), |
| jacocoAgentRuntimeDependency); |
| |
| // we need to force the same version of Jacoco we use for instrumentation |
| variantScope |
| .getVariantDependencies() |
| .getRuntimeClasspath() |
| .resolutionStrategy(r -> r.force(jacocoAgentRuntimeDependency)); |
| } |
| } |
| |
| @NonNull |
| public String getJacocoVersion(@NonNull VariantScope scope) { |
| if (scope.getDexer() == DexerTool.DX) { |
| return JacocoConfigurations.VERSION_FOR_DX; |
| } else { |
| return extension.getJacoco().getVersion(); |
| } |
| } |
| |
| public void createJacocoTransform( |
| @NonNull final VariantScope variantScope) { |
| JacocoTransform jacocoTransform = |
| new JacocoTransform( |
| JacocoConfigurations.getJacocoAntTaskConfiguration( |
| project, getJacocoVersion(variantScope))); |
| |
| variantScope.getTransformManager().addTransform(taskFactory, variantScope, jacocoTransform); |
| } |
| |
| /** |
| * Must be called before the javac task is created so that we it can be earlier in the transform |
| * pipeline. |
| */ |
| private void createDataBindingMergeArtifactsTask(@NonNull VariantScope variantScope) { |
| if (!extension.getDataBinding().isEnabled()) { |
| return; |
| } |
| final BaseVariantData variantData = variantScope.getVariantData(); |
| VariantType type = variantData.getType(); |
| boolean isTest = type == VariantType.ANDROID_TEST || type == VariantType.UNIT_TEST; |
| if (isTest && !extension.getDataBinding().isEnabledForTests()) { |
| BaseVariantData testedVariantData = checkNotNull(variantScope.getTestedVariantData()); |
| if (testedVariantData.getType() != LIBRARY) { |
| return; |
| } |
| } |
| File outFolder = |
| new File( |
| variantScope.getBuildFolderForDataBindingCompiler(), |
| DataBindingBuilder.ARTIFACT_FILES_DIR_FROM_LIBS); |
| |
| |
| Optional<TransformTask> dataBindingMergeTask; |
| dataBindingMergeTask = |
| variantScope |
| .getTransformManager() |
| .addTransform( |
| taskFactory, |
| variantScope, |
| new DataBindingMergeArtifactsTransform(getLogger(), outFolder)); |
| |
| dataBindingMergeTask.ifPresent( |
| task -> |
| variantScope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.DATA_BINDING_DEPENDENCY_ARTIFACTS, |
| outFolder, |
| task.getName())); |
| } |
| |
| private void createDataBindingMergeBaseClassesTask(@NonNull VariantScope variantScope) { |
| final BaseVariantData variantData = variantScope.getVariantData(); |
| VariantType type = variantData.getType(); |
| boolean isTest = type == VariantType.ANDROID_TEST || type == VariantType.UNIT_TEST; |
| if (isTest && !extension.getDataBinding().isEnabledForTests()) { |
| BaseVariantData testedVariantData = checkNotNull(variantScope.getTestedVariantData()); |
| if (testedVariantData.getType() != LIBRARY) { |
| return; |
| } |
| } |
| File outFolder = |
| variantScope.getIntermediateDir(DATA_BINDING_BASE_CLASS_LOGS_DEPENDENCY_ARTIFACTS); |
| |
| Optional<TransformTask> mergeBaseClassesTask; |
| mergeBaseClassesTask = |
| variantScope |
| .getTransformManager() |
| .addTransform( |
| taskFactory, |
| variantScope, |
| new DataBindingMergeGenClassLogTransform(getLogger(), outFolder)); |
| |
| mergeBaseClassesTask.ifPresent( |
| task -> |
| variantScope.addTaskOutput( |
| DATA_BINDING_BASE_CLASS_LOGS_DEPENDENCY_ARTIFACTS, |
| outFolder, |
| task.getName())); |
| } |
| |
| protected void createDataBindingTasksIfNecessary( |
| @NonNull VariantScope scope, @NonNull MergeType mergeType) { |
| if (!extension.getDataBinding().isEnabled()) { |
| return; |
| } |
| createDataBindingMergeBaseClassesTask(scope); |
| createDataBindingMergeArtifactsTask(scope); |
| |
| |
| VariantType type = scope.getVariantData().getType(); |
| boolean isTest = type == VariantType.ANDROID_TEST || type == VariantType.UNIT_TEST; |
| if (isTest && !extension.getDataBinding().isEnabledForTests()) { |
| BaseVariantData testedVariantData = checkNotNull(scope.getTestedVariantData()); |
| if (testedVariantData.getType() != LIBRARY) { |
| return; |
| } |
| } |
| |
| dataBindingBuilder.setDebugLogEnabled(getLogger().isDebugEnabled()); |
| |
| DataBindingExportBuildInfoTask exportBuildInfo = |
| taskFactory.create( |
| new DataBindingExportBuildInfoTask.ConfigAction(scope, mergeType)); |
| |
| exportBuildInfo.dependsOn(scope.getSourceGenTask()); |
| |
| scope.setDataBindingExportBuildInfoTask(exportBuildInfo); |
| |
| // setup generate base class task |
| File baseClassOutFolder = |
| new File( |
| globalScope.getGeneratedDir(), |
| "source/dataBinding/baseClasses/" |
| + scope.getVariantConfiguration().getDirName()); |
| File baseClassLogFolder = |
| scope.getIntermediateDir( |
| TaskOutputHolder.TaskOutputType.DATA_BINDING_BASE_CLASS_LOG_ARTIFACT); |
| |
| DataBindingGenBaseClassesTask.ConfigAction genBaseClassConfigAction = |
| new DataBindingGenBaseClassesTask.ConfigAction( |
| scope, baseClassOutFolder, baseClassLogFolder); |
| |
| DataBindingGenBaseClassesTask generateBaseClasses = |
| taskFactory.create(genBaseClassConfigAction); |
| scope.addTaskOutput( |
| DATA_BINDING_BASE_CLASS_SOURCE_OUT, |
| baseClassOutFolder, |
| generateBaseClasses.getName()); |
| scope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.DATA_BINDING_BASE_CLASS_LOG_ARTIFACT, |
| baseClassLogFolder, |
| generateBaseClasses.getName()); |
| generateBaseClasses.dependsOn(scope.getVariantData().mergeResourcesTask); |
| |
| setDataBindingAnnotationProcessorParams(scope); |
| } |
| |
| private void setDataBindingAnnotationProcessorParams(@NonNull VariantScope scope) { |
| BaseVariantData variantData = scope.getVariantData(); |
| GradleVariantConfiguration variantConfiguration = variantData.getVariantConfiguration(); |
| JavaCompileOptions javaCompileOptions = variantConfiguration.getJavaCompileOptions(); |
| AnnotationProcessorOptions processorOptions = |
| javaCompileOptions.getAnnotationProcessorOptions(); |
| if (processorOptions |
| instanceof com.android.build.gradle.internal.dsl.AnnotationProcessorOptions) { |
| com.android.build.gradle.internal.dsl.AnnotationProcessorOptions ots = |
| (com.android.build.gradle.internal.dsl.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 = checkNotNull(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(); |
| File classLogDir = |
| scope.getOutput( |
| TaskOutputHolder.TaskOutputType |
| .DATA_BINDING_BASE_CLASS_LOG_ARTIFACT) |
| .getSingleFile(); |
| DataBindingCompilerArgs args = |
| DataBindingCompilerArgs.builder() |
| .bundleFolder(scope.getBundleArtifactFolderForDataBinding()) |
| .enabledForTests(extension.getDataBinding().isEnabledForTests()) |
| .enableDebugLogs(getLogger().isDebugEnabled()) |
| .buildFolder(scope.getBuildFolderForDataBindingCompiler()) |
| .sdkDir(scope.getGlobalScope().getSdkHandler().getSdkFolder()) |
| .xmlOutDir(scope.getLayoutInfoOutputForDataBinding()) |
| .classLogDir(classLogDir) |
| .exportClassListTo( |
| variantData.getType().isExportDataBindingClassList() |
| ? scope.getGeneratedClassListOutputFileForDataBinding() |
| : null) |
| .printEncodedErrorLogs( |
| dataBindingBuilder.getPrintMachineReadableOutput()) |
| .modulePackage(packageName) |
| .minApi(minApi) |
| .testVariant(isTest) |
| .type(type) |
| .enableV2( |
| scope.getGlobalScope() |
| .getProjectOptions() |
| .get(BooleanOption.ENABLE_DATA_BINDING_V2)) |
| .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 fullBuildInfoGeneratorTask task that generates the build-info.xml for full build. |
| */ |
| public void createPackagingTask( |
| @NonNull VariantScope variantScope, |
| @Nullable BuildInfoWriterTask fullBuildInfoGeneratorTask) { |
| ApkVariantData variantData = (ApkVariantData) variantScope.getVariantData(); |
| |
| boolean signedApk = variantData.isSigned(); |
| |
| /* |
| * PrePackaging step class that will look if the packaging of the main FULL_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 FULL_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 FULL_APK must be rebuilt (even if the |
| * resources were changed in a previous build). |
| */ |
| VariantScope.TaskOutputType manifestType = |
| variantScope.getInstantRunBuildContext().isInInstantRunMode() |
| ? INSTANT_RUN_MERGED_MANIFESTS |
| : MERGED_MANIFESTS; |
| |
| final boolean splitsArePossible = |
| variantScope.getVariantData().getMultiOutputPolicy() == MultiOutputPolicy.SPLITS; |
| |
| FileCollection manifests = variantScope.getOutput(manifestType); |
| // this is where the final APKs will be located. |
| File finalApkLocation = variantScope.getApkLocation(); |
| // if we are not dealing with possible splits, we can generate in the final folder |
| // directly. |
| File outputDirectory = |
| splitsArePossible |
| ? variantScope.getFullApkPackagesOutputDirectory() |
| : finalApkLocation; |
| |
| TaskOutputHolder.TaskOutputType taskOutputType = |
| splitsArePossible |
| ? TaskOutputHolder.TaskOutputType.FULL_APK |
| : TaskOutputHolder.TaskOutputType.APK; |
| |
| boolean useSeparateApkForResources = |
| variantScope.getInstantRunBuildContext().useSeparateApkForResources(); |
| |
| VariantScope.TaskOutputType resourceFilesInputType = |
| variantScope.useResourceShrinker() |
| ? VariantScope.TaskOutputType.SHRUNK_PROCESSED_RES |
| : VariantScope.TaskOutputType.PROCESSED_RES; |
| |
| PackageApplication packageApp = |
| taskFactory.create( |
| new PackageApplication.StandardConfigAction( |
| variantScope, |
| outputDirectory, |
| useSeparateApkForResources |
| ? INSTANT_RUN_MAIN_APK_RESOURCES |
| : resourceFilesInputType, |
| manifests, |
| manifestType, |
| variantScope.getOutputScope(), |
| globalScope.getBuildCache(), |
| taskOutputType)); |
| variantScope.addTaskOutput(taskOutputType, outputDirectory, packageApp.getName()); |
| |
| Task packageInstantRunResources = null; |
| |
| if (variantScope.getInstantRunBuildContext().isInInstantRunMode()) { |
| packageInstantRunResources = |
| taskFactory.create( |
| new InstantRunResourcesApkBuilder.ConfigAction( |
| resourceFilesInputType, |
| variantScope.getOutput(resourceFilesInputType), |
| variantScope)); |
| packageInstantRunResources.dependsOn(getValidateSigningTask(variantScope)); |
| // make sure the task run even if none of the files we consume are available, |
| // this is necessary so we can clean up output. |
| packageApp.dependsOn(packageInstantRunResources); |
| |
| if (!useSeparateApkForResources) { |
| // in instantRunMode, there is no user configured splits, only one apk. |
| packageInstantRunResources = |
| taskFactory.create( |
| new PackageApplication.InstantRunResourcesConfigAction( |
| variantScope.getInstantRunResourcesFile(), |
| variantScope, |
| resourceFilesInputType, |
| manifests, |
| INSTANT_RUN_MERGED_MANIFESTS, |
| globalScope.getBuildCache(), |
| variantScope.getOutputScope())); |
| } |
| |
| // Make sure the MAIN artifact is registered after the RESOURCES one. |
| packageApp.dependsOn(packageInstantRunResources); |
| } |
| |
| // Common code for both packaging tasks. |
| Consumer<Task> configureResourcesAndAssetsDependencies = |
| task -> { |
| task.dependsOn(variantScope.getMergeAssetsTask()); |
| if (variantScope.getProcessResourcesTask() != null) { |
| task.dependsOn(variantScope.getProcessResourcesTask()); |
| } |
| }; |
| |
| configureResourcesAndAssetsDependencies.accept(packageApp); |
| if (packageInstantRunResources != null) { |
| configureResourcesAndAssetsDependencies.accept(packageInstantRunResources); |
| } |
| |
| CoreSigningConfig signingConfig = variantScope.getVariantConfiguration().getSigningConfig(); |
| |
| //noinspection VariableNotUsedInsideIf - we use the whole packaging scope below. |
| if (signingConfig != null) { |
| packageApp.dependsOn(getValidateSigningTask(variantScope)); |
| } |
| |
| if (variantScope.getJavacTask() != null) { |
| packageApp.dependsOn(variantScope.getJavacTask()); |
| } |
| if (variantData.packageSplitResourcesTask != null) { |
| packageApp.dependsOn(variantData.packageSplitResourcesTask); |
| } |
| if (variantData.packageSplitAbiTask != null) { |
| packageApp.dependsOn(variantData.packageSplitAbiTask); |
| } |
| if (variantScope.getJavacTask() != null) { |
| packageApp.dependsOn(variantScope.getJavacTask()); |
| } |
| // FIX ME : Reinstate once ShrinkResourcesTransform is converted. |
| //if ( variantOutputScope.getShrinkResourcesTask() != null) { |
| // packageApp.dependsOn( variantOutputScope.getShrinkResourcesTask()); |
| //} |
| |
| variantScope.setPackageApplicationTask(packageApp); |
| variantScope.getAssembleTask().dependsOn(packageApp.getName()); |
| |
| checkState(variantScope.getAssembleTask() != null); |
| if (fullBuildInfoGeneratorTask != null) { |
| |
| fullBuildInfoGeneratorTask.mustRunAfter(packageApp.getName()); |
| if (packageInstantRunResources != null) { |
| fullBuildInfoGeneratorTask.mustRunAfter(packageInstantRunResources); |
| } |
| variantScope.getAssembleTask().dependsOn(fullBuildInfoGeneratorTask.getName()); |
| } |
| |
| if (splitsArePossible) { |
| |
| CopyOutputs copyOutputsTask = |
| taskFactory.create( |
| new CopyOutputs.ConfigAction(variantScope, finalApkLocation)); |
| variantScope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.APK, |
| finalApkLocation, |
| copyOutputsTask.getName()); |
| variantScope.getAssembleTask().dependsOn(copyOutputsTask); |
| } |
| |
| // 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) { |
| InstallVariantTask installTask = |
| taskFactory.create(new InstallVariantTask.ConfigAction(variantScope)); |
| installTask.dependsOn(variantScope.getAssembleTask()); |
| } |
| |
| maybeCreateLintVitalTask(variantData); |
| |
| // add an uninstall task |
| final UninstallTask uninstallTask = |
| taskFactory.create(new UninstallTask.ConfigAction(variantScope)); |
| |
| taskFactory.configure( |
| UNINSTALL_ALL, uninstallAll -> uninstallAll.dependsOn(uninstallTask.getName())); |
| } |
| |
| protected Task getValidateSigningTask(@NonNull VariantScope variantScope) { |
| File defaultDebugKeystoreLocation = GradleKeystoreHelper.getDefaultDebugKeystoreLocation(); |
| ValidateSigningTask.ConfigAction configAction = |
| new ValidateSigningTask.ConfigAction(variantScope, defaultDebugKeystoreLocation); |
| |
| Task validateSigningTask = taskFactory.findByName(configAction.getName()); |
| if (validateSigningTask == null) { |
| validateSigningTask = taskFactory.create(configAction); |
| } |
| return validateSigningTask; |
| } |
| |
| public DefaultTask createAssembleTask(@NonNull final BaseVariantData variantData) { |
| return taskFactory.create( |
| variantData.getScope().getTaskName("assemble"), |
| task -> { |
| variantData.addTask(TaskContainer.TaskKind.ASSEMBLE, task); |
| }); |
| } |
| |
| @NonNull |
| public DefaultTask createAssembleTask(@NonNull VariantDimensionData dimensionData) { |
| final String sourceSetName = |
| StringHelper.capitalize(dimensionData.getSourceSet().getName()); |
| return taskFactory.create( |
| StringHelper.appendCapitalized("assemble", sourceSetName), |
| assembleTask -> { |
| assembleTask.setDescription("Assembles all " + sourceSetName + " builds."); |
| assembleTask.setGroup(BasePlugin.BUILD_GROUP); |
| }); |
| } |
| |
| protected void maybeCreateJavaCodeShrinkerTransform(@NonNull final VariantScope variantScope) { |
| CodeShrinker codeShrinker = variantScope.getCodeShrinker(); |
| |
| if (codeShrinker != null) { |
| doCreateJavaCodeShrinkerTransform( |
| variantScope, |
| // No mapping in non-test modules. |
| codeShrinker, |
| null); |
| } |
| } |
| |
| /** |
| * Actually creates the minify transform, using the given mapping configuration. The mapping is |
| * only used by test-only modules. |
| */ |
| protected final void doCreateJavaCodeShrinkerTransform( |
| @NonNull final VariantScope variantScope, |
| @NonNull CodeShrinker codeShrinker, |
| @Nullable FileCollection mappingFileCollection) { |
| Optional<TransformTask> transformTask; |
| switch (codeShrinker) { |
| case PROGUARD: |
| transformTask = createProguardTransform(variantScope, mappingFileCollection); |
| break; |
| case ANDROID_GRADLE: |
| transformTask = createBuiltInShrinkerTransform(variantScope); |
| break; |
| default: |
| throw new AssertionError("Unknown value " + codeShrinker); |
| } |
| |
| if (variantScope.getPostprocessingFeatures() != null && transformTask.isPresent()) { |
| CheckProguardFiles checkFilesTask = |
| taskFactory.create(new CheckProguardFiles.ConfigAction(variantScope)); |
| |
| transformTask.get().dependsOn(checkFilesTask); |
| } |
| } |
| |
| @NonNull |
| private Optional<TransformTask> createBuiltInShrinkerTransform(@NonNull VariantScope scope) { |
| BuiltInShrinkerTransform transform = new BuiltInShrinkerTransform(scope); |
| applyProguardConfig(transform, scope); |
| |
| if (scope.getInstantRunBuildContext().isInInstantRunMode()) { |
| //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.ir.** {*;}"); |
| } |
| |
| return scope.getTransformManager().addTransform(taskFactory, scope, transform); |
| } |
| |
| @NonNull |
| private Optional<TransformTask> createProguardTransform( |
| @NonNull VariantScope variantScope, @Nullable FileCollection mappingFileCollection) { |
| if (variantScope.getInstantRunBuildContext().isInInstantRunMode()) { |
| logger.warn( |
| "ProGuard is disabled for variant {} because it is not compatible with Instant Run. See " |
| + "http://d.android.com/r/studio-ui/shrink-code-with-ir.html " |
| + "for details on how to enable a code shrinker that's compatible with Instant Run.", |
| variantScope.getVariantConfiguration().getFullName()); |
| return Optional.empty(); |
| } |
| |
| final BaseVariantData testedVariantData = variantScope.getTestedVariantData(); |
| |
| ProGuardTransform transform = new ProGuardTransform(variantScope); |
| |
| if (testedVariantData != null) { |
| // This is an androidTest variant inside an app/library. |
| applyProguardDefaultsForTest(transform); |
| |
| // All -dontwarn rules for test dependencies should go in here: |
| transform.setConfigurationFiles( |
| project.files( |
| TaskInputHelper.bypassFileCallable( |
| testedVariantData.getScope()::getTestProguardFiles), |
| variantScope.getArtifactFileCollection( |
| RUNTIME_CLASSPATH, ALL, PROGUARD_RULES))); |
| |
| // Register the mapping file which may or may not exists (only exist if obfuscation) |
| // is enabled. |
| final VariantScope testedScope = testedVariantData.getScope(); |
| transform.applyTestedMapping( |
| testedScope.hasOutput(APK_MAPPING) ? testedScope.getOutput(APK_MAPPING) : null); |
| } else if (isTestedAppObfuscated(variantScope)) { |
| // This is a test-only module and the app being tested was obfuscated with ProGuard. |
| applyProguardDefaultsForTest(transform); |
| |
| // All -dontwarn rules for test dependencies should go in here: |
| transform.setConfigurationFiles( |
| project.files( |
| TaskInputHelper.bypassFileCallable(variantScope::getTestProguardFiles), |
| variantScope.getArtifactFileCollection( |
| RUNTIME_CLASSPATH, ALL, PROGUARD_RULES))); |
| |
| transform.applyTestedMapping(mappingFileCollection); |
| } else { |
| // This is a "normal" variant in an app/library. |
| applyProguardConfig(transform, variantScope); |
| |
| if (mappingFileCollection != null) { |
| transform.applyTestedMapping(mappingFileCollection); |
| } |
| } |
| |
| Optional<TransformTask> task = |
| variantScope |
| .getTransformManager() |
| .addTransform(taskFactory, variantScope, transform); |
| |
| // FIXME remove once the transform support secondary file as a FileCollection. |
| task.ifPresent( |
| t -> { |
| variantScope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.APK_MAPPING, |
| checkNotNull(transform.getMappingFile()), |
| t.getName()); |
| |
| if (mappingFileCollection != null) { |
| t.dependsOn(mappingFileCollection); |
| } |
| |
| if (testedVariantData != null) { |
| // We need the mapping file for the app code to exist by the time we run. |
| t.dependsOn(testedVariantData.getScope().getAssembleTask()); |
| } |
| }); |
| |
| return task; |
| } |
| |
| private static void applyProguardDefaultsForTest(ProGuardTransform transform) { |
| // Don't remove any code in tested app. |
| transform.setActions(new PostprocessingFeatures(false, true, false)); |
| |
| // 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(); |
| } |
| |
| /** |
| * Checks if {@link ShrinkResourcesTransform} should be added to the build pipeline and either |
| * adds it or registers a {@link SyncIssue} with the reason why it was skipped. |
| */ |
| protected void maybeCreateResourcesShrinkerTransform(@NonNull VariantScope scope) { |
| if (!scope.useResourceShrinker()) { |
| return; |
| } |
| |
| // if resources are shrink, insert a no-op transform per variant output |
| // to transform the res package into a stripped res package |
| File shrinkerOutput = |
| FileUtils.join( |
| globalScope.getIntermediatesDir(), |
| "res_stripped", |
| scope.getVariantConfiguration().getDirName()); |
| |
| ShrinkResourcesTransform shrinkResTransform = |
| new ShrinkResourcesTransform( |
| scope.getVariantData(), |
| scope.getOutput(TaskOutputHolder.TaskOutputType.PROCESSED_RES), |
| shrinkerOutput, |
| AaptGeneration.fromProjectOptions(projectOptions), |
| logger); |
| |
| Optional<TransformTask> shrinkTask = |
| scope.getTransformManager().addTransform(taskFactory, scope, shrinkResTransform); |
| |
| if (shrinkTask.isPresent()) { |
| scope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.SHRUNK_PROCESSED_RES, |
| shrinkerOutput, |
| shrinkTask.get().getName()); |
| } else { |
| androidBuilder |
| .getIssueReporter() |
| .reportError( |
| Type.GENERIC, |
| "Internal error, could not add the ShrinkResourcesTransform"); |
| } |
| } |
| |
| private void applyProguardConfig( |
| ProguardConfigurable transform, |
| VariantScope scope) { |
| GradleVariantConfiguration variantConfig = scope.getVariantConfiguration(); |
| |
| PostprocessingFeatures postprocessingFeatures = scope.getPostprocessingFeatures(); |
| if (postprocessingFeatures != null) { |
| transform.setActions(postprocessingFeatures); |
| } |
| |
| Supplier<Collection<File>> proguardConfigFiles = |
| () -> { |
| Set<File> proguardFiles = Sets.newHashSet(scope.getProguardFiles()); |
| |
| // 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. |
| proguardFiles.add(scope.getProcessAndroidResourcesProguardOutputFile()); |
| return proguardFiles; |
| }; |
| |
| transform.setConfigurationFiles( |
| project.files( |
| TaskInputHelper.bypassFileCallable(proguardConfigFiles), |
| scope.getArtifactFileCollection(RUNTIME_CLASSPATH, ALL, PROGUARD_RULES))); |
| |
| if (scope.getVariantData().getType() == LIBRARY) { |
| transform.keep("class **.R"); |
| transform.keep("class **.R$*"); |
| } |
| |
| if (variantConfig.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(final List<VariantScope> variantScopes) { |
| taskFactory.create( |
| "androidDependencies", |
| DependencyReportTask.class, |
| task -> { |
| task.setDescription("Displays the Android dependencies of the project."); |
| task.setVariants(variantScopes); |
| task.setGroup(ANDROID_GROUP); |
| }); |
| |
| taskFactory.create( |
| "signingReport", |
| SigningReportTask.class, |
| task -> { |
| task.setDescription("Displays the signing info for each variant."); |
| task.setVariants(variantScopes); |
| task.setGroup(ANDROID_GROUP); |
| }); |
| } |
| |
| public void createAnchorTasks(@NonNull VariantScope scope) { |
| createPreBuildTasks(scope); |
| |
| // also create sourceGenTask |
| final BaseVariantData variantData = scope.getVariantData(); |
| scope.setSourceGenTask( |
| taskFactory.create( |
| scope.getTaskName("generate", "Sources"), |
| Task.class, |
| task -> { |
| variantData.sourceGenTask = task; |
| task.dependsOn(PrepareLintJar.NAME); |
| })); |
| // and resGenTask |
| scope.setResourceGenTask( |
| taskFactory.create( |
| scope.getTaskName("generate", "Resources"), |
| Task.class, |
| task -> { |
| variantData.resourceGenTask = task; |
| })); |
| |
| scope.setAssetGenTask( |
| taskFactory.create( |
| scope.getTaskName("generate", "Assets"), |
| Task.class, |
| task -> { |
| variantData.assetGenTask = task; |
| })); |
| |
| if (!variantData.getType().isForTesting() |
| && variantData.getVariantConfiguration().getBuildType().isTestCoverageEnabled()) { |
| scope.setCoverageReportTask( |
| taskFactory.create( |
| 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(scope); |
| } |
| |
| protected DefaultTask createVariantPreBuildTask(@NonNull VariantScope scope) { |
| // default pre-built task. |
| return createDefaultPreBuildTask(scope); |
| } |
| |
| protected DefaultTask createDefaultPreBuildTask(@NonNull VariantScope scope) { |
| return taskFactory.create( |
| scope.getTaskName("pre", "Build"), |
| task -> scope.getVariantData().preBuildTask = task); |
| } |
| |
| private void createPreBuildTasks(@NonNull VariantScope scope) { |
| scope.setPreBuildTask(createVariantPreBuildTask(scope)); |
| |
| scope.getPreBuildTask().dependsOn(MAIN_PREBUILD); |
| |
| if (runJavaCodeShrinker(scope)) { |
| scope.getPreBuildTask().dependsOn(EXTRACT_PROGUARD_FILES); |
| } |
| } |
| |
| private void createCompileAnchorTask(@NonNull final VariantScope scope) { |
| final BaseVariantData variantData = scope.getVariantData(); |
| scope.setCompileTask( |
| taskFactory.create( |
| new TaskConfigAction<Task>() { |
| @NonNull |
| @Override |
| public String getName() { |
| return scope.getTaskName("compile", "Sources"); |
| } |
| |
| @NonNull |
| @Override |
| public Class<Task> getType() { |
| return Task.class; |
| } |
| |
| @Override |
| public void execute(@NonNull Task task) { |
| variantData.compileTask = task; |
| variantData.compileTask.setGroup(BUILD_GROUP); |
| } |
| })); |
| scope.getAssembleTask().dependsOn(scope.getCompileTask()); |
| } |
| |
| public void createCheckManifestTask(@NonNull VariantScope scope) { |
| scope.setCheckManifestTask(taskFactory.create(getCheckManifestConfig(scope))); |
| scope.getCheckManifestTask().dependsOn(scope.getPreBuildTask()); |
| // Does |
| } |
| |
| protected CheckManifest.ConfigAction getCheckManifestConfig(@NonNull VariantScope scope) { |
| return new CheckManifest.ConfigAction(scope, false); |
| } |
| |
| @NonNull |
| protected Logger getLogger() { |
| return logger; |
| } |
| |
| public void addDataBindingDependenciesIfNecessary( |
| DataBindingOptions options, List<VariantScope> variantScopes) { |
| if (!options.isEnabled()) { |
| return; |
| } |
| |
| String version = MoreObjects.firstNonNull(options.getVersion(), |
| dataBindingBuilder.getCompilerVersion()); |
| project.getDependencies() |
| .add( |
| "api", |
| 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() || this instanceof LibraryTaskManager) { |
| project.getDependencies().add("androidTestAnnotationProcessor", |
| SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT + ":" + |
| version); |
| } |
| if (options.getAddDefaultAdapters()) { |
| project.getDependencies() |
| .add( |
| "api", |
| SdkConstants.DATA_BINDING_LIB_ARTIFACT |
| + ":" |
| + dataBindingBuilder.getLibraryVersion(version)); |
| project.getDependencies() |
| .add( |
| "api", |
| SdkConstants.DATA_BINDING_ADAPTER_LIB_ARTIFACT |
| + ":" |
| + dataBindingBuilder.getBaseAdaptersVersion(version)); |
| } |
| project.getPluginManager() |
| .withPlugin( |
| "org.jetbrains.kotlin.kapt", |
| appliedPlugin -> { |
| configureKotlinKaptTasksForDataBinding(project, variantScopes, version); |
| }); |
| } |
| |
| private void configureKotlinKaptTasksForDataBinding( |
| Project project, List<VariantScope> variantScopes, String version) { |
| DependencySet kaptDeps = project.getConfigurations().getByName("kapt").getAllDependencies(); |
| kaptDeps.forEach((Dependency dependency) -> { |
| // if it is a data binding compiler dependency w/ a different version, report error |
| if (Objects.equals(dependency.getGroup() + ":" + dependency.getName(), |
| SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT) |
| && !Objects.equals(dependency.getVersion(), version)) { |
| String depString = dependency.getGroup() |
| + ":" + dependency.getName() |
| + ":" + dependency.getVersion(); |
| androidBuilder.getIssueReporter().reportError(Type.GENERIC, |
| "Data Binding annotation processor version needs to match the" + |
| " Android Gradle Plugin version. You can remove the kapt" + |
| " dependency " + depString + |
| " and Android Gradle Plugin will inject" + |
| " the right version."); |
| } |
| }); |
| project.getDependencies() |
| .add( |
| "kapt", |
| SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT + ":" + version); |
| Class<? extends Task> kaptTaskClass = null; |
| try { |
| //noinspection unchecked |
| kaptTaskClass = |
| (Class<? extends Task>) |
| Class.forName("org.jetbrains.kotlin.gradle.internal.KaptTask"); |
| } catch (ClassNotFoundException e) { |
| logger.error( |
| "Kotlin plugin is applied to the project " |
| + project.getPath() |
| + " but we cannot find the KaptTask. Make sure you apply the" |
| + " kotlin-kapt plugin because it is necessary to use kotlin" |
| + " with data binding."); |
| } |
| if (kaptTaskClass == null) { |
| return; |
| } |
| // create a map from kapt task name to variant scope |
| Map<String, VariantScope> kaptTaskLookup = |
| variantScopes |
| .stream() |
| .collect( |
| Collectors.toMap( |
| variantScope -> |
| variantScope |
| .getVariantData() |
| .getTaskName("kapt", "Kotlin"), |
| variantScope -> variantScope)); |
| project.getTasks() |
| .withType( |
| kaptTaskClass, |
| (Action<Task>) |
| kaptTask -> { |
| // find matching scope. |
| VariantScope matchingScope = |
| kaptTaskLookup.get(kaptTask.getName()); |
| if (matchingScope != null) { |
| configureKaptTaskInScope(matchingScope, kaptTask); |
| } |
| }); |
| } |
| |
| private static void configureKaptTaskInScope(VariantScope scope, Task kaptTask) { |
| if (scope.hasOutput(DATA_BINDING_DEPENDENCY_ARTIFACTS)) { |
| // if data binding is enabled and this variant has merged dependency artifacts, then |
| // make the compilation task depend on them. (test variants don't do the merge so they |
| // could not have the artifacts) |
| kaptTask.getInputs() |
| .files(scope.getOutput(DATA_BINDING_DEPENDENCY_ARTIFACTS)) |
| .withPathSensitivity(PathSensitivity.RELATIVE) |
| .withPropertyName("dataBindingDependencyArtifacts"); |
| } |
| if (scope.hasOutput(TaskOutputHolder.TaskOutputType.DATA_BINDING_BASE_CLASS_LOG_ARTIFACT)) { |
| kaptTask.getInputs() |
| .files( |
| scope.getOutput( |
| TaskOutputHolder.TaskOutputType |
| .DATA_BINDING_BASE_CLASS_LOG_ARTIFACT)) |
| .withPathSensitivity(PathSensitivity.RELATIVE) |
| .withPropertyName("dataBindingClassLogDir"); |
| } |
| // the data binding artifact is created by the annotation processor, so we register this |
| // task output (which also publishes it) with javac as the generating task. |
| kaptTask.getOutputs() |
| .files(scope.getBundleArtifactFolderForDataBinding()) |
| .withPropertyName("dataBindingArtifactOutputDir"); |
| if (!scope.hasOutput(TaskOutputHolder.TaskOutputType.DATA_BINDING_ARTIFACT)) { |
| scope.addTaskOutput( |
| TaskOutputHolder.TaskOutputType.DATA_BINDING_ARTIFACT, |
| scope.getBundleArtifactFolderForDataBinding(), |
| kaptTask.getName()); |
| } |
| } |
| |
| protected void configureTestData(AbstractTestDataImpl testData) { |
| testData.setAnimationsDisabled(extension.getTestOptions().getAnimationsDisabled()); |
| } |
| } |