Check if the parent rule is extendable
PiperOrigin-RevId: 574401779
Change-Id: I229188da52fd4474abc52403e8a94c569f2cb6d1
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java
index be9d98b..db57dcf 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkRuleClassFunctions.java
@@ -361,7 +361,12 @@
executable = parent.isExecutableStarlark();
test = parent.getRuleClassType() == RuleClassType.TEST;
- // TODO b/300201845 - verify that parent is extendable
+ failIf(
+ !parent.isExtendable(),
+ "The rule '%s' is not extendable. Only Starlark rules not using deprecated features (like"
+ + " implicit outputs, output to genfiles) may be extended. Special rules like"
+ + " analysis tests or rules using build_settings cannot be extended.",
+ parent.getName());
failIf(
executableUnchecked != Starlark.UNBOUND,
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
index 88dd1a2..72c5ad1 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
@@ -823,6 +823,7 @@
// the condition removes {@link StarlarkRuleClasssFunctions.baseRule} and binaryBaseRule,
// which are marked as Starlark (because of Stardoc) && abstract at the same time
starlarkParent = parents[0];
+ Preconditions.checkArgument(starlarkParent.isExtendable());
}
for (RuleClass parent : parents) {
if (parent.getValidityPredicate() != PredicatesWithMessage.<Rule>alwaysTrue()) {
@@ -928,6 +929,15 @@
this.useToolchainResolution(ToolchainResolutionMode.DISABLED);
}
+ boolean extendable =
+ starlark
+ && (type == RuleClassType.NORMAL || type == RuleClassType.TEST)
+ && implicitOutputsFunction == ImplicitOutputsFunction.NONE
+ && outputsToBindir
+ && !starlarkTestable
+ && !isAnalysisTest
+ && buildSetting == null;
+
return new RuleClass(
name,
callstack,
@@ -935,6 +945,7 @@
type,
starlarkParent,
starlark,
+ extendable,
starlarkTestable,
documented,
outputsToBindir,
@@ -1625,6 +1636,7 @@
private final RuleClassType type;
@Nullable private final RuleClass starlarkParent;
private final boolean isStarlark;
+ private final boolean extendable;
private final boolean starlarkTestable;
private final boolean documented;
private final boolean outputsToBindir;
@@ -1759,6 +1771,7 @@
RuleClassType type,
RuleClass starlarkParent,
boolean isStarlark,
+ boolean extendable,
boolean starlarkTestable,
boolean documented,
boolean outputsToBindir,
@@ -1795,6 +1808,7 @@
this.type = type;
this.starlarkParent = starlarkParent;
this.isStarlark = isStarlark;
+ this.extendable = extendable;
this.targetKind = name + Rule.targetKindSuffix();
this.starlarkTestable = starlarkTestable;
this.documented = documented;
@@ -2630,6 +2644,11 @@
return isStarlark;
}
+ /** Returns true if this RuleClass can be extended. */
+ public boolean isExtendable() {
+ return extendable;
+ }
+
/** Returns true if this RuleClass is Starlark-defined and is subject to analysis-time tests. */
public boolean isStarlarkTestable() {
return starlarkTestable;
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
index df0a491..c4128ff 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
@@ -1026,6 +1026,7 @@
RuleClassType.NORMAL,
/* starlarkParent= */ null,
/* isStarlark= */ starlarkExecutable,
+ /* extendable= */ false,
/* starlarkTestable= */ false,
documented,
binaryOutput,
diff --git a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleClassFunctionsTest.java
index 13f27d5..12c85ee 100644
--- a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleClassFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkRuleClassFunctionsTest.java
@@ -3713,7 +3713,61 @@
.containsExactly("java", "cc");
}
- // TODO: b/300201845 - verify build_setting, analysis_test, ... can't be extended
+ private String notExtendableError(String rule) {
+ return String.format(
+ "The rule '%s' is not extendable. Only Starlark rules not using deprecated features (like"
+ + " implicit outputs, output to genfiles) may be extended. Special rules like"
+ + " analysis tests or rules using build_settings cannot be extended.",
+ rule);
+ }
+
+ @Test
+ public void extendRule_notExtendable() throws Exception {
+ BazelEvaluationTestCase ev = new BazelEvaluationTestCase("//extend_rule_testing:child.bzl");
+ ev.exec(
+ "def impl():", //
+ " pass");
+
+ ev.execAndExport("parent_library = rule(impl, output_to_genfiles = True)");
+ ev.checkEvalError(notExtendableError("parent_library"), "rule(impl, parent = parent_library)");
+
+ ev.execAndExport("parent_library = rule(impl, _skylark_testable = True)");
+ ev.checkEvalError(notExtendableError("parent_library"), "rule(impl, parent = parent_library)");
+
+ ev.execAndExport("parent_test = rule(impl, analysis_test = True)");
+ ev.checkEvalError(notExtendableError("parent_test"), "rule(impl, parent = parent_test)");
+
+ ev.update("config", new StarlarkConfig());
+ ev.execAndExport("parent_library = rule(impl, build_setting = config.int())");
+ ev.checkEvalError(notExtendableError("parent_library"), "rule(impl, parent = parent_library)");
+
+ ev.execAndExport("parent_library = rule(impl, outputs = {'deploy': '%{name}_deploy.jar'})");
+ ev.checkEvalError(notExtendableError("parent_library"), "rule(impl, parent = parent_library)");
+ }
+
+ @Test
+ public void extendRule_nativeRule_notExtendable() throws Exception {
+ scratch.file(
+ "extend_rule_testing/child.bzl",
+ "def _impl(ctx):",
+ " ctx.super()",
+ "my_library = rule(",
+ " implementation = _impl,",
+ " parent = native.alias,",
+ " fragments = ['cc']",
+ ")");
+ scratch.file(
+ "extend_rule_testing/BUILD",
+ "load(':child.bzl', 'my_library')",
+ "my_library(name = 'my_target')");
+
+ reporter.removeHandler(failFastHandler);
+ reporter.addHandler(ev.getEventCollector());
+ getConfiguredTarget("//extend_rule_testing:my_target");
+
+ ev.assertContainsError("Parent needs to be a Starlark rule");
+ }
+
@Test
public void extendRule_toolchains_merged() throws Exception {
scratchParentRule(