Support Fileset expansion inside Runfiles during input expansion.

RELNOTES: None
PiperOrigin-RevId: 252954464
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java
index baad566..5f0cc2f 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java
@@ -33,6 +33,7 @@
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.io.IOException;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.SortedMap;
@@ -44,7 +45,7 @@
  * laid out.
  */
 public class SpawnInputExpander {
-  @VisibleForTesting static final ActionInput EMPTY_FILE = new EmptyActionInput("/dev/null");
+  public static final ActionInput EMPTY_FILE = new EmptyActionInput("/dev/null");
 
   private final Path execRoot;
   private final boolean strict;
@@ -133,6 +134,9 @@
                   location.getRelative(((TreeFileArtifact) input).getParentRelativePath()),
                   input);
             }
+          } else if (localArtifact.isFileset()) {
+            addFilesetManifest(
+                location, localArtifact, artifactExpander.getFileset(localArtifact), inputMap);
           } else {
             if (strict) {
               failIfDirectory(actionFileCache, localArtifact);
@@ -146,6 +150,25 @@
     }
   }
 
+  /** Adds runfiles inputs from runfilesSupplier to inputMappings. */
+  public Map<PathFragment, ActionInput> addRunfilesToInputs(
+      RunfilesSupplier runfilesSupplier,
+      MetadataProvider actionFileCache,
+      ArtifactExpander artifactExpander,
+      ArtifactPathResolver pathResolver,
+      boolean expandTreeArtifactsInRunfiles)
+      throws IOException {
+    Map<PathFragment, ActionInput> inputMap = new HashMap<>();
+    addRunfilesToInputs(
+        inputMap,
+        runfilesSupplier,
+        actionFileCache,
+        artifactExpander,
+        pathResolver,
+        expandTreeArtifactsInRunfiles);
+    return inputMap;
+  }
+
   private static void failIfDirectory(MetadataProvider actionFileCache, ActionInput input)
       throws IOException {
     FileArtifactValue metadata = actionFileCache.getMetadata(input);
@@ -160,10 +183,20 @@
       Map<PathFragment, ActionInput> inputMappings)
       throws IOException {
     for (Artifact fileset : filesetMappings.keySet()) {
-      ImmutableList<FilesetOutputSymlink> outputSymlinks = filesetMappings.get(fileset);
-      FilesetManifest filesetManifest =
-          FilesetManifest.constructFilesetManifest(
-              outputSymlinks, fileset.getExecPath(), relSymlinkBehavior);
+      addFilesetManifest(
+          fileset.getExecPath(), fileset, filesetMappings.get(fileset), inputMappings);
+    }
+  }
+
+  void addFilesetManifest(
+      PathFragment location,
+      Artifact filesetArtifact,
+      ImmutableList<FilesetOutputSymlink> filesetLinks,
+      Map<PathFragment, ActionInput> inputMappings)
+      throws IOException {
+    Preconditions.checkState(filesetArtifact.isFileset(), filesetArtifact);
+    FilesetManifest filesetManifest =
+        FilesetManifest.constructFilesetManifest(filesetLinks, location, relSymlinkBehavior);
 
       for (Map.Entry<PathFragment, String> mapping : filesetManifest.getEntries().entrySet()) {
         String value = mapping.getValue();
@@ -173,7 +206,6 @@
                 : ActionInputHelper.fromPath(execRoot.getRelative(value).getPathString());
         addMapping(inputMappings, mapping.getKey(), artifact);
       }
-    }
   }
 
   private void addInputs(
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java b/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
index a9c22c2..153f44e 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
@@ -103,6 +103,43 @@
   }
 
   @Test
+  public void testRunfilesWithFileset() throws Exception {
+    Artifact artifact = createFilesetArtifact("foo/biz/fs_out");
+    Runfiles runfiles = new Runfiles.Builder("workspace").addArtifact(artifact).build();
+    RunfilesSupplier supplier = new RunfilesSupplierImpl(PathFragment.create("runfiles"), runfiles);
+    FakeActionInputFileCache mockCache = new FakeActionInputFileCache();
+    mockCache.put(
+        artifact,
+        FileArtifactValue.createNormalFile(
+            FAKE_DIGEST, /*proxy=*/ null, 0L, /*isShareable=*/ true));
+
+    ArtifactExpander filesetExpander =
+        new ArtifactExpander() {
+          @Override
+          public void expand(Artifact artifact, Collection<? super Artifact> output) {
+            throw new IllegalStateException("Unexpected tree expansion");
+          }
+
+          @Override
+          public ImmutableList<FilesetOutputSymlink> getFileset(Artifact artifact) {
+            return ImmutableList.of(
+                FilesetOutputSymlink.createForTesting(
+                    PathFragment.create("zizz"),
+                    PathFragment.create("/foo/fake_exec/xyz/zizz"),
+                    PathFragment.create("/foo/fake_exec/")));
+          }
+        };
+
+    expander.addRunfilesToInputs(
+        inputMappings, supplier, mockCache, filesetExpander, ArtifactPathResolver.IDENTITY, true);
+    assertThat(inputMappings).hasSize(1);
+    assertThat(inputMappings)
+        .containsEntry(
+            PathFragment.create("runfiles/workspace/foo/biz/fs_out/zizz"),
+            ActionInputHelper.fromPath("/root/xyz/zizz"));
+  }
+
+  @Test
   public void testRunfilesDirectoryStrict() {
     Artifact artifact =
         ActionsTestUtil.createArtifact(
@@ -327,6 +364,15 @@
   }
 
   private SpecialArtifact createTreeArtifact(String relPath) throws IOException {
+    return createSpecialArtifact(relPath, SpecialArtifactType.TREE);
+  }
+
+  private SpecialArtifact createFilesetArtifact(String relPath) throws IOException {
+    return createSpecialArtifact(relPath, SpecialArtifactType.FILESET);
+  }
+
+  private SpecialArtifact createSpecialArtifact(String relPath, SpecialArtifactType type)
+      throws IOException {
     Path outputDir = execRoot.getRelative("out");
     Path outputPath = outputDir.getRelative(relPath);
     outputPath.createDirectoryAndParents();
@@ -335,7 +381,7 @@
         derivedRoot,
         derivedRoot.getExecPath().getRelative(derivedRoot.getRoot().relativize(outputPath)),
         ActionsTestUtil.NULL_ARTIFACT_OWNER,
-        SpecialArtifactType.TREE);
+        type);
   }
 
   @Test