Search in sources :

Example 61 with RootedPath

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);
}
Also used : RootedPath(com.google.devtools.build.lib.vfs.RootedPath) Path(com.google.devtools.build.lib.vfs.Path) SkyKey(com.google.devtools.build.skyframe.SkyKey) ImmutableList(com.google.common.collect.ImmutableList) RootedPath(com.google.devtools.build.lib.vfs.RootedPath) Pair(com.google.devtools.build.lib.util.Pair) Nullable(javax.annotation.Nullable)

Example 62 with RootedPath

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);
    }
}
Also used : ErrorDeterminingRepositoryException(com.google.devtools.build.lib.packages.ErrorDeterminingRepositoryException) IOException(java.io.IOException) RootedPath(com.google.devtools.build.lib.vfs.RootedPath)

Example 63 with RootedPath

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");
}
Also used : RootedPath(com.google.devtools.build.lib.vfs.RootedPath) Path(com.google.devtools.build.lib.vfs.Path) SequentialBuildDriver(com.google.devtools.build.skyframe.SequentialBuildDriver) SkyKey(com.google.devtools.build.skyframe.SkyKey) StoredEventHandler(com.google.devtools.build.lib.events.StoredEventHandler) ErrorInfo(com.google.devtools.build.skyframe.ErrorInfo) RootedPath(com.google.devtools.build.lib.vfs.RootedPath)

Example 64 with RootedPath

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");
}
Also used : SkyKey(com.google.devtools.build.skyframe.SkyKey) SequentialBuildDriver(com.google.devtools.build.skyframe.SequentialBuildDriver) StoredEventHandler(com.google.devtools.build.lib.events.StoredEventHandler) ImmutableList(com.google.common.collect.ImmutableList) ErrorInfo(com.google.devtools.build.skyframe.ErrorInfo) RootedPath(com.google.devtools.build.lib.vfs.RootedPath)

Example 65 with RootedPath

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));
}
Also used : RootedPath(com.google.devtools.build.lib.vfs.RootedPath) Path(com.google.devtools.build.lib.vfs.Path) SkyKey(com.google.devtools.build.skyframe.SkyKey) PathFragment(com.google.devtools.build.lib.vfs.PathFragment) RootedPath(com.google.devtools.build.lib.vfs.RootedPath) Test(org.junit.Test)

Aggregations

RootedPath (com.google.devtools.build.lib.vfs.RootedPath)84 PathFragment (com.google.devtools.build.lib.vfs.PathFragment)43 SkyKey (com.google.devtools.build.skyframe.SkyKey)41 Test (org.junit.Test)33 Path (com.google.devtools.build.lib.vfs.Path)28 Artifact (com.google.devtools.build.lib.actions.Artifact)21 IOException (java.io.IOException)15 Package (com.google.devtools.build.lib.packages.Package)13 SkyValue (com.google.devtools.build.skyframe.SkyValue)11 TraversalRequest (com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.TraversalRequest)9 ResolvedFile (com.google.devtools.build.lib.skyframe.RecursiveFilesystemTraversalValue.ResolvedFile)8 FilesetTraversalParams (com.google.devtools.build.lib.actions.FilesetTraversalParams)7 EvalException (com.google.devtools.build.lib.syntax.EvalException)7 ErrorInfo (com.google.devtools.build.skyframe.ErrorInfo)7 Dirent (com.google.devtools.build.lib.vfs.Dirent)6 LabelSyntaxException (com.google.devtools.build.lib.cmdline.LabelSyntaxException)5 BuildFileNotFoundException (com.google.devtools.build.lib.packages.BuildFileNotFoundException)5 NoSuchPackageException (com.google.devtools.build.lib.packages.NoSuchPackageException)5 FileValue (com.google.devtools.build.lib.skyframe.FileValue)5 Map (java.util.Map)5