Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 1 | // 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 | |
| 15 | package com.google.devtools.build.lib.syntax; |
| 16 | |
| 17 | import com.google.common.collect.ImmutableList; |
| 18 | import com.google.devtools.build.lib.collect.nestedset.Order; |
| 19 | import com.google.devtools.build.lib.events.Location; |
| 20 | import com.google.devtools.build.lib.skylarkinterface.Param; |
| 21 | import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature; |
brandjon | d0f1312 | 2017-04-07 15:55:12 +0000 | [diff] [blame] | 22 | import com.google.devtools.build.lib.syntax.SkylarkList.MutableList; |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 23 | import java.util.List; |
| 24 | |
| 25 | /** |
| 26 | * A helper class containing additional built in functions for Bazel (BUILD files and .bzl files). |
| 27 | */ |
| 28 | public 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 Moskva | ba4f0bb | 2017-01-30 15:45:49 +0000 | [diff] [blame] | 52 | return EvalUtils.getDataTypeName(object, false); |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 53 | } |
| 54 | }; |
| 55 | |
| 56 | @SkylarkSignature( |
| 57 | name = "depset", |
| 58 | returnType = SkylarkNestedSet.class, |
| 59 | doc = |
Jon Brandvein | 50ca4b6 | 2017-02-24 17:37:56 +0000 | [diff] [blame] | 60 | "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 Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 68 | parameters = { |
| 69 | @Param( |
| 70 | name = "items", |
| 71 | type = Object.class, |
| 72 | defaultValue = "[]", |
| 73 | doc = |
Jon Brandvein | 50ca4b6 | 2017-02-24 17:37:56 +0000 | [diff] [blame] | 74 | "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 Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 77 | ), |
| 78 | @Param( |
| 79 | name = "order", |
| 80 | type = String.class, |
Jon Brandvein | 052f9ce | 2017-01-19 21:50:34 +0000 | [diff] [blame] | 81 | defaultValue = "\"default\"", |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 82 | doc = |
Jon Brandvein | 50ca4b6 | 2017-02-24 17:37:56 +0000 | [diff] [blame] | 83 | "The traversal strategy for the new depset. See <a href=\"depset.html\">here</a> for " |
| 84 | + "the possible values." |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 85 | ) |
| 86 | }, |
vladmos | dcf3683 | 2017-08-09 17:44:10 +0200 | [diff] [blame] | 87 | useLocation = true, |
| 88 | useEnvironment = true |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 89 | ) |
| 90 | private static final BuiltinFunction depset = |
| 91 | new BuiltinFunction("depset") { |
vladmos | dcf3683 | 2017-08-09 17:44:10 +0200 | [diff] [blame] | 92 | public SkylarkNestedSet invoke(Object items, String order, Location loc, Environment env) |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 93 | throws EvalException { |
| 94 | try { |
vladmos | dcf3683 | 2017-08-09 17:44:10 +0200 | [diff] [blame] | 95 | return new SkylarkNestedSet( |
| 96 | Order.parse(order, env.getSemantics().incompatibleDisallowSetConstructor), |
| 97 | items, loc); |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 98 | } catch (IllegalArgumentException ex) { |
| 99 | throw new EvalException(loc, ex); |
| 100 | } |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 101 | } |
| 102 | }; |
| 103 | |
| 104 | @SkylarkSignature( |
| 105 | name = "set", |
| 106 | returnType = SkylarkNestedSet.class, |
Vladimir Moskva | d31c7d0 | 2017-02-14 12:13:20 +0000 | [diff] [blame] | 107 | documentationReturnType = SkylarkNestedSet.LegacySet.class, |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 108 | doc = |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 109 | "A temporary alias for <a href=\"#depset\">depset</a>. " |
| 110 | + "Deprecated in favor of <code>depset</code>.", |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 111 | parameters = { |
| 112 | @Param( |
| 113 | name = "items", |
| 114 | type = Object.class, |
| 115 | defaultValue = "[]", |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 116 | doc = "Same as for <a href=\"#depset\">depset</a>." |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 117 | ), |
| 118 | @Param( |
| 119 | name = "order", |
| 120 | type = String.class, |
Jon Brandvein | 052f9ce | 2017-01-19 21:50:34 +0000 | [diff] [blame] | 121 | defaultValue = "\"default\"", |
Vladimir Moskva | d200daf | 2016-12-23 16:35:37 +0000 | [diff] [blame] | 122 | doc = "Same as for <a href=\"#depset\">depset</a>." |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 123 | ) |
| 124 | }, |
vladmos | a6ad2d5 | 2017-05-03 19:24:18 +0200 | [diff] [blame] | 125 | useLocation = true, |
| 126 | useEnvironment = true |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 127 | ) |
| 128 | private static final BuiltinFunction set = |
| 129 | new BuiltinFunction("set") { |
vladmos | a6ad2d5 | 2017-05-03 19:24:18 +0200 | [diff] [blame] | 130 | public SkylarkNestedSet invoke(Object items, String order, Location loc, Environment env) |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 131 | throws EvalException { |
brandjon | 2a9a1b7 | 2017-05-08 09:11:05 -0400 | [diff] [blame] | 132 | if (env.getSemantics().incompatibleDisallowSetConstructor) { |
vladmos | a6ad2d5 | 2017-05-03 19:24:18 +0200 | [diff] [blame] | 133 | 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 " |
brandjon | 2a9a1b7 | 2017-05-08 09:11:05 -0400 | [diff] [blame] | 138 | + "--incompatible_disallow_set_constructor=false"); |
vladmos | a6ad2d5 | 2017-05-03 19:24:18 +0200 | [diff] [blame] | 139 | } |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 140 | try { |
vladmos | dcf3683 | 2017-08-09 17:44:10 +0200 | [diff] [blame] | 141 | return new SkylarkNestedSet( |
| 142 | Order.parse(order, /*forbidDeprecatedOrderNames=*/false), |
| 143 | items, loc); |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 144 | } 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 Brandvein | 50ca4b6 | 2017-02-24 17:37:56 +0000 | [diff] [blame] | 155 | "<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 Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 158 | parameters = { |
John Cater | d792842 | 2017-01-03 20:06:59 +0000 | [diff] [blame] | 159 | @Param(name = "input", type = SkylarkNestedSet.class, doc = "The input depset."), |
Jon Brandvein | 3cfeeec | 2017-01-20 04:23:37 +0000 | [diff] [blame] | 160 | @Param(name = "new_elements", type = Object.class, doc = "The elements to be added.") |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 161 | }, |
| 162 | useLocation = true |
| 163 | ) |
| 164 | private static final BuiltinFunction union = |
| 165 | new BuiltinFunction("union") { |
| 166 | @SuppressWarnings("unused") |
Jon Brandvein | 50ca4b6 | 2017-02-24 17:37:56 +0000 | [diff] [blame] | 167 | public SkylarkNestedSet invoke(SkylarkNestedSet input, Object newElements, Location loc) |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 168 | throws EvalException { |
Jon Brandvein | 3cfeeec | 2017-01-20 04:23:37 +0000 | [diff] [blame] | 169 | // 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 Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 172 | return new SkylarkNestedSet(input, newElements, loc); |
| 173 | } |
| 174 | }; |
| 175 | |
brandjon | d0f1312 | 2017-04-07 15:55:12 +0000 | [diff] [blame] | 176 | @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 Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 198 | /** |
| 199 | * Returns a function-value implementing "select" (i.e. configurable attributes) in the specified |
| 200 | * package context. |
| 201 | */ |
| 202 | @SkylarkSignature( |
| 203 | name = "select", |
gregce | 33e5a28 | 2017-05-31 22:52:47 +0200 | [diff] [blame] | 204 | doc = "Creates a select from the dict parameter, usable for setting configurable attributes.", |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 205 | 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 Estren | bfd3ec5 | 2017-01-31 23:18:11 +0000 | [diff] [blame] | 213 | }, |
| 214 | useLocation = true |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 215 | ) |
| 216 | private static final BuiltinFunction select = |
| 217 | new BuiltinFunction("select") { |
Greg Estren | bfd3ec5 | 2017-01-31 23:18:11 +0000 | [diff] [blame] | 218 | public Object invoke(SkylarkDict<?, ?> dict, String noMatchError, Location loc) |
| 219 | throws EvalException { |
| 220 | for (Object key : dict.keySet()) { |
| 221 | if (!(key instanceof String)) { |
Jon Brandvein | 50ca4b6 | 2017-02-24 17:37:56 +0000 | [diff] [blame] | 222 | throw new EvalException( |
| 223 | loc, String.format("Invalid key: %s. select keys must be label references", key)); |
Greg Estren | bfd3ec5 | 2017-01-31 23:18:11 +0000 | [diff] [blame] | 224 | } |
| 225 | } |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 226 | return SelectorList.of(new SelectorValue(dict, noMatchError)); |
| 227 | } |
| 228 | }; |
| 229 | |
| 230 | private static Environment.Frame createGlobals() { |
brandjon | c06e746 | 2017-07-11 20:54:58 +0200 | [diff] [blame] | 231 | List<BaseFunction> bazelGlobalFunctions = ImmutableList.of(select, depset, set, type); |
Laurent Le Brun | bd9576a | 2016-11-18 15:10:51 +0000 | [diff] [blame] | 232 | |
| 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 | } |