blob: 8c392c938905db92ef9e559db567c6ed2d0a6e10 [file] [log] [blame]
Laurent Le Brunbd9576a2016-11-18 15:10:51 +00001// Copyright 2016 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.syntax;
16
17import com.google.common.collect.ImmutableList;
18import com.google.devtools.build.lib.collect.nestedset.Order;
19import com.google.devtools.build.lib.events.Location;
20import com.google.devtools.build.lib.skylarkinterface.Param;
21import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
brandjond0f13122017-04-07 15:55:12 +000022import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
Laurent Le Brunbd9576a2016-11-18 15:10:51 +000023import java.util.List;
24
25/**
26 * A helper class containing additional built in functions for Bazel (BUILD files and .bzl files).
27 */
28public class BazelLibrary {
29
30 @SkylarkSignature(
31 name = "type",
32 returnType = String.class,
33 doc =
34 "Returns the type name of its argument. This is useful for debugging and "
35 + "type-checking. Examples:"
36 + "<pre class=\"language-python\">"
37 + "type(2) == \"int\"\n"
38 + "type([1]) == \"list\"\n"
39 + "type(struct(a = 2)) == \"struct\""
40 + "</pre>"
41 + "This function might change in the future. To write Python-compatible code and "
42 + "be future-proof, use it only to compare return values: "
43 + "<pre class=\"language-python\">"
44 + "if type(x) == type([]): # if x is a list"
45 + "</pre>",
46 parameters = {@Param(name = "x", doc = "The object to check type of.")}
47 )
48 private static final BuiltinFunction type =
49 new BuiltinFunction("type") {
50 public String invoke(Object object) {
51 // There is no 'type' type in Skylark, so we return a string with the type name.
Vladimir Moskvaba4f0bb2017-01-30 15:45:49 +000052 return EvalUtils.getDataTypeName(object, false);
Vladimir Moskvad200daf2016-12-23 16:35:37 +000053 }
54 };
55
56 @SkylarkSignature(
57 name = "depset",
58 returnType = SkylarkNestedSet.class,
59 doc =
Jon Brandvein50ca4b62017-02-24 17:37:56 +000060 "Creates a <a href=\"depset.html\">depset</a>. In the case that <code>items</code> is an "
61 + "iterable, its contents become the direct elements of the depset, with their left-to-"
62 + "right order preserved, and the depset has no transitive elements. In the case that "
63 + "<code>items</code> is a depset, it is made the sole transitive element of the new "
64 + "depset, and no direct elements are added. In the second case the given depset's "
65 + "order must match the <code>order</code> param or else one of the two must be <code>"
66 + "\"default\"</code>. See the <a href=\"../depsets.md\">Depsets overview</a> for more "
67 + "information.",
Vladimir Moskvad200daf2016-12-23 16:35:37 +000068 parameters = {
69 @Param(
70 name = "items",
71 type = Object.class,
72 defaultValue = "[]",
73 doc =
Jon Brandvein50ca4b62017-02-24 17:37:56 +000074 "An iterable whose items become the direct elements of the new depset, in left-to-"
75 + "right order; or alternatively, a depset that becomes the transitive element of "
76 + "the new depset."
Vladimir Moskvad200daf2016-12-23 16:35:37 +000077 ),
78 @Param(
79 name = "order",
80 type = String.class,
Jon Brandvein052f9ce2017-01-19 21:50:34 +000081 defaultValue = "\"default\"",
Vladimir Moskvad200daf2016-12-23 16:35:37 +000082 doc =
Jon Brandvein50ca4b62017-02-24 17:37:56 +000083 "The traversal strategy for the new depset. See <a href=\"depset.html\">here</a> for "
84 + "the possible values."
Vladimir Moskvad200daf2016-12-23 16:35:37 +000085 )
86 },
vladmosdcf36832017-08-09 17:44:10 +020087 useLocation = true,
88 useEnvironment = true
Vladimir Moskvad200daf2016-12-23 16:35:37 +000089 )
90 private static final BuiltinFunction depset =
91 new BuiltinFunction("depset") {
vladmosdcf36832017-08-09 17:44:10 +020092 public SkylarkNestedSet invoke(Object items, String order, Location loc, Environment env)
Vladimir Moskvad200daf2016-12-23 16:35:37 +000093 throws EvalException {
94 try {
vladmosdcf36832017-08-09 17:44:10 +020095 return new SkylarkNestedSet(
96 Order.parse(order, env.getSemantics().incompatibleDisallowSetConstructor),
97 items, loc);
Vladimir Moskvad200daf2016-12-23 16:35:37 +000098 } catch (IllegalArgumentException ex) {
99 throw new EvalException(loc, ex);
100 }
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000101 }
102 };
103
104 @SkylarkSignature(
105 name = "set",
106 returnType = SkylarkNestedSet.class,
Vladimir Moskvad31c7d02017-02-14 12:13:20 +0000107 documentationReturnType = SkylarkNestedSet.LegacySet.class,
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000108 doc =
Vladimir Moskvad200daf2016-12-23 16:35:37 +0000109 "A temporary alias for <a href=\"#depset\">depset</a>. "
110 + "Deprecated in favor of <code>depset</code>.",
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000111 parameters = {
112 @Param(
113 name = "items",
114 type = Object.class,
115 defaultValue = "[]",
Vladimir Moskvad200daf2016-12-23 16:35:37 +0000116 doc = "Same as for <a href=\"#depset\">depset</a>."
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000117 ),
118 @Param(
119 name = "order",
120 type = String.class,
Jon Brandvein052f9ce2017-01-19 21:50:34 +0000121 defaultValue = "\"default\"",
Vladimir Moskvad200daf2016-12-23 16:35:37 +0000122 doc = "Same as for <a href=\"#depset\">depset</a>."
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000123 )
124 },
vladmosa6ad2d52017-05-03 19:24:18 +0200125 useLocation = true,
126 useEnvironment = true
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000127 )
128 private static final BuiltinFunction set =
129 new BuiltinFunction("set") {
vladmosa6ad2d52017-05-03 19:24:18 +0200130 public SkylarkNestedSet invoke(Object items, String order, Location loc, Environment env)
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000131 throws EvalException {
brandjon2a9a1b72017-05-08 09:11:05 -0400132 if (env.getSemantics().incompatibleDisallowSetConstructor) {
vladmosa6ad2d52017-05-03 19:24:18 +0200133 throw new EvalException(
134 loc,
135 "The `set` constructor for depsets is deprecated and will be removed. Please use "
136 + "the `depset` constructor instead. You can temporarily enable the "
137 + "deprecated `set` constructor by passing the flag "
brandjon2a9a1b72017-05-08 09:11:05 -0400138 + "--incompatible_disallow_set_constructor=false");
vladmosa6ad2d52017-05-03 19:24:18 +0200139 }
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000140 try {
vladmosdcf36832017-08-09 17:44:10 +0200141 return new SkylarkNestedSet(
142 Order.parse(order, /*forbidDeprecatedOrderNames=*/false),
143 items, loc);
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000144 } catch (IllegalArgumentException ex) {
145 throw new EvalException(loc, ex);
146 }
147 }
148 };
149
150 @SkylarkSignature(
151 name = "union",
152 objectType = SkylarkNestedSet.class,
153 returnType = SkylarkNestedSet.class,
154 doc =
Jon Brandvein50ca4b62017-02-24 17:37:56 +0000155 "<i>(Deprecated)</i> Returns a new <a href=\"depset.html\">depset</a> that is the merge "
156 + "of the given depset and <code>new_elements</code>. This is the same as the <code>+"
157 + "</code> operator.",
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000158 parameters = {
John Caterd7928422017-01-03 20:06:59 +0000159 @Param(name = "input", type = SkylarkNestedSet.class, doc = "The input depset."),
Jon Brandvein3cfeeec2017-01-20 04:23:37 +0000160 @Param(name = "new_elements", type = Object.class, doc = "The elements to be added.")
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000161 },
162 useLocation = true
163 )
164 private static final BuiltinFunction union =
165 new BuiltinFunction("union") {
166 @SuppressWarnings("unused")
Jon Brandvein50ca4b62017-02-24 17:37:56 +0000167 public SkylarkNestedSet invoke(SkylarkNestedSet input, Object newElements, Location loc)
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000168 throws EvalException {
Jon Brandvein3cfeeec2017-01-20 04:23:37 +0000169 // newElements' type is Object because of the polymorphism on unioning two
170 // SkylarkNestedSets versus a set and another kind of iterable.
171 // Can't use EvalUtils#toIterable since that would discard this information.
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000172 return new SkylarkNestedSet(input, newElements, loc);
173 }
174 };
175
brandjond0f13122017-04-07 15:55:12 +0000176 @SkylarkSignature(
177 name = "to_list",
178 objectType = SkylarkNestedSet.class,
179 returnType = MutableList.class,
180 doc =
181 "Returns a list of the elements, without duplicates, in the depset's traversal order. "
182 + "Note that order is unspecified (but deterministic) for elements that were added "
183 + "more than once to the depset. Order is also unspecified for <code>\"default\""
184 + "</code>-ordered depsets, and for elements of child depsets whose order differs "
185 + "from that of the parent depset. The list is a copy; modifying it has no effect "
186 + "on the depset and vice versa.",
187 parameters = {@Param(name = "input", type = SkylarkNestedSet.class, doc = "The input depset.")},
188 useEnvironment = true
189 )
190 private static final BuiltinFunction toList =
191 new BuiltinFunction("to_list") {
192 @SuppressWarnings("unused")
193 public MutableList<Object> invoke(SkylarkNestedSet input, Environment env) {
194 return new MutableList<>(input.toCollection(), env);
195 }
196 };
197
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000198 /**
199 * Returns a function-value implementing "select" (i.e. configurable attributes) in the specified
200 * package context.
201 */
202 @SkylarkSignature(
203 name = "select",
gregce33e5a282017-05-31 22:52:47 +0200204 doc = "Creates a select from the dict parameter, usable for setting configurable attributes.",
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000205 parameters = {
206 @Param(name = "x", type = SkylarkDict.class, doc = "The parameter to convert."),
207 @Param(
208 name = "no_match_error",
209 type = String.class,
210 defaultValue = "''",
211 doc = "Optional custom error to report if no condition matches."
212 )
Greg Estrenbfd3ec52017-01-31 23:18:11 +0000213 },
214 useLocation = true
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000215 )
216 private static final BuiltinFunction select =
217 new BuiltinFunction("select") {
Greg Estrenbfd3ec52017-01-31 23:18:11 +0000218 public Object invoke(SkylarkDict<?, ?> dict, String noMatchError, Location loc)
219 throws EvalException {
220 for (Object key : dict.keySet()) {
221 if (!(key instanceof String)) {
Jon Brandvein50ca4b62017-02-24 17:37:56 +0000222 throw new EvalException(
223 loc, String.format("Invalid key: %s. select keys must be label references", key));
Greg Estrenbfd3ec52017-01-31 23:18:11 +0000224 }
225 }
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000226 return SelectorList.of(new SelectorValue(dict, noMatchError));
227 }
228 };
229
230 private static Environment.Frame createGlobals() {
brandjonc06e7462017-07-11 20:54:58 +0200231 List<BaseFunction> bazelGlobalFunctions = ImmutableList.of(select, depset, set, type);
Laurent Le Brunbd9576a2016-11-18 15:10:51 +0000232
233 try (Mutability mutability = Mutability.create("BUILD")) {
234 Environment env = Environment.builder(mutability).build();
235 Runtime.setupConstants(env);
236 Runtime.setupMethodEnvironment(env, MethodLibrary.defaultGlobalFunctions);
237 Runtime.setupMethodEnvironment(env, bazelGlobalFunctions);
238 return env.getGlobals();
239 }
240 }
241
242 public static final Environment.Frame GLOBALS = createGlobals();
243
244 static {
245 SkylarkSignatureProcessor.configureSkylarkFunctions(BazelLibrary.class);
246 }
247}