Skylark: only allow rules that are exported
For the purpose of package serialization (that will be necessary for caching),
only accept to use RuleFunction-s (as defined by skylark's rule() function)
that have been exported from a .bzl file with foo = rule(...), using
a finalization pass that walks exported identifiers and blesses RuleFunction-s.
--
MOS_MIGRATED_REVID=97236441
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
index f05d222..6fcf85f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleClassFunctions.java
@@ -73,6 +73,7 @@
import com.google.devtools.build.lib.syntax.SkylarkSignature;
import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor;
+import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -284,6 +285,8 @@
// This is fine since we don't modify the builder from here.
private final RuleClass.Builder builder;
private final RuleClassType type;
+ private PathFragment skylarkFile;
+ private String ruleClassName;
public RuleFunction(Builder builder, RuleClassType type) {
super("rule", FunctionSignature.KWARGS);
@@ -297,7 +300,10 @@
public Object call(Object[] args, FuncallExpression ast, Environment env)
throws EvalException, InterruptedException, ConversionException {
try {
- String ruleClassName = ast.getFunction().getName();
+ if (ruleClassName == null || skylarkFile == null) {
+ throw new EvalException(ast.getLocation(),
+ "Invalid rule class hasn't been exported by a Skylark file");
+ }
if (ruleClassName.startsWith("_")) {
throw new EvalException(ast.getLocation(), "Invalid rule class name '" + ruleClassName
+ "', cannot be private");
@@ -315,12 +321,36 @@
}
}
+ /**
+ * Export a RuleFunction from a Skylark file with a given name.
+ */
+ void export(PathFragment skylarkFile, String ruleClassName) {
+ this.skylarkFile = skylarkFile;
+ this.ruleClassName = ruleClassName;
+ }
+
@VisibleForTesting
RuleClass.Builder getBuilder() {
return builder;
}
}
+ public static void exportRuleFunctions(SkylarkEnvironment env, PathFragment skylarkFile) {
+ for (String name : env.getDirectVariableNames()) {
+ try {
+ Object value = env.lookup(name);
+ if (value instanceof RuleFunction) {
+ RuleFunction function = (RuleFunction) value;
+ if (function.skylarkFile == null) {
+ function.export(skylarkFile, name);
+ }
+ }
+ } catch (NoSuchVariableException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
+
@SkylarkSignature(name = "Label", doc = "Creates a Label referring to a BUILD target. Use "
+ "this function only when you want to give a default value for the label attributes. "
+ "Example: <br><pre class=language-python>Label(\"//tools:default\")</pre>",