Simplified ActionContextConsumer by having it operate on a new class which holds a variety of strategy/context maps.
PiperOrigin-RevId: 190491357
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
index 8a96476..a0ea5698 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionContext.java
@@ -183,11 +183,9 @@
return executor.getContext(type);
}
- /**
- * Returns the action context implementation for spawn actions with a given mnemonic.
- */
- public SpawnActionContext getSpawnActionContext(String mnemonic) {
- return executor.getSpawnActionContext(mnemonic);
+ /** Returns the action context implementation for spawn actions with a given mnemonic. */
+ public SpawnActionContext getSpawnActionContext(String mnemonic, Spawn spawn) {
+ return executor.getSpawnActionContext(mnemonic, spawn);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Executor.java b/src/main/java/com/google/devtools/build/lib/actions/Executor.java
index 292e78f..bb1f6b4 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Executor.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Executor.java
@@ -87,8 +87,6 @@
*/
<T extends ActionContext> T getContext(Class<? extends T> type);
- /**
- * Returns the action context implementation for spawn actions with a given mnemonic.
- */
- SpawnActionContext getSpawnActionContext(String mnemonic);
+ /** Returns the action context implementation for spawn actions with a given mnemonic. */
+ SpawnActionContext getSpawnActionContext(String mnemonic, Spawn spawn);
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/PopulateTreeArtifactAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/PopulateTreeArtifactAction.java
index ae6d210..ec46996 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/PopulateTreeArtifactAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/PopulateTreeArtifactAction.java
@@ -194,7 +194,7 @@
// Execute the spawn.
List<SpawnResult> spawnResults;
try {
- spawnResults = getContext(actionExecutionContext).exec(spawn, actionExecutionContext);
+ spawnResults = getContext(spawn, actionExecutionContext).exec(spawn, actionExecutionContext);
} catch (ExecException e) {
throw e.toActionExecutionException(
getMnemonic() + " action failed for target: " + getOwner().getLabel(),
@@ -233,8 +233,9 @@
return true;
}
- private SpawnActionContext getContext(ActionExecutionContext actionExecutionContext) {
- return actionExecutionContext.getSpawnActionContext(getMnemonic());
+ private SpawnActionContext getContext(
+ Spawn spawn, ActionExecutionContext actionExecutionContext) {
+ return actionExecutionContext.getSpawnActionContext(getMnemonic(), spawn);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
index 3a5b225..fc068bc 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/SpawnAction.java
@@ -257,8 +257,8 @@
*/
protected List<SpawnResult> internalExecute(ActionExecutionContext actionExecutionContext)
throws ExecException, InterruptedException, CommandLineExpansionException {
- return getContext(actionExecutionContext)
- .exec(getSpawn(actionExecutionContext.getClientEnv()), actionExecutionContext);
+ Spawn spawn = getSpawn(actionExecutionContext.getClientEnv());
+ return getContext(actionExecutionContext, spawn).exec(spawn, actionExecutionContext);
}
@Override
@@ -449,8 +449,9 @@
return executionInfo;
}
- protected SpawnActionContext getContext(ActionExecutionContext actionExecutionContext) {
- return actionExecutionContext.getSpawnActionContext(getMnemonic());
+ protected SpawnActionContext getContext(
+ ActionExecutionContext actionExecutionContext, Spawn spawn) {
+ return actionExecutionContext.getSpawnActionContext(getMnemonic(), spawn);
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelActionContextConsumer.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelActionContextConsumer.java
index 9b3e370..009d039 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelActionContextConsumer.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelActionContextConsumer.java
@@ -14,20 +14,17 @@
package com.google.devtools.build.lib.bazel.rules;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Multimap;
import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.analysis.actions.FileWriteActionContext;
import com.google.devtools.build.lib.bazel.rules.BazelStrategyModule.BazelExecutionOptions;
import com.google.devtools.build.lib.exec.ActionContextConsumer;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps;
import com.google.devtools.build.lib.exec.SpawnCache;
import com.google.devtools.build.lib.rules.android.WriteAdbArgsActionContext;
import com.google.devtools.build.lib.rules.cpp.CppCompileActionContext;
import com.google.devtools.build.lib.rules.cpp.CppIncludeExtractionContext;
import com.google.devtools.build.lib.rules.cpp.CppIncludeScanningContext;
import java.util.Map;
-import java.util.TreeMap;
/**
* An object describing the {@link ActionContext} implementation that some actions require in Bazel.
@@ -40,12 +37,10 @@
}
@Override
- public ImmutableMap<String, String> getSpawnActionContexts() {
- Map<String, String> contexts = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
+ public void populate(SpawnActionContextMaps.Builder builder) {
// Default strategies for certain mnemonics - they can be overridden by --strategy= flags.
- contexts.put("Javac", "worker");
- contexts.put("Closure", "worker");
+ builder.strategyByMnemonicMap().put("Javac", "worker");
+ builder.strategyByMnemonicMap().put("Closure", "worker");
for (Map.Entry<String, String> strategy : options.strategy) {
String strategyName = strategy.getValue();
@@ -55,30 +50,25 @@
if (strategyName.equals("local")) {
strategyName = "standalone";
}
- contexts.put(strategy.getKey(), strategyName);
+ builder.strategyByMnemonicMap().put(strategy.getKey(), strategyName);
}
if (!options.genruleStrategy.isEmpty()) {
- contexts.put("Genrule", options.genruleStrategy);
+ builder.strategyByMnemonicMap().put("Genrule", options.genruleStrategy);
}
// TODO(bazel-team): put this in getActionContexts (key=SpawnActionContext.class) instead
if (!options.spawnStrategy.isEmpty()) {
- contexts.put("", options.spawnStrategy);
+ builder.strategyByMnemonicMap().put("", options.spawnStrategy);
}
- return ImmutableMap.copyOf(contexts);
- }
-
- @Override
- public Multimap<Class<? extends ActionContext>, String> getActionContexts() {
- return ImmutableMultimap.<Class<? extends ActionContext>, String>builder()
+ builder
+ .strategyByContextMap()
.put(CppCompileActionContext.class, "")
.put(CppIncludeExtractionContext.class, "")
.put(CppIncludeScanningContext.class, "")
.put(FileWriteActionContext.class, "")
.put(WriteAdbArgsActionContext.class, "")
- .put(SpawnCache.class, "")
- .build();
+ .put(SpawnCache.class, "");
}
}
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 3b238d9..3323f58 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
@@ -16,24 +16,15 @@
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Stopwatch;
-import com.google.common.base.Strings;
-import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Table;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionCacheChecker;
-import com.google.devtools.build.lib.actions.ActionContext;
-import com.google.devtools.build.lib.actions.ActionContextMarker;
import com.google.devtools.build.lib.actions.ActionGraph;
import com.google.devtools.build.lib.actions.ActionInputFileCache;
import com.google.devtools.build.lib.actions.ActionInputPrefetcher;
@@ -41,14 +32,12 @@
import com.google.devtools.build.lib.actions.ArtifactFactory;
import com.google.devtools.build.lib.actions.BuildFailedException;
import com.google.devtools.build.lib.actions.ExecException;
-import com.google.devtools.build.lib.actions.ExecutionStrategy;
import com.google.devtools.build.lib.actions.Executor;
import com.google.devtools.build.lib.actions.ExecutorInitException;
import com.google.devtools.build.lib.actions.LocalHostCapacity;
import com.google.devtools.build.lib.actions.PackageRoots;
import com.google.devtools.build.lib.actions.ResourceManager;
import com.google.devtools.build.lib.actions.ResourceSet;
-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;
import com.google.devtools.build.lib.actions.cache.Protos.ActionCacheStatistics;
@@ -60,7 +49,6 @@
import com.google.devtools.build.lib.analysis.actions.SymlinkTreeActionContext;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
-import com.google.devtools.build.lib.analysis.test.TestActionContext;
import com.google.devtools.build.lib.buildtool.buildevent.ExecutionPhaseCompleteEvent;
import com.google.devtools.build.lib.buildtool.buildevent.ExecutionStartingEvent;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
@@ -76,6 +64,7 @@
import com.google.devtools.build.lib.exec.ExecutorBuilder;
import com.google.devtools.build.lib.exec.FilesetActionContextImpl;
import com.google.devtools.build.lib.exec.SingleBuildFileCache;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps;
import com.google.devtools.build.lib.exec.SymlinkTreeStrategy;
import com.google.devtools.build.lib.profiler.AutoProfiler;
import com.google.devtools.build.lib.profiler.ProfilePhase;
@@ -90,7 +79,6 @@
import com.google.devtools.build.lib.skyframe.OutputService;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
import com.google.devtools.build.lib.util.AbruptExitException;
-import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.util.LoggingUtil;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.ModifiedFileSet;
@@ -102,16 +90,11 @@
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
-import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
@@ -129,50 +112,6 @@
* @see com.google.devtools.build.lib.analysis.BuildView
*/
public class ExecutionTool {
- private static class StrategyConverter {
- private Table<Class<? extends ActionContext>, String, ActionContext> classMap =
- HashBasedTable.create();
- private Map<Class<? extends ActionContext>, ActionContext> defaultClassMap =
- new HashMap<>();
-
- /**
- * Aggregates all {@link ActionContext}s that are in {@code contextProviders}.
- */
- @SuppressWarnings("unchecked")
- private StrategyConverter(Iterable<ActionContextProvider> contextProviders) {
- for (ActionContextProvider provider : contextProviders) {
- for (ActionContext strategy : provider.getActionContexts()) {
- ExecutionStrategy annotation =
- strategy.getClass().getAnnotation(ExecutionStrategy.class);
- // TODO(ulfjack): Don't silently ignore action contexts without annotation.
- if (annotation != null) {
- defaultClassMap.put(annotation.contextType(), strategy);
-
- for (String name : annotation.name()) {
- classMap.put(annotation.contextType(), name, strategy);
- }
- }
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- private <T extends ActionContext> T getStrategy(Class<T> clazz, String name) {
- return (T) (name.isEmpty() ? defaultClassMap.get(clazz) : classMap.get(clazz, name));
- }
-
- private String getValidValues(Class<? extends ActionContext> context) {
- return Joiner.on(", ").join(Ordering.natural().sortedCopy(classMap.row(context).keySet()));
- }
-
- private String getUserFriendlyName(Class<? extends ActionContext> context) {
- ActionContextMarker marker = context.getAnnotation(ActionContextMarker.class);
- return marker != null
- ? marker.name()
- : context.getSimpleName();
- }
- }
-
static final Logger logger = Logger.getLogger(ExecutionTool.class.getName());
private final CommandEnvironment env;
@@ -182,10 +121,7 @@
private final ActionInputFileCache fileCache;
private final ActionInputPrefetcher prefetcher;
private final ImmutableList<ActionContextProvider> actionContextProviders;
-
- private Map<String, SpawnActionContext> spawnStrategyMap =
- new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- private List<ActionContext> strategies = new ArrayList<>();
+ private SpawnActionContextMaps spawnActionContextMaps;
ExecutionTool(CommandEnvironment env, BuildRequest request) throws ExecutorInitException {
this.env = env;
@@ -208,20 +144,11 @@
// 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();
- }
+ b -> {
+ b.strategyByContextMap()
+ .put(FilesetActionContext.class, "")
+ .put(WorkspaceStatusAction.Context.class, "")
+ .put(SymlinkTreeActionContext.class, "");
});
// Unfortunately, the exec root cache is not shared with caches in the remote execution client.
@@ -229,60 +156,24 @@
new SingleBuildFileCache(
env.getExecRoot().getPathString(), env.getRuntime().getFileSystem());
this.prefetcher = builder.getActionInputPrefetcher();
-
+
this.actionContextProviders = builder.getActionContextProviders();
for (ActionContextProvider provider : actionContextProviders) {
provider.init(fileCache);
}
- StrategyConverter strategyConverter = new StrategyConverter(actionContextProviders);
-
+ // 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
+ // context class, but also the mnemonic of the action.
+ SpawnActionContextMaps.Builder spawnActionContextMapsBuilder =
+ new SpawnActionContextMaps.Builder();
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
- // context class, but also the mnemonic of the action.
- for (Map.Entry<String, String> entry : consumer.getSpawnActionContexts().entrySet()) {
- SpawnActionContext context =
- strategyConverter.getStrategy(SpawnActionContext.class, entry.getValue());
- if (context == null) {
- String strategy = Strings.emptyToNull(entry.getKey());
- throw makeExceptionForInvalidStrategyValue(
- entry.getValue(),
- Joiner.on(' ').skipNulls().join(strategy, "spawn"),
- strategyConverter.getValidValues(SpawnActionContext.class));
- }
- spawnStrategyMap.put(entry.getKey(), context);
- }
-
- for (Map.Entry<Class<? extends ActionContext>, String> entry :
- consumer.getActionContexts().entries()) {
- ActionContext context = strategyConverter.getStrategy(entry.getKey(), entry.getValue());
- if (context == null) {
- throw makeExceptionForInvalidStrategyValue(
- entry.getValue(),
- strategyConverter.getUserFriendlyName(entry.getKey()),
- strategyConverter.getValidValues(entry.getKey()));
- }
- strategies.add(context);
- }
+ consumer.populate(spawnActionContextMapsBuilder);
}
-
- String testStrategyValue = request.getOptions(ExecutionOptions.class).testStrategy;
- ActionContext context = strategyConverter.getStrategy(TestActionContext.class,
- testStrategyValue);
- if (context == null) {
- throw makeExceptionForInvalidStrategyValue(testStrategyValue, "test",
- strategyConverter.getValidValues(TestActionContext.class));
- }
- strategies.add(context);
- }
-
- private static ExecutorInitException makeExceptionForInvalidStrategyValue(String value,
- String strategy, String validValues) {
- return new ExecutorInitException(String.format(
- "'%s' is an invalid value for %s strategy. Valid values are: %s", value, strategy,
- validValues), ExitCode.COMMAND_LINE_ERROR);
+ spawnActionContextMaps =
+ spawnActionContextMapsBuilder.build(
+ actionContextProviders, request.getOptions(ExecutionOptions.class).testStrategy);
}
Executor getExecutor() throws ExecutorInitException {
@@ -304,8 +195,7 @@
env.getEventBus(),
runtime.getClock(),
request,
- strategies,
- spawnStrategyMap,
+ spawnActionContextMaps,
actionContextProviders);
}
diff --git a/src/main/java/com/google/devtools/build/lib/exec/ActionContextConsumer.java b/src/main/java/com/google/devtools/build/lib/exec/ActionContextConsumer.java
index 660ba4b..a63176b 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/ActionContextConsumer.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/ActionContextConsumer.java
@@ -13,51 +13,43 @@
// limitations under the License.
package com.google.devtools.build.lib.exec;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Multimap;
import com.google.devtools.build.lib.actions.ActionContext;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps.Builder;
/**
- * An object describing that actions require a particular implementation of an
- * {@link ActionContext}.
+ * An object describing that actions require a particular implementation of an {@link
+ * ActionContext}.
*
* <p>This is expected to be implemented by modules that also implement actions which need these
* contexts. Other modules will provide implementations for various action contexts by implementing
* {@link ActionContextProvider}.
*
- * <p>Example: a module requires {@code SpawnActionContext} to do its job, and it creates
- * actions with the mnemonic <code>C++</code>. Then the {@link #getSpawnActionContexts} method of
- * this module would return a map with the key <code>"C++"</code> in it.
+ * <p>Example: a module requires {@code SpawnActionContext} to do its job, and it creates actions
+ * with the mnemonic <code>C++</code>. Then the {@link #populate(Builder)} method of this module
+ * would put <code>("C++", strategy)</code> in the map returned by {@link
+ * Builder#strategyByMnemonicMap()}.
*
* <p>The module can either decide for itself which implementation is needed and make the value
- * associated with this key a constant or defer that decision to the user, for example, by
- * providing a command line option and setting the value in the map based on that.
+ * associated with this key a constant or defer that decision to the user, for example, by providing
+ * a command line option and setting the value in the map based on that.
*
* <p>Other modules are free to provide different implementations of {@code SpawnActionContext}.
- * This can be used, for example, to implement sandboxed or distributed execution of
- * {@code SpawnAction}s in different ways, while giving the user control over how exactly they
- * are executed.
+ * This can be used, for example, to implement sandboxed or distributed execution of {@code
+ * SpawnAction}s in different ways, while giving the user control over how exactly they are
+ * executed.
+ *
+ * <p>Example: if a module requires {@code MyCustomActionContext} to be available, but doesn't
+ * associate it with any strategy, its {@link #populate(Builder)} should add {@code
+ * (MyCustomActionContext.class, "")} to the builder's {@link Builder#strategyByContextMap}.
+ *
+ * <p>Example: if a module requires {@code MyLocalCustomActionContext} to be available, and wants it
+ * to always use the "local" strategy, its {@link #populate(Builder)} should add {@code
+ * (MyCustomActionContext.class, "local")} to the builder's {@link Builder#strategyByContextMap}. .
*/
public interface ActionContextConsumer {
/**
- * Returns a map from spawn action mnemonics created by this module to the name of the
- * implementation of {@code SpawnActionContext} that the module wants to use for executing
- * it.
- *
- * <p>If a spawn action is executed whose mnemonic maps to the empty string or is not
- * present in the map at all, the choice of the implementation is left to Blaze.
- *
- * <p>Matching on mnemonics is done case-insensitively so it is recommended that any
- * implementation of this method makes sure that no two keys that refer to the same mnemonic are
- * present in the returned map. The easiest way to assure this is to use a map created using
- * {@code new TreeMap<>(String.CASE_INSENSITIVE_ORDER)}.
+ * Provides a {@link SpawnActionContextMaps.Builder} instance which modules may use to configure
+ * the {@link ActionContext} instances the module requires for particular actions.
*/
- ImmutableMap<String, String> getSpawnActionContexts();
-
- /**
- * Returns a map from action context class to the implementation required by the module.
- *
- * <p>If the implementation name is the empty string, the choice is left to Blaze.
- */
- Multimap<Class<? extends ActionContext>, String> getActionContexts();
+ void populate(SpawnActionContextMaps.Builder builder);
}
diff --git a/src/main/java/com/google/devtools/build/lib/exec/BlazeExecutor.java b/src/main/java/com/google/devtools/build/lib/exec/BlazeExecutor.java
index 89c22af..fe05762 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/BlazeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/BlazeExecutor.java
@@ -14,12 +14,11 @@
package com.google.devtools.build.lib.exec;
import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.ActionContext;
-import com.google.devtools.build.lib.actions.ExecutionStrategy;
import com.google.devtools.build.lib.actions.Executor;
import com.google.devtools.build.lib.actions.ExecutorInitException;
+import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.clock.Clock;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
@@ -32,12 +31,7 @@
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.common.options.OptionsClassProvider;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
-import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -63,9 +57,8 @@
private final OptionsClassProvider options;
private AtomicBoolean inExecutionPhase;
- private final Map<String, SpawnActionContext> spawnActionContextMap;
- private final Map<Class<? extends ActionContext>, ActionContext> contextMap =
- new HashMap<>();
+ private final Map<Class<? extends ActionContext>, ActionContext> contextMap;
+ private final SpawnActionContextMaps spawnActionContextMaps;
/**
* Constructs an Executor, bound to a specified output base path, and which will use the specified
@@ -85,8 +78,7 @@
EventBus eventBus,
Clock clock,
OptionsClassProvider options,
- List<ActionContext> contextImplementations,
- Map<String, SpawnActionContext> spawnActionContextMap,
+ SpawnActionContextMaps spawnActionContextMaps,
Iterable<ActionContextProvider> contextProviders)
throws ExecutorInitException {
ExecutionOptions executionOptions = options.getOptions(ExecutionOptions.class);
@@ -98,49 +90,16 @@
this.eventBus = eventBus;
this.clock = clock;
this.options = options;
+ this.spawnActionContextMaps = spawnActionContextMaps;
this.inExecutionPhase = new AtomicBoolean(false);
+ this.contextMap = spawnActionContextMaps.contextMap();
- // We need to keep only the last occurrences of the entries in contextImplementations
- // (so we respect insertion order but also instantiate them only once).
- LinkedHashSet<ActionContext> allContexts = new LinkedHashSet<>();
- allContexts.addAll(contextImplementations);
- allContexts.addAll(spawnActionContextMap.values());
- this.spawnActionContextMap = ImmutableMap.copyOf(spawnActionContextMap);
- for (ActionContext context : contextImplementations) {
- ExecutionStrategy annotation = context.getClass().getAnnotation(ExecutionStrategy.class);
- if (annotation != null) {
- contextMap.put(annotation.contextType(), context);
- }
- contextMap.put(context.getClass(), context);
- }
-
- // Print a sorted list of our (Spawn)ActionContext maps.
if (executionOptions.debugPrintActionContexts) {
- for (Entry<String, SpawnActionContext> entry :
- new TreeMap<>(spawnActionContextMap).entrySet()) {
- reporter.handle(
- Event.info(
- String.format(
- "SpawnActionContextMap: \"%s\" = %s",
- entry.getKey(), entry.getValue().getClass().getSimpleName())));
- }
-
- TreeMap<String, String> sortedContextMapWithSimpleNames = new TreeMap<>();
- for (Entry<Class<? extends ActionContext>, ActionContext> entry : contextMap.entrySet()) {
- sortedContextMapWithSimpleNames.put(
- entry.getKey().getSimpleName(), entry.getValue().getClass().getSimpleName());
- }
- for (Entry<String, String> entry : sortedContextMapWithSimpleNames.entrySet()) {
- // Skip uninteresting identity mappings of contexts.
- if (!entry.getKey().equals(entry.getValue())) {
- reporter.handle(
- Event.info(String.format("ContextMap: %s = %s", entry.getKey(), entry.getValue())));
- }
- }
+ spawnActionContextMaps.debugPrintSpawnActionContextMaps(reporter);
}
for (ActionContextProvider factory : contextProviders) {
- factory.executorCreated(allContexts);
+ factory.executorCreated(spawnActionContextMaps.allContexts());
}
}
@@ -231,10 +190,9 @@
* set, then it returns the default strategy for spawn actions.
*/
@Override
- public SpawnActionContext getSpawnActionContext(String mnemonic) {
- SpawnActionContext context = spawnActionContextMap.get(mnemonic);
- return context == null ? spawnActionContextMap.get("") : context;
- }
+ public SpawnActionContext getSpawnActionContext(String mnemonic, Spawn spawn) {
+ return spawnActionContextMaps.getSpawnActionContext(mnemonic, spawn, reporter);
+ }
/** Returns true iff the --verbose_failures option was enabled. */
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnActionContextMaps.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnActionContextMaps.java
new file mode 100644
index 0000000..fd017d0f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnActionContextMaps.java
@@ -0,0 +1,349 @@
+// 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.exec;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Ordering;
+import com.google.common.collect.Table;
+import com.google.devtools.build.lib.actions.ActionContext;
+import com.google.devtools.build.lib.actions.ActionContextMarker;
+import com.google.devtools.build.lib.actions.ExecutionStrategy;
+import com.google.devtools.build.lib.actions.ExecutorInitException;
+import com.google.devtools.build.lib.actions.Spawn;
+import com.google.devtools.build.lib.actions.SpawnActionContext;
+import com.google.devtools.build.lib.analysis.test.TestActionContext;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.events.Reporter;
+import com.google.devtools.build.lib.util.ExitCode;
+import com.google.devtools.build.lib.util.RegexFilter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Container for looking up the {@link ActionContext} to use for a given action.
+ *
+ * <p>Holds {@link ActionContext} mappings populated by {@link ActionContextConsumer} modules. These
+ * include mappings from mnemonics and from description patterns.
+ *
+ * <p>At startup time, the application provides {@link Builder} to each module to register its
+ * contexts and mappings. At runtime, the {@link BlazeExecutor} uses the constructed object to find
+ * the context for each action.
+ */
+public final class SpawnActionContextMaps {
+
+ /** A stored entry for a {@link RegexFilter} to {@link SpawnActionContext} mapping. */
+ @AutoValue
+ public abstract static class RegexFilterSpawnActionContext {
+ public abstract RegexFilter regexFilter();
+
+ public abstract SpawnActionContext spawnActionContext();
+ }
+
+ private final ImmutableSortedMap<String, SpawnActionContext> spawnStrategyMnemonicMap;
+ private final ImmutableList<ActionContext> strategies;
+ private final ImmutableList<RegexFilterSpawnActionContext> spawnStrategyRegexList;
+
+ private SpawnActionContextMaps(
+ ImmutableSortedMap<String, SpawnActionContext> spawnStrategyMnemonicMap,
+ ImmutableList<ActionContext> strategies,
+ ImmutableList<RegexFilterSpawnActionContext> spawnStrategyRegexList) {
+ this.spawnStrategyMnemonicMap = spawnStrategyMnemonicMap;
+ this.strategies = strategies;
+ this.spawnStrategyRegexList = spawnStrategyRegexList;
+ }
+
+ /**
+ * Returns the appropriate {@link ActionContext} to execute an action with the given {@code
+ * mnemonic} and {@link Spawn}.
+ *
+ * <p>If the reason for selecting the context is worth mentioning to the user, logs a message
+ * using the given {@link Reporter}.
+ */
+ public SpawnActionContext getSpawnActionContext(String mnemonic, Spawn spawn, Reporter reporter) {
+ if (!spawnStrategyRegexList.isEmpty() && spawn != null && spawn.getResourceOwner() != null) {
+ String description = spawn.getResourceOwner().getProgressMessage();
+ if (description != null) {
+ for (RegexFilterSpawnActionContext entry : spawnStrategyRegexList) {
+ if (entry.regexFilter().isIncluded(description) && entry.spawnActionContext() != null) {
+ reporter.handle(
+ Event.info(description + " with context " + entry.spawnActionContext().toString()));
+ return entry.spawnActionContext();
+ }
+ }
+ }
+ }
+ SpawnActionContext context = spawnStrategyMnemonicMap.get(mnemonic);
+ if (context != null) {
+ return context;
+ }
+ return spawnStrategyMnemonicMap.get("");
+ }
+
+ /** Returns a map from action context class to its instantiated context object. */
+ public ImmutableMap<Class<? extends ActionContext>, ActionContext> contextMap() {
+ Map<Class<? extends ActionContext>, ActionContext> contextMap = new HashMap<>();
+ for (ActionContext context : strategies) {
+ ExecutionStrategy annotation = context.getClass().getAnnotation(ExecutionStrategy.class);
+ if (annotation != null) {
+ contextMap.put(annotation.contextType(), context);
+ }
+ contextMap.put(context.getClass(), context);
+ }
+ return ImmutableMap.copyOf(contextMap);
+ }
+
+ /** Returns a list of all referenced {@link ActionContext} instances. */
+ public ImmutableList<ActionContext> allContexts() {
+ // We need to keep only the last occurrences of the entries in contextImplementations
+ // (so we respect insertion order but also instantiate them only once).
+ LinkedHashSet<ActionContext> allContexts = new LinkedHashSet<>();
+ allContexts.addAll(strategies);
+ allContexts.addAll(spawnStrategyMnemonicMap.values());
+ spawnStrategyRegexList.forEach(x -> allContexts.add(x.spawnActionContext()));
+ return ImmutableList.copyOf(allContexts);
+ }
+
+ /**
+ * Print a sorted list of our (Spawn)ActionContext maps.
+ *
+ * <p>Prints out debug information about the mappings.
+ */
+ public void debugPrintSpawnActionContextMaps(Reporter reporter) {
+ for (Entry<String, SpawnActionContext> entry : spawnStrategyMnemonicMap.entrySet()) {
+ reporter.handle(
+ Event.info(
+ String.format(
+ "SpawnActionContextMap: \"%s\" = %s",
+ entry.getKey(), entry.getValue().getClass().getSimpleName())));
+ }
+
+ ImmutableMap<Class<? extends ActionContext>, ActionContext> contextMap = contextMap();
+ TreeMap<String, String> sortedContextMapWithSimpleNames = new TreeMap<>();
+ for (Entry<Class<? extends ActionContext>, ActionContext> entry : contextMap.entrySet()) {
+ sortedContextMapWithSimpleNames.put(
+ entry.getKey().getSimpleName(), entry.getValue().getClass().getSimpleName());
+ }
+ for (Entry<String, String> entry : sortedContextMapWithSimpleNames.entrySet()) {
+ // Skip uninteresting identity mappings of contexts.
+ if (!entry.getKey().equals(entry.getValue())) {
+ reporter.handle(
+ Event.info(String.format("ContextMap: %s = %s", entry.getKey(), entry.getValue())));
+ }
+ }
+
+ for (RegexFilterSpawnActionContext entry : spawnStrategyRegexList) {
+ reporter.handle(
+ Event.info(
+ String.format(
+ "SpawnActionContextMap: \"%s\" = %s",
+ entry.regexFilter().toString(),
+ entry.spawnActionContext().getClass().getSimpleName())));
+ }
+ }
+
+ @VisibleForTesting
+ public static SpawnActionContextMaps createStub(
+ List<ActionContext> strategies, Map<String, SpawnActionContext> spawnStrategyMnemonicMap) {
+ return new SpawnActionContextMaps(
+ ImmutableSortedMap.<String, SpawnActionContext>orderedBy(String.CASE_INSENSITIVE_ORDER)
+ .putAll(spawnStrategyMnemonicMap)
+ .build(),
+ ImmutableList.copyOf(strategies),
+ ImmutableList.of());
+ }
+
+ /** A stored entry for a {@link RegexFilter} to {@code strategy} mapping. */
+ @AutoValue
+ public abstract static class RegexFilterStrategy {
+ public abstract RegexFilter regexFilter();
+
+ public abstract String strategy();
+ }
+
+ /** Builder for {@code SpawnActionContextMaps}. */
+ public static final class Builder {
+ private ImmutableListMultimap.Builder<String, String> strategyByMnemonicMapBuilder =
+ ImmutableListMultimap.builder();
+ private ImmutableListMultimap.Builder<Class<? extends ActionContext>, String>
+ strategyByContextMapBuilder = ImmutableListMultimap.builder();
+
+ private final ImmutableList.Builder<RegexFilterStrategy> strategyByRegexpBuilder =
+ new ImmutableList.Builder();
+
+ /*
+ * Returns a builder modules can use to add mappings from mnemonics to strategy names.
+ *
+ * <p>If a spawn action is executed whose mnemonic maps to the empty string or is not present in
+ * the map at all, the choice of the implementation is left to Blaze.
+ *
+ * <p>Matching on mnemonics is done case-insensitively so it is recommended that any
+ * module makes sure that no two strategies refer to the same mnemonic. If they do, Blaze
+ * will pick the last one added.
+ */
+ public ImmutableMultimap.Builder<String, String> strategyByMnemonicMap() {
+ return strategyByMnemonicMapBuilder;
+ }
+
+ /*
+ * Returns a builder modules can use to associate {@link ActionContext} classes with
+ * strategy names.
+ */
+ public ImmutableMultimap.Builder<Class<? extends ActionContext>, String>
+ strategyByContextMap() {
+ return strategyByContextMapBuilder;
+ }
+
+ /** Adds a mapping from the given {@link RegexFilter} to a {@code strategy}. */
+ public void addStrategyByRegexp(RegexFilter regexFilter, String strategy) {
+ strategyByRegexpBuilder.add(
+ new AutoValue_SpawnActionContextMaps_RegexFilterStrategy(regexFilter, strategy));
+ }
+
+ /** Builds a {@link SpawnActionContextMaps} instance. */
+ public SpawnActionContextMaps build(
+ ImmutableList<ActionContextProvider> actionContextProviders, String testStrategyValue)
+ throws ExecutorInitException {
+ StrategyConverter strategyConverter = new StrategyConverter(actionContextProviders);
+
+ ImmutableSortedMap.Builder<String, SpawnActionContext> spawnStrategyMap =
+ ImmutableSortedMap.orderedBy(String.CASE_INSENSITIVE_ORDER);
+ ImmutableList.Builder<ActionContext> strategies = ImmutableList.builder();
+ ImmutableList.Builder<RegexFilterSpawnActionContext> spawnStrategyRegexList =
+ ImmutableList.builder();
+
+ ImmutableListMultimap<String, String> multimap = strategyByMnemonicMapBuilder.build();
+ for (String mnemonic : multimap.keySet()) {
+ String strategy = Iterables.getLast(multimap.get(mnemonic));
+ SpawnActionContext context =
+ strategyConverter.getStrategy(SpawnActionContext.class, strategy);
+ if (context == null) {
+ String strategyOrNull = Strings.emptyToNull(strategy);
+ throw makeExceptionForInvalidStrategyValue(
+ strategy,
+ Joiner.on(' ').skipNulls().join(strategyOrNull, "spawn"),
+ strategyConverter.getValidValues(SpawnActionContext.class));
+ }
+ spawnStrategyMap.put(mnemonic, context);
+ }
+
+ Set<ActionContext> seenContext = new HashSet<>();
+ for (Map.Entry<Class<? extends ActionContext>, String> entry :
+ strategyByContextMapBuilder.orderValuesBy(Collections.reverseOrder()).build().entries()) {
+ ActionContext context = strategyConverter.getStrategy(entry.getKey(), entry.getValue());
+ if (context == null) {
+ throw makeExceptionForInvalidStrategyValue(
+ entry.getValue(),
+ strategyConverter.getUserFriendlyName(entry.getKey()),
+ strategyConverter.getValidValues(entry.getKey()));
+ }
+ if (seenContext.contains(context)) {
+ continue;
+ }
+ seenContext.add(context);
+ strategies.add(context);
+ }
+
+ for (RegexFilterStrategy entry : strategyByRegexpBuilder.build()) {
+ SpawnActionContext context =
+ strategyConverter.getStrategy(SpawnActionContext.class, entry.strategy());
+ if (context == null) {
+ String strategy = Strings.emptyToNull(entry.strategy().toString());
+ throw makeExceptionForInvalidStrategyValue(
+ entry.regexFilter().toString(),
+ Joiner.on(' ').skipNulls().join(strategy, "spawn"),
+ strategyConverter.getValidValues(SpawnActionContext.class));
+ }
+
+ spawnStrategyRegexList.add(
+ new AutoValue_SpawnActionContextMaps_RegexFilterSpawnActionContext(
+ entry.regexFilter(), context));
+ }
+
+ ActionContext context =
+ strategyConverter.getStrategy(TestActionContext.class, testStrategyValue);
+ if (context == null) {
+ throw makeExceptionForInvalidStrategyValue(
+ testStrategyValue, "test", strategyConverter.getValidValues(TestActionContext.class));
+ }
+ strategies.add(context);
+
+ return new SpawnActionContextMaps(
+ spawnStrategyMap.build(), strategies.build(), spawnStrategyRegexList.build());
+ }
+ }
+
+ private static ExecutorInitException makeExceptionForInvalidStrategyValue(
+ String value, String strategy, String validValues) {
+ return new ExecutorInitException(
+ String.format(
+ "'%s' is an invalid value for %s strategy. Valid values are: %s",
+ value, strategy, validValues),
+ ExitCode.COMMAND_LINE_ERROR);
+ }
+
+ private static class StrategyConverter {
+ private Table<Class<? extends ActionContext>, String, ActionContext> classMap =
+ HashBasedTable.create();
+ private Map<Class<? extends ActionContext>, ActionContext> defaultClassMap = new HashMap<>();
+
+ /** Aggregates all {@link ActionContext}s that are in {@code contextProviders}. */
+ @SuppressWarnings("unchecked")
+ private StrategyConverter(Iterable<ActionContextProvider> contextProviders) {
+ for (ActionContextProvider provider : contextProviders) {
+ for (ActionContext strategy : provider.getActionContexts()) {
+ ExecutionStrategy annotation = strategy.getClass().getAnnotation(ExecutionStrategy.class);
+ // TODO(ulfjack): Don't silently ignore action contexts without annotation.
+ if (annotation != null) {
+ defaultClassMap.put(annotation.contextType(), strategy);
+
+ for (String name : annotation.name()) {
+ classMap.put(annotation.contextType(), name, strategy);
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T extends ActionContext> T getStrategy(Class<T> clazz, String name) {
+ return (T) (name.isEmpty() ? defaultClassMap.get(clazz) : classMap.get(clazz, name));
+ }
+
+ private String getValidValues(Class<? extends ActionContext> context) {
+ return Joiner.on(", ").join(Ordering.natural().sortedCopy(classMap.row(context).keySet()));
+ }
+
+ private String getUserFriendlyName(Class<? extends ActionContext> context) {
+ ActionContextMarker marker = context.getAnnotation(ActionContextMarker.class);
+ return marker != null ? marker.name() : context.getSimpleName();
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
index 93134e8..91ff96f 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/StandaloneTestStrategy.java
@@ -348,7 +348,7 @@
long startTime = actionExecutionContext.getClock().currentTimeMillis();
SpawnActionContext spawnActionContext =
- actionExecutionContext.getSpawnActionContext(action.getMnemonic());
+ actionExecutionContext.getSpawnActionContext(action.getMnemonic(), spawn);
List<SpawnResult> spawnResults = ImmutableList.of();
try {
try {
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java b/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java
index 70b0ddd..612b926 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java
@@ -116,16 +116,16 @@
Preconditions.checkState(
actionExecutionContext.getInputPath(inputManifestArtifact).equals(inputManifest));
if (enableRunfiles) {
+ Spawn spawn =
+ createSpawn(
+ owner,
+ actionExecutionContext.getExecRoot(),
+ binTools,
+ shellEnvironment,
+ inputManifestArtifact);
return actionExecutionContext
- .getSpawnActionContext(owner.getMnemonic())
- .exec(
- createSpawn(
- owner,
- actionExecutionContext.getExecRoot(),
- binTools,
- shellEnvironment,
- inputManifestArtifact),
- actionExecutionContext);
+ .getSpawnActionContext(owner.getMnemonic(), spawn)
+ .exec(spawn, actionExecutionContext);
} else {
// Pretend we created the runfiles tree by copying the manifest
try {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
index a0e6c84..2b214ee 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkAction.java
@@ -317,7 +317,7 @@
estimateResourceConsumptionLocal());
return ActionResult.create(
actionExecutionContext
- .getSpawnActionContext(getMnemonic())
+ .getSpawnActionContext(getMnemonic(), spawn)
.exec(spawn, actionExecutionContext));
} catch (ExecException e) {
throw e.toActionExecutionException(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategy.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategy.java
index 2f40917..3f40a10 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategy.java
@@ -63,7 +63,7 @@
List<SpawnResult> spawnResults =
actionExecutionContext
- .getSpawnActionContext(action.getMnemonic())
+ .getSpawnActionContext(action.getMnemonic(), spawn)
.exec(spawn, actionExecutionContext);
return CppCompileActionResult.builder().setSpawnResults(spawnResults).build();
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaHeaderCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaHeaderCompileAction.java
index fca4ffc..51f91c2 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaHeaderCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaHeaderCompileAction.java
@@ -140,9 +140,10 @@
@Override
protected List<SpawnResult> internalExecute(ActionExecutionContext actionExecutionContext)
throws ExecException, InterruptedException {
- SpawnActionContext context = getContext(actionExecutionContext);
+ Spawn spawn = getDirectSpawn();
+ SpawnActionContext context = getContext(actionExecutionContext, spawn);
try {
- return context.exec(getDirectSpawn(), actionExecutionContext);
+ return context.exec(spawn, actionExecutionContext);
} catch (ExecException e) {
// if the direct input spawn failed, try again with transitive inputs to produce better
// better messages
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java
index 64aaf90..08547c30 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java
@@ -16,10 +16,10 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Multimap;
import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.exec.ActionContextConsumer;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
/**
@@ -53,12 +53,8 @@
}
@Override
- public ImmutableMap<String, String> getSpawnActionContexts() {
- return spawnContexts;
- }
-
- @Override
- public Multimap<Class<? extends ActionContext>, String> getActionContexts() {
- return contexts;
+ public void populate(SpawnActionContextMaps.Builder builder) {
+ builder.strategyByMnemonicMap().putAll(spawnContexts.entrySet());
+ builder.strategyByContextMap().putAll(contexts.entries());
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/standalone/StandaloneActionContextConsumer.java b/src/main/java/com/google/devtools/build/lib/standalone/StandaloneActionContextConsumer.java
index 168c596..225b3a3 100644
--- a/src/main/java/com/google/devtools/build/lib/standalone/StandaloneActionContextConsumer.java
+++ b/src/main/java/com/google/devtools/build/lib/standalone/StandaloneActionContextConsumer.java
@@ -14,12 +14,9 @@
package com.google.devtools.build.lib.standalone;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Multimap;
-import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.exec.ActionContextConsumer;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps;
/**
* {@link ActionContextConsumer} that requests the action contexts necessary for standalone
@@ -28,17 +25,13 @@
public class StandaloneActionContextConsumer implements ActionContextConsumer {
@Override
- public ImmutableMap<String, String> getSpawnActionContexts() {
+ public void populate(SpawnActionContextMaps.Builder builder) {
// This makes the "sandboxed" strategy the default Spawn strategy, unless it is overridden by a
// later BlazeModule.
- return ImmutableMap.of("", "standalone");
- }
+ builder.strategyByMnemonicMap().put("", "standalone");
- @Override
- public Multimap<Class<? extends ActionContext>, String> getActionContexts() {
// This makes the "standalone" strategy available via --spawn_strategy=standalone, but it is not
// necessarily the default.
- return ImmutableMultimap.<Class<? extends ActionContext>, String>of(
- SpawnActionContext.class, "standalone");
+ builder.strategyByContextMap().put(SpawnActionContext.class, "standalone");
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerActionContextConsumer.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerActionContextConsumer.java
index 8ff769e..d3c083d 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerActionContextConsumer.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerActionContextConsumer.java
@@ -13,13 +13,9 @@
// limitations under the License.
package com.google.devtools.build.lib.worker;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableMultimap.Builder;
-import com.google.common.collect.Multimap;
-import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.exec.ActionContextConsumer;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps;
/**
* {@link ActionContextConsumer} that requests the action contexts necessary for worker process
@@ -28,15 +24,8 @@
public class WorkerActionContextConsumer implements ActionContextConsumer {
@Override
- public ImmutableMap<String, String> getSpawnActionContexts() {
- return ImmutableMap.of();
- }
-
- @Override
- public Multimap<Class<? extends ActionContext>, String> getActionContexts() {
- Builder<Class<? extends ActionContext>, String> contexts = ImmutableMultimap.builder();
- contexts.put(SpawnActionContext.class, "standalone");
- contexts.put(SpawnActionContext.class, "worker");
- return contexts.build();
+ public void populate(SpawnActionContextMaps.Builder builder) {
+ builder.strategyByContextMap().put(SpawnActionContext.class, "standalone");
+ builder.strategyByContextMap().put(SpawnActionContext.class, "worker");
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 3f55bd0..330c8d7 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -1385,6 +1385,7 @@
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
"//src/main/java/com/google/devtools/common/options",
+ "//src/main/protobuf:test_status_java_proto",
"//third_party:mockito",
"//third_party/protobuf:protobuf_java",
],
diff --git a/src/test/java/com/google/devtools/build/lib/actions/util/DummyExecutor.java b/src/test/java/com/google/devtools/build/lib/actions/util/DummyExecutor.java
index 34f6283..d9d78cd 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/util/DummyExecutor.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/util/DummyExecutor.java
@@ -16,6 +16,7 @@
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.actions.Executor;
+import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.clock.BlazeClock;
import com.google.devtools.build.lib.clock.Clock;
@@ -83,7 +84,7 @@
}
@Override
- public SpawnActionContext getSpawnActionContext(String mnemonic) {
+ public SpawnActionContext getSpawnActionContext(String mnemonic, Spawn spawn) {
throw new UnsupportedOperationException();
}
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SpawnActionContextMapsTest.java b/src/test/java/com/google/devtools/build/lib/exec/SpawnActionContextMapsTest.java
new file mode 100644
index 0000000..8e9f610
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/exec/SpawnActionContextMapsTest.java
@@ -0,0 +1,151 @@
+// 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.exec;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.eventbus.EventBus;
+import com.google.devtools.build.lib.actions.ActionContext;
+import com.google.devtools.build.lib.actions.ActionExecutionContext;
+import com.google.devtools.build.lib.actions.ActionExecutionMetadata;
+import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ExecutionStrategy;
+import com.google.devtools.build.lib.actions.Spawn;
+import com.google.devtools.build.lib.actions.SpawnActionContext;
+import com.google.devtools.build.lib.actions.SpawnResult;
+import com.google.devtools.build.lib.analysis.test.TestActionContext;
+import com.google.devtools.build.lib.analysis.test.TestResult;
+import com.google.devtools.build.lib.analysis.test.TestRunnerAction;
+import com.google.devtools.build.lib.events.Reporter;
+import com.google.devtools.build.lib.testutil.Suite;
+import com.google.devtools.build.lib.testutil.TestSpec;
+import com.google.devtools.build.lib.util.RegexFilter.RegexFilterConverter;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.view.test.TestStatus.TestResultData;
+import java.io.IOException;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+
+/** Tests of {@link SpawnActionContextMaps}. */
+@RunWith(JUnit4.class)
+@TestSpec(size = Suite.SMALL_TESTS)
+public class SpawnActionContextMapsTest {
+
+ private SpawnActionContextMaps.Builder builder;
+ private final RegexFilterConverter converter = new RegexFilterConverter();
+ private final EventBus bus = new EventBus();
+ private final Reporter reporter = new Reporter(bus);
+
+ private static final ImmutableList<ActionContextProvider> PROVIDERS =
+ ImmutableList.of(
+ new ActionContextProvider() {
+ @Override
+ public Iterable<? extends ActionContext> getActionContexts() {
+ return ImmutableList.of(new AC1(), new AC2(), new ACTest());
+ }
+ });
+
+ @Before
+ public void setUp() {
+ builder = new SpawnActionContextMaps.Builder();
+ }
+
+ @Test
+ public void duplicateMnemonics_lastOneWins() throws Exception {
+ builder.strategyByMnemonicMap().put("Spawn1", "ac1").put("Spawn1", "ac2");
+ SpawnActionContextMaps maps = builder.build(PROVIDERS, "actest");
+ SpawnActionContext result = maps.getSpawnActionContext("Spawn1", null, reporter);
+ assertThat(result).isInstanceOf(AC2.class);
+ }
+
+ @Test
+ public void multipleRegexps_firstMatchWins() throws Exception {
+ builder.addStrategyByRegexp(converter.convert("foo"), "ac1");
+ builder.addStrategyByRegexp(converter.convert("foo/bar"), "ac2");
+ SpawnActionContextMaps maps = builder.build(PROVIDERS, "actest");
+
+ SpawnActionContext result =
+ maps.getSpawnActionContext("", mockSpawn("Doing something with foo/bar/baz"), reporter);
+
+ assertThat(result).isInstanceOf(AC1.class);
+ }
+
+ @Test
+ public void regexpAndMnemonic_regexpWins() throws Exception {
+ builder.strategyByMnemonicMap().put("Spawn1", "ac1");
+ builder.addStrategyByRegexp(converter.convert("foo/bar"), "ac2");
+ SpawnActionContextMaps maps = builder.build(PROVIDERS, "actest");
+
+ SpawnActionContext result =
+ maps.getSpawnActionContext(
+ "Spawn1", mockSpawn("Doing something with foo/bar/baz"), reporter);
+
+ assertThat(result).isInstanceOf(AC2.class);
+ }
+
+ @Test
+ public void duplicateContext_noException() throws Exception {
+ builder.strategyByContextMap().put(AC1.class, "one");
+ builder.strategyByContextMap().put(AC1.class, "two");
+ builder.strategyByContextMap().put(AC1.class, "");
+ }
+
+ private Spawn mockSpawn(String message) {
+ Spawn mockSpawn = Mockito.mock(Spawn.class);
+ ActionExecutionMetadata mockOwner = Mockito.mock(ActionExecutionMetadata.class);
+ when(mockOwner.getProgressMessage()).thenReturn(message);
+ when(mockSpawn.getResourceOwner()).thenReturn(mockOwner);
+ return mockSpawn;
+ }
+
+ @ExecutionStrategy(contextType = SpawnActionContext.class, name = "ac1")
+ private static class AC1 implements SpawnActionContext {
+ @Override
+ public List<SpawnResult> exec(Spawn spawn, ActionExecutionContext actionExecutionContext)
+ throws ExecException, InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @ExecutionStrategy(contextType = SpawnActionContext.class, name = "ac2")
+ private static class AC2 implements SpawnActionContext {
+ @Override
+ public List<SpawnResult> exec(Spawn spawn, ActionExecutionContext actionExecutionContext)
+ throws ExecException, InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @ExecutionStrategy(contextType = TestActionContext.class, name = "actest")
+ private static class ACTest implements TestActionContext {
+ @Override
+ public List<SpawnResult> exec(
+ TestRunnerAction action, ActionExecutionContext actionExecutionContext)
+ throws ExecException, InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public TestResult newCachedTestResult(
+ Path execRoot, TestRunnerAction action, TestResultData cached) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java b/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java
index 57ad243..9ebe4aa 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/StandaloneTestStrategyTest.java
@@ -128,7 +128,7 @@
.build();
when(spawnActionContext.exec(any(), any())).thenReturn(ImmutableList.of(expectedSpawnResult));
- when(actionExecutionContext.getSpawnActionContext(any())).thenReturn(spawnActionContext);
+ when(actionExecutionContext.getSpawnActionContext(any(), any())).thenReturn(spawnActionContext);
// actual StandaloneTestStrategy execution
List<SpawnResult> spawnResults =
@@ -208,7 +208,7 @@
expectedSpawnResult,
/*forciblyRunRemotely=*/ false,
/*catastrophe=*/ false));
- when(actionExecutionContext.getSpawnActionContext(any())).thenReturn(spawnActionContext);
+ when(actionExecutionContext.getSpawnActionContext(any(), any())).thenReturn(spawnActionContext);
// actual StandaloneTestStrategy execution
List<SpawnResult> spawnResults =
@@ -285,7 +285,7 @@
SpawnResult expectedSpawnResult = new SpawnResult.Builder().setStatus(Status.SUCCESS).build();
when(spawnActionContext.exec(any(), any())).thenReturn(ImmutableList.of(expectedSpawnResult));
- when(actionExecutionContext.getSpawnActionContext(any())).thenReturn(spawnActionContext);
+ when(actionExecutionContext.getSpawnActionContext(any(), any())).thenReturn(spawnActionContext);
// actual StandaloneTestStrategy execution
List<SpawnResult> spawnResults =
diff --git a/src/test/java/com/google/devtools/build/lib/exec/util/TestExecutorBuilder.java b/src/test/java/com/google/devtools/build/lib/exec/util/TestExecutorBuilder.java
index 70bd8e6..db3214f 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/util/TestExecutorBuilder.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/util/TestExecutorBuilder.java
@@ -14,7 +14,6 @@
package com.google.devtools.build.lib.exec.util;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.ActionContext;
@@ -28,6 +27,7 @@
import com.google.devtools.build.lib.exec.BlazeExecutor;
import com.google.devtools.build.lib.exec.ExecutionOptions;
import com.google.devtools.build.lib.exec.FileWriteStrategy;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps;
import com.google.devtools.build.lib.exec.SymlinkTreeStrategy;
import com.google.devtools.build.lib.runtime.CommonCommandOptions;
import com.google.devtools.build.lib.testutil.TestConstants;
@@ -107,8 +107,7 @@
bus,
BlazeClock.instance(),
optionsParser,
- strategies,
- ImmutableMap.copyOf(spawnStrategyMap),
+ SpawnActionContextMaps.createStub(strategies, spawnStrategyMap),
ImmutableList.<ActionContextProvider>of());
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
index 37fd188..0e93ff2 100644
--- a/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/standalone/StandaloneSpawnStrategyTest.java
@@ -21,7 +21,6 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
-import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionInputPrefetcher;
import com.google.devtools.build.lib.actions.ActionKeyContext;
@@ -44,6 +43,7 @@
import com.google.devtools.build.lib.exec.BlazeExecutor;
import com.google.devtools.build.lib.exec.ExecutionOptions;
import com.google.devtools.build.lib.exec.SingleBuildFileCache;
+import com.google.devtools.build.lib.exec.SpawnActionContextMaps;
import com.google.devtools.build.lib.exec.local.LocalEnvProvider;
import com.google.devtools.build.lib.exec.local.LocalExecutionOptions;
import com.google.devtools.build.lib.exec.local.LocalSpawnRunner;
@@ -134,16 +134,17 @@
bus,
BlazeClock.instance(),
optionsParser,
- ImmutableList.<ActionContext>of(),
- ImmutableMap.<String, SpawnActionContext>of(
- "",
- new StandaloneSpawnStrategy(
- execRoot,
- new LocalSpawnRunner(
+ SpawnActionContextMaps.createStub(
+ ImmutableList.of(),
+ ImmutableMap.<String, SpawnActionContext>of(
+ "",
+ new StandaloneSpawnStrategy(
execRoot,
- localExecutionOptions,
- resourceManager,
- LocalEnvProvider.UNMODIFIED))),
+ new LocalSpawnRunner(
+ execRoot,
+ localExecutionOptions,
+ resourceManager,
+ LocalEnvProvider.UNMODIFIED)))),
ImmutableList.<ActionContextProvider>of());
executor.getExecRoot().createDirectoryAndParents();
@@ -170,14 +171,14 @@
@Test
public void testBinTrueExecutesFine() throws Exception {
Spawn spawn = createSpawn(getTrueCommand());
- executor.getSpawnActionContext(spawn.getMnemonic()).exec(spawn, createContext());
+ executor.getSpawnActionContext(spawn.getMnemonic(), spawn).exec(spawn, createContext());
assertThat(out()).isEmpty();
assertThat(err()).isEmpty();
}
private List<SpawnResult> run(Spawn spawn) throws Exception {
- return executor.getSpawnActionContext(spawn.getMnemonic()).exec(spawn, createContext());
+ return executor.getSpawnActionContext(spawn.getMnemonic(), spawn).exec(spawn, createContext());
}
private ActionExecutionContext createContext() {