| /* |
| * Copyright (C) 2022 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.build.api.dsl.DataBinding |
| import com.android.build.api.variant.VariantBuilder |
| import com.android.build.gradle.BaseExtension |
| import com.android.build.gradle.internal.attribution.CheckJetifierBuildService |
| import com.android.build.gradle.internal.component.DeviceTestCreationConfig |
| import com.android.build.gradle.internal.component.ApkCreationConfig |
| import com.android.build.gradle.internal.component.ComponentCreationConfig |
| import com.android.build.gradle.internal.component.HostTestCreationConfig |
| import com.android.build.gradle.internal.component.NestedComponentCreationConfig |
| import com.android.build.gradle.internal.component.TestComponentCreationConfig |
| import com.android.build.gradle.internal.component.TestFixturesCreationConfig |
| import com.android.build.gradle.internal.component.VariantCreationConfig |
| import com.android.build.gradle.internal.cxx.configure.createCxxTasks |
| import com.android.build.gradle.internal.dependency.AndroidXDependencySubstitution |
| import com.android.build.gradle.internal.dsl.DataBindingOptions |
| import com.android.build.gradle.internal.ide.dependencies.MavenCoordinatesCacheBuildService |
| import com.android.build.gradle.internal.lint.LintTaskManager |
| import com.android.build.gradle.internal.profile.AnalyticsConfiguratorService |
| import com.android.build.gradle.internal.services.AndroidLocationsBuildService |
| import com.android.build.gradle.internal.services.getBuildService |
| import com.android.build.gradle.internal.tasks.CheckJetifierTask |
| import com.android.build.gradle.internal.tasks.DependencyReportTask |
| import com.android.build.gradle.internal.tasks.SigningReportTask |
| import com.android.build.gradle.internal.tasks.ValidateSigningTask |
| import com.android.build.gradle.internal.tasks.factory.GlobalTaskCreationConfig |
| import com.android.build.gradle.internal.tasks.factory.TaskManagerConfig |
| 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.configureKotlinCompileTasks |
| import com.android.build.gradle.internal.utils.isKotlinPluginAppliedInTheSameClassloader |
| import com.android.build.gradle.internal.utils.recordKgpPropertiesForAnalytics |
| import com.android.build.gradle.internal.utils.setDisallowChanges |
| 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.tasks.AnalyzeDependenciesTask |
| import com.android.build.gradle.tasks.registerDataBindingOutputs |
| import com.android.builder.core.ComponentType |
| import com.android.builder.errors.IssueReporter |
| import com.android.utils.usLocaleCapitalize |
| import com.google.common.base.MoreObjects |
| import com.google.common.collect.ArrayListMultimap |
| import com.google.common.collect.ListMultimap |
| import org.gradle.api.Project |
| import org.gradle.api.Task |
| import org.gradle.api.artifacts.Dependency |
| import org.gradle.api.plugins.BasePlugin |
| import org.gradle.api.provider.Provider |
| import org.gradle.api.tasks.TaskProvider |
| import java.util.function.Consumer |
| import java.util.stream.Collectors |
| |
| abstract class VariantTaskManager<VariantBuilderT : VariantBuilder, VariantT : VariantCreationConfig>( |
| project: Project, |
| private val variants: Collection<ComponentInfo<VariantBuilderT, VariantT>>, |
| private val testComponents: Collection<TestComponentCreationConfig>, |
| private val testFixturesComponents: Collection<TestFixturesCreationConfig>, |
| globalConfig: GlobalTaskCreationConfig, |
| @JvmField protected val localConfig: TaskManagerConfig, |
| @JvmField protected val extension: BaseExtension, |
| ): TaskManager(project, globalConfig) { |
| |
| @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 |
| |
| |
| private val lintTaskManager = LintTaskManager(globalConfig, taskFactory, project) |
| |
| private val unitTestTaskManager = UnitTestTaskManager(project, globalConfig) |
| private val screenshotTestTaskManager = ScreenshotTestTaskManager(project, globalConfig) |
| private val androidTestTaskManager = AndroidTestTaskManager(project, globalConfig) |
| private val testFixturesTaskManager = TestFixturesTaskManager(project, globalConfig, localConfig) |
| |
| /** |
| * This is the main entry point into the task manager |
| * |
| * This creates the tasks for all the variants and all the nested 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(globalConfig.taskNames.compileLintChecks) { task: Task -> |
| task.dependsOn(globalConfig.localCustomLintChecks) |
| } |
| |
| // Create top level test tasks. |
| unitTestTaskManager.createTopLevelTasks() |
| androidTestTaskManager.createTopLevelTasks() |
| // For Screenshot tests, only create tasks if we have the screenshot test component |
| if (testComponents.any { it.componentType.isForScreenshotPreview }) { |
| screenshotTestTaskManager.createTopLevelTasks() |
| } |
| |
| // Create tasks for all variants (main, testFixtures and tests) |
| for (variant in variants) { |
| createTasksForVariant(variant) |
| } |
| for (testFixturesComponent in testFixturesComponents) { |
| testFixturesTaskManager.createTasks(testFixturesComponent) |
| } |
| for (testComponent in testComponents) { |
| createTasksForTest(testComponent) |
| } |
| createTopLevelTasks(componentType, variantModel) |
| } |
| |
| 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( |
| componentInfo: ComponentInfo<VariantBuilderT, VariantT>, |
| ) { |
| val variant = componentInfo.variant |
| val componentType = variant.componentType |
| val variantDependencies = variant.variantDependencies |
| if (variant is ApkCreationConfig && variant.dexing.dexingType.isLegacyMultiDex) { |
| val multiDexDependency = |
| if (variant |
| .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 (variant.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(variant) |
| |
| doCreateTasksForVariant(componentInfo) |
| |
| // now that the onVariants callback has run and tasks have been created, |
| // register all the listeners so we can ensure there is a Task providing the artifact |
| // they are listening too. |
| variant.artifacts.listenerManager.executeActions() |
| } |
| |
| open fun createTopLevelTasks(componentType: ComponentType, variantModel: VariantModel) { |
| lintTaskManager.createLintTasks( |
| componentType, |
| variantModel, |
| variantPropertiesList, |
| testComponents, |
| globalConfig.services.projectOptions.get(BooleanOption.LINT_ANALYSIS_PER_COMPONENT) |
| ) |
| 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 |
| ) |
| |
| // Global tasks required for privacy sandbox sdk consumption |
| if (variants.any { it.variant.privacySandboxCreationConfig != null }) { |
| taskFactory.register(ValidateSigningTask.PrivacySandboxSdkCreationAction(globalConfig)) |
| } |
| } |
| |
| 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 |
| } |
| } |
| 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)) |
| } |
| } |
| |
| /** 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).dexing.dexingType.isLegacyMultiDex) { |
| 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) |
| } |
| androidTestTaskManager.createTasks(testVariant as DeviceTestCreationConfig) |
| } else if (testVariant.componentType.isForScreenshotPreview) { |
| // SCREENSHOT_TEST |
| screenshotTestTaskManager.createTasks(testVariant as HostTestCreationConfig) |
| } else { |
| // UNIT_TEST |
| unitTestTaskManager.createTasks(testVariant as HostTestCreationConfig) |
| } |
| } |
| |
| private fun configureKotlinPluginTasksIfNecessary() { |
| if (!isKotlinPluginAppliedInTheSameClassloader(project)) { |
| return |
| } |
| val composeIsEnabled = allPropertiesList |
| .any { componentProperties: ComponentCreationConfig -> |
| componentProperties.buildFeatures.compose } |
| recordKgpPropertiesForAnalytics(project, allPropertiesList) |
| 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 |
| configureKotlinCompileTasks(project, allPropertiesList) { kotlinCompile, creationConfig -> |
| addComposeArgsToKotlinCompile( |
| kotlinCompile, creationConfig, project.files(kotlinExtension), useLiveLiterals |
| ) |
| } |
| } |
| |
| 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.enableForTests || this is LibraryTaskManager) { |
| val dataBindingArtifact = |
| SdkConstants.DATA_BINDING_ANNOTATION_PROCESSOR_ARTIFACT + ":" + version |
| project.dependencies |
| .add("androidTestAnnotationProcessor", dataBindingArtifact) |
| if (globalConfig.unitTestOptions.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 && isKotlinPluginAppliedInTheSameClassloader(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 |
| } |
| |
| // Find a matching variant and configure each Kapt task. Note: The task's name could be |
| // kapt${variant}Kotlin, or kapt${variant}KotlinAndroid in KMP projects. |
| val kaptTaskNameOrPrefixToVariant = |
| allPropertiesList.associateBy { it.computeTaskNameInternal("kapt", "Kotlin") } |
| project.tasks.withType(kaptTaskClass) { kaptTask: Task -> |
| val variant = kaptTaskNameOrPrefixToVariant |
| .keys.firstOrNull { kaptTask.name.startsWith(it) } |
| ?.let { kaptTaskNameOrPrefixToVariant[it]!! } |
| // If we can't find a matching variant, it could be that this is a Kapt task for JVM in |
| // a KMP project (its name is "kaptKotlinJvm"), which we don't need to handle. |
| variant?.let { |
| configureKaptTaskInScopeForDataBinding(variant, kaptTask) |
| } |
| } |
| } |
| |
| 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") |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| private 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) |
| } |
| } |
| } |
| |
| /** |
| * 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> |
| ) |
| |
| 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 = |
| AndroidXDependencySubstitution.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 = |
| AndroidXDependencySubstitution.androidXMappings.getValue("com.android.support:multidex-instrumentation") |
| } |
| } |