@@ -192,6 +192,12 @@ pub fn main() !void {
192
192
std .debug .print ("Expected argument after {s}\n\n " , .{arg });
193
193
usageAndErr (builder , false , stderr_stream );
194
194
} };
195
+ } else if (mem .eql (u8 , arg , "--seed" )) {
196
+ const next_arg = nextArg (args , & arg_idx ) orelse {
197
+ std .debug .print ("Expected a string after {s}\n\n " , .{arg });
198
+ usageAndErr (builder , false , stderr_stream );
199
+ };
200
+ builder .seed = next_arg ;
195
201
} else if (mem .eql (u8 , arg , "--debug-log" )) {
196
202
const next_arg = nextArg (args , & arg_idx ) orelse {
197
203
std .debug .print ("Expected argument after {s}\n\n " , .{arg });
@@ -369,8 +375,14 @@ fn runStepNames(
369
375
}
370
376
371
377
const starting_steps = try arena .dupe (* Step , step_stack .keys ());
378
+
379
+ const useed = std .hash .XxHash64 .hash (0x1312 , b .seed );
380
+ var rng = std .rand .DefaultPrng .init (useed );
381
+ const rand = rng .random ();
382
+ rand .shuffle (* Step , starting_steps );
383
+
372
384
for (starting_steps ) | s | {
373
- checkForDependencyLoop (b , s , & step_stack ) catch | err | switch (err ) {
385
+ buildAndCheckGraphForDependencyLoop (b , s , & step_stack , rand ) catch | err | switch (err ) {
374
386
error .DependencyLoopDetected = > return error .UncleanExit ,
375
387
else = > | e | return e ,
376
388
};
@@ -485,6 +497,9 @@ fn runStepNames(
485
497
stderr .writeAll ("Build Summary:" ) catch {};
486
498
ttyconf .setColor (stderr , .reset ) catch {};
487
499
stderr .writer ().print (" {d}/{d} steps succeeded" , .{ success_count , total_count }) catch {};
500
+ ttyconf .setColor (stderr , .dim ) catch {};
501
+ stderr .writer ().print (" (seed is {s})" , .{b .seed }) catch {};
502
+ ttyconf .setColor (stderr , .reset ) catch {};
488
503
if (skipped_count > 0 ) stderr .writer ().print ("; {d} skipped" , .{skipped_count }) catch {};
489
504
if (failure_count > 0 ) stderr .writer ().print ("; {d} failed" , .{failure_count }) catch {};
490
505
@@ -731,10 +746,22 @@ fn printTreeStep(
731
746
}
732
747
}
733
748
734
- fn checkForDependencyLoop (
749
+ /// Traverse the dependency graph depth-first and make it undirected by having
750
+ /// steps know their dependants (they only know dependencies at start).
751
+ /// Along the way, check that there is no dependency loop, and record the steps
752
+ /// in traversal order in `step_stack`.
753
+ /// Each step has its dependencies traversed in random order, this accomplishes
754
+ /// two things:
755
+ /// - `step_stack` will be in randomized-depth-first order, so the build runner
756
+ /// spawns steps in a random (but optimized) order
757
+ /// - each step's `dependants` list is also filled in a random order, so that
758
+ /// when it finishes executing in `workerMakeOneStep`, it spawns next steps
759
+ /// to run in random order
760
+ fn buildAndCheckGraphForDependencyLoop (
735
761
b : * std.Build ,
736
762
s : * Step ,
737
763
step_stack : * std .AutoArrayHashMapUnmanaged (* Step , void ),
764
+ rand : std.rand.Random ,
738
765
) ! void {
739
766
switch (s .state ) {
740
767
.precheck_started = > {
@@ -745,10 +772,16 @@ fn checkForDependencyLoop(
745
772
s .state = .precheck_started ;
746
773
747
774
try step_stack .ensureUnusedCapacity (b .allocator , s .dependencies .items .len );
748
- for (s .dependencies .items ) | dep | {
775
+
776
+ // We dupe to avoid shuffling the steps in the summary, it depends
777
+ // on s.dependencies' order.
778
+ const deps = b .allocator .dupe (* Step , s .dependencies .items ) catch @panic ("OOM" );
779
+ rand .shuffle (* Step , deps );
780
+
781
+ for (deps ) | dep | {
749
782
try step_stack .put (b .allocator , dep , {});
750
783
try dep .dependants .append (b .allocator , s );
751
- checkForDependencyLoop (b , dep , step_stack ) catch | err | {
784
+ buildAndCheckGraphForDependencyLoop (b , dep , step_stack , rand ) catch | err | {
752
785
if (err == error .DependencyLoopDetected ) {
753
786
std .debug .print (" {s}\n " , .{s .name });
754
787
}
@@ -1014,6 +1047,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
1014
1047
\\ --global-cache-dir [path] Override path to global Zig cache directory
1015
1048
\\ --zig-lib-dir [arg] Override path to Zig lib directory
1016
1049
\\ --build-runner [file] Override path to build runner
1050
+ \\ --seed [string] Seed the prng used in the build runner
1017
1051
\\ --debug-log [scope] Enable debugging the compiler
1018
1052
\\ --debug-pkg-config Fail if unknown pkg-config flags encountered
1019
1053
\\ --verbose-link Enable compiler debug output for linking
0 commit comments