Return list of planted symlinks.

This makes it easier to test SymlinkForest and may allow other parts of the code to
use the symlinks without doing filesystem i/o.

RELNOTES: None.
PiperOrigin-RevId: 305774740
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java b/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java
index fc62e76..97e3bfc 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java
@@ -15,6 +15,7 @@
 package com.google.devtools.build.lib.buildtool;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Iterables;
@@ -121,7 +122,11 @@
   }
 
   private void plantSymlinkForExternalRepo(
-      RepositoryName repository, Path source, Set<Path> externalRepoLinks) throws IOException {
+      ImmutableList.Builder<Path> plantedSymlinks,
+      RepositoryName repository,
+      Path source,
+      Set<Path> externalRepoLinks)
+      throws IOException {
     // For external repositories, create one symlink to each external repository
     // directory.
     // From <output_base>/execroot/<main repo name>/external/<external repo name>
@@ -139,9 +144,11 @@
       return;
     }
     execrootLink.createSymbolicLink(source);
+    plantedSymlinks.add(execrootLink);
   }
 
-  private void plantSymlinkForestWithFullMainRepository(Path mainRepoRoot) throws IOException {
+  private void plantSymlinkForestWithFullMainRepository(
+      ImmutableList.Builder<Path> plantedSymlinks, Path mainRepoRoot) throws IOException {
     // For the main repo top-level directory, generate symlinks to everything in the directory
     // instead of the directory itself.
     if (siblingRepositoryLayout) {
@@ -159,12 +166,14 @@
           && (siblingRepositoryLayout
               || !baseName.equals(LabelConstants.EXTERNAL_PATH_PREFIX.getBaseName()))) {
         execPath.createSymbolicLink(target);
+        plantedSymlinks.add(execPath);
         // TODO(jingwen-external): is this creating execroot/io_bazel/external?
       }
     }
   }
 
-  private void plantSymlinkForestWithPartialMainRepository(Map<Path, Path> mainRepoLinks)
+  private void plantSymlinkForestWithPartialMainRepository(
+      ImmutableList.Builder<Path> plantedSymlinks, Map<Path, Path> mainRepoLinks)
       throws IOException, AbruptExitException {
     if (siblingRepositoryLayout) {
       execroot.createDirectory();
@@ -180,11 +189,14 @@
             ExitCode.COMMAND_LINE_ERROR);
       }
       link.createSymbolicLink(target);
+      plantedSymlinks.add(link);
     }
   }
 
   private void plantSymlinkForestMultiPackagePath(
-      Map<PackageIdentifier, Root> packageRootsForMainRepo) throws IOException {
+      ImmutableList.Builder<Path> plantedSymlinks,
+      Map<PackageIdentifier, Root> packageRootsForMainRepo)
+      throws IOException {
     // Packages come from exactly one root, but their shared ancestors may come from more.
     Map<PackageIdentifier, Set<Root>> dirRootsMap = Maps.newHashMap();
     // Elements in this list are added so that parents come before their children.
@@ -239,20 +251,16 @@
       if (roots.size() == 1) {
         PathFragment parent = dir.getPackageFragment().getParentDirectory();
         if (!parent.isEmpty() && dirRootsMap.get(createInRepo(dir, parent)).size() == 1) {
-          continue;  // skip--an ancestor will link this one in from above
+          continue; // skip--an ancestor will link this one in from above
         }
         // This is the top-most dir that can be linked to a single root. Make it so.
         Root root = roots.iterator().next(); // lone root in set
+        Path link = execroot.getRelative(dir.getExecPath(siblingRepositoryLayout));
         if (LOG_FINER) {
-          logger.finer(
-              "ln -s "
-                  + root.getRelative(dir.getSourceRoot())
-                  + " "
-                  + execroot.getRelative(dir.getExecPath(siblingRepositoryLayout)));
+          logger.finer("ln -s " + root.getRelative(dir.getSourceRoot()) + " " + link);
         }
-        execroot
-            .getRelative(dir.getExecPath(siblingRepositoryLayout))
-            .createSymbolicLink(root.getRelative(dir.getSourceRoot()));
+        link.createSymbolicLink(root.getRelative(dir.getSourceRoot()));
+        plantedSymlinks.add(link);
       }
     }
     // Make links for dirs within packages, skip parent-only dirs.
@@ -272,15 +280,22 @@
               for (Path target : absdir.getDirectoryEntries()) {
                 PathFragment p = root.relativize(target);
                 if (!dirRootsMap.containsKey(createInRepo(pkgId, p))) {
-                  //LOG.finest("ln -s " + target + " " + linkRoot.getRelative(p));
                   execroot.getRelative(p).createSymbolicLink(target);
+                  plantedSymlinks.add(execroot.getRelative(p));
                 }
               }
             } else {
               logger.fine("Symlink planting skipping dir '" + absdir + "'");
             }
           } catch (IOException e) {
-            e.printStackTrace();
+            // TODO(arostovtsev): Why are we swallowing the IOException here instead of letting it
+            // be thrown?
+            logger.log(
+                Level.WARNING,
+                "I/O error while planting symlinks to contents of '"
+                    + root.getRelative(dir.getSourceRoot())
+                    + "'",
+                e);
           }
           // Otherwise its just an otherwise empty common parent dir.
         }
@@ -306,13 +321,18 @@
         // Create any links that don't exist yet and don't start with bazel-.
         if (!baseName.startsWith(productName + "-") && !execPath.exists()) {
           execPath.createSymbolicLink(target);
+          plantedSymlinks.add(execPath);
         }
       }
     }
   }
 
-  /** Performs the filesystem operations to plant the symlink forest. */
-  public void plantSymlinkForest() throws IOException, AbruptExitException {
+  /**
+   * Performs the filesystem operations to plant the symlink forest.
+   *
+   * @return the symlinks that have been planted
+   */
+  public ImmutableList<Path> plantSymlinkForest() throws IOException, AbruptExitException {
     deleteTreesBelowNotPrefixed(execroot, prefix);
 
     if (siblingRepositoryLayout) {
@@ -329,6 +349,7 @@
     Set<Root> mainRepoRoots = Sets.newLinkedHashSet();
     Set<Path> externalRepoLinks = Sets.newLinkedHashSet();
     Map<PackageIdentifier, Root> packageRootsForMainRepo = Maps.newLinkedHashMap();
+    ImmutableList.Builder<Path> plantedSymlinks = ImmutableList.builder();
 
     for (Map.Entry<PackageIdentifier, Root> entry : packageRoots.entrySet()) {
       PackageIdentifier pkgId = entry.getKey();
@@ -367,6 +388,7 @@
         }
       } else {
         plantSymlinkForExternalRepo(
+            plantedSymlinks,
             repository,
             entry.getValue().getRelative(repository.getSourceRoot()),
             externalRepoLinks);
@@ -384,15 +406,16 @@
                 + "not supported together with --package_path option.",
             ExitCode.COMMAND_LINE_ERROR);
       }
-      plantSymlinkForestMultiPackagePath(packageRootsForMainRepo);
+      plantSymlinkForestMultiPackagePath(plantedSymlinks, packageRootsForMainRepo);
     } else if (shouldLinkAllTopLevelItems) {
       Path mainRepoRoot = Iterables.getOnlyElement(mainRepoRoots).asPath();
-      plantSymlinkForestWithFullMainRepository(mainRepoRoot);
+      plantSymlinkForestWithFullMainRepository(plantedSymlinks, mainRepoRoot);
     } else {
-      plantSymlinkForestWithPartialMainRepository(mainRepoLinks);
+      plantSymlinkForestWithPartialMainRepository(plantedSymlinks, mainRepoLinks);
     }
 
     logger.info("Planted symlink forest in " + execroot);
+    return plantedSymlinks.build();
   }
 
   private static PackageIdentifier createInRepo(
diff --git a/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java b/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java
index ba9bac6..bf4d84c 100644
--- a/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java
+++ b/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 import static org.junit.Assert.assertThrows;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
@@ -42,9 +43,7 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-/**
- * Tests {@link SymlinkForest}.
- */
+/** Tests {@link SymlinkForest}. */
 @RunWith(JUnit4.class)
 public class SymlinkForestTest {
   private FileSystem fileSystem;
@@ -97,7 +96,7 @@
     bDir.createDirectory();
     FileSystemUtils.createEmptyFile(file3);
     innerDir.createDirectory();
-    link1.createSymbolicLink(file4);  // simple symlink
+    link1.createSymbolicLink(file4); // simple symlink
     dirLink.createSymbolicLink(bDir);
     FileSystemUtils.createEmptyFile(file4);
     FileSystemUtils.createEmptyFile(file5);
@@ -108,8 +107,8 @@
     for (String prefix : prefixStrs) {
       prefixes.add(PackageIdentifier.createInMainRepo(prefix));
     }
-    PackageIdentifier longest = SymlinkForest.longestPathPrefix(
-        PackageIdentifier.createInMainRepo(path), prefixes.build());
+    PackageIdentifier longest =
+        SymlinkForest.longestPathPrefix(PackageIdentifier.createInMainRepo(path), prefixes.build());
     return longest != null ? longest.getPackageFragment() : null;
   }
 
@@ -201,13 +200,18 @@
             .put(createPkg(rootA, rootB, "pkgB/pkg"), rootA)
             .put(createPkg(rootA, rootB, "pkgB/pkg/pkg"), rootA)
             .build();
-    createPkg(rootA, rootB, "pkgB/dir");  // create a file in there
+    createPkg(rootA, rootB, "pkgB/dir"); // create a file in there
 
     Path linkRoot = fileSystem.getPath("/linkRoot");
     linkRoot.createDirectoryAndParents();
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of(),
+                false)
+            .plantSymlinkForest();
 
     assertLinksTo(linkRoot, rootA, "pkgA");
     assertIsDir(linkRoot, "dir1");
@@ -222,6 +226,17 @@
     assertLinksTo(linkRoot, rootB, "pkgB/dir/file");
     assertLinksTo(linkRoot, rootA, "pkgB/dir/pkg");
     assertLinksTo(linkRoot, rootA, "pkgB/pkg");
+    assertThat(plantedSymlinks)
+        .containsExactly(
+            linkRoot.getRelative("pkgA"),
+            linkRoot.getRelative("dir1/pkgA"),
+            linkRoot.getRelative("dir1/pkgB"),
+            linkRoot.getRelative("dir2/pkg/file"),
+            linkRoot.getRelative("dir2/pkg/pkg"),
+            linkRoot.getRelative("pkgB/file"),
+            linkRoot.getRelative("pkgB/dir/file"),
+            linkRoot.getRelative("pkgB/dir/pkg"),
+            linkRoot.getRelative("pkgB/pkg"));
   }
 
   @Test
@@ -234,10 +249,17 @@
             .put(createPkg(rootX, rootY, "foo"), rootX)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of(),
+                false)
+            .plantSymlinkForest();
     assertLinksTo(linkRoot, rootX, "file");
+    assertThat(plantedSymlinks)
+        .containsExactly(linkRoot.getRelative("file"), linkRoot.getRelative("foo"));
   }
 
   @Test
@@ -263,9 +285,14 @@
             .put(createExternalPkg(outputBase, "Z", ""), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of(),
+                false)
+            .plantSymlinkForest();
 
     assertLinksTo(linkRoot, mainRepo, "dir_main");
     assertLinksTo(linkRoot, mainRepo, "dir_lib");
@@ -285,6 +312,14 @@
                 .getRelative("Z/file")
                 .exists())
         .isTrue();
+    assertThat(plantedSymlinks)
+        .containsExactly(
+            linkRoot.getRelative("dir_main"),
+            linkRoot.getRelative("dir_lib"),
+            linkRoot.getRelative("file"),
+            linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/X"),
+            linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/Y"),
+            linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/Z"));
   }
 
   @Test
@@ -310,9 +345,10 @@
             .put(createExternalPkg(outputBase, "Z", ""), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), true)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), true)
+            .plantSymlinkForest();
 
     // Expected sibling repository layout (X, Y and Z are siblings of ws_name):
     //
@@ -341,6 +377,14 @@
         outputBase.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/Z"));
     assertThat(linkRoot.getParentDirectory().getRelative("Y/file").exists()).isTrue();
     assertThat(linkRoot.getParentDirectory().getRelative("Z/file").exists()).isTrue();
+    assertThat(plantedSymlinks)
+        .containsExactly(
+            linkRoot.getRelative("dir_main"),
+            linkRoot.getRelative("dir_lib"),
+            linkRoot.getRelative("file"),
+            linkRoot.getParentDirectory().getRelative("X"),
+            linkRoot.getParentDirectory().getRelative("Y"),
+            linkRoot.getParentDirectory().getRelative("Z"));
   }
 
   @Test
@@ -364,9 +408,14 @@
             .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of(),
+                false)
+            .plantSymlinkForest();
 
     assertLinksTo(linkRoot, mainRepo, "dir1");
     assertLinksTo(linkRoot, mainRepo, "dir2");
@@ -376,6 +425,12 @@
     assertThat(linkRoot.getChild("dir4").exists()).isFalse();
     assertThat(linkRoot.getChild("file").exists()).isFalse();
     assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/X");
+    assertThat(plantedSymlinks)
+        .containsExactly(
+            linkRoot.getRelative("dir1"),
+            linkRoot.getRelative("dir2"),
+            linkRoot.getRelative("dir3"),
+            linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/X"));
   }
 
   @Test
@@ -398,15 +453,26 @@
             .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of(),
+                false)
+            .plantSymlinkForest();
 
     assertLinksTo(linkRoot, mainRepo, "dir1");
     assertLinksTo(linkRoot, mainRepo, "dir2");
     assertLinksTo(linkRoot, mainRepo, "dir3");
     assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/X");
     assertThat(outputBase.getRelative("external/foo").exists()).isFalse();
+    assertThat(plantedSymlinks)
+        .containsExactly(
+            linkRoot.getRelative("dir1"),
+            linkRoot.getRelative("dir2"),
+            linkRoot.getRelative("dir3"),
+            linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/X"));
   }
 
   @Test
@@ -430,15 +496,27 @@
             .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of(),
+                false)
+            .plantSymlinkForest();
 
     assertLinksTo(linkRoot, mainRepo, "dir1");
     assertLinksTo(linkRoot, mainRepo, "dir2");
     assertLinksTo(linkRoot, mainRepo, "dir3");
     assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/X");
     assertThat(outputBase.getRelative("external/foo").exists()).isFalse();
+    assertThat(plantedSymlinks)
+        .containsExactly(
+            linkRoot.getRelative("dir1"),
+            linkRoot.getRelative("dir2"),
+            linkRoot.getRelative("dir3"),
+            linkRoot.getRelative("file"),
+            linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/X"));
   }
 
   @Test
@@ -461,9 +539,10 @@
             .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), true)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), true)
+            .plantSymlinkForest();
 
     // Expected output base layout with sibling repositories in the execroot where
     // ws_name and X are siblings:
@@ -496,6 +575,13 @@
         outputBase.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX).getRelative("X"));
 
     assertThat(linkRoot.getRelative("external/foo").exists()).isTrue();
+    // TODO(b/153670299): Change to containsExactly once we clarify external repo symlink semantics.
+    assertThat(plantedSymlinks)
+        .containsAtLeast(
+            linkRoot.getRelative("dir1"),
+            linkRoot.getRelative("dir2"),
+            linkRoot.getRelative("dir3"),
+            linkRoot.getParentDirectory().getRelative("X"));
   }
 
   @Test
@@ -517,9 +603,10 @@
             .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), true)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), true)
+            .plantSymlinkForest();
 
     // Expected output base layout with sibling repositories in the execroot where
     // ws_name and X are siblings:
@@ -542,6 +629,8 @@
         outputBase.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX).getRelative("X"));
 
     assertThat(linkRoot.getRelative("external/foo").exists()).isTrue();
+    // TODO(b/153670299): Change to containsExactly once we clarify external repo symlink semantics.
+    assertThat(plantedSymlinks).contains(linkRoot.getParentDirectory().getRelative("X"));
   }
 
   @Test
@@ -556,10 +645,16 @@
             .put(LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER, root)
             .build();
 
-    new SymlinkForest(
-            packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, ImmutableSortedSet.of(), false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of(),
+                false)
+            .plantSymlinkForest();
     assertThat(linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX).exists()).isFalse();
+    assertThat(plantedSymlinks).isEmpty();
   }
 
   @Test
@@ -582,19 +677,27 @@
             .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap,
-            linkRoot,
-            TestConstants.PRODUCT_NAME,
-            ImmutableSortedSet.of("build"),
-            false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of("build"),
+                false)
+            .plantSymlinkForest();
 
     assertLinksTo(linkRoot, mainRepo, "dir1");
     assertLinksTo(linkRoot, mainRepo, "dir2");
     assertLinksTo(linkRoot, mainRepo, "dir3");
     assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/X");
     assertThat(linkRoot.getChild("build").exists()).isFalse();
+    assertThat(plantedSymlinks)
+        .containsExactly(
+            linkRoot.getRelative("file"),
+            linkRoot.getRelative("dir1"),
+            linkRoot.getRelative("dir2"),
+            linkRoot.getRelative("dir3"),
+            linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/X"));
   }
 
   @Test
@@ -664,13 +767,14 @@
             .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase)
             .build();
 
-    new SymlinkForest(
-            packageRootMap,
-            linkRoot,
-            TestConstants.PRODUCT_NAME,
-            ImmutableSortedSet.of("build"),
-            false)
-        .plantSymlinkForest();
+    ImmutableList<Path> plantedSymlinks =
+        new SymlinkForest(
+                packageRootMap,
+                linkRoot,
+                TestConstants.PRODUCT_NAME,
+                ImmutableSortedSet.of("build"),
+                false)
+            .plantSymlinkForest();
 
     assertLinksTo(linkRoot, mainRepo, "dir1");
     assertLinksTo(linkRoot, mainRepo, "dir2");
@@ -678,6 +782,11 @@
     assertThat(linkRoot.getChild("build").exists()).isFalse();
     // Not part of the package roots.
     assertThat(linkRoot.getChild("dir3").exists()).isFalse();
+    assertThat(plantedSymlinks)
+        .containsExactly(
+            linkRoot.getRelative("dir1"),
+            linkRoot.getRelative("dir2"),
+            linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/X"));
   }
 
   @Test