// 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.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.attr; import static com.google.devtools.build.lib.packages.BuildType.LABEL; import static com.google.devtools.build.lib.packages.BuildType.LABEL_KEYED_STRING_DICT; import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; import static com.google.devtools.build.lib.packages.BuildType.TRISTATE; import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromTemplates; import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; import static com.google.devtools.build.lib.syntax.Type.INTEGER; import static com.google.devtools.build.lib.syntax.Type.STRING; import static com.google.devtools.build.lib.syntax.Type.STRING_DICT; import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; import static com.google.devtools.build.lib.util.FileTypeSet.ANY_FILE; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Lists; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Attribute.AllowedValueSet; import com.google.devtools.build.lib.packages.Attribute.LateBoundLabel; import com.google.devtools.build.lib.packages.Attribute.SplitTransition; import com.google.devtools.build.lib.packages.AttributeMap; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; import com.google.devtools.build.lib.packages.RuleTransitionFactory; import com.google.devtools.build.lib.packages.TriState; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidManifestMerger; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.ConfigurationDistinguisher; import com.google.devtools.build.lib.rules.config.ConfigFeatureFlagProvider; import com.google.devtools.build.lib.rules.cpp.CppOptions; import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; import com.google.devtools.build.lib.rules.java.JavaConfiguration; import com.google.devtools.build.lib.rules.java.JavaSemantics; import com.google.devtools.build.lib.rules.java.ProguardHelper; import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter; import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileType; import java.util.List; /** * Rule definitions for Android rules. */ public final class AndroidRuleClasses { /** Sources generated by a given target, in particular, {@code R.java}. */ public static final SafeImplicitOutputsFunction ANDROID_JAVA_SOURCE_JAR = fromTemplates("%{name}.srcjar"); /** Sources compiled in a given target, excluding {@link #ANDROID_JAVA_SOURCE_JAR}. */ public static final SafeImplicitOutputsFunction ANDROID_LIBRARY_SOURCE_JAR = JavaSemantics.JAVA_LIBRARY_SOURCE_JAR; /** * Compiled sources of a given target, excluding {@link #ANDROID_JAVA_SOURCE_JAR}. This is the * conventional output Jar of any java library target, including android libs. */ public static final SafeImplicitOutputsFunction ANDROID_LIBRARY_CLASS_JAR = JavaSemantics.JAVA_LIBRARY_CLASS_JAR; public static final SafeImplicitOutputsFunction ANDROID_LIBRARY_AAR = fromTemplates("%{name}.aar"); /** * Source Jar for {@link #ANDROID_RESOURCES_CLASS_JAR}, which should be the same as * {@link #ANDROID_JAVA_SOURCE_JAR}. */ public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_SOURCE_JAR = fromTemplates("%{name}_resources-src.jar"); /** Compiled {@link #ANDROID_JAVA_SOURCE_JAR}. */ public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_CLASS_JAR = fromTemplates("%{name}_resources.jar"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_APK = fromTemplates("%{name}.ap_"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_AAPT2_LIBRARY_APK = fromTemplates("%{name}_files/aapt2_library.ap_"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_AAPT2_R_TXT = fromTemplates("%{name}_symbols/r.aapt2.txt"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_AAPT2_SOURCE_JAR = fromTemplates("%{name}_files/%{name}_resources_aapt2-src.jar"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_SHRUNK_APK = fromTemplates("%{name}_shrunk.ap_"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_ZIP = fromTemplates("%{name}_files/resource_files.zip"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCES_SHRUNK_ZIP = fromTemplates("%{name}_files/resource_files_shrunk.zip"); public static final SafeImplicitOutputsFunction ANDROID_RESOURCE_SHRINKER_LOG = fromTemplates("%{name}_files/resource_shrinker.log"); public static final SafeImplicitOutputsFunction ANDROID_INCREMENTAL_RESOURCES_APK = fromTemplates("%{name}_files/incremental.ap_"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_APK = fromTemplates("%{name}.apk"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_INCREMENTAL_APK = fromTemplates("%{name}_incremental.apk"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_UNSIGNED_APK = fromTemplates("%{name}_unsigned.apk"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_DEPLOY_JAR = fromTemplates("%{name}_deploy.jar"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_PROGUARD_JAR = fromTemplates("%{name}_proguard.jar"); public static final SafeImplicitOutputsFunction ANDROID_BINARY_INSTRUMENTED_JAR = fromTemplates("%{name}_instrumented.jar"); public static final SafeImplicitOutputsFunction ANDROID_TEST_FILTERED_JAR = fromTemplates("%{name}_filtered.jar"); public static final SafeImplicitOutputsFunction ANDROID_R_TXT = fromTemplates("%{name}_symbols/R.txt"); public static final SafeImplicitOutputsFunction ANDROID_LOCAL_SYMBOLS = fromTemplates("%{name}_symbols/local.bin"); public static final SafeImplicitOutputsFunction ANDROID_MERGED_SYMBOLS = fromTemplates("%{name}_symbols/merged.bin"); public static final SafeImplicitOutputsFunction ANDROID_COMPILED_SYMBOLS = fromTemplates("%{name}_symbols/symbols.zip"); public static final ImplicitOutputsFunction ANDROID_PROCESSED_MANIFEST = fromTemplates("%{name}_processed_manifest/AndroidManifest.xml"); public static final SafeImplicitOutputsFunction MOBILE_INSTALL_STUB_APPLICATION_MANIFEST = fromTemplates("%{name}_files/mobile_install/AndroidManifest.xml"); public static final SafeImplicitOutputsFunction INSTANT_RUN_STUB_APPLICATION_MANIFEST = fromTemplates("%{name}_files/instant_run/AndroidManifest.xml"); public static final SafeImplicitOutputsFunction FULL_DEPLOY_MARKER = fromTemplates("%{name}_files/full_deploy_marker"); public static final SafeImplicitOutputsFunction INCREMENTAL_DEPLOY_MARKER = fromTemplates("%{name}_files/incremental_deploy_marker"); public static final SafeImplicitOutputsFunction SPLIT_DEPLOY_MARKER = fromTemplates("%{name}_files/split_deploy_marker"); public static final SafeImplicitOutputsFunction MOBILE_INSTALL_ARGS = fromTemplates("%{name}_files/mobile_install_args"); public static final SafeImplicitOutputsFunction APK_MANIFEST = fromTemplates("%{name}_files/apk_manifest"); public static final SafeImplicitOutputsFunction APK_MANIFEST_TEXT = fromTemplates("%{name}_files/apk_manifest_text"); public static final SafeImplicitOutputsFunction DEPLOY_INFO = fromTemplates("%{name}_files/deploy_info.deployinfo.pb"); public static final SafeImplicitOutputsFunction DEPLOY_INFO_INCREMENTAL = fromTemplates("%{name}_files/deploy_info_incremental.deployinfo.pb"); public static final SafeImplicitOutputsFunction DEPLOY_INFO_SPLIT = fromTemplates("%{name}_files/deploy_info_split.deployinfo.pb"); // This needs to be in its own directory because ApkBuilder only has a function (-rf) for source // folders but not source files, and it's easiest to guarantee that nothing gets put beside this // file in the ApkBuilder invocation in this manner public static final SafeImplicitOutputsFunction MOBILE_INSTALL_STUB_APPLICATION_DATA = fromTemplates("%{name}_files/stub_application_data/stub_application_data.txt"); public static final SafeImplicitOutputsFunction DEX_MANIFEST = fromTemplates("%{name}_files/dexmanifest.txt"); public static final SafeImplicitOutputsFunction JAVA_RESOURCES_JAR = fromTemplates("%{name}_files/java_resources.jar"); public static final String MANIFEST_MERGE_TOOL_LABEL = "//tools/android:merge_manifests"; public static final String BUILD_INCREMENTAL_DEXMANIFEST_LABEL = "//tools/android:build_incremental_dexmanifest"; public static final String STUBIFY_MANIFEST_LABEL = "//tools/android:stubify_manifest"; public static final String INCREMENTAL_INSTALL_LABEL = "//tools/android:incremental_install"; public static final String BUILD_SPLIT_MANIFEST_LABEL = "//tools/android:build_split_manifest"; public static final String STRIP_RESOURCES_LABEL = "//tools/android:strip_resources"; public static final String DEFAULT_INCREMENTAL_STUB_APPLICATION = "//tools/android:incremental_stub_application"; public static final String DEFAULT_INCREMENTAL_SPLIT_STUB_APPLICATION = "//tools/android:incremental_split_stub_application"; public static final String DEFAULT_RESOURCES_BUSYBOX = "//tools/android:busybox"; public static final String DEFAULT_SDK = "//tools/android:sdk"; public static final SafeImplicitOutputsFunction ANDROID_DEVICE_USERDATA_IMAGES = fromTemplates("%{name}_images/userdata_images.dat"); public static final SafeImplicitOutputsFunction ANDROID_DEVICE_EMULATOR_METADATA = fromTemplates("%{name}_images/emulator-meta-data.pb"); static final FileType APK = FileType.of(".apk"); /** * The default label of android_sdk option */ public static final class AndroidSdkLabel extends LateBoundLabel { public AndroidSdkLabel(Label androidSdk) { super(androidSdk, AndroidConfiguration.class); } @Override public Label resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) { return configuration.getFragment(AndroidConfiguration.class).getSdk(); } } public static final SplitTransition ANDROID_SPLIT_TRANSITION = new AndroidSplitTransition(); private static final class AndroidSplitTransition implements SplitTransition, SkylarkValue { @Override public boolean defaultsToSelf() { return true; } private static void setCrosstoolToAndroid(BuildOptions output, BuildOptions input) { AndroidConfiguration.Options inputAndroidOptions = input.get(AndroidConfiguration.Options.class); AndroidConfiguration.Options outputAndroidOptions = output.get(AndroidConfiguration.Options.class); CppOptions cppOptions = output.get(CppOptions.class); if (inputAndroidOptions.androidCrosstoolTop != null && !cppOptions.crosstoolTop.equals(inputAndroidOptions.androidCrosstoolTop)) { if (cppOptions.hostCrosstoolTop == null) { cppOptions.hostCrosstoolTop = cppOptions.crosstoolTop; } cppOptions.crosstoolTop = inputAndroidOptions.androidCrosstoolTop; } outputAndroidOptions.configurationDistinguisher = ConfigurationDistinguisher.ANDROID; } @Override public List split(BuildOptions buildOptions) { AndroidConfiguration.Options androidOptions = buildOptions.get(AndroidConfiguration.Options.class); CppOptions cppOptions = buildOptions.get(CppOptions.class); Label androidCrosstoolTop = androidOptions.androidCrosstoolTop; if (androidOptions.fatApkCpus.isEmpty()) { if (androidOptions.cpu.isEmpty() || androidCrosstoolTop == null || androidCrosstoolTop.equals(cppOptions.crosstoolTop)) { return ImmutableList.of(); } else { BuildOptions splitOptions = buildOptions.clone(); splitOptions.get(CppOptions.class).cppCompiler = androidOptions.cppCompiler; splitOptions.get(CppOptions.class).libcTopLabel = androidOptions.androidLibcTopLabel; splitOptions.get(BuildConfiguration.Options.class).cpu = androidOptions.cpu; splitOptions.get(CppOptions.class).dynamicMode = androidOptions.dynamicMode; splitOptions.get(CppOptions.class).glibc = null; setCrosstoolToAndroid(splitOptions, buildOptions); return ImmutableList.of(splitOptions); } } else { ImmutableList.Builder result = ImmutableList.builder(); for (String cpu : ImmutableSortedSet.copyOf(androidOptions.fatApkCpus)) { BuildOptions splitOptions = buildOptions.clone(); // Disable fat APKs for the child configurations. splitOptions.get(AndroidConfiguration.Options.class).fatApkCpus = ImmutableList.of(); // Set the cpu & android_cpu. // TODO(bazel-team): --android_cpu doesn't follow --cpu right now; it should. splitOptions.get(AndroidConfiguration.Options.class).cpu = cpu; splitOptions.get(BuildConfiguration.Options.class).cpu = cpu; splitOptions.get(CppOptions.class).cppCompiler = androidOptions.cppCompiler; splitOptions.get(CppOptions.class).libcTopLabel = androidOptions.androidLibcTopLabel; splitOptions.get(CppOptions.class).dynamicMode = androidOptions.dynamicMode; splitOptions.get(CppOptions.class).glibc = null; setCrosstoolToAndroid(splitOptions, buildOptions); result.add(splitOptions); } return result.build(); } } @Override public boolean isImmutable() { return true; } @Override public void repr(SkylarkPrinter printer) { printer.append("android_common.multi_cpu_configuration"); } } /** * Turns of dynamic resource filtering for non-Android targets. This prevents unnecessary * build graph bloat. For example, there's no point analyzing distinct cc_library tarets for * different resource filter configurations because cc_library semantics doesn't care about * filters. */ public static final RuleTransitionFactory REMOVE_DYNAMIC_RESOURCE_FILTERING = new RuleTransitionFactory() { /** Dependencies of these rule class types need to keep resource filtering info. */ private final ImmutableSet keepFilterRuleClasses = ImmutableSet.of("android_binary", "android_library"); @Override public Attribute.Transition buildTransitionFor(Rule depRule) { return keepFilterRuleClasses.contains(depRule.getRuleClass()) ? null : ResourceFilter.REMOVE_DYNAMICALLY_CONFIGURED_RESOURCE_FILTERING_TRANSITION; } }; public static final FileType ANDROID_IDL = FileType.of(".aidl"); public static final String[] ALLOWED_DEPENDENCIES = { "aar_import", "android_library", "cc_library", "java_import", "java_library", }; public static final boolean hasProguardSpecs(AttributeMap rule) { // The below is a hack to support configurable attributes (proguard_specs seems like // too valuable an attribute to make nonconfigurable, and we don't currently // have the ability to know the configuration when determining implicit outputs). // An IllegalArgumentException gets triggered if the attribute instance is configurable. // We assume, heuristically, that means every configurable value is a non-empty list. // // TODO(bazel-team): find a stronger approach for this. One simple approach is to somehow // receive 'rule' as an AggregatingAttributeMapper instead of a RawAttributeMapper, // check that all possible values are non-empty, and simply don't support configurable // instances that mix empty and non-empty lists. A more ambitious approach would be // to somehow determine implicit outputs after the configuration is known. A third // approach is to refactor the Android rule logic to avoid these dependencies in the // first place. try { return !rule.get("proguard_specs", LABEL_LIST).isEmpty(); } catch (IllegalArgumentException e) { // We assume at this point the attribute instance is configurable. return true; } } public static final SafeImplicitOutputsFunction ANDROID_BINARY_IMPLICIT_OUTPUTS = new SafeImplicitOutputsFunction() { @Override public Iterable getImplicitOutputs(AttributeMap rule) { List functions = Lists.newArrayList(); functions.add(AndroidRuleClasses.ANDROID_BINARY_APK); functions.add(AndroidRuleClasses.ANDROID_BINARY_UNSIGNED_APK); functions.add(AndroidRuleClasses.ANDROID_BINARY_DEPLOY_JAR); if (hasProguardSpecs(rule)) { functions.add(AndroidRuleClasses.ANDROID_BINARY_PROGUARD_JAR); functions.add(JavaSemantics.JAVA_BINARY_PROGUARD_CONFIG); if (ProguardHelper.genProguardMapping(rule)) { functions.add(JavaSemantics.JAVA_BINARY_PROGUARD_MAP); } } return fromFunctions(functions).getImplicitOutputs(rule); } }; public static final SafeImplicitOutputsFunction ANDROID_LIBRARY_IMPLICIT_OUTPUTS = new SafeImplicitOutputsFunction() { @Override public Iterable getImplicitOutputs(AttributeMap attributes) { ImmutableList.Builder implicitOutputs = ImmutableList.builder(); implicitOutputs.add( AndroidRuleClasses.ANDROID_LIBRARY_CLASS_JAR, AndroidRuleClasses.ANDROID_LIBRARY_SOURCE_JAR, AndroidRuleClasses.ANDROID_LIBRARY_AAR); if (LocalResourceContainer.definesAndroidResources(attributes)) { implicitOutputs.add( AndroidRuleClasses.ANDROID_JAVA_SOURCE_JAR, AndroidRuleClasses.ANDROID_R_TXT, AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR); } return fromFunctions(implicitOutputs.build()).getImplicitOutputs(attributes); } }; /** * Definition of the {@code android_sdk} rule. */ public static final class AndroidSdkRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder .requiresConfigurationFragments(JavaConfiguration.class, AndroidConfiguration.class) .setUndocumented() // build_tools_version is assumed to be the latest version if omitted. .add(attr("build_tools_version", STRING)) // This is the Proguard that comes from the --proguard_top attribute. .add(attr(":proguard", LABEL).cfg(HOST).value(JavaSemantics.PROGUARD).exec()) // This is the Proguard in the BUILD file that contains the android_sdk rule. Used when // --proguard_top is not specified. .add(attr("proguard", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add(attr("aapt", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add(attr("aapt2", LABEL).cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add(attr("dx", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add( attr("main_dex_list_creator", LABEL) .mandatory() .cfg(HOST) .allowedFileTypes(ANY_FILE) .exec()) .add(attr("adb", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add(attr("framework_aidl", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE)) .add(attr("aidl", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add(attr("aidl_lib", LABEL).allowedFileTypes(JavaSemantics.JAR)) .add(attr("android_jar", LABEL).mandatory().cfg(HOST).allowedFileTypes(JavaSemantics.JAR)) .add(attr("shrinked_android_jar", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE)) .add(attr("annotations_jar", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE)) .add(attr("main_dex_classes", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE)) .add(attr("apkbuilder", LABEL).cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add(attr("apksigner", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add(attr("zipalign", LABEL).mandatory().cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add(attr("resource_extractor", LABEL).cfg(HOST).allowedFileTypes(ANY_FILE).exec()) .add( attr(":java_toolchain", LABEL) .useOutputLicenses() .allowedRuleClasses("java_toolchain") .value(JavaSemantics.JAVA_TOOLCHAIN)) .advertiseProvider(AndroidSdkProvider.class) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("android_sdk") .ancestors(BaseRuleClasses.BaseRule.class) .factoryClass(AndroidSdk.class) .build(); } } /** * Base class for rule definitions that support resource declarations. */ public static final class AndroidResourceSupportRule implements RuleDefinition { @Override public RuleClass build(RuleClass.Builder builder, final RuleDefinitionEnvironment env) { return builder /* The name of the Android manifest file, normally AndroidManifest.xml. Must be defined if resource_files or assets are defined. */ .add(attr("manifest", LABEL).legacyAllowAnyFileType()) /* The list of resources to be packaged. This is typically a glob of all files under the res directory.
Generated files (from genrules) can be referenced by Label here as well. The only restriction is that the generated outputs must be under the same "res" directory as any other resource files that are included. */ .add(attr("resource_files", LABEL_LIST).legacyAllowAnyFileType()) /* The string giving the path to the files in assets. The pair assets and assets_dir describe packaged assets and either both attributes should be provided or none of them. */ .add(attr("assets_dir", STRING)) /* The list of assets to be packaged. This is typically a glob of all files under the assets directory. You can also reference other rules (any rule that produces files) or exported files in the other packages, as long as all those files are under the assets_dir directory in the corresponding package. */ .add(attr("assets", LABEL_LIST).legacyAllowAnyFileType()) /* Let the compiler inline the constants defined in the generated java sources. This attribute must be set to 0 for all android_library rules used directly by an android_binary, and for any android_binary that has an android_library in its transitive closure. */ .add( attr("inline_constants", BOOLEAN) .undocumented("deprecated noop on library") .value(false)) /* Java package for which java sources will be generated. By default the package is inferred from the directory where the BUILD file containing the rule is. You can specify a different package but this is highly discouraged since it can introduce classpath conflicts with other libraries that will only be detected at runtime. */ .add(attr("custom_package", STRING)) /* If true, this rule processes data binding expressions in layout resources included through the resource_files attribute. Without this setting, data binding expressions produce build failures.

To build an Android app with data binding, you must also do the following:

  1. Set this attribute for all Android rules that transitively depend on this one. This is because dependers inherit the rule's data binding expressions through resource merging. So they also need to build with data binding to parse those expressions.
  2. Add a deps = entry for the data binding runtime library to all targets that set this attribute. The location of this library depends on your depot setup.
*/ .add(attr("enable_data_binding", Type.BOOLEAN)) // The javac annotation processor from Android's data binding library that turns // processed XML expressions into Java code. .add(attr(DataBinding.DATABINDING_ANNOTATION_PROCESSOR_ATTR, BuildType.LABEL) // This has to be a computed default because the annotation processor is a // java_plugin, which means it needs the Jvm configuration fragment. That conflicts // with Android builds that use --experimental_disable_jvm. // TODO(gregce): The Jvm dependency is only needed for the host configuration. // --experimental_disable_jvm is really intended for target configurations without // a JDK. So this case isn't conceptually a conflict. Clean this up so we can remove // this computed default. .value(new Attribute.ComputedDefault("enable_data_binding") { @Override public Object getDefault(AttributeMap rule) { return rule.get("enable_data_binding", Type.BOOLEAN) ? env.getToolsLabel("//tools/android:databinding_annotation_processor") : null; } })) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("$android_resource_support") .type(RuleClassType.ABSTRACT) .ancestors(AndroidRuleClasses.AndroidBaseRule.class) .build(); } } /** * Base class for Android rule definitions. */ public static final class AndroidBaseRule implements RuleDefinition { @Override public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { return builder .add( attr(":android_sdk", LABEL) .allowedRuleClasses("android_sdk", "filegroup") .value(new AndroidSdkLabel(env.getToolsLabel(AndroidRuleClasses.DEFAULT_SDK)))) /* Java compiler plugins to run at compile-time. Every java_plugin specified in the plugins attribute will be run whenever this target is built. Resources generated by the plugin will be included in the result jar of the target. */ .add( attr("plugins", LABEL_LIST) .cfg(HOST) .allowedRuleClasses("java_plugin") .legacyAllowAnyFileType()) .add( attr(":java_plugins", LABEL_LIST) .cfg(HOST) .allowedRuleClasses("java_plugin") .silentRuleClassFilter() .value(JavaSemantics.JAVA_PLUGINS)) /* Extra compiler options for this target. Subject to "Make variable" substitution and Bourne shell tokenization.

These compiler options are passed to javac after the global compiler options.

*/ .add(attr("javacopts", STRING_LIST)) .add( attr("$jarjar_bin", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:jarjar_bin"))) .add( attr("$idlclass", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:IdlClass"))) .add( attr("$desugar_java8_extra_bootclasspath", LABEL) .cfg(HOST) .value(env.getToolsLabel("//tools/android:desugar_java8_extra_bootclasspath"))) .add( attr("$android_resources_busybox", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel(DEFAULT_RESOURCES_BUSYBOX))) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("$android_base") .type(RuleClassType.ABSTRACT) .ancestors(BaseRuleClasses.RuleBase.class) .build(); } } /** * Base class for Android rule definitions that produce binaries. */ public static final class AndroidBinaryBaseRule implements RuleDefinition { private final AndroidNeverlinkAspect androidNeverlinkAspect; private final DexArchiveAspect dexArchiveAspect; public AndroidBinaryBaseRule( AndroidNeverlinkAspect androidNeverlinkAspect, DexArchiveAspect dexArchiveAspect) { this.androidNeverlinkAspect = androidNeverlinkAspect; this.dexArchiveAspect = dexArchiveAspect; } @Override public RuleClass build(RuleClass.Builder builder, final RuleDefinitionEnvironment env) { return builder /* The list of source files that are processed to create the target.

srcs files of type .java are compiled. For readability's sake, it is not good to put the name of a generated .java source file into the srcs. Instead, put the depended-on rule name in the srcs, as described below.

srcs files of type .srcjar are unpacked and compiled. (This is useful if you need to generate a set of .java files with a genrule or build extension.)

This rule currently forces source and class compatibility with Java 6.

*/ .add( attr("srcs", LABEL_LIST) .direct_compile_time_input() .allowedFileTypes(JavaSemantics.JAVA_SOURCE, JavaSemantics.SOURCE_JAR)) /* The list of other libraries to be linked in to the binary target. Permitted library types are: android_library, java_library with android constraint and cc_library wrapping or producing .so native libraries for the Android target platform. */ .override( builder .copy("deps") .cfg(ANDROID_SPLIT_TRANSITION) .allowedRuleClasses(ALLOWED_DEPENDENCIES) .allowedFileTypes() .aspect(androidNeverlinkAspect) .aspect(dexArchiveAspect, DexArchiveAspect.PARAM_EXTRACTOR)) .add( attr("feature_of", LABEL) .allowedRuleClasses("android_binary") .allowedFileTypes() .undocumented("experimental, see b/36226333")) .add( attr("feature_after", LABEL) .allowedRuleClasses("android_binary") .allowedFileTypes() .undocumented("experimental, see b/36226333")) .add( attr("$build_incremental_dexmanifest", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel(BUILD_INCREMENTAL_DEXMANIFEST_LABEL))) .add( attr("$stubify_manifest", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel(STUBIFY_MANIFEST_LABEL))) .add( attr("$shuffle_jars", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:shuffle_jars"))) .add( attr("$dexbuilder", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:dexbuilder"))) .add( attr("$dexmerger", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:dexmerger"))) .add( attr("$merge_dexzips", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:merge_dexzips"))) .add( attr("$incremental_install", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel(INCREMENTAL_INSTALL_LABEL))) .add( attr("$build_split_manifest", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel(BUILD_SPLIT_MANIFEST_LABEL))) .add( attr("$strip_resources", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel(STRIP_RESOURCES_LABEL))) .add( attr("$incremental_stub_application", LABEL) .value(env.getToolsLabel(DEFAULT_INCREMENTAL_STUB_APPLICATION)) .aspect(dexArchiveAspect, DexArchiveAspect.ONLY_DESUGAR_JAVA8)) .add( attr("$incremental_split_stub_application", LABEL) .value(env.getToolsLabel(DEFAULT_INCREMENTAL_SPLIT_STUB_APPLICATION)) .aspect(dexArchiveAspect, DexArchiveAspect.ONLY_DESUGAR_JAVA8)) .add( attr("$desugar", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:desugar_java8"))) .add( attr("$rex_wrapper", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:rex_wrapper"))) /* Additional command-line flags for the dx tool when generating classes.dex. Subject to "Make variable" substitution and Bourne shell tokenization. */ .add(attr("dexopts", STRING_LIST)) /* Number of shards dexing should be decomposed into. This is makes dexing much faster at the expense of app installation and startup time. The larger the binary, the more shards should be used. 25 is a good value to start experimenting with.

Note that each shard will result in at least one dex in the final app. For this reason, setting this to more than 1 is not recommended for release binaries. */ .add(attr("dex_shards", INTEGER).value(1)) /* Force the target to be built with or without incremental dexing, overriding defaults and --incremental_dexing flag. Users should set this attribute to 0 for release binaries (e.g., to avoid accidental usage of --incremental_dexing), since incremental dexing can produce slightly larger artifacts than dx. It is an error to set this attribute to 1 for android_binary and android_test rules that have Proguard enabled, as well as for android_test rules with binary_under_test set. We are working on addressing these shortcomings so please check with us if you run into these limitations. */ .add( attr("incremental_dexing", TRISTATE) // Read by DexArchiveAspect's attribute extractor .nonconfigurable("AspectParameters don't support configurations.")) /* Whether to split code into multiple dex files.
Possible values:

  • native: Split code into multiple dex files when the dex 64K index limit is exceeded. Assumes native platform support for loading multidex classes at runtime. This works with only Android L and newer.
  • legacy: Split code into multiple dex files when the dex 64K index limit is exceeded. Assumes multidex classes are loaded through application code (i.e. no native platform support).
  • manual_main_dex: Split code into multiple dex files when the dex 64K index limit is exceeded. The content of the main dex file needs to be specified by providing a list of classes in a text file using the main_dex_list attribute.
  • off: Compile all code to a single dex file, even if it exceeds the index limit.
*/ .add( attr("multidex", STRING) .allowedValues(new AllowedValueSet(MultidexMode.getValidValues())) .value(MultidexMode.OFF.getAttributeValue())) /* Command line options to pass to the main dex list builder. Use this option to affect the classes included in the main dex list. */ .add(attr("main_dex_list_opts", STRING_LIST)) /* A text file contains a list of class file names. Classes defined by those class files are put in the primary classes.dex. e.g.:
          android/support/multidex/MultiDex$V19.class
          android/support/multidex/MultiDex.class
          android/support/multidex/MultiDexApplication.class
          com/google/common/base/Objects.class
                    
Must be used with multidex="manual_main_dex". */ .add(attr("main_dex_list", LABEL).legacyAllowAnyFileType()) /* Files to be used as the Proguard specifications to determine classes that must be kept in the main dex. Only allowed if the multidex attribute is set to legacy. */ .add(attr("main_dex_proguard_specs", LABEL_LIST).legacyAllowAnyFileType()) /* Files to be used as Proguard specification. This file will describe the set of specifications to be used by Proguard. */ .add(attr("proguard_specs", LABEL_LIST).legacyAllowAnyFileType()) /* Whether to generate Proguard mapping file. The mapping file will be generated only if proguard_specs is specified. This file will list the mapping between the original and obfuscated class, method, and field names.

WARNING: If this attribute is used, the Proguard specification should contain neither -dontobfuscate nor -printmapping.

*/ .add( attr("proguard_generate_mapping", BOOLEAN) .value(false) .nonconfigurable("value is referenced in an ImplicitOutputsFunction")) /* File to be used as a mapping for proguard. A mapping file generated by proguard_generate_mapping to be re-used to apply the same mapping to a new build. */ .add(attr("proguard_apply_mapping", LABEL).legacyAllowAnyFileType()) // TODO(mstaib): Remove this attribute and the matching flag after some cleanup of users .add( attr("legacy_native_support", TRISTATE) .value(TriState.AUTO) .undocumented("No-op, soon to be removed")) .add(attr(":extra_proguard_specs", LABEL_LIST).value(JavaSemantics.EXTRA_PROGUARD_SPECS)) .add( attr(":bytecode_optimizers", LABEL_LIST) .cfg(HOST) .value(JavaSemantics.BYTECODE_OPTIMIZERS)) .add(attr("rewrite_dexes_with_rex", BOOLEAN).value(false).undocumented("experimental")) /* File to be used as a package map for Rex tool that keeps the assignment of classes to dex files of a multi-dex application stable over time. Can only be used when proguard_specs is also specified. When proguard_specs is specified, but a package map isn't or there were changes, Rex suggests an updated package map that can be saved and reused for subsequent builds. */ .add(attr("rex_package_map", LABEL).legacyAllowAnyFileType().undocumented("experimental")) /* Select the manifest merger to use for this rule.
Possible values:
  • manifest_merger = "legacy": Use the legacy manifest merger. Does not allow features of the android merger like placeholder substitution and tools attributes for defining merge behavior. Removes all <uses-permission> and <uses-permission-sdk-23> tags. Performs a tag-level merge.
  • manifest_merger = "android": Use the android manifest merger. Allows features like placeholder substitution and tools attributes for defining merge behavior. Follows the semantics from the documentation except it has been modified to also remove all <uses-permission> and <uses-permission-sdk-23> tags. Performs an attribute-level merge.
  • manifest_merger = "auto": Merger is controlled by the --android_manifest_merger flag.
*/ .add( attr("manifest_merger", STRING) .allowedValues(new AllowedValueSet(AndroidManifestMerger.getAttributeValues())) .value(AndroidManifestMerger.getRuleAttributeDefault())) /* A dictionary of values to be overridden in the manifest. Any instance of ${name} in the manifest will be replaced with the value corresponding to name in this dictionary. applicationId, versionCode, versionName, minSdkVersion, targetSdkVersion and maxSdkVersion will also override the corresponding attributes of the manifest and uses-sdk tags. packageName will be ignored and will be set from either applicationId if specified or the package in manifest. When manifest_merger is set to legacy, only applicationId, versionCode and versionName will have any effect. */ .add(attr("manifest_values", STRING_DICT)) /* Select the version of aapt for this rule.
Possible values:
  • aapt_version = "aapt": Use aapt. This is the current default behaviour, and should be used for production binaries. The android_sdk rule must have an aapt binary to use this option.
  • aapt_version = "aapt2": Use aapt2. This is the new resource packaging system that provides improved incremental resource processing, smaller apks and more. The android_sdk rule must have the aapt2 binary to use this option.
  • aapt_version = "auto": aapt is controlled by the --android_aapt_version flag.
*/ .add( attr("aapt_version", STRING) .undocumented("experimental, b/28819519") .allowedValues(new AllowedValueSet(AndroidAaptVersion.getAttributeValues())) .value(AndroidAaptVersion.getRuleAttributeDefault())) .add( attr(AndroidFeatureFlagSetProvider.FEATURE_FLAG_ATTR, LABEL_KEYED_STRING_DICT) .undocumented("the feature flag feature has not yet been launched") .allowedRuleClasses("config_feature_flag") .allowedFileTypes() .nonconfigurable("defines an aspect of configuration") .mandatoryProviders( ImmutableList.of(ConfigFeatureFlagProvider.id()))) // The resource extractor is used at the binary level to extract java resources from the // deploy jar so that they can be added to the APK. .add( attr("$resource_extractor", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/android:resource_extractor"))) .advertiseProvider(JavaCompilationArgsProvider.class) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("$android_binary_base") .type(RuleClassType.ABSTRACT) .ancestors(AndroidRuleClasses.AndroidBaseRule.class, AndroidResourceSupportRule.class) .build(); } } /** * Semantic options for the dexer's multidex behavior. */ public static enum MultidexMode { // Build dexes with multidex, assuming native platform support for multidex. NATIVE, // Build dexes with multidex and implement support at the application level. LEGACY, // Build dexes with multidex, main dex list needs to be manually specified. MANUAL_MAIN_DEX, // Build all dex code into a single classes.dex file. OFF; /** * Returns the attribute value that specifies this mode. */ public String getAttributeValue() { return toString().toLowerCase(); } /** * Returns the name of the output dex classes file. In multidex mode, this is an archive * of (possibly) multiple files. */ public String getOutputDexFilename() { return this == OFF ? "classes.dex" : "classes.dex.zip"; } /** * Converts an attribute value to a corresponding mode. Returns null on no match. */ public static MultidexMode fromValue(String value) { for (MultidexMode mode : values()) { if (mode.getAttributeValue().equals(value)) { return mode; } } return null; } /** * Enumerates valid values for the "multidex" attribute. */ public static List getValidValues() { List ans = Lists.newArrayList(); for (MultidexMode mode : MultidexMode.values()) { ans.add(mode.getAttributeValue()); } return ans; } } }