From a3ac202ec114c092221b296a2695d0aadd50031f Mon Sep 17 00:00:00 2001 From: Francois-Rene Rideau Date: Mon, 20 Apr 2015 18:35:05 +0000 Subject: Migrate SkylarkBuiltin annotations to SkylarkSignature instead. -- MOS_MIGRATED_REVID=91603959 --- .../docgen/SkylarkDocumentationProcessor.java | 84 +++- .../build/docgen/SkylarkJavaInterfaceExplorer.java | 16 +- .../devtools/build/lib/packages/MethodLibrary.java | 421 +++++++++++++-------- .../build/lib/packages/SkylarkNativeModule.java | 69 ++-- .../devtools/build/lib/rules/SkylarkAttr.java | 232 +++++++----- .../build/lib/rules/SkylarkCommandLine.java | 12 +- .../devtools/build/lib/rules/SkylarkModules.java | 12 +- .../build/lib/rules/SkylarkRuleClassFunctions.java | 90 +++-- .../rules/SkylarkRuleImplementationFunctions.java | 158 ++++---- .../devtools/build/lib/syntax/BuiltinFunction.java | 18 +- .../devtools/build/lib/syntax/Environment.java | 9 +- .../build/lib/syntax/FuncallExpression.java | 4 +- .../build/lib/syntax/FunctionSignature.java | 6 +- .../devtools/build/lib/syntax/SkylarkBuiltin.java | 61 --- .../devtools/build/lib/syntax/SkylarkFunction.java | 32 +- .../lib/syntax/SkylarkSignatureProcessor.java | 14 +- .../devtools/build/lib/syntax/SkylarkType.java | 4 +- 17 files changed, 734 insertions(+), 508 deletions(-) delete mode 100644 src/main/java/com/google/devtools/build/lib/syntax/SkylarkBuiltin.java (limited to 'src') diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java index d27e71da40..e3f6df2e57 100644 --- a/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java +++ b/src/main/java/com/google/devtools/build/docgen/SkylarkDocumentationProcessor.java @@ -27,13 +27,17 @@ import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.packages.MethodLibrary; import com.google.devtools.build.lib.rules.SkylarkModules; import com.google.devtools.build.lib.rules.SkylarkRuleContext; +import com.google.devtools.build.lib.syntax.BaseFunction; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.Environment.NoneType; import com.google.devtools.build.lib.syntax.EvalUtils; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param; +import com.google.devtools.build.lib.syntax.FuncallExpression; import com.google.devtools.build.lib.syntax.SkylarkCallable; +import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkModule; +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.HackHackEitherList; import java.io.BufferedWriter; import java.io.File; @@ -42,6 +46,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -163,7 +168,7 @@ public class SkylarkDocumentationProcessor { private void generateBuiltinItemDoc( String moduleId, SkylarkBuiltinMethod method, StringBuilder sb) { - SkylarkBuiltin annotation = method.annotation; + SkylarkSignature annotation = method.annotation; if (!annotation.documented()) { return; } @@ -172,7 +177,7 @@ public class SkylarkDocumentationProcessor { annotation.name(), annotation.name())); - if (com.google.devtools.build.lib.syntax.Function.class.isAssignableFrom(method.fieldClass)) { + if (BaseFunction.class.isAssignableFrom(method.fieldClass)) { sb.append(getSignature(moduleId, annotation)); } else { if (!annotation.returnType().equals(Object.class)) { @@ -184,11 +189,36 @@ public class SkylarkDocumentationProcessor { printParams(moduleId, annotation, sb); } - private void printParams(String moduleId, SkylarkBuiltin annotation, StringBuilder sb) { - if (annotation.mandatoryParams().length + annotation.optionalParams().length > 0) { + // Elide self parameter from mandatoryPositionals in class methods. + private static Param[] adjustedMandatoryPositionals(SkylarkSignature annotation) { + Param[] mandatoryPos = annotation.mandatoryPositionals(); + if (mandatoryPos.length > 0 + && annotation.objectType() != Object.class + && !FuncallExpression.isNamespace(annotation.objectType())) { + // Skip the self parameter, which is the first mandatory positional parameter. + return Arrays.copyOfRange(mandatoryPos, 1, mandatoryPos.length); + } else { + return mandatoryPos; + } + } + + private void printParams(String moduleId, SkylarkSignature annotation, StringBuilder sb) { + Param[] mandatoryPos = adjustedMandatoryPositionals(annotation); + Param[] optionalPos = annotation.optionalPositionals(); + Param[] optionalKey = annotation.optionalNamedOnly(); + Param[] mandatoryKey = annotation.mandatoryNamedOnly(); + Param[] star = annotation.extraPositionals(); + Param[] starStar = annotation.extraKeywords(); + + if (mandatoryPos.length + optionalPos.length + optionalKey.length + mandatoryKey.length + + star.length + starStar.length > 0) { sb.append("

Parameters

\n"); - printParams(moduleId, annotation.name(), annotation.mandatoryParams(), sb); - printParams(moduleId, annotation.name(), annotation.optionalParams(), sb); + printParams(moduleId, annotation.name(), mandatoryPos, sb); + printParams(moduleId, annotation.name(), optionalPos, sb); + printParams(moduleId, annotation.name(), star, sb); + printParams(moduleId, annotation.name(), mandatoryKey, sb); + printParams(moduleId, annotation.name(), optionalKey, sb); + printParams(moduleId, annotation.name(), starStar, sb); } else { sb.append("
\n"); } @@ -229,13 +259,29 @@ public class SkylarkDocumentationProcessor { getTypeAnchor(method.getReturnType()), objectName, methodName, args); } - private String getSignature(String objectName, SkylarkBuiltin method) { + private String getSignature(String objectName, SkylarkSignature method) { List argList = new ArrayList<>(); - for (Param param : method.mandatoryParams()) { + for (Param param : adjustedMandatoryPositionals(method)) { argList.add(param.name()); } - for (Param param : method.optionalParams()) { - argList.add(param.name() + "?"); + for (Param param : method.optionalPositionals()) { + argList.add(param.name() + "?"); // or should we use pythonic " = &#ellipsis;" instead? + } + for (Param param : method.extraPositionals()) { + argList.add("*" + param.name()); + } + if (method.extraPositionals().length == 0 + && (method.optionalNamedOnly().length > 0 || method.mandatoryNamedOnly().length > 0)) { + argList.add("*"); + } + for (Param param : method.mandatoryNamedOnly()) { + argList.add(param.name()); + } + for (Param param : method.optionalNamedOnly()) { + argList.add(param.name() + "?"); // or should we be more pythonic with this? " = ..." + } + for (Param param : method.extraKeywords()) { + argList.add("**" + param.name()); } String args = "(" + Joiner.on(", ").join(argList) + ")"; if (!objectName.equals(TOP_LEVEL_ID)) { @@ -258,7 +304,8 @@ public class SkylarkDocumentationProcessor { return "string"; } else if (Map.class.isAssignableFrom(type)) { return "dict"; - } else if (List.class.isAssignableFrom(type)) { + } else if (List.class.isAssignableFrom(type) || SkylarkList.class.isAssignableFrom(type) + || type == HackHackEitherList.class) { // Annotated Java methods can return simple java.util.Lists (which get auto-converted). return "list"; } else if (type.equals(Void.TYPE) || type.equals(NoneType.class)) { @@ -405,7 +452,7 @@ public class SkylarkDocumentationProcessor { } private void printBuiltinFunctionDoc( - String moduleName, SkylarkBuiltin annotation, StringBuilder sb) { + String moduleName, SkylarkSignature annotation, StringBuilder sb) { if (moduleName != null) { sb.append(moduleName).append("."); } @@ -427,9 +474,9 @@ public class SkylarkDocumentationProcessor { private void collectBuiltinDoc(Map modules, Field[] fields) { for (Field field : fields) { - if (field.isAnnotationPresent(SkylarkBuiltin.class)) { - SkylarkBuiltin skylarkBuiltin = field.getAnnotation(SkylarkBuiltin.class); - Class moduleClass = skylarkBuiltin.objectType(); + if (field.isAnnotationPresent(SkylarkSignature.class)) { + SkylarkSignature skylarkSignature = field.getAnnotation(SkylarkSignature.class); + Class moduleClass = skylarkSignature.objectType(); SkylarkModule skylarkModule = moduleClass.equals(Object.class) ? getTopLevelModule() : moduleClass.getAnnotation(SkylarkModule.class); @@ -437,7 +484,8 @@ public class SkylarkDocumentationProcessor { modules.put(skylarkModule.name(), new SkylarkModuleDoc(skylarkModule, moduleClass)); } modules.get(skylarkModule.name()).getBuiltinMethods() - .put(skylarkBuiltin.name(), new SkylarkBuiltinMethod(skylarkBuiltin, field.getType())); + .put(skylarkSignature.name(), + new SkylarkBuiltinMethod(skylarkSignature, field.getType())); } } } diff --git a/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java b/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java index 8f58f7187a..1118991d66 100644 --- a/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java +++ b/src/main/java/com/google/devtools/build/docgen/SkylarkJavaInterfaceExplorer.java @@ -16,9 +16,9 @@ package com.google.devtools.build.docgen; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.syntax.FuncallExpression; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; import com.google.devtools.build.lib.syntax.SkylarkCallable; import com.google.devtools.build.lib.syntax.SkylarkModule; +import com.google.devtools.build.lib.syntax.SkylarkSignature; import com.google.devtools.build.lib.util.StringUtilities; import java.lang.reflect.Method; @@ -59,17 +59,17 @@ public class SkylarkJavaInterfaceExplorer { * A class representing a Skylark built-in object or method. */ static final class SkylarkBuiltinMethod { - public final SkylarkBuiltin annotation; + public final SkylarkSignature annotation; public final Class fieldClass; - public SkylarkBuiltinMethod(SkylarkBuiltin annotation, Class fieldClass) { + public SkylarkBuiltinMethod(SkylarkSignature annotation, Class fieldClass) { this.annotation = annotation; this.fieldClass = fieldClass; } } /** - * A class representing a Skylark built-in object with its {@link SkylarkBuiltin} annotation + * A class representing a Skylark built-in object with its {@link SkylarkSignature} annotation * and the {@link SkylarkCallable} methods it might have. */ static final class SkylarkModuleDoc { @@ -80,8 +80,8 @@ public class SkylarkJavaInterfaceExplorer { private ArrayList methods = null; SkylarkModuleDoc(SkylarkModule module, Class classObject) { - this.module = Preconditions.checkNotNull(module, - "Class has to be annotated with SkylarkModule: " + classObject); + this.module = Preconditions.checkNotNull( + module, "Class has to be annotated with SkylarkModule: %s", classObject); this.classObject = classObject; this.builtin = new TreeMap<>(); } @@ -113,9 +113,9 @@ public class SkylarkJavaInterfaceExplorer { /** * Collects and returns all the Java objects reachable in Skylark from (and including) - * firstClassObject with the corresponding SkylarkBuiltin annotations. + * firstClassObject with the corresponding SkylarkSignature annotations. * - *

Note that the {@link SkylarkBuiltin} annotation for firstClassObject - firstAnnotation - + *

Note that the {@link SkylarkSignature} annotation for firstClassObject - firstAnnotation - * is also an input parameter, because some top level Skylark built-in objects and methods * are not annotated on the class, but on a field referencing them. */ diff --git a/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java index 82a2f0a3ff..9466c694b1 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/packages/MethodLibrary.java @@ -38,12 +38,13 @@ import com.google.devtools.build.lib.syntax.Function; import com.google.devtools.build.lib.syntax.MixedModeFunction; import com.google.devtools.build.lib.syntax.SelectorList; import com.google.devtools.build.lib.syntax.SelectorValue; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param; import com.google.devtools.build.lib.syntax.SkylarkEnvironment; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkModule; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; +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.HackHackEitherList; import java.util.ArrayList; import java.util.Collection; @@ -65,6 +66,16 @@ public class MethodLibrary { private MethodLibrary() {} + // TODO(bazel-team): + // the Build language and Skylark currently have different list types: + // the Build language uses plain java List (usually ArrayList) which is mutable and accepts + // any argument, whereas Skylark uses SkylarkList which is immutable and accepts only + // arguments of the same kind. Some methods below use HackHackEitherList as a magic marker + // to indicate that either kind of lists is used depending on the evaluation context. + // It might be a good idea to either have separate methods for the two languages where it matters, + // or to unify the two languages so they use the same data structure (which might require + // updating all existing clients). + // Convert string index in the same way Python does. // If index is negative, starts from the end. // If index is outside bounds, it is restricted to the valid range. @@ -109,14 +120,15 @@ public class MethodLibrary { return index; } - // supported string methods + // supported string methods - @SkylarkBuiltin(name = "join", objectType = StringModule.class, returnType = String.class, + @SkylarkSignature(name = "join", objectType = StringModule.class, returnType = String.class, doc = "Returns a string in which the string elements of the argument have been " + "joined by this string as a separator. Example:
" + "

\"|\".join([\"a\", \"b\", \"c\"]) == \"a|b|c\"
", - mandatoryParams = { - @Param(name = "elements", type = SkylarkList.class, doc = "The objects to join.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string, a separator."), + @Param(name = "elements", type = HackHackEitherList.class, doc = "The objects to join.")}) private static Function join = new MixedModeFunction("join", ImmutableList.of("this", "elements"), 2, false) { @Override @@ -126,9 +138,11 @@ public class MethodLibrary { return Joiner.on(thisString).join(seq); }}; - @SkylarkBuiltin(name = "lower", objectType = StringModule.class, returnType = String.class, - doc = "Returns the lower case version of this string.") - private static Function lower = new MixedModeFunction("lower", + @SkylarkSignature(name = "lower", objectType = StringModule.class, returnType = String.class, + doc = "Returns the lower case version of this string.", + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string, to convert to lower case.")}) + private static Function lower = new MixedModeFunction("lower", ImmutableList.of("this"), 1, false) { @Override public Object call(Object[] args, FuncallExpression ast) throws ConversionException { @@ -137,8 +151,10 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "upper", objectType = StringModule.class, returnType = String.class, - doc = "Returns the upper case version of this string.") + @SkylarkSignature(name = "upper", objectType = StringModule.class, returnType = String.class, + doc = "Returns the upper case version of this string.", + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string, to convert to upper case.")}) private static Function upper = new MixedModeFunction("upper", ImmutableList.of("this"), 1, false) { @Override @@ -148,15 +164,18 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "replace", objectType = StringModule.class, returnType = String.class, + @SkylarkSignature(name = "replace", objectType = StringModule.class, returnType = String.class, doc = "Returns a copy of the string in which the occurrences " + "of old have been replaced with new, optionally restricting " + "the number of replacements to maxsplit.", - mandatoryParams = { - @Param(name = "old", type = String.class, doc = "The string to be replaced."), - @Param(name = "new", type = String.class, doc = "The string to replace with.")}, - optionalParams = { - @Param(name = "maxsplit", type = Integer.class, doc = "The maximum number of replacements.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string."), + @Param(name = "old", type = String.class, doc = "The string to be replaced."), + @Param(name = "new", type = String.class, doc = "The string to replace with.")}, + optionalPositionals = { + @Param(name = "maxsplit", type = Integer.class, + doc = "The maximum number of replacements.")}, + useLocation = true) private static Function replace = new MixedModeFunction("replace", ImmutableList.of("this", "old", "new", "maxsplit"), 3, false) { @Override @@ -182,13 +201,17 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "split", objectType = StringModule.class, returnType = SkylarkList.class, + @SkylarkSignature(name = "split", objectType = StringModule.class, + returnType = HackHackEitherList.class, doc = "Returns a list of all the words in the string, using sep " + "as the separator, optionally limiting the number of splits to maxsplit.", - optionalParams = { - @Param(name = "sep", type = String.class, - doc = "The string to split on, default is space (\" \")."), - @Param(name = "maxsplit", type = Integer.class, doc = "The maximum number of splits.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string.")}, + optionalPositionals = { + @Param(name = "sep", type = String.class, + doc = "The string to split on, default is space (\" \")."), + @Param(name = "maxsplit", type = Integer.class, doc = "The maximum number of splits.")}, + useEnvironment = true) private static Function split = new MixedModeFunction("split", ImmutableList.of("this", "sep", "maxsplit"), 1, false) { @Override @@ -233,16 +256,19 @@ public class MethodLibrary { return stringFind(forward, thiz, sub, start, args[3], functionName + " argument"); } - @SkylarkBuiltin(name = "rfind", objectType = StringModule.class, returnType = Integer.class, + @SkylarkSignature(name = "rfind", objectType = StringModule.class, returnType = Integer.class, doc = "Returns the last index where sub is found, " + "or -1 if no such index exists, optionally restricting to " + "[start:end], " + "start being inclusive and end being exclusive.", - mandatoryParams = { - @Param(name = "sub", type = String.class, doc = "The substring to find.")}, - optionalParams = { - @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."), - @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string."), + @Param(name = "sub", type = String.class, doc = "The substring to find.")}, + optionalPositionals = { + @Param(name = "start", type = Integer.class, defaultValue = "0", + doc = "Restrict to search from this position."), + @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None", + doc = "optional position before which to restrict to search.")}) private static Function rfind = new MixedModeFunction("rfind", ImmutableList.of("this", "sub", "start", "end"), 2, false) { @Override @@ -251,16 +277,19 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "find", objectType = StringModule.class, returnType = Integer.class, + @SkylarkSignature(name = "find", objectType = StringModule.class, returnType = Integer.class, doc = "Returns the first index where sub is found, " + "or -1 if no such index exists, optionally restricting to " + "[start:end], " + "start being inclusive and end being exclusive.", - mandatoryParams = { - @Param(name = "sub", type = String.class, doc = "The substring to find.")}, - optionalParams = { - @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."), - @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string."), + @Param(name = "sub", type = String.class, doc = "The substring to find.")}, + optionalPositionals = { + @Param(name = "start", type = Integer.class, defaultValue = "0", + doc = "Restrict to search from this position."), + @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None", + doc = "optional position before which to restrict to search.")}) private static Function find = new MixedModeFunction("find", ImmutableList.of("this", "sub", "start", "end"), 2, false) { @Override @@ -270,16 +299,20 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "rindex", objectType = StringModule.class, returnType = Integer.class, + @SkylarkSignature(name = "rindex", objectType = StringModule.class, returnType = Integer.class, doc = "Returns the last index where sub is found, " + "or throw an error if no such index exists, optionally restricting to " + "[start:end], " + "start being inclusive and end being exclusive.", - mandatoryParams = { - @Param(name = "sub", type = String.class, doc = "The substring to find.")}, - optionalParams = { - @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."), - @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string."), + @Param(name = "sub", type = String.class, doc = "The substring to find.")}, + optionalPositionals = { + @Param(name = "start", type = Integer.class, defaultValue = "0", + doc = "Restrict to search from this position."), + @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None", + doc = "optional position before which to restrict to search.")}, + useLocation = true) private static Function rindex = new MixedModeFunction("rindex", ImmutableList.of("this", "sub", "start", "end"), 2, false) { @Override @@ -295,16 +328,20 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "index", objectType = StringModule.class, returnType = Integer.class, + @SkylarkSignature(name = "index", objectType = StringModule.class, returnType = Integer.class, doc = "Returns the first index where sub is found, " + "or throw an error if no such index exists, optionally restricting to " + "[start:end], " + "start being inclusive and end being exclusive.", - mandatoryParams = { - @Param(name = "sub", type = String.class, doc = "The substring to find.")}, - optionalParams = { - @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."), - @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string."), + @Param(name = "sub", type = String.class, doc = "The substring to find.")}, + optionalPositionals = { + @Param(name = "start", type = Integer.class, defaultValue = "0", + doc = "Restrict to search from this position."), + @Param(name = "end", type = Integer.class, noneable = true, + doc = "optional position before which to restrict to search.")}, + useLocation = true) private static Function index = new MixedModeFunction("index", ImmutableList.of("this", "sub", "start", "end"), 2, false) { @Override @@ -320,15 +357,18 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "count", objectType = StringModule.class, returnType = Integer.class, + @SkylarkSignature(name = "count", objectType = StringModule.class, returnType = Integer.class, doc = "Returns the number of (non-overlapping) occurrences of substring sub in " + "string, optionally restricting to [start:end], " + "start being inclusive and end being exclusive.", - mandatoryParams = { - @Param(name = "sub", type = String.class, doc = "The substring to count.")}, - optionalParams = { - @Param(name = "start", type = Integer.class, doc = "Restrict to search from this position."), - @Param(name = "end", type = Integer.class, doc = "Restrict to search before this position.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string."), + @Param(name = "sub", type = String.class, doc = "The substring to count.")}, + optionalPositionals = { + @Param(name = "start", type = Integer.class, defaultValue = "0", + doc = "Restrict to search from this position."), + @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None", + doc = "optional position before which to restrict to search.")}) private static Function count = new MixedModeFunction("count", ImmutableList.of("this", "sub", "start", "end"), 2, false) { @Override @@ -354,15 +394,18 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "endswith", objectType = StringModule.class, returnType = Boolean.class, + @SkylarkSignature(name = "endswith", objectType = StringModule.class, returnType = Boolean.class, doc = "Returns True if the string ends with sub, " + "otherwise False, optionally restricting to [start:end], " + "start being inclusive and end being exclusive.", - mandatoryParams = { - @Param(name = "sub", type = String.class, doc = "The substring to check.")}, - optionalParams = { - @Param(name = "start", type = Integer.class, doc = "Test beginning at this position."), - @Param(name = "end", type = Integer.class, doc = "Stop comparing at this position.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string."), + @Param(name = "sub", type = String.class, doc = "The substring to check.")}, + optionalPositionals = { + @Param(name = "start", type = Integer.class, defaultValue = "0", + doc = "Test beginning at this position."), + @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None", + doc = "optional position at which to stop comparing.")}) private static Function endswith = new MixedModeFunction("endswith", ImmutableList.of("this", "sub", "start", "end"), 2, false) { @Override @@ -379,15 +422,19 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "startswith", objectType = StringModule.class, returnType = Boolean.class, + @SkylarkSignature(name = "startswith", objectType = StringModule.class, + returnType = Boolean.class, doc = "Returns True if the string starts with sub, " + "otherwise False, optionally restricting to [start:end], " + "start being inclusive and end being exclusive.", - mandatoryParams = { - @Param(name = "sub", type = String.class, doc = "The substring to check.")}, - optionalParams = { - @Param(name = "start", type = Integer.class, doc = "Test beginning at this position."), - @Param(name = "end", type = Integer.class, doc = "Stop comparing at this position.")}) + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string."), + @Param(name = "sub", type = String.class, doc = "The substring to check.")}, + optionalPositionals = { + @Param(name = "start", type = Integer.class, defaultValue = "0", + doc = "Test beginning at this position."), + @Param(name = "end", type = Integer.class, noneable = true, defaultValue = "None", + doc = "Stop comparing at this position.")}) private static Function startswith = new MixedModeFunction("startswith", ImmutableList.of("this", "sub", "start", "end"), 2, false) { @Override @@ -404,9 +451,11 @@ public class MethodLibrary { }; // TODO(bazel-team): Maybe support an argument to tell the type of the whitespace. - @SkylarkBuiltin(name = "strip", objectType = StringModule.class, returnType = String.class, + @SkylarkSignature(name = "strip", objectType = StringModule.class, returnType = String.class, doc = "Returns a copy of the string in which all whitespace characters " - + "have been stripped from the beginning and the end of the string.") + + "have been stripped from the beginning and the end of the string.", + mandatoryPositionals = { + @Param(name = "self", type = String.class, doc = "This string.")}) private static Function strip = new MixedModeFunction("strip", ImmutableList.of("this"), 1, false) { @Override @@ -418,8 +467,13 @@ public class MethodLibrary { }; // slice operator - @SkylarkBuiltin(name = "$slice", documented = false, - doc = "x[start:end] returns a slice or a list slice.") + @SkylarkSignature(name = "$slice", documented = false, + mandatoryPositionals = { + @Param(name = "self", type = Object.class, doc = "This string, list or tuple."), + @Param(name = "start", type = Integer.class, doc = "start position of the slice."), + @Param(name = "end", type = Integer.class, doc = "end position of the slice.")}, + doc = "x[start:end] returns a slice or a list slice.", + useLocation = true, useEnvironment = true) private static Function slice = new MixedModeFunction("$slice", ImmutableList.of("this", "start", "end"), 3, false) { @Override @@ -448,24 +502,34 @@ public class MethodLibrary { }; // supported list methods - @SkylarkBuiltin(name = "sorted", doc = "Sort a collection.") + @SkylarkSignature(name = "sorted", returnType = HackHackEitherList.class, + doc = "Sort a collection.", + mandatoryPositionals = { + @Param(name = "self", type = HackHackEitherList.class, doc = "This list.")}, + useLocation = true, useEnvironment = true) private static Function sorted = new MixedModeFunction("sorted", - ImmutableList.of("this"), 1, false) { + ImmutableList.of("self"), 1, false) { @Override public Object call(Object[] args, FuncallExpression ast, Environment env) throws EvalException, ConversionException { - List thiz = Type.OBJECT_LIST.convert(args[0], "'sorted' operand"); + List self = Type.OBJECT_LIST.convert(args[0], "'sorted' operand"); try { - thiz = Ordering.from(EvalUtils.SKYLARK_COMPARATOR).sortedCopy(thiz); + self = Ordering.from(EvalUtils.SKYLARK_COMPARATOR).sortedCopy(self); } catch (EvalUtils.ComparisonException e) { throw new EvalException(ast.getLocation(), e); } - return convert(thiz, env, ast.getLocation()); + return convert(self, env, ast.getLocation()); } }; - @SkylarkBuiltin(name = "append", documented = false, - doc = "Adds an item to the end of the list.") + // This function has a SkylarkSignature but is only used by the Build language, not Skylark. + @SkylarkSignature(name = "append", returnType = Environment.NoneType.class, documented = false, + doc = "Adds an item to the end of the list.", + mandatoryPositionals = { + // we use List rather than SkylarkList because this is actually for use *outside* Skylark + @Param(name = "self", type = List.class, doc = "This list."), + @Param(name = "item", type = Object.class, doc = "Item to add at the end.")}, + useLocation = true, useEnvironment = true) private static Function append = new MixedModeFunction("append", ImmutableList.of("this", "x"), 2, false) { @Override @@ -477,8 +541,14 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "extend", documented = false, - doc = "Adds all items to the end of the list.") + // This function has a SkylarkSignature but is only used by the Build language, not Skylark. + @SkylarkSignature(name = "extend", returnType = Environment.NoneType.class, documented = false, + doc = "Adds all items to the end of the list.", + mandatoryPositionals = { + // we use List rather than SkylarkList because this is actually for use *outside* Skylark + @Param(name = "self", type = List.class, doc = "This list."), + @Param(name = "items", type = List.class, doc = "Items to add at the end.")}, + useLocation = true, useEnvironment = true) private static Function extend = new MixedModeFunction("extend", ImmutableList.of("this", "x"), 2, false) { @Override @@ -492,9 +562,13 @@ public class MethodLibrary { }; // dictionary access operator - @SkylarkBuiltin(name = "$index", documented = false, + @SkylarkSignature(name = "$index", documented = false, doc = "Returns the nth element of a list or string, " - + "or looks up a value in a dictionary.") + + "or looks up a value in a dictionary.", + mandatoryPositionals = { + @Param(name = "self", type = Object.class, doc = "This object."), + @Param(name = "key", type = Object.class, doc = "The index or key to access.")}, + useLocation = true) private static Function indexOperator = new MixedModeFunction("$index", ImmutableList.of("this", "index"), 2, false) { @Override @@ -543,10 +617,13 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "values", objectType = DictModule.class, returnType = SkylarkList.class, + @SkylarkSignature(name = "values", objectType = DictModule.class, + returnType = HackHackEitherList.class, doc = "Return the list of values. Dictionaries are always sorted by their keys:" + "
"
-          + "{2: \"a\", 4: \"b\", 1: \"c\"}.values() == [\"c\", \"a\", \"b\"]
\n") + + "{2: \"a\", 4: \"b\", 1: \"c\"}.values() == [\"c\", \"a\", \"b\"]\n", + mandatoryPositionals = {@Param(name = "self", type = Map.class, doc = "This dict.")}, + useLocation = true, useEnvironment = true) private static Function values = new NoArgFunction("values") { @Override public Object call(Object self, FuncallExpression ast, Environment env) @@ -557,11 +634,15 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "items", objectType = DictModule.class, returnType = SkylarkList.class, + @SkylarkSignature(name = "items", objectType = DictModule.class, + returnType = HackHackEitherList.class, doc = "Return the list of key-value tuples. Dictionaries are always sorted by their keys:" + "
"
           + "{2: \"a\", 4: \"b\", 1: \"c\"}.items() == [(1, \"c\"), (2, \"a\"), (4, \"b\")]"
-          + "
\n") + + "\n", + mandatoryPositionals = { + @Param(name = "self", type = Map.class, doc = "This dict.")}, + useLocation = true, useEnvironment = true) private static Function items = new NoArgFunction("items") { @Override public Object call(Object self, FuncallExpression ast, Environment env) @@ -577,10 +658,14 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "keys", objectType = DictModule.class, returnType = SkylarkList.class, + @SkylarkSignature(name = "keys", objectType = DictModule.class, + returnType = HackHackEitherList.class, doc = "Return the list of keys. Dictionaries are always sorted by their keys:" + "
{2: \"a\", 4: \"b\", 1: \"c\"}.keys() == [1, 2, 4]"
-          + "
\n") + + "\n", + mandatoryPositionals = { + @Param(name = "self", type = Map.class, doc = "This dict.")}, + useLocation = true, useEnvironment = true) private static Function keys = new NoArgFunction("keys") { @Override @SuppressWarnings("unchecked") // Skylark will only call this on a dict; and @@ -592,14 +677,15 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "get", objectType = DictModule.class, + @SkylarkSignature(name = "get", objectType = DictModule.class, doc = "Return the value for key if key is in the dictionary, " + "else default. If default is not given, it defaults to " + "None, so that this method never throws an error.", - mandatoryParams = { + mandatoryPositionals = { + @Param(name = "self", doc = "This dict."), @Param(name = "key", doc = "The key to look for.")}, - optionalParams = { - @Param(name = "default", + optionalPositionals = { + @Param(name = "default", defaultValue = "None", doc = "The default value to use (instead of None) if the key is not found.")}) private static Function get = new MixedModeFunction("get", ImmutableList.of("this", "key", "default"), 2, false) { @@ -629,7 +715,9 @@ public class MethodLibrary { } // unary minus - @SkylarkBuiltin(name = "-", documented = false, doc = "Unary minus operator.") + @SkylarkSignature(name = "-", documented = false, doc = "Unary minus operator.", + mandatoryPositionals = { + @Param(name = "num", type = Integer.class, doc = "The number to negate.")}) private static Function minus = new MixedModeFunction("-", ImmutableList.of("this"), 1, false) { @Override public Object call(Object[] args, FuncallExpression ast) throws ConversionException { @@ -638,12 +726,13 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "list", returnType = SkylarkList.class, + @SkylarkSignature(name = "list", returnType = SkylarkList.class, doc = "Converts a collection (e.g. set or dictionary) to a list." + "
list([1, 2]) == [1, 2]\n"
         + "list(set([2, 3, 2])) == [2, 3]\n"
         + "list({5: \"a\", 2: \"b\", 4: \"c\"}) == [2, 4, 5]
", - mandatoryParams = {@Param(name = "x", doc = "The object to convert.")}) + mandatoryPositionals = {@Param(name = "x", doc = "The object to convert.")}, + useLocation = true) private static Function list = new MixedModeFunction("list", ImmutableList.of("list"), 1, false) { @Override @@ -653,9 +742,10 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "len", returnType = Integer.class, doc = + @SkylarkSignature(name = "len", returnType = Integer.class, doc = "Returns the length of a string, list, tuple, set, or dictionary.", - mandatoryParams = {@Param(name = "x", doc = "The object to check length of.")}) + mandatoryPositionals = {@Param(name = "x", doc = "The object to check length of.")}, + useLocation = true) private static Function len = new MixedModeFunction("len", ImmutableList.of("list"), 1, false) { @@ -671,9 +761,9 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "str", returnType = String.class, doc = + @SkylarkSignature(name = "str", returnType = String.class, doc = "Converts any object to string. This is useful for debugging.", - mandatoryParams = {@Param(name = "x", doc = "The object to convert.")}) + mandatoryPositionals = {@Param(name = "x", doc = "The object to convert.")}) private static Function str = new MixedModeFunction("str", ImmutableList.of("this"), 1, false) { @Override public Object call(Object[] args, FuncallExpression ast) throws EvalException { @@ -681,12 +771,13 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "bool", returnType = Boolean.class, doc = "Converts an object to boolean. " + @SkylarkSignature(name = "bool", returnType = Boolean.class, + doc = "Converts an object to boolean. " + "It returns False if the object is None, False, an empty string, the number 0, or an " + "empty collection. Otherwise, it returns True. Similarly to Python bool " + "is also a type.", - mandatoryParams = {@Param(name = "x", doc = "The variable to convert.")}) - private static Function bool = new MixedModeFunction("bool", + mandatoryPositionals = {@Param(name = "x", doc = "The variable to convert.")}) + private static Function bool = new MixedModeFunction("bool", ImmutableList.of("this"), 1, false) { @Override public Object call(Object[] args, FuncallExpression ast) throws EvalException { @@ -694,10 +785,12 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "int", returnType = Integer.class, doc = "Converts a string to int, " + @SkylarkSignature(name = "int", returnType = Integer.class, doc = "Converts a string to int, " + "using base 10. It raises an error if the conversion fails." + "
int(\"123\") == 123
", - mandatoryParams = {@Param(name = "x", type = String.class, doc = "The string to convert.")}) + mandatoryPositionals = { + @Param(name = "x", type = String.class, doc = "The string to convert.")}, + useLocation = true) private static Function int_ = new MixedModeFunction("int", ImmutableList.of("x"), 1, false) { @Override @@ -713,13 +806,15 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "struct", returnType = SkylarkClassObject.class, doc = + @SkylarkSignature(name = "struct", returnType = SkylarkClassObject.class, doc = "Creates an immutable struct using the keyword arguments as fields. It is used to group " + "multiple values together.Example:
" + "
s = struct(x = 2, y = 3)\n"
-      + "return s.x + s.y  # returns 5
") + + "return s.x + s.y # returns 5", + extraKeywords = { + @Param(name = "kwarg", doc = "the struct fields")}, + useLocation = true) private static Function struct = new AbstractFunction("struct") { - @Override public Object call(List args, Map kwargs, FuncallExpression ast, Environment env) throws EvalException, InterruptedException { @@ -730,20 +825,21 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "set", returnType = SkylarkNestedSet.class, + @SkylarkSignature(name = "set", returnType = SkylarkNestedSet.class, doc = "Creates a set from the items." + " The set supports nesting other sets of the same element" + " type in it. A desired iteration order can also be specified.
" + " Examples:
set([\"a\", \"b\"])\n"
       + "set([1, 2, 3], order=\"compile\")
", - optionalParams = { - @Param(name = "items", type = SkylarkList.class, - doc = "The items to initialize the set with. May contain both standalone items and other" - + " sets."), - @Param(name = "order", type = String.class, - doc = "The ordering strategy for the set if it's nested, " - + "possible values are: stable (default), compile, " - + "link or naive_link.")}) + optionalPositionals = { + @Param(name = "items", type = Object.class, defaultValue = "[]", + doc = "The items to initialize the set with. May contain both standalone items " + + "and other sets."), + @Param(name = "order", type = String.class, defaultValue = "\"stable\"", + doc = "The ordering strategy for the set if it's nested, " + + "possible values are: stable (default), compile, " + + "link or naive_link.")}, + useLocation = true) private static final Function set = new MixedModeFunction("set", ImmutableList.of("items", "order"), 0, false) { @Override @@ -757,14 +853,12 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "enumerate", returnType = SkylarkList.class, + @SkylarkSignature(name = "enumerate", returnType = SkylarkList.class, doc = "Return a list of pairs (two-element lists), with the index (int) and the item from" + " the input list.\n
"
           + "enumerate([24, 21, 84]) == [[0, 24], [1, 21], [2, 84]]
\n", - mandatoryParams = { - @Param(name = "list", type = SkylarkList.class, - doc = "input list"), - }) + mandatoryPositionals = {@Param(name = "list", type = SkylarkList.class, doc = "input list")}, + useLocation = true) private static Function enumerate = new MixedModeFunction("enumerate", ImmutableList.of("list"), 1, false) { @Override @@ -783,23 +877,25 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "range", returnType = SkylarkList.class, + @SkylarkSignature(name = "range", returnType = SkylarkList.class, doc = "Creates a list where items go from start to stop, using a " + "step increment. If a single argument is provided, items will " + "range from 0 to that element." + "
range(4) == [0, 1, 2, 3]\n"
           + "range(3, 9, 2) == [3, 5, 7]\n"
           + "range(3, 0, -1) == [3, 2, 1]
", - mandatoryParams = { - @Param(name = "start", type = Integer.class, - doc = "Value of the first element"), + mandatoryPositionals = { + @Param(name = "start", type = Integer.class, + doc = "Value of the first element if stop is provided, " + + "otherwise value of stop and the actual start is 0"), }, - optionalParams = { - @Param(name = "stop", type = Integer.class, - doc = "The first item not to be included in the resulting list; " - + "generation of the list stops before stop is reached."), - @Param(name = "step", type = Integer.class, - doc = "The increment (default is 1). It may be negative.")}) + optionalPositionals = { + @Param(name = "stop", type = Integer.class, noneable = true, + doc = "optional index of the first item not to be included in the " + + "resulting list; generation of the list stops before stop is reached."), + @Param(name = "step", type = Integer.class, + doc = "The increment (default is 1). It may be negative.")}, + useLocation = true) private static final Function range = new MixedModeFunction("range", ImmutableList.of("start", "stop", "step"), 1, false) { @Override @@ -838,9 +934,10 @@ public class MethodLibrary { * Returns a function-value implementing "select" (i.e. configurable attributes) * in the specified package context. */ - @SkylarkBuiltin(name = "select", + @SkylarkSignature(name = "select", doc = "Creates a SelectorValue from the dict parameter.", - mandatoryParams = {@Param(name = "x", type = Map.class, doc = "The parameter to convert.")}) + mandatoryPositionals = { + @Param(name = "x", type = Map.class, doc = "The parameter to convert.")}) private static final Function select = new MixedModeFunction("select", ImmutableList.of("x"), 1, false) { @Override @@ -858,16 +955,16 @@ public class MethodLibrary { /** * Returns true if the object has a field of the given name, otherwise false. */ - @SkylarkBuiltin(name = "hasattr", returnType = Boolean.class, + @SkylarkSignature(name = "hasattr", returnType = Boolean.class, doc = "Returns True if the object x has a field of the given name, " + "otherwise False. Example:
" + "
hasattr(ctx.attr, \"myattr\")
", - mandatoryParams = { - @Param(name = "object", doc = "The object to check."), - @Param(name = "name", type = String.class, doc = "The name of the field.")}) + mandatoryPositionals = { + @Param(name = "object", doc = "The object to check."), + @Param(name = "name", type = String.class, doc = "The name of the field.")}, + useLocation = true, useEnvironment = true) private static final Function hasattr = new MixedModeFunction("hasattr", ImmutableList.of("object", "name"), 2, false) { - @Override public Object call(Object[] args, FuncallExpression ast, Environment env) throws EvalException, ConversionException { @@ -891,19 +988,20 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "getattr", + @SkylarkSignature(name = "getattr", doc = "Returns the struct's field of the given name if exists, otherwise default" + " if specified, otherwise rasies an error. For example, getattr(x, \"foobar\")" + " is equivalent to x.foobar." + "Example:
" + "
getattr(ctx.attr, \"myattr\")\n"
           + "getattr(ctx.attr, \"myattr\", \"mydefault\")
", - mandatoryParams = { - @Param(name = "object", doc = "The struct which's field is accessed."), - @Param(name = "name", doc = "The name of the struct field.")}, - optionalParams = { - @Param(name = "default", doc = "The default value to return in case the struct " - + "doesn't have a field of the given name.")}) + mandatoryPositionals = { + @Param(name = "object", doc = "The struct which's field is accessed."), + @Param(name = "name", doc = "The name of the struct field.")}, + optionalPositionals = { + @Param(name = "default", doc = "The default value to return in case the struct " + + "doesn't have a field of the given name.")}, + useLocation = true) private static final Function getattr = new MixedModeFunction( "getattr", ImmutableList.of("object", "name", "default"), 2, false) { @Override @@ -924,10 +1022,11 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "dir", returnType = SkylarkList.class, + @SkylarkSignature(name = "dir", returnType = SkylarkList.class, doc = "Returns a list strings: the names of the fields and " + "methods of the parameter object.", - mandatoryParams = {@Param(name = "object", doc = "The object to check.")}) + mandatoryPositionals = {@Param(name = "object", doc = "The object to check.")}, + useLocation = true, useEnvironment = true) private static final Function dir = new MixedModeFunction( "dir", ImmutableList.of("object"), 1, false) { @Override @@ -950,9 +1049,9 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "type", returnType = String.class, + @SkylarkSignature(name = "type", returnType = String.class, doc = "Returns the type name of its argument.", - mandatoryParams = {@Param(name = "object", doc = "The object to check type of.")}) + mandatoryPositionals = {@Param(name = "object", doc = "The object to check type of.")}) private static final Function type = new MixedModeFunction("type", ImmutableList.of("object"), 1, false) { @Override @@ -962,14 +1061,16 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "fail", + @SkylarkSignature(name = "fail", doc = "Raises an error that cannot be intercepted.", returnType = Environment.NoneType.class, - mandatoryParams = { + mandatoryPositionals = { @Param(name = "msg", type = String.class, doc = "Error message to display for the user")}, - optionalParams = { - @Param(name = "attr", type = String.class, - doc = "The name of the attribute that caused the error")}) + optionalPositionals = { + @Param(name = "attr", type = String.class, noneable = true, + defaultValue = "None", + doc = "The name of the attribute that caused the error")}, + useLocation = true) private static final Function fail = new MixedModeFunction( "fail", ImmutableList.of("msg", "attr"), 1, false) { @Override @@ -984,13 +1085,14 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "print", returnType = Environment.NoneType.class, + @SkylarkSignature(name = "print", returnType = Environment.NoneType.class, doc = "Prints msg to the console.", - mandatoryParams = { - @Param(name = "*args", doc = "The objects to print.")}, - optionalParams = { - @Param(name = "sep", type = String.class, - doc = "The separator string between the objects, default is space (\" \").")}) + optionalNamedOnly = { + @Param(name = "sep", type = String.class, + doc = "The separator string between the objects, default is space (\" \").")}, + // NB: as compared to Python3, we're missing optional named-only arguments 'end' and 'file' + extraPositionals = {@Param(name = "args", doc = "The objects to print.")}, + useLocation = true, useEnvironment = true) private static final Function print = new AbstractFunction("print") { @Override public Object call(List args, Map kwargs, FuncallExpression ast, @@ -1019,7 +1121,7 @@ public class MethodLibrary { } }; - @SkylarkBuiltin(name = "zip", + @SkylarkSignature(name = "zip", doc = "Returns a list of tuples, where the i-th tuple contains " + "the i-th element from each of the argument sequences or iterables. The list has the " + "size of the shortest input. With a single iterable argument, it returns a list of " @@ -1029,7 +1131,8 @@ public class MethodLibrary { + "zip([1, 2]) # == [(1,), (2,)]\n" + "zip([1, 2], [3, 4]) # == [(1, 3), (2, 4)]\n" + "zip([1, 2], [3, 4, 5]) # == [(1, 3), (2, 4)]", - returnType = SkylarkList.class) + extraPositionals = {@Param(name = "args", doc = "lists to zip")}, + returnType = SkylarkList.class, useLocation = true) private static final Function zip = new AbstractFunction("zip") { @Override public Object call(List args, Map kwargs, FuncallExpression ast, diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java index 04ff209823..cbc308e631 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java +++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java @@ -18,11 +18,11 @@ import com.google.devtools.build.lib.packages.Type.ConversionException; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.FuncallExpression; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param; import com.google.devtools.build.lib.syntax.SkylarkFunction; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkModule; +import com.google.devtools.build.lib.syntax.SkylarkSignature; +import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; import java.util.Map; @@ -36,44 +36,51 @@ import java.util.Map; + "Extra helper functions:") public class SkylarkNativeModule { - @SkylarkBuiltin(name = "glob", objectType = SkylarkNativeModule.class, + @SkylarkSignature(name = "glob", objectType = SkylarkNativeModule.class, + returnType = SkylarkList.class, doc = "Glob returns a list of every file in the current package that:
    \n" + "
  • Matches at least one pattern in include.
  • \n" + "
  • Does not match any of the patterns in exclude " + "(default []).
\n" + "If the exclude_directories argument is enabled (set to 1), " + "files of type directory will be omitted from the results (default 1).", - mandatoryParams = { + mandatoryPositionals = { @Param(name = "includes", type = SkylarkList.class, generic1 = String.class, - doc = "The list of glob patterns to include.")}, - optionalParams = { + defaultValue = "[]", doc = "The list of glob patterns to include.")}, + optionalPositionals = { @Param(name = "excludes", type = SkylarkList.class, generic1 = String.class, - doc = "The list of glob patterns to exclude."), - @Param(name = "exclude_directories", type = Integer.class, - doc = "A flag whether to exclude directories or not.")}) + defaultValue = "[]", doc = "The list of glob patterns to exclude."), + // TODO(bazel-team): accept booleans as well as integers? (and eventually migrate?) + @Param(name = "exclude_directories", type = Integer.class, defaultValue = "1", + doc = "A flag whether to exclude directories or not.")}, + useAst = true, useEnvironment = true) private static final SkylarkFunction glob = new SkylarkFunction("glob") { - @Override - public Object call(Map kwargs, FuncallExpression ast, Environment env) - throws EvalException, ConversionException, InterruptedException { - return PackageFactory.callGlob(null, false, ast, env, new Object[] { - kwargs.get("includes"), - kwargs.get("excludes"), - kwargs.get("exclude_directories") - }); - } - }; + @Override + public Object call(Map kwargs, FuncallExpression ast, Environment env) + throws EvalException, ConversionException, InterruptedException { + return PackageFactory.callGlob(null, false, ast, env, new Object[] { + kwargs.get("includes"), + kwargs.get("excludes"), + kwargs.get("exclude_directories") + }); + } + }; - @SkylarkBuiltin(name = "package_group", objectType = SkylarkNativeModule.class, + @SkylarkSignature(name = "package_group", objectType = SkylarkNativeModule.class, + returnType = Environment.NoneType.class, doc = "This function defines a set of packages and assigns a label to the group. " + "The label can be referenced in visibility attributes.", - mandatoryParams = { + mandatoryNamedOnly = { @Param(name = "name", type = String.class, doc = "A unique name for this rule.")}, - optionalParams = { + optionalNamedOnly = { @Param(name = "packages", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = "A complete enumeration of packages in this group."), @Param(name = "includes", type = SkylarkList.class, generic1 = String.class, - doc = "Other package groups that are included in this one.")}) + defaultValue = "[]", + doc = "Other package groups that are included in this one.")}, + useAst = true, useEnvironment = true) private static final SkylarkFunction packageGroup = new SkylarkFunction("package_group") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -86,19 +93,23 @@ public class SkylarkNativeModule { } }; - @SkylarkBuiltin(name = "exports_files", objectType = SkylarkNativeModule.class, + @SkylarkSignature(name = "exports_files", objectType = SkylarkNativeModule.class, + returnType = Environment.NoneType.class, doc = "Specifies a list of files belonging to this package that are exported to other " + "packages but not otherwise mentioned.", - mandatoryParams = { + mandatoryPositionals = { @Param(name = "srcs", type = SkylarkList.class, generic1 = String.class, doc = "The list of files to export.")}, - optionalParams = { - @Param(name = "visibility", type = SkylarkList.class, generic1 = String.class, + optionalPositionals = { + // TODO(bazel-team): make it possible to express the precise type ListOf(LabelDesignator) + @Param(name = "visibility", type = SkylarkList.class, + noneable = true, doc = "A visibility declaration can to be specified. The files will be visible to the " + "targets specified. If no visibility is specified, the files will be visible to " + "every package."), - @Param(name = "licenses", type = SkylarkList.class, generic1 = String.class, - doc = "Lincenses to be specified.")}) + @Param(name = "licenses", type = SkylarkList.class, generic1 = String.class, noneable = true, + doc = "Licenses to be specified.")}, + useAst = true, useEnvironment = true) private static final SkylarkFunction exportsFiles = new SkylarkFunction("exports_files") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java index 549e0252af..408c4841b7 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java @@ -26,13 +26,13 @@ import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.FuncallExpression; import com.google.devtools.build.lib.syntax.Label; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param; 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.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkModule; +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.util.FileTypeSet; @@ -153,16 +153,20 @@ public final class SkylarkAttr { } } - @SkylarkBuiltin(name = "int", doc = + @SkylarkSignature(name = "int", doc = "Creates an attribute of type int.", objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", type = Integer.class, - doc = DEFAULT_DOC + " If not specified, default is 0."), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + @Param(name = "default", type = Integer.class, defaultValue = "0", + doc = DEFAULT_DOC + " If not specified, default is 0."), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, defaultValue = "False", + doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction integer = new SkylarkFunction("int") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -171,16 +175,20 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "string", doc = + @SkylarkSignature(name = "string", doc = "Creates an attribute of type string.", objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", type = String.class, - doc = DEFAULT_DOC + " If not specified, default is \"\"."), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + @Param(name = "default", type = String.class, + defaultValue = "\"\"", doc = DEFAULT_DOC + " If not specified, default is \"\"."), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, + defaultValue = "False", doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction string = new SkylarkFunction("string") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -189,29 +197,35 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "label", doc = + @SkylarkSignature(name = "label", doc = "Creates an attribute of type Label. " + "It is the only way to specify a dependency to another target. " + "If you need a dependency that the user cannot overwrite, make the attribute " + "private (starts with _).", objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", type = Label.class, callbackEnabled = true, - doc = DEFAULT_DOC + " If not specified, default is None. " - + "Use the Label function to specify a default value."), - @Param(name = "executable", type = Boolean.class, doc = EXECUTABLE_DOC), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "allow_files", doc = ALLOW_FILES_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "providers", type = SkylarkList.class, generic1 = String.class, - doc = "mandatory providers every dependency has to have"), - @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class, - doc = ALLOW_RULES_DOC), - @Param(name = "single_file", doc = - "if True, the label must correspond to a single File. " - + "Access it through ctx.file.<attribute_name>."), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + @Param(name = "default", type = Label.class, callbackEnabled = true, noneable = true, + defaultValue = "None", + doc = DEFAULT_DOC + " If not specified, default is None. " + + "Use the Label function to specify a default value."), + @Param(name = "executable", type = Boolean.class, defaultValue = "False", + doc = EXECUTABLE_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "allow_files", defaultValue = "False", doc = ALLOW_FILES_DOC), + @Param(name = "mandatory", type = Boolean.class, defaultValue = "False", + doc = MANDATORY_DOC), + @Param(name = "providers", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = "mandatory providers every dependency has to have"), + @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class, + noneable = true, defaultValue = "None", doc = ALLOW_RULES_DOC), + @Param(name = "single_file", type = Boolean.class, defaultValue = "False", + doc = "if True, the label must correspond to a single File. " + + "Access it through ctx.file.<attribute_name>."), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction label = new SkylarkFunction("label") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -220,11 +234,11 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "string_list", doc = + @SkylarkSignature(name = "string_list", doc = "Creates an attribute of type list of strings", objectType = SkylarkAttr.class, returnType = Attribute.class, - optionalParams = { + optionalPositionals = { @Param(name = "default", type = SkylarkList.class, generic1 = String.class, doc = DEFAULT_DOC + " If not specified, default is []."), @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), @@ -240,25 +254,31 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "label_list", doc = + @SkylarkSignature(name = "label_list", doc = "Creates an attribute of type list of labels. " + "See label for more information.", objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", type = SkylarkList.class, generic1 = Label.class, - callbackEnabled = true, - doc = DEFAULT_DOC + " If not specified, default is []. " - + "Use the Label function to specify a default value."), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "allow_files", doc = ALLOW_FILES_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class, - doc = ALLOW_RULES_DOC), - @Param(name = "non_empty", type = Boolean.class, doc = NON_EMPTY_DOC), - @Param(name = "providers", type = SkylarkList.class, generic1 = String.class, - doc = "mandatory providers every dependency has to have"), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + @Param(name = "default", type = SkylarkList.class, generic1 = Label.class, + callbackEnabled = true, defaultValue = "[]", + doc = DEFAULT_DOC + " If not specified, default is []. " + + "Use the Label function to specify a default value."), + @Param(name = "allow_files", // bool or FileType filter + defaultValue = "False", doc = ALLOW_FILES_DOC), + @Param(name = "allow_rules", type = SkylarkList.class, generic1 = String.class, + noneable = true, defaultValue = "None", doc = ALLOW_RULES_DOC), + @Param(name = "providers", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = "mandatory providers every dependency has to have"), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, defaultValue = "False", + doc = MANDATORY_DOC), + @Param(name = "non_empty", type = Boolean.class, defaultValue = "False", + doc = NON_EMPTY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction labelList = new SkylarkFunction("label_list") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -267,15 +287,20 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "bool", doc = + @SkylarkSignature(name = "bool", doc = "Creates an attribute of type bool. Its default value is False.", objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", type = Boolean.class, doc = DEFAULT_DOC), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + @Param(name = "default", type = Boolean.class, + defaultValue = "False", doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, defaultValue = "False", + doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction bool = new SkylarkFunction("bool") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -284,17 +309,22 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "output", doc = + @SkylarkSignature(name = "output", doc = "Creates an attribute of type output. Its default value is None. " + "The user provides a file name (string) and the rule must create an action that " + "generates the file.", objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", type = Label.class, doc = DEFAULT_DOC), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + @Param(name = "default", type = Label.class, noneable = true, + defaultValue = "None", doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, defaultValue = "False", + doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction output = new SkylarkFunction("output") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -303,17 +333,23 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "output_list", doc = + @SkylarkSignature(name = "output_list", doc = "Creates an attribute of type list of outputs. Its default value is []. " + "See output above for more information.", objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", type = SkylarkList.class, generic1 = Label.class, doc = DEFAULT_DOC), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "non_empty", type = Boolean.class, doc = NON_EMPTY_DOC), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + @Param(name = "default", type = SkylarkList.class, generic1 = Label.class, + defaultValue = "[]", doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, + defaultValue = "False", doc = MANDATORY_DOC), + @Param(name = "non_empty", type = Boolean.class, defaultValue = "False", + doc = NON_EMPTY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction outputList = new SkylarkFunction("output_list") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -322,17 +358,23 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "string_dict", doc = + @SkylarkSignature(name = "string_dict", doc = "Creates an attribute of type dictionary, mapping from string to string. " - + "Its default value is {}.", + + "Its default value is dict().", objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", type = Map.class, doc = DEFAULT_DOC), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "non_empty", type = Boolean.class, doc = NON_EMPTY_DOC), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + @Param(name = "default", type = Map.class, + defaultValue = "{}", doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, defaultValue = "False", + doc = MANDATORY_DOC), + @Param(name = "non_empty", type = Boolean.class, defaultValue = "False", + doc = NON_EMPTY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction stringDict = new SkylarkFunction("string_dict") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) @@ -341,16 +383,22 @@ public final class SkylarkAttr { } }; - @SkylarkBuiltin(name = "license", doc = + @SkylarkSignature(name = "license", doc = "Creates an attribute of type license. Its default value is NO_LICENSE.", // TODO(bazel-team): Implement proper license support for Skylark. objectType = SkylarkAttr.class, - returnType = Attribute.class, - optionalParams = { - @Param(name = "default", doc = DEFAULT_DOC), - @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, doc = FLAGS_DOC), - @Param(name = "mandatory", type = Boolean.class, doc = MANDATORY_DOC), - @Param(name = "cfg", type = ConfigurationTransition.class, doc = CONFIGURATION_DOC)}) + returnType = Attribute.Builder.class, + optionalNamedOnly = { + // TODO(bazel-team): ensure this is the correct default value + @Param(name = "default", defaultValue = "None", noneable = true, + doc = DEFAULT_DOC), + @Param(name = "flags", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = FLAGS_DOC), + @Param(name = "mandatory", type = Boolean.class, defaultValue = "False", + doc = MANDATORY_DOC), + @Param(name = "cfg", type = ConfigurationTransition.class, noneable = true, + defaultValue = "None", doc = CONFIGURATION_DOC)}, + useAst = true, useEnvironment = true) private static SkylarkFunction license = new SkylarkFunction("license") { @Override public Object call(Map kwargs, FuncallExpression ast, Environment env) diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkCommandLine.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkCommandLine.java index dbf72184a0..260c7e4a8f 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkCommandLine.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkCommandLine.java @@ -20,12 +20,12 @@ import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.syntax.EvalException; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param; import com.google.devtools.build.lib.syntax.SkylarkFunction.SimpleSkylarkFunction; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkModule; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; +import com.google.devtools.build.lib.syntax.SkylarkSignature; +import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; import java.util.Map; @@ -36,12 +36,12 @@ import java.util.Map; doc = "Module for creating memory efficient command lines.") public class SkylarkCommandLine { - @SkylarkBuiltin(name = "join_paths", + @SkylarkSignature(name = "join_paths", doc = "Creates a single command line argument joining the paths of a set " + "of files on the separator string.", objectType = SkylarkCommandLine.class, returnType = String.class, - mandatoryParams = { + mandatoryPositionals = { @Param(name = "separator", type = String.class, doc = "the separator string to join on"), @Param(name = "files", type = SkylarkNestedSet.class, generic1 = Artifact.class, doc = "the files to concatenate")}) @@ -59,11 +59,11 @@ public class SkylarkCommandLine { }; // TODO(bazel-team): this method should support sets of objects and substitute all struct fields. - @SkylarkBuiltin(name = "template", + @SkylarkSignature(name = "template", doc = "Transforms a set of files to a list of strings using the template string.", objectType = SkylarkCommandLine.class, returnType = SkylarkList.class, - mandatoryParams = { + mandatoryPositionals = { @Param(name = "items", type = SkylarkNestedSet.class, generic1 = Artifact.class, doc = "The set of structs to transform."), @Param(name = "template", type = String.class, diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java index 7ab0cee813..226ceebee3 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkModules.java @@ -24,10 +24,10 @@ import com.google.devtools.build.lib.packages.SkylarkNativeModule; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.EvaluationContext; import com.google.devtools.build.lib.syntax.Function; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; import com.google.devtools.build.lib.syntax.SkylarkEnvironment; import com.google.devtools.build.lib.syntax.SkylarkFunction; import com.google.devtools.build.lib.syntax.SkylarkModule; +import com.google.devtools.build.lib.syntax.SkylarkSignature; import com.google.devtools.build.lib.syntax.ValidationEnvironment; import java.lang.reflect.Field; @@ -150,11 +150,11 @@ public class SkylarkModules { ImmutableList.Builder functions, ImmutableMap.Builder objects) { try { for (Field field : type.getDeclaredFields()) { - if (field.isAnnotationPresent(SkylarkBuiltin.class)) { + if (field.isAnnotationPresent(SkylarkSignature.class)) { // Fields in Skylark modules are sometimes private. - // Nevertheless they have to be annotated with SkylarkBuiltin. + // Nevertheless they have to be annotated with SkylarkSignature. field.setAccessible(true); - SkylarkBuiltin annotation = field.getAnnotation(SkylarkBuiltin.class); + SkylarkSignature annotation = field.getAnnotation(SkylarkSignature.class); Object value = field.get(null); if (SkylarkFunction.class.isAssignableFrom(field.getType())) { SkylarkFunction function = (SkylarkFunction) value; @@ -179,8 +179,8 @@ public class SkylarkModules { */ private static void collectSkylarkTypesFromFields(Class classObject, Set builtIn) { for (Field field : classObject.getDeclaredFields()) { - if (field.isAnnotationPresent(SkylarkBuiltin.class)) { - SkylarkBuiltin annotation = field.getAnnotation(SkylarkBuiltin.class); + if (field.isAnnotationPresent(SkylarkSignature.class)) { + SkylarkSignature annotation = field.getAnnotation(SkylarkSignature.class); if (SkylarkFunction.class.isAssignableFrom(field.getType())) { // Ignore non-global values. if (annotation.objectType().equals(Object.class)) { 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 68e9cfe53d..3cb4e28f96 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 @@ -55,6 +55,7 @@ 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.ClassObject; import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject; import com.google.devtools.build.lib.syntax.Environment; @@ -64,13 +65,13 @@ 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.Label; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param; 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 java.util.List; @@ -84,11 +85,11 @@ import java.util.concurrent.ExecutionException; public class SkylarkRuleClassFunctions { //TODO(bazel-team): proper enum support - @SkylarkBuiltin(name = "DATA_CFG", returnType = ConfigurationTransition.class, + @SkylarkSignature(name = "DATA_CFG", returnType = ConfigurationTransition.class, doc = "Experimental. Specifies a transition to the data configuration.") private static final Object dataTransition = ConfigurationTransition.DATA; - @SkylarkBuiltin(name = "HOST_CFG", returnType = ConfigurationTransition.class, + @SkylarkSignature(name = "HOST_CFG", returnType = ConfigurationTransition.class, doc = "Specifies a transition to the host configuration.") private static final Object hostTransition = ConfigurationTransition.HOST; @@ -177,39 +178,42 @@ public class SkylarkRuleClassFunctions { // TODO(bazel-team): implement attribute copy and other rule properties - @SkylarkBuiltin(name = "rule", doc = + @SkylarkSignature(name = "rule", doc = "Creates a new rule. Store it in a global value, so that it can be loaded and called " + "from BUILD files.", onlyLoadingPhase = true, - returnType = Function.class, - mandatoryParams = { - @Param(name = "implementation", type = UserDefinedFunction.class, - doc = "the function implementing this rule, has to have exactly one parameter: " - + "ctx. The function is called during analysis phase for each " - + "instance of the rule. It can access the attributes provided by the user. " - + "It must create actions to generate all the declared outputs.") + returnType = BaseFunction.class, + mandatoryPositionals = { + @Param(name = "implementation", type = Function.class, + doc = "the function implementing this rule, has to have exactly one parameter: " + + "ctx. The function is called during analysis phase for each " + + "instance of the rule. It can access the attributes provided by the user. " + + "It must create actions to generate all the declared outputs.") }, - optionalParams = { - @Param(name = "test", type = Boolean.class, doc = "Whether this rule is a test rule. " - + "If True, the rule must end with _test (otherwise it cannot)."), - @Param(name = "attrs", type = Map.class, doc = - "dictionary to declare all the attributes of the rule. It maps from an attribute name " - + "to an attribute object (see attr module). " - + "Attributes starting with _ are private, and can be used to add " - + "an implicit dependency on a label."), - @Param(name = "outputs", doc = "outputs of this rule. " - + "It is a dictionary mapping from string to a template name. " - + "For example: {\"ext\": \"${name}.ext\"}.
" - + "The dictionary key becomes a field in ctx.outputs. " - // TODO(bazel-team): Make doc more clear, wrt late-bound attributes. - + "It may also be a function (which receives ctx.attr as argument) " - + "returning such a dictionary."), - @Param(name = "executable", type = Boolean.class, - doc = "whether this rule always outputs an executable of the same name or not. If True, " - + "there must be an action that generates ctx.outputs.executable."), - @Param(name = "output_to_genfiles", type = Boolean.class, - 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.")}) + optionalPositionals = { + @Param(name = "test", type = Boolean.class, defaultValue = "False", + doc = "Whether this rule is a test rule. " + + "If True, the rule must end with _test (otherwise it cannot)."), + @Param(name = "attrs", type = Map.class, noneable = true, defaultValue = "None", doc = + "dictionary to declare all the attributes of the rule. It maps from an attribute name " + + "to an attribute object (see attr module). " + + "Attributes starting with _ are private, and can be used to add " + + "an implicit dependency on a label."), + @Param(name = "outputs", type = Map.class, callbackEnabled = true, noneable = true, + defaultValue = "None", doc = "outputs of this rule. " + + "It is a dictionary mapping from string to a template name. " + + "For example: {\"ext\": \"${name}.ext\"}.
" + + "The dictionary key becomes a field in ctx.outputs. " + // TODO(bazel-team): Make doc more clear, wrt late-bound attributes. + + "It may also be a function (which receives ctx.attr as argument) " + + "returning such a dictionary."), + @Param(name = "executable", type = Boolean.class, defaultValue = "False", + doc = "whether this rule always outputs an executable of the same name or not. If " + + "True, there must be an action that generates ctx.outputs.executable."), + @Param(name = "output_to_genfiles", type = Boolean.class, defaultValue = "False", + 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") { @Override @@ -313,12 +317,13 @@ public class SkylarkRuleClassFunctions { } } - @SkylarkBuiltin(name = "Label", doc = "Creates a Label referring to a BUILD target. Use " + @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:
Label(\"//tools:default\")
", returnType = Label.class, - mandatoryParams = {@Param(name = "label_string", type = String.class, - doc = "the label string")}) + 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 arguments, Location loc) throws EvalException, @@ -332,11 +337,11 @@ public class SkylarkRuleClassFunctions { } }; - @SkylarkBuiltin(name = "FileType", + @SkylarkSignature(name = "FileType", doc = "Creates a file filter from a list of strings. For example, to match files ending " + "with .cc or .cpp, use:
FileType([\".cc\", \".cpp\"])
", returnType = SkylarkFileType.class, - mandatoryParams = { + mandatoryPositionals = { @Param(name = "types", type = SkylarkList.class, generic1 = String.class, doc = "a list of the accepted file extensions")}) private static final SkylarkFunction fileType = new SimpleSkylarkFunction("FileType") { @@ -347,7 +352,7 @@ public class SkylarkRuleClassFunctions { } }; - @SkylarkBuiltin(name = "to_proto", + @SkylarkSignature(name = "to_proto", doc = "Creates a text message from the struct parameter. This method only works if all " + "struct elements (recursively) are strings, ints, booleans, other structs or a " + "list of these types. Quotes and new lines in strings are escaped. " @@ -362,7 +367,12 @@ public class SkylarkRuleClassFunctions { + "# key {\n# inner_key: 1\n# }\n# key {\n# inner_key: 2\n# }\n\n" + "struct(key=struct(inner_key=struct(inner_inner_key='text'))).to_proto()\n" + "# key {\n# inner_key {\n# inner_inner_key: \"text\"\n# }\n# }\n", - objectType = SkylarkClassObject.class, returnType = String.class) + objectType = SkylarkClassObject.class, returnType = String.class, + mandatoryPositionals = { + // TODO(bazel-team): shouldn't we accept any ClassObject? + @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 arguments, Location loc) throws EvalException, diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java index cc582ab17e..0019fe8683 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleImplementationFunctions.java @@ -36,12 +36,12 @@ import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.syntax.Label; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin; -import com.google.devtools.build.lib.syntax.SkylarkBuiltin.Param; 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.SkylarkNestedSet; +import com.google.devtools.build.lib.syntax.SkylarkSignature; +import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.Map; @@ -69,32 +69,43 @@ public class SkylarkRuleImplementationFunctions { * command = 'command', * ) */ - @SkylarkBuiltin(name = "action", + @SkylarkSignature(name = "action", doc = "Creates an action that runs an executable or a shell command.", objectType = SkylarkRuleContext.class, returnType = Environment.NoneType.class, - mandatoryParams = { - @Param(name = "outputs", type = SkylarkList.class, generic1 = Artifact.class, - doc = "list of the output files of the action")}, - optionalParams = { - @Param(name = "inputs", type = SkylarkList.class, generic1 = Artifact.class, - doc = "list of the input files of the action"), - @Param(name = "executable", doc = "the executable file to be called by the action"), - @Param(name = "arguments", type = SkylarkList.class, generic1 = String.class, - doc = "command line arguments of the action"), - @Param(name = "mnemonic", type = String.class, doc = "mnemonic"), - @Param(name = "command", doc = "shell command to execute"), - @Param(name = "progress_message", type = String.class, - doc = "progress message to show to the user during the build, e.g. \"Compiling foo.cc to" - + " create foo.o\""), - @Param(name = "use_default_shell_env", type = Boolean.class, - doc = "whether the action should use the built in shell environment or not"), - @Param(name = "env", type = Map.class, doc = "sets the dictionary of environment variables"), - @Param(name = "execution_requirements", type = Map.class, - doc = "information for scheduling the action"), - @Param(name = "input_manifests", type = Map.class, - doc = "sets the map of input manifests files; " - + "they are typicially generated by the command_helper")}) + mandatoryPositionals = { + @Param(name = "self", type = SkylarkRuleContext.class, doc = "This RuleContext.")}, + mandatoryNamedOnly = { + @Param(name = "outputs", type = SkylarkList.class, generic1 = Artifact.class, + doc = "list of the output files of the action")}, + optionalNamedOnly = { + @Param(name = "inputs", type = SkylarkList.class, generic1 = Artifact.class, + defaultValue = "[]", doc = "list of the input files of the action"), + @Param(name = "executable", type = Object.class, // File or PathFragment or None + defaultValue = "None", + doc = "the executable file to be called by the action"), + @Param(name = "arguments", type = SkylarkList.class, generic1 = String.class, + defaultValue = "[]", doc = "command line arguments of the action"), + @Param(name = "mnemonic", type = String.class, noneable = true, + defaultValue = "None", doc = "mnemonic"), + @Param(name = "command", type = Object.class, // string or ListOf(string) or NoneType + defaultValue = "None", doc = "shell command to execute"), + @Param(name = "progress_message", type = String.class, noneable = true, + defaultValue = "None", + doc = "progress message to show to the user during the build, " + + "e.g. \"Compiling foo.cc to create foo.o\""), + @Param(name = "use_default_shell_env", type = Boolean.class, defaultValue = "False", + doc = "whether the action should use the built in shell environment or not"), + @Param(name = "env", type = Map.class, noneable = true, defaultValue = "None", + doc = "sets the dictionary of environment variables"), + @Param(name = "execution_requirements", type = Map.class, noneable = true, + defaultValue = "None", + doc = "information for scheduling the action"), + @Param(name = "input_manifests", type = Map.class, noneable = true, + defaultValue = "None", + doc = "sets the map of input manifests files; " + + "they are typicially generated by the command_helper")}, + useLocation = true) private static final SkylarkFunction createSpawnAction = new SimpleSkylarkFunction("action") { @@ -175,17 +186,17 @@ public class SkylarkRuleImplementationFunctions { }; // TODO(bazel-team): improve this method to be more memory friendly - @SkylarkBuiltin(name = "file_action", + @SkylarkSignature(name = "file_action", doc = "Creates a file write action.", objectType = SkylarkRuleContext.class, - returnType = Environment.NoneType.class, - optionalParams = { - @Param(name = "executable", type = Boolean.class, - doc = "whether the output file should be executable (default is False)"), - }, - mandatoryParams = { + returnType = FileWriteAction.class, + mandatoryPositionals = { + @Param(name = "self", type = SkylarkRuleContext.class, doc = "this context"), @Param(name = "output", type = Artifact.class, doc = "the output file"), - @Param(name = "content", type = String.class, doc = "the contents of the file")}) + @Param(name = "content", type = String.class, doc = "the contents of the file")}, + optionalPositionals = { + @Param(name = "executable", type = Boolean.class, + doc = "whether the output file should be executable (default is False)")}) private static final SkylarkFunction createFileWriteAction = new SimpleSkylarkFunction("file_action") { @@ -204,18 +215,22 @@ public class SkylarkRuleImplementationFunctions { } }; - @SkylarkBuiltin(name = "template_action", + @SkylarkSignature(name = "template_action", doc = "Creates a template expansion action.", objectType = SkylarkRuleContext.class, - returnType = Environment.NoneType.class, - mandatoryParams = { - @Param(name = "template", type = Artifact.class, doc = "the template file"), - @Param(name = "output", type = Artifact.class, doc = "the output file"), - @Param(name = "substitutions", type = Map.class, - doc = "substitutions to make when expanding the template")}, - optionalParams = { - @Param(name = "executable", type = Boolean.class, - doc = "whether the output file should be executable (default is False)")}) + returnType = TemplateExpansionAction.class, + mandatoryPositionals = { + @Param(name = "self", type = SkylarkRuleContext.class, doc = "this context")}, + mandatoryNamedOnly = { + @Param(name = "template", type = Artifact.class, + doc = "the template file"), + @Param(name = "output", type = Artifact.class, + doc = "the output file"), + @Param(name = "substitutions", type = Map.class, + doc = "substitutions to make when expanding the template")}, + optionalNamedOnly = { + @Param(name = "executable", type = Boolean.class, + doc = "whether the output file should be executable (default is False)")}) private static final SkylarkFunction createTemplateAction = new SimpleSkylarkFunction("template_action") { @@ -245,12 +260,13 @@ public class SkylarkRuleImplementationFunctions { * A built in Skylark helper function to access the * Transitive info providers of Transitive info collections. */ - @SkylarkBuiltin(name = "provider", + @SkylarkSignature(name = "provider", doc = "Returns the transitive info provider provided by the target.", - mandatoryParams = { - @Param(name = "target", type = TransitiveInfoCollection.class, - doc = "the configured target which provides the provider"), - @Param(name = "type", type = String.class, doc = "the class type of the provider")}) + mandatoryPositionals = { + @Param(name = "target", type = TransitiveInfoCollection.class, + doc = "the configured target which provides the provider"), + @Param(name = "type", type = String.class, doc = "the class type of the provider")}, + useLocation = true) private static final SkylarkFunction provider = new SimpleSkylarkFunction("provider") { @Override public Object call(Map params, Location loc) throws EvalException { @@ -271,21 +287,28 @@ public class SkylarkRuleImplementationFunctions { }; // TODO(bazel-team): Remove runfile states from Skylark. - @SkylarkBuiltin(name = "runfiles", + @SkylarkSignature(name = "runfiles", doc = "Creates a runfiles object.", objectType = SkylarkRuleContext.class, returnType = Runfiles.class, - optionalParams = { - @Param(name = "files", type = SkylarkList.class, generic1 = Artifact.class, - doc = "The list of files to be added to the runfiles."), - // TODO(bazel-team): If we have a memory efficient support for lazy list containing NestedSets - // we can remove this and just use files = [file] + list(set) - @Param(name = "transitive_files", type = SkylarkNestedSet.class, generic1 = Artifact.class, - doc = "The (transitive) set of files to be added to the runfiles."), - @Param(name = "collect_data", type = Boolean.class, doc = "Whether to collect the data " - + "runfiles from the dependencies in srcs, data and deps attributes."), - @Param(name = "collect_default", type = Boolean.class, doc = "Whether to collect the default " - + "runfiles from the dependencies in srcs, data and deps attributes.")}) + mandatoryPositionals = { + @Param(name = "self", type = SkylarkRuleContext.class, doc = "This context.")}, + optionalPositionals = { + @Param(name = "files", type = SkylarkList.class, generic1 = Artifact.class, + defaultValue = "[]", doc = "The list of files to be added to the runfiles."), + // TODO(bazel-team): If we have a memory efficient support for lazy list containing + // NestedSets we can remove this and just use files = [file] + list(set) + // Also, allow empty set for init + @Param(name = "transitive_files", type = SkylarkNestedSet.class, generic1 = Artifact.class, + noneable = true, defaultValue = "None", + doc = "The (transitive) set of files to be added to the runfiles."), + @Param(name = "collect_data", type = Boolean.class, defaultValue = "False", + doc = "Whether to collect the data " + + "runfiles from the dependencies in srcs, data and deps attributes."), + @Param(name = "collect_default", type = Boolean.class, defaultValue = "False", + doc = "Whether to collect the default " + + "runfiles from the dependencies in srcs, data and deps attributes.")}, + useLocation = true) private static final SkylarkFunction runfiles = new SimpleSkylarkFunction("runfiles") { @Override public Object call(Map params, Location loc) throws EvalException, @@ -309,15 +332,16 @@ public class SkylarkRuleImplementationFunctions { } }; - @SkylarkBuiltin(name = "command_helper", doc = "Experimental. Creates a command helper class.", + @SkylarkSignature(name = "command_helper", doc = "Experimental. Creates a command helper class.", objectType = SkylarkRuleContext.class, returnType = CommandHelper.class, - mandatoryParams = { - @Param(name = "tools", type = SkylarkList.class, generic1 = TransitiveInfoCollection.class, - doc = "list of tools (list of targets)"), - @Param(name = "label_dict", type = Map.class, - doc = "dictionary of resolved labels and the corresponding list of Files " - + "(a dict of Label : list of Files)")}) + mandatoryPositionals = { + @Param(name = "self", type = SkylarkRuleContext.class, doc = "this RuleContext"), + @Param(name = "tools", type = SkylarkList.class, generic1 = TransitiveInfoCollection.class, + doc = "list of tools (list of targets)"), + @Param(name = "label_dict", type = Map.class, defaultValue = "{}", + doc = "dictionary of resolved labels and the corresponding list of Files " + + "(a dict of Label : list of Files)")}) private static final SkylarkFunction createCommandHelper = new SimpleSkylarkFunction("command_helper") { @SuppressWarnings("unchecked") diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java index fb9e2fdabb..08ea8ee3c1 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java @@ -16,6 +16,7 @@ package com.google.devtools.build.lib.syntax; import com.google.common.base.Preconditions; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.packages.Type.ConversionException; +import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor.HackHackEitherList; import com.google.devtools.build.lib.syntax.SkylarkType.SkylarkFunctionType; import java.lang.reflect.InvocationTargetException; @@ -59,6 +60,9 @@ public class BuiltinFunction extends BaseFunction { // to be used as size of argument array by the outer call method. private int innerArgumentCount; + // The returnType of the method. + private Class returnType; + /** Create unconfigured function from its name */ public BuiltinFunction(String name) { @@ -202,6 +206,7 @@ public class BuiltinFunction extends BaseFunction { enforcedArgumentTypes = new ArrayList<>(); this.extraArgs = SkylarkSignatureProcessor.getExtraArgs(annotation); super.configure(annotation); + this.returnType = annotation.returnType(); } // finds the method and makes it accessible (which is needed to find it, and later to use it) @@ -236,8 +241,6 @@ public class BuiltinFunction extends BaseFunction { "bad argument count for %s: method has %s arguments, type list has %s", getName(), innerArgumentCount, parameterTypes.length); - // TODO(bazel-team): also grab the returnType from the annotations, - // and check it against method return type if (enforcedArgumentTypes != null) { for (int i = 0; i < arguments; i++) { SkylarkType enforcedType = enforcedArgumentTypes.get(i); @@ -254,8 +257,7 @@ public class BuiltinFunction extends BaseFunction { // No need to enforce Simple types on the Skylark side, the JVM will do it for us. enforcedArgumentTypes.set(i, null); } else if (enforcedType instanceof SkylarkType.Combination) { - Preconditions.checkArgument( - enforcedType.getType() == parameterType, msg); + Preconditions.checkArgument(enforcedType.getType() == parameterType, msg); } else { Preconditions.checkArgument( parameterType == Object.class || parameterType == null, msg); @@ -265,6 +267,14 @@ public class BuiltinFunction extends BaseFunction { } // No need for the enforcedArgumentTypes List if all the types were Simple enforcedArgumentTypes = FunctionSignature.valueListOrNull(enforcedArgumentTypes); + + if (returnType != null) { + Class type = returnType; + if (type == HackHackEitherList.class) { + type = Object.class; + } + Preconditions.checkArgument(type == invokeMethod.getReturnType()); + } } /** Configure by copying another function's configuration */ diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java index bd595127fc..a4a4bce771 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java @@ -37,14 +37,15 @@ import javax.annotation.Nullable; */ public class Environment { - @SkylarkBuiltin(name = "True", returnType = Boolean.class, doc = "Literal for the boolean true.") + @SkylarkSignature(name = "True", returnType = Boolean.class, + doc = "Literal for the boolean true.") private static final Boolean TRUE = true; - @SkylarkBuiltin(name = "False", returnType = Boolean.class, + @SkylarkSignature(name = "False", returnType = Boolean.class, doc = "Literal for the boolean false.") private static final Boolean FALSE = false; - @SkylarkBuiltin(name = "PACKAGE_NAME", returnType = String.class, + @SkylarkSignature(name = "PACKAGE_NAME", returnType = String.class, doc = "The name of the package the rule or build extension is called from. " + "This variable is special, because its value comes from outside of the extension " + "module (it comes from the BUILD file), so it can only be accessed in functions " @@ -65,7 +66,7 @@ public class Environment { private NoneType() {} } - @SkylarkBuiltin(name = "None", returnType = NoneType.class, doc = "Literal for the None value.") + @SkylarkSignature(name = "None", returnType = NoneType.class, doc = "Literal for the None value.") public static final NoneType NONE = new NoneType(); protected final Map env = new HashMap<>(); diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java index 79f2efa677..f32dcdf9ce 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.syntax; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -458,7 +459,8 @@ public final class FuncallExpression extends Expression { } } - static boolean isNamespace(Class classObject) { + @VisibleForTesting + public static boolean isNamespace(Class classObject) { return classObject.isAnnotationPresent(SkylarkModule.class) && classObject.getAnnotation(SkylarkModule.class).namespace(); } diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java b/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java index 49398f576b..3792e21fbc 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/FunctionSignature.java @@ -194,7 +194,7 @@ public abstract class FunctionSignature implements Serializable { * *

V is the class of defaultValues and T is the class of types. * When parsing a function definition at compile-time, they are <Expression, Expression>; - * when processing a @SkylarkBuiltin annotation at build-time, <Object, SkylarkType>. + * when processing a @SkylarkSignature annotation at build-time, <Object, SkylarkType>. */ @AutoValue public abstract static class WithValues implements Serializable { @@ -247,7 +247,7 @@ public abstract class FunctionSignature implements Serializable { /** * Parse a list of Parameter into a FunctionSignature. * - *

To be used both by the Parser and by the SkylarkBuiltin annotation processor. + *

To be used both by the Parser and by the SkylarkSignature annotation processor. */ public static WithValues of(Iterable> parameters) throws SignatureException { @@ -512,7 +512,7 @@ public abstract class FunctionSignature implements Serializable { return of(0, 0, numMandatory, false, false, names); } - /** Invalid signature from Parser or from SkylarkBuiltin annotations */ + /** Invalid signature from Parser or from SkylarkSignature annotations */ protected static class SignatureException extends Exception { @Nullable private final Parameter parameter; diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkBuiltin.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkBuiltin.java deleted file mode 100644 index 894a77a9ea..0000000000 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkBuiltin.java +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2014 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package com.google.devtools.build.lib.syntax; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - - -/** - * An annotation to mark built-in keyword argument methods accessible from Skylark. - */ -@Target({ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface SkylarkBuiltin { - - String name(); - - String doc(); - - Param[] mandatoryParams() default {}; - - Param[] optionalParams() default {}; - - boolean documented() default true; - - Class objectType() default Object.class; - - Class returnType() default Object.class; - - boolean onlyLoadingPhase() default false; - - /** - * An annotation for parameters of Skylark built-in functions. - */ - @Retention(RetentionPolicy.RUNTIME) - public @interface Param { - - String name(); - - String doc(); - - Class type() default Object.class; - - Class generic1() default Object.class; - - boolean callbackEnabled() default false; - } -} diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkFunction.java index b5aa6c9729..c90a4efbeb 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkFunction.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkFunction.java @@ -31,10 +31,10 @@ import java.util.concurrent.ExecutionException; * All usable arguments have to be specified. In case of ambiguous arguments (a parameter is * specified as positional and keyword arguments in the function call) an exception is thrown. */ -public abstract class SkylarkFunction extends AbstractFunction { +public abstract class SkylarkFunction extends BaseFunction { private ImmutableList parameters; - private ImmutableMap parameterTypes; + private ImmutableMap parameterTypes; private int mandatoryParamNum; private boolean configured = false; private Class objectType; @@ -50,19 +50,37 @@ public abstract class SkylarkFunction extends AbstractFunction { /** * Configures the parameter of this Skylark function using the annotation. */ - public void configure(SkylarkBuiltin annotation) { + public void configure(SkylarkSignature annotation) { Preconditions.checkState(!configured); Preconditions.checkArgument( getName().equals(annotation.name()), "%s != %s", getName(), annotation.name()); + Preconditions.checkArgument( + annotation.optionalPositionals().length == 0 || annotation.mandatoryNamedOnly().length == 0, + "SkylarkFunction %s: forbidden simultaneous optionalPositionals and mandatoryNamedOnly", + getName()); + Preconditions.checkArgument( + annotation.extraPositionals().length == 0 && annotation.extraKeywords().length == 0, + "SkylarkFunction %s: forbidden extra arguments", getName()); mandatoryParamNum = 0; ImmutableList.Builder paramListBuilder = ImmutableList.builder(); - ImmutableMap.Builder paramTypeBuilder = ImmutableMap.builder(); - for (SkylarkBuiltin.Param param : annotation.mandatoryParams()) { + ImmutableMap.Builder paramTypeBuilder = ImmutableMap.builder(); + for (SkylarkSignature.Param param : annotation.mandatoryPositionals()) { + if (!param.name().equals("self")) { + paramListBuilder.add(param.name()); + paramTypeBuilder.put(param.name(), param); + mandatoryParamNum++; + } + } + for (SkylarkSignature.Param param : annotation.optionalPositionals()) { + paramListBuilder.add(param.name()); + paramTypeBuilder.put(param.name(), param); + } + for (SkylarkSignature.Param param : annotation.mandatoryNamedOnly()) { paramListBuilder.add(param.name()); paramTypeBuilder.put(param.name(), param); mandatoryParamNum++; } - for (SkylarkBuiltin.Param param : annotation.optionalParams()) { + for (SkylarkSignature.Param param : annotation.optionalNamedOnly()) { paramListBuilder.add(param.name()); paramTypeBuilder.put(param.name(), param); } @@ -152,7 +170,7 @@ public abstract class SkylarkFunction extends AbstractFunction { private void checkTypeAndAddArg(String paramName, Object value, ImmutableMap.Builder arguments, Location loc) throws EvalException { - SkylarkBuiltin.Param param = parameterTypes.get(paramName); + SkylarkSignature.Param param = parameterTypes.get(paramName); if (param.callbackEnabled() && value instanceof Function) { // If we pass a function as an argument we trust the Function implementation with the type // check. It's OK since the function needs to be called manually anyway. diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java index bacb78a7dc..1d9cd818bd 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java @@ -114,6 +114,18 @@ public class SkylarkSignatureProcessor { } } + /** + * Fake class to use in SkylarkSignature annotations to indicate that either List or SkylarkList + * may be used, depending on whether the Build language or Skylark is being evaluated. + */ + // TODO(bazel-team): either make SkylarkList a subclass of List (mutable or immutable throwing + // runtime exceptions), or have the Build language use immutable SkylarkList, but either way, + // do away with this hack. + public static class HackHackEitherList { + private HackHackEitherList() { } + } + + /** * Configures the parameter of this Skylark function using the annotation. */ @@ -134,7 +146,7 @@ public class SkylarkSignatureProcessor { return new Parameter.Star<>(null); } if (param.type() != Object.class) { - if (param.type() == List.class) { + if (param.type() == HackHackEitherList.class) { // NB: a List in the annotation actually indicates either a List or a SkylarkList // and we trust the BuiltinFunction to do the enforcement. // For automatic document generation purpose, we lie and just say it's a list; diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java index 01fb872a0e..f07fd61914 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkType.java @@ -836,9 +836,9 @@ public abstract class SkylarkType { } /** - * Creates a SkylarkType from the SkylarkBuiltin annotation. + * Creates a SkylarkType from the SkylarkSignature annotation. */ - public static SkylarkType getReturnType(SkylarkBuiltin annotation) { + public static SkylarkType getReturnType(SkylarkSignature annotation) { if (Function.class.isAssignableFrom(annotation.returnType())) { return SkylarkFunctionType.of(annotation.name()); } else { -- cgit v1.2.3