Skip to content

Commit c05c626

Browse files
morotencopybara-github
authored andcommitted
Remote: Fix file counting in merkletree.DirectoryTreeBuilder
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
1 parent a556f78 commit c05c626

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
@@ -147,8 +147,8 @@ static class DirectoryNode extends Node {
147147
super(pathSegment);
148148
}
149149

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

154154
@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)