blob: afa5e4383d9db350b376b79d2a18d179e63916c9 [file] [log] [blame]
/*
* 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.core;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
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.model.ApiVersion;
import com.android.builder.model.BuildType;
import com.android.builder.model.ClassField;
import com.android.builder.model.ProductFlavor;
import com.android.builder.model.SigningConfig;
import com.android.builder.model.SourceProvider;
import com.android.ide.common.res2.AssetSet;
import com.android.ide.common.res2.ResourceSet;
import com.android.utils.StringHelper;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
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.Map;
import java.util.Set;
/**
* A Variant configuration.
*
* Variants are made from the combination of:
*
* - a build type (base interface BuildType), and its associated sources.
* - a default configuration (base interface ProductFlavor), and its associated sources.
* - a optional list of product flavors (base interface ProductFlavor) and their associated sources.
* - dependencies (both jar and aar).
*
* @param <T> the type used for the Build Type.
* @param <D> the type used for the default config
* @param <F> the type used for the product flavors.
*/
public class VariantConfiguration<T extends BuildType, D extends ProductFlavor, F extends ProductFlavor> {
// per variant, as is caches some manifest data specific to this variant.
private 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 D mDefaultConfig;
@NonNull
private final SourceProvider mDefaultSourceProvider;
@NonNull
private final T mBuildType;
/** SourceProvider for the BuildType. Can be null */
@Nullable
private final SourceProvider mBuildTypeSourceProvider;
private final List<String> mFlavorDimensionNames = Lists.newArrayList();
private final List<F> mFlavors = 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 VariantType mType;
/**
* Optional tested config in case this variant is used for testing another variant.
*
* @see VariantType#isForTesting()
*/
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;
@NonNull
private ProductFlavor mMergedFlavor;
/**
* External/Jar dependencies.
*/
private final Set<JarDependency> mExternalJars = Sets.newHashSet();
/**
* Local Jar dependencies.
*/
private final Set<JarDependency> mLocalJars = 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.
*
* <p>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();
/**
* Variant-specific build Config fields.
*/
private final Map<String, ClassField> mBuildConfigFields = Maps.newTreeMap();
/**
* Variant-specific res values.
*/
private final Map<String, ClassField> mResValues = Maps.newTreeMap();
/**
* Signing Override to be used instead of any signing config provided by Build Type or
* Product Flavors.
*/
private final SigningConfig mSigningConfigOverride;
/**
* 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 new DefaultManifestParser().getPackage(manifestFile);
}
/**
* Creates the configuration with the base source sets for a given {@link VariantType}. Meant
* for non-testing variants.
*
* @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 signingConfigOverride an optional Signing override to be used for signing.
*/
public VariantConfiguration(
@NonNull D defaultConfig,
@NonNull SourceProvider defaultSourceProvider,
@NonNull T buildType,
@Nullable SourceProvider buildTypeSourceProvider,
@NonNull VariantType type,
@Nullable SigningConfig signingConfigOverride) {
this(
defaultConfig, defaultSourceProvider,
buildType, buildTypeSourceProvider,
type, null /*testedConfig*/, signingConfigOverride);
}
/**
* 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.ANDROID_TEST
* @param signingConfigOverride an optional Signing override to be used for signing.
*/
public VariantConfiguration(
@NonNull D defaultConfig,
@NonNull SourceProvider defaultSourceProvider,
@NonNull T buildType,
@Nullable SourceProvider buildTypeSourceProvider,
@NonNull VariantType type,
@Nullable VariantConfiguration testedConfig,
@Nullable SigningConfig signingConfigOverride) {
checkNotNull(defaultConfig);
checkNotNull(defaultSourceProvider);
checkNotNull(buildType);
checkNotNull(type);
checkArgument(
!type.isForTesting() || testedConfig != null,
"You have to specify the tested variant for this variant type.");
checkArgument(
type.isForTesting() || testedConfig == null,
"This variant type doesn't need a tested variant.");
mDefaultConfig = checkNotNull(defaultConfig);
mDefaultSourceProvider = checkNotNull(defaultSourceProvider);
mBuildType = checkNotNull(buildType);
mBuildTypeSourceProvider = buildTypeSourceProvider;
mType = checkNotNull(type);
mTestedConfig = testedConfig;
mSigningConfigOverride = signingConfigOverride;
mMergedFlavor = DefaultProductFlavor.clone(mDefaultConfig);
}
/**
* 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.isForTesting()) {
sb.append(mType.getSuffix());
}
mFullName = sb.toString();
}
return mFullName;
}
/**
* Returns a full name that includes the given splits name.
* @param splitName the split name
* @return a unique name made up of the variant and split names.
*/
@NonNull
public String computeFullNameWithSplits(@NonNull String splitName) {
StringBuilder sb = new StringBuilder();
String flavorName = getFlavorName();
if (!flavorName.isEmpty()) {
sb.append(flavorName);
sb.append(StringHelper.capitalize(splitName));
} else {
sb.append(splitName);
}
sb.append(StringHelper.capitalize(mBuildType.getName()));
if (mType.isForTesting()) {
sb.append(mType.getSuffix());
}
return sb.toString();
}
/**
* 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 (mFlavors.isEmpty()) {
mFlavorName = "";
} else {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (F flavor : mFlavors) {
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 (!mFlavors.isEmpty()) {
for (ProductFlavor pf : mFlavors) {
sb.append(pf.getName()).append('-');
}
}
sb.append(mBuildType.getName());
if (mType.isForTesting()) {
sb.append('-').append(mType.getPrefix());
}
mBaseName = sb.toString();
}
return mBaseName;
}
/**
* Returns a base name that includes the given splits name.
* @param splitName the split name
* @return a unique name made up of the variant and split names.
*/
@NonNull
public String computeBaseNameWithSplits(@NonNull String splitName) {
StringBuilder sb = new StringBuilder();
if (!mFlavors.isEmpty()) {
for (ProductFlavor pf : mFlavors) {
sb.append(pf.getName()).append('-');
}
}
sb.append(splitName).append('-');
sb.append(mBuildType.getName());
if (mType.isForTesting()) {
sb.append('-').append(mType.getPrefix());
}
return sb.toString();
}
/**
* Returns a unique directory name (can include multiple folders) for the variant,
* based on build type, flavor and test.
*
* <p>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.isForTesting()) {
sb.append(mType.getPrefix()).append("/");
}
if (!mFlavors.isEmpty()) {
boolean first = true;
for (F flavor : mFlavors) {
sb.append(first ? flavor.getName() : StringHelper.capitalize(flavor.getName()));
first = false;
}
sb.append('/').append(mBuildType.getName());
} else {
sb.append(mBuildType.getName());
}
mDirName = sb.toString();
}
return mDirName;
}
/**
* Returns a unique directory name (can include multiple folders) for the variant,
* based on build type, flavor and test, and splits.
*
* <p>This always uses forward slashes ('/') as separator on all platform.
*
* @return the directory name for the variant
*/
@NonNull
public String computeDirNameWithSplits(@NonNull String... splitNames) {
StringBuilder sb = new StringBuilder();
if (mType.isForTesting()) {
sb.append(mType.getPrefix()).append("/");
}
if (!mFlavors.isEmpty()) {
for (F flavor : mFlavors) {
sb.append(flavor.getName());
}
sb.append('/');
}
for (String splitName : splitNames) {
if (splitName != null) {
sb.append(splitName).append('/');
}
}
sb.append(mBuildType.getName());
return sb.toString();
}
/**
* 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 (mFlavors.isEmpty()) {
return Collections.emptyList();
}
List<String> names;
int count = mFlavors.size();
if (count > 1) {
names = Lists.newArrayListWithCapacity(count * 2);
for (int i = 0 ; i < count ; i++) {
names.add(mFlavors.get(i).getName());
names.add(mFlavorDimensionNames.get(i));
}
} else {
names = Collections.singletonList(mFlavors.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 F productFlavor,
@NonNull SourceProvider sourceProvider,
@NonNull String dimensionName) {
mFlavors.add(productFlavor);
mFlavorSourceProviders.add(sourceProvider);
mFlavorDimensionNames.add(dimensionName);
mMergedFlavor = DefaultProductFlavor.mergeFlavors(mMergedFlavor, productFlavor);
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;
}
/**
* Sets the dependencies
*
* @param container a DependencyContainer.
* @return the config object
*/
@NonNull
public VariantConfiguration setDependencies(@NonNull DependencyContainer container) {
// Output of mTestedConfig will not be initialized until the tasks for the tested config are
// created. If library output has never been added to mDirectLibraries, checked the output
// of the mTestedConfig to see if the tasks are now created.
if (mTestedConfig != null &&
mTestedConfig.mType == VariantType.LIBRARY &&
mTestedConfig.mOutput != null &&
!mDirectLibraries.contains(mTestedConfig.mOutput)) {
mDirectLibraries.add(mTestedConfig.mOutput);
}
mDirectLibraries.addAll(container.getAndroidDependencies());
mExternalJars.addAll(container.getJarDependencies());
mLocalJars.addAll(container.getLocalDependencies());
resolveIndirectLibraryDependencies(mDirectLibraries, mFlatLibraries);
return this;
}
/**
* Returns the list of external/module jar dependencies
* @return a non null collection of Jar dependencies.
*/
@NonNull
public Collection<JarDependency> getExternalJarDependencies() {
return mExternalJars;
}
/**
* Returns the list of local jar dependencies
* @return a non null collection of Jar dependencies.
*/
@NonNull
public Collection<JarDependency> getLocalJarDependencies() {
return mLocalJars;
}
/**
* 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;
}
/**
* Returns the {@link LibraryDependency} that this library variant produces. Used so that
* related test variants can use it as a dependency. Returns null if this is not a library
* variant.
*
* @see #mOutput
*/
@Nullable
public LibraryDependency getOutput() {
return mOutput;
}
@NonNull
public D getDefaultConfig() {
return mDefaultConfig;
}
@NonNull
public SourceProvider getDefaultSourceSet() {
return mDefaultSourceProvider;
}
@NonNull
public ProductFlavor getMergedFlavor() {
return mMergedFlavor;
}
@NonNull
public T getBuildType() {
return mBuildType;
}
/**
* The SourceProvider for the BuildType. Can be null.
*/
@Nullable
public SourceProvider getBuildTypeSourceSet() {
return mBuildTypeSourceProvider;
}
public boolean hasFlavors() {
return !mFlavors.isEmpty();
}
/**
* Returns the product flavors. Items earlier in the list override later items.
*/
@NonNull
public List<F> getProductFlavors() {
return mFlavors;
}
/**
* 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;
}
/**
* 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 VariantType 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
static 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() - 1 ; 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 application ID before any overrides from flavors.
* If the variant is a test variant, then the application ID is the one coming from the
* configuration of the tested variant, and this call is similar to {@link #getApplicationId()}
* @return the original application ID
*/
@Nullable
public String getOriginalApplicationId() {
if (mType.isForTesting()) {
return getApplicationId();
}
return getPackageFromManifest();
}
/**
* Returns the application ID 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 application ID
*/
@NonNull
public String getApplicationId() {
String id;
if (mType.isForTesting()) {
checkState(mTestedConfig != null);
id = mMergedFlavor.getTestApplicationId();
String testedPackage = mTestedConfig.getApplicationId();
if (id == null) {
id = testedPackage + ".test";
} else {
if (id.equals(testedPackage)) {
throw new RuntimeException(String.format("Application and test application id "
+ "cannot be the same: both are '%s' for %s",
id, getFullName()));
}
}
} else {
// first get package override.
id = getIdOverride();
// if it's null, this means we just need the default package
// from the manifest since both flavor and build type do nothing.
if (id == null) {
id = getPackageFromManifest();
}
}
if (id == null) {
throw new RuntimeException("Failed to get application id for " + getFullName());
}
return id;
}
@Nullable
public String getTestedApplicationId() {
if (mType.isForTesting()) {
checkState(mTestedConfig != null);
if (mTestedConfig.mType == VariantType.LIBRARY) {
return getApplicationId();
} else {
return mTestedConfig.getApplicationId();
}
}
return null;
}
/**
* Returns the application id override value coming from the Product Flavor and/or the
* Build Type. If the package/id is not overridden then this returns null.
*
* @return the id override or null
*/
@Nullable
public String getIdOverride() {
String idName = mMergedFlavor.getApplicationId();
String idSuffix = mBuildType.getApplicationIdSuffix();
if (idSuffix != null && !idSuffix.isEmpty()) {
if (idName == null) {
idName = getPackageFromManifest();
}
if (idSuffix.charAt(0) == '.') {
idName = idName + idSuffix;
} else {
idName = idName + '.' + idSuffix;
}
}
return idName;
}
/**
* 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 (versionName == null && !mType.isForTesting()) {
versionName = getVersionNameFromManifest();
}
if (versionSuffix != null && !versionSuffix.isEmpty()) {
versionName = Strings.nullToEmpty(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() != null ?
mMergedFlavor.getVersionCode() : -1;
if (versionCode == -1 && !mType.isForTesting()) {
versionCode = getVersionCodeFromManifest();
}
return versionCode;
}
private static final String DEFAULT_TEST_RUNNER = "android.test.InstrumentationTestRunner";
private static final Boolean DEFAULT_HANDLE_PROFILING = false;
private static final 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
*/
@NonNull
public String getInstrumentationRunner() {
VariantConfiguration config = this;
if (mType.isForTesting()) {
config = getTestedConfig();
checkState(config != null);
}
String runner = config.mMergedFlavor.getTestInstrumentationRunner();
return runner != null ? runner : DEFAULT_TEST_RUNNER;
}
/**
* Returns the instrumentationRunner arguments to use to test this variant, or if the
* variant is a test, the ones to use to test the tested variant
*/
@NonNull
public Map<String, String> getInstrumentationRunnerArguments() {
VariantConfiguration config = this;
if (mType.isForTesting()) {
config = getTestedConfig();
checkState(config != null);
}
return config.mMergedFlavor.getTestInstrumentationRunnerArguments();
}
/**
* 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
*/
@NonNull
public Boolean getHandleProfiling() {
VariantConfiguration config = this;
if (mType.isForTesting()) {
config = getTestedConfig();
checkState(config != null);
}
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
*/
@NonNull
public Boolean getFunctionalTest() {
VariantConfiguration config = this;
if (mType.isForTesting()) {
config = getTestedConfig();
checkState(config != null);
}
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() {
checkState(!mType.isForTesting());
File manifestLocation = mDefaultSourceProvider.getManifestFile();
String packageName = sManifestParser.getPackage(manifestLocation);
if (packageName == null) {
throw new RuntimeException(String.format("Cannot read packageName from %1$s",
mDefaultSourceProvider.getManifestFile().getAbsolutePath()));
}
return packageName;
}
/**
* 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
*/
@NonNull
public ApiVersion getMinSdkVersion() {
if (mTestedConfig != null) {
return mTestedConfig.getMinSdkVersion();
}
ApiVersion minSdkVersion = mMergedFlavor.getMinSdkVersion();
if (minSdkVersion == null) {
// read it from the main manifest
File manifestLocation = mDefaultSourceProvider.getManifestFile();
minSdkVersion = DefaultApiVersion.create(
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
*/
@NonNull
public ApiVersion getTargetSdkVersion() {
if (mTestedConfig != null) {
return mTestedConfig.getTargetSdkVersion();
}
ApiVersion targetSdkVersion = mMergedFlavor.getTargetSdkVersion();
if (targetSdkVersion == null) {
// read it from the main manifest
File manifestLocation = mDefaultSourceProvider.getManifestFile();
targetSdkVersion = DefaultApiVersion.create(
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;
}
/**
* Returns a list of sorted SourceProvider in order of ascending order, meaning, the earlier
* items are meant to be overridden by later items.
*
* @return a list of source provider
*/
@NonNull
public List<SourceProvider> getSortedSourceProviders() {
List<SourceProvider> providers = Lists.newArrayList();
// first the default source provider
providers.add(mDefaultSourceProvider);
// the list of flavor must be reversed to use the right overlay order.
for (int n = mFlavorSourceProviders.size() - 1; n >= 0 ; n--) {
providers.add(mFlavorSourceProviders.get(n));
}
// multiflavor specific overrides flavor
if (mMultiFlavorSourceProvider != null) {
providers.add(mMultiFlavorSourceProvider);
}
// build type overrides flavors
if (mBuildTypeSourceProvider != null) {
providers.add(mBuildTypeSourceProvider);
}
// variant specific overrides all
if (mVariantSourceProvider != null) {
providers.add(mVariantSourceProvider);
}
return providers;
}
@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 generatedResFolders a list of generated res folders
* @param includeDependencies whether to include in the result the resources of the dependencies
*
* @return a list ResourceSet.
*/
@NonNull
public List<ResourceSet> getResourceSets(@NonNull List<File> generatedResFolders,
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);
if (!dependency.isOptional()) {
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();
// the main + generated res folders are in the same ResourceSet
ResourceSet resourceSet = new ResourceSet(BuilderConstants.MAIN);
resourceSet.addSources(mainResDirs);
if (!generatedResFolders.isEmpty()) {
for (File generatedResFolder : generatedResFolders) {
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(sourceProvider.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(@NonNull List<File> generatedResFolders,
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();
// the main + generated asset folders are in the same AssetSet
AssetSet assetSet = new AssetSet(BuilderConstants.MAIN);
assetSet.addSources(mainResDirs);
if (!generatedResFolders.isEmpty()) {
for (File generatedResFolder : generatedResFolders) {
assetSet.addSource(generatedResFolder);
}
}
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(mFlavors.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<SourceProvider> providers = getSortedSourceProviders();
List<File> sourceList = Lists.newArrayListWithExpectedSize(providers.size());
for (SourceProvider provider : providers) {
sourceList.addAll(provider.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<SourceProvider> providers = getSortedSourceProviders();
List<File> sourceList = Lists.newArrayListWithExpectedSize(providers.size());
for (SourceProvider provider : providers) {
sourceList.addAll(provider.getAidlDirectories());
}
return sourceList;
}
@NonNull
public List<File> getJniSourceList() {
List<SourceProvider> providers = getSortedSourceProviders();
List<File> sourceList = Lists.newArrayListWithExpectedSize(providers.size());
for (SourceProvider provider : providers) {
sourceList.addAll(provider.getCDirectories());
}
return sourceList;
}
@NonNull
public List<File> getJniLibsList() {
List<SourceProvider> providers = getSortedSourceProviders();
List<File> sourceList = Lists.newArrayListWithExpectedSize(providers.size());
for (SourceProvider provider : providers) {
sourceList.addAll(provider.getJniLibsDirectories());
}
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.newHashSetWithExpectedSize(
mExternalJars.size() + mLocalJars.size() + mFlatLibraries.size());
for (LibraryDependency lib : mFlatLibraries) {
classpath.add(lib.getJarFile());
for (File jarFile : lib.getLocalJars()) {
classpath.add(jarFile);
}
}
for (JarDependency jar : mExternalJars) {
if (jar.isCompiled()) {
classpath.add(jar.getJarFile());
}
}
for (JarDependency jar : mLocalJars) {
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 Set<File> getPackagedJars() {
Set<File> jars = Sets.newHashSetWithExpectedSize(
mExternalJars.size() + mLocalJars.size() + mFlatLibraries.size());
for (JarDependency jar : mExternalJars) {
File jarFile = jar.getJarFile();
if (jar.isPackaged() && jarFile.exists()) {
jars.add(jarFile);
}
}
for (JarDependency jar : mLocalJars) {
File jarFile = jar.getJarFile();
if (jar.isPackaged() && jarFile.exists()) {
jars.add(jarFile);
}
}
for (LibraryDependency libraryDependency : mFlatLibraries) {
if (!libraryDependency.isOptional()) {
File libJar = libraryDependency.getJarFile();
if (libJar.exists()) {
jars.add(libJar);
}
for (File jarFile : libraryDependency.getLocalJars()) {
if (jarFile.isFile()) {
jars.add(jarFile);
}
}
}
}
return jars;
}
/**
* Returns the list of provided-only jars for this config.
*
* @return a non null, but possibly empty list.
*/
@NonNull
public List<File> getProvidedOnlyJars() {
Set<File> jars = Sets.newHashSetWithExpectedSize(mExternalJars.size() + mLocalJars.size());
for (JarDependency jar : mExternalJars) {
File jarFile = jar.getJarFile();
if (jar.isCompiled() && !jar.isPackaged() && jarFile.exists()) {
jars.add(jarFile);
}
}
for (JarDependency jar : mLocalJars) {
File jarFile = jar.getJarFile();
if (jar.isCompiled() && !jar.isPackaged() && jarFile.exists()) {
jars.add(jarFile);
}
}
for (LibraryDependency libraryDependency : mFlatLibraries) {
if (libraryDependency.isOptional()) {
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);
}
/**
* Adds a variant-specific BuildConfig field.
* @param type the type of the field
* @param name the name of the field
* @param value the value of the field
*/
public void addBuildConfigField(@NonNull String type, @NonNull String name, @NonNull String value) {
ClassField classField = AndroidBuilder.createClassField(type, name, value);
mBuildConfigFields.put(name, classField);
}
/**
* Adds a variant-specific res value.
* @param type the type of the field
* @param name the name of the field
* @param value the value of the field
*/
public void addResValue(@NonNull String type, @NonNull String name, @NonNull String value) {
ClassField classField = AndroidBuilder.createClassField(type, name, value);
mResValues.put(name, classField);
}
/**
* 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();
// keep track of the names already added. This is because we show where the items
// come from so we cannot just put everything a map and let the new ones override the
// old ones.
Set<String> usedFieldNames = Sets.newHashSet();
Collection<ClassField> list = mBuildConfigFields.values();
if (!list.isEmpty()) {
fullList.add("Fields from the variant");
fillFieldList(fullList, usedFieldNames, list);
}
list = mBuildType.getBuildConfigFields().values();
if (!list.isEmpty()) {
fullList.add("Fields from build type: " + mBuildType.getName());
fillFieldList(fullList, usedFieldNames, list);
}
for (F flavor : mFlavors) {
list = flavor.getBuildConfigFields().values();
if (!list.isEmpty()) {
fullList.add("Fields from product flavor: " + flavor.getName());
fillFieldList(fullList, usedFieldNames, list);
}
}
list = mDefaultConfig.getBuildConfigFields().values();
if (!list.isEmpty()) {
fullList.add("Fields from default config.");
fillFieldList(fullList, usedFieldNames, list);
}
return fullList;
}
/**
* Return the merged build config fields for the variant.
*
* This is made of of the variant-specific fields overlayed on top of the build type ones,
* the flavors ones, and the default config ones.
*
* @return a map of merged fields
*/
@NonNull
public Map<String, ClassField> getMergedBuildConfigFields() {
Map<String, ClassField> mergedMap = Maps.newHashMap();
// start from the lowest priority and just add it all. Higher priority fields
// will replace lower priority ones.
mergedMap.putAll(mDefaultConfig.getBuildConfigFields());
for (int i = mFlavors.size() - 1; i >= 0 ; i--) {
mergedMap.putAll(mFlavors.get(i).getBuildConfigFields());
}
mergedMap.putAll(mBuildType.getBuildConfigFields());
mergedMap.putAll(mBuildConfigFields);
return mergedMap;
}
/**
* Return the merged res values for the variant.
*
* This is made of of the variant-specific fields overlayed on top of the build type ones,
* the flavors ones, and the default config ones.
*
* @return a map of merged fields
*/
@NonNull
public Map<String, ClassField> getMergedResValues() {
Map<String, ClassField> mergedMap = Maps.newHashMap();
// start from the lowest priority and just add it all. Higher priority fields
// will replace lower priority ones.
mergedMap.putAll(mDefaultConfig.getResValues());
for (int i = mFlavors.size() - 1; i >= 0 ; i--) {
mergedMap.putAll(mFlavors.get(i).getResValues());
}
mergedMap.putAll(mBuildType.getResValues());
mergedMap.putAll(mResValues);
return mergedMap;
}
/**
* Fills a list of Object from a given list of ClassField only if the name isn't in a set.
* Each new item added adds its name to the list.
* @param outList the out list
* @param usedFieldNames the list of field names already in the list
* @param list the list to copy items from
*/
private static void fillFieldList(
@NonNull List<Object> outList,
@NonNull Set<String> usedFieldNames,
@NonNull Collection<ClassField> list) {
for (ClassField f : list) {
String name = f.getName();
if (!usedFieldNames.contains(name)) {
usedFieldNames.add(f.getName());
outList.add(f);
}
}
}
/**
* Returns a list of generated resource values.
*
* 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> getResValues() {
List<Object> fullList = Lists.newArrayList();
// keep track of the names already added. This is because we show where the items
// come from so we cannot just put everything a map and let the new ones override the
// old ones.
Set<String> usedFieldNames = Sets.newHashSet();
Collection<ClassField> list = mResValues.values();
if (!list.isEmpty()) {
fullList.add("Values from the variant");
fillFieldList(fullList, usedFieldNames, list);
}
list = mBuildType.getResValues().values();
if (!list.isEmpty()) {
fullList.add("Values from build type: " + mBuildType.getName());
fillFieldList(fullList, usedFieldNames, list);
}
for (F flavor : mFlavors) {
list = flavor.getResValues().values();
if (!list.isEmpty()) {
fullList.add("Values from product flavor: " + flavor.getName());
fillFieldList(fullList, usedFieldNames, list);
}
}
list = mDefaultConfig.getResValues().values();
if (!list.isEmpty()) {
fullList.add("Values from default config.");
fillFieldList(fullList, usedFieldNames, list);
}
return fullList;
}
@Nullable
public SigningConfig getSigningConfig() {
if (mSigningConfigOverride != null) {
return mSigningConfigOverride;
}
SigningConfig signingConfig = mBuildType.getSigningConfig();
if (signingConfig != null) {
return signingConfig;
}
return mMergedFlavor.getSigningConfig();
}
public boolean isSigningReady() {
SigningConfig signingConfig = getSigningConfig();
return signingConfig != null && signingConfig.isSigningReady();
}
/**
* Returns the proguard config files coming from the project but also from the dependencies.
*
* Note that if the method is set to include config files coming from libraries, they will
* only be included if the aars have already been unzipped.
*
* @param includeLibraries whether to include the library dependencies.
* @return a non null list of proguard files.
*/
@NonNull
public List<File> getProguardFiles(boolean includeLibraries, List<File> defaultProguardConfig) {
List<File> fullList = Lists.newArrayList();
// add the config files from the build type, main config and flavors
fullList.addAll(mDefaultConfig.getProguardFiles());
fullList.addAll(mBuildType.getProguardFiles());
for (F flavor : mFlavors) {
fullList.addAll(flavor.getProguardFiles());
}
if (fullList.isEmpty()) {
fullList.addAll(defaultProguardConfig);
}
// 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;
}
/**
* Returns the proguard config files to be used for the test APK.
*/
@NonNull
public List<File> getTestProguardFiles() {
List<File> fullList = Lists.newArrayList();
// add the config files from the build type, main config and flavors
fullList.addAll(mDefaultConfig.getTestProguardFiles());
fullList.addAll(mBuildType.getTestProguardFiles());
for (F flavor : mFlavors) {
fullList.addAll(flavor.getTestProguardFiles());
}
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 (F flavor : mFlavors) {
fullList.addAll(flavor.getConsumerProguardFiles());
}
return fullList;
}
public boolean isTestCoverageEnabled() {
return mBuildType.isTestCoverageEnabled();
}
/**
* Returns the merged manifest placeholders. All product flavors are merged first, then build
* type specific placeholders are added and potentially overrides product flavors values.
* @return the merged manifest placeholders for a build variant.
*/
@NonNull
public Map<String, Object> getManifestPlaceholders() {
Map<String, Object> mergedFlavorsPlaceholders = mMergedFlavor.getManifestPlaceholders();
// so far, blindly override the build type placeholders
mergedFlavorsPlaceholders.putAll(mBuildType.getManifestPlaceholders());
return mergedFlavorsPlaceholders;
}
public boolean isMultiDexEnabled() {
Boolean value = mBuildType.getMultiDexEnabled();
if (value != null) {
return value;
}
value = mMergedFlavor.getMultiDexEnabled();
if (value != null) {
return value;
}
return false;
}
public File getMultiDexKeepFile() {
File value = mBuildType.getMultiDexKeepFile();
if (value != null) {
return value;
}
value = mMergedFlavor.getMultiDexKeepFile();
if (value != null) {
return value;
}
return null;
}
public File getMultiDexKeepProguard() {
File value = mBuildType.getMultiDexKeepProguard();
if (value != null) {
return value;
}
value = mMergedFlavor.getMultiDexKeepProguard();
if (value != null) {
return value;
}
return null;
}
public boolean isLegacyMultiDexMode() {
return isMultiDexEnabled() && getMinSdkVersion().getApiLevel() < 21;
}
/**
* Returns the renderscript support mode.
*/
public boolean getRenderscriptSupportModeEnabled() {
Boolean value = mMergedFlavor.getRenderscriptSupportModeEnabled();
if (value != null) {
return value;
}
// default is false.
return false;
}
/**
* Returns the renderscript NDK mode.
*/
public boolean getRenderscriptNdkModeEnabled() {
Boolean value = mMergedFlavor.getRenderscriptNdkModeEnabled();
if (value != null) {
return value;
}
// default is false.
return false;
}
public Collection<File> getJarJarRuleFiles() {
ImmutableList.Builder<File> jarjarRuleFiles = ImmutableList.builder();
jarjarRuleFiles.addAll(getMergedFlavor().getJarJarRuleFiles());
jarjarRuleFiles.addAll(mBuildType.getJarJarRuleFiles());
return jarjarRuleFiles.build();
}
}