Allows symlink tree actions to construct fileset links without consuming the input manifest. Instead, construct the links directly from the Fileset links propagated through the action execution values.
PiperOrigin-RevId: 292183775
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java b/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java
index c7cf526..ed2bef9 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RunfilesSupport.java
@@ -373,7 +373,7 @@
inputManifest,
runfiles,
outputManifest,
- /*filesetTree=*/ false));
+ /*filesetRoot=*/ null));
return outputManifest;
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/SymlinkTreeAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/SymlinkTreeAction.java
index 475ce4d..ce05c09 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/actions/SymlinkTreeAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/SymlinkTreeAction.java
@@ -30,6 +30,7 @@
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.vfs.PathFragment;
import javax.annotation.Nullable;
/**
@@ -40,12 +41,12 @@
@AutoCodec
public final class SymlinkTreeAction extends AbstractAction {
- private static final String GUID = "63412bda-4026-4c8e-a3ad-7deb397728d4";
+ private static final String GUID = "7a16371c-cd4a-494d-b622-963cd89f5212";
@Nullable private final Artifact inputManifest;
private final Runfiles runfiles;
private final Artifact outputManifest;
- private final boolean filesetTree;
+ @Nullable private final String filesetRoot;
private final boolean enableRunfiles;
private final boolean inprocessSymlinkCreation;
private final boolean skipRunfilesManifests;
@@ -59,7 +60,7 @@
* @param runfiles the input runfiles
* @param outputManifest the generated symlink tree manifest (must have "MANIFEST" base name).
* Symlink tree root will be set to the artifact's parent directory.
- * @param filesetTree true if this is fileset symlink tree
+ * @param filesetRoot non-null if this is a fileset symlink tree
*/
public SymlinkTreeAction(
ActionOwner owner,
@@ -67,13 +68,13 @@
Artifact inputManifest,
@Nullable Runfiles runfiles,
Artifact outputManifest,
- boolean filesetTree) {
+ String filesetRoot) {
this(
owner,
inputManifest,
runfiles,
outputManifest,
- filesetTree,
+ filesetRoot,
config.getActionEnvironment(),
config.runfilesEnabled(),
config.inprocessSymlinkCreation(),
@@ -90,7 +91,7 @@
* @param runfiles the input runfiles
* @param outputManifest the generated symlink tree manifest (must have "MANIFEST" base name).
* Symlink tree root will be set to the artifact's parent directory.
- * @param filesetTree true if this is fileset symlink tree,
+ * @param filesetRoot non-null if this is a fileset symlink tree,
*/
@AutoCodec.Instantiator
public SymlinkTreeAction(
@@ -98,29 +99,29 @@
Artifact inputManifest,
@Nullable Runfiles runfiles,
Artifact outputManifest,
- boolean filesetTree,
+ @Nullable String filesetRoot,
ActionEnvironment env,
boolean enableRunfiles,
boolean inprocessSymlinkCreation,
boolean skipRunfilesManifests) {
super(
owner,
- skipRunfilesManifests && enableRunfiles && !filesetTree
+ skipRunfilesManifests && enableRunfiles && (filesetRoot == null)
? NestedSetBuilder.emptySet(Order.STABLE_ORDER)
: NestedSetBuilder.create(Order.STABLE_ORDER, inputManifest),
ImmutableSet.of(outputManifest),
env);
Preconditions.checkArgument(outputManifest.getPath().getBaseName().equals("MANIFEST"));
Preconditions.checkArgument(
- (runfiles == null) == filesetTree, "Runfiles must be null iff this is a fileset action");
- this.inputManifest =
- skipRunfilesManifests && enableRunfiles && !filesetTree ? null : inputManifest;
+ (runfiles == null) == (filesetRoot != null),
+ "Runfiles must be null iff this is a fileset action");
this.runfiles = runfiles;
this.outputManifest = outputManifest;
- this.filesetTree = filesetTree;
+ this.filesetRoot = filesetRoot;
this.enableRunfiles = enableRunfiles;
this.inprocessSymlinkCreation = inprocessSymlinkCreation;
- this.skipRunfilesManifests = skipRunfilesManifests && enableRunfiles && !filesetTree;
+ this.skipRunfilesManifests = skipRunfilesManifests && enableRunfiles && (filesetRoot == null);
+ this.inputManifest = this.skipRunfilesManifests ? null : inputManifest;
}
public Artifact getInputManifest() {
@@ -137,7 +138,11 @@
}
public boolean isFilesetTree() {
- return filesetTree;
+ return filesetRoot != null;
+ }
+
+ public PathFragment getFilesetRoot() {
+ return PathFragment.create(filesetRoot);
}
public boolean isRunfilesEnabled() {
@@ -148,10 +153,6 @@
return inprocessSymlinkCreation;
}
- public boolean skipRunfilesManifests() {
- return skipRunfilesManifests;
- }
-
@Override
public String getMnemonic() {
return "SymlinkTree";
@@ -159,14 +160,14 @@
@Override
protected String getRawProgressMessage() {
- return (filesetTree ? "Creating Fileset tree " : "Creating runfiles tree ")
+ return (isFilesetTree() ? "Creating Fileset tree " : "Creating runfiles tree ")
+ outputManifest.getExecPath().getParentDirectory().getPathString();
}
@Override
protected void computeKey(ActionKeyContext actionKeyContext, Fingerprint fp) {
fp.addString(GUID);
- fp.addBoolean(filesetTree);
+ fp.addNullableString(filesetRoot);
fp.addBoolean(enableRunfiles);
fp.addBoolean(inprocessSymlinkCreation);
fp.addBoolean(skipRunfilesManifests);
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java b/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java
index 877fc47..067adb5 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeHelper.java
@@ -13,14 +13,16 @@
// limitations under the License.
package com.google.devtools.build.lib.exec;
-import static java.nio.charset.StandardCharsets.ISO_8859_1;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.EnvironmentalExecException;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
import com.google.devtools.build.lib.shell.Command;
import com.google.devtools.build.lib.shell.CommandException;
import com.google.devtools.build.lib.util.CommandBuilder;
@@ -33,9 +35,7 @@
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 java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -184,32 +184,13 @@
.build();
}
- static Map<PathFragment, PathFragment> readSymlinksFromFilesetManifest(Path manifest)
- throws IOException {
- Map<PathFragment, PathFragment> result = new HashMap<>();
- try (BufferedReader reader =
- new BufferedReader(
- new InputStreamReader(
- // ISO_8859 is used to write the manifest in {Runfiles,Fileset}ManifestAction.
- manifest.getInputStream(), ISO_8859_1))) {
- String line;
- int lineNumber = 0;
- while ((line = reader.readLine()) != null) {
- // If the input has metadata (for fileset), they appear in every other line.
- if (++lineNumber % 2 == 0) {
- continue;
- }
- int spaceIndex = line.indexOf(' ');
- result.put(
- PathFragment.create(line.substring(0, spaceIndex)),
- PathFragment.create(line.substring(spaceIndex + 1)));
- }
- if (lineNumber % 2 != 0) {
- throw new IOException(
- "Possibly corrupted manifest file '" + manifest.getPathString() + "'");
- }
+ static ImmutableMap<PathFragment, PathFragment> processFilesetLinks(
+ ImmutableList<FilesetOutputSymlink> links, PathFragment root, PathFragment execRoot) {
+ Map<PathFragment, PathFragment> symlinks = new HashMap<>();
+ for (FilesetOutputSymlink symlink : links) {
+ symlinks.put(root.getRelative(symlink.getName()), symlink.reconstituteTargetPath(execRoot));
}
- return result;
+ return ImmutableMap.copyOf(symlinks);
}
private static final class Directory {
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeStrategy.java
index 442d2d4..38b8729 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SymlinkTreeStrategy.java
@@ -80,12 +80,14 @@
} else {
Preconditions.checkState(action.isFilesetTree());
Preconditions.checkNotNull(inputManifest);
- try {
- symlinks = SymlinkTreeHelper.readSymlinksFromFilesetManifest(inputManifest);
- } catch (IOException e) {
- throw new EnvironmentalExecException(
- "Failed to read from manifest '" + inputManifest.getPathString() + "'", e);
- }
+
+ symlinks =
+ SymlinkTreeHelper.processFilesetLinks(
+ actionExecutionContext
+ .getArtifactExpander()
+ .getFileset(action.getInputManifest()),
+ action.getFilesetRoot(),
+ actionExecutionContext.getExecRoot().asFragment());
}
outputService.createSymlinkTree(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/NativeLibs.java b/src/main/java/com/google/devtools/build/lib/rules/android/NativeLibs.java
index a065b06..4f7c31e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/NativeLibs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/NativeLibs.java
@@ -193,7 +193,7 @@
inputManifest,
runfiles,
outputManifest,
- /*filesetTree=*/ false));
+ /* filesetRoot = */ null));
return new ManifestAndRunfiles(outputManifest, runfiles);
}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkTreeActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkTreeActionTest.java
index 1b01f43..24a70d7 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkTreeActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/SymlinkTreeActionTest.java
@@ -69,7 +69,7 @@
? new Runfiles.Builder("TESTING", false).addArtifact(runfile).build()
: new Runfiles.Builder("TESTING", false).addArtifact(runfile2).build(),
outputManifest,
- /*filesetTree=*/ false,
+ /*filesetRoot=*/ null,
createActionEnvironment(
attributesToFlip.contains(RunfilesActionAttributes.FIXED_ENVIRONMENT),
attributesToFlip.contains(RunfilesActionAttributes.VARIABLE_ENVIRONMENT)),
@@ -84,7 +84,7 @@
inputManifest,
/*runfiles=*/ null,
outputManifest,
- /*filesetTree=*/ true,
+ /*filesetRoot=*/ "root",
createActionEnvironment(
attributesToFlip.contains(FilesetActionAttributes.FIXED_ENVIRONMENT),
attributesToFlip.contains(FilesetActionAttributes.VARIABLE_ENVIRONMENT)),
@@ -102,7 +102,7 @@
? new Runfiles.Builder("TESTING", false).addArtifact(runfile).build()
: new Runfiles.Builder("TESTING", false).addArtifact(runfile2).build(),
outputManifest,
- /*filesetTree=*/ false,
+ /*filesetRoot=*/ null,
createActionEnvironment(
attributesToFlip.contains(SkipManifestAttributes.FIXED_ENVIRONMENT),
attributesToFlip.contains(SkipManifestAttributes.VARIABLE_ENVIRONMENT)),
@@ -130,7 +130,7 @@
inputManifest,
/*runfiles=*/ null,
outputManifest,
- /*filesetTree=*/ false,
+ /*filesetRoot=*/ null,
createActionEnvironment(false, false),
/*enableRunfiles=*/ true,
/*inprocessSymlinkCreation=*/ false,
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeHelperTest.java b/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeHelperTest.java
index 8e81445..e510e4e 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeHelperTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeHelperTest.java
@@ -15,18 +15,16 @@
package com.google.devtools.build.lib.exec;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
+import com.google.devtools.build.lib.actions.HasDigest;
import com.google.devtools.build.lib.shell.Command;
import com.google.devtools.build.lib.vfs.FileSystem;
-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.FileNotFoundException;
-import java.io.IOException;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,50 +54,53 @@
}
@Test
- public void readManifest() throws Exception {
- Path execRoot = fs.getPath("/my/workspace");
- execRoot.createDirectoryAndParents();
- Path inputManifestPath = execRoot.getRelative("input_manifest");
- FileSystemUtils.writeContentAsLatin1(inputManifestPath, "from to\nmetadata");
+ public void readManifest() {
+ PathFragment execRoot = PathFragment.create("/my/workspace");
+
+ FilesetOutputSymlink link =
+ FilesetOutputSymlink.createForTesting(
+ PathFragment.create("from"), PathFragment.create("to"), execRoot);
+
Map<PathFragment, PathFragment> symlinks =
- SymlinkTreeHelper.readSymlinksFromFilesetManifest(inputManifestPath);
- assertThat(symlinks).containsExactly(PathFragment.create("from"), PathFragment.create("to"));
+ SymlinkTreeHelper.processFilesetLinks(
+ ImmutableList.of(link), PathFragment.create("root"), execRoot);
+ assertThat(symlinks)
+ .containsExactly(PathFragment.create("root/from"), PathFragment.create("to"));
}
@Test
- public void readMultilineManifest() throws Exception {
- Path execRoot = fs.getPath("/my/workspace");
- execRoot.createDirectoryAndParents();
- Path inputManifestPath = execRoot.getRelative("input_manifest");
- FileSystemUtils.writeContentAsLatin1(
- inputManifestPath, "from to\nmetadata\n/foo /bar\nmetadata");
+ public void readMultilineManifest() {
+ PathFragment execRoot = PathFragment.create("/my/workspace");
+
+ FilesetOutputSymlink link1 =
+ FilesetOutputSymlink.createForTesting(
+ PathFragment.create("from"), PathFragment.create("to"), execRoot);
+ FilesetOutputSymlink link2 =
+ FilesetOutputSymlink.createForTesting(
+ PathFragment.create("foo"), PathFragment.create("/bar"), execRoot);
+ FilesetOutputSymlink link3 =
+ FilesetOutputSymlink.createAlreadyRelativized(
+ PathFragment.create("rel"), PathFragment.create("path"), HasDigest.EMPTY, true, true);
+ FilesetOutputSymlink link4 =
+ FilesetOutputSymlink.createAlreadyRelativized(
+ PathFragment.create("rel2"),
+ PathFragment.create("/path"),
+ HasDigest.EMPTY,
+ false,
+ false);
+
Map<PathFragment, PathFragment> symlinks =
- SymlinkTreeHelper.readSymlinksFromFilesetManifest(inputManifestPath);
+ SymlinkTreeHelper.processFilesetLinks(
+ ImmutableList.of(link1, link2, link3, link4), PathFragment.create("root2"), execRoot);
assertThat(symlinks)
.containsExactly(
- PathFragment.create("from"),
+ PathFragment.create("root2/from"),
PathFragment.create("to"),
- PathFragment.create("/foo"),
- PathFragment.create("/bar"));
- }
-
- @Test
- public void readCorruptManifest() throws Exception {
- Path execRoot = fs.getPath("/my/workspace");
- execRoot.createDirectoryAndParents();
- Path inputManifestPath = execRoot.getRelative("input_manifest");
- FileSystemUtils.writeContentAsLatin1(inputManifestPath, "from to");
- assertThrows(
- IOException.class,
- () -> SymlinkTreeHelper.readSymlinksFromFilesetManifest(inputManifestPath));
- }
-
- @Test
- public void readNonExistentManifestFails() {
- Path execRoot = fs.getPath("/my/workspace");
- Path inputManifestPath = execRoot.getRelative("input_manifest");
- assertThrows(
- FileNotFoundException.class,
- () -> SymlinkTreeHelper.readSymlinksFromFilesetManifest(inputManifestPath));
+ PathFragment.create("root2/foo"),
+ PathFragment.create("/bar"),
+ PathFragment.create("root2/rel"),
+ execRoot.getRelative("path"),
+ PathFragment.create("root2/rel2"),
+ PathFragment.create("/path"));
}
}
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeStrategyTest.java b/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeStrategyTest.java
index c50c7f6..839a4bb 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeStrategyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/SymlinkTreeStrategyTest.java
@@ -92,7 +92,7 @@
inputManifest,
runfiles,
outputManifest,
- /*filesetTree=*/ false,
+ /*filesetRoot=*/ null,
ActionEnvironment.EMPTY,
/*enableRunfiles=*/ true,
/*inprocessSymlinkCreation=*/ false,
@@ -138,7 +138,7 @@
inputManifest,
runfiles,
outputManifest,
- /*filesetTree=*/ false,
+ /*filesetRoot=*/ null,
ActionEnvironment.EMPTY,
/*enableRunfiles=*/ true,
/*inprocessSymlinkCreation=*/ true,