Expand tree artifacts in BEP.

Unconditionally expand the tree artifacts in CompletionFunction so we can resolve them in BEP.

RELNOTES: None
PiperOrigin-RevId: 251454593
diff --git a/src/main/java/com/google/devtools/build/lib/actions/CompletionContext.java b/src/main/java/com/google/devtools/build/lib/actions/CompletionContext.java
new file mode 100644
index 0000000..4c59596
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/CompletionContext.java
@@ -0,0 +1,94 @@
+// Copyright 2019 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.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
+import com.google.devtools.build.lib.actions.Artifact.ArtifactExpanderImpl;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * {@link CompletionContext} contains an {@link ArtifactExpander} and {@link ArtifactPathResolver}
+ * used to resolve output files during a {@link
+ * com.google.devtools.build.lib.skyframe.CompletionFunction} evaluation.
+ */
+@AutoValue
+public abstract class CompletionContext {
+
+  public static final CompletionContext FAILED_COMPLETION_CTX = createNull();
+
+  public abstract ArtifactExpander expander();
+
+  public abstract ArtifactPathResolver pathResolver();
+
+  public static CompletionContext create(
+      Map<Artifact, Collection<Artifact>> expandedArtifacts,
+      Map<Artifact, ImmutableList<FilesetOutputSymlink>> expandedFilesets,
+      ActionInputMap inputMap,
+      PathResolverFactory pathResolverFactory) {
+    ArtifactExpander expander = new ArtifactExpanderImpl(expandedArtifacts, expandedFilesets);
+    ArtifactPathResolver pathResolver =
+        pathResolverFactory.shouldCreatePathResolverForArtifactValues()
+            ? pathResolverFactory.createPathResolverForArtifactValues(
+                inputMap, expandedArtifacts, expandedFilesets.keySet())
+            : ArtifactPathResolver.IDENTITY;
+    return new AutoValue_CompletionContext(expander, pathResolver);
+  }
+
+  private static CompletionContext createNull() {
+    return new AutoValue_CompletionContext((artifact, output) -> {}, ArtifactPathResolver.IDENTITY);
+  }
+
+  public void visitArtifacts(Iterable<Artifact> artifacts, ArtifactReceiver receiver) {
+    // If artifact.isSource(), then note that in LocalFile to avoid stat later?
+    // Better yet, get ArtifactValue to determine the output metadata? If we can pass that
+    // in via local-file, can totally avoid xattr() call on the output files too.
+
+    for (Artifact artifact : artifacts) {
+      if (artifact.isMiddlemanArtifact() || artifact.isFileset()) {
+        // We never want to report middleman artifacts. They are for internal use only.
+        // Filesets are not currently supported, but should be in the future.
+        return;
+      } else if (artifact.isTreeArtifact()) {
+        List<Artifact> expandedArtifacts = new ArrayList<>();
+        expander().expand(artifact, expandedArtifacts);
+        for (Artifact expandedArtifact : expandedArtifacts) {
+          receiver.accept(expandedArtifact);
+        }
+      } else {
+        receiver.accept(artifact);
+      }
+    }
+  }
+
+  /** A function that accepts an {@link Artifact}. */
+  @FunctionalInterface
+  public interface ArtifactReceiver {
+    void accept(Artifact a);
+  }
+
+  /** A factory for {@link ArtifactPathResolver}. */
+  public interface PathResolverFactory {
+    ArtifactPathResolver createPathResolverForArtifactValues(
+        ActionInputMap actionInputMap,
+        Map<Artifact, Collection<Artifact>> expandedArtifacts,
+        Iterable<Artifact> filesets);
+
+    boolean shouldCreatePathResolverForArtifactValues();
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/EventReportingArtifacts.java b/src/main/java/com/google/devtools/build/lib/actions/EventReportingArtifacts.java
index 1df2987..e887535 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/EventReportingArtifacts.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/EventReportingArtifacts.java
@@ -21,15 +21,15 @@
 /** Interface for {@link BuildEvent}s reporting artifacts as named sets */
 public interface EventReportingArtifacts extends BuildEvent {
 
-  /** Pair of artifacts and a path resolver. */
+  /** Pair of artifacts and a @{link CompletionContext}. */
   class ReportedArtifacts {
     public final Collection<NestedSet<Artifact>> artifacts;
-    public final ArtifactPathResolver pathResolver;
+    public final CompletionContext completionContext;
 
     public ReportedArtifacts(
-        Collection<NestedSet<Artifact>> artifacts, ArtifactPathResolver pathResolver) {
+        Collection<NestedSet<Artifact>> artifacts, CompletionContext completionContext) {
       this.artifacts = artifacts;
-      this.pathResolver = pathResolver;
+      this.completionContext = completionContext;
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java
index 4a21b51..007531e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java
@@ -18,7 +18,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ArtifactPathResolver;
+import com.google.devtools.build.lib.actions.CompletionContext;
 import com.google.devtools.build.lib.actions.EventReportingArtifacts;
 import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper.ArtifactsInOutputGroup;
 import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper.ArtifactsToBuild;
@@ -44,14 +44,14 @@
   private final AspectValue aspectValue;
   private final NestedSet<Cause> rootCauses;
   private final Collection<BuildEventId> postedAfter;
-  private final ArtifactPathResolver pathResolver;
+  private final CompletionContext completionContext;
   private final ArtifactsToBuild artifacts;
   private final BuildEventId configurationEventId;
 
   private AspectCompleteEvent(
       AspectValue aspectValue,
       NestedSet<Cause> rootCauses,
-      ArtifactPathResolver pathResolver,
+      CompletionContext completionContext,
       ArtifactsToBuild artifacts,
       BuildEventId configurationEventId) {
     this.aspectValue = aspectValue;
@@ -62,7 +62,7 @@
       postedAfterBuilder.add(BuildEventId.fromCause(cause));
     }
     this.postedAfter = postedAfterBuilder.build();
-    this.pathResolver = pathResolver;
+    this.completionContext = completionContext;
     this.artifacts = artifacts;
     this.configurationEventId = configurationEventId;
   }
@@ -70,10 +70,10 @@
   /** Construct a successful target completion event. */
   public static AspectCompleteEvent createSuccessful(
       AspectValue value,
-      ArtifactPathResolver pathResolver,
+      CompletionContext completionContext,
       ArtifactsToBuild artifacts,
       BuildEventId configurationEventId) {
-    return new AspectCompleteEvent(value, null, pathResolver, artifacts, configurationEventId);
+    return new AspectCompleteEvent(value, null, completionContext, artifacts, configurationEventId);
   }
 
   /**
@@ -83,7 +83,7 @@
       AspectValue value, NestedSet<Cause> rootCauses, BuildEventId configurationEventId) {
     Preconditions.checkArgument(!Iterables.isEmpty(rootCauses));
     return new AspectCompleteEvent(
-        value, rootCauses, ArtifactPathResolver.IDENTITY, null, configurationEventId);
+        value, rootCauses, CompletionContext.FAILED_COMPLETION_CTX, null, configurationEventId);
   }
 
   /**
@@ -131,7 +131,7 @@
         builder.add(artifactsInGroup.getArtifacts());
       }
     }
-    return new ReportedArtifacts(builder.build(), pathResolver);
+    return new ReportedArtifacts(builder.build(), completionContext);
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
index 028c033..1aef540 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
@@ -21,7 +21,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ArtifactPathResolver;
+import com.google.devtools.build.lib.actions.CompletionContext;
 import com.google.devtools.build.lib.actions.EventReportingArtifacts;
 import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper.ArtifactsInOutputGroup;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
@@ -38,6 +38,7 @@
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.File;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.OutputGroup;
+import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TargetComplete;
 import com.google.devtools.build.lib.buildeventstream.BuildEventWithConfiguration;
 import com.google.devtools.build.lib.buildeventstream.BuildEventWithOrderConstraint;
 import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent;
@@ -105,7 +106,7 @@
   private final ConfiguredTargetKey configuredTargetKey;
   private final NestedSet<Cause> rootCauses;
   private final ImmutableList<BuildEventId> postedAfter;
-  private final ArtifactPathResolver pathResolver;
+  private final CompletionContext completionContext;
   private final NestedSet<ArtifactsInOutputGroup> outputs;
   private final NestedSet<Artifact> baselineCoverageArtifacts;
   private final Label aliasLabel;
@@ -126,7 +127,7 @@
   private TargetCompleteEvent(
       ConfiguredTargetAndData targetAndData,
       NestedSet<Cause> rootCauses,
-      ArtifactPathResolver pathResolver,
+      CompletionContext completionContext,
       NestedSet<ArtifactsInOutputGroup> outputs,
       boolean isTest) {
     this.rootCauses =
@@ -148,7 +149,7 @@
       postedAfterBuilder.add(BuildEventId.fromCause(cause));
     }
     this.postedAfter = postedAfterBuilder.build();
-    this.pathResolver = pathResolver;
+    this.completionContext = completionContext;
     this.outputs = outputs;
     this.isTest = isTest;
     this.testTimeoutSeconds = isTest ? getTestTimeoutSeconds(targetAndData) : null;
@@ -201,17 +202,17 @@
   /** Construct a successful target completion event. */
   public static TargetCompleteEvent successfulBuild(
       ConfiguredTargetAndData ct,
-      ArtifactPathResolver pathResolver,
+      CompletionContext completionContext,
       NestedSet<ArtifactsInOutputGroup> outputs) {
-    return new TargetCompleteEvent(ct, null, pathResolver, outputs, false);
+    return new TargetCompleteEvent(ct, null, completionContext, outputs, false);
   }
 
   /** Construct a successful target completion event for a target that will be tested. */
   public static TargetCompleteEvent successfulBuildSchedulingTest(
       ConfiguredTargetAndData ct,
-      ArtifactPathResolver pathResolver,
+      CompletionContext completionContext,
       NestedSet<ArtifactsInOutputGroup> outputs) {
-    return new TargetCompleteEvent(ct, null, pathResolver, outputs, true);
+    return new TargetCompleteEvent(ct, null, completionContext, outputs, true);
   }
 
   /**
@@ -223,7 +224,7 @@
     return new TargetCompleteEvent(
         ct,
         rootCauses,
-        ArtifactPathResolver.IDENTITY,
+        CompletionContext.FAILED_COMPLETION_CTX,
         NestedSetBuilder.emptySet(Order.STABLE_ORDER),
         false);
   }
@@ -293,27 +294,30 @@
   // TODO(aehlig): remove as soon as we managed to get rid of the deprecated "important_output"
   // field.
   private static void addImportantOutputs(
-      ArtifactPathResolver pathResolver,
-      BuildEventStreamProtos.TargetComplete.Builder builder,
+      CompletionContext completionContext,
+      TargetComplete.Builder builder,
       BuildEventContext converters,
       Iterable<Artifact> artifacts) {
     addImportantOutputs(
-        pathResolver, builder, Artifact::getRootRelativePathString, converters, artifacts);
+        completionContext, builder, Artifact::getRootRelativePathString, converters, artifacts);
   }
 
   private static void addImportantOutputs(
-      ArtifactPathResolver pathResolver,
+      CompletionContext completionContext,
       BuildEventStreamProtos.TargetComplete.Builder builder,
       Function<Artifact, String> artifactNameFunction,
       BuildEventContext converters,
       Iterable<Artifact> artifacts) {
-    for (Artifact artifact : artifacts) {
-      String name = artifactNameFunction.apply(artifact);
-      String uri = converters.pathConverter().apply(pathResolver.toPath(artifact));
-      if (uri != null) {
-        builder.addImportantOutput(newFileFromArtifact(name, artifact).setUri(uri).build());
-      }
-    }
+    completionContext.visitArtifacts(
+        artifacts,
+        artifact -> {
+          String name = artifactNameFunction.apply(artifact);
+          String uri =
+              converters.pathConverter().apply(completionContext.pathResolver().toPath(artifact));
+          if (uri != null) {
+            builder.addImportantOutput(newFileFromArtifact(name, artifact).setUri(uri).build());
+          }
+        });
   }
 
   public static BuildEventStreamProtos.File.Builder newFileFromArtifact(
@@ -336,14 +340,19 @@
     ImmutableList.Builder<LocalFile> builder = ImmutableList.builder();
     for (ArtifactsInOutputGroup group : outputs) {
       if (group.areImportant()) {
-        for (Artifact artifact : group.getArtifacts()) {
-          builder.add(new LocalFile(pathResolver.toPath(artifact), LocalFileType.OUTPUT));
-        }
+        completionContext.visitArtifacts(
+            group.getArtifacts(),
+            artifact -> {
+              builder.add(
+                  new LocalFile(
+                      completionContext.pathResolver().toPath(artifact), LocalFileType.OUTPUT));
+            });
       }
     }
     if (baselineCoverageArtifacts != null) {
       for (Artifact artifact : baselineCoverageArtifacts) {
-        builder.add(new LocalFile(pathResolver.toPath(artifact), LocalFileType.OUTPUT));
+        builder.add(
+            new LocalFile(completionContext.pathResolver().toPath(artifact), LocalFileType.OUTPUT));
       }
     }
     return builder.build();
@@ -365,10 +374,11 @@
     // TODO(aehlig): remove direct reporting of artifacts as soon as clients no longer
     // need it.
     if (converters.getOptions().legacyImportantOutputs) {
-      addImportantOutputs(pathResolver, builder, converters, getLegacyFilteredImportantArtifacts());
+      addImportantOutputs(
+          completionContext, builder, converters, getLegacyFilteredImportantArtifacts());
       if (baselineCoverageArtifacts != null) {
         addImportantOutputs(
-            pathResolver,
+            completionContext,
             builder,
             (artifact -> BASELINE_COVERAGE),
             converters,
@@ -396,7 +406,7 @@
     if (baselineCoverageArtifacts != null) {
       builder.add(baselineCoverageArtifacts);
     }
-    return new ReportedArtifacts(builder.build(), pathResolver);
+    return new ReportedArtifacts(builder.build(), completionContext);
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java
index 4dbef12..ea8f387 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java
@@ -30,7 +30,7 @@
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.devtools.build.lib.actions.ActionExecutedEvent;
 import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ArtifactPathResolver;
+import com.google.devtools.build.lib.actions.CompletionContext;
 import com.google.devtools.build.lib.actions.EventReportingArtifacts;
 import com.google.devtools.build.lib.actions.EventReportingArtifacts.ReportedArtifacts;
 import com.google.devtools.build.lib.analysis.BuildInfoEvent;
@@ -371,8 +371,7 @@
     halfCloseFuturesMap = halfCloseFuturesMapBuilder.build();
   }
 
-  private void maybeReportArtifactSet(
-      ArtifactPathResolver pathResolver, NestedSetView<Artifact> view) {
+  private void maybeReportArtifactSet(CompletionContext ctx, NestedSetView<Artifact> view) {
     String name = artifactGroupNamer.maybeName(view);
     if (name == null) {
       return;
@@ -386,13 +385,13 @@
       view = view.splitIfExceedsMaximumSize(besOptions.maxNamedSetEntries);
     }
     for (NestedSetView<Artifact> transitive : view.transitives()) {
-      maybeReportArtifactSet(pathResolver, transitive);
+      maybeReportArtifactSet(ctx, transitive);
     }
-    post(new NamedArtifactGroup(name, pathResolver, view));
+    post(new NamedArtifactGroup(name, ctx, view));
   }
 
-  private void maybeReportArtifactSet(ArtifactPathResolver pathResolver, NestedSet<Artifact> set) {
-    maybeReportArtifactSet(pathResolver, new NestedSetView<Artifact>(set));
+  private void maybeReportArtifactSet(CompletionContext ctx, NestedSet<Artifact> set) {
+    maybeReportArtifactSet(ctx, new NestedSetView<Artifact>(set));
   }
 
   private void maybeReportConfiguration(BuildEvent configuration) {
@@ -458,7 +457,7 @@
     if (event instanceof EventReportingArtifacts) {
       ReportedArtifacts reportedArtifacts = ((EventReportingArtifacts) event).reportedArtifacts();
       for (NestedSet<Artifact> artifactSet : reportedArtifacts.artifacts) {
-        maybeReportArtifactSet(reportedArtifacts.pathResolver, artifactSet);
+        maybeReportArtifactSet(reportedArtifacts.completionContext, artifactSet);
       }
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/NamedArtifactGroup.java b/src/main/java/com/google/devtools/build/lib/runtime/NamedArtifactGroup.java
index 320519a..a324b29 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/NamedArtifactGroup.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/NamedArtifactGroup.java
@@ -19,7 +19,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ArtifactPathResolver;
+import com.google.devtools.build.lib.actions.CompletionContext;
 import com.google.devtools.build.lib.actions.EventReportingArtifacts;
 import com.google.devtools.build.lib.buildeventstream.ArtifactGroupNamer;
 import com.google.devtools.build.lib.buildeventstream.BuildEvent;
@@ -39,12 +39,13 @@
  */
 class NamedArtifactGroup implements BuildEvent {
   private final String name;
-  private final ArtifactPathResolver pathResolver;
+  private final CompletionContext completionContext;
   private final NestedSetView<Artifact> view;
 
-  NamedArtifactGroup(String name, ArtifactPathResolver pathResolver, NestedSetView<Artifact> view) {
+  NamedArtifactGroup(
+      String name, CompletionContext completionContext, NestedSetView<Artifact> view) {
     this.name = name;
-    this.pathResolver = pathResolver;
+    this.completionContext = completionContext;
     this.view = view;
   }
 
@@ -62,12 +63,13 @@
   public Collection<LocalFile> referencedLocalFiles() {
     // This has to be consistent with the code below.
     ImmutableList.Builder<LocalFile> artifacts = ImmutableList.builder();
-    for (Artifact artifact : view.directs()) {
-      if (artifact.isMiddlemanArtifact()) {
-        continue;
-      }
-      artifacts.add(new LocalFile(pathResolver.toPath(artifact), LocalFileType.OUTPUT));
-    }
+    completionContext.visitArtifacts(
+        view.directs(),
+        artifact -> {
+          artifacts.add(
+              new LocalFile(
+                  completionContext.pathResolver().toPath(artifact), LocalFileType.OUTPUT));
+        });
     return artifacts.build();
   }
 
@@ -78,16 +80,14 @@
 
     BuildEventStreamProtos.NamedSetOfFiles.Builder builder =
         BuildEventStreamProtos.NamedSetOfFiles.newBuilder();
-    for (Artifact artifact : view.directs()) {
-      // We never want to report middleman artifacts. They are for internal use only.
-      if (artifact.isMiddlemanArtifact()) {
-        continue;
-      }
-      String uri = pathConverter.apply(pathResolver.toPath(artifact));
-      if (uri != null) {
-        builder.addFiles(newFileFromArtifact(artifact).setUri(uri));
-      }
-    }
+    completionContext.visitArtifacts(
+        view.directs(),
+        artifact -> {
+          String uri = pathConverter.apply(completionContext.pathResolver().toPath(artifact));
+          if (uri != null) {
+            builder.addFiles(newFileFromArtifact(artifact).setUri(uri));
+          }
+        });
     for (NestedSetView<Artifact> child : view.transitives()) {
       builder.addFileSets(namer.apply(child.identifier()));
     }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
index 44a9325..7f4e8b4 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
@@ -17,8 +17,9 @@
 import com.google.devtools.build.lib.actions.ActionExecutionException;
 import com.google.devtools.build.lib.actions.ActionInputMap;
 import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ArtifactPathResolver;
 import com.google.devtools.build.lib.actions.ArtifactSkyKey;
+import com.google.devtools.build.lib.actions.CompletionContext;
+import com.google.devtools.build.lib.actions.CompletionContext.PathResolverFactory;
 import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
 import com.google.devtools.build.lib.actions.MissingInputFileException;
 import com.google.devtools.build.lib.analysis.AspectCompleteEvent;
@@ -54,15 +55,6 @@
 public final class CompletionFunction<TValue extends SkyValue, TResult extends SkyValue>
     implements SkyFunction {
 
-  interface PathResolverFactory {
-    ArtifactPathResolver createPathResolverForArtifactValues(
-        ActionInputMap actionInputMap,
-        Map<Artifact, Collection<Artifact>> expandedArtifacts,
-        Iterable<Artifact> filesets);
-
-    boolean shouldCreatePathResolverForArtifactValues();
-  }
-
   /** A strategy for completing the build. */
   interface Completor<TValue, TResult extends SkyValue> {
 
@@ -107,7 +99,7 @@
     ExtendedEventHandler.Postable createSucceeded(
         SkyKey skyKey,
         TValue value,
-        ArtifactPathResolver pathResolver,
+        CompletionContext completionContext,
         TopLevelArtifactContext topLevelArtifactContext,
         Environment env)
         throws InterruptedException;
@@ -201,7 +193,7 @@
     public ExtendedEventHandler.Postable createSucceeded(
         SkyKey skyKey,
         ConfiguredTargetValue value,
-        ArtifactPathResolver pathResolver,
+        CompletionContext completionContext,
         TopLevelArtifactContext topLevelArtifactContext,
         Environment env)
         throws InterruptedException {
@@ -215,10 +207,14 @@
           TopLevelArtifactHelper.getAllArtifactsToBuild(target, topLevelArtifactContext);
       if (((TargetCompletionKey) skyKey.argument()).willTest()) {
         return TargetCompleteEvent.successfulBuildSchedulingTest(
-            configuredTargetAndData, pathResolver, artifactsToBuild.getAllArtifactsByOutputGroup());
+            configuredTargetAndData,
+            completionContext,
+            artifactsToBuild.getAllArtifactsByOutputGroup());
       } else {
         return TargetCompleteEvent.successfulBuild(
-            configuredTargetAndData, pathResolver, artifactsToBuild.getAllArtifactsByOutputGroup());
+            configuredTargetAndData,
+            completionContext,
+            artifactsToBuild.getAllArtifactsByOutputGroup());
       }
     }
   }
@@ -309,7 +305,7 @@
     public ExtendedEventHandler.Postable createSucceeded(
         SkyKey skyKey,
         AspectValue value,
-        ArtifactPathResolver pathResolver,
+        CompletionContext completionContext,
         TopLevelArtifactContext topLevelArtifactContext,
         Environment env)
         throws InterruptedException {
@@ -322,7 +318,7 @@
       }
 
       return AspectCompleteEvent.createSuccessful(
-          value, pathResolver, artifacts, configurationEventId);
+          value, completionContext, artifacts, configurationEventId);
     }
   }
 
@@ -359,15 +355,9 @@
             MissingInputFileException.class,
             ActionExecutionException.class);
 
-    boolean createPathResolver = pathResolverFactory.shouldCreatePathResolverForArtifactValues();
-    ActionInputMap inputMap = null;
-    Map<Artifact, Collection<Artifact>> expandedArtifacts = null;
-    Map<Artifact, ImmutableList<FilesetOutputSymlink>> expandedFilesets = null;
-    if (createPathResolver) {
-      inputMap = new ActionInputMap(inputDeps.size());
-      expandedArtifacts = new HashMap<>();
-      expandedFilesets = new HashMap<>();
-    }
+    ActionInputMap inputMap = new ActionInputMap(inputDeps.size());
+    Map<Artifact, Collection<Artifact>> expandedArtifacts = new HashMap<>();
+    Map<Artifact, ImmutableList<FilesetOutputSymlink>> expandedFilesets = new HashMap<>();
 
     int missingCount = 0;
     ActionExecutionException firstActionExecutionException = null;
@@ -378,7 +368,7 @@
       Artifact input = ArtifactSkyKey.artifact(depsEntry.getKey());
       try {
         SkyValue artifactValue = depsEntry.getValue().get();
-        if (createPathResolver && artifactValue != null) {
+        if (artifactValue != null) {
           ActionInputMapHelper.addToMap(
               inputMap,
               expandedArtifacts,
@@ -437,14 +427,12 @@
       return null;
     }
 
-    ArtifactPathResolver pathResolver =
-        createPathResolver
-            ? pathResolverFactory.createPathResolverForArtifactValues(
-                inputMap, expandedArtifacts, expandedFilesets.keySet())
-            : ArtifactPathResolver.IDENTITY;
+    CompletionContext ctx =
+        CompletionContext.create(
+            expandedArtifacts, expandedFilesets, inputMap, pathResolverFactory);
 
     ExtendedEventHandler.Postable postable =
-        completor.createSucceeded(skyKey, value, pathResolver, topLevelContext, env);
+        completor.createSucceeded(skyKey, value, ctx, topLevelContext, env);
     if (postable == null) {
       return null;
     }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index e6107b5..e79dac8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -53,6 +53,7 @@
 import com.google.devtools.build.lib.actions.ArtifactResolver.ArtifactResolverSupplier;
 import com.google.devtools.build.lib.actions.ArtifactRoot;
 import com.google.devtools.build.lib.actions.CommandLineExpansionException;
+import com.google.devtools.build.lib.actions.CompletionContext.PathResolverFactory;
 import com.google.devtools.build.lib.actions.EnvironmentalExecException;
 import com.google.devtools.build.lib.actions.Executor;
 import com.google.devtools.build.lib.actions.FileStateType;
@@ -134,7 +135,6 @@
 import com.google.devtools.build.lib.rules.repository.ResolvedHashesFunction;
 import com.google.devtools.build.lib.runtime.KeepGoingOption;
 import com.google.devtools.build.lib.skyframe.AspectValue.AspectValueKey;
-import com.google.devtools.build.lib.skyframe.CompletionFunction.PathResolverFactory;
 import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.FileDirtinessChecker;
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
 import com.google.devtools.build.lib.skyframe.FileFunction.NonexistentFileReceiver;
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/TargetCompleteEventTest.java b/src/test/java/com/google/devtools/build/lib/analysis/TargetCompleteEventTest.java
index ea48bb3..77b4a08 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/TargetCompleteEventTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/TargetCompleteEventTest.java
@@ -17,7 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.actions.ArtifactPathResolver;
+import com.google.devtools.build.lib.actions.CompletionContext;
 import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper.ArtifactsToBuild;
 import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
 import com.google.devtools.build.lib.buildeventstream.BuildEvent;
@@ -49,7 +49,7 @@
     TargetCompleteEvent event =
         TargetCompleteEvent.successfulBuild(
             ctAndData,
-            ArtifactPathResolver.IDENTITY,
+            CompletionContext.FAILED_COMPLETION_CTX,
             artifactsToBuild.getAllArtifactsByOutputGroup());
     assertThat(event.referencedLocalFiles())
         .contains(
diff --git a/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java b/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java
index be5cc35..b66080b 100644
--- a/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerTest.java
@@ -30,8 +30,8 @@
 import com.google.devtools.build.lib.actions.ActionExecutedEvent.ErrorTiming;
 import com.google.devtools.build.lib.actions.ActionExecutionException;
 import com.google.devtools.build.lib.actions.Artifact;
-import com.google.devtools.build.lib.actions.ArtifactPathResolver;
 import com.google.devtools.build.lib.actions.ArtifactRoot;
+import com.google.devtools.build.lib.actions.CompletionContext;
 import com.google.devtools.build.lib.actions.EventReportingArtifacts;
 import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
@@ -253,7 +253,7 @@
 
     @Override
     public ReportedArtifacts reportedArtifacts() {
-      return new ReportedArtifacts(artifacts, ArtifactPathResolver.IDENTITY);
+      return new ReportedArtifacts(artifacts, CompletionContext.FAILED_COMPLETION_CTX);
     }
 
     @Override