| /* |
| * 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.BuilderConstants.LINT; |
| import static com.android.builder.core.VariantType.ANDROID_TEST; |
| import static com.android.builder.core.VariantType.LIBRARY; |
| import static com.android.builder.core.VariantType.UNIT_TEST; |
| |
| import com.android.annotations.NonNull; |
| import com.android.annotations.Nullable; |
| import com.android.build.gradle.AndroidConfig; |
| import com.android.build.gradle.AndroidGradleOptions; |
| import com.android.build.gradle.TestAndroidConfig; |
| import com.android.build.gradle.TestedAndroidConfig; |
| import com.android.build.gradle.api.AndroidSourceSet; |
| 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.GradleVariantConfiguration; |
| import com.android.build.gradle.internal.dependency.VariantDependencies; |
| import com.android.build.gradle.internal.dsl.CoreBuildType; |
| import com.android.build.gradle.internal.dsl.CoreProductFlavor; |
| import com.android.build.gradle.internal.profile.SpanRecorders; |
| import com.android.build.gradle.internal.variant.BaseVariantData; |
| import com.android.build.gradle.internal.variant.BaseVariantOutputData; |
| 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.builder.core.AndroidBuilder; |
| import com.android.builder.core.VariantType; |
| import com.android.builder.model.ProductFlavor; |
| import com.android.builder.model.SigningConfig; |
| import com.android.builder.profile.ExecutionType; |
| import com.android.builder.profile.Recorder; |
| import com.android.builder.profile.ThreadRecorder; |
| import com.android.utils.StringHelper; |
| import com.google.common.base.Function; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| |
| import org.gradle.api.Action; |
| import org.gradle.api.NamedDomainObjectContainer; |
| import org.gradle.api.Project; |
| import org.gradle.api.Task; |
| import org.gradle.internal.reflect.Instantiator; |
| |
| import java.io.File; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Class to create, manage variants. |
| */ |
| public class VariantManager implements VariantModel { |
| |
| private static final String MULTIDEX_VERSION = "1.0.1"; |
| |
| protected static final String COM_ANDROID_SUPPORT_MULTIDEX = |
| "com.android.support:multidex:" + MULTIDEX_VERSION; |
| protected static final String COM_ANDROID_SUPPORT_MULTIDEX_INSTRUMENTATION = |
| "com.android.support:multidex-instrumentation:" + MULTIDEX_VERSION; |
| |
| @NonNull |
| private final Project project; |
| @NonNull |
| private final AndroidBuilder androidBuilder; |
| @NonNull |
| private final AndroidConfig extension; |
| @NonNull |
| private final VariantFactory variantFactory; |
| @NonNull |
| private final TaskManager taskManager; |
| @NonNull |
| private final Instantiator instantiator; |
| @NonNull |
| private ProductFlavorData<CoreProductFlavor> defaultConfigData; |
| @NonNull |
| private final Map<String, BuildTypeData> buildTypes = Maps.newHashMap(); |
| @NonNull |
| private final Map<String, ProductFlavorData<CoreProductFlavor>> productFlavors = Maps.newHashMap(); |
| @NonNull |
| private final Map<String, SigningConfig> signingConfigs = Maps.newHashMap(); |
| |
| @NonNull |
| private final ReadOnlyObjectProvider readOnlyObjectProvider = new ReadOnlyObjectProvider(); |
| @NonNull |
| private final VariantFilter variantFilter = new VariantFilter(readOnlyObjectProvider); |
| |
| @NonNull |
| private final List<BaseVariantData<? extends BaseVariantOutputData>> variantDataList = Lists.newArrayList(); |
| @Nullable |
| private SigningConfig signingOverride; |
| |
| public VariantManager( |
| @NonNull Project project, |
| @NonNull AndroidBuilder androidBuilder, |
| @NonNull AndroidConfig extension, |
| @NonNull VariantFactory variantFactory, |
| @NonNull TaskManager taskManager, |
| @NonNull Instantiator instantiator) { |
| this.extension = extension; |
| this.androidBuilder = androidBuilder; |
| this.project = project; |
| this.variantFactory = variantFactory; |
| this.taskManager = taskManager; |
| this.instantiator = instantiator; |
| |
| DefaultAndroidSourceSet mainSourceSet = |
| (DefaultAndroidSourceSet) extension.getSourceSets().getByName(extension.getDefaultConfig().getName()); |
| |
| DefaultAndroidSourceSet androidTestSourceSet = null; |
| DefaultAndroidSourceSet unitTestSourceSet = null; |
| if (variantFactory.hasTestScope()) { |
| androidTestSourceSet = |
| (DefaultAndroidSourceSet) extension.getSourceSets() |
| .getByName(ANDROID_TEST.getPrefix()); |
| unitTestSourceSet = |
| (DefaultAndroidSourceSet) extension.getSourceSets() |
| .getByName(UNIT_TEST.getPrefix()); |
| } |
| |
| defaultConfigData = new ProductFlavorData<CoreProductFlavor>( |
| extension.getDefaultConfig(), mainSourceSet, |
| androidTestSourceSet, unitTestSourceSet, project); |
| signingOverride = createSigningOverride(); |
| } |
| |
| @NonNull |
| @Override |
| public ProductFlavorData<CoreProductFlavor> getDefaultConfig() { |
| return defaultConfigData; |
| } |
| |
| @Override |
| @NonNull |
| public Map<String, BuildTypeData> getBuildTypes() { |
| return buildTypes; |
| } |
| |
| @Override |
| @NonNull |
| public Map<String, ProductFlavorData<CoreProductFlavor>> getProductFlavors() { |
| return productFlavors; |
| } |
| |
| @Override |
| @NonNull |
| public Map<String, SigningConfig> getSigningConfigs() { |
| return signingConfigs; |
| } |
| |
| public void addSigningConfig(@NonNull SigningConfig signingConfig) { |
| signingConfigs.put(signingConfig.getName(), signingConfig); |
| } |
| |
| /** |
| * Adds new BuildType, creating a BuildTypeData, and the associated source set, |
| * and adding it to the map. |
| * @param buildType the build type. |
| */ |
| public void addBuildType(@NonNull CoreBuildType buildType) { |
| String name = buildType.getName(); |
| checkName(name, "BuildType"); |
| |
| if (productFlavors.containsKey(name)) { |
| throw new RuntimeException("BuildType names cannot collide with ProductFlavor names"); |
| } |
| |
| DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSets().maybeCreate(name); |
| |
| DefaultAndroidSourceSet unitTestSourceSet = null; |
| if (variantFactory.hasTestScope()) { |
| unitTestSourceSet = (DefaultAndroidSourceSet) extension |
| .getSourceSets().maybeCreate( |
| computeSourceSetName(buildType.getName(), UNIT_TEST)); |
| } |
| |
| BuildTypeData buildTypeData = new BuildTypeData( |
| buildType, project, mainSourceSet, unitTestSourceSet); |
| |
| buildTypes.put(name, buildTypeData); |
| } |
| |
| /** |
| * Adds a new ProductFlavor, creating a ProductFlavorData and associated source sets, |
| * and adding it to the map. |
| * |
| * @param productFlavor the product flavor |
| */ |
| public void addProductFlavor(@NonNull CoreProductFlavor productFlavor) { |
| String name = productFlavor.getName(); |
| checkName(name, "ProductFlavor"); |
| |
| if (buildTypes.containsKey(name)) { |
| throw new RuntimeException("ProductFlavor names cannot collide with BuildType names"); |
| } |
| |
| DefaultAndroidSourceSet mainSourceSet = (DefaultAndroidSourceSet) extension.getSourceSets().maybeCreate( |
| productFlavor.getName()); |
| |
| DefaultAndroidSourceSet androidTestSourceSet = null; |
| DefaultAndroidSourceSet unitTestSourceSet = null; |
| if (variantFactory.hasTestScope()) { |
| androidTestSourceSet = (DefaultAndroidSourceSet) extension |
| .getSourceSets().maybeCreate( |
| computeSourceSetName(productFlavor.getName(), ANDROID_TEST)); |
| unitTestSourceSet = (DefaultAndroidSourceSet) extension |
| .getSourceSets().maybeCreate( |
| computeSourceSetName(productFlavor.getName(), UNIT_TEST)); |
| } |
| |
| ProductFlavorData<CoreProductFlavor> productFlavorData = |
| new ProductFlavorData<CoreProductFlavor>( |
| productFlavor, |
| mainSourceSet, |
| androidTestSourceSet, |
| unitTestSourceSet, |
| project); |
| |
| productFlavors.put(productFlavor.getName(), productFlavorData); |
| } |
| |
| /** |
| * Return a list of all created VariantData. |
| */ |
| @NonNull |
| public List<BaseVariantData<? extends BaseVariantOutputData>> getVariantDataList() { |
| return variantDataList; |
| } |
| |
| /** |
| * Variant/Task creation entry point. |
| * |
| * Not used by gradle-experimental. |
| */ |
| public void createAndroidTasks() { |
| variantFactory.validateModel(this); |
| variantFactory.preVariantWork(project); |
| |
| final TaskFactory tasks = new TaskContainerAdaptor(project.getTasks()); |
| if (variantDataList.isEmpty()) { |
| ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_VARIANTS, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| populateVariantDataList(); |
| return null; |
| } |
| }); |
| } |
| |
| // Create top level test tasks. |
| ThreadRecorder.get().record(ExecutionType.VARIANT_MANAGER_CREATE_TESTS_TASKS, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| taskManager.createTopLevelTestTasks(tasks, !productFlavors.isEmpty()); |
| return null; |
| } |
| }); |
| |
| for (final BaseVariantData<? extends BaseVariantOutputData> variantData : variantDataList) { |
| |
| SpanRecorders.record(project, ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() throws Exception { |
| createTasksForVariantData(tasks, variantData); |
| return null; |
| } |
| }, |
| new Recorder.Property(SpanRecorders.VARIANT, variantData.getName())); |
| } |
| |
| taskManager.createReportTasks(variantDataList); |
| } |
| |
| /** |
| * Create assemble task for VariantData. |
| */ |
| private void createAssembleTaskForVariantData( |
| TaskFactory tasks, |
| final BaseVariantData<?> variantData) { |
| if (variantData.getType().isForTesting()) { |
| variantData.assembleVariantTask = taskManager.createAssembleTask(tasks, variantData); |
| } else { |
| BuildTypeData buildTypeData = |
| buildTypes.get(variantData.getVariantConfiguration().getBuildType().getName()); |
| |
| if (productFlavors.isEmpty()) { |
| // Reuse assemble task for build type if there is no product flavor. |
| variantData.assembleVariantTask = buildTypeData.getAssembleTask(); |
| } else { |
| variantData.assembleVariantTask = taskManager.createAssembleTask(tasks, variantData); |
| |
| // setup the task dependencies |
| // build type |
| buildTypeData.getAssembleTask().dependsOn(variantData.assembleVariantTask); |
| |
| // each flavor |
| GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration(); |
| for (CoreProductFlavor flavor : variantConfig.getProductFlavors()) { |
| productFlavors.get(flavor.getName()).getAssembleTask() |
| .dependsOn(variantData.assembleVariantTask); |
| } |
| |
| // assembleTask for this flavor(dimension), created on demand if needed. |
| if (variantConfig.getProductFlavors().size() > 1) { |
| final String name = StringHelper.capitalize(variantConfig.getFlavorName()); |
| final String variantAssembleTaskName = "assemble" + name; |
| if (!tasks.containsKey(variantAssembleTaskName)) { |
| tasks.create(variantAssembleTaskName, new Action<Task>() { |
| @Override |
| public void execute(Task task) { |
| task.setDescription( |
| "Assembles all builds for flavor combination: " + name); |
| task.setGroup("Build"); |
| task.dependsOn(variantData.assembleVariantTask); |
| |
| } |
| }); |
| } |
| tasks.named("assemble", new Action<Task>() { |
| @Override |
| public void execute(Task task) { |
| task.dependsOn(variantAssembleTaskName); |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Create tasks for the specified variantData. |
| */ |
| public void createTasksForVariantData( |
| final TaskFactory tasks, |
| final BaseVariantData<? extends BaseVariantOutputData> variantData) { |
| |
| // Add dependency of assemble task on assemble build type task. |
| tasks.named("assemble", new Action<Task>() { |
| @Override |
| public void execute(Task task) { |
| BuildTypeData buildTypeData = buildTypes.get( |
| variantData.getVariantConfiguration().getBuildType().getName()); |
| task.dependsOn(buildTypeData.getAssembleTask()); |
| } |
| }); |
| |
| |
| VariantType variantType = variantData.getType(); |
| |
| createAssembleTaskForVariantData(tasks, variantData); |
| if (variantType.isForTesting()) { |
| final GradleVariantConfiguration testVariantConfig = variantData.getVariantConfiguration(); |
| final BaseVariantData testedVariantData = (BaseVariantData) ((TestVariantData) variantData) |
| .getTestedVariantData(); |
| |
| // Add the container of dependencies, the order of the libraries is important. |
| // In descending order: build type (only for unit test), flavors, defaultConfig. |
| List<ConfigurationProvider> testVariantProviders = Lists.newArrayListWithExpectedSize( |
| 2 + testVariantConfig.getProductFlavors().size()); |
| |
| ConfigurationProvider buildTypeConfigurationProvider = |
| buildTypes.get(testVariantConfig.getBuildType().getName()) |
| .getTestConfigurationProvider(variantType); |
| if (buildTypeConfigurationProvider != null) { |
| testVariantProviders.add(buildTypeConfigurationProvider); |
| } |
| |
| for (CoreProductFlavor productFlavor : testVariantConfig.getProductFlavors()) { |
| ProductFlavorData<CoreProductFlavor> data = |
| productFlavors.get(productFlavor.getName()); |
| testVariantProviders.add(data.getTestConfigurationProvider(variantType)); |
| } |
| |
| // now add the default config |
| testVariantProviders.add(defaultConfigData.getTestConfigurationProvider(variantType)); |
| |
| assert(testVariantConfig.getTestedConfig() != null); |
| VariantDependencies parentVariant = null; |
| if (testVariantConfig.getTestedConfig().getType() == VariantType.LIBRARY) { |
| parentVariant = testedVariantData.getVariantDependency(); |
| } |
| |
| // 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. |
| final VariantDependencies variantDep = VariantDependencies.compute( |
| project, testVariantConfig.getFullName(), |
| false /*publishVariant*/, |
| variantType, |
| parentVariant, |
| testVariantProviders.toArray( |
| new ConfigurationProvider[testVariantProviders.size()])); |
| variantData.setVariantDependency(variantDep); |
| |
| if (variantType == VariantType.ANDROID_TEST && |
| testVariantConfig.isMultiDexEnabled() && |
| testVariantConfig.isLegacyMultiDexMode()) { |
| project.getDependencies().add( |
| variantDep.getCompileConfiguration().getName(), COM_ANDROID_SUPPORT_MULTIDEX_INSTRUMENTATION); |
| project.getDependencies().add( |
| variantDep.getPackageConfiguration().getName(), COM_ANDROID_SUPPORT_MULTIDEX_INSTRUMENTATION); |
| } |
| |
| SpanRecorders.record(project, ExecutionType.RESOLVE_DEPENDENCIES, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() { |
| taskManager.resolveDependencies(variantDep, |
| testVariantConfig.getTestedConfig().getType() == VariantType.LIBRARY |
| ? null |
| : testedVariantData.getVariantDependency(), |
| null /*testedProjectPath*/); |
| return null; |
| } |
| }, |
| new Recorder.Property(SpanRecorders.VARIANT, testVariantConfig.getFullName())); |
| testVariantConfig.setDependencies(variantDep); |
| switch (variantType) { |
| case ANDROID_TEST: |
| taskManager.createAndroidTestVariantTasks(tasks, (TestVariantData) variantData); |
| break; |
| case UNIT_TEST: |
| taskManager.createUnitTestVariantTasks(tasks, (TestVariantData) variantData); |
| break; |
| default: |
| throw new IllegalArgumentException("Unknown test type " + variantType); |
| } |
| } else { |
| taskManager.createTasksForVariantData(tasks, variantData); |
| } |
| } |
| |
| /** |
| * Create all variants. |
| */ |
| public void populateVariantDataList() { |
| if (productFlavors.isEmpty()) { |
| createVariantDataForProductFlavors(Collections.<ProductFlavor>emptyList()); |
| } else { |
| List<String> flavorDimensionList = extension.getFlavorDimensionList(); |
| |
| // Create iterable to get GradleProductFlavor from ProductFlavorData. |
| Iterable<CoreProductFlavor> flavorDsl = |
| Iterables.transform( |
| productFlavors.values(), |
| new Function<ProductFlavorData<CoreProductFlavor>, CoreProductFlavor>() { |
| @Override |
| public CoreProductFlavor apply( |
| ProductFlavorData<CoreProductFlavor> data) { |
| return data.getProductFlavor(); |
| } |
| }); |
| |
| // Get a list of all combinations of product flavors. |
| List<ProductFlavorCombo<CoreProductFlavor>> flavorComboList = |
| ProductFlavorCombo.createCombinations( |
| flavorDimensionList, |
| flavorDsl); |
| |
| for (ProductFlavorCombo<CoreProductFlavor> flavorCombo : flavorComboList) { |
| //noinspection unchecked |
| createVariantDataForProductFlavors( |
| (List<ProductFlavor>) (List) flavorCombo.getFlavorList()); |
| } |
| } |
| } |
| |
| /** |
| * Create a VariantData for a specific combination of BuildType and ProductFlavor list. |
| */ |
| public BaseVariantData<? extends BaseVariantOutputData> createVariantData( |
| @NonNull com.android.builder.model.BuildType buildType, |
| @NonNull List<? extends ProductFlavor> productFlavorList) { |
| BuildTypeData buildTypeData = buildTypes.get(buildType.getName()); |
| |
| GradleVariantConfiguration variantConfig = new GradleVariantConfiguration( |
| defaultConfigData.getProductFlavor(), |
| defaultConfigData.getSourceSet(), |
| buildTypeData.getBuildType(), |
| buildTypeData.getSourceSet(), |
| variantFactory.getVariantConfigurationType(), |
| signingOverride); |
| |
| if (variantConfig.getType() == LIBRARY && variantConfig.getUseJack()) { |
| project.getLogger().warn( |
| "{}, {}: Jack compiler is not supported in library projects, falling back to javac.", |
| project.getPath(), |
| variantConfig.getFullName()); |
| } |
| |
| // sourceSetContainer in case we are creating variant specific sourceSets. |
| NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer = extension |
| .getSourceSets(); |
| |
| // 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 (ProductFlavor productFlavor : productFlavorList) { |
| ProductFlavorData<CoreProductFlavor> data = productFlavors.get( |
| productFlavor.getName()); |
| |
| String dimensionName = productFlavor.getDimension(); |
| if (dimensionName == null) { |
| dimensionName = ""; |
| } |
| |
| variantConfig.addProductFlavor( |
| data.getProductFlavor(), |
| data.getSourceSet(), |
| dimensionName); |
| } |
| |
| createCompoundSourceSets(productFlavorList, variantConfig, sourceSetsContainer); |
| |
| // 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<ConfigurationProvider> variantProviders = |
| Lists.newArrayListWithExpectedSize(productFlavorList.size() + 4); |
| |
| // 1. add the variant-specific if applicable. |
| if (!productFlavorList.isEmpty()) { |
| variantProviders.add( |
| new ConfigurationProviderImpl( |
| project, |
| (DefaultAndroidSourceSet) variantConfig.getVariantSourceProvider())); |
| } |
| |
| // 2. the build type. |
| variantProviders.add(buildTypeData.getMainProvider()); |
| |
| // 3. the multi-flavor combination |
| if (productFlavorList.size() > 1) { |
| variantProviders.add( |
| new ConfigurationProviderImpl( |
| project, |
| (DefaultAndroidSourceSet) variantConfig.getMultiFlavorSourceProvider())); |
| } |
| |
| // 4. the flavors. |
| for (ProductFlavor productFlavor : productFlavorList) { |
| variantProviders.add(productFlavors.get(productFlavor.getName()).getMainProvider()); |
| } |
| |
| // 5. The defaultConfig |
| variantProviders.add(defaultConfigData.getMainProvider()); |
| |
| // Done. Create the variant and get its internal storage object. |
| BaseVariantData<?> variantData = |
| variantFactory.createVariantData(variantConfig, taskManager); |
| |
| final VariantDependencies variantDep = VariantDependencies.compute( |
| project, variantConfig.getFullName(), |
| isVariantPublished(), |
| variantData.getType(), |
| null, |
| variantProviders.toArray(new ConfigurationProvider[variantProviders.size()])); |
| variantData.setVariantDependency(variantDep); |
| |
| if (variantConfig.isMultiDexEnabled() && variantConfig.isLegacyMultiDexMode()) { |
| project.getDependencies().add( |
| variantDep.getCompileConfiguration().getName(), COM_ANDROID_SUPPORT_MULTIDEX); |
| project.getDependencies().add( |
| variantDep.getPackageConfiguration().getName(), COM_ANDROID_SUPPORT_MULTIDEX); |
| } |
| |
| final String testedProjectPath = extension instanceof TestAndroidConfig ? |
| ((TestAndroidConfig) extension).getTargetProjectPath() : |
| null; |
| |
| SpanRecorders.record(project, ExecutionType.RESOLVE_DEPENDENCIES, |
| new Recorder.Block<Void>() { |
| @Override |
| public Void call() { |
| taskManager.resolveDependencies( |
| variantDep, |
| null /*testedVariantDeps*/, |
| testedProjectPath); |
| return null; |
| } |
| }, new Recorder.Property(SpanRecorders.VARIANT, variantConfig.getFullName())); |
| |
| variantConfig.setDependencies(variantDep); |
| |
| return variantData; |
| } |
| |
| private static void createCompoundSourceSets( |
| @NonNull List<? extends ProductFlavor> productFlavorList, |
| GradleVariantConfiguration variantConfig, |
| NamedDomainObjectContainer<AndroidSourceSet> sourceSetsContainer) { |
| if (!productFlavorList.isEmpty() && !variantConfig.getType().isSingleBuildType()) { |
| DefaultAndroidSourceSet variantSourceSet = |
| (DefaultAndroidSourceSet) sourceSetsContainer.maybeCreate( |
| computeSourceSetName( |
| variantConfig.getFullName(), |
| variantConfig.getType())); |
| variantConfig.setVariantSourceProvider(variantSourceSet); |
| } |
| |
| if (productFlavorList.size() > 1) { |
| DefaultAndroidSourceSet multiFlavorSourceSet = |
| (DefaultAndroidSourceSet) sourceSetsContainer.maybeCreate( |
| computeSourceSetName( |
| variantConfig.getFlavorName(), |
| variantConfig.getType())); |
| variantConfig.setMultiFlavorSourceProvider(multiFlavorSourceSet); |
| } |
| } |
| |
| /** |
| * Turns a string into a valid source set name for the given {@link VariantType}, e.g. |
| * "fooBarUnitTest" becomes "testFooBar". |
| */ |
| @NonNull |
| private static String computeSourceSetName( |
| @NonNull String name, |
| @NonNull VariantType variantType) { |
| if (name.endsWith(variantType.getSuffix())) { |
| name = name.substring(0, name.length() - variantType.getSuffix().length()); |
| } |
| |
| if (!variantType.getPrefix().isEmpty()) { |
| name = variantType.getPrefix() + StringHelper.capitalize(name); |
| } |
| |
| return name; |
| } |
| |
| /** |
| * Create a TestVariantData for the specified testedVariantData. |
| */ |
| public TestVariantData createTestVariantData( |
| BaseVariantData testedVariantData, |
| VariantType type) { |
| CoreProductFlavor defaultConfig = defaultConfigData.getProductFlavor(); |
| CoreBuildType buildType = testedVariantData.getVariantConfiguration().getBuildType(); |
| BuildTypeData buildTypeData = buildTypes.get(buildType.getName()); |
| |
| GradleVariantConfiguration testedConfig = testedVariantData.getVariantConfiguration(); |
| List<? extends CoreProductFlavor> productFlavorList = testedConfig.getProductFlavors(); |
| |
| // 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. |
| @SuppressWarnings("ConstantConditions") |
| GradleVariantConfiguration testVariantConfig = new GradleVariantConfiguration( |
| testedVariantData.getVariantConfiguration(), |
| defaultConfig, |
| defaultConfigData.getTestSourceSet(type), |
| buildType, |
| buildTypeData.getTestSourceSet(type), |
| type, |
| signingOverride); |
| |
| |
| for (CoreProductFlavor productFlavor : productFlavorList) { |
| ProductFlavorData<CoreProductFlavor> data = productFlavors |
| .get(productFlavor.getName()); |
| |
| String dimensionName = productFlavor.getDimension(); |
| if (dimensionName == null) { |
| dimensionName = ""; |
| } |
| // same supress warning here. |
| //noinspection ConstantConditions |
| testVariantConfig.addProductFlavor( |
| data.getProductFlavor(), |
| data.getTestSourceSet(type), |
| dimensionName); |
| } |
| |
| createCompoundSourceSets( |
| productFlavorList, |
| testVariantConfig, |
| extension.getSourceSets()); |
| |
| // create the internal storage for this variant. |
| TestVariantData testVariantData = new TestVariantData( |
| extension, taskManager, |
| testVariantConfig, (TestedVariantData) testedVariantData, |
| androidBuilder.getErrorReporter()); |
| // link the testVariant to the tested variant in the other direction |
| ((TestedVariantData) testedVariantData).setTestVariantData(testVariantData, type); |
| |
| return testVariantData; |
| } |
| |
| /** |
| * Creates VariantData for a specified list of product flavor. |
| * |
| * This will create VariantData for all build types of the given flavors. |
| * |
| * @param productFlavorList the flavor(s) to build. |
| */ |
| private void createVariantDataForProductFlavors( |
| @NonNull List<ProductFlavor> productFlavorList) { |
| |
| BuildTypeData testBuildTypeData = null; |
| if (extension instanceof TestedAndroidConfig) { |
| TestedAndroidConfig testedExtension = (TestedAndroidConfig) extension; |
| |
| testBuildTypeData = buildTypes.get(testedExtension.getTestBuildType()); |
| if (testBuildTypeData == null) { |
| throw new RuntimeException(String.format( |
| "Test Build Type '%1$s' does not exist.", testedExtension.getTestBuildType())); |
| } |
| } |
| |
| BaseVariantData variantForAndroidTest = null; |
| |
| CoreProductFlavor defaultConfig = defaultConfigData.getProductFlavor(); |
| |
| Action<com.android.build.gradle.api.VariantFilter> variantFilterAction = |
| extension.getVariantFilter(); |
| |
| for (BuildTypeData buildTypeData : buildTypes.values()) { |
| boolean ignore = false; |
| if (variantFilterAction != null) { |
| variantFilter.reset(defaultConfig, buildTypeData.getBuildType(), productFlavorList); |
| variantFilterAction.execute(variantFilter); |
| ignore = variantFilter.isIgnore(); |
| } |
| |
| if (!ignore) { |
| BaseVariantData<?> variantData = createVariantData( |
| buildTypeData.getBuildType(), |
| productFlavorList); |
| variantDataList.add(variantData); |
| |
| GradleVariantConfiguration variantConfig = variantData.getVariantConfiguration(); |
| ThreadRecorder.get().record( |
| ExecutionType.VARIANT_CONFIG, |
| Recorder.EmptyBlock, |
| new Recorder.Property( |
| "project", |
| project.getName()), |
| new Recorder.Property( |
| "variant", |
| variantData.getName()), |
| new Recorder.Property( |
| "use_jack", |
| Boolean.toString(variantConfig.getUseJack())), |
| new Recorder.Property( |
| "use_minify", |
| Boolean.toString(variantConfig.isMinifyEnabled())), |
| new Recorder.Property( |
| "use_multi_dex", |
| Boolean.toString(variantConfig.isMultiDexEnabled())), |
| new Recorder.Property( |
| "multi_dex_legacy", |
| Boolean.toString(variantConfig.isLegacyMultiDexMode()))); |
| |
| |
| if (variantFactory.hasTestScope()) { |
| TestVariantData unitTestVariantData = createTestVariantData( |
| variantData, |
| UNIT_TEST); |
| variantDataList.add(unitTestVariantData); |
| |
| if (buildTypeData == testBuildTypeData) { |
| if (variantConfig.isMinifyEnabled() && variantConfig.getUseJack()) { |
| throw new RuntimeException( |
| "Cannot test obfuscated variants when compiling with jack."); |
| } |
| variantForAndroidTest = variantData; |
| } |
| } |
| } |
| } |
| |
| if (variantForAndroidTest != null) { |
| TestVariantData androidTestVariantData = createTestVariantData( |
| variantForAndroidTest, |
| ANDROID_TEST); |
| variantDataList.add(androidTestVariantData); |
| } |
| } |
| |
| private boolean isVariantPublished() { |
| return extension.getPublishNonDefault(); |
| } |
| |
| private static void checkName(@NonNull String name, @NonNull String displayName) { |
| checkPrefix(name, displayName, ANDROID_TEST.getPrefix()); |
| checkPrefix(name, displayName, UNIT_TEST.getPrefix()); |
| |
| if (LINT.equals(name)) { |
| throw new RuntimeException(String.format( |
| "%1$s names cannot be %2$s", displayName, LINT)); |
| } |
| } |
| |
| private static void checkPrefix(String name, String displayName, String prefix) { |
| if (name.startsWith(prefix)) { |
| throw new RuntimeException(String.format( |
| "%1$s names cannot start with '%2$s'", displayName, prefix)); |
| } |
| } |
| |
| private SigningConfig createSigningOverride() { |
| AndroidGradleOptions.SigningOptions signingOptions = |
| AndroidGradleOptions.getSigningOptions(project); |
| 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.storeFile)); |
| signingConfigDsl.setStorePassword(signingOptions.storePassword); |
| signingConfigDsl.setKeyAlias(signingOptions.keyAlias); |
| signingConfigDsl.setKeyPassword(signingOptions.keyPassword); |
| |
| if (signingOptions.storeType != null) { |
| signingConfigDsl.setStoreType(signingOptions.storeType); |
| } |
| |
| return signingConfigDsl; |
| } |
| return null; |
| } |
| |
| } |