| // Copyright 2015 The Bazel Authors. All rights reserved. |
| // |
| // 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.google.devtools.build.lib.rules.android; |
| |
| import static com.google.devtools.build.lib.syntax.Type.STRING; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration.EmptyToNullLabelConverter; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment; |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration.LabelConverter; |
| import com.google.devtools.build.lib.analysis.config.BuildOptions; |
| import com.google.devtools.build.lib.analysis.config.ConfigurationEnvironment; |
| import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory; |
| import com.google.devtools.build.lib.analysis.config.FragmentOptions; |
| import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; |
| import com.google.devtools.build.lib.analysis.config.PatchTransition; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.packages.AggregatingAttributeMapper; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.packages.Target; |
| import com.google.devtools.build.lib.rules.cpp.CppConfiguration.DynamicMode; |
| import com.google.devtools.build.lib.rules.cpp.CppOptions.DynamicModeConverter; |
| import com.google.devtools.build.lib.rules.cpp.CppOptions.LibcTopLabelConverter; |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; |
| import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; |
| import com.google.devtools.common.options.Converters; |
| import com.google.devtools.common.options.EnumConverter; |
| import com.google.devtools.common.options.Option; |
| import com.google.devtools.common.options.OptionDocumentationCategory; |
| import com.google.devtools.common.options.OptionEffectTag; |
| import com.google.devtools.common.options.OptionMetadataTag; |
| import java.util.List; |
| import javax.annotation.Nullable; |
| |
| /** Configuration fragment for Android rules. */ |
| @SkylarkModule( |
| name = "android", |
| doc = "A configuration fragment for Android.", |
| category = SkylarkModuleCategory.CONFIGURATION_FRAGMENT |
| ) |
| @Immutable |
| public class AndroidConfiguration extends BuildConfiguration.Fragment { |
| |
| /** |
| * Converter for {@link com.google.devtools.build.lib.rules.android.AndroidConfiguration.ConfigurationDistinguisher} |
| */ |
| public static final class ConfigurationDistinguisherConverter |
| extends EnumConverter<ConfigurationDistinguisher> { |
| public ConfigurationDistinguisherConverter() { |
| super(ConfigurationDistinguisher.class, "Android configuration distinguisher"); |
| } |
| } |
| |
| /** |
| * Converter for {@link ApkSigningMethod}. |
| */ |
| public static final class ApkSigningMethodConverter extends EnumConverter<ApkSigningMethod> { |
| public ApkSigningMethodConverter() { |
| super(ApkSigningMethod.class, "apk signing method"); |
| } |
| } |
| |
| /** |
| * Converter for {@link AndroidManifestMerger} |
| */ |
| public static final class AndroidManifestMergerConverter |
| extends EnumConverter<AndroidManifestMerger> { |
| public AndroidManifestMergerConverter() { |
| super(AndroidManifestMerger.class, "android manifest merger"); |
| } |
| } |
| |
| /** Converter for {@link AndroidAaptVersion} */ |
| public static final class AndroidAaptConverter extends EnumConverter<AndroidAaptVersion> { |
| public AndroidAaptConverter() { |
| super(AndroidAaptVersion.class, "android androidAaptVersion"); |
| } |
| } |
| |
| /** |
| * Value used to avoid multiple configurations from conflicting. |
| * |
| * <p>This is set to {@code ANDROID} in Android configurations and to {@code MAIN} otherwise. This |
| * influences the output directory name: if it didn't, an Android and a non-Android configuration |
| * would conflict if they had the same toolchain identifier. |
| * |
| * <p>Note that this is not just a theoretical concern: even if {@code --crosstool_top} and |
| * {@code --android_crosstool_top} point to different labels, they may end up being redirected to |
| * the same thing, and this is exactly what happens on OSX X. |
| */ |
| public enum ConfigurationDistinguisher { |
| MAIN(null), |
| ANDROID("android"); |
| |
| private final String suffix; |
| |
| private ConfigurationDistinguisher(String suffix) { |
| this.suffix = suffix; |
| } |
| } |
| |
| /** |
| * Which APK signing method to use with the debug key for rules that build APKs. |
| * |
| * <ul> |
| * <li>V1 uses the apksigner attribute from the android_sdk and signs the APK as a JAR. |
| * <li>V2 uses the apksigner attribute from the android_sdk and signs the APK according to the APK |
| * Signing Schema V2 that is only supported on Android N and later. |
| * </ul> |
| */ |
| public enum ApkSigningMethod { |
| V1(true, false), |
| V2(false, true), |
| V1_V2(true, true); |
| |
| private final boolean signV1; |
| private final boolean signV2; |
| |
| ApkSigningMethod(boolean signV1, boolean signV2) { |
| this.signV1 = signV1; |
| this.signV2 = signV2; |
| } |
| |
| /** Whether to JAR sign the APK with the apksigner tool. */ |
| public boolean signV1() { |
| return signV1; |
| } |
| |
| /** Wheter to sign the APK with the apksigner tool with APK Signature Schema V2. */ |
| public boolean signV2() { |
| return signV2; |
| } |
| } |
| |
| /** Types of android manifest mergers. */ |
| public enum AndroidManifestMerger { |
| LEGACY, |
| ANDROID; |
| |
| public static List<String> getAttributeValues() { |
| return ImmutableList.of(LEGACY.name().toLowerCase(), ANDROID.name().toLowerCase(), |
| getRuleAttributeDefault()); |
| } |
| |
| public static String getRuleAttributeDefault() { |
| return "auto"; |
| } |
| |
| public static AndroidManifestMerger fromString(String value) { |
| for (AndroidManifestMerger merger : AndroidManifestMerger.values()) { |
| if (merger.name().equalsIgnoreCase(value)) { |
| return merger; |
| } |
| } |
| return null; |
| } |
| } |
| |
| /** Types of android manifest mergers. */ |
| public enum AndroidAaptVersion { |
| AAPT, |
| AAPT2, |
| AUTO; |
| |
| public static List<String> getAttributeValues() { |
| return ImmutableList.of( |
| AAPT.name().toLowerCase(), AAPT2.name().toLowerCase(), getRuleAttributeDefault()); |
| } |
| |
| public static String getRuleAttributeDefault() { |
| return AUTO.name().toLowerCase(); |
| } |
| |
| public static AndroidAaptVersion fromString(String value) { |
| for (AndroidAaptVersion version : AndroidAaptVersion.values()) { |
| if (version.name().equalsIgnoreCase(value)) { |
| return version; |
| } |
| } |
| return null; |
| } |
| |
| // TODO(corysmith): Move to ApplicationManifest when no longer needed as a public function. |
| @Nullable |
| public static AndroidAaptVersion chooseTargetAaptVersion(RuleContext ruleContext) |
| throws RuleErrorException { |
| if (ruleContext.isLegalFragment(AndroidConfiguration.class)) { |
| boolean hasAapt2 = AndroidSdkProvider.fromRuleContext(ruleContext).getAapt2() != null; |
| AndroidAaptVersion flag = |
| ruleContext.getFragment(AndroidConfiguration.class).getAndroidAaptVersion(); |
| |
| if (ruleContext.getRule().isAttrDefined("aapt_version", STRING)) { |
| // On rules that can choose a version, test attribute then flag choose the aapt version |
| // target. |
| AndroidAaptVersion version = |
| fromString(ruleContext.attributes().get("aapt_version", STRING)); |
| // version is null if the value is "auto" |
| version = version == AndroidAaptVersion.AUTO ? flag : version; |
| |
| if (version == AAPT2 && !hasAapt2) { |
| ruleContext.throwWithRuleError( |
| "aapt2 processing requested but not available on the android_sdk"); |
| return null; |
| } |
| return version == AndroidAaptVersion.AUTO ? AAPT : version; |
| } else { |
| // On rules can't choose, assume aapt2 if aapt2 is present in the sdk. |
| return hasAapt2 ? AAPT2 : AAPT; |
| } |
| } |
| return null; |
| } |
| } |
| |
| /** Android configuration options. */ |
| public static class Options extends FragmentOptions { |
| @Option( |
| name = "Android configuration distinguisher", |
| defaultValue = "MAIN", |
| converter = ConfigurationDistinguisherConverter.class, |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| metadataTags = {OptionMetadataTag.INTERNAL} |
| ) |
| public ConfigurationDistinguisher configurationDistinguisher; |
| |
| // For deploying incremental installation of native libraries. Do not use on the command line. |
| // The idea is that once this option works, we'll flip the default value in a config file, then |
| // once it is proven that it works, remove it from Bazel and said config file. |
| @Option( |
| name = "android_incremental_native_libs", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN} |
| ) |
| public boolean incrementalNativeLibs; |
| |
| @Option( |
| name = "android_crosstool_top", |
| defaultValue = "//external:android/crosstool", |
| category = "semantics", |
| converter = EmptyToNullLabelConverter.class, |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "The location of the C++ compiler used for Android builds." |
| ) |
| public Label androidCrosstoolTop; |
| |
| @Option( |
| name = "android_cpu", |
| defaultValue = "armeabi", |
| category = "semantics", |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "The Android target CPU." |
| ) |
| public String cpu; |
| |
| @Option( |
| name = "android_compiler", |
| defaultValue = "null", |
| category = "semantics", |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "The Android target compiler." |
| ) |
| public String cppCompiler; |
| |
| @Option( |
| name = "android_grte_top", |
| defaultValue = "null", |
| converter = LibcTopLabelConverter.class, |
| category = "semantics", |
| documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY, |
| effectTags = {OptionEffectTag.EXECUTION}, |
| help = "The Android target grte_top." |
| ) |
| public Label androidLibcTopLabel; |
| |
| @Option( |
| name = "android_dynamic_mode", |
| defaultValue = "off", |
| converter = DynamicModeConverter.class, |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "Determines whether C++ deps of Android rules will be linked dynamically when a " |
| + "cc_binary does not explicitly create a shared library. " |
| + "'default' means blaze will choose whether to link dynamically. " |
| + "'fully' means all libraries will be linked dynamically. " |
| + "'off' means that all libraries will be linked in mostly static mode." |
| ) |
| public DynamicMode dynamicMode; |
| |
| // Label of filegroup combining all Android tools used as implicit dependencies of |
| // android_* rules |
| @Option( |
| name = "android_sdk", |
| defaultValue = "@bazel_tools//tools/android:sdk", |
| category = "version", |
| converter = LabelConverter.class, |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Specifies Android SDK/platform that is used to build Android applications." |
| ) |
| public Label sdk; |
| |
| // TODO(bazel-team): Maybe merge this with --android_cpu above. |
| @Option( |
| name = "fat_apk_cpu", |
| converter = Converters.CommaSeparatedOptionListConverter.class, |
| defaultValue = "armeabi-v7a", |
| category = "semantics", |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "Setting this option enables fat APKs, which contain native binaries for all " |
| + "specified target architectures, e.g., --fat_apk_cpu=x86,armeabi-v7a. If this " |
| + "flag is specified, then --android_cpu is ignored for dependencies of " |
| + "android_binary rules." |
| ) |
| public List<String> fatApkCpus; |
| |
| // For desugaring lambdas when compiling Java 8 sources. Do not use on the command line. |
| // The idea is that once this option works, we'll flip the default value in a config file, then |
| // once it is proven that it works, remove it from Bazel and said config file. |
| @Option( |
| name = "desugar_for_android", |
| oldName = "experimental_desugar_for_android", |
| defaultValue = "true", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Whether to desugar Java 8 bytecode before dexing." |
| ) |
| public boolean desugarJava8; |
| |
| // This flag is intended to be flipped globally. |
| @Option( |
| name = "experimental_check_desugar_deps", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Whether to double-check correct desugaring at Android binary level." |
| ) |
| public boolean checkDesugarDeps; |
| |
| @Option( |
| name = "incremental_dexing", |
| defaultValue = "true", |
| category = "semantics", |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Does most of the work for dexing separately for each Jar file." |
| ) |
| public boolean incrementalDexing; |
| |
| /** Whether to look for incrementally dex protos built with java_lite_proto_library. */ |
| // TODO(b/31711689): remove this flag from config files and here |
| @Option( |
| name = "experimental_incremental_dexing_for_lite_protos", |
| defaultValue = "true", |
| category = "experimental", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Do not use." |
| ) |
| public boolean incrementalDexingForLiteProtos; |
| |
| /** |
| * Whether to error out when we find Jar files when building binaries that weren't converted to |
| * a dex archive. This option will soon be removed from Bazel. |
| */ |
| @Option( |
| name = "experimental_incremental_dexing_error_on_missed_jars", |
| defaultValue = "true", |
| category = "experimental", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Do not use." |
| ) |
| public boolean incrementalDexingErrorOnMissedJars; |
| |
| // TODO(b/31711689): Remove this flag when this optimization is proven to work globally. |
| @Option( |
| name = "experimental_android_assume_minsdkversion", |
| defaultValue = "false", |
| metadataTags = {OptionMetadataTag.EXPERIMENTAL}, |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "When enabled, the minSdkVersion is parsed from the merged AndroidManifest and used to " |
| + "instruct Proguard on valid Android build versions." |
| ) |
| public boolean assumeMinSdkVersion; |
| |
| @Option( |
| name = "experimental_android_use_parallel_dex2oat", |
| category = "experimental", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Use dex2oat in parallel to possibly speed up android_test." |
| ) |
| public boolean useParallelDex2Oat; |
| |
| // Do not use on the command line. |
| // This flag is intended to be updated as we add supported flags to the incremental dexing tools |
| @Option( |
| name = "non_incremental_per_target_dexopts", |
| converter = Converters.CommaSeparatedOptionListConverter.class, |
| defaultValue = "--positions", |
| category = "semantics", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "dx flags that that prevent incremental dexing for binary targets that list any of " |
| + "the flags listed here in their 'dexopts' attribute, which are ignored with " |
| + "incremental dexing (superseding --dexopts_supported_in_incremental_dexing). " |
| + "Defaults to --no-locals for safety but can in general be used " |
| + "to make sure the listed dx flags are honored, with additional build latency. " |
| + "Please notify us if you find yourself needing this flag." |
| ) |
| public List<String> nonIncrementalPerTargetDexopts; |
| |
| // Do not use on the command line. |
| // This flag is intended to be updated as we add supported flags to the incremental dexing tools |
| @Option( |
| name = "dexopts_supported_in_incremental_dexing", |
| converter = Converters.CommaSeparatedOptionListConverter.class, |
| defaultValue = "--no-optimize,--no-locals", |
| metadataTags = {OptionMetadataTag.HIDDEN}, |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "dx flags supported when converting Jars to dex archives incrementally." |
| ) |
| public List<String> dexoptsSupportedInIncrementalDexing; |
| |
| // Do not use on the command line. |
| // This flag is intended to be updated as we add supported flags to the incremental dexing tools |
| @Option( |
| name = "dexopts_supported_in_dexmerger", |
| converter = Converters.CommaSeparatedOptionListConverter.class, |
| defaultValue = "--minimal-main-dex,--set-max-idx-number", |
| metadataTags = {OptionMetadataTag.HIDDEN}, |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "dx flags supported in tool that merges dex archives into final classes.dex files." |
| ) |
| public List<String> dexoptsSupportedInDexMerger; |
| |
| @Option( |
| name = "use_workers_with_dexbuilder", |
| defaultValue = "true", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Whether dexbuilder supports being run in local worker mode." |
| ) |
| public boolean useWorkersWithDexbuilder; |
| |
| @Option( |
| name = "experimental_android_rewrite_dexes_with_rex", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "use rex tool to rewrite dex files" |
| ) |
| public boolean useRexToCompressDexFiles; |
| |
| @Option( |
| name = "experimental_allow_android_library_deps_without_srcs", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "Flag to help transition from allowing to disallowing srcs-less android_library" |
| + " rules with deps. The depot needs to be cleaned up to roll this out by default." |
| ) |
| public boolean allowAndroidLibraryDepsWithoutSrcs; |
| |
| @Option( |
| name = "experimental_android_resource_shrinking", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Enables resource shrinking for android_binary APKs that use ProGuard." |
| ) |
| public boolean useExperimentalAndroidResourceShrinking; |
| |
| @Option( |
| name = "android_resource_shrinking", |
| defaultValue = "false", |
| category = "semantics", |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Enables resource shrinking for android_binary APKs that use ProGuard." |
| ) |
| public boolean useAndroidResourceShrinking; |
| |
| @Option( |
| name = "android_manifest_merger", |
| defaultValue = "android", |
| category = "semantics", |
| converter = AndroidManifestMergerConverter.class, |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "Selects the manifest merger to use for android_binary rules. Flag to help the" |
| + "transition to the Android manifest merger from the legacy merger." |
| ) |
| public AndroidManifestMerger manifestMerger; |
| |
| @Option( |
| name = "android_aapt", |
| defaultValue = "aapt", |
| category = "semantics", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| converter = AndroidAaptConverter.class, |
| help = |
| "Selects the version of androidAaptVersion to use for android_binary rules." |
| + "Flag to help the test and transition to aapt2." |
| ) |
| public AndroidAaptVersion androidAaptVersion; |
| |
| @Option( |
| name = "apk_signing_method", |
| converter = ApkSigningMethodConverter.class, |
| defaultValue = "v1_v2", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Implementation to use to sign APKs" |
| ) |
| public ApkSigningMethod apkSigningMethod; |
| |
| @Option( |
| name = "use_singlejar_apkbuilder", |
| defaultValue = "true", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Build Android APKs with SingleJar." |
| ) |
| public boolean useSingleJarApkBuilder; |
| |
| @Option( |
| name = "experimental_android_resource_filtering_method", |
| converter = ResourceFilterFactory.Converter.class, |
| defaultValue = "filter_in_execution", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "Determines when resource filtering attributes, such as the android_binary " |
| + "'resource_configuration_filters' and 'densities' attributes, are applied. " |
| + "By default, bazel will 'filter_in_execution'. The experimental " |
| + "'filter_in_analysis' option instead applies these filters earlier in the build " |
| + "process, with corresponding gains in speed. The experimental " |
| + "'filter_in_analysis_with_dynamic_configuration' option also passes these options " |
| + "to the android_binary's dependencies, which also filter their internal resources " |
| + "in analysis, possibly making the build even faster (especially in systems that " |
| + "do not cache the results of those dependencies)." |
| ) |
| // The ResourceFilterFactory object holds the filtering behavior as well as settings for which |
| // resources should be filtered. The filtering behavior is set from the command line, but the |
| // other settings default to empty and are set or modified via dynamic configuration. |
| public ResourceFilterFactory resourceFilterFactory; |
| |
| @Option( |
| name = "experimental_android_compress_java_resources", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Compress Java resources in APKs" |
| ) |
| public boolean compressJavaResources; |
| |
| @Option( |
| name = "experimental_android_include_library_resource_jars", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "Specifies whether resource JAR files for android_library targets should be included" |
| + " as runtime dependencies. Defaults to the old behavior, including them. These JARs" |
| + " are not nessecary for normal use as all required resources are included in the" |
| + " top-level android_binary resource JAR." |
| ) |
| public boolean includeLibraryResourceJars; |
| |
| @Option( |
| name = "experimental_android_library_exports_manifest_default", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "The default value of the exports_manifest attribute on android_library." |
| ) |
| public boolean exportsManifestDefault; |
| |
| |
| @Option( |
| name = "experimental_android_generate_robolectric_r_class", |
| defaultValue = "true", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = |
| "If passed, R classes will be generated for Robolectric tests. Otherwise, only inherited" |
| + " R classes will be used." |
| ) |
| public boolean generateRobolectricRClass; |
| |
| @Option( |
| name = "experimental_android_throw_on_resource_conflict", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "If passed, resource merge conflicts will be treated as errors instead of warnings" |
| ) |
| public boolean throwOnResourceConflict; |
| |
| @Option( |
| name = "experimental_use_manifest_from_resource_apk", |
| defaultValue = "true", |
| documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, |
| effectTags = {OptionEffectTag.UNKNOWN}, |
| help = "Android library rule will use the AppManifest from the resource APK" |
| + " in the AAR file." |
| ) |
| public boolean useManifestFromResourceApk; |
| |
| @Option( |
| name = "experimental_android_allow_android_resources", |
| defaultValue = "true", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS}, |
| help = "For use in testing before migrating away from android_resources. If false, will" |
| + " fail when non-whitelisted android_resources rules are encountered." |
| ) |
| public boolean allowAndroidResources; |
| |
| @Option( |
| name = "experimental_android_allow_resources_attr", |
| defaultValue = "true", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS}, |
| help = "For use in testing before migrating away from android_resources. If false, will" |
| + " fail when non-whitelisted instances of the 'resources' attribute are encountered." |
| ) |
| public boolean allowResourcesAttr; |
| |
| @Option( |
| name = "experimental_android_inherit_resources_in_tests", |
| defaultValue = "false", |
| documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, |
| effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS}, |
| help = "If true, local_resource_files indicates that resource files should be inherited" |
| + "from deps in android_test targets. Otherwise, resources will not be inherited from" |
| + " deps for those targets." |
| ) |
| public boolean inheritResourcesInTests; |
| |
| @Override |
| public FragmentOptions getHost() { |
| Options host = (Options) super.getHost(); |
| host.androidCrosstoolTop = androidCrosstoolTop; |
| host.sdk = sdk; |
| host.fatApkCpus = ImmutableList.of(); // Fat APK archs don't apply to the host. |
| |
| host.desugarJava8 = desugarJava8; |
| host.checkDesugarDeps = checkDesugarDeps; |
| host.incrementalDexing = incrementalDexing; |
| host.incrementalDexingForLiteProtos = incrementalDexingForLiteProtos; |
| host.incrementalDexingErrorOnMissedJars = incrementalDexingErrorOnMissedJars; |
| host.assumeMinSdkVersion = assumeMinSdkVersion; |
| host.nonIncrementalPerTargetDexopts = nonIncrementalPerTargetDexopts; |
| host.dexoptsSupportedInIncrementalDexing = dexoptsSupportedInIncrementalDexing; |
| host.dexoptsSupportedInDexMerger = dexoptsSupportedInDexMerger; |
| host.useWorkersWithDexbuilder = useWorkersWithDexbuilder; |
| host.manifestMerger = manifestMerger; |
| host.androidAaptVersion = androidAaptVersion; |
| host.allowAndroidLibraryDepsWithoutSrcs = allowAndroidLibraryDepsWithoutSrcs; |
| return host; |
| } |
| |
| @Override |
| public ImmutableList<String> getDefaultsRules() { |
| return ImmutableList.of("android_tools_defaults_jar(name = 'android_jar')"); |
| } |
| } |
| |
| /** |
| * Configuration loader for the Android fragment. |
| */ |
| public static class Loader implements ConfigurationFragmentFactory { |
| @Override |
| public Fragment create(ConfigurationEnvironment env, BuildOptions buildOptions) |
| throws InvalidConfigurationException, InterruptedException { |
| return new AndroidConfiguration(buildOptions.get(Options.class)); |
| } |
| |
| @Override |
| public Class<? extends Fragment> creates() { |
| return AndroidConfiguration.class; |
| } |
| |
| @Override |
| public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() { |
| return ImmutableSet.of(Options.class); |
| } |
| } |
| |
| private final Label sdk; |
| private final String cpu; |
| private final boolean incrementalNativeLibs; |
| private final ConfigurationDistinguisher configurationDistinguisher; |
| private final boolean incrementalDexing; |
| private final boolean incrementalDexingForLiteProtos; |
| private final boolean incrementalDexingErrorOnMissedJars; |
| private final boolean assumeMinSdkVersion; |
| private final ImmutableList<String> dexoptsSupportedInIncrementalDexing; |
| private final ImmutableList<String> targetDexoptsThatPreventIncrementalDexing; |
| private final ImmutableList<String> dexoptsSupportedInDexMerger; |
| private final boolean useWorkersWithDexbuilder; |
| private final boolean desugarJava8; |
| private final boolean checkDesugarDeps; |
| private final boolean useRexToCompressDexFiles; |
| private final boolean allowAndroidLibraryDepsWithoutSrcs; |
| private final boolean useAndroidResourceShrinking; |
| private final AndroidManifestMerger manifestMerger; |
| private final ApkSigningMethod apkSigningMethod; |
| private final boolean useSingleJarApkBuilder; |
| private final ResourceFilterFactory resourceFilterFactory; |
| private final boolean compressJavaResources; |
| private final boolean includeLibraryResourceJars; |
| private final boolean exportsManifestDefault; |
| private final AndroidAaptVersion androidAaptVersion; |
| private final boolean generateRobolectricRClass; |
| private final boolean throwOnResourceConflict; |
| private final boolean useParallelDex2Oat; |
| private final boolean useManifestFromResourceApk; |
| private final boolean allowAndroidResources; |
| private final boolean allowResourcesAttr; |
| private final boolean inheritResourcesInTests; |
| |
| |
| AndroidConfiguration(Options options) throws InvalidConfigurationException { |
| this.sdk = options.sdk; |
| this.incrementalNativeLibs = options.incrementalNativeLibs; |
| this.cpu = options.cpu; |
| this.configurationDistinguisher = options.configurationDistinguisher; |
| this.incrementalDexing = options.incrementalDexing; |
| this.incrementalDexingForLiteProtos = options.incrementalDexingForLiteProtos; |
| this.incrementalDexingErrorOnMissedJars = options.incrementalDexingErrorOnMissedJars; |
| this.assumeMinSdkVersion = options.assumeMinSdkVersion; |
| this.dexoptsSupportedInIncrementalDexing = |
| ImmutableList.copyOf(options.dexoptsSupportedInIncrementalDexing); |
| this.targetDexoptsThatPreventIncrementalDexing = |
| ImmutableList.copyOf(options.nonIncrementalPerTargetDexopts); |
| this.dexoptsSupportedInDexMerger = ImmutableList.copyOf(options.dexoptsSupportedInDexMerger); |
| this.useWorkersWithDexbuilder = options.useWorkersWithDexbuilder; |
| this.desugarJava8 = options.desugarJava8; |
| this.checkDesugarDeps = options.checkDesugarDeps; |
| this.allowAndroidLibraryDepsWithoutSrcs = options.allowAndroidLibraryDepsWithoutSrcs; |
| this.useAndroidResourceShrinking = options.useAndroidResourceShrinking |
| || options.useExperimentalAndroidResourceShrinking; |
| this.manifestMerger = options.manifestMerger; |
| this.apkSigningMethod = options.apkSigningMethod; |
| this.useSingleJarApkBuilder = options.useSingleJarApkBuilder; |
| this.useRexToCompressDexFiles = options.useRexToCompressDexFiles; |
| this.resourceFilterFactory = options.resourceFilterFactory; |
| this.compressJavaResources = options.compressJavaResources; |
| this.includeLibraryResourceJars = options.includeLibraryResourceJars; |
| this.exportsManifestDefault = options.exportsManifestDefault; |
| this.androidAaptVersion = options.androidAaptVersion; |
| this.generateRobolectricRClass = options.generateRobolectricRClass; |
| this.throwOnResourceConflict = options.throwOnResourceConflict; |
| this.useParallelDex2Oat = options.useParallelDex2Oat; |
| this.useManifestFromResourceApk = options.useManifestFromResourceApk; |
| this.allowAndroidResources = options.allowAndroidResources; |
| this.allowResourcesAttr = options.allowResourcesAttr; |
| this.inheritResourcesInTests = options.inheritResourcesInTests; |
| |
| if (!dexoptsSupportedInIncrementalDexing.contains("--no-locals")) { |
| // TODO(bazel-team): Still needed? See DexArchiveAspect |
| throw new InvalidConfigurationException("--dexopts_supported_in_incremental_dexing must " |
| + "include '--no-locals' to enable coverage builds"); |
| } |
| } |
| |
| public String getCpu() { |
| return cpu; |
| } |
| |
| @SkylarkCallable(name = "sdk", structField = true, doc = "Android SDK") |
| public Label getSdk() { |
| return sdk; |
| } |
| |
| public boolean useIncrementalNativeLibs() { |
| return incrementalNativeLibs; |
| } |
| |
| /** |
| * Returns whether to use incremental dexing. |
| */ |
| public boolean useIncrementalDexing() { |
| return incrementalDexing; |
| } |
| |
| /** |
| * Returns whether to look for Jars produced by {@code JavaLiteProtoAspect}. |
| */ |
| public boolean incrementalDexingForLiteProtos() { |
| return incrementalDexingForLiteProtos; |
| } |
| |
| /** |
| * Returns whether to report an error when Jars that weren't converted to dex archives are part |
| * of an android binary. |
| */ |
| public boolean incrementalDexingErrorOnMissedJars() { |
| return incrementalDexingErrorOnMissedJars; |
| } |
| |
| /** |
| * Returns true if an -assumevalues should be generated for Proguard based on the minSdkVersion |
| * of the merged AndroidManifest. |
| */ |
| public boolean assumeMinSdkVersion() { |
| return assumeMinSdkVersion; |
| } |
| |
| /** |
| * dx flags supported in incremental dexing actions. |
| */ |
| public ImmutableList<String> getDexoptsSupportedInIncrementalDexing() { |
| return dexoptsSupportedInIncrementalDexing; |
| } |
| |
| /** |
| * dx flags supported in dexmerger actions. |
| */ |
| public ImmutableList<String> getDexoptsSupportedInDexMerger() { |
| return dexoptsSupportedInDexMerger; |
| } |
| |
| /** |
| * Incremental dexing must not be used for binaries that list any of these flags in their |
| * {@code dexopts} attribute. |
| */ |
| public ImmutableList<String> getTargetDexoptsThatPreventIncrementalDexing() { |
| return targetDexoptsThatPreventIncrementalDexing; |
| } |
| |
| /** Whether to assume the dexbuilder tool supports local worker mode. */ |
| public boolean useWorkersWithDexbuilder() { |
| return useWorkersWithDexbuilder; |
| } |
| |
| public boolean desugarJava8() { |
| return desugarJava8; |
| } |
| |
| public boolean checkDesugarDeps() { |
| return checkDesugarDeps; |
| } |
| |
| public boolean useRexToCompressDexFiles() { |
| return useRexToCompressDexFiles; |
| } |
| |
| public boolean allowSrcsLessAndroidLibraryDeps() { |
| return allowAndroidLibraryDepsWithoutSrcs; |
| } |
| |
| public boolean useAndroidResourceShrinking() { |
| return useAndroidResourceShrinking; |
| } |
| |
| public AndroidAaptVersion getAndroidAaptVersion() { |
| return androidAaptVersion; |
| } |
| |
| public AndroidManifestMerger getManifestMerger() { |
| return manifestMerger; |
| } |
| |
| public ApkSigningMethod getApkSigningMethod() { |
| return apkSigningMethod; |
| } |
| |
| public boolean useSingleJarApkBuilder() { |
| return useSingleJarApkBuilder; |
| } |
| |
| public ResourceFilterFactory getResourceFilterFactory() { |
| return resourceFilterFactory; |
| } |
| |
| public boolean useParallelDex2Oat() { |
| return useParallelDex2Oat; |
| } |
| |
| boolean compressJavaResources() { |
| return compressJavaResources; |
| } |
| |
| public boolean includeLibraryResourceJars() { |
| return includeLibraryResourceJars; |
| } |
| |
| boolean getExportsManifestDefault() { |
| return exportsManifestDefault; |
| } |
| |
| public boolean generateRobolectricRClass() { |
| return generateRobolectricRClass; |
| } |
| |
| boolean throwOnResourceConflict() { |
| return throwOnResourceConflict; |
| } |
| |
| boolean useManifestFromResourceApk() { |
| return useManifestFromResourceApk; |
| } |
| |
| public boolean allowAndroidResources() { |
| return this.allowAndroidResources; |
| } |
| |
| public boolean allowResourcesAttr() { |
| return this.allowResourcesAttr; |
| } |
| |
| public boolean inheritResourcesInTests() { |
| return this.inheritResourcesInTests; |
| } |
| |
| @Override |
| public void addGlobalMakeVariables(ImmutableMap.Builder<String, String> globalMakeEnvBuilder) { |
| globalMakeEnvBuilder.put("ANDROID_CPU", cpu); |
| } |
| |
| @Override |
| public String getOutputDirectoryName() { |
| // We expect this value to be null most of the time - it will only become non-null when a |
| // dynamically configured transition changes the configuration's resource filter object. |
| String resourceFilterSuffix = resourceFilterFactory.getOutputDirectorySuffix(); |
| |
| if (configurationDistinguisher.suffix == null) { |
| return resourceFilterSuffix; |
| } |
| |
| if (resourceFilterSuffix == null) { |
| return configurationDistinguisher.suffix; |
| } |
| |
| return configurationDistinguisher.suffix + "_" + resourceFilterSuffix; |
| } |
| |
| @Nullable |
| @Override |
| public PatchTransition topLevelConfigurationHook(Target toTarget) { |
| return resourceFilterFactory.getTopLevelPatchTransition( |
| toTarget.getAssociatedRule().getRuleClass(), |
| AggregatingAttributeMapper.of(toTarget.getAssociatedRule())); |
| } |
| } |