diff options
author | 2016-06-17 13:36:24 +0000 | |
---|---|---|
committer | 2016-06-20 09:34:06 +0000 | |
commit | f3cf98faa44f3b647956868422a11074aff0e9ee (patch) | |
tree | 93a361a853b3ebccd0af7074c993c29618ddf0e4 /src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java | |
parent | 48f2470b3cc60c11e46a95872a409341c4224096 (diff) |
Rollback of commit bdfd58a8ca2ed5735d6aaa5b238fb0f689515724.
--
MOS_MIGRATED_REVID=125160288
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java | 139 |
1 files changed, 139 insertions, 0 deletions
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 aaac85007c..0facfefde1 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,6 +16,9 @@ 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; @@ -32,6 +35,10 @@ 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 @@ -40,6 +47,9 @@ import java.util.List; @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() {} /**************************************************************************** @@ -127,6 +137,21 @@ 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. @@ -651,6 +676,120 @@ 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! |