diff --git a/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java b/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java index be99bbd147220b..26ccf19bc758f2 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java +++ b/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java @@ -13,6 +13,7 @@ // limitations under the License. package com.google.devtools.build.lib.actions; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static java.nio.charset.StandardCharsets.ISO_8859_1; @@ -282,6 +283,11 @@ public static FileArtifactValue createForVirtualActionInput(byte[] digest, long return new RegularFileArtifactValue(digest, /* proxy= */ null, size); } + public static FileArtifactValue createForUnresolvedSymlink(Artifact artifact) throws IOException { + checkArgument(artifact.isSymlink()); + return createForUnresolvedSymlink(artifact.getPath()); + } + public static FileArtifactValue createForUnresolvedSymlink(Path symlink) throws IOException { return new UnresolvedSymlinkArtifactValue(symlink); } diff --git a/src/main/java/com/google/devtools/build/lib/exec/CompactSpawnLogContext.java b/src/main/java/com/google/devtools/build/lib/exec/CompactSpawnLogContext.java index dbfc6bdc4767f5..8a2d17e2b5e215 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/CompactSpawnLogContext.java +++ b/src/main/java/com/google/devtools/build/lib/exec/CompactSpawnLogContext.java @@ -293,6 +293,8 @@ private int logNestedSet( Path path = fileSystem.getPath(execRoot.getRelative(input.getExecPath())); if (isInputDirectory(input, inputMetadataProvider)) { builder.addDirectoryIds(logDirectory(input, path, inputMetadataProvider)); + } else if (input.isSymlink()) { + builder.addUnresolvedSymlinkIds(logUnresolvedSymlink(input, path)); } else { builder.addFileIds(logFile(input, path, inputMetadataProvider)); } @@ -306,7 +308,8 @@ private int logNestedSet( * Logs a file. * * @param input the input representing the file. - * @param path the path to the file + * @param path the path to the file, which must have already been verified to be of the correct + * type. * @return the entry ID of the {@link ExecLogEntry.File} describing the file. */ private int logFile(ActionInput input, Path path, InputMetadataProvider inputMetadataProvider) diff --git a/src/main/java/com/google/devtools/build/lib/exec/ExpandedSpawnLogContext.java b/src/main/java/com/google/devtools/build/lib/exec/ExpandedSpawnLogContext.java index 6054e5d7f00669..2bb8a00536b2d0 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/ExpandedSpawnLogContext.java +++ b/src/main/java/com/google/devtools/build/lib/exec/ExpandedSpawnLogContext.java @@ -180,7 +180,8 @@ public void logSpawn( builder .addInputsBuilder() .setPath(displayPath.getPathString()) - .setSymlinkTargetPath(metadata.getSymlinkTarget()); + .setSymlinkTargetPath(metadata.getSymlinkTarget()) + .setIsTool(isTool); continue; } diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java index 6b2967c241fe80..228107b4592877 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java +++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnLogContext.java @@ -115,7 +115,7 @@ static boolean isInputDirectory(ActionInput input, InputMetadataProvider inputMe if (input.isDirectory()) { return true; } - if (!(input instanceof SourceArtifact)) { + if (input.isSymlink() || !(input instanceof SourceArtifact)) { return false; } // A source artifact may be a directory in spite of claiming to be a file. Avoid unnecessary I/O diff --git a/src/main/protobuf/spawn.proto b/src/main/protobuf/spawn.proto index fe01830f6f06fc..e6a2e389648a66 100644 --- a/src/main/protobuf/spawn.proto +++ b/src/main/protobuf/spawn.proto @@ -248,17 +248,19 @@ message ExecLogEntry { } // A set of spawn inputs. - // The contents of the set are the directly referenced files and directories - // in addition to the contents of all transitively referenced sets. Sets are - // not canonical: two sets with different structure may yield the same - // contents. + // The contents of the set are the directly referenced files, directories and + // symlinks in addition to the contents of all transitively referenced sets. + // Sets are not canonical: two sets with different structure may yield the + // same contents. message InputSet { // Entry IDs of files belonging to this set. repeated int32 file_ids = 1; // Entry IDs of directories belonging to this set. repeated int32 directory_ids = 2; + // Entry IDs of unresolved symlinks belonging to this set. + repeated int32 unresolved_symlink_ids = 3; // Entry IDs of other sets contained in this set. - repeated int32 transitive_set_ids = 3; + repeated int32 transitive_set_ids = 4; } // A spawn output. diff --git a/src/test/java/com/google/devtools/build/lib/exec/CompactSpawnLogContextTest.java b/src/test/java/com/google/devtools/build/lib/exec/CompactSpawnLogContextTest.java index 69dd8dfeb168ed..1e5d1afed35273 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/CompactSpawnLogContextTest.java +++ b/src/test/java/com/google/devtools/build/lib/exec/CompactSpawnLogContextTest.java @@ -240,6 +240,11 @@ private SortedMap reconstructInputs( inputs.put(dirFile.getPath(), reconstructFile(dir, dirFile, hashFunctionName)); } } + for (int symlinkId : set.getUnresolvedSymlinkIdsList()) { + ExecLogEntry.UnresolvedSymlink symlink = + checkNotNull(entryMap.get(symlinkId)).getUnresolvedSymlink(); + inputs.put(symlink.getPath(), reconstructSymlink(symlink)); + } setsToVisit.addAll(set.getTransitiveSetIdsList()); } return inputs; diff --git a/src/test/java/com/google/devtools/build/lib/exec/SpawnLogContextTestBase.java b/src/test/java/com/google/devtools/build/lib/exec/SpawnLogContextTestBase.java index 3524301a02dd66..d03517d1f18263 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/SpawnLogContextTestBase.java +++ b/src/test/java/com/google/devtools/build/lib/exec/SpawnLogContextTestBase.java @@ -249,6 +249,39 @@ public void testTreeInput( .build()); } + @Test + public void testUnresolvedSymlinkInput(@TestParameter InputsMode inputsMode) throws Exception { + Artifact symlinkInput = ActionsTestUtil.createUnresolvedSymlinkArtifact(outputDir, "symlink"); + + symlinkInput.getPath().getParentDirectory().createDirectoryAndParents(); + symlinkInput.getPath().createSymbolicLink(PathFragment.create("/some/path")); + + SpawnBuilder spawn = defaultSpawnBuilder().withInputs(symlinkInput); + if (inputsMode.isTool()) { + spawn.withTools(symlinkInput); + } + + SpawnLogContext context = createSpawnLogContext(); + + context.logSpawn( + spawn.build(), + createInputMetadataProvider(symlinkInput), + createInputMap(symlinkInput), + fs, + defaultTimeout(), + defaultSpawnResult()); + + closeAndAssertLog( + context, + defaultSpawnExecBuilder() + .addInputs( + File.newBuilder() + .setPath("out/symlink") + .setSymlinkTargetPath("/some/path") + .setIsTool(inputsMode.isTool())) + .build()); + } + @Test public void testRunfilesFileInput() throws Exception { Artifact runfilesInput = ActionsTestUtil.createArtifact(rootDir, "data.txt"); @@ -862,6 +895,8 @@ protected static InputMetadataProvider createInputMetadataProvider(Artifact... a treeMetadata.getChildValues().entrySet()) { builder.put(entry.getKey(), entry.getValue()); } + } else if (artifact.isSymlink()) { + builder.put(artifact, FileArtifactValue.createForUnresolvedSymlink(artifact)); } else { builder.put(artifact, FileArtifactValue.createForTesting(artifact)); }