Update Android rules for Databinding V2.
RELNOTES[NEW]: Android Databinding v2 can be enabled with --experimental_android_databinding_v2.
PiperOrigin-RevId: 221710069
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index 113d995..f1306a1 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -731,6 +731,11 @@
return getUniqueDirectoryArtifact(uniqueDirectorySuffix, relative, getBinOrGenfilesDirectory());
}
+ @Override
+ public Artifact getUniqueDirectoryArtifact(String uniqueDirectorySuffix, PathFragment relative) {
+ return getUniqueDirectoryArtifact(uniqueDirectorySuffix, relative, getBinOrGenfilesDirectory());
+ }
+
/**
* Creates an artifact in a directory that is unique to the rule, thus guaranteeing that it never
* clashes with artifacts created by other rules.
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionConstructionContext.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionConstructionContext.java
index 2ce75c5..ce9c327 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionConstructionContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/ActionConstructionContext.java
@@ -81,6 +81,30 @@
Artifact getUniqueDirectoryArtifact(String uniqueDirectorySuffix, String relative);
/**
+ * Creates an artifact in a directory that is unique to the rule, thus guaranteeing that it never
+ * clashes with artifacts created by other rules.
+ *
+ * @param uniqueDirectorySuffix suffix of the directory - it will be prepended
+ */
+ Artifact getUniqueDirectoryArtifact(String uniqueDirectorySuffix, PathFragment relative);
+
+ /**
+ * Returns a path fragment qualified by the rule name and unique fragment to
+ * disambiguate artifacts produced from the source file appearing in
+ * multiple rules.
+ *
+ * <p>For example "pkg/dir/name" -> "pkg/<fragment>/rule/dir/name.
+ */
+ public PathFragment getUniqueDirectory(PathFragment fragment);
+
+ /**
+ * Returns the root of either the "bin" or "genfiles" tree, based on this target and the current
+ * configuration. The choice of which tree to use is based on the rule with which this target
+ * (which must be an OutputFile or a Rule) is associated.
+ */
+ public ArtifactRoot getBinOrGenfilesDirectory();
+
+ /**
* Returns the root-relative path fragment under which output artifacts of this rule should go.
*
* <p>Note that:
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 0ac3bfa..5a97842 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
@@ -878,10 +878,17 @@
JavaSemantics semantics,
DataBindingContext dataBindingContext,
boolean isLibrary) {
- ImmutableList<Artifact> srcs =
- dataBindingContext.addAnnotationFileToSrcs(
- ruleContext.getPrerequisiteArtifacts("srcs", RuleConfiguredTarget.Mode.TARGET).list(),
- ruleContext);
+
+ ImmutableList<Artifact> ruleSources =
+ ruleContext.getPrerequisiteArtifacts("srcs", RuleConfiguredTarget.Mode.TARGET).list();
+
+ ImmutableList<Artifact> dataBindingSources =
+ dataBindingContext.getAnnotationSourceFiles(ruleContext);
+
+ ImmutableList<Artifact> srcs = ImmutableList.<Artifact>builder()
+ .addAll(ruleSources)
+ .addAll(dataBindingSources)
+ .build();
ImmutableList<TransitiveInfoCollection> compileDeps;
ImmutableList<TransitiveInfoCollection> runtimeDeps;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingProcessorBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingProcessorBuilder.java
new file mode 100644
index 0000000..af31cf9
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingProcessorBuilder.java
@@ -0,0 +1,98 @@
+// Copyright 2018 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.actions.ArtifactRoot;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+/** Builder for creating databinding processing action. */
+public class AndroidDataBindingProcessorBuilder {
+
+ /**
+ * Creates and registers an action to strip databinding from layout xml and generate the layout
+ * info file.
+ *
+ * @param dataContext The android data context.
+ * @param androidResources The resources to process.
+ * @param appId The app id (the app's java package).
+ * @param dataBindingLayoutInfoOut The output layout info file to write.
+ * @return The new AndroidResources that has been processed by databinding.
+ */
+ public static AndroidResources create(
+ AndroidDataContext dataContext,
+ AndroidResources androidResources,
+ String appId,
+ Artifact dataBindingLayoutInfoOut) {
+
+ ImmutableList.Builder<Artifact> databindingProcessedResourcesBuilder = ImmutableList.builder();
+ for (Artifact resource : androidResources.getResources()) {
+
+ // Create resources that will be processed by databinding under paths that look like:
+ //
+ // <bazel-pkg>/databinding-processed-resources/<rule-name>/<bazal-pkg>/<resource-dir>
+
+ Artifact databindingProcessedResource =
+ dataContext.getUniqueDirectoryArtifact("databinding-processed-resources",
+ resource.getRootRelativePath());
+
+ databindingProcessedResourcesBuilder.add(databindingProcessedResource);
+ }
+ ImmutableList<Artifact> databindingProcessedResources =
+ databindingProcessedResourcesBuilder.build();
+
+ BusyBoxActionBuilder builder = BusyBoxActionBuilder.create(dataContext, "PROCESS_DATABINDING");
+
+ // Create output resource roots that correspond to the paths of the resources created above:
+ //
+ // <bazel-pkg>/databinding-processed-resources/<rule-name>/<resource-root>
+ //
+ // AndroidDataBindingProcessingAction will append each value of --resource_root to its
+ // corresponding --output_resource_root, so the only part that needs to be constructed here is
+ //
+ // <bazel-pkg>/databinding-processed-resources/<rule-name>
+ ArtifactRoot binOrGenfiles = dataContext.getBinOrGenfilesDirectory();
+ PathFragment uniqueDir =
+ dataContext.getUniqueDirectory(PathFragment.create("databinding-processed-resources"));
+ PathFragment outputResourceRoot = binOrGenfiles.getExecPath().getRelative(uniqueDir);
+
+ ImmutableList.Builder<PathFragment> outputResourceRootsBuilder = ImmutableList.builder();
+ for (PathFragment resourceRoot : androidResources.getResourceRoots()) {
+
+ outputResourceRootsBuilder.add(outputResourceRoot);
+
+ // The order of these matter, the input root and the output root have to be matched up
+ // because the resource processor will iterate over them in pairs.
+ builder.addFlag("--resource_root", resourceRoot.toString());
+ builder.addFlag("--output_resource_root", outputResourceRoot.toString());
+ }
+
+ // Even though the databinding processor really only cares about layout files, we send
+ // all the resources so that the new resource root that is created for databinding processing
+ // can be used for later processing (e.g. aapt). It would be nice to send only the layout
+ // files, but then we'd have to mix roots and rely on sandboxing to "hide" the
+ // old unprocessed files, which might not work if, for example, the actions run locally.
+ builder.addInputs(androidResources.getResources());
+
+ builder.addOutputs(databindingProcessedResources);
+
+ builder.addOutput("--dataBindingInfoOut", dataBindingLayoutInfoOut);
+ builder.addFlag("--appId", appId);
+
+ builder.buildAndRegister("Processing data binding", "ProcessDatabinding");
+
+ return new AndroidResources(databindingProcessedResources, outputResourceRootsBuilder.build());
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataContext.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataContext.java
index 45463ac..aa65bc2 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDataContext.java
@@ -15,6 +15,7 @@
import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ArtifactRoot;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
@@ -24,6 +25,7 @@
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction;
import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidDataContextApi;
+import com.google.devtools.build.lib.vfs.PathFragment;
/**
* Wraps common tools and settings used for working with Android assets, resources, and manifests.
@@ -37,26 +39,30 @@
* are used in BusyBox actions.
*/
public class AndroidDataContext implements AndroidDataContextApi {
+
private final Label label;
private final ActionConstructionContext actionConstructionContext;
private final FilesToRunProvider busybox;
private final AndroidSdkProvider sdk;
private final boolean persistentBusyboxToolsEnabled;
+ private final boolean useDataBindingV2;
public static AndroidDataContext forNative(RuleContext ruleContext) {
return makeContext(ruleContext);
}
public static AndroidDataContext makeContext(RuleContext ruleContext) {
+ AndroidConfiguration androidConfig = ruleContext
+ .getConfiguration()
+ .getFragment(AndroidConfiguration.class);
+
return new AndroidDataContext(
ruleContext.getLabel(),
ruleContext,
ruleContext.getExecutablePrerequisite("$android_resources_busybox", Mode.HOST),
- ruleContext
- .getConfiguration()
- .getFragment(AndroidConfiguration.class)
- .persistentBusyboxTools(),
- AndroidSdkProvider.fromRuleContext(ruleContext));
+ androidConfig.persistentBusyboxTools(),
+ AndroidSdkProvider.fromRuleContext(ruleContext),
+ androidConfig.useDataBindingV2());
}
protected AndroidDataContext(
@@ -64,12 +70,14 @@
ActionConstructionContext actionConstructionContext,
FilesToRunProvider busybox,
boolean persistentBusyboxToolsEnabled,
- AndroidSdkProvider sdk) {
+ AndroidSdkProvider sdk,
+ boolean useDataBindingV2) {
this.label = label;
this.persistentBusyboxToolsEnabled = persistentBusyboxToolsEnabled;
this.actionConstructionContext = actionConstructionContext;
this.busybox = busybox;
this.sdk = sdk;
+ this.useDataBindingV2 = useDataBindingV2;
}
public Label getLabel() {
@@ -111,6 +119,22 @@
return actionConstructionContext.getUniqueDirectoryArtifact(uniqueDirectorySuffix, relative);
}
+ public Artifact getUniqueDirectoryArtifact(String uniqueDirectorySuffix, PathFragment relative) {
+ return actionConstructionContext.getUniqueDirectoryArtifact(uniqueDirectorySuffix, relative);
+ }
+
+ public PathFragment getUniqueDirectory(PathFragment fragment) {
+ return actionConstructionContext.getUniqueDirectory(fragment);
+ }
+
+ public ArtifactRoot getBinOrGenfilesDirectory() {
+ return actionConstructionContext.getBinOrGenfilesDirectory();
+ }
+
+ public PathFragment getPackageDirectory() {
+ return actionConstructionContext.getPackageDirectory();
+ }
+
public AndroidConfiguration getAndroidConfig() {
return actionConstructionContext.getConfiguration().getFragment(AndroidConfiguration.class);
}
@@ -124,4 +148,8 @@
public boolean isPersistentBusyboxToolsEnabled() {
return persistentBusyboxToolsEnabled;
}
+
+ public boolean useDataBindingV2() {
+ return useDataBindingV2;
+ }
}
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 724f989..f781d05 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
@@ -214,12 +214,6 @@
StampedAndroidManifest primaryManifest,
DataBindingContext dataBindingContext) {
- if (aaptVersion == AndroidAaptVersion.AAPT2) {
- createAapt2ApkAction(dataContext, primaryResources, primaryAssets, primaryManifest);
- } else {
- createAaptAction(dataContext, primaryResources, primaryAssets, primaryManifest);
- }
-
// Wrap the new manifest, if any
ProcessedAndroidManifest processedManifest =
new ProcessedAndroidManifest(
@@ -227,10 +221,28 @@
primaryManifest.getPackage(),
primaryManifest.isExported());
+ // In databinding v2, this strips out the databinding and generates the layout info file.
+ AndroidResources databindingProcessedResources = dataBindingContext.processResources(
+ dataContext, primaryResources, processedManifest.getPackage());
+
+ if (aaptVersion == AndroidAaptVersion.AAPT2) {
+ createAapt2ApkAction(
+ dataContext,
+ databindingProcessedResources,
+ primaryAssets,
+ primaryManifest);
+ } else {
+ createAaptAction(
+ dataContext,
+ databindingProcessedResources,
+ primaryAssets,
+ primaryManifest);
+ }
+
// Wrap the parsed resources
ParsedAndroidResources parsedResources =
ParsedAndroidResources.of(
- primaryResources,
+ databindingProcessedResources,
symbols,
/* compiledSymbols = */ null,
dataContext.getLabel(),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
index 407f737..df9fb65 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
@@ -442,6 +442,11 @@
attr(DataBinding.DATABINDING_ANNOTATION_PROCESSOR_ATTR, LABEL)
.cfg(HostTransition.INSTANCE)
.value(env.getToolsLabel("//tools/android:databinding_annotation_processor")))
+ .add(
+ attr(DataBinding.DATABINDING_EXEC_PROCESSOR_ATTR, LABEL)
+ .cfg(HostTransition.INSTANCE)
+ .exec()
+ .value(env.getToolsLabel("//tools/android:databinding_exec")))
.advertiseSkylarkProvider(
SkylarkProviderIdentifier.forKey(AndroidResourcesInfo.PROVIDER.getKey()))
.advertiseSkylarkProvider(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java
index d816107..f07d87e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java
@@ -93,7 +93,7 @@
}
return ResourceApk.processFromTransitiveLibraryData(
ctx,
- DataBinding.asDisabledDataBindingContext(),
+ DataBinding.getDisabledDataBindingContext(ctx),
ResourceDependencies.fromProviders(deps, /* neverlink = */ neverlink),
AssetDependencies.empty(),
StampedAndroidManifest.createEmpty(
@@ -374,7 +374,7 @@
AndroidManifest.forAarImport(androidManifestArtifact),
ResourceDependencies.fromProviders(
getProviders(deps, AndroidResourcesInfo.PROVIDER), /* neverlink = */ false),
- DataBinding.asDisabledDataBindingContext(),
+ DataBinding.getDisabledDataBindingContext(ctx),
aaptVersion);
MergedAndroidAssets mergedAssets =
@@ -420,7 +420,7 @@
ctx,
getAndroidSemantics(),
errorReporter,
- DataBinding.asDisabledDataBindingContext(),
+ DataBinding.getDisabledDataBindingContext(ctx),
rawManifest,
AndroidResources.from(errorReporter, getFileProviders(resources), "resource_files"),
AndroidAssets.from(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java
index c1cd19f..eb61855 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/BusyBoxActionBuilder.java
@@ -98,6 +98,12 @@
return this;
}
+ /** Adds the given input artifacts without any command line options. */
+ public BusyBoxActionBuilder addInputs(Iterable<Artifact> inputs) {
+ this.inputs.addAll(inputs);
+ return this;
+ }
+
/** Adds an input artifact if it is non-null */
public BusyBoxActionBuilder maybeAddInput(
@CompileTimeConstant String arg, @Nullable Artifact value) {
@@ -150,6 +156,12 @@
return this;
}
+ /** Adds the given output artifacts without adding any command line options. */
+ public BusyBoxActionBuilder addOutputs(Iterable<Artifact> outputs) {
+ this.outputs.addAll(outputs);
+ return this;
+ }
+
/** Adds an output artifact if it is non-null */
public BusyBoxActionBuilder maybeAddOutput(
@CompileTimeConstant String arg, @Nullable Artifact value) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ParsedAndroidResources.java b/src/main/java/com/google/devtools/build/lib/rules/android/ParsedAndroidResources.java
index 256d6f3..9dcf277 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ParsedAndroidResources.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ParsedAndroidResources.java
@@ -56,6 +56,10 @@
getDummyDataBindingArtifact(dataContext.getActionConstructionContext())));
}
+ // In databinding v2, this strips out the databinding and generates the layout info file.
+ AndroidResources databindingProcessedResources =
+ dataBindingContext.processResources(dataContext, resources, manifest.getPackage());
+
return builder
.setOutput(dataContext.createOutputArtifact(AndroidRuleClasses.ANDROID_MERGED_SYMBOLS))
.setCompiledSymbolsOutput(
@@ -64,7 +68,7 @@
: null)
.build(
dataContext,
- dataBindingContext.processResources(resources),
+ databindingProcessedResources,
manifest,
dataBindingContext);
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBinding.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBinding.java
index 6285a1a..d21e344 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBinding.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBinding.java
@@ -13,21 +13,26 @@
// limitations under the License.
package com.google.devtools.build.lib.rules.android.databinding;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
+import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.rules.android.AndroidCommon;
import com.google.devtools.build.lib.rules.android.AndroidConfiguration;
+import com.google.devtools.build.lib.rules.android.AndroidDataContext;
import com.google.devtools.build.lib.rules.android.AndroidResources;
import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.util.ResourceFileLoader;
import com.google.devtools.build.lib.vfs.PathFragment;
+import java.io.IOException;
import java.util.List;
+import javax.annotation.Nullable;
/**
* Support logic for Bazel's <a
@@ -53,67 +58,53 @@
* via the implicit dependencies specified inside this class.
*/
public final class DataBinding {
+
/** The rule attribute supplying data binding's annotation processor. */
public static final String DATABINDING_ANNOTATION_PROCESSOR_ATTR =
"$databinding_annotation_processor";
+ /** The rule attribute supplying data binding's build helper (exec). */
+ public static final String DATABINDING_EXEC_PROCESSOR_ATTR = "$databinding_exec";
+
private static final String ENABLE_DATA_BINDING_ATTR = "enable_data_binding";
- private static final DataBindingContext DISABLED_CONTEXT = new DisabledDataBindingV1Context();
+ /** The directory where the annotation processor looks for dep metadata. */
+ private static final String DEP_METADATA_INPUT_DIR = "dependent-lib-artifacts";
+
+ /** The directory where the annotation processor writes metadata output for the current rule. */
+ private static final String METADATA_OUTPUT_DIR = "bin-files";
+
+ @VisibleForTesting
+ public static final DataBindingContext DISABLED_V1_CONTEXT = new DisabledDataBindingV1Context();
+
+ private static final DataBindingContext DISABLED_V2_CONTEXT = new DisabledDataBindingV2Context();
/** Supplies a databinding context from a rulecontext. */
public static DataBindingContext contextFrom(
RuleContext ruleContext, AndroidConfiguration androidConfig) {
- if (isEnabled(ruleContext)) {
- if (androidConfig.useDataBindingV2()) {
- return asEnabledDataBindingV2ContextFrom(ruleContext);
- }
- return asEnabledDataBindingV1ContextFrom(ruleContext);
- }
- return asDisabledDataBindingContext();
+
+ return contextFrom(isEnabled(ruleContext), ruleContext, androidConfig);
}
/** Supplies a databinding context from an action context. */
public static DataBindingContext contextFrom(
boolean enabled, ActionConstructionContext context, AndroidConfiguration androidConfig) {
+
if (enabled) {
if (androidConfig.useDataBindingV2()) {
- return asEnabledDataBindingV2ContextFrom(context);
+ return new DataBindingV2Context(context);
+ } else {
+ return new DataBindingV1Context(context);
}
- return asEnabledDataBindingV1ContextFrom(context);
+ } else {
+ if (androidConfig.useDataBindingV2()) {
+ return DISABLED_V2_CONTEXT;
+ } else {
+ return DISABLED_V1_CONTEXT;
+ }
}
- return asDisabledDataBindingContext();
}
- /** Supplies an enabled DataBindingContext from the action context. */
- private static DataBindingContext asEnabledDataBindingV1ContextFrom(
- ActionConstructionContext actionContext) {
- return new DataBindingV1Context(actionContext);
- }
-
- private static DataBindingContext asEnabledDataBindingV2ContextFrom(
- ActionConstructionContext actionContext) {
- return new DataBindingV2Context(actionContext);
- }
-
- /** Supplies a disabled (no-op) DataBindingContext. */
- public static DataBindingContext asDisabledDataBindingContext() {
- return DISABLED_CONTEXT;
- }
-
- /**
- * 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.of("setter_store.bin", "layoutinfo.bin", "br.bin");
-
- /** The directory where the annotation processor looks for dep metadata. */
- private static final String DEP_METADATA_INPUT_DIR = "dependent-lib-artifacts";
-
- /** The directory where the annotation processor write metadata output for the current rule. */
- private static final String METADATA_OUTPUT_DIR = "bin-files";
-
/**
* Should data binding support be enabled for this rule?
*
@@ -127,6 +118,15 @@
ruleContext.attributes().get(ENABLE_DATA_BINDING_ATTR, Type.BOOLEAN));
}
+ /** Supplies a disabled (no-op) DataBindingContext. */
+ public static DataBindingContext getDisabledDataBindingContext(AndroidDataContext ctx) {
+ if (ctx.useDataBindingV2()) {
+ return DISABLED_V2_CONTEXT;
+ } else {
+ return DISABLED_V1_CONTEXT;
+ }
+ }
+
/** Returns this rule's data binding base output dir (as an execroot-relative path). */
static PathFragment getDataBindingExecPath(RuleContext ruleContext) {
return ruleContext
@@ -135,6 +135,10 @@
.getRelative(ruleContext.getUniqueDirectory("databinding"));
}
+ static Artifact getLayoutInfoFile(ActionConstructionContext actionConstructionContext) {
+ return actionConstructionContext.getUniqueDirectoryArtifact("databinding", "layout-info.zip");
+ }
+
/** Returns an artifact for the specified output under a standardized data binding base dir. */
static Artifact getDataBindingArtifact(RuleContext ruleContext, String relativePath) {
PathFragment binRelativeBasePath =
@@ -149,26 +153,31 @@
return String.format("-Aandroid.databinding.%s=%s", flag, value);
}
- /**
- * 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}.
- */
- static void maybeAddProvider(
- List<Artifact> dataBindingMetadataOutputs,
- RuleConfiguredTargetBuilder builder,
- RuleContext ruleContext) {
- // Expose the data binding provider if there are outputs.
- dataBindingMetadataOutputs.addAll(getTransitiveMetadata(ruleContext, "exports"));
- if (!AndroidResources.definesAndroidResources(ruleContext.attributes())) {
- // If this rule doesn't declare direct resources, no resource processing is run so no data
- // binding outputs are produced. In that case, we need to explicitly propagate data binding
- // outputs from the deps to make sure they continue up the build graph.
- dataBindingMetadataOutputs.addAll(getTransitiveMetadata(ruleContext, "deps"));
- }
- if (!dataBindingMetadataOutputs.isEmpty()) {
- builder.addNativeDeclaredProvider(new UsesDataBindingProvider(dataBindingMetadataOutputs));
+ /** Turns a key/value pair into a javac annotation processor flag received by data binding. */
+ static String createProcessorFlag(String flag, Artifact value) {
+ return createProcessorFlag(flag, value.getExecPathString());
+ }
+
+ static ImmutableList<Artifact> getAnnotationFile(RuleContext ruleContext) {
+ // Add this rule's annotation processor input. If the rule doesn't have direct resources,
+ // there's no direct data binding info, so there's strictly no need for annotation processing.
+ // But it's still important to process the deps' .bin files so any Java class references get
+ // re-referenced so they don't get filtered out of the compilation classpath by JavaBuilder
+ // (which filters out classpath .jars that "aren't used": see --reduce_classpath). If data
+ // binding didn't reprocess a library's data binding expressions redundantly up the dependency
+ // chain (meaning each depender processes them again as if they were its own), this problem
+ // wouldn't happen.
+ try {
+ String contents =
+ ResourceFileLoader.loadResource(
+ DataBinding.class, "databinding_annotation_template.txt");
+ Artifact annotationFile = getDataBindingArtifact(ruleContext, "DataBindingInfo.java");
+ ruleContext.registerAction(
+ FileWriteAction.create(ruleContext, annotationFile, contents, false));
+ return ImmutableList.of(annotationFile);
+ } catch (IOException e) {
+ ruleContext.ruleError("Cannot load annotation processor template: " + e.getMessage());
+ return ImmutableList.of();
}
}
@@ -196,7 +205,10 @@
* would be a class redefinition conflict. But by feeding the library's metadata outputs into the
* binary's compilation, enough information is available to only use the first version.
*/
- static List<Artifact> getMetadataOutputs(RuleContext ruleContext) {
+ static ImmutableList<Artifact> getMetadataOutputs(
+ RuleContext ruleContext,
+ List<String> metadataOutputSuffixes) {
+
if (!AndroidResources.definesAndroidResources(ruleContext.attributes())) {
// If this rule doesn't define local resources, no resource processing was done, so it
// doesn't produce data binding output.
@@ -204,7 +216,7 @@
}
ImmutableList.Builder<Artifact> outputs = ImmutableList.<Artifact>builder();
String javaPackage = AndroidCommon.getJavaPackage(ruleContext);
- for (String suffix : METADATA_OUTPUT_SUFFIXES) {
+ for (String suffix : metadataOutputSuffixes) {
// The annotation processor automatically creates files with this naming pattern under the
// {@code -Aandroid.databinding.generationalFileOutDir} base directory.
outputs.add(
@@ -215,28 +227,50 @@
return outputs.build();
}
+ @Nullable
+ static Artifact getMetadataOutput(
+ RuleContext ruleContext,
+ String metadataOutputSuffix) {
+
+ if (!AndroidResources.definesAndroidResources(ruleContext.attributes())) {
+ // If this rule doesn't define local resources, no resource processing was done, so it
+ // doesn't produce data binding output.
+ return null;
+ }
+ String javaPackage = AndroidCommon.getJavaPackage(ruleContext);
+
+ // The annotation processor automatically creates files with this naming pattern under the
+ // {@code -Aandroid.databinding.generationalFileOutDir} base directory.
+ return getDataBindingArtifact(
+ ruleContext,
+ String.format("%s/%s-%s-%s",
+ METADATA_OUTPUT_DIR, javaPackage, javaPackage, metadataOutputSuffix));
+ }
+
/**
* Data binding's annotation processor reads the transitive metadata outputs of the target's deps
- * (see {@link #getMetadataOutputs(RuleContext)}) in the directory specified by the processor flag
- * {@code -Aandroid.databinding.bindingBuildFolder}. Since dependencies don't generate their
- * outputs under a common directory, we symlink them into a common place here.
+ * (see {@link #getMetadataOutputs(RuleContext, List<String>)}) in the directory specified by the
+ * processor flag {@code -Aandroid.databinding.bindingBuildFolder}. Since dependencies don't
+ * generate their outputs under a common directory, we symlink them into a common place here.
*
* @return the symlink paths of the transitive dep metadata outputs for this rule
*/
- static Artifact symlinkDepsMetadataIntoOutputTree(
- RuleContext ruleContext, Artifact depMetadata) {
+ static Artifact symlinkDepsMetadataIntoOutputTree(RuleContext ruleContext, Artifact depMetadata) {
+
Label ruleLabel = ruleContext.getRule().getLabel();
Artifact symlink =
getDataBindingArtifact(
ruleContext,
String.format(
"%s/%s", DEP_METADATA_INPUT_DIR, depMetadata.getRootRelativePathString()));
- ruleContext.registerAction(SymlinkAction.toArtifact(
- ruleContext.getActionOwner(),
- depMetadata,
- symlink,
- String.format(
- "Symlinking dep metadata output %s for %s", depMetadata.getFilename(), ruleLabel)));
+ ruleContext.registerAction(
+ SymlinkAction.toArtifact(
+ ruleContext.getActionOwner(),
+ depMetadata,
+ symlink,
+ String.format(
+ "Symlinking dep metadata output %s for %s", depMetadata.getFilename(), ruleLabel)));
return symlink;
}
+
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingContext.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingContext.java
index 95802c2..726a163 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingContext.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingContext.java
@@ -17,6 +17,7 @@
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.rules.android.AndroidDataContext;
import com.google.devtools.build.lib.rules.android.AndroidResources;
import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider;
import java.util.function.BiConsumer;
@@ -55,16 +56,22 @@
/** The javac flags that are needed to configure data binding's annotation processor. */
void supplyJavaCoptsUsing(
- RuleContext ruleContext, boolean isBinary, Consumer<Iterable<String>> consumer);
+ RuleContext ruleContext,
+ boolean isBinary,
+ Consumer<Iterable<String>> consumer);
/**
* Adds data binding's annotation processor as a plugin to the given Java compilation context.
*
* <p>This extends the Java compilation to translate data binding .xml into corresponding
* classes.
+ *
+ * The BiConsumer accepts as its first argument the JavaPluginInfoProvider, and the list of
+ * outputs of the processor as the second argument.
*/
void supplyAnnotationProcessor(
- RuleContext ruleContext, BiConsumer<JavaPluginInfoProvider, Iterable<Artifact>> consumer);
+ RuleContext ruleContext,
+ BiConsumer<JavaPluginInfoProvider, Iterable<Artifact>> consumer);
/**
* Processes deps that also apply data binding.
@@ -83,8 +90,7 @@
* <p>This triggers the annotation processor. Annotation processor settings are configured
* separately in {@link #supplyJavaCoptsUsing(RuleContext, boolean, Consumer)}.
*/
- ImmutableList<Artifact> addAnnotationFileToSrcs(
- ImmutableList<Artifact> srcs, RuleContext ruleContext);
+ ImmutableList<Artifact> getAnnotationSourceFiles(RuleContext ruleContext);
/**
* Adds the appropriate {@link UsesDataBindingProvider} for a rule if it should expose one.
@@ -94,5 +100,12 @@
*/
void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext);
- AndroidResources processResources(AndroidResources resources);
+ /**
+ * Process the given Android Resources for databinding. In databinding v2, this strips out the
+ * databinding and generates the layout info file.
+ */
+ AndroidResources processResources(
+ AndroidDataContext dataContext,
+ AndroidResources resources,
+ String appId);
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV1Context.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV1Context.java
index bea5df8..f136ec9 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV1Context.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV1Context.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.lib.rules.android.databinding;
import static com.google.devtools.build.lib.rules.android.databinding.DataBinding.createProcessorFlag;
-import static com.google.devtools.build.lib.rules.android.databinding.DataBinding.getDataBindingExecPath;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -22,21 +21,25 @@
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
-import com.google.devtools.build.lib.analysis.actions.FileWriteAction;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
import com.google.devtools.build.lib.rules.android.AndroidCommon;
+import com.google.devtools.build.lib.rules.android.AndroidDataContext;
import com.google.devtools.build.lib.rules.android.AndroidResources;
import com.google.devtools.build.lib.rules.java.JavaInfo;
import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider;
-import com.google.devtools.build.lib.util.ResourceFileLoader;
-import java.io.IOException;
import java.util.List;
-import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
final class DataBindingV1Context implements DataBindingContext {
+ /**
+ * 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_V1 =
+ ImmutableList.of("setter_store.bin", "layoutinfo.bin", "br.bin");
+
private final ActionConstructionContext actionConstructionContext;
DataBindingV1Context(ActionConstructionContext actionConstructionContext) {
@@ -45,18 +48,15 @@
@Override
public void supplyLayoutInfo(Consumer<Artifact> consumer) {
- consumer.accept(layoutInfoFile());
- }
-
- Artifact layoutInfoFile() {
- return actionConstructionContext.getUniqueDirectoryArtifact("databinding", "layout-info.zip");
+ consumer.accept(DataBinding.getLayoutInfoFile(actionConstructionContext));
}
@Override
public void supplyJavaCoptsUsing(
RuleContext ruleContext, boolean isBinary, Consumer<Iterable<String>> consumer) {
+
ImmutableList.Builder<String> flags = ImmutableList.builder();
- String metadataOutputDir = getDataBindingExecPath(ruleContext).getPathString();
+ String metadataOutputDir = DataBinding.getDataBindingExecPath(ruleContext).getPathString();
// Directory where the annotation processor looks for deps metadata output. The annotation
// processor automatically appends {@link DEP_METADATA_INPUT_DIR} to this path. Individual
@@ -75,7 +75,7 @@
// The path where data binding's resource processor wrote its output (the data binding XML
// expressions). The annotation processor reads this file to translate that XML into Java.
- flags.add(createProcessorFlag("xmlOutDir", getDataBindingExecPath(ruleContext).toString()));
+ flags.add(createProcessorFlag("xmlOutDir", metadataOutputDir));
// Unused.
flags.add(createProcessorFlag("exportClassListTo", "/tmp/exported_classes"));
@@ -84,7 +84,9 @@
flags.add(createProcessorFlag("modulePackage", AndroidCommon.getJavaPackage(ruleContext)));
// The minimum Android SDK compatible with this rule.
- flags.add(createProcessorFlag("minApi", "14")); // TODO(gregce): update this
+ // TODO(bazel-team): This probably should be based on the actual min-sdk from the manifest,
+ // or an appropriate rule attribute.
+ flags.add(createProcessorFlag("minApi", "14"));
// If enabled, produces cleaner output for Android Studio.
flags.add(createProcessorFlag("printEncodedErrors", "0"));
@@ -94,80 +96,64 @@
@Override
public void supplyAnnotationProcessor(
- RuleContext ruleContext, BiConsumer<JavaPluginInfoProvider, Iterable<Artifact>> consumer) {
- consumer.accept(
- JavaInfo.getProvider(
- JavaPluginInfoProvider.class,
- ruleContext.getPrerequisite(
- DataBinding.DATABINDING_ANNOTATION_PROCESSOR_ATTR, RuleConfiguredTarget.Mode.HOST)),
- DataBinding.getMetadataOutputs(ruleContext));
+ RuleContext ruleContext,
+ BiConsumer<JavaPluginInfoProvider, Iterable<Artifact>> consumer) {
+
+ JavaPluginInfoProvider javaPluginInfoProvider = JavaInfo.getProvider(
+ JavaPluginInfoProvider.class,
+ ruleContext.getPrerequisite(
+ DataBinding.DATABINDING_ANNOTATION_PROCESSOR_ATTR, RuleConfiguredTarget.Mode.HOST));
+
+ ImmutableList<Artifact> annotationProcessorOutputs =
+ DataBinding.getMetadataOutputs(ruleContext, METADATA_OUTPUT_SUFFIXES_V1);
+
+ consumer.accept(javaPluginInfoProvider, annotationProcessorOutputs);
}
@Override
public ImmutableList<Artifact> processDeps(RuleContext ruleContext) {
+
ImmutableList.Builder<Artifact> dataBindingJavaInputs = ImmutableList.builder();
if (AndroidResources.definesAndroidResources(ruleContext.attributes())) {
- dataBindingJavaInputs.add(layoutInfoFile());
+ dataBindingJavaInputs.add(DataBinding.getLayoutInfoFile(actionConstructionContext));
}
+
for (Artifact dataBindingDepMetadata : DataBinding.getTransitiveMetadata(ruleContext, "deps")) {
dataBindingJavaInputs.add(
DataBinding.symlinkDepsMetadataIntoOutputTree(ruleContext, dataBindingDepMetadata));
}
+
return dataBindingJavaInputs.build();
}
@Override
- public ImmutableList<Artifact> addAnnotationFileToSrcs(
- ImmutableList<Artifact> srcs, RuleContext ruleContext) {
- // Add this rule's annotation processor input. If the rule doesn't have direct resources,
- // there's no direct data binding info, so there's strictly no need for annotation processing.
- // But it's still important to process the deps' .bin files so any Java class references get
- // re-referenced so they don't get filtered out of the compilation classpath by JavaBuilder
- // (which filters out classpath .jars that "aren't used": see --reduce_classpath). If data
- // binding didn't reprocess a library's data binding expressions redundantly up the dependency
- // chain (meaning each depender processes them again as if they were its own), this problem
- // wouldn't happen.
- try {
- String contents =
- ResourceFileLoader.loadResource(
- DataBinding.class, "databinding_annotation_template.txt");
- Artifact annotationFile = DataBinding
- .getDataBindingArtifact(ruleContext, "DataBindingInfo.java");
- ruleContext.registerAction(
- FileWriteAction.create(ruleContext, annotationFile, contents, false));
- return ImmutableList.<Artifact>builder().addAll(srcs).add(annotationFile).build();
- } catch (IOException e) {
- ruleContext.ruleError("Cannot load annotation processor template: " + e.getMessage());
- return ImmutableList.of();
- }
+ public ImmutableList<Artifact> getAnnotationSourceFiles(RuleContext ruleContext) {
+ return DataBinding.getAnnotationFile(ruleContext);
}
@Override
public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) {
- List<Artifact> dataBindingMetadataOutputs =
- Lists.newArrayList(DataBinding.getMetadataOutputs(ruleContext));
- DataBinding.maybeAddProvider(dataBindingMetadataOutputs, builder, ruleContext);
- }
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
+ List<Artifact> dataBindingMetadataOutputs = Lists.newArrayList(
+ DataBinding.getMetadataOutputs(ruleContext, METADATA_OUTPUT_SUFFIXES_V1));
+
+ // Expose the data binding provider if there are outputs.
+ dataBindingMetadataOutputs.addAll(DataBinding.getTransitiveMetadata(ruleContext, "exports"));
+ if (!AndroidResources.definesAndroidResources(ruleContext.attributes())) {
+ // If this rule doesn't declare direct resources, no resource processing is run so no data
+ // binding outputs are produced. In that case, we need to explicitly propagate data binding
+ // outputs from the deps to make sure they continue up the build graph.
+ dataBindingMetadataOutputs.addAll(DataBinding.getTransitiveMetadata(ruleContext, "deps"));
}
- if (o == null || getClass() != o.getClass()) {
- return false;
+ if (!dataBindingMetadataOutputs.isEmpty()) {
+ builder.addNativeDeclaredProvider(
+ new UsesDataBindingProvider(dataBindingMetadataOutputs));
}
- DataBindingV1Context that = (DataBindingV1Context) o;
- return Objects.equals(actionConstructionContext, that.actionConstructionContext);
}
@Override
- public int hashCode() {
- return actionConstructionContext.hashCode();
- }
-
- @Override
- public AndroidResources processResources(AndroidResources resources) {
+ public AndroidResources processResources(
+ AndroidDataContext dataContext, AndroidResources resources, String appId) {
return resources;
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java
index 28661d5..642a8f8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java
@@ -13,63 +13,303 @@
// limitations under the License.
package com.google.devtools.build.lib.rules.android.databinding;
+import static com.google.devtools.build.lib.rules.android.databinding.DataBinding.createProcessorFlag;
+
import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
+import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
+import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
+import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.rules.android.AndroidCommon;
+import com.google.devtools.build.lib.rules.android.AndroidDataBindingProcessorBuilder;
+import com.google.devtools.build.lib.rules.android.AndroidDataContext;
import com.google.devtools.build.lib.rules.android.AndroidResources;
+import com.google.devtools.build.lib.rules.java.JavaInfo;
import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider;
+import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
class DataBindingV2Context implements DataBindingContext {
- // TODO(b/112038432): Enable databinding v2.
- @SuppressWarnings("unused")
+ /**
+ * 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_V2 =
+ ImmutableList.of("setter_store.bin", "br.bin");
+
private final ActionConstructionContext actionContext;
DataBindingV2Context(ActionConstructionContext actionContext) {
this.actionContext = actionContext;
- // TODO(b/112038432): Enable databinding v2.
- throw new UnsupportedOperationException("V2 not implemented yet.");
}
@Override
public void supplyLayoutInfo(Consumer<Artifact> consumer) {
-
+ // In v2, The layout info file is generated in processResources below.
}
@Override
public void supplyJavaCoptsUsing(RuleContext ruleContext, boolean isBinary,
Consumer<Iterable<String>> consumer) {
+ ImmutableList.Builder<String> flags = ImmutableList.builder();
+ String metadataOutputDir = DataBinding.getDataBindingExecPath(ruleContext).getPathString();
+
+ // Directory where the annotation processor looks for deps metadata output. The annotation
+ // processor automatically appends {@link DEP_METADATA_INPUT_DIR} to this path. Individual
+ // files can be anywhere under this directory, recursively.
+ flags.add(createProcessorFlag("bindingBuildFolder", metadataOutputDir));
+
+ // Directory where the annotation processor should write this rule's metadata output. The
+ // annotation processor automatically appends {@link METADATA_OUTPUT_DIR} to this path.
+ flags.add(createProcessorFlag("generationalFileOutDir", metadataOutputDir));
+
+ // Path to the Android SDK installation (if available).
+ flags.add(createProcessorFlag("sdkDir", "/not/used"));
+
+ // Whether the current rule is a library or binary.
+ flags.add(createProcessorFlag("artifactType", isBinary ? "APPLICATION" : "LIBRARY"));
+
+ // Unused.
+ flags.add(createProcessorFlag("exportClassListTo", "/tmp/exported_classes"));
+
+ // The Java package for the current rule.
+ flags.add(createProcessorFlag("modulePackage", AndroidCommon.getJavaPackage(ruleContext)));
+
+ // The minimum Android SDK compatible with this rule.
+ // TODO(bazel-team): This probably should be based on the actual min-sdk from the manifest,
+ // or an appropriate rule attribute.
+ flags.add(createProcessorFlag("minApi", "14"));
+
+ // If enabled, produces cleaner output for Android Studio.
+ flags.add(createProcessorFlag("printEncodedErrors", "0"));
+
+ // V2 flags
+ flags.add(createProcessorFlag("enableV2", "1"));
+
+ if (AndroidResources.definesAndroidResources(ruleContext.attributes())) {
+ flags.add(createProcessorFlag("classLogDir", getClassInfoFile(ruleContext)));
+ // The path where data binding's resource processor wrote its output (the data binding XML
+ // expressions). The annotation processor reads this file to translate that XML into Java.
+ flags.add(createProcessorFlag("xmlOutDir", DataBinding.getLayoutInfoFile(ruleContext)));
+ } else {
+ // send dummy files
+ flags.add(createProcessorFlag("classLogDir", "/tmp/no_resources"));
+ flags.add(createProcessorFlag("xmlOutDir", "/tmp/no_resources"));
+ }
+
+ consumer.accept(flags.build());
}
@Override
- public void supplyAnnotationProcessor(RuleContext ruleContext,
+ public void supplyAnnotationProcessor(
+ RuleContext ruleContext,
BiConsumer<JavaPluginInfoProvider, Iterable<Artifact>> consumer) {
+ JavaPluginInfoProvider javaPluginInfoProvider = JavaInfo.getProvider(
+ JavaPluginInfoProvider.class,
+ ruleContext.getPrerequisite(
+ DataBinding.DATABINDING_ANNOTATION_PROCESSOR_ATTR, RuleConfiguredTarget.Mode.HOST));
+
+ ImmutableList<Artifact> annotationProcessorOutputs =
+ DataBinding.getMetadataOutputs(ruleContext, METADATA_OUTPUT_SUFFIXES_V2);
+
+ consumer.accept(javaPluginInfoProvider, annotationProcessorOutputs);
}
@Override
public ImmutableList<Artifact> processDeps(RuleContext ruleContext) {
- return null;
+
+ ImmutableList.Builder<Artifact> dataBindingJavaInputs = ImmutableList.builder();
+ if (AndroidResources.definesAndroidResources(ruleContext.attributes())) {
+ dataBindingJavaInputs.add(DataBinding.getLayoutInfoFile(ruleContext));
+ dataBindingJavaInputs.add(getClassInfoFile(ruleContext));
+ }
+
+ for (Artifact transitiveBRFile : getTransitiveBRFiles(ruleContext)) {
+ dataBindingJavaInputs.add(
+ DataBinding.symlinkDepsMetadataIntoOutputTree(ruleContext, transitiveBRFile));
+ }
+
+ for (Artifact directSetterStoreFile : getDirectSetterStoreFiles(ruleContext)) {
+ dataBindingJavaInputs.add(
+ DataBinding.symlinkDepsMetadataIntoOutputTree(ruleContext, directSetterStoreFile));
+ }
+
+ for (Artifact classInfo : getDirectClassInfo(ruleContext)) {
+ dataBindingJavaInputs.add(
+ DataBinding.symlinkDepsMetadataIntoOutputTree(ruleContext, classInfo));
+ }
+
+ return dataBindingJavaInputs.build();
}
+ private static ImmutableList<Artifact> getTransitiveBRFiles(RuleContext context) {
+ ImmutableList.Builder<Artifact> brFiles = ImmutableList.builder();
+ if (context.attributes().has("deps", BuildType.LABEL_LIST)) {
+
+ Iterable<DataBindingV2Provider> providers = context.getPrerequisites(
+ "deps", RuleConfiguredTarget.Mode.TARGET, DataBindingV2Provider.PROVIDER);
+
+ for (DataBindingV2Provider provider : providers) {
+ brFiles.addAll(provider.getTransitiveBRFiles());
+ }
+ }
+ return brFiles.build();
+ }
+
+ private static List<Artifact> getDirectSetterStoreFiles(RuleContext context) {
+ ImmutableList.Builder<Artifact> setterStoreFiles = ImmutableList.builder();
+ if (context.attributes().has("deps", BuildType.LABEL_LIST)) {
+
+ Iterable<DataBindingV2Provider> providers = context.getPrerequisites(
+ "deps", RuleConfiguredTarget.Mode.TARGET, DataBindingV2Provider.PROVIDER);
+
+ for (DataBindingV2Provider provider : providers) {
+ setterStoreFiles.addAll(provider.getSetterStores());
+ }
+ }
+ return setterStoreFiles.build();
+ }
+
@Override
- public ImmutableList<Artifact> addAnnotationFileToSrcs(ImmutableList<Artifact> srcs,
- RuleContext ruleContext) {
- return null;
+ public ImmutableList<Artifact> getAnnotationSourceFiles(RuleContext ruleContext) {
+ ImmutableList.Builder<Artifact> srcs = ImmutableList.builder();
+
+ srcs.addAll(DataBinding.getAnnotationFile(ruleContext));
+ srcs.addAll(createBaseClasses(ruleContext));
+
+ return srcs.build();
+ }
+
+ private ImmutableList<Artifact> createBaseClasses(RuleContext ruleContext) {
+
+ if (!AndroidResources.definesAndroidResources(ruleContext.attributes())) {
+ return ImmutableList.of(); // no resource, no base classes or class info
+ }
+
+ Artifact layoutInfo = DataBinding.getLayoutInfoFile(ruleContext);
+ Artifact classInfoFile = getClassInfoFile(ruleContext);
+ Artifact srcOutFile = DataBinding.getDataBindingArtifact(ruleContext, "baseClassSrc.srcjar");
+
+ FilesToRunProvider exec = ruleContext
+ .getExecutablePrerequisite(DataBinding.DATABINDING_EXEC_PROCESSOR_ATTR, Mode.HOST);
+
+ CustomCommandLine.Builder commandLineBuilder = CustomCommandLine.builder()
+ .add("GEN_BASE_CLASSES")
+ .addExecPath("-layoutInfoFiles", layoutInfo)
+ .add("-package", AndroidCommon.getJavaPackage(ruleContext))
+ .addExecPath("-classInfoOut", classInfoFile)
+ .addExecPath("-sourceOut", srcOutFile)
+ .add("-zipSourceOutput", "true")
+ .add("-useAndroidX", "false");
+
+ List<Artifact> dependencyClientInfos = getDirectClassInfo(ruleContext);
+ for (Artifact artifact : dependencyClientInfos) {
+ commandLineBuilder.addExecPath("-dependencyClassInfoList", artifact);
+ }
+
+ Action[] action = new SpawnAction.Builder()
+ .setExecutable(exec)
+ .setMnemonic("GenerateDataBindingBaseClasses")
+ .addInput(layoutInfo)
+ .addInputs(dependencyClientInfos)
+ .addOutput(classInfoFile)
+ .addOutput(srcOutFile)
+ .addCommandLine(commandLineBuilder.build())
+ .build(ruleContext);
+ ruleContext.registerAction(action);
+
+ return ImmutableList.of(srcOutFile);
+ }
+
+ private static List<Artifact> getDirectClassInfo(RuleContext context) {
+ ImmutableList.Builder<Artifact> clientInfoFiles = ImmutableList.builder();
+ if (context.attributes().has("deps", BuildType.LABEL_LIST)) {
+
+ Iterable<DataBindingV2Provider> providers = context.getPrerequisites(
+ "deps", RuleConfiguredTarget.Mode.TARGET, DataBindingV2Provider.PROVIDER);
+
+ for (DataBindingV2Provider provider : providers) {
+ clientInfoFiles.addAll(provider.getClassInfos());
+ }
+ }
+ return clientInfoFiles.build();
}
@Override
public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) {
+ Artifact setterStore = DataBinding.getMetadataOutput(ruleContext, "setter_store.bin");
+ Artifact br = DataBinding.getMetadataOutput(ruleContext, "br.bin");
+
+ ImmutableList.Builder<Artifact> setterStores = ImmutableList.builder();
+ if (setterStore != null) {
+ setterStores.add(setterStore);
+ }
+
+ ImmutableList.Builder<Artifact> classInfos = ImmutableList.builder();
+ if (AndroidResources.definesAndroidResources(ruleContext.attributes())) {
+ Artifact classInfo = getClassInfoFile(ruleContext);
+ classInfos.add(classInfo);
+ }
+
+ // android_binary doesn't have "exports"
+ if (ruleContext.attributes().has("exports", BuildType.LABEL_LIST)) {
+ Iterable<DataBindingV2Provider> exportsProviders =
+ ruleContext.getPrerequisites(
+ "exports", RuleConfiguredTarget.Mode.TARGET, DataBindingV2Provider.PROVIDER);
+ for (DataBindingV2Provider provider : exportsProviders) {
+ setterStores.addAll(provider.getSetterStores());
+ classInfos.addAll(provider.getClassInfos());
+ }
+ }
+
+ NestedSetBuilder<Artifact> brFiles = new NestedSetBuilder<>(Order.STABLE_ORDER);
+ if (br != null) {
+ brFiles.add(br);
+ }
+
+ Iterable<DataBindingV2Provider> depsProviders = ruleContext.getPrerequisites(
+ "deps", RuleConfiguredTarget.Mode.TARGET, DataBindingV2Provider.PROVIDER);
+
+ for (DataBindingV2Provider provider : depsProviders) {
+ brFiles.addTransitive(provider.getTransitiveBRFiles());
+ }
+
+ builder.addNativeDeclaredProvider(
+ new DataBindingV2Provider(
+ classInfos.build(),
+ setterStores.build(),
+ brFiles.build()));
}
@Override
- public AndroidResources processResources(AndroidResources resources) {
- return null;
+ public AndroidResources processResources(
+ AndroidDataContext dataContext, AndroidResources resources, String appId) {
+
+ AndroidResources databindingProcessedResources = AndroidDataBindingProcessorBuilder.create(
+ dataContext,
+ resources,
+ appId,
+ DataBinding.getLayoutInfoFile(actionContext));
+
+ return databindingProcessedResources;
+
+ }
+
+ private static Artifact getClassInfoFile(ActionConstructionContext context) {
+ return context.getUniqueDirectoryArtifact("databinding", "class-info.zip");
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Provider.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Provider.java
new file mode 100644
index 0000000..0ffd180
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Provider.java
@@ -0,0 +1,87 @@
+// Copyright 2018 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.databinding;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.packages.BuiltinProvider;
+import com.google.devtools.build.lib.packages.NativeInfo;
+import com.google.devtools.build.lib.skylarkbuildapi.android.DataBindingV2ProviderApi;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.SkylarkList;
+import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
+
+/**
+ * A provider that exposes this enables <a
+ * href="https://developer.android.com/topic/libraries/data-binding/index.html">data binding</a>
+ * version 2 on its resource processing and Java compilation.
+ */
+public final class DataBindingV2Provider extends NativeInfo
+ implements DataBindingV2ProviderApi<Artifact> {
+
+ public static final Provider PROVIDER = new Provider();
+
+ private final ImmutableList<Artifact> classInfos;
+
+ private final ImmutableList<Artifact> setterStores;
+
+ private final NestedSet<Artifact> transitiveBRFiles;
+
+ public DataBindingV2Provider(
+ ImmutableList<Artifact> classInfos,
+ ImmutableList<Artifact> setterStores,
+ NestedSet<Artifact> transitiveBRFiles) {
+ super(PROVIDER);
+ this.classInfos = classInfos;
+ this.setterStores = setterStores;
+ this.transitiveBRFiles = transitiveBRFiles;
+ }
+
+ @Override
+ public ImmutableList<Artifact> getClassInfos() {
+ return classInfos;
+ }
+
+ @Override
+ public ImmutableList<Artifact> getSetterStores() {
+ return setterStores;
+ }
+
+ @Override
+ public NestedSet<Artifact> getTransitiveBRFiles() {
+ return transitiveBRFiles;
+ }
+
+ /** The provider can construct the DataBindingV2Provider provider. */
+ public static class Provider extends BuiltinProvider<DataBindingV2Provider>
+ implements DataBindingV2ProviderApi.Provider<Artifact> {
+
+ private Provider() {
+ super(NAME, DataBindingV2Provider.class);
+ }
+
+ @Override
+ public DataBindingV2ProviderApi<Artifact> createInfo(
+ SkylarkList<Artifact> setterStores,
+ SkylarkList<Artifact> clientInfos,
+ SkylarkNestedSet transitiveBrFiles) throws EvalException {
+
+ return new DataBindingV2Provider(
+ setterStores.getImmutableList(),
+ clientInfos.getImmutableList(),
+ transitiveBrFiles.getSet(Artifact.class));
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DisabledDataBindingV1Context.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DisabledDataBindingV1Context.java
index cae89c4..7b59900 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DisabledDataBindingV1Context.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DisabledDataBindingV1Context.java
@@ -17,6 +17,7 @@
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.rules.android.AndroidDataContext;
import com.google.devtools.build.lib.rules.android.AndroidResources;
import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider;
import java.util.ArrayList;
@@ -39,18 +40,30 @@
}
@Override
- public ImmutableList<Artifact> addAnnotationFileToSrcs(
- ImmutableList<Artifact> srcs, RuleContext ruleContext) {
- return srcs;
- };
-
- @Override
- public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) {
- DataBinding.maybeAddProvider(new ArrayList<>(), builder, ruleContext);
+ public ImmutableList<Artifact> getAnnotationSourceFiles(RuleContext ruleContext) {
+ return ImmutableList.of();
}
@Override
- public AndroidResources processResources(AndroidResources resources) {
+ public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) {
+
+ ArrayList<Artifact> dataBindingMetadataOutputs = new ArrayList<>();
+ // Expose the data binding provider if there are outputs.
+ dataBindingMetadataOutputs.addAll(DataBinding.getTransitiveMetadata(ruleContext, "exports"));
+ if (!AndroidResources.definesAndroidResources(ruleContext.attributes())) {
+ // If this rule doesn't declare direct resources, no resource processing is run so no data
+ // binding outputs are produced. In that case, we need to explicitly propagate data binding
+ // outputs from the deps to make sure they continue up the build graph.
+ dataBindingMetadataOutputs.addAll(DataBinding.getTransitiveMetadata(ruleContext, "deps"));
+ }
+ if (!dataBindingMetadataOutputs.isEmpty()) {
+ builder.addNativeDeclaredProvider(new UsesDataBindingProvider(dataBindingMetadataOutputs));
+ }
+ }
+
+ @Override
+ public AndroidResources processResources(
+ AndroidDataContext dataContext, AndroidResources resources, String appId) {
return resources;
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DisabledDataBindingV2Context.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DisabledDataBindingV2Context.java
new file mode 100644
index 0000000..5bbef27
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DisabledDataBindingV2Context.java
@@ -0,0 +1,92 @@
+// Copyright 2018 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.databinding;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
+import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.rules.android.AndroidDataContext;
+import com.google.devtools.build.lib.rules.android.AndroidResources;
+import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+class DisabledDataBindingV2Context implements DataBindingContext {
+
+ @Override
+ public void supplyJavaCoptsUsing(RuleContext ruleContext, boolean isBinary,
+ Consumer<Iterable<String>> consumer) { }
+
+ @Override
+ public void supplyAnnotationProcessor(RuleContext ruleContext,
+ BiConsumer<JavaPluginInfoProvider, Iterable<Artifact>> consumer) { }
+
+ @Override
+ public ImmutableList<Artifact> processDeps(RuleContext ruleContext) {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public ImmutableList<Artifact> getAnnotationSourceFiles(RuleContext ruleContext) {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) {
+
+ ImmutableList.Builder<Artifact> setterStores = ImmutableList.builder();
+ ImmutableList.Builder<Artifact> classInfos = ImmutableList.builder();
+ NestedSetBuilder<Artifact> brFiles = new NestedSetBuilder<>(Order.STABLE_ORDER);
+
+ // android_binary doesn't have "exports"
+ if (ruleContext.attributes().has("exports", BuildType.LABEL_LIST)) {
+ Iterable<DataBindingV2Provider> exportsProviders =
+ ruleContext.getPrerequisites(
+ "exports", RuleConfiguredTarget.Mode.TARGET, DataBindingV2Provider.PROVIDER);
+ for (DataBindingV2Provider provider : exportsProviders) {
+ setterStores.addAll(provider.getSetterStores());
+ classInfos.addAll(provider.getClassInfos());
+ brFiles.addTransitive(provider.getTransitiveBRFiles());
+ }
+ }
+
+
+ Iterable<DataBindingV2Provider> depsProviders = ruleContext.getPrerequisites(
+ "deps", RuleConfiguredTarget.Mode.TARGET, DataBindingV2Provider.PROVIDER);
+
+ for (DataBindingV2Provider provider : depsProviders) {
+ brFiles.addTransitive(provider.getTransitiveBRFiles());
+ }
+
+ builder.addNativeDeclaredProvider(
+ new DataBindingV2Provider(
+ classInfos.build(),
+ setterStores.build(),
+ brFiles.build()));
+ }
+
+ @Override
+ public AndroidResources processResources(
+ AndroidDataContext dataContext, AndroidResources resources, String appId) {
+ return resources;
+ }
+
+ @Override
+ public void supplyLayoutInfo(Consumer<Artifact> consumer) { }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/UsesDataBindingProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/UsesDataBindingProvider.java
index e6a4aaa..62ce14f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/UsesDataBindingProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/UsesDataBindingProvider.java
@@ -58,4 +58,4 @@
return new UsesDataBindingProvider(metadataOutputs.getImmutableList());
}
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/android/DataBindingV2ProviderApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/android/DataBindingV2ProviderApi.java
new file mode 100644
index 0000000..2c0f28e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/android/DataBindingV2ProviderApi.java
@@ -0,0 +1,99 @@
+// Copyright 2018 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.skylarkbuildapi.android;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
+import com.google.devtools.build.lib.skylarkbuildapi.ProviderApi;
+import com.google.devtools.build.lib.skylarkbuildapi.StructApi;
+import com.google.devtools.build.lib.skylarkinterface.Param;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkConstructor;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.SkylarkList;
+import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
+
+/**
+ * An interface for a provider that exposes the use of <a
+ * href="https://developer.android.com/topic/libraries/data-binding/index.html">data binding</a>.
+ */
+@SkylarkModule(
+ name = "DataBindingV2Info",
+ doc =
+ "Do not use this module. It is intended for migration purposes only. If you depend on it, "
+ + "you will be broken when it is removed.",
+ documented = false)
+public interface DataBindingV2ProviderApi<T extends FileApi> extends StructApi {
+
+ /** Name of this info object. */
+ public static final String NAME = "DataBindingV2Info";
+
+ /** Returns the setter store files from this rule. */
+ @SkylarkCallable(name = "setter_stores", structField = true, doc = "", documented = false)
+ ImmutableList<T> getSetterStores();
+
+ /** Returns the client info files from this rule. */
+ @SkylarkCallable(name = "client_infos", structField = true, doc = "", documented = false)
+ ImmutableList<T> getClassInfos();
+
+ /** Returns the BR files from this rule and its dependencies. */
+ @SkylarkCallable(name = "transitive_br_files", structField = true, doc = "", documented = false)
+ NestedSet<T> getTransitiveBRFiles();
+
+ /** The provider implementing this can construct the DataBindingV2Info provider. */
+ @SkylarkModule(
+ name = "Provider",
+ doc =
+ "Do not use this module. It is intended for migration purposes only. If you depend on "
+ + "it, you will be broken when it is removed.",
+ documented = false)
+ public interface Provider<F extends FileApi> extends ProviderApi {
+
+ @SkylarkCallable(
+ name = NAME,
+ doc = "The <code>DataBindingV2Info</code> constructor.",
+ documented = false,
+ parameters = {
+ @Param(
+ name = "setter_stores",
+ doc = "A list of artifacts of setter_stores.bin.",
+ positional = true,
+ named = false,
+ type = SkylarkList.class,
+ generic1 = FileApi.class),
+ @Param(
+ name = "client_infos",
+ doc = "A list of artifacts of client_infos.bin.",
+ positional = true,
+ named = false,
+ type = SkylarkList.class,
+ generic1 = FileApi.class),
+ @Param(
+ name = "transitive_br_files",
+ doc = "A list of artifacts of br.bin.",
+ positional = true,
+ named = false,
+ type = SkylarkNestedSet.class,
+ generic1 = FileApi.class),
+ },
+ selfCall = true)
+ @SkylarkConstructor(objectType = DataBindingV2ProviderApi.class)
+ DataBindingV2ProviderApi<F> createInfo(
+ SkylarkList<F> setterStores,
+ SkylarkList<F> clientInfos,
+ SkylarkNestedSet transitiveBrFiles) throws EvalException;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingV2Test.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingV2Test.java
new file mode 100644
index 0000000..e62b1cf
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingV2Test.java
@@ -0,0 +1,550 @@
+// Copyright 2017 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.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.getFirstArtifactEndingWith;
+import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.prettyArtifactNames;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.truth.Truth;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.extra.JavaCompileInfo;
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+import com.google.devtools.build.lib.cmdline.RepositoryName;
+import com.google.devtools.build.lib.rules.android.databinding.DataBindingV2Provider;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for Bazel's Android data binding v2 support. */
+@RunWith(JUnit4.class)
+public class AndroidDataBindingV2Test extends AndroidBuildViewTestCase {
+
+ @Before
+ public void setDataBindingV2Flag() throws Exception {
+ useConfiguration("--experimental_android_databinding_v2");
+ }
+
+ private void writeDataBindingFiles() throws Exception {
+
+ scratch.file(
+ "java/android/library/BUILD",
+ "android_library(",
+ " name = 'lib_with_data_binding',",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " srcs = ['MyLib.java'],",
+ " resource_files = [],",
+ ")");
+
+ scratch.file(
+ "java/android/library/MyLib.java", "package android.library; public class MyLib {};");
+
+ scratch.file(
+ "java/android/binary/BUILD",
+ "android_binary(",
+ " name = 'app',",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " srcs = ['MyApp.java'],",
+ " deps = ['//java/android/library:lib_with_data_binding'],",
+ ")");
+
+ scratch.file(
+ "java/android/binary/MyApp.java", "package android.binary; public class MyApp {};");
+ }
+
+ private void writeDataBindingFilesWithNoResourcesDep() throws Exception {
+
+ scratch.file(
+ "java/android/lib_with_resource_files/BUILD",
+ "android_library(",
+ " name = 'lib_with_resource_files',",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " srcs = ['LibWithResourceFiles.java'],",
+ " resource_files = glob(['res/**']),",
+ ")");
+ scratch.file(
+ "java/android/lib_with_resource_files/LibWithResourceFiles.java",
+ "package android.lib_with_resource_files; public class LibWithResourceFiles {};");
+
+ scratch.file(
+ "java/android/lib_no_resource_files/BUILD",
+ "android_library(",
+ " name = 'lib_no_resource_files',",
+ " enable_data_binding = 1,",
+ " srcs = ['LibNoResourceFiles.java'],",
+ " deps = ['//java/android/lib_with_resource_files'],",
+ ")");
+ scratch.file(
+ "java/android/lib_no_resource_files/LibNoResourceFiles.java",
+ "package android.lib_no_resource_files; public class LibNoResourceFiles {};");
+
+ scratch.file(
+ "java/android/binary/BUILD",
+ "android_binary(",
+ " name = 'app',",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " srcs = ['MyApp.java'],",
+ " deps = ['//java/android/lib_no_resource_files'],",
+ ")");
+ scratch.file(
+ "java/android/binary/MyApp.java", "package android.binary; public class MyApp {};");
+ }
+
+ @Test
+ public void basicDataBindingIntegration() throws Exception {
+
+ writeDataBindingFiles();
+
+ ConfiguredTarget ctapp = getConfiguredTarget("//java/android/binary:app");
+ Set<Artifact> allArtifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(ctapp));
+
+ // "Data binding"-enabled targets invoke resource processing with a request for data binding
+ // output:
+ Artifact libResourceInfoOutput =
+ getFirstArtifactEndingWith(
+ allArtifacts, "databinding/lib_with_data_binding/layout-info.zip");
+ assertThat(getGeneratingSpawnActionArgs(libResourceInfoOutput))
+ .containsAllOf("--dataBindingInfoOut", libResourceInfoOutput.getExecPathString())
+ .inOrder();
+
+ Artifact binResourceInfoOutput =
+ getFirstArtifactEndingWith(allArtifacts, "databinding/app/layout-info.zip");
+ assertThat(getGeneratingSpawnActionArgs(binResourceInfoOutput))
+ .containsAllOf("--dataBindingInfoOut", binResourceInfoOutput.getExecPathString())
+ .inOrder();
+
+ // Java compilation includes the data binding annotation processor, the resource processor's
+ // output, and the auto-generated DataBindingInfo.java the annotation processor uses to figure
+ // out what to do:
+ SpawnAction libCompileAction =
+ (SpawnAction)
+ getGeneratingAction(
+ getFirstArtifactEndingWith(allArtifacts, "lib_with_data_binding.jar"));
+ assertThat(getProcessorNames(libCompileAction))
+ .contains("android.databinding.annotationprocessor.ProcessDataBinding");
+ assertThat(prettyArtifactNames(libCompileAction.getInputs()))
+ .containsAllOf(
+ "java/android/library/databinding/lib_with_data_binding/layout-info.zip",
+ "java/android/library/databinding/lib_with_data_binding/DataBindingInfo.java");
+
+ SpawnAction binCompileAction =
+ (SpawnAction) getGeneratingAction(getFirstArtifactEndingWith(allArtifacts, "app.jar"));
+ assertThat(getProcessorNames(binCompileAction))
+ .contains("android.databinding.annotationprocessor.ProcessDataBinding");
+ assertThat(prettyArtifactNames(binCompileAction.getInputs()))
+ .containsAllOf(
+ "java/android/binary/databinding/app/layout-info.zip",
+ "java/android/binary/databinding/app/DataBindingInfo.java");
+ }
+
+ @Test
+ public void dataBindingCompilationUsesMetadataFromDeps() throws Exception {
+
+ writeDataBindingFiles();
+
+ ConfiguredTarget ctapp = getConfiguredTarget("//java/android/binary:app");
+ Set<Artifact> allArtifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(ctapp));
+
+ // The library's compilation doesn't include any of the -setter_store.bin, layoutinfo.bin, etc.
+ // files that store a dependency's data binding results (since the library has no deps).
+ // We check that they don't appear as compilation inputs.
+ SpawnAction libCompileAction =
+ (SpawnAction)
+ getGeneratingAction(
+ getFirstArtifactEndingWith(allArtifacts, "lib_with_data_binding.jar"));
+ assertThat(
+ Iterables.filter(
+ libCompileAction.getInputs(), ActionsTestUtil.getArtifactSuffixMatcher(".bin")))
+ .isEmpty();
+
+ // The binary's compilation includes the library's data binding results.
+ SpawnAction binCompileAction =
+ (SpawnAction) getGeneratingAction(getFirstArtifactEndingWith(allArtifacts, "app.jar"));
+ Iterable<Artifact> depMetadataInputs =
+ Iterables.filter(
+ binCompileAction.getInputs(), ActionsTestUtil.getArtifactSuffixMatcher(".bin"));
+ final String depMetadataBaseDir =
+ Iterables.getFirst(depMetadataInputs, null).getExecPath().getParentDirectory().toString();
+ ActionsTestUtil.execPaths(
+ Iterables.filter(
+ binCompileAction.getInputs(), ActionsTestUtil.getArtifactSuffixMatcher(".bin")));
+ assertThat(ActionsTestUtil.execPaths(depMetadataInputs))
+ .containsExactly(
+ depMetadataBaseDir + "/android.library-android.library-setter_store.bin",
+ depMetadataBaseDir + "/android.library-android.library-br.bin");
+ }
+
+ @Test
+ public void dataBindingAnnotationProcessorFlags() throws Exception {
+
+ writeDataBindingFiles();
+
+ ConfiguredTarget ctapp = getConfiguredTarget("//java/android/binary:app");
+ Set<Artifact> allArtifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(ctapp));
+ SpawnAction binCompileAction =
+ (SpawnAction) getGeneratingAction(getFirstArtifactEndingWith(allArtifacts, "app.jar"));
+ String dataBindingFilesDir =
+ targetConfig
+ .getBinDirectory(RepositoryName.MAIN)
+ .getExecPath()
+ .getRelative("java/android/binary/databinding/app")
+ .getPathString();
+ ImmutableList<String> expectedJavacopts =
+ ImmutableList.of(
+ "-Aandroid.databinding.bindingBuildFolder=" + dataBindingFilesDir,
+ "-Aandroid.databinding.generationalFileOutDir=" + dataBindingFilesDir,
+ "-Aandroid.databinding.sdkDir=/not/used",
+ "-Aandroid.databinding.artifactType=APPLICATION",
+ "-Aandroid.databinding.exportClassListTo=/tmp/exported_classes",
+ "-Aandroid.databinding.modulePackage=android.binary",
+ "-Aandroid.databinding.minApi=14",
+ "-Aandroid.databinding.printEncodedErrors=0",
+ "-Aandroid.databinding.enableV2=1");
+ assertThat(paramFileArgsForAction(binCompileAction)).containsAllIn(expectedJavacopts);
+
+ // Regression test for b/63134122
+ JavaCompileInfo javaCompileInfo =
+ binCompileAction
+ .getExtraActionInfo(actionKeyContext)
+ .getExtension(JavaCompileInfo.javaCompileInfo);
+ assertThat(javaCompileInfo.getJavacOptList()).containsAllIn(expectedJavacopts);
+ }
+
+ @Test
+ public void dataBindingIncludesTransitiveDepsForLibsWithNoResources() throws Exception {
+
+ writeDataBindingFilesWithNoResourcesDep();
+
+ ConfiguredTarget ct = getConfiguredTarget("//java/android/binary:app");
+ Set<Artifact> allArtifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(ct));
+
+ // Data binding resource processing outputs are expected for the app and libs with resources.
+ assertThat(
+ getFirstArtifactEndingWith(
+ allArtifacts, "databinding/lib_with_resource_files/layout-info.zip"))
+ .isNotNull();
+ assertThat(getFirstArtifactEndingWith(allArtifacts, "databinding/app/layout-info.zip"))
+ .isNotNull();
+
+ // Compiling the app's Java source includes data binding metadata from the resource-equipped
+ // lib, but not the resource-empty one.
+ SpawnAction binCompileAction =
+ (SpawnAction) getGeneratingAction(getFirstArtifactEndingWith(allArtifacts, "app.jar"));
+
+ List<String> appJarInputs = prettyArtifactNames(binCompileAction.getInputs());
+
+ String libWithResourcesMetadataBaseDir =
+ "java/android/binary/databinding/app/"
+ + "dependent-lib-artifacts/java/android/lib_with_resource_files/databinding/"
+ + "lib_with_resource_files/bin-files/android.lib_with_resource_files-";
+
+ assertThat(appJarInputs)
+ .containsAllOf(
+ "java/android/binary/databinding/app/layout-info.zip",
+ libWithResourcesMetadataBaseDir + "android.lib_with_resource_files-br.bin");
+
+ for (String compileInput : appJarInputs) {
+ assertThat(compileInput).doesNotMatch(".*lib_no_resource_files.*.bin");
+ }
+ }
+
+ @Test
+ public void libsWithNoResourcesOnlyRunAnnotationProcessor() throws Exception {
+
+ // Bazel skips resource processing because there are no new resources to process. But it still
+ // runs the annotation processor to ensure the Java compiler reads Java sources referenced by
+ // the deps' resources (e.g. "<variable type="some.package.SomeClass" />"). Without this,
+ // JavaBuilder's --reduce_classpath feature would strip out those sources as "unused" and fail
+ // the binary's compilation with unresolved symbol errors.
+ writeDataBindingFilesWithNoResourcesDep();
+
+ ConfiguredTarget ct = getConfiguredTarget("//java/android/lib_no_resource_files");
+ Iterable<Artifact> libArtifacts = getFilesToBuild(ct);
+
+ assertThat(getFirstArtifactEndingWith(libArtifacts, "_resources.jar")).isNull();
+ assertThat(getFirstArtifactEndingWith(libArtifacts, "layout-info.zip")).isNull();
+
+ SpawnAction libCompileAction =
+ (SpawnAction)
+ getGeneratingAction(
+ getFirstArtifactEndingWith(libArtifacts, "lib_no_resource_files.jar"));
+ // The annotation processor is attached to the Java compilation:
+ assertThat(paramFileArgsForAction(libCompileAction))
+ .containsAllOf(
+ "--processors", "android.databinding.annotationprocessor.ProcessDataBinding");
+ // The dummy .java file with annotations that trigger the annotation process is present:
+ assertThat(prettyArtifactNames(libCompileAction.getInputs()))
+ .contains(
+ "java/android/lib_no_resource_files/databinding/lib_no_resource_files/"
+ + "DataBindingInfo.java");
+ }
+
+ @Test
+ public void missingDataBindingAttributeStillAnalyzes() throws Exception {
+
+ // When a library is missing enable_data_binding = 1, we expect it to fail in execution (because
+ // aapt doesn't know how to read the data binding expressions). But analysis should work.
+ scratch.file(
+ "java/android/library/BUILD",
+ "android_library(",
+ " name = 'lib_with_data_binding',",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " srcs = ['MyLib.java'],",
+ " resource_files = [],",
+ ")");
+
+ scratch.file(
+ "java/android/library/MyLib.java", "package android.library; public class MyLib {};");
+
+ scratch.file(
+ "java/android/binary/BUILD",
+ "android_binary(",
+ " name = 'app',",
+ " enable_data_binding = 0,",
+ " manifest = 'AndroidManifest.xml',",
+ " srcs = ['MyApp.java'],",
+ " deps = ['//java/android/library:lib_with_data_binding'],",
+ ")");
+
+ scratch.file(
+ "java/android/binary/MyApp.java", "package android.binary; public class MyApp {};");
+
+ assertThat(getConfiguredTarget("//java/android/binary:app")).isNotNull();
+ }
+
+ @Test
+ public void dataBindingProviderIsProvided() throws Exception {
+
+ useConfiguration("--android_sdk=//sdk:sdk", "--experimental_android_databinding_v2");
+
+ scratch.file(
+ "sdk/BUILD",
+ "android_sdk(",
+ " name = 'sdk',",
+ " aapt = 'aapt',",
+ " aapt2 = 'aapt2',",
+ " adb = 'adb',",
+ " aidl = 'aidl',",
+ " android_jar = 'android.jar',",
+ " apksigner = 'apksigner',",
+ " dx = 'dx',",
+ " framework_aidl = 'framework_aidl',",
+ " main_dex_classes = 'main_dex_classes',",
+ " main_dex_list_creator = 'main_dex_list_creator',",
+ " proguard = 'proguard',",
+ " shrinked_android_jar = 'shrinked_android_jar',",
+ " zipalign = 'zipalign',",
+ " tags = ['__ANDROID_RULES_MIGRATION__'],",
+ ")");
+
+ scratch.file(
+ "java/a/BUILD",
+ "android_library(",
+ " name = 'a', ",
+ " srcs = ['A.java'],",
+ " enable_data_binding = 1,",
+ " manifest = 'a/AndroidManifest.xml',",
+ " resource_files = ['res/values/a.xml'],",
+ ")");
+
+ scratch.file(
+ "java/b/BUILD",
+ "android_library(",
+ " name = 'b', ",
+ " srcs = ['B.java'],",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " resource_files = ['res/values/a.xml'],",
+ ")");
+
+ ConfiguredTarget a = getConfiguredTarget("//java/a:a");
+ final DataBindingV2Provider dataBindingV2Provider = a.get(DataBindingV2Provider.PROVIDER);
+
+ assertThat(dataBindingV2Provider)
+ .named(DataBindingV2Provider.NAME)
+ .isNotNull();
+
+ assertThat(
+ dataBindingV2Provider
+ .getSetterStores()
+ .stream()
+ .map(Artifact::getRootRelativePathString)
+ .collect(Collectors.toList()))
+ .containsExactly("java/a/databinding/a/bin-files/a-a-setter_store.bin");
+
+ assertThat(
+ dataBindingV2Provider
+ .getClassInfos()
+ .stream()
+ .map(Artifact::getRootRelativePathString)
+ .collect(Collectors.toList()))
+ .containsExactly("java/a/databinding/a/class-info.zip");
+
+ assertThat(
+ dataBindingV2Provider
+ .getTransitiveBRFiles()
+ .toCollection()
+ .stream()
+ .map(Artifact::getRootRelativePathString)
+ .collect(Collectors.toList()))
+ .containsExactly("java/a/databinding/a/bin-files/a-a-br.bin");
+ }
+
+ @Test
+ public void ensureDataBindingProviderIsPropagatedThroughNonDataBindingLibs() throws Exception {
+
+ useConfiguration("--android_sdk=//sdk:sdk", "--experimental_android_databinding_v2");
+
+ scratch.file(
+ "sdk/BUILD",
+ "android_sdk(",
+ " name = 'sdk',",
+ " aapt = 'aapt',",
+ " aapt2 = 'aapt2',",
+ " adb = 'adb',",
+ " aidl = 'aidl',",
+ " android_jar = 'android.jar',",
+ " apksigner = 'apksigner',",
+ " dx = 'dx',",
+ " framework_aidl = 'framework_aidl',",
+ " main_dex_classes = 'main_dex_classes',",
+ " main_dex_list_creator = 'main_dex_list_creator',",
+ " proguard = 'proguard',",
+ " shrinked_android_jar = 'shrinked_android_jar',",
+ " zipalign = 'zipalign',",
+ " tags = ['__ANDROID_RULES_MIGRATION__'],",
+ ")");
+ scratch.file(
+ "java/a/BUILD",
+ "android_library(",
+ " name = 'a', ",
+ " srcs = ['A.java'],",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " resource_files = ['res/values/a.xml'],",
+ ")");
+ scratch.file(
+ "java/b/BUILD",
+ "android_library(",
+ " name = 'b', ",
+ " srcs = ['B.java'],",
+ " deps = ['//java/a:a'],",
+ ")");
+
+ ConfiguredTarget b = getConfiguredTarget("//java/b:b");
+ Truth.assertThat(b.get(DataBindingV2Provider.PROVIDER))
+ .named("DataBindingV2Info")
+ .isNotNull();
+ }
+
+ @Test
+ public void testDataBindingCollectedThroughExports() throws Exception {
+
+ useConfiguration("--android_sdk=//sdk:sdk", "--experimental_android_databinding_v2");
+
+ scratch.file(
+ "sdk/BUILD",
+ "android_sdk(",
+ " name = 'sdk',",
+ " aapt = 'aapt',",
+ " aapt2 = 'aapt2',",
+ " adb = 'adb',",
+ " aidl = 'aidl',",
+ " android_jar = 'android.jar',",
+ " apksigner = 'apksigner',",
+ " dx = 'dx',",
+ " framework_aidl = 'framework_aidl',",
+ " main_dex_classes = 'main_dex_classes',",
+ " main_dex_list_creator = 'main_dex_list_creator',",
+ " proguard = 'proguard',",
+ " shrinked_android_jar = 'shrinked_android_jar',",
+ " zipalign = 'zipalign',",
+ " tags = ['__ANDROID_RULES_MIGRATION__'],",
+ ")");
+
+ scratch.file(
+ "java/a/BUILD",
+ "android_library(",
+ " name = 'a', ",
+ " srcs = ['A.java'],",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " resource_files = ['res/values/a.xml'],",
+ ")");
+
+ scratch.file(
+ "java/b/BUILD",
+ "android_library(",
+ " name = 'b', ",
+ " srcs = ['B.java'],",
+ " enable_data_binding = 1,",
+ " manifest = 'AndroidManifest.xml',",
+ " resource_files = ['res/values/a.xml'],",
+ ")");
+
+ scratch.file(
+ "java/c/BUILD",
+ "android_library(",
+ " name = 'c', ",
+ " exports = ['//java/a:a', '//java/b:b']",
+ ")");
+
+ ConfiguredTarget c = getConfiguredTarget("//java/c:c");
+ DataBindingV2Provider provider = c.get(DataBindingV2Provider.PROVIDER);
+
+ assertThat(
+ provider
+ .getClassInfos()
+ .stream()
+ .map(Artifact::getRootRelativePathString)
+ .collect(Collectors.toList()))
+ .containsExactly(
+ "java/a/databinding/a/class-info.zip",
+ "java/b/databinding/b/class-info.zip");
+
+ assertThat(
+ provider
+ .getSetterStores()
+ .stream()
+ .map(Artifact::getRootRelativePathString)
+ .collect(Collectors.toList()))
+ .containsExactly(
+ "java/a/databinding/a/bin-files/a-a-setter_store.bin",
+ "java/b/databinding/b/bin-files/b-b-setter_store.bin");
+
+ assertThat(
+ provider
+ .getTransitiveBRFiles()
+ .toCollection()
+ .stream()
+ .map(Artifact::getRootRelativePathString)
+ .collect(Collectors.toList()))
+ .containsExactly(
+ "java/a/databinding/a/bin-files/a-a-br.bin",
+ "java/b/databinding/b/bin-files/b-b-br.bin");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidResourcesTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidResourcesTest.java
index fcdef8b..6cb9ea0 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidResourcesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidResourcesTest.java
@@ -557,7 +557,10 @@
private ParsedAndroidResources makeParsedResources(RuleContext ruleContext)
throws RuleErrorException, InterruptedException {
- return makeParsedResources(ruleContext, DataBinding.asDisabledDataBindingContext());
+ DataBindingContext dataBindingContext =
+ DataBinding.contextFrom(ruleContext,
+ ruleContext.getConfiguration().getFragment(AndroidConfiguration.class));
+ return makeParsedResources(ruleContext, dataBindingContext);
}
private ParsedAndroidResources makeParsedResources(
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/ResourceTestBase.java b/src/test/java/com/google/devtools/build/lib/rules/android/ResourceTestBase.java
index 0d48adb..f46fd11 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/ResourceTestBase.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/ResourceTestBase.java
@@ -258,7 +258,7 @@
includeAapt2Outs ? getOutput("symbols.zip") : null,
manifest.getManifest().getOwnerLabel(),
manifest,
- DataBinding.asDisabledDataBindingContext()),
+ DataBinding.DISABLED_V1_CONTEXT),
getOutput("merged/resources.zip"),
getOutput("class.jar"),
/* dataBindingInfoZip = */ null,
diff --git a/tools/android/BUILD.tools b/tools/android/BUILD.tools
index 427b33e..c5c3a5e 100644
--- a/tools/android/BUILD.tools
+++ b/tools/android/BUILD.tools
@@ -321,6 +321,14 @@
srcs = ["bazel_debug.keystore"],
)
+java_binary(
+ name = "exec_binary",
+ main_class = "android.databinding.AndroidDataBinding",
+ runtime_deps = [
+ "//third_party/java/android_databinding:exec",
+ ],
+)
+
alias(
name = "databinding_annotation_processor",
actual = "//external:databinding_annotation_processor",