@@ -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,17 @@ 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 , 0 ) catch | err | {
206
+ std .debug .print ("unable to parse seed '{s}' as 32-bit integer: {s}" , .{
207
+ next_arg , @errorName (err ),
208
+ });
209
+ process .exit (1 );
210
+ };
199
211
} else if (mem .eql (u8 , arg , "--debug-log" )) {
200
212
const next_arg = nextArg (args , & arg_idx ) orelse {
201
213
std .debug .print ("Expected argument after {s}\n\n " , .{arg });
@@ -329,6 +341,7 @@ pub fn main() !void {
329
341
main_progress_node ,
330
342
thread_pool_options ,
331
343
& run ,
344
+ seed ,
332
345
) catch | err | switch (err ) {
333
346
error .UncleanExit = > process .exit (1 ),
334
347
else = > return err ,
@@ -355,6 +368,7 @@ fn runStepNames(
355
368
parent_prog_node : * std.Progress.Node ,
356
369
thread_pool_options : std.Thread.Pool.Options ,
357
370
run : * Run ,
371
+ seed : u32 ,
358
372
) ! void {
359
373
const gpa = b .allocator ;
360
374
var step_stack : std .AutoArrayHashMapUnmanaged (* Step , void ) = .{};
@@ -375,8 +389,13 @@ fn runStepNames(
375
389
}
376
390
377
391
const starting_steps = try arena .dupe (* Step , step_stack .keys ());
392
+
393
+ var rng = std .rand .DefaultPrng .init (seed );
394
+ const rand = rng .random ();
395
+ rand .shuffle (* Step , starting_steps );
396
+
378
397
for (starting_steps ) | s | {
379
- checkForDependencyLoop (b , s , & step_stack ) catch | err | switch (err ) {
398
+ constructGraphAndCheckForDependencyLoop (b , s , & step_stack , rand ) catch | err | switch (err ) {
380
399
error .DependencyLoopDetected = > return error .UncleanExit ,
381
400
else = > | e | return e ,
382
401
};
@@ -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