// Copyright 2016 The Bazel Authors. 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.packages; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.skylarkinterface.Param; import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature; import com.google.devtools.build.lib.syntax.BaseFunction; import com.google.devtools.build.lib.syntax.BuiltinFunction; import com.google.devtools.build.lib.syntax.Environment.GlobalFrame; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.syntax.MethodLibrary; import com.google.devtools.build.lib.syntax.Runtime; import com.google.devtools.build.lib.syntax.SelectorList; import com.google.devtools.build.lib.syntax.SelectorValue; import com.google.devtools.build.lib.syntax.SkylarkDict; import com.google.devtools.build.lib.syntax.SkylarkList; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor; import com.google.devtools.build.lib.syntax.SkylarkType; /** * A helper class containing additional built in functions for Bazel (BUILD files and .bzl files). */ public class BazelLibrary { // TODO(bazel-team): Move to MethodLibrary alongside other pure-Skylark builtins. @SkylarkSignature( name = "type", returnType = String.class, doc = "Returns the type name of its argument. This is useful for debugging and " + "type-checking. Examples:" + "
"
            + "type(2) == \"int\"\n"
            + "type([1]) == \"list\"\n"
            + "type(struct(a = 2)) == \"struct\""
            + "
" + "This function might change in the future. To write Python-compatible code and " + "be future-proof, use it only to compare return values: " + "
"
            + "if type(x) == type([]):  # if x is a list"
            + "
", 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 depset. The direct parameter is a list " + "of direct elements of the depset, and transitive 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 order parameter. " + "See the Depsets overview for more information. " + "

All elements (direct and indirect) of a depset must be of the same type. " + "

The order of the created depset should be compatible with the order of " + "its transitive depsets. \"default\" order is compatible " + "with any other order, all other orders are only compatible with themselves." + "

Note on backward/forward compatibility. This function currently accepts a " + "positional items parameter. It is deprecated and will be removed " + "in the future, and after its removal direct will become a sole " + "positional parameter of the depset function. Thus, both of the " + "following calls are equivalent and future-proof:
" + "

"
            + "depset(['a', 'b'], transitive = [...])\n"
            + "depset(direct = ['a', 'b'], transitive = [...])\n"
            + "
", 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, " + "transitive cannot be specified." ), @Param( name = "order", type = String.class, defaultValue = "\"default\"", doc = "The traversal strategy for the new depset. See here for " + "the possible values." ), @Param( name = "direct", type = SkylarkList.class, defaultValue = "None", positional = false, named = true, noneable = true, doc = "A list of direct 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 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 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 = "select() is the helper function that makes a rule attribute " + "configurable. " + "See build encyclopedia 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)); } }; /** Adds bindings for all the builtin functions of this class to the given map builder. */ public static void addBindingsToBuilder(ImmutableMap.Builder builder) { for (BaseFunction function : allFunctions) { builder.put(function.getName(), function); } } private static final ImmutableList allFunctions = ImmutableList.of(select, depset, type); /** A global frame containing pure Skylark builtins and some Bazel builtins. */ public static final GlobalFrame GLOBALS = createGlobals(); private static GlobalFrame createGlobals() { ImmutableMap.Builder builder = ImmutableMap.builder(); addSkylarkGlobalsToBuilder(builder); return GlobalFrame.createForBuiltins(builder.build()); } /** * Adds bindings for skylark built-ins to the given environment map builder. */ public static void addSkylarkGlobalsToBuilder(ImmutableMap.Builder envBuilder) { Runtime.addConstantsToBuilder(envBuilder); MethodLibrary.addBindingsToBuilder(envBuilder); BazelLibrary.addBindingsToBuilder(envBuilder); } static { SkylarkSignatureProcessor.configureSkylarkFunctions(BazelLibrary.class); } }