All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.build.gradle.internal.VariantManager Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*
 * 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 defaultConfigData;
    @NonNull
    private final Map buildTypes = Maps.newHashMap();
    @NonNull
    private final Map> productFlavors = Maps.newHashMap();
    @NonNull
    private final Map signingConfigs = Maps.newHashMap();

    @NonNull
    private final ReadOnlyObjectProvider readOnlyObjectProvider = new ReadOnlyObjectProvider();
    @NonNull
    private final VariantFilter variantFilter = new VariantFilter(readOnlyObjectProvider);

    @NonNull
    private final List> 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(
                extension.getDefaultConfig(), mainSourceSet,
                androidTestSourceSet, unitTestSourceSet, project);
        signingOverride = createSigningOverride();
    }

    @NonNull
    @Override
    public ProductFlavorData getDefaultConfig() {
        return defaultConfigData;
    }

    @Override
    @NonNull
    public Map getBuildTypes() {
        return buildTypes;
    }

    @Override
    @NonNull
    public Map> getProductFlavors() {
        return productFlavors;
    }

    @Override
    @NonNull
    public Map 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 productFlavorData =
                new ProductFlavorData(
                        productFlavor,
                        mainSourceSet,
                        androidTestSourceSet,
                        unitTestSourceSet,
                        project);

        productFlavors.put(productFlavor.getName(), productFlavorData);
    }

    /**
     * Return a list of all created VariantData.
     */
    @NonNull
    public List> 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() {
                        @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() {
                    @Override
                    public Void call() throws Exception {
                        taskManager.createTopLevelTestTasks(tasks, !productFlavors.isEmpty());
                        return null;
                    }
                });

        for (final BaseVariantData variantData : variantDataList) {

            SpanRecorders.record(project, ExecutionType.VARIANT_MANAGER_CREATE_TASKS_FOR_VARIANT,
                    new Recorder.Block() {
                        @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() {
                            @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() {
                        @Override
                        public void execute(Task task) {
                            task.dependsOn(variantAssembleTaskName);
                        }
                    });
                }
            }
        }
    }

    /**
     * Create tasks for the specified variantData.
     */
    public void createTasksForVariantData(
            final TaskFactory tasks,
            final BaseVariantData variantData) {

        // Add dependency of assemble task on assemble build type task.
        tasks.named("assemble", new Action() {
            @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 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 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() {
                        @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.emptyList());
        } else {
            List flavorDimensionList = extension.getFlavorDimensionList();

            // Create iterable to get GradleProductFlavor from ProductFlavorData.
            Iterable flavorDsl =
                    Iterables.transform(
                            productFlavors.values(),
                            new Function, CoreProductFlavor>() {
                                @Override
                                public CoreProductFlavor apply(
                                        ProductFlavorData data) {
                                    return data.getProductFlavor();
                                }
                            });

            // Get a list of all combinations of product flavors.
            List> flavorComboList =
                    ProductFlavorCombo.createCombinations(
                            flavorDimensionList,
                            flavorDsl);

            for (ProductFlavorCombo  flavorCombo : flavorComboList) {
                //noinspection unchecked
                createVariantDataForProductFlavors(
                        (List) (List) flavorCombo.getFlavorList());
            }
        }
    }

    /**
     * Create a VariantData for a specific combination of BuildType and ProductFlavor list.
     */
    public BaseVariantData createVariantData(
            @NonNull com.android.builder.model.BuildType buildType,
            @NonNull List 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 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 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 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() {
                    @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 productFlavorList,
            GradleVariantConfiguration variantConfig,
            NamedDomainObjectContainer 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 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 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 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 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;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy