Create a base implementation of GenRule that can be used by both Bazel and
internal Google code.
--
PiperOrigin-RevId: 145795255
MOS_MIGRATED_REVID=145795255
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 4838cb9..72ba580 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -32,6 +32,7 @@
"//src/main/java/com/google/devtools/build/lib/rules/cpp:srcs",
"//src/main/java/com/google/devtools/build/lib/rules/cpp/proto:srcs",
"//src/main/java/com/google/devtools/build/lib/rules/genquery:srcs",
+ "//src/main/java/com/google/devtools/build/lib/rules/genrule:srcs",
"//src/main/java/com/google/devtools/build/lib/rules/objc:srcs",
"//src/main/java/com/google/devtools/build/lib/sandbox:srcs",
"//src/main/java/com/google/devtools/build/lib/ssd:srcs",
@@ -570,6 +571,7 @@
"//src/main/java/com/google/devtools/build/lib/rules/cpp",
"//src/main/java/com/google/devtools/build/lib/rules/cpp/proto:CcProtoLibrary",
"//src/main/java/com/google/devtools/build/lib/rules/genquery",
+ "//src/main/java/com/google/devtools/build/lib/rules/genrule",
"//src/main/java/com/google/devtools/build/lib/rules/objc",
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/common/options",
@@ -1112,6 +1114,7 @@
"//src/main/java/com/google/devtools/build/lib/rules/apple/cpp:srcs",
"//src/main/java/com/google/devtools/build/lib/rules/cpp:srcs",
"//src/main/java/com/google/devtools/build/lib/rules/genquery:srcs",
+ "//src/main/java/com/google/devtools/build/lib/rules/genrule:srcs",
"//src/main/java/com/google/devtools/build/lib/rules/objc:srcs",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java b/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
index 3ffc18c..c24f659 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/CommandHelper.java
@@ -99,7 +99,7 @@
public CommandHelper(
RuleContext ruleContext,
Iterable<? extends TransitiveInfoCollection> tools,
- ImmutableMap<Label, Iterable<Artifact>> labelMap) {
+ ImmutableMap<Label, ? extends Iterable<Artifact>> labelMap) {
this.ruleContext = ruleContext;
ImmutableList.Builder<Artifact> resolvedToolsBuilder = ImmutableList.builder();
@@ -107,7 +107,7 @@
ImmutableMap.builder();
Map<Label, Collection<Artifact>> tempLabelMap = new HashMap<>();
- for (Map.Entry<Label, Iterable<Artifact>> entry : labelMap.entrySet()) {
+ for (Map.Entry<Label, ? extends Iterable<Artifact>> entry : labelMap.entrySet()) {
Iterables.addAll(mapGet(tempLabelMap, entry.getKey()), entry.getValue());
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index 31a7e39..a0a4f22 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -113,6 +113,7 @@
import com.google.devtools.build.lib.rules.extra.ActionListenerRule;
import com.google.devtools.build.lib.rules.extra.ExtraActionRule;
import com.google.devtools.build.lib.rules.genquery.GenQueryRule;
+import com.google.devtools.build.lib.rules.genrule.GenRuleBaseRule;
import com.google.devtools.build.lib.rules.java.JavaConfigurationLoader;
import com.google.devtools.build.lib.rules.java.JavaImportBaseRule;
import com.google.devtools.build.lib.rules.java.JavaOptions;
@@ -342,6 +343,7 @@
builder.addRuleDefinition(new AliasRule());
builder.addRuleDefinition(new BazelFilegroupRule());
builder.addRuleDefinition(new TestSuiteRule());
+ builder.addRuleDefinition(new GenRuleBaseRule());
builder.addRuleDefinition(new BazelGenRuleRule());
builder.addRuleDefinition(new GenQueryRule());
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRule.java
index a13c022..f588a75 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRule.java
@@ -14,227 +14,20 @@
package com.google.devtools.build.lib.bazel.rules.genrule;
-import static com.google.devtools.build.lib.analysis.RunfilesProvider.withData;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.analysis.CommandHelper;
-import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
-import com.google.devtools.build.lib.analysis.ConfiguredTarget;
-import com.google.devtools.build.lib.analysis.FileProvider;
-import com.google.devtools.build.lib.analysis.FilesToRunProvider;
-import com.google.devtools.build.lib.analysis.MakeVariableExpander.ExpansionException;
-import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
-import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
-import com.google.devtools.build.lib.analysis.Runfiles;
-import com.google.devtools.build.lib.analysis.RunfilesProvider;
-import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
-import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-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.TargetUtils;
-import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
-import com.google.devtools.build.lib.rules.ToolchainProvider;
+import com.google.devtools.build.lib.rules.genrule.GenRuleBase;
import com.google.devtools.build.lib.syntax.Type;
-import com.google.devtools.build.lib.vfs.PathFragment;
-import java.util.List;
-import java.util.Map;
/**
- * An implementation of genrule.
+ * An implementation of genrule for Bazel.
*/
-public class BazelGenRule implements RuleConfiguredTargetFactory {
-
- private Artifact getExecutable(RuleContext ruleContext, NestedSet<Artifact> filesToBuild) {
- if (Iterables.size(filesToBuild) == 1) {
- Artifact out = Iterables.getOnlyElement(filesToBuild);
- if (ruleContext.attributes().get("executable", Type.BOOLEAN)) {
- return out;
- }
- }
- return null;
- }
+public class BazelGenRule extends GenRuleBase {
@Override
- public ConfiguredTarget create(RuleContext ruleContext)
- throws RuleErrorException, InterruptedException {
- final List<Artifact> resolvedSrcs = Lists.newArrayList();
-
- final NestedSet<Artifact> filesToBuild =
- NestedSetBuilder.wrap(Order.STABLE_ORDER, ruleContext.getOutputArtifacts());
- if (filesToBuild.isEmpty()) {
- ruleContext.attributeError("outs", "Genrules without outputs don't make sense");
+ protected boolean isStampingEnabled(RuleContext ruleContext) {
+ if (!ruleContext.attributes().has("stamp", Type.BOOLEAN)) {
+ return false;
}
- if (ruleContext.attributes().get("executable", Type.BOOLEAN)
- && Iterables.size(filesToBuild) > 1) {
- ruleContext.attributeError("executable",
- "if genrules produce executables, they are allowed only one output. "
- + "If you need the executable=1 argument, then you should split this genrule into "
- + "genrules producing single outputs");
- }
-
- ImmutableMap.Builder<Label, Iterable<Artifact>> labelMap = ImmutableMap.builder();
- for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("srcs", Mode.TARGET)) {
- Iterable<Artifact> files = dep.getProvider(FileProvider.class).getFilesToBuild();
- Iterables.addAll(resolvedSrcs, files);
- labelMap.put(dep.getLabel(), files);
- }
-
- CommandHelper commandHelper =
- new CommandHelper(
- ruleContext, ruleContext.getPrerequisites("tools", Mode.HOST), labelMap.build());
-
- if (ruleContext.hasErrors()) {
- return null;
- }
-
- String baseCommand = commandHelper.resolveCommandAndExpandLabels(
- ruleContext.attributes().get("heuristic_label_expansion", Type.BOOLEAN), false);
-
- // Adds the genrule environment setup script before the actual shell command
- String command = String.format("source %s; %s",
- ruleContext.getPrerequisiteArtifact("$genrule_setup", Mode.HOST).getExecPath(),
- baseCommand);
-
- command = resolveCommand(ruleContext, command, resolvedSrcs, filesToBuild);
-
- String message = ruleContext.attributes().get("message", Type.STRING);
- if (message.isEmpty()) {
- message = "Executing genrule";
- }
-
- ImmutableMap<String, String> env = ruleContext.getConfiguration().getLocalShellEnvironment();
- ImmutableSet<String> clientEnvVars =
- ruleContext.getConfiguration().getVariableShellEnvironment();
-
- Map<String, String> executionInfo = Maps.newLinkedHashMap();
- executionInfo.putAll(TargetUtils.getExecutionInfo(ruleContext.getRule()));
-
- if (ruleContext.attributes().get("local", Type.BOOLEAN)) {
- executionInfo.put("local", "");
- }
-
- NestedSetBuilder<Artifact> inputs = NestedSetBuilder.stableOrder();
- inputs.addAll(resolvedSrcs);
- inputs.addAll(commandHelper.getResolvedTools());
- FilesToRunProvider genruleSetup =
- ruleContext.getPrerequisite("$genrule_setup", Mode.HOST, FilesToRunProvider.class);
- inputs.addAll(genruleSetup.getFilesToRun());
- List<String> argv = commandHelper.buildCommandLine(command, inputs, ".genrule_script.sh",
- ImmutableMap.copyOf(executionInfo));
-
- if (ruleContext.attributes().get("stamp", Type.BOOLEAN)) {
- inputs.add(ruleContext.getAnalysisEnvironment().getStableWorkspaceStatusArtifact());
- inputs.add(ruleContext.getAnalysisEnvironment().getVolatileWorkspaceStatusArtifact());
- }
-
- ruleContext.registerAction(
- new BazelGenRuleAction(
- ruleContext.getActionOwner(),
- ImmutableList.copyOf(commandHelper.getResolvedTools()),
- inputs.build(),
- filesToBuild,
- argv,
- env,
- clientEnvVars,
- ImmutableMap.copyOf(executionInfo),
- ImmutableMap.copyOf(commandHelper.getRemoteRunfileManifestMap()),
- message + ' ' + ruleContext.getLabel()));
-
- RunfilesProvider runfilesProvider = withData(
- // No runfiles provided if not a data dependency.
- Runfiles.EMPTY,
- // We only need to consider the outputs of a genrule
- // No need to visit the dependencies of a genrule. They cross from the target into the host
- // configuration, because the dependencies of a genrule are always built for the host
- // configuration.
- new Runfiles.Builder(
- ruleContext.getWorkspaceName(),
- ruleContext.getConfiguration().legacyExternalRunfiles())
- .addTransitiveArtifacts(filesToBuild)
- .build());
-
- return new RuleConfiguredTargetBuilder(ruleContext)
- .setFilesToBuild(filesToBuild)
- .setRunfilesSupport(null, getExecutable(ruleContext, filesToBuild))
- .addProvider(RunfilesProvider.class, runfilesProvider)
- .build();
- }
-
- private String resolveCommand(final RuleContext ruleContext, final String command,
- final List<Artifact> resolvedSrcs, final NestedSet<Artifact> filesToBuild) {
- return ruleContext.expandMakeVariables(
- "cmd",
- command,
- new ConfigurationMakeVariableContext(
- ruleContext.getRule().getPackage(), ruleContext.getConfiguration(),
- ToolchainProvider.getToolchainMakeVariables(ruleContext, "toolchains")) {
- @Override
- public String lookupMakeVariable(String name) throws ExpansionException {
- if (name.equals("SRCS")) {
- return Artifact.joinExecPaths(" ", resolvedSrcs);
- } else if (name.equals("<")) {
- return expandSingletonArtifact(resolvedSrcs, "$<", "input file");
- } else if (name.equals("OUTS")) {
- return Artifact.joinExecPaths(" ", filesToBuild);
- } else if (name.equals("@")) {
- return expandSingletonArtifact(filesToBuild, "$@", "output file");
- } else if (name.equals("@D")) {
- // The output directory. If there is only one filename in outs,
- // this expands to the directory containing that file. If there are
- // multiple filenames, this variable instead expands to the
- // package's root directory in the genfiles tree, even if all the
- // generated files belong to the same subdirectory!
- if (Iterables.size(filesToBuild) == 1) {
- Artifact outputFile = Iterables.getOnlyElement(filesToBuild);
- PathFragment relativeOutputFile = outputFile.getExecPath();
- if (relativeOutputFile.segmentCount() <= 1) {
- // This should never happen, since the path should contain at
- // least a package name and a file name.
- throw new IllegalStateException(
- "$(@D) for genrule " + ruleContext.getLabel() + " has less than one segment");
- }
- return relativeOutputFile.getParentDirectory().getPathString();
- } else {
- PathFragment dir;
- if (ruleContext.getRule().hasBinaryOutput()) {
- dir = ruleContext.getConfiguration().getBinFragment();
- } else {
- dir = ruleContext.getConfiguration().getGenfilesFragment();
- }
- PathFragment relPath =
- ruleContext.getRule().getLabel().getPackageIdentifier().getSourceRoot();
- return dir.getRelative(relPath).getPathString();
- }
- } else {
- return super.lookupMakeVariable(name);
- }
- }
- });
- }
-
- // Returns the path of the sole element "artifacts", generating an exception
- // with an informative error message iff the set is not a singleton.
- //
- // Used to expand "$<", "$@"
- private String expandSingletonArtifact(Iterable<Artifact> artifacts,
- String variable,
- String artifactName)
- throws ExpansionException {
- if (Iterables.isEmpty(artifacts)) {
- throw new ExpansionException("variable '" + variable
- + "' : no " + artifactName);
- } else if (Iterables.size(artifacts) > 1) {
- throw new ExpansionException("variable '" + variable
- + "' : more than one " + artifactName);
- }
- return Iterables.getOnlyElement(artifacts).getExecPathString();
+ return ruleContext.attributes().get("stamp", Type.BOOLEAN);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleRule.java
index 2579c1e..21683f2 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleRule.java
@@ -16,33 +16,21 @@
import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
import static com.google.devtools.build.lib.packages.Attribute.attr;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
-import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
-import static com.google.devtools.build.lib.packages.BuildType.LICENSE;
-import static com.google.devtools.build.lib.packages.BuildType.OUTPUT_LIST;
import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
-import static com.google.devtools.build.lib.syntax.Type.STRING;
-import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.analysis.BaseRuleClasses;
import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
-import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
-import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.packages.AttributeMap;
-import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.RuleClass;
-import com.google.devtools.build.lib.packages.RuleClass.Builder;
-import com.google.devtools.build.lib.rules.ToolchainProvider;
-import com.google.devtools.build.lib.util.FileTypeSet;
+import com.google.devtools.build.lib.rules.genrule.GenRuleBaseRule;
/**
- * Rule definition for the genrule rule.
+ * Rule definition for genrule for Bazel.
*/
public final class BazelGenRuleRule implements RuleDefinition {
public static final String GENRULE_SETUP_LABEL = "//tools/genrule:genrule-setup.sh";
@Override
- public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
/* <!-- #BLAZE_RULE(genrule).NAME -->
<br/>You may refer to this rule by name in the
<code>srcs</code> or <code>deps</code> section of other <code>BUILD</code>
@@ -50,196 +38,26 @@
<code>srcs</code> attribute.
<!-- #END_BLAZE_RULE.NAME --> */
return builder
+
.setOutputToGenfiles()
- /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(srcs) -->
- A list of inputs for this rule, such as source files to process.
- <p>
- <em>This attributes is not suitable to list tools executed by the <code>cmd</code>; use
- the <a href="${link genrule.tools}"><code>tools</code></a> attribute for them instead.
- </em>
- </p>
- <p>
- The build system ensures these prerequisites are built before running the genrule
- command; they are built using the same configuration as the original build request. The
- names of the files of these prerequisites are available to the command as a
- space-separated list in <code>$(SRCS)</code>; alternatively the path of an individual
- <code>srcs</code> target <code>//x:y</code> can be obtained using <code>$(location
- //x:y)</code>, or using <code>$<</code> provided it's the only entry in
- //<code>srcs</code>.
- </p>
- <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- .add(attr("srcs", LABEL_LIST)
- .direct_compile_time_input()
- .legacyAllowAnyFileType())
- /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(tools) -->
- A list of <i>tool</i> dependencies for this rule. See the definition of
- <a href="../build-ref.html#deps">dependencies</a> for more information. <br/>
- <p>
- The build system ensures these prerequisites are built before running the genrule command;
- they are built using the <a href='../blaze-user-manual.html#configurations'><i>host</i>
- configuration</a>, since these tools are executed as part of the build. The path of an
- individual <code>tools</code> target <code>//x:y</code> can be obtained using
- <code>$(location //x:y)</code>.
- </p>
- <p>
- Any <code>*_binary</code> or tool to be executed by <code>cmd</code> must appear in this
- list, not in <code>srcs</code>, to ensure they are built in the correct configuration.
- </p>
- <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- .add(attr("tools", LABEL_LIST).cfg(HOST).legacyAllowAnyFileType())
- .add(attr("toolchains", LABEL_LIST)
- .allowedFileTypes(FileTypeSet.NO_FILE)
- .mandatoryNativeProviders(ImmutableList.<Class<? extends TransitiveInfoProvider>>of(
- ToolchainProvider.class)))
.add(attr("$genrule_setup", LABEL).cfg(HOST).value(env.getToolsLabel(GENRULE_SETUP_LABEL)))
- /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(outs) -->
- A list of files generated by this rule.
- <p>
- Output files must not cross package boundaries.
- Output filenames are interpreted as relative to the package.
- </p>
- <p>
- If the <code>executable</code> flag is set, <code>outs</code> must contain exactly one
- label.
- </p>
- <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- .add(attr("outs", OUTPUT_LIST).mandatory())
-
- /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(cmd) -->
- The command to run.
- Subject to <a href="${link make-variables#location}">$(location)</a> and
- <a href="${link make-variables}">"Make" variable</a> substitution.
- <ol>
- <li>
- First <a href="${link make-variables#location}">$(location)</a> substitution is
- applied, replacing all occurrences of <code>$(location <i>label</i>)</code> and of
- <code>$(locations <i>label</i>)</code>.
- </li>
- <li>
- <p>
- Note that <code>outs</code> are <i>not</i> included in this substitution. Output files
- are always generated into a predictable location (available via <code>$(@D)</code>,
- <code>$@</code>, <code>$(OUTS)</code> or <code>$(location <i>output_name</i>)</code>;
- see below).
- </p>
- </li>
- <li>
- Next, <a href="${link make-variables}">"Make" variables</a> are expanded. Note that
- predefined variables <code>$(JAVA)</code>, <code>$(JAVAC)</code> and
- <code>$(JAVABASE)</code> expand under the <i>host</i> configuration, so Java invocations
- that run as part of a build step can correctly load shared libraries and other
- dependencies.
- </li>
- <li>
- Finally, the resulting command is executed using the Bash shell. If its exit code is
- non-zero the command is considered to have failed.
- </li>
- </ol>
- <p>
- The command may refer to <code>*_binary</code>targets; it should use a <a
- href="../build-ref.html#labels">label</a> for this. The following
- variables are available within the <code>cmd</code> sublanguage:</p>
- <ul>
- <li>
- <a href="${link make-variables#predefined_variables.genrule.cmd}">"Make" variables</a>
- </li>
- <li>
- "Make" variables that are predefined by the build tools.
- Please use these variables instead of hardcoded values.
- See <a href="${link make-variables#predefined_variables}">Predefined "Make" Variables
- </a> in this document for a list of supported values.
- </li>
- </ul>
- <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- .add(attr("cmd", STRING).mandatory())
-
- /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(output_to_bindir) -->
- <p>
- If set to 1, this option causes output files to be written into the <code>bin</code>
- directory instead of the <code>genfiles</code> directory.
- </p>
- <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- // TODO(bazel-team): find a location to document genfiles/binfiles, link to them from here.
- .add(attr("output_to_bindir", BOOLEAN).value(false)
- .nonconfigurable("policy decision: no reason for this to depend on the configuration"))
-
- /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(local) -->
- <p>
- If set to 1, this option force this <code>genrule</code> to run with the
- <code>standalone</code> strategy, without sandboxing.
- </p>
- <p>
- This is equivalent to providing 'local' as a tag (<code>tags=["local"]</code>). The
- local strategy is applied if either one is specified.
- </p>
- <p>
- The <code>--genrule_strategy</code> option value <code>local</code>
- overrides this attribute.
- <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- .add(attr("local", BOOLEAN).value(false))
-
- /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(message) -->
- A progress message.
- <p>
- A progress message that will be printed as this build step is executed. By default, the
- message is "Generating <i>output</i>" (or something equally bland) but you may provide a
- more specific one. Use this attribute instead of <code>echo</code> or other print
- statements in your <code>cmd</code> command, as this allows the build tool to control
- whether such progress messages are printed or not.
- </p>
- <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- .add(attr("message", STRING))
- /*<!-- #BLAZE_RULE(genrule).ATTRIBUTE(output_licenses) -->
- See <a href="${link common-definitions#binary.output_licenses}"><code>common attributes
- </code></a>
- <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
- .add(attr("output_licenses", LICENSE))
-
- /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(executable) -->
- Declare output to be executable.
- <p>
- Setting this flag to 1 means the output is an executable file and can be run using the
- <code>run</code> command. The genrule must produce exactly one output in this case.
- If this attribute is set, <code>run</code> will try executing the file regardless of
- its content.
- </p>
- <p>Declaring data dependencies for the generated executable is not supported.</p>
- <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
- .add(attr("executable", BOOLEAN).value(false).nonconfigurable(
- "Used in computed default for $is_executable, which is itself non-configurable (and "
- + " thus expects its dependencies to be non-configurable), because $is_executable"
- + " is called from RunCommand.isExecutable, which has no configuration context"))
-
// TODO(bazel-team): stamping doesn't seem to work. Fix it or remove attribute.
.add(attr("stamp", BOOLEAN).value(false))
- // This is a misfeature, so don't document it. We would like to get rid of it, but that
- // would require a cleanup of existing rules.
- .add(attr("heuristic_label_expansion", BOOLEAN).value(false))
- .add(attr("$is_executable", BOOLEAN)
- .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
- .value(
- new Attribute.ComputedDefault() {
- @Override
- public Object getDefault(AttributeMap rule) {
- return (rule.get("outs", BuildType.OUTPUT_LIST).size() == 1)
- && rule.get("executable", BOOLEAN);
- }
- }))
- .removeAttribute("data")
- .removeAttribute("deps")
+
.build();
}
@Override
- public Metadata getMetadata() {
+ public RuleDefinition.Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("genrule")
- .ancestors(BaseRuleClasses.RuleBase.class)
+ .ancestors(GenRuleBaseRule.class)
.factoryClass(BazelGenRule.class)
.build();
}
+
}
/*<!-- #BLAZE_RULE (NAME = genrule, TYPE = OTHER, FAMILY = General)[GENERIC_RULE] -->
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/BUILD b/src/main/java/com/google/devtools/build/lib/rules/genrule/BUILD
new file mode 100644
index 0000000..92001c6
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/BUILD
@@ -0,0 +1,28 @@
+# Description:
+# Genrule support
+
+package(
+ default_visibility = ["//src:__subpackages__"],
+)
+
+java_library(
+ name = "genrule",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib:build-base",
+ "//src/main/java/com/google/devtools/build/lib:events",
+ "//src/main/java/com/google/devtools/build/lib:packages",
+ "//src/main/java/com/google/devtools/build/lib:vfs",
+ "//src/main/java/com/google/devtools/build/lib/actions",
+ "//src/main/java/com/google/devtools/build/lib/cmdline",
+ "//third_party:guava",
+ "//third_party:jsr305",
+ "//third_party/protobuf",
+ ],
+)
+
+filegroup(
+ name = "srcs",
+ testonly = 0, # All srcs should be not test only, overwrite package default.
+ srcs = glob(["**"]),
+)
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleAction.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleAction.java
similarity index 92%
rename from src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleAction.java
rename to src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleAction.java
index 209b15d..d77b894 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/genrule/BazelGenRuleAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleAction.java
@@ -1,4 +1,4 @@
-// Copyright 2014 The Bazel Authors. All rights reserved.
+// 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.
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.devtools.build.lib.bazel.rules.genrule;
+package com.google.devtools.build.lib.rules.genrule;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -31,13 +31,13 @@
* A spawn action for genrules. Genrules are handled specially in that inputs and outputs are
* checked for directories.
*/
-public final class BazelGenRuleAction extends SpawnAction {
+public class GenRuleAction extends SpawnAction {
private static final ResourceSet GENRULE_RESOURCES =
// Not chosen scientifically/carefully. 300MB memory, 100% CPU, no I/O.
ResourceSet.createWithRamCpuIo(300, 1.0, 0.0);
- public BazelGenRuleAction(
+ public GenRuleAction(
ActionOwner owner,
Iterable<Artifact> tools,
Iterable<Artifact> inputs,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java
new file mode 100644
index 0000000..c990b15
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBase.java
@@ -0,0 +1,307 @@
+// 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.genrule;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.CommandHelper;
+import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.FileProvider;
+import com.google.devtools.build.lib.analysis.FilesToRunProvider;
+import com.google.devtools.build.lib.analysis.MakeVariableExpander.ExpansionException;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
+import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.Runfiles;
+import com.google.devtools.build.lib.analysis.RunfilesProvider;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+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.TargetUtils;
+import com.google.devtools.build.lib.rules.AliasProvider;
+import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.rules.ToolchainProvider;
+import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A base implementation of genrule, to be used by specific implementing rules which can change some
+ * of the semantics around when the execution info and inputs are changed.
+ */
+public abstract class GenRuleBase implements RuleConfiguredTargetFactory {
+
+ /**
+ * Returns a {@link Map} of execution info, which will be used in later processing to construct
+ * the actual command line that will be executed.
+ *
+ * <p>GenRule implementations can override this method to include additional specific information
+ * needed.
+ */
+ protected Map<String, String> getExtraExecutionInfo(RuleContext ruleContext, String command) {
+ return ImmutableMap.of();
+ }
+
+ /**
+ * Returns an {@link Iterable} of {@link NestedSet}s, which will be added to the genrule's inputs
+ * using the {@link NestedSetBuilder#addTransitive} method.
+ *
+ * <p>GenRule implementations can override this method to better control what inputs are needed
+ * for specific command inputs.
+ */
+ protected Iterable<NestedSet<Artifact>> getExtraInputArtifacts(
+ RuleContext ruleContext, String command) {
+ return ImmutableList.of();
+ }
+
+ /**
+ * Returns {@code true} if the rule should be stamped.
+ *
+ * <p>Genrule implementations can set this based on the rule context, including by defining their
+ * own attributes over and above what is present in {@link GenRuleBaseRule}.
+ */
+ protected abstract boolean isStampingEnabled(RuleContext ruleContext);
+
+ /**
+ * Updates the {@link RuleConfiguredTargetBuilder} that is used for this rule.
+ *
+ * <p>GenRule implementations can override this method to enhance and update the builder without
+ * needing to entirely override the {@link #create} method.
+ */
+ protected RuleConfiguredTargetBuilder updateBuilder(
+ RuleConfiguredTargetBuilder builder,
+ RuleContext ruleContext,
+ NestedSet<Artifact> filesToBuild) {
+ return builder;
+ }
+
+ @Override
+ public ConfiguredTarget create(RuleContext ruleContext)
+ throws RuleErrorException, InterruptedException {
+ NestedSet<Artifact> filesToBuild =
+ NestedSetBuilder.wrap(Order.STABLE_ORDER, ruleContext.getOutputArtifacts());
+ NestedSetBuilder<Artifact> resolvedSrcsBuilder = NestedSetBuilder.stableOrder();
+
+ if (filesToBuild.isEmpty()) {
+ ruleContext.attributeError("outs", "Genrules without outputs don't make sense");
+ }
+ if (ruleContext.attributes().get("executable", Type.BOOLEAN)
+ && Iterables.size(filesToBuild) > 1) {
+ ruleContext.attributeError(
+ "executable",
+ "if genrules produce executables, they are allowed only one output. "
+ + "If you need the executable=1 argument, then you should split this genrule into "
+ + "genrules producing single outputs");
+ }
+
+ ImmutableMap.Builder<Label, NestedSet<Artifact>> labelMap = ImmutableMap.builder();
+ for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("srcs", Mode.TARGET)) {
+ // This target provides specific types of files for genrules.
+ GenRuleSourcesProvider provider = dep.getProvider(GenRuleSourcesProvider.class);
+ NestedSet<Artifact> files = (provider != null)
+ ? provider.getGenruleFiles()
+ : dep.getProvider(FileProvider.class).getFilesToBuild();
+ resolvedSrcsBuilder.addTransitive(files);
+ labelMap.put(AliasProvider.getDependencyLabel(dep), files);
+ }
+ NestedSet<Artifact> resolvedSrcs = resolvedSrcsBuilder.build();
+
+ CommandHelper commandHelper =
+ new CommandHelper(
+ ruleContext, ruleContext.getPrerequisites("tools", Mode.HOST), labelMap.build());
+
+ if (ruleContext.hasErrors()) {
+ return null;
+ }
+
+ String baseCommand = commandHelper.resolveCommandAndExpandLabels(
+ ruleContext.attributes().get("heuristic_label_expansion", Type.BOOLEAN), false);
+
+ // Adds the genrule environment setup script before the actual shell command
+ String command = String.format("source %s; %s",
+ ruleContext.getPrerequisiteArtifact("$genrule_setup", Mode.HOST).getExecPath(),
+ baseCommand);
+
+ command = resolveCommand(command, ruleContext, resolvedSrcs, filesToBuild);
+
+ String message = ruleContext.attributes().get("message", Type.STRING);
+ if (message.isEmpty()) {
+ message = "Executing genrule";
+ }
+
+ ImmutableMap<String, String> env = ruleContext.getConfiguration().getLocalShellEnvironment();
+ ImmutableSet<String> clientEnvVars =
+ ruleContext.getConfiguration().getVariableShellEnvironment();
+
+ Map<String, String> executionInfo = Maps.newLinkedHashMap();
+ executionInfo.putAll(TargetUtils.getExecutionInfo(ruleContext.getRule()));
+
+ if (ruleContext.attributes().get("local", Type.BOOLEAN)) {
+ executionInfo.put("local", "");
+ }
+
+ executionInfo.putAll(getExtraExecutionInfo(ruleContext, baseCommand));
+
+ NestedSetBuilder<Artifact> inputs = NestedSetBuilder.stableOrder();
+ inputs.addAll(resolvedSrcs);
+ inputs.addAll(commandHelper.getResolvedTools());
+ FilesToRunProvider genruleSetup =
+ ruleContext.getPrerequisite("$genrule_setup", Mode.HOST, FilesToRunProvider.class);
+ inputs.addAll(genruleSetup.getFilesToRun());
+ List<String> argv = commandHelper.buildCommandLine(command, inputs, ".genrule_script.sh",
+ ImmutableMap.copyOf(executionInfo));
+
+ for (NestedSet<Artifact> extraInputs : getExtraInputArtifacts(ruleContext, baseCommand)) {
+ inputs.addTransitive(extraInputs);
+ }
+
+ if (isStampingEnabled(ruleContext)) {
+ inputs.add(ruleContext.getAnalysisEnvironment().getStableWorkspaceStatusArtifact());
+ inputs.add(ruleContext.getAnalysisEnvironment().getVolatileWorkspaceStatusArtifact());
+ }
+
+ ruleContext.registerAction(
+ new GenRuleAction(
+ ruleContext.getActionOwner(),
+ ImmutableList.copyOf(commandHelper.getResolvedTools()),
+ inputs.build(),
+ filesToBuild,
+ argv,
+ env,
+ clientEnvVars,
+ ImmutableMap.copyOf(executionInfo),
+ ImmutableMap.copyOf(commandHelper.getRemoteRunfileManifestMap()),
+ message + ' ' + ruleContext.getLabel()));
+
+ RunfilesProvider runfilesProvider = RunfilesProvider.withData(
+ // No runfiles provided if not a data dependency.
+ Runfiles.EMPTY,
+ // We only need to consider the outputs of a genrule
+ // No need to visit the dependencies of a genrule. They cross from the target into the host
+ // configuration, because the dependencies of a genrule are always built for the host
+ // configuration.
+ new Runfiles.Builder(
+ ruleContext.getWorkspaceName(),
+ ruleContext.getConfiguration().legacyExternalRunfiles())
+ .addTransitiveArtifacts(filesToBuild)
+ .build());
+
+ RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext)
+ .setFilesToBuild(filesToBuild)
+ .setRunfilesSupport(null, getExecutable(ruleContext, filesToBuild))
+ .addProvider(RunfilesProvider.class, runfilesProvider);
+
+ builder = updateBuilder(builder, ruleContext, filesToBuild);
+ return builder.build();
+ }
+
+ /**
+ * Returns the executable artifact, if the rule is marked as executable and there is only one
+ * artifact.
+ */
+ private static Artifact getExecutable(RuleContext ruleContext, NestedSet<Artifact> filesToBuild) {
+ if (!ruleContext.attributes().get("executable", Type.BOOLEAN)) {
+ return null;
+ }
+ if (Iterables.size(filesToBuild) == 1) {
+ return Iterables.getOnlyElement(filesToBuild);
+ }
+ return null;
+ }
+
+ /**
+ * Resolves any variables, including make and genrule-specific variables, in the command and
+ * returns the expanded command.
+ *
+ * <p>GenRule implementations may override this method to perform additional expansions.
+ */
+ protected String resolveCommand(String command, final RuleContext ruleContext,
+ final NestedSet<Artifact> resolvedSrcs, final NestedSet<Artifact> filesToBuild) {
+ return ruleContext.expandMakeVariables(
+ "cmd",
+ command,
+ new ConfigurationMakeVariableContext(
+ ruleContext.getRule().getPackage(), ruleContext.getConfiguration(),
+ ToolchainProvider.getToolchainMakeVariables(ruleContext, "toolchains")) {
+ @Override
+ public String lookupMakeVariable(String name) throws ExpansionException {
+ if (name.equals("SRCS")) {
+ return Artifact.joinExecPaths(" ", resolvedSrcs);
+ } else if (name.equals("<")) {
+ return expandSingletonArtifact(resolvedSrcs, "$<", "input file");
+ } else if (name.equals("OUTS")) {
+ return Artifact.joinExecPaths(" ", filesToBuild);
+ } else if (name.equals("@")) {
+ return expandSingletonArtifact(filesToBuild, "$@", "output file");
+ } else if (name.equals("@D")) {
+ // The output directory. If there is only one filename in outs,
+ // this expands to the directory containing that file. If there are
+ // multiple filenames, this variable instead expands to the
+ // package's root directory in the genfiles tree, even if all the
+ // generated files belong to the same subdirectory!
+ if (Iterables.size(filesToBuild) == 1) {
+ Artifact outputFile = Iterables.getOnlyElement(filesToBuild);
+ PathFragment relativeOutputFile = outputFile.getExecPath();
+ if (relativeOutputFile.segmentCount() <= 1) {
+ // This should never happen, since the path should contain at
+ // least a package name and a file name.
+ throw new IllegalStateException(
+ "$(@D) for genrule " + ruleContext.getLabel() + " has less than one segment");
+ }
+ return relativeOutputFile.getParentDirectory().getPathString();
+ } else {
+ PathFragment dir;
+ if (ruleContext.getRule().hasBinaryOutput()) {
+ dir = ruleContext.getConfiguration().getBinFragment();
+ } else {
+ dir = ruleContext.getConfiguration().getGenfilesFragment();
+ }
+ PathFragment relPath =
+ ruleContext.getRule().getLabel().getPackageIdentifier().getSourceRoot();
+ return dir.getRelative(relPath).getPathString();
+ }
+ } else {
+ return super.lookupMakeVariable(name);
+ }
+ }
+ });
+ }
+
+ /**
+ * Returns the path of the sole element "artifacts", generating an exception with an informative
+ * error message iff the set is not a singleton. Used to expand "$<", "$@".
+ */
+ private String expandSingletonArtifact(Iterable<Artifact> artifacts,
+ String variable,
+ String artifactName)
+ throws ExpansionException {
+ if (Iterables.isEmpty(artifacts)) {
+ throw new ExpansionException("variable '" + variable
+ + "' : no " + artifactName);
+ } else if (Iterables.size(artifacts) > 1) {
+ throw new ExpansionException("variable '" + variable
+ + "' : more than one " + artifactName);
+ }
+ return Iterables.getOnlyElement(artifacts).getExecPathString();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBaseRule.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBaseRule.java
new file mode 100644
index 0000000..3a389ca
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleBaseRule.java
@@ -0,0 +1,388 @@
+// 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.genrule;
+
+import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+import static com.google.devtools.build.lib.packages.BuildType.LICENSE;
+import static com.google.devtools.build.lib.packages.BuildType.OUTPUT_LIST;
+import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
+import static com.google.devtools.build.lib.syntax.Type.STRING;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.analysis.BaseRuleClasses;
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.AttributeMap;
+import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.rules.ToolchainProvider;
+import com.google.devtools.build.lib.util.FileTypeSet;
+
+/**
+ * Rule definition for the genrule rule, intended to be inherited by specific GenRule
+ * implementations. Implementations will need to include any needed additional dependencies, such
+ * as a setup script target.
+ */
+public class GenRuleBaseRule implements RuleDefinition {
+
+ @Override
+ public RuleClass build(
+ RuleClass.Builder builder, RuleDefinitionEnvironment env) {
+ return builder
+
+ /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(srcs) -->
+ A list of inputs for this rule, such as source files to process.
+ <p>
+ <em>This attributes is not suitable to list tools executed by the <code>cmd</code>; use
+ the <a href="${link genrule.tools}"><code>tools</code></a> attribute for them instead.
+ </em>
+ </p>
+ <p>
+ The build system ensures these prerequisites are built before running the genrule
+ command; they are built using the same configuration as the original build request. The
+ names of the files of these prerequisites are available to the command as a
+ space-separated list in <code>$(SRCS)</code>; alternatively the path of an individual
+ <code>srcs</code> target <code>//x:y</code> can be obtained using <code>$(location
+ //x:y)</code>, or using <code>$<</code> provided it's the only entry in
+ //<code>srcs</code>.
+ </p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("srcs", LABEL_LIST)
+ .direct_compile_time_input()
+ .allowedFileTypes(FileTypeSet.ANY_FILE))
+
+
+ /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(tools) -->
+ A list of <i>tool</i> dependencies for this rule. See the definition of
+ <a href="../build-ref.html#deps">dependencies</a> for more information. <br/>
+ <p>
+ The build system ensures these prerequisites are built before running the genrule command;
+ they are built using the <a href='../blaze-user-manual.html#configurations'><i>host</i>
+ configuration</a>, since these tools are executed as part of the build. The path of an
+ individual <code>tools</code> target <code>//x:y</code> can be obtained using
+ <code>$(location //x:y)</code>.
+ </p>
+ <p>
+ Any <code>*_binary</code> or tool to be executed by <code>cmd</code> must appear in this
+ list, not in <code>srcs</code>, to ensure they are built in the correct configuration.
+ </p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("tools", LABEL_LIST).cfg(HOST)
+ .allowedFileTypes(FileTypeSet.ANY_FILE))
+ .add(attr("toolchains", LABEL_LIST)
+ .allowedFileTypes(FileTypeSet.NO_FILE)
+ .mandatoryNativeProviders(ImmutableList.<Class<? extends TransitiveInfoProvider>>of(
+ ToolchainProvider.class)))
+
+ /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(outs) -->
+ A list of files generated by this rule.
+ <p>
+ Output files must not cross package boundaries.
+ Output filenames are interpreted as relative to the package.
+ </p>
+ <p>
+ If the <code>executable</code> flag is set, <code>outs</code> must contain exactly one
+ label.
+ </p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("outs", OUTPUT_LIST).mandatory())
+
+ /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(cmd) -->
+ The command to run.
+ Subject to <a href="${link make-variables#location}">$(location)</a> and
+ <a href="${link make-variables}">"Make" variable</a> substitution.
+ <ol>
+ <li>
+ First <a href="${link make-variables#location}">$(location)</a> substitution is
+ applied, replacing all occurrences of <code>$(location <i>label</i>)</code> and of
+ <code>$(locations <i>label</i>)</code>.
+ </li>
+ <li>
+ <p>
+ Note that <code>outs</code> are <i>not</i> included in this substitution. Output files
+ are always generated into a predictable location (available via <code>$(@D)</code>,
+ <code>$@</code>, <code>$(OUTS)</code> or <code>$(location <i>output_name</i>)</code>;
+ see below).
+ </p>
+ </li>
+ <li>
+ Next, <a href="${link make-variables}">"Make" variables</a> are expanded. Note that
+ predefined variables <code>$(JAVA)</code>, <code>$(JAVAC)</code> and
+ <code>$(JAVABASE)</code> expand under the <i>host</i> configuration, so Java invocations
+ that run as part of a build step can correctly load shared libraries and other
+ dependencies.
+ </li>
+ <li>
+ Finally, the resulting command is executed using the Bash shell. If its exit code is
+ non-zero the command is considered to have failed.
+ </li>
+ </ol>
+ <p>
+ The command may refer to <code>*_binary</code>targets; it should use a <a
+ href="../build-ref.html#labels">label</a> for this. The following
+ variables are available within the <code>cmd</code> sublanguage:</p>
+ <ul>
+ <li>
+ <a href="${link make-variables#predefined_variables.genrule.cmd}">"Make" variables</a>
+ </li>
+ <li>
+ "Make" variables that are predefined by the build tools.
+ Please use these variables instead of hardcoded values.
+ See <a href="${link make-variables#predefined_variables}">Predefined "Make" Variables
+ </a> in this document for a list of supported values.
+ </li>
+ </ul>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("cmd", STRING).mandatory())
+
+ /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(output_to_bindir) -->
+ <p>
+ If set to 1, this option causes output files to be written into the <code>bin</code>
+ directory instead of the <code>genfiles</code> directory.
+ </p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ // TODO(bazel-team): find a location to document genfiles/binfiles, link to them from here.
+ .add(attr("output_to_bindir", BOOLEAN).value(false)
+ .nonconfigurable("policy decision: no reason for this to depend on the configuration"))
+
+ /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(local) -->
+ <p>
+ If set to 1, this option force this <code>genrule</code> to run with the
+ <code>standalone</code> strategy, without sandboxing.
+ </p>
+ <p>
+ This is equivalent to providing 'local' as a tag (<code>tags=["local"]</code>). The
+ local strategy is applied if either one is specified.
+ </p>
+ <p>
+ The <code>--genrule_strategy</code> option value <code>local</code>
+ overrides this attribute.
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("local", BOOLEAN).value(false))
+
+ /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(message) -->
+ A progress message.
+ <p>
+ A progress message that will be printed as this build step is executed. By default, the
+ message is "Generating <i>output</i>" (or something equally bland) but you may provide a
+ more specific one. Use this attribute instead of <code>echo</code> or other print
+ statements in your <code>cmd</code> command, as this allows the build tool to control
+ whether such progress messages are printed or not.
+ </p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("message", STRING))
+ /*<!-- #BLAZE_RULE(genrule).ATTRIBUTE(output_licenses) -->
+ See <a href="${link common-definitions#binary.output_licenses}"><code>common attributes
+ </code></a>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE -->*/
+ .add(attr("output_licenses", LICENSE))
+
+ /* <!-- #BLAZE_RULE(genrule).ATTRIBUTE(executable) -->
+ Declare output to be executable.
+ <p>
+ Setting this flag to 1 means the output is an executable file and can be run using the
+ <code>run</code> command. The genrule must produce exactly one output in this case.
+ If this attribute is set, <code>run</code> will try executing the file regardless of
+ its content.
+ </p>
+ <p>Declaring data dependencies for the generated executable is not supported.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("executable", BOOLEAN).value(false).nonconfigurable(
+ "Used in computed default for $is_executable, which is itself non-configurable (and "
+ + " thus expects its dependencies to be non-configurable), because $is_executable"
+ + " is called from RunCommand.isExecutable, which has no configuration context"))
+
+ .add(attr("$is_executable", BOOLEAN)
+ .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
+ .value(
+ new Attribute.ComputedDefault() {
+ @Override
+ public Object getDefault(AttributeMap rule) {
+ return (rule.get("outs", BuildType.OUTPUT_LIST).size() == 1)
+ && rule.get("executable", BOOLEAN);
+ }
+ }))
+
+ // This is a misfeature, so don't document it. We would like to get rid of it, but that
+ // would require a cleanup of existing rules.
+ .add(attr("heuristic_label_expansion", BOOLEAN).value(false))
+
+ .removeAttribute("data")
+ .removeAttribute("deps")
+
+ .build();
+ }
+
+ @Override
+ public RuleDefinition.Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name("$genrule_base")
+ .type(RuleClassType.ABSTRACT)
+ .ancestors(BaseRuleClasses.RuleBase.class)
+ .build();
+ }
+}
+
+/*<!-- #BLAZE_RULE (NAME = genrule, TYPE = OTHER, FAMILY = General)[GENERIC_RULE] -->
+
+<p>A <code>genrule</code> generates one or more files using a user-defined Bash command.</p>
+
+<p>
+ Genrules are generic build rules that you can use if there's no specific rule for the task. If for
+ example you want to minify JavaScript files then you can use a genrule to do so. If however you
+ need to compile C++ files, stick to the existing <code>cc_*</code> rules, because all the heavy
+ lifting has already been done for you.
+</p>
+<p>
+ Do not use a genrule for running tests. There are special dispensations for tests and test
+ results, including caching policies and environment variables. Tests generally need to be run
+ after the build is complete and on the target architecture, whereas genrules are executed during
+ the build and on the host architecture (the two may be different). If you need a general purpose
+ testing rule, use <a href="${link sh_test}"><code>sh_test</code></a>.
+</p>
+
+<h4>Cross-compilation Considerations</h4>
+
+<p>
+ <em>See <a href="../bazel-user-manual.html#configurations">the user manual</a> for more info about
+ cross-compilation.</em>
+</p>
+<p>
+ While genrules run during a build, their outputs are often used after the build, for deployment or
+ testing. Consider the example of compiling C code for a microcontroller: the compiler accepts C
+ source files and generates code that runs on a microcontroller. The generated code obviously
+ cannot run on the CPU that was used for building it, but the C compiler (if compiled from source)
+ itself has to.
+</p>
+<p>
+ The build system uses the host configuration to describe the machine(s) on which the build runs
+ and the target configuration to describe the machine(s) on which the output of the build is
+ supposed to run. It provides options to configure each of these and it segregates the
+ corresponding files into separate directories to avoid conflicts.
+</p>
+<p>
+ For genrules, the build system ensures that dependencies are built appropriately:
+ <code>srcs</code> are built (if necessary) for the <em>target</em> configuration,
+ <code>tools</code> are built for the <em>host</em> configuration, and the output is considered to
+ be for the <em>target</em> configuration. It also provides <a href="${link make-variables}">
+ "Make" variables</a> that genrule commands can pass to the corresponding tools.
+</p>
+<p>
+ It is intentional that genrule defines no <code>deps</code> attribute: other built-in rules use
+ language-dependent meta information passed between the rules to automatically determine how to
+ handle dependent rules, but this level of automation is not possible for genrules. Genrules work
+ purely at the file and runfiles level.
+</p>
+
+<h4>Special Cases</h4>
+
+<p>
+ <i>Host-host compilation</i>: in some cases, the build system needs to run genrules such that the
+ output can also be executed during the build. If for example a genrule builds some custom compiler
+ which is subsequently used by another genrule, the first one has to produce its output for the
+ host configuration, because that's where the compiler will run in the other genrule. In this case,
+ the build system does the right thing automatically: it builds the <code>srcs</code> and
+ <code>outs</code> of the first genrule for the host configuration instead of the target
+ configuration. See <a href="../bazel-user-manual.html#configurations">the user manual</a> for more
+ info.
+</p>
+<p>
+ <i>JDK & C++ Tooling</i>: to use a tool from the JDK or the C++ compiler suite, the build system
+ provides a set of variables to use. See <a href="${link make-variables}">"Make" variable</a> for
+ details.
+</p>
+
+<h4>Genrule Environment</h4>
+
+<p>
+ The genrule command is executed in a Bash shell, configured to fail when a command or a pipeline
+ fails (<code>set -e -o pipefail</code>). Genrules should not access the network (except to create
+ connections between processes running within the same genrule on the same machine), though this is
+ not currently enforced.
+</p>
+<p>
+ The build system automatically deletes any existing output files, but creates any necessary parent
+ directories before it runs a genrule. It also removes any output files in case of a failure.
+</p>
+
+<h4>General Advice</h4>
+
+<ul>
+ <li>Do ensure that tools run by a genrule are deterministic and hermetic. They should not write
+ timestamps to their output, and they should use stable ordering for sets and maps, as well as
+ write only relative file paths to the output, no absolute paths. Not following this rule will
+ lead to unexpected build behavior (Bazel not rebuilding a genrule you thought it would) and
+ degrade cache performance.</li>
+ <li>Do use <code>$(location)</code> extensively, for outputs, tools and sources. Due to the
+ segregation of output files for different configurations, genrules cannot rely on hard-coded
+ and/or absolute paths.</li>
+ <li>Do write a common Skylark macro in case the same or very similar genrules are used in multiple
+ places. If the genrule is complex, consider implementing it in a script or as a Skylark rule.
+ This improves readability as well as testability.</li>
+ <li>Do make sure that the exit code correctly indicates success or failure of the genrule.</li>
+ <li>Do not write informational messages to stdout or stderr. While useful for debugging, this can
+ easily become noise; a successful genrule should be silent. On the other hand, a failing genrule
+ should emit good error messages.</li>
+ <li><code>$$</code> evaluates to a <code>$</code>, a literal dollar-sign, so in order to invoke a
+ shell command containing dollar-signs such as <code>ls $(dirname $x)</code>, one must escape it
+ thus: <code>ls $$(dirname $$x)</code>.</li>
+ <li>Avoid creating symlinks and directories. Bazel doesn't copy over the directory/symlink
+ structure created by genrules and its dependency checking of directories is unsound.</li>
+ <li>When referencing the genrule in other rules, you can use either the genrule's label or the
+ labels of individual output files. Sometimes the one approach is more readable, sometimes the
+ other: referencing outputs by name in a consuming rule's <code>srcs</code> will avoid
+ unintentionally picking up other outputs of the genrule, but can be tedious if the genrule
+ produces many outputs.</li>
+</ul>
+
+<h4 id="genrule_examples">Examples</h4>
+
+<p>
+ This example generates <code>foo.h</code>. There are no sources, because the command doesn't take
+ any input. The "binary" run by the command is a perl script in the same package as the genrule.
+</p>
+<pre class="code">
+genrule(
+ name = "foo",
+ srcs = [],
+ outs = ["foo.h"],
+ cmd = "./$(location create_foo.pl) > \"$@\"",
+ tools = ["create_foo.pl"],
+)
+</pre>
+
+<p>
+ The following example shows how to use a <a href="${link filegroup}"><code>filegroup</code>
+ </a> and the outputs of another <code>genrule</code>. Note that using <code>$(SRCS)</code> instead
+ of explicit <code>$(location)</code> directives would also work; this example uses the latter for
+ sake of demonstration.
+</p>
+<pre class="code">
+genrule(
+ name = "concat_all_files",
+ srcs = [
+ "//some:files", # a filegroup with multiple files in it ==> $(location<b>s</b>)
+ "//other:gen", # a genrule with a single output ==> $(location)
+ ],
+ outs = ["concatenated.txt"],
+ cmd = "cat $(locations //some:files) $(location //other:gen) > $@",
+)
+</pre>
+
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleSourcesProvider.java b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleSourcesProvider.java
new file mode 100644
index 0000000..fab2f78
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/genrule/GenRuleSourcesProvider.java
@@ -0,0 +1,37 @@
+// 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.genrule;
+
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+
+/**
+ * A transitive info provider that will give source files to genrule.
+ */
+@Immutable
+public final class GenRuleSourcesProvider implements TransitiveInfoProvider {
+
+ private final NestedSet<Artifact> genruleFiles;
+
+ public GenRuleSourcesProvider(NestedSet<Artifact> genruleFiles) {
+ this.genruleFiles = genruleFiles;
+ }
+
+ public NestedSet<Artifact> getGenruleFiles() {
+ return genruleFiles;
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java
index de7949c..063da16 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java
@@ -527,48 +527,41 @@
"consume_rule(name = 'c_str', s = [cdict['kind'], cdict['name'], cdict['x']])");
SkylarkRuleContext allContext = createRuleContext("//test/getrule:all_str");
- Object result = evalRuleContextCode(allContext, "ruleContext.attr.s");
- assertEquals(
- SkylarkList.createImmutable(ImmutableList.<String>of("genrule", "a", "nop_rule", "c")),
- result);
+ List<String> result = (List<String>) evalRuleContextCode(allContext, "ruleContext.attr.s");
+ assertThat(result).containsExactly("genrule", "a", "nop_rule", "c");
- result = evalRuleContextCode(createRuleContext("//test/getrule:a_str"), "ruleContext.attr.s");
- assertEquals(
- SkylarkList.createImmutable(
- ImmutableList.<String>of("genrule", "a", ":a.txt", "//test:bla")),
- result);
+ result = (List<String>) evalRuleContextCode(
+ createRuleContext("//test/getrule:a_str"), "ruleContext.attr.s");
+ assertThat(result).containsExactly("genrule", "a", ":a.txt", "//test:bla");
- result = evalRuleContextCode(createRuleContext("//test/getrule:c_str"), "ruleContext.attr.s");
- assertEquals(
- SkylarkList.createImmutable(ImmutableList.<String>of("nop_rule", "c", ":a")), result);
+ result = (List<String>) evalRuleContextCode(
+ createRuleContext("//test/getrule:c_str"), "ruleContext.attr.s");
+ assertThat(result).containsExactly("nop_rule", "c", ":a");
- result =
- evalRuleContextCode(createRuleContext("//test/getrule:genrule_attr"), "ruleContext.attr.s");
- assertEquals(
- SkylarkList.createImmutable(
- ImmutableList.<String>of(
- "name",
- "visibility",
- "tags",
- "generator_name",
- "generator_function",
- "generator_location",
- "features",
- "compatible_with",
- "restricted_to",
- "srcs",
- "tools",
- "toolchains",
- "outs",
- "cmd",
- "output_to_bindir",
- "local",
- "message",
- "executable",
- "stamp",
- "heuristic_label_expansion",
- "kind")),
- result);
+ result = (List<String>) evalRuleContextCode(
+ createRuleContext("//test/getrule:genrule_attr"), "ruleContext.attr.s");
+ assertThat(result).containsExactly(
+ "name",
+ "visibility",
+ "tags",
+ "generator_name",
+ "generator_function",
+ "generator_location",
+ "features",
+ "compatible_with",
+ "restricted_to",
+ "srcs",
+ "tools",
+ "toolchains",
+ "outs",
+ "cmd",
+ "output_to_bindir",
+ "local",
+ "message",
+ "executable",
+ "stamp",
+ "heuristic_label_expansion",
+ "kind");
}
@Test