diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index 943dee6176f0d4..38fba4feffd434 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD @@ -1030,12 +1030,15 @@ java_library( java_library( name = "android-rules", srcs = glob( - ["rules/android/*.java"], + [ + "rules/android/*.java", + "rules/android/databinding/*.java", + ], ), resources = [ "rules/android/android_device_stub_template.txt", "rules/android/android_instrumentation_test_template.txt", - "rules/android/databinding_annotation_template.txt", + "rules/android/databinding/databinding_annotation_template.txt", "rules/android/robolectric_properties_template.txt", "rules/android/test_suite_property_name.txt", ], diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java b/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java index f8e28826a64c64..58e9011d0a3e41 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java @@ -32,6 +32,7 @@ import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; import com.google.devtools.build.lib.rules.java.ImportDepsCheckActionBuilder; import com.google.devtools.build.lib.rules.java.JavaCommon; import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java index 8d0a5d4d9f8b7a..4c0cb343eddac3 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java @@ -65,6 +65,7 @@ import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode; import com.google.devtools.build.lib.rules.android.ZipFilterBuilder.CheckHashMismatchMode; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; import com.google.devtools.build.lib.rules.cpp.CppSemantics; import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder; import com.google.devtools.build.lib.rules.java.JavaCommon; 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 2270052327e931..0ac3bfa1c8a470 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 @@ -45,8 +45,8 @@ import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.packages.TriState; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; import com.google.devtools.build.lib.rules.android.ZipFilterBuilder.CheckHashMismatchMode; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import com.google.devtools.build.lib.rules.cpp.CcInfo; import com.google.devtools.build.lib.rules.cpp.CcLinkParams; import com.google.devtools.build.lib.rules.cpp.CcLinkingInfo; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java index 5361bfb01b2056..fd1e89cdf2dda1 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java @@ -30,6 +30,7 @@ import com.google.devtools.build.lib.packages.TriState; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.AndroidLibraryAarInfo.Aar; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; import com.google.devtools.build.lib.rules.java.JavaCommon; import com.google.devtools.build.lib.rules.java.JavaSemantics; import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java index 528160fbb3ece5..2640ed2aa76c1b 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java @@ -38,7 +38,8 @@ import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.RuleErrorConsumer; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import com.google.devtools.build.lib.rules.java.ClasspathConfiguredFragment; import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder; import com.google.devtools.build.lib.rules.java.JavaCommon; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceParsingActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceParsingActionBuilder.java index f152265a37f6e6..e0c701a47aae34 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceParsingActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResourceParsingActionBuilder.java @@ -19,7 +19,7 @@ import com.google.common.collect.Streams; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import com.google.devtools.build.lib.vfs.PathFragment; import javax.annotation.Nullable; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java index 654397b4bfbbcc..9af441e23244cd 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java @@ -29,7 +29,7 @@ import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.packages.RuleErrorConsumer; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Arrays; import java.util.LinkedHashSet; 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 398d4397c2fa9c..724f9893cbb008 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 @@ -18,7 +18,7 @@ import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.AndroidDataConverter.JoinerType; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; import java.util.Collections; 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 ffe9513a735c7d..407f73763bb304 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 @@ -49,6 +49,7 @@ import com.google.devtools.build.lib.packages.TriState; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.ConfigurationDistinguisher; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; import com.google.devtools.build.lib.rules.config.ConfigFeatureFlagProvider; import com.google.devtools.build.lib.rules.cpp.CppConfiguration; import com.google.devtools.build.lib.rules.cpp.CppOptions; 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 918786a40dec3c..d816107afab44c 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 @@ -32,6 +32,7 @@ import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; import com.google.devtools.build.lib.rules.android.AndroidLibraryAarInfo.Aar; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; import com.google.devtools.build.lib.rules.java.JavaCompilationInfoProvider; import com.google.devtools.build.lib.rules.java.JavaInfo; 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 78cb128b5b84b2..256d6f3329d2ee 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 @@ -21,7 +21,7 @@ import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.packages.RuleErrorConsumer; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ProcessedAndroidData.java b/src/main/java/com/google/devtools/build/lib/rules/android/ProcessedAndroidData.java index fec7ff6e2a0d44..68eea318f58445 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ProcessedAndroidData.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/ProcessedAndroidData.java @@ -19,7 +19,8 @@ import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.packages.RuleErrorConsumer; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import com.google.devtools.build.lib.rules.java.ProguardHelper; import com.google.devtools.build.lib.syntax.Type; import java.util.List; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceApk.java b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceApk.java index bfba2218bfbfe2..67d7d58299fb9a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceApk.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceApk.java @@ -18,7 +18,7 @@ import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import java.util.Optional; import javax.annotation.Nullable; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/DataBinding.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBinding.java similarity index 50% rename from src/main/java/com/google/devtools/build/lib/rules/android/DataBinding.java rename to src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBinding.java index cdbd2724a2dcc9..6285a1a56ec11f 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/DataBinding.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBinding.java @@ -11,30 +11,23 @@ // 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; +package com.google.devtools.build.lib.rules.android.databinding; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; 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.java.JavaInfo; -import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider; +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.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.ArrayList; import java.util.List; -import java.util.Objects; -import java.util.function.BiConsumer; -import java.util.function.Consumer; /** * Support logic for Bazel's {@code - * - * - * - * - * - * - * ... - * - * } - * - *

data binding strips out and processes this part: - * - *

{@code
-     * 
-     *   
-     * 
-     * }
- * - * for each layout file with data binding expressions. Since this may produce multiple files, - * outputs are zipped up into a single container. - */ - default void supplyLayoutInfo(Consumer consumer) {} - - /** The javac flags that are needed to configure data binding's annotation processor. */ - default void supplyJavaCoptsUsing( - RuleContext ruleContext, boolean isBinary, Consumer> consumer) {} - - /** - * Adds data binding's annotation processor as a plugin to the given Java compilation context. - * - *

This extends the Java compilation to translate data binding .xml into corresponding - * classes. - */ - default void supplyAnnotationProcessor( - RuleContext ruleContext, BiConsumer> consumer) {} - - /** - * Processes deps that also apply data binding. - * - * @param ruleContext the current rule - * @return the deps' metadata outputs. These need to be staged as compilation inputs to the - * current rule. - */ - default ImmutableList processDeps(RuleContext ruleContext) { - return ImmutableList.of(); - } - - /** - * Creates and adds the generated Java source for data binding annotation processor to read and - * translate layout info xml (from {@link #supplyLayoutInfo(Consumer)} into the classes that end - * user code consumes. - * - *

This triggers the annotation processor. Annotation processor settings are configured - * separately in {@link #supplyJavaCoptsUsing(RuleContext, boolean, Consumer)}. - */ - default ImmutableList addAnnotationFileToSrcs( - ImmutableList srcs, RuleContext ruleContext) { - return srcs; - }; - - /** - * Adds the appropriate {@link UsesDataBindingProvider} for a rule if it should expose one. - * - *

A rule exposes {@link UsesDataBindingProvider} if either it or its deps set {@code - * enable_data_binding = 1}. - */ - default void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) { - maybeAddProvider(new ArrayList<>(), builder, ruleContext); - } - - default AndroidResources processResources(AndroidResources resources) { - return resources; - } - } - - private static final class EnabledDataBindingV1Context implements DataBindingContext { - - private final ActionConstructionContext actionConstructionContext; - - private EnabledDataBindingV1Context(ActionConstructionContext actionConstructionContext) { - this.actionConstructionContext = actionConstructionContext; - } - - @Override - public void supplyLayoutInfo(Consumer consumer) { - consumer.accept(layoutInfoFile()); - } - - Artifact layoutInfoFile() { - return actionConstructionContext.getUniqueDirectoryArtifact("databinding", "layout-info.zip"); - } - - @Override - public void supplyJavaCoptsUsing( - RuleContext ruleContext, boolean isBinary, Consumer> consumer) { - ImmutableList.Builder flags = ImmutableList.builder(); - String metadataOutputDir = 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")); - - // 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())); - - // 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. - flags.add(createProcessorFlag("minApi", "14")); // TODO(gregce): update this - - // If enabled, produces cleaner output for Android Studio. - flags.add(createProcessorFlag("printEncodedErrors", "0")); - - consumer.accept(flags.build()); - } - - @Override - public void supplyAnnotationProcessor( - RuleContext ruleContext, BiConsumer> consumer) { - consumer.accept( - JavaInfo.getProvider( - JavaPluginInfoProvider.class, - ruleContext.getPrerequisite( - DATABINDING_ANNOTATION_PROCESSOR_ATTR, RuleConfiguredTarget.Mode.HOST)), - getMetadataOutputs(ruleContext)); - } - - @Override - public ImmutableList processDeps(RuleContext ruleContext) { - ImmutableList.Builder dataBindingJavaInputs = ImmutableList.builder(); - if (AndroidResources.definesAndroidResources(ruleContext.attributes())) { - dataBindingJavaInputs.add(layoutInfoFile()); - } - for (Artifact dataBindingDepMetadata : getTransitiveMetadata(ruleContext, "deps")) { - dataBindingJavaInputs.add( - symlinkDepsMetadataIntoOutputTree(ruleContext, dataBindingDepMetadata)); - } - return dataBindingJavaInputs.build(); - } - - @Override - public ImmutableList addAnnotationFileToSrcs( - ImmutableList 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 = getDataBindingArtifact(ruleContext, "DataBindingInfo.java"); - ruleContext.registerAction( - FileWriteAction.create(ruleContext, annotationFile, contents, false)); - return ImmutableList.builder().addAll(srcs).add(annotationFile).build(); - } catch (IOException e) { - ruleContext.ruleError("Cannot load annotation processor template: " + e.getMessage()); - return ImmutableList.of(); - } - } - - @Override - public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) { - List dataBindingMetadataOutputs = - Lists.newArrayList(getMetadataOutputs(ruleContext)); - maybeAddProvider(dataBindingMetadataOutputs, builder, ruleContext); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - EnabledDataBindingV1Context that = (EnabledDataBindingV1Context) o; - return Objects.equals(actionConstructionContext, that.actionConstructionContext); - } - - @Override - public int hashCode() { - return actionConstructionContext.hashCode(); - } - - @Override - public AndroidResources processResources(AndroidResources resources) { - return resources; - } - } - - private static class EnabledDataBindingV2Context implements DataBindingContext { - - private final ActionConstructionContext actionContext; - - private EnabledDataBindingV2Context(ActionConstructionContext actionContext) { - this.actionContext = actionContext; - throw new UnsupportedOperationException("V2 not implemented yet."); - } - // TODO(b/112038432): Enable databinding v2. - } - - private static final DataBindingContext DISABLED_CONTEXT = new DataBindingContext() {}; + private static final DataBindingContext DISABLED_CONTEXT = new DisabledDataBindingV1Context(); /** Supplies a databinding context from a rulecontext. */ public static DataBindingContext contextFrom( @@ -324,12 +88,12 @@ public static DataBindingContext contextFrom( /** Supplies an enabled DataBindingContext from the action context. */ private static DataBindingContext asEnabledDataBindingV1ContextFrom( ActionConstructionContext actionContext) { - return new EnabledDataBindingV1Context(actionContext); + return new DataBindingV1Context(actionContext); } private static DataBindingContext asEnabledDataBindingV2ContextFrom( ActionConstructionContext actionContext) { - return new EnabledDataBindingV2Context(actionContext); + return new DataBindingV2Context(actionContext); } /** Supplies a disabled (no-op) DataBindingContext. */ @@ -364,7 +128,7 @@ private static boolean isEnabled(RuleContext ruleContext) { } /** Returns this rule's data binding base output dir (as an execroot-relative path). */ - private static PathFragment getDataBindingExecPath(RuleContext ruleContext) { + static PathFragment getDataBindingExecPath(RuleContext ruleContext) { return ruleContext .getBinOrGenfilesDirectory() .getExecPath() @@ -372,7 +136,7 @@ private static PathFragment getDataBindingExecPath(RuleContext ruleContext) { } /** Returns an artifact for the specified output under a standardized data binding base dir. */ - private static Artifact getDataBindingArtifact(RuleContext ruleContext, String relativePath) { + static Artifact getDataBindingArtifact(RuleContext ruleContext, String relativePath) { PathFragment binRelativeBasePath = getDataBindingExecPath(ruleContext) .relativeTo(ruleContext.getBinOrGenfilesDirectory().getExecPath()); @@ -381,7 +145,7 @@ private static Artifact getDataBindingArtifact(RuleContext ruleContext, String r } /** Turns a key/value pair into a javac annotation processor flag received by data binding. */ - private static String createProcessorFlag(String flag, String value) { + static String createProcessorFlag(String flag, String value) { return String.format("-Aandroid.databinding.%s=%s", flag, value); } @@ -391,7 +155,7 @@ private static String createProcessorFlag(String flag, String value) { *

A rule exposes {@link UsesDataBindingProvider} if either it or its deps set {@code * enable_data_binding = 1}. */ - private static void maybeAddProvider( + static void maybeAddProvider( List dataBindingMetadataOutputs, RuleConfiguredTargetBuilder builder, RuleContext ruleContext) { @@ -409,7 +173,7 @@ private static void maybeAddProvider( } /** Returns the data binding resource processing output from deps under the given attribute. */ - private static List getTransitiveMetadata(RuleContext ruleContext, String attr) { + static List getTransitiveMetadata(RuleContext ruleContext, String attr) { ImmutableList.Builder dataBindingMetadataOutputs = ImmutableList.builder(); if (ruleContext.attributes().has(attr, BuildType.LABEL_LIST)) { for (UsesDataBindingProvider provider : @@ -432,7 +196,7 @@ private static List getTransitiveMetadata(RuleContext ruleContext, Str * 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. */ - private static List getMetadataOutputs(RuleContext ruleContext) { + static List getMetadataOutputs(RuleContext ruleContext) { 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. @@ -459,7 +223,7 @@ private static List getMetadataOutputs(RuleContext ruleContext) { * * @return the symlink paths of the transitive dep metadata outputs for this rule */ - private static Artifact symlinkDepsMetadataIntoOutputTree( + static Artifact symlinkDepsMetadataIntoOutputTree( RuleContext ruleContext, Artifact depMetadata) { Label ruleLabel = ruleContext.getRule().getLabel(); Artifact 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 new file mode 100644 index 00000000000000..95802c298165c3 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingContext.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.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.rules.android.AndroidResources; +import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** Contains Android Databinding configuration and resource generation information. */ +public interface DataBindingContext { + + /** + * Returns the file where data binding's resource processing produces binding xml. For example, + * given: + * + *

{@code
+   * 
+   *   
+   *     
+   *   
+   * 
+   * 
+   *   ...
+   * 
+   * }
+ * + *

data binding strips out and processes this part: + * + *

{@code
+   * 
+   *   
+   * 
+   * }
+ * + * for each layout file with data binding expressions. Since this may produce multiple files, + * outputs are zipped up into a single container. + */ + void supplyLayoutInfo(Consumer consumer); + + /** The javac flags that are needed to configure data binding's annotation processor. */ + void supplyJavaCoptsUsing( + RuleContext ruleContext, boolean isBinary, Consumer> consumer); + + /** + * Adds data binding's annotation processor as a plugin to the given Java compilation context. + * + *

This extends the Java compilation to translate data binding .xml into corresponding + * classes. + */ + void supplyAnnotationProcessor( + RuleContext ruleContext, BiConsumer> consumer); + + /** + * Processes deps that also apply data binding. + * + * @param ruleContext the current rule + * @return the deps' metadata outputs. These need to be staged as compilation inputs to the + * current rule. + */ + ImmutableList processDeps(RuleContext ruleContext); + + /** + * Creates and adds the generated Java source for data binding annotation processor to read and + * translate layout info xml (from {@link #supplyLayoutInfo(Consumer)} into the classes that end + * user code consumes. + * + *

This triggers the annotation processor. Annotation processor settings are configured + * separately in {@link #supplyJavaCoptsUsing(RuleContext, boolean, Consumer)}. + */ + ImmutableList addAnnotationFileToSrcs( + ImmutableList srcs, RuleContext ruleContext); + + /** + * Adds the appropriate {@link UsesDataBindingProvider} for a rule if it should expose one. + * + *

A rule exposes {@link UsesDataBindingProvider} if either it or its deps set {@code + * enable_data_binding = 1}. + */ + void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext); + + AndroidResources processResources(AndroidResources resources); +} 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 new file mode 100644 index 00000000000000..bea5df8d045473 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV1Context.java @@ -0,0 +1,173 @@ +// 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 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; +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.configuredtargets.RuleConfiguredTarget; +import com.google.devtools.build.lib.rules.android.AndroidCommon; +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 { + + private final ActionConstructionContext actionConstructionContext; + + DataBindingV1Context(ActionConstructionContext actionConstructionContext) { + this.actionConstructionContext = actionConstructionContext; + } + + @Override + public void supplyLayoutInfo(Consumer consumer) { + consumer.accept(layoutInfoFile()); + } + + Artifact layoutInfoFile() { + return actionConstructionContext.getUniqueDirectoryArtifact("databinding", "layout-info.zip"); + } + + @Override + public void supplyJavaCoptsUsing( + RuleContext ruleContext, boolean isBinary, Consumer> consumer) { + ImmutableList.Builder flags = ImmutableList.builder(); + String metadataOutputDir = 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")); + + // 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())); + + // 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. + flags.add(createProcessorFlag("minApi", "14")); // TODO(gregce): update this + + // If enabled, produces cleaner output for Android Studio. + flags.add(createProcessorFlag("printEncodedErrors", "0")); + + consumer.accept(flags.build()); + } + + @Override + public void supplyAnnotationProcessor( + RuleContext ruleContext, BiConsumer> consumer) { + consumer.accept( + JavaInfo.getProvider( + JavaPluginInfoProvider.class, + ruleContext.getPrerequisite( + DataBinding.DATABINDING_ANNOTATION_PROCESSOR_ATTR, RuleConfiguredTarget.Mode.HOST)), + DataBinding.getMetadataOutputs(ruleContext)); + } + + @Override + public ImmutableList processDeps(RuleContext ruleContext) { + ImmutableList.Builder dataBindingJavaInputs = ImmutableList.builder(); + if (AndroidResources.definesAndroidResources(ruleContext.attributes())) { + dataBindingJavaInputs.add(layoutInfoFile()); + } + for (Artifact dataBindingDepMetadata : DataBinding.getTransitiveMetadata(ruleContext, "deps")) { + dataBindingJavaInputs.add( + DataBinding.symlinkDepsMetadataIntoOutputTree(ruleContext, dataBindingDepMetadata)); + } + return dataBindingJavaInputs.build(); + } + + @Override + public ImmutableList addAnnotationFileToSrcs( + ImmutableList 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.builder().addAll(srcs).add(annotationFile).build(); + } catch (IOException e) { + ruleContext.ruleError("Cannot load annotation processor template: " + e.getMessage()); + return ImmutableList.of(); + } + } + + @Override + public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) { + List dataBindingMetadataOutputs = + Lists.newArrayList(DataBinding.getMetadataOutputs(ruleContext)); + DataBinding.maybeAddProvider(dataBindingMetadataOutputs, builder, ruleContext); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DataBindingV1Context that = (DataBindingV1Context) o; + return Objects.equals(actionConstructionContext, that.actionConstructionContext); + } + + @Override + public int hashCode() { + return actionConstructionContext.hashCode(); + } + + @Override + public AndroidResources processResources(AndroidResources resources) { + 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 new file mode 100644 index 00000000000000..28661d572c5945 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java @@ -0,0 +1,75 @@ +// 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.actions.ActionConstructionContext; +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 DataBindingV2Context implements DataBindingContext { + + // TODO(b/112038432): Enable databinding v2. + @SuppressWarnings("unused") + 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 consumer) { + + } + + @Override + public void supplyJavaCoptsUsing(RuleContext ruleContext, boolean isBinary, + Consumer> consumer) { + + } + + @Override + public void supplyAnnotationProcessor(RuleContext ruleContext, + BiConsumer> consumer) { + + } + + @Override + public ImmutableList processDeps(RuleContext ruleContext) { + return null; + } + + @Override + public ImmutableList addAnnotationFileToSrcs(ImmutableList srcs, + RuleContext ruleContext) { + return null; + } + + @Override + public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) { + + } + + @Override + public AndroidResources processResources(AndroidResources resources) { + return null; + } +} 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 new file mode 100644 index 00000000000000..cae89c4fb27aa5 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DisabledDataBindingV1Context.java @@ -0,0 +1,61 @@ +// 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.rules.android.AndroidResources; +import com.google.devtools.build.lib.rules.java.JavaPluginInfoProvider; +import java.util.ArrayList; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +class DisabledDataBindingV1Context implements DataBindingContext { + + @Override + public void supplyJavaCoptsUsing(RuleContext ruleContext, boolean isBinary, + Consumer> consumer) { } + + @Override + public void supplyAnnotationProcessor(RuleContext ruleContext, + BiConsumer> consumer) { } + + @Override + public ImmutableList processDeps(RuleContext ruleContext) { + return ImmutableList.of(); + } + + @Override + public ImmutableList addAnnotationFileToSrcs( + ImmutableList srcs, RuleContext ruleContext) { + return srcs; + }; + + @Override + public void addProvider(RuleConfiguredTargetBuilder builder, RuleContext ruleContext) { + DataBinding.maybeAddProvider(new ArrayList<>(), builder, ruleContext); + } + + @Override + public AndroidResources processResources(AndroidResources resources) { + return resources; + } + + @Override + public void supplyLayoutInfo(Consumer consumer) { + + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/UsesDataBindingProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/UsesDataBindingProvider.java similarity index 97% rename from src/main/java/com/google/devtools/build/lib/rules/android/UsesDataBindingProvider.java rename to src/main/java/com/google/devtools/build/lib/rules/android/databinding/UsesDataBindingProvider.java index 87cd49a26cf8bf..525bbaca4293ca 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/UsesDataBindingProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/UsesDataBindingProvider.java @@ -11,7 +11,7 @@ // 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; +package com.google.devtools.build.lib.rules.android.databinding; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding_annotation_template.txt b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/databinding_annotation_template.txt similarity index 100% rename from src/main/java/com/google/devtools/build/lib/rules/android/databinding_annotation_template.txt rename to src/main/java/com/google/devtools/build/lib/rules/android/databinding/databinding_annotation_template.txt diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingTest.java index 88f46bb487806b..1d1d59c0714594 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingTest.java @@ -26,6 +26,7 @@ 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.UsesDataBindingProvider; import java.util.List; import java.util.Set; import java.util.stream.Collectors; 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 a57588a1f165d2..fcdef8b2fe0014 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 @@ -25,7 +25,8 @@ import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion; -import com.google.devtools.build.lib.rules.android.DataBinding.DataBindingContext; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingContext; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Optional; import org.junit.Before; 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 0025cb28452196..0d48adba5d64a5 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 @@ -35,6 +35,7 @@ import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.packages.RuleErrorConsumer; +import com.google.devtools.build.lib.rules.android.databinding.DataBinding; import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.Path;