diff options
Diffstat (limited to 'src')
48 files changed, 1172 insertions, 1105 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java index aaac32e758..dc52388d83 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BinTools.java @@ -194,7 +194,7 @@ public final class BinTools { } private void linkTool(Path sourcePath, Path linkPath) throws ExecException { - if (linkPath.getFileSystem().supportsSymbolicLinksNatively(linkPath.getLocalPath())) { + if (linkPath.getFileSystem().supportsSymbolicLinksNatively(linkPath)) { try { if (!linkPath.isSymbolicLink()) { // ensureSymbolicLink() does not handle the case where there is already diff --git a/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java b/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java index 4a79ee12f6..7b82dcc06a 100644 --- a/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java @@ -16,7 +16,6 @@ package com.google.devtools.build.lib.unix; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Striped; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; import com.google.devtools.build.lib.profiler.Profiler; import com.google.devtools.build.lib.profiler.ProfilerTask; @@ -25,19 +24,18 @@ import com.google.devtools.build.lib.unix.NativePosixFiles.ReadTypes; import com.google.devtools.build.lib.vfs.AbstractFileSystemWithCustomStat; import com.google.devtools.build.lib.vfs.Dirent; import com.google.devtools.build.lib.vfs.FileStatus; -import com.google.devtools.build.lib.vfs.LocalPath; +import com.google.devtools.build.lib.vfs.Path; +import com.google.devtools.build.lib.vfs.PathFragment; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.concurrent.locks.Lock; /** * This class implements the FileSystem interface using direct calls to the UNIX filesystem. */ @ThreadSafe public class UnixFileSystem extends AbstractFileSystemWithCustomStat { - private final Striped<Lock> pathLock = Striped.lock(64); public UnixFileSystem() { } @@ -102,7 +100,7 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected Collection<String> getDirectoryEntries(LocalPath path) throws IOException { + protected Collection<String> getDirectoryEntries(Path path) throws IOException { String name = path.getPathString(); String[] entries; long startTime = Profiler.nanoTimeMaybe(); @@ -119,7 +117,7 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected String resolveOneLink(LocalPath path) throws IOException { + protected PathFragment resolveOneLink(Path path) throws IOException { // Beware, this seemingly simple code belies the complex specification of // FileSystem.resolveOneLink(). return stat(path, false).isSymbolicLink() @@ -147,7 +145,7 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) throws IOException { + protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException { String name = path.getPathString(); long startTime = Profiler.nanoTimeMaybe(); try { @@ -166,12 +164,12 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + protected FileStatus stat(Path path, boolean followSymlinks) throws IOException { return statInternal(path, followSymlinks); } @VisibleForTesting - protected UnixFileStatus statInternal(LocalPath path, boolean followSymlinks) throws IOException { + protected UnixFileStatus statInternal(Path path, boolean followSymlinks) throws IOException { String name = path.getPathString(); long startTime = Profiler.nanoTimeMaybe(); try { @@ -187,7 +185,7 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { // This is a performance optimization in the case where clients // catch and don't re-throw. @Override - protected FileStatus statNullable(LocalPath path, boolean followSymlinks) { + protected FileStatus statNullable(Path path, boolean followSymlinks) { String name = path.getPathString(); long startTime = Profiler.nanoTimeMaybe(); try { @@ -201,16 +199,16 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected boolean exists(LocalPath path, boolean followSymlinks) { + protected boolean exists(Path path, boolean followSymlinks) { return statNullable(path, followSymlinks) != null; } /** - * Return true iff the {@code stat} of {@code path} resulted in an {@code ENOENT} or {@code - * ENOTDIR} error. + * Return true iff the {@code stat} of {@code path} resulted in an {@code ENOENT} + * or {@code ENOTDIR} error. */ @Override - protected FileStatus statIfFound(LocalPath path, boolean followSymlinks) throws IOException { + protected FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException { String name = path.getPathString(); long startTime = Profiler.nanoTimeMaybe(); try { @@ -236,29 +234,29 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected boolean isReadable(LocalPath path) throws IOException { + protected boolean isReadable(Path path) throws IOException { return (statInternal(path, true).getPermissions() & 0400) != 0; } @Override - protected boolean isWritable(LocalPath path) throws IOException { + protected boolean isWritable(Path path) throws IOException { return (statInternal(path, true).getPermissions() & 0200) != 0; } @Override - protected boolean isExecutable(LocalPath path) throws IOException { + protected boolean isExecutable(Path path) throws IOException { return (statInternal(path, true).getPermissions() & 0100) != 0; } /** - * Adds or remove the bits specified in "permissionBits" to the permission mask of the file - * specified by {@code path}. If the argument {@code add} is true, the specified permissions are - * added, otherwise they are removed. + * Adds or remove the bits specified in "permissionBits" to the permission + * mask of the file specified by {@code path}. If the argument {@code add} is + * true, the specified permissions are added, otherwise they are removed. * * @throws IOException if there was an error writing the file's metadata */ - private void modifyPermissionBits(LocalPath path, int permissionBits, boolean add) - throws IOException { + private void modifyPermissionBits(Path path, int permissionBits, boolean add) + throws IOException { synchronized (path) { int oldMode = statInternal(path, true).getPermissions(); int newMode = add ? (oldMode | permissionBits) : (oldMode & ~permissionBits); @@ -267,39 +265,39 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected void setReadable(LocalPath path, boolean readable) throws IOException { + protected void setReadable(Path path, boolean readable) throws IOException { modifyPermissionBits(path, 0400, readable); } @Override - public void setWritable(LocalPath path, boolean writable) throws IOException { + public void setWritable(Path path, boolean writable) throws IOException { modifyPermissionBits(path, 0200, writable); } @Override - protected void setExecutable(LocalPath path, boolean executable) throws IOException { + protected void setExecutable(Path path, boolean executable) throws IOException { modifyPermissionBits(path, 0111, executable); } @Override - protected void chmod(LocalPath path, int mode) throws IOException { + protected void chmod(Path path, int mode) throws IOException { synchronized (path) { NativePosixFiles.chmod(path.toString(), mode); } } @Override - public boolean supportsModifications(LocalPath path) { + public boolean supportsModifications(Path path) { return true; } @Override - public boolean supportsSymbolicLinksNatively(LocalPath path) { + public boolean supportsSymbolicLinksNatively(Path path) { return true; } @Override - public boolean supportsHardLinksNatively(LocalPath path) { + public boolean supportsHardLinksNatively(Path path) { return true; } @@ -309,10 +307,8 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - public boolean createDirectory(LocalPath path) throws IOException { - Lock lock = getPathLock(path); - lock.lock(); - try { + public boolean createDirectory(Path path) throws IOException { + synchronized (path) { // Note: UNIX mkdir(2), FilesystemUtils.mkdir() and createDirectory all // have different ways of representing failure! if (NativePosixFiles.mkdir(path.toString(), 0777)) { @@ -325,30 +321,25 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } else { throw new IOException(path + " (File exists)"); } - } finally { - lock.unlock(); } } @Override - protected void createSymbolicLink(LocalPath linkPath, String targetFragment) throws IOException { - Lock lock = getPathLock(linkPath); - lock.lock(); - try { - NativePosixFiles.symlink(targetFragment, linkPath.toString()); - } finally { - lock.unlock(); + protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) + throws IOException { + synchronized (linkPath) { + NativePosixFiles.symlink(targetFragment.toString(), linkPath.toString()); } } @Override - protected String readSymbolicLink(LocalPath path) throws IOException { + protected PathFragment readSymbolicLink(Path path) throws IOException { // Note that the default implementation of readSymbolicLinkUnchecked calls this method and thus // is optimal since we only make one system call in here. String name = path.toString(); long startTime = Profiler.nanoTimeMaybe(); try { - return NativePosixFiles.readlink(name); + return PathFragment.create(NativePosixFiles.readlink(name)); } catch (IOException e) { // EINVAL => not a symbolic link. Anything else is a real error. throw e.getMessage().endsWith("(Invalid argument)") ? new NotASymlinkException(path) : e; @@ -358,45 +349,38 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - public void renameTo(LocalPath sourcePath, LocalPath targetPath) throws IOException { - Lock lock = getPathLock(sourcePath); - lock.lock(); - try { + public void renameTo(Path sourcePath, Path targetPath) throws IOException { + synchronized (sourcePath) { NativePosixFiles.rename(sourcePath.toString(), targetPath.toString()); - } finally { - lock.unlock(); } } @Override - protected long getFileSize(LocalPath path, boolean followSymlinks) throws IOException { + protected long getFileSize(Path path, boolean followSymlinks) throws IOException { return stat(path, followSymlinks).getSize(); } @Override - public boolean delete(LocalPath path) throws IOException { + public boolean delete(Path path) throws IOException { String name = path.toString(); long startTime = Profiler.nanoTimeMaybe(); - Lock lock = getPathLock(path); - lock.lock(); - try { - return NativePosixFiles.remove(name); - } finally { - lock.unlock(); - profiler.logSimpleTask(startTime, ProfilerTask.VFS_DELETE, name); + synchronized (path) { + try { + return NativePosixFiles.remove(name); + } finally { + profiler.logSimpleTask(startTime, ProfilerTask.VFS_DELETE, name); + } } } @Override - protected long getLastModifiedTime(LocalPath path, boolean followSymlinks) throws IOException { + protected long getLastModifiedTime(Path path, boolean followSymlinks) throws IOException { return stat(path, followSymlinks).getLastModifiedTime(); } @Override - public void setLastModifiedTime(LocalPath path, long newTime) throws IOException { - Lock lock = getPathLock(path); - lock.lock(); - try { + public void setLastModifiedTime(Path path, long newTime) throws IOException { + synchronized (path) { if (newTime == -1L) { // "now" NativePosixFiles.utime(path.toString(), true, 0); } else { @@ -404,13 +388,11 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { int unixTime = (int) (newTime / 1000); NativePosixFiles.utime(path.toString(), false, unixTime); } - } finally { - lock.unlock(); } } @Override - public byte[] getxattr(LocalPath path, String name) throws IOException { + public byte[] getxattr(Path path, String name) throws IOException { String pathName = path.toString(); long startTime = Profiler.nanoTimeMaybe(); try { @@ -425,7 +407,7 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected byte[] getDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException { String name = path.toString(); long startTime = Profiler.nanoTimeMaybe(); try { @@ -439,13 +421,8 @@ public class UnixFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected void createFSDependentHardLink(LocalPath linkPath, LocalPath originalPath) + protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException { NativePosixFiles.link(originalPath.toString(), linkPath.toString()); } - - /** Returns a per-path lock. The lock is re-entrant. */ - protected Lock getPathLock(LocalPath path) { - return pathLock.get(path); - } } diff --git a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java index 8b9cd5376a..ef1946bb8e 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystem.java @@ -37,7 +37,7 @@ abstract class AbstractFileSystem extends FileSystem { } @Override - protected InputStream getInputStream(LocalPath path) throws IOException { + protected InputStream getInputStream(Path path) throws IOException { // This loop is a workaround for an apparent bug in FileInputStream.open, which delegates // ultimately to JVM_Open in the Hotspot JVM. This call is not EINTR-safe, so we must do the // retry here. @@ -55,7 +55,7 @@ abstract class AbstractFileSystem extends FileSystem { } /** Returns either normal or profiled FileInputStream. */ - private InputStream createFileInputStream(LocalPath path) throws FileNotFoundException { + private InputStream createFileInputStream(Path path) throws FileNotFoundException { final String name = path.toString(); if (profiler.isActive() && (profiler.isProfiling(ProfilerTask.VFS_READ) @@ -77,7 +77,7 @@ abstract class AbstractFileSystem extends FileSystem { * Returns either normal or profiled FileOutputStream. Should be used by subclasses to create * default OutputStream instance. */ - protected OutputStream createFileOutputStream(LocalPath path, boolean append) + protected OutputStream createFileOutputStream(Path path, boolean append) throws FileNotFoundException { final String name = path.toString(); if (profiler.isActive() @@ -95,7 +95,7 @@ abstract class AbstractFileSystem extends FileSystem { } @Override - protected OutputStream getOutputStream(LocalPath path, boolean append) throws IOException { + protected OutputStream getOutputStream(Path path, boolean append) throws IOException { synchronized (path) { try { return createFileOutputStream(path, append); diff --git a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystemWithCustomStat.java b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystemWithCustomStat.java index b73aa0c4df..875df98bab 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystemWithCustomStat.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/AbstractFileSystemWithCustomStat.java @@ -29,30 +29,30 @@ public abstract class AbstractFileSystemWithCustomStat extends AbstractFileSyste } @Override - protected boolean isFile(LocalPath path, boolean followSymlinks) { + protected boolean isFile(Path path, boolean followSymlinks) { FileStatus stat = statNullable(path, followSymlinks); return stat != null ? stat.isFile() : false; } @Override - protected boolean isSpecialFile(LocalPath path, boolean followSymlinks) { + protected boolean isSpecialFile(Path path, boolean followSymlinks) { FileStatus stat = statNullable(path, followSymlinks); return stat != null ? stat.isSpecialFile() : false; } @Override - protected boolean isSymbolicLink(LocalPath path) { + protected boolean isSymbolicLink(Path path) { FileStatus stat = statNullable(path, false); return stat != null ? stat.isSymbolicLink() : false; } @Override - protected boolean isDirectory(LocalPath path, boolean followSymlinks) { + protected boolean isDirectory(Path path, boolean followSymlinks) { FileStatus stat = statNullable(path, followSymlinks); return stat != null ? stat.isDirectory() : false; } @Override - protected abstract FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException; + protected abstract FileStatus stat(Path path, boolean followSymlinks) throws IOException; } diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java index 0e92b3dbac..fef88b892b 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java @@ -39,6 +39,7 @@ import java.util.List; */ @ThreadSafe public abstract class FileSystem { + /** Type of hash function to use for digesting files. */ // The underlying HashFunctions are immutable and thread safe. @SuppressWarnings("ImmutableEnumChecker") @@ -99,15 +100,15 @@ public abstract class FileSystem { public Path getCachedChildPathInternal(Path path, String childName) { return Path.getCachedChildPathInternal(path, childName, /*cacheable=*/ true); } - } + }; } /** * An exception thrown when attempting to resolve an ordinary file as a symlink. */ protected static final class NotASymlinkException extends IOException { - public NotASymlinkException(LocalPath path) { - super(path.getPathString()); + public NotASymlinkException(Path path) { + super(path.toString()); } } @@ -120,22 +121,24 @@ public abstract class FileSystem { } /** - * Returns an absolute path instance, given an absolute path name, without double slashes, .., or - * . segments. While this method will normalize the path representation by creating a - * structured/parsed representation, it will not cause any IO. (e.g., it will not resolve symbolic - * links if it's a Unix file system. + * Returns an absolute path instance, given an absolute path name, without + * double slashes, .., or . segments. While this method will normalize the + * path representation by creating a structured/parsed representation, it will + * not cause any IO. (e.g., it will not resolve symbolic links if it's a Unix + * file system. */ public Path getPath(String pathName) { return getPath(PathFragment.create(pathName)); } /** - * Returns an absolute path instance, given an absolute path name, without double slashes, .., or - * . segments. While this method will normalize the path representation by creating a - * structured/parsed representation, it will not cause any IO. (e.g., it will not resolve symbolic - * links if it's a Unix file system. + * Returns an absolute path instance, given an absolute path name, without + * double slashes, .., or . segments. While this method will normalize the + * path representation by creating a structured/parsed representation, it will + * not cause any IO. (e.g., it will not resolve symbolic links if it's a Unix + * file system. */ - public final Path getPath(PathFragment pathName) { + public Path getPath(PathFragment pathName) { if (!pathName.isAbsolute()) { throw new IllegalArgumentException(pathName.getPathString() + " (not an absolute path)"); } @@ -162,14 +165,14 @@ public abstract class FileSystem { * <p>Returns true if FileSystem supports the following: * * <ul> - * <li>{@link #setWritable(LocalPath, boolean)} - * <li>{@link #setExecutable(LocalPath, boolean)} + * <li>{@link #setWritable(Path, boolean)} + * <li>{@link #setExecutable(Path, boolean)} * </ul> * * The above calls will result in an {@link UnsupportedOperationException} on a FileSystem where * this method returns {@code false}. */ - public abstract boolean supportsModifications(LocalPath path); + public abstract boolean supportsModifications(Path path); /** * Returns whether or not the FileSystem supports symbolic links. @@ -177,17 +180,17 @@ public abstract class FileSystem { * <p>Returns true if FileSystem supports the following: * * <ul> - * <li>{@link #createSymbolicLink(LocalPath, String)} - * <li>{@link #getFileSize(LocalPath, boolean)} where {@code followSymlinks=false} - * <li>{@link #getLastModifiedTime(LocalPath, boolean)} where {@code followSymlinks=false} - * <li>{@link #readSymbolicLink(LocalPath)} where the link points to a non-existent file + * <li>{@link #createSymbolicLink(Path, PathFragment)} + * <li>{@link #getFileSize(Path, boolean)} where {@code followSymlinks=false} + * <li>{@link #getLastModifiedTime(Path, boolean)} where {@code followSymlinks=false} + * <li>{@link #readSymbolicLink(Path)} where the link points to a non-existent file * </ul> * * The above calls may result in an {@link UnsupportedOperationException} on a FileSystem where * this method returns {@code false}. The implementation can try to emulate these calls at its own * discretion. */ - public abstract boolean supportsSymbolicLinksNatively(LocalPath path); + public abstract boolean supportsSymbolicLinksNatively(Path path); /** * Returns whether or not the FileSystem supports hard links. @@ -195,14 +198,14 @@ public abstract class FileSystem { * <p>Returns true if FileSystem supports the following: * * <ul> - * <li>{@link #createFSDependentHardLink(LocalPath, LocalPath)} + * <li>{@link #createFSDependentHardLink(Path, Path)} * </ul> * * The above calls may result in an {@link UnsupportedOperationException} on a FileSystem where * this method returns {@code false}. The implementation can try to emulate these calls at its own * discretion. */ - protected abstract boolean supportsHardLinksNatively(LocalPath path); + protected abstract boolean supportsHardLinksNatively(Path path); /*** * Returns true if file path is case-sensitive on this file system. Default is true. @@ -212,27 +215,28 @@ public abstract class FileSystem { /** * Returns the type of the file system path belongs to. * - * <p>The string returned is obtained directly from the operating system, so it's a best guess in - * absence of a guaranteed api. + * <p>The string returned is obtained directly from the operating system, so + * it's a best guess in absence of a guaranteed api. * - * <p>This implementation uses <code>/proc/mounts</code> to determine the file system type. + * <p>This implementation uses <code>/proc/mounts</code> to determine the + * file system type. */ - public String getFileSystemType(LocalPath path) { + public String getFileSystemType(Path path) { String fileSystem = "unknown"; int bestMountPointSegmentCount = -1; try { - LocalPath canonicalPath = resolveSymbolicLinks(path); - LocalPath mountTable = path.getRelative("/proc/mounts"); - try (InputStreamReader reader = - new InputStreamReader(getInputStream(mountTable), ISO_8859_1)) { + Path canonicalPath = path.resolveSymbolicLinks(); + Path mountTable = path.getRelative("/proc/mounts"); + try (InputStreamReader reader = new InputStreamReader(mountTable.getInputStream(), + ISO_8859_1)) { for (String line : CharStreams.readLines(reader)) { String[] words = line.split("\\s+"); if (words.length >= 3) { if (!words[1].startsWith("/")) { continue; } - LocalPath mountPoint = LocalPath.create(words[1]); - int segmentCount = mountPoint.split().size(); + Path mountPoint = path.getFileSystem().getPath(words[1]); + int segmentCount = mountPoint.asFragment().segmentCount(); if (canonicalPath.startsWith(mountPoint) && segmentCount > bestMountPointSegmentCount) { bestMountPointSegmentCount = segmentCount; fileSystem = words[2]; @@ -250,62 +254,62 @@ public abstract class FileSystem { * Creates a directory with the name of the current path. See {@link Path#createDirectory} for * specification. */ - public abstract boolean createDirectory(LocalPath path) throws IOException; + public abstract boolean createDirectory(Path path) throws IOException; /** * Returns the size in bytes of the file denoted by {@code path}. See {@link * Path#getFileSize(Symlinks)} for specification. * - * <p>Note: for <@link FileSystem>s where {@link #supportsSymbolicLinksNatively(LocalPath)} - * returns false, this method will throw an {@link UnsupportedOperationException} if {@code + * <p>Note: for <@link FileSystem>s where {@link #supportsSymbolicLinksNatively(Path)} returns + * false, this method will throw an {@link UnsupportedOperationException} if {@code * followSymLinks=false}. */ - protected abstract long getFileSize(LocalPath path, boolean followSymlinks) throws IOException; + protected abstract long getFileSize(Path path, boolean followSymlinks) throws IOException; /** Deletes the file denoted by {@code path}. See {@link Path#delete} for specification. */ - public abstract boolean delete(LocalPath path) throws IOException; + public abstract boolean delete(Path path) throws IOException; /** * Returns the last modification time of the file denoted by {@code path}. See {@link * Path#getLastModifiedTime(Symlinks)} for specification. * - * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(LocalPath)} - * returns false, this method will throw an {@link UnsupportedOperationException} if {@code + * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(Path)} returns + * false, this method will throw an {@link UnsupportedOperationException} if {@code * followSymLinks=false}. */ - protected abstract long getLastModifiedTime(LocalPath path, boolean followSymlinks) - throws IOException; + protected abstract long getLastModifiedTime(Path path, boolean followSymlinks) throws IOException; /** * Sets the last modification time of the file denoted by {@code path}. See {@link * Path#setLastModifiedTime} for specification. */ - public abstract void setLastModifiedTime(LocalPath path, long newTime) throws IOException; + public abstract void setLastModifiedTime(Path path, long newTime) throws IOException; /** - * Returns value of the given extended attribute name or null if attribute does not exist or file - * system does not support extended attributes. Follows symlinks. - * - * <p>Default implementation assumes that file system does not support extended attributes and - * always returns null. Specific file system implementations should override this method if they - * do provide support for extended attributes. + * Returns value of the given extended attribute name or null if attribute + * does not exist or file system does not support extended attributes. Follows symlinks. + * <p>Default implementation assumes that file system does not support + * extended attributes and always returns null. Specific file system + * implementations should override this method if they do provide support + * for extended attributes. * * @param path the file whose extended attribute is to be returned. * @param name the name of the extended attribute key. - * @return the value of the extended attribute associated with 'path', if any, or null if no such - * attribute is defined (ENODATA) or file system does not support extended attributes at all. + * @return the value of the extended attribute associated with 'path', if + * any, or null if no such attribute is defined (ENODATA) or file + * system does not support extended attributes at all. * @throws IOException if the call failed for any other reason. */ - public byte[] getxattr(LocalPath path, String name) throws IOException { + public byte[] getxattr(Path path, String name) throws IOException { return null; } /** - * Gets a fast digest for the given path and hash function type, or {@code null} if there isn't - * one available or the filesystem doesn't support them. This digest should be suitable for - * detecting changes to the file. + * Gets a fast digest for the given path and hash function type, or {@code null} if there + * isn't one available or the filesystem doesn't support them. This digest should be + * suitable for detecting changes to the file. */ - protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException { return null; } @@ -314,7 +318,7 @@ public abstract class FileSystem { * filesystem doesn't support them. This digest should be suitable for detecting changes to the * file. */ - protected final byte[] getFastDigest(LocalPath path) throws IOException { + protected final byte[] getFastDigest(Path path) throws IOException { return getFastDigest(path, digestFunction); } @@ -326,14 +330,15 @@ public abstract class FileSystem { } /** - * Returns the digest of the file denoted by the path, following symbolic links, for the given - * hash digest function. + * Returns the digest of the file denoted by the path, following + * symbolic links, for the given hash digest function. * * @return a new byte array containing the file's digest * @throws IOException if the digest could not be computed for any reason - * <p>Subclasses may (and do) optimize this computation for particular digest functions. + * + * Subclasses may (and do) optimize this computation for particular digest functions. */ - protected byte[] getDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getDigest(final Path path, HashFunction hashFunction) throws IOException { return new ByteSource() { @Override public InputStream openStream() throws IOException { @@ -348,33 +353,34 @@ public abstract class FileSystem { * @return a new byte array containing the file's digest * @throws IOException if the digest could not be computed for any reason */ - protected final byte[] getDigest(LocalPath path) throws IOException { + protected final byte[] getDigest(final Path path) throws IOException { return getDigest(path, digestFunction); } /** - * Returns true if "path" denotes an existing symbolic link. See {@link Path#isSymbolicLink} for - * specification. + * Returns true if "path" denotes an existing symbolic link. See + * {@link Path#isSymbolicLink} for specification. */ - protected abstract boolean isSymbolicLink(LocalPath path); + protected abstract boolean isSymbolicLink(Path path); /** - * Appends a single regular path segment 'child' to 'dir', recursively resolving symbolic links in - * 'child'. 'dir' must be canonical. 'maxLinks' is the maximum number of symbolic links that may - * be traversed before it gives up (the Linux kernel uses 32). + * Appends a single regular path segment 'child' to 'dir', recursively + * resolving symbolic links in 'child'. 'dir' must be canonical. 'maxLinks' is + * the maximum number of symbolic links that may be traversed before it gives + * up (the Linux kernel uses 32). * - * <p>(This method does not need to be synchronized; but the result may be stale in the case of - * concurrent modification.) + * <p>(This method does not need to be synchronized; but the result may be + * stale in the case of concurrent modification.) * - * @throws IOException if 'dir' is not an existing directory; or if stat(child) fails for any - * reason, or if 'child' is a symlink and readlink(child) fails for any reason (e.g. ENOENT, - * EACCES), or if the chain of symbolic links exceeds 'maxLinks'. + * @throws IOException if 'dir' is not an existing directory; or if + * stat(child) fails for any reason, or if 'child' is a symlink and + * readlink(child) fails for any reason (e.g. ENOENT, EACCES), or if + * the chain of symbolic links exceeds 'maxLinks'. */ - protected final LocalPath appendSegment(LocalPath dir, String child, int maxLinks) - throws IOException { - LocalPath naive = dir.getRelative(child); + protected final Path appendSegment(Path dir, String child, int maxLinks) throws IOException { + Path naive = dir.getChild(child); - String linkTarget = resolveOneLink(naive); + PathFragment linkTarget = resolveOneLink(naive); if (linkTarget == null) { return naive; // regular file or directory } @@ -382,15 +388,14 @@ public abstract class FileSystem { if (maxLinks-- == 0) { throw new IOException(naive + " (Too many levels of symbolic links)"); } - LocalPath linkTargetPath = LocalPath.create(linkTarget); - if (linkTargetPath.isAbsolute()) { - dir = linkTargetPath.getDrive(); + if (linkTarget.isAbsolute()) { + dir = getRootDirectory(); } - for (String name : linkTargetPath.split()) { + for (String name : linkTarget.segments()) { if (name.equals(".") || name.isEmpty()) { // no-op } else if (name.equals("..")) { - LocalPath parent = dir.getParentDirectory(); + Path parent = dir.getParentDirectory(); // root's parent is root, when canonicalizing, so this is a no-op. if (parent != null) { dir = parent; } } else { @@ -401,19 +406,21 @@ public abstract class FileSystem { } /** - * Helper method of {@link #resolveSymbolicLinks(LocalPath)}. This method encapsulates the I/O - * component of a full canonicalization operation. Subclasses can (and do) provide more efficient - * implementations. + * Helper method of {@link #resolveSymbolicLinks(Path)}. This method + * encapsulates the I/O component of a full canonicalization operation. + * Subclasses can (and do) provide more efficient implementations. * - * <p>(This method does not need to be synchronized; but the result may be stale in the case of - * concurrent modification.) + * <p>(This method does not need to be synchronized; but the result may be + * stale in the case of concurrent modification.) * - * @param path a path, of which all but the last segment is guaranteed to be canonical - * @return {@link #readSymbolicLink} iff path is a symlink or null iff path exists but is not a - * symlink - * @throws IOException if the file did not exist, or a parent directory could not be searched + * @param path a path, of which all but the last segment is guaranteed to be + * canonical + * @return {@link #readSymbolicLink} iff path is a symlink or null iff + * path exists but is not a symlink + * @throws IOException if the file did not exist, or a parent directory could + * not be searched */ - protected String resolveOneLink(LocalPath path) throws IOException { + protected PathFragment resolveOneLink(Path path) throws IOException { try { return readSymbolicLink(path); } catch (NotASymlinkException e) { @@ -433,25 +440,28 @@ public abstract class FileSystem { } /** - * Returns the canonical path for the given path. See {@link Path#resolveSymbolicLinks} for - * specification. + * Returns the canonical path for the given path. See + * {@link Path#resolveSymbolicLinks} for specification. */ - protected LocalPath resolveSymbolicLinks(LocalPath path) throws IOException { - LocalPath parentNode = path.getParentDirectory(); + protected Path resolveSymbolicLinks(Path path) + throws IOException { + Path parentNode = path.getParentDirectory(); return parentNode == null ? path // (root) : appendSegment(resolveSymbolicLinks(parentNode), path.getBaseName(), 32); } /** - * Returns the status of a file. See {@link Path#stat(Symlinks)} for specification. + * Returns the status of a file. See {@link Path#stat(Symlinks)} for + * specification. * - * <p>The default implementation of this method is a "lazy" one, based on other accessor methods - * such as {@link #isFile}, etc. Subclasses may provide more efficient specializations. However, - * we still try to follow Unix-like semantics of failing fast in case of non-existent files (or in - * case of permission issues). + * <p>The default implementation of this method is a "lazy" one, based on + * other accessor methods such as {@link #isFile}, etc. Subclasses may provide + * more efficient specializations. However, we still try to follow Unix-like + * semantics of failing fast in case of non-existent files (or in case of + * permission issues). */ - protected FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + protected FileStatus stat(final Path path, final boolean followSymlinks) throws IOException { FileStatus status = new FileStatus() { volatile Boolean isFile; volatile Boolean isDirectory; @@ -516,8 +526,10 @@ public abstract class FileSystem { return status; } - /** Like stat(), but returns null on failures instead of throwing. */ - protected FileStatus statNullable(LocalPath path, boolean followSymlinks) { + /** + * Like stat(), but returns null on failures instead of throwing. + */ + protected FileStatus statNullable(Path path, boolean followSymlinks) { try { return stat(path, followSymlinks); } catch (IOException e) { @@ -526,12 +538,12 @@ public abstract class FileSystem { } /** - * Like {@link #stat}, but returns null if the file is not found (corresponding to {@code ENOENT} - * or {@code ENOTDIR} in Unix's stat(2) function) instead of throwing. Note that this - * implementation does <i>not</i> successfully catch {@code ENOTDIR} exceptions. If the + * Like {@link #stat}, but returns null if the file is not found (corresponding to + * {@code ENOENT} or {@code ENOTDIR} in Unix's stat(2) function) instead of throwing. Note that + * this implementation does <i>not</i> successfully catch {@code ENOTDIR} exceptions. If the * instantiated filesystem can catch such errors, it should override this method to do so. */ - protected FileStatus statIfFound(LocalPath path, boolean followSymlinks) throws IOException { + protected FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException { try { return stat(path, followSymlinks); } catch (FileNotFoundException e) { @@ -540,65 +552,65 @@ public abstract class FileSystem { } /** - * Returns true iff {@code path} denotes an existing directory. See {@link - * Path#isDirectory(Symlinks)} for specification. + * Returns true iff {@code path} denotes an existing directory. See + * {@link Path#isDirectory(Symlinks)} for specification. */ - protected abstract boolean isDirectory(LocalPath path, boolean followSymlinks); + protected abstract boolean isDirectory(Path path, boolean followSymlinks); /** - * Returns true iff {@code path} denotes an existing regular or special file. See {@link - * Path#isFile(Symlinks)} for specification. + * Returns true iff {@code path} denotes an existing regular or special file. + * See {@link Path#isFile(Symlinks)} for specification. */ - protected abstract boolean isFile(LocalPath path, boolean followSymlinks); + protected abstract boolean isFile(Path path, boolean followSymlinks); /** - * Returns true iff {@code path} denotes a special file. See {@link Path#isSpecialFile(Symlinks)} - * for specification. + * Returns true iff {@code path} denotes a special file. + * See {@link Path#isSpecialFile(Symlinks)} for specification. */ - protected abstract boolean isSpecialFile(LocalPath path, boolean followSymlinks); + protected abstract boolean isSpecialFile(Path path, boolean followSymlinks); /** * Creates a symbolic link. See {@link Path#createSymbolicLink(Path)} for specification. * - * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(LocalPath)} - * returns false, this method will throw an {@link UnsupportedOperationException} + * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(Path)} returns + * false, this method will throw an {@link UnsupportedOperationException} */ - protected abstract void createSymbolicLink(LocalPath linkPath, String targetFragment) + protected abstract void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException; /** * Returns the target of a symbolic link. See {@link Path#readSymbolicLink} for specification. * - * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(LocalPath)} - * returns false, this method will throw an {@link UnsupportedOperationException} if the link - * points to a non-existent file. + * <p>Note: for {@link FileSystem}s where {@link #supportsSymbolicLinksNatively(Path)} returns + * false, this method will throw an {@link UnsupportedOperationException} if the link points to a + * non-existent file. * * @throws NotASymlinkException if the current path is not a symbolic link * @throws IOException if the contents of the link could not be read for any reason. */ - protected abstract String readSymbolicLink(LocalPath path) throws IOException; + protected abstract PathFragment readSymbolicLink(Path path) throws IOException; /** * Returns the target of a symbolic link, under the assumption that the given path is indeed a - * symbolic link (this assumption permits efficient implementations). See {@link - * Path#readSymbolicLinkUnchecked} for specification. + * symbolic link (this assumption permits efficient implementations). See + * {@link Path#readSymbolicLinkUnchecked} for specification. * * @throws IOException if the contents of the link could not be read for any reason. */ - protected String readSymbolicLinkUnchecked(LocalPath path) throws IOException { + protected PathFragment readSymbolicLinkUnchecked(Path path) throws IOException { return readSymbolicLink(path); } /** Returns true iff this path denotes an existing file of any kind. Follows symbolic links. */ - public boolean exists(LocalPath path) { + public boolean exists(Path path) { return exists(path, true); } /** - * Returns true iff {@code path} denotes an existing file of any kind. See {@link - * Path#exists(Symlinks)} for specification. + * Returns true iff {@code path} denotes an existing file of any kind. See + * {@link Path#exists(Symlinks)} for specification. */ - protected abstract boolean exists(LocalPath path, boolean followSymlinks); + protected abstract boolean exists(Path path, boolean followSymlinks); /** * Returns a collection containing the names of all entities within the directory denoted by the @@ -606,7 +618,7 @@ public abstract class FileSystem { * * @throws IOException if there was an error reading the directory entries */ - protected abstract Collection<String> getDirectoryEntries(LocalPath path) throws IOException; + protected abstract Collection<String> getDirectoryEntries(Path path) throws IOException; protected static Dirent.Type direntFromStat(FileStatus stat) { if (stat == null) { @@ -625,19 +637,19 @@ public abstract class FileSystem { } /** - * Returns a Dirents structure, listing the names of all entries within the directory {@code - * path}, plus their types (file, directory, other). + * Returns a Dirents structure, listing the names of all entries within the + * directory {@code path}, plus their types (file, directory, other). * - * @param followSymlinks whether to follow symlinks when determining the file types of individual - * directory entries. No matter the value of this parameter, symlinks are followed when - * resolving the directory whose entries are to be read. + * @param followSymlinks whether to follow symlinks when determining the file types of + * individual directory entries. No matter the value of this parameter, symlinks are + * followed when resolving the directory whose entries are to be read. * @throws IOException if there was an error reading the directory entries */ - protected Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) throws IOException { + protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException { Collection<String> children = getDirectoryEntries(path); List<Dirent> dirents = Lists.newArrayListWithCapacity(children.size()); for (String child : children) { - LocalPath childPath = path.getRelative(child); + Path childPath = path.getChild(child); Dirent.Type type = direntFromStat(statNullable(childPath, followSymlinks)); dirents.add(new Dirent(child, type)); } @@ -649,54 +661,54 @@ public abstract class FileSystem { * * @throws IOException if there was an error reading the file's metadata */ - protected abstract boolean isReadable(LocalPath path) throws IOException; + protected abstract boolean isReadable(Path path) throws IOException; /** * Sets the file to readable (if the argument is true) or non-readable (if the argument is false) * - * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(LocalPath)} returns false - * or which do not support unreadable files, this method will throw an {@link + * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(Path)} returns false or + * which do not support unreadable files, this method will throw an {@link * UnsupportedOperationException}. * * @throws IOException if there was an error reading or writing the file's metadata */ - protected abstract void setReadable(LocalPath path, boolean readable) throws IOException; + protected abstract void setReadable(Path path, boolean readable) throws IOException; /** * Returns true iff the file represented by {@code path} is writable. * * @throws IOException if there was an error reading the file's metadata */ - protected abstract boolean isWritable(LocalPath path) throws IOException; + protected abstract boolean isWritable(Path path) throws IOException; /** * Sets the file to writable (if the argument is true) or non-writable (if the argument is false) * - * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(LocalPath)} returns false, - * this method will throw an {@link UnsupportedOperationException}. + * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(Path)} returns false, this + * method will throw an {@link UnsupportedOperationException}. * * @throws IOException if there was an error reading or writing the file's metadata */ - public abstract void setWritable(LocalPath path, boolean writable) throws IOException; + public abstract void setWritable(Path path, boolean writable) throws IOException; /** * Returns true iff the file represented by the path is executable. * * @throws IOException if there was an error reading the file's metadata */ - protected abstract boolean isExecutable(LocalPath path) throws IOException; + protected abstract boolean isExecutable(Path path) throws IOException; /** * Sets the file to executable, if the argument is true. It is currently not supported to unset * the executable status of a file, so {code executable=false} yields an {@link * UnsupportedOperationException}. * - * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(LocalPath)} returns false, - * this method will throw an {@link UnsupportedOperationException}. + * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(Path)} returns false, this + * method will throw an {@link UnsupportedOperationException}. * * @throws IOException if there was an error reading or writing the file's metadata */ - protected abstract void setExecutable(LocalPath path, boolean executable) throws IOException; + protected abstract void setExecutable(Path path, boolean executable) throws IOException; /** * Sets the file permissions. If permission changes on this {@link FileSystem} are slow (e.g. one @@ -704,12 +716,12 @@ public abstract class FileSystem { * individually. If this {@link FileSystem} does not support group or others permissions, those * bits will be ignored. * - * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(LocalPath)} returns false, - * this method will throw an {@link UnsupportedOperationException}. + * <p>Note: for {@link FileSystem}s where {@link #supportsModifications(Path)} returns false, this + * method will throw an {@link UnsupportedOperationException}. * * @throws IOException if there was an error reading or writing the file's metadata */ - protected void chmod(LocalPath path, int mode) throws IOException { + protected void chmod(Path path, int mode) throws IOException { setReadable(path, (mode & 0400) != 0); setWritable(path, (mode & 0200) != 0); setExecutable(path, (mode & 0100) != 0); @@ -720,14 +732,14 @@ public abstract class FileSystem { * * @throws IOException if there was an error opening the file for reading */ - protected abstract InputStream getInputStream(LocalPath path) throws IOException; + protected abstract InputStream getInputStream(Path path) throws IOException; /** * Creates an OutputStream accessing the file denoted by path. * * @throws IOException if there was an error opening the file for writing */ - protected final OutputStream getOutputStream(LocalPath path) throws IOException { + protected final OutputStream getOutputStream(Path path) throws IOException { return getOutputStream(path, false); } @@ -737,14 +749,13 @@ public abstract class FileSystem { * @param append whether to open the output stream in append mode * @throws IOException if there was an error opening the file for writing */ - protected abstract OutputStream getOutputStream(LocalPath path, boolean append) - throws IOException; + protected abstract OutputStream getOutputStream(Path path, boolean append) throws IOException; /** * Renames the file denoted by "sourceNode" to the location "targetNode". See {@link * Path#renameTo} for specification. */ - public abstract void renameTo(LocalPath sourcePath, LocalPath targetPath) throws IOException; + public abstract void renameTo(Path sourcePath, Path targetPath) throws IOException; /** * Create a new hard link file at "linkPath" for file at "originalPath". @@ -753,9 +764,9 @@ public abstract class FileSystem { * @param originalPath The path of the original file * @throws IOException if the original file does not exist or the link file already exists */ - protected void createHardLink(LocalPath linkPath, LocalPath originalPath) throws IOException { + protected void createHardLink(Path linkPath, Path originalPath) throws IOException { - if (!exists(originalPath, true)) { + if (!originalPath.exists()) { throw new FileNotFoundException( "File \"" + originalPath.getBaseName() @@ -764,7 +775,7 @@ public abstract class FileSystem { + "\" does not exist"); } - if (exists(linkPath, true)) { + if (linkPath.exists()) { throw new FileAlreadyExistsException( "New link file \"" + linkPath.getBaseName() + "\" already exists"); } @@ -779,13 +790,14 @@ public abstract class FileSystem { * @param originalPath The path of the original file * @throws IOException if there was an I/O error */ - protected abstract void createFSDependentHardLink(LocalPath linkPath, LocalPath originalPath) + protected abstract void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException; /** - * Prefetch all directories and symlinks within the package rooted at "path". Enter at most - * "maxDirs" total directories. Specializations for high-latency remote filesystems may wish to + * Prefetch all directories and symlinks within the package + * rooted at "path". Enter at most "maxDirs" total directories. + * Specializations for high-latency remote filesystems may wish to * implement this in order to warm the filesystem's internal caches. */ - protected void prefetchPackageAsync(LocalPath path, int maxDirs) {} + protected void prefetchPackageAsync(Path path, int maxDirs) { } } 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 930a8beb13..064e4b2e16 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 @@ -378,37 +378,23 @@ public class FileSystemUtils { } public static ByteSource asByteSource(final Path path) { - return asByteSource(path.getFileSystem(), path.getLocalPath()); - } - - public static ByteSink asByteSink(final Path path, final boolean append) { - return asByteSink(path.getFileSystem(), path.getLocalPath(), append); - } - - public static ByteSink asByteSink(final Path path) { - return asByteSink(path, false); - } - - public static ByteSource asByteSource(FileSystem fileSystem, LocalPath path) { return new ByteSource() { - @Override - public InputStream openStream() throws IOException { - return fileSystem.getInputStream(path); + @Override public InputStream openStream() throws IOException { + return path.getInputStream(); } }; } - public static ByteSink asByteSink(FileSystem fileSystem, LocalPath path, final boolean append) { + public static ByteSink asByteSink(final Path path, final boolean append) { return new ByteSink() { - @Override - public OutputStream openStream() throws IOException { - return fileSystem.getOutputStream(path, append); + @Override public OutputStream openStream() throws IOException { + return path.getOutputStream(append); } }; } - public static ByteSink asByteSink(FileSystem fileSystem, LocalPath path) { - return asByteSink(fileSystem, path, false); + public static ByteSink asByteSink(final Path path) { + return asByteSink(path, false); } /** @@ -422,34 +408,18 @@ public class FileSystemUtils { */ @ThreadSafe // but not atomic public static void copyFile(Path from, Path to) throws IOException { - copyFile(from.getFileSystem(), from.getLocalPath(), to.getFileSystem(), to.getLocalPath()); - } - - /** - * Copies the file from location "from" to location "to", while overwriting a potentially existing - * "to". File's last modified time, executable and writable bits are also preserved. - * - * <p>If no error occurs, the method returns normally. If a parent directory does not exist, a - * FileNotFoundException is thrown. An IOException is thrown when other erroneous situations - * occur. (e.g. read errors) - */ - @ThreadSafe // but not atomic - public static void copyFile( - FileSystem fromFileSystem, LocalPath from, FileSystem toFileSystem, LocalPath to) - throws IOException { try { - toFileSystem.delete(to); + to.delete(); } catch (IOException e) { throw new IOException("error copying file: " + "couldn't delete destination: " + e.getMessage()); } - asByteSource(fromFileSystem, from).copyTo(asByteSink(toFileSystem, to)); - toFileSystem.setLastModifiedTime( - to, fromFileSystem.getLastModifiedTime(from, true)); // Preserve mtime. - if (!fromFileSystem.isWritable(from)) { - toFileSystem.setWritable(to, false); // Make file read-only if original was read-only. + asByteSource(from).copyTo(asByteSink(to)); + to.setLastModifiedTime(from.getLastModifiedTime()); // Preserve mtime. + if (!from.isWritable()) { + to.setWritable(false); // Make file read-only if original was read-only. } - toFileSystem.setExecutable(to, fromFileSystem.isExecutable(from)); // Copy executable bit. + to.setExecutable(from.isExecutable()); // Copy executable bit. } /** @@ -695,7 +665,7 @@ public class FileSystemUtils { if (filesystem instanceof UnionFileSystem) { // If using UnionFS, make sure that we do not traverse filesystem boundaries when creating // parent directories by rehoming the path on the most specific filesystem. - FileSystem delegate = ((UnionFileSystem) filesystem).getDelegate(dir.getLocalPath()); + FileSystem delegate = ((UnionFileSystem) filesystem).getDelegate(dir); dir = delegate.getPath(dir.asFragment()); } @@ -787,18 +757,7 @@ public class FileSystemUtils { * @throws IOException if there was an error */ public static void writeContentAsLatin1(Path outputFile, String content) throws IOException { - writeContentAsLatin1(outputFile.getFileSystem(), outputFile.getLocalPath(), content); - } - - /** - * Writes the specified String as ISO-8859-1 (latin1) encoded bytes to the file. Follows symbolic - * links. - * - * @throws IOException if there was an error - */ - public static void writeContentAsLatin1( - FileSystem fileSystem, LocalPath outputFile, String content) throws IOException { - writeContent(fileSystem, outputFile, ISO_8859_1, content); + writeContent(outputFile, ISO_8859_1, content); } /** @@ -809,18 +768,7 @@ public class FileSystemUtils { */ public static void writeContent(Path outputFile, Charset charset, String content) throws IOException { - writeContent(outputFile.getFileSystem(), outputFile.getLocalPath(), charset, content); - } - - /** - * Writes the specified String using the specified encoding to the file. Follows symbolic links. - * - * @throws IOException if there was an error - */ - public static void writeContent( - FileSystem fileSystem, LocalPath outputFile, Charset charset, String content) - throws IOException { - asByteSink(fileSystem, outputFile).asCharSink(charset).write(content); + asByteSink(outputFile).asCharSink(charset).write(content); } /** @@ -1029,7 +977,7 @@ public class FileSystemUtils { * Returns the type of the file system path belongs to. */ public static String getFileSystem(Path path) { - return path.getFileSystem().getFileSystemType(path.getLocalPath()); + return path.getFileSystem().getFileSystemType(path); } /** diff --git a/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java index 13813882c5..dca8b95906 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java @@ -14,7 +14,6 @@ package com.google.devtools.build.lib.vfs; import com.google.common.annotations.VisibleForTesting; -import com.google.common.util.concurrent.Striped; import com.google.devtools.build.lib.clock.Clock; import com.google.devtools.build.lib.clock.JavaClock; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; @@ -29,7 +28,6 @@ import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collection; -import java.util.concurrent.locks.Lock; /** * A FileSystem that does not use any JNI and hence, does not require a shared library be present at @@ -41,7 +39,6 @@ import java.util.concurrent.locks.Lock; */ @ThreadSafe public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { - private final Striped<Lock> pathLock = Striped.lock(64); private static final LinkOption[] NO_LINK_OPTION = new LinkOption[0]; // This isn't generally safe; we rely on the file system APIs not modifying the array. private static final LinkOption[] NOFOLLOW_LINKS_OPTION = @@ -69,7 +66,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { this.clock = clock; } - protected File getIoFile(LocalPath path) { + protected File getIoFile(Path path) { return new File(path.toString()); } @@ -81,7 +78,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { * avoids extra allocations and does not lose track of the underlying Java filesystem, which is * useful for some in-memory filesystem implementations like JimFS. */ - protected java.nio.file.Path getNioPath(LocalPath path) { + protected java.nio.file.Path getNioPath(Path path) { return Paths.get(path.toString()); } @@ -90,7 +87,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected Collection<String> getDirectoryEntries(LocalPath path) throws IOException { + protected Collection<String> getDirectoryEntries(Path path) throws IOException { File file = getIoFile(path); String[] entries = null; long startTime = Profiler.nanoTimeMaybe(); @@ -116,7 +113,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected boolean exists(LocalPath path, boolean followSymlinks) { + protected boolean exists(Path path, boolean followSymlinks) { java.nio.file.Path nioPath = getNioPath(path); long startTime = Profiler.nanoTimeMaybe(); try { @@ -127,7 +124,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected boolean isReadable(LocalPath path) throws IOException { + protected boolean isReadable(Path path) throws IOException { File file = getIoFile(path); long startTime = Profiler.nanoTimeMaybe(); try { @@ -141,7 +138,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected boolean isWritable(LocalPath path) throws IOException { + protected boolean isWritable(Path path) throws IOException { File file = getIoFile(path); long startTime = Profiler.nanoTimeMaybe(); try { @@ -159,7 +156,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected boolean isExecutable(LocalPath path) throws IOException { + protected boolean isExecutable(Path path) throws IOException { File file = getIoFile(path); long startTime = Profiler.nanoTimeMaybe(); try { @@ -173,7 +170,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected void setReadable(LocalPath path, boolean readable) throws IOException { + protected void setReadable(Path path, boolean readable) throws IOException { File file = getIoFile(path); if (!file.exists()) { throw new FileNotFoundException(path + ERR_NO_SUCH_FILE_OR_DIR); @@ -182,7 +179,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - public void setWritable(LocalPath path, boolean writable) throws IOException { + public void setWritable(Path path, boolean writable) throws IOException { File file = getIoFile(path); if (!file.exists()) { throw new FileNotFoundException(path + ERR_NO_SUCH_FILE_OR_DIR); @@ -191,7 +188,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected void setExecutable(LocalPath path, boolean executable) throws IOException { + protected void setExecutable(Path path, boolean executable) throws IOException { File file = getIoFile(path); if (!file.exists()) { throw new FileNotFoundException(path + ERR_NO_SUCH_FILE_OR_DIR); @@ -200,17 +197,17 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - public boolean supportsModifications(LocalPath path) { + public boolean supportsModifications(Path path) { return true; } @Override - public boolean supportsSymbolicLinksNatively(LocalPath path) { + public boolean supportsSymbolicLinksNatively(Path path) { return true; } @Override - public boolean supportsHardLinksNatively(LocalPath path) { + public boolean supportsHardLinksNatively(Path path) { return true; } @@ -220,26 +217,22 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - public boolean createDirectory(LocalPath path) throws IOException { + public boolean createDirectory(Path path) throws IOException { // We always synchronize on the current path before doing it on the parent path and file system // path structure ensures that this locking order will never be reversed. // When refactoring, check that subclasses still work as expected and there can be no // deadlocks. - Lock lock = getPathLock(path); - lock.lock(); - try { + synchronized (path) { File file = getIoFile(path); if (file.mkdir()) { return true; } // We will be checking the state of the parent path as well. Synchronize on it before - // attempting anything. The striped lock used is re-entrant so this is safe. - LocalPath parentDirectory = path.getParentDirectory(); - Lock parentLock = getPathLock(parentDirectory); - parentLock.lock(); - try { + // attempting anything. + Path parentDirectory = path.getParentDirectory(); + synchronized (parentDirectory) { if (fileIsSymbolicLink(file)) { throw new IOException(path + ERR_FILE_EXISTS); } @@ -261,11 +254,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { // Parent exists, is writable, yet we can't create our directory. throw new FileNotFoundException(path.getParentDirectory() + ERR_NOT_A_DIRECTORY); } - } finally { - parentLock.unlock(); } - } finally { - lock.unlock(); } } @@ -288,10 +277,11 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected void createSymbolicLink(LocalPath linkPath, String targetFragment) throws IOException { + protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) + throws IOException { java.nio.file.Path nioPath = getNioPath(linkPath); try { - Files.createSymbolicLink(nioPath, Paths.get(targetFragment)); + Files.createSymbolicLink(nioPath, Paths.get(targetFragment.getPathString())); } catch (java.nio.file.FileAlreadyExistsException e) { throw new IOException(linkPath + ERR_FILE_EXISTS); } catch (java.nio.file.AccessDeniedException e) { @@ -302,12 +292,12 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected String readSymbolicLink(LocalPath path) throws IOException { + protected PathFragment readSymbolicLink(Path path) throws IOException { java.nio.file.Path nioPath = getNioPath(path); long startTime = Profiler.nanoTimeMaybe(); try { String link = Files.readSymbolicLink(nioPath).toString(); - return link; + return PathFragment.create(link); } catch (java.nio.file.NotLinkException e) { throw new NotASymlinkException(path); } catch (java.nio.file.NoSuchFileException e) { @@ -318,10 +308,8 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - public void renameTo(LocalPath sourcePath, LocalPath targetPath) throws IOException { - Lock lock = getPathLock(sourcePath); - lock.lock(); - try { + public void renameTo(Path sourcePath, Path targetPath) throws IOException { + synchronized (sourcePath) { File sourceFile = getIoFile(sourcePath); File targetFile = getIoFile(targetPath); if (!sourceFile.renameTo(targetFile)) { @@ -342,13 +330,11 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { throw new FileAccessException(sourcePath + " -> " + targetPath + ERR_PERMISSION_DENIED); } } - } finally { - lock.unlock(); } } @Override - protected long getFileSize(LocalPath path, boolean followSymlinks) throws IOException { + protected long getFileSize(Path path, boolean followSymlinks) throws IOException { long startTime = Profiler.nanoTimeMaybe(); try { return stat(path, followSymlinks).getSize(); @@ -358,7 +344,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - public boolean delete(LocalPath path) throws IOException { + public boolean delete(Path path) throws IOException { File file = getIoFile(path); long startTime = Profiler.nanoTimeMaybe(); synchronized (path) { @@ -381,7 +367,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected long getLastModifiedTime(LocalPath path, boolean followSymlinks) throws IOException { + protected long getLastModifiedTime(Path path, boolean followSymlinks) throws IOException { File file = getIoFile(path); long startTime = Profiler.nanoTimeMaybe(); try { @@ -396,7 +382,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - public void setLastModifiedTime(LocalPath path, long newTime) throws IOException { + public void setLastModifiedTime(Path path, long newTime) throws IOException { File file = getIoFile(path); if (!file.setLastModified(newTime == -1L ? clock.currentTimeMillis() : newTime)) { if (!file.exists()) { @@ -410,7 +396,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected byte[] getDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException { String name = path.toString(); long startTime = Profiler.nanoTimeMaybe(); try { @@ -421,15 +407,17 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } /** - * Returns the status of a file. See {@link Path#stat(Symlinks)} for specification. + * Returns the status of a file. See {@link Path#stat(Symlinks)} for + * specification. * - * <p>The default implementation of this method is a "lazy" one, based on other accessor methods - * such as {@link #isFile}, etc. Subclasses may provide more efficient specializations. However, - * we still try to follow Unix-like semantics of failing fast in case of non-existent files (or in - * case of permission issues). + * <p>The default implementation of this method is a "lazy" one, based on + * other accessor methods such as {@link #isFile}, etc. Subclasses may provide + * more efficient specializations. However, we still try to follow Unix-like + * semantics of failing fast in case of non-existent files (or in case of + * permission issues). */ @Override - protected FileStatus stat(LocalPath path, final boolean followSymlinks) throws IOException { + protected FileStatus stat(final Path path, final boolean followSymlinks) throws IOException { java.nio.file.Path nioPath = getNioPath(path); final BasicFileAttributes attributes; try { @@ -486,7 +474,7 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected FileStatus statIfFound(LocalPath path, boolean followSymlinks) { + protected FileStatus statIfFound(Path path, boolean followSymlinks) { try { return stat(path, followSymlinks); } catch (FileNotFoundException e) { @@ -503,15 +491,10 @@ public class JavaIoFileSystem extends AbstractFileSystemWithCustomStat { } @Override - protected void createFSDependentHardLink(LocalPath linkPath, LocalPath originalPath) + protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException { Files.createLink( java.nio.file.Paths.get(linkPath.toString()), java.nio.file.Paths.get(originalPath.toString())); } - - /** Returns a per-path lock. The lock is re-entrant. */ - protected Lock getPathLock(LocalPath path) { - return pathLock.get(path); - } } diff --git a/src/main/java/com/google/devtools/build/lib/vfs/LocalPath.java b/src/main/java/com/google/devtools/build/lib/vfs/LocalPath.java index b05c89f7bf..a32a4e356c 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/LocalPath.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/LocalPath.java @@ -15,14 +15,12 @@ package com.google.devtools.build.lib.vfs; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.windows.WindowsShortPath; import com.google.devtools.build.lib.windows.jni.WindowsFileOperations; import java.io.IOException; -import java.util.List; -import java.util.regex.Pattern; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; /** @@ -52,8 +50,6 @@ public final class LocalPath implements Comparable<LocalPath> { public static final LocalPath EMPTY = create(""); - private static final Splitter PATH_SPLITTER = Splitter.on('/').omitEmptyStrings(); - private final String path; private final int driveStrLength; // 0 for relative paths, 1 on Unix, 3 on Windows private final OsPathPolicy os; @@ -232,33 +228,19 @@ public final class LocalPath implements Comparable<LocalPath> { * Splits a path into its constituent parts. The root is not included. This is an inefficient * operation and should be avoided. */ - public List<String> split() { - List<String> segments = PATH_SPLITTER.splitToList(path); - if (driveStrLength > 1) { - return segments.subList(1, segments.size()); + public String[] split() { + String[] segments = path.split("/"); + if (driveStrLength > 0) { + // String#split("/") for some reason returns a zero-length array + // String#split("/hello") returns a 2-length array, so this makes little sense + if (segments.length == 0) { + return segments; + } + return Arrays.copyOfRange(segments, 1, segments.length); } return segments; } - /** Returns the drive of this local path, eg. "/" on Unix or "C:/" on Windows. */ - public LocalPath getDrive() { - if (driveStrLength == 0) { - throw new IllegalArgumentException("Cannot get mount of non-absolute path."); - } - return new LocalPath(path.substring(0, driveStrLength), driveStrLength, os); - } - - /** - * Returns whether this is the root of the entire file system. - * - * <p>Please avoid this method. On Unix, this corresponds to the '/' mount point. Windows drives - * (C:/) do not have a parent and are not the root of the entire file system, so do not return - * true. - */ - public boolean isRoot() { - return os.isRoot(this); - } - /** * Returns whether this path is an ancestor of another path. * @@ -357,14 +339,10 @@ public final class LocalPath implements Comparable<LocalPath> { char getSeparator(); boolean isCaseSensitive(); - - boolean isRoot(LocalPath localPath); } @VisibleForTesting static class UnixOsPathPolicy implements OsPathPolicy { - private static Splitter UNIX_PATH_SPLITTER = - Splitter.on(Pattern.compile("/+")).omitEmptyStrings(); @Override public int needsToNormalize(String path) { @@ -384,7 +362,7 @@ public final class LocalPath implements Comparable<LocalPath> { dotCount = c == '.' ? dotCount + 1 : 0; prevChar = c; } - if ((n > 1 && prevChar == '/') || dotCount == 1 || dotCount == 2) { + if (prevChar == '/' || dotCount == 1 || dotCount == 2) { return NEEDS_NORMALIZE; } return NORMALIZED; @@ -399,8 +377,8 @@ public final class LocalPath implements Comparable<LocalPath> { return path; } boolean isAbsolute = path.charAt(0) == '/'; - String[] segments = Iterables.toArray(UNIX_PATH_SPLITTER.split(path), String.class); - int segmentCount = removeRelativePaths(segments, 0); + String[] segments = path.split("/+"); + int segmentCount = removeRelativePaths(segments, isAbsolute ? 1 : 0); StringBuilder sb = new StringBuilder(path.length()); if (isAbsolute) { sb.append('/'); @@ -447,11 +425,6 @@ public final class LocalPath implements Comparable<LocalPath> { public boolean isCaseSensitive() { return true; } - - @Override - public boolean isRoot(LocalPath localPath) { - return localPath.path.equals("/"); - } } /** Mac is a unix file system that is case insensitive. */ @@ -478,9 +451,8 @@ public final class LocalPath implements Comparable<LocalPath> { private static final int NEEDS_SHORT_PATH_NORMALIZATION = NEEDS_NORMALIZE + 1; - private static Splitter WINDOWS_PATH_SPLITTER = - Splitter.on(Pattern.compile("[\\\\/]+")).omitEmptyStrings(); - + // msys root, used to resolve paths from msys starting with "/" + private static final AtomicReference<String> UNIX_ROOT = new AtomicReference<>(null); private final ShortPathResolver shortPathResolver; interface ShortPathResolver { @@ -510,6 +482,10 @@ public final class LocalPath implements Comparable<LocalPath> { public int needsToNormalize(String path) { int n = path.length(); int normalizationLevel = 0; + // Check for unix path + if (n > 0 && path.charAt(0) == '/') { + normalizationLevel = Math.max(normalizationLevel, NEEDS_NORMALIZE); + } int dotCount = 0; char prevChar = 0; int segmentBeginIndex = 0; // The start index of the current path index @@ -541,7 +517,7 @@ public final class LocalPath implements Comparable<LocalPath> { dotCount = c == '.' ? dotCount + 1 : 0; prevChar = c; } - if ((n > 1 && prevChar == '/') || dotCount == 1 || dotCount == 2) { + if (prevChar == '/' || dotCount == 1 || dotCount == 2) { normalizationLevel = Math.max(normalizationLevel, NEEDS_NORMALIZE); } return normalizationLevel; @@ -558,19 +534,26 @@ public final class LocalPath implements Comparable<LocalPath> { path = resolvedPath; } } - String[] segments = Iterables.toArray(WINDOWS_PATH_SPLITTER.splitToList(path), String.class); + String[] segments = path.split("[\\\\/]+"); int driveStrLength = getDriveStrLength(path); boolean isAbsolute = driveStrLength > 0; - int segmentSkipCount = isAbsolute && driveStrLength > 1 ? 1 : 0; + int segmentSkipCount = isAbsolute ? 1 : 0; StringBuilder sb = new StringBuilder(path.length()); if (isAbsolute) { - char c = path.charAt(0); - if (c == '/') { - sb.append('/'); - } else { - sb.append(Character.toUpperCase(c)); + char driveLetter = path.charAt(0); + sb.append(Character.toUpperCase(driveLetter)); + sb.append(":/"); + } + // unix path support + if (!path.isEmpty() && path.charAt(0) == '/') { + if (path.length() == 2 || (path.length() > 2 && path.charAt(2) == '/')) { + sb.append(Character.toUpperCase(path.charAt(1))); sb.append(":/"); + segmentSkipCount = 2; + } else { + String unixRoot = getUnixRoot(); + sb.append(unixRoot); } } int segmentCount = removeRelativePaths(segments, segmentSkipCount); @@ -587,12 +570,6 @@ public final class LocalPath implements Comparable<LocalPath> { @Override public int getDriveStrLength(String path) { int n = path.length(); - if (n == 0) { - return 0; - } - if (path.charAt(0) == '/') { - return 1; - } if (n < 3) { return 0; } @@ -645,10 +622,44 @@ public final class LocalPath implements Comparable<LocalPath> { return false; } - @Override - public boolean isRoot(LocalPath localPath) { - // Return true for Unix paths for testing - return localPath.path.equals("/"); + private String getUnixRoot() { + String value = UNIX_ROOT.get(); + if (value == null) { + String jvmFlag = "bazel.windows_unix_root"; + value = determineUnixRoot(jvmFlag); + if (value == null) { + throw new IllegalStateException( + String.format( + "\"%1$s\" JVM flag is not set. Use the --host_jvm_args flag or export the " + + "BAZEL_SH environment variable. For example " + + "\"--host_jvm_args=-D%1$s=c:/tools/msys64\" or " + + "\"set BAZEL_SH=c:/tools/msys64/usr/bin/bash.exe\".", + jvmFlag)); + } + if (getDriveStrLength(value) != 3) { + throw new IllegalStateException( + String.format("\"%s\" must be an absolute path, got: \"%s\"", jvmFlag, value)); + } + value = value.replace('\\', '/'); + if (value.length() > 3 && value.endsWith("/")) { + value = value.substring(0, value.length() - 1); + } + UNIX_ROOT.set(value); + } + return value; + } + + private String determineUnixRoot(String jvmArgName) { + // Get the path from a JVM flag, if specified. + String path = System.getProperty(jvmArgName); + if (path == null) { + return null; + } + path = path.trim(); + if (path.isEmpty()) { + return null; + } + return path; } } @@ -674,8 +685,7 @@ public final class LocalPath implements Comparable<LocalPath> { private static int removeRelativePaths(String[] segments, int starti) { int segmentCount = 0; int shift = starti; - int n = segments.length; - for (int i = starti; i < n; ++i) { + for (int i = starti; i < segments.length; ++i) { String segment = segments[i]; switch (segment) { case ".": diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Path.java b/src/main/java/com/google/devtools/build/lib/vfs/Path.java index b09b713cd1..1d3947dcdc 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/Path.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/Path.java @@ -39,11 +39,7 @@ import java.util.IdentityHashMap; import java.util.Objects; /** - * NOTE: This class is superseded by {@link LocalPath}. You should prefer storing simple strings / - * path fragments, then converting to a {@link LocalPath} only when you need to do local file system - * access. A migration is underway. - * - * <p>Instances of this class represent pathnames, forming a tree structure to implement sharing of + * Instances of this class represent pathnames, forming a tree structure to implement sharing of * common prefixes (parent directory names). A node in these trees is something like foo, bar, .., * ., or /. If the instance is not a root path, it will have a parent path. A path can also have * children, which are indexed by name in a map. @@ -427,10 +423,6 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { return builder.toString(); } - public LocalPath getLocalPath() { - return LocalPath.create(getPathString()); - } - @Override public void repr(SkylarkPrinter printer) { printer.append(getPathString()); @@ -508,7 +500,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * Path. */ public boolean exists(FileSystem fileSystem) { - return fileSystem.exists(this.getLocalPath(), true); + return fileSystem.exists(this, true); } /** Prefer to use {@link #exists(FileSystem, Symlinks)}. */ @@ -527,7 +519,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * link is dereferenced until a file other than a symbolic link is found */ public boolean exists(FileSystem fileSystem, Symlinks followSymlinks) { - return fileSystem.exists(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.exists(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #getDirectoryEntries(FileSystem)}. */ @@ -548,7 +540,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public Collection<Path> getDirectoryEntries(FileSystem fileSystem) throws IOException, FileNotFoundException { - Collection<String> entries = fileSystem.getDirectoryEntries(this.getLocalPath()); + Collection<String> entries = fileSystem.getDirectoryEntries(this); Collection<Path> result = new ArrayList<>(entries.size()); for (String entry : entries) { result.add(getChild(entry)); @@ -576,7 +568,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public Collection<Dirent> readdir(FileSystem fileSystem, Symlinks followSymlinks) throws IOException { - return fileSystem.readdir(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.readdir(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #stat(FileSystem)}. */ @@ -596,7 +588,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * {@code FileStatus} are called. */ public FileStatus stat(FileSystem fileSystem) throws IOException { - return fileSystem.stat(this.getLocalPath(), true); + return fileSystem.stat(this, true); } /** Prefer to use {@link #statNullable(FileSystem)}. */ @@ -628,7 +620,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * Path. */ public FileStatus statNullable(FileSystem fileSystem, Symlinks symlinks) { - return fileSystem.statNullable(this.getLocalPath(), symlinks.toBoolean()); + return fileSystem.statNullable(this, symlinks.toBoolean()); } /** Prefer to use {@link #stat(FileSystem, Symlinks)}. */ @@ -650,7 +642,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * {@code FileStatus} are called */ public FileStatus stat(FileSystem fileSystem, Symlinks followSymlinks) throws IOException { - return fileSystem.stat(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.stat(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #statIfFound(FileSystem)}. */ @@ -668,7 +660,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * Path. */ public FileStatus statIfFound(FileSystem fileSystem) throws IOException { - return fileSystem.statIfFound(this.getLocalPath(), true); + return fileSystem.statIfFound(this, true); } /** Prefer to use {@link #statIfFound(FileSystem, Symlinks)}. */ @@ -688,7 +680,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * link is dereferenced until a file other than a symbolic link is found */ public FileStatus statIfFound(FileSystem fileSystem, Symlinks followSymlinks) throws IOException { - return fileSystem.statIfFound(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.statIfFound(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #isDirectory()} (FileSystem)}. */ @@ -704,7 +696,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * Path. */ public boolean isDirectory(FileSystem fileSystem) { - return fileSystem.isDirectory(this.getLocalPath(), true); + return fileSystem.isDirectory(this, true); } /** Prefer to use {@link #isDirectory(FileSystem, Symlinks)}. */ @@ -723,7 +715,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * link is dereferenced until a file other than a symbolic link is found */ public boolean isDirectory(FileSystem fileSystem, Symlinks followSymlinks) { - return fileSystem.isDirectory(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.isDirectory(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #isFile(FileSystem)}. */ @@ -742,7 +734,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * it excludes symbolic links and directories. */ public boolean isFile(FileSystem fileSystem) { - return fileSystem.isFile(this.getLocalPath(), true); + return fileSystem.isFile(this, true); } /** Prefer to use {@link #isFile(FileSystem, Symlinks)}. */ @@ -764,7 +756,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * link is dereferenced until a file other than a symbolic link is found. */ public boolean isFile(FileSystem fileSystem, Symlinks followSymlinks) { - return fileSystem.isFile(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.isFile(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #isSpecialFile(FileSystem)}. */ @@ -781,7 +773,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * Path. */ public boolean isSpecialFile(FileSystem fileSystem) { - return fileSystem.isSpecialFile(this.getLocalPath(), true); + return fileSystem.isSpecialFile(this, true); } /** Prefer to use {@link #isSpecialFile(FileSystem, Symlinks)}. */ @@ -800,7 +792,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * link is dereferenced until a path other than a symbolic link is found. */ public boolean isSpecialFile(FileSystem fileSystem, Symlinks followSymlinks) { - return fileSystem.isSpecialFile(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.isSpecialFile(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #isSymbolicLink(FileSystem)}. */ @@ -816,7 +808,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * Path. */ public boolean isSymbolicLink(FileSystem fileSystem) { - return fileSystem.isSymbolicLink(this.getLocalPath()); + return fileSystem.isSymbolicLink(this); } /** @@ -1010,7 +1002,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public OutputStream getOutputStream(FileSystem fileSystem, boolean append) throws IOException, FileNotFoundException { - return fileSystem.getOutputStream(this.getLocalPath(), append); + return fileSystem.getOutputStream(this, append); } /** Prefer to use {@link #createDirectory(FileSystem)}. */ @@ -1031,7 +1023,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the directory creation failed for any reason */ public boolean createDirectory(FileSystem fileSystem) throws IOException { - return fileSystem.createDirectory(this.getLocalPath()); + return fileSystem.createDirectory(this); } /** Prefer to use {@link #createSymbolicLink(FileSystem, Path)}. */ @@ -1052,7 +1044,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public void createSymbolicLink(FileSystem fileSystem, Path target) throws IOException { checkSameFilesystem(target); - fileSystem.createSymbolicLink(this.getLocalPath(), target.asFragment().getPathString()); + fileSystem.createSymbolicLink(this, target.asFragment()); } /** Prefer to use {@link #createSymbolicLink(FileSystem, PathFragment)}. */ @@ -1072,7 +1064,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the creation of the symbolic link was unsuccessful for any reason */ public void createSymbolicLink(FileSystem fileSystem, PathFragment target) throws IOException { - fileSystem.createSymbolicLink(this.getLocalPath(), target.getPathString()); + fileSystem.createSymbolicLink(this, target); } /** Prefer to use {@link #readSymbolicLink(FileSystem)}. */ @@ -1097,7 +1089,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * could not be read for any reason */ public PathFragment readSymbolicLink(FileSystem fileSystem) throws IOException { - return PathFragment.create(fileSystem.readSymbolicLink(this.getLocalPath())); + return fileSystem.readSymbolicLink(this); } /** Prefer to use {@link #readSymbolicLinkUnchecked(FileSystem)}. */ @@ -1118,7 +1110,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * could not be read for any reason */ public PathFragment readSymbolicLinkUnchecked(FileSystem fileSystem) throws IOException { - return PathFragment.create(fileSystem.readSymbolicLinkUnchecked(this.getLocalPath())); + return fileSystem.readSymbolicLinkUnchecked(this); } /** Prefer to use {@link #createHardLink(FileSystem, Path)}. */ @@ -1137,7 +1129,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if there was an error executing {@link FileSystem#createHardLink} */ public void createHardLink(FileSystem fileSystem, Path link) throws IOException { - fileSystem.createHardLink(link.getLocalPath(), this.getLocalPath()); + fileSystem.createHardLink(link, this); } /** Prefer to use {@link #resolveSymbolicLinks(FileSystem)}. */ @@ -1158,7 +1150,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * example, the path does not exist) */ public Path resolveSymbolicLinks(FileSystem fileSystem) throws IOException { - return fileSystem.getPath(fileSystem.resolveSymbolicLinks(this.getLocalPath()).getPathString()); + return fileSystem.resolveSymbolicLinks(this); } /** Prefer to use {@link #renameTo(FileSystem, Path)}. */ @@ -1181,7 +1173,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public void renameTo(FileSystem fileSystem, Path target) throws IOException { checkSameFilesystem(target); - fileSystem.renameTo(this.getLocalPath(), target.getLocalPath()); + fileSystem.renameTo(this, target); } /** Prefer to use {@link #getFileSize(FileSystem)}. */ @@ -1202,7 +1194,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the file's metadata could not be read, or some other error occurred */ public long getFileSize(FileSystem fileSystem) throws IOException, FileNotFoundException { - return fileSystem.getFileSize(this.getLocalPath(), true); + return fileSystem.getFileSize(this, true); } /** Prefer to use {@link #getFileSize(FileSystem, Symlinks)}. */ @@ -1227,7 +1219,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public long getFileSize(FileSystem fileSystem, Symlinks followSymlinks) throws IOException, FileNotFoundException { - return fileSystem.getFileSize(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.getFileSize(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #delete(FileSystem)}. */ @@ -1248,7 +1240,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the deletion failed but the file was present prior to the call */ public boolean delete(FileSystem fileSystem) throws IOException { - return fileSystem.delete(this.getLocalPath()); + return fileSystem.delete(this); } /** Prefer to use {@link #getLastModifiedTime(FileSystem)}. */ @@ -1270,7 +1262,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the operation failed for any reason */ public long getLastModifiedTime(FileSystem fileSystem) throws IOException { - return fileSystem.getLastModifiedTime(this.getLocalPath(), true); + return fileSystem.getLastModifiedTime(this, true); } /** Prefer to use {@link #getLastModifiedTime(FileSystem, Symlinks)}. */ @@ -1295,7 +1287,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public long getLastModifiedTime(FileSystem fileSystem, Symlinks followSymlinks) throws IOException { - return fileSystem.getLastModifiedTime(this.getLocalPath(), followSymlinks.toBoolean()); + return fileSystem.getLastModifiedTime(this, followSymlinks.toBoolean()); } /** Prefer to use {@link #setLastModifiedTime(FileSystem, long)}. */ @@ -1320,7 +1312,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the modification time for the file could not be set for any reason */ public void setLastModifiedTime(FileSystem fileSystem, long newTime) throws IOException { - fileSystem.setLastModifiedTime(this.getLocalPath(), newTime); + fileSystem.setLastModifiedTime(this, newTime); } /** Prefer to use {@link #getxattr(FileSystem, String)}. */ @@ -1337,7 +1329,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * Path. */ public byte[] getxattr(FileSystem fileSystem, String name) throws IOException { - return fileSystem.getxattr(this.getLocalPath(), name); + return fileSystem.getxattr(this, name); } /** Prefer to use {@link #getFastDigest(FileSystem)}. */ @@ -1354,7 +1346,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * Path. */ public byte[] getFastDigest(FileSystem fileSystem) throws IOException { - return fileSystem.getFastDigest(this.getLocalPath()); + return fileSystem.getFastDigest(this); } /** Prefer to use {@link #isValidDigest(FileSystem, byte[])}. */ @@ -1389,7 +1381,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the digest could not be computed for any reason */ public byte[] getDigest(FileSystem fileSystem) throws IOException { - return fileSystem.getDigest(this.getLocalPath()); + return fileSystem.getDigest(this); } /** Prefer to use {@link #getDigest(FileSystem, HashFunction)}. */ @@ -1409,7 +1401,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the digest could not be computed for any reason */ public byte[] getDigest(FileSystem fileSystem, HashFunction hashFunction) throws IOException { - return fileSystem.getDigest(this.getLocalPath(), hashFunction); + return fileSystem.getDigest(this, hashFunction); } /** Prefer to use {@link #getInputStream(FileSystem)}. */ @@ -1428,7 +1420,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the file was not found or could not be opened for reading */ public InputStream getInputStream(FileSystem fileSystem) throws IOException { - return fileSystem.getInputStream(this.getLocalPath()); + return fileSystem.getInputStream(this); } /** @@ -1458,7 +1450,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * encountered, or the file's metadata could not be read */ public boolean isWritable(FileSystem fileSystem) throws IOException, FileNotFoundException { - return fileSystem.isWritable(this.getLocalPath()); + return fileSystem.isWritable(this); } /** Prefer to use {@link #setReadable(FileSystem, boolean)}. */ @@ -1480,7 +1472,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public void setReadable(FileSystem fileSystem, boolean readable) throws IOException, FileNotFoundException { - fileSystem.setReadable(this.getLocalPath(), readable); + fileSystem.setReadable(this, readable); } /** Prefer to use {@link #setWritable(FileSystem, boolean)}. */ @@ -1504,7 +1496,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public void setWritable(FileSystem fileSystem, boolean writable) throws IOException, FileNotFoundException { - fileSystem.setWritable(this.getLocalPath(), writable); + fileSystem.setWritable(this, writable); } /** Prefer to use {@link #isExecutable(FileSystem)}. */ @@ -1525,7 +1517,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if some other I/O error occurred */ public boolean isExecutable(FileSystem fileSystem) throws IOException, FileNotFoundException { - return fileSystem.isExecutable(this.getLocalPath()); + return fileSystem.isExecutable(this); } /** Prefer to use {@link #isReadable(FileSystem)}. */ @@ -1546,7 +1538,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if some other I/O error occurred */ public boolean isReadable(FileSystem fileSystem) throws IOException, FileNotFoundException { - return fileSystem.isReadable(this.getLocalPath()); + return fileSystem.isReadable(this); } /** Prefer to use {@link #setExecutable(FileSystem, boolean)}. */ @@ -1568,7 +1560,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { */ public void setExecutable(FileSystem fileSystem, boolean executable) throws IOException, FileNotFoundException { - fileSystem.setExecutable(this.getLocalPath(), executable); + fileSystem.setExecutable(this, executable); } /** Prefer to use {@link #chmod(FileSystem, int)}. */ @@ -1591,7 +1583,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { * @throws IOException if the metadata change failed, for example because of permissions */ public void chmod(FileSystem fileSystem, int mode) throws IOException { - fileSystem.chmod(this.getLocalPath(), mode); + fileSystem.chmod(this, mode); } /** Prefer to use {@link #prefetchPackageAsync(FileSystem, int)}. */ @@ -1601,7 +1593,7 @@ public class Path implements Comparable<Path>, Serializable, SkylarkPrintable { } public void prefetchPackageAsync(FileSystem fileSystem, int maxDirs) { - fileSystem.prefetchPackageAsync(this.getLocalPath(), maxDirs); + fileSystem.prefetchPackageAsync(this, maxDirs); } /** diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathTrie.java b/src/main/java/com/google/devtools/build/lib/vfs/PathTrie.java new file mode 100644 index 0000000000..fd783c5929 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/vfs/PathTrie.java @@ -0,0 +1,82 @@ +// Copyright 2014 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.vfs; + +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible; +import java.util.HashMap; +import java.util.Map; + +/** + * A trie that operates on path segments. + * + * @param <T> the type of the values. + */ +@ThreadCompatible +public class PathTrie<T> { + @SuppressWarnings("unchecked") + private static class Node<T> { + private Node() { + children = new HashMap<>(); + } + + private T value; + private Map<String, Node<T>> children; + } + + private final Node<T> root; + + public PathTrie() { + root = new Node<T>(); + } + + /** + * Puts a value in the trie. + * + * @param key must be an absolute path. + */ + public void put(PathFragment key, T value) { + Preconditions.checkArgument(key.isAbsolute(), "PathTrie only accepts absolute paths as keys."); + Node<T> current = root; + for (String segment : key.getSegments()) { + current.children.putIfAbsent(segment, new Node<T>()); + current = current.children.get(segment); + } + current.value = value; + } + + /** + * Gets a value from the trie. If there is an entry with the same key, that will be returned, + * otherwise, the value corresponding to the key that matches the longest prefix of the input. + */ + public T get(PathFragment key) { + Node<T> current = root; + T lastValue = current.value; + + for (String segment : key.getSegments()) { + if (current.children.containsKey(segment)) { + current = current.children.get(segment); + // Track the values of increasing matching prefixes. + if (current.value != null) { + lastValue = current.value; + } + } else { + // We've reached the longest prefix, no further to go. + break; + } + } + + return lastValue; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java index 0eab17c93f..bfcc4f97e0 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystem.java @@ -17,21 +17,20 @@ import java.io.IOException; import java.io.OutputStream; /** - * An abstract partial implementation of FileSystem for read-only implementations. + * An abstract partial implementation of FileSystem for read-only + * implementations. * * <p>Any ReadonlyFileSystem does not support the following: - * * <ul> - * <li>{@link #createDirectory(LocalPath)} - * <li>{@link #createSymbolicLink(LocalPath, String)} - * <li>{@link #delete(LocalPath)} - * <li>{@link #getOutputStream(LocalPath)} - * <li>{@link #renameTo(LocalPath, LocalPath)} - * <li>{@link #setExecutable(LocalPath, boolean)} - * <li>{@link #setLastModifiedTime(LocalPath, long)} - * <li>{@link #setWritable(LocalPath, boolean)} + * <li>{@link #createDirectory(Path)}</li> + * <li>{@link #createSymbolicLink(Path, PathFragment)}</li> + * <li>{@link #delete(Path)}</li> + * <li>{@link #getOutputStream(Path)}</li> + * <li>{@link #renameTo(Path, Path)}</li> + * <li>{@link #setExecutable(Path, boolean)}</li> + * <li>{@link #setLastModifiedTime(Path, long)}</li> + * <li>{@link #setWritable(Path, boolean)}</li> * </ul> - * * The above calls will always result in an {@link IOException}. */ public abstract class ReadonlyFileSystem extends AbstractFileSystem { @@ -47,37 +46,37 @@ public abstract class ReadonlyFileSystem extends AbstractFileSystem { } @Override - protected OutputStream getOutputStream(LocalPath path, boolean append) throws IOException { + protected OutputStream getOutputStream(Path path, boolean append) throws IOException { throw modificationException(); } @Override - protected void setReadable(LocalPath path, boolean readable) throws IOException { + protected void setReadable(Path path, boolean readable) throws IOException { throw modificationException(); } @Override - public void setWritable(LocalPath path, boolean writable) throws IOException { + public void setWritable(Path path, boolean writable) throws IOException { throw modificationException(); } @Override - protected void setExecutable(LocalPath path, boolean executable) { + protected void setExecutable(Path path, boolean executable) { throw new UnsupportedOperationException("setExecutable"); } @Override - public boolean supportsModifications(LocalPath path) { + public boolean supportsModifications(Path path) { return false; } @Override - public boolean supportsSymbolicLinksNatively(LocalPath path) { + public boolean supportsSymbolicLinksNatively(Path path) { return false; } @Override - public boolean supportsHardLinksNatively(LocalPath path) { + public boolean supportsHardLinksNatively(Path path) { return false; } @@ -87,32 +86,32 @@ public abstract class ReadonlyFileSystem extends AbstractFileSystem { } @Override - public boolean createDirectory(LocalPath path) throws IOException { + public boolean createDirectory(Path path) throws IOException { throw modificationException(); } @Override - protected void createSymbolicLink(LocalPath linkPath, String targetFragment) throws IOException { + protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException { throw modificationException(); } @Override - public void renameTo(LocalPath sourcePath, LocalPath targetPath) throws IOException { + public void renameTo(Path sourcePath, Path targetPath) throws IOException { throw modificationException(); } @Override - public boolean delete(LocalPath path) throws IOException { + public boolean delete(Path path) throws IOException { throw modificationException(); } @Override - public void setLastModifiedTime(LocalPath path, long newTime) throws IOException { + public void setLastModifiedTime(Path path, long newTime) throws IOException { throw modificationException(); } @Override - protected void createFSDependentHardLink(LocalPath linkPath, LocalPath originalPath) + protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException { throw modificationException(); } diff --git a/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java b/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java index 46e058cb67..de5daca181 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/ReadonlyFileSystemWithCustomStat.java @@ -31,37 +31,37 @@ public abstract class ReadonlyFileSystemWithCustomStat extends AbstractFileSyste } @Override - protected OutputStream getOutputStream(LocalPath path, boolean append) throws IOException { + protected OutputStream getOutputStream(Path path, boolean append) throws IOException { throw modificationException(); } @Override - protected void setReadable(LocalPath path, boolean readable) throws IOException { + protected void setReadable(Path path, boolean readable) throws IOException { throw modificationException(); } @Override - public void setWritable(LocalPath path, boolean writable) throws IOException { + public void setWritable(Path path, boolean writable) throws IOException { throw modificationException(); } @Override - protected void setExecutable(LocalPath path, boolean executable) { + protected void setExecutable(Path path, boolean executable) { throw new UnsupportedOperationException("setExecutable"); } @Override - public boolean supportsModifications(LocalPath path) { + public boolean supportsModifications(Path path) { return false; } @Override - public boolean supportsSymbolicLinksNatively(LocalPath path) { + public boolean supportsSymbolicLinksNatively(Path path) { return false; } @Override - public boolean supportsHardLinksNatively(LocalPath path) { + public boolean supportsHardLinksNatively(Path path) { return false; } @@ -71,33 +71,33 @@ public abstract class ReadonlyFileSystemWithCustomStat extends AbstractFileSyste } @Override - public boolean createDirectory(LocalPath path) throws IOException { + public boolean createDirectory(Path path) throws IOException { throw modificationException(); } @Override - protected void createSymbolicLink(LocalPath linkPath, String targetFragment) throws IOException { + protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException { throw modificationException(); } @Override - protected void createFSDependentHardLink(LocalPath linkPath, LocalPath originalPath) + protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException { throw modificationException(); } @Override - public void renameTo(LocalPath sourcePath, LocalPath targetPath) throws IOException { + public void renameTo(Path sourcePath, Path targetPath) throws IOException { throw modificationException(); } @Override - public boolean delete(LocalPath path) throws IOException { + public boolean delete(Path path) throws IOException { throw modificationException(); } @Override - public void setLastModifiedTime(LocalPath path, long newTime) throws IOException { + public void setLastModifiedTime(Path path, long newTime) throws IOException { throw modificationException(); } } diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java index 504aa93700..824e71dc69 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/UnionFileSystem.java @@ -15,14 +15,12 @@ package com.google.devtools.build.lib.vfs; import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; import com.google.devtools.build.lib.concurrent.ThreadSafety; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; -import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -44,21 +42,12 @@ import javax.annotation.Nullable; * are not currently supported. */ @ThreadSafety.ThreadSafe -public final class UnionFileSystem extends FileSystem { +public class UnionFileSystem extends FileSystem { - private static class FileSystemAndPrefix { - final LocalPath prefix; - final FileSystem fileSystem; - - public FileSystemAndPrefix(LocalPath prefix, FileSystem fileSystem) { - this.prefix = prefix; - this.fileSystem = fileSystem; - } - } - - // List of file systems and their mappings, sorted by prefix length descending. - private final List<FileSystemAndPrefix> fileSystems; - private final FileSystem rootFileSystem; + // Prefix trie index, allowing children to easily inherit prefix mappings + // of their parents. + // This does not currently handle unicode filenames. + private final PathTrie<FileSystem> pathDelegate; // True if the file path is case-sensitive on all the FileSystem // or False if they are all case-insensitive, otherwise error. @@ -70,35 +59,30 @@ public final class UnionFileSystem extends FileSystem { * @param prefixMapping map of path prefixes to {@link FileSystem}s * @param rootFileSystem root for default requests; i.e. mapping of "/" */ - public UnionFileSystem(Map<LocalPath, FileSystem> prefixMapping, FileSystem rootFileSystem) { + public UnionFileSystem(Map<PathFragment, FileSystem> prefixMapping, FileSystem rootFileSystem) { super(); Preconditions.checkNotNull(prefixMapping); Preconditions.checkNotNull(rootFileSystem); Preconditions.checkArgument(rootFileSystem != this, "Circular root filesystem."); Preconditions.checkArgument( - !prefixMapping.containsKey(LocalPath.EMPTY), + !prefixMapping.containsKey(PathFragment.EMPTY_FRAGMENT), "Attempted to specify an explicit root prefix mapping; " + "please use the rootFileSystem argument instead."); - this.fileSystems = new ArrayList<>(); - this.rootFileSystem = rootFileSystem; + this.pathDelegate = new PathTrie<>(); this.isCaseSensitive = rootFileSystem.isFilePathCaseSensitive(); - for (Map.Entry<LocalPath, FileSystem> prefix : prefixMapping.entrySet()) { + for (Map.Entry<PathFragment, FileSystem> prefix : prefixMapping.entrySet()) { FileSystem delegate = prefix.getValue(); Preconditions.checkArgument( delegate.isFilePathCaseSensitive() == this.isCaseSensitive, "The case sensitiveness of FileSystem are different in UnionFileSystem"); - LocalPath prefixPath = prefix.getKey(); + PathFragment prefixPath = prefix.getKey(); // Extra slash prevents within-directory mappings, which Path can't handle. - fileSystems.add(new FileSystemAndPrefix(prefixPath, delegate)); + pathDelegate.put(prefixPath, delegate); } - // Order by length descending. This ensures that more specific mapping takes precedence - // when we try to find the file system of a given path. - Comparator<FileSystemAndPrefix> comparator = - Comparator.comparing(f -> f.prefix.getPathString().length()); - fileSystems.sort(comparator.reversed()); + pathDelegate.put(PathFragment.ROOT_FRAGMENT, rootFileSystem); } /** @@ -108,24 +92,19 @@ public final class UnionFileSystem extends FileSystem { * @param path the {@link Path} to map to a filesystem * @throws IllegalArgumentException if no delegate exists for the path */ - FileSystem getDelegate(LocalPath path) { + protected FileSystem getDelegate(Path path) { Preconditions.checkNotNull(path); - FileSystem delegate = null; - // Linearly iterate over each mapped file system and find the one that handles this path. - // For small number of mappings, this will be more efficient than using a trie - for (FileSystemAndPrefix fileSystemAndPrefix : this.fileSystems) { - if (path.startsWith(fileSystemAndPrefix.prefix)) { - delegate = fileSystemAndPrefix.fileSystem; - break; - } - } - return delegate != null ? delegate : rootFileSystem; + FileSystem immediateDelegate = pathDelegate.get(path.asFragment()); + + // Should never actually happen if the root delegate is present. + Preconditions.checkNotNull(immediateDelegate, "No delegate filesystem exists for %s", path); + return immediateDelegate; } // Associates the path with the root of the given delegate filesystem. // Necessary to avoid null pointer problems inside of the delegates. - LocalPath adjustPath(LocalPath path, FileSystem delegate) { - return path; + protected Path adjustPath(Path path, FileSystem delegate) { + return delegate.getPath(path.asFragment()); } /** @@ -135,20 +114,20 @@ public final class UnionFileSystem extends FileSystem { * @param path {@link Path} to the symbolic link */ @Override - protected String readSymbolicLink(LocalPath path) throws IOException { + protected PathFragment readSymbolicLink(Path path) throws IOException { Preconditions.checkNotNull(path); FileSystem delegate = getDelegate(path); return delegate.readSymbolicLink(adjustPath(path, delegate)); } @Override - protected String resolveOneLink(LocalPath path) throws IOException { + protected PathFragment resolveOneLink(Path path) throws IOException { Preconditions.checkNotNull(path); FileSystem delegate = getDelegate(path); return delegate.resolveOneLink(adjustPath(path, delegate)); } - private void checkModifiable(LocalPath path) { + private void checkModifiable(Path path) { if (!supportsModifications(path)) { throw new UnsupportedOperationException( String.format("Modifications to this %s are disabled.", getClass().getSimpleName())); @@ -156,21 +135,21 @@ public final class UnionFileSystem extends FileSystem { } @Override - public boolean supportsModifications(LocalPath path) { + public boolean supportsModifications(Path path) { FileSystem delegate = getDelegate(path); path = adjustPath(path, delegate); return delegate.supportsModifications(path); } @Override - public boolean supportsSymbolicLinksNatively(LocalPath path) { + public boolean supportsSymbolicLinksNatively(Path path) { FileSystem delegate = getDelegate(path); path = adjustPath(path, delegate); return delegate.supportsSymbolicLinksNatively(path); } @Override - public boolean supportsHardLinksNatively(LocalPath path) { + public boolean supportsHardLinksNatively(Path path) { FileSystem delegate = getDelegate(path); path = adjustPath(path, delegate); return delegate.supportsHardLinksNatively(path); @@ -182,7 +161,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - public String getFileSystemType(LocalPath path) { + public String getFileSystemType(Path path) { try { path = internalResolveSymlink(path); } catch (IOException e) { @@ -193,14 +172,14 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected byte[] getDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException { path = internalResolveSymlink(path); FileSystem delegate = getDelegate(path); return delegate.getDigest(adjustPath(path, delegate), hashFunction); } @Override - public boolean createDirectory(LocalPath path) throws IOException { + public boolean createDirectory(Path path) throws IOException { checkModifiable(path); // When creating the exact directory that is mapped, // create it on both the parent's delegate and the path's delegate. @@ -213,7 +192,7 @@ public final class UnionFileSystem extends FileSystem { // ls / ("foo" would be missing if not created on the parent) // ls /foo (would fail if foo weren't also present on the child) FileSystem delegate = getDelegate(path); - LocalPath parent = path.getParentDirectory(); + Path parent = path.getParentDirectory(); if (parent != null) { parent = internalResolveSymlink(parent); FileSystem parentDelegate = getDelegate(parent); @@ -227,28 +206,28 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected long getFileSize(LocalPath path, boolean followSymlinks) throws IOException { + protected long getFileSize(Path path, boolean followSymlinks) throws IOException { path = followSymlinks ? internalResolveSymlink(path) : path; FileSystem delegate = getDelegate(path); return delegate.getFileSize(adjustPath(path, delegate), false); } @Override - public boolean delete(LocalPath path) throws IOException { + public boolean delete(Path path) throws IOException { checkModifiable(path); FileSystem delegate = getDelegate(path); return delegate.delete(adjustPath(path, delegate)); } @Override - protected long getLastModifiedTime(LocalPath path, boolean followSymlinks) throws IOException { + protected long getLastModifiedTime(Path path, boolean followSymlinks) throws IOException { path = followSymlinks ? internalResolveSymlink(path) : path; FileSystem delegate = getDelegate(path); return delegate.getLastModifiedTime(adjustPath(path, delegate), false); } @Override - public void setLastModifiedTime(LocalPath path, long newTime) throws IOException { + public void setLastModifiedTime(Path path, long newTime) throws IOException { path = internalResolveSymlink(path); checkModifiable(path); FileSystem delegate = getDelegate(path); @@ -256,14 +235,14 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected boolean isSymbolicLink(LocalPath path) { + protected boolean isSymbolicLink(Path path) { FileSystem delegate = getDelegate(path); path = adjustPath(path, delegate); return delegate.isSymbolicLink(path); } @Override - protected boolean isDirectory(LocalPath path, boolean followSymlinks) { + protected boolean isDirectory(Path path, boolean followSymlinks) { try { path = followSymlinks ? internalResolveSymlink(path) : path; } catch (IOException e) { @@ -274,7 +253,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected boolean isFile(LocalPath path, boolean followSymlinks) { + protected boolean isFile(Path path, boolean followSymlinks) { try { path = followSymlinks ? internalResolveSymlink(path) : path; } catch (IOException e) { @@ -285,7 +264,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected boolean isSpecialFile(LocalPath path, boolean followSymlinks) { + protected boolean isSpecialFile(Path path, boolean followSymlinks) { try { path = followSymlinks ? internalResolveSymlink(path) : path; } catch (IOException e) { @@ -296,7 +275,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected void createSymbolicLink(LocalPath linkPath, String targetFragment) throws IOException { + protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException { checkModifiable(linkPath); if (!supportsSymbolicLinksNatively(linkPath)) { throw new UnsupportedOperationException( @@ -308,7 +287,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected boolean exists(LocalPath path, boolean followSymlinks) { + protected boolean exists(Path path, boolean followSymlinks) { try { path = followSymlinks ? internalResolveSymlink(path) : path; } catch (IOException e) { @@ -319,7 +298,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + protected FileStatus stat(Path path, boolean followSymlinks) throws IOException { path = followSymlinks ? internalResolveSymlink(path) : path; FileSystem delegate = getDelegate(path); return delegate.stat(adjustPath(path, delegate), false); @@ -329,7 +308,7 @@ public final class UnionFileSystem extends FileSystem { // UnixFileSystem implements statNullable and stat as separate codepaths. // More generally, we wish to delegate all filesystem operations. @Override - protected FileStatus statNullable(LocalPath path, boolean followSymlinks) { + protected FileStatus statNullable(Path path, boolean followSymlinks) { try { path = followSymlinks ? internalResolveSymlink(path) : path; } catch (IOException e) { @@ -341,7 +320,7 @@ public final class UnionFileSystem extends FileSystem { @Override @Nullable - protected FileStatus statIfFound(LocalPath path, boolean followSymlinks) throws IOException { + protected FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException { path = followSymlinks ? internalResolveSymlink(path) : path; FileSystem delegate = getDelegate(path); return delegate.statIfFound(adjustPath(path, delegate), false); @@ -354,30 +333,35 @@ public final class UnionFileSystem extends FileSystem { * @param path the {@link Path} whose children are to be retrieved */ @Override - protected Collection<String> getDirectoryEntries(LocalPath path) throws IOException { + protected Collection<String> getDirectoryEntries(Path path) throws IOException { path = internalResolveSymlink(path); FileSystem delegate = getDelegate(path); - LocalPath resolvedPath = adjustPath(path, delegate); - return delegate.getDirectoryEntries(resolvedPath); + Path resolvedPath = adjustPath(path, delegate); + Collection<Path> entries = resolvedPath.getDirectoryEntries(); + Collection<String> result = Lists.newArrayListWithCapacity(entries.size()); + for (Path entry : entries) { + result.add(entry.getBaseName()); + } + return result; } // No need for the more complex logic of getDirectoryEntries; it calls it implicitly. @Override - protected Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) throws IOException { + protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException { path = followSymlinks ? internalResolveSymlink(path) : path; FileSystem delegate = getDelegate(path); return delegate.readdir(adjustPath(path, delegate), false); } @Override - protected boolean isReadable(LocalPath path) throws IOException { + protected boolean isReadable(Path path) throws IOException { path = internalResolveSymlink(path); FileSystem delegate = getDelegate(path); return delegate.isReadable(adjustPath(path, delegate)); } @Override - protected void setReadable(LocalPath path, boolean readable) throws IOException { + protected void setReadable(Path path, boolean readable) throws IOException { path = internalResolveSymlink(path); checkModifiable(path); FileSystem delegate = getDelegate(path); @@ -385,7 +369,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected boolean isWritable(LocalPath path) throws IOException { + protected boolean isWritable(Path path) throws IOException { if (!supportsModifications(path)) { return false; } @@ -395,7 +379,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - public void setWritable(LocalPath path, boolean writable) throws IOException { + public void setWritable(Path path, boolean writable) throws IOException { checkModifiable(path); path = internalResolveSymlink(path); FileSystem delegate = getDelegate(path); @@ -403,14 +387,14 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected boolean isExecutable(LocalPath path) throws IOException { + protected boolean isExecutable(Path path) throws IOException { path = internalResolveSymlink(path); FileSystem delegate = getDelegate(path); return delegate.isExecutable(adjustPath(path, delegate)); } @Override - protected void setExecutable(LocalPath path, boolean executable) throws IOException { + protected void setExecutable(Path path, boolean executable) throws IOException { path = internalResolveSymlink(path); checkModifiable(path); FileSystem delegate = getDelegate(path); @@ -418,28 +402,28 @@ public final class UnionFileSystem extends FileSystem { } @Override - protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException { path = internalResolveSymlink(path); FileSystem delegate = getDelegate(path); return delegate.getFastDigest(adjustPath(path, delegate), hashFunction); } @Override - public byte[] getxattr(LocalPath path, String name) throws IOException { + public byte[] getxattr(Path path, String name) throws IOException { path = internalResolveSymlink(path); FileSystem delegate = getDelegate(path); return delegate.getxattr(adjustPath(path, delegate), name); } @Override - protected InputStream getInputStream(LocalPath path) throws IOException { + protected InputStream getInputStream(Path path) throws IOException { path = internalResolveSymlink(path); FileSystem delegate = getDelegate(path); return delegate.getInputStream(adjustPath(path, delegate)); } @Override - protected OutputStream getOutputStream(LocalPath path, boolean append) throws IOException { + protected OutputStream getOutputStream(Path path, boolean append) throws IOException { path = internalResolveSymlink(path); checkModifiable(path); FileSystem delegate = getDelegate(path); @@ -447,7 +431,7 @@ public final class UnionFileSystem extends FileSystem { } @Override - public void renameTo(LocalPath sourcePath, LocalPath targetPath) throws IOException { + public void renameTo(Path sourcePath, Path targetPath) throws IOException { sourcePath = internalResolveSymlink(sourcePath); FileSystem sourceDelegate = getDelegate(sourcePath); if (!sourceDelegate.supportsModifications(sourcePath)) { @@ -474,14 +458,13 @@ public final class UnionFileSystem extends FileSystem { } else { // Copy across filesystems, then delete. // copyFile throws on failure, so delete will never be reached if it fails. - FileSystemUtils.copyFile(sourceDelegate, sourcePath, targetDelegate, targetPath); + FileSystemUtils.copyFile(sourcePath, targetPath); sourceDelegate.delete(sourcePath); } } @Override - protected void createFSDependentHardLink(LocalPath linkPath, LocalPath originalPath) - throws IOException { + protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException { checkModifiable(linkPath); originalPath = internalResolveSymlink(originalPath); @@ -497,9 +480,9 @@ public final class UnionFileSystem extends FileSystem { adjustPath(linkPath, linkDelegate), adjustPath(originalPath, originalDelegate)); } - private LocalPath internalResolveSymlink(LocalPath path) throws IOException { + private Path internalResolveSymlink(Path path) throws IOException { while (isSymbolicLink(path)) { - String pathFragment = resolveOneLink(path); + PathFragment pathFragment = resolveOneLink(path); path = path.getRelative(pathFragment); } return path; diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java index 333c3bb1e3..93c4eb294c 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryContentInfo.java @@ -17,7 +17,7 @@ import com.google.common.base.Preconditions; import com.google.devtools.build.lib.clock.Clock; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; import com.google.devtools.build.lib.vfs.FileStatus; -import com.google.devtools.build.lib.vfs.LocalPath; +import com.google.devtools.build.lib.vfs.Path; import java.io.IOException; /** @@ -201,5 +201,6 @@ public abstract class InMemoryContentInfo implements FileStatus { * @param targetPath where the inode is relocated. * @throws IOException */ - protected void movedTo(LocalPath targetPath) throws IOException {} + protected void movedTo(Path targetPath) throws IOException { + } } diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java index 0d3594263a..ff6d88a387 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java @@ -21,7 +21,6 @@ import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.vfs.FileAccessException; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import java.io.ByteArrayInputStream; @@ -49,8 +48,7 @@ import javax.annotation.Nullable; @ThreadSafe public class InMemoryFileSystem extends FileSystem { - private final LocalPath scopeRoot; - private final int scopeSegmentCount; + private final PathFragment scopeRoot; private final Clock clock; // The root inode (a directory). @@ -84,7 +82,6 @@ public class InMemoryFileSystem extends FileSystem { this.clock = clock; this.rootInode = newRootInode(clock); this.scopeRoot = null; - this.scopeSegmentCount = 0; } /** @@ -92,8 +89,7 @@ public class InMemoryFileSystem extends FileSystem { * not below scopeRoot is considered to be out of scope. */ public InMemoryFileSystem(Clock clock, PathFragment scopeRoot) { - this.scopeRoot = scopeRoot != null ? LocalPath.create(scopeRoot.getPathString()) : null; - this.scopeSegmentCount = scopeRoot != null ? scopeRoot.segmentCount() : 0; + this.scopeRoot = scopeRoot; this.clock = clock; this.rootInode = newRootInode(clock); } @@ -113,7 +109,7 @@ public class InMemoryFileSystem extends FileSystem { * @param normalizedPath input path, expected to be normalized such that all ".." and "." segments * are removed (with the exception of a possible prefix sequence of contiguous ".." segments) */ - private boolean inScope(int parentDepth, LocalPath normalizedPath) { + private boolean inScope(int parentDepth, PathFragment normalizedPath) { if (scopeRoot == null) { return true; } else if (normalizedPath.isAbsolute()) { @@ -124,7 +120,7 @@ public class InMemoryFileSystem extends FileSystem { // unnecessary re-delegation back into the same FS. we're choosing to forgo that // optimization under the assumption that such scenarios are rare and unimportant to // overall performance. We can always enhance this if needed. - return parentDepth - leadingParentReferences(normalizedPath) >= scopeSegmentCount; + return parentDepth - leadingParentReferences(normalizedPath) >= scopeRoot.segmentCount(); } } @@ -135,14 +131,12 @@ public class InMemoryFileSystem extends FileSystem { * <p>Example allowed inputs: "/absolute/path", "relative/path", "../../relative/path". Example * disallowed inputs: "/absolute/path/../path2", "relative/../path", "../relative/../p". */ - private int leadingParentReferences(LocalPath normalizedPath) { + private int leadingParentReferences(PathFragment normalizedPath) { int leadingParentReferences = 0; - for (String segment : normalizedPath.split()) { - if (segment.equals("..")) { - ++leadingParentReferences; - } else { - break; - } + for (int i = 0; + i < normalizedPath.segmentCount() && normalizedPath.getSegment(i).equals(".."); + i++) { + leadingParentReferences++; } return leadingParentReferences; } @@ -251,10 +245,11 @@ public class InMemoryFileSystem extends FileSystem { } /** - * Returns a new IOException for the error. The exception message contains 'path', and is - * consistent with the messages returned by c.g.common.unix.FilesystemUtils. + * Returns a new IOException for the error. The exception message + * contains 'path', and is consistent with the messages returned by + * c.g.common.unix.FilesystemUtils. */ - public IOException exception(LocalPath path) throws IOException { + public IOException exception(Path path) throws IOException { String m = path + " (" + message + ")"; if (this == EACCES) { throw new FileAccessExceptionWithError(m, this); @@ -272,44 +267,48 @@ public class InMemoryFileSystem extends FileSystem { * <p>If <code>/proc/mounts</code> does not exist return {@code "inmemoryfs"}. */ @Override - public String getFileSystemType(LocalPath path) { - return exists(path.getRelative("/proc/mounts")) ? super.getFileSystemType(path) : "inmemoryfs"; + public String getFileSystemType(Path path) { + return path.getRelative("/proc/mounts").exists() ? super.getFileSystemType(path) : "inmemoryfs"; } - /** - * ************************************************************************** "Kernel" primitives: - * basic directory lookup primitives, in topological order. + /**************************************************************************** + * "Kernel" primitives: basic directory lookup primitives, in topological + * order. */ /** - * Unlinks the entry 'child' from its existing parent directory 'dir'. Dual to insert. This - * succeeds even if 'child' names a non-empty directory; we need that for renameTo. 'child' must - * be a member of its parent directory, however. Fails if the directory was read-only. + * Unlinks the entry 'child' from its existing parent directory 'dir'. Dual to + * insert. This succeeds even if 'child' names a non-empty directory; we need + * that for renameTo. 'child' must be a member of its parent directory, + * however. Fails if the directory was read-only. */ - private void unlink(InMemoryDirectoryInfo dir, String child, LocalPath errorPath) + private void unlink(InMemoryDirectoryInfo dir, String child, Path errorPath) throws IOException { if (!dir.isWritable()) { throw Error.EACCES.exception(errorPath); } dir.removeChild(child); } /** - * Inserts inode 'childInode' into the existing directory 'dir' under the specified 'name'. Dual - * to unlink. Fails if the directory was read-only. + * Inserts inode 'childInode' into the existing directory 'dir' under the + * specified 'name'. Dual to unlink. Fails if the directory was read-only. */ - private void insert( - InMemoryDirectoryInfo dir, String child, InMemoryContentInfo childInode, LocalPath errorPath) + private void insert(InMemoryDirectoryInfo dir, String child, + InMemoryContentInfo childInode, Path errorPath) throws IOException { if (!dir.isWritable()) { throw Error.EACCES.exception(errorPath); } dir.addChild(child, childInode); } /** - * Given an existing directory 'dir', looks up 'name' within it and returns its inode. Assumes the - * file exists, unless 'create', in which case it will try to create it. May fail with ENOTDIR, - * EACCES, ENOENT. Error messages will be reported against file 'path'. + * Given an existing directory 'dir', looks up 'name' within it and returns + * its inode. Assumes the file exists, unless 'create', in which case it will + * try to create it. May fail with ENOTDIR, EACCES, ENOENT. Error messages + * will be reported against file 'path'. */ - private InMemoryContentInfo directoryLookup( - InMemoryContentInfo dir, String name, boolean create, LocalPath path) throws IOException { + private InMemoryContentInfo directoryLookup(InMemoryContentInfo dir, + String name, + boolean create, + Path path) throws IOException { if (!dir.isDirectory()) { throw Error.ENOTDIR.exception(path); } InMemoryDirectoryInfo imdi = (InMemoryDirectoryInfo) dir; if (!imdi.isExecutable()) { throw Error.EACCES.exception(path); } @@ -337,8 +336,7 @@ public class InMemoryFileSystem extends FileSystem { * * <p>May fail with ENOTDIR, ENOENT, EACCES, ELOOP. */ - private synchronized InMemoryContentInfo pathWalk(LocalPath path, boolean create) - throws IOException { + private synchronized InMemoryContentInfo pathWalk(Path path, boolean create) throws IOException { // Implementation note: This is where we check for out-of-scope symlinks and // trigger re-delegation to another file system accordingly. This code handles // both absolute and relative symlinks. Some assumptions we make: First, only @@ -350,7 +348,8 @@ public class InMemoryFileSystem extends FileSystem { // and it may only appear as part of a contiguous prefix sequence. Stack<String> stack = new Stack<>(); - for (LocalPath p = path; p != null && !p.isRoot(); p = p.getParentDirectory()) { + PathFragment rootPathFragment = getRootDirectory().asFragment(); + for (Path p = path; !p.asFragment().equals(rootPathFragment); p = p.getParentDirectory()) { stack.push(p.getBaseName()); } @@ -367,7 +366,7 @@ public class InMemoryFileSystem extends FileSystem { // ENOENT on last segment with 'create' => create a new file. InMemoryContentInfo child = directoryLookup(inode, name, create && stack.isEmpty(), path); if (child.isSymbolicLink()) { - LocalPath linkTarget = ((InMemoryLinkInfo) child).getNormalizedLinkContent(); + PathFragment linkTarget = ((InMemoryLinkInfo) child).getNormalizedLinkContent(); if (!inScope(parentDepth, linkTarget)) { throw Error.ENOENT.exception(path); } @@ -378,9 +377,8 @@ public class InMemoryFileSystem extends FileSystem { if (traversals > MAX_TRAVERSALS) { throw Error.ELOOP.exception(path); } - List<String> linkSegments = linkTarget.split(); - for (int ii = linkSegments.size() - 1; ii >= 0; --ii) { - stack.push(linkSegments.get(ii)); // Note this may include ".." segments. + for (int ii = linkTarget.segmentCount() - 1; ii >= 0; --ii) { + stack.push(linkTarget.getSegment(ii)); // Note this may include ".." segments. } } else { inode = child; @@ -390,11 +388,12 @@ public class InMemoryFileSystem extends FileSystem { } /** - * Given 'path', returns the existing directory inode it designates, following symbolic links. + * Given 'path', returns the existing directory inode it designates, + * following symbolic links. * * <p>May fail with ENOTDIR, or any exception from pathWalk. */ - private InMemoryDirectoryInfo getDirectory(LocalPath path) throws IOException { + private InMemoryDirectoryInfo getDirectory(Path path) throws IOException { InMemoryContentInfo dirInfo = pathWalk(path, false); if (!dirInfo.isDirectory()) { throw Error.ENOTDIR.exception(path); @@ -404,28 +403,28 @@ public class InMemoryFileSystem extends FileSystem { } /** - * Helper method for stat, scopeLimitedStat: lock the internal state and return the path's (no - * symlink-followed) stat if the path's parent directory is within scope, else return an "out of - * scope" reference to the path's parent directory (which will presumably be re-delegated to - * another FS). + * Helper method for stat, scopeLimitedStat: lock the internal state and return the + * path's (no symlink-followed) stat if the path's parent directory is within scope, + * else return an "out of scope" reference to the path's parent directory (which will + * presumably be re-delegated to another FS). */ - private synchronized InMemoryContentInfo getNoFollowStatOrOutOfScopeParent(LocalPath path) - throws IOException { + private synchronized InMemoryContentInfo getNoFollowStatOrOutOfScopeParent(Path path) + throws IOException { InMemoryDirectoryInfo dirInfo = getDirectory(path.getParentDirectory()); return directoryLookup(dirInfo, path.getBaseName(), /*create=*/ false, path); } /** - * Given 'path', returns the existing inode it designates, optionally following symbolic links. - * Analogous to UNIX stat(2)/lstat(2), except that it returns a mutable inode we can modify - * directly. + * Given 'path', returns the existing inode it designates, optionally + * following symbolic links. Analogous to UNIX stat(2)/lstat(2), except that + * it returns a mutable inode we can modify directly. */ @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (followSymlinks) { return scopeLimitedStat(path, true); } else { - if (path.isRoot()) { + if (path.equals(getRootDirectory())) { return rootInode; } else { return getNoFollowStatOrOutOfScopeParent(path); @@ -435,7 +434,7 @@ public class InMemoryFileSystem extends FileSystem { @Override @Nullable - public FileStatus statIfFound(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException { try { return stat(path, followSymlinks); } catch (IOException e) { @@ -453,12 +452,12 @@ public class InMemoryFileSystem extends FileSystem { * Version of stat that returns an inode if the input path stays entirely within this file * system's scope, otherwise throws. */ - private InMemoryContentInfo scopeLimitedStat(LocalPath path, boolean followSymlinks) + private InMemoryContentInfo scopeLimitedStat(Path path, boolean followSymlinks) throws IOException { if (followSymlinks) { return pathWalk(path, false); } else { - if (path.isRoot()) { + if (path.equals(getRootDirectory())) { return rootInode; } else { return getNoFollowStatOrOutOfScopeParent(path); @@ -466,20 +465,21 @@ public class InMemoryFileSystem extends FileSystem { } } - /** - * ************************************************************************** FileSystem methods + /**************************************************************************** + * FileSystem methods */ /** - * This is a helper routing for {@link #resolveSymbolicLinks(LocalPath)}, i.e. the "user-mode" - * routing for canonicalising paths. It is analogous to the code in glibc's realpath(3). + * This is a helper routing for {@link #resolveSymbolicLinks(Path)}, i.e. + * the "user-mode" routing for canonicalising paths. It is analogous to the + * code in glibc's realpath(3). * - * <p>Just like realpath, resolveSymbolicLinks requires a quadratic number of directory lookups: n - * path segments are statted, and each stat requires a linear amount of work in the "kernel" - * routine. + * <p>Just like realpath, resolveSymbolicLinks requires a quadratic number of + * directory lookups: n path segments are statted, and each stat requires a + * linear amount of work in the "kernel" routine. */ @Override - protected String resolveOneLink(LocalPath path) throws IOException { + protected PathFragment resolveOneLink(Path path) throws IOException { // Beware, this seemingly simple code belies the complex specification of // FileSystem.resolveOneLink(). InMemoryContentInfo status = scopeLimitedStat(path, false); @@ -487,7 +487,7 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected boolean isDirectory(LocalPath path, boolean followSymlinks) { + protected boolean isDirectory(Path path, boolean followSymlinks) { try { return stat(path, followSymlinks).isDirectory(); } catch (IOException e) { @@ -496,7 +496,7 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected boolean isFile(LocalPath path, boolean followSymlinks) { + protected boolean isFile(Path path, boolean followSymlinks) { try { return stat(path, followSymlinks).isFile(); } catch (IOException e) { @@ -505,7 +505,7 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected boolean isSpecialFile(LocalPath path, boolean followSymlinks) { + protected boolean isSpecialFile(Path path, boolean followSymlinks) { try { return stat(path, followSymlinks).isSpecialFile(); } catch (IOException e) { @@ -514,7 +514,7 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected boolean isSymbolicLink(LocalPath path) { + protected boolean isSymbolicLink(Path path) { try { return stat(path, false).isSymbolicLink(); } catch (IOException e) { @@ -523,7 +523,7 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected boolean exists(LocalPath path, boolean followSymlinks) { + protected boolean exists(Path path, boolean followSymlinks) { try { stat(path, followSymlinks); return true; @@ -533,13 +533,13 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected boolean isReadable(LocalPath path) throws IOException { + protected boolean isReadable(Path path) throws IOException { InMemoryContentInfo status = scopeLimitedStat(path, true); return status.isReadable(); } @Override - protected void setReadable(LocalPath path, boolean readable) throws IOException { + protected void setReadable(Path path, boolean readable) throws IOException { synchronized (this) { InMemoryContentInfo status = scopeLimitedStat(path, true); status.setReadable(readable); @@ -547,13 +547,13 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected boolean isWritable(LocalPath path) throws IOException { + protected boolean isWritable(Path path) throws IOException { InMemoryContentInfo status = scopeLimitedStat(path, true); return status.isWritable(); } @Override - public void setWritable(LocalPath path, boolean writable) throws IOException { + public void setWritable(Path path, boolean writable) throws IOException { InMemoryContentInfo status; synchronized (this) { status = scopeLimitedStat(path, true); @@ -562,13 +562,14 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected boolean isExecutable(LocalPath path) throws IOException { + protected boolean isExecutable(Path path) throws IOException { InMemoryContentInfo status = scopeLimitedStat(path, true); return status.isExecutable(); } @Override - protected void setExecutable(LocalPath path, boolean executable) throws IOException { + protected void setExecutable(Path path, boolean executable) + throws IOException { synchronized (this) { InMemoryContentInfo status = scopeLimitedStat(path, true); status.setExecutable(executable); @@ -576,17 +577,17 @@ public class InMemoryFileSystem extends FileSystem { } @Override - public boolean supportsModifications(LocalPath path) { + public boolean supportsModifications(Path path) { return true; } @Override - public boolean supportsSymbolicLinksNatively(LocalPath path) { + public boolean supportsSymbolicLinksNatively(Path path) { return true; } @Override - public boolean supportsHardLinksNatively(LocalPath path) { + public boolean supportsHardLinksNatively(Path path) { return true; } @@ -596,8 +597,8 @@ public class InMemoryFileSystem extends FileSystem { } @Override - public boolean createDirectory(LocalPath path) throws IOException { - if (path.isRoot()) { + public boolean createDirectory(Path path) throws IOException { + if (path.equals(getRootDirectory())) { throw Error.EACCES.exception(path); } @@ -623,8 +624,9 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected void createSymbolicLink(LocalPath path, String targetFragment) throws IOException { - if (path.isRoot()) { + protected void createSymbolicLink(Path path, PathFragment targetFragment) + throws IOException { + if (path.equals(getRootDirectory())) { throw Error.EACCES.exception(path); } @@ -638,7 +640,7 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected String readSymbolicLink(LocalPath path) throws IOException { + protected PathFragment readSymbolicLink(Path path) throws IOException { InMemoryContentInfo status = scopeLimitedStat(path, false); if (status.isSymbolicLink()) { Preconditions.checkState(status instanceof InMemoryLinkInfo); @@ -649,12 +651,13 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected long getFileSize(LocalPath path, boolean followSymlinks) throws IOException { + protected long getFileSize(Path path, boolean followSymlinks) + throws IOException { return stat(path, followSymlinks).getSize(); } @Override - protected Collection<String> getDirectoryEntries(LocalPath path) throws IOException { + protected Collection<String> getDirectoryEntries(Path path) throws IOException { synchronized (this) { InMemoryDirectoryInfo dirInfo = getDirectory(path); FileStatus status = stat(path, false); @@ -675,8 +678,8 @@ public class InMemoryFileSystem extends FileSystem { } @Override - public boolean delete(LocalPath path) throws IOException { - if (path.isRoot()) { + public boolean delete(Path path) throws IOException { + if (path.equals(getRootDirectory())) { throw Error.EBUSY.exception(path); } if (!exists(path, false)) { return false; } @@ -693,12 +696,13 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected long getLastModifiedTime(LocalPath path, boolean followSymlinks) throws IOException { + protected long getLastModifiedTime(Path path, boolean followSymlinks) + throws IOException { return stat(path, followSymlinks).getLastModifiedTime(); } @Override - public void setLastModifiedTime(LocalPath path, long newTime) throws IOException { + public void setLastModifiedTime(Path path, long newTime) throws IOException { synchronized (this) { InMemoryContentInfo status = scopeLimitedStat(path, true); status.setLastModifiedTime(newTime == -1L ? clock.currentTimeMillis() : newTime); @@ -706,13 +710,13 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected InputStream getInputStream(LocalPath path) throws IOException { + protected InputStream getInputStream(Path path) throws IOException { synchronized (this) { InMemoryContentInfo status = scopeLimitedStat(path, true); if (status.isDirectory()) { throw Error.EISDIR.exception(path); } - if (!isReadable(path)) { + if (!path.isReadable()) { throw Error.EACCES.exception(path); } Preconditions.checkState(status instanceof FileInfo); @@ -721,7 +725,7 @@ public class InMemoryFileSystem extends FileSystem { } /** Creates a new file at the given path and returns its inode. */ - private InMemoryContentInfo getOrCreateWritableInode(LocalPath path) throws IOException { + private InMemoryContentInfo getOrCreateWritableInode(Path path) throws IOException { // open(WR_ONLY) of a dangling link writes through the link. That means // that the usual path lookup operations have to behave differently when // resolving a path with the intent to create it: instead of failing with @@ -738,7 +742,8 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected OutputStream getOutputStream(LocalPath path, boolean append) throws IOException { + protected OutputStream getOutputStream(Path path, boolean append) + throws IOException { synchronized (this) { InMemoryContentInfo status = getOrCreateWritableInode(path); return ((FileInfo) status).getOutputStream(append); @@ -746,11 +751,12 @@ public class InMemoryFileSystem extends FileSystem { } @Override - public void renameTo(LocalPath sourcePath, LocalPath targetPath) throws IOException { - if (sourcePath.isRoot()) { + public void renameTo(Path sourcePath, Path targetPath) + throws IOException { + if (sourcePath.equals(getRootDirectory())) { throw Error.EACCES.exception(sourcePath); } - if (targetPath.isRoot()) { + if (targetPath.equals(getRootDirectory())) { throw Error.EACCES.exception(targetPath); } synchronized (this) { @@ -794,11 +800,11 @@ public class InMemoryFileSystem extends FileSystem { } @Override - protected void createFSDependentHardLink(LocalPath linkPath, LocalPath originalPath) + protected void createFSDependentHardLink(Path linkPath, Path originalPath) throws IOException { // Same check used when creating a symbolic link - if (originalPath.isRoot()) { + if (originalPath.equals(getRootDirectory())) { throw Error.EACCES.exception(originalPath); } diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java index cee3ebd6be..107f319115 100644 --- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java +++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryLinkInfo.java @@ -16,7 +16,7 @@ package com.google.devtools.build.lib.vfs.inmemoryfs; import com.google.devtools.build.lib.clock.Clock; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; -import com.google.devtools.build.lib.vfs.LocalPath; +import com.google.devtools.build.lib.vfs.PathFragment; /** * This interface represents a symbolic link to an absolute or relative path, @@ -25,13 +25,13 @@ import com.google.devtools.build.lib.vfs.LocalPath; @ThreadSafe @Immutable class InMemoryLinkInfo extends InMemoryContentInfo { - private final String linkContent; - private final LocalPath normalizedLinkContent; + private final PathFragment linkContent; + private final PathFragment normalizedLinkContent; - InMemoryLinkInfo(Clock clock, String linkContent) { + InMemoryLinkInfo(Clock clock, PathFragment linkContent) { super(clock); this.linkContent = linkContent; - this.normalizedLinkContent = LocalPath.create(linkContent); + this.normalizedLinkContent = linkContent.normalize(); } @Override @@ -59,16 +59,18 @@ class InMemoryLinkInfo extends InMemoryContentInfo { return linkContent.toString().length(); } - /** Returns the content of the symbolic link. */ - String getLinkContent() { + /** + * Returns the content of the symbolic link. + */ + PathFragment getLinkContent() { return linkContent; } /** - * Returns the content of the symbolic link, with ".." and "." removed (except for the possibility - * of necessary ".." segments at the beginning). + * Returns the content of the symbolic link, with ".." and "." removed + * (except for the possibility of necessary ".." segments at the beginning). */ - LocalPath getNormalizedLinkContent() { + PathFragment getNormalizedLinkContent() { return normalizedLinkContent; } diff --git a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java index c98a6c1f15..af97e27d3d 100644 --- a/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java +++ b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java @@ -21,7 +21,6 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.JavaIoFileSystem; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.Path.PathFactory; import com.google.devtools.build.lib.vfs.PathFragment; @@ -33,11 +32,41 @@ import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.attribute.DosFileAttributes; import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; /** File system implementation for Windows. */ @ThreadSafe public class WindowsFileSystem extends JavaIoFileSystem { + // Properties of 8dot3 (DOS-style) short file names: + // - they are at most 11 characters long + // - they have a prefix (before "~") that is {1..6} characters long, may contain numbers, letters, + // "_", even "~", and maybe even more + // - they have a "~" after the prefix + // - have {1..6} numbers after "~" (according to [1] this is only one digit, but MSDN doesn't + // clarify this), the combined length up till this point is at most 8 + // - they have an optional "." afterwards, and another {0..3} more characters + // - just because a path looks like a short name it isn't necessarily one; the user may create + // such names and they'd resolve to themselves + // [1] https://en.wikipedia.org/wiki/8.3_filename#VFAT_and_Computer-generated_8.3_filenames + // bullet point (3) (on 2016-12-05) + @VisibleForTesting + static final Predicate<String> SHORT_NAME_MATCHER = + new Predicate<String>() { + private final Pattern pattern = Pattern.compile("^(.{1,6})~([0-9]{1,6})(\\..{0,3}){0,1}"); + + @Override + public boolean apply(@Nullable String input) { + Matcher m = pattern.matcher(input); + return input.length() <= 12 + && m.matches() + && m.groupCount() >= 2 + && (m.group(1).length() + m.group(2).length()) < 8; // the "~" makes it at most 8 + } + }; + /** Resolves DOS-style, shortened path names, returning the last segment's long form. */ private static final Function<String, String> WINDOWS_SHORT_PATH_RESOLVER = path -> { @@ -85,7 +114,7 @@ public class WindowsFileSystem extends JavaIoFileSystem { } String resolvedChild = child; - if (parent != null && !parent.isRootDirectory() && WindowsShortPath.isShortPath(child)) { + if (parent != null && !parent.isRootDirectory() && SHORT_NAME_MATCHER.apply(child)) { String pathString = parent.getPathString(); if (!pathString.endsWith("/")) { pathString += "/"; @@ -257,18 +286,18 @@ public class WindowsFileSystem extends JavaIoFileSystem { } @Override - public String getFileSystemType(LocalPath path) { + public String getFileSystemType(Path path) { // TODO(laszlocsomor): implement this properly, i.e. actually query this information from // somewhere (java.nio.Filesystem? System.getProperty? implement JNI method and use WinAPI?). return "ntfs"; } @Override - protected void createSymbolicLink(LocalPath linkPath, String targetFragment) throws IOException { - LocalPath targetPath = LocalPath.create(targetFragment); - if (!targetPath.isAbsolute()) { - targetPath = linkPath.getParentDirectory().getRelative(targetPath); - } + protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException { + Path targetPath = + targetFragment.isAbsolute() + ? getPath(targetFragment) + : linkPath.getParentDirectory().getRelative(targetFragment); try { java.nio.file.Path link = getIoFile(linkPath).toPath(); java.nio.file.Path target = getIoFile(targetPath).toPath(); @@ -288,7 +317,7 @@ public class WindowsFileSystem extends JavaIoFileSystem { } @Override - public boolean supportsSymbolicLinksNatively(LocalPath path) { + public boolean supportsSymbolicLinksNatively(Path path) { return false; } @@ -314,7 +343,7 @@ public class WindowsFileSystem extends JavaIoFileSystem { } @Override - protected FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + protected FileStatus stat(Path path, boolean followSymlinks) throws IOException { File file = getIoFile(path); final DosFileAttributes attributes; try { @@ -373,7 +402,7 @@ public class WindowsFileSystem extends JavaIoFileSystem { } @Override - protected boolean isDirectory(LocalPath path, boolean followSymlinks) { + protected boolean isDirectory(Path path, boolean followSymlinks) { if (!followSymlinks) { try { if (isJunction(getIoFile(path))) { diff --git a/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java index 6fee2c1061..259e0f2088 100644 --- a/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java +++ b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java @@ -25,7 +25,6 @@ import com.google.devtools.build.lib.testutil.TestUtils; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.FileSystem.HashFunction; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -59,7 +58,7 @@ public class DigestUtilsTest { FileSystem myfs = new InMemoryFileSystem(BlazeClock.instance(), hf) { @Override - protected byte[] getDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException { try { barrierLatch.countDown(); readyLatch.countDown(); @@ -73,8 +72,7 @@ public class DigestUtilsTest { } @Override - protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) - throws IOException { + protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException { return fastDigest ? super.getDigest(path, hashFunction) : null; } }; @@ -133,7 +131,7 @@ public class DigestUtilsTest { FileSystem myFS = new InMemoryFileSystem(BlazeClock.instance(), hf) { @Override - protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) + protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException { // Digest functions have more than 3 bytes, usually at least 16. return malformed; @@ -141,7 +139,6 @@ public class DigestUtilsTest { }; Path path = myFS.getPath("/file"); FileSystemUtils.writeContentAsLatin1(path, "a"); - byte[] result = DigestUtils.getDigestOrFail(path, 1); assertThat(result).isEqualTo(path.getDigest()); assertThat(result).isNotSameAs(malformed); @@ -224,14 +221,13 @@ public class DigestUtilsTest { FileSystem tracingFileSystem = new InMemoryFileSystem(BlazeClock.instance()) { @Override - protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) - throws IOException { + protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException { getFastDigestCounter.incrementAndGet(); return null; } @Override - protected byte[] getDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getDigest(Path path, HashFunction hashFunction) throws IOException { getDigestCounter.incrementAndGet(); return super.getDigest(path, hashFunction); } diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java index b7711aaa8d..67d9b6254a 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java @@ -19,7 +19,7 @@ import com.google.devtools.build.lib.analysis.util.AnalysisTestCase; import com.google.devtools.build.lib.clock.BlazeClock; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; -import com.google.devtools.build.lib.vfs.LocalPath; +import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; import java.util.function.Function; @@ -30,15 +30,15 @@ import org.junit.runners.JUnit4; /** {@link AnalysisTestCase} with custom filesystem that can throw on stat if desired. */ @RunWith(JUnit4.class) public class AnalysisWithIOExceptionsTest extends AnalysisTestCase { - private static final Function<LocalPath, String> NULL_FUNCTION = (path) -> null; + private static final Function<Path, String> NULL_FUNCTION = (path) -> null; - private Function<LocalPath, String> crashMessage = NULL_FUNCTION; + private Function<Path, String> crashMessage = NULL_FUNCTION; @Override protected FileSystem createFileSystem() { return new InMemoryFileSystem(BlazeClock.instance()) { @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { String crash = crashMessage.apply(path); if (crash != null) { throw new IOException(crash); diff --git a/src/test/java/com/google/devtools/build/lib/exec/SingleBuildFileCacheTest.java b/src/test/java/com/google/devtools/build/lib/exec/SingleBuildFileCacheTest.java index 023b41e57c..f9d0372e0a 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/SingleBuildFileCacheTest.java +++ b/src/test/java/com/google/devtools/build/lib/exec/SingleBuildFileCacheTest.java @@ -24,7 +24,6 @@ import com.google.devtools.build.lib.actions.DigestOfDirectoryException; import com.google.devtools.build.lib.testutil.Suite; import com.google.devtools.build.lib.testutil.TestSpec; import com.google.devtools.build.lib.vfs.FileSystem; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.protobuf.ByteString; @@ -55,23 +54,23 @@ public class SingleBuildFileCacheTest { public final void setUp() throws Exception { calls = new HashMap<>(); md5Overrides = new HashMap<>(); - fs = - new InMemoryFileSystem() { - @Override - protected InputStream getInputStream(LocalPath path) throws IOException { - int c = calls.containsKey(path.toString()) ? calls.get(path.toString()) : 0; - c++; - calls.put(path.toString(), c); - return super.getInputStream(path); - } - - @Override - protected byte[] getDigest(LocalPath path, HashFunction hf) throws IOException { - assertThat(hf).isEqualTo(HashFunction.MD5); - byte[] override = md5Overrides.get(path.getPathString()); - return override != null ? override : super.getDigest(path, hf); - } - }; + fs = new InMemoryFileSystem() { + @Override + protected InputStream getInputStream(Path path) throws IOException { + int c = calls.containsKey(path.toString()) + ? calls.get(path.toString()) : 0; + c++; + calls.put(path.toString(), c); + return super.getInputStream(path); + } + + @Override + protected byte[] getDigest(Path path, HashFunction hf) throws IOException { + assertThat(hf).isEqualTo(HashFunction.MD5); + byte[] override = md5Overrides.get(path.getPathString()); + return override != null ? override : super.getDigest(path, hf); + } + }; underTest = new SingleBuildFileCache("/", fs); Path root = fs.getRootDirectory(); Path file = root.getChild("empty"); diff --git a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java index 7f1f342d5e..bbb8be7e82 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java +++ b/src/test/java/com/google/devtools/build/lib/packages/PackageFactoryTest.java @@ -28,7 +28,6 @@ import com.google.devtools.build.lib.packages.util.PackageFactoryApparatus; import com.google.devtools.build.lib.packages.util.PackageFactoryTestBase; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.testutil.TestUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; @@ -1000,7 +999,7 @@ public class PackageFactoryTest extends PackageFactoryTestBase { scratch.file("/e/BUILD", "sh_library(name = 'e', data = glob(['*.txt']))"); Path parentDir = buildFile.getParentDirectory(); scratch.file("/e/data.txt"); - throwOnReaddir = LocalPath.create(parentDir.getPathString()); + throwOnReaddir = parentDir; try { packages.createPackage("e", buildFile); } catch (NoSuchPackageException expected) { diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java index fa5e7605ac..eb614fc49c 100644 --- a/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java +++ b/src/test/java/com/google/devtools/build/lib/packages/util/PackageFactoryTestBase.java @@ -39,7 +39,6 @@ import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.Dirent; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.FileNotFoundException; @@ -81,7 +80,7 @@ public abstract class PackageFactoryTestBase { protected abstract PackageFactoryApparatus createPackageFactoryApparatus(); - protected LocalPath throwOnReaddir = null; + protected Path throwOnReaddir = null; protected static AttributeMap attributes(Rule rule) { return RawAttributeMapper.of(rule); @@ -123,8 +122,7 @@ public abstract class PackageFactoryTestBase { FileSystem fs = new InMemoryFileSystem() { @Override - public Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) - throws IOException { + public Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException { if (path.equals(throwOnReaddir)) { throw new FileNotFoundException(path.getPathString()); } diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java index 9f84f73a57..184f22201b 100644 --- a/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java +++ b/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java @@ -25,7 +25,6 @@ import com.google.devtools.build.lib.skyframe.TransitiveTargetKey; import com.google.devtools.build.lib.skyframe.TransitiveTargetValue; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.ModifiedFileSet; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; @@ -49,16 +48,15 @@ public class IOExceptionsTest extends PackageLoadingTestCase { private static final String FS_ROOT = "/fsg"; - private static final Function<LocalPath, String> NULL_FUNCTION = - new Function<LocalPath, String>() { - @Override - @Nullable - public String apply(LocalPath path) { - return null; - } - }; + private static final Function<Path, String> NULL_FUNCTION = new Function<Path, String>() { + @Override + @Nullable + public String apply(Path path) { + return null; + } + }; - private Function<LocalPath, String> crashMessage = NULL_FUNCTION; + private Function<Path, String> crashMessage = NULL_FUNCTION; @Before public final void initializeVisitor() throws Exception { @@ -84,7 +82,7 @@ public class IOExceptionsTest extends PackageLoadingTestCase { protected FileSystem createFileSystem() { return new InMemoryFileSystem(BlazeClock.instance(), PathFragment.create(FS_ROOT)) { @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { String crash = crashMessage.apply(path); if (crash != null) { throw new IOException(crash); @@ -99,16 +97,15 @@ public class IOExceptionsTest extends PackageLoadingTestCase { reporter.removeHandler(failFastHandler); // expect errors final Path buildPath = scratch.file("pkg/BUILD", "sh_library(name = 'x')"); - crashMessage = - new Function<LocalPath, String>() { - @Override - public String apply(LocalPath path) { - if (buildPath.getLocalPath().equals(path)) { - return "custom crash: " + buildPath; - } - return null; - } - }; + crashMessage = new Function<Path, String>() { + @Override + public String apply(Path path) { + if (buildPath.equals(path)) { + return "custom crash: " + buildPath; + } + return null; + } + }; assertThat(visitTransitively(Label.parseAbsolute("//pkg:x"))).isFalse(); scratch.overwriteFile("pkg/BUILD", "# another comment to force reload", @@ -129,16 +126,15 @@ public class IOExceptionsTest extends PackageLoadingTestCase { "sh_library(name = 'top', deps = ['//pkg:x'])"); final Path buildPath = scratch.file("pkg/BUILD", "sh_library(name = 'x')"); - crashMessage = - new Function<LocalPath, String>() { - @Override - public String apply(LocalPath path) { - if (buildPath.getLocalPath().equals(path)) { - return "custom crash: " + buildPath; - } - return null; - } - }; + crashMessage = new Function<Path, String>() { + @Override + public String apply(Path path) { + if (buildPath.equals(path)) { + return "custom crash: " + buildPath; + } + return null; + } + }; assertThat(visitTransitively(Label.parseAbsolute("//top:top"))).isFalse(); assertContainsEvent("no such package 'pkg'"); // The traditional label visitor does not propagate the original IOException message. @@ -163,16 +159,15 @@ public class IOExceptionsTest extends PackageLoadingTestCase { final Path buildPath = scratch.file("top/BUILD", "sh_library(name = 'x')"); buildPath.getParentDirectory().getRelative("pkg").createDirectory(); - crashMessage = - new Function<LocalPath, String>() { - @Override - public String apply(LocalPath path) { - if (buildPath.getLocalPath().equals(path)) { - return "custom crash: " + buildPath; - } - return null; - } - }; + crashMessage = new Function<Path, String>() { + @Override + public String apply(Path path) { + if (buildPath.equals(path)) { + return "custom crash: " + buildPath; + } + return null; + } + }; assertThat(visitTransitively(Label.parseAbsolute("//top/pkg:x"))).isFalse(); } } diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java index 04e41a3c79..6e83bbedca 100644 --- a/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java +++ b/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java @@ -48,7 +48,6 @@ import com.google.devtools.build.lib.vfs.Dirent; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.ModifiedFileSet; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; @@ -77,8 +76,8 @@ import org.junit.runners.JUnit4; public class IncrementalLoadingTest { protected PackageCacheTester tester; - private LocalPath throwOnReaddir = null; - private LocalPath throwOnStat = null; + private Path throwOnReaddir = null; + private Path throwOnStat = null; @Before public final void createTester() throws Exception { @@ -86,8 +85,7 @@ public class IncrementalLoadingTest { FileSystem fs = new InMemoryFileSystem(clock) { @Override - public Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) - throws IOException { + public Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException { if (path.equals(throwOnReaddir)) { throw new FileNotFoundException(path.getPathString()); } @@ -96,7 +94,7 @@ public class IncrementalLoadingTest { @Nullable @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (path.equals(throwOnStat)) { throw new IOException("bork " + path.getPathString()); } @@ -339,7 +337,7 @@ public class IncrementalLoadingTest { Path buildFile = tester.addFile("e/BUILD", "sh_library(name = 'e', data = glob(['*.txt']))"); Path parentDir = buildFile.getParentDirectory(); tester.addFile("e/data.txt"); - throwOnReaddir = parentDir.getLocalPath(); + throwOnReaddir = parentDir; tester.sync(); try { tester.getTarget("//e:e"); diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java index 1eb87496a4..37a992d405 100644 --- a/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java +++ b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java @@ -21,7 +21,6 @@ import com.google.devtools.build.lib.vfs.Dirent; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryContentInfo; @@ -42,15 +41,14 @@ public class TargetPatternEvaluatorIOTest extends AbstractTargetPatternEvaluator private static class Transformer { @SuppressWarnings("unused") @Nullable - public FileStatus stat(FileStatus stat, LocalPath path, boolean followSymlinks) - throws IOException { + public FileStatus stat(FileStatus stat, Path path, boolean followSymlinks) throws IOException { return stat; } @SuppressWarnings("unused") @Nullable - public Collection<Dirent> readdir( - Collection<Dirent> readdir, LocalPath path, boolean followSymlinks) throws IOException { + public Collection<Dirent> readdir(Collection<Dirent> readdir, Path path, boolean followSymlinks) + throws IOException { return readdir; } } @@ -61,14 +59,13 @@ public class TargetPatternEvaluatorIOTest extends AbstractTargetPatternEvaluator protected FileSystem createFileSystem() { return new InMemoryFileSystem(BlazeClock.instance(), PathFragment.create(FS_ROOT)) { @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { FileStatus defaultResult = super.stat(path, followSymlinks); return transformer.stat(defaultResult, path, followSymlinks); } @Override - protected Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) - throws IOException { + protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException { Collection<Dirent> defaultResult = super.readdir(path, followSymlinks); return transformer.readdir(defaultResult, path, followSymlinks); } @@ -136,7 +133,7 @@ public class TargetPatternEvaluatorIOTest extends AbstractTargetPatternEvaluator return new Transformer() { @Nullable @Override - public FileStatus stat(FileStatus stat, LocalPath path, boolean followSymlinks) + public FileStatus stat(final FileStatus stat, Path path, boolean followSymlinks) throws IOException { if (path.getPathString().endsWith(badPathSuffix)) { return new InMemoryContentInfo(BlazeClock.instance()) { @@ -203,8 +200,8 @@ public class TargetPatternEvaluatorIOTest extends AbstractTargetPatternEvaluator return new Transformer() { @Nullable @Override - public Collection<Dirent> readdir( - Collection<Dirent> readdir, LocalPath path, boolean followSymlinks) throws IOException { + public Collection<Dirent> readdir(Collection<Dirent> readdir, Path path, + boolean followSymlinks) throws IOException { if (path.getPathString().endsWith(badPathSuffix)) { throw new IOException("Path ended in " + badPathSuffix + ", so readdir failed."); } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java index 289b420eac..68437dbdb3 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java @@ -26,7 +26,7 @@ import com.google.devtools.build.lib.syntax.SkylarkImport; import com.google.devtools.build.lib.testutil.TestConstants; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; -import com.google.devtools.build.lib.vfs.LocalPath; +import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.devtools.build.skyframe.ErrorInfo; import com.google.devtools.build.skyframe.EvaluationResult; @@ -47,9 +47,10 @@ public class ASTFileLookupFunctionTest extends BuildViewTestCase { boolean statThrowsIoException; @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (statThrowsIoException - && path.getPathString() + && path.asFragment() + .getPathString() .equals("/workspace/tools/build_rules/prelude_" + TestConstants.PRODUCT_NAME)) { throw new IOException("bork"); } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java index de9a9ab594..4004e85b8f 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java @@ -40,7 +40,6 @@ import com.google.devtools.build.lib.events.NullEventHandler; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.EvaluationResult; @@ -102,7 +101,7 @@ public class ArtifactFunctionTest extends ArtifactFunctionTestCase { setupRoot( new CustomInMemoryFs() { @Override - public byte[] getDigest(LocalPath path, HashFunction hf) throws IOException { + public byte[] getDigest(Path path, HashFunction hf) throws IOException { return path.getBaseName().equals("unreadable") ? expectedDigest : super.getDigest(path, hf); @@ -159,7 +158,7 @@ public class ArtifactFunctionTest extends ArtifactFunctionTestCase { setupRoot( new CustomInMemoryFs() { @Override - public byte[] getDigest(LocalPath path, HashFunction hf) throws IOException { + public byte[] getDigest(Path path, HashFunction hf) throws IOException { throw exception; } }); @@ -183,7 +182,7 @@ public class ArtifactFunctionTest extends ArtifactFunctionTestCase { setupRoot( new CustomInMemoryFs() { @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (path.getBaseName().equals("bad")) { throw exception; } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java index 6fd4837e1e..ff83c4a4dd 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java @@ -29,7 +29,6 @@ import com.google.devtools.build.lib.testutil.TestRuleClassProvider; import com.google.devtools.build.lib.testutil.TestUtils; import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator; @@ -172,7 +171,7 @@ abstract class ArtifactFunctionTestCase { /** InMemoryFileSystem that can pretend to do a fast digest. */ protected class CustomInMemoryFs extends InMemoryFileSystem { @Override - protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException { return fastDigest ? getDigest(path, hashFunction) : null; } } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java index ee0f01976d..e28fef3774 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java @@ -52,7 +52,6 @@ import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.RootedPath; @@ -445,7 +444,7 @@ public class FileFunctionTest { createFsAndRoot( new CustomInMemoryFs(manualClock) { @Override - protected byte[] getFastDigest(LocalPath path, HashFunction hf) throws IOException { + protected byte[] getFastDigest(Path path, HashFunction hf) throws IOException { return digest; } }); @@ -486,7 +485,7 @@ public class FileFunctionTest { createFsAndRoot( new CustomInMemoryFs(manualClock) { @Override - protected byte[] getFastDigest(LocalPath path, HashFunction hf) { + protected byte[] getFastDigest(Path path, HashFunction hf) { return path.getBaseName().equals("unreadable") ? expectedDigest : null; } }); @@ -831,7 +830,7 @@ public class FileFunctionTest { fs = new CustomInMemoryFs(manualClock) { @Override - protected byte[] getDigest(LocalPath path, HashFunction hf) throws IOException { + protected byte[] getDigest(Path path, HashFunction hf) throws IOException { digestCalls.incrementAndGet(); return super.getDigest(path, hf); } @@ -896,8 +895,8 @@ public class FileFunctionTest { // Our custom filesystem says "a" does not exist, so FileFunction shouldn't bother trying to // think about "a/b". Test for this by having a stat of "a/b" fail with an io error, and // observing that we don't encounter the error. - fs.stubStat(path("a").getLocalPath(), null); - fs.stubStatError(path("a/b").getLocalPath(), new IOException("ouch!")); + fs.stubStat(path("a"), null); + fs.stubStatError(path("a/b"), new IOException("ouch!")); assertThat(valueForPath(path("a/b")).exists()).isFalse(); } @@ -947,7 +946,7 @@ public class FileFunctionTest { return 0; } }; - fs.stubStat(path("a").getLocalPath(), inconsistentParentFileStatus); + fs.stubStat(path("a"), inconsistentParentFileStatus); // Disable fast-path md5 so that we don't try try to md5 the "a" (since it actually physically // is a directory). fastDigest = false; @@ -968,7 +967,7 @@ public class FileFunctionTest { public void testFilesystemInconsistencies_GetFastDigest() throws Exception { file("a"); // Our custom filesystem says "a/b" exists but "a" does not exist. - fs.stubFastDigestError(path("a").getLocalPath(), new IOException("nope")); + fs.stubFastDigestError(path("a"), new IOException("nope")); SequentialBuildDriver driver = makeDriver(); SkyKey skyKey = skyKey("a"); EvaluationResult<FileValue> result = @@ -986,7 +985,7 @@ public class FileFunctionTest { createFsAndRoot( new CustomInMemoryFs(manualClock) { @Override - protected boolean isReadable(LocalPath path) throws IOException { + protected boolean isReadable(Path path) throws IOException { if (path.getBaseName().equals("unreadable")) { throw new IOException("isReadable failed"); } @@ -1292,7 +1291,7 @@ public class FileFunctionTest { public void testInjectionOverIOException() throws Exception { Path foo = file("foo"); SkyKey fooKey = skyKey("foo"); - fs.stubStatError(foo.getLocalPath(), new IOException("bork")); + fs.stubStatError(foo, new IOException("bork")); BuildDriver driver = makeDriver(); EvaluationResult<FileValue> result = driver.evaluate( @@ -1307,7 +1306,7 @@ public class FileFunctionTest { .hasExceptionThat() .hasMessageThat() .isEqualTo("bork"); - fs.stubbedStatErrors.remove(foo.getLocalPath()); + fs.stubbedStatErrors.remove(foo); differencer.inject( fileStateSkyKey("foo"), FileStateValue.create( @@ -1677,36 +1676,36 @@ public class FileFunctionTest { private class CustomInMemoryFs extends InMemoryFileSystem { - private final Map<LocalPath, FileStatus> stubbedStats = Maps.newHashMap(); - private final Map<LocalPath, IOException> stubbedStatErrors = Maps.newHashMap(); - private final Map<LocalPath, IOException> stubbedFastDigestErrors = Maps.newHashMap(); + private final Map<Path, FileStatus> stubbedStats = Maps.newHashMap(); + private final Map<Path, IOException> stubbedStatErrors = Maps.newHashMap(); + private final Map<Path, IOException> stubbedFastDigestErrors = Maps.newHashMap(); public CustomInMemoryFs(ManualClock manualClock) { super(manualClock); } - public void stubFastDigestError(LocalPath path, IOException error) { + public void stubFastDigestError(Path path, IOException error) { stubbedFastDigestErrors.put(path, error); } @Override - protected byte[] getFastDigest(LocalPath path, HashFunction hashFunction) throws IOException { + protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException { if (stubbedFastDigestErrors.containsKey(path)) { throw stubbedFastDigestErrors.get(path); } return fastDigest ? getDigest(path) : null; } - public void stubStat(LocalPath path, @Nullable FileStatus stubbedResult) { + public void stubStat(Path path, @Nullable FileStatus stubbedResult) { stubbedStats.put(path, stubbedResult); } - public void stubStatError(LocalPath path, IOException error) { + public void stubStatError(Path path, IOException error) { stubbedStatErrors.put(path, error); } @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (stubbedStatErrors.containsKey(path)) { throw stubbedStatErrors.get(path); } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java index a57f39526f..eb2d141b1c 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java @@ -48,7 +48,6 @@ import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileStatusWithDigest; import com.google.devtools.build.lib.vfs.FileStatusWithDigestAdapter; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.ModifiedFileSet; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; @@ -808,7 +807,7 @@ public class FilesystemValueCheckerTest { } @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (statThrowsRuntimeException) { throw new RuntimeException("bork"); } @@ -816,7 +815,7 @@ public class FilesystemValueCheckerTest { } @Override - protected String readSymbolicLink(LocalPath path) throws IOException { + protected PathFragment readSymbolicLink(Path path) throws IOException { if (readlinkThrowsIoException) { throw new IOException("readlink failed"); } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java index 3ac3dc49a6..13acf704e9 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java @@ -43,7 +43,6 @@ import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor; import com.google.devtools.build.lib.vfs.Dirent; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.RootedPath; @@ -630,7 +629,7 @@ public abstract class GlobFunctionTest { @Test public void testResilienceToFilesystemInconsistencies_DirectoryExistence() throws Exception { // Our custom filesystem says "pkgPath/BUILD" exists but "pkgPath" does not exist. - fs.stubStat(pkgPath.getLocalPath(), null); + fs.stubStat(pkgPath, null); RootedPath pkgRootedPath = RootedPath.toRootedPath(root, pkgPath); FileStateValue pkgDirFileStateValue = FileStateValue.create(pkgRootedPath, null); FileValue pkgDirValue = @@ -655,7 +654,7 @@ public abstract class GlobFunctionTest { // Our custom filesystem says directory "pkgPath/foo/bar" contains a subdirectory "wiz" but a // direct stat on "pkgPath/foo/bar/wiz" says it does not exist. Path fooBarDir = pkgPath.getRelative("foo/bar"); - fs.stubStat(fooBarDir.getRelative("wiz").getLocalPath(), null); + fs.stubStat(fooBarDir.getRelative("wiz"), null); RootedPath fooBarDirRootedPath = RootedPath.toRootedPath(root, fooBarDir); SkyValue fooBarDirListingValue = DirectoryListingStateValue.create( @@ -684,7 +683,7 @@ public abstract class GlobFunctionTest { RootedPath.toRootedPath(root, pkgPath.getRelative("foo/bar/wiz/file")); final FileStatus realStat = fileRootedPath.asPath().stat(); fs.stubStat( - fileRootedPath.asPath().getLocalPath(), + fileRootedPath.asPath(), new FileStatus() { @Override @@ -761,18 +760,18 @@ public abstract class GlobFunctionTest { private static final class CustomInMemoryFs extends InMemoryFileSystem { - private Map<LocalPath, FileStatus> stubbedStats = Maps.newHashMap(); + private Map<Path, FileStatus> stubbedStats = Maps.newHashMap(); public CustomInMemoryFs(ManualClock manualClock) { super(manualClock); } - public void stubStat(LocalPath path, @Nullable FileStatus stubbedResult) { + public void stubStat(Path path, @Nullable FileStatus stubbedResult) { stubbedStats.put(path, stubbedResult); } @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (stubbedStats.containsKey(path)) { return stubbedStats.get(path); } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java index 54f23f430d..8773d54825 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java @@ -39,7 +39,6 @@ import com.google.devtools.build.lib.vfs.Dirent; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.ModifiedFileSet; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; @@ -165,7 +164,7 @@ public class PackageFunctionTest extends BuildViewTestCase { return 0; } }; - fs.stubStat(fooDir.getLocalPath(), inconsistentParentFileStatus); + fs.stubStat(fooDir, inconsistentParentFileStatus); RootedPath pkgRootedPath = RootedPath.toRootedPath(pkgRoot, fooDir); SkyValue fooDirValue = FileStateValue.create(pkgRootedPath, tsgm); differencer.inject(ImmutableMap.of(FileStateValue.key(pkgRootedPath), fooDirValue)); @@ -197,7 +196,7 @@ public class PackageFunctionTest extends BuildViewTestCase { // Our custom filesystem says "foo/bar/baz" does not exist but it also says that "foo/bar" // has a child directory "baz". - fs.stubStat(bazDir.getLocalPath(), null); + fs.stubStat(bazDir, null); RootedPath barDirRootedPath = RootedPath.toRootedPath(pkgRoot, barDir); FileStateValue barDirFileStateValue = FileStateValue.create(barDirRootedPath, tsgm); FileValue barDirFileValue = FileValue.value(barDirRootedPath, barDirFileStateValue, @@ -226,7 +225,7 @@ public class PackageFunctionTest extends BuildViewTestCase { Path fooDir = fooBuildFile.getParentDirectory(); Path barDir = fooDir.getRelative("bar"); scratch.file("foo/bar/baz.sh"); - fs.scheduleMakeUnreadableAfterReaddir(barDir.getLocalPath()); + fs.scheduleMakeUnreadableAfterReaddir(barDir); SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo")); String expectedMessage = "Encountered error 'Directory is not readable'"; @@ -537,7 +536,7 @@ public class PackageFunctionTest extends BuildViewTestCase { scratch.file("foo/BUILD", "sh_library(name = 'foo', srcs = ['bar/baz.sh'])"); Path barBuildFile = scratch.file("foo/bar/BUILD"); - fs.stubStatError(barBuildFile.getLocalPath(), new IOException("nope")); + fs.stubStatError(barBuildFile, new IOException("nope")); SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo")); EvaluationResult<PackageValue> result = SkyframeExecutorTestUtils.evaluate( getSkyframeExecutor(), skyKey, /*keepGoing=*/false, reporter); @@ -675,7 +674,7 @@ public class PackageFunctionTest extends BuildViewTestCase { public void testPackageLoadingErrorOnIOExceptionReadingBuildFile() throws Exception { Path fooBuildFilePath = scratch.file("foo/BUILD"); IOException exn = new IOException("nope"); - fs.throwExceptionOnGetInputStream(fooBuildFilePath.getLocalPath(), exn); + fs.throwExceptionOnGetInputStream(fooBuildFilePath, exn); SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo")); EvaluationResult<PackageValue> result = SkyframeExecutorTestUtils.evaluate( @@ -693,7 +692,7 @@ public class PackageFunctionTest extends BuildViewTestCase { scratch.file("foo/BUILD", "load('//foo:bzl.bzl', 'x')"); Path fooBzlFilePath = scratch.file("foo/bzl.bzl"); IOException exn = new IOException("nope"); - fs.throwExceptionOnGetInputStream(fooBzlFilePath.getLocalPath(), exn); + fs.throwExceptionOnGetInputStream(fooBzlFilePath, exn); SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo")); EvaluationResult<PackageValue> result = SkyframeExecutorTestUtils.evaluate( @@ -740,49 +739,49 @@ public class PackageFunctionTest extends BuildViewTestCase { } } - private final Map<LocalPath, FileStatusOrException> stubbedStats = Maps.newHashMap(); - private final Set<LocalPath> makeUnreadableAfterReaddir = Sets.newHashSet(); - private final Map<LocalPath, IOException> pathsToErrorOnGetInputStream = Maps.newHashMap(); + private final Map<Path, FileStatusOrException> stubbedStats = Maps.newHashMap(); + private final Set<Path> makeUnreadableAfterReaddir = Sets.newHashSet(); + private final Map<Path, IOException> pathsToErrorOnGetInputStream = Maps.newHashMap(); public CustomInMemoryFs(ManualClock manualClock) { super(manualClock); } - public void stubStat(LocalPath path, @Nullable FileStatus stubbedResult) { + public void stubStat(Path path, @Nullable FileStatus stubbedResult) { stubbedStats.put(path, new FileStatusOrException.FileStatusImpl(stubbedResult)); } - public void stubStatError(LocalPath path, IOException stubbedResult) { + public void stubStatError(Path path, IOException stubbedResult) { stubbedStats.put(path, new FileStatusOrException.ExceptionImpl(stubbedResult)); } @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (stubbedStats.containsKey(path)) { return stubbedStats.get(path).get(); } return super.stat(path, followSymlinks); } - public void scheduleMakeUnreadableAfterReaddir(LocalPath path) { + public void scheduleMakeUnreadableAfterReaddir(Path path) { makeUnreadableAfterReaddir.add(path); } @Override - public Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) throws IOException { + public Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException { Collection<Dirent> result = super.readdir(path, followSymlinks); if (makeUnreadableAfterReaddir.contains(path)) { - setReadable(path, false); + path.setReadable(false); } return result; } - public void throwExceptionOnGetInputStream(LocalPath path, IOException exn) { + public void throwExceptionOnGetInputStream(Path path, IOException exn) { pathsToErrorOnGetInputStream.put(path, exn); } @Override - protected InputStream getInputStream(LocalPath path) throws IOException { + protected InputStream getInputStream(Path path) throws IOException { IOException exnToThrow = pathsToErrorOnGetInputStream.get(path); if (exnToThrow != null) { throw exnToThrow; diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java index 13f5fa2df3..e271d90237 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java @@ -45,7 +45,6 @@ import com.google.devtools.build.lib.testutil.TestUtils; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.FileNotFoundException; @@ -242,59 +241,58 @@ public class ParallelBuilderTest extends TimestampBuilderTestCase { @Test public void testUpdateCacheError() throws Exception { - FileSystem fs = - new InMemoryFileSystem() { - @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { - final FileStatus stat = super.stat(path, followSymlinks); - if (path.toString().endsWith("/out/foo")) { - return new FileStatus() { - private final FileStatus original = stat; + FileSystem fs = new InMemoryFileSystem() { + @Override + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { + final FileStatus stat = super.stat(path, followSymlinks); + if (path.toString().endsWith("/out/foo")) { + return new FileStatus() { + private final FileStatus original = stat; - @Override - public boolean isSymbolicLink() { - return original.isSymbolicLink(); - } + @Override + public boolean isSymbolicLink() { + return original.isSymbolicLink(); + } - @Override - public boolean isFile() { - return original.isFile(); - } + @Override + public boolean isFile() { + return original.isFile(); + } - @Override - public boolean isDirectory() { - return original.isDirectory(); - } + @Override + public boolean isDirectory() { + return original.isDirectory(); + } - @Override - public boolean isSpecialFile() { - return original.isSpecialFile(); - } + @Override + public boolean isSpecialFile() { + return original.isSpecialFile(); + } - @Override - public long getSize() throws IOException { - return original.getSize(); - } + @Override + public long getSize() throws IOException { + return original.getSize(); + } - @Override - public long getNodeId() throws IOException { - return original.getNodeId(); - } + @Override + public long getNodeId() throws IOException { + return original.getNodeId(); + } - @Override - public long getLastModifiedTime() throws IOException { - throw new IOException(); - } + @Override + public long getLastModifiedTime() throws IOException { + throw new IOException(); + } - @Override - public long getLastChangeTime() throws IOException { - return original.getLastChangeTime(); - } - }; + @Override + public long getLastChangeTime() throws IOException { + return original.getLastChangeTime(); } - return stat; - } - }; + }; + } + return stat; + } + }; Artifact foo = createDerivedArtifact(fs, "foo"); registerAction(new TestAction(TestAction.NO_EFFECT, emptySet, ImmutableList.of(foo))); reporter.removeHandler(failFastHandler); diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java index 8bca6bd55c..63e900f344 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTest.java @@ -490,7 +490,7 @@ public class SkyframeLabelVisitorTest extends SkyframeLabelVisitorTestCase { return 0; } }; - fs.stubStat(bazDir.getLocalPath(), inconsistentParentFileStatus); + fs.stubStat(bazDir, inconsistentParentFileStatus); Set<Label> labels = ImmutableSet.of(Label.parseAbsolute("//foo:foo")); getSkyframeExecutor() .getPackageManager() diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java index e3c54a55a2..f20cea8a51 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java @@ -36,8 +36,8 @@ import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader; import com.google.devtools.build.lib.testutil.ManualClock; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.ModifiedFileSet; +import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.devtools.build.skyframe.DelegatingWalkableGraph; import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator; @@ -240,18 +240,18 @@ abstract public class SkyframeLabelVisitorTestCase extends PackageLoadingTestCas protected static class CustomInMemoryFs extends InMemoryFileSystem { - private Map<LocalPath, FileStatus> stubbedStats = Maps.newHashMap(); + private Map<Path, FileStatus> stubbedStats = Maps.newHashMap(); public CustomInMemoryFs(ManualClock manualClock) { super(manualClock); } - public void stubStat(LocalPath path, @Nullable FileStatus stubbedResult) { + public void stubStat(Path path, @Nullable FileStatus stubbedResult) { stubbedStats.put(path, stubbedResult); } @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (stubbedStats.containsKey(path)) { return stubbedStats.get(path); } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java index 986c5a5038..1e944d22cb 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java @@ -26,7 +26,6 @@ import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystem; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.ModifiedFileSet; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; @@ -133,7 +132,7 @@ public class TargetMarkerFunctionTest extends BuildViewTestCase { reporter.removeHandler(failFastHandler); scratch.file("a/BUILD", "sh_library(name = 'b/c')"); Path subpackageBuildFile = scratch.file("a/b/BUILD", "sh_library(name = 'c')"); - fs.stubStatIOException(subpackageBuildFile.getLocalPath(), new IOException("nope")); + fs.stubStatIOException(subpackageBuildFile, new IOException("nope")); BuildFileNotFoundException exn = (BuildFileNotFoundException) getErrorFromTargetValue("//a:b/c"); assertThat(exn).hasMessageThat().contains("nope"); @@ -141,18 +140,18 @@ public class TargetMarkerFunctionTest extends BuildViewTestCase { private static class CustomInMemoryFs extends InMemoryFileSystem { - private Map<LocalPath, IOException> stubbedStatExceptions = Maps.newHashMap(); + private Map<Path, IOException> stubbedStatExceptions = Maps.newHashMap(); public CustomInMemoryFs() { super(BlazeClock.instance()); } - public void stubStatIOException(LocalPath path, IOException stubbedResult) { + public void stubStatIOException(Path path, IOException stubbedResult) { stubbedStatExceptions.put(path, stubbedResult); } @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (stubbedStatExceptions.containsKey(path)) { throw stubbedStatExceptions.get(path); } diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java index 1e52a54ec5..3e156beaec 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java @@ -40,7 +40,6 @@ import com.google.devtools.build.lib.actions.util.TestAction.DummyAction; import com.google.devtools.build.lib.events.NullEventHandler; import com.google.devtools.build.lib.vfs.FileStatus; import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.LocalPath; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.EvaluationResult; @@ -181,7 +180,7 @@ public class TreeArtifactMetadataTest extends ArtifactFunctionTestCase { setupRoot( new CustomInMemoryFs() { @Override - public FileStatus stat(LocalPath path, boolean followSymlinks) throws IOException { + public FileStatus stat(Path path, boolean followSymlinks) throws IOException { if (path.getBaseName().equals("one")) { throw exception; } diff --git a/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java b/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java index be410fdf6f..d4a079db94 100644 --- a/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java +++ b/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java @@ -38,13 +38,11 @@ public class DependencySetWindowsTest { @Test public void dotDParser_windowsPaths() throws Exception { - Path dotd = - scratch.file( - "C:/tmp/foo.d", - "bazel-out/hello-lib/cpp/hello-lib.o: \\", - " cpp/hello-lib.cc cpp/hello-lib.h c:\\mingw\\include\\stdio.h \\", - " c:\\mingw\\include\\_mingw.h \\", - " c:\\mingw\\lib\\gcc\\mingw32\\4.8.1\\include\\stdarg.h"); + Path dotd = scratch.file("/tmp/foo.d", + "bazel-out/hello-lib/cpp/hello-lib.o: \\", + " cpp/hello-lib.cc cpp/hello-lib.h c:\\mingw\\include\\stdio.h \\", + " c:\\mingw\\include\\_mingw.h \\", + " c:\\mingw\\lib\\gcc\\mingw32\\4.8.1\\include\\stdarg.h"); Set<Path> expected = Sets.newHashSet( root.getRelative("cpp/hello-lib.cc"), @@ -58,11 +56,9 @@ public class DependencySetWindowsTest { @Test public void dotDParser_windowsPathsWithSpaces() throws Exception { - Path dotd = - scratch.file( - "C:/tmp/foo.d", - "bazel-out/hello-lib/cpp/hello-lib.o: \\", - "C:\\Program\\ Files\\ (x86)\\LLVM\\stddef.h"); + Path dotd = scratch.file("/tmp/foo.d", + "bazel-out/hello-lib/cpp/hello-lib.o: \\", + "C:\\Program\\ Files\\ (x86)\\LLVM\\stddef.h"); assertThat(newDependencySet().read(dotd).getDependencies()) .containsExactlyElementsIn( Sets.newHashSet(fileSystem.getPath("C:/Program Files (x86)/LLVM/stddef.h"))); @@ -73,14 +69,12 @@ public class DependencySetWindowsTest { // This is (slightly simplified) actual output from clang. Yes, clang will happily mix // forward slashes and backslashes in a single path, not to mention using backslashes as // separators next to backslashes as escape characters. - Path dotd = - scratch.file( - "C:/tmp/foo.d", - "bazel-out/hello-lib/cpp/hello-lib.o: \\", - "cpp/hello-lib.cc cpp/hello-lib.h /mingw/include\\stdio.h \\", - "/mingw/include\\_mingw.h \\", - "C:\\Program\\ Files\\ (x86)\\LLVM\\bin\\..\\lib\\clang\\3.5.0\\include\\stddef.h \\", - "C:\\Program\\ Files\\ (x86)\\LLVM\\bin\\..\\lib\\clang\\3.5.0\\include\\stdarg.h"); + Path dotd = scratch.file("/tmp/foo.d", + "bazel-out/hello-lib/cpp/hello-lib.o: \\", + "cpp/hello-lib.cc cpp/hello-lib.h /mingw/include\\stdio.h \\", + "/mingw/include\\_mingw.h \\", + "C:\\Program\\ Files\\ (x86)\\LLVM\\bin\\..\\lib\\clang\\3.5.0\\include\\stddef.h \\", + "C:\\Program\\ Files\\ (x86)\\LLVM\\bin\\..\\lib\\clang\\3.5.0\\include\\stdarg.h"); Set<Path> expected = Sets.newHashSet( root.getRelative("cpp/hello-lib.cc"), @@ -99,8 +93,10 @@ public class DependencySetWindowsTest { Path file2 = fileSystem.getPath("C:/blah/blah/genhello/hello.h"); Path file2DiffCase = fileSystem.getPath("C:/Blah/blah/Genhello/hello.h"); String filename = "hello.o"; - Path dotd = - scratch.file("C:/tmp/foo.d", filename + ": \\", " " + file1 + " \\", " " + file2 + " "); + Path dotd = scratch.file("/tmp/foo.d", + filename + ": \\", + " " + file1 + " \\", + " " + file2 + " "); assertThat(newDependencySet().read(dotd).getDependencies()) .containsExactly(file1, file2DiffCase); } diff --git a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java index 09ac79eec6..278a288dda 100644 --- a/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java +++ b/src/test/java/com/google/devtools/build/lib/vfs/FileSystemTest.java @@ -379,7 +379,7 @@ public abstract class FileSystemTest { @Test public void testSymbolicFileLinkExists() throws Exception { Path someLink = absolutize("some-link"); - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { someLink.createSymbolicLink(xFile); assertThat(someLink.exists()).isTrue(); assertThat(someLink.statIfFound()).isNotNull(); @@ -389,7 +389,7 @@ public abstract class FileSystemTest { @Test public void testSymbolicFileLinkIsSymbolicLink() throws Exception { Path someLink = absolutize("some-link"); - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { someLink.createSymbolicLink(xFile); assertThat(someLink.isSymbolicLink()).isTrue(); } @@ -398,7 +398,7 @@ public abstract class FileSystemTest { @Test public void testSymbolicFileLinkIsFile() throws Exception { Path someLink = absolutize("some-link"); - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { someLink.createSymbolicLink(xFile); assertThat(someLink.isFile()).isTrue(); } @@ -407,7 +407,7 @@ public abstract class FileSystemTest { @Test public void testSymbolicFileLinkIsNotDirectory() throws Exception { Path someLink = absolutize("some-link"); - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { someLink.createSymbolicLink(xFile); assertThat(someLink.isDirectory()).isFalse(); } @@ -416,7 +416,7 @@ public abstract class FileSystemTest { @Test public void testSymbolicDirLinkExists() throws Exception { Path someLink = absolutize("some-link"); - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { someLink.createSymbolicLink(xEmptyDirectory); assertThat(someLink.exists()).isTrue(); assertThat(someLink.statIfFound()).isNotNull(); @@ -426,7 +426,7 @@ public abstract class FileSystemTest { @Test public void testSymbolicDirLinkIsSymbolicLink() throws Exception { Path someLink = absolutize("some-link"); - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { someLink.createSymbolicLink(xEmptyDirectory); assertThat(someLink.isSymbolicLink()).isTrue(); } @@ -435,7 +435,7 @@ public abstract class FileSystemTest { @Test public void testSymbolicDirLinkIsDirectory() throws Exception { Path someLink = absolutize("some-link"); - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { someLink.createSymbolicLink(xEmptyDirectory); assertThat(someLink.isDirectory()).isTrue(); } @@ -444,7 +444,7 @@ public abstract class FileSystemTest { @Test public void testSymbolicDirLinkIsNotFile() throws Exception { Path someLink = absolutize("some-link"); - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { someLink.createSymbolicLink(xEmptyDirectory); assertThat(someLink.isFile()).isFalse(); } @@ -1287,7 +1287,7 @@ public abstract class FileSystemTest { Path xNonEmptyDirectoryBar = xNonEmptyDirectory.getChild("bar"); xNonEmptyDirectory.setWritable(false); - if (testFS.supportsSymbolicLinksNatively(xNonEmptyDirectoryBar.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(xNonEmptyDirectoryBar)) { try { createSymbolicLink(xNonEmptyDirectoryBar, xNonEmptyDirectoryFoo); fail("No exception thrown."); @@ -1335,19 +1335,19 @@ public abstract class FileSystemTest { @Test public void testResolveSymlinks() throws Exception { - if (testFS.supportsSymbolicLinksNatively(xLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(xLink)) { createSymbolicLink(xLink, xFile); FileSystemUtils.createEmptyFile(xFile); - assertThat(testFS.resolveOneLink(xLink.getLocalPath())).isEqualTo(xFile.getPathString()); + assertThat(testFS.resolveOneLink(xLink)).isEqualTo(xFile.asFragment()); assertThat(xLink.resolveSymbolicLinks()).isEqualTo(xFile); } } @Test public void testResolveDanglingSymlinks() throws Exception { - if (testFS.supportsSymbolicLinksNatively(xLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(xLink)) { createSymbolicLink(xLink, xNothing); - assertThat(testFS.resolveOneLink(xLink.getLocalPath())).isEqualTo(xNothing.getPathString()); + assertThat(testFS.resolveOneLink(xLink)).isEqualTo(xNothing.asFragment()); try { xLink.resolveSymbolicLinks(); fail(); @@ -1358,15 +1358,15 @@ public abstract class FileSystemTest { @Test public void testResolveNonSymlinks() throws Exception { - if (testFS.supportsSymbolicLinksNatively(xFile.getLocalPath())) { - assertThat(testFS.resolveOneLink(xFile.getLocalPath())).isNull(); + if (testFS.supportsSymbolicLinksNatively(xFile)) { + assertThat(testFS.resolveOneLink(xFile)).isNull(); assertThat(xFile.resolveSymbolicLinks()).isEqualTo(xFile); } } @Test public void testCreateHardLink_Success() throws Exception { - if (!testFS.supportsHardLinksNatively(xFile.getLocalPath())) { + if (!testFS.supportsHardLinksNatively(xFile)) { return; } xFile.createHardLink(xLink); @@ -1379,7 +1379,7 @@ public abstract class FileSystemTest { @Test public void testCreateHardLink_NeitherOriginalNorLinkExists() throws Exception { - if (!testFS.supportsHardLinksNatively(xFile.getLocalPath())) { + if (!testFS.supportsHardLinksNatively(xFile)) { return; } @@ -1398,7 +1398,7 @@ public abstract class FileSystemTest { @Test public void testCreateHardLink_OriginalDoesNotExistAndLinkExists() throws Exception { - if (!testFS.supportsHardLinksNatively(xFile.getLocalPath())) { + if (!testFS.supportsHardLinksNatively(xFile)) { return; } @@ -1419,7 +1419,7 @@ public abstract class FileSystemTest { @Test public void testCreateHardLink_BothOriginalAndLinkExist() throws Exception { - if (!testFS.supportsHardLinksNatively(xFile.getLocalPath())) { + if (!testFS.supportsHardLinksNatively(xFile)) { return; } /* Both original file and link file exist */ @@ -1437,7 +1437,6 @@ public abstract class FileSystemTest { } protected boolean isHardLinked(Path a, Path b) throws IOException { - return testFS.stat(a.getLocalPath(), false).getNodeId() - == testFS.stat(b.getLocalPath(), false).getNodeId(); + return testFS.stat(a, false).getNodeId() == testFS.stat(b, false).getNodeId(); } } 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 3b9a2dc76d..0e9b74ed3b 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 @@ -851,8 +851,8 @@ public class FileSystemUtilsTest { FileSystemUtils.createHardLink(linkPath, originalPath); assertThat(originalPath.exists()).isTrue(); assertThat(linkPath.exists()).isTrue(); - assertThat(fileSystem.stat(linkPath.getLocalPath(), false).getNodeId()) - .isEqualTo(fileSystem.stat(originalPath.getLocalPath(), false).getNodeId()); + assertThat(fileSystem.stat(linkPath, false).getNodeId()) + .isEqualTo(fileSystem.stat(originalPath, false).getNodeId()); } @Test @@ -892,11 +892,11 @@ public class FileSystemUtilsTest { assertThat(linkPath1.exists()).isTrue(); assertThat(linkPath2.exists()).isTrue(); assertThat(linkPath3.exists()).isTrue(); - assertThat(fileSystem.stat(linkPath1.getLocalPath(), false).getNodeId()) - .isEqualTo(fileSystem.stat(originalPath1.getLocalPath(), false).getNodeId()); - assertThat(fileSystem.stat(linkPath2.getLocalPath(), false).getNodeId()) - .isEqualTo(fileSystem.stat(originalPath2.getLocalPath(), false).getNodeId()); - assertThat(fileSystem.stat(linkPath3.getLocalPath(), false).getNodeId()) - .isEqualTo(fileSystem.stat(originalPath3.getLocalPath(), false).getNodeId()); + assertThat(fileSystem.stat(linkPath1, false).getNodeId()) + .isEqualTo(fileSystem.stat(originalPath1, false).getNodeId()); + assertThat(fileSystem.stat(linkPath2, false).getNodeId()) + .isEqualTo(fileSystem.stat(originalPath2, false).getNodeId()); + assertThat(fileSystem.stat(linkPath3, false).getNodeId()) + .isEqualTo(fileSystem.stat(originalPath3, false).getNodeId()); } } diff --git a/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java b/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java index 70fc86dc48..5b0958fae1 100644 --- a/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java +++ b/src/test/java/com/google/devtools/build/lib/vfs/GlobTest.java @@ -54,18 +54,15 @@ public class GlobTest { @Before public final void initializeFileSystem() throws Exception { - fs = - new InMemoryFileSystem() { - @Override - public Collection<Dirent> readdir(LocalPath path, boolean followSymlinks) - throws IOException { - if (throwOnReaddir != null - && path.getPathString().equals(throwOnReaddir.getPathString())) { - throw new FileNotFoundException(path.getPathString()); - } - return super.readdir(path, followSymlinks); - } - }; + fs = new InMemoryFileSystem() { + @Override + public Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException { + if (path.equals(throwOnReaddir)) { + throw new FileNotFoundException(path.getPathString()); + } + return super.readdir(path, followSymlinks); + } + }; tmpPath = fs.getPath("/globtmp"); for (String dir : ImmutableList.of("foo/bar/wiz", "foo/barnacle/wiz", diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java new file mode 100644 index 0000000000..0807b4aa5d --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/vfs/PathTrieTest.java @@ -0,0 +1,78 @@ +// Copyright 2014 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.vfs; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link PathTrie}. */ +@RunWith(JUnit4.class) +public class PathTrieTest { + @Test + public void empty() { + PathTrie<Integer> pathTrie = new PathTrie<>(); + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull(); + assertThat(pathTrie.get(PathFragment.create("/x"))).isNull(); + assertThat(pathTrie.get(PathFragment.create("/x/y"))).isNull(); + } + + @Test + public void simpleBranches() { + PathTrie<Integer> pathTrie = new PathTrie<>(); + pathTrie.put(PathFragment.create("/a"), 1); + pathTrie.put(PathFragment.create("/b"), 2); + + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/b"))).isEqualTo(2); + assertThat(pathTrie.get(PathFragment.create("/b/c"))).isEqualTo(2); + } + + @Test + public void nestedDirectories() { + PathTrie<Integer> pathTrie = new PathTrie<>(); + pathTrie.put(PathFragment.create("/a/b/c"), 3); + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a"))).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a/b"))).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(3); + assertThat(pathTrie.get(PathFragment.create("/a/b/c/d"))).isEqualTo(3); + + pathTrie.put(PathFragment.create("/a"), 1); + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isNull(); + assertThat(pathTrie.get(PathFragment.create("/b"))).isNull(); + assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b/c"))).isEqualTo(3); + assertThat(pathTrie.get(PathFragment.create("/a/b/c/d"))).isEqualTo(3); + + pathTrie.put(PathFragment.ROOT_FRAGMENT, 0); + assertThat(pathTrie.get(PathFragment.EMPTY_FRAGMENT)).isEqualTo(0); + assertThat(pathTrie.get(PathFragment.create("/b"))).isEqualTo(0); + assertThat(pathTrie.get(PathFragment.create("/a"))).isEqualTo(1); + assertThat(pathTrie.get(PathFragment.create("/a/b"))).isEqualTo(1); + } + + @Test + public void unrootedDirectoriesError() { + PathTrie<Integer> pathTrie = new PathTrie<>(); + assertThrows(IllegalArgumentException.class, () -> pathTrie.put(PathFragment.create("a"), 1)); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/vfs/SearchPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/SearchPathTest.java index b49af40df9..6c545ac790 100644 --- a/src/test/java/com/google/devtools/build/lib/vfs/SearchPathTest.java +++ b/src/test/java/com/google/devtools/build/lib/vfs/SearchPathTest.java @@ -38,7 +38,7 @@ public class SearchPathTest { assertThat(SearchPath.parse(fs, "/:/bin")).isEqualTo(searchPath); assertThat(SearchPath.parse(fs, ".:/:/bin")).isEqualTo(searchPath); - fs.getOutputStream(fs.getPath("/bin/exe").getLocalPath()).write(new byte[5]); + fs.getOutputStream(fs.getPath("/bin/exe")).write(new byte[5]); assertThat(SearchPath.which(searchPath, "exe")).isNull(); diff --git a/src/test/java/com/google/devtools/build/lib/vfs/SymlinkAwareFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/SymlinkAwareFileSystemTest.java index b9f6485c8a..4720a6e773 100644 --- a/src/test/java/com/google/devtools/build/lib/vfs/SymlinkAwareFileSystemTest.java +++ b/src/test/java/com/google/devtools/build/lib/vfs/SymlinkAwareFileSystemTest.java @@ -78,7 +78,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { assertThat(linkPath.isDirectory(Symlinks.NOFOLLOW)).isFalse(); assertThat(linkPath.isDirectory(Symlinks.FOLLOW)).isFalse(); - if (testFS.supportsSymbolicLinksNatively(linkPath.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(linkPath)) { assertThat(linkPath.getFileSize(Symlinks.NOFOLLOW)).isEqualTo(newPath.toString().length()); assertThat(linkPath.getFileSize()).isEqualTo(newPath.getFileSize(Symlinks.NOFOLLOW)); } @@ -196,7 +196,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { PathFragment relative = PathFragment.create(linkTarget); linkPath.delete(); createSymbolicLink(linkPath, relative); - if (testFS.supportsSymbolicLinksNatively(linkPath.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(linkPath)) { assertThat(linkPath.getFileSize(Symlinks.NOFOLLOW)).isEqualTo(linkTarget.length()); assertThat(linkPath.readSymbolicLink()).isEqualTo(relative); } @@ -224,7 +224,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { // The path may not be a symlink, neither on Darwin nor on Linux. String nonLinkEntry = null; - for (String child : testFS.getDirectoryEntries(rootPath.getLocalPath())) { + for (String child : testFS.getDirectoryEntries(rootPath)) { Path p = rootPath.getChild(child); if (!p.isSymbolicLink() && p.isDirectory()) { nonLinkEntry = p.getBaseName(); @@ -259,7 +259,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { Path link = absolutize("recursive-link"); createSymbolicLink(link, link); - if (testFS.supportsSymbolicLinksNatively(link.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(link)) { try { link.resolveSymbolicLinks(); fail(); @@ -276,7 +276,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { createSymbolicLink(link2, link1); createSymbolicLink(link1, link2); - if (testFS.supportsSymbolicLinksNatively(link1.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(link1)) { try { link1.resolveSymbolicLinks(); fail(); @@ -288,7 +288,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { @Test public void testResolveSymbolicLinksENOENT() { - if (testFS.supportsSymbolicLinksNatively(xDanglingLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(xDanglingLink)) { try { xDanglingLink.resolveSymbolicLinks(); fail(); @@ -302,7 +302,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { public void testResolveSymbolicLinksENOTDIR() throws IOException { Path badLinkTarget = xFile.getChild("bad"); // parent is not a directory! Path badLink = absolutize("badLink"); - if (testFS.supportsSymbolicLinksNatively(badLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(badLink)) { createSymbolicLink(badLink, badLinkTarget); try { badLink.resolveSymbolicLinks(); @@ -317,7 +317,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { @Test public void testResolveSymbolicLinksWithUplevelRefs() throws IOException { - if (testFS.supportsSymbolicLinksNatively(xLinkToFile.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(xLinkToFile)) { // Create a series of links that refer to xFile as ./xFile, // ./../foo/xFile, ./../../bar/foo/xFile, etc. They should all resolve // to xFile. @@ -335,7 +335,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { @Test public void testReadSymbolicLink() throws IOException { - if (testFS.supportsSymbolicLinksNatively(xDanglingLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(xDanglingLink)) { assertThat(xDanglingLink.readSymbolicLink().toString()).isEqualTo(xNothing.toString()); } @@ -364,7 +364,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { throws IOException { xEmptyDirectory.setWritable(false); Path xChildOfReadonlyDir = xEmptyDirectory.getChild("x"); - if (testFS.supportsSymbolicLinksNatively(xChildOfReadonlyDir.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(xChildOfReadonlyDir)) { try { xChildOfReadonlyDir.createSymbolicLink(xNothing); fail(); @@ -386,7 +386,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { assertThat(someLink.isSymbolicLink()).isTrue(); assertThat(someLink.exists(Symlinks.NOFOLLOW)).isTrue(); // the link itself exists assertThat(someLink.exists()).isFalse(); // ...but the referent doesn't - if (testFS.supportsSymbolicLinksNatively(someLink.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(someLink)) { try { someLink.resolveSymbolicLinks(); } catch (FileNotFoundException e) { @@ -398,7 +398,7 @@ public abstract class SymlinkAwareFileSystemTest extends FileSystemTest { @Test public void testCannotCreateSymbolicLinkWithoutParent() throws IOException { Path xChildOfMissingDir = xNothing.getChild("x"); - if (testFS.supportsSymbolicLinksNatively(xChildOfMissingDir.getLocalPath())) { + if (testFS.supportsSymbolicLinksNatively(xChildOfMissingDir)) { try { xChildOfMissingDir.createSymbolicLink(xFile); fail(); diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java index a330197a99..1e877a5255 100644 --- a/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java +++ b/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java @@ -54,8 +54,8 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { private UnionFileSystem createDefaultUnionFileSystem() { return new UnionFileSystem( ImmutableMap.of( - LocalPath.create("/in"), inDelegate, - LocalPath.create("/out"), outDelegate), + PathFragment.create("/in"), inDelegate, + PathFragment.create("/out"), outDelegate), defaultDelegate); } @@ -76,9 +76,9 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { @Test public void testBasicDelegation() throws Exception { unionfs = createDefaultUnionFileSystem(); - LocalPath fooPath = LocalPath.create("/foo"); - LocalPath inPath = LocalPath.create("/in"); - LocalPath outPath = LocalPath.create("/out/in.txt"); + Path fooPath = unionfs.getPath("/foo"); + Path inPath = unionfs.getPath("/in"); + Path outPath = unionfs.getPath("/out/in.txt"); assertThat(unionfs.getDelegate(inPath)).isSameAs(inDelegate); assertThat(unionfs.getDelegate(outPath)).isSameAs(outDelegate); assertThat(unionfs.getDelegate(fooPath)).isSameAs(defaultDelegate); @@ -101,7 +101,7 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { @Test public void testDefaultFileSystemRequired() throws Exception { try { - new UnionFileSystem(ImmutableMap.of(), null); + new UnionFileSystem(ImmutableMap.<PathFragment, FileSystem>of(), null); fail("Able to create a UnionFileSystem with no default!"); } catch (NullPointerException expected) { // OK - should fail in this case. @@ -114,16 +114,16 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { public void testPrefixDelegation() throws Exception { unionfs = new UnionFileSystem( - ImmutableMap.of( - LocalPath.create("/foo"), inDelegate, - LocalPath.create("/foo/bar"), outDelegate), + ImmutableMap.<PathFragment, FileSystem>of( + PathFragment.create("/foo"), inDelegate, + PathFragment.create("/foo/bar"), outDelegate), defaultDelegate); - assertThat(unionfs.getDelegate(LocalPath.create("/foo/foo.txt"))).isSameAs(inDelegate); - assertThat(unionfs.getDelegate(LocalPath.create("/foo/bar/foo.txt"))).isSameAs(outDelegate); - assertThat(unionfs.getDelegate(LocalPath.create("/foo/bar/../foo.txt"))).isSameAs(inDelegate); - assertThat(unionfs.getDelegate(LocalPath.create("/bar/foo.txt"))).isSameAs(defaultDelegate); - assertThat(unionfs.getDelegate(LocalPath.create("/foo/bar/../.."))).isSameAs(defaultDelegate); + assertThat(unionfs.getDelegate(unionfs.getPath("/foo/foo.txt"))).isSameAs(inDelegate); + assertThat(unionfs.getDelegate(unionfs.getPath("/foo/bar/foo.txt"))).isSameAs(outDelegate); + assertThat(unionfs.getDelegate(unionfs.getPath("/foo/bar/../foo.txt"))).isSameAs(inDelegate); + assertThat(unionfs.getDelegate(unionfs.getPath("/bar/foo.txt"))).isSameAs(defaultDelegate); + assertThat(unionfs.getDelegate(unionfs.getPath("/foo/bar/../.."))).isSameAs(defaultDelegate); } // Checks that files cannot be modified when the filesystem is created @@ -133,17 +133,17 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { unionfs = new UnionFileSystem( ImmutableMap.of( - LocalPath.create("/rw"), new XAttrInMemoryFs(BlazeClock.instance()), - LocalPath.create("/ro"), + PathFragment.create("/rw"), new XAttrInMemoryFs(BlazeClock.instance()), + PathFragment.create("/ro"), new XAttrInMemoryFs(BlazeClock.instance()) { @Override - public boolean supportsModifications(LocalPath path) { + public boolean supportsModifications(Path path) { return false; } }), defaultDelegate); - LocalPath rwPath = LocalPath.create("/rw/foo.txt"); - LocalPath roPath = LocalPath.create("/ro/foo.txt"); + Path rwPath = unionfs.getPath("/rw/foo.txt"); + Path roPath = unionfs.getPath("/ro/foo.txt"); assertThat(unionfs.supportsModifications(rwPath)).isTrue(); assertThat(unionfs.supportsModifications(roPath)).isFalse(); } @@ -152,18 +152,18 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { // delegate filesystems; i.e. they can be seen from the filesystem of the parent. @Test public void testDelegateRootDirectoryCreation() throws Exception { - LocalPath foo = LocalPath.create("/foo"); - LocalPath bar = LocalPath.create("/bar"); - LocalPath out = LocalPath.create("/out"); + Path foo = unionfs.getPath("/foo"); + Path bar = unionfs.getPath("/bar"); + Path out = unionfs.getPath("/out"); assertThat(unionfs.createDirectory(foo)).isTrue(); assertThat(unionfs.createDirectory(bar)).isTrue(); assertThat(unionfs.createDirectory(out)).isTrue(); - LocalPath outFile = LocalPath.create("/out/in"); - FileSystemUtils.writeContentAsLatin1(unionfs, outFile, "Out"); + Path outFile = unionfs.getPath("/out/in"); + FileSystemUtils.writeContentAsLatin1(outFile, "Out"); // FileSystemTest.setUp() silently creates the test root on the filesystem... Path testDirUnderRoot = unionfs.getPath(workingDir.asFragment().subFragment(0, 1)); - assertThat(unionfs.getDirectoryEntries(LocalPath.create("/"))) + assertThat(unionfs.getDirectoryEntries(unionfs.getRootDirectory())) .containsExactly( foo.getBaseName(), bar.getBaseName(), @@ -172,30 +172,31 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { assertThat(unionfs.getDirectoryEntries(out)).containsExactly(outFile.getBaseName()); assertThat(defaultDelegate).isSameAs(unionfs.getDelegate(foo)); - assertThat(unionfs.adjustPath(foo, defaultDelegate)).isEqualTo(foo); + assertThat(unionfs.adjustPath(foo, defaultDelegate).asFragment()).isEqualTo(foo.asFragment()); assertThat(defaultDelegate).isSameAs(unionfs.getDelegate(bar)); assertThat(outDelegate).isSameAs(unionfs.getDelegate(outFile)); assertThat(outDelegate).isSameAs(unionfs.getDelegate(out)); // As a fragment (i.e. without filesystem or root info), the path name should be preserved. - assertThat(unionfs.adjustPath(outFile, outDelegate)).isEqualTo(outFile); + assertThat(unionfs.adjustPath(outFile, outDelegate).asFragment()) + .isEqualTo(outFile.asFragment()); } // Ensure that the right filesystem is still chosen when paths contain "..". @Test public void testDelegationOfUpLevelReferences() throws Exception { - assertThat(unionfs.getDelegate(LocalPath.create("/in/../foo.txt"))).isSameAs(defaultDelegate); - assertThat(unionfs.getDelegate(LocalPath.create("/out/../in"))).isSameAs(inDelegate); - assertThat(unionfs.getDelegate(LocalPath.create("/out/../in/../out/foo.txt"))) + assertThat(unionfs.getDelegate(unionfs.getPath("/in/../foo.txt"))).isSameAs(defaultDelegate); + assertThat(unionfs.getDelegate(unionfs.getPath("/out/../in"))).isSameAs(inDelegate); + assertThat(unionfs.getDelegate(unionfs.getPath("/out/../in/../out/foo.txt"))) .isSameAs(outDelegate); - assertThat(unionfs.getDelegate(LocalPath.create("/in/./foo.txt"))).isSameAs(inDelegate); + assertThat(unionfs.getDelegate(unionfs.getPath("/in/./foo.txt"))).isSameAs(inDelegate); } // Basic *explicit* cross-filesystem symlink check. // Note: This does not work implicitly yet, as the next test illustrates. @Test public void testCrossDeviceSymlinks() throws Exception { - assertThat(unionfs.createDirectory(LocalPath.create("/out"))).isTrue(); + assertThat(unionfs.createDirectory(unionfs.getPath("/out"))).isTrue(); // Create an "/in" directory directly on the output delegate to bypass the // UnionFileSystem's mapping. @@ -204,8 +205,8 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { outStream.write('i'); outStream.close(); - LocalPath outFoo = LocalPath.create("/out/foo"); - unionfs.createSymbolicLink(outFoo, "../in/bar.txt"); + Path outFoo = unionfs.getPath("/out/foo"); + unionfs.createSymbolicLink(outFoo, PathFragment.create("../in/bar.txt")); assertThat(unionfs.stat(outFoo, false).isSymbolicLink()).isTrue(); try { @@ -215,13 +216,24 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { // OK } - LocalPath resolved = unionfs.resolveSymbolicLinks(outFoo); - InputStream barInput = unionfs.getInputStream(resolved); + Path resolved = unionfs.resolveSymbolicLinks(outFoo); + assertThat(resolved.getFileSystem()).isSameAs(unionfs); + InputStream barInput = resolved.getInputStream(); int barChar = barInput.read(); barInput.close(); assertThat(barChar).isEqualTo('i'); } + @Test + public void testNoDelegateLeakage() throws Exception { + assertThat(unionfs.getPath("/in/foo.txt").getFileSystem()).isSameAs(unionfs); + assertThat(unionfs.getPath("/in/foo/bar").getParentDirectory().getFileSystem()) + .isSameAs(unionfs); + unionfs.createDirectory(unionfs.getPath("/out")); + unionfs.createDirectory(unionfs.getPath("/out/foo")); + unionfs.createDirectory(unionfs.getPath("/out/foo/bar")); + } + // Write using the VFS through a UnionFileSystem and check that the file can // be read back in the same location using standard Java IO. // There is a similar test in UnixFileSystem, but this is essential to ensure @@ -230,11 +242,12 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { public void testDelegateOperationsReflectOnLocalFilesystem() throws Exception { unionfs = new UnionFileSystem( - ImmutableMap.of(workingDir.getLocalPath().getParentDirectory(), new UnixFileSystem()), + ImmutableMap.<PathFragment, FileSystem>of( + workingDir.getParentDirectory().asFragment(), new UnixFileSystem()), defaultDelegate); // This is a child of the current tmpdir, and doesn't exist on its own. // It would be created in setup(), but of course, that didn't use a UnixFileSystem. - unionfs.createDirectory(workingDir.getLocalPath()); + unionfs.createDirectory(workingDir); Path testFile = unionfs.getPath(workingDir.getRelative("test_file").asFragment()); assertThat(testFile.asFragment().startsWith(workingDir.asFragment())).isTrue(); String testString = "This is a test file"; @@ -243,7 +256,7 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { assertThat(new String(FileSystemUtils.readContentAsLatin1(testFile))).isEqualTo(testString); } finally { testFile.delete(); - assertThat(unionfs.delete(workingDir.getLocalPath())).isTrue(); + assertThat(unionfs.delete(workingDir)).isTrue(); } } @@ -252,7 +265,8 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { public void testCreateParentsAcrossMapping() throws Exception { unionfs = new UnionFileSystem( - ImmutableMap.of(LocalPath.create("/out/dir"), outDelegate), defaultDelegate); + ImmutableMap.<PathFragment, FileSystem>of(PathFragment.create("/out/dir"), outDelegate), + defaultDelegate); Path outDir = unionfs.getPath("/out/dir/biz/bang"); FileSystemUtils.createDirectoryAndParents(outDir); assertThat(outDir.isDirectory()).isTrue(); @@ -264,7 +278,8 @@ public class UnionFileSystemTest extends SymlinkAwareFileSystemTest { } @Override - public byte[] getxattr(LocalPath path, String name) { + public byte[] getxattr(Path path, String name) { + assertThat(path.getFileSystem()).isSameAs(this); return (name.equals(XATTR_KEY)) ? XATTR_VAL.getBytes(UTF_8) : null; } } diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixLocalPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/UnixLocalPathTest.java index 16ae52cc4c..2cdb4014ae 100644 --- a/src/test/java/com/google/devtools/build/lib/vfs/UnixLocalPathTest.java +++ b/src/test/java/com/google/devtools/build/lib/vfs/UnixLocalPathTest.java @@ -162,17 +162,6 @@ public class UnixLocalPathTest extends LocalPathAbstractTest { assertThat(create("/..")).isEqualTo(create("/..")); } - @Test - public void testRootsUnix() { - assertThat(create("/").getDrive().getPathString()).isEqualTo("/"); - assertThat(create("/usr").getDrive().getPathString()).isEqualTo("/"); - assertThrows(IllegalArgumentException.class, () -> create("").getDrive()); - - assertThat(create("/").isRoot()).isTrue(); - assertThat(create("/usr").isRoot()).isFalse(); - assertThat(create("").isRoot()).isFalse(); - } - @Override protected OsPathPolicy getFilePathOs() { return new UnixOsPathPolicy(); diff --git a/src/test/java/com/google/devtools/build/lib/vfs/WindowsLocalPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/WindowsLocalPathTest.java index 88c7b6f248..ac5acef1f4 100644 --- a/src/test/java/com/google/devtools/build/lib/vfs/WindowsLocalPathTest.java +++ b/src/test/java/com/google/devtools/build/lib/vfs/WindowsLocalPathTest.java @@ -14,9 +14,9 @@ package com.google.devtools.build.lib.vfs; import static com.google.common.truth.Truth.assertThat; -import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; import com.google.common.testing.EqualsTester; +import com.google.devtools.build.lib.testutil.MoreAsserts; import com.google.devtools.build.lib.vfs.LocalPath.OsPathPolicy; import com.google.devtools.build.lib.vfs.LocalPath.WindowsOsPathPolicy; import com.google.devtools.build.lib.vfs.LocalPath.WindowsOsPathPolicy.ShortPathResolver; @@ -101,14 +101,8 @@ public class WindowsLocalPathTest extends LocalPathAbstractTest { @Test public void testisAbsoluteWindows() { assertThat(create("C:/").isAbsolute()).isTrue(); - } - - // We support unix paths to make test sharing easier - @Test - public void testUnixPathSupport() { + // test that msys paths turn into absolute paths assertThat(create("/").isAbsolute()).isTrue(); - assertThat(create("/foo").isAbsolute()).isTrue(); - assertThat(create("/foo").getParentDirectory().getPathString()).isEqualTo("/"); } @Test @@ -116,7 +110,22 @@ public class WindowsLocalPathTest extends LocalPathAbstractTest { assertThat(create("C:/foo").relativeTo(create("C:/"))).isEqualTo(create("foo")); // Case insensitivity test assertThat(create("C:/foo/bar").relativeTo(create("C:/FOO"))).isEqualTo(create("bar")); - assertThrows(IllegalArgumentException.class, () -> create("D:/foo").relativeTo(create("C:/"))); + MoreAsserts.assertThrows( + IllegalArgumentException.class, () -> create("D:/foo").relativeTo(create("C:/"))); + } + + @Test + public void testAbsoluteUnixPathIsRelativeToWindowsUnixRoot() { + assertThat(create("/").getPathString()).isEqualTo("C:/fake/msys"); + assertThat(create("/foo/bar").getPathString()).isEqualTo("C:/fake/msys/foo/bar"); + assertThat(create("/foo/bar").getPathString()).isEqualTo("C:/fake/msys/foo/bar"); + } + + @Test + public void testAbsoluteUnixPathReferringToDriveIsRecognized() { + assertThat(create("/c/foo").getPathString()).isEqualTo("C:/foo"); + assertThat(create("/c/foo").getPathString()).isEqualTo("C:/foo"); + assertThat(create("/c:").getPathString()).isNotEqualTo("C:/foo"); } @Test @@ -152,16 +161,4 @@ public class WindowsLocalPathTest extends LocalPathAbstractTest { // Assert relative paths that look like short paths are untouched assertThat(create("progra~1").getPathString()).isEqualTo("progra~1"); } - - @Test - public void testRootsWindows() { - assertThat(create("C:/").getDrive().getPathString()).isEqualTo("C:/"); - assertThat(create("C:/usr").getDrive().getPathString()).isEqualTo("C:/"); - assertThrows(IllegalArgumentException.class, () -> create("").getDrive()); - - assertThat(create("C:/").isRoot()).isFalse(); - assertThat(create("C:/usr").isRoot()).isFalse(); - assertThat(create("/").isRoot()).isTrue(); - assertThat(create("").isRoot()).isFalse(); - } } diff --git a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java index d50b243f5f..8937c453a7 100644 --- a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java +++ b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java @@ -25,6 +25,7 @@ import com.google.common.collect.Iterables; import com.google.devtools.build.lib.testutil.TestSpec; import com.google.devtools.build.lib.util.OS; 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.windows.WindowsFileSystem.WindowsPath; import com.google.devtools.build.lib.windows.jni.WindowsFileOperations; @@ -99,9 +100,9 @@ public class WindowsFileSystemTest { assertThat(juncBadPath.exists(Symlinks.NOFOLLOW)).isTrue(); // TODO(bazel-team): fix https://github.com/bazelbuild/bazel/issues/1690 and uncomment the // assertion below. - // assertThat(fs.isSymbolicLink(juncBadPath)).isTrue(); - assertThat(fs.isDirectory(juncBadPath.getLocalPath(), /* followSymlinks */ true)).isFalse(); - assertThat(fs.isDirectory(juncBadPath.getLocalPath(), /* followSymlinks */ false)).isFalse(); + //assertThat(fs.isSymbolicLink(juncBadPath)).isTrue(); + assertThat(fs.isDirectory(juncBadPath, /* followSymlinks */ true)).isFalse(); + assertThat(fs.isDirectory(juncBadPath, /* followSymlinks */ false)).isFalse(); // Test deleting a dangling junction. assertThat(juncBadPath.delete()).isTrue(); @@ -332,21 +333,19 @@ public class WindowsFileSystemTest { assertThat(fs.getPath(scratchRoot).createDirectory()).isTrue(); // Create symlink with directory target, relative path. Path link1 = fs.getPath(scratchRoot).getRelative("link1"); - fs.createSymbolicLink(link1.getLocalPath(), ".."); + fs.createSymbolicLink(link1, PathFragment.create("..")); // Create symlink with directory target, absolute path. Path link2 = fs.getPath(scratchRoot).getRelative("link2"); - fs.createSymbolicLink( - link2.getLocalPath(), fs.getPath(scratchRoot).getRelative("link1").getPathString()); + fs.createSymbolicLink(link2, fs.getPath(scratchRoot).getRelative("link1").asFragment()); // Create scratch files that'll be symlink targets. testUtil.scratchFile("foo.txt", "hello"); testUtil.scratchFile("bar.txt", "hello"); // Create symlink with file target, relative path. Path link3 = fs.getPath(scratchRoot).getRelative("link3"); - fs.createSymbolicLink(link3.getLocalPath(), "foo.txt"); + fs.createSymbolicLink(link3, PathFragment.create("foo.txt")); // Create symlink with file target, absolute path. Path link4 = fs.getPath(scratchRoot).getRelative("link4"); - fs.createSymbolicLink( - link4.getLocalPath(), fs.getPath(scratchRoot).getRelative("bar.txt").getPathString()); + fs.createSymbolicLink(link4, fs.getPath(scratchRoot).getRelative("bar.txt").asFragment()); // Assert that link1 and link2 are true junctions and have the right contents. for (Path p : ImmutableList.of(link1, link2)) { assertThat(WindowsFileOperations.isJunction(p.getPathString())).isTrue(); |