@@ -95,6 +95,7 @@ pub fn main() !void {
95
95
var max_rss : usize = 0 ;
96
96
var skip_oom_steps : bool = false ;
97
97
var color : Color = .auto ;
98
+ var seed : u32 = 0 ;
98
99
99
100
const stderr_stream = io .getStdErr ().writer ();
100
101
const stdout_stream = io .getStdOut ().writer ();
@@ -196,6 +197,15 @@ pub fn main() !void {
196
197
std .debug .print ("Expected argument after {s}\n\n " , .{arg });
197
198
usageAndErr (builder , false , stderr_stream );
198
199
} };
200
+ } else if (mem .eql (u8 , arg , "--seed" )) {
201
+ const next_arg = nextArg (args , & arg_idx ) orelse {
202
+ std .debug .print ("Expected u32 after {s}\n\n " , .{arg });
203
+ usageAndErr (builder , false , stderr_stream );
204
+ };
205
+ seed = std .fmt .parseUnsigned (u32 , next_arg , 10 ) catch | err | {
206
+ std .debug .print ("unable to parse seed '{s}' as u32: {s}" , .{ next_arg , @errorName (err ) });
207
+ process .exit (1 );
208
+ };
199
209
} else if (mem .eql (u8 , arg , "--debug-log" )) {
200
210
const next_arg = nextArg (args , & arg_idx ) orelse {
201
211
std .debug .print ("Expected argument after {s}\n\n " , .{arg });
@@ -329,6 +339,7 @@ pub fn main() !void {
329
339
main_progress_node ,
330
340
thread_pool_options ,
331
341
& run ,
342
+ seed ,
332
343
) catch | err | switch (err ) {
333
344
error .UncleanExit = > process .exit (1 ),
334
345
else = > return err ,
@@ -355,6 +366,7 @@ fn runStepNames(
355
366
parent_prog_node : * std.Progress.Node ,
356
367
thread_pool_options : std.Thread.Pool.Options ,
357
368
run : * Run ,
369
+ seed : u32 ,
358
370
) ! void {
359
371
const gpa = b .allocator ;
360
372
var step_stack : std .AutoArrayHashMapUnmanaged (* Step , void ) = .{};
@@ -375,8 +387,13 @@ fn runStepNames(
375
387
}
376
388
377
389
const starting_steps = try arena .dupe (* Step , step_stack .keys ());
390
+
391
+ var rng = std .rand .DefaultPrng .init (seed );
392
+ const rand = rng .random ();
393
+ rand .shuffle (* Step , starting_steps );
394
+
378
395
for (starting_steps ) | s | {
379
- checkForDependencyLoop (b , s , & step_stack ) catch | err | switch (err ) {
396
+ constructGraphAndCheckForDependencyLoop (b , s , & step_stack , rand ) catch | err | switch (err ) {
380
397
error .DependencyLoopDetected = > return error .UncleanExit ,
381
398
else = > | e | return e ,
382
399
};
@@ -509,7 +526,9 @@ fn runStepNames(
509
526
stderr .writeAll (" (disable with --summary none)" ) catch {};
510
527
ttyconf .setColor (stderr , .reset ) catch {};
511
528
}
512
- stderr .writeAll ("\n " ) catch {};
529
+ ttyconf .setColor (stderr , .dim ) catch {};
530
+ stderr .writer ().print ("\n seed is {}\n " , .{seed }) catch {};
531
+ ttyconf .setColor (stderr , .reset ) catch {};
513
532
const failures_only = run .summary != Summary .all ;
514
533
515
534
// Print a fancy tree with build results.
@@ -748,10 +767,22 @@ fn printTreeStep(
748
767
}
749
768
}
750
769
751
- fn checkForDependencyLoop (
770
+ /// Traverse the dependency graph depth-first and make it undirected by having
771
+ /// steps know their dependants (they only know dependencies at start).
772
+ /// Along the way, check that there is no dependency loop, and record the steps
773
+ /// in traversal order in `step_stack`.
774
+ /// Each step has its dependencies traversed in random order, this accomplishes
775
+ /// two things:
776
+ /// - `step_stack` will be in randomized-depth-first order, so the build runner
777
+ /// spawns steps in a random (but optimized) order
778
+ /// - each step's `dependants` list is also filled in a random order, so that
779
+ /// when it finishes executing in `workerMakeOneStep`, it spawns next steps
780
+ /// to run in random order
781
+ fn constructGraphAndCheckForDependencyLoop (
752
782
b : * std.Build ,
753
783
s : * Step ,
754
784
step_stack : * std .AutoArrayHashMapUnmanaged (* Step , void ),
785
+ rand : std.rand.Random ,
755
786
) ! void {
756
787
switch (s .state ) {
757
788
.precheck_started = > {
@@ -762,10 +793,16 @@ fn checkForDependencyLoop(
762
793
s .state = .precheck_started ;
763
794
764
795
try step_stack .ensureUnusedCapacity (b .allocator , s .dependencies .items .len );
765
- for (s .dependencies .items ) | dep | {
796
+
797
+ // We dupe to avoid shuffling the steps in the summary, it depends
798
+ // on s.dependencies' order.
799
+ const deps = b .allocator .dupe (* Step , s .dependencies .items ) catch @panic ("OOM" );
800
+ rand .shuffle (* Step , deps );
801
+
802
+ for (deps ) | dep | {
766
803
try step_stack .put (b .allocator , dep , {});
767
804
try dep .dependants .append (b .allocator , s );
768
- checkForDependencyLoop (b , dep , step_stack ) catch | err | {
805
+ constructGraphAndCheckForDependencyLoop (b , dep , step_stack , rand ) catch | err | {
769
806
if (err == error .DependencyLoopDetected ) {
770
807
std .debug .print (" {s}\n " , .{s .name });
771
808
}
@@ -1034,6 +1071,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
1034
1071
\\ --global-cache-dir [path] Override path to global Zig cache directory
1035
1072
\\ --zig-lib-dir [arg] Override path to Zig lib directory
1036
1073
\\ --build-runner [file] Override path to build runner
1074
+ \\ --seed [integer] For shuffling dependency traversal order (default: random)
1037
1075
\\ --debug-log [scope] Enable debugging the compiler
1038
1076
\\ --debug-pkg-config Fail if unknown pkg-config flags encountered
1039
1077
\\ --verbose-link Enable compiler debug output for linking
0 commit comments