authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-17 20:36:45-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:33-07:00
logafd7507a197d516c7240f92fc19e4f43adb0b79c
tree10dcb45cb2c0ba386e75b17ec9df18b834c7517f
parent0505318efe0d2757a344dded9ae1607f948f7511

make runner: prepare steps for execution


7 files changed, 506 insertions(+), 504 deletions(-)

lib/compiler/configure_runner.zig+79-63
......@@ -186,6 +186,8 @@ pub fn main(init: process.Init.Minimal) !void {
186186 // but it is handled by the parent process. The build runner
187187 // only sees this flag.
188188 graph.system_package_mode = true;
189 } else if (mem.eql(u8, arg, "--have-run-args")) {
190 graph.have_run_args = true;
189191 } else {
190192 fatalWithHint("unrecognized argument: '{s}'", .{arg});
191193 }
......@@ -226,13 +228,69 @@ pub fn main(init: process.Init.Minimal) !void {
226228 process.exit(0);
227229}
228230
231const Serialize = struct {
232 arena: Allocator,
233 wc: *Configuration.Wip,
234 module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty,
235 package_map: std.AutoArrayHashMapUnmanaged(*std.Build, Configuration.Package.Index) = .empty,
236
237 fn builderToPackage(s: *Serialize, b: *std.Build) !Configuration.Package.Index {
238 if (b.pkg_hash.len == 0) return .root;
239 const arena = s.arena;
240 const wc = s.wc;
241 const gop = try s.package_map.getOrPut(arena, b);
242 if (!gop.found_existing) {
243 gop.value_ptr.* = @enumFromInt(try wc.addExtra(@as(Configuration.Package, .{
244 .hash = try wc.addString(b.pkg_hash),
245 .dep_prefix = try wc.addString(b.dep_prefix),
246 })));
247 }
248 return gop.value_ptr.*;
249 }
250
251 fn addOptionalLazyPath(s: *Serialize, lp: ?std.Build.LazyPath) !Configuration.OptionalLazyPath {
252 const wc = s.wc;
253 return @enumFromInt(switch (lp orelse return .none) {
254 .src_path => |src_path| i: {
255 const sub_path = try wc.addString(src_path.sub_path);
256 break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{
257 .flags = .{},
258 .owner = try s.builderToPackage(src_path.owner),
259 .sub_path = sub_path,
260 }));
261 },
262 .generated => |generated| i: {
263 const sub_path = try wc.addString(generated.sub_path);
264 break :i try wc.addExtra(@as(Configuration.LazyPath.Generated, .{
265 .flags = .{ .up = @intCast(generated.up) },
266 .sub_path = sub_path,
267 }));
268 },
269 .cwd_relative => |cwd_relative_sub_path| i: {
270 const sub_path = try wc.addString(cwd_relative_sub_path);
271 break :i try wc.addExtra(@as(Configuration.LazyPath.Relative, .{
272 .flags = .{ .base = .cwd },
273 .sub_path = sub_path,
274 }));
275 },
276 .dependency => |dependency| i: {
277 const sub_path = try wc.addString(dependency.sub_path);
278 break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{
279 .flags = .{},
280 .owner = try s.builderToPackage(dependency.dependency.builder),
281 .sub_path = sub_path,
282 }));
283 },
284 });
285 }
286};
287
229288fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
230289 const graph = b.graph;
231290 const arena = graph.arena;
232291 const gpa = wc.gpa;
233292
234 var module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty;
235 defer module_map.deinit(gpa);
293 var s: Serialize = .{ .wc = wc, .arena = arena };
236294
237295 // Starting from all top-level steps in `b`, traverse the entire step graph
238296 // and add all step dependencies implied by module graphs.
......@@ -267,6 +325,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
267325 try wc.steps.ensureTotalCapacity(gpa, step_map.entries.capacity);
268326 wc.steps.appendAssumeCapacity(.{
269327 .name = try wc.addString(step.name),
328 .owner = try s.builderToPackage(step.owner),
270329 .deps = deps,
271330 .max_rss = .fromBytes(step.max_rss),
272331 .extra_index = switch (step.tag) {
......@@ -367,7 +426,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
367426 .install_name = c.install_name != null,
368427 .entitlements = c.entitlements != null,
369428 },
370 .root_module = try addModule(wc, &module_map, c.root_module),
429 .root_module = try addModule(&s, c.root_module),
371430 .root_name = try wc.addString(c.name),
372431 }));
373432
......@@ -383,13 +442,13 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
383442 },
384443 .dest_dir = try addInstallDir(wc, ia.dest_dir),
385444 .dest_sub_path = try wc.addString(ia.dest_sub_path),
386 .emitted_bin = try addOptionalLazyPath(wc, ia.emitted_bin),
445 .emitted_bin = try s.addOptionalLazyPath(ia.emitted_bin),
387446 .implib_dir = try addInstallDir(wc, ia.implib_dir),
388 .emitted_implib = try addOptionalLazyPath(wc, ia.emitted_implib),
447 .emitted_implib = try s.addOptionalLazyPath(ia.emitted_implib),
389448 .pdb_dir = try addInstallDir(wc, ia.pdb_dir),
390 .emitted_pdb = try addOptionalLazyPath(wc, ia.emitted_pdb),
449 .emitted_pdb = try s.addOptionalLazyPath(ia.emitted_pdb),
391450 .h_dir = try addInstallDir(wc, ia.h_dir),
392 .emitted_h = try addOptionalLazyPath(wc, ia.emitted_h),
451 .emitted_h = try s.addOptionalLazyPath(ia.emitted_h),
393452 .artifact = stepIndex(&step_map, &ia.artifact.step),
394453 }));
395454 },
......@@ -440,7 +499,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
440499 },
441500 .file_inputs_len = @intCast(run.file_inputs.items.len),
442501 .args_len = @intCast(run.argv.items.len),
443 .cwd = try addOptionalLazyPath(wc, run.cwd),
502 .cwd = try s.addOptionalLazyPath(run.cwd),
444503 .captured_stdout = captured_stdout,
445504 .captured_stderr = captured_stderr,
446505 }));
......@@ -469,13 +528,11 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
469528 });
470529}
471530
472fn addModule(
473 wc: *Configuration.Wip,
474 module_map: *std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index),
475 m: *std.Build.Module,
476) !Configuration.Module.Index {
477 if (module_map.get(m)) |index| return index;
531fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index {
532 if (s.module_map.get(m)) |index| return index;
478533
534 const wc = s.wc;
535 const arena = s.arena;
479536 const gpa = wc.gpa;
480537 const import_table: Configuration.ImportTable = @enumFromInt(wc.extra.items.len);
481538 const import_table_extra_len = 1 + 2 * m.import_table.entries.len;
......@@ -494,7 +551,7 @@ fn addModule(
494551 @intFromEnum(import_table) + 1 + m.import_table.entries.len..,
495552 ) |dep, extra_index| {
496553 log.err("TODO module dependencies can be cyclic", .{});
497 wc.extra.items[extra_index] = @intFromEnum(try addModule(wc, module_map, dep));
554 wc.extra.items[extra_index] = @intFromEnum(try addModule(s, dep));
498555 }
499556
500557 const module_index: Configuration.Module.Index = @enumFromInt(try wc.addExtra(@as(Configuration.Module, .{
......@@ -528,15 +585,15 @@ fn addModule(
528585 .link_libcpp = .init(m.strip),
529586 .no_builtin = .init(m.strip),
530587 },
531 .owner = try builderToPackage(wc, m.owner),
532 .root_source_file = try addOptionalLazyPath(wc, m.root_source_file),
588 .owner = try s.builderToPackage(m.owner),
589 .root_source_file = try s.addOptionalLazyPath(m.root_source_file),
533590 .import_table = import_table,
534591 .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),
535592 })));
536593
537594 log.err("TODO serialize the trailing Module data", .{});
538595
539 try module_map.putNoClobber(gpa, m, module_index);
596 try s.module_map.putNoClobber(arena, m, module_index);
540597
541598 return module_index;
542599}
......@@ -553,46 +610,6 @@ fn addOptionalResolvedTarget(
553610 })));
554611}
555612
556fn addOptionalLazyPath(wc: *Configuration.Wip, lp: ?std.Build.LazyPath) !Configuration.OptionalLazyPath {
557 return @enumFromInt(switch (lp orelse return .none) {
558 .src_path => |src_path| i: {
559 const sub_path = try wc.addString(src_path.sub_path);
560 break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{
561 .flags = .{},
562 .owner = try builderToPackage(wc, src_path.owner),
563 .sub_path = sub_path,
564 }));
565 },
566 .generated => |generated| i: {
567 const sub_path = try wc.addString(generated.sub_path);
568 break :i try wc.addExtra(@as(Configuration.LazyPath.Generated, .{
569 .flags = .{ .up = @intCast(generated.up) },
570 .sub_path = sub_path,
571 }));
572 },
573 .cwd_relative => |cwd_relative_sub_path| i: {
574 const sub_path = try wc.addString(cwd_relative_sub_path);
575 break :i try wc.addExtra(@as(Configuration.LazyPath.Relative, .{
576 .flags = .{ .base = .cwd },
577 .sub_path = sub_path,
578 }));
579 },
580 .dependency => |dependency| i: {
581 const sub_path = try wc.addString(dependency.sub_path);
582 break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{
583 .flags = .{},
584 .owner = try builderToPackage(wc, dependency.dependency.builder),
585 .sub_path = sub_path,
586 }));
587 },
588 });
589}
590
591fn builderToPackage(wc: *Configuration.Wip, b: *std.Build) !Configuration.Package {
592 if (b.pkg_hash.len == 0) return .root;
593 return .fromHash(try wc.addString(b.pkg_hash));
594}
595
596613fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir {
597614 switch (install_dir orelse return .none) {
598615 .prefix => return .prefix,
......@@ -665,9 +682,7 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
665682
666683fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
667684 return nextArg(args, idx) orelse {
668 fatal("expected argument after {q}\n access the help menu with \"zig build -h\"", .{
669 args[idx.* - 1],
670 });
685 fatalWithHint("expected argument after: {s}", .{args[idx.* - 1]});
671686 };
672687}
673688
......@@ -700,7 +715,8 @@ const MultilineErrors = enum { indent, newline, none };
700715const Summary = enum { all, new, failures, line, none };
701716
702717fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
703 fatal(f ++ "\n access the help menu with \"zig build -h\"", args);
718 log.info("to access the help menu: zig build -h", .{});
719 fatal(f, args);
704720}
705721
706722fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void {
......@@ -725,7 +741,7 @@ fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration
725741 });
726742 }
727743 if (bad) {
728 log.info("access the help menu with \"zig build -h\"", .{});
744 log.info("help menu contains available options: zig build -h", .{});
729745 process.exit(1);
730746 }
731747}
lib/compiler/maker.zig+352-326
......@@ -17,7 +17,7 @@ const process = std.process;
1717
1818const Fuzz = @import("maker/Fuzz.zig");
1919const Graph = @import("maker/Graph.zig");
20const Step = void; // @import("maker/Step.zig");
20const Step = @import("maker/Step.zig");
2121const Watch = @import("maker/Watch.zig");
2222const WebServer = @import("maker/WebServer.zig");
2323
......@@ -100,8 +100,8 @@ pub fn main(init: process.Init.Minimal) !void {
100100 graph.cache.addPrefix(global_cache_directory);
101101 graph.cache.hash.addBytes(builtin.zig_version_string);
102102
103 var targets = std.array_list.Managed([]const u8).init(arena);
104 var debug_log_scopes = std.array_list.Managed([]const u8).init(arena);
103 var step_names: std.ArrayList([]const u8) = .empty;
104 var debug_log_scopes: std.ArrayList([]const u8) = .empty;
105105 var help_menu = false;
106106 var steps_menu = false;
107107 var print_configuration = false;
......@@ -151,29 +151,6 @@ pub fn main(init: process.Init.Minimal) !void {
151151 }
152152 }
153153
154 const scanned_config: ScannedConfig = sc: {
155 const configuration = c: {
156 var file = cwd.openFile(io, configure_path, .{}) catch |err|
157 fatal("failed to open configuration file {s}: {t}", .{ configure_path, err });
158 defer file.close(io);
159 break :c Configuration.loadFile(arena, io, file) catch |err|
160 fatal("failed to load configuration file {s}: {t}", .{ configure_path, err });
161 };
162 var top_level_steps: std.ArrayList(Configuration.Step.Index) = .empty;
163 for (configuration.steps, 0..) |*conf_step, step_index| {
164 const flags: Configuration.Step.Flags = @bitCast(configuration.extra[conf_step.extra_index]);
165 if (flags.tag == .top_level) {
166 try top_level_steps.append(arena, @enumFromInt(step_index));
167 }
168 }
169 break :sc .{
170 .configuration = configuration,
171 .top_level_steps = top_level_steps.items,
172 };
173 };
174
175 log.err("TODO handle user -D options", .{});
176
177154 while (nextArg(args, &arg_idx)) |arg| {
178155 if (mem.startsWith(u8, arg, "-")) {
179156 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
......@@ -291,7 +268,7 @@ pub fn main(init: process.Init.Minimal) !void {
291268 };
292269 } else if (mem.eql(u8, arg, "--debug-log")) {
293270 const next_arg = nextArgOrFatal(args, &arg_idx);
294 try debug_log_scopes.append(next_arg);
271 try debug_log_scopes.append(arena, next_arg);
295272 } else if (mem.eql(u8, arg, "--debug-pkg-config")) {
296273 debug_pkg_config = true;
297274 } else if (mem.eql(u8, arg, "--debug-rt")) {
......@@ -395,7 +372,7 @@ pub fn main(init: process.Init.Minimal) !void {
395372 fatalWithHint("unrecognized argument: '{s}'", .{arg});
396373 }
397374 } else {
398 try targets.append(arg);
375 try step_names.append(arena, arg);
399376 }
400377 }
401378
......@@ -408,6 +385,29 @@ pub fn main(init: process.Init.Minimal) !void {
408385 .off => .no_color,
409386 };
410387
388 const scanned_config: ScannedConfig = sc: {
389 const configuration = c: {
390 var file = cwd.openFile(io, configure_path, .{}) catch |err|
391 fatal("failed to open configuration file {s}: {t}", .{ configure_path, err });
392 defer file.close(io);
393 break :c Configuration.loadFile(arena, io, file) catch |err|
394 fatal("failed to load configuration file {s}: {t}", .{ configure_path, err });
395 };
396 var top_level_steps: std.StringArrayHashMapUnmanaged(Configuration.Step.Index) = .empty;
397 for (configuration.steps, 0..) |*conf_step, step_index_usize| {
398 const step_index: Configuration.Step.Index = @enumFromInt(step_index_usize);
399 const flags: Configuration.Step.Flags = @bitCast(configuration.extra[conf_step.extra_index]);
400 if (flags.tag == .top_level) {
401 const name = step_index.ptr(&configuration).name.slice(&configuration);
402 try top_level_steps.put(arena, name, step_index);
403 }
404 }
405 break :sc .{
406 .configuration = configuration,
407 .top_level_steps = top_level_steps,
408 };
409 };
410
411411 if (help_menu) {
412412 var w = initStdoutWriter(io);
413413 scanned_config.printUsage(&graph, w) catch |err| switch (err) {
......@@ -467,10 +467,17 @@ pub fn main(init: process.Init.Minimal) !void {
467467 .sub_path = cwd_relative,
468468 } else try install_prefix_path.join(arena, "include");
469469
470 if (true) @panic("TODO");
471
472470 var run: Run = .{
473471 .gpa = gpa,
472 .graph = &graph,
473 .scanned_config = &scanned_config,
474 .install_paths = .{
475 .prefix = install_prefix_path,
476 .lib = install_lib_path,
477 .bin = install_bin_path,
478 .include = install_include_path,
479 },
480 .steps = try arena.alloc(Step, scanned_config.configuration.steps.len),
474481
475482 .available_rss = max_rss,
476483 .max_rss_is_default = false,
......@@ -486,13 +493,6 @@ pub fn main(init: process.Init.Minimal) !void {
486493 .error_style = error_style,
487494 .multiline_errors = multiline_errors,
488495 .summary = summary orelse if (watch or webui_listen != null) .line else .failures,
489
490 .install_paths = .{
491 .prefix = install_prefix_path,
492 .lib = install_lib_path,
493 .bin = install_bin_path,
494 .include = install_include_path,
495 },
496496 };
497497 defer {
498498 run.memory_blocked_steps.deinit(gpa);
......@@ -504,17 +504,16 @@ pub fn main(init: process.Init.Minimal) !void {
504504 run.max_rss_is_default = true;
505505 }
506506
507 prepare(arena, &graph, targets.items, &run) catch |err| switch (err) {
507 run.prepare(step_names.items) catch |err| switch (err) {
508508 error.DependencyLoopDetected, error.InsufficientMemory => {
509 // Perhaps in the future there could be an Advanced Options flag
510 // such as --debug-build-runner-leaks which would make this code
511 // return instead of calling exit.
512509 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
513510 process.exit(1);
514511 },
515512 else => |e| return e,
516513 };
517514
515 if (true) @panic("TODO");
516
518517 var w: Watch = w: {
519518 if (!watch) break :w undefined;
520519 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag});
......@@ -547,7 +546,7 @@ pub fn main(init: process.Init.Minimal) !void {
547546 }) {
548547 if (run.web_server) |*ws| ws.startBuild();
549548
550 try runStepNames(graph, targets.items, main_progress_node, &run, fuzz);
549 try run.makeStepNames(step_names, main_progress_node, fuzz);
551550
552551 if (run.web_server) |*web_server| {
553552 if (fuzz) |mode| if (mode != .forever) fatal(
......@@ -628,6 +627,10 @@ fn countSubProcesses(all_steps: []const *Step) usize {
628627
629628const Run = struct {
630629 gpa: Allocator,
630 graph: *Graph,
631 install_paths: InstallPaths,
632 scanned_config: *const ScannedConfig,
633 steps: []Step,
631634
632635 available_rss: usize,
633636 max_rss_is_default: bool,
......@@ -637,309 +640,331 @@ const Run = struct {
637640 watch: bool,
638641 web_server: if (!builtin.single_threaded) ?WebServer else ?noreturn,
639642 /// Allocated into `gpa`.
640 memory_blocked_steps: std.ArrayList(*Step),
643 memory_blocked_steps: std.ArrayList(Configuration.Step.Index),
641644 /// Allocated into `gpa`.
642 step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),
645 step_stack: std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
643646
644647 error_style: ErrorStyle,
645648 multiline_errors: MultilineErrors,
646649 summary: Summary,
647};
648650
649fn prepare(graph: *Graph, step_names: []const []const u8, run: *Run) !void {
650 const arena = graph.arena;
651 const seed: u32 = graph.random_seed;
652 const gpa = run.gpa;
653 const step_stack = &run.step_stack;
651 const InstallPaths = struct {
652 prefix: Path,
653 lib: Path,
654 bin: Path,
655 include: Path,
656 };
654657
655 if (step_names.len == 0) {
656 try step_stack.put(gpa, graph.configuration.default_step, {});
657 } else {
658 try step_stack.ensureUnusedCapacity(gpa, step_names.len);
659 for (0..step_names.len) |i| {
660 const step_name = step_names[step_names.len - i - 1];
661 const s = run.top_level_steps.get(step_name) orelse {
662 log.info("access the help menu with 'zig build -h'", .{});
663 fatal("no such step: {s}", .{step_name});
664 };
665 step_stack.putAssumeCapacity(&s.step, {});
666 }
658 fn stepByIndex(run: *const Run, i: Configuration.Step.Index) *Step {
659 return &run.steps[@intFromEnum(i)];
667660 }
668661
669 const starting_steps = try arena.dupe(*Step, step_stack.keys());
662 fn prepare(run: *Run, step_names: []const []const u8) !void {
663 const gpa = run.gpa;
664 const graph = run.graph;
665 const arena = graph.arena;
666 const seed: u32 = graph.random_seed;
667 const step_stack = &run.step_stack;
668 const c = &run.scanned_config.configuration;
670669
671 var rng = std.Random.DefaultPrng.init(seed);
672 const rand = rng.random();
673 rand.shuffle(*Step, starting_steps);
670 @memset(run.steps, .{});
674671
675 for (starting_steps) |s| {
676 try constructGraphAndCheckForDependencyLoop(gpa, s, &run.step_stack, rand);
677 }
672 if (step_names.len == 0) {
673 try step_stack.put(gpa, c.default_step, {});
674 } else {
675 try step_stack.ensureUnusedCapacity(gpa, step_names.len);
676 for (0..step_names.len) |i| {
677 const step_name = step_names[step_names.len - i - 1];
678 const s = run.scanned_config.top_level_steps.get(step_name) orelse {
679 log.info("to list available steps: zig build -l", .{});
680 fatal("no such step: {s}", .{step_name});
681 };
682 step_stack.putAssumeCapacity(s, {});
683 }
684 }
678685
679 {
680 // Check that we have enough memory to complete the build.
681 var any_problems = false;
682 var max_needed: usize = 0;
683 for (step_stack.keys()) |s| {
684 if (s.max_rss == 0) continue;
685 max_needed = @max(max_needed, s.max_rss);
686 if (s.max_rss > run.available_rss) {
687 if (run.skip_oom_steps) {
688 s.state = .skipped_oom;
689 for (s.dependants.items) |dependant| {
690 dependant.pending_deps -= 1;
686 const starting_steps = try arena.dupe(Configuration.Step.Index, step_stack.keys());
687
688 var rng = std.Random.DefaultPrng.init(seed);
689 const rand = rng.random();
690 rand.shuffle(Configuration.Step.Index, starting_steps);
691
692 for (starting_steps) |s| {
693 try constructGraphAndCheckForDependencyLoop(gpa, c, run.steps, s, &run.step_stack, rand);
694 }
695
696 {
697 // Check that we have enough memory to complete the build.
698 var any_problems = false;
699 var max_needed: usize = 0;
700 for (step_stack.keys()) |step_index| {
701 const make_step = run.stepByIndex(step_index);
702 const conf_step = step_index.ptr(c);
703 const max_rss = conf_step.max_rss.toBytes();
704 if (max_rss == 0) continue;
705 max_needed = @max(max_needed, max_rss);
706 if (max_rss > run.available_rss) {
707 if (run.skip_oom_steps) {
708 make_step.state = .skipped_oom;
709 for (make_step.dependants.items) |dependant| {
710 dependant.pending_deps -= 1;
711 }
712 } else {
713 log.err("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory", .{
714 conf_step.owner.depPrefixSlice(c),
715 conf_step.name.slice(c),
716 max_rss,
717 run.available_rss,
718 });
719 any_problems = true;
691720 }
692 } else {
693 std.log.err("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory", .{
694 s.owner.dep_prefix, s.name, s.max_rss, run.available_rss,
695 });
696 any_problems = true;
697721 }
698722 }
699 }
700 if (any_problems) {
701 if (run.max_rss_is_default) {
702 std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{
703 max_needed,
704 });
723 if (any_problems) {
724 if (run.max_rss_is_default) {
725 std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{
726 max_needed,
727 });
728 }
729 return error.InsufficientMemory;
705730 }
706 return error.InsufficientMemory;
707731 }
708732 }
709}
710733
711fn runStepNames(
712 graph: *Graph,
713 step_names: []const []const u8,
714 parent_prog_node: std.Progress.Node,
715 run: *Run,
716 fuzz: ?Fuzz.Mode,
717) !void {
718 const gpa = run.gpa;
719 const io = graph.io;
720 const step_stack = &run.step_stack;
734 fn makeStepNames(
735 run: *Run,
736 step_names: []const []const u8,
737 parent_prog_node: std.Progress.Node,
738 fuzz: ?Fuzz.Mode,
739 ) !void {
740 const graph = run.graph;
741 const gpa = run.gpa;
742 const io = graph.io;
743 const step_stack = &run.step_stack;
744 const top_level_steps = &run.scanned_config.top_level_steps;
721745
722 {
723 // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer,
724 // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking
725 // a step is initial when it actually became ready due to an earlier initial step.
726 var initial_set: std.ArrayList(*Step) = .empty;
727 defer initial_set.deinit(gpa);
728 try initial_set.ensureUnusedCapacity(gpa, step_stack.count());
729 for (step_stack.keys()) |s| {
730 if (s.state == .precheck_done and s.pending_deps == 0) {
731 initial_set.appendAssumeCapacity(s);
746 {
747 // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer,
748 // then spawn them. The buffer is so that we don't race with `makeStep` and end up thinking
749 // a step is initial when it actually became ready due to an earlier initial step.
750 var initial_set: std.ArrayList(*Step) = .empty;
751 defer initial_set.deinit(gpa);
752 try initial_set.ensureUnusedCapacity(gpa, step_stack.count());
753 for (step_stack.keys()) |s| {
754 if (s.state == .precheck_done and s.pending_deps == 0) {
755 initial_set.appendAssumeCapacity(s);
756 }
732757 }
733 }
734758
735 const step_prog = parent_prog_node.start("steps", step_stack.count());
736 defer step_prog.end();
759 const step_prog = parent_prog_node.start("steps", step_stack.count());
760 defer step_prog.end();
737761
738 var group: Io.Group = .init;
739 defer group.cancel(io);
740 // Start working on all of the initial steps...
741 for (initial_set.items) |s| try stepReady(&group, s, step_prog, run);
742 // ...and `makeStep` will trigger every other step when their last dependency finishes.
743 try group.await(io);
744 }
762 var group: Io.Group = .init;
763 defer group.cancel(io);
764 // Start working on all of the initial steps...
765 for (initial_set.items) |s| try stepReady(&group, s, step_prog, run);
766 // ...and `makeStep` will trigger every other step when their last dependency finishes.
767 try group.await(io);
768 }
745769
746 assert(run.memory_blocked_steps.items.len == 0);
770 assert(run.memory_blocked_steps.items.len == 0);
747771
748 var test_pass_count: usize = 0;
749 var test_skip_count: usize = 0;
750 var test_fail_count: usize = 0;
751 var test_crash_count: usize = 0;
752 var test_timeout_count: usize = 0;
772 var test_pass_count: usize = 0;
773 var test_skip_count: usize = 0;
774 var test_fail_count: usize = 0;
775 var test_crash_count: usize = 0;
776 var test_timeout_count: usize = 0;
753777
754 var test_count: usize = 0;
778 var test_count: usize = 0;
755779
756 var success_count: usize = 0;
757 var skipped_count: usize = 0;
758 var failure_count: usize = 0;
759 var pending_count: usize = 0;
760 var total_compile_errors: usize = 0;
780 var success_count: usize = 0;
781 var skipped_count: usize = 0;
782 var failure_count: usize = 0;
783 var pending_count: usize = 0;
784 var total_compile_errors: usize = 0;
761785
762 var cleanup_task = io.async(cleanTmpFiles, .{ io, step_stack.keys() });
763 defer cleanup_task.await(io);
786 var cleanup_task = io.async(cleanTmpFiles, .{ io, step_stack.keys() });
787 defer cleanup_task.await(io);
764788
765 for (step_stack.keys()) |s| {
766 test_pass_count += s.test_results.passCount();
767 test_skip_count += s.test_results.skip_count;
768 test_fail_count += s.test_results.fail_count;
769 test_crash_count += s.test_results.crash_count;
770 test_timeout_count += s.test_results.timeout_count;
789 for (step_stack.keys()) |s| {
790 test_pass_count += s.test_results.passCount();
791 test_skip_count += s.test_results.skip_count;
792 test_fail_count += s.test_results.fail_count;
793 test_crash_count += s.test_results.crash_count;
794 test_timeout_count += s.test_results.timeout_count;
771795
772 test_count += s.test_results.test_count;
796 test_count += s.test_results.test_count;
773797
774 switch (s.state) {
775 .precheck_unstarted => unreachable,
776 .precheck_started => unreachable,
777 .precheck_done => unreachable,
778 .dependency_failure => pending_count += 1,
779 .success => success_count += 1,
780 .skipped, .skipped_oom => skipped_count += 1,
781 .failure => {
782 failure_count += 1;
783 const compile_errors_len = s.result_error_bundle.errorMessageCount();
784 if (compile_errors_len > 0) {
785 total_compile_errors += compile_errors_len;
786 }
787 },
788 }
789 }
790
791 if (fuzz) |mode| blk: {
792 switch (builtin.os.tag) {
793 // Current implementation depends on two things that need to be ported to Windows:
794 // * Memory-mapping to share data between the fuzzer and build runner.
795 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
796 // many addresses to source locations).
797 .windows => fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}),
798 else => {},
799 }
800 if (@bitSizeOf(usize) != 64) {
801 // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
802 // being compatible with file system's u64 return value. This is not the case
803 // on 32-bit platforms.
804 // Affects or affected by issues #5185, #22523, and #22464.
805 fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});
798 switch (s.state) {
799 .precheck_unstarted => unreachable,
800 .precheck_started => unreachable,
801 .precheck_done => unreachable,
802 .dependency_failure => pending_count += 1,
803 .success => success_count += 1,
804 .skipped, .skipped_oom => skipped_count += 1,
805 .failure => {
806 failure_count += 1;
807 const compile_errors_len = s.result_error_bundle.errorMessageCount();
808 if (compile_errors_len > 0) {
809 total_compile_errors += compile_errors_len;
810 }
811 },
812 }
806813 }
807814
808 switch (mode) {
809 .forever => break :blk,
810 .limit => {},
811 }
815 if (fuzz) |mode| blk: {
816 switch (builtin.os.tag) {
817 // Current implementation depends on two things that need to be ported to Windows:
818 // * Memory-mapping to share data between the fuzzer and build runner.
819 // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
820 // many addresses to source locations).
821 .windows => fatal("--fuzz not yet implemented for {t}", .{builtin.os.tag}),
822 else => {},
823 }
824 if (@bitSizeOf(usize) != 64) {
825 // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
826 // being compatible with file system's u64 return value. This is not the case
827 // on 32-bit platforms.
828 // Affects or affected by issues #5185, #22523, and #22464.
829 fatal("--fuzz not yet implemented on {d}-bit platforms", .{@bitSizeOf(usize)});
830 }
812831
813 assert(mode == .limit);
814 var f = Fuzz.init(
815 gpa,
816 io,
817 step_stack.keys(),
818 parent_prog_node,
819 mode,
820 ) catch |err| fatal("failed to start fuzzer: {t}", .{err});
821 defer f.deinit();
822
823 f.start();
824 try f.waitAndPrintReport();
825 }
832 switch (mode) {
833 .forever => break :blk,
834 .limit => {},
835 }
826836
827 // Every test has a state
828 assert(test_pass_count + test_skip_count + test_fail_count + test_crash_count + test_timeout_count == test_count);
837 assert(mode == .limit);
838 var f = Fuzz.init(
839 gpa,
840 io,
841 step_stack.keys(),
842 parent_prog_node,
843 mode,
844 ) catch |err| fatal("failed to start fuzzer: {t}", .{err});
845 defer f.deinit();
846
847 f.start();
848 try f.waitAndPrintReport();
849 }
829850
830 if (failure_count == 0) {
831 std.Progress.setStatus(.success);
832 } else {
833 std.Progress.setStatus(.failure);
834 }
851 // Every test has a state
852 assert(test_pass_count + test_skip_count + test_fail_count + test_crash_count + test_timeout_count == test_count);
835853
836 summary: {
837 switch (run.summary) {
838 .all, .new, .line => {},
839 .failures => if (failure_count == 0) break :summary,
840 .none => break :summary,
854 if (failure_count == 0) {
855 std.Progress.setStatus(.success);
856 } else {
857 std.Progress.setStatus(.failure);
841858 }
842859
843 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
844 defer io.unlockStderr();
845 const t = stderr.terminal();
846 const w = &stderr.file_writer.interface;
847
848 const total_count = success_count + failure_count + pending_count + skipped_count;
849 t.setColor(.cyan) catch {};
850 t.setColor(.bold) catch {};
851 w.writeAll("Build Summary: ") catch {};
852 t.setColor(.reset) catch {};
853 w.print("{d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
854 {
855 t.setColor(.dim) catch {};
856 var first = true;
857 if (skipped_count > 0) {
858 w.print("{s}{d} skipped", .{ if (first) " (" else ", ", skipped_count }) catch {};
859 first = false;
860 }
861 if (failure_count > 0) {
862 w.print("{s}{d} failed", .{ if (first) " (" else ", ", failure_count }) catch {};
863 first = false;
860 summary: {
861 switch (run.summary) {
862 .all, .new, .line => {},
863 .failures => if (failure_count == 0) break :summary,
864 .none => break :summary,
864865 }
865 if (!first) w.writeByte(')') catch {};
866 t.setColor(.reset) catch {};
867 }
868866
869 if (test_count > 0) {
870 w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
871 t.setColor(.dim) catch {};
872 var first = true;
873 if (test_skip_count > 0) {
874 w.print("{s}{d} skipped", .{ if (first) " (" else ", ", test_skip_count }) catch {};
875 first = false;
876 }
877 if (test_fail_count > 0) {
878 w.print("{s}{d} failed", .{ if (first) " (" else ", ", test_fail_count }) catch {};
879 first = false;
880 }
881 if (test_crash_count > 0) {
882 w.print("{s}{d} crashed", .{ if (first) " (" else ", ", test_crash_count }) catch {};
883 first = false;
867 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
868 defer io.unlockStderr();
869 const t = stderr.terminal();
870 const w = &stderr.file_writer.interface;
871
872 const total_count = success_count + failure_count + pending_count + skipped_count;
873 t.setColor(.cyan) catch {};
874 t.setColor(.bold) catch {};
875 w.writeAll("Build Summary: ") catch {};
876 t.setColor(.reset) catch {};
877 w.print("{d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
878 {
879 t.setColor(.dim) catch {};
880 var first = true;
881 if (skipped_count > 0) {
882 w.print("{s}{d} skipped", .{ if (first) " (" else ", ", skipped_count }) catch {};
883 first = false;
884 }
885 if (failure_count > 0) {
886 w.print("{s}{d} failed", .{ if (first) " (" else ", ", failure_count }) catch {};
887 first = false;
888 }
889 if (!first) w.writeByte(')') catch {};
890 t.setColor(.reset) catch {};
884891 }
885 if (test_timeout_count > 0) {
886 w.print("{s}{d} timed out", .{ if (first) " (" else ", ", test_timeout_count }) catch {};
887 first = false;
892
893 if (test_count > 0) {
894 w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};
895 t.setColor(.dim) catch {};
896 var first = true;
897 if (test_skip_count > 0) {
898 w.print("{s}{d} skipped", .{ if (first) " (" else ", ", test_skip_count }) catch {};
899 first = false;
900 }
901 if (test_fail_count > 0) {
902 w.print("{s}{d} failed", .{ if (first) " (" else ", ", test_fail_count }) catch {};
903 first = false;
904 }
905 if (test_crash_count > 0) {
906 w.print("{s}{d} crashed", .{ if (first) " (" else ", ", test_crash_count }) catch {};
907 first = false;
908 }
909 if (test_timeout_count > 0) {
910 w.print("{s}{d} timed out", .{ if (first) " (" else ", ", test_timeout_count }) catch {};
911 first = false;
912 }
913 if (!first) w.writeByte(')') catch {};
914 t.setColor(.reset) catch {};
888915 }
889 if (!first) w.writeByte(')') catch {};
890 t.setColor(.reset) catch {};
891 }
892916
893 w.writeAll("\n") catch {};
917 w.writeAll("\n") catch {};
894918
895 if (run.summary == .line) break :summary;
919 if (run.summary == .line) break :summary;
896920
897 // Print a fancy tree with build results.
898 var step_stack_copy = try step_stack.clone(gpa);
899 defer step_stack_copy.deinit(gpa);
921 // Print a fancy tree with build results.
922 var step_stack_copy = try step_stack.clone(gpa);
923 defer step_stack_copy.deinit(gpa);
900924
901 var print_node: PrintNode = .{ .parent = null };
902 if (step_names.len == 0) {
903 print_node.last = true;
904 printTreeStep(graph, graph.default_step, run, t, &print_node, &step_stack_copy) catch {};
905 } else {
906 const last_index = if (run.summary == .all) run.top_level_steps.count() else blk: {
907 var i: usize = step_names.len;
908 while (i > 0) {
909 i -= 1;
910 const step = run.top_level_steps.get(step_names[i]).?.step;
911 const found = switch (run.summary) {
912 .all, .line, .none => unreachable,
913 .failures => step.state != .success,
914 .new => !step.result_cached,
915 };
916 if (found) break :blk i;
925 var print_node: PrintNode = .{ .parent = null };
926 if (step_names.len == 0) {
927 print_node.last = true;
928 printTreeStep(graph, graph.default_step, run, t, &print_node, &step_stack_copy) catch {};
929 } else {
930 const last_index = if (run.summary == .all) top_level_steps.count() else blk: {
931 var i: usize = step_names.len;
932 while (i > 0) {
933 i -= 1;
934 const step = top_level_steps.get(step_names[i]).?.step;
935 const found = switch (run.summary) {
936 .all, .line, .none => unreachable,
937 .failures => step.state != .success,
938 .new => !step.result_cached,
939 };
940 if (found) break :blk i;
941 }
942 break :blk top_level_steps.count();
943 };
944 for (step_names, 0..) |step_name, i| {
945 const tls = top_level_steps.get(step_name).?;
946 print_node.last = i + 1 == last_index;
947 printTreeStep(graph, &tls.step, run, t, &print_node, &step_stack_copy) catch {};
917948 }
918 break :blk run.top_level_steps.count();
919 };
920 for (step_names, 0..) |step_name, i| {
921 const tls = run.top_level_steps.get(step_name).?;
922 print_node.last = i + 1 == last_index;
923 printTreeStep(graph, &tls.step, run, t, &print_node, &step_stack_copy) catch {};
924949 }
950 w.writeByte('\n') catch {};
925951 }
926 w.writeByte('\n') catch {};
927 }
928952
929 if (run.watch or run.web_server != null) return;
953 if (run.watch or run.web_server != null) return;
930954
931 // Perhaps in the future there could be an Advanced Options flag such as
932 // --debug-build-runner-leaks which would make this code return instead of
933 // calling exit.
955 // Perhaps in the future there could be an Advanced Options flag such as
956 // --debug-build-runner-leaks which would make this code return instead of
957 // calling exit.
934958
935 const code: u8 = code: {
936 if (failure_count == 0) break :code 0; // success
937 if (run.error_style.verboseContext()) break :code 1; // failure; print build command
938 break :code 2; // failure; do not print build command
939 };
940 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
941 process.exit(code);
942}
959 const code: u8 = code: {
960 if (failure_count == 0) break :code 0; // success
961 if (run.error_style.verboseContext()) break :code 1; // failure; print build command
962 break :code 2; // failure; do not print build command
963 };
964 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
965 process.exit(code);
966 }
967};
943968
944969const PrintNode = struct {
945970 parent: ?*PrintNode,
......@@ -1221,40 +1246,47 @@ fn printTreeStep(
12211246/// random order
12221247fn constructGraphAndCheckForDependencyLoop(
12231248 gpa: Allocator,
1224 s: *Step,
1225 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
1249 c: *const Configuration,
1250 steps: []Step,
1251 step_index: Configuration.Step.Index,
1252 step_stack: *std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
12261253 rand: std.Random,
1227) !void {
1254) error{ DependencyLoopDetected, OutOfMemory }!void {
1255 const s: *Step = &steps[@intFromEnum(step_index)];
12281256 switch (s.state) {
12291257 .precheck_started => {
1230 std.debug.print("dependency loop detected:\n {s}\n", .{s.name});
1258 log.err("dependency loop detected: {s}", .{step_index.ptr(c).name.slice(c)});
12311259 return error.DependencyLoopDetected;
12321260 },
12331261 .precheck_unstarted => {
12341262 s.state = .precheck_started;
12351263
1236 try step_stack.ensureUnusedCapacity(gpa, s.dependencies.items.len);
1264 const step = step_index.ptr(c);
1265 const dependencies = step.deps.slice(c);
1266 try step_stack.ensureUnusedCapacity(gpa, dependencies.len);
12371267
12381268 // We dupe to avoid shuffling the steps in the summary, it depends
1239 // on s.dependencies' order.
1240 const deps = try gpa.dupe(*Step, s.dependencies.items);
1269 // on dependencies' order.
1270 const deps = try gpa.dupe(Configuration.Step.Index, dependencies);
12411271 defer gpa.free(deps);
12421272
1243 rand.shuffle(*Step, deps);
1273 rand.shuffle(Configuration.Step.Index, deps);
12441274
12451275 for (deps) |dep| {
1276 const dep_step: *Step = &steps[@intFromEnum(dep)];
12461277 try step_stack.put(gpa, dep, {});
1247 try dep.dependants.append(gpa, s);
1248 constructGraphAndCheckForDependencyLoop(gpa, dep, step_stack, rand) catch |err| {
1249 if (err == error.DependencyLoopDetected) {
1250 std.debug.print(" {s}\n", .{s.name});
1251 }
1252 return err;
1278 try dep_step.dependants.append(gpa, s);
1279 constructGraphAndCheckForDependencyLoop(gpa, c, steps, dep, step_stack, rand) catch |err| switch (err) {
1280 error.DependencyLoopDetected => {
1281 log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});
1282 return err;
1283 },
1284 else => return err,
12531285 };
12541286 }
12551287
12561288 s.state = .precheck_done;
1257 s.pending_deps = @intCast(s.dependencies.items.len);
1289 s.pending_deps = @intCast(dependencies.len);
12581290 },
12591291 .precheck_done => {},
12601292
......@@ -1492,8 +1524,7 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
14921524
14931525fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
14941526 return nextArg(args, idx) orelse {
1495 log.info("access the help menu with \"zig build -h\"", .{});
1496 fatal("expected argument after {q}", .{args[idx.* - 1]});
1527 fatalWithHint("expected argument after {q}", .{args[idx.* - 1]});
14971528 };
14981529}
14991530
......@@ -1532,7 +1563,7 @@ const MultilineErrors = enum { indent, newline, none };
15321563const Summary = enum { all, new, failures, line, none };
15331564
15341565fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
1535 log.info("access the help menu with 'zig build -h'", .{});
1566 log.info("to access the help menu: zig build -h", .{});
15361567 fatal(f, args);
15371568}
15381569
......@@ -1547,13 +1578,6 @@ fn cleanTmpFiles(io: Io, steps: []const *Step) void {
15471578 }
15481579}
15491580
1550const InstallPaths = struct {
1551 prefix: Path,
1552 lib: Path,
1553 bin: Path,
1554 include: Path,
1555};
1556
15571581var stdio_buffer_allocation: [256]u8 = undefined;
15581582var stdout_writer_allocation: Io.File.Writer = undefined;
15591583
......@@ -1564,17 +1588,20 @@ fn initStdoutWriter(io: Io) *Writer {
15641588
15651589const ScannedConfig = struct {
15661590 configuration: Configuration,
1567 top_level_steps: []const Configuration.Step.Index,
1591 top_level_steps: std.StringArrayHashMapUnmanaged(Configuration.Step.Index),
15681592
15691593 fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
1594 const c = &sc.configuration;
15701595 var serializer: std.zon.Serializer = .{ .writer = w };
15711596 var s = try serializer.beginStruct(.{});
15721597
1573 try s.field("default_step", @intFromEnum(sc.configuration.default_step), .{});
1598 try s.field("default_step", @intFromEnum(c.default_step), .{});
15741599 {
1575 var tuple = try s.beginTupleField("top_level_steps", .{});
1576 for (sc.top_level_steps) |step| try tuple.field(@intFromEnum(step), .{});
1577 try tuple.end();
1600 var ss = try s.beginStructField("top_level_steps", .{});
1601 for (sc.top_level_steps.keys(), sc.top_level_steps.values()) |name, step| {
1602 try ss.field(name, @intFromEnum(step), .{});
1603 }
1604 try ss.end();
15781605 }
15791606
15801607 try s.end();
......@@ -1583,9 +1610,8 @@ const ScannedConfig = struct {
15831610 fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
15841611 const arena = graph.arena;
15851612 const c = &sc.configuration;
1586 for (sc.top_level_steps) |step_index| {
1613 for (sc.top_level_steps.keys(), sc.top_level_steps.values()) |name, step_index| {
15871614 const step = step_index.ptr(c);
1588 const name = step.name.slice(c);
15891615 const decorated_name = if (step_index == c.default_step)
15901616 try fmt.allocPrint(arena, "{s} (default)", .{name})
15911617 else
......@@ -1679,8 +1705,8 @@ const ScannedConfig = struct {
16791705 try w.writeAll(
16801706 \\
16811707 \\General Options:
1682 \\ -h, --help Print this help and exit
1683 \\ -l, --list-steps Print available steps
1708 \\ -h, --help Print this help to stdout and exit
1709 \\ -l, --list-steps Print available steps to stdout and exit
16841710 \\
16851711 \\ -p, --prefix [path] Where to install files (default: zig-out)
16861712 \\ --prefix-lib-dir [path] Where to install libraries
lib/compiler/maker/Package.zig deleted-30
......@@ -1,30 +0,0 @@
1const Package = @This();
2
3const std = @import("std");
4
5install_prefix: []const u8,
6install_path: []const u8,
7dest_dir: ?[]const u8,
8lib_dir: []const u8,
9exe_dir: []const u8,
10h_dir: []const u8,
11/// Path to the directory containing build.zig.
12build_root: std.Build.Cache.Path,
13
14fn determineAndApplyInstallPrefix(p: *Package) error{OutOfMemory}!void {
15 // Create an installation directory local to this package. This will be used when
16 // dependant packages require a standard prefix, such as include directories for C headers.
17 var hash = p.graph.cache.hash;
18 // Random bytes to make unique. Refresh this with new random bytes when
19 // implementation is modified in a non-backwards-compatible way.
20 hash.add(@as(u32, 0xd8cb0056));
21 hash.addBytes(p.dep_prefix);
22
23 var wyhash = std.hash.Wyhash.init(0);
24 hashUserInputOptionsMap(p.allocator, p.user_input_options, &wyhash);
25 hash.add(wyhash.final());
26
27 const digest = hash.final();
28 const install_prefix = try p.cache_root.join(p.allocator, &.{ "i", &digest });
29 p.resolveInstallPrefix(install_prefix, .{});
30}
lib/compiler/maker/Step.zig+48-74
......@@ -1,19 +1,27 @@
1//! The state that maker needs in order to process a step.
12const Step = @This();
23
4const builtin = @import("builtin");
5
36const std = @import("std");
4const Io = std.Io;
57const Allocator = std.mem.Allocator;
68const Cache = std.Build.Cache;
9const Io = std.Io;
10const LazyPath = std.Build.Configuration.LazyPath;
11const Package = std.Build.Configuration.Package;
12const Path = std.Build.Cache.Path;
713const assert = std.debug.assert;
814
915const WebServer = @import("WebServer.zig");
1016
11pub const Compile = @import("Step/Compile.zig");
12pub const Run = @import("Step/Run.zig");
17pub const Compile = void; // @import("Step/Compile.zig");
18pub const Run = void; // @import("Step/Run.zig");
1319
14state: State,
15makeFn: MakeFn,
16dependants: std.ArrayList(*Step),
20/// Avoid false sharing.
21_: void align(std.atomic.cache_line) = {},
22
23state: State = .precheck_unstarted,
24dependants: std.ArrayList(*Step) = .empty,
1725/// Collects the set of files that retrigger this step to run.
1826///
1927/// This is used by the build system's implementation of `--watch` but it can
......@@ -23,20 +31,19 @@ dependants: std.ArrayList(*Step),
2331/// Populated within `make`. Implementation may choose to clear and repopulate,
2432/// retain previous value, or update.
2533inputs: Inputs = .init,
26pending_deps: u32,
34pending_deps: u32 = undefined,
2735
28result_error_msgs: std.ArrayList([]const u8),
29result_error_bundle: std.zig.ErrorBundle,
30result_stderr: []const u8,
31result_cached: bool,
32result_duration_ns: ?u64,
36result_error_msgs: std.ArrayList([]const u8) = .empty,
37result_error_bundle: std.zig.ErrorBundle = .empty,
38result_stderr: []const u8 = "",
39result_cached: bool = false,
40result_duration_ns: ?u64 = null,
3341/// 0 means unavailable or not reported.
34result_peak_rss: usize,
42result_peak_rss: usize = 0,
3543/// If the step is failed and this field is populated, this is the command which failed.
3644/// This field may be populated even if the step succeeded.
37result_failed_command: ?[]const u8,
38test_results: TestResults,
39
45result_failed_command: ?[]const u8 = null,
46test_results: TestResults = .{},
4047
4148pub const State = enum {
4249 precheck_unstarted,
......@@ -172,18 +179,6 @@ pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!voi
172179 }
173180}
174181
175fn makeNoOp(step: *Step, options: MakeOptions) anyerror!void {
176 _ = options;
177
178 var all_cached = true;
179
180 for (step.dependencies.items) |dep| {
181 all_cached = all_cached and dep.result_cached;
182 }
183
184 step.result_cached = all_cached;
185}
186
187182/// Implementation detail of file watching. Prepares the step for being re-evaluated.
188183/// Returns `true` if the step was newly invalidated, `false` if it was already invalidated.
189184pub fn invalidateResult(step: *Step, gpa: Allocator) bool {
......@@ -233,7 +228,7 @@ pub fn captureChildProcess(
233228 s.result_failed_command = try allocPrintCmd(gpa, .inherit, null, argv);
234229
235230 try handleChildProcUnsupported(s);
236 try handleVerbose(s.owner, .inherit, argv);
231 try handleVerbose(s, .inherit, argv);
237232
238233 const result = std.process.run(arena, io, .{
239234 .argv = argv,
......@@ -340,7 +335,7 @@ pub fn evalZigProcess(
340335 assert(argv.len != 0);
341336
342337 try handleChildProcUnsupported(s);
343 try handleVerbose(s.owner, .inherit, argv);
338 try handleVerbose(s, .inherit, argv);
344339
345340 const zp = try gpa.create(ZigProcess);
346341 defer if (!watch) gpa.destroy(zp);
......@@ -399,11 +394,11 @@ pub fn evalZigProcess(
399394}
400395
401396/// Wrapper around `Io.Dir.updateFile` that handles verbose and error output.
402pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !Io.Dir.PrevStatus {
397pub fn installFile(s: *Step, src_lazy_path: LazyPath, dest_path: []const u8) !Io.Dir.PrevStatus {
403398 const b = s.owner;
404399 const io = b.graph.io;
405400 const src_path = src_lazy_path.getPath3(b, s);
406 try handleVerbose(b, .inherit, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
401 try handleVerbose(s, .inherit, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
407402 return Io.Dir.updateFile(src_path.root_dir.handle, io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err|
408403 return s.fail("unable to update file from '{f}' to '{s}': {t}", .{ src_path, dest_path, err });
409404}
......@@ -412,7 +407,7 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u
412407pub fn installDir(s: *Step, dest_path: []const u8) !Io.Dir.CreatePathStatus {
413408 const b = s.owner;
414409 const io = b.graph.io;
415 try handleVerbose(b, .inherit, &.{ "install", "-d", dest_path });
410 try handleVerbose(s, .inherit, &.{ "install", "-d", dest_path });
416411 return Io.Dir.cwd().createDirPathStatus(io, dest_path, .default_dir) catch |err|
417412 return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err });
418413}
......@@ -567,29 +562,21 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {
567562}
568563
569564pub fn handleVerbose(
570 b: *Build,
571 cwd: std.process.Child.Cwd,
572 argv: []const []const u8,
573) error{OutOfMemory}!void {
574 return handleVerbose2(b, cwd, null, argv);
575}
576
577pub fn handleVerbose2(
578 b: *Build,
565 s: *Step,
566 arena: Allocator,
579567 cwd: std.process.Child.Cwd,
580568 opt_env: ?*const std.process.Environ.Map,
581569 argv: []const []const u8,
582570) error{OutOfMemory}!void {
583 if (b.verbose) {
584 const graph = b.graph;
585 // Intention of verbose is to print all sub-process command lines to
586 // stderr before spawning them.
587 const text = try allocPrintCmd(b.allocator, cwd, if (opt_env) |env| .{
588 .child = env,
589 .parent = &graph.environ_map,
590 } else null, argv);
591 std.debug.print("{s}\n", .{text});
592 }
571 if (!s.verbose) return;
572 const graph = s.graph;
573 // Intention of verbose is to print all sub-process command lines to
574 // stderr before spawning them.
575 const text = try allocPrintCmd(arena, cwd, if (opt_env) |env| .{
576 .child = env,
577 .parent = &graph.environ_map,
578 } else null, argv);
579 std.log.scoped(.verbose).info("{s}", .{text});
593580}
594581
595582/// Asserts that the caller has already populated `s.result_failed_command`.
......@@ -688,7 +675,7 @@ fn setWatchInputsFromManifest(s: *Step, man: *Cache.Manifest) !void {
688675}
689676
690677/// For steps that have a single input that never changes when re-running `make`.
691pub fn singleUnchangingWatchInput(step: *Step, lazy_path: Build.LazyPath) Allocator.Error!void {
678pub fn singleUnchangingWatchInput(step: *Step, lazy_path: LazyPath) Allocator.Error!void {
692679 if (!step.inputs.populated()) try step.addWatchInput(lazy_path);
693680}
694681
......@@ -698,7 +685,7 @@ pub fn clearWatchInputs(step: *Step) void {
698685}
699686
700687/// Places a *file* dependency on the path.
701pub fn addWatchInput(step: *Step, lazy_file: Build.LazyPath) Allocator.Error!void {
688pub fn addWatchInput(step: *Step, lazy_file: LazyPath) Allocator.Error!void {
702689 switch (lazy_file) {
703690 .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),
704691 .dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),
......@@ -723,7 +710,7 @@ pub fn addWatchInput(step: *Step, lazy_file: Build.LazyPath) Allocator.Error!voi
723710/// Paths derived from this directory should also be manually added via
724711/// `addDirectoryWatchInputFromPath` if and only if this function returns
725712/// `true`.
726pub fn addDirectoryWatchInput(step: *Step, lazy_directory: Build.LazyPath) Allocator.Error!bool {
713pub fn addDirectoryWatchInput(step: *Step, lazy_directory: LazyPath) Allocator.Error!bool {
727714 switch (lazy_directory) {
728715 .src_path => |src_path| try addDirectoryWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),
729716 .dependency => |d| try addDirectoryWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),
......@@ -744,26 +731,26 @@ pub fn addDirectoryWatchInput(step: *Step, lazy_directory: Build.LazyPath) Alloc
744731
745732/// Any changes inside the directory will trigger invalidation.
746733///
747/// See also `addDirectoryWatchInput` which takes a `Build.LazyPath` instead.
734/// See also `addDirectoryWatchInput` which takes a `LazyPath` instead.
748735///
749736/// This function should only be called when it has been verified that the
750737/// dependency on `path` is not already accounted for by a `Step` dependency.
751738/// In other words, before calling this function, first check that the
752/// `Build.LazyPath` which this `path` is derived from is not `generated`.
739/// `LazyPath` which this `path` is derived from is not `generated`.
753740pub fn addDirectoryWatchInputFromPath(step: *Step, path: Cache.Path) !void {
754741 return addWatchInputFromPath(step, path, ".");
755742}
756743
757fn addWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void {
744fn addWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {
758745 return addWatchInputFromPath(step, .{
759 .root_dir = builder.build_root,
746 .root_dir = package.build_root,
760747 .sub_path = std.fs.path.dirname(sub_path) orelse "",
761748 }, std.fs.path.basename(sub_path));
762749}
763750
764fn addDirectoryWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void {
751fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {
765752 return addDirectoryWatchInputFromPath(step, .{
766 .root_dir = builder.build_root,
753 .root_dir = package.build_root,
767754 .sub_path = sub_path,
768755 });
769756}
......@@ -847,16 +834,3 @@ pub fn allocPrintCmd(
847834 }
848835 return aw.toOwnedSlice();
849836}
850
851pub fn getInstallPath(b: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
852 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
853 const base_dir = switch (dir) {
854 .prefix => b.install_path,
855 .bin => b.exe_dir,
856 .lib => b.lib_dir,
857 .header => b.h_dir,
858 .custom => |p| b.pathJoin(&.{ b.install_path, p }),
859 };
860 return b.pathResolve(&.{ base_dir, dest_rel_path });
861}
862
lib/std/Build.zig+4
......@@ -112,6 +112,10 @@ pub const Graph = struct {
112112 /// respects the '--color' flag.
113113 stderr_mode: ?Io.Terminal.Mode = null,
114114 release_mode: ReleaseMode = .off,
115 /// Whether the user passed in "--" arguments. They can be added to a child
116 /// process via `Step.Run` API but cannot be observed in the configure
117 /// phase.
118 have_run_args: bool = false,
115119};
116120
117121const AvailableDeps = []const struct { []const u8, []const u8 };
lib/std/Build/Step/Run.zig+2
......@@ -141,6 +141,8 @@ pub const Arg = union(enum) {
141141 bytes: []u8,
142142 output_file: *Output,
143143 output_directory: *Output,
144 /// The arguments passed after "--" on the "zig build" CLI.
145 cli_rest_positionals,
144146};
145147
146148pub const PrefixedArtifact = struct {
lib/std/zig/Configuration.zig+21-11
......@@ -413,6 +413,7 @@ pub const AvailableOption = extern struct {
413413
414414pub const Step = extern struct {
415415 name: String,
416 owner: Package.Index,
416417 deps: Deps,
417418 max_rss: MaxRss,
418419 /// Points into `extra` for step-specific data. First element has flags
......@@ -534,6 +535,7 @@ pub const Step = extern struct {
534535 bytes,
535536 output_file,
536537 output_directory,
538 cli_rest_positionals,
537539 };
538540 };
539541
......@@ -841,7 +843,7 @@ pub const LazyPath = enum(u32) {
841843
842844 pub const SourcePath = struct {
843845 flags: Flags,
844 owner: Package,
846 owner: Package.Index,
845847 sub_path: String,
846848
847849 pub const Flags = packed struct(u32) {
......@@ -877,16 +879,19 @@ pub const LazyPath = enum(u32) {
877879 };
878880};
879881
880/// It's an OptionalString which points to the package hash.
881pub const Package = enum(u32) {
882 root = maxInt(u32),
883 _,
882pub const Package = struct {
883 dep_prefix: String,
884 hash: String,
884885
885 pub fn fromHash(hash: String) Package {
886 const result: Package = @enumFromInt(@intFromEnum(hash));
887 assert(result != .root);
888 return result;
889 }
886 pub const Index = enum(u32) {
887 root = maxInt(u32),
888 _,
889
890 pub fn depPrefixSlice(i: Index, c: *const Configuration) [:0]const u8 {
891 if (i == .root) return "";
892 return extraData(c, Package, @intFromEnum(i)).dep_prefix.slice(c);
893 }
894 };
890895};
891896
892897/// Trailing:
......@@ -900,7 +905,7 @@ pub const Package = enum(u32) {
900905pub const Module = struct {
901906 flags: Flags,
902907 flags2: Flags2,
903 owner: Package,
908 owner: Package.Index,
904909 root_source_file: OptionalLazyPath,
905910 import_table: ImportTable,
906911 resolved_target: ResolvedTarget.OptionalIndex,
......@@ -1048,6 +1053,11 @@ pub const ImportTable = enum(u32) {
10481053/// elements is `Step.Index` per count.
10491054pub const Deps = enum(u32) {
10501055 _,
1056
1057 pub fn slice(deps: Deps, c: *const Configuration) []Step.Index {
1058 const len = c.extra[@intFromEnum(deps)];
1059 return @ptrCast(c.extra[@intFromEnum(deps) + 1 ..][0..len]);
1060 }
10511061};
10521062
10531063/// Points into `extra`, where the first element is count of strings, following