Use BuiltinFunction for all builtins
Replace the uses of AbstractFunction, MixedModeFunction,
SkylarkFunction and SimpleSkylarkFunction by BuiltinFunction.
--
MOS_MIGRATED_REVID=91763158
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 3cb4e28..14bdcd7 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
@@ -54,8 +54,8 @@
import com.google.devtools.build.lib.packages.TestSize;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.packages.Type.ConversionException;
-import com.google.devtools.build.lib.syntax.AbstractFunction;
import com.google.devtools.build.lib.syntax.BaseFunction;
+import com.google.devtools.build.lib.syntax.BuiltinFunction;
import com.google.devtools.build.lib.syntax.ClassObject;
import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
import com.google.devtools.build.lib.syntax.Environment;
@@ -64,17 +64,15 @@
import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.FuncallExpression;
import com.google.devtools.build.lib.syntax.Function;
+import com.google.devtools.build.lib.syntax.FunctionSignature;
import com.google.devtools.build.lib.syntax.Label;
import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction;
import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
-import com.google.devtools.build.lib.syntax.SkylarkFunction;
-import com.google.devtools.build.lib.syntax.SkylarkFunction.SimpleSkylarkFunction;
import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.lib.syntax.SkylarkSignature;
import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
-import com.google.devtools.build.lib.syntax.UserDefinedFunction;
+import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
@@ -182,7 +180,7 @@
"Creates a new rule. Store it in a global value, so that it can be loaded and called "
+ "from BUILD files.",
onlyLoadingPhase = true,
- returnType = BaseFunction.class,
+ returnType = Function.class,
mandatoryPositionals = {
@Param(name = "implementation", type = Function.class,
doc = "the function implementing this rule, has to have exactly one parameter: "
@@ -214,85 +212,85 @@
doc = "If true, the files will be generated in the genfiles directory instead of the "
+ "bin directory. This is used for compatibility with existing rules.")},
useAst = true, useEnvironment = true)
- private static final SkylarkFunction rule = new SkylarkFunction("rule") {
+ private static final BuiltinFunction rule = new BuiltinFunction("rule") {
+ @SuppressWarnings({"rawtypes", "unchecked"}) // castMap produces
+ // an Attribute.Builder instead of a Attribute.Builder<?> but it's OK.
+ public Function invoke(Function implementation, Boolean test,
+ Object attrs, Object implicitOutputs, Boolean executable, Boolean outputToGenfiles,
+ FuncallExpression ast, Environment funcallEnv)
+ throws EvalException, ConversionException {
- @Override
- @SuppressWarnings({"rawtypes", "unchecked"}) // castMap produces
- // an Attribute.Builder instead of a Attribute.Builder<?> but it's OK.
- public Object call(Map<String, Object> arguments, FuncallExpression ast,
- Environment funcallEnv) throws EvalException, ConversionException {
- final Location loc = ast.getLocation();
+ RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL;
- RuleClassType type = RuleClassType.NORMAL;
- if (arguments.containsKey("test") && EvalUtils.toBoolean(arguments.get("test"))) {
- type = RuleClassType.TEST;
- }
+ // We'll set the name later, pass the empty string for now.
+ RuleClass.Builder builder = test
+ ? new RuleClass.Builder("", type, true, testBaseRule)
+ : new RuleClass.Builder("", type, true, baseRule);
- // We'll set the name later, pass the empty string for now.
- final RuleClass.Builder builder = type == RuleClassType.TEST
- ? new RuleClass.Builder("", type, true, testBaseRule)
- : new RuleClass.Builder("", type, true, baseRule);
-
- for (Map.Entry<String, Attribute.Builder> attr : castMap(arguments.get("attrs"),
- String.class, Attribute.Builder.class, "attrs").entrySet()) {
- Attribute.Builder<?> attrBuilder = attr.getValue();
- String attrName = attributeToNative(attr.getKey(), loc,
+ if (attrs != Environment.NONE) {
+ for (Map.Entry<String, Attribute.Builder> attr : castMap(
+ attrs, String.class, Attribute.Builder.class, "attrs").entrySet()) {
+ Attribute.Builder<?> attrBuilder = (Attribute.Builder<?>) attr.getValue();
+ String attrName = attributeToNative(attr.getKey(), ast.getLocation(),
attrBuilder.hasLateBoundValue());
builder.addOrOverrideAttribute(attrBuilder.build(attrName));
}
- if (arguments.containsKey("executable") && (Boolean) arguments.get("executable")) {
- builder.addOrOverrideAttribute(
- attr("$is_executable", BOOLEAN).value(true)
- .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
- .build());
- builder.setOutputsDefaultExecutable();
- }
+ }
+ if (executable) {
+ builder.addOrOverrideAttribute(
+ attr("$is_executable", BOOLEAN).value(true)
+ .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
+ .build());
+ builder.setOutputsDefaultExecutable();
+ }
- if (arguments.containsKey("outputs")) {
- final Object implicitOutputs = arguments.get("outputs");
- if (implicitOutputs instanceof UserDefinedFunction) {
- UserDefinedFunction func = (UserDefinedFunction) implicitOutputs;
- final SkylarkCallbackFunction callback =
- new SkylarkCallbackFunction(func, ast, (SkylarkEnvironment) funcallEnv);
- builder.setImplicitOutputsFunction(
- new SkylarkImplicitOutputsFunctionWithCallback(callback, loc));
- } else {
- builder.setImplicitOutputsFunction(new SkylarkImplicitOutputsFunctionWithMap(
- ImmutableMap.copyOf(castMap(arguments.get("outputs"), String.class, String.class,
- "implicit outputs of the rule class"))));
+ if (!(funcallEnv instanceof SkylarkEnvironment)) {
+ System.out.println("rule called from non-Skylark environment!");
+ // throw new EvaluationException("rule not accessible at the toplevel");
+ }
+
+ if (implicitOutputs != Environment.NONE) {
+ if (implicitOutputs instanceof Function) {
+ Function func = (Function) implicitOutputs;
+ final SkylarkCallbackFunction callback =
+ new SkylarkCallbackFunction(func, ast, (SkylarkEnvironment) funcallEnv);
+ builder.setImplicitOutputsFunction(
+ new SkylarkImplicitOutputsFunctionWithCallback(callback, ast.getLocation()));
+ } else {
+ builder.setImplicitOutputsFunction(new SkylarkImplicitOutputsFunctionWithMap(
+ ImmutableMap.copyOf(castMap(implicitOutputs, String.class, String.class,
+ "implicit outputs of the rule class"))));
}
- }
+ }
- if (arguments.containsKey("output_to_genfiles")
- && (Boolean) arguments.get("output_to_genfiles")) {
- builder.setOutputToGenfiles();
- }
+ if (outputToGenfiles) {
+ builder.setOutputToGenfiles();
+ }
- builder.setConfiguredTargetFunction(
- (UserDefinedFunction) arguments.get("implementation"));
- builder.setRuleDefinitionEnvironment((SkylarkEnvironment) funcallEnv);
- return new RuleFunction(builder, type);
+ builder.setConfiguredTargetFunction(implementation);
+ builder.setRuleDefinitionEnvironment((SkylarkEnvironment) funcallEnv);
+ return new RuleFunction(builder, type);
}
};
// This class is needed for testing
- static final class RuleFunction extends AbstractFunction {
+ static final class RuleFunction extends BaseFunction {
// Note that this means that we can reuse the same builder.
// This is fine since we don't modify the builder from here.
private final RuleClass.Builder builder;
private final RuleClassType type;
public RuleFunction(Builder builder, RuleClassType type) {
- super("rule");
+ super("rule", FunctionSignature.KWARGS);
this.builder = builder;
this.type = type;
}
+ @Override
@SuppressWarnings("unchecked") // the magic hidden $pkg_context variable is guaranteed
// to be a PackageContext
- @Override
- public Object call(List<Object> args, Map<String, Object> kwargs, FuncallExpression ast,
- Environment env) throws EvalException, InterruptedException {
+ public Object call(Object[] args, FuncallExpression ast, Environment env)
+ throws EvalException, InterruptedException, ConversionException {
try {
String ruleClassName = ast.getFunction().getName();
if (ruleClassName.startsWith("_")) {
@@ -305,7 +303,8 @@
}
RuleClass ruleClass = builder.build(ruleClassName);
PackageContext pkgContext = (PackageContext) env.lookup(PackageFactory.PKG_CONTEXT);
- return RuleFactory.createAndAddRule(pkgContext, ruleClass, kwargs, ast);
+ return RuleFactory.createAndAddRule(
+ pkgContext, ruleClass, (Map<String, Object>) args[0], ast);
} catch (InvalidRuleException | NameConflictException | NoSuchVariableException e) {
throw new EvalException(ast.getLocation(), e.getMessage());
}
@@ -324,33 +323,29 @@
mandatoryPositionals = {@Param(name = "label_string", type = String.class,
doc = "the label string")},
useLocation = true)
- private static final SkylarkFunction label = new SimpleSkylarkFunction("Label") {
- @Override
- public Object call(Map<String, Object> arguments, Location loc) throws EvalException,
- ConversionException {
- String labelString = (String) arguments.get("label_string");
+ private static final BuiltinFunction label = new BuiltinFunction("Label") {
+ public Label invoke(String labelString,
+ Location loc) throws EvalException, ConversionException {
try {
return labelCache.get(labelString);
} catch (ExecutionException e) {
throw new EvalException(loc, "Illegal absolute label syntax: " + labelString);
}
- }
- };
+ }
+ };
@SkylarkSignature(name = "FileType",
doc = "Creates a file filter from a list of strings. For example, to match files ending "
+ "with .cc or .cpp, use: <pre class=language-python>FileType([\".cc\", \".cpp\"])</pre>",
returnType = SkylarkFileType.class,
mandatoryPositionals = {
- @Param(name = "types", type = SkylarkList.class, generic1 = String.class,
+ @Param(name = "types", type = SkylarkList.class, generic1 = String.class, defaultValue = "[]",
doc = "a list of the accepted file extensions")})
- private static final SkylarkFunction fileType = new SimpleSkylarkFunction("FileType") {
- @Override
- public Object call(Map<String, Object> arguments, Location loc) throws EvalException,
- ConversionException {
- return SkylarkFileType.of(castList(arguments.get("types"), String.class));
- }
- };
+ private static final BuiltinFunction fileType = new BuiltinFunction("FileType") {
+ public SkylarkFileType invoke(SkylarkList types) throws ConversionException {
+ return SkylarkFileType.of(castList(types, String.class));
+ }
+ };
@SkylarkSignature(name = "to_proto",
doc = "Creates a text message from the struct parameter. This method only works if all "
@@ -373,68 +368,69 @@
@Param(name = "self", type = SkylarkClassObject.class,
doc = "this struct")},
useLocation = true)
- private static final SkylarkFunction toProto = new SimpleSkylarkFunction("to_proto") {
- @Override
- public Object call(Map<String, Object> arguments, Location loc) throws EvalException,
- ConversionException {
- ClassObject self = (ClassObject) arguments.get("self");
- StringBuilder sb = new StringBuilder();
- printTextMessage(self, sb, 0, loc);
- return sb.toString();
- }
-
- private void printTextMessage(ClassObject object, StringBuilder sb,
- int indent, Location loc) throws EvalException {
- for (String key : object.getKeys()) {
- printTextMessage(key, object.getValue(key), sb, indent, loc);
+ private static final BuiltinFunction toProto = new BuiltinFunction("to_proto") {
+ public String invoke(SkylarkClassObject self, Location loc) throws EvalException {
+ StringBuilder sb = new StringBuilder();
+ printTextMessage(self, sb, 0, loc);
+ return sb.toString();
}
- }
- private void printSimpleTextMessage(String key, Object value, StringBuilder sb,
- int indent, Location loc, String container) throws EvalException {
- if (value instanceof ClassObject) {
- print(sb, key + " {", indent);
- printTextMessage((ClassObject) value, sb, indent + 1, loc);
- print(sb, "}", indent);
- } else if (value instanceof String) {
- print(sb, key + ": \"" + escape((String) value) + "\"", indent);
- } else if (value instanceof Integer) {
- print(sb, key + ": " + value, indent);
- } else if (value instanceof Boolean) {
- // We're relying on the fact that Java converts Booleans to Strings in the same way
- // as the protocol buffers do.
- print(sb, key + ": " + value, indent);
- } else {
- throw new EvalException(loc,
- "Invalid text format, expected a struct, a string, a bool, or an int but got a "
- + EvalUtils.getDataTypeName(value) + " for " + container + " '" + key + "'");
- }
- }
-
- private void printTextMessage(String key, Object value, StringBuilder sb,
- int indent, Location loc) throws EvalException {
- if (value instanceof SkylarkList) {
- for (Object item : ((SkylarkList) value)) {
- // TODO(bazel-team): There should be some constraint on the fields of the structs
- // in the same list but we ignore that for now.
- printSimpleTextMessage(key, item, sb, indent, loc, "list element in struct field");
+ private void printTextMessage(ClassObject object, StringBuilder sb,
+ int indent, Location loc) throws EvalException {
+ for (String key : object.getKeys()) {
+ printTextMessage(key, object.getValue(key), sb, indent, loc);
}
- } else {
+ }
+
+ private void printSimpleTextMessage(String key, Object value, StringBuilder sb,
+ int indent, Location loc, String container) throws EvalException {
+ if (value instanceof ClassObject) {
+ print(sb, key + " {", indent);
+ printTextMessage((ClassObject) value, sb, indent + 1, loc);
+ print(sb, "}", indent);
+ } else if (value instanceof String) {
+ print(sb, key + ": \"" + escape((String) value) + "\"", indent);
+ } else if (value instanceof Integer) {
+ print(sb, key + ": " + value, indent);
+ } else if (value instanceof Boolean) {
+ // We're relying on the fact that Java converts Booleans to Strings in the same way
+ // as the protocol buffers do.
+ print(sb, key + ": " + value, indent);
+ } else {
+ throw new EvalException(loc,
+ "Invalid text format, expected a struct, a string, a bool, or an int but got a "
+ + EvalUtils.getDataTypeName(value) + " for " + container + " '" + key + "'");
+ }
+ }
+
+ private void printTextMessage(String key, Object value, StringBuilder sb,
+ int indent, Location loc) throws EvalException {
+ if (value instanceof SkylarkList) {
+ for (Object item : ((SkylarkList) value)) {
+ // TODO(bazel-team): There should be some constraint on the fields of the structs
+ // in the same list but we ignore that for now.
+ printSimpleTextMessage(key, item, sb, indent, loc, "list element in struct field");
+ }
+ } else {
printSimpleTextMessage(key, value, sb, indent, loc, "struct field");
+ }
}
- }
- private String escape(String string) {
- // TODO(bazel-team): use guava's SourceCodeEscapers when it's released.
- return string.replace("\"", "\\\"").replace("\n", "\\n");
- }
-
- private void print(StringBuilder sb, String text, int indent) {
- for (int i = 0; i < indent; i++) {
- sb.append(" ");
+ private String escape(String string) {
+ // TODO(bazel-team): use guava's SourceCodeEscapers when it's released.
+ return string.replace("\"", "\\\"").replace("\n", "\\n");
}
+
+ private void print(StringBuilder sb, String text, int indent) {
+ for (int i = 0; i < indent; i++) {
+ sb.append(" ");
+ }
sb.append(text);
sb.append("\n");
- }
- };
+ }
+ };
+
+ static {
+ SkylarkSignatureProcessor.configureSkylarkFunctions(SkylarkRuleClassFunctions.class);
+ }
}