Introduce CommandEnvironment and pass it instead of BlazeRuntime.

The very long term plan is to make BlazeRuntime immutable, so that we can
run multiple commands in the same server.

--
MOS_MIGRATED_REVID=103080946
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
index 7e4e41c..c1ec448 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
@@ -51,7 +51,6 @@
 import com.google.devtools.build.lib.analysis.config.ConfigurationFactory;
 import com.google.devtools.build.lib.analysis.config.DefaultsPackage;
 import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
-import com.google.devtools.build.lib.buildtool.BuildTool;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.OutputFilter;
 import com.google.devtools.build.lib.events.Reporter;
@@ -154,10 +153,11 @@
 import javax.annotation.Nullable;
 
 /**
- * The BlazeRuntime class encapsulates the runtime settings and services that
- * are available to most parts of any Blaze application for the duration of the
- * batch run or server lifetime. A single instance of this runtime will exist
- * and will be passed around as needed.
+ * The BlazeRuntime class encapsulates the immutable configuration of the current instance. These
+ * runtime settings and services are available to most parts of any Blaze application for the
+ * duration of the batch run or server lifetime.
+ *
+ * <p>The parts specific to the current command are stored in {@link CommandEnvironment}.
  */
 public final class BlazeRuntime {
   public static final String DO_NOT_BUILD_FILE_NAME = "DO_NOT_BUILD_HERE";
@@ -177,7 +177,6 @@
   private final SkyframeExecutor skyframeExecutor;
 
   private final Reporter reporter;
-  private EventBus eventBus;
   private final LoadingPhaseRunner loadingPhaseRunner;
   private final PackageFactory packageFactory;
   private final PackageRootResolver packageRootResolver;
@@ -187,7 +186,6 @@
   private ActionCache actionCache;
   private final TimestampGranularityMonitor timestampGranularityMonitor;
   private final Clock clock;
-  private final BuildTool buildTool;
 
   private OutputService outputService;
 
@@ -270,8 +268,6 @@
 
     this.eventBusExceptionHandler = eventBusExceptionHandler;
     this.blazeModuleEnvironment = new BlazeModuleEnvironment();
-    this.buildTool = new BuildTool(this);
-    initEventBus();
 
     if (inWorkspace()) {
       writeOutputBaseReadmeFile();
@@ -311,26 +307,22 @@
     return outputFileSystem;
   }
 
-  @VisibleForTesting
-  public void initEventBus() {
-    setEventBus(new EventBus(eventBusExceptionHandler));
+  public CommandEnvironment initCommand() {
+    EventBus eventBus = new EventBus(eventBusExceptionHandler);
+    skyframeExecutor.setEventBus(eventBus);
+    return new CommandEnvironment(this, eventBus);
   }
 
   private void clearEventBus() {
     // EventBus does not have an unregister() method, so this is how we release memory associated
     // with handlers.
-    setEventBus(null);
-  }
-
-  private void setEventBus(EventBus eventBus) {
-    this.eventBus = eventBus;
-    skyframeExecutor.setEventBus(eventBus);
+    skyframeExecutor.setEventBus(null);
   }
 
   /**
    * Conditionally enable profiling.
    */
-  private final boolean initProfiler(CommonCommandOptions options,
+  private final boolean initProfiler(CommandEnvironment env, CommonCommandOptions options,
       UUID buildID, long execStartTimeNanos) {
     OutputStream out = null;
     boolean recordFullProfilerData = false;
@@ -342,7 +334,7 @@
 
         recordFullProfilerData = options.recordFullProfilerData;
         out = new BufferedOutputStream(profilePath.getOutputStream(), 1024 * 1024);
-        getReporter().handle(Event.info("Writing profile data to '" + profilePath + "'"));
+        env.getReporter().handle(Event.info("Writing profile data to '" + profilePath + "'"));
         profiledTasks = ProfiledTaskKinds.ALL;
       } else if (options.alwaysProfileSlowOperations) {
         recordFullProfilerData = false;
@@ -357,7 +349,7 @@
         return true;
       }
     } catch (IOException e) {
-      getReporter().handle(Event.error("Error while creating profile file: " + e.getMessage()));
+      env.getReporter().handle(Event.error("Error while creating profile file: " + e.getMessage()));
     }
     return false;
   }
@@ -517,13 +509,6 @@
     return reporter;
   }
 
-  /**
-   * Returns the current event bus. Only valid within the scope of a single Blaze command.
-   */
-  public EventBus getEventBus() {
-    return eventBus;
-  }
-
   public BinTools getBinTools() {
     return binTools;
   }
@@ -542,13 +527,6 @@
     return packageFactory;
   }
 
-  /**
-   * Returns the build tool.
-   */
-  public BuildTool getBuildTool() {
-    return buildTool;
-  }
-
   public ImmutableList<OutputFormatter> getQueryOutputFormatters() {
     ImmutableList.Builder<OutputFormatter> result = ImmutableList.builder();
     result.addAll(OutputFormatter.getDefaultFormatters());
@@ -702,12 +680,12 @@
    * @param options The CommonCommandOptions used by every command.
    * @throws AbruptExitException if this command is unsuitable to be run as specified
    */
-  void beforeCommand(Command command, OptionsParser optionsParser,
+  void beforeCommand(Command command, CommandEnvironment env, OptionsParser optionsParser,
       CommonCommandOptions options, long execStartTimeNanos)
       throws AbruptExitException {
     commandStartTime -= options.startupTime;
 
-    eventBus.post(new GotOptionsEvent(startupOptionsProvider,
+    env.getEventBus().post(new GotOptionsEvent(startupOptionsProvider,
         optionsParser));
     throwPendingException();
 
@@ -753,7 +731,7 @@
     // Conditionally enable profiling
     // We need to compensate for launchTimeNanos (measurements taken outside of the jvm).
     long startupTimeNanos = options.startupTime * 1000000L;
-    if (initProfiler(options, this.getCommandId(), execStartTimeNanos - startupTimeNanos)) {
+    if (initProfiler(env, options, this.getCommandId(), execStartTimeNanos - startupTimeNanos)) {
       Profiler profiler = Profiler.instance();
 
       // Instead of logEvent() we're calling the low level function to pass the timings we took in
@@ -770,7 +748,7 @@
       try {
         MemoryProfiler.instance().start(memoryProfilePath.getOutputStream());
       } catch (IOException e) {
-        getReporter().handle(
+        env.getReporter().handle(
             Event.error("Error while creating memory profile file: " + e.getMessage()));
       }
     }
@@ -802,7 +780,8 @@
       module.handleOptions(optionsParser);
     }
 
-    eventBus.post(new CommandStartEvent(command.name(), commandId, clientEnv, workingDirectory));
+    env.getEventBus().post(
+        new CommandStartEvent(command.name(), commandId, clientEnv, workingDirectory));
     // Initialize exit code to dummy value for afterCommand.
     storedExitCode.set(ExitCode.RESERVED.getNumericExitCode());
   }
@@ -811,8 +790,8 @@
    * Hook method called by the BlazeCommandDispatcher right before the dispatch
    * of each command ends (while its outcome can still be modified).
    */
-  ExitCode precompleteCommand(ExitCode originalExit) {
-    eventBus.post(new CommandPrecompleteEvent(originalExit));
+  ExitCode precompleteCommand(CommandEnvironment env, ExitCode originalExit) {
+    env.getEventBus().post(new CommandPrecompleteEvent(originalExit));
     // If Blaze did not suffer an infrastructure failure, check for errors in modules.
     ExitCode exitCode = originalExit;
     if (!originalExit.isInfrastructureFailure() && pendingException != null) {
@@ -833,7 +812,7 @@
       // thread won the race (unlikely, but possible), this may be incorrectly logged as a success.
       return;
     }
-    eventBus.post(new CommandCompleteEvent(exitCode));
+    skyframeExecutor.getEventBus().post(new CommandCompleteEvent(exitCode));
   }
 
   /**
@@ -841,9 +820,9 @@
    * command.
    */
   @VisibleForTesting
-  public void afterCommand(int exitCode) {
+  public void afterCommand(CommandEnvironment env, int exitCode) {
     // Remove any filters that the command might have added to the reporter.
-    getReporter().setOutputFilter(OutputFilter.OUTPUT_EVERYTHING);
+    env.getReporter().setOutputFilter(OutputFilter.OUTPUT_EVERYTHING);
 
     notifyCommandComplete(exitCode);
 
@@ -857,7 +836,7 @@
       Profiler.instance().stop();
       MemoryProfiler.instance().stop();
     } catch (IOException e) {
-      getReporter().handle(Event.error("Error while writing profile file: " + e.getMessage()));
+      env.getReporter().handle(Event.error("Error while writing profile file: " + e.getMessage()));
     }
   }