Move BazelLibrary from syntax/ to packages/
This helps the Skylark interpreter to not depend on Bazel concepts, though it adds a temporary dependency of Skylint on packages/. The fix for that will be to create a Build API interface for BazelLibrary (e.g., "BazelLibraryAPI").
Refactored some GlobalFrame construction logic to be more uniform. Instead of constructing a whole Environment just to get a frame, we build the frame directly, using ImmutableMap.Builder to accumulate bindings. This convention may further change once we convert MethodLibrary and the like to @SkylarkGlobalLibrary, but for now it's more readable.
RELNOTES: None
PiperOrigin-RevId: 194960824
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BazelLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/BazelLibrary.java
deleted file mode 100644
index 4e5845f..0000000
--- a/src/main/java/com/google/devtools/build/lib/syntax/BazelLibrary.java
+++ /dev/null
@@ -1,227 +0,0 @@
-// 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.syntax;
-
-import com.google.common.collect.ImmutableList;
-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 java.util.List;
-
-/**
- * A helper class containing additional built in functions for Bazel (BUILD files and .bzl files).
- */
-public class BazelLibrary {
-
- @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));
- }
- };
-
- private static Environment.GlobalFrame createGlobals() {
- List<BaseFunction> bazelGlobalFunctions = ImmutableList.of(select, depset, type);
-
- try (Mutability mutability = Mutability.create("BUILD")) {
- Environment env = Environment.builder(mutability)
- .useDefaultSemantics()
- .build();
- Runtime.setupConstants(env);
- Runtime.setupMethodEnvironment(env, MethodLibrary.defaultGlobalFunctions);
- Runtime.setupMethodEnvironment(env, bazelGlobalFunctions);
- return env.getGlobals();
- }
- }
-
- public static final Environment.GlobalFrame GLOBALS = createGlobals();
-
- static {
- SkylarkSignatureProcessor.configureSkylarkFunctions(BazelLibrary.class);
- }
-}
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 46253fa..c536a68 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
@@ -281,19 +281,36 @@
this.bindings = new LinkedHashMap<>();
}
- public GlobalFrame(Mutability mutability, @Nullable GlobalFrame parent, @Nullable Label label) {
+ public GlobalFrame(
+ Mutability mutability,
+ @Nullable GlobalFrame parent,
+ @Nullable Label label,
+ @Nullable Map<String, Object> bindings) {
this.mutability = Preconditions.checkNotNull(mutability);
this.parent = parent;
this.label = label;
this.bindings = new LinkedHashMap<>();
+ if (bindings != null) {
+ this.bindings.putAll(bindings);
+ }
}
public GlobalFrame(Mutability mutability) {
- this(mutability, null, null);
+ this(mutability, null, null, null);
}
- public GlobalFrame(Mutability mutability, GlobalFrame parent) {
- this(mutability, parent, null);
+ public GlobalFrame(Mutability mutability, @Nullable GlobalFrame parent) {
+ this(mutability, parent, null, null);
+ }
+
+ public GlobalFrame(Mutability mutability, @Nullable GlobalFrame parent, @Nullable Label label) {
+ this(mutability, parent, label, null);
+ }
+
+ /** Constructs a global frame for the given builtin bindings. */
+ public static GlobalFrame createForBuiltins(Map<String, Object> bindings) {
+ Mutability mutability = Mutability.create("<builtins>").freeze();
+ return new GlobalFrame(mutability, null, null, bindings);
}
private void checkInitialized() {
@@ -1217,34 +1234,29 @@
return transitiveHashCode;
}
- /** A read-only {@link Environment.GlobalFrame} with global constants in it only */
+ /** A read-only {@link Environment.GlobalFrame} with False/True/None constants only. */
static final GlobalFrame CONSTANTS_ONLY = createConstantsGlobals();
- /** A read-only {@link Environment.GlobalFrame} with initial globals */
+ /**
+ * A read-only {@link Environment.GlobalFrame} with initial globals as defined in
+ * MethodLibrary.
+ */
public static final GlobalFrame DEFAULT_GLOBALS = createDefaultGlobals();
/** To be removed when all call-sites are updated. */
public static final GlobalFrame SKYLARK = DEFAULT_GLOBALS;
private static Environment.GlobalFrame createConstantsGlobals() {
- try (Mutability mutability = Mutability.create("CONSTANTS")) {
- Environment env = Environment.builder(mutability)
- .useDefaultSemantics()
- .build();
- Runtime.setupConstants(env);
- return env.getGlobals();
- }
+ ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
+ Runtime.addConstantsToBuilder(builder);
+ return GlobalFrame.createForBuiltins(builder.build());
}
private static Environment.GlobalFrame createDefaultGlobals() {
- try (Mutability mutability = Mutability.create("BUILD")) {
- Environment env = Environment.builder(mutability)
- .useDefaultSemantics()
- .build();
- Runtime.setupConstants(env);
- Runtime.setupMethodEnvironment(env, MethodLibrary.defaultGlobalFunctions);
- return env.getGlobals();
- }
+ ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
+ Runtime.addConstantsToBuilder(builder);
+ MethodLibrary.addBindingsToBuilder(builder);
+ return GlobalFrame.createForBuiltins(builder.build());
}
/** An exception thrown by {@link #FAIL_FAST_HANDLER}. */
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 fe519b0..97c582a 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
@@ -960,7 +960,14 @@
)
public static final class BoolModule {}
- static final List<BaseFunction> defaultGlobalFunctions =
+ /** Adds bindings for all the builtin functions of this class to the given map builder. */
+ public static void addBindingsToBuilder(ImmutableMap.Builder<String, Object> builder) {
+ for (BaseFunction function : allFunctions) {
+ builder.put(function.getName(), function);
+ }
+ }
+
+ 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);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java b/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java
index 86f95b3..6606029 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Runtime.java
@@ -16,6 +16,7 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.skylarkinterface.SkylarkGlobalLibrary;
@@ -137,14 +138,16 @@
)
public static final String REPOSITORY_NAME = "REPOSITORY_NAME";
- /**
- * Set up a given environment for supported class methods.
- */
- static Environment setupConstants(Environment env) {
+ /** Adds bindings for False/True/None constants to the given map builder. */
+ public static void addConstantsToBuilder(ImmutableMap.Builder<String, Object> builder) {
// In Python 2.x, True and False are global values and can be redefined by the user.
- // In Python 3.x, they are keywords. We implement them as values, for the sake of
- // simplicity. We define them as Boolean objects.
- return env.setup("False", FALSE).setup("True", TRUE).setup("None", NONE);
+ // In Python 3.x, they are keywords. We implement them as values. Currently they can't be
+ // redefined because builtins can't be overridden. In the future we should permit shadowing of
+ // most builtins but still prevent shadowing of these constants.
+ builder
+ .put("False", FALSE)
+ .put("True", TRUE)
+ .put("None", NONE);
}
@@ -390,11 +393,4 @@
public static void registerModuleGlobals(Environment env, Class<?> moduleClass) {
setupModuleGlobals(env, moduleClass);
}
-
- static void setupMethodEnvironment(
- Environment env, Iterable<BaseFunction> functions) {
- for (BaseFunction function : functions) {
- env.setup(function.getName(), function);
- }
- }
}