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