blob: 1378784351f27449575b76422eabaa5ede37c8b0 [file] [log] [blame]
/*
* Copyright (C) 2014 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 static com.android.builder.core.VariantTypeImpl.ANDROID_TEST;
import static com.android.builder.core.VariantTypeImpl.UNIT_TEST;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.api.artifact.impl.ArtifactsImpl;
import com.android.build.api.attributes.ProductFlavorAttr;
import com.android.build.api.component.ComponentIdentity;
import com.android.build.api.component.impl.AndroidTestImpl;
import com.android.build.api.component.impl.AndroidTestPropertiesImpl;
import com.android.build.api.component.impl.TestComponentImpl;
import com.android.build.api.component.impl.TestComponentPropertiesImpl;
import com.android.build.api.component.impl.UnitTestImpl;
import com.android.build.api.component.impl.UnitTestPropertiesImpl;
import com.android.build.api.variant.impl.VariantImpl;
import com.android.build.api.variant.impl.VariantPropertiesImpl;
import com.android.build.gradle.BaseExtension;
import com.android.build.gradle.TestedAndroidConfig;
import com.android.build.gradle.internal.api.DefaultAndroidSourceSet;
import com.android.build.gradle.internal.api.ReadOnlyObjectProvider;
import com.android.build.gradle.internal.api.VariantFilter;
import com.android.build.gradle.internal.core.VariantBuilder;
import com.android.build.gradle.internal.core.VariantDslInfo;
import com.android.build.gradle.internal.core.VariantDslInfoImpl;
import com.android.build.gradle.internal.core.VariantSources;
import com.android.build.gradle.internal.crash.ExternalApiUsageException;
import com.android.build.gradle.internal.dependency.VariantDependencies;
import com.android.build.gradle.internal.dependency.VariantDependenciesBuilder;
import com.android.build.gradle.internal.dsl.ActionableVariantObjectOperationsExecutor;
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension;
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.manifest.LazyManifestParser;
import com.android.build.gradle.internal.pipeline.TransformManager;
import com.android.build.gradle.internal.profile.AnalyticsUtil;
import com.android.build.gradle.internal.scope.BuildFeatureValues;
import com.android.build.gradle.internal.scope.GlobalScope;
import com.android.build.gradle.internal.scope.MutableTaskContainer;
import com.android.build.gradle.internal.scope.VariantScope;
import com.android.build.gradle.internal.scope.VariantScopeImpl;
import com.android.build.gradle.internal.services.DslServices;
import com.android.build.gradle.internal.services.ProjectServices;
import com.android.build.gradle.internal.services.TaskCreationServices;
import com.android.build.gradle.internal.services.TaskCreationServicesImpl;
import com.android.build.gradle.internal.services.VariantApiServices;
import com.android.build.gradle.internal.services.VariantApiServicesImpl;
import com.android.build.gradle.internal.services.VariantPropertiesApiServicesImpl;
import com.android.build.gradle.internal.variant.BaseVariantData;
import com.android.build.gradle.internal.variant.ComponentInfo;
import com.android.build.gradle.internal.variant.DimensionCombination;
import com.android.build.gradle.internal.variant.DimensionCombinator;
import com.android.build.gradle.internal.variant.TestVariantData;
import com.android.build.gradle.internal.variant.TestedVariantData;
import com.android.build.gradle.internal.variant.VariantFactory;
import com.android.build.gradle.internal.variant.VariantInputModel;
import com.android.build.gradle.internal.variant.VariantPathHelper;
import com.android.build.gradle.options.BooleanOption;
import com.android.build.gradle.options.ProjectOptions;
import com.android.build.gradle.options.SigningOptions;
import com.android.builder.core.VariantType;
import com.android.builder.errors.IssueReporter;
import com.android.builder.profile.ProcessProfileWriter;
import com.android.builder.profile.Recorder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.wireless.android.sdk.stats.ApiVersion;
import com.google.wireless.android.sdk.stats.GradleBuildVariant;
import java.io.File;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.model.ObjectFactory;
/** Class to create, manage variants. */
public class VariantManager<
VariantT extends VariantImpl<VariantPropertiesT>,
VariantPropertiesT extends VariantPropertiesImpl> {
@NonNull private final Project project;
@NonNull private final ProjectOptions projectOptions;
@NonNull private final BaseExtension extension;
@NonNull private final VariantFactory<VariantT, VariantPropertiesT> variantFactory;
@NonNull
private final VariantInputModel<DefaultConfig, BuildType, ProductFlavor, SigningConfig>
variantInputModel;
@NonNull private final VariantApiServices variantApiServices;
@NonNull private final VariantPropertiesApiServicesImpl variantPropertiesApiServices;
@NonNull private final TaskCreationServices taskCreationServices;
@NonNull private final ProjectServices projectServices;
@NonNull private final Recorder recorder;
@NonNull private final VariantFilter variantFilter;
@NonNull
private final List<ComponentInfo<VariantT, VariantPropertiesT>> variants = Lists.newArrayList();
@NonNull
private final List<
ComponentInfo<
TestComponentImpl<? extends TestComponentPropertiesImpl>,
TestComponentPropertiesImpl>>
testComponents = Lists.newArrayList();
@NonNull
private final Map<File, LazyManifestParser> lazyManifestParserMap =
Maps.newHashMapWithExpectedSize(3);
@NonNull protected final GlobalScope globalScope;
@Nullable private final SigningConfig signingOverride;
// We cannot use gradle's state of executed as that returns true while inside afterEvalute.
// Wew want this to only be true after all tasks have been create.
private boolean hasCreatedTasks = false;
public static final Attribute<String> SHRINKER_ATTR =
Attribute.of("codeShrinker", String.class);
public VariantManager(
@NonNull GlobalScope globalScope,
@NonNull Project project,
@NonNull ProjectOptions projectOptions,
@NonNull BaseExtension extension,
@NonNull VariantFactory<VariantT, VariantPropertiesT> variantFactory,
@NonNull VariantInputModel variantInputModel,
@NonNull ProjectServices projectServices,
@NonNull Recorder recorder) {
this.globalScope = globalScope;
this.extension = extension;
this.project = project;
this.projectOptions = projectOptions;
this.variantFactory = variantFactory;
this.variantInputModel = variantInputModel;
this.projectServices = projectServices;
this.recorder = recorder;
this.signingOverride = createSigningOverride();
this.variantFilter = new VariantFilter(new ReadOnlyObjectProvider());
variantApiServices = new VariantApiServicesImpl(projectServices);
variantPropertiesApiServices = new VariantPropertiesApiServicesImpl(projectServices);
taskCreationServices = new TaskCreationServicesImpl(projectServices);
}
/**
* Returns a list of all main components.
*
* @see #createVariants()
*/
@NonNull
public List<ComponentInfo<VariantT, VariantPropertiesT>> getMainComponents() {
return variants;
}
/**
* Returns a list of all test components.
*
* @see #createVariants()
*/
@NonNull
public List<
ComponentInfo<
TestComponentImpl<? extends TestComponentPropertiesImpl>,
TestComponentPropertiesImpl>>
getTestComponents() {
return testComponents;
}
/** Creates the variants. */
public void createVariants() {
variantFactory.validateModel(variantInputModel);
variantFactory.preVariantWork(project);
computeVariants();
}
@NonNull
private Map<Attribute<ProductFlavorAttr>, ProductFlavorAttr> getFlavorSelection(
@NonNull VariantDslInfo variantDslInfo) {
ObjectFactory factory = project.getObjects();
return variantDslInfo.getMissingDimensionStrategies().entrySet().stream()
.collect(
Collectors.toMap(
entry -> Attribute.of(entry.getKey(), ProductFlavorAttr.class),
entry ->
factory.named(
ProductFlavorAttr.class,
entry.getValue().getRequested())));
}
/**
* Returns a modified name.
*
* <p>This name is used to request a missing dimension. It is the same name as the flavor that
* sets up the request, which means it's not going to be matched, and instead it'll go to a
* custom fallbacks provided by the flavor.
*
* <p>We are just modifying the name to avoid collision in case the same name exists in
* different dimensions
*/
public static String getModifiedName(@NonNull String name) {
return "____" + name;
}
/** Create all variants. */
private void computeVariants() {
List<String> flavorDimensionList = extension.getFlavorDimensionList();
DimensionCombinator computer =
new DimensionCombinator(
variantInputModel,
projectServices.getIssueReporter(),
flavorDimensionList);
List<DimensionCombination> variants = computer.computeVariants();
// get some info related to testing
BuildTypeData<BuildType> testBuildTypeData = getTestBuildTypeData();
// loop on all the new variant objects to create the legacy ones.
for (DimensionCombination variant : variants) {
createVariantsFromCombination(variant, testBuildTypeData);
}
// FIXME we should lock the variant API properties after all the onVariants, and
// before any onVariantProperties to avoid cross access between the two.
// This means changing the way to run onVariants vs onVariantProperties.
variantApiServices.lockValues();
}
@Nullable
private BuildTypeData<BuildType> getTestBuildTypeData() {
BuildTypeData<BuildType> testBuildTypeData = null;
if (extension instanceof TestedAndroidConfig) {
TestedAndroidConfig testedExtension = (TestedAndroidConfig) extension;
testBuildTypeData =
variantInputModel.getBuildTypes().get(testedExtension.getTestBuildType());
if (testBuildTypeData == null) {
throw new RuntimeException(
String.format(
"Test Build Type '%1$s' does not" + " exist.",
testedExtension.getTestBuildType()));
}
}
return testBuildTypeData;
}
@Nullable
private ComponentInfo<VariantT, VariantPropertiesT> createVariant(
@NonNull DimensionCombination dimensionCombination,
@NonNull BuildTypeData<BuildType> buildTypeData,
@NonNull List<ProductFlavorData<ProductFlavor>> productFlavorDataList,
@NonNull VariantType variantType) {
// entry point for a given buildType/Flavors/VariantType combo.
// Need to run the new variant API to selectively ignore variants.
// in order to do this, we need access to the VariantDslInfo, to create a
DslServices dslServices = globalScope.getDslServices();
final DefaultConfigData<DefaultConfig> defaultConfig =
variantInputModel.getDefaultConfigData();
DefaultAndroidSourceSet defaultConfigSourceProvider = defaultConfig.getSourceSet();
VariantBuilder variantBuilder =
VariantBuilder.getBuilder(
dimensionCombination,
variantType,
defaultConfig.getDefaultConfig(),
defaultConfigSourceProvider,
buildTypeData.getBuildType(),
buildTypeData.getSourceSet(),
signingOverride,
getLazyManifestParser(
defaultConfigSourceProvider.getManifestFile(),
variantType.getRequiresManifest(),
this::canParseManifest),
dslServices,
variantPropertiesApiServices);
// We must first add the flavors to the variant config, in order to get the proper
// variant-specific and multi-flavor name as we add/create the variant providers later.
for (ProductFlavorData<ProductFlavor> productFlavorData : productFlavorDataList) {
variantBuilder.addProductFlavor(
productFlavorData.getProductFlavor(), productFlavorData.getSourceSet());
}
VariantDslInfoImpl variantDslInfo = variantBuilder.createVariantDslInfo();
ComponentIdentity componentIdentity = variantDslInfo.getComponentIdentity();
// create the Variant object so that we can run the action which may interrupt the creation
// (in case of enabled = false)
VariantT variant =
variantFactory.createVariantObject(
componentIdentity, variantDslInfo, variantApiServices);
// HACK, we need access to the new type rather than the old. This will go away in the
// future
//noinspection unchecked
ActionableVariantObjectOperationsExecutor<VariantT, VariantPropertiesT> commonExtension =
(ActionableVariantObjectOperationsExecutor<VariantT, VariantPropertiesT>) extension;
commonExtension.executeVariantOperations(variant);
if (!variant.getEnabled()) {
return null;
}
// now that we have the result of the filter, we can continue configuring the variant
createCompoundSourceSets(productFlavorDataList, variantBuilder);
VariantSources variantSources = variantBuilder.createVariantSources();
// Only record release artifacts
if (!buildTypeData.getBuildType().isDebuggable()
&& variantType.isApk()
&& !variantDslInfo.getVariantType().isForTesting()) {
ProcessProfileWriter.get()
.recordApplicationId(() -> variantDslInfo.getApplicationId().get());
}
// Add the container of dependencies.
// The order of the libraries is important, in descending order:
// variant-specific, build type, multi-flavor, flavor1, flavor2, ..., defaultConfig.
// variant-specific if the full combo of flavors+build type. Does not exist if no flavors.
// multi-flavor is the combination of all flavor dimensions. Does not exist if <2 dimension.
final List<DefaultAndroidSourceSet> variantSourceSets =
Lists.newArrayListWithExpectedSize(productFlavorDataList.size() + 4);
// 1. add the variant-specific if applicable.
if (!productFlavorDataList.isEmpty()) {
variantSourceSets.add(
(DefaultAndroidSourceSet) variantSources.getVariantSourceProvider());
}
// 2. the build type.
variantSourceSets.add(buildTypeData.getSourceSet());
// 3. the multi-flavor combination
if (productFlavorDataList.size() > 1) {
variantSourceSets.add(
(DefaultAndroidSourceSet) variantSources.getMultiFlavorSourceProvider());
}
// 4. the flavors.
for (ProductFlavorData<ProductFlavor> productFlavor : productFlavorDataList) {
variantSourceSets.add(productFlavor.getSourceSet());
}
// 5. The defaultConfig
variantSourceSets.add(variantInputModel.getDefaultConfigData().getSourceSet());
// Create VariantDependencies
VariantDependenciesBuilder builder =
VariantDependenciesBuilder.builder(
project,
projectOptions,
projectServices.getIssueReporter(),
variantDslInfo)
.setFlavorSelection(getFlavorSelection(variantDslInfo))
.addSourceSets(variantSourceSets);
if (extension instanceof BaseAppModuleExtension) {
builder.setFeatureList(((BaseAppModuleExtension) extension).getDynamicFeatures());
}
final VariantDependencies variantDependencies = builder.build();
// Done. Create the (too) many variant objects
VariantPathHelper pathHelper = new VariantPathHelper(project, variantDslInfo, dslServices);
ArtifactsImpl artifacts = new ArtifactsImpl(project, componentIdentity.getName());
MutableTaskContainer taskContainer = new MutableTaskContainer();
TransformManager transformManager =
new TransformManager(project, dslServices.getIssueReporter(), recorder);
// create the obsolete VariantScope
VariantScopeImpl variantScope =
new VariantScopeImpl(
componentIdentity,
variantDslInfo,
variantDependencies,
pathHelper,
artifacts,
globalScope,
null /* testedVariantProperties*/);
// and the obsolete variant data
BaseVariantData variantData =
variantFactory.createVariantData(
componentIdentity,
variantDslInfo,
variantDependencies,
variantSources,
pathHelper,
artifacts,
variantPropertiesApiServices,
globalScope,
taskContainer);
// then the new VariantProperties which will contain the 2 old objects.
VariantPropertiesT variantProperties =
variantFactory.createVariantPropertiesObject(
variant,
componentIdentity,
variantFactory.createBuildFeatureValues(
extension.getBuildFeatures(), projectOptions),
variantDslInfo,
variantDependencies,
variantSources,
pathHelper,
artifacts,
variantScope,
variantData,
transformManager,
variantPropertiesApiServices,
taskCreationServices);
// Run the VariantProperties actions
commonExtension.executeVariantPropertiesOperations(variantProperties);
// also execute the delayed actions registered on the Variant object itself
variant.executePropertiesActions(variantProperties);
return new ComponentInfo<>(variant, variantProperties);
}
private void createCompoundSourceSets(
@NonNull List<ProductFlavorData<ProductFlavor>> productFlavorList,
@NonNull VariantBuilder variantBuilder) {
final VariantType variantType = variantBuilder.getVariantType();
if (!productFlavorList.isEmpty() /* && !variantConfig.getType().isSingleBuildType()*/) {
DefaultAndroidSourceSet variantSourceSet =
(DefaultAndroidSourceSet)
variantInputModel
.getSourceSetManager()
.setUpSourceSet(
VariantBuilder.computeSourceSetName(
variantBuilder.getName(), variantType),
variantType.isTestComponent());
variantBuilder.setVariantSourceProvider(variantSourceSet);
}
if (productFlavorList.size() > 1) {
DefaultAndroidSourceSet multiFlavorSourceSet =
(DefaultAndroidSourceSet)
variantInputModel
.getSourceSetManager()
.setUpSourceSet(
VariantBuilder.computeSourceSetName(
variantBuilder.getFlavorName(), variantType),
variantType.isTestComponent());
variantBuilder.setMultiFlavorSourceProvider(multiFlavorSourceSet);
}
}
/** Create a TestVariantData for the specified testedVariantData. */
@Nullable
public ComponentInfo<
TestComponentImpl<? extends TestComponentPropertiesImpl>,
TestComponentPropertiesImpl>
createTestComponents(
@NonNull DimensionCombination dimensionCombination,
@NonNull BuildTypeData<BuildType> buildTypeData,
@NonNull List<ProductFlavorData<ProductFlavor>> productFlavorDataList,
@NonNull VariantT testedVariant,
@NonNull VariantPropertiesT testedVariantProperties,
@NonNull VariantType variantType) {
// handle test variant
// need a suppress warning because ProductFlavor.getTestSourceSet(type) is annotated
// to return @Nullable and the constructor is @NonNull on this parameter,
// but it's never the case on defaultConfigData
// The constructor does a runtime check on the instances so we should be safe.
final DefaultAndroidSourceSet testSourceSet =
variantInputModel.getDefaultConfigData().getTestSourceSet(variantType);
DslServices dslServices = globalScope.getDslServices();
@SuppressWarnings("ConstantConditions")
VariantBuilder variantBuilder =
VariantBuilder.getBuilder(
dimensionCombination,
variantType,
variantInputModel.getDefaultConfigData().getDefaultConfig(),
testSourceSet,
buildTypeData.getBuildType(),
buildTypeData.getTestSourceSet(variantType),
signingOverride,
getLazyManifestParser(
testSourceSet.getManifestFile(),
variantType.getRequiresManifest(),
this::canParseManifest),
dslServices,
variantPropertiesApiServices);
variantBuilder.setTestedVariant(
(VariantDslInfoImpl) testedVariantProperties.getVariantDslInfo());
List<ProductFlavor> productFlavorList =
testedVariantProperties.getVariantDslInfo().getProductFlavorList();
// We must first add the flavors to the variant builder, in order to get the proper
// variant-specific and multi-flavor name as we add/create the variant providers later.
final Map<String, ProductFlavorData<ProductFlavor>> productFlavors =
variantInputModel.getProductFlavors();
for (ProductFlavor productFlavor : productFlavorList) {
ProductFlavorData<ProductFlavor> data = productFlavors.get(productFlavor.getName());
//noinspection ConstantConditions
variantBuilder.addProductFlavor(
data.getProductFlavor(), data.getTestSourceSet(variantType));
}
VariantDslInfoImpl variantDslInfo = variantBuilder.createVariantDslInfo();
TestComponentImpl<? extends TestComponentPropertiesImpl> component;
// this is ANDROID_TEST
if (variantType.isApk()) {
AndroidTestImpl androidTestVariant =
variantFactory.createAndroidTestObject(
variantDslInfo.getComponentIdentity(),
variantDslInfo,
variantApiServices);
// run the action registered on the tested variant via androidTest {}
testedVariant.executeAndroidTestActions(androidTestVariant);
component = androidTestVariant;
} else {
// this is UNIT_TEST
UnitTestImpl unitTestVariant =
variantFactory.createUnitTestObject(
variantDslInfo.getComponentIdentity(),
variantDslInfo,
variantApiServices);
// run the action registered on the tested variant via unitTest {}
testedVariant.executeUnitTestActions(unitTestVariant);
component = unitTestVariant;
}
if (!component.getEnabled()) {
return null;
}
// now that we have the result of the filter, we can continue configuring the variant
createCompoundSourceSets(productFlavorDataList, variantBuilder);
VariantSources variantSources = variantBuilder.createVariantSources();
// Add the container of dependencies, the order of the libraries is important.
// In descending order: build type (only for unit test), flavors, defaultConfig.
// Add the container of dependencies.
// The order of the libraries is important, in descending order:
// variant-specific, build type (, multi-flavor, flavor1, flavor2, ..., defaultConfig.
// variant-specific if the full combo of flavors+build type. Does not exist if no flavors.
// multi-flavor is the combination of all flavor dimensions. Does not exist if <2 dimension.
List<ProductFlavor> testProductFlavors = variantDslInfo.getProductFlavorList();
List<DefaultAndroidSourceSet> testVariantSourceSets =
Lists.newArrayListWithExpectedSize(4 + testProductFlavors.size());
// 1. add the variant-specific if applicable.
if (!testProductFlavors.isEmpty()) {
testVariantSourceSets.add(
(DefaultAndroidSourceSet) variantSources.getVariantSourceProvider());
}
// 2. the build type.
DefaultAndroidSourceSet buildTypeConfigurationProvider =
buildTypeData.getTestSourceSet(variantType);
if (buildTypeConfigurationProvider != null) {
testVariantSourceSets.add(buildTypeConfigurationProvider);
}
// 3. the multi-flavor combination
if (testProductFlavors.size() > 1) {
testVariantSourceSets.add(
(DefaultAndroidSourceSet) variantSources.getMultiFlavorSourceProvider());
}
// 4. the flavors.
for (ProductFlavor productFlavor : testProductFlavors) {
testVariantSourceSets.add(
variantInputModel
.getProductFlavors()
.get(productFlavor.getName())
.getTestSourceSet(variantType));
}
// now add the default config
testVariantSourceSets.add(
variantInputModel.getDefaultConfigData().getTestSourceSet(variantType));
// If the variant being tested is a library variant, VariantDependencies must be
// computed after the tasks for the tested variant is created. Therefore, the
// VariantDependencies is computed here instead of when the VariantData was created.
VariantDependenciesBuilder builder =
VariantDependenciesBuilder.builder(
project,
projectOptions,
projectServices.getIssueReporter(),
variantDslInfo)
.addSourceSets(testVariantSourceSets)
.setFlavorSelection(getFlavorSelection(variantDslInfo))
.setTestedVariant(testedVariantProperties);
final VariantDependencies variantDependencies = builder.build();
VariantPathHelper pathHelper = new VariantPathHelper(project, variantDslInfo, dslServices);
ComponentIdentity componentIdentity = variantDslInfo.getComponentIdentity();
ArtifactsImpl artifacts = new ArtifactsImpl(project, componentIdentity.getName());
MutableTaskContainer taskContainer = new MutableTaskContainer();
TransformManager transformManager =
new TransformManager(project, dslServices.getIssueReporter(), recorder);
VariantScopeImpl variantScope =
new VariantScopeImpl(
componentIdentity,
variantDslInfo,
variantDependencies,
pathHelper,
artifacts,
globalScope,
testedVariantProperties);
// create the internal storage for this variant.
TestVariantData testVariantData =
new TestVariantData(
componentIdentity,
variantDslInfo,
variantDependencies,
variantSources,
pathHelper,
artifacts,
(TestedVariantData) testedVariantProperties.getVariantData(),
variantPropertiesApiServices,
globalScope,
taskContainer);
// in order to call executePropertiesActions, we need a slightly different type....
//noinspection unchecked
TestComponentImpl<TestComponentPropertiesImpl> testComponent =
(TestComponentImpl<TestComponentPropertiesImpl>) component;
TestComponentPropertiesImpl componentProperties;
BuildFeatureValues buildFeatureValues =
variantFactory.createTestBuildFeatureValues(
extension.getBuildFeatures(), extension.getDataBinding(), projectOptions);
// this is ANDROID_TEST
if (variantType.isApk()) {
AndroidTestPropertiesImpl androidTestProperties =
variantFactory.createAndroidTestProperties(
componentIdentity,
buildFeatureValues,
variantDslInfo,
variantDependencies,
variantSources,
pathHelper,
artifacts,
variantScope,
testVariantData,
testedVariantProperties,
transformManager,
variantPropertiesApiServices,
taskCreationServices);
// also execute the delayed actions registered on the Component via
// androidTest { onProperties {} }
testComponent.executePropertiesActions(androidTestProperties);
// or on the tested variant via unitTestProperties {}
testedVariant.executeAndroidTestPropertiesActions(androidTestProperties);
componentProperties = androidTestProperties;
} else {
// this is UNIT_TEST
UnitTestPropertiesImpl unitTestProperties =
variantFactory.createUnitTestProperties(
componentIdentity,
buildFeatureValues,
variantDslInfo,
variantDependencies,
variantSources,
pathHelper,
artifacts,
variantScope,
testVariantData,
testedVariantProperties,
transformManager,
variantPropertiesApiServices,
taskCreationServices);
// execute the delayed actions registered on the Component via
// unitTest { onProperties {} }
testComponent.executePropertiesActions(unitTestProperties);
// or on the tested variant via unitTestProperties {}
testedVariant.executeUnitTestPropertiesActions(unitTestProperties);
componentProperties = unitTestProperties;
}
// register
testedVariantProperties
.getTestComponents()
.put(variantDslInfo.getVariantType(), componentProperties);
return new ComponentInfo<>(component, componentProperties);
}
/**
* Creates Variant objects for a specific {@link ComponentIdentity}
*
* <p>This will create both the prod and the androidTest/unitTest variants.
*/
private void createVariantsFromCombination(
@NonNull DimensionCombination dimensionCombination,
@Nullable BuildTypeData<BuildType> testBuildTypeData) {
VariantType variantType = variantFactory.getVariantType();
// first run the old variantFilter API
// This acts on buildtype/flavor only, and applies in one pass to prod/tests.
Action<com.android.build.api.variant.VariantFilter> variantFilterAction =
extension.getVariantFilter();
DefaultConfig defaultConfig = variantInputModel.getDefaultConfigData().getDefaultConfig();
BuildTypeData<BuildType> buildTypeData =
variantInputModel.getBuildTypes().get(dimensionCombination.getBuildType());
BuildType buildType = buildTypeData.getBuildType();
// get the list of ProductFlavorData from the list of flavor name
List<ProductFlavorData<ProductFlavor>> productFlavorDataList =
dimensionCombination
.getProductFlavors()
.stream()
.map(it -> variantInputModel.getProductFlavors().get(it.getSecond()))
.collect(Collectors.toList());
List<ProductFlavor> productFlavorList =
productFlavorDataList
.stream()
.map(ProductFlavorData::getProductFlavor)
.collect(Collectors.toList());
boolean ignore = false;
if (variantFilterAction != null) {
variantFilter.reset(
dimensionCombination, defaultConfig, buildType, variantType, productFlavorList);
try {
// variantFilterAction != null always true here.
variantFilterAction.execute(variantFilter);
} catch (Throwable t) {
throw new ExternalApiUsageException(t);
}
ignore = variantFilter.getIgnore();
}
if (!ignore) {
// create the prod variant
ComponentInfo<VariantT, VariantPropertiesT> variantInfo =
createVariant(
dimensionCombination,
buildTypeData,
productFlavorDataList,
variantType);
if (variantInfo != null) {
addVariant(variantInfo);
VariantPropertiesT variantProperties = variantInfo.getProperties();
VariantDslInfo variantDslInfo = variantProperties.getVariantDslInfo();
VariantScope variantScope = variantProperties.getVariantScope();
int minSdkVersion = variantDslInfo.getMinSdkVersion().getApiLevel();
int targetSdkVersion = variantDslInfo.getTargetSdkVersion().getApiLevel();
if (minSdkVersion > 0 && targetSdkVersion > 0 && minSdkVersion > targetSdkVersion) {
projectServices
.getIssueReporter()
.reportWarning(
IssueReporter.Type.GENERIC,
String.format(
Locale.US,
"minSdkVersion (%d) is greater than targetSdkVersion"
+ " (%d) for variant \"%s\". Please change the"
+ " values such that minSdkVersion is less than or"
+ " equal to targetSdkVersion.",
minSdkVersion,
targetSdkVersion,
variantProperties.getName()));
}
GradleBuildVariant.Builder profileBuilder =
ProcessProfileWriter.getOrCreateVariant(
project.getPath(), variantProperties.getName())
.setIsDebug(buildType.isDebuggable())
.setMinSdkVersion(
AnalyticsUtil.toProto(variantDslInfo.getMinSdkVersion()))
.setMinifyEnabled(variantScope.getCodeShrinker() != null)
.setUseMultidex(variantDslInfo.isMultiDexEnabled())
.setUseLegacyMultidex(variantDslInfo.isLegacyMultiDexMode())
.setVariantType(
variantProperties
.getVariantType()
.getAnalyticsVariantType())
.setDexBuilder(AnalyticsUtil.toProto(variantScope.getDexer()))
.setDexMerger(AnalyticsUtil.toProto(variantScope.getDexMerger()))
.setCoreLibraryDesugaringEnabled(
variantScope.isCoreLibraryDesugaringEnabled())
.setTestExecution(
AnalyticsUtil.toProto(
globalScope
.getExtension()
.getTestOptions()
.getExecutionEnum()));
if (variantScope.getCodeShrinker() != null) {
profileBuilder.setCodeShrinker(
AnalyticsUtil.toProto(variantScope.getCodeShrinker()));
}
if (variantDslInfo.getTargetSdkVersion().getApiLevel() > 0) {
profileBuilder.setTargetSdkVersion(
AnalyticsUtil.toProto(variantDslInfo.getTargetSdkVersion()));
}
if (variantDslInfo.getMaxSdkVersion() != null) {
profileBuilder.setMaxSdkVersion(
ApiVersion.newBuilder().setApiLevel(variantDslInfo.getMaxSdkVersion()));
}
VariantScope.Java8LangSupport supportType = variantScope.getJava8LangSupportType();
if (supportType != VariantScope.Java8LangSupport.INVALID
&& supportType != VariantScope.Java8LangSupport.UNUSED) {
profileBuilder.setJava8LangSupport(AnalyticsUtil.toProto(supportType));
}
if (variantFactory.getVariantType().getHasTestComponents()) {
if (buildTypeData == testBuildTypeData) {
ComponentInfo<
TestComponentImpl<? extends TestComponentPropertiesImpl>,
TestComponentPropertiesImpl>
androidTest =
createTestComponents(
dimensionCombination,
buildTypeData,
productFlavorDataList,
variantInfo.getVariant(),
variantProperties,
ANDROID_TEST);
if (androidTest != null) {
addTestComponent(androidTest);
}
}
ComponentInfo<
TestComponentImpl<? extends TestComponentPropertiesImpl>,
TestComponentPropertiesImpl>
unitTest =
createTestComponents(
dimensionCombination,
buildTypeData,
productFlavorDataList,
variantInfo.getVariant(),
variantProperties,
UNIT_TEST);
if (unitTest != null) {
addTestComponent(unitTest);
}
}
}
}
}
private void addVariant(@NonNull ComponentInfo<VariantT, VariantPropertiesT> variant) {
variants.add(variant);
}
private void addTestComponent(
@NonNull
ComponentInfo<
TestComponentImpl<? extends TestComponentPropertiesImpl>,
TestComponentPropertiesImpl>
testComponent) {
testComponents.add(testComponent);
}
private SigningConfig createSigningOverride() {
SigningOptions signingOptions = SigningOptions.readSigningOptions(projectOptions);
if (signingOptions != null) {
com.android.build.gradle.internal.dsl.SigningConfig signingConfigDsl =
new com.android.build.gradle.internal.dsl.SigningConfig("externalOverride");
signingConfigDsl.setStoreFile(new File(signingOptions.getStoreFile()));
signingConfigDsl.setStorePassword(signingOptions.getStorePassword());
signingConfigDsl.setKeyAlias(signingOptions.getKeyAlias());
signingConfigDsl.setKeyPassword(signingOptions.getKeyPassword());
if (signingOptions.getStoreType() != null) {
signingConfigDsl.setStoreType(signingOptions.getStoreType());
}
if (signingOptions.getV1Enabled() != null) {
signingConfigDsl.setV1SigningEnabled(signingOptions.getV1Enabled());
}
if (signingOptions.getV2Enabled() != null) {
signingConfigDsl.setV2SigningEnabled(signingOptions.getV2Enabled());
}
return signingConfigDsl;
}
return null;
}
@NonNull
private LazyManifestParser getLazyManifestParser(
@NonNull File file,
boolean isManifestFileRequired,
@NonNull BooleanSupplier isInExecutionPhase) {
return lazyManifestParserMap.computeIfAbsent(
file,
f ->
new LazyManifestParser(
projectServices.getObjectFactory().fileProperty().fileValue(f),
isManifestFileRequired,
projectServices,
isInExecutionPhase));
}
private boolean canParseManifest() {
return hasCreatedTasks || !projectOptions.get(BooleanOption.DISABLE_EARLY_MANIFEST_PARSING);
}
public void setHasCreatedTasks(boolean hasCreatedTasks) {
this.hasCreatedTasks = hasCreatedTasks;
}
public void lockVariantProperties() {
variantPropertiesApiServices.lockProperties();
}
}