Add ActionKeyContext to Action#getKey.

This key context can be used by actions to share partial key computations, for instance when computing MD5s for nested sets.

RELNOTES: None
PiperOrigin-RevId: 177359607
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
index 5483581..1ad13c1 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
@@ -23,6 +23,7 @@
 import com.google.common.eventbus.SubscriberExceptionHandler;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.Uninterruptibles;
+import com.google.devtools.build.lib.actions.ActionKeyContext;
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
 import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
@@ -149,6 +150,7 @@
   private final SubscriberExceptionHandler eventBusExceptionHandler;
   private final String productName;
   private final PathConverter pathToUriConverter;
+  private final ActionKeyContext actionKeyContext;
 
   // Workspace state (currently exactly one workspace per server)
   private BlazeWorkspace workspace;
@@ -162,6 +164,7 @@
       ConfiguredRuleClassProvider ruleClassProvider,
       ImmutableList<ConfigurationFragmentFactory> configurationFragmentFactories,
       ImmutableMap<String, InfoItem> infoItems,
+      ActionKeyContext actionKeyContext,
       Clock clock,
       Runnable abruptShutdownHandler,
       OptionsProvider startupOptionsProvider,
@@ -184,6 +187,7 @@
     this.ruleClassProvider = ruleClassProvider;
     this.configurationFragmentFactories = configurationFragmentFactories;
     this.infoItems = infoItems;
+    this.actionKeyContext = actionKeyContext;
     this.clock = clock;
     this.abruptShutdownHandler = abruptShutdownHandler;
     this.startupOptionsProvider = startupOptionsProvider;
@@ -294,6 +298,10 @@
     return workspace;
   }
 
+  public ActionKeyContext getActionKeyContext() {
+    return actionKeyContext;
+  }
+
   /**
    * The directory in which blaze stores the server state - that is, the socket
    * file and a log.
@@ -440,6 +448,8 @@
       env.getReporter().handle(Event.error("Error while writing profile file: " + e.getMessage()));
     }
     env.getReporter().clearEventBus();
+
+    actionKeyContext.clear();
   }
 
   // Make sure we keep a strong reference to this logger, so that the
@@ -971,6 +981,7 @@
             .setProductName(productName)
             .setFileSystem(fs)
             .setServerDirectories(serverDirectories)
+            .setActionKeyContext(new ActionKeyContext())
             .setStartupOptionsProvider(options)
             .setClock(clock)
             .setAbruptShutdownHandler(abruptShutdownHandler)
@@ -1122,11 +1133,14 @@
     private SubscriberExceptionHandler eventBusExceptionHandler = new RemoteExceptionHandler();
     private UUID instanceId;
     private String productName;
+    private ActionKeyContext actionKeyContext;
 
     public BlazeRuntime build() throws AbruptExitException {
       Preconditions.checkNotNull(productName);
       Preconditions.checkNotNull(serverDirectories);
       Preconditions.checkNotNull(startupOptionsProvider);
+      ActionKeyContext actionKeyContext =
+          this.actionKeyContext != null ? this.actionKeyContext : new ActionKeyContext();
       Clock clock = (this.clock == null) ? BlazeClock.instance() : this.clock;
       UUID instanceId =  (this.instanceId == null) ? UUID.randomUUID() : this.instanceId;
 
@@ -1197,6 +1211,7 @@
           ruleClassProvider,
           ruleClassProvider.getConfigurationFragments(),
           serverBuilder.getInfoItems(),
+          actionKeyContext,
           clock,
           abruptShutdownHandler,
           startupOptionsProvider,
@@ -1255,5 +1270,10 @@
       this.eventBusExceptionHandler = eventBusExceptionHandler;
       return this;
     }
+
+    public Builder setActionKeyContext(ActionKeyContext actionKeyContext) {
+      this.actionKeyContext = actionKeyContext;
+      return this;
+    }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuildSummaryStatsModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BuildSummaryStatsModule.java
index b5ea61d..6423f94 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BuildSummaryStatsModule.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BuildSummaryStatsModule.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.eventbus.EventBus;
 import com.google.common.eventbus.Subscribe;
+import com.google.devtools.build.lib.actions.ActionKeyContext;
 import com.google.devtools.build.lib.buildeventstream.BuildToolLogs;
 import com.google.devtools.build.lib.buildtool.BuildRequest;
 import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
@@ -41,6 +42,7 @@
 
   private static final Logger logger = Logger.getLogger(BuildSummaryStatsModule.class.getName());
 
+  private ActionKeyContext actionKeyContext;
   private SimpleCriticalPathComputer criticalPathComputer;
   private EventBus eventBus;
   private Reporter reporter;
@@ -51,6 +53,7 @@
   public void beforeCommand(CommandEnvironment env) {
     this.reporter = env.getReporter();
     this.eventBus = env.getEventBus();
+    this.actionKeyContext = env.getSkyframeExecutor().getActionKeyContext();
     eventBus.register(this);
   }
 
@@ -70,7 +73,8 @@
   @Subscribe
   public void executionPhaseStarting(ExecutionStartingEvent event) {
     if (enabled) {
-      criticalPathComputer = new SimpleCriticalPathComputer(BlazeClock.instance(), discardActions);
+      criticalPathComputer =
+          new SimpleCriticalPathComputer(actionKeyContext, BlazeClock.instance(), discardActions);
       eventBus.register(criticalPathComputer);
     }
   }
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/CriticalPathComputer.java b/src/main/java/com/google/devtools/build/lib/runtime/CriticalPathComputer.java
index 0b83b50..1c6ce03 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/CriticalPathComputer.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/CriticalPathComputer.java
@@ -21,6 +21,7 @@
 import com.google.common.eventbus.Subscribe;
 import com.google.devtools.build.lib.actions.Action;
 import com.google.devtools.build.lib.actions.ActionCompletionEvent;
+import com.google.devtools.build.lib.actions.ActionKeyContext;
 import com.google.devtools.build.lib.actions.ActionMiddlemanEvent;
 import com.google.devtools.build.lib.actions.ActionStartedEvent;
 import com.google.devtools.build.lib.actions.Actions;
@@ -48,6 +49,7 @@
   static final int SLOWEST_COMPONENTS_SIZE = 30;
   // outputArtifactToComponent is accessed from multiple event handlers.
   protected final ConcurrentMap<Artifact, C> outputArtifactToComponent = Maps.newConcurrentMap();
+  private final ActionKeyContext actionKeyContext;
 
   /** Maximum critical path found. */
   private C maxCriticalPath;
@@ -71,7 +73,9 @@
 
   private final Object lock = new Object();
 
-  protected CriticalPathComputer(Clock clock, boolean discardActions) {
+  protected CriticalPathComputer(
+      ActionKeyContext actionKeyContext, Clock clock, boolean discardActions) {
+    this.actionKeyContext = actionKeyContext;
     this.clock = clock;
     this.discardActions = discardActions;
     maxCriticalPath = null;
@@ -134,7 +138,7 @@
     if (storedComponent != null) {
       Action oldAction = storedComponent.maybeGetAction();
       if (oldAction != null) {
-        if (!Actions.canBeShared(newAction, oldAction)) {
+        if (!Actions.canBeShared(actionKeyContext, newAction, oldAction)) {
           throw new IllegalStateException(
               "Duplicate output artifact found for unsharable actions."
                   + "This can happen if a previous event registered the action.\n"
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/SimpleCriticalPathComputer.java b/src/main/java/com/google/devtools/build/lib/runtime/SimpleCriticalPathComputer.java
index bb855ed..482c55f 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/SimpleCriticalPathComputer.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/SimpleCriticalPathComputer.java
@@ -15,6 +15,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.ActionKeyContext;
 import com.google.devtools.build.lib.clock.Clock;
 
 /**
@@ -24,8 +25,9 @@
     extends CriticalPathComputer<SimpleCriticalPathComponent,
         AggregatedCriticalPath<SimpleCriticalPathComponent>> {
 
-  SimpleCriticalPathComputer(Clock clock, boolean discardActions) {
-    super(clock, discardActions);
+  SimpleCriticalPathComputer(
+      ActionKeyContext actionKeyContext, Clock clock, boolean discardActions) {
+    super(actionKeyContext, clock, discardActions);
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/WorkspaceBuilder.java b/src/main/java/com/google/devtools/build/lib/runtime/WorkspaceBuilder.java
index 4ae356d..c0460af 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/WorkspaceBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/WorkspaceBuilder.java
@@ -73,6 +73,7 @@
             packageFactory,
             runtime.getFileSystem(),
             directories,
+            runtime.getActionKeyContext(),
             workspaceStatusActionFactory,
             ruleClassProvider.getBuildInfoFactories(),
             diffAwarenessFactories.build(),
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
index f9bbde4..465f29e 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/PrintActionCommand.java
@@ -19,6 +19,7 @@
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
 import com.google.devtools.build.lib.actions.ActionGraph;
+import com.google.devtools.build.lib.actions.ActionKeyContext;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.CommandLineExpansionException;
 import com.google.devtools.build.lib.actions.extra.DetailedExtraActionInfo;
@@ -179,9 +180,14 @@
         if (!filesToCompile.isEmpty()) {
           try {
             if (compileOneDependency) {
-              gatherActionsForFiles(configuredTarget, actionGraph, targets);
+              gatherActionsForFiles(
+                  configuredTarget,
+                  actionGraph,
+                  env.getSkyframeExecutor().getActionKeyContext(),
+                  targets);
             } else {
-              gatherActionsForTarget(configuredTarget, actionGraph);
+              gatherActionsForTarget(
+                  configuredTarget, actionGraph, env.getSkyframeExecutor().getActionKeyContext());
             }
           } catch (CommandLineExpansionException e) {
             env.getReporter().handle(Event.error(null, "Error expanding command line: " + e));
@@ -199,16 +205,22 @@
     }
 
     private BuildResult gatherActionsForFiles(
-        ConfiguredTarget configuredTarget, ActionGraph actionGraph, List<String> files)
+        ConfiguredTarget configuredTarget,
+        ActionGraph actionGraph,
+        ActionKeyContext actionKeyContext,
+        List<String> files)
         throws CommandLineExpansionException {
       Set<String> filesDesired = new LinkedHashSet<>(files);
       ActionFilter filter = new DefaultActionFilter(filesDesired, actionMnemonicMatcher);
 
-      gatherActionsForFile(configuredTarget, filter, actionGraph);
+      gatherActionsForFile(configuredTarget, filter, actionGraph, actionKeyContext);
       return null;
     }
 
-    private void gatherActionsForTarget(ConfiguredTarget configuredTarget, ActionGraph actionGraph)
+    private void gatherActionsForTarget(
+        ConfiguredTarget configuredTarget,
+        ActionGraph actionGraph,
+        ActionKeyContext actionKeyContext)
         throws CommandLineExpansionException {
       if (!(configuredTarget.getTarget() instanceof Rule)) {
         return;
@@ -227,7 +239,7 @@
       for (ActionAnalysisMetadata action : actions) {
         if (action instanceof Action) {
           DetailedExtraActionInfo.Builder detail = DetailedExtraActionInfo.newBuilder();
-          detail.setAction(((Action) action).getExtraActionInfo());
+          detail.setAction(((Action) action).getExtraActionInfo(actionKeyContext));
           summaryBuilder.addAction(detail);
         }
       }
@@ -238,7 +250,10 @@
      * extra_action if the filter evaluates to {@code true}.
      */
     private void gatherActionsForFile(
-        ConfiguredTarget configuredTarget, ActionFilter filter, ActionGraph actionGraph)
+        ConfiguredTarget configuredTarget,
+        ActionFilter filter,
+        ActionGraph actionGraph,
+        ActionKeyContext actionKeyContext)
         throws CommandLineExpansionException {
       NestedSet<Artifact> artifacts = OutputGroupProvider.get(configuredTarget)
           .getOutputGroup(OutputGroupProvider.FILES_TO_COMPILE);
@@ -252,7 +267,7 @@
         if (filter.shouldOutput(action, configuredTarget, actionGraph)) {
           if (action instanceof Action) {
             DetailedExtraActionInfo.Builder detail = DetailedExtraActionInfo.newBuilder();
-            detail.setAction(((Action) action).getExtraActionInfo());
+            detail.setAction(((Action) action).getExtraActionInfo(actionKeyContext));
             summaryBuilder.addAction(detail);
           }
         }