Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.1.0] Fix genrule autostamping in bazel #21512

Merged
merged 2 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ java_library(
name = "genrule",
srcs = glob(["*.java"]),
deps = [
"//src/main/java/com/google/devtools/build/lib/actions:artifacts",
"//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster",
"//src/main/java/com/google/devtools/build/lib/analysis:config/execution_transition_factory",
"//src/main/java/com/google/devtools/build/lib/analysis:file_provider",
"//src/main/java/com/google/devtools/build/lib/analysis:rule_definition_environment",
"//src/main/java/com/google/devtools/build/lib/analysis:transitive_info_collection",
"//src/main/java/com/google/devtools/build/lib/cmdline",
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
"//src/main/java/com/google/devtools/build/lib/packages",
"//src/main/java/com/google/devtools/build/lib/rules/genrule",
"//src/main/java/com/google/devtools/build/lib/util:filetype",
"//third_party:guava",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,30 @@

package com.google.devtools.build.lib.bazel.rules.genrule;

import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.packages.Type;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.AliasProvider;
import com.google.devtools.build.lib.analysis.FileProvider;
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.rules.genrule.GenRuleBase;
import java.util.List;

/**
* An implementation of genrule for Bazel.
*/
public class BazelGenRule extends GenRuleBase {
/** An implementation of genrule for Bazel. */
public final class BazelGenRule extends GenRuleBase {

@Override
protected boolean isStampingEnabled(RuleContext ruleContext) {
if (!ruleContext.attributes().has("stamp", Type.BOOLEAN)) {
return false;
protected ImmutableMap<Label, NestedSet<Artifact>> collectSources(
List<? extends TransitiveInfoCollection> srcs) {
ImmutableMap.Builder<Label, NestedSet<Artifact>> labelMap =
ImmutableMap.builderWithExpectedSize(srcs.size());

for (TransitiveInfoCollection dep : srcs) {
NestedSet<Artifact> files = dep.getProvider(FileProvider.class).getFilesToBuild();
labelMap.put(AliasProvider.getDependencyLabel(dep), files);
}
return ruleContext.attributes().get("stamp", Type.BOOLEAN);

return labelMap.buildOrThrow();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@

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.Type.BOOLEAN;
import static com.google.devtools.build.lib.packages.BuildType.TRISTATE;

import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
import com.google.devtools.build.lib.analysis.config.ExecutionTransitionFactory;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.TriState;
import com.google.devtools.build.lib.rules.genrule.GenRuleBaseRule;

/**
* Rule definition for genrule for Bazel.
*/
public final class BazelGenRuleRule implements RuleDefinition {
public static final String GENRULE_SETUP_LABEL = "//tools/genrule:genrule-setup.sh";
private static final String GENRULE_SETUP_LABEL = "//tools/genrule:genrule-setup.sh";

@Override
public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) {
Expand All @@ -43,9 +44,7 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env)
attr("$genrule_setup", LABEL)
.cfg(ExecutionTransitionFactory.createFactory())
.value(env.getToolsLabel(GENRULE_SETUP_LABEL)))

// TODO(bazel-team): stamping doesn't seem to work. Fix it or remove attribute.
.add(attr("stamp", BOOLEAN).value(false))
.add(attr("stamp", TRISTATE).value(TriState.NO))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.google.devtools.build.lib.actions.CommandLines;
import com.google.devtools.build.lib.actions.CompositeRunfilesSupplier;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
import com.google.devtools.build.lib.analysis.AliasProvider;
import com.google.devtools.build.lib.analysis.CommandConstructor;
import com.google.devtools.build.lib.analysis.CommandHelper;
import com.google.devtools.build.lib.analysis.ConfigurationMakeVariableContext;
Expand All @@ -46,77 +45,28 @@
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.AttributeMap;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.packages.TriState;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.OnDemandString;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.errorprone.annotations.ForOverride;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
* 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.
* A base implementation of genrule, to be used by specific implementing rules which can change the
* semantics of {@link #collectSources}.
*/
public abstract class GenRuleBase implements RuleConfiguredTargetFactory {

/**
* 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);

/** Collects sources from src attribute. */
protected ImmutableMap<Label, NestedSet<Artifact>> collectSources(
List<? extends TransitiveInfoCollection> srcs) throws RuleErrorException {
ImmutableMap.Builder<Label, NestedSet<Artifact>> labelMap = ImmutableMap.builder();

for (TransitiveInfoCollection dep : srcs) {
NestedSet<Artifact> files = dep.getProvider(FileProvider.class).getFilesToBuild();
labelMap.put(AliasProvider.getDependencyLabel(dep), files);
}

return labelMap.build();
}

enum CommandType {
BASH,
WINDOWS_BATCH,
WINDOWS_POWERSHELL,
}

@Nullable
private static Pair<CommandType, String> determineCommandTypeAndAttribute(
RuleContext ruleContext) {
AttributeMap attributeMap = ruleContext.attributes();
if (ruleContext.isExecutedOnWindows()) {
if (attributeMap.isAttributeValueExplicitlySpecified("cmd_ps")) {
return Pair.of(CommandType.WINDOWS_POWERSHELL, "cmd_ps");
}
if (attributeMap.isAttributeValueExplicitlySpecified("cmd_bat")) {
return Pair.of(CommandType.WINDOWS_BATCH, "cmd_bat");
}
}
if (attributeMap.isAttributeValueExplicitlySpecified("cmd_bash")) {
return Pair.of(CommandType.BASH, "cmd_bash");
}
if (attributeMap.isAttributeValueExplicitlySpecified("cmd")) {
return Pair.of(CommandType.BASH, "cmd");
}
ruleContext.attributeError(
"cmd",
"missing value for `cmd` attribute, you can also set `cmd_ps` or `cmd_bat` on"
+ " Windows and `cmd_bash` on other platforms.");
return null;
}

@Override
@Nullable
public ConfiguredTarget create(RuleContext ruleContext)
public final ConfiguredTarget create(RuleContext ruleContext)
throws InterruptedException, RuleErrorException, ActionConflictException {
NestedSet<Artifact> filesToBuild =
NestedSetBuilder.wrap(Order.STABLE_ORDER, ruleContext.getOutputArtifacts());
Expand Down Expand Up @@ -145,7 +95,9 @@ public ConfiguredTarget create(RuleContext ruleContext)
// The CommandHelper class makes an explicit copy of this in the constructor, so flattening
// here should be benign.
CommandHelper commandHelper =
commandHelperBuilder(ruleContext)
CommandHelper.builder(ruleContext)
.addToolDependencies("tools")
.addToolDependencies("toolchains")
.addLabelMap(
labelMap.entrySet().stream()
.collect(toImmutableMap(Map.Entry::getKey, e -> e.getValue().toList())))
Expand All @@ -167,7 +119,7 @@ public ConfiguredTarget create(RuleContext ruleContext)
ruleContext,
resolvedSrcs,
filesToBuild,
/* makeVariableSuppliers = */ ImmutableList.of(),
/* makeVariableSuppliers= */ ImmutableList.of(),
expandToWindowsPath);
String command =
ruleContext
Expand Down Expand Up @@ -281,10 +233,49 @@ public String toString() {
.build();
}

protected CommandHelper.Builder commandHelperBuilder(RuleContext ruleContext) {
return CommandHelper.builder(ruleContext)
.addToolDependencies("tools")
.addToolDependencies("toolchains");
/** Collects sources from src attribute. */
@ForOverride
protected abstract ImmutableMap<Label, NestedSet<Artifact>> collectSources(
List<? extends TransitiveInfoCollection> srcs) throws RuleErrorException;

private static boolean isStampingEnabled(RuleContext ruleContext) {
// This intentionally does not call AnalysisUtils.isStampingEnabled(). That method returns false
// in the exec configuration (regardless of the attribute value), which is the behavior for
// binaries, but not genrules.
TriState stamp = ruleContext.attributes().get("stamp", BuildType.TRISTATE);
return stamp == TriState.YES
|| (stamp == TriState.AUTO && ruleContext.getConfiguration().stampBinaries());
}

private enum CommandType {
BASH,
WINDOWS_BATCH,
WINDOWS_POWERSHELL,
}

@Nullable
private static Pair<CommandType, String> determineCommandTypeAndAttribute(
RuleContext ruleContext) {
AttributeMap attributeMap = ruleContext.attributes();
if (ruleContext.isExecutedOnWindows()) {
if (attributeMap.isAttributeValueExplicitlySpecified("cmd_ps")) {
return Pair.of(CommandType.WINDOWS_POWERSHELL, "cmd_ps");
}
if (attributeMap.isAttributeValueExplicitlySpecified("cmd_bat")) {
return Pair.of(CommandType.WINDOWS_BATCH, "cmd_bat");
}
}
if (attributeMap.isAttributeValueExplicitlySpecified("cmd_bash")) {
return Pair.of(CommandType.BASH, "cmd_bash");
}
if (attributeMap.isAttributeValueExplicitlySpecified("cmd")) {
return Pair.of(CommandType.BASH, "cmd");
}
ruleContext.attributeError(
"cmd",
"missing value for `cmd` attribute, you can also set `cmd_ps` or `cmd_bat` on"
+ " Windows and `cmd_bash` on other platforms.");
return null;
}

/**
Expand All @@ -303,14 +294,14 @@ private static Artifact getExecutable(RuleContext ruleContext, NestedSet<Artifac
* Implementation of {@link ConfigurationMakeVariableContext} used to expand variables in a
* genrule command string.
*/
protected static class CommandResolverContext extends ConfigurationMakeVariableContext {
private static final class CommandResolverContext extends ConfigurationMakeVariableContext {

private final RuleContext ruleContext;
private final NestedSet<Artifact> resolvedSrcs;
private final NestedSet<Artifact> filesToBuild;
private final boolean windowsPath;

public CommandResolverContext(
CommandResolverContext(
RuleContext ruleContext,
NestedSet<Artifact> resolvedSrcs,
NestedSet<Artifact> filesToBuild,
Expand All @@ -327,10 +318,6 @@ public CommandResolverContext(
this.windowsPath = windowsPath;
}

public RuleContext getRuleContext() {
return ruleContext;
}

@Override
public String lookupVariable(String variableName)
throws ExpansionException, InterruptedException {
Expand Down Expand Up @@ -397,15 +384,13 @@ private String lookupVariableImpl(String variableName)
* 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 static final String expandSingletonArtifact(
private static String expandSingletonArtifact(
NestedSet<Artifact> artifacts, String variable, String artifactName)
throws ExpansionException {
if (artifacts.isEmpty()) {
throw new ExpansionException("variable '" + variable
+ "' : no " + artifactName);
throw new ExpansionException("variable '" + variable + "' : no " + artifactName);
} else if (!artifacts.isSingleton()) {
throw new ExpansionException("variable '" + variable
+ "' : more than one " + artifactName);
throw new ExpansionException("variable '" + variable + "' : more than one " + artifactName);
}
return artifacts.getSingleton().getExecPathString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ private void createStampingTargets() throws Exception {
"u/BUILD",
"genrule(name='foo_stamp', srcs=[], outs=['uu'], stamp=1, cmd='')",
"genrule(name='foo_nostamp', srcs=[], outs=['vv'], stamp=0, cmd='')",
"genrule(name='foo_autostamp', srcs=[], outs=['aa'], stamp=-1, cmd='')",
"genrule(name='foo_default', srcs=[], outs=['xx'], cmd='')");
}

Expand Down Expand Up @@ -562,6 +563,8 @@ public void testStampingWithNoStamp() throws Exception {
assertStamped(getExecConfiguredTarget("//u:foo_stamp"));
assertNotStamped("//u:foo_nostamp");
assertNotStamped(getExecConfiguredTarget("//u:foo_nostamp"));
assertNotStamped("//u:foo_autostamp");
assertNotStamped(getExecConfiguredTarget("//u:foo_autostamp"));
assertNotStamped("//u:foo_default");
}

Expand All @@ -571,8 +574,10 @@ public void testStampingWithStamp() throws Exception {
createStampingTargets();
assertStamped("//u:foo_stamp");
assertStamped(getExecConfiguredTarget("//u:foo_stamp"));
// assertStamped("//u:foo_nostamp");
assertNotStamped("//u:foo_nostamp");
assertNotStamped(getExecConfiguredTarget("//u:foo_nostamp"));
assertStamped("//u:foo_autostamp");
assertNotStamped(getExecConfiguredTarget("//u:foo_autostamp"));
assertNotStamped("//u:foo_default");
}

Expand Down
Loading