Introduce Fileset manifest RESOLVE_FULLY behavior, which resolves internal Fileset directory links
Instead of trying to further encode symlink resolution with full generality, we re-use existing logic to do so found in InMemoryFilesystem. Therefore we are able to encode the Fileset layout in the in-memory filesystem and let the existing logic do the resolution for us.
RELNOTES: None
PiperOrigin-RevId: 314959366
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD
index d6aff69..1e0f888 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -68,6 +68,7 @@
"//src/main/java/com/google/devtools/build/lib/util/io",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
+ "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
"//src/main/java/com/google/devtools/build/skyframe",
"//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
"//src/main/java/com/google/devtools/common/options",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/FilesetManifest.java b/src/main/java/com/google/devtools/build/lib/actions/FilesetManifest.java
index f60f0ed..0dbdfdf 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/FilesetManifest.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/FilesetManifest.java
@@ -16,7 +16,10 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.flogger.GoogleLogger;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
@@ -30,6 +33,7 @@
* Representation of a Fileset manifest.
*/
public final class FilesetManifest {
+ private static final int MAX_SYMLINK_TRAVERSALS = 256;
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
/**
@@ -43,7 +47,10 @@
ERROR,
/** Resolve all relative target paths. */
- RESOLVE;
+ RESOLVE,
+
+ /** Fully resolve all relative paths, even those pointing to internal directories. */
+ RESOLVE_FULLY;
}
public static FilesetManifest constructFilesetManifest(
@@ -66,7 +73,7 @@
artifactValues.put(artifact, (FileArtifactValue) outputSymlink.getMetadata());
}
}
- resolveRelativeSymlinks(entries, relativeLinks);
+ resolveRelativeSymlinks(entries, relativeLinks, targetPrefix.isAbsolute(), relSymlinkBehavior);
return new FilesetManifest(entries, artifactValues);
}
@@ -87,6 +94,7 @@
case ERROR:
throw new IOException("runfiles target is not absolute: " + artifact);
case RESOLVE:
+ case RESOLVE_FULLY:
if (!relativeLinks.containsKey(fullLocation)) { // Keep consistent behavior: no overwriting.
relativeLinks.put(fullLocation, artifact);
}
@@ -96,51 +104,106 @@
}
}
- private static final int MAX_SYMLINK_TRAVERSALS = 256;
+ /** Fully resolve relative symlinks including internal directory symlinks. */
+ private static void fullyResolveRelativeSymlinks(
+ Map<PathFragment, String> entries, Map<PathFragment, String> relativeLinks, boolean absolute)
+ throws IOException {
+ // Construct an in-memory Filesystem containing all the non-relative-symlink entries in the
+ // Fileset. Treat these as regular files in the filesystem whose contents are the "real" symlink
+ // pointing out of the Fileset. For relative symlinks, we encode these as symlinks in the
+ // in-memory Filesystem. This allows us to then crawl the filesystem for files. Any readable
+ // file is a valid part of the FilesetManifest. Dangling internal links or symlink cycles will
+ // be discovered by the in-memory filesystem.
+ InMemoryFileSystem fs = new InMemoryFileSystem();
+ Path root = fs.getPath("/");
+ for (Map.Entry<PathFragment, String> e : entries.entrySet()) {
+ PathFragment location = e.getKey();
+ Path locationPath = root.getRelative(location);
+ locationPath.getParentDirectory().createDirectoryAndParents();
+ FileSystemUtils.writeContentAsLatin1(locationPath, Strings.nullToEmpty(e.getValue()));
+ }
+ for (Map.Entry<PathFragment, String> e : relativeLinks.entrySet()) {
+ PathFragment location = e.getKey();
+ Path locationPath = fs.getPath("/").getRelative(location);
+ PathFragment value = PathFragment.create(Preconditions.checkNotNull(e.getValue(), e));
+ Preconditions.checkState(!value.isAbsolute(), e);
+
+ locationPath.getParentDirectory().createDirectoryAndParents();
+ locationPath.createSymbolicLink(value);
+ }
+
+ addSymlinks(root, entries, absolute);
+ }
+
+ private static void addSymlinks(Path root, Map<PathFragment, String> entries, boolean absolute)
+ throws IOException {
+ for (Path path : root.getDirectoryEntries()) {
+ try {
+ if (path.isDirectory()) {
+ addSymlinks(path, entries, absolute);
+ } else {
+ String contents = new String(FileSystemUtils.readContentAsLatin1(path));
+ entries.put(
+ absolute ? path.asFragment() : path.asFragment().toRelative(),
+ Strings.emptyToNull(contents));
+ }
+ } catch (IOException e) {
+ logger.atWarning().log("Symlink %s is dangling or cyclic: %s", path, e.getMessage());
+ }
+ }
+ }
/**
* Resolves relative symlinks and puts them in the {@code entries} map.
*
* <p>Note that {@code relativeLinks} should only contain entries in {@link
- * RelativeSymlinkBehavior#RESOLVE} mode.
+ * RelativeSymlinkBehavior#RESOLVE} or {@link RelativeSymlinkBehavior#RESOLVE_FULLY} mode.
*/
private static void resolveRelativeSymlinks(
- Map<PathFragment, String> entries, Map<PathFragment, String> relativeLinks) {
- for (Map.Entry<PathFragment, String> e : relativeLinks.entrySet()) {
- PathFragment location = e.getKey();
- String value = e.getValue();
- String actual = Preconditions.checkNotNull(value, e);
- Preconditions.checkState(!actual.startsWith("/"), e);
- PathFragment actualLocation = location;
+ Map<PathFragment, String> entries,
+ Map<PathFragment, String> relativeLinks,
+ boolean absolute,
+ RelativeSymlinkBehavior relSymlinkBehavior)
+ throws IOException {
+ if (relSymlinkBehavior == RelativeSymlinkBehavior.RESOLVE_FULLY && !relativeLinks.isEmpty()) {
+ fullyResolveRelativeSymlinks(entries, relativeLinks, absolute);
+ } else if (relSymlinkBehavior == RelativeSymlinkBehavior.RESOLVE) {
+ for (Map.Entry<PathFragment, String> e : relativeLinks.entrySet()) {
+ PathFragment location = e.getKey();
+ String value = e.getValue();
+ String actual = Preconditions.checkNotNull(value, e);
+ Preconditions.checkState(!actual.startsWith("/"), e);
+ PathFragment actualLocation = location;
- // Recursively resolve relative symlinks.
- LinkedHashSet<String> seen = new LinkedHashSet<>();
- int traversals = 0;
- do {
- actualLocation = actualLocation.getParentDirectory().getRelative(actual);
- actual = relativeLinks.get(actualLocation);
- } while (++traversals <= MAX_SYMLINK_TRAVERSALS && actual != null && seen.add(actual));
+ // Recursively resolve relative symlinks.
+ LinkedHashSet<String> seen = new LinkedHashSet<>();
+ int traversals = 0;
+ do {
+ actualLocation = actualLocation.getParentDirectory().getRelative(actual);
+ actual = relativeLinks.get(actualLocation);
+ } while (++traversals <= MAX_SYMLINK_TRAVERSALS && actual != null && seen.add(actual));
- if (traversals >= MAX_SYMLINK_TRAVERSALS) {
- logger.atWarning().log(
- "Symlink %s is part of a chain of length at least %d"
- + " which exceeds Blaze's maximum allowable symlink chain length",
- location, traversals);
- } else if (actual != null) {
- // TODO(b/113128395): throw here.
- logger.atWarning().log("Symlink %s forms a symlink cycle: %s", location, seen);
- } else if (!entries.containsKey(actualLocation)) {
- // We've found a relative symlink that points out of the fileset. We should really always
- // throw here, but current behavior is that we tolerate such symlinks when they occur in
- // runfiles, which is the only time this code is hit.
- // TODO(b/113128395): throw here.
- logger.atWarning().log(
- "Symlink %s (transitively) points to %s"
- + " that is not in this fileset (or was pruned because of a cycle)",
- location, actualLocation);
- } else {
- // We have successfully resolved the symlink.
- entries.put(location, entries.get(actualLocation));
+ if (traversals >= MAX_SYMLINK_TRAVERSALS) {
+ logger.atWarning().log(
+ "Symlink %s is part of a chain of length at least %d"
+ + " which exceeds Blaze's maximum allowable symlink chain length",
+ location, traversals);
+ } else if (actual != null) {
+ // TODO(b/113128395): throw here.
+ logger.atWarning().log("Symlink %s forms a symlink cycle: %s", location, seen);
+ } else if (!entries.containsKey(actualLocation)) {
+ // We've found a relative symlink that points out of the fileset. We should really always
+ // throw here, but current behavior is that we tolerate such symlinks when they occur in
+ // runfiles, which is the only time this code is hit.
+ // TODO(b/113128395): throw here.
+ logger.atWarning().log(
+ "Symlink %s (transitively) points to %s"
+ + " that is not in this fileset (or was pruned because of a cycle)",
+ location, actualLocation);
+ } else {
+ // We have successfully resolved the symlink.
+ entries.put(location, entries.get(actualLocation));
+ }
}
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/exec/FilesetManifestTest.java b/src/test/java/com/google/devtools/build/lib/exec/FilesetManifestTest.java
index 8024bc1..b3d63ba 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/FilesetManifestTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/FilesetManifestTest.java
@@ -17,20 +17,33 @@
import static com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior.ERROR;
import static com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior.IGNORE;
import static com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior.RESOLVE;
+import static com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior.RESOLVE_FULLY;
import static org.junit.Assert.assertThrows;
+import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.FilesetManifest;
+import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior;
import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
+import com.google.devtools.build.lib.exec.FilesetManifestTest.ManifestCommonTests;
+import com.google.devtools.build.lib.exec.FilesetManifestTest.OneOffManifestTests;
+import com.google.devtools.build.lib.exec.FilesetManifestTest.ResolvingManifestTests;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Suite;
/** Tests for {@link FilesetManifest}. */
-@RunWith(JUnit4.class)
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ ManifestCommonTests.class,
+ OneOffManifestTests.class,
+ ResolvingManifestTests.class
+})
public final class FilesetManifestTest {
private static final PathFragment EXEC_ROOT = PathFragment.create("/root");
@@ -40,176 +53,289 @@
PathFragment.create(from), PathFragment.create(to), EXEC_ROOT);
}
- @Test
- public void testEmptyManifest() throws Exception {
- List<FilesetOutputSymlink> symlinks = ImmutableList.of();
+ /** Manifest tests that apply to all relative symlink behavior. */
+ @RunWith(Parameterized.class)
+ public static final class ManifestCommonTests {
+ private final RelativeSymlinkBehavior behavior;
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), IGNORE);
+ @Parameterized.Parameters
+ public static ImmutableCollection<Object[]> behaviors() {
+ return ImmutableList.of(
+ new Object[] {ERROR},
+ new Object[] {RESOLVE},
+ new Object[] {IGNORE},
+ new Object[] {RESOLVE_FULLY});
+ }
- assertThat(manifest.getEntries()).isEmpty();
+ public ManifestCommonTests(RelativeSymlinkBehavior behavior) {
+ this.behavior = behavior;
+ }
+
+ @Test
+ public void testEmptyManifest() throws Exception {
+ List<FilesetOutputSymlink> symlinks = ImmutableList.of();
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
+
+ assertThat(manifest.getEntries()).isEmpty();
+ }
+
+ @Test
+ public void testManifestWithSingleFile() throws Exception {
+ List<FilesetOutputSymlink> symlinks = ImmutableList.of(filesetSymlink("bar", "/dir/file"));
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
+
+ assertThat(manifest.getEntries())
+ .containsExactly(PathFragment.create("out/foo/bar"), "/dir/file");
+ }
+
+ @Test
+ public void testManifestWithTwoFiles() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(filesetSymlink("bar", "/dir/file"), filesetSymlink("baz", "/dir/file"));
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
+
+ assertThat(manifest.getEntries())
+ .containsExactly(
+ PathFragment.create("out/foo/bar"), "/dir/file",
+ PathFragment.create("out/foo/baz"), "/dir/file");
+ }
+
+ @Test
+ public void testManifestWithDirectory() throws Exception {
+ List<FilesetOutputSymlink> symlinks = ImmutableList.of(filesetSymlink("bar", "/some"));
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
+
+ assertThat(manifest.getEntries())
+ .containsExactly(PathFragment.create("out/foo/bar"), "/some");
+ }
+
+ /** Regression test: code was previously crashing in this case. */
+ @Test
+ public void testManifestWithEmptyPath() throws Exception {
+ List<FilesetOutputSymlink> symlinks = ImmutableList.of(filesetSymlink("bar", ""));
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
+
+ assertThat(manifest.getEntries()).containsExactly(PathFragment.create("out/foo/bar"), null);
+ }
+
+ @Test
+ public void testManifestWithExecRootRelativePath() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(filesetSymlink("bar", EXEC_ROOT.getRelative("foo/bar").getPathString()));
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
+
+ assertThat(manifest.getEntries())
+ .containsExactly(PathFragment.create("out/foo/bar"), "foo/bar");
+ }
+
+ /** Current behavior is first one wins. */
+ @Test
+ public void testDefactoBehaviorWithDuplicateEntries() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(filesetSymlink("bar", "/foo/bar"), filesetSymlink("bar", "/baz"));
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
+
+ assertThat(manifest.getEntries())
+ .containsExactly(PathFragment.create("out/foo/bar"), "/foo/bar");
+ }
}
- @Test
- public void testManifestWithSingleFile() throws Exception {
- List<FilesetOutputSymlink> symlinks = ImmutableList.of(filesetSymlink("bar", "/dir/file"));
+ /** Manifest tests that apply to a specific relative symlink behavior. */
+ @RunWith(JUnit4.class)
+ public static final class OneOffManifestTests {
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), IGNORE);
+ @Test
+ public void testManifestWithErrorOnRelativeSymlink() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "/foo/bar"));
- assertThat(manifest.getEntries())
- .containsExactly(PathFragment.create("out/foo/bar"), "/dir/file");
+ IOException e =
+ assertThrows(
+ IOException.class,
+ () ->
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), ERROR));
+ assertThat(e).hasMessageThat().isEqualTo("runfiles target is not absolute: foo");
+ }
+
+ @Test
+ public void testManifestWithIgnoredRelativeSymlink() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "/foo/bar"));
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), IGNORE);
+
+ assertThat(manifest.getEntries())
+ .containsExactly(PathFragment.create("out/foo/foo"), "/foo/bar");
+ }
+
+ @Test
+ public void testManifestWithResolvedRelativeDirectorySymlink() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(
+ filesetSymlink("foo/subdir/f1", "/foo/subdir/f1"),
+ filesetSymlink("foo/bar", "subdir"));
+
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out"), RESOLVE_FULLY);
+
+ assertThat(manifest.getEntries())
+ .containsExactly(
+ PathFragment.create("out/foo/subdir/f1"), "/foo/subdir/f1",
+ PathFragment.create("out/foo/bar/f1"), "/foo/subdir/f1");
+ }
}
- @Test
- public void testManifestWithTwoFiles() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(filesetSymlink("bar", "/dir/file"), filesetSymlink("baz", "/dir/file"));
+ /** Manifest tests that apply resolving relative symlink behavior. */
+ @RunWith(Parameterized.class)
+ public static final class ResolvingManifestTests {
+ private final RelativeSymlinkBehavior behavior;
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), IGNORE);
+ @Parameterized.Parameters
+ public static ImmutableCollection<Object[]> behaviors() {
+ return ImmutableList.of(new Object[] {RESOLVE}, new Object[] {RESOLVE_FULLY});
+ }
- assertThat(manifest.getEntries())
- .containsExactly(
- PathFragment.create("out/foo/bar"), "/dir/file",
- PathFragment.create("out/foo/baz"), "/dir/file");
- }
+ public ResolvingManifestTests(RelativeSymlinkBehavior behavior) {
+ this.behavior = behavior;
+ }
- @Test
- public void testManifestWithDirectory() throws Exception {
- List<FilesetOutputSymlink> symlinks = ImmutableList.of(filesetSymlink("bar", "/some"));
+ @Test
+ public void testManifestWithResolvedRelativeSymlink() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "/foo/bar"));
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), IGNORE);
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
- assertThat(manifest.getEntries()).containsExactly(PathFragment.create("out/foo/bar"), "/some");
- }
+ assertThat(manifest.getEntries())
+ .containsExactly(
+ PathFragment.create("out/foo/bar"), "/foo/bar",
+ PathFragment.create("out/foo/foo"), "/foo/bar");
+ }
- /** Regression test: code was previously crashing in this case. */
- @Test
- public void testManifestWithEmptyPath() throws Exception {
- List<FilesetOutputSymlink> symlinks = ImmutableList.of(filesetSymlink("bar", ""));
+ @Test
+ public void testManifestWithResolvedRelativeSymlinkWithDotSlash() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(filesetSymlink("bar", "./foo"), filesetSymlink("foo", "/foo/bar"));
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), IGNORE);
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
- assertThat(manifest.getEntries()).containsExactly(PathFragment.create("out/foo/bar"), null);
- }
+ assertThat(manifest.getEntries())
+ .containsExactly(
+ PathFragment.create("out/foo/bar"), "/foo/bar",
+ PathFragment.create("out/foo/foo"), "/foo/bar");
+ }
- @Test
- public void testManifestWithErrorOnRelativeSymlink() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "/foo/bar"));
+ @Test
+ public void testManifestWithSymlinkCycle() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(
+ filesetSymlink("bar", "foo"),
+ filesetSymlink("foo", "biz"),
+ filesetSymlink("biz", "bar"),
+ filesetSymlink("reg", "/file"));
- IOException e =
- assertThrows(
- IOException.class,
- () ->
- FilesetManifest.constructFilesetManifest(
- symlinks, PathFragment.create("out/foo"), ERROR));
- assertThat(e).hasMessageThat().isEqualTo("runfiles target is not absolute: foo");
- }
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out"), behavior);
- @Test
- public void testManifestWithIgnoredRelativeSymlink() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "/foo/bar"));
+ assertThat(manifest.getEntries()).containsExactly(PathFragment.create("out/reg"), "/file");
+ }
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), IGNORE);
+ @Test
+ public void testUnboundedSymlinkDescendant() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(
+ filesetSymlink("p", "a/b"),
+ filesetSymlink("a/b", "../b/c"),
+ filesetSymlink("reg", "/file"));
- assertThat(manifest.getEntries())
- .containsExactly(PathFragment.create("out/foo/foo"), "/foo/bar");
- }
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out"), behavior);
- @Test
- public void testManifestWithResolvedRelativeSymlink() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "/foo/bar"));
+ assertThat(manifest.getEntries()).containsExactly(PathFragment.create("out/reg"), "/file");
+ }
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), RESOLVE);
+ @Test
+ public void testUnboundedSymlinkAncestor() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(
+ filesetSymlink("a/b", "c/d"),
+ filesetSymlink("a/c/d", ".././a"),
+ filesetSymlink("reg", "/file"));
- assertThat(manifest.getEntries())
- .containsExactly(
- PathFragment.create("out/foo/bar"), "/foo/bar",
- PathFragment.create("out/foo/foo"), "/foo/bar");
- }
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out"), behavior);
- @Test
- public void testManifestWithResolvedRelativeSymlinkWithDotSlash() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(filesetSymlink("bar", "./foo"), filesetSymlink("foo", "/foo/bar"));
+ assertThat(manifest.getEntries()).containsExactly(PathFragment.create("out/reg"), "/file");
+ }
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), RESOLVE);
+ @Test
+ public void testManifestWithResolvedRelativeSymlinkWithDotDotSlash() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(
+ filesetSymlink("bar/bar", "../foo/foo"), filesetSymlink("foo/foo", "/foo/bar"));
- assertThat(manifest.getEntries())
- .containsExactly(
- PathFragment.create("out/foo/bar"), "/foo/bar",
- PathFragment.create("out/foo/foo"), "/foo/bar");
- }
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
- @Test
- public void testManifestWithResolvedRelativeSymlinkWithDotDotSlash() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(
- filesetSymlink("bar/bar", "../foo/foo"), filesetSymlink("foo/foo", "/foo/bar"));
+ assertThat(manifest.getEntries())
+ .containsExactly(
+ PathFragment.create("out/foo/bar/bar"), "/foo/bar",
+ PathFragment.create("out/foo/foo/foo"), "/foo/bar");
+ }
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), RESOLVE);
+ @Test
+ public void testManifestWithUnresolvableRelativeSymlink() throws Exception {
+ List<FilesetOutputSymlink> symlinks = ImmutableList.of(filesetSymlink("bar", "foo"));
- assertThat(manifest.getEntries())
- .containsExactly(
- PathFragment.create("out/foo/bar/bar"), "/foo/bar",
- PathFragment.create("out/foo/foo/foo"), "/foo/bar");
- }
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
- @Test
- public void testManifestWithUnresolvableRelativeSymlink() throws Exception {
- List<FilesetOutputSymlink> symlinks = ImmutableList.of(filesetSymlink("bar", "foo"));
+ assertThat(manifest.getEntries()).isEmpty();
+ assertThat(manifest.getArtifactValues()).isEmpty();
+ }
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), RESOLVE);
+ @Test
+ public void testManifestWithUnresolvableRelativeSymlinkToRelativeSymlink() throws Exception {
+ List<FilesetOutputSymlink> symlinks =
+ ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "baz"));
- assertThat(manifest.getEntries()).isEmpty();
- assertThat(manifest.getArtifactValues()).isEmpty();
- }
+ FilesetManifest manifest =
+ FilesetManifest.constructFilesetManifest(
+ symlinks, PathFragment.create("out/foo"), behavior);
- @Test
- public void testManifestWithUnresolvableRelativeSymlinkToRelativeSymlink() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "baz"));
-
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), RESOLVE);
-
- assertThat(manifest.getEntries()).isEmpty();
- assertThat(manifest.getArtifactValues()).isEmpty();
- }
-
- /** Current behavior is first one wins. */
- @Test
- public void testDefactoBehaviorWithDuplicateEntries() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(filesetSymlink("bar", "/foo/bar"), filesetSymlink("bar", "/baz"));
-
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), IGNORE);
-
- assertThat(manifest.getEntries())
- .containsExactly(PathFragment.create("out/foo/bar"), "/foo/bar");
- }
-
- @Test
- public void testManifestWithExecRootRelativePath() throws Exception {
- List<FilesetOutputSymlink> symlinks =
- ImmutableList.of(filesetSymlink("bar", EXEC_ROOT.getRelative("foo/bar").getPathString()));
-
- FilesetManifest manifest =
- FilesetManifest.constructFilesetManifest(symlinks, PathFragment.create("out/foo"), IGNORE);
-
- assertThat(manifest.getEntries())
- .containsExactly(PathFragment.create("out/foo/bar"), "foo/bar");
+ assertThat(manifest.getEntries()).isEmpty();
+ assertThat(manifest.getArtifactValues()).isEmpty();
+ }
}
}