Skip to content

Commit fabdff4

Browse files
coeuvremoroten
andauthored
Remote: Fix file counting in merkletree.DirectoryTreeBuilder (bazelbuild#14331)
The DirectoryTreeBuilder did not check if files already existed in the resulting map, so the file counter got wrong and an assertion failed. The error was visible when adding a file and the directory containing that file as inputs for an action. Fixes bazelbuild#14286. Closes bazelbuild#14299. PiperOrigin-RevId: 412051374 Co-authored-by: Fredrik Medley <[email protected]>
1 parent dc76f74 commit fabdff4

File tree

3 files changed

+50
-9
lines changed

3 files changed

+50
-9
lines changed

src/main/java/com/google/devtools/build/lib/remote/merkletree/DirectoryTree.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ static class DirectoryNode extends Node {
148148
super(pathSegment);
149149
}
150150

151-
void addChild(Node child) {
152-
children.add(Preconditions.checkNotNull(child, "child"));
151+
boolean addChild(Node child) {
152+
return children.add(Preconditions.checkNotNull(child, "child"));
153153
}
154154

155155
@Override

src/main/java/com/google/devtools/build/lib/remote/merkletree/DirectoryTreeBuilder.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ private static int buildFromPaths(
101101
throw new IOException(String.format("Input '%s' is not a file.", input));
102102
}
103103
Digest d = digestUtil.compute(input);
104-
currDir.addChild(FileNode.createExecutable(path.getBaseName(), input, d));
105-
return 1;
104+
boolean childAdded =
105+
currDir.addChild(FileNode.createExecutable(path.getBaseName(), input, d));
106+
return childAdded ? 1 : 0;
106107
});
107108
}
108109

@@ -127,9 +128,11 @@ private static int buildFromActionInputs(
127128
if (input instanceof VirtualActionInput) {
128129
VirtualActionInput virtualActionInput = (VirtualActionInput) input;
129130
Digest d = digestUtil.compute(virtualActionInput);
130-
currDir.addChild(
131-
FileNode.createExecutable(path.getBaseName(), virtualActionInput.getBytes(), d));
132-
return 1;
131+
boolean childAdded =
132+
currDir.addChild(
133+
FileNode.createExecutable(
134+
path.getBaseName(), virtualActionInput.getBytes(), d));
135+
return childAdded ? 1 : 0;
133136
}
134137

135138
FileArtifactValue metadata =
@@ -141,8 +144,9 @@ private static int buildFromActionInputs(
141144
case REGULAR_FILE:
142145
Digest d = DigestUtil.buildDigest(metadata.getDigest(), metadata.getSize());
143146
Path inputPath = ActionInputHelper.toInputPath(input, execRoot);
144-
currDir.addChild(FileNode.createExecutable(path.getBaseName(), inputPath, d));
145-
return 1;
147+
boolean childAdded =
148+
currDir.addChild(FileNode.createExecutable(path.getBaseName(), inputPath, d));
149+
return childAdded ? 1 : 0;
146150

147151
case DIRECTORY:
148152
SortedMap<PathFragment, ActionInput> directoryInputs =

src/test/java/com/google/devtools/build/lib/remote/merkletree/ActionInputDirectoryTreeTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,43 @@ public void directoryInputShouldBeExpanded() throws Exception {
130130
assertThat(fileNodesAtDepth(tree, 3)).containsExactly(expectedBuzzNode);
131131
}
132132

133+
@Test
134+
public void filesShouldBeDeduplicated() throws Exception {
135+
// Test that a file specified as part of a directory and as an individual file is not counted
136+
// twice.
137+
138+
SortedMap<PathFragment, ActionInput> sortedInputs = new TreeMap<>();
139+
Map<ActionInput, FileArtifactValue> metadata = new HashMap<>();
140+
141+
Path dirPath = execRoot.getRelative("srcs");
142+
dirPath.createDirectoryAndParents();
143+
Artifact dir = ActionsTestUtil.createArtifact(artifactRoot, dirPath);
144+
sortedInputs.put(dirPath.relativeTo(execRoot), dir);
145+
metadata.put(dir, FileArtifactValue.createForTesting(dirPath));
146+
147+
Path fooPath = dirPath.getRelative("foo.cc");
148+
FileSystemUtils.writeContentAsLatin1(fooPath, "foo");
149+
ActionInput foo = ActionInputHelper.fromPath(fooPath.relativeTo(execRoot));
150+
sortedInputs.put(fooPath.relativeTo(execRoot), foo);
151+
metadata.put(foo, FileArtifactValue.createForTesting(fooPath));
152+
153+
DirectoryTree tree =
154+
DirectoryTreeBuilder.fromActionInputs(
155+
sortedInputs, new StaticMetadataProvider(metadata), execRoot, digestUtil);
156+
assertLexicographicalOrder(tree);
157+
158+
assertThat(directoriesAtDepth(0, tree)).containsExactly("srcs");
159+
assertThat(directoriesAtDepth(1, tree)).isEmpty();
160+
161+
FileNode expectedFooNode =
162+
FileNode.createExecutable(
163+
"foo.cc", execRoot.getRelative(foo.getExecPath()), digestUtil.computeAsUtf8("foo"));
164+
assertThat(fileNodesAtDepth(tree, 0)).isEmpty();
165+
assertThat(fileNodesAtDepth(tree, 1)).containsExactly(expectedFooNode);
166+
167+
assertThat(tree.numFiles()).isEqualTo(1);
168+
}
169+
133170
private static VirtualActionInput addVirtualFile(
134171
String path, String content, SortedMap<PathFragment, ActionInput> sortedInputs) {
135172
PathFragment pathFragment = PathFragment.create(path);

0 commit comments

Comments
 (0)