3131import com .google .common .annotations .VisibleForTesting ;
3232import com .google .common .base .Preconditions ;
3333import com .google .common .collect .ImmutableList ;
34+ import com .google .common .collect .Lists ;
3435import com .google .devtools .build .lib .actions .ActionExecutionMetadata ;
3536import com .google .devtools .build .lib .actions .ActionUploadFinishedEvent ;
3637import com .google .devtools .build .lib .actions .ActionUploadStartedEvent ;
5455import com .google .devtools .build .lib .vfs .PathFragment ;
5556import com .google .devtools .build .lib .vfs .Symlinks ;
5657import com .google .protobuf .ByteString ;
58+ import com .google .protobuf .CodedOutputStream ;
5759import com .google .protobuf .Timestamp ;
5860import io .reactivex .rxjava3 .core .Completable ;
5961import io .reactivex .rxjava3 .core .Flowable ;
6062import io .reactivex .rxjava3 .core .Single ;
63+ import java .io .ByteArrayOutputStream ;
6164import java .io .IOException ;
6265import java .time .Duration ;
6366import java .time .Instant ;
6467import java .util .ArrayList ;
6568import java .util .Collection ;
6669import java .util .Comparator ;
6770import java .util .HashMap ;
71+ import java .util .LinkedHashSet ;
6872import java .util .List ;
6973import java .util .Map ;
7074import java .util .Optional ;
75+ import java .util .Set ;
7176import java .util .stream .Collectors ;
7277import javax .annotation .Nullable ;
7378
@@ -303,25 +308,43 @@ private void addFile(Digest digest, Path file) throws IOException {
303308 digestToFile .put (digest , file );
304309 }
305310
311+ // Field numbers of the 'root' and 'directory' fields in the Tree message.
312+ private static final int TREE_ROOT_FIELD_NUMBER =
313+ Tree .getDescriptor ().findFieldByName ("root" ).getNumber ();
314+ private static final int TREE_CHILDREN_FIELD_NUMBER =
315+ Tree .getDescriptor ().findFieldByName ("children" ).getNumber ();
316+
306317 private void addDirectory (Path dir ) throws ExecException , IOException {
307- Tree .Builder tree = Tree .newBuilder ();
308- Directory root = computeDirectory (dir , tree );
309- tree .setRoot (root );
318+ Set <ByteString > directories = new LinkedHashSet <>();
319+ var ignored = computeDirectory (dir , directories );
320+
321+ // Convert individual Directory messages to a Tree message. As we want the
322+ // records to be topologically sorted (parents before children), we iterate
323+ // over the directories in reverse insertion order.
324+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream ();
325+ CodedOutputStream codedOutputStream = CodedOutputStream .newInstance (byteArrayOutputStream );
326+ int fieldNumber = TREE_ROOT_FIELD_NUMBER ;
327+ for (ByteString directory : Lists .reverse (new ArrayList <ByteString >(directories ))) {
328+ codedOutputStream .writeBytes (fieldNumber , directory );
329+ fieldNumber = TREE_CHILDREN_FIELD_NUMBER ;
330+ }
331+ codedOutputStream .flush ();
310332
311- ByteString data = tree . build (). toByteString ( );
333+ ByteString data = ByteString . copyFrom ( byteArrayOutputStream . toByteArray () );
312334 Digest digest = digestUtil .compute (data .toByteArray ());
313335
314336 if (result != null ) {
315337 result
316338 .addOutputDirectoriesBuilder ()
317339 .setPath (remotePathResolver .localPathToOutputPath (dir ))
318- .setTreeDigest (digest );
340+ .setTreeDigest (digest )
341+ .setIsTopologicallySorted (true );
319342 }
320343
321344 digestToBlobs .put (digest , data );
322345 }
323346
324- private Directory computeDirectory (Path path , Tree . Builder tree )
347+ private ByteString computeDirectory (Path path , Set < ByteString > directories )
325348 throws ExecException , IOException {
326349 Directory .Builder b = Directory .newBuilder ();
327350
@@ -332,9 +355,8 @@ private Directory computeDirectory(Path path, Tree.Builder tree)
332355 String name = dirent .getName ();
333356 Path child = path .getRelative (name );
334357 if (dirent .getType () == Dirent .Type .DIRECTORY ) {
335- Directory dir = computeDirectory (child , tree );
336- b .addDirectoriesBuilder ().setName (name ).setDigest (digestUtil .compute (dir ));
337- tree .addChildren (dir );
358+ ByteString dir = computeDirectory (child , directories );
359+ b .addDirectoriesBuilder ().setName (name ).setDigest (digestUtil .compute (dir .toByteArray ()));
338360 } else if (dirent .getType () == Dirent .Type .SYMLINK ) {
339361 PathFragment target = child .readSymbolicLink ();
340362 if (!followSymlinks && !target .isAbsolute ()) {
@@ -353,9 +375,8 @@ private Directory computeDirectory(Path path, Tree.Builder tree)
353375 b .addFilesBuilder ().setName (name ).setDigest (digest ).setIsExecutable (child .isExecutable ());
354376 digestToFile .put (digest , child );
355377 } else if (statFollow .isDirectory ()) {
356- Directory dir = computeDirectory (child , tree );
357- b .addDirectoriesBuilder ().setName (name ).setDigest (digestUtil .compute (dir ));
358- tree .addChildren (dir );
378+ ByteString dir = computeDirectory (child , directories );
379+ b .addDirectoriesBuilder ().setName (name ).setDigest (digestUtil .compute (dir .toByteArray ()));
359380 } else {
360381 illegalOutput (child );
361382 }
@@ -368,7 +389,9 @@ private Directory computeDirectory(Path path, Tree.Builder tree)
368389 }
369390 }
370391
371- return b .build ();
392+ ByteString directory = b .build ().toByteString ();
393+ directories .add (directory );
394+ return directory ;
372395 }
373396
374397 private void illegalOutput (Path path ) throws ExecException {
0 commit comments