| /* |
| * Copyright (C) 2019 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 com.android.build.api.attributes.BuildTypeAttr.Companion.ATTRIBUTE |
| import com.android.build.api.attributes.ProductFlavorAttr |
| import com.android.build.api.component.impl.ComponentPropertiesImpl |
| import com.android.build.api.component.impl.TestComponentImpl |
| import com.android.build.api.component.impl.TestComponentPropertiesImpl |
| import com.android.build.api.variant.impl.VariantImpl |
| import com.android.build.api.variant.impl.VariantPropertiesImpl |
| import com.android.build.gradle.internal.dependency.AarResourcesCompilerTransform |
| import com.android.build.gradle.internal.dependency.AarToClassTransform |
| import com.android.build.gradle.internal.dependency.AarTransform |
| import com.android.build.gradle.internal.dependency.AlternateCompatibilityRule |
| import com.android.build.gradle.internal.dependency.AlternateDisambiguationRule |
| import com.android.build.gradle.internal.dependency.AndroidXDependencySubstitution.replaceOldSupportLibraries |
| import com.android.build.gradle.internal.dependency.ClassesDirToClassesTransform |
| import com.android.build.gradle.internal.dependency.EnumerateClassesTransform |
| import com.android.build.gradle.internal.dependency.ExtractAarTransform |
| import com.android.build.gradle.internal.dependency.ExtractProGuardRulesTransform |
| import com.android.build.gradle.internal.dependency.FilterShrinkerRulesTransform |
| import com.android.build.gradle.internal.dependency.GenericTransformParameters |
| import com.android.build.gradle.internal.dependency.IdentityTransform |
| import com.android.build.gradle.internal.dependency.JetifyTransform |
| import com.android.build.gradle.internal.dependency.LibraryDependencySourcesTransform |
| import com.android.build.gradle.internal.dependency.LibrarySymbolTableTransform |
| import com.android.build.gradle.internal.dependency.MockableJarTransform |
| import com.android.build.gradle.internal.dependency.ModelArtifactCompatibilityRule.Companion.setUp |
| import com.android.build.gradle.internal.dependency.PlatformAttrTransform |
| import com.android.build.gradle.internal.dependency.VersionedCodeShrinker.Companion.of |
| import com.android.build.gradle.internal.dependency.getDexingArtifactConfigurations |
| import com.android.build.gradle.internal.dependency.registerDexingOutputSplitTransform |
| import com.android.build.gradle.internal.dsl.BaseFlavor |
| import com.android.build.gradle.internal.dsl.BuildType |
| import com.android.build.gradle.internal.dsl.DefaultConfig |
| import com.android.build.gradle.internal.dsl.ProductFlavor |
| import com.android.build.gradle.internal.dsl.SigningConfig |
| import com.android.build.gradle.internal.publishing.AndroidArtifacts |
| import com.android.build.gradle.internal.res.getAapt2FromMavenAndVersion |
| import com.android.build.gradle.internal.res.namespaced.AutoNamespacePreProcessTransform |
| import com.android.build.gradle.internal.res.namespaced.AutoNamespaceTransform |
| import com.android.build.gradle.internal.res.namespaced.init |
| import com.android.build.gradle.internal.scope.GlobalScope |
| import com.android.build.gradle.internal.services.getBuildService |
| import com.android.build.gradle.internal.utils.getDesugarLibConfig |
| import com.android.build.gradle.internal.utils.setDisallowChanges |
| import com.android.build.gradle.internal.variant.ComponentInfo |
| import com.android.build.gradle.internal.variant.VariantInputModel |
| import com.android.build.gradle.options.BooleanOption |
| import com.android.build.gradle.options.StringOption |
| import com.android.build.gradle.options.SyncOptions |
| import com.android.builder.model.CodeShrinker |
| import com.google.common.base.Strings |
| import com.google.common.collect.Maps |
| import org.gradle.api.Action |
| import org.gradle.api.ActionConfiguration |
| import org.gradle.api.Project |
| import org.gradle.api.artifacts.Configuration |
| import org.gradle.api.artifacts.dsl.DependencyHandler |
| import org.gradle.api.artifacts.transform.TransformSpec |
| import org.gradle.api.artifacts.type.ArtifactTypeDefinition |
| import org.gradle.api.attributes.Attribute |
| import org.gradle.api.attributes.AttributesSchema |
| import org.gradle.api.attributes.Usage |
| import org.gradle.api.internal.artifacts.ArtifactAttributes |
| |
| /** |
| * configures the dependencies for a set of variant inputs. |
| */ |
| class DependencyConfigurator( |
| private val project: Project, |
| private val projectName: String, |
| private val globalScope: GlobalScope, |
| private val variantInputModel: VariantInputModel<DefaultConfig, BuildType, ProductFlavor, SigningConfig> |
| ) { |
| |
| fun configureDependencySubstitutions(): DependencyConfigurator { |
| // If Jetifier is enabled, replace old support libraries with AndroidX. |
| if (globalScope.projectOptions[BooleanOption.ENABLE_JETIFIER]) { |
| replaceOldSupportLibraries(project) |
| } |
| return this |
| } |
| |
| fun configureGeneralTransforms(): DependencyConfigurator { |
| val dependencies: DependencyHandler = project.dependencies |
| |
| // The aars/jars may need to be processed (e.g., jetified to AndroidX) before they can be |
| // used |
| // Arguments passed to an ArtifactTransform must not be null |
| val jetifierSkipIfPossible = |
| globalScope.projectOptions[BooleanOption.JETIFIER_SKIP_IF_POSSIBLE] |
| val jetifierBlackList = Strings.nullToEmpty( |
| globalScope.projectOptions[StringOption.JETIFIER_BLACKLIST] |
| ) |
| val autoNamespaceDependencies = |
| globalScope.extension.aaptOptions.namespaced && |
| globalScope.projectOptions[BooleanOption.CONVERT_NON_NAMESPACED_DEPENDENCIES] |
| val jetifiedAarOutputType = if (autoNamespaceDependencies) { |
| AndroidArtifacts.ArtifactType.MAYBE_NON_NAMESPACED_PROCESSED_AAR |
| } else { |
| AndroidArtifacts.ArtifactType.PROCESSED_AAR |
| } |
| if (globalScope.projectOptions[BooleanOption.ENABLE_JETIFIER]) { |
| dependencies.registerTransform( |
| JetifyTransform::class.java |
| ) { spec: TransformSpec<JetifyTransform.Parameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.parameters.skipIfPossible.set(jetifierSkipIfPossible) |
| spec.parameters.blackListOption.set(jetifierBlackList) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.AAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| jetifiedAarOutputType.type |
| ) |
| } |
| dependencies.registerTransform( |
| JetifyTransform::class.java |
| ) { spec: TransformSpec<JetifyTransform.Parameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.parameters.skipIfPossible.set(jetifierSkipIfPossible) |
| spec.parameters.blackListOption.set(jetifierBlackList) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.JAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_JAR.type |
| ) |
| } |
| } else { |
| dependencies.registerTransform( |
| IdentityTransform::class.java |
| ) { spec: TransformSpec<GenericTransformParameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.AAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| jetifiedAarOutputType.type |
| ) |
| } |
| dependencies.registerTransform( |
| IdentityTransform::class.java |
| ) { spec: TransformSpec<GenericTransformParameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.JAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_JAR.type |
| ) |
| } |
| } |
| dependencies.registerTransform( |
| ExtractAarTransform::class.java |
| ) { spec: TransformSpec<GenericTransformParameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_AAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.EXPLODED_AAR.type |
| ) |
| } |
| dependencies.registerTransform( |
| MockableJarTransform::class.java |
| ) { spec: TransformSpec<MockableJarTransform.Parameters> -> |
| // Query for JAR instead of PROCESSED_JAR as android.jar doesn't need processing |
| spec.parameters.projectName.set(projectName) |
| spec.parameters.returnDefaultValues.set(true) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.JAR.type |
| ) |
| spec.from.attribute( |
| AndroidArtifacts.MOCKABLE_JAR_RETURN_DEFAULT_VALUES, |
| true |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.TYPE_MOCKABLE_JAR |
| ) |
| spec.to.attribute( |
| AndroidArtifacts.MOCKABLE_JAR_RETURN_DEFAULT_VALUES, |
| true |
| ) |
| } |
| dependencies.registerTransform( |
| MockableJarTransform::class.java |
| ) { spec: TransformSpec<MockableJarTransform.Parameters> -> |
| // Query for JAR instead of PROCESSED_JAR as android.jar doesn't need processing |
| spec.parameters.projectName.set(projectName) |
| spec.parameters.returnDefaultValues.set(false) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.JAR.type |
| ) |
| spec.from.attribute( |
| AndroidArtifacts.MOCKABLE_JAR_RETURN_DEFAULT_VALUES, |
| false |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.TYPE_MOCKABLE_JAR |
| ) |
| spec.to.attribute( |
| AndroidArtifacts.MOCKABLE_JAR_RETURN_DEFAULT_VALUES, |
| false |
| ) |
| } |
| // transform to extract attr info from android.jar |
| dependencies.registerTransform( |
| PlatformAttrTransform::class.java |
| ) { spec: TransformSpec<GenericTransformParameters> -> |
| spec.parameters.projectName.set(projectName) |
| // Query for JAR instead of PROCESSED_JAR as android.jar doesn't need processing |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.JAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.TYPE_PLATFORM_ATTR |
| ) |
| } |
| val sharedLibSupport = globalScope |
| .projectOptions[BooleanOption.CONSUME_DEPENDENCIES_AS_SHARED_LIBRARIES] |
| |
| for (transformTarget in AarTransform.getTransformTargets()) { |
| dependencies.registerTransform( |
| AarTransform::class.java |
| ) { spec: TransformSpec<AarTransform.Parameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.parameters.targetType.set(transformTarget) |
| spec.parameters.sharedLibSupport.set(sharedLibSupport) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.EXPLODED_AAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| transformTarget.type |
| ) |
| } |
| } |
| if (globalScope.projectOptions[BooleanOption.PRECOMPILE_DEPENDENCIES_RESOURCES]) { |
| dependencies.registerTransform( |
| AarResourcesCompilerTransform::class.java |
| ) { reg: TransformSpec<AarResourcesCompilerTransform.Parameters> -> |
| reg.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.EXPLODED_AAR.type |
| ) |
| reg.to |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.COMPILED_DEPENDENCIES_RESOURCES.type |
| ) |
| reg.parameters { params: AarResourcesCompilerTransform.Parameters -> |
| val (first, second) = getAapt2FromMavenAndVersion( |
| globalScope |
| ) |
| params.aapt2FromMaven |
| .from(first) |
| params.aapt2Version |
| .set(second) |
| params.errorFormatMode |
| .set( |
| SyncOptions.getErrorFormatMode( |
| globalScope.projectOptions |
| ) |
| ) |
| params.aapt2DaemonBuildService |
| .setDisallowChanges(getBuildService(project.gradle.sharedServices)) |
| } |
| } |
| } |
| // API Jar: Produce a single API jar that can also contain the library R class from the AAR |
| val apiUsage: Usage = project.objects.named(Usage::class.java, Usage.JAVA_API) |
| |
| dependencies.registerTransform( |
| AarToClassTransform::class.java |
| ) { reg: TransformSpec<AarToClassTransform.Params> -> |
| reg.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_AAR.type |
| ) |
| reg.from.attribute( |
| Usage.USAGE_ATTRIBUTE, |
| apiUsage |
| ) |
| reg.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.CLASSES_JAR.type |
| ) |
| reg.to.attribute( |
| Usage.USAGE_ATTRIBUTE, |
| apiUsage |
| ) |
| reg.parameters { params: AarToClassTransform.Params -> |
| params.forCompileUse.set(true) |
| params.generateRClassJar |
| .set( |
| globalScope.projectOptions.get( |
| BooleanOption.COMPILE_CLASSPATH_LIBRARY_R_CLASSES |
| ) |
| ) |
| } |
| } |
| // Produce a single runtime jar from the AAR. |
| val runtimeUsage: Usage = project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME) |
| |
| dependencies.registerTransform( |
| AarToClassTransform::class.java |
| ) { reg: TransformSpec<AarToClassTransform.Params> -> |
| reg.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_AAR.type |
| ) |
| reg.from.attribute( |
| Usage.USAGE_ATTRIBUTE, |
| runtimeUsage |
| ) |
| reg.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.CLASSES_JAR.type |
| ) |
| reg.to.attribute( |
| Usage.USAGE_ATTRIBUTE, |
| runtimeUsage |
| ) |
| reg.parameters { params: AarToClassTransform.Params -> |
| params.forCompileUse.set(false) |
| |
| params.generateRClassJar.set(false) |
| } |
| } |
| if (globalScope.projectOptions[BooleanOption.ENABLE_PROGUARD_RULES_EXTRACTION]) { |
| dependencies.registerTransform( |
| ExtractProGuardRulesTransform::class.java |
| ) { spec: TransformSpec<GenericTransformParameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_JAR.type |
| ) |
| spec.to |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.UNFILTERED_PROGUARD_RULES.type |
| ) |
| } |
| } |
| dependencies.registerTransform( |
| LibrarySymbolTableTransform::class.java |
| ) { spec: TransformSpec<GenericTransformParameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.EXPLODED_AAR.type |
| ) |
| spec.to |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.SYMBOL_LIST_WITH_PACKAGE_NAME.type |
| ) |
| } |
| if (autoNamespaceDependencies) { |
| dependencies.registerTransform(AutoNamespacePreProcessTransform::class.java) { spec -> |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.MAYBE_NON_NAMESPACED_PROCESSED_AAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PREPROCESSED_AAR_FOR_AUTO_NAMESPACE.type |
| ) |
| spec.parameters.init(globalScope) |
| } |
| dependencies.registerTransform(AutoNamespacePreProcessTransform::class.java) { spec -> |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.JAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PREPROCESSED_AAR_FOR_AUTO_NAMESPACE.type |
| ) |
| spec.parameters.init(globalScope) |
| } |
| |
| dependencies.registerTransform(AutoNamespaceTransform::class.java) { spec -> |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PREPROCESSED_AAR_FOR_AUTO_NAMESPACE.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_AAR.type |
| ) |
| spec.parameters.init(globalScope) |
| } |
| |
| } |
| // Transform to go from external jars to CLASSES and JAVA_RES artifacts. This returns the |
| // same exact file but with different types, since a jar file can contain both. |
| for (classesOrResources in arrayOf( |
| AndroidArtifacts.ArtifactType.CLASSES_JAR.type, |
| AndroidArtifacts.ArtifactType.JAVA_RES.type |
| )) { |
| dependencies.registerTransform(IdentityTransform::class.java) { spec: TransformSpec<GenericTransformParameters?> -> |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_JAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| classesOrResources |
| ) |
| } |
| } |
| // The Kotlin Kapt plugin should query for PROCESSED_JAR, but it is currently querying for |
| // JAR, so we need to have the workaround below to make it get PROCESSED_JAR. See |
| // http://issuetracker.google.com/111009645. |
| project.configurations.all { configuration: Configuration -> |
| if (configuration.name.startsWith("kapt")) { |
| configuration |
| .attributes |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.PROCESSED_JAR.type |
| ) |
| } |
| } |
| |
| // When consuming classes from Android libraries, there are 2 transforms: |
| // 1. `android-classes-directory` -> `android-classes` |
| // 2. `android-classes-jar` -> `android-classes` |
| // Currently Gradle always takes transform flow #1, which is ideal for incremental dexing. |
| // (We don't know why Gradle does that, but IncrementalDesugaringTest should catch it if |
| // this behavior changes.) |
| dependencies.registerTransform( |
| ClassesDirToClassesTransform::class.java, |
| Action { spec: TransformSpec<GenericTransformParameters?> -> |
| spec.from |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.CLASSES_DIR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.CLASSES.type |
| ) |
| } |
| ) |
| dependencies.registerTransform( |
| IdentityTransform::class.java, |
| Action { spec: TransformSpec<GenericTransformParameters?> -> |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.CLASSES_JAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.CLASSES.type |
| ) |
| } |
| ) |
| dependencies.registerTransform( |
| LibraryDependencySourcesTransform::class.java, |
| Action { spec: TransformSpec<GenericTransformParameters> -> |
| spec.parameters.projectName.set(projectName) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.EXPLODED_AAR.type |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.AAR_CLASS_LIST_AND_RES_SYMBOLS.type |
| ) |
| } |
| ) |
| |
| |
| // When consuming classes from Java libraries, there are 2 transforms: |
| // 1. `java-classes-directory` -> `android-classes` |
| // 2. `jar` -> `processed-jar` -> `android-classes-jar` -> `android-classes` |
| // Currently Gradle always takes transform flow #2, which is not ideal for incremental |
| // dexing. |
| // TODO(147137579): Configure Gradle to take transform flow #1. |
| dependencies.registerTransform(IdentityTransform::class.java) { spec: TransformSpec<GenericTransformParameters?> -> |
| spec.from |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| ArtifactTypeDefinition.JVM_CLASS_DIRECTORY |
| ) |
| spec.to.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.CLASSES.type |
| ) |
| } |
| |
| val schema = dependencies.attributesSchema |
| // custom strategy for build-type and product-flavor. |
| setBuildTypeStrategy(schema) |
| setupFlavorStrategy(schema) |
| setupModelStrategy(schema) |
| |
| return this |
| } |
| |
| private fun setBuildTypeStrategy(schema: AttributesSchema) { |
| // this is ugly but because the getter returns a very base class we have no choices. |
| val dslBuildTypes = variantInputModel.buildTypes.values.map { it.buildType } |
| |
| if (dslBuildTypes.isEmpty()) { |
| return |
| } |
| |
| val alternateMap: MutableMap<String, List<String>> = |
| Maps.newHashMap() |
| for (buildType in dslBuildTypes) { |
| if (!buildType.matchingFallbacks.isEmpty()) { |
| alternateMap[buildType.name] = buildType.matchingFallbacks |
| } |
| } |
| if (!alternateMap.isEmpty()) { |
| val buildTypeStrategy = |
| schema.attribute( |
| ATTRIBUTE |
| ) |
| buildTypeStrategy |
| .compatibilityRules |
| .add( |
| AlternateCompatibilityRule.BuildTypeRule::class.java |
| ) { config: ActionConfiguration -> |
| config.setParams( |
| alternateMap |
| ) |
| } |
| buildTypeStrategy |
| .disambiguationRules |
| .add( |
| AlternateDisambiguationRule.BuildTypeRule::class.java |
| ) { config: ActionConfiguration -> |
| config.setParams( |
| alternateMap |
| ) |
| } |
| } |
| } |
| |
| private fun setupFlavorStrategy(schema: AttributesSchema) { |
| // this is ugly but because the getter returns a very base class we have no choices. |
| val flavors = variantInputModel.productFlavors.values.map { it.productFlavor } |
| |
| // first loop through all the flavors and collect for each dimension, and each value, its |
| // fallbacks |
| // map of (dimension > (requested > fallbacks)) |
| val alternateMap: MutableMap<String, MutableMap<String, List<String>>> = |
| Maps.newHashMap() |
| for (flavor in flavors) { |
| if (flavor.matchingFallbacks.isNotEmpty()) { |
| val name = flavor.name |
| val dimension = flavor.dimension!! |
| val dimensionMap = |
| alternateMap.computeIfAbsent( |
| dimension |
| ) { s: String? -> Maps.newHashMap() } |
| dimensionMap[name] = flavor.matchingFallbacks |
| } |
| handleMissingDimensions(alternateMap, flavor) |
| } |
| // also handle missing dimensions on the default config. |
| handleMissingDimensions(alternateMap, variantInputModel.defaultConfigData.defaultConfig) |
| // now that we know we have all the fallbacks for each dimensions, we can create the |
| // rule instances. |
| for ((key, value) in alternateMap) { |
| addFlavorStrategy(schema, key, value) |
| } |
| } |
| |
| private fun setupModelStrategy(attributesSchema: AttributesSchema) { |
| setUp(attributesSchema) |
| } |
| |
| private fun handleMissingDimensions( |
| alternateMap: MutableMap<String, MutableMap<String, List<String>>>, |
| flavor: BaseFlavor |
| ) { |
| val missingStrategies = flavor.missingDimensionStrategies |
| if (missingStrategies.isNotEmpty()) { |
| for ((dimension, value) in missingStrategies) { |
| val dimensionMap = alternateMap.computeIfAbsent(dimension) { Maps.newHashMap() } |
| dimensionMap[value.requested] = value.fallbacks |
| } |
| } |
| } |
| |
| /** Configure artifact transforms that require variant-specific attribute information. */ |
| fun <VariantT: VariantImpl<VariantPropertiesT>, VariantPropertiesT: VariantPropertiesImpl> configureVariantTransforms( |
| variants: List<ComponentInfo<VariantT, VariantPropertiesT>>, |
| testComponents: List<ComponentInfo<TestComponentImpl<out TestComponentPropertiesImpl>, TestComponentPropertiesImpl>> |
| ): DependencyConfigurator { |
| val allComponents: List<ComponentPropertiesImpl> = (variants + testComponents).map { it.properties } |
| |
| val dependencies = project.dependencies |
| if (globalScope.projectOptions[BooleanOption.ENABLE_DEXING_ARTIFACT_TRANSFORM]) { |
| for (artifactConfiguration in getDexingArtifactConfigurations( |
| allComponents |
| )) { |
| artifactConfiguration.registerTransform( |
| globalScope.project.name, |
| dependencies, |
| project.files(globalScope.bootClasspath), |
| getDesugarLibConfig(globalScope.project), |
| SyncOptions.getErrorFormatMode(globalScope.projectOptions), |
| globalScope.projectOptions.get(BooleanOption.ENABLE_INCREMENTAL_DEXING_TRANSFORM) |
| ) |
| } |
| } |
| if (globalScope.projectOptions[BooleanOption.ENABLE_PROGUARD_RULES_EXTRACTION]) { |
| val shrinkers: Set<CodeShrinker> = allComponents |
| .asSequence() |
| .map { it.variantScope.codeShrinker } |
| .filterNotNull() |
| .toSet() |
| for (shrinker in shrinkers) { |
| dependencies.registerTransform( |
| FilterShrinkerRulesTransform::class.java |
| ) { reg: TransformSpec<FilterShrinkerRulesTransform.Parameters> -> |
| reg.from |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.UNFILTERED_PROGUARD_RULES.type |
| ) |
| reg.to |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.FILTERED_PROGUARD_RULES.type |
| ) |
| reg.from.attribute( |
| VariantManager.SHRINKER_ATTR, |
| shrinker.toString() |
| ) |
| reg.to.attribute( |
| VariantManager.SHRINKER_ATTR, |
| shrinker.toString() |
| ) |
| reg.parameters { params: FilterShrinkerRulesTransform.Parameters -> |
| params.shrinker |
| .set(of(shrinker)) |
| params.projectName.set(project.name) |
| } |
| } |
| } |
| } |
| |
| if (globalScope.projectOptions[BooleanOption.ENABLE_DUPLICATE_CLASSES_CHECK]) { |
| dependencies.registerTransform( |
| EnumerateClassesTransform::class.java |
| ) { spec: TransformSpec<GenericTransformParameters> -> |
| spec.parameters.projectName.set(globalScope.project.name) |
| spec.from.attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.CLASSES_JAR.type |
| ) |
| spec.to |
| .attribute( |
| ArtifactAttributes.ARTIFACT_FORMAT, |
| AndroidArtifacts.ArtifactType.ENUMERATED_RUNTIME_CLASSES.type |
| ) |
| } |
| } |
| |
| registerDexingOutputSplitTransform(dependencies) |
| |
| return this |
| } |
| |
| companion object { |
| @JvmStatic |
| fun addFlavorStrategy( |
| schema: AttributesSchema, |
| dimension: String, |
| alternateMap: Map<String, List<String>> |
| ) { |
| val attr = Attribute.of(dimension, ProductFlavorAttr::class.java) |
| val flavorStrategy = schema.attribute(attr) |
| flavorStrategy |
| .compatibilityRules |
| .add(AlternateCompatibilityRule.ProductFlavorRule::class.java) { |
| it.setParams(alternateMap) |
| } |
| flavorStrategy |
| .disambiguationRules |
| .add(AlternateDisambiguationRule.ProductFlavorRule::class.java) { |
| it.setParams(alternateMap) |
| } |
| } |
| } |
| } |