blob: 0da1ed95175b3d4275a8958b89ce988995e308a6 [file] [log] [blame]
/*
* 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.api.component.impl.features
import com.android.build.api.artifact.MultipleArtifact
import com.android.build.gradle.internal.component.DeviceTestCreationConfig
import com.android.build.gradle.internal.component.ApkCreationConfig
import com.android.build.gradle.internal.component.DynamicFeatureCreationConfig
import com.android.build.gradle.internal.component.features.DexingCreationConfig
import com.android.build.gradle.internal.scope.Java8LangSupport
import com.android.build.gradle.internal.services.VariantServices
import com.android.build.gradle.options.BooleanOption
import com.android.builder.dexing.DexingType
import com.android.builder.errors.IssueReporter
import org.gradle.api.file.RegularFileProperty
import java.io.File
class DexingImpl(
private val component: ApkCreationConfig,
private val multiDexEnabledFromDsl: Boolean?,
multiDexProguardFile: File?,
multiDexKeepFile: File?,
private val internalServices: VariantServices,
): DexingCreationConfig {
override fun finalizeAndLock() {
if (multiDexKeepProguard.isPresent) {
component.artifacts
.getArtifactContainer(MultipleArtifact.MULTIDEX_KEEP_PROGUARD)
.addInitialProvider(multiDexKeepProguard)
}
}
override val multiDexKeepProguard: RegularFileProperty =
internalServices.regularFileProperty().also { regularFileProperty ->
multiDexProguardFile?.let {
regularFileProperty.set(
internalServices.projectInfo.projectDirectory.file(it.absolutePath)
)
}
}
override val multiDexKeepFile: RegularFileProperty =
internalServices.regularFileProperty().also { regularFileProperty ->
multiDexKeepFile?.let {
regularFileProperty.set(
internalServices.projectInfo.projectDirectory.file(it.absolutePath)
)
}
}
override val needsMainDexListForBundle: Boolean
get() = component.componentType.isBaseModule
&& component.global.hasDynamicFeatures
&& dexingType.isLegacyMultiDex
/**
* Package desugar_lib DEX for base feature androidTest only if the base packages shrunk
* desugar_lib. This should be fixed properly by analyzing the test code when generating L8
* keep rules, and thus packaging desugar_lib dex in the tested APK only which contains
* necessary classes used both in test and tested APKs.
*/
override val shouldPackageDesugarLibDex: Boolean
get() = when (component) {
is DeviceTestCreationConfig -> {
when {
!isCoreLibraryDesugaringEnabled -> false
component.mainVariant.componentType.isAar -> true
else -> component.mainVariant.componentType.isBaseModule && needsShrinkDesugarLibrary
}
}
is DynamicFeatureCreationConfig -> false
else -> isCoreLibraryDesugaringEnabled
}
override val dexingType: DexingType =
@Suppress("IntroduceWhenSubject")
when {
multiDexEnabledFromDsl == false -> DexingType.MONO_DEX
multiDexEnabledFromDsl == true -> when {
canRunNativeMultiDex() -> DexingType.NATIVE_MULTIDEX
else -> DexingType.LEGACY_MULTIDEX
}
// multiDexEnabledFromDsl == null
else -> when {
canRunNativeMultiDex() -> DexingType.NATIVE_MULTIDEX
else -> DexingType.MONO_DEX
}
}
/**
* We can run native multidex if:
* - minSdkVersion >= 21, or
* - targetDeployApiFromIDE >= 21 (to improve performance), or
* - if this is a dynamic feature module (see [DexingType]'s kdoc).
*/
private fun canRunNativeMultiDex(): Boolean =
component.minSdk.apiLevel >= 21 ||
component.global.targetDeployApiFromIDE?.let { it >= 21 } == true ||
component is DynamicFeatureCreationConfig
override val isMultiDexEnabled: Boolean = dexingType.isMultiDex
override val java8LangSupportType: Java8LangSupport
get() {
// in order of precedence
return if (!component.global.compileOptions.targetCompatibility.isJava8Compatible) {
Java8LangSupport.UNUSED
} else if (component.services.projectInfo.hasPlugin("me.tatarka.retrolambda")) {
Java8LangSupport.RETROLAMBDA
} else if (component.optimizationCreationConfig.minifiedEnabled) {
Java8LangSupport.R8
} else {
// D8 cannot be used if R8 is used
Java8LangSupport.D8
}
}
override val needsShrinkDesugarLibrary: Boolean
get() {
if (!isCoreLibraryDesugaringEnabled) {
return false
}
// Assume Java8LangSupport is either D8 or R8 as we checked that in
// isCoreLibraryDesugaringEnabled()
return !(java8LangSupportType == Java8LangSupport.D8 && component.debuggable)
}
/**
* Returns if core library desugaring is enabled.
*
* Java language desugaring and multidex are required for enabling core library desugaring.
*/
override val isCoreLibraryDesugaringEnabled: Boolean
get() {
if (component is DeviceTestCreationConfig &&
component.services.projectOptions.get(
BooleanOption.ENABLE_INSTRUMENTATION_TEST_DESUGARING
)) {
return true
}
val libDesugarEnabled = component.global.compileOptions.isCoreLibraryDesugaringEnabled
val langSupportType = java8LangSupportType
val langDesugarEnabled = langSupportType == Java8LangSupport.D8 ||
langSupportType == Java8LangSupport.R8
if (libDesugarEnabled && !langDesugarEnabled) {
component
.services
.issueReporter
.reportError(
IssueReporter.Type.GENERIC, "In order to use core library desugaring, "
+ "please enable java 8 language desugaring with D8 or R8.")
}
if (libDesugarEnabled && !dexingType.isMultiDex) {
component
.services
.issueReporter
.reportError(
IssueReporter.Type.GENERIC, "In order to use core library desugaring, "
+ "please enable multidex.")
}
return libDesugarEnabled
}
/**
* Returns the minimum SDK version which we want to use for dexing.
* In most cases this will be equal the minSdkVersion, but when the IDE is deploying to:
* - device running API 24+, the min sdk version for dexing is max(24, minSdkVersion)
* - device running API 23-, the min sdk version for dexing is minSdkVersion
* - there is no device, the min sdk version for dexing is minSdkVersion
* It is used to enable some optimizations to build the APK faster.
*
* This has no relation with targetSdkVersion from build.gradle/manifest.
*/
override val minSdkVersionForDexing: Int
get() {
var minSdkVersion = component.minSdk.apiLevel
val deviceApiLevel = component.global.targetDeployApiFromIDE
if (minSdkVersion < 24 && deviceApiLevel != null && deviceApiLevel >= 24) {
minSdkVersion = 24
}
return minSdkVersion
}
}