authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-27 20:58:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
logc8b583885d75524fc92cc02a9d00a49a76f2ea70
tree085230c5da972f023c5db37c3ceafd48611f9c7f
parent0d48cbb822551c07ac987c9c2e20d3251ee3a09c

maker: port Run step logic up to spawnChildAndCollect


8 files changed, 328 insertions(+), 262 deletions(-)

BRANCH_TODO+1
...@@ -18,6 +18,7 @@...@@ -18,6 +18,7 @@
18* https://codeberg.org/ziglang/zig/pulls/3076218* https://codeberg.org/ziglang/zig/pulls/30762
1919
20## Followup Issues20## Followup Issues
21* reduce the size of Maker.Step.Extended (make Run smaller) probably by using an arena per make
21* link_eh_frame_hdr should be DefaultingBool22* link_eh_frame_hdr should be DefaultingBool
22* make --foo, --no-foo CLI args uniform (make them -f args instead)23* make --foo, --no-foo CLI args uniform (make them -f args instead)
23* install steps should provide generated files for installed things, then delete the run step hack24* install steps should provide generated files for installed things, then delete the run step hack
lib/compiler/Maker.zig+12-22
...@@ -153,17 +153,6 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -153,17 +153,6 @@ pub fn main(init: process.Init.Minimal) !void {
153 var debounce_interval_ms: u16 = 50;153 var debounce_interval_ms: u16 = 50;
154 var webui_listen: ?Io.net.IpAddress = null;154 var webui_listen: ?Io.net.IpAddress = null;
155 var debug_pkg_config: bool = false;155 var debug_pkg_config: bool = false;
156 // After following the steps in https://codeberg.org/ziglang/infra/src/branch/master/libc-update/glibc.md,
157 // this will be the directory $glibc-build-dir/install/glibcs
158 // Given the example of the aarch64 target, this is the directory
159 // that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
160 // Also works for dynamic musl.
161 var libc_runtimes_dir: ?[]const u8 = null;
162 var enable_wine = false;
163 var enable_qemu = false;
164 var enable_wasmtime = false;
165 var enable_darling = false;
166 var enable_rosetta = false;
167 var run_args: ?[]const []const u8 = null;156 var run_args: ?[]const []const u8 = null;
168157
169 if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| {158 if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| {
...@@ -314,7 +303,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -314,7 +303,7 @@ pub fn main(init: process.Init.Minimal) !void {
314 fatal("unrecognized optimization mode: {s}", .{rest});303 fatal("unrecognized optimization mode: {s}", .{rest});
315 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {304 } else if (mem.eql(u8, arg, "--libc-runtimes") or mem.eql(u8, arg, "--glibc-runtimes")) {
316 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.305 // --glibc-runtimes was the old name of the flag; kept for compatibility for now.
317 libc_runtimes_dir = nextArgOrFatal(args, &arg_idx);306 graph.libc_runtimes_dir = nextArgOrFatal(args, &arg_idx);
318 } else if (mem.eql(u8, arg, "--verbose")) {307 } else if (mem.eql(u8, arg, "--verbose")) {
319 graph.verbose = true;308 graph.verbose = true;
320 } else if (mem.eql(u8, arg, "--verbose-air")) {309 } else if (mem.eql(u8, arg, "--verbose-air")) {
...@@ -370,25 +359,25 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -370,25 +359,25 @@ pub fn main(init: process.Init.Minimal) !void {
370 } else if (mem.eql(u8, arg, "-fno-incremental")) {359 } else if (mem.eql(u8, arg, "-fno-incremental")) {
371 graph.incremental = false;360 graph.incremental = false;
372 } else if (mem.eql(u8, arg, "-fwine")) {361 } else if (mem.eql(u8, arg, "-fwine")) {
373 enable_wine = true;362 graph.enable_wine = true;
374 } else if (mem.eql(u8, arg, "-fno-wine")) {363 } else if (mem.eql(u8, arg, "-fno-wine")) {
375 enable_wine = false;364 graph.enable_wine = false;
376 } else if (mem.eql(u8, arg, "-fqemu")) {365 } else if (mem.eql(u8, arg, "-fqemu")) {
377 enable_qemu = true;366 graph.enable_qemu = true;
378 } else if (mem.eql(u8, arg, "-fno-qemu")) {367 } else if (mem.eql(u8, arg, "-fno-qemu")) {
379 enable_qemu = false;368 graph.enable_qemu = false;
380 } else if (mem.eql(u8, arg, "-fwasmtime")) {369 } else if (mem.eql(u8, arg, "-fwasmtime")) {
381 enable_wasmtime = true;370 graph.enable_wasmtime = true;
382 } else if (mem.eql(u8, arg, "-fno-wasmtime")) {371 } else if (mem.eql(u8, arg, "-fno-wasmtime")) {
383 enable_wasmtime = false;372 graph.enable_wasmtime = false;
384 } else if (mem.eql(u8, arg, "-frosetta")) {373 } else if (mem.eql(u8, arg, "-frosetta")) {
385 enable_rosetta = true;374 graph.enable_rosetta = true;
386 } else if (mem.eql(u8, arg, "-fno-rosetta")) {375 } else if (mem.eql(u8, arg, "-fno-rosetta")) {
387 enable_rosetta = false;376 graph.enable_rosetta = false;
388 } else if (mem.eql(u8, arg, "-fdarling")) {377 } else if (mem.eql(u8, arg, "-fdarling")) {
389 enable_darling = true;378 graph.enable_darling = true;
390 } else if (mem.eql(u8, arg, "-fno-darling")) {379 } else if (mem.eql(u8, arg, "-fno-darling")) {
391 enable_darling = false;380 graph.enable_darling = false;
392 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {381 } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
393 graph.allow_so_scripts = true;382 graph.allow_so_scripts = true;
394 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {383 } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
...@@ -533,6 +522,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -533,6 +522,7 @@ pub fn main(init: process.Init.Minimal) !void {
533 .bin = install_bin_path,522 .bin = install_bin_path,
534 .include = install_include_path,523 .include = install_include_path,
535 },524 },
525
536 .steps = try arena.alloc(Step, scanned_config.configuration.steps.len),526 .steps = try arena.alloc(Step, scanned_config.configuration.steps.len),
537 .generated_files = try arena.alloc(Path, scanned_config.configuration.generated_files_len),527 .generated_files = try arena.alloc(Path, scanned_config.configuration.generated_files_len),
538 .run_args = run_args,528 .run_args = run_args,
lib/compiler/Maker/Graph.zig+12
...@@ -52,6 +52,18 @@ error_limit: ?u32 = null,...@@ -52,6 +52,18 @@ error_limit: ?u32 = null,
52/// a single step spawning a fixed number of processes this can be used.52/// a single step spawning a fixed number of processes this can be used.
53max_jobs: ?u32 = null,53max_jobs: ?u32 = null,
5454
55/// After following the steps in https://codeberg.org/ziglang/infra/src/branch/master/libc-update/glibc.md,
56/// this will be the directory $glibc-build-dir/install/glibcs
57/// Given the example of the aarch64 target, this is the directory
58/// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
59/// Also works for dynamic musl.
60libc_runtimes_dir: ?[]const u8 = null,
61enable_wine: bool = false,
62enable_qemu: bool = false,
63enable_wasmtime: bool = false,
64enable_darling: bool = false,
65enable_rosetta: bool = false,
66
55/// Intention of verbose is to print all sub-process command lines to stderr67/// Intention of verbose is to print all sub-process command lines to stderr
56/// before spawning them.68/// before spawning them.
57pub fn handleVerbose(69pub fn handleVerbose(
lib/compiler/Maker/Step.zig+5-8
...@@ -62,12 +62,11 @@ comptime {...@@ -62,12 +62,11 @@ comptime {
62 // Common cache line size is 128. This check prevents accidentally crossing62 // Common cache line size is 128. This check prevents accidentally crossing
63 // an additional cache line. In the future it might be nice to try to fit63 // an additional cache line. In the future it might be nice to try to fit
64 // this struct in 128 bytes or less.64 // this struct in 128 bytes or less.
65 assert(@sizeOf(@This()) <= 128 * 3);65 assert(@sizeOf(@This()) <= 128 * 4);
66}66}
6767
68pub const Extended = union(enum) {68pub const Extended = union(enum) {
69 check_file: Todo,69 check_file: Todo,
70 check_object: Todo,
71 compile: Compile,70 compile: Compile,
72 config_header: Todo,71 config_header: Todo,
73 fail: Todo,72 fail: Todo,
...@@ -87,7 +86,6 @@ pub const Extended = union(enum) {...@@ -87,7 +86,6 @@ pub const Extended = union(enum) {
87 pub fn init(tag: Configuration.Step.Tag) Extended {86 pub fn init(tag: Configuration.Step.Tag) Extended {
88 return switch (tag) {87 return switch (tag) {
89 .check_file => .{ .check_file = .{} },88 .check_file => .{ .check_file = .{} },
90 .check_object => .{ .check_object = .{} },
91 .compile => .{ .compile = .{} },89 .compile => .{ .compile = .{} },
92 .config_header => .{ .config_header = .{} },90 .config_header => .{ .config_header = .{} },
93 .fail => .{ .fail = .{} },91 .fail => .{ .fail = .{} },
...@@ -645,9 +643,8 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {...@@ -645,9 +643,8 @@ fn sendMessage(io: Io, file: Io.File, tag: std.zig.Client.Message.Tag) !void {
645/// Asserts that the caller has already populated `s.result_failed_command`.643/// Asserts that the caller has already populated `s.result_failed_command`.
646pub inline fn handleChildProcUnsupported(s: *Step, maker: *Maker) FailError!void {644pub inline fn handleChildProcUnsupported(s: *Step, maker: *Maker) FailError!void {
647 assert(s.result_failed_command != null);645 assert(s.result_failed_command != null);
648 if (!std.process.can_spawn) {646 if (!std.process.can_spawn)
649 return s.fail(maker, "unable to spawn process: host cannot spawn child processes", .{});647 return s.fail(maker, "unable to spawn process: host cannot spawn child processes", .{});
650 }
651}648}
652649
653/// Asserts that the caller has already populated `s.result_failed_command`.650/// Asserts that the caller has already populated `s.result_failed_command`.
...@@ -708,10 +705,10 @@ fn failWithCacheError(...@@ -708,10 +705,10 @@ fn failWithCacheError(
708705
709/// Prefer `writeManifestAndWatch` unless you already added watch inputs706/// Prefer `writeManifestAndWatch` unless you already added watch inputs
710/// separately from using the cache system.707/// separately from using the cache system.
711pub fn writeManifest(s: *Step, man: *Cache.Manifest) !void {708pub fn writeManifest(s: *Step, maker: *Maker, man: *Cache.Manifest) !void {
712 if (s.test_results.isSuccess()) {709 if (s.test_results.isSuccess()) {
713 man.writeManifest() catch |err| {710 man.writeManifest() catch |err| {
714 try s.addError("unable to write cache manifest: {t}", .{err});711 try s.addError(maker, "unable to write cache manifest: {t}", .{err});
715 };712 };
716 }713 }
717}714}
...@@ -721,7 +718,7 @@ pub fn writeManifest(s: *Step, man: *Cache.Manifest) !void {...@@ -721,7 +718,7 @@ pub fn writeManifest(s: *Step, man: *Cache.Manifest) !void {
721///718///
722/// Must be accompanied with `cacheHitAndWatch`.719/// Must be accompanied with `cacheHitAndWatch`.
723pub fn writeManifestAndWatch(s: *Step, maker: *Maker, man: *Cache.Manifest) !void {720pub fn writeManifestAndWatch(s: *Step, maker: *Maker, man: *Cache.Manifest) !void {
724 try writeManifest(s, man);721 try writeManifest(s, maker, man);
725 try setWatchInputsFromManifest(s, maker, man);722 try setWatchInputsFromManifest(s, maker, man);
726}723}
727724
lib/compiler/Maker/Step/Run.zig+293-209
...@@ -16,6 +16,7 @@ const allocPrint = std.fmt.allocPrint;...@@ -16,6 +16,7 @@ const allocPrint = std.fmt.allocPrint;
1616
17const Step = @import("../Step.zig");17const Step = @import("../Step.zig");
18const Maker = @import("../../Maker.zig");18const Maker = @import("../../Maker.zig");
19const Fuzz = @import("../../Maker/Fuzz.zig");
1920
20/// If this is a Zig unit test binary, this tracks the names of the unit21/// If this is a Zig unit test binary, this tracks the names of the unit
21/// tests that are also fuzz tests. Indexes cannot be used as they may22/// tests that are also fuzz tests. Indexes cannot be used as they may
...@@ -31,6 +32,8 @@ rebuilt_executable: ?Path = null,...@@ -31,6 +32,8 @@ rebuilt_executable: ?Path = null,
31argv: std.ArrayList([]const u8) = .empty,32argv: std.ArrayList([]const u8) = .empty,
32/// Persisted to reuse memory on subsequent calls to `make`.33/// Persisted to reuse memory on subsequent calls to `make`.
33output_placeholders: std.ArrayList(IndexedOutput) = .empty,34output_placeholders: std.ArrayList(IndexedOutput) = .empty,
35/// Persisted to reuse memory on subsequent calls to `make`.
36environ_map: std.process.Environ.Map = .{ .array_hash_map = .empty, .allocator = undefined },
3437
35pub fn make(38pub fn make(
36 run: *Run,39 run: *Run,
...@@ -67,6 +70,8 @@ pub fn make(...@@ -67,6 +70,8 @@ pub fn make(
67 man.hash.add(conf_run.flags.color);70 man.hash.add(conf_run.flags.color);
68 man.hash.add(conf_run.flags.disable_zig_progress);71 man.hash.add(conf_run.flags.disable_zig_progress);
6972
73 var dep_file_count: usize = 0;
74
70 for (conf_run.args.slice) |arg_index| {75 for (conf_run.args.slice) |arg_index| {
71 const arg = arg_index.get(conf);76 const arg = arg_index.get(conf);
72 try argv_list.ensureUnusedCapacity(gpa, 1);77 try argv_list.ensureUnusedCapacity(gpa, 1);
...@@ -157,6 +162,9 @@ pub fn make(...@@ -157,6 +162,9 @@ pub fn make(
157 man.hash.addBytesZ(basename);162 man.hash.addBytesZ(basename);
158 man.hash.addBytesZ(suffix);163 man.hash.addBytesZ(suffix);
159164
165 man.hash.add(arg.flags.dep_file);
166 dep_file_count += @intFromBool(arg.flags.dep_file);
167
160 // Add a placeholder into the argument list because we need the168 // Add a placeholder into the argument list because we need the
161 // manifest hash to be updated with all arguments before the169 // manifest hash to be updated with all arguments before the
162 // object directory is computed.170 // object directory is computed.
...@@ -220,145 +228,95 @@ pub fn make(...@@ -220,145 +228,95 @@ pub fn make(
220228
221 const has_side_effects = conf_run.flags.has_side_effects;229 const has_side_effects = conf_run.flags.has_side_effects;
222230
223 if (true) @panic("TODO");
224
225 if (!has_side_effects and try step.cacheHitAndWatch(maker, &man)) {231 if (!has_side_effects and try step.cacheHitAndWatch(maker, &man)) {
226 // cache hit, skip running command232 // Cache hit; skip running command.
227 const digest = man.final();233 const digest = man.final();
228234 try populateGeneratedStdIo(maker, &conf_run, cache_root, &digest);
229 try populateGeneratedPaths(235 try populateGeneratedPaths(maker, output_placeholders.items, cache_root, &digest);
230 arena,
231 output_placeholders.items,
232 &conf_run,
233 cache_root,
234 &digest,
235 );
236
237 step.result_cached = true;236 step.result_cached = true;
238 return;237 return;
239 }238 }
240239
241 const dep_output_file = conf_run.dep_output_file orelse {240 if (dep_file_count == 0) {
242 // We already know the final output paths, use them directly.241 // We already know the final output paths; use them directly.
243 const digest = if (has_side_effects)242 const digest = if (has_side_effects) man.hash.final() else man.final();
244 man.hash.final()
245 else
246 man.final();
247
248 try populateGeneratedPaths(
249 arena,
250 output_placeholders.items,
251 &conf_run,
252 cache_root,
253 &digest,
254 );
255
256 const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest;243 const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest;
257 for (output_placeholders.items) |placeholder| {244 try populateGeneratedStdIo(maker, &conf_run, cache_root, &digest);
258 const output_sub_path = graph.pathJoin(&.{ output_dir_path, placeholder.output.basename });245 try populateGeneratedPathsCreateDirs(run, run_index, maker, output_dir_path);
259 const output_sub_dir_path = switch (placeholder.tag) {246 try runCommand(run, run_index, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null);
260 .output_file => Dir.path.dirname(output_sub_path).?,247 if (!has_side_effects) try step.writeManifestAndWatch(maker, &man);
261 .output_directory => output_sub_path,
262 else => unreachable,
263 };
264 cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
265 return step.fail(maker, "unable to make path '{f}{s}': {t}", .{
266 cache_root, output_sub_dir_path, err,
267 });
268 };
269 const arg_output_path = try convertPathArg(run_index, maker, .{
270 .root_dir = .cwd(),
271 .sub_path = placeholder.output.generated_file.getPath(),
272 });
273 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
274 arg_output_path
275 else
276 try allocPrint(arena, "{s}{s}", .{ placeholder.output.prefix, arg_output_path });
277 }
278
279 try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null);
280 if (!has_side_effects) try step.writeManifestAndWatch(&man);
281 return;248 return;
282 };249 }
283250
284 // We do not know the final output paths yet, use temp paths to run the command.251 // We do not know the final output paths yet; use temporary directory to run the command.
285 var rand_int: u64 = undefined;252 var rand_int: u64 = undefined;
286 io.random(@ptrCast(&rand_int));253 io.random(@ptrCast(&rand_int));
287 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);254 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
288255
256 try populateGeneratedPathsCreateDirs(run, run_index, maker, tmp_dir_path);
257 try runCommand(run, run_index, maker, progress_node, argv_list.items, has_side_effects, tmp_dir_path, null);
258
289 for (output_placeholders.items) |placeholder| {259 for (output_placeholders.items) |placeholder| {
290 const output_components = .{ tmp_dir_path, placeholder.output.basename };260 const arg = placeholder.arg_index.get(conf);
291 const output_sub_path = graph.pathJoin(&output_components);261 switch (arg.flags.tag) {
292 const output_sub_dir_path = switch (placeholder.tag) {262 .output_file => if (arg.flags.dep_file) {
293 .output_file => Dir.path.dirname(output_sub_path).?,263 const generated_path = maker.generatedPath(arg.generated.value.?).*;
294 .output_directory => output_sub_path,264 const result = if (has_side_effects)
265 man.addDepFile(generated_path.root_dir.handle, generated_path.sub_path)
266 else
267 man.addDepFilePost(generated_path.root_dir.handle, generated_path.sub_path);
268 result catch |err| switch (err) {
269 error.OutOfMemory, error.Canceled => |e| return e,
270 else => |e| return step.fail(maker, "failed adding to cache the file {f}: {t}", .{
271 generated_path, e,
272 }),
273 };
274 },
275 .output_directory => continue,
295 else => unreachable,276 else => unreachable,
296 };277 }
297 cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
298 return step.fail(maker, "unable to make path '{f}{s}': {t}", .{
299 cache_root, output_sub_dir_path, err,
300 });
301 };
302 const raw_output_path: Path = .{
303 .root_dir = cache_root,
304 .sub_path = graph.pathJoin(&output_components),
305 };
306 placeholder.output.generated_file.path = raw_output_path.toString(arena) catch @panic("OOM");
307 argv_list.items[placeholder.index] = try mem.concat(arena, u8, .{
308 placeholder.output.prefix,
309 try convertPathArg(run_index, maker, raw_output_path),
310 });
311 }278 }
312279
313 try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, tmp_dir_path, null);280 const digest = if (has_side_effects) man.hash.final() else man.final();
314
315 const dep_file_dir = Dir.cwd();
316 const dep_file_basename = dep_output_file.generated_file.getPath2(graph, step);
317 if (has_side_effects)
318 try man.addDepFile(dep_file_dir, dep_file_basename)
319 else
320 try man.addDepFilePost(dep_file_dir, dep_file_basename);
321
322 const digest = if (has_side_effects)
323 man.hash.final()
324 else
325 man.final();
326281
327 const any_output = output_placeholders.items.len > 0 or282 const any_output = output_placeholders.items.len > 0 or
328 conf_run.captured_stdout != null or conf_run.captured_stderr != null;283 conf_run.captured_stdout.value != null or conf_run.captured_stderr.value != null;
329284
330 // Rename into place
331 if (any_output) {285 if (any_output) {
332 const o_sub_path = "o" ++ Dir.path.sep_str ++ &digest;286 // Rename into place.
333287 const tmp_path: Path = .{ .root_dir = cache_root, .sub_path = tmp_dir_path };
334 cache_root.handle.rename(tmp_dir_path, cache_root.handle, o_sub_path, io) catch |err| switch (err) {288 const dst_path: Path = .{ .root_dir = cache_root, .sub_path = "o" ++ Dir.path.sep_str ++ &digest };
335 Dir.RenameError.DirNotEmpty => {289 Dir.rename(
336 cache_root.handle.deleteTree(io, o_sub_path) catch |del_err| {290 tmp_path.root_dir.handle,
337 return step.fail(maker, "unable to remove dir '{f}'{s}: {t}", .{291 tmp_path.sub_path,
338 cache_root, tmp_dir_path, del_err,292 dst_path.root_dir.handle,
339 });293 dst_path.sub_path,
340 };294 io,
341 cache_root.handle.rename(tmp_dir_path, cache_root.handle, o_sub_path, io) catch |retry_err| {295 ) catch |err| switch (err) {
342 return step.fail(maker, "unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{296 error.DirNotEmpty => {
343 cache_root, tmp_dir_path, cache_root, o_sub_path, retry_err,297 dst_path.root_dir.handle.deleteTree(io, dst_path.sub_path) catch |del_err|
344 });298 return step.fail(maker, "failed to remove tree {f}: {t}", .{ dst_path, del_err });
345 };299
300 Dir.rename(
301 tmp_path.root_dir.handle,
302 tmp_path.sub_path,
303 dst_path.root_dir.handle,
304 dst_path.sub_path,
305 io,
306 ) catch |retry_err| return step.fail(maker, "failed to rename directory {f} to {f}: {t}", .{
307 tmp_path, dst_path, retry_err,
308 });
346 },309 },
347 else => return step.fail(maker, "unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{310 else => return step.fail(maker, "failed to rename directory {f} to {f}: {t}", .{
348 cache_root, tmp_dir_path, cache_root, o_sub_path, err,311 tmp_path, dst_path, err,
349 }),312 }),
350 };313 };
351 }314 }
352315
353 if (!has_side_effects) try step.writeManifestAndWatch(&man);316 if (!has_side_effects) try step.writeManifestAndWatch(maker, &man);
354317
355 try populateGeneratedPaths(318 try populateGeneratedStdIo(maker, &conf_run, cache_root, &digest);
356 arena,319 try populateGeneratedPaths(maker, output_placeholders.items, cache_root, &digest);
357 output_placeholders.items,
358 &conf_run,
359 cache_root,
360 &digest,
361 );
362}320}
363321
364/// Reads stdout of a Zig test process until a termination condition is reached:322/// Reads stdout of a Zig test process until a termination condition is reached:
...@@ -918,9 +876,12 @@ const FuzzTestRunner = struct {...@@ -918,9 +876,12 @@ const FuzzTestRunner = struct {
918 }876 }
919877
920 fn saveCrash(f: *FuzzTestRunner, id: u32, term: process.Child.Term) !void {878 fn saveCrash(f: *FuzzTestRunner, id: u32, term: process.Child.Term) !void {
879 const fuzz = f.context.fuzz;
880 const maker = fuzz.maker;
921 const step = &f.run.step;881 const step = &f.run.step;
922 const b = step.owner;882 const graph = maker.graph;
923 const io = b.graph.io;883 const io = graph.io;
884 const cache_root = graph.local_cache_root;
924885
925 if (f.coverage_id == null) return;886 if (f.coverage_id == null) return;
926887
...@@ -938,7 +899,7 @@ const FuzzTestRunner = struct {...@@ -938,7 +899,7 @@ const FuzzTestRunner = struct {
938 }) {899 }) {
939 const name_prefix = "f" ++ Io.Dir.path.sep_str ++ "in";900 const name_prefix = "f" ++ Io.Dir.path.sep_str ++ "in";
940 in_name = std.fmt.bufPrint(&in_name_buf, name_prefix ++ "{x}", .{i}) catch unreachable;901 in_name = std.fmt.bufPrint(&in_name_buf, name_prefix ++ "{x}", .{i}) catch unreachable;
941 in_f = b.cache_root.handle.openFile(io, in_name, .{902 in_f = cache_root.handle.openFile(io, in_name, .{
942 .lock = .exclusive,903 .lock = .exclusive,
943 .lock_nonblocking = true,904 .lock_nonblocking = true,
944 }) catch |e| switch (e) {905 }) catch |e| switch (e) {
...@@ -946,7 +907,7 @@ const FuzzTestRunner = struct {...@@ -946,7 +907,7 @@ const FuzzTestRunner = struct {
946 error.WouldBlock => continue, // Can not be from907 error.WouldBlock => continue, // Can not be from
947 // the crashed instance since it is still locked.908 // the crashed instance since it is still locked.
948 else => return step.fail("failed to open file '{f}{s}': {t}", .{909 else => return step.fail("failed to open file '{f}{s}': {t}", .{
949 b.cache_root, in_name, e,910 cache_root, in_name, e,
950 }),911 }),
951 };912 };
952913
...@@ -955,7 +916,7 @@ const FuzzTestRunner = struct {...@@ -955,7 +916,7 @@ const FuzzTestRunner = struct {
955 in_f.close(io);916 in_f.close(io);
956 switch (e) {917 switch (e) {
957 error.ReadFailed => return step.fail("failed to read file '{f}{s}': {t}", .{918 error.ReadFailed => return step.fail("failed to read file '{f}{s}': {t}", .{
958 b.cache_root, in_name, in_r.err.?,919 cache_root, in_name, in_r.err.?,
959 }),920 }),
960 error.EndOfStream => continue,921 error.EndOfStream => continue,
961 }922 }
...@@ -974,10 +935,10 @@ const FuzzTestRunner = struct {...@@ -974,10 +935,10 @@ const FuzzTestRunner = struct {
974935
975 // Save it to a seperate file936 // Save it to a seperate file
976 const crash_name = "f" ++ Io.Dir.path.sep_str ++ "crash";937 const crash_name = "f" ++ Io.Dir.path.sep_str ++ "crash";
977 const out = b.cache_root.handle.createFile(io, crash_name, .{938 const out = cache_root.handle.createFile(io, crash_name, .{
978 .lock = .exclusive, // Multiple run steps could have found a crash at the same time939 .lock = .exclusive, // Multiple run steps could have found a crash at the same time
979 }) catch |e| return step.fail("failed to create file '{f}{s}': {t}", .{940 }) catch |e| return step.fail("failed to create file '{f}{s}': {t}", .{
980 b.cache_root, crash_name, e,941 cache_root, crash_name, e,
981 });942 });
982 defer out.close(io);943 defer out.close(io);
983944
...@@ -985,17 +946,17 @@ const FuzzTestRunner = struct {...@@ -985,17 +946,17 @@ const FuzzTestRunner = struct {
985 var out_w = out.writerStreaming(io, &out_w_buf);946 var out_w = out.writerStreaming(io, &out_w_buf);
986 _ = out_w.interface.sendFileAll(&in_r, .limited(header.len)) catch |e| switch (e) {947 _ = out_w.interface.sendFileAll(&in_r, .limited(header.len)) catch |e| switch (e) {
987 error.ReadFailed => return step.fail("failed to read file '{f}{s}': {t}", .{948 error.ReadFailed => return step.fail("failed to read file '{f}{s}': {t}", .{
988 b.cache_root, in_name, in_r.err.?,949 cache_root, in_name, in_r.err.?,
989 }),950 }),
990 error.WriteFailed => return step.fail("failed to write file '{f}{s}': {t}", .{951 error.WriteFailed => return step.fail("failed to write file '{f}{s}': {t}", .{
991 b.cache_root, crash_name, out_w.err.?,952 cache_root, crash_name, out_w.err.?,
992 }),953 }),
993 };954 };
994955
995 return f.run.step.fail("test '{s}' {f}; input saved to '{f}{s}'", .{956 return f.run.step.fail("test '{s}' {f}; input saved to '{f}{s}'", .{
996 f.run.fuzz_tests.items[header.test_i],957 f.run.fuzz_tests.items[header.test_i],
997 fmtTerm(term),958 fmtTerm(term),
998 b.cache_root,959 cache_root,
999 crash_name,960 crash_name,
1000 });961 });
1001 }962 }
...@@ -1492,54 +1453,85 @@ pub fn rerunInFuzzMode(...@@ -1492,54 +1453,85 @@ pub fn rerunInFuzzMode(
1492 const maker = fuzz.maker;1453 const maker = fuzz.maker;
1493 const graph = maker.graph;1454 const graph = maker.graph;
1494 const step = &run.step;1455 const step = &run.step;
1495 const b = step.owner;
1496 const io = graph.io;1456 const io = graph.io;
1497 const arena = b.allocator;1457 const arena = graph.arena; // TODO don't leak into the process arena
1498 var argv_list: std.ArrayList([]const u8) = .empty;1458 const gpa = maker.gpa;
1499 for (run.argv.items) |arg| {1459 const conf = &maker.scanned_config.configuration;
1500 switch (arg) {1460 const conf_step = run_index.ptr(conf);
1501 .bytes => |bytes| {1461 const conf_run = conf_step.extended.get(conf.extra).run;
1502 try argv_list.append(arena, bytes);1462 const argv_list = &run.argv;
1463
1464 argv_list.clearRetainingCapacity();
1465
1466 for (conf_run.args.slice) |arg_index| {
1467 const arg = arg_index.get(conf);
1468 try argv_list.ensureUnusedCapacity(gpa, 1);
1469 switch (arg.flags.tag) {
1470 .string => {
1471 const prefix = arg.prefix.value.?.slice(conf);
1472 argv_list.appendAssumeCapacity(prefix);
1503 },1473 },
1504 .lazy_path => |file| {1474 .path_file => {
1505 const file_path = file.lazy_path.getPath3(b, step);1475 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
1506 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, convertPathArg(run_index, maker, file_path) }));1476 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
1477 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
1478 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
1479 prefix, try convertPathArg(run_index, maker, file_path), suffix,
1480 }));
1507 },1481 },
1508 .decorated_directory => |dd| {1482 .path_directory => {
1509 const file_path = dd.lazy_path.getPath3(b, step);1483 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
1510 try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, convertPathArg(run_index, maker, file_path), dd.suffix }));1484 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
1485 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
1486 const resolved_arg = try mem.concat(arena, u8, &.{
1487 prefix, try convertPathArg(run_index, maker, file_path), suffix,
1488 });
1489 argv_list.appendAssumeCapacity(resolved_arg);
1511 },1490 },
1512 .file_content => |file_plp| {1491 .file_content => {
1513 const file_path = file_plp.lazy_path.getPath3(b, step);1492 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
1493 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
1494 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
15141495
1515 var result: std.Io.Writer.Allocating = .init(arena);1496 var result: std.Io.Writer.Allocating = .init(arena);
1516 errdefer result.deinit();1497 result.writer.writeAll(prefix) catch return error.OutOfMemory;
1517 result.writer.writeAll(file_plp.prefix) catch return error.OutOfMemory;
15181498
1519 const file = try file_path.root_dir.handle.openFile(io, file_path.subPathOrDot(), .{});1499 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
1500 return step.fail(maker, "unable to open input file {f}: {t}", .{ file_path, err });
1520 defer file.close(io);1501 defer file.close(io);
15211502
1522 var buf: [1024]u8 = undefined;1503 var file_reader = file.reader(io, &.{});
1523 var file_reader = file.reader(io, &buf);
1524 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {1504 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
1525 error.ReadFailed => return file_reader.err.?,1505 error.ReadFailed => switch (file_reader.err.?) {
1506 error.Canceled => |e| return e,
1507 else => |e| return step.fail(maker, "failed to read from {f}: {t}", .{ file_path, e }),
1508 },
1526 error.WriteFailed => return error.OutOfMemory,1509 error.WriteFailed => return error.OutOfMemory,
1527 };1510 };
1511 result.writer.writeAll(suffix) catch return error.OutOfMemory;
15281512
1529 try argv_list.append(arena, result.written());1513 argv_list.appendAssumeCapacity(result.written());
1530 },1514 },
1531 .artifact => |pa| {1515 .artifact => {
1532 const artifact = pa.artifact;1516 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
1533 const file_path: []const u8 = p: {1517 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
1534 if (artifact == run.producer.?) break :p b.fmt("{f}", .{run.rebuilt_executable.?});1518 const producer_index = arg.producer.value.?;
1535 break :p artifact.installed_path orelse artifact.generated_bin.?.path.?;1519 const producer_step = producer_index.ptr(conf);
1536 };1520 const producer = producer_step.extended.get(conf.extra).compile;
1537 try argv_list.append(arena, b.fmt("{s}{s}", .{1521 const producer_make_comp_step = maker.stepByIndex(producer_index);
1538 pa.prefix,1522 const producer_make_comp = &producer_make_comp_step.extended.compile;
1539 convertPathArg(run_index, maker, .{ .root_dir = .cwd(), .sub_path = file_path }),1523 const file_path: Path = if (producer_index == conf_run.producer.value.?)
1524 run.rebuilt_executable.?
1525 else
1526 producer_make_comp.installed_path orelse
1527 maker.generatedPath(producer.generated_bin.value.?).*;
1528 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
1529 prefix, try convertPathArg(run_index, maker, file_path), suffix,
1540 }));1530 }));
1541 },1531 },
1542 .output_file, .output_directory => unreachable,1532 .output_file => unreachable,
1533 .output_directory => unreachable,
1534 .cli_rest_positionals => unreachable,
1543 }1535 }
1544 }1536 }
15451537
...@@ -1552,7 +1544,7 @@ pub fn rerunInFuzzMode(...@@ -1552,7 +1544,7 @@ pub fn rerunInFuzzMode(
1552 var rand_int: u64 = undefined;1544 var rand_int: u64 = undefined;
1553 io.random(@ptrCast(&rand_int));1545 io.random(@ptrCast(&rand_int));
1554 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);1546 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
1555 try runCommand(run, maker, prog_node, argv_list.items, has_side_effects, tmp_dir_path, .{1547 try runCommand(run, run_index, maker, prog_node, argv_list.items, has_side_effects, tmp_dir_path, .{
1556 .fuzz = fuzz,1548 .fuzz = fuzz,
1557 });1549 });
1558}1550}
...@@ -1560,28 +1552,94 @@ pub fn rerunInFuzzMode(...@@ -1560,28 +1552,94 @@ pub fn rerunInFuzzMode(
1560const CapturedStdIo = void; // TODO get it from Configuration1552const CapturedStdIo = void; // TODO get it from Configuration
15611553
1562fn populateGeneratedPaths(1554fn populateGeneratedPaths(
1563 arena: std.mem.Allocator,1555 maker: *Maker,
1564 output_placeholders: []const IndexedOutput,1556 output_placeholders: []const IndexedOutput,
1565 conf_run: *const Configuration.Step.Run,
1566 cache_root: Cache.Directory,1557 cache_root: Cache.Directory,
1567 digest: *const Cache.HexDigest,1558 digest: *const Cache.HexDigest,
1568) !void {1559) !void {
1560 const conf = &maker.scanned_config.configuration;
1561 const graph = maker.graph;
1562 const arena = graph.arena; // TODO don't leak into the process arena
1563
1569 for (output_placeholders) |placeholder| {1564 for (output_placeholders) |placeholder| {
1570 placeholder.output.generated_file.path = try cache_root.join(arena, &.{1565 const arg = placeholder.arg_index.get(conf);
1571 "o", digest, placeholder.output.basename,1566 maker.generatedPath(arg.generated.value.?).* = .{
1572 });1567 .root_dir = cache_root,
1568 .sub_path = try Dir.path.join(arena, &.{
1569 "o", digest, arg.basename.value.?.slice(conf),
1570 }),
1571 };
1572 }
1573}
1574
1575fn populateGeneratedPathsCreateDirs(
1576 run: *Run,
1577 run_index: Configuration.Step.Index,
1578 maker: *Maker,
1579 output_dir_path: []const u8,
1580) !void {
1581 const step = maker.stepByIndex(run_index);
1582 const conf = &maker.scanned_config.configuration;
1583 const graph = maker.graph;
1584 const io = graph.io;
1585 const arena = graph.arena; // TODO don't leak into the process arena
1586 const cache_root = graph.local_cache_root;
1587 const argv = run.argv.items;
1588
1589 for (run.output_placeholders.items) |placeholder| {
1590 const arg = placeholder.arg_index.get(conf);
1591 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
1592 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
1593 const basename = arg.basename.value.?.slice(conf);
1594
1595 const generated_path: Path = .{
1596 .root_dir = cache_root,
1597 .sub_path = try Dir.path.join(arena, &.{ output_dir_path, basename }),
1598 };
1599 const create_path: Path = .{
1600 .root_dir = cache_root,
1601 .sub_path = switch (arg.flags.tag) {
1602 .output_file => Dir.path.dirname(generated_path.sub_path).?,
1603 .output_directory => generated_path.sub_path,
1604 else => unreachable,
1605 },
1606 };
1607 create_path.root_dir.handle.createDirPath(io, create_path.sub_path) catch |err|
1608 return step.fail(maker, "unable to make path {f}: {t}", .{ create_path, err });
1609
1610 maker.generatedPath(arg.generated.value.?).* = generated_path;
1611
1612 const arg_output_path = try convertPathArg(run_index, maker, generated_path);
1613 argv[placeholder.index] = try mem.concat(arena, u8, &.{ prefix, arg_output_path, suffix });
1573 }1614 }
1615}
1616
1617fn populateGeneratedStdIo(
1618 maker: *Maker,
1619 conf_run: *const Configuration.Step.Run,
1620 cache_root: Cache.Directory,
1621 digest: *const Cache.HexDigest,
1622) !void {
1623 const conf = &maker.scanned_config.configuration;
1624 const graph = maker.graph;
1625 const arena = graph.arena; // TODO don't leak into the process arena
15741626
1575 if (conf_run.captured_stdout.value) |captured| {1627 if (conf_run.captured_stdout.value) |captured| {
1576 captured.output.generated_file.path = try cache_root.join(arena, &.{1628 maker.generatedPath(captured.generated_file).* = .{
1577 "o", digest, captured.output.basename,1629 .root_dir = cache_root,
1578 });1630 .sub_path = try Dir.path.join(arena, &.{
1631 "o", digest, captured.basename.slice(conf),
1632 }),
1633 };
1579 }1634 }
15801635
1581 if (conf_run.captured_stderr.value) |captured| {1636 if (conf_run.captured_stderr.value) |captured| {
1582 captured.output.generated_file.path = try cache_root.join(arena, &.{1637 maker.generatedPath(captured.generated_file).* = .{
1583 "o", digest, captured.output.basename,1638 .root_dir = cache_root,
1584 });1639 .sub_path = try Dir.path.join(arena, &.{
1640 "o", digest, captured.basename.slice(conf),
1641 }),
1642 };
1585 }1643 }
1586}1644}
15871645
...@@ -1600,11 +1658,12 @@ fn fmtTerm(term: ?process.Child.Term) std.fmt.Alt(?process.Child.Term, formatTer...@@ -1600,11 +1658,12 @@ fn fmtTerm(term: ?process.Child.Term) std.fmt.Alt(?process.Child.Term, formatTer
1600}1658}
16011659
1602const FuzzContext = struct {1660const FuzzContext = struct {
1603 fuzz: *std.Build.Fuzz,1661 fuzz: *Fuzz,
1604};1662};
16051663
1606fn runCommand(1664fn runCommand(
1607 run: *Run,1665 run: *Run,
1666 run_index: Configuration.Step.Index,
1608 maker: *Maker,1667 maker: *Maker,
1609 progress_node: std.Progress.Node,1668 progress_node: std.Progress.Node,
1610 argv: []const []const u8,1669 argv: []const []const u8,
...@@ -1615,30 +1674,45 @@ fn runCommand(...@@ -1615,30 +1674,45 @@ fn runCommand(
1615 const graph = maker.graph;1674 const graph = maker.graph;
1616 const arena = graph.arena; // TODO don't leak into process arena1675 const arena = graph.arena; // TODO don't leak into process arena
1617 const gpa = maker.gpa;1676 const gpa = maker.gpa;
1618 const step = &run.step;1677 const step = maker.stepByIndex(run_index);
1619 const b = step.owner;
1620 const io = graph.io;1678 const io = graph.io;
1679 const cache_root = graph.local_cache_root;
1680 const conf = &maker.scanned_config.configuration;
1681 const conf_step = run_index.ptr(conf);
1682 const conf_run = conf_step.extended.get(conf.extra).run;
1683 const environ_map = &run.environ_map;
16211684
1622 const cwd: process.Child.Cwd = if (run.cwd) |lazy_cwd| .{ .path = lazy_cwd.getPath2(b, step) } else .inherit;1685 const cwd: process.Child.Cwd = if (conf_run.cwd.value) |lazy_cwd|
16231686 .{ .path = try maker.resolveLazyPathIndexAbs(arena, lazy_cwd, run_index) }
1624 try step.handleChildProcUnsupported();1687 else
1625 try Step.handleVerbose(step.owner, cwd, run.environ_map, argv);1688 .inherit;
16261689
1627 const allow_skip = switch (run.stdio) {1690 const allow_skip = switch (conf_run.flags.stdio) {
1628 .check, .zig_test => run.skip_foreign_checks,1691 .check, .zig_test => conf_run.flags.skip_foreign_checks,
1629 else => false,1692 else => false,
1630 };1693 };
16311694
1632 var interp_argv = std.array_list.Managed([]const u8).init(b.allocator);1695 var interp_argv: std.ArrayList([]const u8) = .empty;
1633 defer interp_argv.deinit();1696
1697 // `environ_map` is initialized with an undefined `allocator` field; lazily
1698 // initialize it here.
1699 environ_map.allocator = gpa;
1700 // In either case we add to this mutatable data structure so that we can
1701 // tweak the environment below.
1702 environ_map.clearRetainingCapacity();
1703 if (conf_run.environ_map.value) |env_map_index| {
1704 const conf_env_map = env_map_index.get(conf);
1705 for (conf_env_map.keys.slice(conf), conf_env_map.values.slice(conf)) |k, v| {
1706 try environ_map.put(k.slice(conf), v.slice(conf));
1707 }
1708 } else {
1709 try environ_map.putAll(&graph.environ_map);
1710 }
1711 try graph.handleVerbose(cwd, environ_map, argv);
16341712
1635 var environ_map: EnvMap = env: {1713 if (true) @panic("TODO");
1636 const orig = run.environ_map orelse &graph.environ_map;
1637 break :env try orig.clone(gpa);
1638 };
1639 defer environ_map.deinit();
16401714
1641 const opt_generic_result = spawnChildAndCollect(run, maker, progress_node, argv, &environ_map, has_side_effects, fuzz_context) catch |err| term: {1715 const opt_generic_result = spawnChildAndCollect(run_index, run, maker, progress_node, argv, &environ_map, has_side_effects, fuzz_context) catch |err| term: {
1642 // InvalidExe: cpu arch mismatch1716 // InvalidExe: cpu arch mismatch
1643 // FileNotFound: can happen with a wrong dynamic linker path1717 // FileNotFound: can happen with a wrong dynamic linker path
1644 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {1718 if (err == error.InvalidExe or err == error.FileNotFound) interpret: {
...@@ -1660,7 +1734,7 @@ fn runCommand(...@@ -1660,7 +1734,7 @@ fn runCommand(
1660 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));1734 (root_target.isGnuLibC() or (root_target.isMuslLibC() and exe.linkage == .dynamic));
1661 const other_target = exe.root_module.resolved_target.?.result;1735 const other_target = exe.root_module.resolved_target.?.result;
1662 switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{1736 switch (std.zig.system.getExternalExecutor(io, &graph.host.result, &other_target, .{
1663 .qemu_fixes_dl = need_cross_libc and b.libc_runtimes_dir != null,1737 .qemu_fixes_dl = need_cross_libc and graph.libc_runtimes_dir != null,
1664 .link_libc = exe.is_linking_libc,1738 .link_libc = exe.is_linking_libc,
1665 })) {1739 })) {
1666 .native, .rosetta => {1740 .native, .rosetta => {
...@@ -1668,7 +1742,7 @@ fn runCommand(...@@ -1668,7 +1742,7 @@ fn runCommand(
1668 break :interpret;1742 break :interpret;
1669 },1743 },
1670 .wine => |bin_name| {1744 .wine => |bin_name| {
1671 if (b.enable_wine) {1745 if (graph.enable_wine) {
1672 try interp_argv.append(bin_name);1746 try interp_argv.append(bin_name);
1673 try interp_argv.appendSlice(argv);1747 try interp_argv.appendSlice(argv);
16741748
...@@ -1682,21 +1756,21 @@ fn runCommand(...@@ -1682,21 +1756,21 @@ fn runCommand(
1682 }1756 }
1683 },1757 },
1684 .qemu => |bin_name| {1758 .qemu => |bin_name| {
1685 if (b.enable_qemu) {1759 if (graph.enable_qemu) {
1686 try interp_argv.append(bin_name);1760 try interp_argv.append(bin_name);
16871761
1688 if (need_cross_libc) {1762 if (need_cross_libc) {
1689 if (b.libc_runtimes_dir) |dir| {1763 if (graph.libc_runtimes_dir) |dir| {
1690 try interp_argv.append("-L");1764 try interp_argv.append("-L");
1691 try interp_argv.append(b.pathJoin(&.{1765 try interp_argv.append(try Dir.path.join(arena, &.{
1692 dir,1766 dir,
1693 try if (root_target.isGnuLibC()) std.zig.target.glibcRuntimeTriple(1767 try if (root_target.isGnuLibC()) std.zig.target.glibcRuntimeTriple(
1694 b.allocator,1768 arena,
1695 root_target.cpu.arch,1769 root_target.cpu.arch,
1696 root_target.os.tag,1770 root_target.os.tag,
1697 root_target.abi,1771 root_target.abi,
1698 ) else if (root_target.isMuslLibC()) std.zig.target.muslRuntimeTriple(1772 ) else if (root_target.isMuslLibC()) std.zig.target.muslRuntimeTriple(
1699 b.allocator,1773 arena,
1700 root_target.cpu.arch,1774 root_target.cpu.arch,
1701 root_target.abi,1775 root_target.abi,
1702 ) else unreachable,1776 ) else unreachable,
...@@ -1708,7 +1782,7 @@ fn runCommand(...@@ -1708,7 +1782,7 @@ fn runCommand(
1708 } else return failForeign(run, "-fqemu", argv[0], exe);1782 } else return failForeign(run, "-fqemu", argv[0], exe);
1709 },1783 },
1710 .darling => |bin_name| {1784 .darling => |bin_name| {
1711 if (b.enable_darling) {1785 if (graph.enable_darling) {
1712 try interp_argv.append(bin_name);1786 try interp_argv.append(bin_name);
1713 try interp_argv.appendSlice(argv);1787 try interp_argv.appendSlice(argv);
1714 } else {1788 } else {
...@@ -1716,7 +1790,7 @@ fn runCommand(...@@ -1716,7 +1790,7 @@ fn runCommand(
1716 }1790 }
1717 },1791 },
1718 .wasmtime => |bin_name| {1792 .wasmtime => |bin_name| {
1719 if (b.enable_wasmtime) {1793 if (graph.enable_wasmtime) {
1720 try interp_argv.append(bin_name);1794 try interp_argv.append(bin_name);
1721 try interp_argv.append("--dir=.");1795 try interp_argv.append("--dir=.");
1722 // Wasmtime doeesn't inherit environment variables from the parent process1796 // Wasmtime doeesn't inherit environment variables from the parent process
...@@ -1743,8 +1817,8 @@ fn runCommand(...@@ -1743,8 +1817,8 @@ fn runCommand(
1743 .bad_os_or_cpu => {1817 .bad_os_or_cpu => {
1744 if (allow_skip) return error.MakeSkipped;1818 if (allow_skip) return error.MakeSkipped;
17451819
1746 const host_name = try graph.host.result.zigTriple(b.allocator);1820 const host_name = try graph.host.result.zigTriple(arena);
1747 const foreign_name = try root_target.zigTriple(b.allocator);1821 const foreign_name = try root_target.zigTriple(arena);
17481822
1749 return step.fail(maker, "the host system ({s}) is unable to execute binaries from the target ({s})", .{1823 return step.fail(maker, "the host system ({s}) is unable to execute binaries from the target ({s})", .{
1750 host_name, foreign_name,1824 host_name, foreign_name,
...@@ -1761,7 +1835,7 @@ fn runCommand(...@@ -1761,7 +1835,7 @@ fn runCommand(
1761 step.result_failed_command = null;1835 step.result_failed_command = null;
1762 try Step.handleVerbose(step.owner, cwd, run.environ_map, interp_argv.items);1836 try Step.handleVerbose(step.owner, cwd, run.environ_map, interp_argv.items);
17631837
1764 break :term spawnChildAndCollect(run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| {1838 break :term spawnChildAndCollect(run_index, run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| {
1765 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;1839 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
1766 if (e == error.MakeFailed) return error.MakeFailed; // error already reported1840 if (e == error.MakeFailed) return error.MakeFailed; // error already reported
1767 return step.fail(maker, "unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });1841 return step.fail(maker, "unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });
...@@ -1800,14 +1874,14 @@ fn runCommand(...@@ -1800,14 +1874,14 @@ fn runCommand(
1800 }) |stream| {1874 }) |stream| {
1801 if (stream.captured) |captured| {1875 if (stream.captured) |captured| {
1802 const output_components = .{ output_dir_path, captured.output.basename };1876 const output_components = .{ output_dir_path, captured.output.basename };
1803 const output_path = try b.cache_root.join(arena, &output_components);1877 const output_path = try cache_root.join(arena, &output_components);
1804 captured.output.generated_file.path = output_path;1878 captured.output.generated_file.path = output_path;
18051879
1806 const sub_path = b.pathJoin(&output_components);1880 const sub_path = try Dir.path.join(arena, &output_components);
1807 const sub_path_dirname = Dir.path.dirname(sub_path).?;1881 const sub_path_dirname = Dir.path.dirname(sub_path).?;
1808 b.cache_root.handle.createDirPath(io, sub_path_dirname) catch |err| {1882 cache_root.handle.createDirPath(io, sub_path_dirname) catch |err| {
1809 return step.fail(maker, "unable to make path '{f}{s}': {s}", .{1883 return step.fail(maker, "unable to make path '{f}{s}': {t}", .{
1810 b.cache_root, sub_path_dirname, @errorName(err),1884 cache_root, sub_path_dirname, err,
1811 });1885 });
1812 };1886 };
1813 const data = switch (captured.trim_whitespace) {1887 const data = switch (captured.trim_whitespace) {
...@@ -1816,9 +1890,9 @@ fn runCommand(...@@ -1816,9 +1890,9 @@ fn runCommand(
1816 .leading => mem.trimStart(u8, stream.bytes.?, &std.ascii.whitespace),1890 .leading => mem.trimStart(u8, stream.bytes.?, &std.ascii.whitespace),
1817 .trailing => mem.trimEnd(u8, stream.bytes.?, &std.ascii.whitespace),1891 .trailing => mem.trimEnd(u8, stream.bytes.?, &std.ascii.whitespace),
1818 };1892 };
1819 b.cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = data }) catch |err| {1893 cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = data }) catch |err| {
1820 return step.fail(maker, "unable to write file '{f}{s}': {s}", .{1894 return step.fail(maker, "unable to write file '{f}{s}': {t}", .{
1821 b.cache_root, sub_path, @errorName(err),1895 cache_root, sub_path, err,
1822 });1896 });
1823 };1897 };
1824 }1898 }
...@@ -1912,6 +1986,7 @@ const EvalGenericResult = struct {...@@ -1912,6 +1986,7 @@ const EvalGenericResult = struct {
1912};1986};
19131987
1914fn spawnChildAndCollect(1988fn spawnChildAndCollect(
1989 run_index: Configuration.Step.Index,
1915 run: *Run,1990 run: *Run,
1916 maker: *Maker,1991 maker: *Maker,
1917 progress_node: std.Progress.Node,1992 progress_node: std.Progress.Node,
...@@ -1920,25 +1995,34 @@ fn spawnChildAndCollect(...@@ -1920,25 +1995,34 @@ fn spawnChildAndCollect(
1920 has_side_effects: bool,1995 has_side_effects: bool,
1921 fuzz_context: ?FuzzContext,1996 fuzz_context: ?FuzzContext,
1922) !?EvalGenericResult {1997) !?EvalGenericResult {
1923 const b = run.step.owner;1998 const step = run.step;
1924 const graph = maker.graph;1999 const graph = maker.graph;
1925 const gpa = maker.gpa;2000 const gpa = maker.gpa;
1926 const io = graph.io;2001 const io = graph.io;
2002 const arena = graph.arena; // TODO don't leak into process arena
2003 const conf = &maker.scanned_config.configuration;
2004 const conf_step = run_index.ptr(conf);
2005 const conf_run = conf_step.extended.get(conf.extra).run;
19272006
1928 if (fuzz_context != null) {2007 if (fuzz_context != null) {
1929 assert(!has_side_effects);2008 assert(!has_side_effects);
1930 assert(run.stdio == .zig_test);2009 assert(run.stdio == .zig_test);
1931 }2010 }
19322011
1933 const child_cwd: process.Child.Cwd = if (run.cwd) |lazy_cwd| .{ .path = lazy_cwd.getPath2(b, &run.step) } else .inherit;2012 const child_cwd: process.Child.Cwd = if (conf_run.cwd) |lazy_cwd|
2013 .{ .path = try maker.resolveLazyPathIndexAbs(arena, lazy_cwd, run_index) }
2014 else
2015 .inherit;
19342016
1935 // If an error occurs, it's caused by this command:2017 // If an error occurs, it's caused by this command:
1936 assert(run.step.result_failed_command == null);2018 assert(step.result_failed_command == null);
1937 run.step.result_failed_command = try Step.allocPrintCmd(gpa, child_cwd, .{2019 step.result_failed_command = try Step.allocPrintCmd(gpa, child_cwd, .{
1938 .child = environ_map,2020 .child = environ_map,
1939 .parent = &graph.environ_map,2021 .parent = &graph.environ_map,
1940 }, argv);2022 }, argv);
19412023
2024 try step.handleChildProcUnsupported(maker);
2025
1942 var spawn_options: process.SpawnOptions = .{2026 var spawn_options: process.SpawnOptions = .{
1943 .argv = argv,2027 .argv = argv,
1944 .cwd = child_cwd,2028 .cwd = child_cwd,
...@@ -1973,7 +2057,7 @@ fn spawnChildAndCollect(...@@ -1973,7 +2057,7 @@ fn spawnChildAndCollect(
1973 error.Canceled => |e| return e,2057 error.Canceled => |e| return e,
1974 else => |e| e,2058 else => |e| e,
1975 };2059 };
1976 run.step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds);2060 step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds);
1977 try result;2061 try result;
1978 return null;2062 return null;
1979 } else {2063 } else {
...@@ -1993,7 +2077,7 @@ fn spawnChildAndCollect(...@@ -1993,7 +2077,7 @@ fn spawnChildAndCollect(
1993 error.Canceled => |e| return e,2077 error.Canceled => |e| return e,
1994 else => |e| e,2078 else => |e| e,
1995 };2079 };
1996 run.step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds);2080 step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds);
1997 return try result;2081 return try result;
1998 }2082 }
1999}2083}
lib/compiler/configurer.zig+2-3
...@@ -455,7 +455,7 @@ const Serialize = struct {...@@ -455,7 +455,7 @@ const Serialize = struct {
455 .producer = .{ .value = null },455 .producer = .{ .value = null },
456 .generated = .{ .value = null },456 .generated = .{ .value = null },
457 },457 },
458 .output_file => |a| .{458 .output_file, .output_file_dep => |a, tag| .{
459 .flags = .{459 .flags = .{
460 .tag = .output_file,460 .tag = .output_file,
461 .prefix = a.prefix.len != 0,461 .prefix = a.prefix.len != 0,
...@@ -464,7 +464,7 @@ const Serialize = struct {...@@ -464,7 +464,7 @@ const Serialize = struct {
464 .path = false,464 .path = false,
465 .producer = false,465 .producer = false,
466 .generated = true,466 .generated = true,
467 .dep_file = false,467 .dep_file = tag == .output_file_dep,
468 },468 },
469 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },469 .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
470 .suffix = .{ .value = null },470 .suffix = .{ .value = null },
...@@ -1023,7 +1023,6 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {...@@ -1023,7 +1023,6 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
1023 break :e @enumFromInt(extra_index);1023 break :e @enumFromInt(extra_index);
1024 },1024 },
1025 .check_file => @panic("TODO"),1025 .check_file => @panic("TODO"),
1026 .check_object => @panic("TODO"),
1027 .config_header => @panic("TODO"),1026 .config_header => @panic("TODO"),
1028 .objcopy => @panic("TODO"),1027 .objcopy => @panic("TODO"),
1029 .options => @panic("TODO"),1028 .options => @panic("TODO"),
lib/std/Build/Configuration.zig-11
...@@ -427,7 +427,6 @@ pub const Step = extern struct {...@@ -427,7 +427,6 @@ pub const Step = extern struct {
427 max_rss: MaxRss,427 max_rss: MaxRss,
428 extended: Storage.Extended(Flags, union(Tag) {428 extended: Storage.Extended(Flags, union(Tag) {
429 check_file: CheckFile,429 check_file: CheckFile,
430 check_object: CheckObject,
431 compile: Compile,430 compile: Compile,
432 config_header: ConfigHeader,431 config_header: ConfigHeader,
433 fail: Fail,432 fail: Fail,
...@@ -462,7 +461,6 @@ pub const Step = extern struct {...@@ -462,7 +461,6 @@ pub const Step = extern struct {
462461
463 pub const Tag = enum(u5) {462 pub const Tag = enum(u5) {
464 check_file,463 check_file,
465 check_object,
466 compile,464 compile,
467 config_header,465 config_header,
468 fail,466 fail,
...@@ -997,15 +995,6 @@ pub const Step = extern struct {...@@ -997,15 +995,6 @@ pub const Step = extern struct {
997 };995 };
998 };996 };
999997
1000 pub const CheckObject = struct {
1001 flags: @This().Flags,
1002
1003 pub const Flags = packed struct(u32) {
1004 tag: Tag = .check_object,
1005 _: u27 = 0,
1006 };
1007 };
1008
1009 pub const ConfigHeader = struct {998 pub const ConfigHeader = struct {
1010 flags: @This().Flags,999 flags: @This().Flags,
10111000
lib/std/Build/Step/Run.zig+3-9
...@@ -85,8 +85,6 @@ stdio_limit: std.Io.Limit,...@@ -85,8 +85,6 @@ stdio_limit: std.Io.Limit,
85captured_stdout: ?*CapturedStdIo,85captured_stdout: ?*CapturedStdIo,
86captured_stderr: ?*CapturedStdIo,86captured_stderr: ?*CapturedStdIo,
8787
88dep_output_file: ?*Output,
89
90has_side_effects: bool,88has_side_effects: bool,
91test_runner_mode: bool = false,89test_runner_mode: bool = false,
9290
...@@ -141,6 +139,7 @@ pub const Arg = union(enum) {...@@ -141,6 +139,7 @@ pub const Arg = union(enum) {
141 file_content: PrefixedLazyPath,139 file_content: PrefixedLazyPath,
142 bytes: []const u8,140 bytes: []const u8,
143 output_file: *Output,141 output_file: *Output,
142 output_file_dep: *Output,
144 output_directory: *Output,143 output_directory: *Output,
145 /// The arguments passed after "--" on the "zig build" CLI.144 /// The arguments passed after "--" on the "zig build" CLI.
146 cli_rest_positionals,145 cli_rest_positionals,
...@@ -203,7 +202,6 @@ pub fn create(owner: *std.Build, name: []const u8) *Run {...@@ -203,7 +202,6 @@ pub fn create(owner: *std.Build, name: []const u8) *Run {
203 .stdio_limit = .unlimited,202 .stdio_limit = .unlimited,
204 .captured_stdout = null,203 .captured_stdout = null,
205 .captured_stderr = null,204 .captured_stderr = null,
206 .dep_output_file = null,
207 .has_side_effects = false,205 .has_side_effects = false,
208 .producer = null,206 .producer = null,
209 };207 };
...@@ -476,12 +474,10 @@ pub fn addDepFileOutputArg(run: *Run, basename: []const u8) std.Build.LazyPath {...@@ -476,12 +474,10 @@ pub fn addDepFileOutputArg(run: *Run, basename: []const u8) std.Build.LazyPath {
476474
477/// Add a prefixed path argument to a dep file (.d) for the child process to475/// Add a prefixed path argument to a dep file (.d) for the child process to
478/// write its discovered additional dependencies.476/// write its discovered additional dependencies.
479/// Only one dep file argument is allowed by instance.
480pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath {477pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath {
481 const b = run.step.owner;478 const b = run.step.owner;
482 const graph = b.graph;479 const graph = b.graph;
483 const arena = graph.arena;480 const arena = graph.arena;
484 assert(run.dep_output_file == null);
485481
486 const dep_file = arena.create(Output) catch @panic("OOM");482 const dep_file = arena.create(Output) catch @panic("OOM");
487 dep_file.* = .{483 dep_file.* = .{
...@@ -490,9 +486,7 @@ pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []co...@@ -490,9 +486,7 @@ pub fn addPrefixedDepFileOutputArg(run: *Run, prefix: []const u8, basename: []co
490 .generated_file = graph.addGeneratedFile(&run.step),486 .generated_file = graph.addGeneratedFile(&run.step),
491 };487 };
492488
493 run.dep_output_file = dep_file;489 run.argv.append(arena, .{ .output_file_dep = dep_file }) catch @panic("OOM");
494
495 run.argv.append(arena, .{ .output_file = dep_file }) catch @panic("OOM");
496490
497 return .{ .generated = .{ .index = dep_file.generated_file } };491 return .{ .generated = .{ .index = dep_file.generated_file } };
498}492}
...@@ -544,7 +538,7 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void {...@@ -544,7 +538,7 @@ pub fn addPathDir(run: *Run, search_path: []const u8) void {
544 .decorated_directory => false,538 .decorated_directory => false,
545 .file_content => unreachable, // not allowed as first arg539 .file_content => unreachable, // not allowed as first arg
546 .bytes => |bytes| std.mem.endsWith(u8, bytes, ".exe"),540 .bytes => |bytes| std.mem.endsWith(u8, bytes, ".exe"),
547 .output_file, .output_directory => false,541 .output_file, .output_file_dep, .output_directory => false,
548 };542 };
549 const key = if (use_wine) "WINEPATH" else "PATH";543 const key = if (use_wine) "WINEPATH" else "PATH";
550 const prev_path = environ_map.get(key);544 const prev_path = environ_map.get(key);