@@ -243,7 +243,7 @@ pub fn addDepFileOutputArg(self: *Run, basename: []const u8) std.Build.LazyPath
243243/// Add a prefixed path argument to a dep file (.d) for the child process to
244244/// write its discovered additional dependencies.
245245/// Only one dep file argument is allowed by instance.
246- pub fn addPrefixedDepFileOutputArg (self : * Run , prefix : []const u8 , basename : []const u8 ) void {
246+ pub fn addPrefixedDepFileOutputArg (self : * Run , prefix : []const u8 , basename : []const u8 ) std.Build.LazyPath {
247247 assert (self .dep_output_file == null );
248248
249249 const b = self .step .owner ;
@@ -258,6 +258,8 @@ pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []c
258258 self .dep_output_file = dep_file ;
259259
260260 self .argv .append (.{ .output = dep_file }) catch @panic ("OOM" );
261+
262+ return .{ .generated = & dep_file .generated_file };
261263}
262264
263265pub fn addArg (self : * Run , arg : []const u8 ) void {
@@ -448,17 +450,18 @@ fn checksContainStderr(checks: []const StdIo.Check) bool {
448450 return false ;
449451}
450452
453+ const IndexedOutput = struct {
454+ index : usize ,
455+ output : * Output ,
456+ };
451457fn make (step : * Step , prog_node : * std.Progress.Node ) ! void {
452458 const b = step .owner ;
453459 const arena = b .allocator ;
454460 const self = @fieldParentPtr (Run , "step" , step );
455461 const has_side_effects = self .hasSideEffects ();
456462
457463 var argv_list = ArrayList ([]const u8 ).init (arena );
458- var output_placeholders = ArrayList (struct {
459- index : usize ,
460- output : * Output ,
461- }).init (arena );
464+ var output_placeholders = ArrayList (IndexedOutput ).init (arena );
462465
463466 var man = b .cache .obtain ();
464467 defer man .deinit ();
@@ -540,32 +543,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
540543 if (try step .cacheHit (& man )) {
541544 // cache hit, skip running command
542545 const digest = man .final ();
543- for (output_placeholders .items ) | placeholder | {
544- placeholder .output .generated_file .path = try b .cache_root .join (arena , &.{
545- "o" , & digest , placeholder .output .basename ,
546- });
547- }
548546
549- if (self .captured_stdout ) | output | {
550- output .generated_file .path = try b .cache_root .join (arena , &.{
551- "o" , & digest , output .basename ,
552- });
553- }
554-
555- if (self .captured_stderr ) | output | {
556- output .generated_file .path = try b .cache_root .join (arena , &.{
557- "o" , & digest , output .basename ,
558- });
559- }
547+ try populateGeneratedPaths (
548+ arena ,
549+ output_placeholders .items ,
550+ self .captured_stdout ,
551+ self .captured_stderr ,
552+ b .cache_root ,
553+ & digest ,
554+ );
560555
561556 step .result_cached = true ;
562557 return ;
563558 }
564559
565- const digest = man .final ();
560+ const rand_int = std .crypto .random .int (u64 );
561+ const tmp_dir_path = "tmp" ++ fs .path .sep_str ++ std .Build .hex64 (rand_int );
566562
567563 for (output_placeholders .items ) | placeholder | {
568- const output_components = .{ "o" , & digest , placeholder .output .basename };
564+ const output_components = .{ tmp_dir_path , placeholder .output .basename };
569565 const output_sub_path = try fs .path .join (arena , & output_components );
570566 const output_sub_dir_path = fs .path .dirname (output_sub_path ).? ;
571567 b .cache_root .handle .makePath (output_sub_dir_path ) catch | err | {
@@ -582,12 +578,83 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
582578 argv_list .items [placeholder .index ] = cli_arg ;
583579 }
584580
585- try runCommand (self , argv_list .items , has_side_effects , & digest , prog_node );
581+ try runCommand (self , argv_list .items , has_side_effects , tmp_dir_path , prog_node );
586582
587583 if (self .dep_output_file ) | dep_output_file |
588584 try man .addDepFilePost (std .fs .cwd (), dep_output_file .generated_file .getPath ());
589585
586+ const digest = man .final ();
587+
588+ const any_output = output_placeholders .items .len > 0 or
589+ self .captured_stdout != null or self .captured_stderr != null ;
590+
591+ // Rename into place
592+ if (any_output ) {
593+ const o_sub_path = "o" ++ fs .path .sep_str ++ & digest ;
594+
595+ b .cache_root .handle .rename (tmp_dir_path , o_sub_path ) catch | err | {
596+ if (err == error .PathAlreadyExists ) {
597+ b .cache_root .handle .deleteTree (o_sub_path ) catch | del_err | {
598+ return step .fail ("unable to remove dir '{}'{s}: {s}" , .{
599+ b .cache_root ,
600+ tmp_dir_path ,
601+ @errorName (del_err ),
602+ });
603+ };
604+ b .cache_root .handle .rename (tmp_dir_path , o_sub_path ) catch | retry_err | {
605+ return step .fail ("unable to rename dir '{}{s}' to '{}{s}': {s}" , .{
606+ b .cache_root , tmp_dir_path ,
607+ b .cache_root , o_sub_path ,
608+ @errorName (retry_err ),
609+ });
610+ };
611+ } else {
612+ return step .fail ("unable to rename dir '{}{s}' to '{}{s}': {s}" , .{
613+ b .cache_root , tmp_dir_path ,
614+ b .cache_root , o_sub_path ,
615+ @errorName (err ),
616+ });
617+ }
618+ };
619+ }
620+
590621 try step .writeManifest (& man );
622+
623+ try populateGeneratedPaths (
624+ arena ,
625+ output_placeholders .items ,
626+ self .captured_stdout ,
627+ self .captured_stderr ,
628+ b .cache_root ,
629+ & digest ,
630+ );
631+ }
632+
633+ fn populateGeneratedPaths (
634+ arena : std.mem.Allocator ,
635+ output_placeholders : []const IndexedOutput ,
636+ captured_stdout : ? * Output ,
637+ captured_stderr : ? * Output ,
638+ cache_root : Build.Cache.Directory ,
639+ digest : * const Build.Cache.HexDigest ,
640+ ) ! void {
641+ for (output_placeholders ) | placeholder | {
642+ placeholder .output .generated_file .path = try cache_root .join (arena , &.{
643+ "o" , digest , placeholder .output .basename ,
644+ });
645+ }
646+
647+ if (captured_stdout ) | output | {
648+ output .generated_file .path = try cache_root .join (arena , &.{
649+ "o" , digest , output .basename ,
650+ });
651+ }
652+
653+ if (captured_stderr ) | output | {
654+ output .generated_file .path = try cache_root .join (arena , &.{
655+ "o" , digest , output .basename ,
656+ });
657+ }
591658}
592659
593660fn formatTerm (
@@ -639,7 +706,7 @@ fn runCommand(
639706 self : * Run ,
640707 argv : []const []const u8 ,
641708 has_side_effects : bool ,
642- digest : ? * const [ std . Build . Cache . hex_digest_len ] u8 ,
709+ tmp_dir_path : ? [] const u8 ,
643710 prog_node : * std.Progress.Node ,
644711) ! void {
645712 const step = & self .step ;
@@ -812,7 +879,7 @@ fn runCommand(
812879 },
813880 }) | stream | {
814881 if (stream .captured ) | output | {
815- const output_components = .{ "o" , digest .? , output .basename };
882+ const output_components = .{ tmp_dir_path .? , output .basename };
816883 const output_path = try b .cache_root .join (arena , & output_components );
817884 output .generated_file .path = output_path ;
818885
0 commit comments