Skip to content

Commit

Permalink
query: expose is_executable
Browse files Browse the repository at this point in the history
It can be useful to know this, and currently the best proxy appears to
be to look for `attributes.$is_executable` (which isn't documented), and
or-ing that with a match on `rule_class.ends_with("_test")`.

Instead, let's have a clearly documented API here.
  • Loading branch information
illicitonion committed May 16, 2024
1 parent 413e147 commit e760a13
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy.MissingFragmentPolicy;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
import com.google.devtools.build.lib.packages.TargetUtils;
Expand Down Expand Up @@ -500,7 +501,7 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env)
.add(attr("env", STRING_DICT))
.add(attr("output_licenses", LICENSE))
.add(
attr("$is_executable", BOOLEAN)
attr(Rule.IS_EXECUTABLE_ATTRIBUTE_NAME, BOOLEAN)
.value(true)
.nonconfigurable("Called from RunCommand.isExecutable, which takes a Target"))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
import com.google.devtools.build.lib.packages.MacroInstance;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.PredicateWithMessage;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
import com.google.devtools.build.lib.packages.RuleFactory;
Expand Down Expand Up @@ -170,7 +171,7 @@ public class StarlarkRuleClassFunctions implements StarlarkRuleFunctionsApi {
.add(attr("args", STRING_LIST))
.add(attr("output_licenses", LICENSE))
.addAttribute(
attr("$is_executable", BOOLEAN)
attr(Rule.IS_EXECUTABLE_ATTRIBUTE_NAME, BOOLEAN)
.value(true)
.nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
.build())
Expand Down Expand Up @@ -268,7 +269,7 @@ public static RuleClass getTestBaseRule(RuleDefinitionEnvironment env) {
+ BaseRuleClasses.DEFAULT_COVERAGE_REPORT_GENERATOR_VALUE))))
.add(attr(":run_under", LABEL).value(RUN_UNDER))
.addAttribute(
attr("$is_executable", BOOLEAN)
attr(Rule.IS_EXECUTABLE_ATTRIBUTE_NAME, BOOLEAN)
.value(true)
.nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
.build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ public class Rule implements Target, DependencyFilter.AttributeInfoProvider {

private static final OutputFile[] NO_OUTPUTS = new OutputFile[0];

public static final String IS_EXECUTABLE_ATTRIBUTE_NAME = "$is_executable";

private final Package pkg;
private final Label label;
private final RuleClass ruleClass;
Expand Down Expand Up @@ -1281,6 +1283,13 @@ public boolean useToolchainResolution() {
}
}

public boolean isExecutable() {
if (getRuleClassObject().hasAttr(IS_EXECUTABLE_ATTRIBUTE_NAME, Type.BOOLEAN)) {
return NonconfigurableAttributeMapper.of(this).get(IS_EXECUTABLE_ATTRIBUTE_NAME, Type.BOOLEAN);
}
return false;
}

public RepositoryName getRepository() {
return label.getPackageIdentifier().getRepository();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.google.devtools.build.lib.packages.ProtoUtils;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.packages.Types;
import com.google.devtools.build.lib.query2.common.CommonQueryOptions;
Expand Down Expand Up @@ -201,6 +202,8 @@ public Build.Target toTargetProtoBuffer(
BaseEncoding.base16().lowerCase().encode(transitiveDigest))); // hexify
}

rulePb.setIsExecutable(rule.isExecutable() || TargetUtils.isTestRule(target));

ImmutableMap<Aspect, ImmutableMultimap<Attribute, Label>> aspectsDependencies =
aspectResolver.computeAspectDependencies(target, dependencyFilter);
if (!aspectsDependencies.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
import com.google.devtools.build.lib.util.FileTypeSet;
Expand Down Expand Up @@ -276,12 +277,13 @@ expanded to Windows style paths (with backslash).
attr("executable", BOOLEAN)
.value(false)
.nonconfigurable(
"Used in computed default for $is_executable, which is itself non-configurable"
"Used in computed default for " + Rule.IS_EXECUTABLE_ATTRIBUTE_NAME
+ " 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"))
+ " " + Rule.IS_EXECUTABLE_ATTRIBUTE_NAME + " is called from "
+ " RunCommand.isExecutable, which has no configuration context"))
.add(
attr("$is_executable", BOOLEAN)
attr(Rule.IS_EXECUTABLE_ATTRIBUTE_NAME, BOOLEAN)
.nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
.value(
new Attribute.ComputedDefault() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.BuiltinProvider;
import com.google.devtools.build.lib.packages.MacroClass;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
import com.google.devtools.build.lib.packages.StarlarkDefinedAspect;
Expand Down Expand Up @@ -401,7 +402,7 @@ protected void visitRule(String qualifiedName, StarlarkRuleFunction ruleFunction
if (ruleClass.getRuleClassType() == RuleClassType.TEST) {
ruleInfoBuilder.setTest(true);
}
if (ruleClass.hasAttr("$is_executable", Type.BOOLEAN)) {
if (ruleClass.hasAttr(Rule.IS_EXECUTABLE_ATTRIBUTE_NAME, Type.BOOLEAN)) {
ruleInfoBuilder.setExecutable(true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1123,10 +1123,7 @@ private static boolean isExecutableNonTestRule(Target target) {
if (!(target instanceof Rule rule)) {
return false;
}
if (rule.getRuleClassObject().hasAttr("$is_executable", Type.BOOLEAN)) {
return NonconfigurableAttributeMapper.of(rule).get("$is_executable", Type.BOOLEAN);
}
return false;
return rule.isExecutable();
}

private static boolean isPlainFile(Target target) {
Expand Down
3 changes: 3 additions & 0 deletions src/main/protobuf/build.proto
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ message Rule {
// The rule class (e.g., java_library)
required string rule_class = 2;

// Whether the rule can be `bazel run` (i.e. is a binary or test rule).
optional bool is_executable = 16;

// The BUILD file and line number of the location (formatted as
// <absolute_path>:<line_number>:<column_number>) in the rule's package's
// BUILD file where the rule instance was instantiated. The line number will
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/com/google/devtools/build/lib/packages/RuleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,20 @@ private TargetData roundTrip(Target target) throws SerializationException, IOExc
ImmutableClassToInstanceMap.of(
RuleClassProvider.class, skyframeExecutor.getRuleClassProviderForTesting()));
}

@Test
public void testIsExecutable() throws Exception {
scratch.file(
"x/BUILD",
"cc_binary(name = 'cb')",
"cc_library(name = 'cl')",
"java_binary(name = 'jb')",
"java_library(name = 'jl')");
Package pkg = getTarget("//x:BUILD").getPackage();
assertThat(pkg.getRule("cb").isExecutable()).isTrue();
assertThat(pkg.getRule("jb").isExecutable()).isTrue();

assertThat(pkg.getRule("cl").isExecutable()).isFalse();
assertThat(pkg.getRule("jl").isExecutable()).isFalse();
}
}

0 comments on commit e760a13

Please sign in to comment.