| /* |
| * 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.LoggingUtil; |
| import com.android.builder.core.AndroidBuilder; |
| import com.android.builder.core.BuilderConstants; |
| import com.android.builder.core.DefaultApiVersion; |
| import com.android.builder.core.DefaultProductFlavor; |
| import com.android.builder.model.ApiVersion; |
| import com.android.builder.model.ClassField; |
| import com.google.common.base.Strings; |
| import org.gradle.api.Action; |
| import org.gradle.api.Project; |
| import org.gradle.api.logging.Logger; |
| import org.gradle.internal.reflect.Instantiator; |
| |
| import static com.android.build.gradle.tasks.NdkCompile.USE_DEPRECATED_NDK; |
| |
| import java.util.Collection; |
| import java.util.Map; |
| |
| /** |
| * DSL object used to configure product flavors. |
| */ |
| public class ProductFlavor extends DefaultProductFlavor implements CoreProductFlavor { |
| |
| @NonNull |
| protected final Project project; |
| |
| @NonNull |
| protected final Logger logger; |
| |
| @NonNull |
| private final NdkOptions ndkConfig; |
| |
| @Nullable |
| private Boolean useJack; |
| |
| public ProductFlavor(@NonNull String name, |
| @NonNull Project project, |
| @NonNull Instantiator instantiator, |
| @NonNull Logger logger) { |
| super(name); |
| this.project = project; |
| this.logger = logger; |
| ndkConfig = instantiator.newInstance(NdkOptions.class); |
| } |
| |
| @Override |
| @Nullable |
| public CoreNdkOptions getNdkConfig() { |
| return ndkConfig; |
| } |
| |
| 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 targetSdkVersion) { |
| setMaxSdkVersion(targetSdkVersion); |
| } |
| |
| @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> |
| * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest |
| * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,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> |
| * INSTRUMENTATION_TEST_RUNNER_ARGS=size=medium,foo=bar ./gradlew connectedAndroidTest |
| * ./gradlew connectedAndroidTest -Pcom.android.tools.instrumentationTestRunnerArgs=size=medium,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. |
| * |
| * The field is generated as: |
| * |
| * <type> <name> = <value>; |
| * |
| * 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(AndroidBuilder.createClassField(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(AndroidBuilder.createClassField(type, name, value)); |
| } |
| |
| /** |
| * Adds a new ProGuard configuration file. |
| * |
| * <p><code>proguardFile getDefaultProguardFile('proguard-android.txt')</code></p> |
| * |
| * <p>There are 2 default rules files |
| * <ul> |
| * <li>proguard-android.txt |
| * <li>proguard-android-optimize.txt |
| * </ul> |
| * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the |
| * full path to the files. They are identical except for enabling optimizations. |
| */ |
| public void proguardFile(@NonNull Object proguardFile) { |
| getProguardFiles().add(project.file(proguardFile)); |
| } |
| |
| /** |
| * Adds new ProGuard configuration files. |
| * |
| * <p>There are 2 default rules files |
| * <ul> |
| * <li>proguard-android.txt |
| * <li>proguard-android-optimize.txt |
| * </ul> |
| * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the |
| * full path to the files. They are identical except for enabling optimizations. |
| */ |
| public void proguardFiles(@NonNull Object... proguardFileArray) { |
| getProguardFiles().addAll(project.files(proguardFileArray).getFiles()); |
| } |
| |
| /** |
| * Sets the ProGuard configuration files. |
| * |
| * <p>There are 2 default rules files |
| * <ul> |
| * <li>proguard-android.txt |
| * <li>proguard-android-optimize.txt |
| * </ul> |
| * <p>They are located in the SDK. Using <code>getDefaultProguardFile(String filename)</code> will return the |
| * full path to the files. They are identical except for enabling optimizations. |
| */ |
| public void setProguardFiles(@NonNull Iterable<?> proguardFileIterable) { |
| getProguardFiles().clear(); |
| for (Object proguardFile : proguardFileIterable) { |
| getProguardFiles().add(project.file(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 testProguardFile(@NonNull Object proguardFile) { |
| getTestProguardFiles().add(project.file(proguardFile)); |
| } |
| |
| /** |
| * Adds new ProGuard configuration files. |
| */ |
| public void testProguardFiles(Object... proguardFileArray) { |
| getTestProguardFiles().addAll(project.files(proguardFileArray).getFiles()); |
| } |
| |
| public void consumerProguardFiles(Object... proguardFileArray) { |
| getConsumerProguardFiles().addAll(project.files(proguardFileArray).getFiles()); |
| } |
| |
| /** |
| * 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(); |
| for (Object proguardFile : proguardFileIterable) { |
| getConsumerProguardFiles().add(project.file(proguardFile)); |
| } |
| } |
| |
| public void ndk(Action<NdkOptions> action) { |
| action.execute(ndkConfig); |
| if (!project.hasProperty(USE_DEPRECATED_NDK)) { |
| throw new RuntimeException( |
| "Error: NDK integration is deprecated in the current plugin. Consider trying " + |
| "the new experimental plugin. For details, see " + |
| "http://tools.android.com/tech-docs/new-build-system/gradle-experimental. " + |
| "Set \"" + USE_DEPRECATED_NDK + "=true\" in gradle.properties to " + |
| "continue using the current NDK integration."); |
| } |
| } |
| |
| /** |
| * Adds a resource configuration filter. |
| * |
| * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type |
| * but of different value will be ignored from the final packaging of the APK. |
| * |
| * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc... |
| */ |
| public void resConfig(@NonNull String config) { |
| addResourceConfiguration(config); |
| } |
| |
| /** |
| * Adds several resource configuration filters. |
| * |
| * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type |
| * but of different value will be ignored from the final packaging of the APK. |
| * |
| * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc... |
| */ |
| public void resConfigs(@NonNull String... config) { |
| addResourceConfigurations(config); |
| } |
| |
| /** |
| * Adds several resource configuration filters. |
| * |
| * <p>If a qualifier value is passed, then all other resources using a qualifier of the same type |
| * but of different value will be ignored from the final packaging of the APK. |
| * |
| * <p>For instance, specifying 'hdpi', will ignore all resources using mdpi, xhdpi, etc... |
| */ |
| public void resConfigs(@NonNull Collection<String> config) { |
| addResourceConfigurations(config); |
| } |
| |
| /** |
| * Whether the experimental Jack toolchain should be used. |
| * |
| * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a> |
| */ |
| @Override |
| @Nullable |
| public Boolean getUseJack() { |
| return useJack; |
| } |
| |
| /** |
| * Whether the experimental Jack toolchain should be used. |
| * |
| * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a> |
| */ |
| public void setUseJack(Boolean useJack) { |
| this.useJack = useJack; |
| } |
| |
| /** |
| * Whether the experimental Jack toolchain should be used. |
| * |
| * <p>See <a href="http://tools.android.com/tech-docs/jackandjill">Jack and Jill</a> |
| */ |
| public void useJack(Boolean useJack) { |
| setUseJack(useJack); |
| } |
| |
| @Deprecated |
| public void setFlavorDimension(String dimension) { |
| LoggingUtil.displayDeprecationWarning(logger, project, |
| "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " + |
| "it has been replaced by 'dimension'."); |
| setDimension(dimension); |
| } |
| |
| /** |
| * Name of the dimension this product flavor belongs to. Has been replaced by |
| * <code>dimension</code> |
| */ |
| @Deprecated |
| public String getFlavorDimension() { |
| LoggingUtil.displayDeprecationWarning(logger, project, |
| "'flavorDimension' will be removed by Android Gradle Plugin 2.0, " + |
| "it has been replaced by 'dimension'."); |
| return getDimension(); |
| } |
| |
| public void jarJarRuleFile(Object file) { |
| getJarJarRuleFiles().add(project.file(file)); |
| } |
| |
| public void jarJarRuleFiles(Object ...files) { |
| getJarJarRuleFiles().clear(); |
| for (Object file : files) { |
| getJarJarRuleFiles().add(project.file(file)); |
| } |
| } |
| } |