Package com.android.builder

Source Code of com.android.builder.VariantConfiguration

/*
* Copyright (C) 2012 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.builder;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.annotations.VisibleForTesting;
import com.android.builder.dependency.DependencyContainer;
import com.android.builder.dependency.JarDependency;
import com.android.builder.dependency.LibraryDependency;
import com.android.builder.internal.MergedNdkConfig;
import com.android.builder.internal.StringHelper;
import com.android.builder.model.ClassField;
import com.android.builder.model.NdkConfig;
import com.android.builder.model.ProductFlavor;
import com.android.builder.model.SigningConfig;
import com.android.builder.model.SourceProvider;
import com.android.builder.testing.TestData;
import com.android.ide.common.res2.AssetSet;
import com.android.ide.common.res2.ResourceSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
* A Variant configuration.
*/
public class VariantConfiguration implements TestData {

    private static final ManifestParser sManifestParser = new DefaultManifestParser();

    /**
     * Full, unique name of the variant in camel case, including BuildType and Flavors (and Test)
     */
    private String mFullName;
    /**
     * Flavor Name of the variant, including all flavors in camel case (starting with a lower
     * case).
     */
    private String mFlavorName;
    /**
     * Full, unique name of the variant, including BuildType, flavors and test, dash separated.
     * (similar to full name but with dashes)
     */
    private String mBaseName;
    /**
     * Unique directory name (can include multiple folders) for the variant, based on build type,
     * flavor and test.
     * This always uses forward slashes ('/') as separator on all platform.
     *
     */
    private String mDirName;

    @NonNull
    private final DefaultProductFlavor mDefaultConfig;
    @NonNull
    private final SourceProvider mDefaultSourceProvider;

    @NonNull
    private final DefaultBuildType mBuildType;
    /** SourceProvider for the BuildType. Can be null */
    @Nullable
    private final SourceProvider mBuildTypeSourceProvider;

    private final List<String> mFlavorDimensionNames = Lists.newArrayList();
    private final List<DefaultProductFlavor> mFlavorConfigs = Lists.newArrayList();
    private final List<SourceProvider> mFlavorSourceProviders = Lists.newArrayList();

    /** Variant specific source provider, may be null */
    @Nullable
    private SourceProvider mVariantSourceProvider;

    /** MultiFlavors specific source provider, may be null */
    @Nullable
    private SourceProvider mMultiFlavorSourceProvider;

    @NonNull
    private final Type mType;
    /** Optional tested config in case type is Type#TEST */
    private final VariantConfiguration mTestedConfig;
    /** An optional output that is only valid if the type is Type#LIBRARY so that the test
     * for the library can use the library as if it was a normal dependency. */
    private LibraryDependency mOutput;

    private DefaultProductFlavor mMergedFlavor;
    private final MergedNdkConfig mMergedNdkConfig = new MergedNdkConfig();

    private final Set<JarDependency> mJars = Sets.newHashSet();

    /** List of direct library dependencies. Each object defines its own dependencies. */
    private final List<LibraryDependency> mDirectLibraries = Lists.newArrayList();

    /** list of all library dependencies in a flat list.
     * The order is based on the order needed to call aapt: earlier libraries override resources
     * of latter ones. */
    private final List<LibraryDependency> mFlatLibraries = Lists.newArrayList();

    public static enum Type {
        DEFAULT, LIBRARY, TEST
    }

    /**
     * Parses the manifest file and return the package name.
     * @param manifestFile the manifest file
     * @return the package name found or null
     */
    @Nullable
    public static String getManifestPackage(@NonNull File manifestFile) {
        return sManifestParser.getPackage(manifestFile);
    }

    /**
     * Creates the configuration with the base source sets.
     *
     * This creates a config with a {@link Type#DEFAULT} type.
     *
     * @param defaultConfig the default configuration. Required.
     * @param defaultSourceProvider the default source provider. Required
     * @param buildType the build type for this variant. Required.
     * @param buildTypeSourceProvider the source provider for the build type. Required.
     */
    public VariantConfiguration(
            @NonNull DefaultProductFlavor defaultConfig,
            @NonNull SourceProvider defaultSourceProvider,
            @NonNull DefaultBuildType buildType,
            @Nullable SourceProvider buildTypeSourceProvider) {
        this(
                defaultConfig, defaultSourceProvider,
                buildType, buildTypeSourceProvider,
                Type.DEFAULT, null /*testedConfig*/);
    }

    /**
     * Creates the configuration with the base source sets for a given {@link Type}.
     *
     * @param defaultConfig the default configuration. Required.
     * @param defaultSourceProvider the default source provider. Required
     * @param buildType the build type for this variant. Required.
     * @param buildTypeSourceProvider the source provider for the build type.
     * @param type the type of the project.
     */
    public VariantConfiguration(
            @NonNull DefaultProductFlavor defaultConfig,
            @NonNull SourceProvider defaultSourceProvider,
            @NonNull DefaultBuildType buildType,
            @Nullable SourceProvider buildTypeSourceProvider,
            @NonNull Type type) {
        this(
                defaultConfig, defaultSourceProvider,
                buildType, buildTypeSourceProvider,
                type, null /*testedConfig*/);
    }

    /**
     * Creates the configuration with the base source sets, and an optional tested variant.
     *
     * @param defaultConfig the default configuration. Required.
     * @param defaultSourceProvider the default source provider. Required
     * @param buildType the build type for this variant. Required.
     * @param buildTypeSourceProvider the source provider for the build type.
     * @param type the type of the project.
     * @param testedConfig the reference to the tested project. Required if type is Type.TEST
     */
    public VariantConfiguration(
            @NonNull DefaultProductFlavor defaultConfig,
            @NonNull SourceProvider defaultSourceProvider,
            @NonNull DefaultBuildType buildType,
            @Nullable SourceProvider buildTypeSourceProvider,
            @NonNull Type type,
            @Nullable VariantConfiguration testedConfig) {
        mDefaultConfig = checkNotNull(defaultConfig);
        mDefaultSourceProvider = checkNotNull(defaultSourceProvider);
        mBuildType = checkNotNull(buildType);
        mBuildTypeSourceProvider = buildTypeSourceProvider;
        mType = checkNotNull(type);
        mTestedConfig = testedConfig;
        checkState(mType != Type.TEST || mTestedConfig != null);

        mMergedFlavor = mDefaultConfig;
        computeNdkConfig();

        if (testedConfig != null &&
                testedConfig.mType == Type.LIBRARY &&
                testedConfig.mOutput != null) {
            mDirectLibraries.add(testedConfig.mOutput);
        }

        validate();
    }

    /**
     * Returns the full, unique name of the variant in camel case (starting with a lower case),
     * including BuildType, Flavors and Test (if applicable).
     *
     * @return the name of the variant
     */
    @NonNull
    public String getFullName() {
        if (mFullName == null) {
            StringBuilder sb = new StringBuilder();
            String flavorName = getFlavorName();
            if (!flavorName.isEmpty()) {
                sb.append(flavorName);
                sb.append(StringHelper.capitalize(mBuildType.getName()));
            } else {
                sb.append(mBuildType.getName());
            }

            if (mType == Type.TEST) {
                sb.append("Test");
            }

            mFullName = sb.toString();
        }

        return mFullName;
    }


    /**
     * Returns the flavor name of the variant, including all flavors in camel case (starting
     * with a lower case). If the variant has no flavor, then an empty string is returned.
     *
     * @return the flavor name or an empty string.
     */
    @NonNull
    public String getFlavorName() {
        if (mFlavorName == null) {
            if (mFlavorConfigs.isEmpty()) {
                mFlavorName = "";
            } else {
                StringBuilder sb = new StringBuilder();
                boolean first = true;
                for (DefaultProductFlavor flavor : mFlavorConfigs) {
                    sb.append(first ? flavor.getName() : StringHelper.capitalize(flavor.getName()));
                    first = false;
                }

                mFlavorName = sb.toString();
            }
        }

        return mFlavorName;
    }

    /**
     * Returns the full, unique name of the variant, including BuildType, flavors and test,
     * dash separated. (similar to full name but with dashes)
     *
     * @return the name of the variant
     */
    @NonNull
    public String getBaseName() {
        if (mBaseName == null) {
            StringBuilder sb = new StringBuilder();

            if (!mFlavorConfigs.isEmpty()) {
                for (ProductFlavor pf : mFlavorConfigs) {
                    sb.append(pf.getName()).append('-');
                }
            }

            sb.append(mBuildType.getName());

            if (mType == Type.TEST) {
                sb.append('-').append("test");
            }

            mBaseName = sb.toString();
        }

        return mBaseName;
    }

    /**
     * Returns a unique directory name (can include multiple folders) for the variant,
     * based on build type, flavor and test.
     * This always uses forward slashes ('/') as separator on all platform.
     *
     * @return the directory name for the variant
     */
    @NonNull
    public String getDirName() {
        if (mDirName == null) {
            StringBuilder sb = new StringBuilder();

            if (mType == Type.TEST) {
                sb.append("test/");
            }

            if (!mFlavorConfigs.isEmpty()) {
                for (DefaultProductFlavor flavor : mFlavorConfigs) {
                    sb.append(flavor.getName());
                }

                sb.append('/').append(mBuildType.getName());

            } else {
                sb.append(mBuildType.getName());
            }

            mDirName = sb.toString();

        }

        return mDirName;
    }

    /**
     * Return the names of the applied flavors.
     *
     * The list contains the dimension names as well.
     *
     * @return the list, possibly empty if there are no flavors.
     */
    @NonNull
    public List<String> getFlavorNamesWithDimensionNames() {
        if (mFlavorConfigs.isEmpty()) {
            return Collections.emptyList();
        }

        List<String> names;
        int count = mFlavorConfigs.size();

        if (count > 1) {
            names = Lists.newArrayListWithCapacity(count * 2);

            for (int i = 0 ; i < count ; i++) {
                names.add(mFlavorConfigs.get(i).getName());
                names.add(mFlavorDimensionNames.get(i));
            }

        } else {
            names = Collections.singletonList(mFlavorConfigs.get(0).getName());
        }

        return names;
    }


    /**
     * Add a new configured ProductFlavor.
     *
     * If multiple flavors are added, the priority follows the order they are added when it
     * comes to resolving Android resources overlays (ie earlier added flavors supersedes
     * latter added ones).
     *
     * @param productFlavor the configured product flavor
     * @param sourceProvider the source provider for the product flavor
     * @param dimensionName the name of the dimension associated with the flavor
     *
     * @return the config object
     */
    @NonNull
    public VariantConfiguration addProductFlavor(
            @NonNull DefaultProductFlavor productFlavor,
            @NonNull SourceProvider sourceProvider,
            @NonNull String dimensionName) {

        mFlavorConfigs.add(productFlavor);
        mFlavorSourceProviders.add(sourceProvider);
        mFlavorDimensionNames.add(dimensionName);

        mMergedFlavor = productFlavor.mergeOver(mMergedFlavor);
        computeNdkConfig();

        return this;
    }

    /**
     * Sets the variant-specific source provider.
     * @param sourceProvider the source provider for the product flavor
     *
     * @return the config object
     */
    public VariantConfiguration setVariantSourceProvider(@Nullable SourceProvider sourceProvider) {
        mVariantSourceProvider = sourceProvider;
        return this;
    }

    /**
     * Sets the variant-specific source provider.
     * @param sourceProvider the source provider for the product flavor
     *
     * @return the config object
     */
    public VariantConfiguration setMultiFlavorSourceProvider(@Nullable SourceProvider sourceProvider) {
        mMultiFlavorSourceProvider = sourceProvider;
        return this;
    }

    /**
     * Returns the variant specific source provider
     * @return the source provider or null if none has been provided.
     */
    @Nullable
    public SourceProvider getVariantSourceProvider() {
        return mVariantSourceProvider;
    }

    @Nullable
    public SourceProvider getMultiFlavorSourceProvider() {
        return mMultiFlavorSourceProvider;
    }

    private void computeNdkConfig() {
        mMergedNdkConfig.reset();

        if (mDefaultConfig.getNdkConfig() != null) {
            mMergedNdkConfig.append(mDefaultConfig.getNdkConfig());
        }

        for (int i = mFlavorConfigs.size() - 1 ; i >= 0 ; i--) {
            NdkConfig ndkConfig = mFlavorConfigs.get(i).getNdkConfig();
            if (ndkConfig != null) {
                mMergedNdkConfig.append(ndkConfig);
            }
        }

        if (mBuildType.getNdkConfig() != null && mType != Type.TEST) {
            mMergedNdkConfig.append(mBuildType.getNdkConfig());
        }
    }

    /**
     * Sets the dependencies
     *
     * @param container a DependencyContainer.
     * @return the config object
     */
    @NonNull
    public VariantConfiguration setDependencies(@NonNull DependencyContainer container) {

        mDirectLibraries.addAll(container.getAndroidDependencies());
        mJars.addAll(container.getJarDependencies());
        mJars.addAll(container.getLocalDependencies());

        resolveIndirectLibraryDependencies(mDirectLibraries, mFlatLibraries);

        for (LibraryDependency libraryDependency : mFlatLibraries) {
            mJars.addAll(libraryDependency.getLocalDependencies());
        }
        return this;
    }

    /**
     * Returns the list of jar dependencies
     * @return a non null collection of Jar dependencies.
     */
    @NonNull
    public Collection<JarDependency> getJars() {
        return mJars;
    }

    /**
     * Sets the output of this variant. This is required when the variant is a library so that
     * the variant that tests this library can properly include the tested library in its own
     * package.
     *
     * @param output the output of the library as an LibraryDependency that will provides the
     *               location of all the created items.
     * @return the config object
     */
    @NonNull
    public VariantConfiguration setOutput(LibraryDependency output) {
        mOutput = output;
        return this;
    }

    @NonNull
    public DefaultProductFlavor getDefaultConfig() {
        return mDefaultConfig;
    }

    @NonNull
    public SourceProvider getDefaultSourceSet() {
        return mDefaultSourceProvider;
    }

    @NonNull
    public DefaultProductFlavor getMergedFlavor() {
        return mMergedFlavor;
    }

    @NonNull
    public DefaultBuildType getBuildType() {
        return mBuildType;
    }

    /**
     * The SourceProvider for the BuildType. Can be null.
     */
    @Nullable
    public SourceProvider getBuildTypeSourceSet() {
        return mBuildTypeSourceProvider;
    }

    public boolean hasFlavors() {
        return !mFlavorConfigs.isEmpty();
    }

    @NonNull
    public List<DefaultProductFlavor> getFlavorConfigs() {
        return mFlavorConfigs;
    }

    /**
     * Returns the list of SourceProviders for the flavors.
     *
     * The list is ordered from higher priority to lower priority.
     *
     * @return the list of Source Providers for the flavors. Never null.
     */
    @NonNull
    public List<SourceProvider> getFlavorSourceProviders() {
        return mFlavorSourceProviders;
    }

    public boolean hasLibraries() {
        return !mDirectLibraries.isEmpty();
    }

    /**
     * Returns the direct library dependencies
     */
    @NonNull
    public List<LibraryDependency> getDirectLibraries() {
        return mDirectLibraries;
    }

    /**
     * Returns all the library dependencies, direct and transitive.
     */
    @NonNull
    public List<LibraryDependency> getAllLibraries() {
        return mFlatLibraries;
    }

    @NonNull
    public Type getType() {
        return mType;
    }

    @Nullable
    public VariantConfiguration getTestedConfig() {
        return mTestedConfig;
    }

    /**
     * Resolves a given list of libraries, finds out if they depend on other libraries, and
     * returns a flat list of all the direct and indirect dependencies in the proper order (first
     * is higher priority when calling aapt).
     * @param directDependencies the libraries to resolve
     * @param outFlatDependencies where to store all the libraries.
     */
    @VisibleForTesting
    void resolveIndirectLibraryDependencies(List<LibraryDependency> directDependencies,
                                            List<LibraryDependency> outFlatDependencies) {
        if (directDependencies == null) {
            return;
        }
        // loop in the inverse order to resolve dependencies on the libraries, so that if a library
        // is required by two higher level libraries it can be inserted in the correct place
        for (int i = directDependencies.size() - ; i >= 0 ; i--) {
            LibraryDependency library = directDependencies.get(i);

            // get its libraries
            Collection<LibraryDependency> dependencies = library.getDependencies();
            List<LibraryDependency> depList = Lists.newArrayList(dependencies);

            // resolve the dependencies for those libraries
            resolveIndirectLibraryDependencies(depList, outFlatDependencies);

            // and add the current one (if needed) in front (higher priority)
            if (!outFlatDependencies.contains(library)) {
                outFlatDependencies.add(0, library);
            }
        }
    }

    /**
     * Returns the original package name before any overrides from flavors.
     * If the variant is a test variant, then the package name is the one coming from the
     * configuration of the tested variant, and this call is similar to #getPackageName()
     * @return the package name
     */
    @Nullable
    public String getOriginalPackageName() {
        if (mType == VariantConfiguration.Type.TEST) {
            return getPackageName();
        }

        return getPackageFromManifest();
    }

    /**
     * Returns the package name for this variant. This could be coming from the manifest or
     * could be overridden through the product flavors and/or the build Type.
     * @return the package
     */
    @Override
    @NonNull
    public String getPackageName() {
        String packageName;

        if (mType == Type.TEST) {
            assert mTestedConfig != null;

            packageName = mMergedFlavor.getTestPackageName();
            if (packageName == null) {
                String testedPackage = mTestedConfig.getPackageName();
                packageName = testedPackage + ".test";
            }
        } else {
            // first get package override.
            packageName = getPackageOverride();
            // if it's null, this means we just need the default package
            // from the manifest since both flavor and build type do nothing.
            if (packageName == null) {
                packageName = getPackageFromManifest();
            }
        }

        if (packageName == null) {
            throw new RuntimeException("Failed get query package name for " + getFullName());
        }

        return packageName;
    }

    @Override
    @Nullable
    public String getTestedPackageName() {
        if (mType == Type.TEST) {
            assert mTestedConfig != null;
            if (mTestedConfig.mType == Type.LIBRARY) {
                return getPackageName();
            } else {
                return mTestedConfig.getPackageName();
            }
        }

        return null;
    }

    /**
     * Returns the package override values coming from the Product Flavor and/or the Build Type.
     * If the package is not overridden then this returns null.
     *
     * @return the package override or null
     */
    @Nullable
    public String getPackageOverride() {
        String packageName = mMergedFlavor.getPackageName();
        String packageSuffix = mBuildType.getPackageNameSuffix();

        if (packageSuffix != null && packageSuffix.length() > 0) {
            if (packageName == null) {
                packageName = getPackageFromManifest();
            }

            if (packageSuffix.charAt(0) == '.') {
                packageName = packageName + packageSuffix;
            } else {
                packageName = packageName + '.' + packageSuffix;
            }
        }

        return packageName;
    }

    /**
     * Returns the version name for this variant. This could be coming from the manifest or
     * could be overridden through the product flavors, and can have a suffix specified by
     * the build type.
     *
     * @return the version name
     */
    @Nullable
    public String getVersionName() {
        String versionName = mMergedFlavor.getVersionName();
        String versionSuffix = mBuildType.getVersionNameSuffix();

        if (versionSuffix != null && versionSuffix.length() > 0) {
            if (versionName == null) {
                if (mType != Type.TEST) {
                    versionName = getVersionNameFromManifest();
                } else {
                    versionName = "";
                }
            }

            versionName = versionName + versionSuffix;
        }

        return versionName;
    }

    /**
     * Returns the version code for this variant. This could be coming from the manifest or
     * could be overridden through the product flavors, and can have a suffix specified by
     * the build type.
     *
     * @return the version code or -1 if there was non defined.
     */
    public int getVersionCode() {
        int versionCode = mMergedFlavor.getVersionCode();

        if (versionCode == -1 && mType != Type.TEST) {

            versionCode = getVersionCodeFromManifest();
        }

        return versionCode;
    }

    private final static String DEFAULT_TEST_RUNNER = "android.test.InstrumentationTestRunner";
    private final static Boolean DEFAULT_HANDLE_PROFILING = false;
    private final static Boolean DEFAULT_FUNCTIONAL_TEST = false;

    /**
     * Returns the instrumentationRunner to use to test this variant, or if the
     * variant is a test, the one to use to test the tested variant.
     * @return the instrumentation test runner name
     */
    @Override
    @NonNull
    public String getInstrumentationRunner() {
        VariantConfiguration config = this;
        if (mType == Type.TEST) {
            config = getTestedConfig();
        }
        String runner = config.mMergedFlavor.getTestInstrumentationRunner();
        return runner != null ? runner : DEFAULT_TEST_RUNNER;
    }

    /**
     * Returns handleProfiling value to use to test this variant, or if the
     * variant is a test, the one to use to test the tested variant.
     * @return the handleProfiling value
     */
    @Override
    @NonNull
    public Boolean getHandleProfiling() {
        VariantConfiguration config = this;
        if (mType == Type.TEST) {
            config = getTestedConfig();
        }
        Boolean handleProfiling = config.mMergedFlavor.getTestHandleProfiling();
        return handleProfiling != null ? handleProfiling : DEFAULT_HANDLE_PROFILING;
    }

    /**
     * Returns functionalTest value to use to test this variant, or if the
     * variant is a test, the one to use to test the tested variant.
     * @return the functionalTest value
     */
    @Override
    @NonNull
    public Boolean getFunctionalTest() {
        VariantConfiguration config = this;
        if (mType == Type.TEST) {
            config = getTestedConfig();
        }
        Boolean functionalTest = config.mMergedFlavor.getTestFunctionalTest();
        return functionalTest != null ? functionalTest : DEFAULT_FUNCTIONAL_TEST;
    }

    /**
     * Reads the package name from the manifest. This is unmodified by the build type.
     */
    @Nullable
    public String getPackageFromManifest() {
        assert mType != Type.TEST;
        File manifestLocation = mDefaultSourceProvider.getManifestFile();
        return sManifestParser.getPackage(manifestLocation);
    }

    /**
     * Reads the version name from the manifest.
     */
    @Nullable
    public String getVersionNameFromManifest() {
        File manifestLocation = mDefaultSourceProvider.getManifestFile();
        return sManifestParser.getVersionName(manifestLocation);
    }

    /**
     * Reads the version code from the manifest.
     */
    public int getVersionCodeFromManifest() {
        File manifestLocation = mDefaultSourceProvider.getManifestFile();
        return sManifestParser.getVersionCode(manifestLocation);
    }

    /**
     * Return the minSdkVersion for this variant.
     *
     * This uses both the value from the manifest (if present), and the override coming
     * from the flavor(s) (if present).
     * @return the minSdkVersion
     */
    @Override
    public int getMinSdkVersion() {
        if (mTestedConfig != null) {
            return mTestedConfig.getMinSdkVersion();
        }
        int minSdkVersion = mMergedFlavor.getMinSdkVersion();
        if (minSdkVersion == -1) {
            // read it from the main manifest
            File manifestLocation = mDefaultSourceProvider.getManifestFile();
            minSdkVersion = sManifestParser.getMinSdkVersion(manifestLocation);
        }

        return minSdkVersion;
    }

    /**
     * Return the targetSdkVersion for this variant.
     *
     * This uses both the value from the manifest (if present), and the override coming
     * from the flavor(s) (if present).
     * @return the targetSdkVersion
     */
    public int getTargetSdkVersion() {
        if (mTestedConfig != null) {
            return mTestedConfig.getTargetSdkVersion();
        }
        int targetSdkVersion = mMergedFlavor.getTargetSdkVersion();
        if (targetSdkVersion == -1) {
            // read it from the main manifest
            File manifestLocation = mDefaultSourceProvider.getManifestFile();
            targetSdkVersion = sManifestParser.getTargetSdkVersion(manifestLocation);
        }

        return targetSdkVersion;
    }

    @Nullable
    public File getMainManifest() {
        File defaultManifest = mDefaultSourceProvider.getManifestFile();

        // this could not exist in a test project.
        if (defaultManifest.isFile()) {
            return defaultManifest;
        }

        return null;
    }

    @NonNull
    public List<File> getManifestOverlays() {
        List<File> inputs = Lists.newArrayList();

        if (mVariantSourceProvider != null) {
            File variantLocation = mVariantSourceProvider.getManifestFile();
            if (variantLocation.isFile()) {
                inputs.add(variantLocation);
            }
        }

        if (mBuildTypeSourceProvider != null) {
            File typeLocation = mBuildTypeSourceProvider.getManifestFile();
            if (typeLocation.isFile()) {
                inputs.add(typeLocation);
            }
        }

        if (mMultiFlavorSourceProvider != null) {
            File variantLocation = mMultiFlavorSourceProvider.getManifestFile();
            if (variantLocation.isFile()) {
                inputs.add(variantLocation);
            }
        }

        for (SourceProvider sourceProvider : mFlavorSourceProviders) {
            File f = sourceProvider.getManifestFile();
            if (f.isFile()) {
                inputs.add(f);
            }
        }

        return inputs;
    }

    /**
     * Returns the dynamic list of {@link ResourceSet} based on the configuration, its dependencies,
     * as well as tested config if applicable (test of a library).
     *
     * The list is ordered in ascending order of importance, meaning the first set is meant to be
     * overridden by the 2nd one and so on. This is meant to facilitate usage of the list in a
     * {@link com.android.ide.common.res2.ResourceMerger}.
     *
     * @param generatedResFolder the generated res folder typically the output of the renderscript
     *                           compilation
     * @param includeDependencies whether to include in the result the resources of the dependencies
     *
     * @return a list ResourceSet.
     */
    @NonNull
    public List<ResourceSet> getResourceSets(@Nullable File generatedResFolder,
                                             boolean includeDependencies) {
        List<ResourceSet> resourceSets = Lists.newArrayList();

        // the list of dependency must be reversed to use the right overlay order.
        if (includeDependencies) {
            for (int n = mFlatLibraries.size() - 1 ; n >= 0 ; n--) {
                LibraryDependency dependency = mFlatLibraries.get(n);
                File resFolder = dependency.getResFolder();
                if (resFolder.isDirectory()) {
                    ResourceSet resourceSet = new ResourceSet(dependency.getFolder().getName());
                    resourceSet.addSource(resFolder);
                    resourceSets.add(resourceSet);
                }
            }
        }

        Collection<File> mainResDirs = mDefaultSourceProvider.getResDirectories();

        ResourceSet resourceSet = new ResourceSet(BuilderConstants.MAIN);
        resourceSet.addSources(mainResDirs);
        if (generatedResFolder != null) {
            resourceSet.addSource(generatedResFolder);
        }
        resourceSets.add(resourceSet);

        // the list of flavor must be reversed to use the right overlay order.
        for (int n = mFlavorSourceProviders.size() - 1; n >= 0 ; n--) {
            SourceProvider sourceProvider = mFlavorSourceProviders.get(n);

            Collection<File> flavorResDirs = sourceProvider.getResDirectories();
            // we need the same of the flavor config, but it's in a different list.
            // This is fine as both list are parallel collections with the same number of items.
            resourceSet = new ResourceSet(mFlavorConfigs.get(n).getName());
            resourceSet.addSources(flavorResDirs);
            resourceSets.add(resourceSet);
        }

        // multiflavor specific overrides flavor
        if (mMultiFlavorSourceProvider != null) {
            Collection<File> variantResDirs = mMultiFlavorSourceProvider.getResDirectories();
            resourceSet = new ResourceSet(getFlavorName());
            resourceSet.addSources(variantResDirs);
            resourceSets.add(resourceSet);
        }

        // build type overrides the flavors
        if (mBuildTypeSourceProvider != null) {
            Collection<File> typeResDirs = mBuildTypeSourceProvider.getResDirectories();
            resourceSet = new ResourceSet(mBuildType.getName());
            resourceSet.addSources(typeResDirs);
            resourceSets.add(resourceSet);
        }

        // variant specific overrides all
        if (mVariantSourceProvider != null) {
            Collection<File> variantResDirs = mVariantSourceProvider.getResDirectories();
            resourceSet = new ResourceSet(getFullName());
            resourceSet.addSources(variantResDirs);
            resourceSets.add(resourceSet);
        }

        return resourceSets;
    }

    /**
     * Returns the dynamic list of {@link AssetSet} based on the configuration, its dependencies,
     * as well as tested config if applicable (test of a library).
     *
     * The list is ordered in ascending order of importance, meaning the first set is meant to be
     * overridden by the 2nd one and so on. This is meant to facilitate usage of the list in a
     * {@link com.android.ide.common.res2.AssetMerger}.
     *
     * @return a list ResourceSet.
     */
    @NonNull
    public List<AssetSet> getAssetSets(boolean includeDependencies) {
        List<AssetSet> assetSets = Lists.newArrayList();

        if (includeDependencies) {
            // the list of dependency must be reversed to use the right overlay order.
            for (int n = mFlatLibraries.size() - 1 ; n >= 0 ; n--) {
                LibraryDependency dependency = mFlatLibraries.get(n);
                File assetFolder = dependency.getAssetsFolder();
                if (assetFolder.isDirectory()) {
                    AssetSet assetSet = new AssetSet(dependency.getFolder().getName());
                    assetSet.addSource(assetFolder);
                    assetSets.add(assetSet);
                }
            }
        }

        Collection<File> mainResDirs = mDefaultSourceProvider.getAssetsDirectories();

        AssetSet assetSet = new AssetSet(BuilderConstants.MAIN);
        assetSet.addSources(mainResDirs);
        assetSets.add(assetSet);

        // the list of flavor must be reversed to use the right overlay order.
        for (int n = mFlavorSourceProviders.size() - 1; n >= 0 ; n--) {
            SourceProvider sourceProvider = mFlavorSourceProviders.get(n);

            Collection<File> flavorResDirs = sourceProvider.getAssetsDirectories();
            // we need the same of the flavor config, but it's in a different list.
            // This is fine as both list are parallel collections with the same number of items.
            assetSet = new AssetSet(mFlavorConfigs.get(n).getName());
            assetSet.addSources(flavorResDirs);
            assetSets.add(assetSet);
        }

        // multiflavor specific overrides flavor
        if (mMultiFlavorSourceProvider != null) {
            Collection<File> variantResDirs = mMultiFlavorSourceProvider.getAssetsDirectories();
            assetSet = new AssetSet(getFlavorName());
            assetSet.addSources(variantResDirs);
            assetSets.add(assetSet);
        }

        // build type overrides flavors
        if (mBuildTypeSourceProvider != null) {
            Collection<File> typeResDirs = mBuildTypeSourceProvider.getAssetsDirectories();
            assetSet = new AssetSet(mBuildType.getName());
            assetSet.addSources(typeResDirs);
            assetSets.add(assetSet);
        }

        // variant specific overrides all
        if (mVariantSourceProvider != null) {
            Collection<File> variantResDirs = mVariantSourceProvider.getAssetsDirectories();
            assetSet = new AssetSet(getFullName());
            assetSet.addSources(variantResDirs);
            assetSets.add(assetSet);
        }

        return assetSets;
    }

    @NonNull
    public List<File> getLibraryJniFolders() {
        List<File> list = Lists.newArrayListWithExpectedSize(mFlatLibraries.size());

        for (int n = mFlatLibraries.size() - 1 ; n >= 0 ; n--) {
            LibraryDependency dependency = mFlatLibraries.get(n);
            File jniFolder = dependency.getJniFolder();
            if (jniFolder.isDirectory()) {
                list.add(jniFolder);
            }
        }

        return list;
    }

    /**
     * Returns all the renderscript import folder that are outside of the current project.
     */
    @NonNull
    public List<File> getRenderscriptImports() {
        List<File> list = Lists.newArrayList();

        for (LibraryDependency lib : mFlatLibraries) {
            File rsLib = lib.getRenderscriptFolder();
            if (rsLib.isDirectory()) {
                list.add(rsLib);
            }
        }

        return list;
    }

    /**
     * Returns all the renderscript source folder from the main config, the flavors and the
     * build type.
     *
     * @return a list of folders.
     */
    @NonNull
    public List<File> getRenderscriptSourceList() {
        List<File> sourceList = Lists.newArrayList();
        sourceList.addAll(mDefaultSourceProvider.getRenderscriptDirectories());
        if (mType != Type.TEST && mBuildTypeSourceProvider != null) {
            sourceList.addAll(mBuildTypeSourceProvider.getRenderscriptDirectories());
        }

        if (hasFlavors()) {
            for (SourceProvider flavorSourceSet : mFlavorSourceProviders) {
                sourceList.addAll(flavorSourceSet.getRenderscriptDirectories());
            }
        }

        if (mMultiFlavorSourceProvider != null) {
            sourceList.addAll(mMultiFlavorSourceProvider.getRenderscriptDirectories());
        }

        if (mVariantSourceProvider != null) {
            sourceList.addAll(mVariantSourceProvider.getRenderscriptDirectories());
        }

        return sourceList;
    }

    /**
     * Returns all the aidl import folder that are outside of the current project.
     */
    @NonNull
    public List<File> getAidlImports() {
        List<File> list = Lists.newArrayList();

        for (LibraryDependency lib : mFlatLibraries) {
            File aidlLib = lib.getAidlFolder();
            if (aidlLib.isDirectory()) {
                list.add(aidlLib);
            }
        }

        return list;
    }

    @NonNull
    public List<File> getAidlSourceList() {
        List<File> sourceList = Lists.newArrayList();
        sourceList.addAll(mDefaultSourceProvider.getAidlDirectories());
        if (mType != Type.TEST && mBuildTypeSourceProvider != null) {
            sourceList.addAll(mBuildTypeSourceProvider.getAidlDirectories());
        }

        if (hasFlavors()) {
            for (SourceProvider flavorSourceSet : mFlavorSourceProviders) {
                sourceList.addAll(flavorSourceSet.getAidlDirectories());
            }
        }

        if (mMultiFlavorSourceProvider != null) {
            sourceList.addAll(mMultiFlavorSourceProvider.getAidlDirectories());
        }

        if (mVariantSourceProvider != null) {
            sourceList.addAll(mVariantSourceProvider.getAidlDirectories());
        }

        return sourceList;
    }

    @NonNull
    public List<File> getJniSourceList() {
        List<File> sourceList = Lists.newArrayList();
        sourceList.addAll(mDefaultSourceProvider.getJniDirectories());
        if (mType != Type.TEST && mBuildTypeSourceProvider != null) {
            sourceList.addAll(mBuildTypeSourceProvider.getJniDirectories());
        }

        if (hasFlavors()) {
            for (SourceProvider flavorSourceSet : mFlavorSourceProviders) {
                sourceList.addAll(flavorSourceSet.getJniDirectories());
            }
        }

        if (mMultiFlavorSourceProvider != null) {
            sourceList.addAll(mMultiFlavorSourceProvider.getJniDirectories());
        }

        if (mVariantSourceProvider != null) {
            sourceList.addAll(mVariantSourceProvider.getJniDirectories());
        }

        return sourceList;
    }

    /**
     * Returns the compile classpath for this config. If the config tests a library, this
     * will include the classpath of the tested config
     *
     * @return a non null, but possibly empty set.
     */
    @NonNull
    public Set<File> getCompileClasspath() {
        Set<File> classpath = Sets.newHashSet();

        for (LibraryDependency lib : mFlatLibraries) {
            classpath.add(lib.getJarFile());
            for (File jarFile : lib.getLocalJars()) {
                classpath.add(jarFile);
            }
        }

        for (JarDependency jar : mJars) {
            if (jar.isCompiled()) {
                classpath.add(jar.getJarFile());
            }
        }

        return classpath;
    }

    /**
     * Returns the list of packaged jars for this config. If the config tests a library, this
     * will include the jars of the tested config
     *
     * @return a non null, but possibly empty list.
     */
    @NonNull
    public List<File> getPackagedJars() {
        Set<File> jars = Sets.newHashSetWithExpectedSize(mJars.size() + mFlatLibraries.size());

        for (JarDependency jar : mJars) {
            File jarFile = jar.getJarFile();
            if (jar.isPackaged() && jarFile.exists()) {
                jars.add(jarFile);
            }
        }

        for (LibraryDependency libraryDependency : mFlatLibraries) {
            File libJar = libraryDependency.getJarFile();
            if (libJar.exists()) {
                jars.add(libJar);
            }
            for (File jarFile : libraryDependency.getLocalJars()) {
                if (jarFile.isFile()) {
                    jars.add(jarFile);
                }
            }
        }

        return Lists.newArrayList(jars);
    }

    /**
     * Returns a list of items for the BuildConfig class.
     *
     * Items can be either fields (instance of {@link com.android.builder.model.ClassField})
     * or comments (instance of String).
     *
     * @return a list of items.
     */
    @NonNull
    public List<Object> getBuildConfigItems() {
        List<Object> fullList = Lists.newArrayList();

        Set<String> usedFieldNames = Sets.newHashSet();

        List<ClassField> list = mBuildType.getBuildConfigFields();
        if (!list.isEmpty()) {
            fullList.add("Fields from build type: " + mBuildType.getName());
            for (ClassField f : list) {
                usedFieldNames.add(f.getName());
                fullList.add(f);
            }
        }

        for (DefaultProductFlavor flavor : mFlavorConfigs) {
            list = flavor.getBuildConfigFields();
            if (!list.isEmpty()) {
                fullList.add("Fields from product flavor: " + flavor.getName());
                for (ClassField f : list) {
                    String name = f.getName();
                    if (!usedFieldNames.contains(name)) {
                        usedFieldNames.add(f.getName());
                        fullList.add(f);
                    }
                }
            }
        }

        list = mDefaultConfig.getBuildConfigFields();
        if (!list.isEmpty()) {
            fullList.add("Fields from default config.");
            for (ClassField f : list) {
                String name = f.getName();
                if (!usedFieldNames.contains(name)) {
                    usedFieldNames.add(f.getName());
                    fullList.add(f);
                }
            }
        }

        return fullList;
    }

    @Nullable
    public SigningConfig getSigningConfig() {
        SigningConfig signingConfig = mBuildType.getSigningConfig();
        if (signingConfig != null) {
            return signingConfig;
        }
        return mMergedFlavor.getSigningConfig();
    }

    public boolean isSigningReady() {
        SigningConfig signingConfig = getSigningConfig();
        return signingConfig != null && signingConfig.isSigningReady();
    }

    @NonNull
    public List<Object> getProguardFiles(boolean includeLibraries) {
        List<Object> fullList = Lists.newArrayList();

        // add the config files from the build type, main config and flavors
        fullList.addAll(mDefaultConfig.getProguardFiles());
        fullList.addAll(mBuildType.getProguardFiles());

        for (DefaultProductFlavor flavor : mFlavorConfigs) {
            fullList.addAll(flavor.getProguardFiles());
        }

        // now add the one coming from the library dependencies
        if (includeLibraries) {
            for (LibraryDependency libraryDependency : mFlatLibraries) {
                File proguardRules = libraryDependency.getProguardRules();
                if (proguardRules.exists()) {
                    fullList.add(proguardRules);
                }
            }
        }

        return fullList;
    }

    @NonNull
    public List<Object> getConsumerProguardFiles() {
        List<Object> fullList = Lists.newArrayList();

        // add the config files from the build type, main config and flavors
        fullList.addAll(mDefaultConfig.getConsumerProguardFiles());
        fullList.addAll(mBuildType.getConsumerProguardFiles());

        for (DefaultProductFlavor flavor : mFlavorConfigs) {
            fullList.addAll(flavor.getConsumerProguardFiles());
        }

        return fullList;
    }

    protected void validate() {
        if (mType != Type.TEST) {
            File manifest = mDefaultSourceProvider.getManifestFile();
            if (!manifest.isFile()) {
                throw new IllegalArgumentException(
                        "Main Manifest missing from " + manifest.getAbsolutePath());
            }
        }
    }

    @NonNull
    public NdkConfig getNdkConfig() {
        return mMergedNdkConfig;
    }

    @Nullable
    @Override
    public Set<String> getSupportedAbis() {
        if (mMergedNdkConfig != null) {
            return mMergedNdkConfig.getAbiFilters();
        }

        return null;
    }
}
TOP

Related Classes of com.android.builder.VariantConfiguration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.