use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class FileFunction method getSymlinkTargetRootedPath.
/**
* Returns the symlink target and file state of {@code rootedPath}'s symlink to {@code
* symlinkTarget}, accounting for ancestor symlinks, or {@code null} if there was a missing dep.
*/
@Nullable
private Pair<RootedPath, FileStateValue> getSymlinkTargetRootedPath(RootedPath rootedPath, PathFragment symlinkTarget, TreeSet<Path> orderedSeenPaths, Iterable<RootedPath> symlinkChain, Environment env) throws FileFunctionException, InterruptedException {
RootedPath symlinkTargetRootedPath;
if (symlinkTarget.isAbsolute()) {
Path path = rootedPath.asPath().getFileSystem().getRootDirectory().getRelative(symlinkTarget);
symlinkTargetRootedPath = RootedPath.toRootedPathMaybeUnderRoot(path, pkgLocator.get().getPathEntries());
} else {
Path path = rootedPath.asPath();
Path symlinkTargetPath;
if (path.getParentDirectory() != null) {
RootedPath parentRootedPath = RootedPath.toRootedPathMaybeUnderRoot(path.getParentDirectory(), pkgLocator.get().getPathEntries());
FileValue parentFileValue = (FileValue) env.getValue(FileValue.key(parentRootedPath));
if (parentFileValue == null) {
return null;
}
symlinkTargetPath = parentFileValue.realRootedPath().asPath().getRelative(symlinkTarget);
} else {
// This means '/' is a symlink to 'symlinkTarget'.
symlinkTargetPath = path.getRelative(symlinkTarget);
}
symlinkTargetRootedPath = RootedPath.toRootedPathMaybeUnderRoot(symlinkTargetPath, pkgLocator.get().getPathEntries());
}
// Suppose we have a symlink chain p -> p1 -> p2 -> ... pK. We want to determine the fully
// resolved path, if any, of p. This entails following the chain and noticing if there's a
// symlink issue. There three sorts of issues:
// (i) Symlink cycle:
// p -> p1 -> p2 -> p1
// (ii) Unbounded expansion caused by a symlink to a descendant of a member of the chain:
// p -> a/b -> c/d -> a/b/e
// (iii) Unbounded expansion caused by a symlink to an ancestor of a member of the chain:
// p -> a/b -> c/d -> a
//
// We can detect all three of these symlink issues by following the chain and deciding if each
// new element is problematic. Here is our incremental algorithm:
//
// Suppose we encounter the symlink target p and we have already encountered all the paths in P:
// If p is in P then we have a found a cycle (i).
// If p is a descendant of any path p' in P then we have unbounded expansion (ii).
// If p is an ancestor of any path p' in P then we have unbounded expansion (iii).
// We can check for these cases efficiently (read: sublinear time) by finding the extremal
// candidate p' for (ii) and (iii).
Path symlinkTargetPath = symlinkTargetRootedPath.asPath();
SkyKey uniquenessKey = null;
FileSymlinkException fse = null;
Path seenFloorPath = orderedSeenPaths.floor(symlinkTargetPath);
Path seenCeilingPath = orderedSeenPaths.ceiling(symlinkTargetPath);
if (orderedSeenPaths.contains(symlinkTargetPath)) {
// 'rootedPath' is a symlink to a previous element in the symlink chain (i).
Pair<ImmutableList<RootedPath>, ImmutableList<RootedPath>> pathAndChain = CycleUtils.splitIntoPathAndChain(isPathPredicate(symlinkTargetRootedPath.asPath()), symlinkChain);
FileSymlinkCycleException fsce = new FileSymlinkCycleException(pathAndChain.getFirst(), pathAndChain.getSecond());
uniquenessKey = FileSymlinkCycleUniquenessFunction.key(fsce.getCycle());
fse = fsce;
} else if (seenFloorPath != null && symlinkTargetPath.startsWith(seenFloorPath)) {
// 'rootedPath' is a symlink to a descendant of a previous element in the symlink chain (ii).
Pair<ImmutableList<RootedPath>, ImmutableList<RootedPath>> pathAndChain = CycleUtils.splitIntoPathAndChain(isPathPredicate(seenFloorPath), ImmutableList.copyOf(Iterables.concat(symlinkChain, ImmutableList.of(symlinkTargetRootedPath))));
uniquenessKey = FileSymlinkInfiniteExpansionUniquenessFunction.key(pathAndChain.getSecond());
fse = new FileSymlinkInfiniteExpansionException(pathAndChain.getFirst(), pathAndChain.getSecond());
} else if (seenCeilingPath != null && seenCeilingPath.startsWith(symlinkTargetPath)) {
// 'rootedPath' is a symlink to an ancestor of a previous element in the symlink chain (iii).
Pair<ImmutableList<RootedPath>, ImmutableList<RootedPath>> pathAndChain = CycleUtils.splitIntoPathAndChain(isPathPredicate(seenCeilingPath), ImmutableList.copyOf(Iterables.concat(symlinkChain, ImmutableList.of(symlinkTargetRootedPath))));
uniquenessKey = FileSymlinkInfiniteExpansionUniquenessFunction.key(pathAndChain.getSecond());
fse = new FileSymlinkInfiniteExpansionException(pathAndChain.getFirst(), pathAndChain.getSecond());
}
if (uniquenessKey != null) {
if (env.getValue(uniquenessKey) == null) {
// reported exactly once.
return null;
}
throw new FileFunctionException(Preconditions.checkNotNull(fse, rootedPath));
}
return resolveFromAncestors(symlinkTargetRootedPath, env);
}
use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class LocalRepositoryLookupFunction method maybeGetWorkspaceFileExistence.
private Optional<Boolean> maybeGetWorkspaceFileExistence(Environment env, RootedPath directory) throws InterruptedException, LocalRepositoryLookupFunctionException {
try {
RootedPath workspaceRootedFile = RootedPath.toRootedPath(directory.getRoot(), directory.getRelativePath().getChild(PackageLookupValue.BuildFileName.WORKSPACE.getFilename()));
FileValue workspaceFileValue = (FileValue) env.getValueOrThrow(FileValue.key(workspaceRootedFile), IOException.class, FileSymlinkException.class, InconsistentFilesystemException.class);
if (workspaceFileValue == null) {
return Optional.absent();
}
if (workspaceFileValue.isDirectory()) {
// There is a directory named WORKSPACE, ignore it for checking repository existence.
return Optional.of(false);
}
return Optional.of(workspaceFileValue.exists());
} catch (IOException e) {
throw new LocalRepositoryLookupFunctionException(new ErrorDeterminingRepositoryException("IOException while checking if there is a WORKSPACE file in " + directory.asPath().getPathString(), e), Transience.PERSISTENT);
} catch (FileSymlinkException e) {
throw new LocalRepositoryLookupFunctionException(new ErrorDeterminingRepositoryException("FileSymlinkException while checking if there is a WORKSPACE file in " + directory.asPath().getPathString(), e), Transience.PERSISTENT);
} catch (InconsistentFilesystemException e) {
throw new LocalRepositoryLookupFunctionException(new ErrorDeterminingRepositoryException("InconsistentFilesystemException while checking if there is a WORKSPACE file in " + directory.asPath().getPathString(), e), Transience.PERSISTENT);
}
}
use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class FileFunctionTest method runTestInfiniteSymlinkExpansion.
private void runTestInfiniteSymlinkExpansion(boolean symlinkToAncestor, boolean absoluteSymlink) throws Exception {
Path otherPath = path("other");
RootedPath otherRootedPath = RootedPath.toRootedPath(pkgRoot, otherPath.relativeTo(pkgRoot));
Path ancestorPath = path("a");
RootedPath ancestorRootedPath = RootedPath.toRootedPath(pkgRoot, ancestorPath.relativeTo(pkgRoot));
FileSystemUtils.ensureSymbolicLink(otherPath, ancestorPath);
Path intermediatePath = path("inter");
RootedPath intermediateRootedPath = RootedPath.toRootedPath(pkgRoot, intermediatePath.relativeTo(pkgRoot));
Path descendantPath = path("a/b/c/d/e");
RootedPath descendantRootedPath = RootedPath.toRootedPath(pkgRoot, descendantPath.relativeTo(pkgRoot));
if (symlinkToAncestor) {
FileSystemUtils.ensureSymbolicLink(descendantPath, intermediatePath);
if (absoluteSymlink) {
FileSystemUtils.ensureSymbolicLink(intermediatePath, ancestorPath);
} else {
FileSystemUtils.ensureSymbolicLink(intermediatePath, ancestorRootedPath.getRelativePath());
}
} else {
FileSystemUtils.ensureSymbolicLink(ancestorPath, intermediatePath);
if (absoluteSymlink) {
FileSystemUtils.ensureSymbolicLink(intermediatePath, descendantPath);
} else {
FileSystemUtils.ensureSymbolicLink(intermediatePath, descendantRootedPath.getRelativePath());
}
}
StoredEventHandler eventHandler = new StoredEventHandler();
SequentialBuildDriver driver = makeDriver();
SkyKey ancestorPathKey = FileValue.key(ancestorRootedPath);
SkyKey descendantPathKey = FileValue.key(descendantRootedPath);
SkyKey otherPathKey = FileValue.key(otherRootedPath);
ImmutableList<SkyKey> keys;
ImmutableList<SkyKey> errorKeys;
ImmutableList<RootedPath> expectedChain;
if (symlinkToAncestor) {
keys = ImmutableList.of(descendantPathKey, otherPathKey);
errorKeys = ImmutableList.of(descendantPathKey);
expectedChain = ImmutableList.of(descendantRootedPath, intermediateRootedPath, ancestorRootedPath);
} else {
keys = ImmutableList.of(ancestorPathKey, otherPathKey);
errorKeys = keys;
expectedChain = ImmutableList.of(ancestorRootedPath, intermediateRootedPath, descendantRootedPath);
}
EvaluationResult<FileValue> result = driver.evaluate(keys, /*keepGoing=*/
true, DEFAULT_THREAD_COUNT, eventHandler);
assertTrue(result.hasError());
for (SkyKey key : errorKeys) {
ErrorInfo errorInfo = result.getError(key);
// FileFunction detects infinite symlink expansion explicitly.
assertThat(errorInfo.getCycleInfo()).isEmpty();
FileSymlinkInfiniteExpansionException fsiee = (FileSymlinkInfiniteExpansionException) errorInfo.getException();
assertThat(fsiee.getMessage()).contains("Infinite symlink expansion");
assertThat(fsiee.getChain()).containsExactlyElementsIn(expectedChain).inOrder();
}
// Check that the unique symlink expansion error was reported exactly once.
assertThat(eventHandler.getEvents()).hasSize(1);
assertThat(Iterables.getOnlyElement(eventHandler.getEvents()).getMessage()).contains("infinite symlink expansion detected");
}
use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class FileFunctionTest method runTestSymlinkCycle.
private void runTestSymlinkCycle(boolean ancestorCycle, boolean startInCycle) throws Exception {
symlink("a", "b");
symlink("b", "c");
symlink("c", "d");
symlink("d", "e");
symlink("e", "c");
// We build multiple keys at once to make sure the cycle is reported exactly once.
Map<RootedPath, ImmutableList<RootedPath>> startToCycleMap = ImmutableMap.<RootedPath, ImmutableList<RootedPath>>builder().put(rootedPath("a"), ImmutableList.of(rootedPath("c"), rootedPath("d"), rootedPath("e"))).put(rootedPath("b"), ImmutableList.of(rootedPath("c"), rootedPath("d"), rootedPath("e"))).put(rootedPath("d"), ImmutableList.<RootedPath>of(rootedPath("d"), rootedPath("e"), rootedPath("c"))).put(rootedPath("e"), ImmutableList.<RootedPath>of(rootedPath("e"), rootedPath("c"), rootedPath("d"))).put(rootedPath("a/some/descendant"), ImmutableList.of(rootedPath("c"), rootedPath("d"), rootedPath("e"))).put(rootedPath("b/some/descendant"), ImmutableList.of(rootedPath("c"), rootedPath("d"), rootedPath("e"))).put(rootedPath("d/some/descendant"), ImmutableList.<RootedPath>of(rootedPath("d"), rootedPath("e"), rootedPath("c"))).put(rootedPath("e/some/descendant"), ImmutableList.<RootedPath>of(rootedPath("e"), rootedPath("c"), rootedPath("d"))).build();
Map<RootedPath, ImmutableList<RootedPath>> startToPathToCycleMap = ImmutableMap.<RootedPath, ImmutableList<RootedPath>>builder().put(rootedPath("a"), ImmutableList.of(rootedPath("a"), rootedPath("b"))).put(rootedPath("b"), ImmutableList.of(rootedPath("b"))).put(rootedPath("d"), ImmutableList.<RootedPath>of()).put(rootedPath("e"), ImmutableList.<RootedPath>of()).put(rootedPath("a/some/descendant"), ImmutableList.of(rootedPath("a"), rootedPath("b"))).put(rootedPath("b/some/descendant"), ImmutableList.of(rootedPath("b"))).put(rootedPath("d/some/descendant"), ImmutableList.<RootedPath>of()).put(rootedPath("e/some/descendant"), ImmutableList.<RootedPath>of()).build();
ImmutableList<SkyKey> keys;
if (ancestorCycle && startInCycle) {
keys = ImmutableList.of(skyKey("d/some/descendant"), skyKey("e/some/descendant"));
} else if (ancestorCycle && !startInCycle) {
keys = ImmutableList.of(skyKey("a/some/descendant"), skyKey("b/some/descendant"));
} else if (!ancestorCycle && startInCycle) {
keys = ImmutableList.of(skyKey("d"), skyKey("e"));
} else {
keys = ImmutableList.of(skyKey("a"), skyKey("b"));
}
StoredEventHandler eventHandler = new StoredEventHandler();
SequentialBuildDriver driver = makeDriver();
EvaluationResult<FileValue> result = driver.evaluate(keys, /*keepGoing=*/
true, DEFAULT_THREAD_COUNT, eventHandler);
assertTrue(result.hasError());
for (SkyKey key : keys) {
ErrorInfo errorInfo = result.getError(key);
// FileFunction detects symlink cycles explicitly.
assertThat(errorInfo.getCycleInfo()).isEmpty();
FileSymlinkCycleException fsce = (FileSymlinkCycleException) errorInfo.getException();
RootedPath start = (RootedPath) key.argument();
assertThat(fsce.getPathToCycle()).containsExactlyElementsIn(startToPathToCycleMap.get(start)).inOrder();
assertThat(fsce.getCycle()).containsExactlyElementsIn(startToCycleMap.get(start)).inOrder();
}
// Check that the unique cycle was reported exactly once.
assertThat(eventHandler.getEvents()).hasSize(1);
assertThat(Iterables.getOnlyElement(eventHandler.getEvents()).getMessage()).contains("circular symlinks detected");
}
use of com.google.devtools.build.lib.vfs.RootedPath in project bazel by bazelbuild.
the class FilesystemValueCheckerTest method testDirtySymlink.
/**
* Tests that an already-invalidated value can still be marked changed: symlink points at sym1.
* Invalidate symlink by changing sym1 from pointing at path to point to sym2. This only dirties
* (rather than changes) symlink because sym2 still points at path, so all symlink stats remain
* the same. Then do a null build, change sym1 back to point at path, and change symlink to not be
* a symlink anymore. The fact that it is not a symlink should be detected.
*/
@Test
public void testDirtySymlink() throws Exception {
FilesystemValueChecker checker = new FilesystemValueChecker(null, null);
Path path = fs.getPath("/foo");
FileSystemUtils.writeContentAsLatin1(path, "foo contents");
// We need the intermediate sym1 and sym2 so that we can dirty a child of symlink without
// actually changing the FileValue calculated for symlink (if we changed the contents of foo,
// the the FileValue created for symlink would notice, since it stats foo).
Path sym1 = fs.getPath("/sym1");
Path sym2 = fs.getPath("/sym2");
Path symlink = fs.getPath("/bar");
FileSystemUtils.ensureSymbolicLink(symlink, sym1);
FileSystemUtils.ensureSymbolicLink(sym1, path);
FileSystemUtils.ensureSymbolicLink(sym2, path);
SkyKey fooKey = FileValue.key(RootedPath.toRootedPath(fs.getRootDirectory(), new PathFragment("foo")));
RootedPath symlinkRootedPath = RootedPath.toRootedPath(fs.getRootDirectory(), new PathFragment("bar"));
SkyKey symlinkKey = FileValue.key(symlinkRootedPath);
SkyKey symlinkFileStateKey = FileStateValue.key(symlinkRootedPath);
RootedPath sym1RootedPath = RootedPath.toRootedPath(fs.getRootDirectory(), new PathFragment("sym1"));
SkyKey sym1FileStateKey = FileStateValue.key(sym1RootedPath);
Iterable<SkyKey> allKeys = ImmutableList.of(symlinkKey, fooKey);
// First build -- prime the graph.
EvaluationResult<FileValue> result = driver.evaluate(allKeys, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
assertFalse(result.hasError());
FileValue symlinkValue = result.get(symlinkKey);
FileValue fooValue = result.get(fooKey);
assertTrue(symlinkValue.toString(), symlinkValue.isSymlink());
// Digest is not always available, so use size as a proxy for contents.
assertEquals(fooValue.getSize(), symlinkValue.getSize());
assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
// Before second build, move sym1 to point to sym2.
assertTrue(sym1.delete());
FileSystemUtils.ensureSymbolicLink(sym1, sym2);
assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
differencer.invalidate(ImmutableList.of(sym1FileStateKey));
result = driver.evaluate(ImmutableList.<SkyKey>of(), false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
assertFalse(result.hasError());
assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
// Before third build, move sym1 back to original (so change pruning will prevent signaling of
// its parents, but change symlink for real.
assertTrue(sym1.delete());
FileSystemUtils.ensureSymbolicLink(sym1, path);
assertTrue(symlink.delete());
FileSystemUtils.writeContentAsLatin1(symlink, "new symlink contents");
assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), symlinkFileStateKey);
differencer.invalidate(ImmutableList.of(symlinkFileStateKey));
result = driver.evaluate(allKeys, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
assertFalse(result.hasError());
symlinkValue = result.get(symlinkKey);
assertFalse(symlinkValue.toString(), symlinkValue.isSymlink());
assertEquals(fooValue, result.get(fooKey));
assertThat(symlinkValue.getSize()).isNotEqualTo(fooValue.getSize());
assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
}
Aggregations