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",