Adds internal data binding support to Bazel, although this
is not yet exposed at the user level.
(https://developer.android.com/topic/libraries/data-binding/index.html).

See comments in DataBinding.java for the high-level overview of
how this works.

This does not yet work with
--experimental_use_parallel_android_resource_processing.

Exposing this at the user level additionally requires:

1) making the data binding support libraries available at an
expected place in the depot

2) Opting in android_binary / android_library rules through a new
"enable_data_binding" attribute.

--
MOS_MIGRATED_REVID=139797558
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
index 118228a..834b1e1 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
@@ -102,10 +102,8 @@
     }
 
     NestedSetBuilder<Artifact> filesBuilder = NestedSetBuilder.stableOrder();
-    ImmutableList<TransitiveInfoCollection> deps = ImmutableList.<TransitiveInfoCollection>copyOf(
-        ruleContext.getPrerequisites("deps", Mode.TARGET));
-    JavaCommon javaCommon = new JavaCommon(
-        ruleContext, javaSemantics, deps, deps, deps);
+    JavaCommon javaCommon =
+        AndroidCommon.createJavaCommonWithAndroidDataBinding(ruleContext, javaSemantics, false);
     javaSemantics.checkRule(ruleContext, javaCommon);
     javaSemantics.checkForProtoLibraryAndJavaProtoLibraryOnSameProto(ruleContext, javaCommon);
 
@@ -225,7 +223,8 @@
           ProguardHelper.getProguardConfigArtifact(ruleContext, ""),
           createMainDexProguardSpec(ruleContext),
           ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST),
-          ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP));
+          ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP),
+          DataBinding.isEnabled(ruleContext) ? DataBinding.getLayoutInfoFile(ruleContext) : null);
       ruleContext.assertNoErrors();
 
       incrementalResourceApk = applicationManifest
@@ -246,7 +245,8 @@
               ProguardHelper.getProguardConfigArtifact(ruleContext, "incremental"),
               null, /* mainDexProguardCfg */
               null, /* manifestOut */
-              null /* mergedResourcesOut */);
+              null, /* mergedResourcesOut */
+              null /* dataBindingInfoZip */);
       ruleContext.assertNoErrors();
 
       instantRunResourceApk = applicationManifest
@@ -266,7 +266,8 @@
               ProguardHelper.getProguardConfigArtifact(ruleContext, "instant_run"),
               null, /* mainDexProguardCfg */
               null, /* manifestOut */
-              null /* mergedResourcesOut */);
+              null /* mergedResourcesOut */,
+              null /* dataBindingInfoZip */);
       ruleContext.assertNoErrors();
 
       splitResourceApk = applicationManifest
@@ -286,7 +287,8 @@
               ProguardHelper.getProguardConfigArtifact(ruleContext, "incremental_split"),
               null, /* mainDexProguardCfg */
               null, /* manifestOut */
-              null /* mergedResourcesOut */);
+              null /* mergedResourcesOut */,
+              null /* dataBindingInfoZip */);
       ruleContext.assertNoErrors();
 
     } else {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
index 455ee02..2105496 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
@@ -22,6 +22,7 @@
 import com.google.devtools.build.lib.analysis.FileProvider;
 import com.google.devtools.build.lib.analysis.FilesToRunProvider;
 import com.google.devtools.build.lib.analysis.OutputGroupProvider;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
 import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
 import com.google.devtools.build.lib.analysis.RuleContext;
@@ -548,6 +549,9 @@
                 idlHelper.getIdlGeneratedJavaSources(),
                 androidSemantics.getJavacArguments(ruleContext))
             .setBootClassPath(bootclasspath);
+    if (DataBinding.isEnabled(ruleContext)) {
+      DataBinding.addAnnotationProcessor(ruleContext, attributes);
+    }
 
     JavaCompilationArtifacts.Builder artifactsBuilder = new JavaCompilationArtifacts.Builder();
     NestedSetBuilder<Artifact> jarsProducedForRuntime = NestedSetBuilder.<Artifact>stableOrder();
@@ -607,8 +611,10 @@
 
   private JavaCompilationHelper initAttributes(
       JavaTargetAttributes.Builder attributes, JavaSemantics semantics) {
-    JavaCompilationHelper helper = new JavaCompilationHelper(
-        ruleContext, semantics, javaCommon.getJavacOpts(), attributes);
+    JavaCompilationHelper helper = new JavaCompilationHelper(ruleContext, semantics,
+        javaCommon.getJavacOpts(), attributes,
+        DataBinding.isEnabled(ruleContext)
+            ? DataBinding.processDeps(ruleContext, attributes) : ImmutableList.<Artifact>of());
 
     helper.addLibrariesToAttributes(javaCommon.targetsTreatedAsDeps(ClasspathType.COMPILE_ONLY));
     attributes.setRuleKind(ruleContext.getRule().getRuleClass());
@@ -961,4 +967,48 @@
     }
     return builder.build();
   }
+
+  /**
+   * Returns a {@link JavaCommon} instance with Android data binding support.
+   *
+   * <p>Binaries need both compile-time and runtime support, while libraries only need compile-time
+   * support.
+   *
+   * <p>No rule needs <i>any</i> support if data binding is disabled.
+   */
+  static JavaCommon createJavaCommonWithAndroidDataBinding(RuleContext ruleContext,
+      JavaSemantics semantics, boolean isLibrary) {
+    boolean useDataBinding = DataBinding.isEnabled(ruleContext);
+
+    ImmutableList<Artifact> srcs =
+        ruleContext.getPrerequisiteArtifacts("srcs", RuleConfiguredTarget.Mode.TARGET).list();
+    if (useDataBinding) {
+      srcs = ImmutableList.<Artifact>builder().addAll(srcs)
+          .add(DataBinding.createAnnotationFile(ruleContext, isLibrary)).build();
+    }
+
+    ImmutableList<TransitiveInfoCollection> compileDeps;
+    ImmutableList<TransitiveInfoCollection> runtimeDeps;
+    ImmutableList<TransitiveInfoCollection> bothDeps;
+
+    if (isLibrary) {
+      compileDeps = JavaCommon.defaultDeps(ruleContext, semantics, ClasspathType.COMPILE_ONLY);
+      if (useDataBinding) {
+        compileDeps = DataBinding.addSupportLibs(ruleContext, compileDeps);
+      }
+      runtimeDeps = JavaCommon.defaultDeps(ruleContext, semantics, ClasspathType.RUNTIME_ONLY);
+      bothDeps = JavaCommon.defaultDeps(ruleContext, semantics, ClasspathType.BOTH);
+    } else {
+      // Binary:
+      List<? extends TransitiveInfoCollection> ruleDeps =
+          ruleContext.getPrerequisites("deps", RuleConfiguredTarget.Mode.TARGET);
+      compileDeps = useDataBinding
+          ? DataBinding.addSupportLibs(ruleContext, ruleDeps)
+          : ImmutableList.<TransitiveInfoCollection>copyOf(ruleDeps);
+      runtimeDeps = compileDeps;
+      bothDeps = compileDeps;
+    }
+
+    return new JavaCommon(ruleContext, semantics, srcs, compileDeps, runtimeDeps, bothDeps);
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
index 9eb88a1..90ad509 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
@@ -64,7 +64,8 @@
             AndroidCommon.collectTransitiveInfo(ruleContext, Mode.TARGET));
     NestedSet<Artifact> transitiveProguardConfigs =
         new ProguardLibrary(ruleContext).collectProguardSpecs();
-    JavaCommon javaCommon = new JavaCommon(ruleContext, javaSemantics);
+    JavaCommon javaCommon =
+        AndroidCommon.createJavaCommonWithAndroidDataBinding(ruleContext, javaSemantics, true);
     javaSemantics.checkRule(ruleContext, javaCommon);
     AndroidCommon androidCommon = new AndroidCommon(javaCommon);
 
@@ -93,8 +94,8 @@
           null, /* proguardCfgOut */
           null, /* mainDexProguardCfg */
           ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST),
-          // This is just to communicate the results from the merge step to the validator step.
-          ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP));
+          ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP),
+          DataBinding.isEnabled(ruleContext) ? DataBinding.getLayoutInfoFile(ruleContext) : null);
       if (ruleContext.hasErrors()) {
         return null;
       }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
index 45ee468..bbbc9b0 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourcesProcessorBuilder.java
@@ -83,6 +83,7 @@
   private String applicationId;
   private String versionName;
   private Artifact symbolsTxt;
+  private Artifact dataBindingInfoZip;
 
   private Artifact manifestOut;
   private Artifact mergedResourcesOut;
@@ -107,6 +108,16 @@
     return this;
   }
 
+  /**
+   * The output zip for resource-processed data binding expressions (i.e. a zip of .xml files).
+   * If null, data binding processing is skipped (and data binding expressions aren't allowed in
+   * layout resources).
+   */
+  public AndroidResourcesProcessorBuilder setDataBindingInfoZip(Artifact zip) {
+    this.dataBindingInfoZip = zip;
+    return this;
+  }
+
   public AndroidResourcesProcessorBuilder withDependencies(ResourceDependencies resourceDeps) {
     this.dependencies = resourceDeps;
     return this;
@@ -287,6 +298,11 @@
       builder.add("--applicationId").add(applicationId);
     }
 
+    if (dataBindingInfoZip != null) {
+      builder.addExecPath("--dataBindingInfoOut", dataBindingInfoZip);
+      outs.add(dataBindingInfoZip);
+    }
+
     if (!Strings.isNullOrEmpty(customJavaPackage)) {
       // Sets an alternative java package for the generated R.java
       // this allows android rules to generate resources outside of the java{,tests} tree.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
index cd7ee9b..3a00d2a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
@@ -313,7 +313,8 @@
         proguardCfg,
         null, /* Artifact mainDexProguardCfg */
         null, /* Artifact manifestOut */
-        null /* Artifact mergedResources */);
+        null /* Artifact mergedResources */,
+        null /* Artifact dataBindingInfoZip */);
   }
 
   /** Packages up the manifest with resource and assets from the LocalResourceContainer. */
@@ -349,7 +350,8 @@
         null, /* Artifact proguardCfg */
         null, /* Artifact mainDexProguardCfg */
         manifestOut,
-        mergedResources);
+        mergedResources,
+        null /* Artifact dataBindingInfoZip */);
   }
 
   /** Packages up the manifest with resource and assets from the rule and dependent resources. */
@@ -368,7 +370,8 @@
       Artifact proguardCfg,
       @Nullable Artifact mainDexProguardCfg,
       Artifact manifestOut,
-      Artifact mergedResources) throws InterruptedException {
+      Artifact mergedResources,
+      Artifact dataBindingInfoZip) throws InterruptedException {
     LocalResourceContainer data = new LocalResourceContainer.Builder(ruleContext)
         .withAssets(
             AndroidCommon.getAssetDir(ruleContext),
@@ -404,7 +407,7 @@
         proguardCfg,
         mainDexProguardCfg,
         manifestOut,
-        mergedResources);
+        mergedResources, dataBindingInfoZip);
   }
 
   private ResourceApk createApk(
@@ -421,7 +424,8 @@
       Artifact proguardCfg,
       @Nullable Artifact mainDexProguardCfg,
       Artifact manifestOut,
-      Artifact mergedResources) throws InterruptedException {
+      Artifact mergedResources,
+      Artifact dataBindingInfoZip) throws InterruptedException {
     ResourceContainer resourceContainer = checkForInlinedResources(
         maybeInlinedResourceContainer,
         resourceDeps.getResources(),  // TODO(bazel-team): Figure out if we really need to check
@@ -484,6 +488,7 @@
               .setDensities(densities)
               .setProguardOut(proguardCfg)
               .setMainDexProguardOut(mainDexProguardCfg)
+              .setDataBindingInfoZip(dataBindingInfoZip)
               .setApplicationId(manifestValues.get("applicationId"))
               .setVersionCode(manifestValues.get("versionCode"))
               .setVersionName(manifestValues.get("versionName"));
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/DataBinding.java b/src/main/java/com/google/devtools/build/lib/rules/android/DataBinding.java
new file mode 100644
index 0000000..7290259
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/DataBinding.java
@@ -0,0 +1,276 @@
+// Copyright 2016 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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Template;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider;
+import com.google.devtools.build.lib.rules.java.JavaTargetAttributes;
+import com.google.devtools.build.lib.syntax.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Support logic for Bazel's
+ * <a href="https://developer.android.com/topic/libraries/data-binding/index.html">data binding</a>
+ * integration.
+ *
+ * <p>In short, data binding in Bazel works as follows:
+ * <ol>
+ *   <li>If a rule enables data binding and has layout resources with data binding expressions,
+ *     resource processing invokes the data binding library to preprocess these expressions, then
+ *     strips them out before feeding the resources into aapt. A separate "layout info" XML file
+ *     gets produced that contains the bindings.</li>
+ *   <li>The data binding annotation processor gets activated on Java compilation. This processor
+ *     reads a custom-generated <code>DataBindingInfo.java</code> which specifies the path to the
+ *     layout info file (as an annotation). The processor reads that file and produces the
+ *     corresponding Java classes that end-user code uses to access the resources.</li>
+ *   <li>The data binding compile-time and runtime support libraries get linked into the binary's
+ *     deploy jar.</li>
+ * </ol>
+ *
+ * <p>For data binding to work, the corresponding support libraries must be checked into the depot
+ * via the implicit dependencies specified inside this class.
+ *
+ * <p>Unless otherwise specified, all methods in this class assume the current rule applies data
+ * binding. Callers can intelligently trigger this logic by checking {@link #isEnabled}.
+ *
+ */
+public final class DataBinding {
+  /**
+   * The rule attribute supplying the data binding runtime/compile-time support libraries.
+   */
+  private static final String DATABINDING_RUNTIME_ATTR = "$databinding_runtime";
+
+  /**
+   * The rule attribute supplying the data binding annotation processor.
+   */
+  private static final String DATABINDING_ANNOTATION_PROCESSOR_ATTR =
+      "$databinding_annotation_processor";
+
+  /**
+   * Should data binding support be enabled for this rule?
+   *
+   * <p>This is true if either the rule or any of its transitive dependencies declares data binding
+   * support in its attributes.
+   *
+   * <p>Data binding incurs additional resource processing and compilation work as well as
+   * additional compile/runtime dependencies. But rules with data binding disabled will fail if
+   * any data binding expressions appear in their layout resources.
+   */
+  public static boolean isEnabled(RuleContext ruleContext) {
+    if (ruleContext.attributes().has("enable_data_binding", Type.BOOLEAN)
+        && ruleContext.attributes().get("enable_data_binding", Type.BOOLEAN)) {
+      return true;
+    } else {
+      return !Iterables.isEmpty(ruleContext.getPrerequisites("deps",
+          RuleConfiguredTarget.Mode.TARGET, UsesDataBindingProvider.class));
+    }
+  }
+
+  /**
+   * Returns the file where data binding's resource processing produces binding xml. For
+   * example, given:
+   *
+   * <pre>{@code
+   *   <layout>
+   *     <data>
+   *       <variable name="foo" type="String" />
+   *     </data>
+   *   </layout>
+   *   <LinearLayout>
+   *     ...
+   *   </LinearLayout>
+   * }
+   * </pre>
+   *
+   * <p>data binding strips out and processes this part:
+   *
+   * <pre>{@code
+   *     <data>
+   *       <variable name="foo" type="String" />
+   *     </data>
+   * }
+   * </pre>
+   *
+   * for each layout file with data binding expressions. Since this may produce multiple
+   * files, outputs are zipped up into a single container.
+   */
+  static Artifact getLayoutInfoFile(RuleContext ruleContext) {
+    // The data binding library expects this to be called "layout-info.zip".
+    return ruleContext.getUniqueDirectoryArtifact("databinding", "layout-info.zip",
+        ruleContext.getBinOrGenfilesDirectory());
+  }
+
+  /**
+   * Adds the support libraries needed to compile/run Java code with data binding.
+   *
+   * <p>This excludes the annotation processor, which is injected separately as a Java plugin
+   * (see {@link #addAnnotationProcessor}).
+   */
+  static ImmutableList<TransitiveInfoCollection> addSupportLibs(RuleContext ruleContext,
+      List<? extends TransitiveInfoCollection> deps) {
+    RuleConfiguredTarget.Mode mode = RuleConfiguredTarget.Mode.TARGET;
+    return ImmutableList.<TransitiveInfoCollection>builder()
+        .addAll(deps)
+        .addAll(ruleContext.getPrerequisites(DATABINDING_RUNTIME_ATTR, mode))
+        .build();
+  }
+
+  /**
+   * Adds data binding's annotation processor as a plugin to the given Java compilation context.
+   *
+   * <p>This, in conjunction with {@link #createAnnotationFile} extends the Java compilation to
+   * translate data binding .xml into corresponding classes.
+   */
+  static void addAnnotationProcessor(RuleContext ruleContext,
+      JavaTargetAttributes.Builder attributes) {
+    JavaPluginInfoProvider plugin = ruleContext.getPrerequisite(
+        DATABINDING_ANNOTATION_PROCESSOR_ATTR, RuleConfiguredTarget.Mode.TARGET,
+        JavaPluginInfoProvider.class);
+    for (String name : plugin.getProcessorClasses()) {
+      // For header compilation (see JavaHeaderCompileAction):
+      attributes.addApiGeneratingProcessorName(name);
+      // For full compilation:
+      attributes.addProcessorName(name);
+    }
+    // For header compilation (see JavaHeaderCompileAction):
+    attributes.addApiGeneratingProcessorPath(plugin.getProcessorClasspath());
+    // For full compilation:
+    attributes.addProcessorPath(plugin.getProcessorClasspath());
+    attributes.addAdditionalOutputs(getMetadataOutputs(ruleContext));
+  }
+
+  /**
+   * Creates and returns the generated Java source that data binding's annotation processor
+   * reads to translate layout info xml (from {@link #getLayoutInfoFile} into the classes that
+   * end user code consumes.
+   */
+  static Artifact createAnnotationFile(RuleContext ruleContext, boolean isLibrary) {
+    Template template =
+        Template.forResource(DataBinding.class, "databinding_annotation_template.txt");
+
+    List<Substitution> subs = new ArrayList<>();
+    subs.add(Substitution.of("%module_package%", AndroidCommon.getJavaPackage(ruleContext)));
+    // TODO(gregce): clarify or remove the sdk root
+    subs.add(Substitution.of("%sdk_root%", "/not/used"));
+    subs.add(Substitution.of("%layout_info_dir%",
+        getLayoutInfoFile(ruleContext).getExecPath().getParentDirectory().toString()));
+    subs.add(Substitution.of("%export_class_list_to%", "/tmp/exported_classes")); // Unused.
+    subs.add(Substitution.of("%is_library%", Boolean.toString(isLibrary)));
+    subs.add(Substitution.of("%min_sdk%", "14")); // TODO(gregce): update this
+
+    Artifact output = ruleContext.getPackageRelativeArtifact(
+        String.format("databinding/%s/DataBindingInfo.java", ruleContext.getLabel().getName()),
+        ruleContext.getConfiguration().getGenfilesDirectory());
+
+    ruleContext.registerAction
+        (new TemplateExpansionAction(ruleContext.getActionOwner(), output, template, subs, false));
+
+    return output;
+  }
+
+  /**
+   * Adds the appropriate {@link UsesDataBindingProvider} for a rule if it should expose one.
+   *
+   * <p>A rule exposes {@link UsesDataBindingProvider} if either it or its deps set
+   * {@code enable_data_binding = 1}.
+   */
+  public static void maybeAddProvider(RuleConfiguredTargetBuilder builder,
+      RuleContext ruleContext) {
+    // Expose the data binding provider if this rule either applies data binding or exports a dep
+    // that applies it.
+    List<Artifact> dataBindingMetadataOutputs = new ArrayList<>();
+    if (DataBinding.isEnabled(ruleContext)) {
+      dataBindingMetadataOutputs.addAll(getMetadataOutputs(ruleContext));
+    }
+    if (ruleContext.getAttribute("exports") != null) {
+      for (UsesDataBindingProvider provider : ruleContext.getPrerequisites("exports",
+          RuleConfiguredTarget.Mode.TARGET, UsesDataBindingProvider.class)) {
+        dataBindingMetadataOutputs.addAll(provider.getMetadataOutputs());
+      }
+    }
+    if (!dataBindingMetadataOutputs.isEmpty()) {
+      // QUESTION(gregce): does a rule need to propagate the metadata outputs of its deps, or do
+      // they get integrated automatically into its own outputs?
+      builder.addProvider(UsesDataBindingProvider.class,
+          new UsesDataBindingProvider(dataBindingMetadataOutputs));
+    }
+  }
+
+  /**
+   * Annotation processing creates the following metadata files that describe how data binding is
+   * applied. The full file paths include prefixes as implemented in {@link #getMetadataOutputs}.
+   */
+  private static final ImmutableList<String> METADATA_OUTPUT_SUFFIXES = ImmutableList.<String>of(
+      "setter_store.bin", "layoutinfo.bin", "br.bin");
+
+  /**
+   * Returns metadata outputs from this rule's annotation processing that describe what it did with
+   * data binding. This is used by parent rules to ensure consistent binding patterns.
+   *
+   * <p>>For example, if an {@code android_binary} depends on an {@code android_library} in a
+   * different package, the {@code android_library}'s version gets packaged with the application
+   * jar, even though (due to resource merging) both modules compile against their own instances.
+   */
+  public static List<Artifact> getMetadataOutputs(RuleContext ruleContext) {
+    ImmutableList.Builder<Artifact> outputs = ImmutableList.<Artifact>builder();
+    String javaPackage = AndroidCommon.getJavaPackage(ruleContext);
+    Label ruleLabel = ruleContext.getRule().getLabel();
+    String pathPrefix =
+        String.format(
+            "_javac/%s/lib%s_classes/%s/%s-",
+            ruleLabel.getName(),
+            ruleLabel.getPackageIdentifier().getPackageFragment().getBaseName(),
+            javaPackage.replace('.', '/'),
+            javaPackage);
+    for (String suffix : METADATA_OUTPUT_SUFFIXES) {
+      outputs.add(ruleContext.getBinArtifact(pathPrefix + suffix));
+    }
+    return outputs.build();
+  }
+
+  /**
+   * Processes deps that also apply data binding.
+   *
+   * @param ruleContext the current rule
+   * @param attributes java compilation attributes. The directories of the deps' metadata outputs
+   *     (see {@link #getMetadataOutputs}) are added to this rule's annotation processor classpath.
+   * @return the deps' metadata outputs. These need to be staged as compilation inputs to the
+   *     current rule.
+   */
+  static ImmutableList<Artifact> processDeps(RuleContext ruleContext,
+      JavaTargetAttributes.Builder attributes) {
+    ImmutableList.Builder<Artifact> dataBindingJavaInputs = ImmutableList.<Artifact>builder();
+    dataBindingJavaInputs.add(DataBinding.getLayoutInfoFile(ruleContext));
+    for (UsesDataBindingProvider p : ruleContext.getPrerequisites("deps",
+        RuleConfiguredTarget.Mode.TARGET, UsesDataBindingProvider.class)) {
+      for (Artifact dataBindingDepMetadata : p.getMetadataOutputs()) {
+        attributes.addProcessorPathDir(dataBindingDepMetadata.getExecPath().getParentDirectory());
+        dataBindingJavaInputs.add(dataBindingDepMetadata);
+      }
+    }
+    return dataBindingJavaInputs.build();
+  }
+}
+
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/UsesDataBindingProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/UsesDataBindingProvider.java
new file mode 100644
index 0000000..7f69dac
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/UsesDataBindingProvider.java
@@ -0,0 +1,40 @@
+// Copyright 2016 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 com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import java.util.Collection;
+
+/**
+ * An Android rule that exposes this enables
+ * <a href="https://developer.android.com/topic/libraries/data-binding/index.html">data binding</a>
+ * on its resource processing and Java compilation.
+ */
+public final class UsesDataBindingProvider implements TransitiveInfoProvider {
+  private final ImmutableList<Artifact> metadataOutputs;
+
+  public UsesDataBindingProvider(Collection<Artifact> metadataOutputs) {
+    this.metadataOutputs = ImmutableList.copyOf(metadataOutputs);
+  }
+
+  /**
+   * Returns the metadata outputs from this rule's annotation processing that describe how it
+   * applies data binding. See {@link DataBinding#getMetadataOutputs} for details.
+   */
+  public ImmutableList<Artifact> getMetadataOutputs() {
+    return metadataOutputs;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding_annotation_template.txt b/src/main/java/com/google/devtools/build/lib/rules/android/databinding_annotation_template.txt
new file mode 100644
index 0000000..f847a20
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding_annotation_template.txt
@@ -0,0 +1,27 @@
+package android.databinding.layouts;
+
+import android.databinding.BindingBuildInfo;
+
+/**
+ * Template for the file that feeds data binding's annotation processor. The
+ * processor reads the values set here to generate .java files that link XML
+ * data binding declarations (from layoutInfoDir) to app code.
+ */
+@BindingBuildInfo(
+  // Setting a random build ID triggers incremental recompiling for the
+  // annotation processor. But Bazel is already incrementally correct, so
+  // this is unnecessary.
+  buildId="not_used_here",
+  modulePackage="%module_package%",
+  sdkRoot="%sdk_root%",
+  // The layout info file's *directory* (not the file itself):
+  layoutInfoDir="%layout_info_dir%",
+  exportClassListTo="%export_class_list_to%",
+  isLibrary=%is_library%,
+  minSdk=%min_sdk%,
+  enableDebugLogs=false,
+  printEncodedError=true
+)
+public class DataBindingInfo {
+  /* This only exists for annotation processing. */
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCommon.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCommon.java
index aeb96b5..9f528c4 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCommon.java
@@ -608,6 +608,14 @@
     return targetsTreatedAsDeps.get(type);
   }
 
+  /**
+   * Returns the default dependencies for the given classpath context.
+   */
+  public static ImmutableList<TransitiveInfoCollection> defaultDeps(RuleContext ruleContext,
+      JavaSemantics semantics, ClasspathType type) {
+    return collectTargetsTreatedAsDeps(ruleContext, semantics, type);
+  }
+
   private static ImmutableList<TransitiveInfoCollection> collectTargetsTreatedAsDeps(
       RuleContext ruleContext, JavaSemantics semantics, ClasspathType type) {
     ImmutableList.Builder<TransitiveInfoCollection> builder = new Builder<>();
@@ -798,8 +806,8 @@
    * @return the value of the neverlink attribute.
    */
   public static final boolean isNeverLink(RuleContext ruleContext) {
-    return ruleContext.getRule().isAttrDefined("neverlink", Type.BOOLEAN) &&
-        ruleContext.attributes().get("neverlink", Type.BOOLEAN);
+    return ruleContext.getRule().isAttrDefined("neverlink", Type.BOOLEAN)
+        && ruleContext.attributes().get("neverlink", Type.BOOLEAN);
   }
 
   private static NestedSet<Artifact> getFilesToCompile(Artifact classJar) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
index 9ae2dbe..77e2b02 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
@@ -66,6 +66,7 @@
   private final List<Artifact> translations = new ArrayList<>();
   private boolean translationsFrozen;
   private final JavaSemantics semantics;
+  private final ImmutableList<Artifact> additionalJavaBaseInputs;
 
   private static final String DEFAULT_ATTRIBUTES_SUFFIX = "";
 
@@ -73,7 +74,8 @@
       ImmutableList<String> javacOpts, JavaTargetAttributes.Builder attributes,
       JavaToolchainProvider javaToolchainProvider,
       NestedSet<Artifact> hostJavabase,
-      Iterable<Artifact> jacocoInstrumentation) {
+      Iterable<Artifact> jacocoInstrumentation,
+      ImmutableList<Artifact> additionalJavaBaseInputs) {
     this.ruleContext = ruleContext;
     this.javaToolchain = javaToolchainProvider;
     this.hostJavabase = hostJavabase;
@@ -82,6 +84,16 @@
     this.customJavacOpts = javacOpts;
     this.customJavacJvmOpts = javaToolchain.getJvmOptions();
     this.semantics = semantics;
+    this.additionalJavaBaseInputs = additionalJavaBaseInputs;
+  }
+
+  public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
+      ImmutableList<String> javacOpts, JavaTargetAttributes.Builder attributes,
+      JavaToolchainProvider javaToolchainProvider,
+      NestedSet<Artifact> hostJavabase,
+      Iterable<Artifact> jacocoInstrumentation) {
+    this(ruleContext, semantics, javacOpts, attributes, javaToolchainProvider, hostJavabase,
+        jacocoInstrumentation, ImmutableList.<Artifact>of());
   }
 
   public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
@@ -93,6 +105,16 @@
   }
 
   public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
+      ImmutableList<String> javacOpts, JavaTargetAttributes.Builder attributes,
+      ImmutableList<Artifact> additionalJavaBaseInputs) {
+    this(ruleContext, semantics, javacOpts, attributes,
+        getJavaToolchainProvider(ruleContext),
+        getHostJavabaseInputsNonStatic(ruleContext),
+        getInstrumentationJars(ruleContext),
+        additionalJavaBaseInputs);
+  }
+
+  public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
       JavaTargetAttributes.Builder attributes) {
     this(ruleContext, semantics, getDefaultJavacOptsFromRule(ruleContext), attributes);
   }
@@ -152,6 +174,7 @@
     builder.setManifestProtoOutput(manifestProtoOutput);
     builder.setGensrcOutputJar(gensrcOutputJar);
     builder.setOutputDepsProto(outputDepsProto);
+    builder.setAdditionalOutputs(attributes.getAdditionalOutputs());
     builder.setMetadata(outputMetadata);
     builder.setInstrumentationJars(jacocoInstrumentation);
     builder.addSourceFiles(attributes.getSourceFiles());
@@ -164,6 +187,7 @@
     builder.setTempDirectory(tempDir(outputJar));
     builder.setClassDirectory(classDir(outputJar));
     builder.addProcessorPaths(attributes.getProcessorPath());
+    builder.addProcessorPathDirs(attributes.getProcessorPathDirs());
     builder.addProcessorNames(attributes.getProcessorNames());
     builder.setStrictJavaDeps(attributes.getStrictJavaDeps());
     builder.setDirectJars(attributes.getDirectJars());
@@ -240,8 +264,8 @@
 
   private boolean shouldInstrumentJar() {
     // TODO(bazel-team): What about source jars?
-    return getConfiguration().isCodeCoverageEnabled() && attributes.hasSourceFiles() &&
-        InstrumentedFilesCollector.shouldIncludeLocalSources(getRuleContext());
+    return getConfiguration().isCodeCoverageEnabled() && attributes.hasSourceFiles()
+        && InstrumentedFilesCollector.shouldIncludeLocalSources(getRuleContext());
   }
 
   private boolean shouldUseHeaderCompilation() {
@@ -302,7 +326,11 @@
     builder.setDirectJars(attributes.getDirectJars());
     builder.setRuleKind(attributes.getRuleKind());
     builder.setTargetLabel(attributes.getTargetLabel());
-      builder.setJavaBaseInputs(hostJavabase);
+    builder.setJavaBaseInputs(
+        NestedSetBuilder
+            .fromNestedSet(hostJavabase)
+            .addAll(additionalJavaBaseInputs)
+            .build());
     builder.setJavacJar(javaToolchain.getJavac());
     builder.build(javaToolchain);
 
@@ -425,8 +453,8 @@
    * targets acting as aliases have to be filtered out.
    */
   private boolean generatesOutputDeps() {
-    return getJavaConfiguration().getGenerateJavaDeps() &&
-        (attributes.hasSourceFiles() || attributes.hasSourceJars());
+    return getJavaConfiguration().getGenerateJavaDeps()
+        && (attributes.hasSourceFiles() || attributes.hasSourceJars());
   }
 
   /**
@@ -466,7 +494,11 @@
     JavaCompileAction.Builder builder = new JavaCompileAction.Builder(ruleContext, semantics);
     builder.setJavaExecutable(
         ruleContext.getHostConfiguration().getFragment(Jvm.class).getJavaExecutable());
-    builder.setJavaBaseInputs(hostJavabase);
+    builder.setJavaBaseInputs(
+        NestedSetBuilder
+            .fromNestedSet(hostJavabase)
+            .addAll(additionalJavaBaseInputs)
+            .build());
     builder.setTargetLabel(ruleContext.getLabel());
     return builder;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
index eac7e6e..d1cc48e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
@@ -594,7 +594,8 @@
    * @param outputJar output jar
    * @param compressJar if true compress the output jar
    * @param outputDepsProto the proto file capturing dependency information
-   * @param processorPath the classpath where javac should search for annotation processors
+   * @param processorPath the classpath files where javac should search for annotation processors
+   * @param processorPathDirs the classpath dirs where javac should search for annotation processors
    * @param processorNames the classes that javac should use as annotation processors
    * @param messages the message files for translation
    * @param resources the set of resources to put into the jar
@@ -614,6 +615,7 @@
       final boolean compressJar,
       final Artifact outputDepsProto,
       final List<Artifact> processorPath,
+      final Set<PathFragment> processorPathDirs,
       final List<String> processorNames,
       final Collection<Artifact> messages,
       final Map<PathFragment, Artifact> resources,
@@ -660,8 +662,13 @@
           }
           result.add(Joiner.on(pathSeparator).join(extdirs));
         }
-        if (!processorPath.isEmpty()) {
-          result.addJoinExecPaths("--processorpath", pathSeparator, processorPath);
+        if (!processorPath.isEmpty() || !processorPathDirs.isEmpty()) {
+          ImmutableList.Builder<String> execPathStrings = ImmutableList.<String>builder();
+          execPathStrings.addAll(Artifact.toExecPaths(processorPath));
+          for (PathFragment processorPathDir : processorPathDirs) {
+            execPathStrings.add(processorPathDir.toString());
+          }
+          result.addJoinStrings("--processorpath", pathSeparator, execPathStrings.build());
         }
         if (!processorNames.isEmpty()) {
           result.add("--processors", processorNames);
@@ -927,6 +934,7 @@
     private Artifact gensrcOutputJar;
     private Artifact manifestProtoOutput;
     private Artifact outputDepsProto;
+    private Collection<Artifact> additionalOutputs;
     private Artifact paramFile;
     private Artifact metadata;
     private final Collection<Artifact> sourceFiles = new ArrayList<>();
@@ -953,6 +961,7 @@
     private PathFragment tempDirectory;
     private PathFragment classDirectory;
     private final List<Artifact> processorPath = new ArrayList<>();
+    private final Set<PathFragment> processorPathDirs = new LinkedHashSet<>();
     private final List<String> processorNames = new ArrayList<>();
     private String ruleKind;
     private Label targetLabel;
@@ -1039,12 +1048,18 @@
       Preconditions.checkState(javaExecutable.isAbsolute() ^ !javabaseInputs.isEmpty(),
           javaExecutable);
 
-      ArrayList<Artifact> outputs = new ArrayList<>(Collections2.filter(Arrays.asList(
-          outputJar,
-          metadata,
-          gensrcOutputJar,
-          manifestProtoOutput,
-          outputDepsProto), Predicates.notNull()));
+      ImmutableList.Builder<Artifact> outputsBuilder = ImmutableList.<Artifact>builder()
+          .addAll(
+              new ArrayList<>(Collections2.filter(Arrays.asList(
+                  outputJar,
+                  metadata,
+                  gensrcOutputJar,
+                  manifestProtoOutput,
+                  outputDepsProto), Predicates.notNull())));
+      if (additionalOutputs != null) {
+        outputsBuilder.addAll(additionalOutputs);
+      }
+      ImmutableList<Artifact> outputs = outputsBuilder.build();
 
       CustomMultiArgv commonJavaBuilderArgs = commonJavaBuilderArgs(
           semantics,
@@ -1057,6 +1072,7 @@
           compressJar,
           outputDepsProto,
           processorPath,
+          processorPathDirs,
           processorNames,
           translations,
           resources,
@@ -1180,6 +1196,11 @@
       return this;
     }
 
+    public Builder setAdditionalOutputs(Collection<Artifact> outputs) {
+      this.additionalOutputs = outputs;
+      return this;
+    }
+
     public Builder setMetadata(Artifact metadata) {
       this.metadata = metadata;
       return this;
@@ -1293,6 +1314,11 @@
       return this;
     }
 
+    public Builder addProcessorPathDirs(Collection<PathFragment> processorPathDirs) {
+      this.processorPathDirs.addAll(processorPathDirs);
+      return this;
+    }
+
     public Builder addProcessorNames(Collection<String> processorNames) {
       this.processorNames.addAll(processorNames);
       return this;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaTargetAttributes.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaTargetAttributes.java
index 3004755..4f996ad 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaTargetAttributes.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaTargetAttributes.java
@@ -70,6 +70,9 @@
     private final List<Artifact> nativeLibraries = new ArrayList<>();
 
     private final Set<Artifact> processorPath = new LinkedHashSet<>();
+    // Classpath directories can't be represented as artifacts (TreeArtifact isn't appropriate
+    // here since all we need is a path string to apply to the command line).
+    private final Set<PathFragment> processorPathDirs = new LinkedHashSet<>();
     private final Set<String> processorNames = new LinkedHashSet<>();
 
     private final Set<Artifact> apiGeneratingProcessorPath = new LinkedHashSet<>();
@@ -82,6 +85,8 @@
 
     private final List<Artifact> classPathResources = new ArrayList<>();
 
+    private final Set<Artifact> additionalOutputs = new LinkedHashSet<>();
+
     private BuildConfiguration.StrictDepsMode strictJavaDeps =
         BuildConfiguration.StrictDepsMode.OFF;
     private final NestedSetBuilder<Artifact> directJars = NestedSetBuilder.naiveLinkOrder();
@@ -302,6 +307,12 @@
       return this;
     }
 
+    public Builder addProcessorPathDir(PathFragment dir) {
+      Preconditions.checkArgument(!built);
+      processorPathDirs.add(dir);
+      return this;
+    }
+
     public Builder addApiGeneratingProcessorName(String processor) {
       Preconditions.checkArgument(!built);
       apiGeneratingProcessorNames.add(processor);
@@ -326,6 +337,15 @@
       return this;
     }
 
+    /**
+     * Adds additional outputs to this target's compile action.
+     */
+    public Builder addAdditionalOutputs(Iterable<Artifact> outputs) {
+      Preconditions.checkArgument(!built);
+      Iterables.addAll(additionalOutputs, outputs);
+      return this;
+    }
+
     public JavaTargetAttributes build() {
       built = true;
       return new JavaTargetAttributes(
@@ -336,6 +356,7 @@
           bootClassPath,
           nativeLibraries,
           processorPath,
+          processorPathDirs,
           processorNames,
           apiGeneratingProcessorPath,
           apiGeneratingProcessorNames,
@@ -343,6 +364,7 @@
           messages,
           sourceJars,
           classPathResources,
+          additionalOutputs,
           directJars.build(),
           compileTimeDependencyArtifacts,
           ruleKind,
@@ -387,6 +409,7 @@
   private final ImmutableList<Artifact> nativeLibraries;
 
   private final ImmutableSet<Artifact> processorPath;
+  private final ImmutableSet<PathFragment> processorPathDirs;
   private final ImmutableSet<String> processorNames;
 
   private final ImmutableSet<Artifact> apiGeneratingProcessorPath;
@@ -398,6 +421,8 @@
 
   private final ImmutableList<Artifact> classPathResources;
 
+  private final ImmutableSet<Artifact> additionalOutputs;
+
   private final NestedSet<Artifact> directJars;
   private final ImmutableList<Artifact> compileTimeDependencyArtifacts;
   private final String ruleKind;
@@ -415,6 +440,7 @@
       List<Artifact> bootClassPath,
       List<Artifact> nativeLibraries,
       Set<Artifact> processorPath,
+      Set<PathFragment> processorPathDirs,
       Set<String> processorNames,
       Set<Artifact> apiGeneratingProcessorPath,
       Set<String> apiGeneratingProcessorNames,
@@ -422,6 +448,7 @@
       List<Artifact> messages,
       List<Artifact> sourceJars,
       List<Artifact> classPathResources,
+      Set<Artifact> additionalOutputs,
       NestedSet<Artifact> directJars,
       List<Artifact> compileTimeDependencyArtifacts,
       String ruleKind,
@@ -440,6 +467,7 @@
     this.bootClassPath = ImmutableList.copyOf(bootClassPath);
     this.nativeLibraries = ImmutableList.copyOf(nativeLibraries);
     this.processorPath = ImmutableSet.copyOf(processorPath);
+    this.processorPathDirs = ImmutableSet.copyOf(processorPathDirs);
     this.processorNames = ImmutableSet.copyOf(processorNames);
     this.apiGeneratingProcessorPath = ImmutableSet.copyOf(apiGeneratingProcessorPath);
     this.apiGeneratingProcessorNames = ImmutableSet.copyOf(apiGeneratingProcessorNames);
@@ -447,6 +475,7 @@
     this.messages = ImmutableList.copyOf(messages);
     this.sourceJars = ImmutableList.copyOf(sourceJars);
     this.classPathResources = ImmutableList.copyOf(classPathResources);
+    this.additionalOutputs = ImmutableSet.copyOf(additionalOutputs);
     this.compileTimeDependencyArtifacts = ImmutableList.copyOf(compileTimeDependencyArtifacts);
     this.ruleKind = ruleKind;
     this.targetLabel = targetLabel;
@@ -478,6 +507,10 @@
     return classPathResources;
   }
 
+  public ImmutableSet<Artifact> getAdditionalOutputs() {
+    return additionalOutputs;
+  }
+
   private NestedSet<Artifact> getExcludedArtifacts() {
     return excludedArtifacts;
   }
@@ -520,6 +553,10 @@
     return processorPath;
   }
 
+  public ImmutableSet<PathFragment> getProcessorPathDirs() {
+    return processorPathDirs;
+  }
+
   public Collection<Artifact> getApiGeneratingProcessorPath() {
     return apiGeneratingProcessorPath;
   }