Refactor the module API to use the builder pattern for executor creation.

This significantly simplifies several of our modules.

--
MOS_MIGRATED_REVID=137713119
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ExecutorBuilder.java b/src/main/java/com/google/devtools/build/lib/actions/ExecutorBuilder.java
new file mode 100644
index 0000000..c25fc07
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/ExecutorBuilder.java
@@ -0,0 +1,64 @@
+// Copyright 2016 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.actions;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Executor.ActionContext;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Builder class to create an {@link Executor} instance. This class is part of the module API,
+ * which allows modules to affect how the executor is initialized.
+ */
+public class ExecutorBuilder {
+  private final List<ActionContextProvider> actionContextProviders = new ArrayList<>();
+  private final List<ActionContextConsumer> actionContextConsumers = new ArrayList<>();
+
+  // These methods shouldn't be public, but they have to be right now as ExecutionTool is in another
+  // package.
+  public ImmutableList<ActionContextProvider> getActionContextProviders() {
+    return ImmutableList.copyOf(actionContextProviders);
+  }
+
+  public ImmutableList<ActionContextConsumer> getActionContextConsumers() {
+    return ImmutableList.copyOf(actionContextConsumers);
+  }
+
+  /**
+   * Adds the specified action context providers to the executor.
+   */
+  public ExecutorBuilder addActionContextProvider(ActionContextProvider provider) {
+    this.actionContextProviders.add(provider);
+    return this;
+  }
+
+  /**
+   * Adds the specified action context to the executor, by wrapping it in a simple action context
+   * provider implementation.
+   */
+  public ExecutorBuilder addActionContext(ActionContext context) {
+    return addActionContextProvider(new SimpleActionContextProvider(context));
+  }
+
+  /**
+   * Adds the specified action context consumer to the executor.
+   */
+  public ExecutorBuilder addActionContextConsumer(ActionContextConsumer consumer) {
+    this.actionContextConsumers.add(consumer);
+    return this;
+  }
+
+}
+
diff --git a/src/main/java/com/google/devtools/build/lib/actions/SimpleActionContextProvider.java b/src/main/java/com/google/devtools/build/lib/actions/SimpleActionContextProvider.java
index 1239ccb..642bc09 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/SimpleActionContextProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/SimpleActionContextProvider.java
@@ -15,25 +15,15 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.actions.Executor.ActionContext;
-
 import java.util.List;
 
 /**
  * An {@link ActionContextProvider} that just provides the {@link ActionContext}s it's given.
  */
-public class SimpleActionContextProvider extends ActionContextProvider {
-
-  /**
-   * Creates an immutable list containing a single SimpleActionContextProvider with the given
-   * contexts as a convenience for BlazeModule.getActionContextProviders().
-   */
-  public static List<ActionContextProvider> of(ActionContext ... contexts) {
-    return ImmutableList.<ActionContextProvider>of(new SimpleActionContextProvider(contexts));
-  }
-
+final class SimpleActionContextProvider extends ActionContextProvider {
   private final List<ActionContext> actionContexts;
 
-  public SimpleActionContextProvider(ActionContext ... contexts) {
+  public SimpleActionContextProvider(ActionContext... contexts) {
     actionContexts = ImmutableList.<ActionContext>copyOf(contexts);
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java
index 7f205f8..569ce9f 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelWorkspaceStatusModule.java
@@ -22,7 +22,6 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.eventbus.Subscribe;
-import com.google.devtools.build.lib.actions.ActionContextProvider;
 import com.google.devtools.build.lib.actions.ActionExecutionContext;
 import com.google.devtools.build.lib.actions.ActionExecutionException;
 import com.google.devtools.build.lib.actions.ActionOwner;
@@ -31,14 +30,15 @@
 import com.google.devtools.build.lib.actions.ArtifactOwner;
 import com.google.devtools.build.lib.actions.ExecutionStrategy;
 import com.google.devtools.build.lib.actions.Executor;
+import com.google.devtools.build.lib.actions.ExecutorBuilder;
 import com.google.devtools.build.lib.actions.ResourceSet;
 import com.google.devtools.build.lib.actions.Root;
-import com.google.devtools.build.lib.actions.SimpleActionContextProvider;
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
 import com.google.devtools.build.lib.analysis.BuildInfo;
 import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
 import com.google.devtools.build.lib.analysis.WorkspaceStatusAction.Key;
 import com.google.devtools.build.lib.analysis.WorkspaceStatusAction.KeyType;
+import com.google.devtools.build.lib.buildtool.BuildRequest;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.runtime.BlazeModule;
 import com.google.devtools.build.lib.runtime.Command;
@@ -246,7 +246,6 @@
   }
 
   private class BazelStatusActionFactory implements WorkspaceStatusAction.Factory {
-
     private String hostname;
 
     @Override
@@ -286,7 +285,13 @@
   }
 
   @ExecutionStrategy(contextType = WorkspaceStatusAction.Context.class)
-  private class BazelWorkspaceStatusActionContext implements WorkspaceStatusAction.Context {
+  private static final class BazelWorkspaceStatusActionContext
+      implements WorkspaceStatusAction.Context {
+    private final WorkspaceStatusAction.Options options;
+
+    private BazelWorkspaceStatusActionContext(WorkspaceStatusAction.Options options) {
+      this.options = options;
+    }
 
     @Override
     public ImmutableMap<String, Key> getStableKeys() {
@@ -339,12 +344,12 @@
   }
 
   @Override
-  public Iterable<ActionContextProvider> getActionContextProviders() {
-    return SimpleActionContextProvider.of(new BazelWorkspaceStatusActionContext());
+  public void workspaceInit(BlazeDirectories directories, WorkspaceBuilder builder) {
+    builder.setWorkspaceStatusActionFactory(new BazelStatusActionFactory());
   }
 
   @Override
-  public void workspaceInit(BlazeDirectories directories, WorkspaceBuilder builder) {
-    builder.setWorkspaceStatusActionFactory(new BazelStatusActionFactory());
+  public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
+    builder.addActionContext(new BazelWorkspaceStatusActionContext(options));
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java
index 0fe4aa2..314e440 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java
@@ -16,13 +16,11 @@
 
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
-import com.google.common.eventbus.Subscribe;
-import com.google.devtools.build.lib.actions.ActionContextConsumer;
-import com.google.devtools.build.lib.actions.ActionContextProvider;
-import com.google.devtools.build.lib.actions.SimpleActionContextProvider;
+import com.google.devtools.build.lib.actions.ExecutorBuilder;
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
 import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses;
+import com.google.devtools.build.lib.buildtool.BuildRequest;
 import com.google.devtools.build.lib.query2.output.OutputFormatter;
 import com.google.devtools.build.lib.rules.android.WriteAdbArgsActionContext;
 import com.google.devtools.build.lib.rules.cpp.FdoSupportFunction;
@@ -31,7 +29,6 @@
 import com.google.devtools.build.lib.runtime.BlazeModule;
 import com.google.devtools.build.lib.runtime.Command;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
-import com.google.devtools.build.lib.runtime.GotOptionsEvent;
 import com.google.devtools.build.lib.runtime.WorkspaceBuilder;
 import com.google.devtools.build.lib.skyframe.PrecomputedValue;
 import com.google.devtools.build.lib.util.ResourceFileLoader;
@@ -84,7 +81,6 @@
   }
 
   private CommandEnvironment env;
-  protected BazelExecutionOptions options;
 
   @Override
   public void beforeCommand(Command command, CommandEnvironment env) {
@@ -95,7 +91,6 @@
   @Override
   public void afterCommand() {
     this.env = null;
-    this.options = null;
   }
 
   @Override
@@ -106,22 +101,6 @@
   }
 
   @Override
-  public Iterable<ActionContextProvider> getActionContextProviders() {
-    return ImmutableList.<ActionContextProvider>of(new SimpleActionContextProvider(
-        new WriteAdbArgsActionContext(env.getClientEnv().get("HOME"))));
-  }
-
-  @Override
-  public Iterable<ActionContextConsumer> getActionContextConsumers() {
-    return ImmutableList.<ActionContextConsumer>of(new BazelActionContextConsumer(options));
-  }
-
-  @Subscribe
-  public void gotOptions(GotOptionsEvent event) {
-    options = event.getOptions().getOptions(BazelExecutionOptions.class);
-  }
-
-  @Override
   public void initializeRuleClasses(ConfiguredRuleClassProvider.Builder builder) {
     builder.setToolsRepository(BazelRuleClassProvider.TOOLS_REPOSITORY);
     BazelRuleClassProvider.setup(builder);
@@ -149,4 +128,11 @@
           }
         }));
   }
+
+  @Override
+  public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
+    builder.addActionContext(new WriteAdbArgsActionContext(env.getClientEnv().get("HOME")));
+    BazelExecutionOptions options = env.getOptions().getOptions(BazelExecutionOptions.class);
+    builder.addActionContextConsumer(new BazelActionContextConsumer(options));
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
index 891c2cf..7543b53 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
@@ -43,11 +43,11 @@
 import com.google.devtools.build.lib.actions.ExecutionStrategy;
 import com.google.devtools.build.lib.actions.Executor;
 import com.google.devtools.build.lib.actions.Executor.ActionContext;
+import com.google.devtools.build.lib.actions.ExecutorBuilder;
 import com.google.devtools.build.lib.actions.ExecutorInitException;
 import com.google.devtools.build.lib.actions.LocalHostCapacity;
 import com.google.devtools.build.lib.actions.ResourceManager;
 import com.google.devtools.build.lib.actions.ResourceSet;
-import com.google.devtools.build.lib.actions.SimpleActionContextProvider;
 import com.google.devtools.build.lib.actions.SpawnActionContext;
 import com.google.devtools.build.lib.actions.TestExecException;
 import com.google.devtools.build.lib.actions.cache.ActionCache;
@@ -187,39 +187,38 @@
     // determine whether the host actually supports certain strategies (e.g. sandboxing).
     createToolsSymlinks();
 
-    this.actionContextProviders =
-        getActionContextProvidersFromModules(
-            runtime,
-            new FilesetActionContextImpl.Provider(
-                env.getReporter(), env.getWorkspaceName()),
-            new SimpleActionContextProvider(
-                new SymlinkTreeStrategy(
-                    env.getOutputService(), env.getBlazeWorkspace().getBinTools())));
+    ExecutorBuilder builder = new ExecutorBuilder();
+    for (BlazeModule module : runtime.getBlazeModules()) {
+      module.executorInit(env, request, builder);
+    }
+    builder.addActionContextProvider(
+        new FilesetActionContextImpl.Provider(env.getReporter(), env.getWorkspaceName()));
+    builder.addActionContext(new SymlinkTreeStrategy(
+                env.getOutputService(), env.getBlazeWorkspace().getBinTools()));
+    // TODO(philwo) - the ExecutionTool should not add arbitrary dependencies on its own, instead
+    // these dependencies should be added to the ActionContextConsumer of the module that actually
+    // depends on them.
+    builder.addActionContextConsumer(
+        new ActionContextConsumer() {
+          @Override
+          public ImmutableMap<String, String> getSpawnActionContexts() {
+            return ImmutableMap.of();
+          }
+
+          @Override
+          public Multimap<Class<? extends ActionContext>, String> getActionContexts() {
+            return ImmutableMultimap.<Class<? extends ActionContext>, String>builder()
+                .put(FilesetActionContext.class, "")
+                .put(WorkspaceStatusAction.Context.class, "")
+                .put(SymlinkTreeActionContext.class, "")
+                .build();
+          }
+        });
+
+    this.actionContextProviders = builder.getActionContextProviders();
     StrategyConverter strategyConverter = new StrategyConverter(actionContextProviders);
 
-    ImmutableList<ActionContextConsumer> actionContextConsumers =
-        getActionContextConsumersFromModules(
-            runtime,
-            // TODO(philwo) - the ExecutionTool should not add arbitrary dependencies on its own,
-            // instead these dependencies should be added to the ActionContextConsumer of the module
-            // that actually depends on them.
-            new ActionContextConsumer() {
-              @Override
-              public ImmutableMap<String, String> getSpawnActionContexts() {
-                return ImmutableMap.of();
-              }
-
-              @Override
-              public Multimap<Class<? extends ActionContext>, String> getActionContexts() {
-                return ImmutableMultimap.<Class<? extends ActionContext>, String>builder()
-                    .put(FilesetActionContext.class, "")
-                    .put(WorkspaceStatusAction.Context.class, "")
-                    .put(SymlinkTreeActionContext.class, "")
-                    .build();
-              }
-            });
-
-    for (ActionContextConsumer consumer : actionContextConsumers) {
+    for (ActionContextConsumer consumer : builder.getActionContextConsumers()) {
       // There are many different SpawnActions, and we want to control the action context they use
       // independently from each other, for example, to run genrules locally and Java compile action
       // in prod. Thus, for SpawnActions, we decide the action context to use not only based on the
@@ -259,26 +258,6 @@
     strategies.add(context);
   }
 
-  private static ImmutableList<ActionContextConsumer> getActionContextConsumersFromModules(
-      BlazeRuntime runtime, ActionContextConsumer... extraConsumers) {
-    ImmutableList.Builder<ActionContextConsumer> builder = ImmutableList.builder();
-    for (BlazeModule module : runtime.getBlazeModules()) {
-      builder.addAll(module.getActionContextConsumers());
-    }
-    builder.add(extraConsumers);
-    return builder.build();
-  }
-
-  private static ImmutableList<ActionContextProvider> getActionContextProvidersFromModules(
-      BlazeRuntime runtime, ActionContextProvider... extraProviders) {
-    ImmutableList.Builder<ActionContextProvider> builder = ImmutableList.builder();
-    for (BlazeModule module : runtime.getBlazeModules()) {
-      builder.addAll(module.getActionContextProviders());
-    }
-    builder.add(extraProviders);
-    return builder.build();
-  }
-
   private static ExecutorInitException makeExceptionForInvalidStrategyValue(String value,
       String strategy, String validValues) {
     return new ExecutorInitException(String.format(
@@ -602,7 +581,6 @@
    * file, iff the --explain flag is specified during a build.
    */
   private static class ExplanationHandler implements EventHandler {
-
     private final PrintWriter log;
 
     private ExplanationHandler(OutputStream log, String optionsDescription) {
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
index 7db69e8..52343b0 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java
@@ -16,7 +16,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.eventbus.Subscribe;
-import com.google.devtools.build.lib.actions.ActionContextProvider;
+import com.google.devtools.build.lib.actions.ExecutorBuilder;
 import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
 import com.google.devtools.build.lib.buildtool.BuildRequest;
 import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
@@ -29,18 +29,9 @@
 /** RemoteModule provides distributed cache and remote execution for Bazel. */
 public final class RemoteModule extends BlazeModule {
   private CommandEnvironment env;
-  private BuildRequest buildRequest;
   private RemoteActionCache actionCache;
   private RemoteWorkExecutor workExecutor;
 
-  public RemoteModule() {}
-
-  @Override
-  public Iterable<ActionContextProvider> getActionContextProviders() {
-    return ImmutableList.<ActionContextProvider>of(
-        new RemoteActionContextProvider(env, buildRequest, actionCache, workExecutor));
-  }
-
   @Override
   public void beforeCommand(Command command, CommandEnvironment env) {
     this.env = env;
@@ -50,13 +41,17 @@
   @Override
   public void afterCommand() {
     this.env = null;
-    this.buildRequest = null;
+  }
+
+  @Override
+  public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
+    builder.addActionContextProvider(
+        new RemoteActionContextProvider(env, request, actionCache, workExecutor));
   }
 
   @Subscribe
   public void buildStarting(BuildStartingEvent event) {
-    buildRequest = event.getRequest();
-    RemoteOptions options = buildRequest.getOptions(RemoteOptions.class);
+    RemoteOptions options = event.getRequest().getOptions(RemoteOptions.class);
 
     try {
       // Reinitialize the remote cache and worker from options every time, because the options
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
index e6cce93..cea042f 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeModule.java
@@ -14,13 +14,13 @@
 package com.google.devtools.build.lib.runtime;
 
 import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.actions.ActionContextConsumer;
-import com.google.devtools.build.lib.actions.ActionContextProvider;
 import com.google.devtools.build.lib.actions.ActionInputFileCache;
+import com.google.devtools.build.lib.actions.ExecutorBuilder;
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
 import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
 import com.google.devtools.build.lib.analysis.ServerDirectories;
+import com.google.devtools.build.lib.buildtool.BuildRequest;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.exec.ActionInputPrefetcher;
 import com.google.devtools.build.lib.exec.OutputService;
@@ -213,24 +213,15 @@
   }
 
   /**
-   * Returns the action context providers the module contributes to Blaze, if any.
+   * Called when Bazel initializes the action execution subsystem. This is called once per build if
+   * action execution is enabled. Modules can override this method to affect how execution is
+   * performed.
    *
-   * <p>This method will be called at the beginning of the execution phase, e.g. of the
-   * "blaze build" command.
+   * @param env the command environment
+   * @param request the build request
+   * @param builder the builder to add action context providers and consumers to
    */
-  public Iterable<ActionContextProvider> getActionContextProviders() {
-    return ImmutableList.of();
-  }
-
-  /**
-   * Returns the action context consumers that pulls in action contexts required by this module,
-   * if any.
-   *
-   * <p>This method will be called at the beginning of the execution phase, e.g. of the
-   * "blaze build" command.
-   */
-  public Iterable<ActionContextConsumer> getActionContextConsumers() {
-    return ImmutableList.of();
+  public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java b/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java
index e22b09b..c72c05e 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java
@@ -93,6 +93,8 @@
   private ImmutableList<ActionInputPrefetcher> actionInputPrefetchers = ImmutableList.of();
   private Path workingDirectory;
 
+  private OptionsClassProvider options;
+
   private AtomicReference<AbruptExitException> pendingException = new AtomicReference<>();
 
   private class BlazeModuleEnvironment implements BlazeModule.ModuleEnvironment {
@@ -180,6 +182,10 @@
     return Collections.unmodifiableMap(clientEnv);
   }
 
+  public OptionsClassProvider getOptions() {
+    return options;
+  }
+
   /**
    * Return an ordered version of the client environment restricted to those variables whitelisted
    * by the command-line options to be inheritable by actions.
@@ -527,6 +533,7 @@
         throw new IllegalStateException(e);
       }
     }
+    this.options = optionsParser;
 
     eventBus.post(new GotOptionsEvent(runtime.getStartupOptionsProvider(), optionsParser));
     throwPendingException();
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java
index 8334e00..a8b6713 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java
@@ -15,15 +15,11 @@
 package com.google.devtools.build.lib.sandbox;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.eventbus.Subscribe;
-import com.google.devtools.build.lib.actions.ActionContextConsumer;
-import com.google.devtools.build.lib.actions.ActionContextProvider;
+import com.google.devtools.build.lib.actions.ExecutorBuilder;
 import com.google.devtools.build.lib.buildtool.BuildRequest;
-import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
 import com.google.devtools.build.lib.runtime.BlazeModule;
 import com.google.devtools.build.lib.runtime.Command;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
-import com.google.devtools.build.lib.util.Preconditions;
 import com.google.devtools.common.options.OptionsBase;
 import java.io.IOException;
 
@@ -31,28 +27,6 @@
  * This module provides the Sandbox spawn strategy.
  */
 public final class SandboxModule extends BlazeModule {
-  // Per-command state
-  private CommandEnvironment env;
-  private BuildRequest buildRequest;
-
-  @Override
-  public Iterable<ActionContextProvider> getActionContextProviders() {
-    Preconditions.checkNotNull(env);
-    Preconditions.checkNotNull(buildRequest);
-    try {
-      return ImmutableList.<ActionContextProvider>of(
-          SandboxActionContextProvider.create(env, buildRequest));
-    } catch (IOException e) {
-      throw new IllegalStateException(e);
-    }
-  }
-
-  @Override
-  public Iterable<ActionContextConsumer> getActionContextConsumers() {
-    Preconditions.checkNotNull(env);
-    return ImmutableList.<ActionContextConsumer>of(new SandboxActionContextConsumer(env));
-  }
-
   @Override
   public Iterable<Class<? extends OptionsBase>> getCommandOptions(Command command) {
     return "build".equals(command.name())
@@ -61,19 +35,12 @@
   }
 
   @Override
-  public void beforeCommand(Command command, CommandEnvironment env) {
-    this.env = env;
-    env.getEventBus().register(this);
-  }
-
-  @Override
-  public void afterCommand() {
-    env = null;
-    buildRequest = null;
-  }
-
-  @Subscribe
-  public void buildStarting(BuildStartingEvent event) {
-    buildRequest = event.getRequest();
+  public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
+    try {
+      builder.addActionContextProvider(SandboxActionContextProvider.create(env, request));
+    } catch (IOException e) {
+      throw new IllegalStateException(e);
+    }
+    builder.addActionContextConsumer(new SandboxActionContextConsumer(env));
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/standalone/StandaloneModule.java b/src/main/java/com/google/devtools/build/lib/standalone/StandaloneModule.java
index 58145b0..bde53f0 100644
--- a/src/main/java/com/google/devtools/build/lib/standalone/StandaloneModule.java
+++ b/src/main/java/com/google/devtools/build/lib/standalone/StandaloneModule.java
@@ -13,50 +13,18 @@
 // limitations under the License.
 package com.google.devtools.build.lib.standalone;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.eventbus.Subscribe;
-import com.google.devtools.build.lib.actions.ActionContextConsumer;
-import com.google.devtools.build.lib.actions.ActionContextProvider;
+import com.google.devtools.build.lib.actions.ExecutorBuilder;
 import com.google.devtools.build.lib.buildtool.BuildRequest;
-import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
 import com.google.devtools.build.lib.runtime.BlazeModule;
-import com.google.devtools.build.lib.runtime.Command;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
-import com.google.devtools.build.lib.util.Preconditions;
 
 /**
  * StandaloneModule provides pluggable functionality for blaze.
  */
 public class StandaloneModule extends BlazeModule {
-  private CommandEnvironment env;
-  private BuildRequest buildRequest;
-
   @Override
-  public Iterable<ActionContextConsumer> getActionContextConsumers() {
-    Preconditions.checkNotNull(env);
-    return ImmutableList.<ActionContextConsumer>of(new StandaloneActionContextConsumer());
-  }
-
-  @Override
-  public Iterable<ActionContextProvider> getActionContextProviders() {
-    return ImmutableList.<ActionContextProvider>of(
-        new StandaloneActionContextProvider(env, buildRequest));
-  }
-
-  @Override
-  public void beforeCommand(Command command, CommandEnvironment env) {
-    this.env = env;
-    env.getEventBus().register(this);
-  }
-
-  @Override
-  public void afterCommand() {
-    this.env = null;
-    this.buildRequest = null;
-  }
-
-  @Subscribe
-  public void buildStarting(BuildStartingEvent event) {
-    buildRequest = event.getRequest();
+  public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
+    builder.addActionContextProvider(new StandaloneActionContextProvider(env, request));
+    builder.addActionContextConsumer(new StandaloneActionContextConsumer());
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java
index c57a168..1ec0122 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java
@@ -15,8 +15,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.eventbus.Subscribe;
-import com.google.devtools.build.lib.actions.ActionContextConsumer;
-import com.google.devtools.build.lib.actions.ActionContextProvider;
+import com.google.devtools.build.lib.actions.ExecutorBuilder;
 import com.google.devtools.build.lib.buildtool.BuildRequest;
 import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
 import com.google.devtools.build.lib.buildtool.buildevent.BuildInterruptedEvent;
@@ -35,7 +34,6 @@
  */
 public class WorkerModule extends BlazeModule {
   private CommandEnvironment env;
-  private BuildRequest buildRequest;
 
   private WorkerFactory workerFactory;
   private WorkerPool workerPool;
@@ -57,8 +55,7 @@
 
   @Subscribe
   public void buildStarting(BuildStartingEvent event) {
-    buildRequest = event.getRequest();
-    options = buildRequest.getOptions(WorkerOptions.class);
+    options = event.getRequest().getOptions(WorkerOptions.class);
 
     if (workerFactory == null) {
       Path workerDir =
@@ -133,25 +130,17 @@
   }
 
   @Override
-  public Iterable<ActionContextProvider> getActionContextProviders() {
-    Preconditions.checkNotNull(env);
-    Preconditions.checkNotNull(buildRequest);
+  public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder) {
     Preconditions.checkNotNull(workerPool);
-
-    return ImmutableList.<ActionContextProvider>of(
-        new WorkerActionContextProvider(env, buildRequest, workerPool));
-  }
-
-  @Override
-  public Iterable<ActionContextConsumer> getActionContextConsumers() {
-    return ImmutableList.<ActionContextConsumer>of(new WorkerActionContextConsumer());
+    builder.addActionContextProvider(
+        new WorkerActionContextProvider(env, request, workerPool));
+    builder.addActionContextConsumer(new WorkerActionContextConsumer());
   }
 
   @Subscribe
   public void buildComplete(BuildCompleteEvent event) {
-    if (buildRequest != null
-        && buildRequest.getOptions(WorkerOptions.class) != null
-        && buildRequest.getOptions(WorkerOptions.class).workerQuitAfterBuild) {
+    if (options != null
+        && options.workerQuitAfterBuild) {
       shutdownPool("Build completed, shutting down worker pool...");
     }
   }
@@ -182,7 +171,6 @@
   @Override
   public void afterCommand() {
     this.env = null;
-    this.buildRequest = null;
     this.options = null;
 
     if (this.workerFactory != null) {