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