bazel syntax: move Selector{List,Value} into lib.packages
The Bazel 'select' function is no longer in Starlark.UNIVERSE.
It is pre-declared in all Bazel file environments (BUILD, .bzl, WORKSPACE).
This is a new category (though depset belongs in it too),
exposed as StarlarkLibrary.COMMON.
Also:
- make various methods private
- improve the annotation doc comment
- remove legacyNamed for select(x={...}).
There are no uses in Google's depot.
A later change will merge Selector{List,Value} into a single data
type that represents a node in a binary tree whose branch nodes
are deferred '+' operations and whose leaves are dictionaries.
While removing what looked like unnecessarily qualified class
names, I discovered that BuildType.SelectorList is yet a third (!)
Starlark value class representing the same concept.
PiperOrigin-RevId: 291938455
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkModules.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkModules.java
index 35b05a8..b6b72d6 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkModules.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkModules.java
@@ -19,6 +19,7 @@
import com.google.devtools.build.lib.analysis.DefaultInfo;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.packages.SkylarkNativeModule;
+import com.google.devtools.build.lib.packages.StarlarkLibrary;
import com.google.devtools.build.lib.packages.StructProvider;
import com.google.devtools.build.lib.skylarkbuildapi.TopLevelBootstrap;
import com.google.devtools.build.lib.syntax.Starlark;
@@ -42,12 +43,14 @@
OutputGroupInfo.SKYLARK_CONSTRUCTOR,
ActionsProvider.INSTANCE,
DefaultInfo.PROVIDER);
+
/**
* Adds bindings for skylark built-ins and non-rules-specific globals of the build API to the
* given environment map builder.
*/
public static void addSkylarkGlobalsToBuilder(ImmutableMap.Builder<String, Object> env) {
env.putAll(Starlark.UNIVERSE);
+ env.putAll(StarlarkLibrary.COMMON); // e.g. select, depset
topLevelBootstrap.addBindingsToBuilder(env);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/BuildType.java b/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
index 59f567f..65fafb6 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
@@ -32,7 +32,6 @@
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Printer;
import com.google.devtools.build.lib.syntax.Printer.BasePrinter;
-import com.google.devtools.build.lib.syntax.SelectorValue;
import com.google.devtools.build.lib.syntax.Starlark;
import com.google.devtools.build.lib.syntax.StarlarkValue;
import java.util.ArrayList;
@@ -145,10 +144,12 @@
public static <T> Object selectableConvert(
Type<T> type, Object x, Object what, LabelConversionContext context)
throws ConversionException {
- if (x instanceof com.google.devtools.build.lib.syntax.SelectorList) {
+ if (x instanceof com.google.devtools.build.lib.packages.SelectorList) {
return new SelectorList<T>(
- ((com.google.devtools.build.lib.syntax.SelectorList) x).getElements(),
- what, context, type);
+ ((com.google.devtools.build.lib.packages.SelectorList) x).getElements(),
+ what,
+ context,
+ type);
} else {
return type.convert(x, what, context);
}
@@ -491,6 +492,8 @@
* rawValue + select(...) + select(...) + ..."} syntax. For consistency's sake, raw values are
* stored as selects with only a default condition.
*/
+ // TODO(adonovan): merge with packages.Selector{List,Value}.
+ // We don't need three classes for the same concept.
public static final class SelectorList<T> implements StarlarkValue {
private final Type<T> originalType;
private final List<Selector<T>> elements;
@@ -568,7 +571,7 @@
selectorValueList.add(new SelectorValue(element.getEntries(), element.getNoMatchError()));
}
try {
- printer.repr(com.google.devtools.build.lib.syntax.SelectorList.of(selectorValueList));
+ printer.repr(com.google.devtools.build.lib.packages.SelectorList.of(selectorValueList));
} catch (EvalException e) {
throw new IllegalStateException("this list should have been validated on creation");
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 598c33f..a19ba89 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -875,7 +875,7 @@
private void populateEnvironment(ImmutableMap.Builder<String, Object> env) {
env.putAll(Starlark.UNIVERSE);
- env.putAll(StarlarkBuildLibrary.BINDINGS);
+ env.putAll(StarlarkLibrary.BUILD); // e.g. rule, select, depset
env.putAll(SkylarkNativeModule.BINDINGS_FOR_BUILD_FILES);
env.put("package", newPackageFunction(packageArguments));
env.putAll(ruleFunctions);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SelectorList.java b/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
similarity index 73%
rename from src/main/java/com/google/devtools/build/lib/syntax/SelectorList.java
rename to src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
index 57b57a7..7e0799d 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SelectorList.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SelectorList.java
@@ -11,13 +11,21 @@
// 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;
+package com.google.devtools.build.lib.packages;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
+import com.google.devtools.build.lib.syntax.Dict;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.HasBinary;
+import com.google.devtools.build.lib.syntax.Printer;
+import com.google.devtools.build.lib.syntax.SkylarkType;
+import com.google.devtools.build.lib.syntax.Starlark;
+import com.google.devtools.build.lib.syntax.StarlarkValue;
+import com.google.devtools.build.lib.syntax.TokenKind;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -46,8 +54,8 @@
@AutoCodec
public final class SelectorList implements StarlarkValue, HasBinary {
- // TODO(adonovan): move to lib.packages.
- // TODO(adonovan): combine Selector{List,Value}. There's no need for two data types.
+ // TODO(adonovan): combine Selector{List,Value} and BuildType.SelectorList.
+ // We don't need three classes for the same concept
private final Class<?> type;
private final List<Object> elements;
@@ -59,24 +67,36 @@
}
/**
- * Returns an ordered list of the elements in this expression. Each element may be a
- * native type or a select.
+ * Returns an ordered list of the elements in this expression. Each element may be a native type
+ * or a select.
*/
- public List<Object> getElements() {
+ List<Object> getElements() {
return elements;
}
- /**
- * Returns the native type contained by this expression.
- */
- public Class<?> getType() {
+ /** Returns the native type contained by this expression. */
+ Class<?> getType() {
return type;
}
- /**
- * Creates a "wrapper" list that consists of a single select.
- */
- public static SelectorList of(SelectorValue selector) {
+ /** Implementation of the Starlark {@code select} function exposed to BUILD and .bzl files. */
+ public static Object select(Dict<?, ?> dict, String noMatchError) throws EvalException {
+ if (dict.isEmpty()) {
+ throw Starlark.errorf(
+ "select({}) with an empty dictionary can never resolve because it includes no conditions"
+ + " to match");
+ }
+ for (Object key : dict.keySet()) {
+ if (!(key instanceof String)) {
+ throw Starlark.errorf(
+ "select: got %s for dict key, want a label string", Starlark.type(key));
+ }
+ }
+ return SelectorList.of(new SelectorValue(dict, noMatchError));
+ }
+
+ /** Creates a "wrapper" list that consists of a single select. */
+ static SelectorList of(SelectorValue selector) {
return new SelectorList(selector.getType(), ImmutableList.of(selector));
}
@@ -86,7 +106,7 @@
*
* @throws EvalException if the values don't have the same underlying type
*/
- public static SelectorList concat(Object x, Object y) throws EvalException {
+ static SelectorList concat(Object x, Object y) throws EvalException {
return of(Arrays.asList(x, y));
}
@@ -104,7 +124,7 @@
*
* @throws EvalException if all values don't have the same underlying type
*/
- public static SelectorList of(Iterable<?> values) throws EvalException {
+ static SelectorList of(Iterable<?> values) throws EvalException {
Preconditions.checkArgument(!Iterables.isEmpty(values));
ImmutableList.Builder<Object> elements = ImmutableList.builder();
Object firstValue = null;
@@ -132,9 +152,9 @@
private static String getTypeName(Object x) {
if (x instanceof SelectorList) {
- return "select of " + EvalUtils.getDataTypeNameFromClass(((SelectorList) x).getType());
+ return "select of " + SkylarkType.of(((SelectorList) x).getType());
} else if (x instanceof SelectorValue) {
- return "select of " + EvalUtils.getDataTypeNameFromClass(((SelectorValue) x).getType());
+ return "select of " + SkylarkType.of(((SelectorValue) x).getType());
} else {
return Starlark.type(x);
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SelectorValue.java b/src/main/java/com/google/devtools/build/lib/packages/SelectorValue.java
similarity index 66%
rename from src/main/java/com/google/devtools/build/lib/syntax/SelectorValue.java
rename to src/main/java/com/google/devtools/build/lib/packages/SelectorValue.java
index ba6359c..e61d6e8 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SelectorValue.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SelectorValue.java
@@ -11,17 +11,23 @@
// 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;
+package com.google.devtools.build.lib.packages;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.HasBinary;
+import com.google.devtools.build.lib.syntax.Printer;
+import com.google.devtools.build.lib.syntax.Starlark;
+import com.google.devtools.build.lib.syntax.StarlarkValue;
+import com.google.devtools.build.lib.syntax.TokenKind;
import java.util.Map;
/**
- * The value passed to a select({...}) statement, e.g.:
+ * The value returned by a call to {@code select({...})}, for example:
*
* <pre>
* rule(
@@ -34,38 +40,40 @@
*/
@SkylarkModule(
name = "selector",
- doc = "A selector between configuration-dependent entities.",
+ doc = "A selector between configuration-dependent values.",
documented = false)
@AutoCodec
public final class SelectorValue implements StarlarkValue, HasBinary {
- // TODO(adonovan): move to lib.packages.
- // TODO(adonovan): combine Selector{List,Value}. There's no need for two data types.
+ // TODO(adonovan): combine Selector{List,Value} and BuildType.SelectorList.
+ // We don't need three classes for the same concept.
private final ImmutableMap<?, ?> dictionary;
private final Class<?> type;
private final String noMatchError;
- public SelectorValue(Map<?, ?> dictionary, String noMatchError) {
+ SelectorValue(Map<?, ?> dictionary, String noMatchError) {
Preconditions.checkArgument(!dictionary.isEmpty());
this.dictionary = ImmutableMap.copyOf(dictionary);
- this.type = Iterables.get(dictionary.values(), 0).getClass();
+ // TODO(adonovan): doesn't this assume all the elements have the same type?
+ this.type = Iterables.getFirst(dictionary.values(), null).getClass();
this.noMatchError = noMatchError;
}
- public ImmutableMap<?, ?> getDictionary() {
+ ImmutableMap<?, ?> getDictionary() {
return dictionary;
}
+ // TODO(adonovan): use SkylarkType not Class, like Depset.
Class<?> getType() {
return type;
}
/**
- * Returns a custom error message for this select when no condition matches, or an empty
- * string if no such message is declared.
+ * Returns a custom error message for this select when no condition matches, or an empty string if
+ * no such message is declared.
*/
- public String getNoMatchError() {
+ String getNoMatchError() {
return noMatchError;
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
index c35e3c8..fd9664c 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkNativeModule.java
@@ -399,7 +399,7 @@
// Even though this is clearly imperfect, we return this value because otherwise
// native.rules() fails if there is any rule using a select() in the BUILD file.
//
- // To remedy this, we should return a syntax.SelectorList. To do so, we have to
+ // To remedy this, we should return a SelectorList. To do so, we have to
// 1) recurse into the Selector contents of SelectorList, so those values are skylarkified too
// 2) get the right Class<?> value. We could probably get at that by looking at
// ((SelectorList)val).getSelectors().first().getEntries().first().getClass().
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkBuildLibrary.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkBuildLibrary.java
deleted file mode 100644
index ce9afaf..0000000
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkBuildLibrary.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2019 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 static com.google.devtools.build.lib.packages.PackageFactory.getContext;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
-import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.packages.License.DistributionType;
-import com.google.devtools.build.lib.packages.PackageFactory.PackageContext;
-import com.google.devtools.build.lib.packages.Type.ConversionException;
-import com.google.devtools.build.lib.skylarkinterface.Param;
-import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
-import com.google.devtools.build.lib.skylarkinterface.SkylarkGlobalLibrary;
-import com.google.devtools.build.lib.syntax.EvalException;
-import com.google.devtools.build.lib.syntax.NoneType;
-import com.google.devtools.build.lib.syntax.Sequence;
-import com.google.devtools.build.lib.syntax.Starlark;
-import com.google.devtools.build.lib.syntax.StarlarkThread;
-import java.util.List;
-import java.util.Set;
-
-/** A global library of Starlark functions which are available only when evaluating BUILD files. */
-@SkylarkGlobalLibrary
-class StarlarkBuildLibrary {
-
- /**
- * Map of Starlark values (keyed by their symbol name) defined by this global library for use in
- * the global Starlark environment for BUILD files.
- */
- public static final ImmutableMap<String, Object> BINDINGS = initializeBindings();
-
- private static ImmutableMap<String, Object> initializeBindings() {
- ImmutableMap.Builder<String, Object> env = ImmutableMap.builder();
- Starlark.addMethods(env, new StarlarkBuildLibrary());
- return env.build();
- }
-
- private StarlarkBuildLibrary() {
- // Not instantiable outside this class.
- }
-
- @SkylarkCallable(
- name = "environment_group",
- doc =
- "Defines a set of related environments that can be tagged onto rules to prevent"
- + "incompatible rules from depending on each other.",
- parameters = {
- @Param(
- name = "name",
- type = String.class,
- positional = false,
- named = true,
- doc = "The name of the rule."),
- // Both parameter below are lists of label designators
- @Param(
- name = "environments",
- type = Sequence.class,
- generic1 = Object.class,
- positional = false,
- named = true,
- doc = "A list of Labels for the environments to be grouped, from the same package."),
- @Param(
- name = "defaults",
- type = Sequence.class,
- generic1 = Object.class,
- positional = false,
- named = true,
- doc = "A list of Labels.")
- }, // TODO(bazel-team): document what that is
- // Not documented by docgen, as this is only available in BUILD files.
- // TODO(cparsons): Devise a solution to document BUILD functions.
- documented = false,
- useStarlarkThread = true)
- public NoneType environmentGroup(
- String name,
- Sequence<?> environmentsList, // <Label>
- Sequence<?> defaultsList, // <Label>
- StarlarkThread thread)
- throws EvalException {
- PackageContext context = getContext(thread);
- List<Label> environments =
- BuildType.LABEL_LIST.convert(
- environmentsList,
- "'environment_group argument'",
- context.pkgBuilder.getBuildFileLabel());
- List<Label> defaults =
- BuildType.LABEL_LIST.convert(
- defaultsList, "'environment_group argument'", context.pkgBuilder.getBuildFileLabel());
-
- if (environments.isEmpty()) {
- throw Starlark.errorf("environment group %s must contain at least one environment", name);
- }
- try {
- Location loc = thread.getCallerLocation();
- context.pkgBuilder.addEnvironmentGroup(
- name, environments, defaults, context.eventHandler, loc);
- return Starlark.NONE;
- } catch (LabelSyntaxException e) {
- throw Starlark.errorf("environment group has invalid name: %s: %s", name, e.getMessage());
- } catch (Package.NameConflictException e) {
- throw Starlark.errorf("%s", e.getMessage());
- }
- }
-
- @SkylarkCallable(
- name = "licenses",
- doc = "Declare the license(s) for the code in the current package.",
- parameters = {
- @Param(
- name = "license_strings",
- type = Sequence.class,
- generic1 = String.class,
- doc = "A list of strings, the names of the licenses used.")
- },
- // Not documented by docgen, as this is only available in BUILD files.
- // TODO(cparsons): Devise a solution to document BUILD functions.
- documented = false,
- useStarlarkThread = true)
- public NoneType licenses(
- Sequence<?> licensesList, // list of license strings
- StarlarkThread thread)
- throws EvalException {
- PackageContext context = getContext(thread);
- try {
- License license = BuildType.LICENSE.convert(licensesList, "'licenses' operand");
- context.pkgBuilder.setDefaultLicense(license);
- } catch (ConversionException e) {
- context.eventHandler.handle(Event.error(thread.getCallerLocation(), e.getMessage()));
- context.pkgBuilder.setContainsErrors();
- }
- return Starlark.NONE;
- }
-
- @SkylarkCallable(
- name = "distribs",
- doc = "Declare the distribution(s) for the code in the current package.",
- parameters = {
- @Param(name = "distribution_strings", type = Object.class, doc = "The distributions.")
- },
- // Not documented by docgen, as this is only available in BUILD files.
- // TODO(cparsons): Devise a solution to document BUILD functions.
- documented = false,
- useStarlarkThread = true)
- public NoneType distribs(Object object, StarlarkThread thread) throws EvalException {
- PackageContext context = getContext(thread);
-
- try {
- Set<DistributionType> distribs =
- BuildType.DISTRIBUTIONS.convert(object, "'distribs' operand");
- context.pkgBuilder.setDefaultDistribs(distribs);
- } catch (ConversionException e) {
- context.eventHandler.handle(Event.error(thread.getCallerLocation(), e.getMessage()));
- context.pkgBuilder.setContainsErrors();
- }
- return Starlark.NONE;
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkLibrary.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkLibrary.java
new file mode 100644
index 0000000..a943f94
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkLibrary.java
@@ -0,0 +1,226 @@
+// Copyright 2019 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 static com.google.devtools.build.lib.packages.PackageFactory.getContext;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.License.DistributionType;
+import com.google.devtools.build.lib.packages.PackageFactory.PackageContext;
+import com.google.devtools.build.lib.packages.Type.ConversionException;
+import com.google.devtools.build.lib.skylarkinterface.Param;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkGlobalLibrary;
+import com.google.devtools.build.lib.syntax.Dict;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.NoneType;
+import com.google.devtools.build.lib.syntax.Sequence;
+import com.google.devtools.build.lib.syntax.Starlark;
+import com.google.devtools.build.lib.syntax.StarlarkThread;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A library of pre-declared Bazel Starlark functions.
+ *
+ * <p>For functions pre-declared in a BUILD file, use {@link #BUILD}. For Bazel functions such as
+ * {@code select} and {@code depset} that are pre-declared in all BUILD, .bzl, and WORKSPACE files,
+ * use {@link #COMMON}. For functions pre-declared in every Starlark file, use {@link
+ * Starlark#UNIVERSE}.
+ */
+public final class StarlarkLibrary {
+
+ private StarlarkLibrary() {} // uninstantiable
+
+ /**
+ * A library of Starlark functions (keyed by name) that are not part of core Starlark but are
+ * common to all Bazel Starlark file environments (BUILD, .bzl, and WORKSPACE). Examples: depset,
+ * select.
+ */
+ public static final ImmutableMap<String, Object> COMMON = initCommon();
+
+ private static ImmutableMap<String, Object> initCommon() {
+ ImmutableMap.Builder<String, Object> env = ImmutableMap.builder();
+ Starlark.addMethods(env, new CommonLibrary());
+ return env.build();
+ }
+
+ @SkylarkGlobalLibrary
+ private static class CommonLibrary {
+ @SkylarkCallable(
+ 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 = Dict.class,
+ positional = true,
+ doc =
+ "A dict that maps configuration conditions to values. Each key is a label string"
+ + " that identifies a config_setting instance."),
+ @Param(
+ name = "no_match_error",
+ type = String.class,
+ defaultValue = "''",
+ doc = "Optional custom error to report if no condition matches.",
+ named = true),
+ })
+ public Object select(Dict<?, ?> dict, String noMatchError) throws EvalException {
+ return SelectorList.select(dict, noMatchError);
+ }
+
+ // TODO(adonovan): move depset here.
+ }
+
+ /**
+ * A library of Starlark functions (keyed by name) pre-declared in BUILD files. A superset of
+ * {@link #COMMON} (e.g. select). Excludes functions in the native module, such as exports_files.
+ * Examples: environment_group, select.
+ */
+ public static final ImmutableMap<String, Object> BUILD = initBUILD();
+
+ private static ImmutableMap<String, Object> initBUILD() {
+ ImmutableMap.Builder<String, Object> env = ImmutableMap.builder();
+ Starlark.addMethods(env, new BuildLibrary());
+ env.putAll(COMMON);
+ return env.build();
+ }
+
+ @SkylarkGlobalLibrary
+ private static class BuildLibrary {
+ @SkylarkCallable(
+ name = "environment_group",
+ doc =
+ "Defines a set of related environments that can be tagged onto rules to prevent"
+ + "incompatible rules from depending on each other.",
+ parameters = {
+ @Param(
+ name = "name",
+ type = String.class,
+ positional = false,
+ named = true,
+ doc = "The name of the rule."),
+ // Both parameter below are lists of label designators
+ @Param(
+ name = "environments",
+ type = Sequence.class,
+ generic1 = Object.class,
+ positional = false,
+ named = true,
+ doc = "A list of Labels for the environments to be grouped, from the same package."),
+ @Param(
+ name = "defaults",
+ type = Sequence.class,
+ generic1 = Object.class,
+ positional = false,
+ named = true,
+ doc = "A list of Labels.")
+ }, // TODO(bazel-team): document what that is
+ // Not documented by docgen, as this is only available in BUILD files.
+ // TODO(cparsons): Devise a solution to document BUILD functions.
+ documented = false,
+ useStarlarkThread = true)
+ public NoneType environmentGroup(
+ String name,
+ Sequence<?> environmentsList, // <Label>
+ Sequence<?> defaultsList, // <Label>
+ StarlarkThread thread)
+ throws EvalException {
+ PackageContext context = getContext(thread);
+ List<Label> environments =
+ BuildType.LABEL_LIST.convert(
+ environmentsList,
+ "'environment_group argument'",
+ context.pkgBuilder.getBuildFileLabel());
+ List<Label> defaults =
+ BuildType.LABEL_LIST.convert(
+ defaultsList, "'environment_group argument'", context.pkgBuilder.getBuildFileLabel());
+
+ if (environments.isEmpty()) {
+ throw Starlark.errorf("environment group %s must contain at least one environment", name);
+ }
+ try {
+ Location loc = thread.getCallerLocation();
+ context.pkgBuilder.addEnvironmentGroup(
+ name, environments, defaults, context.eventHandler, loc);
+ return Starlark.NONE;
+ } catch (LabelSyntaxException e) {
+ throw Starlark.errorf("environment group has invalid name: %s: %s", name, e.getMessage());
+ } catch (Package.NameConflictException e) {
+ throw Starlark.errorf("%s", e.getMessage());
+ }
+ }
+
+ @SkylarkCallable(
+ name = "licenses",
+ doc = "Declare the license(s) for the code in the current package.",
+ parameters = {
+ @Param(
+ name = "license_strings",
+ type = Sequence.class,
+ generic1 = String.class,
+ doc = "A list of strings, the names of the licenses used.")
+ },
+ // Not documented by docgen, as this is only available in BUILD files.
+ // TODO(cparsons): Devise a solution to document BUILD functions.
+ documented = false,
+ useStarlarkThread = true)
+ public NoneType licenses(
+ Sequence<?> licensesList, // list of license strings
+ StarlarkThread thread)
+ throws EvalException {
+ PackageContext context = getContext(thread);
+ try {
+ License license = BuildType.LICENSE.convert(licensesList, "'licenses' operand");
+ context.pkgBuilder.setDefaultLicense(license);
+ } catch (ConversionException e) {
+ context.eventHandler.handle(Event.error(thread.getCallerLocation(), e.getMessage()));
+ context.pkgBuilder.setContainsErrors();
+ }
+ return Starlark.NONE;
+ }
+
+ @SkylarkCallable(
+ name = "distribs",
+ doc = "Declare the distribution(s) for the code in the current package.",
+ parameters = {
+ @Param(name = "distribution_strings", type = Object.class, doc = "The distributions.")
+ },
+ // Not documented by docgen, as this is only available in BUILD files.
+ // TODO(cparsons): Devise a solution to document BUILD functions.
+ documented = false,
+ useStarlarkThread = true)
+ public NoneType distribs(Object object, StarlarkThread thread) throws EvalException {
+ PackageContext context = getContext(thread);
+
+ try {
+ Set<DistributionType> distribs =
+ BuildType.DISTRIBUTIONS.convert(object, "'distribs' operand");
+ context.pkgBuilder.setDefaultDistribs(distribs);
+ } catch (ConversionException e) {
+ context.eventHandler.handle(Event.error(thread.getCallerLocation(), e.getMessage()));
+ context.pkgBuilder.setContainsErrors();
+ }
+ return Starlark.NONE;
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
index f59f28e..b3cc560 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
@@ -349,6 +349,7 @@
private ImmutableMap<String, Object> getDefaultEnvironment() {
ImmutableMap.Builder<String, Object> env = ImmutableMap.builder();
env.putAll(Starlark.UNIVERSE);
+ env.putAll(StarlarkLibrary.COMMON); // e.g. select, depset
env.putAll(workspaceFunctions);
if (installDir != null) {
env.put("__embedded_dir__", installDir.getPathString());
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BUILD b/src/main/java/com/google/devtools/build/lib/syntax/BUILD
index f857248..c5c9413 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BUILD
@@ -113,8 +113,6 @@
"ParamDescriptor.java",
"Printer.java",
"RangeList.java",
- "SelectorList.java",
- "SelectorValue.java",
"Sequence.java",
"SkylarkClassObject.java",
"SkylarkIndexable.java",
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Depset.java b/src/main/java/com/google/devtools/build/lib/syntax/Depset.java
index 96f4849..2987d72 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Depset.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Depset.java
@@ -40,7 +40,10 @@
* elements must have the same type. An empty depset has type {@code SkylarkType.TOP}, and may be
* combined with any other depset.
*/
-// TODO(adonovan): move to lib.packages, as this is a Bazelism.
+// TODO(adonovan): move to lib.packages, as this is a Bazelism. Requires:
+// - moving the function to StarlarkLibrary.COMMON.
+// - making SkylarkType.getGenericArgType extensible somehow
+// - relaxing StarlarkThread.checkStateEquals (or defining Depset.equals)
@SkylarkModule(
name = "depset",
category = SkylarkModuleCategory.BUILTIN,
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
index 178fa02..2635576 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
@@ -252,8 +252,6 @@
} else if (StarlarkCallable.class.isAssignableFrom(c)) {
// TODO(adonovan): each StarlarkCallable should report its own type string.
return "function";
- } else if (c.equals(SelectorValue.class)) {
- return "select";
} else {
if (c.getSimpleName().isEmpty()) {
return c.getName();
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 16a9ebc..82ec505 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
@@ -1090,45 +1090,6 @@
return o instanceof Sequence && ((Sequence) o).isEmpty();
}
- /**
- * Returns a function-value implementing "select" (i.e. configurable attributes) in the specified
- * package context.
- */
- @SkylarkCallable(
- 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 = Dict.class,
- doc = "The parameter to convert.",
- // TODO(cparsons): This parameter should be positional-only.
- legacyNamed = true),
- @Param(
- name = "no_match_error",
- type = String.class,
- defaultValue = "''",
- doc = "Optional custom error to report if no condition matches.",
- named = true)
- })
- public Object select(Dict<?, ?> dict, String noMatchError) throws EvalException {
- if (dict.isEmpty()) {
- throw Starlark.errorf(
- "select({}) with an empty dictionary can never resolve because it includes no conditions"
- + " to match");
- }
- for (Object key : dict.keySet()) {
- if (!(key instanceof String)) {
- throw Starlark.errorf("Invalid key: %s. select keys must be label references", key);
- }
- }
- return SelectorList.of(new SelectorValue(dict, noMatchError));
- }
-
@SkylarkCallable(
name = "zip",
doc =
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java b/src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java
index 553a9d8..8925cca 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java
@@ -401,8 +401,7 @@
" name = 'int_key',",
" srcs = select({123: ['a.java']})",
")");
- assertTargetError("//java/foo:int_key",
- "Invalid key: 123. select keys must be label references");
+ assertTargetError("//java/foo:int_key", "select: got int for dict key, want a label string");
}
@Test
@@ -414,8 +413,7 @@
" name = 'bool_key',",
" srcs = select({True: ['a.java']})",
")");
- assertTargetError("//java/foo:bool_key",
- "Invalid key: true. select keys must be label references");
+ assertTargetError("//java/foo:bool_key", "select: got bool for dict key, want a label string");
}
@Test
@@ -427,8 +425,8 @@
" name = 'none_key',",
" srcs = select({None: ['a.java']})",
")");
- assertTargetError("//java/foo:none_key",
- "Invalid key: None. select keys must be label references");
+ assertTargetError(
+ "//java/foo:none_key", "select: got NoneType for dict key, want a label string");
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java b/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java
index 9db91b9..f424e76 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/BuildTypeTest.java
@@ -28,8 +28,6 @@
import com.google.devtools.build.lib.packages.Type.ConversionException;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.EvalUtils;
-import com.google.devtools.build.lib.syntax.SelectorList;
-import com.google.devtools.build.lib.syntax.SelectorValue;
import com.google.devtools.build.lib.syntax.Starlark;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/src/test/java/com/google/devtools/build/lib/packages/SelectTest.java b/src/test/java/com/google/devtools/build/lib/packages/SelectTest.java
new file mode 100644
index 0000000..2cbdff7
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/packages/SelectTest.java
@@ -0,0 +1,76 @@
+// Copyright 2020 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 static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
+
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.syntax.EvalUtils;
+import com.google.devtools.build.lib.syntax.Module;
+import com.google.devtools.build.lib.syntax.Mutability;
+import com.google.devtools.build.lib.syntax.ParserInput;
+import com.google.devtools.build.lib.syntax.StarlarkThread;
+import com.google.devtools.build.lib.syntax.SyntaxError;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests of @{code select} function and data type. */
+@RunWith(JUnit4.class)
+public class SelectTest {
+
+ private static Object eval(String expr) throws SyntaxError, EvalException, InterruptedException {
+ ParserInput input = ParserInput.fromLines(expr);
+ StarlarkThread thread =
+ StarlarkThread.builder(Mutability.create("test"))
+ .setGlobals(Module.createForBuiltins(StarlarkLibrary.COMMON)) // select et al
+ .useDefaultSemantics()
+ .build();
+ return EvalUtils.eval(input, thread);
+ }
+
+ private static void assertFails(String expr, String wantError) {
+ EvalException ex = assertThrows(EvalException.class, () -> eval(expr));
+ assertThat(ex).hasMessageThat().contains(wantError);
+ }
+
+ @Test
+ public void testSelect() throws Exception {
+ SelectorList result = (SelectorList) eval("select({'a': 1})");
+ assertThat(((SelectorValue) Iterables.getOnlyElement(result.getElements())).getDictionary())
+ .containsExactly("a", 1);
+ }
+
+ @Test
+ public void testPlus() throws Exception {
+ SelectorList x = (SelectorList) eval("select({'foo': ['FOO'], 'bar': ['BAR']}) + []");
+ List<Object> elements = x.getElements();
+ assertThat(elements).hasSize(2);
+ assertThat(elements.get(0)).isInstanceOf(SelectorValue.class);
+ assertThat((Iterable<?>) elements.get(1)).isEmpty();
+ }
+
+ @Test
+ public void testPlusIncompatibleType() throws Exception {
+ assertFails(
+ "select({'foo': ['FOO'], 'bar': ['BAR']}) + 1",
+ "'+' operator applied to incompatible types (select of list, int)");
+ assertFails(
+ "select({'foo': ['FOO']}) + select({'bar': 2})",
+ "'+' operator applied to incompatible types (select of list, select of int)");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
index c930845..da7e30c 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
@@ -21,7 +21,6 @@
import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
import com.google.devtools.build.lib.testutil.TestMode;
import java.util.Collections;
-import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -606,33 +605,6 @@
.testExpression("-4 * (1, 2)", Tuple.empty());
}
- @SuppressWarnings("unchecked")
- @Test
- public void testSelectorListConcatenation() throws Exception {
- // TODO(fwe): cannot be handled by current testing suite
- SelectorList x = (SelectorList) eval("select({'foo': ['FOO'], 'bar': ['BAR']}) + []");
- List<Object> elements = x.getElements();
- assertThat(elements).hasSize(2);
- assertThat(elements.get(0)).isInstanceOf(SelectorValue.class);
- assertThat((Iterable<Object>) elements.get(1)).isEmpty();
- }
-
- @Test
- public void testAddSelectIncompatibleType() throws Exception {
- newTest()
- .testIfErrorContains(
- "'+' operator applied to incompatible types (select of list, int)",
- "select({'foo': ['FOO'], 'bar': ['BAR']}) + 1");
- }
-
- @Test
- public void testAddSelectIncompatibleType2() throws Exception {
- newTest()
- .testIfErrorContains(
- "'+' operator applied to incompatible types (select of list, select of int)",
- "select({'foo': ['FOO']}) + select({'bar': 2})");
- }
-
@Test
public void testListComprehensionFailsOnNonSequence() throws Exception {
newTest().testIfErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]");
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
index 39b226e..ec14d4c 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
@@ -14,11 +14,8 @@
package com.google.devtools.build.lib.syntax;
-import static com.google.common.truth.Truth.assertThat;
-
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
@@ -625,16 +622,6 @@
.testExpression("type(str)", "function");
}
- // TODO(bazel-team): Move select and this test into lib/packages.
- @Test
- public void testSelectFunction() throws Exception {
- enableSkylarkMode();
- exec("a = select({'a': 1})");
- SelectorList result = (SelectorList) lookup("a");
- assertThat(((SelectorValue) Iterables.getOnlyElement(result.getElements())).getDictionary())
- .containsExactly("a", 1);
- }
-
@Test
public void testZipFunction() throws Exception {
new BothModesTest()
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadTest.java b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadTest.java
index 7361fbf..d8806c5 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/StarlarkThreadTest.java
@@ -125,7 +125,6 @@
"range",
"repr",
"reversed",
- "select",
"sorted",
"str",
"tuple",