blob: 7e0c48457d00b25cee950f1f587b5f1e2317128a [file] [log] [blame]
/*
* 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.AgpVersionAttr
import com.android.build.api.attributes.BuildTypeAttr.Companion.ATTRIBUTE
import com.android.build.api.attributes.ProductFlavorAttr
import com.android.build.api.component.impl.ComponentImpl
import com.android.build.api.variant.impl.VariantBuilderImpl
import com.android.build.api.variant.impl.VariantImpl
import com.android.build.gradle.internal.component.ComponentCreationConfig
import com.android.build.gradle.internal.component.ConsumableCreationConfig
import com.android.build.gradle.internal.coverage.JacocoConfigurations
import com.android.build.gradle.internal.coverage.JacocoOptions
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.AgpVersionCompatibilityRule
import com.android.build.gradle.internal.dependency.AlternateCompatibilityRule
import com.android.build.gradle.internal.dependency.AlternateDisambiguationRule
import com.android.build.gradle.internal.dependency.AndroidXDependencyCheck
import com.android.build.gradle.internal.dependency.AndroidXDependencySubstitution.replaceOldSupportLibraries
import com.android.build.gradle.internal.dependency.AsmClassesTransform.Companion.registerAsmTransformForComponent
import com.android.build.gradle.internal.dependency.ClassesDirToClassesTransform
import com.android.build.gradle.internal.dependency.CollectClassesTransform
import com.android.build.gradle.internal.dependency.CollectResourceSymbolsTransform
import com.android.build.gradle.internal.dependency.EnumerateClassesTransform
import com.android.build.gradle.internal.dependency.ExtractAarTransform
import com.android.build.gradle.internal.dependency.ExtractJniTransform
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.JacocoTransform
import com.android.build.gradle.internal.dependency.JetifyTransform
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.RecalculateStackFramesTransform.Companion.registerGlobalRecalculateStackFramesTransform
import com.android.build.gradle.internal.dependency.RecalculateStackFramesTransform.Companion.registerRecalculateStackFramesTransformForComponent
import com.android.build.gradle.internal.dependency.VersionedCodeShrinker
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.namespaced.AutoNamespacePreProcessTransform
import com.android.build.gradle.internal.res.namespaced.AutoNamespaceTransform
import com.android.build.gradle.internal.scope.GlobalScope
import com.android.build.gradle.internal.services.ProjectServices
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.ProjectOptions
import com.android.build.gradle.options.StringOption
import com.android.build.gradle.options.SyncOptions
import com.google.common.collect.Maps
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.TransformAction
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 projectOptions: ProjectOptions,
private val globalScope: GlobalScope,
private val variantInputModel: VariantInputModel<DefaultConfig, BuildType, ProductFlavor, SigningConfig>,
private val projectServices: ProjectServices
) {
fun configureDependencySubstitutions(): DependencyConfigurator {
// If Jetifier is enabled, replace old support libraries with AndroidX.
if (projectOptions.get(BooleanOption.ENABLE_JETIFIER)) {
replaceOldSupportLibraries(
project,
// Inline the property name for a slight memory improvement (so that the JVM doesn't
// create a new string every time this code is executed, which could be many when
// there are many subprojects).
reasonToReplace = "android.enableJetifier=true")
}
return this
}
fun configureDependencyChecks(): DependencyConfigurator {
val useAndroidX = projectServices.projectOptions.get(BooleanOption.USE_ANDROID_X)
val enableJetifier = projectServices.projectOptions.get(BooleanOption.ENABLE_JETIFIER)
when {
!useAndroidX && !enableJetifier -> {
project.configurations.all { configuration ->
if (configuration.isCanBeResolved) {
configuration.incoming.afterResolve(
AndroidXDependencyCheck.AndroidXDisabledJetifierDisabled(
project, configuration.name, projectServices.issueReporter
)
)
}
}
}
useAndroidX && !enableJetifier -> {
project.configurations.all { configuration ->
if (configuration.isCanBeResolved) {
configuration.incoming.afterResolve(
AndroidXDependencyCheck.AndroidXEnabledJetifierDisabled(
project, configuration.name, projectServices.issueReporter
)
)
}
}
}
}
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
val autoNamespaceDependencies =
projectServices.projectInfo.getExtension().aaptOptions.namespaced &&
projectOptions[BooleanOption.CONVERT_NON_NAMESPACED_DEPENDENCIES]
val jetifiedAarOutputType = if (autoNamespaceDependencies) {
AndroidArtifacts.ArtifactType.MAYBE_NON_NAMESPACED_PROCESSED_AAR
} else {
AndroidArtifacts.ArtifactType.PROCESSED_AAR
}
// Arguments passed to an ArtifactTransform must not be null
val jetifierIgnoreList = projectOptions[StringOption.JETIFIER_IGNORE_LIST] ?: ""
if (projectOptions.get(BooleanOption.ENABLE_JETIFIER)) {
registerTransform(
JetifyTransform::class.java,
AndroidArtifacts.ArtifactType.AAR,
jetifiedAarOutputType
) { params ->
params.ignoreListOption.setDisallowChanges(jetifierIgnoreList)
}
registerTransform(
JetifyTransform::class.java,
AndroidArtifacts.ArtifactType.JAR,
AndroidArtifacts.ArtifactType.PROCESSED_JAR
) { params ->
params.ignoreListOption.setDisallowChanges(jetifierIgnoreList)
}
} else {
registerTransform(
IdentityTransform::class.java,
AndroidArtifacts.ArtifactType.AAR,
jetifiedAarOutputType
)
registerTransform(
IdentityTransform::class.java,
AndroidArtifacts.ArtifactType.JAR,
AndroidArtifacts.ArtifactType.PROCESSED_JAR
)
}
registerTransform(
ExtractAarTransform::class.java,
AndroidArtifacts.ArtifactType.PROCESSED_AAR,
AndroidArtifacts.ArtifactType.EXPLODED_AAR
)
registerTransform(
ExtractAarTransform::class.java,
AndroidArtifacts.ArtifactType.LOCAL_AAR_FOR_LINT,
AndroidArtifacts.ArtifactType.LOCAL_EXPLODED_AAR_FOR_LINT
)
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
registerTransform(
PlatformAttrTransform::class.java,
// Query for JAR instead of PROCESSED_JAR as android.jar doesn't need processing
AndroidArtifacts.ArtifactType.JAR.type,
AndroidArtifacts.TYPE_PLATFORM_ATTR
)
val sharedLibSupport = projectOptions[BooleanOption.CONSUME_DEPENDENCIES_AS_SHARED_LIBRARIES]
for (transformTarget in AarTransform.getTransformTargets()) {
registerTransform(
AarTransform::class.java,
AndroidArtifacts.ArtifactType.EXPLODED_AAR,
transformTarget
) { params ->
params.targetType.setDisallowChanges(transformTarget)
params.sharedLibSupport.setDisallowChanges(sharedLibSupport)
}
}
if (projectOptions[BooleanOption.PRECOMPILE_DEPENDENCIES_RESOURCES]) {
registerTransform(
AarResourcesCompilerTransform::class.java,
AndroidArtifacts.ArtifactType.EXPLODED_AAR,
AndroidArtifacts.ArtifactType.COMPILED_DEPENDENCIES_RESOURCES
) { params ->
projectServices.initializeAapt2Input(params.aapt2)
}
}
// 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(
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 (projectOptions[BooleanOption.ENABLE_JACOCO_TRANSFORM_INSTRUMENTATION]) {
val jacocoTransformParametersConfig: (JacocoTransform.Params) -> Unit = {
val jacocoVersion = JacocoOptions.DEFAULT_VERSION
val jacocoConfiguration = JacocoConfigurations
.getJacocoAntTaskConfiguration(project, jacocoVersion)
it.jacocoInstrumentationService
.set(getBuildService(projectServices.buildServiceRegistry))
it.jacocoConfiguration.from(jacocoConfiguration)
it.jacocoVersion.setDisallowChanges(jacocoVersion)
}
registerTransform(
JacocoTransform::class.java,
AndroidArtifacts.ArtifactType.CLASSES,
AndroidArtifacts.ArtifactType.JACOCO_CLASSES,
jacocoTransformParametersConfig
)
registerTransform(
JacocoTransform::class.java,
AndroidArtifacts.ArtifactType.CLASSES_JAR,
AndroidArtifacts.ArtifactType.JACOCO_CLASSES_JAR,
jacocoTransformParametersConfig
)
registerTransform(
JacocoTransform::class.java,
AndroidArtifacts.ArtifactType.ASM_INSTRUMENTED_JARS,
AndroidArtifacts.ArtifactType.JACOCO_ASM_INSTRUMENTED_JARS,
jacocoTransformParametersConfig
)
}
if (projectOptions[BooleanOption.ENABLE_PROGUARD_RULES_EXTRACTION]) {
registerTransform(
ExtractProGuardRulesTransform::class.java,
AndroidArtifacts.ArtifactType.PROCESSED_JAR,
AndroidArtifacts.ArtifactType.UNFILTERED_PROGUARD_RULES
)
}
registerTransform(
LibrarySymbolTableTransform::class.java,
AndroidArtifacts.ArtifactType.EXPLODED_AAR,
AndroidArtifacts.ArtifactType.SYMBOL_LIST_WITH_PACKAGE_NAME
)
if (autoNamespaceDependencies) {
registerTransform(
AutoNamespacePreProcessTransform::class.java,
AndroidArtifacts.ArtifactType.MAYBE_NON_NAMESPACED_PROCESSED_AAR,
AndroidArtifacts.ArtifactType.PREPROCESSED_AAR_FOR_AUTO_NAMESPACE
) { params ->
projectServices.initializeAapt2Input(params.aapt2)
}
registerTransform(
AutoNamespacePreProcessTransform::class.java,
AndroidArtifacts.ArtifactType.JAR,
AndroidArtifacts.ArtifactType.PREPROCESSED_AAR_FOR_AUTO_NAMESPACE
) { params ->
projectServices.initializeAapt2Input(params.aapt2)
}
registerTransform(
AutoNamespaceTransform::class.java,
AndroidArtifacts.ArtifactType.PREPROCESSED_AAR_FOR_AUTO_NAMESPACE,
AndroidArtifacts.ArtifactType.PROCESSED_AAR
) { params ->
projectServices.initializeAapt2Input(params.aapt2)
}
}
// 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,
AndroidArtifacts.ArtifactType.JAVA_RES
)) {
registerTransform(
IdentityTransform::class.java,
AndroidArtifacts.ArtifactType.PROCESSED_JAR,
classesOrResources
)
}
registerTransform(
ExtractJniTransform::class.java,
AndroidArtifacts.ArtifactType.PROCESSED_JAR,
AndroidArtifacts.ArtifactType.JNI
)
// 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
)
}
}
// From an Android library subproject, there are 2 transform flows to CLASSES:
// 1. CLASSES_DIR -> CLASSES
// 2. CLASSES_JAR -> CLASSES
// From a Java library subproject, there are also 2 transform flows to CLASSES:
// 1. JVM_CLASS_DIRECTORY -> CLASSES
// 2. JAR -> PROCESSED_JAR -> `CLASSES_JAR -> CLASSES
registerTransform(
ClassesDirToClassesTransform::class.java,
AndroidArtifacts.ArtifactType.CLASSES_DIR,
AndroidArtifacts.ArtifactType.CLASSES
)
registerTransform(
IdentityTransform::class.java,
AndroidArtifacts.ArtifactType.CLASSES_JAR,
AndroidArtifacts.ArtifactType.CLASSES
)
registerTransform(
IdentityTransform::class.java,
ArtifactTypeDefinition.JVM_CLASS_DIRECTORY,
AndroidArtifacts.ArtifactType.CLASSES.type
) { params ->
params.acceptNonExistentInputFile.setDisallowChanges(true)
}
registerTransform(
CollectResourceSymbolsTransform::class.java,
AndroidArtifacts.ArtifactType.ANDROID_RES.type,
AndroidArtifacts.ArtifactType.ANDROID_RES_SYMBOLS.type
)
registerTransform(
CollectClassesTransform::class.java,
AndroidArtifacts.ArtifactType.CLASSES_JAR,
AndroidArtifacts.ArtifactType.JAR_CLASS_LIST
)
registerGlobalRecalculateStackFramesTransform(
projectName,
dependencies,
globalScope.fullBootClasspathProvider,
projectServices.buildServiceRegistry
)
return this
}
private fun <T : GenericTransformParameters> registerTransform(
transformClass: Class<out TransformAction<T>>,
fromArtifactType: AndroidArtifacts.ArtifactType,
toArtifactType: AndroidArtifacts.ArtifactType,
parametersSetter: ((T) -> Unit)? = null
) {
registerTransform(
transformClass,
fromArtifactType.type,
toArtifactType.type,
parametersSetter
)
}
private fun <T : GenericTransformParameters> registerTransform(
transformClass: Class<out TransformAction<T>>,
fromArtifactType: String,
toArtifactType: String,
parametersSetter: ((T) -> Unit)? = null
) {
project.dependencies.registerTransform(
transformClass
) { spec: TransformSpec<T> ->
spec.from.attribute(ArtifactAttributes.ARTIFACT_FORMAT, fromArtifactType)
spec.to.attribute(ArtifactAttributes.ARTIFACT_FORMAT, toArtifactType)
spec.parameters.projectName.setDisallowChanges(projectName)
parametersSetter?.let { it(spec.parameters) }
}
}
fun configureAttributeMatchingStrategies(): DependencyConfigurator {
val schema = project.dependencies.attributesSchema
// custom strategy for build-type and product-flavor.
setBuildTypeStrategy(schema)
setupFlavorStrategy(schema)
setupModelStrategy(schema)
setUpAgpVersionStrategy(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)
}
/** This is to enforce AGP version across a single or composite build. */
private fun setUpAgpVersionStrategy(attributesSchema: AttributesSchema) {
val strategy = attributesSchema.attribute(AgpVersionAttr.ATTRIBUTE)
strategy.compatibilityRules.add(AgpVersionCompatibilityRule::class.java)
}
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 <VariantBuilderT : VariantBuilderImpl, VariantT : VariantImpl>
configureVariantTransforms(
variants: List<ComponentInfo<VariantBuilderT, VariantT>>,
nestedComponents: List<ComponentImpl>
): DependencyConfigurator {
val allComponents: List<ComponentCreationConfig> =
variants.map { it.variant }.plus(nestedComponents)
val dependencies = project.dependencies
for (component in allComponents) {
registerAsmTransformForComponent(
projectServices.projectInfo.getProject().name,
dependencies,
component
)
registerRecalculateStackFramesTransformForComponent(
projectServices.projectInfo.getProject().name,
dependencies,
component
)
}
if (allComponents.isNotEmpty()) {
val bootClasspath = project.files(allComponents.first().sdkComponents.bootClasspath)
if (projectOptions[BooleanOption.ENABLE_DEXING_ARTIFACT_TRANSFORM]) {
for (artifactConfiguration in getDexingArtifactConfigurations(
allComponents
)) {
artifactConfiguration.registerTransform(
projectServices.projectInfo.getProject().name,
dependencies,
bootClasspath,
getDesugarLibConfig(projectServices.projectInfo.getProject()),
SyncOptions.getErrorFormatMode(projectOptions),
)
}
}
}
if (projectOptions[BooleanOption.ENABLE_PROGUARD_RULES_EXTRACTION]
&& allComponents.any { it is ConsumableCreationConfig && it.minifiedEnabled }) {
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.parameters { params: FilterShrinkerRulesTransform.Parameters ->
params.shrinker.set(VersionedCodeShrinker.create())
params.projectName.set(project.name)
}
}
}
if (projectOptions[BooleanOption.ENABLE_DUPLICATE_CLASSES_CHECK]) {
registerTransform(
EnumerateClassesTransform::class.java,
AndroidArtifacts.ArtifactType.CLASSES_JAR,
AndroidArtifacts.ArtifactType.ENUMERATED_RUNTIME_CLASSES
)
}
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)
}
}
}
}