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 {...@@ -186,6 +186,8 @@ pub fn main(init: process.Init.Minimal) !void {
186 // but it is handled by the parent process. The build runner186 // but it is handled by the parent process. The build runner
187 // only sees this flag.187 // only sees this flag.
188 graph.system_package_mode = true;188 graph.system_package_mode = true;
189 } else if (mem.eql(u8, arg, "--have-run-args")) {
190 graph.have_run_args = true;
189 } else {191 } else {
190 fatalWithHint("unrecognized argument: '{s}'", .{arg});192 fatalWithHint("unrecognized argument: '{s}'", .{arg});
191 }193 }
...@@ -226,13 +228,69 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -226,13 +228,69 @@ pub fn main(init: process.Init.Minimal) !void {
226 process.exit(0);228 process.exit(0);
227}229}
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
229fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {288fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
230 const graph = b.graph;289 const graph = b.graph;
231 const arena = graph.arena;290 const arena = graph.arena;
232 const gpa = wc.gpa;291 const gpa = wc.gpa;
233292
234 var module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty;293 var s: Serialize = .{ .wc = wc, .arena = arena };
235 defer module_map.deinit(gpa);
236294
237 // Starting from all top-level steps in `b`, traverse the entire step graph295 // Starting from all top-level steps in `b`, traverse the entire step graph
238 // and add all step dependencies implied by module graphs.296 // 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 {...@@ -267,6 +325,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
267 try wc.steps.ensureTotalCapacity(gpa, step_map.entries.capacity);325 try wc.steps.ensureTotalCapacity(gpa, step_map.entries.capacity);
268 wc.steps.appendAssumeCapacity(.{326 wc.steps.appendAssumeCapacity(.{
269 .name = try wc.addString(step.name),327 .name = try wc.addString(step.name),
328 .owner = try s.builderToPackage(step.owner),
270 .deps = deps,329 .deps = deps,
271 .max_rss = .fromBytes(step.max_rss),330 .max_rss = .fromBytes(step.max_rss),
272 .extra_index = switch (step.tag) {331 .extra_index = switch (step.tag) {
...@@ -367,7 +426,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -367,7 +426,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
367 .install_name = c.install_name != null,426 .install_name = c.install_name != null,
368 .entitlements = c.entitlements != null,427 .entitlements = c.entitlements != null,
369 },428 },
370 .root_module = try addModule(wc, &module_map, c.root_module),429 .root_module = try addModule(&s, c.root_module),
371 .root_name = try wc.addString(c.name),430 .root_name = try wc.addString(c.name),
372 }));431 }));
373432
...@@ -383,13 +442,13 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -383,13 +442,13 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
383 },442 },
384 .dest_dir = try addInstallDir(wc, ia.dest_dir),443 .dest_dir = try addInstallDir(wc, ia.dest_dir),
385 .dest_sub_path = try wc.addString(ia.dest_sub_path),444 .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),
387 .implib_dir = try addInstallDir(wc, ia.implib_dir),446 .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),
389 .pdb_dir = try addInstallDir(wc, ia.pdb_dir),448 .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),
391 .h_dir = try addInstallDir(wc, ia.h_dir),450 .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),
393 .artifact = stepIndex(&step_map, &ia.artifact.step),452 .artifact = stepIndex(&step_map, &ia.artifact.step),
394 }));453 }));
395 },454 },
...@@ -440,7 +499,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -440,7 +499,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
440 },499 },
441 .file_inputs_len = @intCast(run.file_inputs.items.len),500 .file_inputs_len = @intCast(run.file_inputs.items.len),
442 .args_len = @intCast(run.argv.items.len),501 .args_len = @intCast(run.argv.items.len),
443 .cwd = try addOptionalLazyPath(wc, run.cwd),502 .cwd = try s.addOptionalLazyPath(run.cwd),
444 .captured_stdout = captured_stdout,503 .captured_stdout = captured_stdout,
445 .captured_stderr = captured_stderr,504 .captured_stderr = captured_stderr,
446 }));505 }));
...@@ -469,13 +528,11 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -469,13 +528,11 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
469 });528 });
470}529}
471530
472fn addModule(531fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index {
473 wc: *Configuration.Wip,532 if (s.module_map.get(m)) |index| return index;
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;
478533
534 const wc = s.wc;
535 const arena = s.arena;
479 const gpa = wc.gpa;536 const gpa = wc.gpa;
480 const import_table: Configuration.ImportTable = @enumFromInt(wc.extra.items.len);537 const import_table: Configuration.ImportTable = @enumFromInt(wc.extra.items.len);
481 const import_table_extra_len = 1 + 2 * m.import_table.entries.len;538 const import_table_extra_len = 1 + 2 * m.import_table.entries.len;
...@@ -494,7 +551,7 @@ fn addModule(...@@ -494,7 +551,7 @@ fn addModule(
494 @intFromEnum(import_table) + 1 + m.import_table.entries.len..,551 @intFromEnum(import_table) + 1 + m.import_table.entries.len..,
495 ) |dep, extra_index| {552 ) |dep, extra_index| {
496 log.err("TODO module dependencies can be cyclic", .{});553 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));
498 }555 }
499556
500 const module_index: Configuration.Module.Index = @enumFromInt(try wc.addExtra(@as(Configuration.Module, .{557 const module_index: Configuration.Module.Index = @enumFromInt(try wc.addExtra(@as(Configuration.Module, .{
...@@ -528,15 +585,15 @@ fn addModule(...@@ -528,15 +585,15 @@ fn addModule(
528 .link_libcpp = .init(m.strip),585 .link_libcpp = .init(m.strip),
529 .no_builtin = .init(m.strip),586 .no_builtin = .init(m.strip),
530 },587 },
531 .owner = try builderToPackage(wc, m.owner),588 .owner = try s.builderToPackage(m.owner),
532 .root_source_file = try addOptionalLazyPath(wc, m.root_source_file),589 .root_source_file = try s.addOptionalLazyPath(m.root_source_file),
533 .import_table = import_table,590 .import_table = import_table,
534 .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),591 .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),
535 })));592 })));
536593
537 log.err("TODO serialize the trailing Module data", .{});594 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
541 return module_index;598 return module_index;
542}599}
...@@ -553,46 +610,6 @@ fn addOptionalResolvedTarget(...@@ -553,46 +610,6 @@ fn addOptionalResolvedTarget(
553 })));610 })));
554}611}
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
596fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir {613fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDir {
597 switch (install_dir orelse return .none) {614 switch (install_dir orelse return .none) {
598 .prefix => return .prefix,615 .prefix => return .prefix,
...@@ -665,9 +682,7 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {...@@ -665,9 +682,7 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
665682
666fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {683fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
667 return nextArg(args, idx) orelse {684 return nextArg(args, idx) orelse {
668 fatal("expected argument after {q}\n access the help menu with \"zig build -h\"", .{685 fatalWithHint("expected argument after: {s}", .{args[idx.* - 1]});
669 args[idx.* - 1],
670 });
671 };686 };
672}687}
673688
...@@ -700,7 +715,8 @@ const MultilineErrors = enum { indent, newline, none };...@@ -700,7 +715,8 @@ const MultilineErrors = enum { indent, newline, none };
700const Summary = enum { all, new, failures, line, none };715const Summary = enum { all, new, failures, line, none };
701716
702fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {717fn 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);
704}720}
705721
706fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void {722fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void {
...@@ -725,7 +741,7 @@ fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration...@@ -725,7 +741,7 @@ fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration
725 });741 });
726 }742 }
727 if (bad) {743 if (bad) {
728 log.info("access the help menu with \"zig build -h\"", .{});744 log.info("help menu contains available options: zig build -h", .{});
729 process.exit(1);745 process.exit(1);
730 }746 }
731}747}
lib/compiler/maker.zig+352-326
...@@ -17,7 +17,7 @@ const process = std.process;...@@ -17,7 +17,7 @@ const process = std.process;
1717
18const Fuzz = @import("maker/Fuzz.zig");18const Fuzz = @import("maker/Fuzz.zig");
19const Graph = @import("maker/Graph.zig");19const Graph = @import("maker/Graph.zig");
20const Step = void; // @import("maker/Step.zig");20const Step = @import("maker/Step.zig");
21const Watch = @import("maker/Watch.zig");21const Watch = @import("maker/Watch.zig");
22const WebServer = @import("maker/WebServer.zig");22const WebServer = @import("maker/WebServer.zig");
2323
...@@ -100,8 +100,8 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -100,8 +100,8 @@ pub fn main(init: process.Init.Minimal) !void {
100 graph.cache.addPrefix(global_cache_directory);100 graph.cache.addPrefix(global_cache_directory);
101 graph.cache.hash.addBytes(builtin.zig_version_string);101 graph.cache.hash.addBytes(builtin.zig_version_string);
102102
103 var targets = std.array_list.Managed([]const u8).init(arena);103 var step_names: std.ArrayList([]const u8) = .empty;
104 var debug_log_scopes = std.array_list.Managed([]const u8).init(arena);104 var debug_log_scopes: std.ArrayList([]const u8) = .empty;
105 var help_menu = false;105 var help_menu = false;
106 var steps_menu = false;106 var steps_menu = false;
107 var print_configuration = false;107 var print_configuration = false;
...@@ -151,29 +151,6 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -151,29 +151,6 @@ pub fn main(init: process.Init.Minimal) !void {
151 }151 }
152 }152 }
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
177 while (nextArg(args, &arg_idx)) |arg| {154 while (nextArg(args, &arg_idx)) |arg| {
178 if (mem.startsWith(u8, arg, "-")) {155 if (mem.startsWith(u8, arg, "-")) {
179 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {156 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
...@@ -291,7 +268,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -291,7 +268,7 @@ pub fn main(init: process.Init.Minimal) !void {
291 };268 };
292 } else if (mem.eql(u8, arg, "--debug-log")) {269 } else if (mem.eql(u8, arg, "--debug-log")) {
293 const next_arg = nextArgOrFatal(args, &arg_idx);270 const next_arg = nextArgOrFatal(args, &arg_idx);
294 try debug_log_scopes.append(next_arg);271 try debug_log_scopes.append(arena, next_arg);
295 } else if (mem.eql(u8, arg, "--debug-pkg-config")) {272 } else if (mem.eql(u8, arg, "--debug-pkg-config")) {
296 debug_pkg_config = true;273 debug_pkg_config = true;
297 } else if (mem.eql(u8, arg, "--debug-rt")) {274 } else if (mem.eql(u8, arg, "--debug-rt")) {
...@@ -395,7 +372,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -395,7 +372,7 @@ pub fn main(init: process.Init.Minimal) !void {
395 fatalWithHint("unrecognized argument: '{s}'", .{arg});372 fatalWithHint("unrecognized argument: '{s}'", .{arg});
396 }373 }
397 } else {374 } else {
398 try targets.append(arg);375 try step_names.append(arena, arg);
399 }376 }
400 }377 }
401378
...@@ -408,6 +385,29 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -408,6 +385,29 @@ pub fn main(init: process.Init.Minimal) !void {
408 .off => .no_color,385 .off => .no_color,
409 };386 };
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
411 if (help_menu) {411 if (help_menu) {
412 var w = initStdoutWriter(io);412 var w = initStdoutWriter(io);
413 scanned_config.printUsage(&graph, w) catch |err| switch (err) {413 scanned_config.printUsage(&graph, w) catch |err| switch (err) {
...@@ -467,10 +467,17 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -467,10 +467,17 @@ pub fn main(init: process.Init.Minimal) !void {
467 .sub_path = cwd_relative,467 .sub_path = cwd_relative,
468 } else try install_prefix_path.join(arena, "include");468 } else try install_prefix_path.join(arena, "include");
469469
470 if (true) @panic("TODO");
471
472 var run: Run = .{470 var run: Run = .{
473 .gpa = gpa,471 .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
475 .available_rss = max_rss,482 .available_rss = max_rss,
476 .max_rss_is_default = false,483 .max_rss_is_default = false,
...@@ -486,13 +493,6 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -486,13 +493,6 @@ pub fn main(init: process.Init.Minimal) !void {
486 .error_style = error_style,493 .error_style = error_style,
487 .multiline_errors = multiline_errors,494 .multiline_errors = multiline_errors,
488 .summary = summary orelse if (watch or webui_listen != null) .line else .failures,495 .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 },
496 };496 };
497 defer {497 defer {
498 run.memory_blocked_steps.deinit(gpa);498 run.memory_blocked_steps.deinit(gpa);
...@@ -504,17 +504,16 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -504,17 +504,16 @@ pub fn main(init: process.Init.Minimal) !void {
504 run.max_rss_is_default = true;504 run.max_rss_is_default = true;
505 }505 }
506506
507 prepare(arena, &graph, targets.items, &run) catch |err| switch (err) {507 run.prepare(step_names.items) catch |err| switch (err) {
508 error.DependencyLoopDetected, error.InsufficientMemory => {508 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.
512 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};509 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
513 process.exit(1);510 process.exit(1);
514 },511 },
515 else => |e| return e,512 else => |e| return e,
516 };513 };
517514
515 if (true) @panic("TODO");
516
518 var w: Watch = w: {517 var w: Watch = w: {
519 if (!watch) break :w undefined;518 if (!watch) break :w undefined;
520 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag});519 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 {...@@ -547,7 +546,7 @@ pub fn main(init: process.Init.Minimal) !void {
547 }) {546 }) {
548 if (run.web_server) |*ws| ws.startBuild();547 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
552 if (run.web_server) |*web_server| {551 if (run.web_server) |*web_server| {
553 if (fuzz) |mode| if (mode != .forever) fatal(552 if (fuzz) |mode| if (mode != .forever) fatal(
...@@ -628,6 +627,10 @@ fn countSubProcesses(all_steps: []const *Step) usize {...@@ -628,6 +627,10 @@ fn countSubProcesses(all_steps: []const *Step) usize {
628627
629const Run = struct {628const Run = struct {
630 gpa: Allocator,629 gpa: Allocator,
630 graph: *Graph,
631 install_paths: InstallPaths,
632 scanned_config: *const ScannedConfig,
633 steps: []Step,
631634
632 available_rss: usize,635 available_rss: usize,
633 max_rss_is_default: bool,636 max_rss_is_default: bool,
...@@ -637,309 +640,331 @@ const Run = struct {...@@ -637,309 +640,331 @@ const Run = struct {
637 watch: bool,640 watch: bool,
638 web_server: if (!builtin.single_threaded) ?WebServer else ?noreturn,641 web_server: if (!builtin.single_threaded) ?WebServer else ?noreturn,
639 /// Allocated into `gpa`.642 /// Allocated into `gpa`.
640 memory_blocked_steps: std.ArrayList(*Step),643 memory_blocked_steps: std.ArrayList(Configuration.Step.Index),
641 /// Allocated into `gpa`.644 /// Allocated into `gpa`.
642 step_stack: std.AutoArrayHashMapUnmanaged(*Step, void),645 step_stack: std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
643646
644 error_style: ErrorStyle,647 error_style: ErrorStyle,
645 multiline_errors: MultilineErrors,648 multiline_errors: MultilineErrors,
646 summary: Summary,649 summary: Summary,
647};
648650
649fn prepare(graph: *Graph, step_names: []const []const u8, run: *Run) !void {651 const InstallPaths = struct {
650 const arena = graph.arena;652 prefix: Path,
651 const seed: u32 = graph.random_seed;653 lib: Path,
652 const gpa = run.gpa;654 bin: Path,
653 const step_stack = &run.step_stack;655 include: Path,
656 };
654657
655 if (step_names.len == 0) {658 fn stepByIndex(run: *const Run, i: Configuration.Step.Index) *Step {
656 try step_stack.put(gpa, graph.configuration.default_step, {});659 return &run.steps[@intFromEnum(i)];
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 }
667 }660 }
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);670 @memset(run.steps, .{});
672 const rand = rng.random();
673 rand.shuffle(*Step, starting_steps);
674671
675 for (starting_steps) |s| {672 if (step_names.len == 0) {
676 try constructGraphAndCheckForDependencyLoop(gpa, s, &run.step_stack, rand);673 try step_stack.put(gpa, c.default_step, {});
677 }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 {686 const starting_steps = try arena.dupe(Configuration.Step.Index, step_stack.keys());
680 // Check that we have enough memory to complete the build.687
681 var any_problems = false;688 var rng = std.Random.DefaultPrng.init(seed);
682 var max_needed: usize = 0;689 const rand = rng.random();
683 for (step_stack.keys()) |s| {690 rand.shuffle(Configuration.Step.Index, starting_steps);
684 if (s.max_rss == 0) continue;691
685 max_needed = @max(max_needed, s.max_rss);692 for (starting_steps) |s| {
686 if (s.max_rss > run.available_rss) {693 try constructGraphAndCheckForDependencyLoop(gpa, c, run.steps, s, &run.step_stack, rand);
687 if (run.skip_oom_steps) {694 }
688 s.state = .skipped_oom;695
689 for (s.dependants.items) |dependant| {696 {
690 dependant.pending_deps -= 1;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;
691 }720 }
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;
697 }721 }
698 }722 }
699 }723 if (any_problems) {
700 if (any_problems) {724 if (run.max_rss_is_default) {
701 if (run.max_rss_is_default) {725 std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{
702 std.log.info("use --maxrss {d} to proceed, risking system memory exhaustion", .{726 max_needed,
703 max_needed,727 });
704 });728 }
729 return error.InsufficientMemory;
705 }730 }
706 return error.InsufficientMemory;
707 }731 }
708 }732 }
709}
710733
711fn runStepNames(734 fn makeStepNames(
712 graph: *Graph,735 run: *Run,
713 step_names: []const []const u8,736 step_names: []const []const u8,
714 parent_prog_node: std.Progress.Node,737 parent_prog_node: std.Progress.Node,
715 run: *Run,738 fuzz: ?Fuzz.Mode,
716 fuzz: ?Fuzz.Mode,739 ) !void {
717) !void {740 const graph = run.graph;
718 const gpa = run.gpa;741 const gpa = run.gpa;
719 const io = graph.io;742 const io = graph.io;
720 const step_stack = &run.step_stack;743 const step_stack = &run.step_stack;
744 const top_level_steps = &run.scanned_config.top_level_steps;
721745
722 {746 {
723 // Collect the initial set of tasks (those with no outstanding dependencies) into a buffer,747 // 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 thinking748 // 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.749 // a step is initial when it actually became ready due to an earlier initial step.
726 var initial_set: std.ArrayList(*Step) = .empty;750 var initial_set: std.ArrayList(*Step) = .empty;
727 defer initial_set.deinit(gpa);751 defer initial_set.deinit(gpa);
728 try initial_set.ensureUnusedCapacity(gpa, step_stack.count());752 try initial_set.ensureUnusedCapacity(gpa, step_stack.count());
729 for (step_stack.keys()) |s| {753 for (step_stack.keys()) |s| {
730 if (s.state == .precheck_done and s.pending_deps == 0) {754 if (s.state == .precheck_done and s.pending_deps == 0) {
731 initial_set.appendAssumeCapacity(s);755 initial_set.appendAssumeCapacity(s);
756 }
732 }757 }
733 }
734758
735 const step_prog = parent_prog_node.start("steps", step_stack.count());759 const step_prog = parent_prog_node.start("steps", step_stack.count());
736 defer step_prog.end();760 defer step_prog.end();
737761
738 var group: Io.Group = .init;762 var group: Io.Group = .init;
739 defer group.cancel(io);763 defer group.cancel(io);
740 // Start working on all of the initial steps...764 // Start working on all of the initial steps...
741 for (initial_set.items) |s| try stepReady(&group, s, step_prog, run);765 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.766 // ...and `makeStep` will trigger every other step when their last dependency finishes.
743 try group.await(io);767 try group.await(io);
744 }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;772 var test_pass_count: usize = 0;
749 var test_skip_count: usize = 0;773 var test_skip_count: usize = 0;
750 var test_fail_count: usize = 0;774 var test_fail_count: usize = 0;
751 var test_crash_count: usize = 0;775 var test_crash_count: usize = 0;
752 var test_timeout_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;780 var success_count: usize = 0;
757 var skipped_count: usize = 0;781 var skipped_count: usize = 0;
758 var failure_count: usize = 0;782 var failure_count: usize = 0;
759 var pending_count: usize = 0;783 var pending_count: usize = 0;
760 var total_compile_errors: usize = 0;784 var total_compile_errors: usize = 0;
761785
762 var cleanup_task = io.async(cleanTmpFiles, .{ io, step_stack.keys() });786 var cleanup_task = io.async(cleanTmpFiles, .{ io, step_stack.keys() });
763 defer cleanup_task.await(io);787 defer cleanup_task.await(io);
764788
765 for (step_stack.keys()) |s| {789 for (step_stack.keys()) |s| {
766 test_pass_count += s.test_results.passCount();790 test_pass_count += s.test_results.passCount();
767 test_skip_count += s.test_results.skip_count;791 test_skip_count += s.test_results.skip_count;
768 test_fail_count += s.test_results.fail_count;792 test_fail_count += s.test_results.fail_count;
769 test_crash_count += s.test_results.crash_count;793 test_crash_count += s.test_results.crash_count;
770 test_timeout_count += s.test_results.timeout_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) {798 switch (s.state) {
775 .precheck_unstarted => unreachable,799 .precheck_unstarted => unreachable,
776 .precheck_started => unreachable,800 .precheck_started => unreachable,
777 .precheck_done => unreachable,801 .precheck_done => unreachable,
778 .dependency_failure => pending_count += 1,802 .dependency_failure => pending_count += 1,
779 .success => success_count += 1,803 .success => success_count += 1,
780 .skipped, .skipped_oom => skipped_count += 1,804 .skipped, .skipped_oom => skipped_count += 1,
781 .failure => {805 .failure => {
782 failure_count += 1;806 failure_count += 1;
783 const compile_errors_len = s.result_error_bundle.errorMessageCount();807 const compile_errors_len = s.result_error_bundle.errorMessageCount();
784 if (compile_errors_len > 0) {808 if (compile_errors_len > 0) {
785 total_compile_errors += compile_errors_len;809 total_compile_errors += compile_errors_len;
786 }810 }
787 },811 },
788 }812 }
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)});
806 }813 }
807814
808 switch (mode) {815 if (fuzz) |mode| blk: {
809 .forever => break :blk,816 switch (builtin.os.tag) {
810 .limit => {},817 // Current implementation depends on two things that need to be ported to Windows:
811 }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);832 switch (mode) {
814 var f = Fuzz.init(833 .forever => break :blk,
815 gpa,834 .limit => {},
816 io,835 }
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 }
826836
827 // Every test has a state837 assert(mode == .limit);
828 assert(test_pass_count + test_skip_count + test_fail_count + test_crash_count + test_timeout_count == test_count);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) {851 // Every test has a state
831 std.Progress.setStatus(.success);852 assert(test_pass_count + test_skip_count + test_fail_count + test_crash_count + test_timeout_count == test_count);
832 } else {
833 std.Progress.setStatus(.failure);
834 }
835853
836 summary: {854 if (failure_count == 0) {
837 switch (run.summary) {855 std.Progress.setStatus(.success);
838 .all, .new, .line => {},856 } else {
839 .failures => if (failure_count == 0) break :summary,857 std.Progress.setStatus(.failure);
840 .none => break :summary,
841 }858 }
842859
843 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);860 summary: {
844 defer io.unlockStderr();861 switch (run.summary) {
845 const t = stderr.terminal();862 .all, .new, .line => {},
846 const w = &stderr.file_writer.interface;863 .failures => if (failure_count == 0) break :summary,
847864 .none => break :summary,
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;
864 }865 }
865 if (!first) w.writeByte(')') catch {};
866 t.setColor(.reset) catch {};
867 }
868866
869 if (test_count > 0) {867 const stderr = try io.lockStderr(&stdio_buffer_allocation, graph.stderr_mode);
870 w.print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {};868 defer io.unlockStderr();
871 t.setColor(.dim) catch {};869 const t = stderr.terminal();
872 var first = true;870 const w = &stderr.file_writer.interface;
873 if (test_skip_count > 0) {871
874 w.print("{s}{d} skipped", .{ if (first) " (" else ", ", test_skip_count }) catch {};872 const total_count = success_count + failure_count + pending_count + skipped_count;
875 first = false;873 t.setColor(.cyan) catch {};
876 }874 t.setColor(.bold) catch {};
877 if (test_fail_count > 0) {875 w.writeAll("Build Summary: ") catch {};
878 w.print("{s}{d} failed", .{ if (first) " (" else ", ", test_fail_count }) catch {};876 t.setColor(.reset) catch {};
879 first = false;877 w.print("{d}/{d} steps succeeded", .{ success_count, total_count }) catch {};
880 }878 {
881 if (test_crash_count > 0) {879 t.setColor(.dim) catch {};
882 w.print("{s}{d} crashed", .{ if (first) " (" else ", ", test_crash_count }) catch {};880 var first = true;
883 first = false;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 {};
884 }891 }
885 if (test_timeout_count > 0) {892
886 w.print("{s}{d} timed out", .{ if (first) " (" else ", ", test_timeout_count }) catch {};893 if (test_count > 0) {
887 first = false;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 {};
888 }915 }
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.921 // Print a fancy tree with build results.
898 var step_stack_copy = try step_stack.clone(gpa);922 var step_stack_copy = try step_stack.clone(gpa);
899 defer step_stack_copy.deinit(gpa);923 defer step_stack_copy.deinit(gpa);
900924
901 var print_node: PrintNode = .{ .parent = null };925 var print_node: PrintNode = .{ .parent = null };
902 if (step_names.len == 0) {926 if (step_names.len == 0) {
903 print_node.last = true;927 print_node.last = true;
904 printTreeStep(graph, graph.default_step, run, t, &print_node, &step_stack_copy) catch {};928 printTreeStep(graph, graph.default_step, run, t, &print_node, &step_stack_copy) catch {};
905 } else {929 } else {
906 const last_index = if (run.summary == .all) run.top_level_steps.count() else blk: {930 const last_index = if (run.summary == .all) top_level_steps.count() else blk: {
907 var i: usize = step_names.len;931 var i: usize = step_names.len;
908 while (i > 0) {932 while (i > 0) {
909 i -= 1;933 i -= 1;
910 const step = run.top_level_steps.get(step_names[i]).?.step;934 const step = top_level_steps.get(step_names[i]).?.step;
911 const found = switch (run.summary) {935 const found = switch (run.summary) {
912 .all, .line, .none => unreachable,936 .all, .line, .none => unreachable,
913 .failures => step.state != .success,937 .failures => step.state != .success,
914 .new => !step.result_cached,938 .new => !step.result_cached,
915 };939 };
916 if (found) break :blk i;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 {};
917 }948 }
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 {};
924 }949 }
950 w.writeByte('\n') catch {};
925 }951 }
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 as955 // 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 of956 // --debug-build-runner-leaks which would make this code return instead of
933 // calling exit.957 // calling exit.
934958
935 const code: u8 = code: {959 const code: u8 = code: {
936 if (failure_count == 0) break :code 0; // success960 if (failure_count == 0) break :code 0; // success
937 if (run.error_style.verboseContext()) break :code 1; // failure; print build command961 if (run.error_style.verboseContext()) break :code 1; // failure; print build command
938 break :code 2; // failure; do not print build command962 break :code 2; // failure; do not print build command
939 };963 };
940 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};964 _ = io.lockStderr(&.{}, graph.stderr_mode) catch {};
941 process.exit(code);965 process.exit(code);
942}966 }
967};
943968
944const PrintNode = struct {969const PrintNode = struct {
945 parent: ?*PrintNode,970 parent: ?*PrintNode,
...@@ -1221,40 +1246,47 @@ fn printTreeStep(...@@ -1221,40 +1246,47 @@ fn printTreeStep(
1221/// random order1246/// random order
1222fn constructGraphAndCheckForDependencyLoop(1247fn constructGraphAndCheckForDependencyLoop(
1223 gpa: Allocator,1248 gpa: Allocator,
1224 s: *Step,1249 c: *const Configuration,
1225 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),1250 steps: []Step,
1251 step_index: Configuration.Step.Index,
1252 step_stack: *std.AutoArrayHashMapUnmanaged(Configuration.Step.Index, void),
1226 rand: std.Random,1253 rand: std.Random,
1227) !void {1254) error{ DependencyLoopDetected, OutOfMemory }!void {
1255 const s: *Step = &steps[@intFromEnum(step_index)];
1228 switch (s.state) {1256 switch (s.state) {
1229 .precheck_started => {1257 .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)});
1231 return error.DependencyLoopDetected;1259 return error.DependencyLoopDetected;
1232 },1260 },
1233 .precheck_unstarted => {1261 .precheck_unstarted => {
1234 s.state = .precheck_started;1262 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
1238 // We dupe to avoid shuffling the steps in the summary, it depends1268 // We dupe to avoid shuffling the steps in the summary, it depends
1239 // on s.dependencies' order.1269 // on dependencies' order.
1240 const deps = try gpa.dupe(*Step, s.dependencies.items);1270 const deps = try gpa.dupe(Configuration.Step.Index, dependencies);
1241 defer gpa.free(deps);1271 defer gpa.free(deps);
12421272
1243 rand.shuffle(*Step, deps);1273 rand.shuffle(Configuration.Step.Index, deps);
12441274
1245 for (deps) |dep| {1275 for (deps) |dep| {
1276 const dep_step: *Step = &steps[@intFromEnum(dep)];
1246 try step_stack.put(gpa, dep, {});1277 try step_stack.put(gpa, dep, {});
1247 try dep.dependants.append(gpa, s);1278 try dep_step.dependants.append(gpa, s);
1248 constructGraphAndCheckForDependencyLoop(gpa, dep, step_stack, rand) catch |err| {1279 constructGraphAndCheckForDependencyLoop(gpa, c, steps, dep, step_stack, rand) catch |err| switch (err) {
1249 if (err == error.DependencyLoopDetected) {1280 error.DependencyLoopDetected => {
1250 std.debug.print(" {s}\n", .{s.name});1281 log.info("needed by: {s}", .{step_index.ptr(c).name.slice(c)});
1251 }1282 return err;
1252 return err;1283 },
1284 else => return err,
1253 };1285 };
1254 }1286 }
12551287
1256 s.state = .precheck_done;1288 s.state = .precheck_done;
1257 s.pending_deps = @intCast(s.dependencies.items.len);1289 s.pending_deps = @intCast(dependencies.len);
1258 },1290 },
1259 .precheck_done => {},1291 .precheck_done => {},
12601292
...@@ -1492,8 +1524,7 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {...@@ -1492,8 +1524,7 @@ fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
14921524
1493fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {1525fn nextArgOrFatal(args: []const [:0]const u8, idx: *usize) [:0]const u8 {
1494 return nextArg(args, idx) orelse {1526 return nextArg(args, idx) orelse {
1495 log.info("access the help menu with \"zig build -h\"", .{});1527 fatalWithHint("expected argument after {q}", .{args[idx.* - 1]});
1496 fatal("expected argument after {q}", .{args[idx.* - 1]});
1497 };1528 };
1498}1529}
14991530
...@@ -1532,7 +1563,7 @@ const MultilineErrors = enum { indent, newline, none };...@@ -1532,7 +1563,7 @@ const MultilineErrors = enum { indent, newline, none };
1532const Summary = enum { all, new, failures, line, none };1563const Summary = enum { all, new, failures, line, none };
15331564
1534fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {1565fn 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", .{});
1536 fatal(f, args);1567 fatal(f, args);
1537}1568}
15381569
...@@ -1547,13 +1578,6 @@ fn cleanTmpFiles(io: Io, steps: []const *Step) void {...@@ -1547,13 +1578,6 @@ fn cleanTmpFiles(io: Io, steps: []const *Step) void {
1547 }1578 }
1548}1579}
15491580
1550const InstallPaths = struct {
1551 prefix: Path,
1552 lib: Path,
1553 bin: Path,
1554 include: Path,
1555};
1556
1557var stdio_buffer_allocation: [256]u8 = undefined;1581var stdio_buffer_allocation: [256]u8 = undefined;
1558var stdout_writer_allocation: Io.File.Writer = undefined;1582var stdout_writer_allocation: Io.File.Writer = undefined;
15591583
...@@ -1564,17 +1588,20 @@ fn initStdoutWriter(io: Io) *Writer {...@@ -1564,17 +1588,20 @@ fn initStdoutWriter(io: Io) *Writer {
15641588
1565const ScannedConfig = struct {1589const ScannedConfig = struct {
1566 configuration: Configuration,1590 configuration: Configuration,
1567 top_level_steps: []const Configuration.Step.Index,1591 top_level_steps: std.StringArrayHashMapUnmanaged(Configuration.Step.Index),
15681592
1569 fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {1593 fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
1594 const c = &sc.configuration;
1570 var serializer: std.zon.Serializer = .{ .writer = w };1595 var serializer: std.zon.Serializer = .{ .writer = w };
1571 var s = try serializer.beginStruct(.{});1596 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), .{});
1574 {1599 {
1575 var tuple = try s.beginTupleField("top_level_steps", .{});1600 var ss = try s.beginStructField("top_level_steps", .{});
1576 for (sc.top_level_steps) |step| try tuple.field(@intFromEnum(step), .{});1601 for (sc.top_level_steps.keys(), sc.top_level_steps.values()) |name, step| {
1577 try tuple.end();1602 try ss.field(name, @intFromEnum(step), .{});
1603 }
1604 try ss.end();
1578 }1605 }
15791606
1580 try s.end();1607 try s.end();
...@@ -1583,9 +1610,8 @@ const ScannedConfig = struct {...@@ -1583,9 +1610,8 @@ const ScannedConfig = struct {
1583 fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {1610 fn printSteps(sc: *const ScannedConfig, graph: *Graph, w: *Writer) !void {
1584 const arena = graph.arena;1611 const arena = graph.arena;
1585 const c = &sc.configuration;1612 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| {
1587 const step = step_index.ptr(c);1614 const step = step_index.ptr(c);
1588 const name = step.name.slice(c);
1589 const decorated_name = if (step_index == c.default_step)1615 const decorated_name = if (step_index == c.default_step)
1590 try fmt.allocPrint(arena, "{s} (default)", .{name})1616 try fmt.allocPrint(arena, "{s} (default)", .{name})
1591 else1617 else
...@@ -1679,8 +1705,8 @@ const ScannedConfig = struct {...@@ -1679,8 +1705,8 @@ const ScannedConfig = struct {
1679 try w.writeAll(1705 try w.writeAll(
1680 \\1706 \\
1681 \\General Options:1707 \\General Options:
1682 \\ -h, --help Print this help and exit1708 \\ -h, --help Print this help to stdout and exit
1683 \\ -l, --list-steps Print available steps1709 \\ -l, --list-steps Print available steps to stdout and exit
1684 \\1710 \\
1685 \\ -p, --prefix [path] Where to install files (default: zig-out)1711 \\ -p, --prefix [path] Where to install files (default: zig-out)
1686 \\ --prefix-lib-dir [path] Where to install libraries1712 \\ --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,19 +1,27 @@
1//! The state that maker needs in order to process a step.
1const Step = @This();2const Step = @This();
23
4const builtin = @import("builtin");
5
3const std = @import("std");6const std = @import("std");
4const Io = std.Io;
5const Allocator = std.mem.Allocator;7const Allocator = std.mem.Allocator;
6const Cache = std.Build.Cache;8const 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;
7const assert = std.debug.assert;13const assert = std.debug.assert;
814
9const WebServer = @import("WebServer.zig");15const WebServer = @import("WebServer.zig");
1016
11pub const Compile = @import("Step/Compile.zig");17pub const Compile = void; // @import("Step/Compile.zig");
12pub const Run = @import("Step/Run.zig");18pub const Run = void; // @import("Step/Run.zig");
1319
14state: State,20/// Avoid false sharing.
15makeFn: MakeFn,21_: void align(std.atomic.cache_line) = {},
16dependants: std.ArrayList(*Step),22
23state: State = .precheck_unstarted,
24dependants: std.ArrayList(*Step) = .empty,
17/// Collects the set of files that retrigger this step to run.25/// Collects the set of files that retrigger this step to run.
18///26///
19/// This is used by the build system's implementation of `--watch` but it can27/// This is used by the build system's implementation of `--watch` but it can
...@@ -23,20 +31,19 @@ dependants: std.ArrayList(*Step),...@@ -23,20 +31,19 @@ dependants: std.ArrayList(*Step),
23/// Populated within `make`. Implementation may choose to clear and repopulate,31/// Populated within `make`. Implementation may choose to clear and repopulate,
24/// retain previous value, or update.32/// retain previous value, or update.
25inputs: Inputs = .init,33inputs: Inputs = .init,
26pending_deps: u32,34pending_deps: u32 = undefined,
2735
28result_error_msgs: std.ArrayList([]const u8),36result_error_msgs: std.ArrayList([]const u8) = .empty,
29result_error_bundle: std.zig.ErrorBundle,37result_error_bundle: std.zig.ErrorBundle = .empty,
30result_stderr: []const u8,38result_stderr: []const u8 = "",
31result_cached: bool,39result_cached: bool = false,
32result_duration_ns: ?u64,40result_duration_ns: ?u64 = null,
33/// 0 means unavailable or not reported.41/// 0 means unavailable or not reported.
34result_peak_rss: usize,42result_peak_rss: usize = 0,
35/// If the step is failed and this field is populated, this is the command which failed.43/// If the step is failed and this field is populated, this is the command which failed.
36/// This field may be populated even if the step succeeded.44/// This field may be populated even if the step succeeded.
37result_failed_command: ?[]const u8,45result_failed_command: ?[]const u8 = null,
38test_results: TestResults,46test_results: TestResults = .{},
39
4047
41pub const State = enum {48pub const State = enum {
42 precheck_unstarted,49 precheck_unstarted,
...@@ -172,18 +179,6 @@ pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!voi...@@ -172,18 +179,6 @@ pub fn make(s: *Step, options: MakeOptions) error{ MakeFailed, MakeSkipped }!voi
172 }179 }
173}180}
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
187/// Implementation detail of file watching. Prepares the step for being re-evaluated.182/// Implementation detail of file watching. Prepares the step for being re-evaluated.
188/// Returns `true` if the step was newly invalidated, `false` if it was already invalidated.183/// Returns `true` if the step was newly invalidated, `false` if it was already invalidated.
189pub fn invalidateResult(step: *Step, gpa: Allocator) bool {184pub fn invalidateResult(step: *Step, gpa: Allocator) bool {
...@@ -233,7 +228,7 @@ pub fn captureChildProcess(...@@ -233,7 +228,7 @@ pub fn captureChildProcess(
233 s.result_failed_command = try allocPrintCmd(gpa, .inherit, null, argv);228 s.result_failed_command = try allocPrintCmd(gpa, .inherit, null, argv);
234229
235 try handleChildProcUnsupported(s);230 try handleChildProcUnsupported(s);
236 try handleVerbose(s.owner, .inherit, argv);231 try handleVerbose(s, .inherit, argv);
237232
238 const result = std.process.run(arena, io, .{233 const result = std.process.run(arena, io, .{
239 .argv = argv,234 .argv = argv,
...@@ -340,7 +335,7 @@ pub fn evalZigProcess(...@@ -340,7 +335,7 @@ pub fn evalZigProcess(
340 assert(argv.len != 0);335 assert(argv.len != 0);
341336
342 try handleChildProcUnsupported(s);337 try handleChildProcUnsupported(s);
343 try handleVerbose(s.owner, .inherit, argv);338 try handleVerbose(s, .inherit, argv);
344339
345 const zp = try gpa.create(ZigProcess);340 const zp = try gpa.create(ZigProcess);
346 defer if (!watch) gpa.destroy(zp);341 defer if (!watch) gpa.destroy(zp);
...@@ -399,11 +394,11 @@ pub fn evalZigProcess(...@@ -399,11 +394,11 @@ pub fn evalZigProcess(
399}394}
400395
401/// Wrapper around `Io.Dir.updateFile` that handles verbose and error output.396/// 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 {
403 const b = s.owner;398 const b = s.owner;
404 const io = b.graph.io;399 const io = b.graph.io;
405 const src_path = src_lazy_path.getPath3(b, s);400 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 });
407 return Io.Dir.updateFile(src_path.root_dir.handle, io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err|402 return Io.Dir.updateFile(src_path.root_dir.handle, io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err|
408 return s.fail("unable to update file from '{f}' to '{s}': {t}", .{ src_path, dest_path, err });403 return s.fail("unable to update file from '{f}' to '{s}': {t}", .{ src_path, dest_path, err });
409}404}
...@@ -412,7 +407,7 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u...@@ -412,7 +407,7 @@ pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u
412pub fn installDir(s: *Step, dest_path: []const u8) !Io.Dir.CreatePathStatus {407pub fn installDir(s: *Step, dest_path: []const u8) !Io.Dir.CreatePathStatus {
413 const b = s.owner;408 const b = s.owner;
414 const io = b.graph.io;409 const io = b.graph.io;
415 try handleVerbose(b, .inherit, &.{ "install", "-d", dest_path });410 try handleVerbose(s, .inherit, &.{ "install", "-d", dest_path });
416 return Io.Dir.cwd().createDirPathStatus(io, dest_path, .default_dir) catch |err|411 return Io.Dir.cwd().createDirPathStatus(io, dest_path, .default_dir) catch |err|
417 return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err });412 return s.fail("unable to create dir '{s}': {t}", .{ dest_path, err });
418}413}
...@@ -567,29 +562,21 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {...@@ -567,29 +562,21 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {
567}562}
568563
569pub fn handleVerbose(564pub fn handleVerbose(
570 b: *Build,565 s: *Step,
571 cwd: std.process.Child.Cwd,566 arena: Allocator,
572 argv: []const []const u8,
573) error{OutOfMemory}!void {
574 return handleVerbose2(b, cwd, null, argv);
575}
576
577pub fn handleVerbose2(
578 b: *Build,
579 cwd: std.process.Child.Cwd,567 cwd: std.process.Child.Cwd,
580 opt_env: ?*const std.process.Environ.Map,568 opt_env: ?*const std.process.Environ.Map,
581 argv: []const []const u8,569 argv: []const []const u8,
582) error{OutOfMemory}!void {570) error{OutOfMemory}!void {
583 if (b.verbose) {571 if (!s.verbose) return;
584 const graph = b.graph;572 const graph = s.graph;
585 // Intention of verbose is to print all sub-process command lines to573 // Intention of verbose is to print all sub-process command lines to
586 // stderr before spawning them.574 // stderr before spawning them.
587 const text = try allocPrintCmd(b.allocator, cwd, if (opt_env) |env| .{575 const text = try allocPrintCmd(arena, cwd, if (opt_env) |env| .{
588 .child = env,576 .child = env,
589 .parent = &graph.environ_map,577 .parent = &graph.environ_map,
590 } else null, argv);578 } else null, argv);
591 std.debug.print("{s}\n", .{text});579 std.log.scoped(.verbose).info("{s}", .{text});
592 }
593}580}
594581
595/// Asserts that the caller has already populated `s.result_failed_command`.582/// Asserts that the caller has already populated `s.result_failed_command`.
...@@ -688,7 +675,7 @@ fn setWatchInputsFromManifest(s: *Step, man: *Cache.Manifest) !void {...@@ -688,7 +675,7 @@ fn setWatchInputsFromManifest(s: *Step, man: *Cache.Manifest) !void {
688}675}
689676
690/// For steps that have a single input that never changes when re-running `make`.677/// 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 {
692 if (!step.inputs.populated()) try step.addWatchInput(lazy_path);679 if (!step.inputs.populated()) try step.addWatchInput(lazy_path);
693}680}
694681
...@@ -698,7 +685,7 @@ pub fn clearWatchInputs(step: *Step) void {...@@ -698,7 +685,7 @@ pub fn clearWatchInputs(step: *Step) void {
698}685}
699686
700/// Places a *file* dependency on the path.687/// 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 {
702 switch (lazy_file) {689 switch (lazy_file) {
703 .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),690 .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),
704 .dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),691 .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...@@ -723,7 +710,7 @@ pub fn addWatchInput(step: *Step, lazy_file: Build.LazyPath) Allocator.Error!voi
723/// Paths derived from this directory should also be manually added via710/// Paths derived from this directory should also be manually added via
724/// `addDirectoryWatchInputFromPath` if and only if this function returns711/// `addDirectoryWatchInputFromPath` if and only if this function returns
725/// `true`.712/// `true`.
726pub fn addDirectoryWatchInput(step: *Step, lazy_directory: Build.LazyPath) Allocator.Error!bool {713pub fn addDirectoryWatchInput(step: *Step, lazy_directory: LazyPath) Allocator.Error!bool {
727 switch (lazy_directory) {714 switch (lazy_directory) {
728 .src_path => |src_path| try addDirectoryWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),715 .src_path => |src_path| try addDirectoryWatchInputFromBuilder(step, src_path.owner, src_path.sub_path),
729 .dependency => |d| try addDirectoryWatchInputFromBuilder(step, d.dependency.builder, d.sub_path),716 .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...@@ -744,26 +731,26 @@ pub fn addDirectoryWatchInput(step: *Step, lazy_directory: Build.LazyPath) Alloc
744731
745/// Any changes inside the directory will trigger invalidation.732/// Any changes inside the directory will trigger invalidation.
746///733///
747/// See also `addDirectoryWatchInput` which takes a `Build.LazyPath` instead.734/// See also `addDirectoryWatchInput` which takes a `LazyPath` instead.
748///735///
749/// This function should only be called when it has been verified that the736/// This function should only be called when it has been verified that the
750/// dependency on `path` is not already accounted for by a `Step` dependency.737/// dependency on `path` is not already accounted for by a `Step` dependency.
751/// In other words, before calling this function, first check that the738/// 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`.
753pub fn addDirectoryWatchInputFromPath(step: *Step, path: Cache.Path) !void {740pub fn addDirectoryWatchInputFromPath(step: *Step, path: Cache.Path) !void {
754 return addWatchInputFromPath(step, path, ".");741 return addWatchInputFromPath(step, path, ".");
755}742}
756743
757fn addWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void {744fn addWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {
758 return addWatchInputFromPath(step, .{745 return addWatchInputFromPath(step, .{
759 .root_dir = builder.build_root,746 .root_dir = package.build_root,
760 .sub_path = std.fs.path.dirname(sub_path) orelse "",747 .sub_path = std.fs.path.dirname(sub_path) orelse "",
761 }, std.fs.path.basename(sub_path));748 }, std.fs.path.basename(sub_path));
762}749}
763750
764fn addDirectoryWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void {751fn addDirectoryWatchInputFromBuilder(step: *Step, package: Package, sub_path: []const u8) !void {
765 return addDirectoryWatchInputFromPath(step, .{752 return addDirectoryWatchInputFromPath(step, .{
766 .root_dir = builder.build_root,753 .root_dir = package.build_root,
767 .sub_path = sub_path,754 .sub_path = sub_path,
768 });755 });
769}756}
...@@ -847,16 +834,3 @@ pub fn allocPrintCmd(...@@ -847,16 +834,3 @@ pub fn allocPrintCmd(
847 }834 }
848 return aw.toOwnedSlice();835 return aw.toOwnedSlice();
849}836}
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 {...@@ -112,6 +112,10 @@ pub const Graph = struct {
112 /// respects the '--color' flag.112 /// respects the '--color' flag.
113 stderr_mode: ?Io.Terminal.Mode = null,113 stderr_mode: ?Io.Terminal.Mode = null,
114 release_mode: ReleaseMode = .off,114 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,
115};119};
116120
117const AvailableDeps = []const struct { []const u8, []const u8 };121const AvailableDeps = []const struct { []const u8, []const u8 };
lib/std/Build/Step/Run.zig+2
...@@ -141,6 +141,8 @@ pub const Arg = union(enum) {...@@ -141,6 +141,8 @@ pub const Arg = union(enum) {
141 bytes: []u8,141 bytes: []u8,
142 output_file: *Output,142 output_file: *Output,
143 output_directory: *Output,143 output_directory: *Output,
144 /// The arguments passed after "--" on the "zig build" CLI.
145 cli_rest_positionals,
144};146};
145147
146pub const PrefixedArtifact = struct {148pub const PrefixedArtifact = struct {
lib/std/zig/Configuration.zig+21-11
...@@ -413,6 +413,7 @@ pub const AvailableOption = extern struct {...@@ -413,6 +413,7 @@ pub const AvailableOption = extern struct {
413413
414pub const Step = extern struct {414pub const Step = extern struct {
415 name: String,415 name: String,
416 owner: Package.Index,
416 deps: Deps,417 deps: Deps,
417 max_rss: MaxRss,418 max_rss: MaxRss,
418 /// Points into `extra` for step-specific data. First element has flags419 /// Points into `extra` for step-specific data. First element has flags
...@@ -534,6 +535,7 @@ pub const Step = extern struct {...@@ -534,6 +535,7 @@ pub const Step = extern struct {
534 bytes,535 bytes,
535 output_file,536 output_file,
536 output_directory,537 output_directory,
538 cli_rest_positionals,
537 };539 };
538 };540 };
539541
...@@ -841,7 +843,7 @@ pub const LazyPath = enum(u32) {...@@ -841,7 +843,7 @@ pub const LazyPath = enum(u32) {
841843
842 pub const SourcePath = struct {844 pub const SourcePath = struct {
843 flags: Flags,845 flags: Flags,
844 owner: Package,846 owner: Package.Index,
845 sub_path: String,847 sub_path: String,
846848
847 pub const Flags = packed struct(u32) {849 pub const Flags = packed struct(u32) {
...@@ -877,16 +879,19 @@ pub const LazyPath = enum(u32) {...@@ -877,16 +879,19 @@ pub const LazyPath = enum(u32) {
877 };879 };
878};880};
879881
880/// It's an OptionalString which points to the package hash.882pub const Package = struct {
881pub const Package = enum(u32) {883 dep_prefix: String,
882 root = maxInt(u32),884 hash: String,
883 _,
884885
885 pub fn fromHash(hash: String) Package {886 pub const Index = enum(u32) {
886 const result: Package = @enumFromInt(@intFromEnum(hash));887 root = maxInt(u32),
887 assert(result != .root);888 _,
888 return result;889
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 };
890};895};
891896
892/// Trailing:897/// Trailing:
...@@ -900,7 +905,7 @@ pub const Package = enum(u32) {...@@ -900,7 +905,7 @@ pub const Package = enum(u32) {
900pub const Module = struct {905pub const Module = struct {
901 flags: Flags,906 flags: Flags,
902 flags2: Flags2,907 flags2: Flags2,
903 owner: Package,908 owner: Package.Index,
904 root_source_file: OptionalLazyPath,909 root_source_file: OptionalLazyPath,
905 import_table: ImportTable,910 import_table: ImportTable,
906 resolved_target: ResolvedTarget.OptionalIndex,911 resolved_target: ResolvedTarget.OptionalIndex,
...@@ -1048,6 +1053,11 @@ pub const ImportTable = enum(u32) {...@@ -1048,6 +1053,11 @@ pub const ImportTable = enum(u32) {
1048/// elements is `Step.Index` per count.1053/// elements is `Step.Index` per count.
1049pub const Deps = enum(u32) {1054pub const Deps = enum(u32) {
1050 _,1055 _,
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 }
1051};1061};
10521062
1053/// Points into `extra`, where the first element is count of strings, following1063/// Points into `extra`, where the first element is count of strings, following