@@ -93,6 +93,7 @@ pub fn main() !void {
93
93
var summary : ? Summary = null ;
94
94
var max_rss : usize = 0 ;
95
95
var color : Color = .auto ;
96
+ var seed : u32 = 0 ;
96
97
97
98
const stderr_stream = io .getStdErr ().writer ();
98
99
const stdout_stream = io .getStdOut ().writer ();
@@ -192,6 +193,15 @@ pub fn main() !void {
192
193
std .debug .print ("Expected argument after {s}\n\n " , .{arg });
193
194
usageAndErr (builder , false , stderr_stream );
194
195
} };
196
+ } else if (mem .eql (u8 , arg , "--seed" )) {
197
+ const next_arg = nextArg (args , & arg_idx ) orelse {
198
+ std .debug .print ("Expected u32 after {s}\n\n " , .{arg });
199
+ usageAndErr (builder , false , stderr_stream );
200
+ };
201
+ seed = std .fmt .parseUnsigned (u32 , next_arg , 10 ) catch | err | {
202
+ std .debug .print ("unable to parse seed '{s}' as u32: {s}" , .{ next_arg , @errorName (err ) });
203
+ process .exit (1 );
204
+ };
195
205
} else if (mem .eql (u8 , arg , "--debug-log" )) {
196
206
const next_arg = nextArg (args , & arg_idx ) orelse {
197
207
std .debug .print ("Expected argument after {s}\n\n " , .{arg });
@@ -324,6 +334,7 @@ pub fn main() !void {
324
334
main_progress_node ,
325
335
thread_pool_options ,
326
336
& run ,
337
+ seed ,
327
338
) catch | err | switch (err ) {
328
339
error .UncleanExit = > process .exit (1 ),
329
340
else = > return err ,
@@ -349,6 +360,7 @@ fn runStepNames(
349
360
parent_prog_node : * std.Progress.Node ,
350
361
thread_pool_options : std.Thread.Pool.Options ,
351
362
run : * Run ,
363
+ seed : u32 ,
352
364
) ! void {
353
365
const gpa = b .allocator ;
354
366
var step_stack : std .AutoArrayHashMapUnmanaged (* Step , void ) = .{};
@@ -369,8 +381,13 @@ fn runStepNames(
369
381
}
370
382
371
383
const starting_steps = try arena .dupe (* Step , step_stack .keys ());
384
+
385
+ var rng = std .rand .DefaultPrng .init (seed );
386
+ const rand = rng .random ();
387
+ rand .shuffle (* Step , starting_steps );
388
+
372
389
for (starting_steps ) | s | {
373
- checkForDependencyLoop (b , s , & step_stack ) catch | err | switch (err ) {
390
+ constructGraphAndCheckForDependencyLoop (b , s , & step_stack , rand ) catch | err | switch (err ) {
374
391
error .DependencyLoopDetected = > return error .UncleanExit ,
375
392
else = > | e | return e ,
376
393
};
@@ -498,7 +515,9 @@ fn runStepNames(
498
515
stderr .writeAll (" (disable with --summary none)" ) catch {};
499
516
ttyconf .setColor (stderr , .reset ) catch {};
500
517
}
501
- stderr .writeAll ("\n " ) catch {};
518
+ ttyconf .setColor (stderr , .dim ) catch {};
519
+ stderr .writer ().print ("\n seed is {}\n " , .{seed }) catch {};
520
+ ttyconf .setColor (stderr , .reset ) catch {};
502
521
const failures_only = run .summary != Summary .all ;
503
522
504
523
// Print a fancy tree with build results.
@@ -731,10 +750,22 @@ fn printTreeStep(
731
750
}
732
751
}
733
752
734
- fn checkForDependencyLoop (
753
+ /// Traverse the dependency graph depth-first and make it undirected by having
754
+ /// steps know their dependants (they only know dependencies at start).
755
+ /// Along the way, check that there is no dependency loop, and record the steps
756
+ /// in traversal order in `step_stack`.
757
+ /// Each step has its dependencies traversed in random order, this accomplishes
758
+ /// two things:
759
+ /// - `step_stack` will be in randomized-depth-first order, so the build runner
760
+ /// spawns steps in a random (but optimized) order
761
+ /// - each step's `dependants` list is also filled in a random order, so that
762
+ /// when it finishes executing in `workerMakeOneStep`, it spawns next steps
763
+ /// to run in random order
764
+ fn constructGraphAndCheckForDependencyLoop (
735
765
b : * std.Build ,
736
766
s : * Step ,
737
767
step_stack : * std .AutoArrayHashMapUnmanaged (* Step , void ),
768
+ rand : std.rand.Random ,
738
769
) ! void {
739
770
switch (s .state ) {
740
771
.precheck_started = > {
@@ -745,10 +776,16 @@ fn checkForDependencyLoop(
745
776
s .state = .precheck_started ;
746
777
747
778
try step_stack .ensureUnusedCapacity (b .allocator , s .dependencies .items .len );
748
- for (s .dependencies .items ) | dep | {
779
+
780
+ // We dupe to avoid shuffling the steps in the summary, it depends
781
+ // on s.dependencies' order.
782
+ const deps = b .allocator .dupe (* Step , s .dependencies .items ) catch @panic ("OOM" );
783
+ rand .shuffle (* Step , deps );
784
+
785
+ for (deps ) | dep | {
749
786
try step_stack .put (b .allocator , dep , {});
750
787
try dep .dependants .append (b .allocator , s );
751
- checkForDependencyLoop (b , dep , step_stack ) catch | err | {
788
+ constructGraphAndCheckForDependencyLoop (b , dep , step_stack , rand ) catch | err | {
752
789
if (err == error .DependencyLoopDetected ) {
753
790
std .debug .print (" {s}\n " , .{s .name });
754
791
}
@@ -1014,6 +1051,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
1014
1051
\\ --global-cache-dir [path] Override path to global Zig cache directory
1015
1052
\\ --zig-lib-dir [arg] Override path to Zig lib directory
1016
1053
\\ --build-runner [file] Override path to build runner
1054
+ \\ --seed [integer] For shuffling dependency traversal order (default: random)
1017
1055
\\ --debug-log [scope] Enable debugging the compiler
1018
1056
\\ --debug-pkg-config Fail if unknown pkg-config flags encountered
1019
1057
\\ --verbose-link Enable compiler debug output for linking
0 commit comments