diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java index ca4e494cf85c83..187bead2246eca 100644 --- a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java +++ b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java @@ -453,8 +453,7 @@ private void prepare(PackageRoots packageRoots) // Plant the symlink forest. try (SilentCloseable c = Profiler.instance().profile("plantSymlinkForest")) { - new SymlinkForest( - packageRootMap.get(), getExecRoot(), runtime.getProductName(), env.getWorkspaceName()) + new SymlinkForest(packageRootMap.get(), getExecRoot(), runtime.getProductName()) .plantSymlinkForest(); } catch (IOException e) { throw new ExecutorInitException("Source forest creation failed", e); 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 59757857892d8d..0d70cb6b54748c 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 @@ -16,251 +16,116 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.devtools.build.lib.cmdline.LabelConstants; import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.concurrent.ThreadSafety; -import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.Root; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; import java.util.Map; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Creates a symlink forest based on a package path map. */ class SymlinkForest { - - private static final Logger logger = Logger.getLogger(SymlinkForest.class.getName()); - private static final boolean LOG_FINER = logger.isLoggable(Level.FINER); - private final ImmutableMap packageRoots; private final Path execroot; - private final String workspaceName; - private final String productName; - private final String[] prefixes; + private final String prefix; SymlinkForest( - ImmutableMap packageRoots, - Path execroot, - String productName, - String workspaceName) { + ImmutableMap packageRoots, Path execroot, String productName) { this.packageRoots = packageRoots; this.execroot = execroot; - this.workspaceName = workspaceName; - this.productName = productName; - this.prefixes = new String[] { ".", "_", productName + "-"}; + this.prefix = productName + "-"; } /** - * Returns the longest prefix from a given set of 'prefixes' that are - * contained in 'path'. I.e the closest ancestor directory containing path. - * Returns null if none found. - * @param path - * @param prefixes - */ - @VisibleForTesting - static PackageIdentifier longestPathPrefix( - PackageIdentifier path, ImmutableSet prefixes) { - for (int i = path.getPackageFragment().segmentCount(); i >= 0; i--) { - PackageIdentifier prefix = createInRepo(path, path.getPackageFragment().subFragment(0, i)); - if (prefixes.contains(prefix)) { - return prefix; - } - } - return null; - } - - /** - * Delete all dir trees under a given 'dir' that don't start with one of a set - * of given 'prefixes'. Does not follow any symbolic links. + * Delete all dir trees under a given 'dir' that don't start with a given 'prefix'. Does not + * follow any symbolic links. */ @VisibleForTesting @ThreadSafety.ThreadSafe - static void deleteTreesBelowNotPrefixed(Path dir, String[] prefixes) throws IOException { - dirloop: + static void deleteTreesBelowNotPrefixed(Path dir, String prefix) throws IOException { for (Path p : dir.getDirectoryEntries()) { - String name = p.getBaseName(); - for (String prefix : prefixes) { - if (name.startsWith(prefix)) { - continue dirloop; - } + if (!p.getBaseName().startsWith(prefix)) { + p.deleteTree(); } - p.deleteTree(); } } + /** + * Plant a symlink forest under execution root to ensure sources file are available and up to + * date. For the main repo: If root package ("//:") is used, link every file and directory under + * the top-level directory of the main repo. Otherwise, we only link the directories that are used + * in presented main repo packages. For every external repo: make a such a directory link: + * //external/ --> /external/ + */ void plantSymlinkForest() throws IOException { - deleteTreesBelowNotPrefixed(execroot, prefixes); - // TODO(kchodorow): this can be removed once the execution root is rearranged. - // Current state: symlink tree was created under execroot/$(basename ws) and then - // execroot/wsname is symlinked to that. The execution root change creates (and cleans up) - // subtrees for each repository and has been rolled forward and back several times. Thus, if - // someone was using a with-execroot-change version of bazel and then switched to this one, - // their execution root would contain a subtree for execroot/wsname that would never be - // cleaned up by this version of Bazel. - Path realWorkspaceDir = execroot.getParentDirectory().getRelative(workspaceName); - if (!workspaceName.equals(execroot.getBaseName()) && realWorkspaceDir.exists() - && !realWorkspaceDir.isSymbolicLink()) { - realWorkspaceDir.deleteTree(); - } + deleteTreesBelowNotPrefixed(execroot, prefix); + + Path mainRepoRoot = null; + Map mainRepoLinks = Maps.newHashMap(); + Set externalRepoLinks = Sets.newHashSet(); - // Packages come from exactly one root, but their shared ancestors may come from more. - Map> dirRootsMap = Maps.newHashMap(); - // Elements in this list are added so that parents come before their children. - ArrayList dirsParentsFirst = new ArrayList<>(); for (Map.Entry entry : packageRoots.entrySet()) { PackageIdentifier pkgId = entry.getKey(); if (pkgId.equals(LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER)) { // This isn't a "real" package, don't add it to the symlink tree. continue; } - Root pkgRoot = entry.getValue(); - ArrayList newDirs = new ArrayList<>(); - for (PathFragment fragment = pkgId.getPackageFragment(); - !fragment.isEmpty(); - fragment = fragment.getParentDirectory()) { - PackageIdentifier dirId = createInRepo(pkgId, fragment); - Set roots = dirRootsMap.get(dirId); - if (roots == null) { - roots = Sets.newHashSet(); - dirRootsMap.put(dirId, roots); - newDirs.add(dirId); + RepositoryName repository = pkgId.getRepository(); + if (repository.isMain() || repository.isDefault()) { + // If root package of the main repo is required, we record the main repo root so that + // we can later link everything under main repo's top-level directory. And in this case, + // we don't need to record other links for directories under the top-level directory any + // more. + if (pkgId.getPackageFragment().equals(PathFragment.EMPTY_FRAGMENT)) { + mainRepoRoot = entry.getValue().getRelative(pkgId.getSourceRoot()); } - roots.add(pkgRoot); - } - Collections.reverse(newDirs); - dirsParentsFirst.addAll(newDirs); - } - // Now add in roots for all non-pkg dirs that are in between two packages, and missed above. - for (PackageIdentifier dir : dirsParentsFirst) { - if (!packageRoots.containsKey(dir)) { - PackageIdentifier pkgId = longestPathPrefix(dir, packageRoots.keySet()); - if (pkgId != null) { - dirRootsMap.get(dir).add(packageRoots.get(pkgId)); + if (mainRepoRoot == null) { + Path execrootLink = execroot.getRelative(pkgId.getPackageFragment().getSegment(0)); + Path sourcePath = entry.getValue().getRelative(pkgId.getSourceRoot().getSegment(0)); + mainRepoLinks.putIfAbsent(execrootLink, sourcePath); } - } - } - // Create output dirs for all dirs that have more than one root and need to be split. - for (PackageIdentifier dir : dirsParentsFirst) { - if (!dir.getRepository().isMain()) { - FileSystemUtils.createDirectoryAndParents( - execroot.getRelative(dir.getRepository().getPathUnderExecRoot())); - } - if (dirRootsMap.get(dir).size() > 1) { - if (LOG_FINER) { - logger.finer("mkdir " + execroot.getRelative(dir.getPathUnderExecRoot())); + } else { + // For other external repositories, generate a symlink to the external repository + // directory itself. + // /execroot/
/external/ --> + // /external/ + Path execrootLink = execroot.getRelative(repository.getPathUnderExecRoot()); + Path sourcePath = entry.getValue().getRelative(repository.getSourceRoot()); + if (externalRepoLinks.contains(execrootLink)) { + continue; } - FileSystemUtils.createDirectoryAndParents( - execroot.getRelative(dir.getPathUnderExecRoot())); - } - } - - // Make dir links for single rooted dirs. - for (PackageIdentifier dir : dirsParentsFirst) { - Set roots = dirRootsMap.get(dir); - // Simple case of one root for this dir. - 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 - } - // 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 - if (LOG_FINER) { - logger.finer( - "ln -s " - + root.getRelative(dir.getSourceRoot()) - + " " - + execroot.getRelative(dir.getPathUnderExecRoot())); - } - execroot.getRelative(dir.getPathUnderExecRoot()) - .createSymbolicLink(root.getRelative(dir.getSourceRoot())); - } - } - // Make links for dirs within packages, skip parent-only dirs. - for (PackageIdentifier dir : dirsParentsFirst) { - if (dirRootsMap.get(dir).size() > 1) { - // If this dir is at or below a package dir, link in its contents. - PackageIdentifier pkgId = longestPathPrefix(dir, packageRoots.keySet()); - if (pkgId != null) { - Root root = packageRoots.get(pkgId); - try { - Path absdir = root.getRelative(dir.getSourceRoot()); - if (absdir.isDirectory()) { - if (LOG_FINER) { - logger.finer( - "ln -s " + absdir + "/* " + execroot.getRelative(dir.getSourceRoot()) + "/"); - } - 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); - } - } - } else { - logger.fine("Symlink planting skipping dir '" + absdir + "'"); - } - } catch (IOException e) { - e.printStackTrace(); - } - // Otherwise its just an otherwise empty common parent dir. + if (externalRepoLinks.isEmpty()) { + execroot.getRelative(LabelConstants.EXTERNAL_PACKAGE_NAME).createDirectoryAndParents(); } + externalRepoLinks.add(execrootLink); + execrootLink.createSymbolicLink(sourcePath); } } - - for (Map.Entry entry : packageRoots.entrySet()) { - PackageIdentifier pkgId = entry.getKey(); - if (!pkgId.getPackageFragment().equals(PathFragment.EMPTY_FRAGMENT)) { - continue; - } - Path execrootDirectory = execroot.getRelative(pkgId.getPathUnderExecRoot()); - // If there were no subpackages, this directory might not exist yet. - if (!execrootDirectory.exists()) { - FileSystemUtils.createDirectoryAndParents(execrootDirectory); - } - // For the top-level directory, generate symlinks to everything in the directory instead of - // the directory itself. - Path sourceDirectory = entry.getValue().getRelative(pkgId.getSourceRoot()); - for (Path target : sourceDirectory.getDirectoryEntries()) { + if (mainRepoRoot != null) { + // For the main repo top-level directory, generate symlinks to everything in the directory + // instead of the directory itself. + for (Path target : mainRepoRoot.getDirectoryEntries()) { String baseName = target.getBaseName(); - Path execPath = execrootDirectory.getRelative(baseName); - // Create any links that don't exist yet and don't start with bazel-. - if (!baseName.startsWith(productName + "-") && !execPath.exists()) { + Path execPath = execroot.getRelative(baseName); + // Create any links that don't start with bazel-. + if (!baseName.startsWith(prefix)) { execPath.createSymbolicLink(target); } } + } else { + for (Map.Entry entry : mainRepoLinks.entrySet()) { + Path link = entry.getKey(); + Path target = entry.getValue(); + link.createSymbolicLink(target); + } } - - symlinkCorrectWorkspaceName(); - } - - /** - * Right now, the execution root is under the basename of the source directory, not the name - * defined in the WORKSPACE file. Thus, this adds a symlink with the WORKSPACE's workspace name - * to the old-style execution root. - * TODO(kchodorow): get rid of this once exec root is always under the WORKSPACE's workspace - * name. - * @throws IOException - */ - private void symlinkCorrectWorkspaceName() throws IOException { - Path correctDirectory = execroot.getParentDirectory().getRelative(workspaceName); - if (!correctDirectory.exists()) { - correctDirectory.createSymbolicLink(execroot); - } - } - - private static PackageIdentifier createInRepo( - PackageIdentifier repo, PathFragment packageFragment) { - return PackageIdentifier.create(repo.getRepository(), packageFragment); } } 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 da35f94693d1e6..2c086db6188f33 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 @@ -15,10 +15,8 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; -import static com.google.devtools.build.lib.vfs.FileSystemUtils.createDirectoryAndParents; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.cmdline.LabelConstants; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.PackageIdentifier; @@ -30,7 +28,6 @@ import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.Root; -import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; import org.junit.Before; @@ -45,39 +42,30 @@ public class SymlinkForestTest { private FileSystem fileSystem; - private Path topDir; - private Path file1; - private Path file2; - private Path aDir; - - // The execution root. - private Path linkRoot; - @Before - public final void initializeFileSystem() throws Exception { + public final void initializeFileSystem() { ManualClock clock = new ManualClock(); fileSystem = new InMemoryFileSystem(clock); - linkRoot = fileSystem.getPath("/linkRoot"); - createDirectoryAndParents(linkRoot); } - /* - * Build a directory tree that looks like: - * top-dir/ - * file-1 - * file-2 - * a-dir/ - * file-3 - * inner-dir/ - * link-1 => file-4 - * dir-link => b-dir - * file-4 - */ - private void createTestDirectoryTree() throws IOException { - topDir = fileSystem.getPath("/top-dir"); - file1 = fileSystem.getPath("/top-dir/file-1"); - file2 = fileSystem.getPath("/top-dir/file-2"); - aDir = fileSystem.getPath("/top-dir/a-dir"); + @Test + public void testDeleteTreesBelowNotPrefixed() throws IOException { + /* + * Build a directory tree that looks like: + * top-dir/ + * file-1 + * file-2 + * a-dir/ + * file-3 + * inner-dir/ + * link-1 => file-4 + * dir-link => b-dir + * file-4 + */ + Path topDir = fileSystem.getPath("/top-dir"); + Path file1 = fileSystem.getPath("/top-dir/file-1"); + Path file2 = fileSystem.getPath("/top-dir/file-2"); + Path aDir = fileSystem.getPath("/top-dir/a-dir"); Path bDir = fileSystem.getPath("/top-dir/b-dir"); Path file3 = fileSystem.getPath("/top-dir/a-dir/file-3"); Path innerDir = fileSystem.getPath("/top-dir/a-dir/inner-dir"); @@ -97,63 +85,34 @@ private void createTestDirectoryTree() throws IOException { dirLink.createSymbolicLink(bDir); FileSystemUtils.createEmptyFile(file4); FileSystemUtils.createEmptyFile(file5); - } - private static PathFragment longestPathPrefix(String path, String... prefixStrs) { - ImmutableSet.Builder prefixes = ImmutableSet.builder(); - for (String prefix : prefixStrs) { - prefixes.add(PackageIdentifier.createInMainRepo(prefix)); - } - PackageIdentifier longest = SymlinkForest.longestPathPrefix( - PackageIdentifier.createInMainRepo(path), prefixes.build()); - return longest != null ? longest.getPackageFragment() : null; - } - - @Test - public void testLongestPathPrefix() { - PathFragment a = PathFragment.create("A"); - assertThat(longestPathPrefix("A/b", "A", "B")).isEqualTo(a); // simple parent - assertThat(longestPathPrefix("A", "A", "B")).isEqualTo(a); // self - assertThat(longestPathPrefix("A/B/c", "A", "A/B")) - .isEqualTo(a.getRelative("B")); // want longest - assertThat(longestPathPrefix("C/b", "A", "B")).isNull(); // not found in other parents - assertThat(longestPathPrefix("A", "A/B", "B")).isNull(); // not found in child - assertThat(longestPathPrefix("A/B/C/d/e/f.h", "A/B/C", "B/C/d")) - .isEqualTo(a.getRelative("B/C")); - assertThat(longestPathPrefix("A/f.h", "", "B/C/d")).isEqualTo(PathFragment.EMPTY_FRAGMENT); - } - - @Test - public void testDeleteTreesBelowNotPrefixed() throws IOException { - createTestDirectoryTree(); - SymlinkForest.deleteTreesBelowNotPrefixed(topDir, new String[]{"file-"}); + SymlinkForest.deleteTreesBelowNotPrefixed(topDir, "file-"); assertThat(file1.exists()).isTrue(); assertThat(file2.exists()).isTrue(); assertThat(aDir.exists()).isFalse(); } - private PackageIdentifier createPkg(Root rootA, Root rootB, String pkg) throws IOException { - if (rootA != null) { - createDirectoryAndParents(rootA.getRelative(pkg)); - FileSystemUtils.createEmptyFile(rootA.getRelative(pkg).getChild("file")); - } - if (rootB != null) { - createDirectoryAndParents(rootB.getRelative(pkg)); - FileSystemUtils.createEmptyFile(rootB.getRelative(pkg).getChild("file")); - } - return PackageIdentifier.createInMainRepo(pkg); - } - - private PackageIdentifier createPkg(Root root, String repo, String pkg) + // Create package for external repo + private PackageIdentifier createExternalPkg(Root root, String repo, String pkg) throws IOException, LabelSyntaxException { if (root != null) { Path repoRoot = root.getRelative(LabelConstants.EXTERNAL_PACKAGE_NAME).getRelative(repo); - createDirectoryAndParents(repoRoot.getRelative(pkg)); + repoRoot.getRelative(pkg).createDirectoryAndParents(); FileSystemUtils.createEmptyFile(repoRoot.getRelative(pkg).getChild("file")); } return PackageIdentifier.create(RepositoryName.create("@" + repo), PathFragment.create(pkg)); } + // Create package for main repo + private PackageIdentifier createMainPkg(Root repoRoot, String pkg) + throws IOException, LabelSyntaxException { + if (repoRoot != null) { + repoRoot.getRelative(pkg).createDirectoryAndParents(); + FileSystemUtils.createEmptyFile(repoRoot.getRelative(pkg).getChild("file")); + } + return PackageIdentifier.create(RepositoryName.create("@"), PathFragment.create(pkg)); + } + private void assertLinksTo(Path fromRoot, Root toRoot, String relpart) throws IOException { assertLinksTo(fromRoot.getRelative(relpart), toRoot.getRelative(relpart)); } @@ -163,105 +122,74 @@ private void assertLinksTo(Path fromRoot, Path toRoot) throws IOException { assertThat(fromRoot.readSymbolicLink()).isEqualTo(toRoot.asFragment()); } - private void assertIsDir(Path root, String relpart) { - assertThat(root.getRelative(relpart).isDirectory(Symlinks.NOFOLLOW)).isTrue(); - } - @Test - public void testPlantLinkForest() throws IOException { - Root rootA = Root.fromPath(fileSystem.getPath("/A")); - Root rootB = Root.fromPath(fileSystem.getPath("/B")); - - ImmutableMap packageRootMap = - ImmutableMap.builder() - .put(createPkg(rootA, rootB, "pkgA"), rootA) - .put(createPkg(rootA, rootB, "dir1/pkgA"), rootA) - .put(createPkg(rootA, rootB, "dir1/pkgB"), rootB) - .put(createPkg(rootA, rootB, "dir2/pkg"), rootA) - .put(createPkg(rootA, rootB, "dir2/pkg/pkg"), rootB) - .put(createPkg(rootA, rootB, "pkgB"), rootB) - .put(createPkg(rootA, rootB, "pkgB/dir/pkg"), rootA) - .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 - - Path linkRoot = fileSystem.getPath("/linkRoot"); - createDirectoryAndParents(linkRoot); - new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, "wsname") - .plantSymlinkForest(); + public void testPlantSymlinkForest() throws Exception { + Root outputBase = Root.fromPath(fileSystem.getPath("/ob")); + Root mainRepo = Root.fromPath(fileSystem.getPath("/my_repo")); + Path linkRoot = outputBase.getRelative("execroot/ws_name"); - assertLinksTo(linkRoot, rootA, "pkgA"); - assertIsDir(linkRoot, "dir1"); - assertLinksTo(linkRoot, rootA, "dir1/pkgA"); - assertLinksTo(linkRoot, rootB, "dir1/pkgB"); - assertIsDir(linkRoot, "dir2"); - assertIsDir(linkRoot, "dir2/pkg"); - assertLinksTo(linkRoot, rootA, "dir2/pkg/file"); - assertLinksTo(linkRoot, rootB, "dir2/pkg/pkg"); - assertIsDir(linkRoot, "pkgB"); - assertIsDir(linkRoot, "pkgB/dir"); - assertLinksTo(linkRoot, rootB, "pkgB/dir/file"); - assertLinksTo(linkRoot, rootA, "pkgB/dir/pkg"); - assertLinksTo(linkRoot, rootA, "pkgB/pkg"); - } + mainRepo.asPath().createDirectoryAndParents(); + linkRoot.createDirectoryAndParents(); - @Test - public void testTopLevelPackage() throws Exception { - Root rootX = Root.fromPath(fileSystem.getPath("/X")); - Root rootY = Root.fromPath(fileSystem.getPath("/Y")); ImmutableMap packageRootMap = ImmutableMap.builder() - .put(createPkg(rootX, rootY, ""), rootX) - .put(createPkg(rootX, rootY, "foo"), rootX) + .put(createMainPkg(mainRepo, "dir_main"), mainRepo) + .put(createMainPkg(mainRepo, "dir_lib/pkg"), mainRepo) + .put(createMainPkg(mainRepo, ""), mainRepo) + .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase) + .put(createExternalPkg(outputBase, "Y", "dir_y/pkg"), outputBase) + .put(createExternalPkg(outputBase, "Z", "dir_z/pkg"), outputBase) .build(); - new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, "wsname") - .plantSymlinkForest(); - assertLinksTo(linkRoot, rootX, "file"); + new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME).plantSymlinkForest(); + + assertLinksTo(linkRoot, mainRepo, "dir_main"); + assertLinksTo(linkRoot, mainRepo, "dir_lib"); + // This file will be a copy on Windows, so just check if it exists instead of if it's a symlink. + assertThat(linkRoot.getChild("file").exists()).isTrue(); + assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/X"); + assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/Y"); + assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/Z"); } @Test - public void testRemotePackage() throws Exception { + public void testPlantSymlinkForestForMainRepo() throws Exception { + // For the main repo, plantSymlinkForest function should only link all files and dirs under + // main repo root that're presented in packageRootMap. Root outputBase = Root.fromPath(fileSystem.getPath("/ob")); - Root rootY = - Root.fromPath(outputBase.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX).getRelative("y")); - Root rootZ = - Root.fromPath(outputBase.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX).getRelative("z")); - Root rootW = - Root.fromPath(outputBase.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX).getRelative("w")); - createDirectoryAndParents(rootY.asPath()); - FileSystemUtils.createEmptyFile(rootY.getRelative("file")); + Root mainRepo = Root.fromPath(fileSystem.getPath("/my_repo")); + Path linkRoot = outputBase.getRelative("execroot/ws_name"); + + linkRoot.createDirectoryAndParents(); + mainRepo.asPath().createDirectoryAndParents(); + mainRepo.getRelative("dir4").createDirectoryAndParents(); + FileSystemUtils.createEmptyFile(mainRepo.getRelative("file")); ImmutableMap packageRootMap = ImmutableMap.builder() - // Remote repo without top-level package. - .put(createPkg(outputBase, "y", "w"), outputBase) - // Remote repo with and without top-level package. - .put(createPkg(outputBase, "z", ""), outputBase) - .put(createPkg(outputBase, "z", "a/b/c"), outputBase) - // Only top-level pkg. - .put(createPkg(outputBase, "w", ""), outputBase) + .put(createMainPkg(mainRepo, "dir1/pkg/foo"), mainRepo) + .put(createMainPkg(mainRepo, "dir2/pkg"), mainRepo) + .put(createMainPkg(mainRepo, "dir3"), mainRepo) + .put(createExternalPkg(outputBase, "X", "dir_x/pkg"), outputBase) .build(); - new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, "wsname") - .plantSymlinkForest(); - assertThat(linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/y/file").exists()) - .isFalse(); - assertLinksTo( - linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/y/w"), rootY.getRelative("w")); - assertLinksTo( - linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/z/file"), - rootZ.getRelative("file")); - assertLinksTo( - linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/z/a"), rootZ.getRelative("a")); - assertLinksTo( - linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX + "/w/file"), - rootW.getRelative("file")); + new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME).plantSymlinkForest(); + + assertLinksTo(linkRoot, mainRepo, "dir1"); + assertLinksTo(linkRoot, mainRepo, "dir2"); + assertLinksTo(linkRoot, mainRepo, "dir3"); + // dir4 and the file under main repo root should not be linked + // because they are not presented in packageRootMap. + assertThat(linkRoot.getChild("dir4").exists()).isFalse(); + assertThat(linkRoot.getChild("file").exists()).isFalse(); + assertLinksTo(linkRoot, outputBase, LabelConstants.EXTERNAL_PATH_PREFIX + "/X"); } @Test public void testExternalPackage() throws Exception { + Path linkRoot = fileSystem.getPath("/linkRoot"); + linkRoot.createDirectoryAndParents(); + Root root = Root.fromPath(fileSystem.getPath("/src")); ImmutableMap packageRootMap = ImmutableMap.builder() @@ -269,31 +197,7 @@ public void testExternalPackage() throws Exception { .put(LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER, root) .build(); - new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, "wsname") - .plantSymlinkForest(); + new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME).plantSymlinkForest(); assertThat(linkRoot.getRelative(LabelConstants.EXTERNAL_PATH_PREFIX).exists()).isFalse(); } - - @Test - public void testWorkspaceName() throws Exception { - Root root = Root.fromPath(fileSystem.getPath("/src")); - ImmutableMap packageRootMap = - ImmutableMap.builder() - // Remote repo without top-level package. - .put(createPkg(root, "y", "w"), root) - .build(); - - new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, "wsname") - .plantSymlinkForest(); - assertThat(linkRoot.getRelative("../wsname").exists()).isTrue(); - } - - @Test - public void testExecrootVersionChanges() throws Exception { - ImmutableMap packageRootMap = ImmutableMap.of(); - linkRoot.getRelative("wsname").createDirectory(); - new SymlinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME, "wsname") - .plantSymlinkForest(); - assertThat(linkRoot.getRelative("../wsname").isSymbolicLink()).isTrue(); - } }