blob: 02cc70e4d6573f92fdb1dff4e6fa1849710823c9 [file] [log] [blame]
/*
* Copyright (C) 2020 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.variant.impl
import com.android.build.api.artifact.impl.ArtifactsImpl
import com.android.build.api.component.analytics.AnalyticsEnabledDynamicFeatureVariant
import com.android.build.api.component.impl.TestFixturesImpl
import com.android.build.api.component.impl.features.DexingImpl
import com.android.build.api.component.impl.features.OptimizationCreationConfigImpl
import com.android.build.api.component.impl.isTestApk
import com.android.build.api.variant.AndroidVersion
import com.android.build.api.variant.Component
import com.android.build.api.variant.DeviceTest
import com.android.build.api.variant.DynamicFeatureVariant
import com.android.build.api.variant.HasHostTests
import com.android.build.api.variant.HasUnitTest
import com.android.build.api.variant.Renderscript
import com.android.build.gradle.internal.component.DynamicFeatureCreationConfig
import com.android.build.gradle.internal.component.HostTestCreationConfig
import com.android.build.gradle.internal.component.features.DexingCreationConfig
import com.android.build.gradle.internal.core.VariantSources
import com.android.build.gradle.internal.core.dsl.DynamicFeatureVariantDslInfo
import com.android.build.gradle.internal.dependency.VariantDependencies
import com.android.build.gradle.internal.dsl.ModulePropertyKey
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.scope.BuildFeatureValues
import com.android.build.gradle.internal.scope.MutableTaskContainer
import com.android.build.gradle.internal.services.TaskCreationServices
import com.android.build.gradle.internal.services.VariantServices
import com.android.build.gradle.internal.tasks.ModuleMetadata
import com.android.build.gradle.internal.tasks.factory.GlobalTaskCreationConfig
import com.android.build.gradle.internal.tasks.featuresplit.FeatureSetMetadata
import com.android.build.gradle.internal.variant.BaseVariantData
import com.android.build.gradle.internal.variant.VariantPathHelper
import com.android.build.gradle.options.StringOption
import com.android.builder.errors.IssueReporter
import com.google.wireless.android.sdk.stats.GradleBuildVariant
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import javax.inject.Inject
open class DynamicFeatureVariantImpl @Inject constructor(
override val variantBuilder: DynamicFeatureVariantBuilderImpl,
buildFeatureValues: BuildFeatureValues,
dslInfo: DynamicFeatureVariantDslInfo,
variantDependencies: VariantDependencies,
variantSources: VariantSources,
paths: VariantPathHelper,
artifacts: ArtifactsImpl,
variantData: BaseVariantData,
taskContainer: MutableTaskContainer,
internalServices: VariantServices,
taskCreationServices: TaskCreationServices,
globalTaskCreationConfig: GlobalTaskCreationConfig,
) : VariantImpl<DynamicFeatureVariantDslInfo>(
variantBuilder,
buildFeatureValues,
dslInfo,
variantDependencies,
variantSources,
paths,
artifacts,
variantData,
taskContainer,
internalServices,
taskCreationServices,
globalTaskCreationConfig
), DynamicFeatureVariant,
DynamicFeatureCreationConfig,
HasDeviceTestsCreationConfig,
HasTestFixtures,
HasHostTestsCreationConfig,
HasHostTests,
HasUnitTest {
init {
// TODO: Should be removed once we stop implementing all build type interfaces in one class
if (dslInfo.isMultiDexSetFromDsl) {
services.issueReporter
.reportWarning(
IssueReporter.Type.GENERIC,
"Native multidex is always used for dynamic features. Please " +
"remove 'multiDexEnabled true|false' from your " +
"build.gradle file."
)
}
}
/*
* Providers of data coming from the base modules. These are loaded just once and finalized.
*/
private val baseModuleMetadata: Provider<ModuleMetadata> = instantiateBaseModuleMetadata(variantDependencies)
private val featureSetMetadata: Provider<FeatureSetMetadata> = instantiateFeatureSetMetadata(variantDependencies)
// ---------------------------------------------------------------------------------------------
// PUBLIC API
// ---------------------------------------------------------------------------------------------
override val applicationId: Provider<String> =
internalServices.providerOf(String::class.java, baseModuleMetadata.map { it.applicationId })
override val androidResources: AndroidResourcesImpl by lazy {
getAndroidResources(dslInfo.androidResourcesDsl.androidResources)
}
override val packaging: TestedApkPackagingImpl by lazy {
TestedApkPackagingImpl(
dslInfo.packaging,
internalServices,
minSdk.apiLevel
)
}
override val deviceTests: List<DeviceTest>
get() = internalDeviceTests
override val hostTests: Map<String, HostTestCreationConfig>
get() = internalHostTests
override var testFixtures: TestFixturesImpl? = null
override val renderscript: Renderscript? by lazy {
renderscriptCreationConfig?.renderscript
}
override val dexing: DexingCreationConfig by lazy(LazyThreadSafetyMode.NONE) {
DexingImpl(
this,
variantBuilder._enableMultiDex,
dslInfo.dexingDslInfo.multiDexKeepProguard,
dslInfo.dexingDslInfo.multiDexKeepFile,
internalServices,
)
}
// ---------------------------------------------------------------------------------------------
// INTERNAL API
// ---------------------------------------------------------------------------------------------
override val targetSdk: AndroidVersion by lazy(LazyThreadSafetyMode.NONE) {
variantBuilder.targetSdkVersion
}
override val targetSdkVersion: AndroidVersion
get() = targetSdk
override val targetSdkOverride: AndroidVersion?
get() = variantBuilder.mutableTargetSdk?.sanitize()
// always false for this type
override val embedsMicroApp: Boolean
get() = false
override val testOnlyApk: Boolean
get() = isTestApk()
override val baseModuleDebuggable: Provider<Boolean> = internalServices.providerOf(
Boolean::class.java,
baseModuleMetadata.map { it.debuggable })
override val featureName: Provider<String> = run {
val projectPath = internalServices.projectInfo.path
internalServices.providerOf(String::class.java, featureSetMetadata.map {
it.getFeatureNameFor(projectPath)
?: throw RuntimeException("Failed to find feature name for $projectPath in ${it.sourceFile}")
})
}
/**
* resource offset for resource compilation of a feature.
* This is computed by the base module and consumed by the features. */
override val resOffset: Provider<Int> = run {
val projectPath = internalServices.projectInfo.path
internalServices.providerOf(Int::class.java, featureSetMetadata.map {
it.getResOffsetFor(projectPath)
?: throw RuntimeException("Failed to find resource offset for $projectPath in ${it.sourceFile}")
})
}
override val debuggable: Boolean
get() = dslInfo.isDebuggable
override val shouldPackageProfilerDependencies: Boolean = false
override val advancedProfilingTransforms: List<String>
get() {
return services.projectOptions[StringOption.IDE_ANDROID_CUSTOM_CLASS_TRANSFORMS]?.split(
","
) ?: emptyList()
}
override val signingConfig: SigningConfigImpl? = null
override val useJacocoTransformInstrumentation: Boolean
get() = deviceTests?.any { it.codeCoverageEnabled } ?: false
override val packageJacocoRuntime: Boolean
get() = false
override fun addTestComponent(testTypeName: String, testComponent: HostTestCreationConfig) {
internalHostTests[testTypeName] = testComponent
}
override fun addDeviceTest(deviceTest: DeviceTest) {
internalDeviceTests.add(deviceTest)
}
// ---------------------------------------------------------------------------------------------
// Private stuff
// ---------------------------------------------------------------------------------------------
private val internalHostTests = mutableMapOf<String, HostTestCreationConfig>()
private val internalDeviceTests = mutableListOf<DeviceTest>()
private fun instantiateBaseModuleMetadata(
variantDependencies: VariantDependencies
): Provider<ModuleMetadata> {
val artifact = variantDependencies
.getArtifactFileCollection(
ConsumedConfigType.COMPILE_CLASSPATH,
ArtifactScope.PROJECT,
AndroidArtifacts.ArtifactType.BASE_MODULE_METADATA
)
// Have to wrap the return of artifact.elements.map because we cannot call
// finalizeValueOnRead directly on Provider
return internalServices.providerOf(
ModuleMetadata::class.java,
artifact.elements.map { ModuleMetadata.load(it.single().asFile) })
}
private fun instantiateFeatureSetMetadata(
variantDependencies: VariantDependencies
): Provider<FeatureSetMetadata> {
val artifact = variantDependencies.getArtifactFileCollection(
ConsumedConfigType.COMPILE_CLASSPATH,
ArtifactScope.PROJECT,
AndroidArtifacts.ArtifactType.FEATURE_SET_METADATA
)
// Have to wrap the return of artifact.elements.map because we cannot call
// finalizeValueOnRead directly on Provider
return internalServices.providerOf(
FeatureSetMetadata::class.java,
artifact.elements.map { FeatureSetMetadata.load(it.single().asFile) })
}
// version name is coming from the base module via a published artifact, and therefore
// is ready only
// The public API does not expose this so this is ok, but it's safer to make it read-only
// directly to catch potential errors.
// The old API has a check for this type of plugins to avoid calling set() on it.
override val baseModuleVersionName: Property<String?> =
internalServices.nullablePropertyOf(
String::class.java,
baseModuleMetadata.map { it.versionName },
).also {
it.disallowChanges()
it.finalizeValueOnRead()
}
// version code is coming from the base module via a published artifact, and therefore
// is ready only
// The public API does not expose this so this is ok, but it's safer to make it read-only
// directly to catch potential errors.
// The old API has a check for this type of plugins to avoid calling set() on it.
override val baseModuleVersionCode : Property<Int?> =
internalServices.nullablePropertyOf(
Int::class.java,
baseModuleMetadata.map { it.versionCode?.toInt() },
).also {
it.disallowChanges()
it.finalizeValueOnRead()
}
override fun <T : Component> createUserVisibleVariantObject(
stats: GradleBuildVariant.Builder?
): T =
if (stats == null) {
this as T
} else {
services.newInstance(
AnalyticsEnabledDynamicFeatureVariant::class.java,
this,
stats
) as T
}
override val optimizationCreationConfig by lazy(LazyThreadSafetyMode.NONE) {
OptimizationCreationConfigImpl(
this,
dslInfo.optimizationDslInfo,
null,
null,
internalServices,
baseModuleMetadata
)
}
override val enableApiModeling: Boolean
get() = isApiModelingEnabled()
override val enableGlobalSynthetics: Boolean
get() = isGlobalSyntheticsEnabled()
override fun finalizeAndLock() {
super.finalizeAndLock()
dexing.finalizeAndLock()
}
override val isForceAotCompilation: Boolean
get() = experimentalProperties.map {
ModulePropertyKey.BooleanWithDefault.FORCE_AOT_COMPILATION.getValue(it)
}.getOrElse(false)
}