diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/syntax')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java | 175 |
1 files changed, 173 insertions, 2 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java index 856c262e49..cbf8ce4e17 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.skylarkinterface.Param; @@ -861,6 +862,176 @@ public class MethodLibrary { }; @SkylarkSignature( + name = "type", + returnType = String.class, + doc = + "Returns the type name of its argument. This is useful for debugging and " + + "type-checking. Examples:" + + "<pre class=\"language-python\">" + + "type(2) == \"int\"\n" + + "type([1]) == \"list\"\n" + + "type(struct(a = 2)) == \"struct\"" + + "</pre>" + + "This function might change in the future. To write Python-compatible code and " + + "be future-proof, use it only to compare return values: " + + "<pre class=\"language-python\">" + + "if type(x) == type([]): # if x is a list" + + "</pre>", + parameters = {@Param(name = "x", doc = "The object to check type of.")}) + private static final BuiltinFunction type = + new BuiltinFunction("type") { + public String invoke(Object object) { + // There is no 'type' type in Skylark, so we return a string with the type name. + return EvalUtils.getDataTypeName(object, false); + } + }; + + @SkylarkSignature( + name = "depset", + returnType = SkylarkNestedSet.class, + doc = + "Creates a <a href=\"depset.html\">depset</a>. The <code>direct</code> parameter is a list " + + "of direct elements of the depset, and <code>transitive</code> parameter is " + + "a list of depsets whose elements become indirect elements of the created depset. " + + "The order in which elements are returned when the depset is converted to a list " + + "is specified by the <code>order</code> parameter. " + + "See the <a href=\"../depsets.md\">Depsets overview</a> for more information. " + + "<p> All elements (direct and indirect) of a depset must be of the same type. " + + "<p> The order of the created depset should be <i>compatible</i> with the order of " + + "its <code>transitive</code> depsets. <code>\"default\"</code> order is compatible " + + "with any other order, all other orders are only compatible with themselves." + + "<p> Note on backward/forward compatibility. This function currently accepts a " + + "positional <code>items</code> parameter. It is deprecated and will be removed " + + "in the future, and after its removal <code>direct</code> will become a sole " + + "positional parameter of the <code>depset</code> function. Thus, both of the " + + "following calls are equivalent and future-proof:<br>" + + "<pre class=language-python>" + + "depset(['a', 'b'], transitive = [...])\n" + + "depset(direct = ['a', 'b'], transitive = [...])\n" + + "</pre>", + parameters = { + @Param( + name = "items", + type = Object.class, + defaultValue = "[]", + doc = + "Deprecated: Either an iterable whose items become the direct elements of " + + "the new depset, in left-to-right order, or else a depset that becomes " + + "a transitive element of the new depset. In the latter case, " + + "<code>transitive</code> cannot be specified."), + @Param( + name = "order", + type = String.class, + defaultValue = "\"default\"", + doc = + "The traversal strategy for the new depset. See <a href=\"depset.html\">here</a> for " + + "the possible values."), + @Param( + name = "direct", + type = SkylarkList.class, + defaultValue = "None", + positional = false, + named = true, + noneable = true, + doc = "A list of <i>direct</i> elements of a depset."), + @Param( + name = "transitive", + named = true, + positional = false, + type = SkylarkList.class, + generic1 = SkylarkNestedSet.class, + noneable = true, + doc = "A list of depsets whose elements will become indirect elements of the depset.", + defaultValue = "None") + }, + useLocation = true) + private static final BuiltinFunction depset = + new BuiltinFunction("depset") { + public SkylarkNestedSet invoke( + Object items, String orderString, Object direct, Object transitive, Location loc) + throws EvalException { + Order order; + try { + order = Order.parse(orderString); + } catch (IllegalArgumentException ex) { + throw new EvalException(loc, ex); + } + + if (transitive == Runtime.NONE && direct == Runtime.NONE) { + // Legacy behavior. + return SkylarkNestedSet.of(order, items, loc); + } + + if (direct != Runtime.NONE && !isEmptySkylarkList(items)) { + throw new EvalException( + loc, "Do not pass both 'direct' and 'items' argument to depset constructor."); + } + + // Non-legacy behavior: either 'transitive' or 'direct' were specified. + Iterable<Object> directElements; + if (direct != Runtime.NONE) { + directElements = ((SkylarkList<?>) direct).getContents(Object.class, "direct"); + } else { + SkylarkType.checkType(items, SkylarkList.class, "items"); + directElements = ((SkylarkList<?>) items).getContents(Object.class, "items"); + } + + Iterable<SkylarkNestedSet> transitiveList; + if (transitive != Runtime.NONE) { + SkylarkType.checkType(transitive, SkylarkList.class, "transitive"); + transitiveList = + ((SkylarkList<?>) transitive).getContents(SkylarkNestedSet.class, "transitive"); + } else { + transitiveList = ImmutableList.of(); + } + SkylarkNestedSet.Builder builder = SkylarkNestedSet.builder(order, loc); + for (Object directElement : directElements) { + builder.addDirect(directElement); + } + for (SkylarkNestedSet transitiveSet : transitiveList) { + builder.addTransitive(transitiveSet); + } + return builder.build(); + } + }; + + private static boolean isEmptySkylarkList(Object o) { + return o instanceof SkylarkList && ((SkylarkList) o).isEmpty(); + } + + /** + * Returns a function-value implementing "select" (i.e. configurable attributes) in the specified + * package context. + */ + @SkylarkSignature( + name = "select", + doc = "<code>select()</code> is the helper function that makes a rule attribute " + + "<a href=\"$BE_ROOT/common-definitions.html#configurable-attributes\">configurable</a>. " + + "See <a href=\"$BE_ROOT/functions.html#select\">build encyclopedia</a> for details.", + parameters = { + @Param(name = "x", type = SkylarkDict.class, doc = "The parameter to convert."), + @Param( + name = "no_match_error", + type = String.class, + defaultValue = "''", + doc = "Optional custom error to report if no condition matches.") + }, + useLocation = true) + private static final BuiltinFunction select = + new BuiltinFunction("select") { + public Object invoke(SkylarkDict<?, ?> dict, String noMatchError, Location loc) + throws EvalException { + for (Object key : dict.keySet()) { + if (!(key instanceof String)) { + throw new EvalException( + loc, String.format("Invalid key: %s. select keys must be label references", key)); + } + } + return SelectorList.of(new SelectorValue(dict, noMatchError)); + } + }; + + @SkylarkSignature( name = "zip", doc = "Returns a <code>list</code> of <code>tuple</code>s, where the i-th tuple contains " @@ -947,8 +1118,8 @@ public class MethodLibrary { private static final ImmutableList<BaseFunction> allFunctions = ImmutableList.of( - all, any, bool, dict, dir, fail, getattr, hasattr, hash, enumerate, int_, len, list, max, - min, print, range, repr, reversed, sorted, str, tuple, zip); + all, any, bool, depset, dict, dir, fail, getattr, hasattr, hash, enumerate, int_, len, + list, max, min, print, range, repr, reversed, select, sorted, str, tuple, type, zip); static { SkylarkSignatureProcessor.configureSkylarkFunctions(MethodLibrary.class); |