blob: 6a23e617177799f670263c14496a15cbc67282c6 [file] [log] [blame]
/*
* 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 android.databinding.tool.DataBindingBuilder
import com.android.SdkConstants
import com.android.SdkConstants.DATA_BINDING_KTX_LIB_ARTIFACT
import com.android.SdkConstants.DOT_JAR
import com.android.build.api.artifact.Artifact.Single
import com.android.build.api.artifact.MultipleArtifact
import com.android.build.api.artifact.SingleArtifact
import com.android.build.api.dsl.DataBinding
import com.android.build.api.dsl.DeviceGroup
import com.android.build.api.instrumentation.FramesComputationMode
import com.android.build.api.artifact.ScopedArtifact
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.variant.ScopedArtifacts
import com.android.build.api.variant.VariantBuilder
import com.android.build.api.variant.impl.TaskProviderBasedDirectoryEntryImpl
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.internal.attribution.CheckJetifierBuildService
import com.android.build.gradle.internal.component.AndroidTestCreationConfig
import com.android.build.gradle.internal.component.ApkCreationConfig
import com.android.build.gradle.internal.component.ApplicationCreationConfig
import com.android.build.gradle.internal.component.ComponentCreationConfig
import com.android.build.gradle.internal.component.ConsumableCreationConfig
import com.android.build.gradle.internal.component.InstrumentedTestCreationConfig
import com.android.build.gradle.internal.component.NestedComponentCreationConfig
import com.android.build.gradle.internal.component.TestComponentCreationConfig
import com.android.build.gradle.internal.component.TestCreationConfig
import com.android.build.gradle.internal.component.TestFixturesCreationConfig
import com.android.build.gradle.internal.component.UnitTestCreationConfig
import com.android.build.gradle.internal.component.VariantCreationConfig
import com.android.build.gradle.internal.coverage.JacocoConfigurations
import com.android.build.gradle.internal.coverage.JacocoPropertiesTask
import com.android.build.gradle.internal.coverage.JacocoReportTask
import com.android.build.gradle.internal.cxx.configure.createCxxTasks
import com.android.build.gradle.internal.dependency.AndroidAttributes
import com.android.build.gradle.internal.dependency.AndroidXDependencySubstitution.androidXMappings
import com.android.build.gradle.internal.dependency.ConfigurationVariantMapping
import com.android.build.gradle.internal.dependency.VariantDependencies
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import com.android.build.gradle.internal.dsl.DataBindingOptions
import com.android.build.gradle.internal.dsl.ManagedVirtualDevice
import com.android.build.gradle.internal.ide.dependencies.MavenCoordinatesCacheBuildService
import com.android.build.gradle.internal.lint.LintTaskManager
import com.android.build.gradle.internal.packaging.getDefaultDebugKeystoreLocation
import com.android.build.gradle.internal.pipeline.OriginalStream
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.build.gradle.internal.pipeline.TransformTask
import com.android.build.gradle.internal.profile.AnalyticsConfiguratorService
import com.android.build.gradle.internal.publishing.AndroidArtifacts
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactScope
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType
import com.android.build.gradle.internal.publishing.AndroidArtifacts.PublishedConfigType
import com.android.build.gradle.internal.publishing.PublishedConfigSpec
import com.android.build.gradle.internal.res.GenerateApiPublicTxtTask
import com.android.build.gradle.internal.res.GenerateEmptyResourceFilesTask
import com.android.build.gradle.internal.res.GenerateLibraryRFileTask
import com.android.build.gradle.internal.res.GenerateLibraryRFileTask.TestRuntimeStubRClassCreationAction
import com.android.build.gradle.internal.res.LinkAndroidResForBundleTask
import com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask
import com.android.build.gradle.internal.res.ParseLibraryResourcesTask
import com.android.build.gradle.internal.res.namespaced.NamespacedResourcesTaskManager
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.scope.InternalArtifactType.COMPILE_AND_RUNTIME_NOT_NAMESPACED_R_CLASS_JAR
import com.android.build.gradle.internal.scope.InternalArtifactType.FEATURE_DEX
import com.android.build.gradle.internal.scope.InternalArtifactType.FEATURE_RESOURCE_PKG
import com.android.build.gradle.internal.scope.InternalArtifactType.JACOCO_INSTRUMENTED_CLASSES
import com.android.build.gradle.internal.scope.InternalArtifactType.JACOCO_INSTRUMENTED_JARS
import com.android.build.gradle.internal.scope.InternalArtifactType.JAVAC
import com.android.build.gradle.internal.scope.InternalArtifactType.JAVA_RES
import com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_JAVA_RES
import com.android.build.gradle.internal.scope.InternalArtifactType.MERGED_RES
import com.android.build.gradle.internal.scope.InternalArtifactType.PACKAGED_RES
import com.android.build.gradle.internal.scope.InternalArtifactType.PROCESSED_RES
import com.android.build.gradle.internal.scope.InternalArtifactType.RUNTIME_R_CLASS_CLASSES
import com.android.build.gradle.internal.scope.InternalMultipleArtifactType
import com.android.build.gradle.internal.scope.Java8LangSupport
import com.android.build.gradle.internal.scope.getDirectories
import com.android.build.gradle.internal.scope.getRegularFiles
import com.android.build.gradle.internal.scope.publishArtifactToConfiguration
import com.android.build.gradle.internal.services.AndroidLocationsBuildService
import com.android.build.gradle.internal.services.R8ParallelBuildService
import com.android.build.gradle.internal.services.getBuildService
import com.android.build.gradle.internal.tasks.AarMetadataTask
import com.android.build.gradle.internal.tasks.AndroidReportTask
import com.android.build.gradle.internal.tasks.AndroidVariantTask
import com.android.build.gradle.internal.tasks.BundleLibraryClassesDir
import com.android.build.gradle.internal.tasks.BundleLibraryClassesJar
import com.android.build.gradle.internal.tasks.BundleLibraryJavaRes
import com.android.build.gradle.internal.tasks.CheckAarMetadataTask
import com.android.build.gradle.internal.tasks.CheckDuplicateClassesTask
import com.android.build.gradle.internal.tasks.CheckJetifierTask
import com.android.build.gradle.internal.tasks.CheckProguardFiles
import com.android.build.gradle.internal.tasks.ClassesClasspathUtils
import com.android.build.gradle.internal.tasks.CompressAssetsTask
import com.android.build.gradle.internal.tasks.D8BundleMainDexListTask
import com.android.build.gradle.internal.tasks.DependencyReportTask
import com.android.build.gradle.internal.tasks.DesugarLibKeepRulesMergeTask
import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask
import com.android.build.gradle.internal.tasks.DeviceSerialTestTask
import com.android.build.gradle.internal.tasks.DexArchiveBuilderTask
import com.android.build.gradle.internal.tasks.DexFileDependenciesTask
import com.android.build.gradle.internal.tasks.DexMergingAction
import com.android.build.gradle.internal.tasks.DexMergingTask
import com.android.build.gradle.internal.tasks.ExtractProguardFiles
import com.android.build.gradle.internal.tasks.FeatureDexMergeTask
import com.android.build.gradle.internal.tasks.GenerateLibraryProguardRulesTask
import com.android.build.gradle.internal.tasks.InstallVariantTask
import com.android.build.gradle.internal.tasks.JacocoTask
import com.android.build.gradle.internal.tasks.L8DexDesugarLibTask
import com.android.build.gradle.internal.tasks.LibraryAarJarsTask
import com.android.build.gradle.internal.tasks.LintCompile
import com.android.build.gradle.internal.tasks.ListingFileRedirectTask
import com.android.build.gradle.internal.tasks.ManagedDeviceCleanTask
import com.android.build.gradle.internal.tasks.ManagedDeviceInstrumentationTestResultAggregationTask
import com.android.build.gradle.internal.tasks.ManagedDeviceInstrumentationTestTask
import com.android.build.gradle.internal.tasks.ManagedDeviceSetupTask
import com.android.build.gradle.internal.tasks.MergeAaptProguardFilesCreationAction
import com.android.build.gradle.internal.tasks.MergeClassesTask
import com.android.build.gradle.internal.tasks.MergeGeneratedProguardFilesCreationAction
import com.android.build.gradle.internal.tasks.MergeJavaResourceTask
import com.android.build.gradle.internal.tasks.MergeNativeLibsTask
import com.android.build.gradle.internal.tasks.OptimizeResourcesTask
import com.android.build.gradle.internal.tasks.PackageForUnitTest
import com.android.build.gradle.internal.tasks.PrepareLintJarForPublish
import com.android.build.gradle.internal.tasks.ProcessJavaResTask
import com.android.build.gradle.internal.tasks.R8Task
import com.android.build.gradle.internal.tasks.RecalculateStackFramesTask
import com.android.build.gradle.internal.tasks.ShrinkResourcesOldShrinkerTask
import com.android.build.gradle.internal.tasks.SigningConfigVersionsWriterTask
import com.android.build.gradle.internal.tasks.SigningConfigWriterTask
import com.android.build.gradle.internal.tasks.SigningReportTask
import com.android.build.gradle.internal.tasks.SourceSetsTask
import com.android.build.gradle.internal.tasks.TestServerTask.TestServerTaskCreationAction
import com.android.build.gradle.internal.tasks.UninstallTask
import com.android.build.gradle.internal.tasks.ValidateSigningTask
import com.android.build.gradle.internal.tasks.databinding.DataBindingCompilerArguments.Companion.createArguments
import com.android.build.gradle.internal.tasks.databinding.DataBindingGenBaseClassesTask
import com.android.build.gradle.internal.tasks.databinding.DataBindingMergeDependencyArtifactsTask
import com.android.build.gradle.internal.tasks.databinding.DataBindingTriggerTask
import com.android.build.gradle.internal.tasks.databinding.KAPT_FIX_KOTLIN_VERSION
import com.android.build.gradle.internal.tasks.databinding.MergeRFilesForDataBindingTask
import com.android.build.gradle.internal.tasks.factory.GlobalTaskCreationConfig
import com.android.build.gradle.internal.tasks.factory.TaskConfigAction
import com.android.build.gradle.internal.tasks.factory.TaskFactory
import com.android.build.gradle.internal.tasks.factory.TaskFactoryImpl
import com.android.build.gradle.internal.tasks.factory.TaskManagerConfig
import com.android.build.gradle.internal.tasks.factory.TaskProviderCallback
import com.android.build.gradle.internal.tasks.factory.VariantTaskCreationAction
import com.android.build.gradle.internal.tasks.factory.dependsOn
import com.android.build.gradle.internal.tasks.featuresplit.getFeatureName
import com.android.build.gradle.internal.tasks.mlkit.GenerateMlModelClass
import com.android.build.gradle.internal.test.AbstractTestDataImpl
import com.android.build.gradle.internal.test.BundleTestDataImpl
import com.android.build.gradle.internal.test.TestDataImpl
import com.android.build.gradle.internal.testing.utp.TEST_RESULT_PB_FILE_NAME
import com.android.build.gradle.internal.testing.utp.shouldEnableUtp
import com.android.build.gradle.internal.transforms.LegacyShrinkBundleModuleResourcesTask
import com.android.build.gradle.internal.transforms.ShrinkAppBundleResourcesTask
import com.android.build.gradle.internal.transforms.ShrinkResourcesNewShrinkerTask
import com.android.build.gradle.internal.utils.KOTLIN_KAPT_PLUGIN_ID
import com.android.build.gradle.internal.utils.addComposeArgsToKotlinCompile
import com.android.build.gradle.internal.utils.configureKotlinCompileForProject
import com.android.build.gradle.internal.utils.getProjectKotlinPluginKotlinVersion
import com.android.build.gradle.internal.utils.isKotlinKaptPluginApplied
import com.android.build.gradle.internal.utils.isKotlinPluginApplied
import com.android.build.gradle.internal.utils.isKspPluginApplied
import com.android.build.gradle.internal.utils.recordIrBackendForAnalytics
import com.android.build.gradle.internal.utils.setDisallowChanges
import com.android.build.gradle.internal.variant.ApkVariantData
import com.android.build.gradle.internal.variant.ComponentInfo
import com.android.build.gradle.internal.variant.VariantModel
import com.android.build.gradle.options.BooleanOption
import com.android.build.gradle.options.IntegerOption
import com.android.build.gradle.tasks.AidlCompile
import com.android.build.gradle.tasks.AnalyzeDependenciesTask
import com.android.build.gradle.tasks.BundleAar
import com.android.build.gradle.tasks.CompatibleScreensManifest
import com.android.build.gradle.tasks.CompileLibraryResourcesTask
import com.android.build.gradle.tasks.ExtractAnnotations
import com.android.build.gradle.tasks.ExtractDeepLinksTask
import com.android.build.gradle.tasks.GenerateBuildConfig
import com.android.build.gradle.tasks.GenerateManifestJarTask
import com.android.build.gradle.tasks.GenerateResValues
import com.android.build.gradle.tasks.GenerateTestConfig
import com.android.build.gradle.tasks.GenerateTestConfig.TestConfigInputs
import com.android.build.gradle.tasks.JavaCompileCreationAction
import com.android.build.gradle.tasks.JavaPreCompileTask
import com.android.build.gradle.tasks.ManifestProcessorTask
import com.android.build.gradle.tasks.MapSourceSetPathsTask
import com.android.build.gradle.tasks.MergeResources
import com.android.build.gradle.tasks.MergeSourceSetFolders
import com.android.build.gradle.tasks.MergeSourceSetFolders.LibraryAssetCreationAction
import com.android.build.gradle.tasks.MergeSourceSetFolders.MergeMlModelsSourceFoldersCreationAction
import com.android.build.gradle.tasks.MergeSourceSetFolders.MergeShaderSourceFoldersCreationAction
import com.android.build.gradle.tasks.PackageApplication
import com.android.build.gradle.tasks.ProcessApplicationManifest
import com.android.build.gradle.tasks.ProcessLibraryManifest
import com.android.build.gradle.tasks.ProcessManifestForBundleTask
import com.android.build.gradle.tasks.ProcessManifestForInstantAppTask
import com.android.build.gradle.tasks.ProcessManifestForMetadataFeatureTask
import com.android.build.gradle.tasks.ProcessMultiApkApplicationManifest
import com.android.build.gradle.tasks.ProcessPackagedManifestTask
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.TransformClassesWithAsmTask
import com.android.build.gradle.tasks.VerifyLibraryResourcesTask
import com.android.build.gradle.tasks.ZipMergingTask
import com.android.build.gradle.tasks.factory.AndroidUnitTest
import com.android.build.gradle.tasks.registerDataBindingOutputs
import com.android.builder.core.BuilderConstants
import com.android.builder.core.ComponentType
import com.android.builder.dexing.DexingType
import com.android.builder.dexing.isLegacyMultiDexMode
import com.android.builder.errors.IssueReporter
import com.android.utils.appendCapitalized
import com.android.utils.usLocaleCapitalize
import com.google.common.base.MoreObjects
import com.google.common.base.Preconditions
import com.google.common.base.Strings
import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.ImmutableSet
import com.google.common.collect.ListMultimap
import com.google.common.collect.Sets
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.UnknownTaskException
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ResolutionStrategy
import org.gradle.api.component.AdhocComponentWithVariants
import org.gradle.api.execution.TaskExecutionGraph
import org.gradle.api.file.Directory
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFile
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.provider.Provider
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.PathSensitivity
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.testing.jacoco.plugins.JacocoPlugin
import java.io.File
import java.util.concurrent.Callable
import java.util.function.Consumer
import java.util.stream.Collectors
/**
* Manages tasks creation
*
* @param variants these are all the variants
* @param testComponents these are all the test components
* @param extension the extension
*/
abstract class TaskManager<VariantBuilderT : VariantBuilder, VariantT : VariantCreationConfig>(
@JvmField protected val project: Project,
private val variants: Collection<ComponentInfo<VariantBuilderT, VariantT>>,
private val testComponents: Collection<TestComponentCreationConfig>,
private val testFixturesComponents: Collection<TestFixturesCreationConfig>,
@JvmField protected val globalConfig: GlobalTaskCreationConfig,
@JvmField protected val localConfig: TaskManagerConfig,
@JvmField protected val extension: BaseExtension,
) {
protected val logger: Logger = Logging.getLogger(this.javaClass)
@JvmField
protected val taskFactory: TaskFactory = TaskFactoryImpl(project.tasks)
protected val lintTaskManager: LintTaskManager = LintTaskManager(globalConfig, taskFactory, project)
@JvmField
protected val variantPropertiesList: List<VariantT> =
variants.map(ComponentInfo<VariantBuilderT, VariantT>::variant)
private val nestedComponents: List<NestedComponentCreationConfig> =
testComponents + testFixturesComponents
private val allPropertiesList: List<ComponentCreationConfig> =
variantPropertiesList + nestedComponents
/**
* This is the main entry point into the task manager
*
*
* This creates the tasks for all the variants and all the test components
*/
fun createTasks(
componentType: ComponentType, variantModel: VariantModel) {
// this is called before all the variants are created since they are all going to depend
// on the global LINT_PUBLISH_JAR task output
// 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
createPrepareLintJarForPublishTask()
// create a lifecycle task to build the lintChecks dependencies
taskFactory.register(COMPILE_LINT_CHECKS_TASK) { task: Task ->
task.dependsOn(globalConfig.localCustomLintChecks)
}
// Create top level test tasks.
createTopLevelTestTasks()
// Create tasks to manage test devices.
createTestDevicesTasks()
// Create tasks for all variants (main, testFixtures and tests)
for (variant in variants) {
createTasksForVariant(variant)
}
for (testFixturesComponent in testFixturesComponents) {
createTasksForTestFixtures(testFixturesComponent)
}
for (testComponent in testComponents) {
createTasksForTest(testComponent)
}
createTopLevelTasks(componentType, variantModel)
}
open fun createTopLevelTasks(componentType: ComponentType, variantModel: VariantModel) {
lintTaskManager.createLintTasks(
componentType,
variantModel,
variantPropertiesList,
testComponents
)
createReportTasks()
// Create C/C++ configuration, build, and clean tasks
val androidLocationBuildService: Provider<AndroidLocationsBuildService> =
getBuildService(project.gradle.sharedServices)
createCxxTasks(
androidLocationBuildService.get(),
getBuildService(
globalConfig.services.buildServiceRegistry,
SdkComponentsBuildService::class.java
).get(),
globalConfig.services.issueReporter,
taskFactory,
globalConfig.services.projectOptions,
variants,
project.providers,
project.layout
)
// Global tasks required for privacy sandbox sdk consumption
if (globalConfig.services.projectOptions.get(BooleanOption.PRIVACY_SANDBOX_SDK_SUPPORT)) {
taskFactory.register(ValidateSigningTask.PrivacySandboxSdkCreationAction(globalConfig))
}
}
fun createPostApiTasks() {
// must run this after scopes are created so that we can configure kotlin
// kapt tasks
addBindingDependenciesIfNecessary(globalConfig.dataBinding)
// configure Kotlin compilation if needed.
configureKotlinPluginTasksIfNecessary()
createAnchorAssembleTasks(
globalConfig.productFlavorCount,
globalConfig.productFlavorDimensionCount)
}
/**
* Create tasks for the specified variant.
*
*
* This creates tasks common to all variant types.
*/
private fun createTasksForVariant(
variant: ComponentInfo<VariantBuilderT, VariantT>,
) {
val variantProperties = variant.variant
val componentType = variantProperties.componentType
val variantDependencies = variantProperties.variantDependencies
if (variantProperties.dexingType.isLegacyMultiDexMode()
&& variantProperties.componentType.isApk) {
val multiDexDependency =
if (variantProperties
.services
.projectOptions[BooleanOption.USE_ANDROID_X])
ANDROIDX_MULTIDEX_MULTIDEX
else COM_ANDROID_SUPPORT_MULTIDEX
project.dependencies
.add(variantDependencies.compileClasspath.name, multiDexDependency)
project.dependencies
.add(variantDependencies.runtimeClasspath.name, multiDexDependency)
}
if (variantProperties.renderscriptCreationConfig?.renderscript?.supportModeEnabled?.get()
== true) {
val fileCollection = project.files(
globalConfig.versionedSdkLoader.flatMap {
it.renderScriptSupportJarProvider
}
)
project.dependencies.add(variantDependencies.compileClasspath.name, fileCollection)
if (componentType.isApk && !componentType.isForTesting) {
project.dependencies.add(variantDependencies.runtimeClasspath.name, fileCollection)
}
}
createAssembleTask(variantProperties)
if (variantProperties.services.projectOptions.get(BooleanOption.IDE_INVOKED_FROM_IDE)) {
variantProperties.taskContainer.assembleTask.configure {
it.dependsOn(variantProperties.artifacts.get(InternalArtifactType.VARIANT_MODEL))
}
}
if (componentType.isBaseModule) {
createBundleTask(variantProperties)
}
doCreateTasksForVariant(variant)
}
/**
* Entry point for each specialized TaskManager to create the tasks for a given VariantT
*
* @param variantInfo the variantInfo for which to create the tasks
*/
protected abstract fun doCreateTasksForVariant(
variantInfo: ComponentInfo<VariantBuilderT, VariantT>)
/** Create tasks for the specified test fixtures component. */
private fun createTasksForTestFixtures(testFixturesComponent: TestFixturesCreationConfig) {
createAssembleTask(testFixturesComponent)
createAnchorTasks(testFixturesComponent)
// Create all current streams (dependencies mostly at this point)
createDependencyStreams(testFixturesComponent)
// java resources tasks
createProcessJavaResTask(testFixturesComponent)
// android resources tasks
if (testFixturesComponent.buildFeatures.androidResources) {
taskFactory.register(ExtractDeepLinksTask.CreationAction(testFixturesComponent))
taskFactory.register(ExtractDeepLinksTask.AarCreationAction(testFixturesComponent))
createGenerateResValuesTask(testFixturesComponent)
val flags: Set<MergeResources.Flag> =
if (globalConfig.namespacedAndroidResources) {
Sets.immutableEnumSet(
MergeResources.Flag.REMOVE_RESOURCE_NAMESPACES,
MergeResources.Flag.PROCESS_VECTOR_DRAWABLES
)
} else {
Sets.immutableEnumSet(MergeResources.Flag.PROCESS_VECTOR_DRAWABLES)
}
// Create a merge task to only merge the resources from this library and not
// the dependencies. This is what gets packaged in the aar.
basicCreateMergeResourcesTask(
testFixturesComponent,
MergeType.PACKAGE,
includeDependencies = false,
processResources = false,
alsoOutputNotCompiledResources = false,
flags = flags,
taskProviderCallback = object: TaskProviderCallback<MergeResources> {
override fun handleProvider(taskProvider: TaskProvider<MergeResources>) {
testFixturesComponent.artifacts
.setInitialProvider<RegularFile, MergeResources>(taskProvider)
{ obj: MergeResources -> obj.publicFile }
.withName(SdkConstants.FN_PUBLIC_TXT)
.on(InternalArtifactType.PUBLIC_RES)
}
}
)
// This task merges all the resources, including the dependencies of this library.
// This should be unused, except that external libraries might consume it.
// Also used by the VerifyLibraryResourcesTask (only ran in release builds).
createMergeResourcesTask(
testFixturesComponent,
processResources = false,
flags = ImmutableSet.of()
)
// Task to generate the public.txt for the API that always exists
// Unlike the internal one which is packaged in the AAR which only exists if the
// developer has explicitly marked resources as public.
taskFactory.register(GenerateApiPublicTxtTask.CreationAction(testFixturesComponent))
taskFactory.register(CompileLibraryResourcesTask.CreationAction(testFixturesComponent))
// Add a task to generate resource source files, directing the location
// of the r.txt file to be directly in the bundle.
createProcessResTask(
testFixturesComponent,
null,
MergeType.PACKAGE,
testFixturesComponent.services.projectInfo.getProjectBaseName()
)
// Only verify resources if in Release and not namespaced.
if (!testFixturesComponent.debuggable &&
!globalConfig.namespacedAndroidResources) {
createVerifyLibraryResTask(testFixturesComponent)
}
registerLibraryRClassTransformStream(testFixturesComponent)
} else {
// Create a task to generate empty/mock required resource artifacts.
taskFactory.register(GenerateEmptyResourceFilesTask.CreateAction(testFixturesComponent))
}
// Add a task to merge or generate the manifest
taskFactory.register(
ProcessLibraryManifest.CreationAction(
testFixturesComponent,
targetSdkVersion = null,
maxSdkVersion = null,
manifestPlaceholders = null
)
)
// Add tasks to merge the assets folders
createMergeAssetsTask(testFixturesComponent)
taskFactory.register(LibraryAssetCreationAction(testFixturesComponent))
// compilation tasks
// Add data binding tasks if enabled
createDataBindingTasksIfNecessary(testFixturesComponent)
// Add a task to auto-generate classes for ML model files.
createMlkitTask(testFixturesComponent)
val javacTask = createJavacTask(testFixturesComponent)
addJavacClassesStream(testFixturesComponent)
setJavaCompilerTask(javacTask, testFixturesComponent)
// Some versions of retrolambda remove the actions from the extract annotations task.
// TODO: remove this hack once tests are moved to a version that doesn't do this
// b/37564303
if (testFixturesComponent
.services
.projectOptions
.get(BooleanOption.ENABLE_EXTRACT_ANNOTATIONS)) {
taskFactory.register(ExtractAnnotations.CreationAction(testFixturesComponent))
}
maybeCreateTransformClassesWithAsmTask(testFixturesComponent)
// packaging tasks
// Create jar with library classes used for publishing to runtime elements.
taskFactory.register(
BundleLibraryClassesJar.CreationAction(
testFixturesComponent, PublishedConfigType.RUNTIME_ELEMENTS
)
)
// Also create a directory containing the same classes for incremental dexing
taskFactory.register(BundleLibraryClassesDir.CreationAction(testFixturesComponent))
taskFactory.register(BundleLibraryJavaRes.CreationAction(testFixturesComponent))
// Add a task to create the AAR metadata file
taskFactory.register(AarMetadataTask.CreationAction(testFixturesComponent))
// Add a task to write the local lint AAR file
taskFactory.register(BundleAar.TestFixturesLocalLintCreationAction(testFixturesComponent))
// Create a jar with both classes and java resources. This artifact is not
// used by the Android application plugin and the task usually don't need to
// be executed. The artifact is useful for other Gradle users who needs the
// 'jar' artifact as API dependency.
taskFactory.register(ZipMergingTask.CreationAction(testFixturesComponent))
// now add a task that will take all the classes and java resources and package them
// into the main and secondary jar files that goes in the AAR.
// This is used for building the AAR.
taskFactory.register(
LibraryAarJarsTask.CreationAction(
testFixturesComponent,
minifyEnabled = false
)
)
createBundleTaskForTestFixtures(testFixturesComponent)
// This hides the assemble test fixtures task from the task list.
testFixturesComponent.taskContainer.assembleTask.configure { task: Task ->
task.group = null
}
}
private fun createBundleTaskForTestFixtures(testFixturesComponent: TestFixturesCreationConfig) {
taskFactory.register(BundleAar.TestFixturesCreationAction(testFixturesComponent))
testFixturesComponent.taskContainer
.assembleTask
.configure { task: Task ->
task.dependsOn(
testFixturesComponent.artifacts.get(
SingleArtifact.AAR
)
)
}
// publish testFixtures for libraries only
if (this !is LibraryTaskManager) {
return
}
val variantDependencies = testFixturesComponent.variantDependencies
testFixturesComponent.publishInfo?.components?.forEach {
val componentName = it.componentName
val component = project.components.findByName(componentName) as AdhocComponentWithVariants? ?:
localConfig.componentFactory.adhoc(componentName).let { project.components.add(it) } as AdhocComponentWithVariants
val apiPub = variantDependencies.getElements(PublishedConfigSpec(PublishedConfigType.API_PUBLICATION, it))
val runtimePub = variantDependencies.getElements(PublishedConfigSpec(PublishedConfigType.RUNTIME_PUBLICATION, it))
component.addVariantsFromConfiguration(
apiPub, ConfigurationVariantMapping("compile", it.isClassifierRequired)
)
component.addVariantsFromConfiguration(
runtimePub, ConfigurationVariantMapping("runtime", it.isClassifierRequired)
)
}
}
protected fun createVerifyLibraryResTask(component: ComponentCreationConfig) {
taskFactory.register(VerifyLibraryResourcesTask.CreationAction(component))
component.taskContainer
.assembleTask
.configure { task: Task ->
task.dependsOn(
component.artifacts.get(
InternalArtifactType.VERIFIED_LIBRARY_RESOURCES
)
)
}
}
@Suppress("DEPRECATION") // Legacy support
protected fun registerLibraryRClassTransformStream(component: ComponentCreationConfig) {
if (!component.buildFeatures.androidResources) {
return
}
val compileRClass: FileCollection = project.files(
component.artifacts
.get(InternalArtifactType.COMPILE_R_CLASS_JAR)
)
component.transformManager
.addStream(
OriginalStream.builder("compile-only-r-class")
.addContentTypes(TransformManager.CONTENT_CLASS)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.PROVIDED_ONLY)
.setFileCollection(compileRClass)
.build()
)
}
/** Create tasks for the specified variant. */
private fun createTasksForTest(testVariant: TestComponentCreationConfig) {
createAssembleTask(testVariant)
val testedVariant = testVariant.mainVariant
val variantDependencies = testVariant.variantDependencies
if (testedVariant?.renderscriptCreationConfig?.renderscript?.supportModeEnabled?.get()
== true) {
project.dependencies
.add(
variantDependencies.compileClasspath.name,
project.files(
globalConfig.versionedSdkLoader.flatMap {
it.renderScriptSupportJarProvider
}
))
}
if (testVariant.componentType.isApk) { // ANDROID_TEST
if ((testVariant as ApkCreationConfig).dexingType.isLegacyMultiDexMode()) {
val multiDexInstrumentationDep = if (testVariant
.services
.projectOptions[BooleanOption.USE_ANDROID_X])
ANDROIDX_MULTIDEX_MULTIDEX_INSTRUMENTATION
else COM_ANDROID_SUPPORT_MULTIDEX_INSTRUMENTATION
project.dependencies
.add(
variantDependencies.compileClasspath.name,
multiDexInstrumentationDep)
project.dependencies
.add(
variantDependencies.runtimeClasspath.name,
multiDexInstrumentationDep)
}
createAndroidTestVariantTasks(testVariant as AndroidTestCreationConfig)
} else {
// UNIT_TEST
createUnitTestVariantTasks(testVariant as UnitTestCreationConfig)
}
}
protected open fun createPrepareLintJarForPublishTask() {
taskFactory.register(PrepareLintJarForPublish.CreationAction(globalConfig))
}
private fun configureKotlinPluginTasksIfNecessary() {
if (!isKotlinPluginApplied(project)) {
return
}
val composeIsEnabled = allPropertiesList
.any { componentProperties: ComponentCreationConfig ->
componentProperties.buildFeatures.compose }
recordIrBackendForAnalytics(
allPropertiesList, extension, project, composeIsEnabled)
if (!composeIsEnabled) {
return
}
// any override coming from the DSL.
val kotlinCompilerExtensionVersionInDsl =
globalConfig.composeOptions.kotlinCompilerExtensionVersion
val useLiveLiterals = globalConfig.composeOptions.useLiveLiterals
// record in our metrics that compose is enabled.
getBuildService(
project.gradle.sharedServices, AnalyticsConfiguratorService::class.java)
.get()
.getProjectBuilder(project.path)?.composeEnabled = true
// Create a project configuration that holds the androidx compose kotlin
// compiler extension
val kotlinExtension = project.configurations.create("kotlin-extension")
project.dependencies
.add(
kotlinExtension.name, "androidx.compose.compiler:compiler:"
+ (kotlinCompilerExtensionVersionInDsl
?: COMPOSE_KOTLIN_COMPILER_EXTENSION_VERSION))
kotlinExtension.isTransitive = false
kotlinExtension.description = "Configuration for Compose related kotlin compiler extension"
// add compose args to all kotlin compile tasks
for (creationConfig in allPropertiesList) {
try {
configureKotlinCompileForProject(project, creationConfig) {
addComposeArgsToKotlinCompile(
it, creationConfig, project.files(kotlinExtension), useLiveLiterals
)
}
} catch (e: UnknownTaskException) {
// ignore
}
}
}
// This is for config attribute debugging
open class ConfigAttrTask : DefaultTask() {
@get:Internal
var consumable = false
@get:Internal
var resolvable = false
@TaskAction
fun run() {
for (config in project.configurations) {
val attributes = config.attributes
if (consumable && config.isCanBeConsumed
|| resolvable && config.isCanBeResolved) {
println(config.name)
println("\tcanBeResolved: " + config.isCanBeResolved)
println("\tcanBeConsumed: " + config.isCanBeConsumed)
for (attr in attributes.keySet()) {
println(
"\t" + attr.name + ": " + attributes.getAttribute(attr))
}
if (consumable && config.isCanBeConsumed) {
for (artifact in config.artifacts) {
println("\tArtifact: " + artifact.name + " (" + artifact.file.name + ")")
}
for (cv in config.outgoing.variants) {
println("\tConfigurationVariant: " + cv.name)
for (pa in cv.artifacts) {
println("\t\tArtifact: " + pa.file)
println("\t\tType:" + pa.type)
}
}
}
}
}
}
}
private fun createMockableJarTask() {
project.dependencies
.add(
VariantDependencies.CONFIG_NAME_ANDROID_APIS,
project.files(
Callable {
globalConfig.versionedSdkLoader.flatMap {
it.androidJarProvider
}.orNull
} as Callable<*>))
// Adding this task to help the IDE find the mockable JAR.
taskFactory.register(
CREATE_MOCKABLE_JAR_TASK_NAME
) { task: Task ->
task.dependsOn(globalConfig.mockableJarArtifact)
}
}
@Suppress("DEPRECATION") // Legacy support
protected open fun createDependencyStreams(creationConfig: ComponentCreationConfig) {
// Since it's going to chance the configurations, we need to do it before
// we start doing queries to fill the streams.
handleJacocoDependencies(creationConfig)
creationConfig.instrumentationCreationConfig?.configureAndLockAsmClassesVisitors(
project.objects
)
val transformManager = creationConfig.transformManager
fun getFinalRuntimeClassesJarsFromComponent(
component: ComponentCreationConfig,
scope: ArtifactScope
): FileCollection {
return component.instrumentationCreationConfig?.getDependenciesClassesJarsPostInstrumentation(
scope
) ?: component.variantDependencies.getArtifactFileCollection(
ConsumedConfigType.RUNTIME_CLASSPATH,
scope,
AndroidArtifacts.ArtifactType.CLASSES_JAR
)
}
// This might be consumed by RecalculateFixedStackFrames if that's created
transformManager.addStream(
OriginalStream.builder("ext-libs-classes")
.addContentTypes(TransformManager.CONTENT_CLASS)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.EXTERNAL_LIBRARIES)
.setFileCollection(
getFinalRuntimeClassesJarsFromComponent(
creationConfig,
ArtifactScope.EXTERNAL
)
).build()
)
// Add stream of external java resources if EXTERNAL_LIBRARIES isn't in the set of java res
// merging scopes.
if (!getJavaResMergingScopes(creationConfig)
.contains(com.android.build.api.transform.QualifiedContent.Scope.EXTERNAL_LIBRARIES)) {
transformManager.addStream(
OriginalStream.builder("ext-libs-java-res")
.addContentTypes(com.android.build.api.transform.QualifiedContent.DefaultContentType.RESOURCES)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.EXTERNAL_LIBRARIES)
.setArtifactCollection(
creationConfig
.variantDependencies
.getArtifactCollection(ConsumedConfigType.RUNTIME_CLASSPATH,
ArtifactScope.EXTERNAL,
AndroidArtifacts.ArtifactType.JAVA_RES))
.build())
}
// for the sub modules, new intermediary classes artifact has its own stream
transformManager.addStream(
OriginalStream.builder("sub-projects-classes")
.addContentTypes(TransformManager.CONTENT_CLASS)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.SUB_PROJECTS)
.setFileCollection(
getFinalRuntimeClassesJarsFromComponent(
creationConfig,
ArtifactScope.PROJECT
)
).build()
)
// same for the java resources, if SUB_PROJECTS isn't in the set of java res merging scopes.
if (!getJavaResMergingScopes(creationConfig).contains(
com.android.build.api.transform.QualifiedContent.Scope.SUB_PROJECTS)) {
transformManager.addStream(
OriginalStream.builder("sub-projects-java-res")
.addContentTypes(com.android.build.api.transform.QualifiedContent.DefaultContentType.RESOURCES)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.SUB_PROJECTS)
.setArtifactCollection(
creationConfig
.variantDependencies
.getArtifactCollection(ConsumedConfigType.RUNTIME_CLASSPATH,
ArtifactScope.PROJECT,
AndroidArtifacts.ArtifactType.JAVA_RES))
.build())
}
// if consumesFeatureJars, add streams of classes from features or
// dynamic-features.
// The main dex list calculation for the bundle also needs the feature classes for reference
// only
if ((creationConfig as? ApplicationCreationConfig)?.consumesFeatureJars == true ||
(creationConfig as? ConsumableCreationConfig)?.needsMainDexListForBundle == true) {
transformManager.addStream(
OriginalStream.builder("metadata-classes")
.addContentTypes(TransformManager.CONTENT_CLASS)
.addScope(InternalScope.FEATURES)
.setArtifactCollection(
creationConfig
.variantDependencies
.getArtifactCollection(ConsumedConfigType.REVERSE_METADATA_VALUES,
ArtifactScope.PROJECT,
AndroidArtifacts.ArtifactType.REVERSE_METADATA_CLASSES))
.build())
}
// provided only scopes.
transformManager.addStream(
OriginalStream.builder("provided-classes")
.addContentTypes(TransformManager.CONTENT_CLASS)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.PROVIDED_ONLY)
.setFileCollection(creationConfig.providedOnlyClasspath)
.build())
(creationConfig as? TestComponentCreationConfig)?.onTestedVariant { testedVariant ->
val testedCodeDeps = getFinalRuntimeClassesJarsFromComponent(
testedVariant,
ArtifactScope.ALL
)
transformManager.addStream(
OriginalStream.builder("tested-code-deps")
.addContentTypes(com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.TESTED_CODE)
.setFileCollection(testedCodeDeps)
.build())
null
}
}
fun createMergeApkManifestsTask(creationConfig: ApkCreationConfig) {
val apkVariantData = creationConfig.oldVariantApiLegacySupport!!.variantData as ApkVariantData
val screenSizes = apkVariantData.compatibleScreens
taskFactory.register(
CompatibleScreensManifest.CreationAction(creationConfig, screenSizes))
val processManifestTask = createMergeManifestTasks(creationConfig)
val taskContainer = creationConfig.taskContainer
if (taskContainer.microApkTask != null) {
processManifestTask.dependsOn(taskContainer.microApkTask)
}
}
/** Creates the merge manifests task. */
protected open fun createMergeManifestTasks(
creationConfig: ApkCreationConfig): TaskProvider<out ManifestProcessorTask?> {
taskFactory.register(ProcessManifestForBundleTask.CreationAction(creationConfig))
taskFactory.register(
ProcessManifestForMetadataFeatureTask.CreationAction(creationConfig))
taskFactory.register(ProcessManifestForInstantAppTask.CreationAction(creationConfig))
taskFactory.register(ProcessPackagedManifestTask.CreationAction(creationConfig))
taskFactory.register(GenerateManifestJarTask.CreationAction(creationConfig))
taskFactory.register(ProcessApplicationManifest.CreationAction(creationConfig))
return taskFactory.register(
ProcessMultiApkApplicationManifest.CreationAction(creationConfig))
}
protected fun createProcessTestManifestTask(creationConfig: TestCreationConfig) {
taskFactory.register(ProcessTestManifest.CreationAction(creationConfig))
}
fun createRenderscriptTask(creationConfig: ConsumableCreationConfig) {
creationConfig.renderscriptCreationConfig?.let { renderscriptCreationConfig ->
val taskContainer = creationConfig.taskContainer
val rsTask = taskFactory.register(
RenderscriptCompile.
CreationAction(
creationConfig,
ndkConfig = if (creationConfig is AndroidTestCreationConfig) {
creationConfig.mainVariant.ndkConfig
} else (creationConfig as VariantCreationConfig).ndkConfig
)
)
if (!renderscriptCreationConfig.renderscript.ndkModeEnabled.get()) {
creationConfig.sources.java.addSource(
TaskProviderBasedDirectoryEntryImpl(
name = "generated_renderscript",
directoryProvider = creationConfig.artifacts.get(
InternalArtifactType.RENDERSCRIPT_SOURCE_OUTPUT_DIR
),
)
)
}
taskContainer.resourceGenTask.dependsOn(rsTask)
// since rs may generate Java code, always set the dependency.
taskContainer.sourceGenTask.dependsOn(rsTask)
}
}
fun createMergeResourcesTask(
creationConfig: ComponentCreationConfig,
processResources: Boolean,
flags: Set<MergeResources.Flag>) {
if (!creationConfig.buildFeatures.androidResources &&
creationConfig !is AndroidTestCreationConfig) {
return
}
val alsoOutputNotCompiledResources = (creationConfig.componentType.isApk
&& !creationConfig.componentType.isForTesting
&& creationConfig.androidResourcesCreationConfig!!.useResourceShrinker)
val includeDependencies = true
basicCreateMergeResourcesTask(
creationConfig,
MergeType.MERGE,
includeDependencies,
processResources,
alsoOutputNotCompiledResources,
flags,
null /*configCallback*/)
if (creationConfig
.services
.projectOptions[BooleanOption.ENABLE_SOURCE_SET_PATHS_MAP]) {
taskFactory.register(
MapSourceSetPathsTask.CreateAction(
creationConfig,
includeDependencies
))
}
}
/** Defines the merge type for [.basicCreateMergeResourcesTask] */
enum class MergeType {
/** Merge all resources with all the dependencies resources (i.e. "big merge"). */
MERGE {
override val outputType: Single<Directory>
get() = MERGED_RES
},
/**
* Merge all resources without the dependencies resources for an aar (i.e. "small merge").
*/
PACKAGE {
override val outputType: Single<Directory>
get() = PACKAGED_RES
};
abstract val outputType: Single<Directory>
}
fun basicCreateMergeResourcesTask(
creationConfig: ComponentCreationConfig,
mergeType: MergeType,
includeDependencies: Boolean,
processResources: Boolean,
alsoOutputNotCompiledResources: Boolean,
flags: Set<MergeResources.Flag>,
taskProviderCallback: TaskProviderCallback<MergeResources>?
): TaskProvider<MergeResources> {
val mergedNotCompiledDir = if (alsoOutputNotCompiledResources) File(
creationConfig.services.projectInfo.getIntermediatesDir()
.toString() + "/merged-not-compiled-resources/"
+ creationConfig.dirName) else null
val mergeResourcesTask: TaskProvider<MergeResources> = taskFactory.register(
MergeResources.CreationAction(
creationConfig,
mergeType,
mergedNotCompiledDir,
includeDependencies,
processResources,
flags,
creationConfig.componentType.isAar),
null,
null,
taskProviderCallback)
if (globalConfig.testOptions.unitTests.isIncludeAndroidResources) {
creationConfig.taskContainer.compileTask.dependsOn(mergeResourcesTask)
}
return mergeResourcesTask
}
fun createMergeAssetsTask(creationConfig: ComponentCreationConfig) {
taskFactory.register(MergeSourceSetFolders.MergeAppAssetCreationAction(creationConfig))
}
fun createMergeJniLibFoldersTasks(creationConfig: ConsumableCreationConfig) {
// merge the source folders together using the proper priority.
taskFactory.register(
MergeSourceSetFolders.MergeJniLibFoldersCreationAction(creationConfig))
taskFactory.register(MergeNativeLibsTask.CreationAction(creationConfig))
}
fun createBuildConfigTask(creationConfig: ConsumableCreationConfig) {
creationConfig.buildConfigCreationConfig?.let { buildConfigCreationConfig ->
val generateBuildConfigTask =
taskFactory.register(GenerateBuildConfig.CreationAction(creationConfig))
val isBuildConfigBytecodeEnabled = creationConfig
.services
.projectOptions[BooleanOption.ENABLE_BUILD_CONFIG_AS_BYTECODE]
if (!isBuildConfigBytecodeEnabled
// TODO(b/224758957): This is wrong we need to check the final build config
// fields from the variant API
|| buildConfigCreationConfig.dslBuildConfigFields.isNotEmpty()
) {
creationConfig.taskContainer.sourceGenTask.dependsOn(generateBuildConfigTask)
}
}
}
fun createGenerateResValuesTask(creationConfig: ComponentCreationConfig) {
if (creationConfig.buildFeatures.resValues) {
val generateResValuesTask =
taskFactory.register(GenerateResValues.
CreationAction(creationConfig))
creationConfig.taskContainer.resourceGenTask.dependsOn(generateResValuesTask)
}
}
fun createMlkitTask(creationConfig: ComponentCreationConfig) {
if (creationConfig.buildFeatures.mlModelBinding) {
taskFactory.register(
MergeMlModelsSourceFoldersCreationAction(
creationConfig))
val generateMlModelClassTask =
taskFactory.register(GenerateMlModelClass.CreationAction(creationConfig))
creationConfig.taskContainer.sourceGenTask.dependsOn(generateMlModelClassTask)
}
}
fun createApkProcessResTask(creationConfig: ApkCreationConfig) {
val componentType = creationConfig.componentType
val packageOutputType: InternalArtifactType<Directory>? =
if (componentType.isApk && !componentType.isForTesting) FEATURE_RESOURCE_PKG else null
createApkProcessResTask(creationConfig, packageOutputType)
if ((creationConfig as? ApplicationCreationConfig)?.consumesFeatureJars == true) {
taskFactory.register(MergeAaptProguardFilesCreationAction(creationConfig))
}
}
private fun createApkProcessResTask(
creationConfig: ComponentCreationConfig,
packageOutputType: Single<Directory>?) {
// Check AAR metadata files
taskFactory.register(CheckAarMetadataTask.CreationAction(creationConfig))
val projectInfo = creationConfig.services.projectInfo
// Create the APK_ file with processed resources and manifest. Generate the R class.
createProcessResTask(
creationConfig,
packageOutputType,
MergeType.MERGE,
projectInfo.getProjectBaseName())
val projectOptions = creationConfig.services.projectOptions
val nonTransitiveR = projectOptions[BooleanOption.NON_TRANSITIVE_R_CLASS]
val namespaced: Boolean = globalConfig.namespacedAndroidResources
// TODO(b/138780301): Also use compile time R class in android tests.
if ((projectOptions[BooleanOption.ENABLE_APP_COMPILE_TIME_R_CLASS] || nonTransitiveR)
&& !creationConfig.componentType.isForTesting
&& !namespaced) {
// Generate the COMPILE TIME only R class using the local resources instead of waiting
// for the above full link to finish. Linking will still output the RUN TIME R class.
// Since we're gonna use AAPT2 to generate the keep rules, do not generate them here.
createProcessResTask(
creationConfig,
packageOutputType,
MergeType.PACKAGE,
projectInfo.getProjectBaseName())
}
}
fun createProcessResTask(
creationConfig: ComponentCreationConfig,
packageOutputType: Single<Directory>?,
mergeType: MergeType,
baseName: String) {
if (!creationConfig.buildFeatures.androidResources &&
creationConfig !is AndroidTestCreationConfig) {
return
}
creationConfig.oldVariantApiLegacySupport?.variantData?.calculateFilters(
creationConfig.global.splits
)
// The manifest main dex list proguard rules are always needed for the bundle,
// even if legacy multidex is not explicitly enabled.
val useAaptToGenerateLegacyMultidexMainDexProguardRules =
(creationConfig is ApkCreationConfig
&& creationConfig
.dexingType
.needsMainDexList)
if (globalConfig.namespacedAndroidResources) {
// TODO: make sure we generate the proguard rules in the namespaced case.
NamespacedResourcesTaskManager(taskFactory, creationConfig)
.createNamespacedResourceTasks(
packageOutputType,
baseName,
useAaptToGenerateLegacyMultidexMainDexProguardRules)
val rFiles: FileCollection = project.files(
creationConfig.artifacts.get(RUNTIME_R_CLASS_CLASSES))
@Suppress("DEPRECATION") // Legacy support
creationConfig
.transformManager
.addStream(
OriginalStream.builder("final-r-classes")
.addContentTypes(
if (creationConfig.needsJavaResStreams) TransformManager.CONTENT_JARS else setOf(
com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES))
.addScope(com.android.build.api.transform.QualifiedContent.Scope.PROJECT)
.setFileCollection(rFiles)
.build())
creationConfig
.artifacts
.appendTo(MultipleArtifact.ALL_CLASSES_DIRS, RUNTIME_R_CLASS_CLASSES)
return
}
createNonNamespacedResourceTasks(
creationConfig,
packageOutputType,
mergeType,
baseName,
useAaptToGenerateLegacyMultidexMainDexProguardRules)
}
private fun createNonNamespacedResourceTasks(
creationConfig: ComponentCreationConfig,
packageOutputType: Single<Directory>?,
mergeType: MergeType,
baseName: String,
useAaptToGenerateLegacyMultidexMainDexProguardRules: Boolean) {
val artifacts = creationConfig.artifacts
val projectOptions = creationConfig.services.projectOptions
when(mergeType) {
MergeType.PACKAGE -> {
// MergeType.PACKAGE means we will only merged the resources from our current module
// (little merge). This is used for finding what goes into the AAR (packaging), and also
// for parsing the local resources and merging them with the R.txt files from its
// dependencies to write the R.txt for this module and R.jar for this module and its
// dependencies.
// First collect symbols from this module.
taskFactory.register(ParseLibraryResourcesTask.CreateAction(creationConfig))
// Only generate the keep rules when we need them. We don't need to generate them here
// for non-library modules since AAPT2 will generate them from MergeType.MERGE.
if (generatesProguardOutputFile(creationConfig) &&
creationConfig.componentType.isAar) {
taskFactory.register(
GenerateLibraryProguardRulesTask.CreationAction(creationConfig))
}
val nonTransitiveRClassInApp = projectOptions[BooleanOption.NON_TRANSITIVE_R_CLASS]
val compileTimeRClassInApp = projectOptions[BooleanOption.ENABLE_APP_COMPILE_TIME_R_CLASS]
// Generate the R class for a library using both local symbols and symbols
// from dependencies.
// TODO: double check this (what about dynamic features?)
if (!nonTransitiveRClassInApp || compileTimeRClassInApp || creationConfig.componentType.isAar) {
taskFactory.register(GenerateLibraryRFileTask.CreationAction(
creationConfig,
creationConfig.componentType.isAar
))
}
}
MergeType.MERGE -> {
// MergeType.MERGE means we merged the whole universe.
taskFactory.register(
LinkApplicationAndroidResourcesTask.CreationAction(
creationConfig,
useAaptToGenerateLegacyMultidexMainDexProguardRules,
mergeType,
baseName,
creationConfig.componentType.isAar))
if (packageOutputType != null) {
creationConfig.artifacts.republish(PROCESSED_RES, packageOutputType)
}
// TODO: also support stable IDs for the bundle (does it matter?)
// create the task that creates the aapt output for the bundle.
if (creationConfig is ApkCreationConfig
&& !creationConfig.componentType.isForTesting) {
taskFactory.register(
LinkAndroidResForBundleTask.CreationAction(
creationConfig))
}
artifacts.appendTo(
MultipleArtifact.ALL_CLASSES_JARS,
COMPILE_AND_RUNTIME_NOT_NAMESPACED_R_CLASS_JAR)
if (!creationConfig.debuggable &&
!creationConfig.componentType.isForTesting &&
projectOptions[BooleanOption.ENABLE_RESOURCE_OPTIMIZATIONS]) {
taskFactory.register(OptimizeResourcesTask.CreateAction(creationConfig))
}
}
else -> throw RuntimeException("Unhandled merge type : $mergeType")
}
}
/**
* Returns the scopes for which the java resources should be merged.
*
* @param creationConfig the scope of the variant being processed.
* @param contentType the contentType of java resources, must be RESOURCES or NATIVE_LIBS
* @return the list of scopes for which to merge the java resources.
*/
@Suppress("DEPRECATION") // Legacy support
protected abstract fun getJavaResMergingScopes(
creationConfig: ComponentCreationConfig): Set<com.android.build.api.transform.QualifiedContent.ScopeType>
/**
* Creates the java resources processing tasks.
*
*
* The java processing will happen in two steps:
*
*
* * [Sync] task configured with [ProcessJavaResTask.CreationAction] will sync
* all source folders into a single folder identified by [InternalArtifactType]
* * [MergeJavaResourceTask] will take the output of this merge plus the dependencies
* and will create a single merge with the [PackagingOptions] settings applied.
*
*
* This sets up only the Sync part. The java res merging is setup via [ ][.createMergeJavaResTask]
*/
fun createProcessJavaResTask(creationConfig: ComponentCreationConfig) {
// Copy the source folders java resources into the temporary location, mainly to
// maintain the PluginDsl COPY semantics.
taskFactory.register(ProcessJavaResTask.CreationAction(creationConfig))
// create the stream generated from this task, but only if a library with custom transforms,
// in which case the custom transforms must be applied before java res merging.
if (creationConfig.needsJavaResStreams) {
@Suppress("DEPRECATION") // Legacy support
creationConfig
.transformManager
.addStream(
OriginalStream.builder("processed-java-res")
.addContentType(com.android.build.api.transform.QualifiedContent.DefaultContentType.RESOURCES)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.PROJECT)
.setFileCollection(
creationConfig
.services
.fileCollection(
creationConfig
.artifacts
.get(
JAVA_RES)))
.build())
}
}
/**
* Sets up the Merge Java Res task.
*
* @see .createProcessJavaResTask
*/
fun createMergeJavaResTask(creationConfig: ConsumableCreationConfig) {
val transformManager = creationConfig.transformManager
// Compute the scopes that need to be merged.
@Suppress("DEPRECATION") // Legacy support
val mergeScopes = getJavaResMergingScopes(creationConfig)
taskFactory.register(MergeJavaResourceTask.CreationAction(mergeScopes, creationConfig))
// also add a new merged java res stream if needed.
if (creationConfig.needsMergedJavaResStream) {
val mergedJavaResProvider = creationConfig.artifacts.get(MERGED_JAVA_RES)
transformManager.addStream(
OriginalStream.builder("merged-java-res")
.addContentTypes(TransformManager.CONTENT_RESOURCES)
.addScopes(mergeScopes)
.setFileCollection(project.layout.files(mergedJavaResProvider))
.build())
}
}
fun createAidlTask(creationConfig: ConsumableCreationConfig) {
if (creationConfig.buildFeatures.aidl) {
val taskContainer = creationConfig.taskContainer
val aidlCompileTask = taskFactory.register(AidlCompile.CreationAction(creationConfig))
taskContainer.sourceGenTask.dependsOn(aidlCompileTask)
}
}
fun createShaderTask(creationConfig: ConsumableCreationConfig) {
if (creationConfig.buildFeatures.shaders) {
// merge the shader folders together using the proper priority.
taskFactory.register(
MergeShaderSourceFoldersCreationAction(
creationConfig))
// compile the shaders
val shaderCompileTask =
taskFactory.register(ShaderCompile.CreationAction(creationConfig))
creationConfig.taskContainer.assetGenTask.dependsOn(shaderCompileTask)
}
}
protected open fun postJavacCreation(creationConfig: ComponentCreationConfig) {
// Use the deprecated public artifact types to register the pre/post JavaC hooks as well as
// the javac output itself.
// It is necessary to do so in case some third-party plugin is using those deprecated public
// artifact type to append/transform/replace content.
// Once the deprecated types can be removed, all the methods below should use the
// [ScopedArtifacts.setInitialContent] methods to initialize directly the scoped container.
creationConfig.oldVariantApiLegacySupport?.variantData?.let { variantData ->
creationConfig
.artifacts
.appendAll(
MultipleArtifact.ALL_CLASSES_JARS,
variantData.allPreJavacGeneratedBytecode.getRegularFiles(
project.layout.projectDirectory
)
)
creationConfig
.artifacts
.appendAll(
MultipleArtifact.ALL_CLASSES_DIRS,
variantData.allPreJavacGeneratedBytecode.getDirectories(
project.layout.projectDirectory
)
)
creationConfig
.artifacts
.appendAll(
MultipleArtifact.ALL_CLASSES_JARS,
variantData.allPostJavacGeneratedBytecode.getRegularFiles(
project.layout.projectDirectory
)
)
creationConfig
.artifacts
.appendAll(
MultipleArtifact.ALL_CLASSES_DIRS,
variantData.allPostJavacGeneratedBytecode.getDirectories(
project.layout.projectDirectory
)
)
}
creationConfig
.artifacts
.appendTo(
MultipleArtifact.ALL_CLASSES_DIRS,
JAVAC)
}
/**
* 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.
*/
fun createJavacTask(
creationConfig: ComponentCreationConfig
): TaskProvider<out JavaCompile> {
val usingKapt = isKotlinKaptPluginApplied(project)
val usingKsp = isKspPluginApplied(project)
taskFactory.register(JavaPreCompileTask.CreationAction(creationConfig, usingKapt, usingKsp))
val javacTask: TaskProvider<out JavaCompile> =
taskFactory.register(
JavaCompileCreationAction(
creationConfig,
project.objects,
usingKapt
)
)
postJavacCreation(creationConfig)
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.
*/
@Suppress("DEPRECATION") // Legacy support
protected fun addJavacClassesStream(creationConfig: ComponentCreationConfig) {
// create separate streams for all the classes coming from javac, pre/post hooks and R.
val transformManager = creationConfig.transformManager
transformManager.addStream(
OriginalStream.builder("all-classes") // Need both classes and resources because some annotation
// processors generate resources
.addContentTypes(
if (creationConfig.needsJavaResStreams) TransformManager.CONTENT_JARS else setOf(
com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES))
.addScope(com.android.build.api.transform.QualifiedContent.Scope.PROJECT)
.setFileCollection(creationConfig
.artifacts
.forScope(ScopedArtifacts.Scope.PROJECT)
.getFinalArtifacts(ScopedArtifact.CLASSES))
.build())
}
/** Creates the tasks to build unit tests. */
private fun createUnitTestVariantTasks(
unitTestCreationConfig: UnitTestCreationConfig) {
val taskContainer = unitTestCreationConfig.taskContainer
val testedVariant = unitTestCreationConfig.mainVariant
val includeAndroidResources = globalConfig.testOptions.unitTests
.isIncludeAndroidResources
createAnchorTasks(unitTestCreationConfig)
// Create all current streams (dependencies mostly at this point)
createDependencyStreams(unitTestCreationConfig)
// process java resources
createProcessJavaResTask(unitTestCreationConfig)
if (includeAndroidResources) {
if (testedVariant.componentType.isAar) {
// Add a task to process the manifest
createProcessTestManifestTask(unitTestCreationConfig)
// Add a task to create the res values
createGenerateResValuesTask(unitTestCreationConfig)
// Add a task to merge the assets folders
createMergeAssetsTask(unitTestCreationConfig)
createMergeResourcesTask(unitTestCreationConfig, true, ImmutableSet.of())
// Add a task to process the Android Resources and generate source files
createApkProcessResTask(unitTestCreationConfig, FEATURE_RESOURCE_PKG)
taskFactory.register(PackageForUnitTest.CreationAction(unitTestCreationConfig))
// Add data binding tasks if enabled
createDataBindingTasksIfNecessary(unitTestCreationConfig)
} else if (testedVariant.componentType.isApk) {
// The IDs will have been inlined for an non-namespaced application
// so just re-export the artifacts here.
unitTestCreationConfig
.artifacts
.copy(PROCESSED_RES, testedVariant.artifacts)
unitTestCreationConfig
.artifacts
.copy(SingleArtifact.ASSETS, testedVariant.artifacts)
taskFactory.register(PackageForUnitTest.CreationAction(unitTestCreationConfig))
} else {
throw IllegalStateException(
"Tested variant "
+ testedVariant.name
+ " in "
+ project.path
+ " must be a library or an application to have unit tests.")
}
val generateTestConfig = taskFactory.register(
GenerateTestConfig.
CreationAction(unitTestCreationConfig))
val compileTask = taskContainer.compileTask
compileTask.dependsOn(generateTestConfig)
// The GenerateTestConfig task has 2 types of inputs: direct inputs and indirect inputs.
// Only the direct inputs are registered with Gradle, whereas the indirect inputs are
// not (see that class for details).
// Since the compile task also depends on the indirect inputs to the GenerateTestConfig
// task, making the compile task depend on the GenerateTestConfig task is not enough, we
// also need to register those inputs with Gradle explicitly here. (We can't register
// @Nested objects programmatically, so it's important to keep these inputs consistent
// with those defined in TestConfigInputs.)
compileTask.configure { task: Task ->
val testConfigInputs = TestConfigInputs(unitTestCreationConfig)
val taskInputs = task.inputs
taskInputs.property(
"isUseRelativePathEnabled",
testConfigInputs.isUseRelativePathEnabled)
taskInputs
.files(testConfigInputs.resourceApk)
.withPropertyName("resourceApk")
.optional()
.withPathSensitivity(PathSensitivity.RELATIVE)
taskInputs
.files(testConfigInputs.mergedAssets)
.withPropertyName("mergedAssets")
.withPathSensitivity(PathSensitivity.RELATIVE)
taskInputs
.files(testConfigInputs.mergedManifest)
.withPropertyName("mergedManifest")
.withPathSensitivity(PathSensitivity.RELATIVE)
taskInputs.property(
"mainVariantOutput", testConfigInputs.mainVariantOutput)
taskInputs.property(
"packageNameOfFinalRClassProvider",
testConfigInputs.packageNameOfFinalRClass)
}
} else {
if (testedVariant.componentType.isAar && testedVariant.buildFeatures.androidResources) {
// With compile classpath R classes, we need to generate a dummy R class for unit
// tests
// See https://issuetracker.google.com/143762955 for more context.
taskFactory.register(
TestRuntimeStubRClassCreationAction(
unitTestCreationConfig))
}
}
// :app:compileDebugUnitTestSources should be enough for running tests from AS, so add
// dependencies on tasks that prepare necessary data files.
val compileTask = taskContainer.compileTask
compileTask.dependsOn(taskContainer.processJavaResourcesTask,
testedVariant.taskContainer.processJavaResourcesTask)
val javacTask = createJavacTask(unitTestCreationConfig)
addJavacClassesStream(unitTestCreationConfig)
setJavaCompilerTask(javacTask, unitTestCreationConfig)
// This should be done automatically by the classpath
// TaskFactoryUtils.dependsOn(javacTask,
// testedVariantScope.getTaskContainer().getJavacTask());
maybeCreateTransformClassesWithAsmTask(unitTestCreationConfig)
// TODO: use merged java res for unit tests (bug 118690729)
createRunUnitTestTask(unitTestCreationConfig)
// This hides the assemble unit test task from the task list.
taskContainer.assembleTask.configure { task: Task -> task.group = null }
}
/** Creates the tasks to build android tests. */
private fun createAndroidTestVariantTasks(androidTestProperties: AndroidTestCreationConfig) {
createAnchorTasks(androidTestProperties)
// Create all current streams (dependencies mostly at this point)
createDependencyStreams(androidTestProperties)
// Add a task to process the manifest
createProcessTestManifestTask(androidTestProperties)
// Add a task to create the res values
createGenerateResValuesTask(androidTestProperties)
// Add a task to compile renderscript files.
createRenderscriptTask(androidTestProperties)
// Add a task to merge the resource folders
createMergeResourcesTask(androidTestProperties, true, ImmutableSet.of())
// Add tasks to compile shader
createShaderTask(androidTestProperties)
// Add a task to merge the assets folders
createMergeAssetsTask(androidTestProperties)
taskFactory.register(CompressAssetsTask.CreationAction(androidTestProperties))
// Add a task to create the BuildConfig class
createBuildConfigTask(androidTestProperties)
// Add a task to generate resource source files
createApkProcessResTask(androidTestProperties)
// process java resources
createProcessJavaResTask(androidTestProperties)
createAidlTask(androidTestProperties)
// add tasks to merge jni libs.
createMergeJniLibFoldersTasks(androidTestProperties)
// Add data binding tasks if enabled
createDataBindingTasksIfNecessary(androidTestProperties)
// Add a task to compile the test application
val javacTask = createJavacTask(androidTestProperties)
addJavacClassesStream(androidTestProperties)
setJavaCompilerTask(javacTask, androidTestProperties)
createPostCompilationTasks(androidTestProperties)
// Add tasks to produce the signing config files
createValidateSigningTask(androidTestProperties)
taskFactory.register(SigningConfigWriterTask.CreationAction(androidTestProperties))
taskFactory.register(SigningConfigVersionsWriterTask.CreationAction(androidTestProperties))
createPackagingTask(androidTestProperties)
taskFactory.configure(
ASSEMBLE_ANDROID_TEST
) { assembleTest: Task ->
assembleTest.dependsOn(
androidTestProperties
.taskContainer
.assembleTask
.name)
}
createConnectedTestForVariant(androidTestProperties)
}
/** Returns the full path of a task given its name. */
private fun getTaskPath(taskName: String): String {
return if (project.rootProject === project) ":$taskName" else project.path + ':' + taskName
}
private fun createRunUnitTestTask(unitTestCreationConfig: UnitTestCreationConfig) {
val ant = JacocoConfigurations.getJacocoAntTaskConfiguration(
project, JacocoTask.getJacocoVersion(unitTestCreationConfig))
if (unitTestCreationConfig.isUnitTestCoverageEnabled) {
project.pluginManager.apply(JacocoPlugin::class.java)
}
val runTestsTask =
taskFactory.register(AndroidUnitTest.CreationAction(unitTestCreationConfig))
taskFactory.configure(JavaPlugin.TEST_TASK_NAME) {
test: Task -> test.dependsOn(runTestsTask)
}
if (unitTestCreationConfig.isUnitTestCoverageEnabled) {
project.plugins.withType(JacocoPlugin::class.java) {
// Jacoco plugin is applied and test coverage enabled, ∴ generate coverage report.
taskFactory.register(
JacocoReportTask.CreateActionUnitTest(unitTestCreationConfig, ant)
)
}
}
}
private fun createTopLevelTestTasks() {
createMockableJarTask()
val reportTasks: MutableList<String> = mutableListOf()
val providers = globalConfig.deviceProviders
// 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
// Task.
val connectedAndroidTestTask: TaskProvider<out Task>
if (globalConfig.productFlavorCount > 0) {
connectedAndroidTestTask = taskFactory.register(
AndroidReportTask.CreationAction(
globalConfig,
AndroidReportTask.CreationAction.TaskKind.CONNECTED))
reportTasks.add(connectedAndroidTestTask.name)
} else {
connectedAndroidTestTask = taskFactory.register(
CONNECTED_ANDROID_TEST
) { connectedTask: Task ->
connectedTask.group = JavaBasePlugin.VERIFICATION_GROUP
connectedTask.description = (
"Installs and runs instrumentation tests "
+ "for all flavors on connected devices.")
}
}
taskFactory.configure(
CONNECTED_CHECK) { check: Task -> check.dependsOn(connectedAndroidTestTask.name) }
val deviceAndroidTestTask: TaskProvider<out Task>
// 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 || globalConfig.productFlavorCount > 0) {
deviceAndroidTestTask = taskFactory.register(
AndroidReportTask.CreationAction(
globalConfig,
AndroidReportTask.CreationAction.TaskKind.DEVICE_PROVIDER))
reportTasks.add(deviceAndroidTestTask.name)
} else {
deviceAndroidTestTask = taskFactory.register(
DEVICE_ANDROID_TEST
) { providerTask: Task ->
providerTask.group = JavaBasePlugin.VERIFICATION_GROUP
providerTask.description = (
"Installs and runs instrumentation tests "
+ "using all Device Providers.")
}
}
taskFactory.configure(
DEVICE_CHECK) { check: Task -> check.dependsOn(deviceAndroidTestTask.name) }
// Create top level unit test tasks.
taskFactory.register(
JavaPlugin.TEST_TASK_NAME
) { unitTestTask: Task ->
unitTestTask.group = JavaBasePlugin.VERIFICATION_GROUP
unitTestTask.description = "Run unit tests for all variants."
}
taskFactory.configure(
JavaBasePlugin.CHECK_TASK_NAME
) { check: Task -> 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.isNotEmpty() && project.gradle.startParameter
.isContinueOnFailure) {
project.gradle
.taskGraph
.whenReady { taskGraph: TaskExecutionGraph ->
for (reportTask in reportTasks) {
if (taskGraph.hasTask(getTaskPath(reportTask))) {
taskFactory.configure(
reportTask
) { task: Task -> (task as AndroidReportTask).setWillRun() }
}
}
}
}
}
protected fun createTestDevicesTasks() {
if (!shouldEnableUtp(
globalConfig.services.projectOptions,
globalConfig.testOptions,
componentType = null
) ||
globalConfig.testOptions.devices.isEmpty()
) {
return
}
val managedDevices = getManagedDevices()
val cleanTask = taskFactory.register(
ManagedDeviceCleanTask.CreationAction(
"cleanManagedDevices",
globalConfig,
managedDevices))
val allDevices = taskFactory.register(
ALL_DEVICES_CHECK
) { allDevicesCheckTask: Task ->
allDevicesCheckTask.description =
"Runs all device checks on all managed devices defined in the TestOptions dsl."
allDevicesCheckTask.group = JavaBasePlugin.VERIFICATION_GROUP
}
for (device in managedDevices) {
val setupTask = taskFactory.register(
ManagedDeviceSetupTask.CreationAction(
setupTaskName(device),
device,
globalConfig))
setupTask.configure {
it.mustRunAfter(cleanTask)
}
val deviceAllVariantsTask = taskFactory.register(
managedDeviceAllVariantsTaskName(device)
) { deviceVariantTask: Task ->
deviceVariantTask.description =
"Runs all device checks on the managed device ${device.name}."
deviceVariantTask.group = JavaBasePlugin.VERIFICATION_GROUP
}
allDevices.dependsOn(deviceAllVariantsTask)
}
for (group in getDeviceGroups()) {
taskFactory.register(
managedDeviceGroupAllVariantsTaskName(group)
) { deviceGroupTask: Task ->
deviceGroupTask.description =
"Runs all device checks on all devices defined in group ${group.name}."
deviceGroupTask.group = JavaBasePlugin.VERIFICATION_GROUP
}
}
}
/**
* Creates the individual managed device tasks for the given variant
*
* @param creationConfig the test config
* @param testData the extra test data
* @param variant the testing variant, used for test coverage
* @param variantName name of the variant under test. This can be different from the testing
* variant.
* @param testTaskSuffix the suffix to be applied to the individual task names. This should be
* used if the test config's name does not include a test suffix.
*/
protected fun createTestDevicesForVariant(
creationConfig: InstrumentedTestCreationConfig,
testData: AbstractTestDataImpl,
testedVariant: VariantCreationConfig?,
variantName: String,
testTaskSuffix: String = ""
) {
val managedDevices = getManagedDevices()
if (!shouldEnableUtp(
globalConfig.services.projectOptions,
globalConfig.testOptions,
testedVariant?.componentType
) ||
managedDevices.isEmpty()) {
return
}
val allDevicesVariantTask = taskFactory.register(
creationConfig.computeTaskName("allDevices", testTaskSuffix)
) { allDevicesVariant: Task ->
allDevicesVariant.description =
"Runs the tests for $variantName on all managed devices in the dsl."
allDevicesVariant.group = JavaBasePlugin.VERIFICATION_GROUP
}
taskFactory.configure(
ALL_DEVICES_CHECK
) { allDevices: Task ->
allDevices.dependsOn(allDevicesVariantTask)
}
val resultsRootDir = if (extension.testOptions.resultsDir.isNullOrEmpty()) {
creationConfig.paths.outputDir(BuilderConstants.FD_ANDROID_RESULTS)
.get().asFile
} else {
File(requireNotNull(extension.testOptions.resultsDir))
}
val reportRootDir = if (extension.testOptions.resultsDir.isNullOrEmpty()) {
creationConfig.paths.reportsDir(BuilderConstants.FD_ANDROID_TESTS)
.get().asFile
} else {
File(requireNotNull(extension.testOptions.reportDir))
}
val additionalOutputRootDir = creationConfig.paths.outputDir(
InternalArtifactType.MANAGED_DEVICE_ANDROID_TEST_ADDITIONAL_OUTPUT.getFolderName()
).get().asFile
val coverageOutputRootDir = creationConfig.paths.outputDir(
InternalArtifactType.MANAGED_DEVICE_CODE_COVERAGE.getFolderName()
).get().asFile
val flavor: String? = testData.flavorName.orNull
val flavorDir = if (flavor.isNullOrEmpty()) "" else "${BuilderConstants.FD_FLAVORS}/$flavor"
val resultsDir = File(resultsRootDir, "${BuilderConstants.MANAGED_DEVICE}/${flavorDir}")
val reportDir = File(reportRootDir, "${BuilderConstants.MANAGED_DEVICE}/${flavorDir}")
val additionalTestOutputDir = File(additionalOutputRootDir, flavorDir)
val coverageOutputDir = File(coverageOutputRootDir, flavorDir)
val deviceToProvider = mutableMapOf<String, TaskProvider<out Task>>()
for (managedDevice in managedDevices) {
val managedDeviceTestTask = taskFactory.register(
ManagedDeviceInstrumentationTestTask.CreationAction(
creationConfig,
managedDevice,
testData,
File(resultsDir, managedDevice.name),
File(reportDir, managedDevice.name),
File(additionalTestOutputDir, managedDevice.name),
File(coverageOutputDir, managedDevice.name),
testTaskSuffix
)
)
managedDeviceTestTask.dependsOn(setupTaskName(managedDevice))
allDevicesVariantTask.dependsOn(managedDeviceTestTask)
taskFactory.configure(
managedDeviceAllVariantsTaskName(managedDevice)
) { managedDeviceTests: Task ->
managedDeviceTests.dependsOn(managedDeviceTestTask)
}
deviceToProvider[managedDevice.name] = managedDeviceTestTask
}
// Register a task to aggregate test suite result protos.
val testResultAggregationTask = taskFactory.register(
ManagedDeviceInstrumentationTestResultAggregationTask.CreationAction(
creationConfig,
managedDevices.map { File(File(resultsDir, it.name), TEST_RESULT_PB_FILE_NAME) },
File(resultsDir, TEST_RESULT_PB_FILE_NAME),
reportDir,
)
)
for (managedDevice in managedDevices) {
taskFactory.configure(
managedDeviceAllVariantsTaskName(managedDevice)
) { managedDeviceCheck ->
managedDeviceCheck.dependsOn(testResultAggregationTask)
}
}
deviceToProvider.values.forEach { managedDeviceTestTask ->
testResultAggregationTask.configure {
it.mustRunAfter(managedDeviceTestTask)
}
// Run test result aggregation task even after test failures.
managedDeviceTestTask.configure {
it.finalizedBy(testResultAggregationTask)
}
}
// Register a test coverage report generation task to every managedDeviceCheck
// task.
if ((testedVariant?.isAndroidTestCoverageEnabled == true) &&
creationConfig is TestComponentCreationConfig) {
val jacocoAntConfiguration = JacocoConfigurations.getJacocoAntTaskConfiguration(
project, JacocoTask.getJacocoVersion(creationConfig)
)
val reportTask = taskFactory.register(
JacocoReportTask.CreationActionManagedDeviceTest(
creationConfig, jacocoAntConfiguration
)
)
testedVariant.taskContainer.coverageReportTask?.dependsOn(reportTask)
// Run the report task after all tests are finished on all devices.
deviceToProvider.values.forEach { managedDeviceTestTask ->
reportTask.dependsOn(managedDeviceTestTask)
reportTask.configure {
it.mustRunAfter(managedDeviceTestTask)
}
}
taskFactory.configure(ALL_DEVICES_CHECK) { allDevices: Task ->
allDevices.dependsOn(reportTask)
}
}
// Lastly the Device Group Tasks.
for (group in getDeviceGroups()) {
val variantDeviceGroupTask = taskFactory.register(
managedDeviceGroupSingleVariantTaskName(creationConfig, group)
.appendCapitalized(testTaskSuffix)
) { deviceGroupVariant: Task ->
deviceGroupVariant.description =
"Runs the tests for $variantName on all devices defined in ${group.name}."
deviceGroupVariant.group = JavaBasePlugin.VERIFICATION_GROUP
}
for (device in group.targetDevices) {
variantDeviceGroupTask.dependsOn(deviceToProvider.getValue(device.name))
}
taskFactory.configure(
managedDeviceGroupAllVariantsTaskName(group)
) { deviceGroupTask: Task ->
deviceGroupTask.dependsOn(variantDeviceGroupTask)
}
}
}
private fun createConnectedTestForVariant(androidTestProperties: AndroidTestCreationConfig) {
val testedVariant = androidTestProperties.mainVariant
val isLibrary = testedVariant.componentType.isAar
val testData: AbstractTestDataImpl = if (testedVariant.componentType.isDynamicFeature) {
BundleTestDataImpl(
androidTestProperties.namespace,
androidTestProperties,
androidTestProperties.artifacts.get(SingleArtifact.APK),
getFeatureName(project.path),
testedVariant
.variantDependencies
.getArtifactFileCollection(ConsumedConfigType.RUNTIME_CLASSPATH,
ArtifactScope.PROJECT,
AndroidArtifacts.ArtifactType.APKS_FROM_BUNDLE))
} else {
val testedApkFileCollection =
project.files(testedVariant.artifacts.get(SingleArtifact.APK))
TestDataImpl(
androidTestProperties.namespace,
androidTestProperties,
androidTestProperties.artifacts.get(SingleArtifact.APK),
if (isLibrary) null else testedApkFileCollection)
}
configureTestData(androidTestProperties, testData)
val connectedCheckSerials: Provider<List<String>> =
taskFactory.named(CONNECTED_CHECK).flatMap { test ->
(test as DeviceSerialTestTask).serialValues
}
val connectedTask = taskFactory.register(
DeviceProviderInstrumentTestTask.CreationAction(
androidTestProperties, testData, connectedCheckSerials))
taskFactory.configure(
CONNECTED_ANDROID_TEST
) { connectedAndroidTest: Task -> connectedAndroidTest.dependsOn(connectedTask) }
if (testedVariant.isAndroidTestCoverageEnabled) {
val jacocoAntConfiguration = JacocoConfigurations.getJacocoAntTaskConfiguration(
project, JacocoTask.getJacocoVersion(androidTestProperties))
val reportTask = taskFactory.register(
JacocoReportTask.CreationActionConnectedTest(
androidTestProperties, jacocoAntConfiguration))
testedVariant.taskContainer.coverageReportTask.dependsOn(reportTask)
taskFactory.configure(
CONNECTED_ANDROID_TEST
) { connectedAndroidTest: Task -> connectedAndroidTest.dependsOn(reportTask) }
}
val providers = globalConfig.deviceProviders
if (providers.isNotEmpty()) {
getBuildService(project.gradle.sharedServices, AnalyticsConfiguratorService::class.java)
.get()
.getProjectBuilder(project.path)
?.projectApiUseBuilder
?.builderTestApiDeviceProvider = true
}
// now the providers.
for (deviceProvider in providers) {
val providerTask = taskFactory.register(
DeviceProviderInstrumentTestTask.CreationAction(
androidTestProperties, deviceProvider, testData, connectedCheckSerials))
taskFactory.configure(
DEVICE_ANDROID_TEST
) { deviceAndroidTest: Task -> deviceAndroidTest.dependsOn(providerTask) }
}
// now the test servers
val servers = globalConfig.testServers
if (servers.isNotEmpty()) {
getBuildService(project.gradle.sharedServices, AnalyticsConfiguratorService::class.java)
.get()
.getProjectBuilder(project.path)
?.projectApiUseBuilder
?.builderTestApiTestServer = true
}
for (testServer in servers) {
val serverTask = taskFactory.register(
TestServerTaskCreationAction(
androidTestProperties, testServer))
serverTask.dependsOn<Task>(androidTestProperties.taskContainer.assembleTask)
taskFactory.configure(
DEVICE_CHECK) { deviceAndroidTest: Task ->
deviceAndroidTest.dependsOn(serverTask)
}
}
createTestDevicesForVariant(
androidTestProperties,
testData,
testedVariant,
androidTestProperties.mainVariant.name
)
}
/**
* 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
*/
fun createPostCompilationTasks(creationConfig: ApkCreationConfig) {
Preconditions.checkNotNull(creationConfig.taskContainer.javacTask)
val transformManager = creationConfig.transformManager
taskFactory.register(MergeGeneratedProguardFilesCreationAction(creationConfig))
// Merge Java Resources.
createMergeJavaResTask(creationConfig)
// -----------------------------------------------------------------------------------------
// The following task registrations MUST follow the order:
// ASM API -> Legacy transforms -> jacoco transforms
// -----------------------------------------------------------------------------------------
maybeCreateTransformClassesWithAsmTask(creationConfig)
// ----- External Transforms -----
val registeredLegacyTransform = addExternalLegacyTransforms(transformManager, creationConfig)
// New gradle-transform jacoco instrumentation support.
if (creationConfig.isAndroidTestCoverageEnabled &&
!creationConfig.componentType.isForTesting) {
if (registeredLegacyTransform) {
createJacocoTaskWithLegacyTransformSupport(creationConfig)
} else {
createJacocoTask(creationConfig)
}
}
// initialize the all classes scope, at this point we do not consume the classes, just read
// the content as folks can be accessing these classes without transforming them and
// re-injecting them in the build flow.
creationConfig.artifacts.forScope(ScopedArtifacts.Scope.ALL)
.getScopedArtifactsContainer(ScopedArtifact.CLASSES)
.initialScopedContent
.from(
creationConfig
.transformManager
.getPipelineOutputAsFileCollection { contentTypes, scopes ->
contentTypes.contains(com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES)
&& scopes.intersect(TransformManager.SCOPE_FULL_PROJECT_WITH_LOCAL_JARS).isNotEmpty()
}
)
// let's check if the ALL scoped classes are transformed.
if (creationConfig.artifacts.forScope(ScopedArtifacts.Scope.ALL)
.getScopedArtifactsContainer(ScopedArtifact.CLASSES).artifactsAltered.get()) {
// at this point, we need to consume all these streams as they will be provided by the
// final producer of the CLASSES artifact.
creationConfig.transformManager
.consumeStreams(
TransformManager.SCOPE_FULL_PROJECT_WITH_LOCAL_JARS,
setOf(com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES)
)
// and register the final transformed version back into the transform pipeline.
creationConfig.transformManager
.addStream(
OriginalStream.builder("variant-api-transformed-classes")
.addContentTypes(TransformManager.CONTENT_CLASS)
.addScope(QualifiedContent.Scope.PROJECT)
.setFileCollection(
creationConfig.artifacts.forScope(ScopedArtifacts.Scope.ALL)
.getFinalArtifacts(ScopedArtifact.CLASSES)
)
.build()
)
}
// Add a task to create merged runtime classes if this is a dynamic-feature,
// or a base module consuming feature jars. Merged runtime classes are needed if code
// minification is enabled in a project with features or dynamic-features.
if (creationConfig.componentType.isDynamicFeature
|| (creationConfig as? ApplicationCreationConfig)?.consumesFeatureJars == true) {
taskFactory.register(MergeClassesTask.CreationAction(creationConfig))
}
// Produce better error messages when we have duplicated classes.
maybeCreateCheckDuplicateClassesTask(creationConfig)
// Resource Shrinking
maybeCreateResourcesShrinkerTasks(creationConfig)
// Code Shrinking
// Since the shrinker (R8) also dexes the class files, if we have minifedEnabled we stop
// the flow and don't set-up dexing.
maybeCreateJavaCodeShrinkerTask(creationConfig)
if (creationConfig.minifiedEnabled) {
maybeCreateDesugarLibTask(creationConfig, false)
return
}
// Code Dexing (MonoDex, Legacy Multidex or Native Multidex)
if (creationConfig.needsMainDexListForBundle) {
taskFactory.register(D8BundleMainDexListTask.CreationAction(creationConfig))
}
if (creationConfig.componentType.isDynamicFeature) {
taskFactory.register(FeatureDexMergeTask.CreationAction(creationConfig))
}
createDexTasks(creationConfig, creationConfig.dexingType, registeredLegacyTransform)
}
/**
* Adds any transforms registered in a module build file. Returns true if any transforms have
* been added.
*/
private fun addExternalLegacyTransforms(
transformManager: TransformManager,
creationConfig: ApkCreationConfig
): Boolean {
val customTransforms = globalConfig.transforms
val customTransformsDependencies = globalConfig.transformsDependencies
var registeredLegacyTransform = false
for (i in customTransforms.indices) {
val transform = customTransforms[i]
val deps = customTransformsDependencies[i]
registeredLegacyTransform = registeredLegacyTransform or transformManager
.addTransform(
taskFactory,
creationConfig,
transform,
null,
object : TaskConfigAction<TransformTask> {
override fun configure(task: TransformTask) {
if (deps.isNotEmpty()) {
task.dependsOn(deps)
}
}
},
object : TaskProviderCallback<TransformTask> {
override fun handleProvider(taskProvider: TaskProvider<TransformTask>) {
// if the task is a no-op then we make assemble task depend
// on it.
if (transform.scopes.isEmpty()) {
creationConfig
.taskContainer
.assembleTask.dependsOn<Task>(taskProvider)
}
}
}
)
.isPresent
}
return registeredLegacyTransform
}
/**
* Creates tasks used for DEX generation. This will use an incremental pipeline that uses dex
* archives in order to enable incremental dexing support.
*/
private fun createDexTasks(
creationConfig: ApkCreationConfig,
dexingType: DexingType,
registeredLegacyTransforms: Boolean) {
val java8LangSupport = creationConfig.getJava8LangSupportType()
val supportsDesugaringViaArtifactTransform =
(java8LangSupport == Java8LangSupport.UNUSED
|| (java8LangSupport == Java8LangSupport.D8
&& creationConfig
.services
.projectOptions[BooleanOption.ENABLE_DEXING_DESUGARING_ARTIFACT_TRANSFORM]))
val classesAlteredTroughVariantAPI = creationConfig
.artifacts
.forScope(ScopedArtifacts.Scope.ALL)
.getScopedArtifactsContainer(ScopedArtifact.CLASSES)
.artifactsAltered
.get()
val enableDexingArtifactTransform = (creationConfig
.services
.projectOptions[BooleanOption.ENABLE_DEXING_ARTIFACT_TRANSFORM]
&& !registeredLegacyTransforms
&& supportsDesugaringViaArtifactTransform)
&& !classesAlteredTroughVariantAPI
val classpathUtils = ClassesClasspathUtils(
creationConfig,
enableDexingArtifactTransform,
registeredLegacyTransforms,
classesAlteredTroughVariantAPI
)
taskFactory.register(
DexArchiveBuilderTask.CreationAction(
creationConfig,
classpathUtils,
)
)
maybeCreateDesugarLibTask(creationConfig, enableDexingArtifactTransform)
createDexMergingTasks(creationConfig, dexingType, enableDexingArtifactTransform, classesAlteredTroughVariantAPI)
}
/**
* Set up dex merging tasks when artifact transforms are used.
*
*
* External libraries are merged in mono-dex and native multidex modes. In case of a native
* multidex debuggable variant these dex files get packaged. In mono-dex case, we will re-merge
* these files. Because this task will be almost always up-to-date, having a second merger run
* over the external libraries will not cause a performance regression. In addition to that,
* second dex merger will perform less I/O compared to reading all external library dex files
* individually. For legacy multidex, we must merge all dex files in a single invocation in
* order to generate correct primary dex file in presence of desugaring. See b/120039166.
*
*
* When merging native multidex, debuggable variant, project's dex files are merged
* independently. Also, the library projects' dex files are merged independently.
*
*
* For all other variants (release, mono-dex, legacy-multidex), we merge all dex files in a
* single invocation. This means that external libraries, library projects and project dex files
* will be merged in a single task.
*/
private fun createDexMergingTasks(
creationConfig: ApkCreationConfig,
dexingType: DexingType,
dexingUsingArtifactTransforms: Boolean,
classesAlteredThroughVariantAPI: Boolean,
) {
// if classes were altered at the ALL scoped level, we just need to merge the single jar
// file resulting.
if (classesAlteredThroughVariantAPI) {
taskFactory.register(DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_TRANSFORMED_CLASSES,
dexingType,
dexingUsingArtifactTransforms))
return
}
// When desugaring, The file dependencies are dexed in a task with the whole
// remote classpath present, as they lack dependency information to desugar
// them correctly in an artifact transform.
// This should only be passed to Legacy Multidex MERGE_ALL or MERGE_EXTERNAL_LIBS of
// other dexing modes, otherwise it will cause the output of DexFileDependenciesTask
// to be included multiple times and will cause the build to fail because of same types
// being defined multiple times in the final dex.
val separateFileDependenciesDexingTask =
(creationConfig.getJava8LangSupportType() == Java8LangSupport.D8
&& dexingUsingArtifactTransforms)
if (separateFileDependenciesDexingTask) {
val desugarFileDeps = DexFileDependenciesTask.CreationAction(creationConfig)
taskFactory.register(desugarFileDeps)
}
when (dexingType) {
DexingType.MONO_DEX -> {
taskFactory.register(
DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_EXTERNAL_LIBS,
dexingType,
dexingUsingArtifactTransforms,
separateFileDependenciesDexingTask,
InternalMultipleArtifactType.EXTERNAL_LIBS_DEX))
taskFactory.register(
DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_ALL,
dexingType,
dexingUsingArtifactTransforms))
}
DexingType.LEGACY_MULTIDEX -> {
// For Legacy Multidex we cannot employ the same optimization of first merging
// the external libraries, because in that step we don't have a main dex list file
// to pass to D8 yet, and together with the fact that we'll be setting minApiLevel
// to 20 or less it will make the external libs be merged in a way equivalent to
// MonoDex, which might cause the build to fail if the external libraries alone
// cannot fit into a single dex.
taskFactory.register(
DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_ALL,
dexingType,
dexingUsingArtifactTransforms,
separateFileDependenciesDexingTask))
}
DexingType.NATIVE_MULTIDEX -> {
// For a debuggable variant, we merge different bits in separate tasks.
// Potentially more .dex files being created, but during development-cycle of
// developers, code changes will hopefully impact less .dex files and will make
// the build be faster.
// For non-debuggable (release) builds, we do only a MERGE_EXTERNAL_LIBS in a
// separate task and then merge everything using a single MERGE_ALL pass in order
// to minimize the number of produced .dex files since there is a systemic overhead
// (size-wise) when we have multiple .dex files.
if (creationConfig.debuggable) {
taskFactory.register(
DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_EXTERNAL_LIBS,
dexingType,
dexingUsingArtifactTransforms,
separateFileDependenciesDexingTask))
taskFactory.register(
DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_PROJECT,
dexingType,
dexingUsingArtifactTransforms))
taskFactory.register(
DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_LIBRARY_PROJECTS,
dexingType,
dexingUsingArtifactTransforms))
} else {
taskFactory.register(
DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_EXTERNAL_LIBS,
dexingType,
dexingUsingArtifactTransforms,
separateFileDependenciesDexingTask,
InternalMultipleArtifactType.EXTERNAL_LIBS_DEX))
taskFactory.register(
DexMergingTask.CreationAction(
creationConfig,
DexMergingAction.MERGE_ALL,
dexingType,
dexingUsingArtifactTransforms))
}
}
}
}
protected fun handleJacocoDependencies(creationConfig: ComponentCreationConfig) {
if (creationConfig.packageJacocoRuntime) {
val jacocoAgentRuntimeDependency = JacocoConfigurations.getAgentRuntimeDependency(
JacocoTask.getJacocoVersion(creationConfig))
project.dependencies
.add(
creationConfig.variantDependencies.runtimeClasspath.name,
jacocoAgentRuntimeDependency)
// we need to force the same version of Jacoco we use for instrumentation
creationConfig
.variantDependencies
.runtimeClasspath
.resolutionStrategy { r: ResolutionStrategy ->
r.force(jacocoAgentRuntimeDependency)
}
taskFactory.register(JacocoPropertiesTask.CreationAction(creationConfig))
}
}
private fun createJacocoTaskWithLegacyTransformSupport(creationConfig: ComponentCreationConfig) {
// Assume the following is true
// * test coverage is enabled
// * BooleanOption ENABLE_JACOCO_TRANSFORM_INSTRUMENTATION=true
// * a legacy transform is registered
val classesFromLegacyTransforms =
creationConfig.transformManager.getPipelineOutputAsFileCollection(
{ _, _ -> true},
{ types, _ -> types.contains(
com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES) }
)
if (creationConfig is ApplicationCreationConfig) {
taskFactory.register(
JacocoTask.CreationActionLegacyTransform(creationConfig, classesFromLegacyTransforms))
}
}
fun createJacocoTask(creationConfig: ComponentCreationConfig) {
@Suppress("DEPRECATION") // Legacy support
creationConfig
.transformManager
.consumeStreams(
mutableSetOf(com.android.build.api.transform.QualifiedContent.Scope.PROJECT),
setOf(com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES)
)
// Instrumented refers to ASM and not Jacoco in this case.
if (creationConfig.instrumentationCreationConfig?.projectClassesAreInstrumented == true) {
taskFactory.register(JacocoTask.CreationActionWithTransformAsmClasses(creationConfig))
} else {
taskFactory.register(JacocoTask.CreationActionWithNoTransformAsmClasses(creationConfig))
}
val instrumentedClasses: FileCollection =
if (creationConfig.isAndroidTestCoverageEnabled &&
creationConfig !is ApplicationCreationConfig) {
// For libraries that can be published,avoid publishing classes
// with runtime dependencies on Jacoco.
creationConfig.artifacts
.forScope(ScopedArtifacts.Scope.PROJECT)
.getFinalArtifacts(ScopedArtifact.CLASSES)
} else {
project.files(
creationConfig.artifacts.get(JACOCO_INSTRUMENTED_CLASSES),
project.files(creationConfig.artifacts.get(JACOCO_INSTRUMENTED_JARS)).asFileTree
)
}
@Suppress("DEPRECATION") // Legacy support
creationConfig
.transformManager
.addStream(
OriginalStream.builder("jacoco-instrumented-classes")
.addContentTypes(com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.PROJECT)
.setFileCollection(instrumentedClasses)
.build()
)
}
protected fun createDataBindingTasksIfNecessary(creationConfig: ComponentCreationConfig) {
val dataBindingEnabled = creationConfig.buildFeatures.dataBinding
val viewBindingEnabled = creationConfig.buildFeatures.viewBinding
if (!dataBindingEnabled && !viewBindingEnabled) {
return
}
taskFactory.register(
DataBindingMergeDependencyArtifactsTask.CreationAction(creationConfig))
DataBindingBuilder.setDebugLogEnabled(logger.isDebugEnabled)
taskFactory.register(DataBindingGenBaseClassesTask.CreationAction(creationConfig))
// DATA_BINDING_TRIGGER artifact is created for data binding only (not view binding)
if (dataBindingEnabled) {
if (creationConfig.services.projectOptions.get(BooleanOption.NON_TRANSITIVE_R_CLASS)
&& isKotlinKaptPluginApplied(project)) {
val kotlinVersion = getProjectKotlinPluginKotlinVersion(project)
if (kotlinVersion != null && kotlinVersion < KAPT_FIX_KOTLIN_VERSION) {
// Before Kotlin version 1.5.20 there was an issue with KAPT resolving files
// at configuration time. We only need this task as a workaround for it, if the
// version is newer than 1.5.20 or KAPT isn't applied, we can skip it.
taskFactory.register(
MergeRFilesForDataBindingTask.CreationAction(creationConfig))
}
}
taskFactory.register(DataBindingTriggerTask.CreationAction(creationConfig))
creationConfig.sources.java.addSource(
TaskProviderBasedDirectoryEntryImpl(
name = "databinding_generated",
directoryProvider = creationConfig.artifacts.get(
InternalArtifactType.DATA_BINDING_TRIGGER
),
)
)
setDataBindingAnnotationProcessorParams(creationConfig)
}
}
private fun setDataBindingAnnotationProcessorParams(
creationConfig: ComponentCreationConfig) {
val processorOptions = creationConfig.javaCompilation.annotationProcessor
val dataBindingArgs = createArguments(
creationConfig,
logger.isDebugEnabled,
DataBindingBuilder.getPrintMachineReadableOutput(),
isKotlinKaptPluginApplied(project),
getProjectKotlinPluginKotlinVersion(project))
// Even though at this point, the old variantDsl related objects are dead, the KAPT plugin
// is using reflection to query the [CompilerArgumentProvider] to look if databinding is
// turned on, so keep on adding to the [VariantDslInfo]'s list until KAPT switches to the
// new variant API.
creationConfig.oldVariantApiLegacySupport?.addDataBindingArgsToOldVariantApi(dataBindingArgs)
// add it the new Variant API objects, this is what our tasks use.
processorOptions.argumentProviders.add(dataBindingArgs)
}
/**
* Creates the final packaging task, and optionally the zipalign task (if the variant is signed)
*/
fun createPackagingTask(creationConfig: ApkCreationConfig) {
// ApkVariantData variantData = (ApkVariantData) variantScope.getVariantData();
val taskContainer = creationConfig.taskContainer
val signedApk = creationConfig.signingConfigImpl?.isSigningReady() ?: false
/*
* 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).
*/
val manifestType: InternalArtifactType<Directory> =
creationConfig.global.manifestArtifactType
val manifests = creationConfig.artifacts.get(manifestType)
// Common code for both packaging tasks.
val configureResourcesAndAssetsDependencies = Action { task: Task ->
task.dependsOn(taskContainer.mergeAssetsTask)
if (taskContainer.processAndroidResTask != null) {
task.dependsOn(taskContainer.processAndroidResTask)
}
}
taskFactory.register(
PackageApplication.CreationAction(
creationConfig,
creationConfig.paths.apkLocation,
manifests,
manifestType),
null,
object : TaskConfigAction<PackageApplication> {
override fun configure(task: PackageApplication) {
task.dependsOn(taskContainer.javacTask)
if (taskContainer.packageSplitResourcesTask != null) {
task.dependsOn(taskContainer.packageSplitResourcesTask)
}
if (taskContainer.packageSplitAbiTask != null) {
task.dependsOn(taskContainer.packageSplitAbiTask)
}
configureResourcesAndAssetsDependencies.execute(task)
}
},
null)
// create the listing file redirect
taskFactory.register(
ListingFileRedirectTask.CreationAction(
creationConfig = creationConfig,
taskSuffix = "Apk",
inputArtifactType = InternalArtifactType.APK_IDE_MODEL,
outputArtifactType = InternalArtifactType.APK_IDE_REDIRECT_FILE
)
)
taskContainer
.assembleTask
.configure { task: Task ->
task.dependsOn(
creationConfig.artifacts.get(SingleArtifact.APK),
)
}
// 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) {
createInstallTask(creationConfig)
}
// add an uninstall task
val uninstallTask = taskFactory.register(UninstallTask.CreationAction(creationConfig))
taskFactory.configure(UNINSTALL_ALL) { uninstallAll: Task ->
uninstallAll.dependsOn(uninstallTask)
}
}
protected open fun createInstallTask(creationConfig: ApkCreationConfig) {
taskFactory.register(InstallVariantTask.CreationAction(creationConfig))
}
protected fun createValidateSigningTask(creationConfig: ApkCreationConfig) {
if (creationConfig.signingConfigImpl?.isSigningReady() != true) {
return
}
val service: Provider<AndroidLocationsBuildService> =
getBuildService(
creationConfig.services.buildServiceRegistry,
AndroidLocationsBuildService::class.java
)
// FIXME create one per signing config instead of one per variant.
taskFactory.register(
ValidateSigningTask.CreationAction(
creationConfig,
service.get().getDefaultDebugKeystoreLocation()
))
}
/**
* Create assemble* and bundle* anchor tasks.
*
*
* This does not create the variant specific version of these tasks only the ones that are
* per build-type, per-flavor, per-flavor-combo and the main 'assemble' and 'bundle' ones.
*
* @param flavorCount the number of flavors
* @param flavorDimensionCount whether there are flavor dimensions at all.
*/
fun createAnchorAssembleTasks(
flavorCount: Int,
flavorDimensionCount: Int) {
// sub anchor tasks that the main 'assemble' and 'bundle task will depend.
val subAssembleTasks= mutableListOf<TaskProvider<out Task>>()
val subBundleTasks= mutableListOf<TaskProvider<out Task>?>()
// There are 2 different scenarios:
// 1. There are 1+ flavors. In this case the variant-specific assemble task is
// different from all the assemble<BuildType> or assemble<Flavor>
// 2. Else, the assemble<BuildType> is the same as the variant specific assemble task.
// Case #1
if (flavorCount > 0) {
// loop on the variants and record their build type/flavor usage.
// map from build type/flavor names to the variant-specific assemble/bundle tasks
val assembleMap: ListMultimap<String, TaskProvider<out Task>> =
ArrayListMultimap.create()
val bundleMap: ListMultimap<String, TaskProvider<out Task>?> =
ArrayListMultimap.create()
for (creationConfig in allPropertiesList) {
val componentType = creationConfig.componentType
if (!componentType.isNestedComponent) {
val taskContainer = creationConfig.taskContainer
val buildType = creationConfig.buildType
val assembleTask = taskContainer.assembleTask
if (buildType != null) {
assembleMap.put(buildType, assembleTask)
}
for (flavor in creationConfig.productFlavorList) {
assembleMap.put(flavor.name, assembleTask)
}
// if 2+ flavor dimensions, then make an assemble for the flavor combo
if (flavorDimensionCount > 1) {
assembleMap.put(creationConfig.flavorName, assembleTask)
}
// fill the bundle map only if the variant supports bundles.
if (componentType.isBaseModule) {
val bundleTask = taskContainer.bundleTask
if (buildType != null) {
bundleMap.put(buildType, bundleTask)
}
for (flavor in creationConfig.productFlavorList) {
bundleMap.put(flavor.name, bundleTask)
}
// if 2+ flavor dimensions, then make an assemble for the flavor combo
if (flavorDimensionCount > 1) {
bundleMap.put(creationConfig.flavorName, bundleTask)
}
}
}
}
// loop over the map of build-type/flavor to create tasks for each, setting a dependency
// on the variant-specific task.
// these keys should be the same for bundle and assemble
val dimensionKeys = assembleMap.keySet()
for (dimensionKey in dimensionKeys) {
val dimensionName = dimensionKey.usLocaleCapitalize()
// create the task and add it to the list
subAssembleTasks.add(
taskFactory.register(
"assemble$dimensionName"
) { task: Task ->
task.description = ("Assembles main outputs for all "
+ dimensionName
+ " variants.")
task.group = BasePlugin.BUILD_GROUP
task.dependsOn(assembleMap[dimensionKey])
})
val subBundleMap = bundleMap[dimensionKey]
if (!subBundleMap.isEmpty()) {
// create the task and add it to the list
subBundleTasks.add(
taskFactory.register(
"bundle$dimensionName"
) { task: Task ->
task.description = ("Assembles bundles for all "
+ dimensionName
+ " variants.")
task.group = BasePlugin.BUILD_GROUP
task.dependsOn(subBundleMap)
})
}
}
} else {
// Case #2
for (creationConfig in allPropertiesList) {
val componentType = creationConfig.componentType
if (!componentType.isNestedComponent) {
val taskContainer = creationConfig.taskContainer
subAssembleTasks.add(taskContainer.assembleTask)
if (componentType.isBaseModule) {
subBundleTasks.add(taskContainer.bundleTask)
}
}
}
}
// ---
// ok now we can create the main 'assemble' and 'bundle' tasks and make them depend on the
// sub-tasks.
if (subAssembleTasks.isNotEmpty()) {
// "assemble" task is already created by the java base plugin.
taskFactory.configure(
"assemble"
) { task: Task ->
task.description = "Assemble main outputs for all the variants."
task.group = BasePlugin.BUILD_GROUP
task.dependsOn(subAssembleTasks)
}
}
if (subBundleTasks.isNotEmpty()) {
// root bundle task
taskFactory.register(
"bundle"
) { task: Task ->
task.description = "Assemble bundles for all the variants."
task.group = BasePlugin.BUILD_GROUP
task.dependsOn(subBundleTasks)
}
}
}
private fun createAssembleTask(component: ComponentCreationConfig) {
taskFactory.register(
component.computeTaskName("assemble"),
null /*preConfigAction*/,
object : TaskConfigAction<Task> {
override fun configure(task: Task) {
task.description =
"Assembles main output for variant " + component.name
}
},
object : TaskProviderCallback<Task> {
override fun handleProvider(taskProvider: TaskProvider<Task>) {
component.taskContainer.assembleTask =
taskProvider
}
}
)
}
private fun createBundleTask(component: ComponentCreationConfig) {
taskFactory.register(
component.computeTaskName("bundle"),
null,
object : TaskConfigAction<Task> {
override fun configure(task: Task) {
task.description = "Assembles bundle for variant " + component.name
task.dependsOn(component.artifacts.get(SingleArtifact.BUNDLE))
}
},
object : TaskProviderCallback<Task> {
override fun handleProvider(taskProvider: TaskProvider<Task>) {
component.taskContainer.bundleTask = taskProvider
}
}
)
}
protected open fun maybeCreateJavaCodeShrinkerTask(
creationConfig: ConsumableCreationConfig) {
if (creationConfig.minifiedEnabled) {
doCreateJavaCodeShrinkerTask(creationConfig)
}
}
/**
* Actually creates the minify transform, using the given mapping configuration. The mapping is
* only used by test-only modules.
*/
protected fun doCreateJavaCodeShrinkerTask(
creationConfig: ConsumableCreationConfig,
isTestApplication: Boolean = false) {
// The compile R class jar is added to the classes to be processed in libraries so that
// proguard can shrink an empty library project, as the R class is always kept and
// then removed by library jar transforms.
val addCompileRClass = (this is LibraryTaskManager
&& creationConfig.buildFeatures.androidResources)
val task: TaskProvider<out Task> =
createR8Task(creationConfig, isTestApplication, addCompileRClass)
if (creationConfig.postProcessingFeatures != null) {
val checkFilesTask =
taskFactory.register(CheckProguardFiles.CreationAction(creationConfig))
task.dependsOn(checkFilesTask)
}
}
private fun createR8Task(
creationConfig: ConsumableCreationConfig,
isTestApplication: Boolean,
addCompileRClass: Boolean): TaskProvider<R8Task> {
if (creationConfig is ApplicationCreationConfig) {
publishArtifactsToDynamicFeatures(
creationConfig,
FEATURE_DEX,
AndroidArtifacts.ArtifactType.FEATURE_DEX,
null)
publishArtifactsToDynamicFeatures(
creationConfig,
InternalArtifactType.FEATURE_SHRUNK_JAVA_RES,
AndroidArtifacts.ArtifactType.FEATURE_SHRUNK_JAVA_RES,
DOT_JAR)
}
R8ParallelBuildService.RegistrationAction(
project,
creationConfig.services.projectOptions.get(IntegerOption.R8_MAX_WORKERS)
).execute()
return taskFactory.register(
R8Task.CreationAction(creationConfig, isTestApplication, addCompileRClass))
}
/**
* We have a separate method for publishing artifacts back to the features (instead of using the
* typical PublishingSpecs pipeline) because multiple artifacts are published with different
* attributes for the given ArtifactType in this case.
*
* <p>This method will publish each of the children of the directory corresponding to the given
* internalArtifactType. The children files' names must match the names of the corresponding
* feature modules + the given fileExtension.
*
* @param creationConfig the ApplicationCreationConfig
* @param internalArtifactType the InternalArtifactType of the directory whose children will be
* published to the features
* @param artifactType the ArtifactType used when publishing to the features
* @param fileExtension the fileExtension of the directory's children files, or null if the
* children files are directories
*/
private fun publishArtifactsToDynamicFeatures(
creationConfig: ApplicationCreationConfig,
internalArtifactType: InternalArtifactType<Directory>,
artifactType: AndroidArtifacts.ArtifactType,
fileExtension: String?) {
// first calculate the list of module paths
val modulePaths: Collection<String> = if (extension is BaseAppModuleExtension) {
creationConfig.global.dynamicFeatures
} else {
return
}
val configuration =
creationConfig.variantDependencies.getElements(PublishedConfigSpec(PublishedConfigType.RUNTIME_ELEMENTS))
Preconditions.checkNotNull(
configuration,
"Publishing to Runtime Element with no Runtime Elements configuration object. "
+ "componentType: "
+ creationConfig.componentType)
val artifact = creationConfig.artifacts.get(internalArtifactType)
val artifactDirectory = project.objects.directoryProperty()
artifactDirectory.set(artifact)
for (modulePath in modulePaths) {
val file = artifactDirectory.file(getFeatureFileName(modulePath, fileExtension))
publishArtifactToConfiguration(
configuration!!,
file,
artifactType,
AndroidAttributes(
AndroidArtifacts.MODULE_PATH to project.absoluteProjectPath(modulePath)
)
)
}
}
/**
* If resource shrinker is enabled, set-up and register the appropriate tasks.
*/
private fun maybeCreateResourcesShrinkerTasks(
creationConfig: ConsumableCreationConfig) {
if (creationConfig.androidResourcesCreationConfig?.useResourceShrinker != true) {
return
}
if (creationConfig.componentType.isDynamicFeature) {
// For bundles resources are shrunk once bundle is packaged so the task is applicable
// for base module only.
return
}
if (creationConfig.services.projectOptions.get(BooleanOption.ENABLE_NEW_RESOURCE_SHRINKER)) {
// Shrink resources in APK with a new resource shrinker and produce stripped res
// package.
taskFactory.register(ShrinkResourcesNewShrinkerTask.CreationAction(creationConfig))
// Shrink resources in bundles with new resource shrinker.
taskFactory.register(ShrinkAppBundleResourcesTask.CreationAction(creationConfig))
} else {
// Shrink resources in APK with old resource shrinker and produce stripped res package.
taskFactory.register(ShrinkResourcesOldShrinkerTask.CreationAction(creationConfig))
// Shrink base module resources in proto format to be packaged to bundle.
taskFactory.register(
LegacyShrinkBundleModuleResourcesTask.CreationAction(creationConfig))
}
}
private fun createReportTasks() {
taskFactory.register(
"androidDependencies",
DependencyReportTask::class.java
) { task: DependencyReportTask ->
task.description = "Displays the Android dependencies of the project."
task.variants.setDisallowChanges(variantPropertiesList)
task.nestedComponents.setDisallowChanges(nestedComponents)
task.group = ANDROID_GROUP
task.mavenCoordinateCache.setDisallowChanges(
getBuildService(
project.gradle.sharedServices,
MavenCoordinatesCacheBuildService::class.java
).get()
)
task.notCompatibleWithConfigurationCache(
"DependencyReportTask not compatible with config caching"
)
}
val signingReportComponents = allPropertiesList.stream()
.filter { component: ComponentCreationConfig ->
component is ApkCreationConfig
}
.map { component -> component as ApkCreationConfig }
.collect(Collectors.toList())
if (signingReportComponents.isNotEmpty()) {
taskFactory.register(
"signingReport",
SigningReportTask::class.java
) { task: SigningReportTask ->
task.description = "Displays the signing info for the base and test modules"
task.setComponents(signingReportComponents)
task.group = ANDROID_GROUP
task.notCompatibleWithConfigurationCache(
"SigningReportTask is not compatible with config caching")
}
}
createDependencyAnalyzerTask()
val checkJetifierBuildService =
CheckJetifierBuildService
.RegistrationAction(project, globalConfig.services.projectOptions)
.execute()
taskFactory.register(
CheckJetifierTask.CreationAction(
globalConfig,
checkJetifierBuildService,
variants,
testComponents,
testFixturesComponents
)
)
}
private fun createDependencyAnalyzerTask() {
for (variant in variantPropertiesList) {
taskFactory.register(AnalyzeDependenciesTask.CreationAction(variant))
}
for (component in nestedComponents) {
taskFactory.register(AnalyzeDependenciesTask.CreationAction(component))
}
}
fun createAnchorTasks(creationConfig: ComponentCreationConfig) {
createVariantPreBuildTask(creationConfig)
// also create sourceGenTask
creationConfig
.taskContainer
.sourceGenTask = taskFactory.register(
creationConfig.computeTaskName("generate", "Sources")
) { task: Task ->
task.dependsOn(COMPILE_LINT_CHECKS_TASK)
if (creationConfig.componentType.isAar) {
task.dependsOn(PrepareLintJarForPublish.NAME)
}
creationConfig.oldVariantApiLegacySupport?.variantData?.extraGeneratedResFolders?.let {
task.dependsOn(it)
}
}
// and resGenTask
creationConfig
.taskContainer
.resourceGenTask = taskFactory.register(
creationConfig.computeTaskName("generate", "Resources"))
creationConfig
.taskContainer
.assetGenTask =
taskFactory.register(creationConfig.computeTaskName("generate", "Assets"))
if (!creationConfig.componentType.isForTesting
&& creationConfig.isAndroidTestCoverageEnabled) {
creationConfig
.taskContainer
.coverageReportTask = taskFactory.register(
creationConfig.computeTaskName("create", "CoverageReport")
) { task: Task ->
task.group = JavaBasePlugin.VERIFICATION_GROUP
task.description = String.format(
"Creates test coverage reports for the %s variant.",
creationConfig.name)
}
}
// and compile task
createCompileAnchorTask(creationConfig)
}
protected open fun createVariantPreBuildTask(creationConfig: ComponentCreationConfig) {
// default pre-built task.
createDefaultPreBuildTask(creationConfig)
}
protected fun createDefaultPreBuildTask(creationConfig: ComponentCreationConfig) {
taskFactory.register(PreBuildCreationAction(creationConfig))
}
abstract class AbstractPreBuildCreationAction<
TaskT : AndroidVariantTask,
ComponentT: ComponentCreationConfig>(
creationConfig: ComponentT
) : VariantTaskCreationAction<TaskT, ComponentT>(creationConfig, false) {
override val name: String
get() = computeTaskName("pre", "Build")
override fun handleProvider(taskProvider: TaskProvider<TaskT>) {
super.handleProvider(taskProvider)
creationConfig.taskContainer.preBuildTask = taskProvider
}
override fun configure(task: TaskT) {
super.configure(task)
task.dependsOn(MAIN_PREBUILD)
}
}
private class PreBuildCreationAction(creationConfig: ComponentCreationConfig) :
AbstractPreBuildCreationAction<AndroidVariantTask, ComponentCreationConfig>(creationConfig) {
override val type: Class<AndroidVariantTask>
get() = AndroidVariantTask::class.java
}
private fun createCompileAnchorTask(creationConfig: ComponentCreationConfig) {
val taskContainer = creationConfig.taskContainer
taskContainer.compileTask = taskFactory.register(
creationConfig.computeTaskName("compile", "Sources")
) { task: Task -> task.group = BUILD_GROUP }
}
private fun addBindingDependenciesIfNecessary(dataBindingOptions: DataBinding) {
val viewBindingEnabled = allPropertiesList.stream()
.anyMatch { componentProperties: ComponentCreationConfig -> componentProperties.buildFeatures.viewBinding }
val dataBindingEnabled = allPropertiesList.stream()
.anyMatch { componentProperties: ComponentCreationConfig -> componentProperties.buildFeatures.dataBinding }
val useAndroidX = globalConfig.services.projectOptions.get(BooleanOption.USE_ANDROID_X)
val dataBindingBuilder = localConfig.dataBindingBuilder
if (viewBindingEnabled) {
val version = dataBindingBuilder.getLibraryVersion(dataBindingBuilder.compilerVersion)
val groupAndArtifact =
if (useAndroidX)
SdkConstants.ANDROIDX_VIEW_BINDING_ARTIFACT
else SdkConstants.VIEW_BINDING_ARTIFACT
project.dependencies.add("api", "$groupAndArtifact:$version")
}
if (dataBindingEnabled) {
val version = MoreObjects.firstNonNull(
dataBindingOptions.version,
dataBindingBuilder.compilerVersion)
val baseLibArtifact =
if (useAndroidX)
SdkConstants.ANDROIDX_DATA_BINDING_BASELIB_ARTIFACT
else SdkConstants.DATA_BINDING_BASELIB_ARTIFACT
project.dependencies
.add(
"api",
baseLibArtifact
+ ":"
+ dataBindingBuilder.getBaseLibraryVersion(version))
project.dependencies
.add(
"annotationProcessor",
SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT
+ ":"
+ version)
// TODO load config name from source sets
if (dataBindingOptions.isEnabledForTests || this is LibraryTaskManager) {
val dataBindingArtifact =
SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT + ":" + version
project.dependencies
.add("androidTestAnnotationProcessor", dataBindingArtifact)
if (globalConfig.testOptions.unitTests.isIncludeAndroidResources) {
project.dependencies.add("testAnnotationProcessor", dataBindingArtifact)
}
}
if ((dataBindingOptions as DataBindingOptions).addDefaultAdapters) {
val libArtifact =
if (useAndroidX)
SdkConstants.ANDROIDX_DATA_BINDING_LIB_ARTIFACT
else SdkConstants.DATA_BINDING_LIB_ARTIFACT
val adaptersArtifact =
if (useAndroidX)
SdkConstants.ANDROIDX_DATA_BINDING_ADAPTER_LIB_ARTIFACT
else SdkConstants.DATA_BINDING_ADAPTER_LIB_ARTIFACT
project.dependencies
.add(
"api",
libArtifact + ":" + dataBindingBuilder.getLibraryVersion(version))
project.dependencies
.add(
"api",
adaptersArtifact
+ ":"
+ dataBindingBuilder.getBaseAdaptersVersion(version))
}
addDataBindingKtxIfNecessary(project, dataBindingOptions, dataBindingBuilder, version,
useAndroidX)
project.pluginManager
.withPlugin(KOTLIN_KAPT_PLUGIN_ID) {
configureKotlinKaptTasksForDataBinding(project, version)
}
}
}
private fun addDataBindingKtxIfNecessary(
project: Project,
dataBindingOptions: DataBindingOptions,
dataBindingBuilder: DataBindingBuilder,
version: String,
useAndroidX: Boolean
) {
val ktxDataBindingDslValue: Boolean? = dataBindingOptions.addKtx
val ktxGradlePropertyValue = globalConfig.services.projectOptions
.get(BooleanOption.ENABLE_DATABINDING_KTX)
val enableKtx = ktxDataBindingDslValue ?: ktxGradlePropertyValue
if (enableKtx) {
// Add Ktx dependency if AndroidX and Kotlin is used
if (useAndroidX && isKotlinPluginApplied(project)) {
project.dependencies
.add(
"api",
DATA_BINDING_KTX_LIB_ARTIFACT
+ ":"
+ dataBindingBuilder.getLibraryVersion(version))
} else {
// Warn if user manually enabled Ktx via the DSL option and
// it's not a Kotlin or AndroidX project.
if (ktxDataBindingDslValue == true) {
globalConfig
.services
.issueReporter
.reportWarning(
IssueReporter.Type.GENERIC,
"The `android.dataBinding.addKtx` DSL option has no effect because " +
"the `android.useAndroidX` property is not enabled or " +
"the project does not use Kotlin."
)
}
}
}
}
private fun configureKotlinKaptTasksForDataBinding(
project: Project,
version: String) {
val kaptDeps = project.configurations.getByName("kapt").allDependencies
kaptDeps.forEach(
Consumer { dependency: Dependency ->
// if it is a data binding compiler dependency w/ a different version, report
// error
if (dependency.group + ":" + dependency.name == SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT
&& dependency.version != version) {
val depString = (dependency.group
+ ":"
+ dependency.name
+ ":"
+ dependency.version)
globalConfig
.services
.issueReporter
.reportError(
IssueReporter.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.dependencies
.add(
"kapt",
SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT + ":" + version)
var kaptTaskClass: Class<out Task?>? = null
try {
kaptTaskClass =
Class.forName("org.jetbrains.kotlin.gradle.internal.KaptTask") as Class<out Task?>
} catch (e: ClassNotFoundException) {
logger.error(
"Kotlin plugin is applied to the project "
+ project.path
+ " 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
val kaptTaskLookup= allPropertiesList
.map{ it.computeTaskName("kapt", "kotlin") to it }
.toMap()
project.tasks
.withType(
kaptTaskClass,
Action { kaptTask: Task ->
// find matching scope.
val matchingComponent = kaptTaskLookup[kaptTask.name]
matchingComponent?.let {
configureKaptTaskInScopeForDataBinding(it,
kaptTask)
}
} as Action<Task>)
}
private fun configureKaptTaskInScopeForDataBinding(
creationConfig: ComponentCreationConfig, kaptTask: Task) {
val dataBindingArtifactDir = project.objects.directoryProperty()
val exportClassListFile = project.objects.fileProperty()
val kaptTaskProvider = taskFactory.named(kaptTask.name)
// Register data binding artifacts as outputs
registerDataBindingOutputs(
dataBindingArtifactDir,
exportClassListFile,
creationConfig.componentType.isExportDataBindingClassList,
kaptTaskProvider,
creationConfig.artifacts,
false // forJavaCompile = false as this task is Kapt
)
// Register the DirectoryProperty / RegularFileProperty as outputs as they are not yet
// annotated as outputs (same with the code in JavaCompileCreationAction.configure).
kaptTask.outputs
.dir(dataBindingArtifactDir)
.withPropertyName("dataBindingArtifactDir")
if (creationConfig.componentType.isExportDataBindingClassList) {
kaptTask.outputs
.file(exportClassListFile)
.withPropertyName("dataBindingExportClassListFile")
}
}
protected fun configureTestData(
creationConfig: InstrumentedTestCreationConfig, testData: AbstractTestDataImpl) {
testData.animationsDisabled = creationConfig
.services
.provider(globalConfig.testOptions::animationsDisabled)
testData.setExtraInstrumentationTestRunnerArgs(
creationConfig
.services
.projectOptions
.extraInstrumentationTestRunnerArgs)
}
private fun maybeCreateCheckDuplicateClassesTask(
creationConfig: ComponentCreationConfig) {
if (creationConfig
.services
.projectOptions[BooleanOption.ENABLE_DUPLICATE_CLASSES_CHECK]) {
taskFactory.register(CheckDuplicateClassesTask.CreationAction(creationConfig))
}
}
private fun maybeCreateDesugarLibTask(
apkCreationConfig: ApkCreationConfig,
enableDexingArtifactTransform: Boolean) {
val separateFileDependenciesDexingTask =
(apkCreationConfig.getJava8LangSupportType() == Java8LangSupport.D8
&& enableDexingArtifactTransform)
if (apkCreationConfig.shouldPackageDesugarLibDex) {
taskFactory.register(
L8DexDesugarLibTask.CreationAction(
apkCreationConfig,
enableDexingArtifactTransform,
separateFileDependenciesDexingTask))
}
if(apkCreationConfig.componentType.isDynamicFeature
&& apkCreationConfig.needsShrinkDesugarLibrary
) {
taskFactory.register(
DesugarLibKeepRulesMergeTask.CreationAction(
apkCreationConfig,
enableDexingArtifactTransform,
separateFileDependenciesDexingTask
)
)
}
}
private fun getManagedDevices(): List<ManagedVirtualDevice> {
val managedDevices = mutableListOf<ManagedVirtualDevice>()
// First add devices from within the managed device block in testOptions.
extension
.testOptions
.managedDevices
.devices
.forEach { device ->
if (device is ManagedVirtualDevice) {
managedDevices.add(device)
} else {
error("Unsupported managed device type: ${device.javaClass}")
}
}
return managedDevices
}
private fun getDeviceGroups(): Collection<DeviceGroup> =
globalConfig.testOptions.managedDevices.groups
@Suppress("DEPRECATION") // Legacy support
protected fun maybeCreateTransformClassesWithAsmTask(
creationConfig: ComponentCreationConfig
) {
val instrumentationCreationConfig = creationConfig.instrumentationCreationConfig ?: return
if (instrumentationCreationConfig.projectClassesAreInstrumented) {
creationConfig
.transformManager
.consumeStreams(
mutableSetOf(com.android.build.api.transform.QualifiedContent.Scope.PROJECT),
setOf(com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES))
taskFactory.register(
TransformClassesWithAsmTask.CreationAction(
creationConfig
)
)
if (instrumentationCreationConfig.asmFramesComputationMode
== FramesComputationMode.COMPUTE_FRAMES_FOR_ALL_CLASSES) {
taskFactory.register(RecalculateStackFramesTask.CreationAction(creationConfig))
}
creationConfig
.transformManager
.addStream(
OriginalStream.builder("asm-instrumented-classes")
.addContentTypes(com.android.build.api.transform.QualifiedContent.DefaultContentType.CLASSES)
.addScope(com.android.build.api.transform.QualifiedContent.Scope.PROJECT)
.setFileCollection(
instrumentationCreationConfig.projectClassesPostInstrumentation
).build()
)
}
}
companion object {
private const val MULTIDEX_VERSION = "1.0.2"
private const val COM_ANDROID_SUPPORT_MULTIDEX =
"com.android.support:multidex:" + MULTIDEX_VERSION
private val ANDROIDX_MULTIDEX_MULTIDEX = androidXMappings.getValue("com.android.support:multidex")
private const val COM_ANDROID_SUPPORT_MULTIDEX_INSTRUMENTATION =
"com.android.support:multidex-instrumentation:" + MULTIDEX_VERSION
private val ANDROIDX_MULTIDEX_MULTIDEX_INSTRUMENTATION =
androidXMappings.getValue("com.android.support:multidex-instrumentation")
// name of the task that triggers compilation of the custom lint Checks
private const val COMPILE_LINT_CHECKS_TASK = "compileLintChecks"
const val INSTALL_GROUP = "Install"
const val BUILD_GROUP = BasePlugin.BUILD_GROUP
const val ANDROID_GROUP = "Android"
const val 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.
const val MAIN_PREBUILD = "preBuild"
const val UNINSTALL_ALL = "uninstallAll"
const val DEVICE_CHECK = "deviceCheck"
const val DEVICE_ANDROID_TEST = BuilderConstants.DEVICE + ComponentType.ANDROID_TEST_SUFFIX
const val CONNECTED_CHECK = "connectedCheck"
const val ALL_DEVICES_CHECK = "allDevicesCheck"
const val CONNECTED_ANDROID_TEST =
BuilderConstants.CONNECTED + ComponentType.ANDROID_TEST_SUFFIX
const val ASSEMBLE_ANDROID_TEST = "assembleAndroidTest"
const val LINT = "lint"
const val LINT_FIX = "lintFix"
// Temporary static variables for Kotlin+Compose configuration
const val KOTLIN_COMPILER_CLASSPATH_CONFIGURATION_NAME = "kotlinCompilerClasspath"
const val COMPOSE_KOTLIN_COMPILER_EXTENSION_VERSION = "1.2.0"
const val CREATE_MOCKABLE_JAR_TASK_NAME = "createMockableJar"
/**
* Create tasks before the evaluation (on plugin apply). This is useful for tasks that could be
* referenced by custom build logic.
*
* @param componentType the main variant type as returned by the [ ]
* @param sourceSetContainer the container of source set from the DSL.
*/
@JvmStatic
fun createTasksBeforeEvaluate(
project: Project,
componentType: ComponentType,
sourceSetContainer: Iterable<AndroidSourceSet?>,
globalConfig: GlobalTaskCreationConfig
) {
val taskFactory = TaskFactoryImpl(project.tasks)
taskFactory.register(
UNINSTALL_ALL
) { uninstallAllTask: Task ->
uninstallAllTask.description = "Uninstall all applications."
uninstallAllTask.group = INSTALL_GROUP
}
taskFactory.register(
DEVICE_CHECK
) { deviceCheckTask: Task ->
deviceCheckTask.description =
"Runs all device checks using Device Providers and Test Servers."
deviceCheckTask.group = JavaBasePlugin.VERIFICATION_GROUP
}
taskFactory.register(
CONNECTED_CHECK,
DeviceSerialTestTask::class.java
) { connectedCheckTask: DeviceSerialTestTask ->
connectedCheckTask.description =
"Runs all device checks on currently connected devices."
connectedCheckTask.group = JavaBasePlugin.VERIFICATION_GROUP
}
// Make sure MAIN_PREBUILD runs first:
taskFactory.register(MAIN_PREBUILD)
taskFactory.register(ExtractProguardFiles.CreationAction(globalConfig))
.configure { it: ExtractProguardFiles -> it.dependsOn(MAIN_PREBUILD) }
taskFactory.register(SourceSetsTask.CreationAction(sourceSetContainer))
taskFactory.register(
ASSEMBLE_ANDROID_TEST
) { assembleAndroidTestTask: Task ->
assembleAndroidTestTask.group = BasePlugin.BUILD_GROUP
assembleAndroidTestTask.description = "Assembles all the Test applications."
}
taskFactory.register(LintCompile.CreationAction(globalConfig))
// Don't register global lint or lintFix tasks for dynamic features because dynamic
// features are analyzed and their lint issues are reported and/or fixed when running
// lint or lintFix from the base app.
if (!componentType.isForTesting && !componentType.isDynamicFeature) {
LintTaskManager(globalConfig, taskFactory, project).createBeforeEvaluateLintTasks()
}
// for testing only.
taskFactory.register(
"resolveConfigAttr",
ConfigAttrTask::class.java) { task: ConfigAttrTask -> task.resolvable = true }
taskFactory.register(
"consumeConfigAttr",
ConfigAttrTask::class.java) { task: ConfigAttrTask -> task.consumable = true }
createCoreLibraryDesugaringConfig(project)
}
fun createCoreLibraryDesugaringConfig(project: Project) {
var coreLibraryDesugaring =
project.configurations.findByName(VariantDependencies.CONFIG_NAME_CORE_LIBRARY_DESUGARING)
if (coreLibraryDesugaring == null) {
coreLibraryDesugaring =
project.configurations.create(VariantDependencies.CONFIG_NAME_CORE_LIBRARY_DESUGARING)
coreLibraryDesugaring.isVisible = false
coreLibraryDesugaring.isCanBeConsumed = false
coreLibraryDesugaring.description = "Configuration to desugar libraries"
}
}
private fun generatesProguardOutputFile(creationConfig: ComponentCreationConfig): Boolean {
return ((creationConfig is ConsumableCreationConfig
&& creationConfig.minifiedEnabled)
|| creationConfig.componentType.isDynamicFeature)
}
/** Makes the given task the one used by top-level "compile" task. */
@JvmStatic
fun setJavaCompilerTask(
javaCompilerTask: TaskProvider<out JavaCompile>,
creationConfig: ComponentCreationConfig) {
creationConfig.taskContainer.compileTask.dependsOn(javaCompilerTask)
}
/**
* Method to reliably generate matching feature file names when dex splitter is used.
*
* @param modulePath the gradle module path for the feature
* @param fileExtension the desired file extension (e.g., ".jar"), or null if no file extension
* (e.g., for a folder)
* @return name of file
*/
fun getFeatureFileName(
modulePath: String, fileExtension: String?): String {
val featureName = getFeatureName(modulePath)
val sanitizedFeatureName = if (":" == featureName) "" else featureName
// Prepend "feature-" to fileName in case a non-base module has module path ":base".
return "feature-" + sanitizedFeatureName + Strings.nullToEmpty(fileExtension)
}
}
}