/* * 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.checkNotNull; import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.builder.internal.BaseConfigImpl; import com.android.builder.model.ApiVersion; import com.android.builder.model.ProductFlavor; import com.android.builder.model.SigningConfig; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.Set; /** * The configuration of a product flavor. * * This is also used to describe the default configuration of all builds, even those that * do not contain any flavors. */ public class DefaultProductFlavor extends BaseConfigImpl implements ProductFlavor { private static final long serialVersionUID = 1L; private final String mName; @Nullable private String mDimension; @Nullable private ApiVersion mMinSdkVersion; @Nullable private ApiVersion mTargetSdkVersion; @Nullable private Integer mMaxSdkVersion; @Nullable private Integer mRenderscriptTargetApi; @Nullable private Boolean mRenderscriptSupportModeEnabled; @Nullable private Boolean mRenderscriptNdkModeEnabled; @Nullable private Integer mVersionCode; @Nullable private String mVersionName; @Nullable private String mApplicationId; @Nullable private String mTestApplicationId; @Nullable private String mTestInstrumentationRunner; @NonNull private Map<String, String> mTestInstrumentationRunnerArguments = Maps.newHashMap(); @Nullable private Boolean mTestHandleProfiling; @Nullable private Boolean mTestFunctionalTest; @Nullable private SigningConfig mSigningConfig; @Nullable private Set<String> mResourceConfiguration; /** * Creates a ProductFlavor with a given name. * * Names can be important when dealing with flavor groups. * @param name the name of the flavor. * * @see BuilderConstants#MAIN */ public DefaultProductFlavor(@NonNull String name) { mName = name; } @Override @NonNull public String getName() { return mName; } public void setDimension(@NonNull String dimension) { mDimension = dimension; } /** Name of the dimension this product flavor belongs to. */ @Nullable @Override public String getDimension() { return mDimension; } /** * Sets the application id. */ @NonNull public ProductFlavor setApplicationId(String applicationId) { mApplicationId = applicationId; return this; } /** * Returns the application ID. * * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/applicationid-vs-packagename">ApplicationId versus PackageName</a> */ @Override @Nullable public String getApplicationId() { return mApplicationId; } /** * Sets the version code. * * @param versionCode the version code * @return the flavor object */ @NonNull public ProductFlavor setVersionCode(Integer versionCode) { mVersionCode = versionCode; return this; } /** * Version code. * * <p>See <a href="http://developer.android.com/tools/publishing/versioning.html">Versioning Your Application</a> */ @Override @Nullable public Integer getVersionCode() { return mVersionCode; } /** * Sets the version name. * * @param versionName the version name * @return the flavor object */ @NonNull public ProductFlavor setVersionName(String versionName) { mVersionName = versionName; return this; } /** * Version name. * * <p>See <a href="http://developer.android.com/tools/publishing/versioning.html">Versioning Your Application</a> */ @Override @Nullable public String getVersionName() { return mVersionName; } /** * Sets the minSdkVersion to the given value. */ @NonNull public ProductFlavor setMinSdkVersion(ApiVersion minSdkVersion) { mMinSdkVersion = minSdkVersion; return this; } /** * Min SDK version. */ @Nullable @Override public ApiVersion getMinSdkVersion() { return mMinSdkVersion; } /** Sets the targetSdkVersion to the given value. */ @NonNull public ProductFlavor setTargetSdkVersion(@Nullable ApiVersion targetSdkVersion) { mTargetSdkVersion = targetSdkVersion; return this; } /** * Target SDK version. */ @Nullable @Override public ApiVersion getTargetSdkVersion() { return mTargetSdkVersion; } @NonNull public ProductFlavor setMaxSdkVersion(Integer maxSdkVersion) { mMaxSdkVersion = maxSdkVersion; return this; } @Nullable @Override public Integer getMaxSdkVersion() { return mMaxSdkVersion; } @Override @Nullable public Integer getRenderscriptTargetApi() { return mRenderscriptTargetApi; } /** Sets the renderscript target API to the given value. */ public void setRenderscriptTargetApi(Integer renderscriptTargetApi) { mRenderscriptTargetApi = renderscriptTargetApi; } @Override @Nullable public Boolean getRenderscriptSupportModeEnabled() { return mRenderscriptSupportModeEnabled; } /** * Sets whether the renderscript code should be compiled in support mode to make it compatible * with older versions of Android. */ public ProductFlavor setRenderscriptSupportModeEnabled(Boolean renderscriptSupportMode) { mRenderscriptSupportModeEnabled = renderscriptSupportMode; return this; } @Override @Nullable public Boolean getRenderscriptNdkModeEnabled() { return mRenderscriptNdkModeEnabled; } /** Sets whether the renderscript code should be compiled to generate C/C++ bindings. */ public ProductFlavor setRenderscriptNdkModeEnabled(Boolean renderscriptNdkMode) { mRenderscriptNdkModeEnabled = renderscriptNdkMode; return this; } /** Sets the test application ID. */ @NonNull public ProductFlavor setTestApplicationId(String applicationId) { mTestApplicationId = applicationId; return this; } /** * Test application ID. * * <p>See <a href="http://tools.android.com/tech-docs/new-build-system/applicationid-vs-packagename">ApplicationId versus PackageName</a> */ @Override @Nullable public String getTestApplicationId() { return mTestApplicationId; } /** Sets the test instrumentation runner to the given value. */ @NonNull public ProductFlavor setTestInstrumentationRunner(String testInstrumentationRunner) { mTestInstrumentationRunner = testInstrumentationRunner; return this; } /** * Test instrumentation runner class name. * * <p>This is a fully qualified class name of the runner, e.g. * <code>android.test.InstrumentationTestRunner</code> * * <p>See <a href="http://developer.android.com/guide/topics/manifest/instrumentation-element.html"> * instrumentation</a>. */ @Override @Nullable public String getTestInstrumentationRunner() { return mTestInstrumentationRunner; } /** Sets the test instrumentation runner custom arguments. */ @NonNull public ProductFlavor setTestInstrumentationRunnerArguments( @NonNull Map<String, String> testInstrumentationRunnerArguments) { mTestInstrumentationRunnerArguments = checkNotNull(testInstrumentationRunnerArguments); return this; } /** * Test instrumentation runner custom arguments. * * e.g. <code>[key: "value"]</code> will give * <code>adb shell am instrument -w <b>-e key value</b> com.example</code>...". * * <p>See <a href="http://developer.android.com/guide/topics/manifest/instrumentation-element.html"> * instrumentation</a>. * * <p>Test runner arguments can also be specified from the command line: * * <p><pre> * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,foo=bar * </pre> */ @Override @NonNull public Map<String, String> getTestInstrumentationRunnerArguments() { return mTestInstrumentationRunnerArguments; } /** * See <a href="http://developer.android.com/guide/topics/manifest/instrumentation-element.html"> * instrumentation</a>. */ @Override @Nullable public Boolean getTestHandleProfiling() { return mTestHandleProfiling; } @NonNull public ProductFlavor setTestHandleProfiling(boolean handleProfiling) { mTestHandleProfiling = handleProfiling; return this; } /** * See <a href="http://developer.android.com/guide/topics/manifest/instrumentation-element.html"> * instrumentation</a>. */ @Override @Nullable public Boolean getTestFunctionalTest() { return mTestFunctionalTest; } @NonNull public ProductFlavor setTestFunctionalTest(boolean functionalTest) { mTestFunctionalTest = functionalTest; return this; } /** * Signing config used by this product flavor. */ @Override @Nullable public SigningConfig getSigningConfig() { return mSigningConfig; } /** Sets the signing configuration. e.g.: {@code signingConfig signingConfigs.myConfig} */ @NonNull public ProductFlavor setSigningConfig(SigningConfig signingConfig) { mSigningConfig = signingConfig; return this; } /** * Adds a res config filter (for instance 'hdpi') */ public void addResourceConfiguration(@NonNull String configuration) { if (mResourceConfiguration == null) { mResourceConfiguration = Sets.newHashSet(); } mResourceConfiguration.add(configuration); } /** * Adds a res config filter (for instance 'hdpi') */ public void addResourceConfigurations(@NonNull String... configurations) { if (mResourceConfiguration == null) { mResourceConfiguration = Sets.newHashSet(); } mResourceConfiguration.addAll(Arrays.asList(configurations)); } /** * Adds a res config filter (for instance 'hdpi') */ public void addResourceConfigurations(@NonNull Collection<String> configurations) { if (mResourceConfiguration == null) { mResourceConfiguration = Sets.newHashSet(); } mResourceConfiguration.addAll(configurations); } /** * Adds a res config filter (for instance 'hdpi') */ @NonNull @Override public Collection<String> getResourceConfigurations() { if (mResourceConfiguration == null) { mResourceConfiguration = Sets.newHashSet(); } return mResourceConfiguration; } /** * Merges two flavors on top of one another and returns a new object with the result. * * The behavior is that if a value is present in the overlay, then it is used, otherwise * we use the value from the base. * * @param base the flavor to merge on top of * @param overlay the flavor to apply on top of the base. * * @return a new ProductFlavor that represents the merge. */ @NonNull static ProductFlavor mergeFlavors(@NonNull ProductFlavor base, @NonNull ProductFlavor overlay) { DefaultProductFlavor flavor = new DefaultProductFlavor(""); flavor.mMinSdkVersion = chooseNotNull( overlay.getMinSdkVersion(), base.getMinSdkVersion()); flavor.mTargetSdkVersion = chooseNotNull( overlay.getTargetSdkVersion(), base.getTargetSdkVersion()); flavor.mMaxSdkVersion = chooseNotNull( overlay.getMaxSdkVersion(), base.getMaxSdkVersion()); flavor.mRenderscriptTargetApi = chooseNotNull( overlay.getRenderscriptTargetApi(), base.getRenderscriptTargetApi()); flavor.mRenderscriptSupportModeEnabled = chooseNotNull( overlay.getRenderscriptSupportModeEnabled(), base.getRenderscriptSupportModeEnabled()); flavor.mRenderscriptNdkModeEnabled = chooseNotNull( overlay.getRenderscriptNdkModeEnabled(), base.getRenderscriptNdkModeEnabled()); flavor.mVersionCode = chooseNotNull(overlay.getVersionCode(), base.getVersionCode()); flavor.mVersionName = chooseNotNull(overlay.getVersionName(), base.getVersionName()); flavor.mApplicationId = chooseNotNull(overlay.getApplicationId(), base.getApplicationId()); flavor.mTestApplicationId = chooseNotNull( overlay.getTestApplicationId(), base.getTestApplicationId()); flavor.mTestInstrumentationRunner = chooseNotNull( overlay.getTestInstrumentationRunner(), base.getTestInstrumentationRunner()); flavor.mTestInstrumentationRunnerArguments.putAll( base.getTestInstrumentationRunnerArguments()); flavor.mTestInstrumentationRunnerArguments.putAll( overlay.getTestInstrumentationRunnerArguments()); flavor.mTestHandleProfiling = chooseNotNull( overlay.getTestHandleProfiling(), base.getTestHandleProfiling()); flavor.mTestFunctionalTest = chooseNotNull( overlay.getTestFunctionalTest(), base.getTestFunctionalTest()); flavor.mSigningConfig = chooseNotNull( overlay.getSigningConfig(), base.getSigningConfig()); flavor.addResourceConfigurations(base.getResourceConfigurations()); flavor.addResourceConfigurations(overlay.getResourceConfigurations()); flavor.addManifestPlaceholders(base.getManifestPlaceholders()); flavor.addManifestPlaceholders(overlay.getManifestPlaceholders()); flavor.addResValues(base.getResValues()); flavor.addResValues(overlay.getResValues()); flavor.addBuildConfigFields(base.getBuildConfigFields()); flavor.addBuildConfigFields(overlay.getBuildConfigFields()); flavor.setMultiDexEnabled(chooseNotNull( overlay.getMultiDexEnabled(), base.getMultiDexEnabled())); flavor.setMultiDexKeepFile(chooseNotNull( overlay.getMultiDexKeepFile(), base.getMultiDexKeepFile())); flavor.setMultiDexKeepProguard(chooseNotNull( overlay.getMultiDexKeepProguard(), base.getMultiDexKeepProguard())); flavor.setJarJarRuleFiles(ImmutableList.<File>builder() .addAll(overlay.getJarJarRuleFiles()) .addAll(base.getJarJarRuleFiles()) .build()); return flavor; } /** * Clone a given product flavor. * * @param productFlavor the flavor to clone. * * @return a new instance that is a clone of the flavor. */ @NonNull static ProductFlavor clone(@NonNull ProductFlavor productFlavor) { DefaultProductFlavor flavor = new DefaultProductFlavor(productFlavor.getName()); flavor._initWith(productFlavor); flavor.mDimension = productFlavor.getDimension(); flavor.mMinSdkVersion = productFlavor.getMinSdkVersion(); flavor.mTargetSdkVersion = productFlavor.getTargetSdkVersion(); flavor.mMaxSdkVersion = productFlavor.getMaxSdkVersion(); flavor.mRenderscriptTargetApi = productFlavor.getRenderscriptTargetApi(); flavor.mRenderscriptSupportModeEnabled = productFlavor.getRenderscriptSupportModeEnabled(); flavor.mRenderscriptNdkModeEnabled = productFlavor.getRenderscriptNdkModeEnabled(); flavor.mVersionCode = productFlavor.getVersionCode(); flavor.mVersionName = productFlavor.getVersionName(); flavor.mApplicationId = productFlavor.getApplicationId(); flavor.mTestApplicationId = productFlavor.getTestApplicationId(); flavor.mTestInstrumentationRunner = productFlavor.getTestInstrumentationRunner(); flavor.mTestInstrumentationRunnerArguments = productFlavor.getTestInstrumentationRunnerArguments(); flavor.mTestHandleProfiling = productFlavor.getTestHandleProfiling(); flavor.mTestFunctionalTest = productFlavor.getTestFunctionalTest(); flavor.mSigningConfig = productFlavor.getSigningConfig(); flavor.addResourceConfigurations(productFlavor.getResourceConfigurations()); flavor.addManifestPlaceholders(productFlavor.getManifestPlaceholders()); flavor.addResValues(productFlavor.getResValues()); flavor.addBuildConfigFields(productFlavor.getBuildConfigFields()); flavor.setMultiDexEnabled(productFlavor.getMultiDexEnabled()); flavor.setMultiDexKeepFile(productFlavor.getMultiDexKeepFile()); flavor.setMultiDexKeepProguard(productFlavor.getMultiDexKeepProguard()); flavor.setJarJarRuleFiles(ImmutableList.copyOf(productFlavor.getJarJarRuleFiles())); return flavor; } private static <T> T chooseNotNull(T overlay, T base) { return overlay != null ? overlay : base; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } DefaultProductFlavor that = (DefaultProductFlavor) o; return Objects.equal(mDimension, that.mDimension) && Objects.equal(mApplicationId, that.mApplicationId) && Objects.equal(mMaxSdkVersion, that.mMaxSdkVersion) && Objects.equal(mMinSdkVersion, that.mMinSdkVersion) && Objects.equal(mName, that.mName) && Objects.equal(mRenderscriptNdkModeEnabled, that.mRenderscriptNdkModeEnabled) && Objects.equal(mRenderscriptSupportModeEnabled, that.mRenderscriptSupportModeEnabled) && Objects.equal(mRenderscriptTargetApi, that.mRenderscriptTargetApi) && Objects.equal(mResourceConfiguration, that.mResourceConfiguration) && Objects.equal(mSigningConfig, that.mSigningConfig) && Objects.equal(mTargetSdkVersion, that.mTargetSdkVersion) && Objects.equal(mTestApplicationId, that.mTestApplicationId) && Objects.equal(mTestFunctionalTest, that.mTestFunctionalTest) && Objects.equal(mTestHandleProfiling, that.mTestHandleProfiling) && Objects.equal(mTestInstrumentationRunner, that.mTestInstrumentationRunner) && Objects.equal(mTestInstrumentationRunnerArguments, that.mTestInstrumentationRunnerArguments) && Objects.equal(mVersionCode, that.mVersionCode) && Objects.equal(mVersionName, that.mVersionName); } @Override public int hashCode() { return Objects.hashCode( super.hashCode(), mName, mDimension, mMinSdkVersion, mTargetSdkVersion, mMaxSdkVersion, mRenderscriptTargetApi, mRenderscriptSupportModeEnabled, mRenderscriptNdkModeEnabled, mVersionCode, mVersionName, mApplicationId, mTestApplicationId, mTestInstrumentationRunner, mTestInstrumentationRunnerArguments, mTestHandleProfiling, mTestFunctionalTest, mSigningConfig, mResourceConfiguration); } @Override @NonNull public String toString() { return Objects.toStringHelper(this) .add("name", mName) .add("dimension", mDimension) .add("minSdkVersion", mMinSdkVersion) .add("targetSdkVersion", mTargetSdkVersion) .add("renderscriptTargetApi", mRenderscriptTargetApi) .add("renderscriptSupportModeEnabled", mRenderscriptSupportModeEnabled) .add("renderscriptNdkModeEnabled", mRenderscriptNdkModeEnabled) .add("versionCode", mVersionCode) .add("versionName", mVersionName) .add("applicationId", mApplicationId) .add("testApplicationId", mTestApplicationId) .add("testInstrumentationRunner", mTestInstrumentationRunner) .add("testInstrumentationRunnerArguments", mTestInstrumentationRunnerArguments) .add("testHandleProfiling", mTestHandleProfiling) .add("testFunctionalTest", mTestFunctionalTest) .add("signingConfig", mSigningConfig) .add("resConfig", mResourceConfiguration) .add("mBuildConfigFields", getBuildConfigFields()) .add("mResValues", getResValues()) .add("mProguardFiles", getProguardFiles()) .add("mConsumerProguardFiles", getConsumerProguardFiles()) .add("mManifestPlaceholders", getManifestPlaceholders()) .toString(); } }