Create GlobalBootstrap and the Bootstrap interface to add portions of the build API to a global environment.

RELNOTES: None.
PiperOrigin-RevId: 197742427
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
index efe086b..04c3c1a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -61,8 +61,10 @@
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.Environment.Extension;
+import com.google.devtools.build.lib.syntax.Environment.GlobalFrame;
 import com.google.devtools.build.lib.syntax.Environment.Phase;
 import com.google.devtools.build.lib.syntax.Mutability;
+import com.google.devtools.build.lib.syntax.Runtime;
 import com.google.devtools.build.lib.syntax.SkylarkSemantics;
 import com.google.devtools.build.lib.syntax.SkylarkUtils;
 import com.google.devtools.build.lib.syntax.Type;
@@ -239,8 +241,8 @@
     private PrerequisiteValidator prerequisiteValidator;
     private ImmutableMap.Builder<String, Object> skylarkAccessibleTopLevels =
         ImmutableMap.builder();
-    private ImmutableList.Builder<Class<?>> skylarkModules =
-        ImmutableList.<Class<?>>builder().addAll(SkylarkModules.MODULES);
+     private ImmutableList.Builder<Class<?>> skylarkModules =
+        ImmutableList.<Class<?>>builder();
     private ImmutableList.Builder<NativeProvider> nativeProviders = ImmutableList.builder();
     private Set<String> reservedActionMnemonics = new TreeSet<>();
     private BuildConfiguration.ActionEnvironmentProvider actionEnvironmentProvider =
@@ -790,21 +792,17 @@
   }
 
   private Environment.GlobalFrame createGlobals(
-      ImmutableMap<String, Object> skylarkAccessibleToplLevels,
+      ImmutableMap<String, Object> skylarkAccessibleTopLevels,
       ImmutableList<Class<?>> modules) {
-    try (Mutability mutability = Mutability.create("ConfiguredRuleClassProvider globals")) {
-      Environment env = createSkylarkRuleClassEnvironment(
-          mutability,
-          SkylarkModules.getGlobals(modules),
-          SkylarkSemantics.DEFAULT_SEMANTICS,
-          /*eventHandler=*/ null,
-          /*astFileContentHashCode=*/ null,
-          /*importMap=*/ null);
-      for (Map.Entry<String, Object> entry : skylarkAccessibleToplLevels.entrySet()) {
-        env.setup(entry.getKey(), entry.getValue());
-      }
-      return env.getGlobals();
+    ImmutableMap.Builder<String, Object> envBuilder = ImmutableMap.builder();
+
+    SkylarkModules.addSkylarkGlobalsToBuilder(envBuilder);
+    for (Class<?> module : modules) {
+      Runtime.setupModuleGlobals(envBuilder, module);
     }
+    envBuilder.putAll(skylarkAccessibleTopLevels.entrySet());
+
+    return GlobalFrame.createForBuiltins(envBuilder.build());
   }
 
   private static ImmutableMap<String, Class<?>> createFragmentMap(
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 b57b96b..9271102 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
@@ -14,16 +14,10 @@
 
 package com.google.devtools.build.lib.analysis.skylark;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.packages.BazelLibrary;
 import com.google.devtools.build.lib.packages.SkylarkNativeModule;
-import com.google.devtools.build.lib.syntax.Environment;
-import com.google.devtools.build.lib.syntax.Environment.GlobalFrame;
-import com.google.devtools.build.lib.syntax.Mutability;
-import com.google.devtools.build.lib.syntax.Runtime;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import com.google.devtools.build.lib.skylarkbuildapi.TopLevelBootstrap;
 
 /**
  * The basis for a Skylark Environment with all build-related modules registered.
@@ -33,37 +27,21 @@
   private SkylarkModules() { }
 
   /**
-   * The list of built in Skylark modules. Documentation is generated automatically for all these
-   * modules. They are also registered with the {@link Environment}.
+   * A bootstrap for non-rules-specific globals of the build API.
    */
-  public static final ImmutableList<Class<?>> MODULES =
-      ImmutableList.of(
-          BazelBuildApiGlobals.class,
-          SkylarkAttr.class,
-          SkylarkCommandLine.class,
-          SkylarkNativeModule.class,
-          SkylarkRuleClassFunctions.class);
+  private static TopLevelBootstrap topLevelBootstrap = new TopLevelBootstrap(
+      BazelBuildApiGlobals.class,
+      SkylarkAttr.class,
+      SkylarkCommandLine.class,
+      SkylarkNativeModule.class,
+      SkylarkRuleClassFunctions.class);
 
-  /** Global bindings for all Skylark modules */
-  private static final Map<List<Class<?>>, GlobalFrame> cache = new HashMap<>();
-
-  public static Environment.GlobalFrame getGlobals(List<Class<?>> modules) {
-    if (!cache.containsKey(modules)) {
-      cache.put(modules, createGlobals(modules));
-    }
-    return cache.get(modules);
-  }
-
-  private static Environment.GlobalFrame createGlobals(List<Class<?>> modules) {
-    try (Mutability mutability = Mutability.create("SkylarkModules")) {
-      Environment env = Environment.builder(mutability)
-          .useDefaultSemantics()
-          .setGlobals(BazelLibrary.GLOBALS)
-          .build();
-      for (Class<?> moduleClass : modules) {
-        Runtime.setupModuleGlobals(env, moduleClass);
-      }
-      return env.getGlobals();
-    }
+  /**
+   * 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> envBuilder) {
+    BazelLibrary.addSkylarkGlobalsToBuilder(envBuilder);
+    topLevelBootstrap.addBindingsToBuilder(envBuilder);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/packages/BazelLibrary.java b/src/main/java/com/google/devtools/build/lib/packages/BazelLibrary.java
index 205f98f..d660940 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/BazelLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/BazelLibrary.java
@@ -235,12 +235,19 @@
 
   private static GlobalFrame createGlobals() {
     ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
-    Runtime.addConstantsToBuilder(builder);
-    MethodLibrary.addBindingsToBuilder(builder);
-    BazelLibrary.addBindingsToBuilder(builder);
+    addSkylarkGlobalsToBuilder(builder);
     return GlobalFrame.createForBuiltins(builder.build());
   }
 
+  /**
+   * Adds bindings for skylark built-ins to the given environment map builder.
+   */
+  public static void addSkylarkGlobalsToBuilder(ImmutableMap.Builder<String, Object> envBuilder) {
+    Runtime.addConstantsToBuilder(envBuilder);
+    MethodLibrary.addBindingsToBuilder(envBuilder);
+    BazelLibrary.addBindingsToBuilder(envBuilder);
+  }
+
   static {
     SkylarkSignatureProcessor.configureSkylarkFunctions(BazelLibrary.class);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/Bootstrap.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/Bootstrap.java
new file mode 100644
index 0000000..5d531da
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/Bootstrap.java
@@ -0,0 +1,31 @@
+// Copyright 2018 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.skylarkbuildapi;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * A helper for registering a portion of the build API to skylark environment globals.
+ *
+ * <p>A global environment may be initialized by tabulating globals into a single map by passing
+ * a single map builder to {@link #addBindingsToBuilder} for several bootstrap helpers.
+ */
+public interface Bootstrap {
+
+  /**
+   * Adds this bootstrap's bindings to the given environment map builder.
+   */
+  public void addBindingsToBuilder(ImmutableMap.Builder<String, Object> builder);
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/TopLevelBootstrap.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/TopLevelBootstrap.java
new file mode 100644
index 0000000..910a67a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/TopLevelBootstrap.java
@@ -0,0 +1,51 @@
+// Copyright 2018 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.skylarkbuildapi;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.syntax.Runtime;
+
+/**
+ * A {@link Bootstrap} for top-level libraries of the build API.
+ */
+public class TopLevelBootstrap implements Bootstrap {
+  private final Class<? extends SkylarkBuildApiGlobals> skylarkBuildApiGlobals;
+  private final Class<? extends SkylarkAttrApi> skylarkAttrApi;
+  private final Class<? extends SkylarkCommandLineApi> skylarkCommandLineApi;
+  private final Class<? extends SkylarkNativeModuleApi> skylarkNativeModuleApi;
+  private final Class<? extends SkylarkRuleFunctionsApi<?>> skylarkRuleFunctionsApi;
+
+  public TopLevelBootstrap(
+      Class<? extends SkylarkBuildApiGlobals> skylarkBuildApiGlobals,
+      Class<? extends SkylarkAttrApi> skylarkAttrApi,
+      Class<? extends SkylarkCommandLineApi> skylarkCommandLineApi,
+      Class<? extends SkylarkNativeModuleApi> skylarkNativeModuleApi,
+      Class<? extends SkylarkRuleFunctionsApi<?>> skylarkRuleFunctionsApi) {
+    this.skylarkAttrApi = skylarkAttrApi;
+    this.skylarkBuildApiGlobals = skylarkBuildApiGlobals;
+    this.skylarkCommandLineApi = skylarkCommandLineApi;
+    this.skylarkNativeModuleApi = skylarkNativeModuleApi;
+    this.skylarkRuleFunctionsApi = skylarkRuleFunctionsApi;
+  }
+
+  @Override
+  public void addBindingsToBuilder(ImmutableMap.Builder<String, Object> builder) {
+    Runtime.setupModuleGlobals(builder, skylarkAttrApi);
+    Runtime.setupModuleGlobals(builder, skylarkBuildApiGlobals);
+    Runtime.setupModuleGlobals(builder, skylarkCommandLineApi);
+    Runtime.setupModuleGlobals(builder, skylarkNativeModuleApi);
+    Runtime.setupModuleGlobals(builder, skylarkRuleFunctionsApi);
+  }
+}
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 0ada911..9774ec5 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
@@ -327,7 +327,24 @@
   }
 
   /**
-   * Registers global (top-level) symbols provided by the given class object.
+   * Convenience overload of {@link #setupModuleGlobals(ImmutableMap.Builder, Class)} to add
+   * bindings directly to an {@link Environment}.
+   *
+   * @param env the Environment into which to register fields.
+   * @param moduleClass the Class object containing globals.
+   */
+  public static void setupModuleGlobals(Environment env, Class<?> moduleClass) {
+    ImmutableMap.Builder<String, Object> envBuilder = ImmutableMap.builder();
+
+    setupModuleGlobals(envBuilder, moduleClass);
+    for (Map.Entry<String, Object> envEntry : envBuilder.build().entrySet()) {
+      env.setup(envEntry.getKey(), envEntry.getValue());
+    }
+  }
+
+  /**
+   * Adds global (top-level) symbols, provided by the given class object, to the given bindings
+   * builder.
    *
    * <p>Global symbols may be provided by the given class in the following ways:
    * <ul>
@@ -345,14 +362,16 @@
    * multiple global libraries have functions of the same name, two modules of the same name
    * are given, or if two subclasses of the same module are given.
    *
-   * @param env the Environment into which to register fields.
-   * @param moduleClass the Class object containing globals.
+   * @param builder the builder for the "bindings" map, which maps from symbol names to objects,
+   *     and which will be built into a global frame
+   * @param moduleClass the Class object containing globals
    */
-  public static void setupModuleGlobals(Environment env, Class<?> moduleClass) {
+  public static void setupModuleGlobals(ImmutableMap.Builder<String, Object> builder,
+      Class<?> moduleClass) {
     try {
       SkylarkModule skylarkModule = SkylarkInterfaceUtils.getSkylarkModule(moduleClass);
       if (skylarkModule != null) {
-        env.setup(
+        builder.put(
             skylarkModule.name(),
             moduleClass.getConstructor().newInstance());
       }
@@ -367,14 +386,14 @@
           if (!(value instanceof BuiltinFunction.Factory
               || (value instanceof BaseFunction
                   && !annotation.objectType().equals(Object.class)))) {
-            env.setup(annotation.name(), value);
+            builder.put(annotation.name(), value);
           }
         }
       }
       if (SkylarkInterfaceUtils.hasSkylarkGlobalLibrary(moduleClass)) {
         Object moduleInstance = moduleClass.getConstructor().newInstance();
         for (String methodName : FuncallExpression.getMethodNames(moduleClass)) {
-          env.setup(methodName, FuncallExpression.getBuiltinCallable(moduleInstance, methodName));
+          builder.put(methodName, FuncallExpression.getBuiltinCallable(moduleInstance, methodName));
         }
       }
     } catch (ReflectiveOperationException e) {