// Copyright 2016 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.rules.android; import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; 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.java.JavaTargetAttributes; 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; /** * Support logic for Bazel's * data binding * integration. * *
In short, data binding in Bazel works as follows: *
DataBindingInfo.java
which specifies the path to the
* layout info file (as an annotation). The processor reads that file and produces the
* corresponding Java classes that end-user code uses to access the resources.For data binding to work, the corresponding support libraries must be checked into the depot * via the implicit dependencies specified inside this class. * *
Unless otherwise specified, all methods in this class assume the current rule applies data
* binding. Callers can intelligently trigger this logic by checking {@link #isEnabled}.
*
*/
public final class DataBinding {
/**
* The rule attribute supplying data binding's annotation processor.
*/
public static final String DATABINDING_ANNOTATION_PROCESSOR_ATTR =
"$databinding_annotation_processor";
/**
* 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 Data binding incurs additional resource processing and compilation work as well as
* additional compile/runtime dependencies. But rules with data binding disabled will fail if
* data binding expressions appear in their layout resources.
*/
public static boolean isEnabled(RuleContext ruleContext) {
return ruleContext.attributes().has("enable_data_binding", Type.BOOLEAN)
&& ruleContext.attributes().get("enable_data_binding", Type.BOOLEAN);
}
/**
* Returns this rule's data binding base output dir (as an execroot-relative path).
*/
private static PathFragment getDataBindingExecPath(RuleContext ruleContext) {
return ruleContext.getBinOrGenfilesDirectory().getExecPath().getRelative(
ruleContext.getUniqueDirectory("databinding"));
}
/**
* Returns an artifact for the specified output under a standardized data binding base dir.
*/
private static Artifact getDataBindingArtifact(RuleContext ruleContext, String relativePath) {
PathFragment binRelativeBasePath = getDataBindingExecPath(ruleContext)
.relativeTo(ruleContext.getBinOrGenfilesDirectory().getExecPath());
return ruleContext.getDerivedArtifact(binRelativeBasePath.getRelative(relativePath),
ruleContext.getBinOrGenfilesDirectory());
}
/**
* Returns the file where data binding's resource processing produces binding xml. For
* example, given:
*
* data binding strips out and processes this part:
*
* This, in conjunction with {@link #createAnnotationFile} extends the Java compilation to
* translate data binding .xml into corresponding classes.
*/
static void addAnnotationProcessor(
RuleContext ruleContext, JavaTargetAttributes.Builder attributes) {
JavaPluginInfoProvider plugin =
JavaInfo.getProvider(
JavaPluginInfoProvider.class,
ruleContext.getPrerequisite(
DATABINDING_ANNOTATION_PROCESSOR_ATTR, RuleConfiguredTarget.Mode.HOST));
for (String name : plugin.getProcessorClasses()) {
// For header compilation (see JavaHeaderCompileAction):
attributes.addApiGeneratingProcessorName(name);
// For full compilation:
attributes.addProcessorName(name);
}
// For header compilation (see JavaHeaderCompileAction):
attributes.addApiGeneratingProcessorPath(plugin.getProcessorClasspath());
// For full compilation:
attributes.addProcessorPath(plugin.getProcessorClasspath());
attributes.addAdditionalOutputs(getMetadataOutputs(ruleContext));
}
/**
* The javac flags that are needed to configure data binding's annotation processor.
*/
static ImmutableList This mostly just triggers the annotation processor. Annotation processor settings
* are configured separately in {@link #getJavacopts}.
*/
static Artifact createAnnotationFile(RuleContext ruleContext) {
String contents;
try {
contents = ResourceFileLoader.loadResource(DataBinding.class,
"databinding_annotation_template.txt");
} catch (IOException e) {
ruleContext.ruleError("Cannot load annotation processor template: " + e.getMessage());
return null;
}
Artifact output = getDataBindingArtifact(ruleContext, "DataBindingInfo.java");
ruleContext.registerAction(FileWriteAction.create(ruleContext, output, contents, false));
return output;
}
/**
* 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}.
*/
public static void maybeAddProvider(RuleConfiguredTargetBuilder builder,
RuleContext ruleContext) {
// Expose the data binding provider if this rule either applies data binding or exports a dep
// that applies it.
List >For example, if {@code foo.AndroidBinary} depends on {@code foo.lib.AndroidLibrary} and
* the library defines data binding expression {@code Bar}, compiling the library produces Java
* class {@code foo.lib.Bar}. But since the binary applies data binding over the merged resources
* of its deps, that means the binary also sees {@code Bar}, so it compiles it into
* {@code foo.Bar}. This 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{@code
*
*
* {@code
*
*
*
* for each layout file with data binding expressions. Since this may produce multiple
* files, outputs are zipped up into a single container.
*/
static Artifact getLayoutInfoFile(RuleContext ruleContext) {
// The data binding library expects this to be called "layout-info.zip".
return ruleContext.getUniqueDirectoryArtifact("databinding", "layout-info.zip",
ruleContext.getBinOrGenfilesDirectory());
}
/**
* Adds data binding's annotation processor as a plugin to the given Java compilation context.
*
*