blob: 1da03a9fa5fed9d2028640fffd1e62d2d64adfc3 [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.build.gradle.internal.dsl;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.internal.errors.DeprecationReporter;
import com.android.builder.core.BuilderConstants;
import com.android.builder.core.DefaultApiVersion;
import com.android.builder.core.DefaultProductFlavor;
import com.android.builder.internal.ClassFieldImpl;
import com.android.builder.model.ApiVersion;
import com.android.builder.model.ClassField;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.gradle.api.Action;
import org.gradle.api.Project;
import org.gradle.api.logging.Logger;
import org.gradle.api.model.ObjectFactory;
/** Base DSL object used to configure product flavors. */
public abstract class BaseFlavor extends DefaultProductFlavor implements CoreProductFlavor {
@NonNull protected final Project project;
@NonNull protected final Logger logger;
@NonNull private final NdkOptions ndkConfig;
@NonNull private final ExternalNativeBuildOptions externalNativeBuildOptions;
@NonNull private final JavaCompileOptions javaCompileOptions;
@NonNull private final ShaderOptions shaderOptions;
@NonNull private final DeprecationReporter deprecationReporter;
public BaseFlavor(
@NonNull String name,
@NonNull Project project,
@NonNull ObjectFactory objectFactory,
@NonNull DeprecationReporter deprecationReporter,
@NonNull Logger logger) {
super(name, objectFactory.newInstance(VectorDrawablesOptions.class));
this.project = project;
this.deprecationReporter = deprecationReporter;
this.logger = logger;
ndkConfig = objectFactory.newInstance(NdkOptions.class);
externalNativeBuildOptions =
objectFactory.newInstance(ExternalNativeBuildOptions.class, objectFactory);
javaCompileOptions = objectFactory.newInstance(JavaCompileOptions.class, objectFactory);
shaderOptions = objectFactory.newInstance(ShaderOptions.class);
}
/** Encapsulates per-variant configurations for the NDK, such as ABI filters. */
@Nullable
public NdkOptions getNdk() {
return ndkConfig;
}
@Override
@Nullable
public CoreNdkOptions getNdkConfig() {
return ndkConfig;
}
/**
* Encapsulates per-variant CMake and ndk-build configurations for your external native build.
*
* <p>To learn more, see <a
* href="http://developer.android.com/studio/projects/add-native-code.html#">Add C and C++ Code
* to Your Project</a>.
*/
@Nullable
public ExternalNativeBuildOptions getExternalNativeBuild() {
return externalNativeBuildOptions;
}
@Nullable
@Override
public CoreExternalNativeBuildOptions getExternalNativeBuildOptions() {
return externalNativeBuildOptions;
}
public void setMinSdkVersion(int minSdkVersion) {
setMinSdkVersion(new DefaultApiVersion(minSdkVersion));
}
/**
* Sets minimum SDK version.
*
* <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
* uses-sdk element documentation</a>.
*/
public void minSdkVersion(int minSdkVersion) {
setMinSdkVersion(minSdkVersion);
}
public void setMinSdkVersion(@Nullable String minSdkVersion) {
setMinSdkVersion(getApiVersion(minSdkVersion));
}
/**
* Sets minimum SDK version.
*
* <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
* uses-sdk element documentation</a>.
*/
public void minSdkVersion(@Nullable String minSdkVersion) {
setMinSdkVersion(minSdkVersion);
}
@NonNull
public com.android.builder.model.ProductFlavor setTargetSdkVersion(int targetSdkVersion) {
setTargetSdkVersion(new DefaultApiVersion(targetSdkVersion));
return this;
}
/**
* Sets the target SDK version to the given value.
*
* <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
* uses-sdk element documentation</a>.
*/
public void targetSdkVersion(int targetSdkVersion) {
setTargetSdkVersion(targetSdkVersion);
}
public void setTargetSdkVersion(@Nullable String targetSdkVersion) {
setTargetSdkVersion(getApiVersion(targetSdkVersion));
}
/**
* Sets the target SDK version to the given value.
*
* <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
* uses-sdk element documentation</a>.
*/
public void targetSdkVersion(@Nullable String targetSdkVersion) {
setTargetSdkVersion(targetSdkVersion);
}
/**
* Sets the maximum SDK version to the given value.
*
* <p>See <a href="http://developer.android.com/guide/topics/manifest/uses-sdk-element.html">
* uses-sdk element documentation</a>.
*/
public void maxSdkVersion(int maxSdkVersion) {
setMaxSdkVersion(maxSdkVersion);
}
@Nullable
private static ApiVersion getApiVersion(@Nullable String value) {
if (!Strings.isNullOrEmpty(value)) {
if (Character.isDigit(value.charAt(0))) {
try {
int apiLevel = Integer.valueOf(value);
return new DefaultApiVersion(apiLevel);
} catch (NumberFormatException e) {
throw new RuntimeException("'" + value + "' is not a valid API level. ", e);
}
}
return new DefaultApiVersion(value);
}
return null;
}
/**
* Adds a custom argument to the test instrumentation runner, e.g:
*
* <p>
*
* <pre>testInstrumentationRunnerArgument "size", "medium"</pre>
*
* <p>Test runner arguments can also be specified from the command line:
*
* <p>
*
* <pre>
* ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.size=medium
* ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.foo=bar
* </pre>
*/
public void testInstrumentationRunnerArgument(@NonNull String key, @NonNull String value) {
getTestInstrumentationRunnerArguments().put(key, value);
}
/**
* Adds custom arguments to the test instrumentation runner, e.g:
*
* <p>
*
* <pre>testInstrumentationRunnerArguments(size: "medium", foo: "bar")</pre>
*
* <p>Test runner arguments can also be specified from the command line:
*
* <p>
*
* <pre>
* ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.size=medium
* ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.foo=bar
* </pre>
*/
public void testInstrumentationRunnerArguments(@NonNull Map<String, String> args) {
getTestInstrumentationRunnerArguments().putAll(args);
}
/** Signing config used by this product flavor. */
@Override
@Nullable
public SigningConfig getSigningConfig() {
return (SigningConfig) super.getSigningConfig();
}
// -- DSL Methods. TODO remove once the instantiator does what I expect it to do.
/**
* Adds a new field to the generated BuildConfig class.
*
* <p>The field is generated as: {@code <type> <name> = <value>;}
*
* <p>This means each of these must have valid Java content. If the type is a String, then the
* value should include quotes.
*
* @param type the type of the field
* @param name the name of the field
* @param value the value of the field
*/
public void buildConfigField(
@NonNull String type, @NonNull String name, @NonNull String value) {
ClassField alreadyPresent = getBuildConfigFields().get(name);
if (alreadyPresent != null) {
String flavorName = getName();
if (BuilderConstants.MAIN.equals(flavorName)) {
logger.info(
"DefaultConfig: buildConfigField '{}' value is being replaced: {} -> {}",
name,
alreadyPresent.getValue(),
value);
} else {
logger.info(
"ProductFlavor({}): buildConfigField '{}' "
+ "value is being replaced: {} -> {}",
flavorName,
name,
alreadyPresent.getValue(),
value);
}
}
addBuildConfigField(new ClassFieldImpl(type, name, value));
}
/**
* Adds a new generated resource.
*
* <p>This is equivalent to specifying a resource in res/values.
*
* <p>See <a
* href="http://developer.android.com/guide/topics/resources/available-resources.html">Resource
* Types</a>.
*
* @param type the type of the resource
* @param name the name of the resource
* @param value the value of the resource
*/
public void resValue(@NonNull String type, @NonNull String name, @NonNull String value) {
ClassField alreadyPresent = getResValues().get(name);
if (alreadyPresent != null) {
String flavorName = getName();
if (BuilderConstants.MAIN.equals(flavorName)) {
logger.info(
"DefaultConfig: resValue '{}' value is being replaced: {} -> {}",
name,
alreadyPresent.getValue(),
value);
} else {
logger.info(
"ProductFlavor({}): resValue '{}' value is being replaced: {} -> {}",
flavorName,
name,
alreadyPresent.getValue(),
value);
}
}
addResValue(new ClassFieldImpl(type, name, value));
}
/**
* Specifies a ProGuard configuration file that the plugin should use.
*
* <p>There are two ProGuard rules files that ship with the Android plugin and are used by
* default:
*
* <ul>
* <li>proguard-android.txt
* <li>proguard-android-optimize.txt
* </ul>
*
* <p><code>proguard-android-optimize.txt</code> is identical to <code>proguard-android.txt
* </code>, exccept with optimizations enabled. You can use <code>
* getDefaultProguardFile(String filename)</code> to return the full path of each file.
*/
public void proguardFile(@NonNull Object proguardFile) {
getProguardFiles().add(project.file(proguardFile));
}
/**
* Specifies ProGuard configuration files that the plugin should use.
*
* <p>There are two ProGuard rules files that ship with the Android plugin and are used by
* default:
*
* <ul>
* <li>proguard-android.txt
* <li>proguard-android-optimize.txt
* </ul>
*
* <p><code>proguard-android-optimize.txt</code> is identical to <code>proguard-android.txt
* </code>, exccept with optimizations enabled. You can use <code>
* getDefaultProguardFile(String filename)</code> to return the full path of each file.
*/
public void proguardFiles(@NonNull Object... files) {
for (Object file : files) {
proguardFile(file);
}
}
/**
* Sets the ProGuard configuration files.
*
* <p>There are two ProGuard rules files that ship with the Android plugin and are used by
* default:
*
* <ul>
* <li>proguard-android.txt
* <li>proguard-android-optimize.txt
* </ul>
*
* <p><code>proguard-android-optimize.txt</code> is identical to <code>proguard-android.txt
* </code>, exccept with optimizations enabled. You can use <code>
* getDefaultProguardFile(String filename)</code> to return the full path of the files.
*/
public void setProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
getProguardFiles().clear();
proguardFiles(Iterables.toArray(proguardFileIterable, Object.class));
}
/**
* Adds a proguard rule file to be used when processing test code.
*
* <p>Test code needs to be processed to apply the same obfuscation as was done to main code.
*/
public void testProguardFile(@NonNull Object proguardFile) {
getTestProguardFiles().add(project.file(proguardFile));
}
/**
* Adds proguard rule files to be used when processing test code.
*
* <p>Test code needs to be processed to apply the same obfuscation as was done to main code.
*/
public void testProguardFiles(@NonNull Object... proguardFiles) {
for (Object proguardFile : proguardFiles) {
testProguardFile(proguardFile);
}
}
/**
* Specifies proguard rule files to be used when processing test code.
*
* <p>Test code needs to be processed to apply the same obfuscation as was done to main code.
*/
public void setTestProguardFiles(@NonNull Iterable<?> files) {
getTestProguardFiles().clear();
testProguardFiles(Iterables.toArray(files, Object.class));
}
/**
* Adds a proguard rule file to be included in the published AAR.
*
* <p>This proguard rule file will then be used by any application project that consume the AAR
* (if proguard is enabled).
*
* <p>This allows AAR to specify shrinking or obfuscation exclude rules.
*
* <p>This is only valid for Library project. This is ignored in Application project.
*/
public void consumerProguardFile(@NonNull Object proguardFile) {
getConsumerProguardFiles().add(project.file(proguardFile));
}
/**
* Adds proguard rule files to be included in the published AAR.
*
* <p>This proguard rule file will then be used by any application project that consume the AAR
* (if proguard is enabled).
*
* <p>This allows AAR to specify shrinking or obfuscation exclude rules.
*
* <p>This is only valid for Library project. This is ignored in Application project.
*/
public void consumerProguardFiles(@NonNull Object... proguardFiles) {
for (Object proguardFile : proguardFiles) {
consumerProguardFile(proguardFile);
}
}
/**
* Specifies a proguard rule file to be included in the published AAR.
*
* <p>This proguard rule file will then be used by any application project that consume the AAR
* (if proguard is enabled).
*
* <p>This allows AAR to specify shrinking or obfuscation exclude rules.
*
* <p>This is only valid for Library project. This is ignored in Application project.
*/
public void setConsumerProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
getConsumerProguardFiles().clear();
consumerProguardFiles(Iterables.toArray(proguardFileIterable, Object.class));
}
/** Encapsulates per-variant configurations for the NDK, such as ABI filters. */
public void ndk(Action<NdkOptions> action) {
action.execute(ndkConfig);
}
/**
* Encapsulates per-variant CMake and ndk-build configurations for your external native build.
*
* <p>To learn more, see <a
* href="http://developer.android.com/studio/projects/add-native-code.html#">Add C and C++ Code
* to Your Project</a>.
*/
public void externalNativeBuild(@NonNull Action<ExternalNativeBuildOptions> action) {
action.execute(externalNativeBuildOptions);
}
/**
* Specifies an <a
* href="https://d.android.com/guide/topics/resources/providing-resources.html#AlternativeResources">
* alternative resource</a> to keep.
*
* <p>For example, if you are using a library that includes language resources (such as
* AppCompat or Google Play Services), then your APK includes all translated language strings
* for the messages in those libraries whether the rest of your app is translated to the same
* languages or not. If you'd like to keep only the language that your app officially supports,
* you can specify those languages using the <code>resConfig</code> property, as shown in the
* sample below. Any resources for languages not specified are removed.
*
* <pre>
* android {
* defaultConfig {
* ...
* // Keeps language resources for only the locale specified below.
* resConfig "en"
* }
* }
* </pre>
*
* <p>You can also use this property to filter resources for screen densities. For example,
* specifying <code>hdpi</code> removes all other screen density resources (such as <code>mdpi
* </code>, <code>xhdpi</code>, etc) from the final APK.
*
* <p><b>Note:</b> <code>auto</code> is no longer supported because it created a number of
* issues with multi-module projects. Instead, you should specify the locale that your app
* supports, as shown in the sample above. Android plugin 3.1.0 and higher ignore the <code>auto
* </code> argument, and Gradle packages all string resources your app and its dependencies
* provide.
*
* <p>To learn more, see <a
* href="https://d.android.com/studio/build/shrink-code.html#unused-alt-resources">Remove unused
* alternative resources</a>.
*/
public void resConfig(@NonNull String config) {
addResourceConfiguration(config);
}
/**
* Specifies a list of <a
* href="https://d.android.com/guide/topics/resources/providing-resources.html#AlternativeResources">
* alternative resources</a> to keep.
*
* <p>For example, if you are using a library that includes language resources (such as
* AppCompat or Google Play Services), then your APK includes all translated language strings
* for the messages in those libraries whether the rest of your app is translated to the same
* languages or not. If you'd like to keep only the languages that your app officially supports,
* you can specify those languages using the <code>resConfigs</code> property, as shown in the
* sample below. Any resources for languages not specified are removed.
*
* <pre>
* android {
* defaultConfig {
* ...
* // Keeps language resources for only the locales specified below.
* resConfigs "en", "fr"
* }
* }
* </pre>
*
* <p>You can also use this property to filter resources for screen densities. For example,
* specifying <code>hdpi</code> removes all other screen density resources (such as <code>mdpi
* </code>, <code>xhdpi</code>, etc) from the final APK.
*
* <p><b>Note:</b> <code>auto</code> is no longer supported because it created a number of
* issues with multi-module projects. Instead, you should specify a list of locales that your
* app supports, as shown in the sample above. Android plugin 3.1.0 and higher ignore the <code>
* auto</code> argument, and Gradle packages all string resources your app and its dependencies
* provide.
*
* <p>To learn more, see <a
* href="https://d.android.com/studio/build/shrink-code.html#unused-alt-resources">Remove unused
* alternative resources</a>.
*/
public void resConfigs(@NonNull String... config) {
addResourceConfigurations(config);
}
/**
* Specifies a list of <a
* href="https://d.android.com/guide/topics/resources/providing-resources.html#AlternativeResources">
* alternative resources</a> to keep.
*
* <p>For example, if you are using a library that includes language resources (such as
* AppCompat or Google Play Services), then your APK includes all translated language strings
* for the messages in those libraries whether the rest of your app is translated to the same
* languages or not. If you'd like to keep only the languages that your app officially supports,
* you can specify those languages using the <code>resConfigs</code> property, as shown in the
* sample below. Any resources for languages not specified are removed.
*
* <pre>
* android {
* defaultConfig {
* ...
* // Keeps language resources for only the locales specified below.
* resConfigs "en", "fr"
* }
* }
* </pre>
*
* <p>You can also use this property to filter resources for screen densities. For example,
* specifying <code>hdpi</code> removes all other screen density resources (such as <code>mdpi
* </code>, <code>xhdpi</code>, etc) from the final APK.
*
* <p><b>Note:</b> <code>auto</code> is no longer supported because it created a number of
* issues with multi-module projects. Instead, you should specify a list of locales that your
* app supports, as shown in the sample above. Android plugin 3.1.0 and higher ignore the <code>
* auto</code> argument, and Gradle packages all string resources your app and its dependencies
* provide.
*
* <p>To learn more, see <a
* href="https://d.android.com/studio/build/shrink-code.html#unused-alt-resources">Remove unused
* alternative resources</a>.
*/
public void resConfigs(@NonNull Collection<String> config) {
addResourceConfigurations(config);
}
/** Options for configuration Java compilation. */
@Override
@NonNull
public JavaCompileOptions getJavaCompileOptions() {
return javaCompileOptions;
}
public void javaCompileOptions(@NonNull Action<JavaCompileOptions> action) {
action.execute(javaCompileOptions);
}
/** Options for configuring the shader compiler. */
@NonNull
@Override
public CoreShaderOptions getShaders() {
return shaderOptions;
}
/** Configure the shader compiler options for this product flavor. */
public void shaders(@NonNull Action<ShaderOptions> action) {
action.execute(shaderOptions);
}
/**
* Deprecated equivalent of {@code vectorDrawablesOptions.generatedDensities}.
*
* @deprecated
*/
@Deprecated
@Nullable
public Set<String> getGeneratedDensities() {
return getVectorDrawables().getGeneratedDensities();
}
@Deprecated
public void setGeneratedDensities(@Nullable Iterable<String> densities) {
getVectorDrawables().setGeneratedDensities(densities);
}
/** Configures {@link VectorDrawablesOptions}. */
public void vectorDrawables(Action<VectorDrawablesOptions> action) {
action.execute(getVectorDrawables());
}
/** Options to configure the build-time support for {@code vector} drawables. */
@NonNull
@Override
public VectorDrawablesOptions getVectorDrawables() {
return (VectorDrawablesOptions) super.getVectorDrawables();
}
/**
* Sets whether to enable unbundling mode for embedded wear app.
*
* <p>If true, this enables the app to transition from an embedded wear app to one distributed
* by the play store directly.
*/
public void wearAppUnbundled(@Nullable Boolean wearAppUnbundled) {
setWearAppUnbundled(wearAppUnbundled);
}
}