aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Kristina Chodorow <kchodorow@google.com>2016-06-20 14:27:56 +0000
committerGravatar Philipp Wollermann <philwo@google.com>2016-06-21 09:57:49 +0000
commit52bc57cd26af622bbf8366d56fcdbad49e6886df (patch)
tree229d50b7e8d95ac14a691d7eec058177a91269de
parent4873e56c6ce2c29650f2197eb492179b341e872a (diff)
Move execroot tree creation to its own class
Part 1 of many for #1262, rolling forward. -- MOS_MIGRATED_REVID=125334954
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java18
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java194
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java157
-rw-r--r--src/test/java/com/google/devtools/build/lib/buildtool/BUILD27
-rw-r--r--src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java184
-rw-r--r--src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java113
6 files changed, 411 insertions, 282 deletions
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 a07fb9de73..76706bc538 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
@@ -504,7 +504,12 @@ public class ExecutionTool {
createActionLogDirectory();
// Plant the symlink forest.
- plantSymlinkForest(packageRoots);
+ try {
+ new SymlinkForest(
+ packageRoots, getExecRoot(), runtime.getProductName()).plantSymlinkForest();
+ } catch (IOException e) {
+ throw new ExecutorInitException("Source forest creation failed", e);
+ }
}
private void createToolsSymlinks() throws ExecutorInitException {
@@ -515,17 +520,6 @@ public class ExecutionTool {
}
}
- private void plantSymlinkForest(ImmutableMap<PathFragment, Path> packageRoots)
- throws ExecutorInitException {
- try {
- FileSystemUtils.deleteTreesBelowNotPrefixed(getExecRoot(),
- new String[] { ".", "_", runtime.getProductName() + "-"});
- FileSystemUtils.plantLinkForest(packageRoots, getExecRoot(), runtime.getProductName());
- } catch (IOException e) {
- throw new ExecutorInitException("Source forest creation failed", e);
- }
- }
-
private void createActionLogDirectory() throws ExecutorInitException {
Path directory = env.getDirectories().getActionConsoleOutputDirectory();
try {
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
new file mode 100644
index 0000000000..ceeb77a726
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/SymlinkForest.java
@@ -0,0 +1,194 @@
+// Copyright 2016 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.buildtool;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+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 java.io.IOException;
+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 LOG = Logger.getLogger(SymlinkForest.class.getName());
+ private static final boolean LOG_FINER = LOG.isLoggable(Level.FINER);
+
+ private final ImmutableMap<PathFragment, Path> packageRoots;
+ private final Path workspace;
+ private final String productName;
+ private final String[] prefixes;
+
+ SymlinkForest(
+ ImmutableMap<PathFragment, Path> packageRoots, Path workspace, String productName) {
+ this.packageRoots = packageRoots;
+ this.workspace = workspace;
+ this.productName = productName;
+ this.prefixes = new String[] { ".", "_", 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.
+ */
+ @VisibleForTesting
+ static PathFragment longestPathPrefix(PathFragment path, Set<PathFragment> prefixes) {
+ for (int i = path.segmentCount(); i >= 0; i--) {
+ PathFragment prefix = path.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.
+ */
+ @VisibleForTesting
+ @ThreadSafety.ThreadSafe
+ static void deleteTreesBelowNotPrefixed(Path dir, String[] prefixes) throws IOException {
+ dirloop:
+ for (Path p : dir.getDirectoryEntries()) {
+ String name = p.getBaseName();
+ for (int i = 0; i < prefixes.length; i++) {
+ if (name.startsWith(prefixes[i])) {
+ continue dirloop;
+ }
+ }
+ FileSystemUtils.deleteTree(p);
+ }
+ }
+
+ void plantSymlinkForest() throws IOException {
+ deleteTreesBelowNotPrefixed(workspace, prefixes);
+ Path emptyPackagePath = null;
+
+ // Create a sorted map of all dirs (packages and their ancestors) to sets of their roots.
+ // Packages come from exactly one root, but their shared ancestors may come from more.
+ // The map is maintained sorted lexicographically, so parents are before their children.
+ Map<PathFragment, Set<Path>> dirRootsMap = Maps.newTreeMap();
+ for (Map.Entry<PathFragment, Path> entry : packageRoots.entrySet()) {
+ PathFragment pkgDir = entry.getKey();
+ Path pkgRoot = entry.getValue();
+ if (pkgDir.segmentCount() == 0) {
+ emptyPackagePath = entry.getValue();
+ }
+ for (int i = 1; i <= pkgDir.segmentCount(); i++) {
+ PathFragment dir = pkgDir.subFragment(0, i);
+ Set<Path> roots = dirRootsMap.get(dir);
+ if (roots == null) {
+ roots = Sets.newHashSet();
+ dirRootsMap.put(dir, roots);
+ }
+ roots.add(pkgRoot);
+ }
+ }
+ // Now add in roots for all non-pkg dirs that are in between two packages, and missed above.
+ for (Map.Entry<PathFragment, Set<Path>> entry : dirRootsMap.entrySet()) {
+ PathFragment dir = entry.getKey();
+ if (!packageRoots.containsKey(dir)) {
+ PathFragment pkgDir = longestPathPrefix(dir, packageRoots.keySet());
+ if (pkgDir != null) {
+ entry.getValue().add(packageRoots.get(pkgDir));
+ }
+ }
+ }
+ // Create output dirs for all dirs that have more than one root and need to be split.
+ for (Map.Entry<PathFragment, Set<Path>> entry : dirRootsMap.entrySet()) {
+ PathFragment dir = entry.getKey();
+ if (entry.getValue().size() > 1) {
+ if (LOG_FINER) {
+ LOG.finer("mkdir " + workspace.getRelative(dir));
+ }
+ FileSystemUtils.createDirectoryAndParents(workspace.getRelative(dir));
+ }
+ }
+ // Make dir links for single rooted dirs.
+ for (Map.Entry<PathFragment, Set<Path>> entry : dirRootsMap.entrySet()) {
+ PathFragment dir = entry.getKey();
+ Set<Path> roots = entry.getValue();
+ // Simple case of one root for this dir.
+ if (roots.size() == 1) {
+ if (dir.segmentCount() > 1 && dirRootsMap.get(dir.getParentDirectory()).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.
+ Path root = roots.iterator().next(); // lone root in set
+ if (LOG_FINER) {
+ LOG.finer("ln -s " + root.getRelative(dir) + " " + workspace.getRelative(dir));
+ }
+ workspace.getRelative(dir).createSymbolicLink(root.getRelative(dir));
+ }
+ }
+ // Make links for dirs within packages, skip parent-only dirs.
+ for (Map.Entry<PathFragment, Set<Path>> entry : dirRootsMap.entrySet()) {
+ PathFragment dir = entry.getKey();
+ if (entry.getValue().size() > 1) {
+ // If this dir is at or below a package dir, link in its contents.
+ PathFragment pkgDir = longestPathPrefix(dir, packageRoots.keySet());
+ if (pkgDir != null) {
+ Path root = packageRoots.get(pkgDir);
+ try {
+ Path absdir = root.getRelative(dir);
+ if (absdir.isDirectory()) {
+ if (LOG_FINER) {
+ LOG.finer("ln -s " + absdir + "/* " + workspace.getRelative(dir) + "/");
+ }
+ for (Path target : absdir.getDirectoryEntries()) {
+ PathFragment p = target.relativeTo(root);
+ if (!dirRootsMap.containsKey(p)) {
+ //LOG.finest("ln -s " + target + " " + linkRoot.getRelative(p));
+ workspace.getRelative(p).createSymbolicLink(target);
+ }
+ }
+ } else {
+ LOG.fine("Symlink planting skipping dir '" + absdir + "'");
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ // Otherwise its just an otherwise empty common parent dir.
+ }
+ }
+ }
+
+ if (emptyPackagePath != null) {
+ // For the top-level directory, generate symlinks to everything in the directory instead of
+ // the directory itself.
+ for (Path target : emptyPackagePath.getDirectoryEntries()) {
+ String baseName = target.getBaseName();
+ // Create any links that don't exist yet and don't start with bazel-.
+ if (!baseName.startsWith(productName + "-")
+ && !workspace.getRelative(baseName).exists()) {
+ workspace.getRelative(baseName).createSymbolicLink(target);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
index 0facfefde1..8c864547ff 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
@@ -16,9 +16,6 @@ package com.google.devtools.build.lib.vfs;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
import com.google.common.io.ByteSink;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
@@ -35,10 +32,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
/**
* Helper functions that implement often-used complex operations on file
@@ -47,9 +40,6 @@ import java.util.logging.Logger;
@ConditionallyThreadSafe // ThreadSafe except for deleteTree.
public class FileSystemUtils {
- static final Logger LOG = Logger.getLogger(FileSystemUtils.class.getName());
- static final boolean LOG_FINER = LOG.isLoggable(Level.FINER);
-
private FileSystemUtils() {}
/****************************************************************************
@@ -137,21 +127,6 @@ public class FileSystemUtils {
}
/**
- * 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.
- */
- static PathFragment longestPathPrefix(PathFragment path, Set<PathFragment> prefixes) {
- for (int i = path.segmentCount(); i >= 0; i--) {
- PathFragment prefix = path.subFragment(0, i);
- if (prefixes.contains(prefix)) {
- return prefix;
- }
- }
- return null;
- }
-
- /**
* Removes the shortest suffix beginning with '.' from the basename of the
* filename string. If the basename contains no '.', the filename is returned
* unchanged.
@@ -563,24 +538,6 @@ public class FileSystemUtils {
}
/**
- * 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.
- */
- @ThreadSafe
- public static void deleteTreesBelowNotPrefixed(Path dir, String[] prefixes) throws IOException {
- dirloop:
- for (Path p : dir.getDirectoryEntries()) {
- String name = p.getBaseName();
- for (int i = 0; i < prefixes.length; i++) {
- if (name.startsWith(prefixes[i])) {
- continue dirloop;
- }
- }
- deleteTree(p);
- }
- }
-
- /**
* Copies all dir trees under a given 'from' dir to location 'to', while overwriting
* all files in the potentially existing 'to'. Resolves symbolic links.
*
@@ -676,120 +633,6 @@ public class FileSystemUtils {
return true;
}
- /**
- * Takes a map of directory fragments to root paths, and creates a symlink
- * forest under an existing linkRoot to the corresponding source dirs or
- * files. Symlink are made at the highest dir possible, linking files directly
- * only when needed with nested packages.
- */
- public static void plantLinkForest(ImmutableMap<PathFragment, Path> packageRootMap,
- Path linkRoot, String productName)
- throws IOException {
- Path emptyPackagePath = null;
-
- // Create a sorted map of all dirs (packages and their ancestors) to sets of their roots.
- // Packages come from exactly one root, but their shared ancestors may come from more.
- // The map is maintained sorted lexicographically, so parents are before their children.
- Map<PathFragment, Set<Path>> dirRootsMap = Maps.newTreeMap();
- for (Map.Entry<PathFragment, Path> entry : packageRootMap.entrySet()) {
- PathFragment pkgDir = entry.getKey();
- Path pkgRoot = entry.getValue();
- if (pkgDir.segmentCount() == 0) {
- emptyPackagePath = entry.getValue();
- }
- for (int i = 1; i <= pkgDir.segmentCount(); i++) {
- PathFragment dir = pkgDir.subFragment(0, i);
- Set<Path> roots = dirRootsMap.get(dir);
- if (roots == null) {
- roots = Sets.newHashSet();
- dirRootsMap.put(dir, roots);
- }
- roots.add(pkgRoot);
- }
- }
- // Now add in roots for all non-pkg dirs that are in between two packages, and missed above.
- for (Map.Entry<PathFragment, Set<Path>> entry : dirRootsMap.entrySet()) {
- PathFragment dir = entry.getKey();
- if (!packageRootMap.containsKey(dir)) {
- PathFragment pkgDir = longestPathPrefix(dir, packageRootMap.keySet());
- if (pkgDir != null) {
- entry.getValue().add(packageRootMap.get(pkgDir));
- }
- }
- }
- // Create output dirs for all dirs that have more than one root and need to be split.
- for (Map.Entry<PathFragment, Set<Path>> entry : dirRootsMap.entrySet()) {
- PathFragment dir = entry.getKey();
- if (entry.getValue().size() > 1) {
- if (LOG_FINER) {
- LOG.finer("mkdir " + linkRoot.getRelative(dir));
- }
- createDirectoryAndParents(linkRoot.getRelative(dir));
- }
- }
- // Make dir links for single rooted dirs.
- for (Map.Entry<PathFragment, Set<Path>> entry : dirRootsMap.entrySet()) {
- PathFragment dir = entry.getKey();
- Set<Path> roots = entry.getValue();
- // Simple case of one root for this dir.
- if (roots.size() == 1) {
- if (dir.segmentCount() > 1 && dirRootsMap.get(dir.getParentDirectory()).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.
- Path root = roots.iterator().next(); // lone root in set
- if (LOG_FINER) {
- LOG.finer("ln -s " + root.getRelative(dir) + " " + linkRoot.getRelative(dir));
- }
- linkRoot.getRelative(dir).createSymbolicLink(root.getRelative(dir));
- }
- }
- // Make links for dirs within packages, skip parent-only dirs.
- for (Map.Entry<PathFragment, Set<Path>> entry : dirRootsMap.entrySet()) {
- PathFragment dir = entry.getKey();
- if (entry.getValue().size() > 1) {
- // If this dir is at or below a package dir, link in its contents.
- PathFragment pkgDir = longestPathPrefix(dir, packageRootMap.keySet());
- if (pkgDir != null) {
- Path root = packageRootMap.get(pkgDir);
- try {
- Path absdir = root.getRelative(dir);
- if (absdir.isDirectory()) {
- if (LOG_FINER) {
- LOG.finer("ln -s " + absdir + "/* " + linkRoot.getRelative(dir) + "/");
- }
- for (Path target : absdir.getDirectoryEntries()) {
- PathFragment p = target.relativeTo(root);
- if (!dirRootsMap.containsKey(p)) {
- //LOG.finest("ln -s " + target + " " + linkRoot.getRelative(p));
- linkRoot.getRelative(p).createSymbolicLink(target);
- }
- }
- } else {
- LOG.fine("Symlink planting skipping dir '" + absdir + "'");
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- // Otherwise its just an otherwise empty common parent dir.
- }
- }
- }
-
- if (emptyPackagePath != null) {
- // For the top-level directory, generate symlinks to everything in the directory instead of
- // the directory itself.
- for (Path target : emptyPackagePath.getDirectoryEntries()) {
- String baseName = target.getBaseName();
- // Create any links that don't exist yet and don't start with bazel-.
- if (!baseName.startsWith(productName + "-")
- && !linkRoot.getRelative(baseName).exists()) {
- linkRoot.getRelative(baseName).createSymbolicLink(target);
- }
- }
- }
- }
-
/****************************************************************************
* Whole-file I/O utilities for characters and bytes. These convenience
* methods are not efficient and should not be used for large amounts of data!
diff --git a/src/test/java/com/google/devtools/build/lib/buildtool/BUILD b/src/test/java/com/google/devtools/build/lib/buildtool/BUILD
new file mode 100644
index 0000000000..3b4268ade6
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/buildtool/BUILD
@@ -0,0 +1,27 @@
+java_library(
+ name = "testutil",
+ srcs = glob(["util/*.java"]),
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib:bazel-main",
+ "//src/main/java/com/google/devtools/build/lib:runtime",
+ "//src/test/java/com/google/devtools/build/lib:packages_testutil",
+ "//src/test/java/com/google/devtools/build/lib:testutil",
+ ],
+)
+
+java_test(
+ name = "BuildtoolTests",
+ srcs = glob(["*.java"]),
+ test_class = "com.google.devtools.build.lib.AllTests",
+ runtime_deps = ["//src/test/java/com/google/devtools/build/lib:test_runner"],
+ deps = [
+ ":testutil",
+ "//src/main/java/com/google/devtools/build/lib:inmemoryfs",
+ "//src/main/java/com/google/devtools/build/lib:packages-internal",
+ "//src/main/java/com/google/devtools/build/lib:runtime",
+ "//src/main/java/com/google/devtools/build/lib:vfs",
+ "//src/test/java/com/google/devtools/build/lib:packages_testutil",
+ "//src/test/java/com/google/devtools/build/lib:testutil",
+ "//third_party:junit4",
+ ],
+)
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
new file mode 100644
index 0000000000..7ea95a4c3e
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/buildtool/SymlinkForestTest.java
@@ -0,0 +1,184 @@
+// Copyright 2016 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.buildtool;
+
+import static com.google.devtools.build.lib.vfs.FileSystemUtils.createDirectoryAndParents;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.testutil.ManualClock;
+import com.google.devtools.build.lib.testutil.TestConstants;
+import com.google.devtools.build.lib.vfs.FileSystem;
+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.Symlinks;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests {@link SymlinkForest}.
+ */
+@RunWith(JUnit4.class)
+public class SymlinkForestTest {
+ private FileSystem fileSystem;
+
+ private Path topDir;
+ private Path file1;
+ private Path file2;
+ private Path aDir;
+
+ @Before
+ public final void initializeFileSystem() throws Exception {
+ ManualClock clock = new ManualClock();
+ fileSystem = new InMemoryFileSystem(clock);
+ }
+
+ /*
+ * 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");
+ 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");
+ Path link1 = fileSystem.getPath("/top-dir/a-dir/inner-dir/link-1");
+ Path dirLink = fileSystem.getPath("/top-dir/a-dir/inner-dir/dir-link");
+ Path file4 = fileSystem.getPath("/file-4");
+ Path file5 = fileSystem.getPath("/top-dir/b-dir/file-5");
+
+ topDir.createDirectory();
+ FileSystemUtils.createEmptyFile(file1);
+ FileSystemUtils.createEmptyFile(file2);
+ aDir.createDirectory();
+ bDir.createDirectory();
+ FileSystemUtils.createEmptyFile(file3);
+ innerDir.createDirectory();
+ link1.createSymbolicLink(file4); // simple symlink
+ dirLink.createSymbolicLink(bDir);
+ FileSystemUtils.createEmptyFile(file4);
+ FileSystemUtils.createEmptyFile(file5);
+ }
+
+ private static String longestPathPrefixStr(String path, String... prefixStrs) {
+ Set<PathFragment> prefixes = new HashSet<>();
+ for (String prefix : prefixStrs) {
+ prefixes.add(new PathFragment(prefix));
+ }
+ PathFragment longest = SymlinkForest.longestPathPrefix(new PathFragment(path), prefixes);
+ return longest != null ? longest.getPathString() : null;
+ }
+
+ @Test
+ public void testLongestPathPrefix() {
+ assertEquals("A", longestPathPrefixStr("A/b", "A", "B")); // simple parent
+ assertEquals("A", longestPathPrefixStr("A", "A", "B")); // self
+ assertEquals("A/B", longestPathPrefixStr("A/B/c", "A", "A/B")); // want longest
+ assertNull(longestPathPrefixStr("C/b", "A", "B")); // not found in other parents
+ assertNull(longestPathPrefixStr("A", "A/B", "B")); // not found in child
+ assertEquals("A/B/C", longestPathPrefixStr("A/B/C/d/e/f.h", "A/B/C", "B/C/d"));
+ assertEquals("", longestPathPrefixStr("A/f.h", "", "B/C/d"));
+ }
+
+ @Test
+ public void testDeleteTreesBelowNotPrefixed() throws IOException {
+ createTestDirectoryTree();
+ SymlinkForest.deleteTreesBelowNotPrefixed(topDir, new String[] { "file-"});
+ assertTrue(file1.exists());
+ assertTrue(file2.exists());
+ assertFalse(aDir.exists());
+ }
+
+ private PathFragment createPkg(Path rootA, Path 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 new PathFragment(pkg);
+ }
+
+ private void assertLinksTo(Path fromRoot, Path toRoot, String relpart) throws IOException {
+ assertTrue(fromRoot.getRelative(relpart).isSymbolicLink());
+ assertEquals(toRoot.getRelative(relpart).asFragment(),
+ fromRoot.getRelative(relpart).readSymbolicLink());
+ }
+
+ private void assertIsDir(Path root, String relpart) {
+ assertTrue(root.getRelative(relpart).isDirectory(Symlinks.NOFOLLOW));
+ }
+
+ @Test
+ public void testPlantLinkForest() throws IOException {
+ Path rootA = fileSystem.getPath("/A");
+ Path rootB = fileSystem.getPath("/B");
+
+ ImmutableMap<PathFragment, Path> packageRootMap = ImmutableMap.<PathFragment, Path>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).plantSymlinkForest();
+
+ 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");
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java
index 23e05102dd..5c4aaa32d7 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemUtilsTest.java
@@ -19,9 +19,6 @@ import static com.google.devtools.build.lib.vfs.FileSystemUtils.copyFile;
import static com.google.devtools.build.lib.vfs.FileSystemUtils.copyTool;
import static com.google.devtools.build.lib.vfs.FileSystemUtils.createDirectoryAndParents;
import static com.google.devtools.build.lib.vfs.FileSystemUtils.deleteTree;
-import static com.google.devtools.build.lib.vfs.FileSystemUtils.deleteTreesBelowNotPrefixed;
-import static com.google.devtools.build.lib.vfs.FileSystemUtils.longestPathPrefix;
-import static com.google.devtools.build.lib.vfs.FileSystemUtils.plantLinkForest;
import static com.google.devtools.build.lib.vfs.FileSystemUtils.relativePath;
import static com.google.devtools.build.lib.vfs.FileSystemUtils.removeExtension;
import static com.google.devtools.build.lib.vfs.FileSystemUtils.touchFile;
@@ -37,11 +34,9 @@ import static org.junit.Assert.fail;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.testutil.BlazeTestUtils;
import com.google.devtools.build.lib.testutil.ManualClock;
-import com.google.devtools.build.lib.testutil.TestConstants;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
import org.junit.Before;
@@ -51,12 +46,9 @@ import org.junit.runners.JUnit4;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
/**
* This class tests the file system utilities.
@@ -191,26 +183,6 @@ public class FileSystemUtilsTest {
assertEquals("../../../file-4", relativePath(innerDir, file4).getPathString());
}
- private static String longestPathPrefixStr(String path, String... prefixStrs) {
- Set<PathFragment> prefixes = new HashSet<>();
- for (String prefix : prefixStrs) {
- prefixes.add(new PathFragment(prefix));
- }
- PathFragment longest = longestPathPrefix(new PathFragment(path), prefixes);
- return longest != null ? longest.getPathString() : null;
- }
-
- @Test
- public void testLongestPathPrefix() {
- assertEquals("A", longestPathPrefixStr("A/b", "A", "B")); // simple parent
- assertEquals("A", longestPathPrefixStr("A", "A", "B")); // self
- assertEquals("A/B", longestPathPrefixStr("A/B/c", "A", "A/B")); // want longest
- assertNull(longestPathPrefixStr("C/b", "A", "B")); // not found in other parents
- assertNull(longestPathPrefixStr("A", "A/B", "B")); // not found in child
- assertEquals("A/B/C", longestPathPrefixStr("A/B/C/d/e/f.h", "A/B/C", "B/C/d"));
- assertEquals("", longestPathPrefixStr("A/f.h", "", "B/C/d"));
- }
-
@Test
public void testRemoveExtension_Strings() throws Exception {
assertEquals("foo", removeExtension("foo.c"));
@@ -642,15 +614,6 @@ public class FileSystemUtilsTest {
}
@Test
- public void testDeleteTreesBelowNotPrefixed() throws IOException {
- createTestDirectoryTree();
- deleteTreesBelowNotPrefixed(topDir, new String[] { "file-"});
- assertTrue(file1.exists());
- assertTrue(file2.exists());
- assertFalse(aDir.exists());
- }
-
- @Test
public void testCreateDirectories() throws IOException {
Path mainPath = fileSystem.getPath("/some/where/deep/in/the/hierarchy");
assertTrue(createDirectoryAndParents(mainPath));
@@ -699,82 +662,6 @@ public class FileSystemUtilsTest {
assertTrue(createDirectoryAndParents(theHierarchy));
}
- PathFragment createPkg(Path rootA, Path 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 new PathFragment(pkg);
- }
-
- void assertLinksTo(Path fromRoot, Path toRoot, String relpart) throws IOException {
- assertTrue(fromRoot.getRelative(relpart).isSymbolicLink());
- assertEquals(toRoot.getRelative(relpart).asFragment(),
- fromRoot.getRelative(relpart).readSymbolicLink());
- }
-
- void assertIsDir(Path root, String relpart) {
- assertTrue(root.getRelative(relpart).isDirectory(Symlinks.NOFOLLOW));
- }
-
- void dumpTree(Path root, PrintStream out) throws IOException {
- out.println("\n" + root);
- for (Path p : FileSystemUtils.traverseTree(root, Predicates.alwaysTrue())) {
- if (p.isDirectory(Symlinks.NOFOLLOW)) {
- out.println(" " + p + "/");
- } else if (p.isSymbolicLink()) {
- out.println(" " + p + " => " + p.readSymbolicLink());
- } else {
- out.println(" " + p + " [" + p.resolveSymbolicLinks() + "]");
- }
- }
- }
-
- @Test
- public void testPlantLinkForest() throws IOException {
- Path rootA = fileSystem.getPath("/A");
- Path rootB = fileSystem.getPath("/B");
-
- ImmutableMap<PathFragment, Path> packageRootMap = ImmutableMap.<PathFragment, Path>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
-
- //dumpTree(rootA, System.err);
- //dumpTree(rootB, System.err);
-
- Path linkRoot = fileSystem.getPath("/linkRoot");
- createDirectoryAndParents(linkRoot);
- plantLinkForest(packageRootMap, linkRoot, TestConstants.PRODUCT_NAME);
-
- //dumpTree(linkRoot, System.err);
-
- 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");
- }
@Test
public void testWriteIsoLatin1() throws Exception {