| ... | ... | @@ -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("\nseed 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 |