authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-22 21:42:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:34-07:00
loga399d37886bfb8358e2f93744f0dd3f59542dcee
tree073b4d050fd92cc2009d06a78cf3ec3d4d498d60
parent81ee4ab32c8617af3ce9690d562131a6a0924f1c

maker: upgrade some of the run step logic


6 files changed, 175 insertions(+), 118 deletions(-)

BRANCH_TODO+1
......@@ -18,5 +18,6 @@
1818* link_eh_frame_hdr should be DefaultingBool
1919* make --foo, --no-foo CLI args uniform (make them -f args instead)
2020* install steps should provide generated files for installed things, then delete the run step hack
21 - but artifact install steps also add paths for dyn libs on windows
2122
2223
lib/compiler/Maker.zig+2
......@@ -35,6 +35,7 @@ install_paths: InstallPaths,
3535scanned_config: *const ScannedConfig,
3636steps: []Step,
3737generated_files: []Path,
38run_args: ?[]const []const u8,
3839
3940available_rss: usize,
4041max_rss_is_default: bool,
......@@ -534,6 +535,7 @@ pub fn main(init: process.Init.Minimal) !void {
534535 },
535536 .steps = try arena.alloc(Step, scanned_config.configuration.steps.len),
536537 .generated_files = try arena.alloc(Path, scanned_config.configuration.generated_files_len),
538 .run_args = run_args,
537539
538540 .available_rss = max_rss,
539541 .max_rss_is_default = false,
lib/compiler/Maker/Fuzz.zig+1-1
......@@ -203,7 +203,7 @@ fn fuzzWorkerRun(fuzz: *Fuzz, run: Configuration.Step.Index) void {
203203 const graph = owner.graph;
204204 const io = graph.io;
205205
206 run.rerunInFuzzMode(fuzz, fuzz.prog_node) catch |err| switch (err) {
206 run.rerunInFuzzMode(run, fuzz, fuzz.prog_node) catch |err| switch (err) {
207207 error.MakeFailed => {
208208 var buf: [256]u8 = undefined;
209209 const stderr = io.lockStderr(&buf, graph.stderr_mode) catch |e| switch (e) {
lib/compiler/Maker/Step/Run.zig+165-116
......@@ -12,6 +12,7 @@ const Path = std.Build.Cache.Path;
1212const assert = std.debug.assert;
1313const mem = std.mem;
1414const process = std.process;
15const allocPrint = std.fmt.allocPrint;
1516
1617const Step = @import("../Step.zig");
1718const Maker = @import("../../Maker.zig");
......@@ -26,115 +27,158 @@ cached_test_metadata: ?CachedTestMetadata = null,
2627/// executable that contains fuzz tests.
2728rebuilt_executable: ?Path = null,
2829
30/// Persisted to reuse memory on subsequent calls to `make`.
31argv: std.ArrayList([]const u8) = .empty,
32/// Persisted to reuse memory on subsequent calls to `make`.
33output_placeholders: std.ArrayList(IndexedOutput) = .empty,
34
2935pub fn make(
3036 run: *Run,
31 step_index: Configuration.Step.Index,
37 run_index: Configuration.Step.Index,
3238 maker: *Maker,
3339 progress_node: std.Progress.Node,
3440) Step.ExtendedMakeError!void {
35 if (true) @panic("TODO implement run.make()");
3641 const graph = maker.graph;
37 const step = maker.stepByIndex(step_index);
42 const gpa = maker.gpa;
43 const step = maker.stepByIndex(run_index);
3844 const io = graph.io;
3945 const arena = graph.arena; // TODO don't leak into the process arena
40 const has_side_effects = run.hasSideEffects();
46 const conf = &maker.scanned_config.configuration;
47 const conf_step = run_index.ptr(conf);
48 const conf_run = conf_step.extended.get(conf.extra).run;
49 const argv_list = &run.argv;
50 const output_placeholders = &run.output_placeholders;
4151
42 var argv_list = std.array_list.Managed([]const u8).init(arena);
43 var output_placeholders = std.array_list.Managed(IndexedOutput).init(arena);
52 argv_list.clearRetainingCapacity();
53 output_placeholders.clearRetainingCapacity();
4454
4555 var man = graph.cache.obtain();
4656 defer man.deinit();
4757
48 if (run.environ_map) |environ_map| {
49 for (environ_map.keys(), environ_map.values()) |key, value| {
50 man.hash.addBytes(key);
51 man.hash.addBytes(value);
58 if (conf_run.environ_map.value) |environ_map_index| {
59 const environ_map = environ_map_index.get(conf);
60 for (environ_map.keys.slice(conf), environ_map.values.slice(conf)) |key, value| {
61 man.hash.addBytesZ(key.slice(conf));
62 man.hash.addBytesZ(value.slice(conf));
5263 }
5364 }
5465
55 man.hash.add(run.color);
56 man.hash.add(run.disable_zig_progress);
57
58 for (run.argv.items) |arg| {
59 switch (arg) {
60 .bytes => |bytes| {
61 try argv_list.append(bytes);
62 man.hash.addBytes(bytes);
66 man.hash.add(conf_run.flags.color);
67 man.hash.add(conf_run.flags.disable_zig_progress);
68
69 for (conf_run.args.slice) |arg_index| {
70 const arg = arg_index.get(conf);
71 try argv_list.ensureUnusedCapacity(gpa, 1);
72 switch (arg.flags.tag) {
73 .string => {
74 const prefix = arg.prefix.value.?.slice(conf);
75 argv_list.appendAssumeCapacity(prefix);
76 man.hash.addBytesZ(prefix);
6377 },
64 .lazy_path => |file| {
65 const file_path = file.lazy_path.getPath3(graph, step);
66 try argv_list.append(graph.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) }));
67 man.hash.addBytes(file.prefix);
78 .path_file => {
79 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
80 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
81 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
82 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
83 prefix, try convertPathArg(run_index, maker, file_path), suffix,
84 }));
85 man.hash.addBytesZ(prefix);
86 man.hash.addBytesZ(suffix);
6887 _ = try man.addFilePath(file_path, null);
6988 },
70 .decorated_directory => |dd| {
71 const file_path = dd.lazy_path.getPath3(graph, step);
72 const resolved_arg = graph.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix });
73 try argv_list.append(resolved_arg);
89 .path_directory => {
90 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
91 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
92 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
93 const resolved_arg = try mem.concat(arena, u8, &.{
94 prefix, try convertPathArg(run_index, maker, file_path), suffix,
95 });
96 argv_list.appendAssumeCapacity(resolved_arg);
7497 man.hash.addBytes(resolved_arg);
7598 },
76 .file_content => |file_plp| {
77 const file_path = file_plp.lazy_path.getPath3(graph, step);
99 .file_content => {
100 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
101 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
102 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
78103
79104 var result: std.Io.Writer.Allocating = .init(arena);
80 errdefer result.deinit();
81 result.writer.writeAll(file_plp.prefix) catch return error.OutOfMemory;
105 result.writer.writeAll(prefix) catch return error.OutOfMemory;
82106
83 const file = file_path.root_dir.handle.openFile(io, file_path.subPathOrDot(), .{}) catch |err| {
84 return step.fail(
85 "unable to open input file '{f}': {t}",
86 .{ file_path, err },
87 );
88 };
107 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
108 return step.fail(maker, "unable to open input file {f}: {t}", .{ file_path, err });
89109 defer file.close(io);
90110
91 var buf: [1024]u8 = undefined;
92 var file_reader = file.reader(io, &buf);
111 var file_reader = file.reader(io, &.{});
93112 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
94 error.ReadFailed => return step.fail(
95 "failed to read from '{f}': {t}",
96 .{ file_path, file_reader.err.? },
97 ),
113 error.ReadFailed => switch (file_reader.err.?) {
114 error.Canceled => |e| return e,
115 else => |e| return step.fail(maker, "failed to read from {f}: {t}", .{ file_path, e }),
116 },
98117 error.WriteFailed => return error.OutOfMemory,
99118 };
119 result.writer.writeAll(suffix) catch return error.OutOfMemory;
100120
101 try argv_list.append(result.written());
102 man.hash.addBytes(file_plp.prefix);
121 argv_list.appendAssumeCapacity(result.written());
122 man.hash.addBytesZ(prefix);
123 man.hash.addBytesZ(suffix);
103124 _ = try man.addFilePath(file_path, null);
104125 },
105 .artifact => |pa| {
106 const artifact = pa.artifact;
107
108 if (artifact.rootModuleTarget().os.tag == .windows) {
126 .artifact => {
127 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
128 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
129 const producer_index = arg.producer.value.?;
130 const producer_step = producer_index.ptr(conf);
131 const producer = producer_step.extended.get(conf.extra).compile;
132 const root_module = producer.root_module.get(conf);
133 const root_module_target = root_module.resolved_target.get(conf).?.result.get(conf);
134 const os_tag = root_module_target.flags.os_tag.unwrap().?;
135
136 if (true) @panic("TODO");
137
138 if (os_tag == .windows) {
109139 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
110 addPathForDynLibs(artifact);
140 addPathForDynLibs(producer_index);
111141 }
112 const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?;
142 const file_path = producer_index.installed_path orelse producer_index.generated_bin.?.path.?;
113143
114 try argv_list.append(graph.fmt("{s}{s}", .{
115 pa.prefix,
116 run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
144 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
145 prefix,
146 try convertPathArg(run_index, maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
147 suffix,
117148 }));
118149
119150 _ = try man.addFile(file_path, null);
120151 },
121 .output_file, .output_directory => |output| {
122 man.hash.addBytes(output.prefix);
123 man.hash.addBytes(output.basename);
152 .output_file, .output_directory => {
153 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
154 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
155 const basename = arg.basename.value.?.slice(conf);
156
157 man.hash.addBytesZ(prefix);
158 man.hash.addBytesZ(basename);
159 man.hash.addBytesZ(suffix);
160
124161 // Add a placeholder into the argument list because we need the
125162 // manifest hash to be updated with all arguments before the
126163 // object directory is computed.
127 try output_placeholders.append(.{
128 .index = argv_list.items.len,
129 .tag = arg,
130 .output = output,
164 try output_placeholders.append(gpa, .{
165 .index = @intCast(argv_list.items.len),
166 .arg_index = arg_index,
131167 });
132 _ = try argv_list.addOne();
168 argv_list.items.len += 1;
169 },
170 .cli_rest_positionals => {
171 if (maker.run_args) |run_args| {
172 try argv_list.appendSlice(gpa, run_args);
173 for (run_args) |s| man.hash.addBytes(s);
174 }
133175 },
134176 }
135177 }
136178
137 switch (run.stdin) {
179 if (true) @panic("TODO");
180
181 switch (conf_run.stdin.u) {
138182 .bytes => |bytes| {
139183 man.hash.addBytes(bytes);
140184 },
......@@ -145,28 +189,30 @@ pub fn make(
145189 .none => {},
146190 }
147191
148 if (run.captured_stdout) |captured| {
192 if (conf_run.captured_stdout) |captured| {
149193 man.hash.addBytes(captured.output.basename);
150194 man.hash.add(captured.trim_whitespace);
151195 }
152196
153 if (run.captured_stderr) |captured| {
197 if (conf_run.captured_stderr) |captured| {
154198 man.hash.addBytes(captured.output.basename);
155199 man.hash.add(captured.trim_whitespace);
156200 }
157201
158202 std.log.err("TODO hashStdIo", .{});
159 //hashStdIo(&man.hash, run.stdio);
203 //hashStdIo(&man.hash, conf_run.stdio);
160204
161 for (run.file_inputs.items) |lazy_path| {
205 for (conf_run.file_inputs.items) |lazy_path| {
162206 _ = try man.addFile(lazy_path.getPath2(graph, step), null);
163207 }
164208
165 if (run.cwd) |cwd| {
209 if (conf_run.cwd) |cwd| {
166210 const cwd_path = cwd.getPath3(graph, step);
167211 _ = man.hash.addBytes(try cwd_path.toString(arena));
168212 }
169213
214 const has_side_effects = conf_run.flags.has_side_effects;
215
170216 if (!has_side_effects and try step.cacheHitAndWatch(&man)) {
171217 // cache hit, skip running command
172218 const digest = man.final();
......@@ -182,7 +228,7 @@ pub fn make(
182228 return;
183229 }
184230
185 const dep_output_file = run.dep_output_file orelse {
231 const dep_output_file = conf_run.dep_output_file orelse {
186232 // We already know the final output paths, use them directly.
187233 const digest = if (has_side_effects)
188234 man.hash.final()
......@@ -205,18 +251,18 @@ pub fn make(
205251 else => unreachable,
206252 };
207253 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
208 return step.fail("unable to make path '{f}{s}': {t}", .{
254 return step.fail(maker, "unable to make path '{f}{s}': {t}", .{
209255 graph.cache_root, output_sub_dir_path, err,
210256 });
211257 };
212 const arg_output_path = run.convertPathArg(maker, .{
258 const arg_output_path = try convertPathArg(run_index, maker, .{
213259 .root_dir = .cwd(),
214260 .sub_path = placeholder.output.generated_file.getPath(),
215261 });
216262 argv_list.items[placeholder.index] = if (placeholder.output.prefix.len == 0)
217263 arg_output_path
218264 else
219 graph.fmt("{s}{s}", .{ placeholder.output.prefix, arg_output_path });
265 try allocPrint(arena, "{s}{s}", .{ placeholder.output.prefix, arg_output_path });
220266 }
221267
222268 try runCommand(run, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null);
......@@ -238,7 +284,7 @@ pub fn make(
238284 else => unreachable,
239285 };
240286 graph.cache_root.handle.createDirPath(io, output_sub_dir_path) catch |err| {
241 return step.fail("unable to make path '{f}{s}': {t}", .{
287 return step.fail(maker, "unable to make path '{f}{s}': {t}", .{
242288 graph.cache_root, output_sub_dir_path, err,
243289 });
244290 };
......@@ -247,9 +293,9 @@ pub fn make(
247293 .sub_path = graph.pathJoin(&output_components),
248294 };
249295 placeholder.output.generated_file.path = raw_output_path.toString(arena) catch @panic("OOM");
250 argv_list.items[placeholder.index] = graph.fmt("{s}{s}", .{
296 argv_list.items[placeholder.index] = try mem.concat(arena, u8, .{
251297 placeholder.output.prefix,
252 run.convertPathArg(maker, raw_output_path),
298 try convertPathArg(run_index, maker, raw_output_path),
253299 });
254300 }
255301
......@@ -268,7 +314,7 @@ pub fn make(
268314 man.final();
269315
270316 const any_output = output_placeholders.items.len > 0 or
271 run.captured_stdout != null or run.captured_stderr != null;
317 conf_run.captured_stdout != null or conf_run.captured_stderr != null;
272318
273319 // Rename into place
274320 if (any_output) {
......@@ -277,17 +323,17 @@ pub fn make(
277323 graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |err| switch (err) {
278324 Dir.RenameError.DirNotEmpty => {
279325 graph.cache_root.handle.deleteTree(io, o_sub_path) catch |del_err| {
280 return step.fail("unable to remove dir '{f}'{s}: {t}", .{
326 return step.fail(maker, "unable to remove dir '{f}'{s}: {t}", .{
281327 graph.cache_root, tmp_dir_path, del_err,
282328 });
283329 };
284330 graph.cache_root.handle.rename(tmp_dir_path, graph.cache_root.handle, o_sub_path, io) catch |retry_err| {
285 return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{
331 return step.fail(maker, "unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{
286332 graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, retry_err,
287333 });
288334 };
289335 },
290 else => return step.fail("unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{
336 else => return step.fail(maker, "unable to rename dir '{f}{s}' to '{f}{s}': {t}", .{
291337 graph.cache_root, tmp_dir_path, graph.cache_root, o_sub_path, err,
292338 }),
293339 };
......@@ -309,6 +355,7 @@ pub fn make(
309355/// * The wait fails, indicating the child closed stdout and stderr
310356fn waitZigTest(
311357 run: *Run,
358 maker: *Maker,
312359 child: *process.Child,
313360 options: Step.MakeOptions,
314361 multi_reader: *Io.File.MultiReader,
......@@ -412,6 +459,7 @@ fn waitZigTest(
412459 switch (header.tag) {
413460 .zig_version => {
414461 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return run.step.fail(
462 maker,
415463 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
416464 .{ builtin.zig_version_string, body },
417465 );
......@@ -1028,6 +1076,7 @@ const StdioPollEnum = enum { stdout, stderr };
10281076
10291077fn evalZigTest(
10301078 run: *Run,
1079 maker: *Maker,
10311080 spawn_options: process.SpawnOptions,
10321081 options: Step.MakeOptions,
10331082 fuzz_context: ?FuzzContext,
......@@ -1102,7 +1151,7 @@ fn evalZigTest(
11021151
11031152 // The individual unit test results are irrelevant: the test runner itself broke!
11041153 // Fail immediately without populating `s.test_results`.
1105 return run.step.fail("unable to write stdin ({t}); test process unexpectedly {f}", .{ err, fmtTerm(term) });
1154 return run.step.fail(maker, "unable to write stdin ({t}); test process unexpectedly {f}", .{ err, fmtTerm(term) });
11061155 },
11071156 .no_poll => |no_poll| {
11081157 // This might be a success (we requested exit and the child dutifully closed stdout) or
......@@ -1141,7 +1190,7 @@ fn evalZigTest(
11411190 if (!tests_done or !termMatches(.{ .exited = 0 }, term)) {
11421191 // The individual unit test results are irrelevant: the test runner itself broke!
11431192 // Fail immediately without populating `s.test_results`.
1144 return run.step.fail("test process unexpectedly {f}", .{fmtTerm(term)});
1193 return run.step.fail(maker, "test process unexpectedly {f}", .{fmtTerm(term)});
11451194 }
11461195
11471196 // We're done with all of the tests! Commit the test results and return.
......@@ -1181,7 +1230,7 @@ fn evalZigTest(
11811230 run.step.result_stderr = try arena.dupe(u8, stderr);
11821231 // The individual unit test results in `results` are irrelevant: the test runner
11831232 // is broken! Fail immediately without populating `s.test_results`.
1184 return run.step.fail("test runner failed to respond for {f}", .{Io.Duration{ .nanoseconds = timeout.ns_elapsed }});
1233 return run.step.fail(maker, "test runner failed to respond for {f}", .{Io.Duration{ .nanoseconds = timeout.ns_elapsed }});
11851234 },
11861235 }
11871236 comptime unreachable;
......@@ -1313,7 +1362,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
13131362 switch (run.stdin) {
13141363 .bytes => |bytes| {
13151364 child.stdin.?.writeStreamingAll(io, bytes) catch |err| {
1316 return run.step.fail("unable to write stdin: {t}", .{err});
1365 return run.step.fail(maker, "unable to write stdin: {t}", .{err});
13171366 };
13181367 child.stdin.?.close(io);
13191368 child.stdin = null;
......@@ -1321,7 +1370,7 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
13211370 .lazy_path => |lazy_path| {
13221371 const path = lazy_path.getPath3(graph, &run.step);
13231372 const file = path.root_dir.handle.openFile(io, path.subPathOrDot(), .{}) catch |err| {
1324 return run.step.fail("unable to open stdin file: {t}", .{err});
1373 return run.step.fail(maker, "unable to open stdin file: {t}", .{err});
13251374 };
13261375 defer file.close(io);
13271376 // TODO https://github.com/ziglang/zig/issues/23955
......@@ -1330,15 +1379,15 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
13301379 var write_buffer: [1024]u8 = undefined;
13311380 var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer);
13321381 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
1333 error.ReadFailed => return run.step.fail("failed to read from {f}: {t}", .{
1382 error.ReadFailed => return run.step.fail(maker, "failed to read from {f}: {t}", .{
13341383 path, file_reader.err.?,
13351384 }),
1336 error.WriteFailed => return run.step.fail("failed to write to stdin: {t}", .{
1385 error.WriteFailed => return run.step.fail(maker, "failed to write to stdin: {t}", .{
13371386 stdin_writer.err.?,
13381387 }),
13391388 };
13401389 stdin_writer.interface.flush() catch |err| switch (err) {
1341 error.WriteFailed => return run.step.fail("failed to write to stdin: {t}", .{
1390 error.WriteFailed => return run.step.fail(maker, "failed to write to stdin: {t}", .{
13421391 stdin_writer.err.?,
13431392 }),
13441393 };
......@@ -1418,15 +1467,13 @@ fn evalGeneric(run: *Run, maker: *Maker, spawn_options: process.SpawnOptions) !E
14181467}
14191468
14201469const IndexedOutput = struct {
1421 index: usize,
1422 tag: Configuration.Step.Run.Arg.Tag,
1423 output: *Output,
1470 index: u32,
1471 arg_index: Configuration.Step.Run.Arg.Index,
14241472};
14251473
1426const Output = void; // TODO
1427
14281474pub fn rerunInFuzzMode(
14291475 run: *Run,
1476 run_index: Configuration.Step.Index,
14301477 fuzz: *std.Build.Fuzz,
14311478 prog_node: std.Progress.Node,
14321479) !void {
......@@ -1444,11 +1491,11 @@ pub fn rerunInFuzzMode(
14441491 },
14451492 .lazy_path => |file| {
14461493 const file_path = file.lazy_path.getPath3(b, step);
1447 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, run.convertPathArg(maker, file_path) }));
1494 try argv_list.append(arena, b.fmt("{s}{s}", .{ file.prefix, convertPathArg(run_index, maker, file_path) }));
14481495 },
14491496 .decorated_directory => |dd| {
14501497 const file_path = dd.lazy_path.getPath3(b, step);
1451 try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, run.convertPathArg(maker, file_path), dd.suffix }));
1498 try argv_list.append(arena, b.fmt("{s}{s}{s}", .{ dd.prefix, convertPathArg(run_index, maker, file_path), dd.suffix }));
14521499 },
14531500 .file_content => |file_plp| {
14541501 const file_path = file_plp.lazy_path.getPath3(b, step);
......@@ -1477,7 +1524,7 @@ pub fn rerunInFuzzMode(
14771524 };
14781525 try argv_list.append(arena, b.fmt("{s}{s}", .{
14791526 pa.prefix,
1480 run.convertPathArg(maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
1527 convertPathArg(run_index, maker, .{ .root_dir = .cwd(), .sub_path = file_path }),
14811528 }));
14821529 },
14831530 .output_file, .output_directory => unreachable,
......@@ -1675,7 +1722,7 @@ fn runCommand(
16751722
16761723 const host_dl = graph.host.result.dynamic_linker.get() orelse "(none)";
16771724
1678 return step.fail(
1725 return step.fail(maker,
16791726 \\the host system is unable to execute binaries from the target
16801727 \\ because the host dynamic linker is '{s}',
16811728 \\ while the target dynamic linker is '{s}'.
......@@ -1688,7 +1735,7 @@ fn runCommand(
16881735 const host_name = try graph.host.result.zigTriple(b.allocator);
16891736 const foreign_name = try root_target.zigTriple(b.allocator);
16901737
1691 return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{
1738 return step.fail(maker, "the host system ({s}) is unable to execute binaries from the target ({s})", .{
16921739 host_name, foreign_name,
16931740 });
16941741 },
......@@ -1706,12 +1753,12 @@ fn runCommand(
17061753 break :term spawnChildAndCollect(run, maker, progress_node, interp_argv.items, &environ_map, has_side_effects, fuzz_context) catch |e| {
17071754 if (!run.failing_to_execute_foreign_is_an_error) return error.MakeSkipped;
17081755 if (e == error.MakeFailed) return error.MakeFailed; // error already reported
1709 return step.fail("unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });
1756 return step.fail(maker, "unable to spawn interpreter {s}: {t}", .{ interp_argv.items[0], e });
17101757 };
17111758 }
17121759 if (err == error.MakeFailed) return error.MakeFailed; // error already reported
17131760
1714 return step.fail("failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err });
1761 return step.fail(maker, "failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err });
17151762 };
17161763
17171764 const generic_result = opt_generic_result orelse {
......@@ -1748,7 +1795,7 @@ fn runCommand(
17481795 const sub_path = b.pathJoin(&output_components);
17491796 const sub_path_dirname = Dir.path.dirname(sub_path).?;
17501797 b.cache_root.handle.createDirPath(io, sub_path_dirname) catch |err| {
1751 return step.fail("unable to make path '{f}{s}': {s}", .{
1798 return step.fail(maker, "unable to make path '{f}{s}': {s}", .{
17521799 b.cache_root, sub_path_dirname, @errorName(err),
17531800 });
17541801 };
......@@ -1759,7 +1806,7 @@ fn runCommand(
17591806 .trailing => mem.trimEnd(u8, stream.bytes.?, &std.ascii.whitespace),
17601807 };
17611808 b.cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = data }) catch |err| {
1762 return step.fail("unable to write file '{f}{s}': {s}", .{
1809 return step.fail(maker, "unable to write file '{f}{s}': {s}", .{
17631810 b.cache_root, sub_path, @errorName(err),
17641811 });
17651812 };
......@@ -1771,7 +1818,7 @@ fn runCommand(
17711818 .check => |checks| for (checks.items) |check| switch (check) {
17721819 .expect_stderr_exact => |expected_bytes| {
17731820 if (!mem.eql(u8, expected_bytes, generic_result.stderr.?)) {
1774 return step.fail(
1821 return step.fail(maker,
17751822 \\========= expected this stderr: =========
17761823 \\{s}
17771824 \\========= but found: ====================
......@@ -1784,7 +1831,7 @@ fn runCommand(
17841831 },
17851832 .expect_stderr_match => |match| {
17861833 if (mem.find(u8, generic_result.stderr.?, match) == null) {
1787 return step.fail(
1834 return step.fail(maker,
17881835 \\========= expected to find in stderr: =========
17891836 \\{s}
17901837 \\========= but stderr does not contain it: =====
......@@ -1797,7 +1844,7 @@ fn runCommand(
17971844 },
17981845 .expect_stdout_exact => |expected_bytes| {
17991846 if (!mem.eql(u8, expected_bytes, generic_result.stdout.?)) {
1800 return step.fail(
1847 return step.fail(maker,
18011848 \\========= expected this stdout: =========
18021849 \\{s}
18031850 \\========= but found: ====================
......@@ -1810,7 +1857,7 @@ fn runCommand(
18101857 },
18111858 .expect_stdout_match => |match| {
18121859 if (mem.find(u8, generic_result.stdout.?, match) == null) {
1813 return step.fail(
1860 return step.fail(maker,
18141861 \\========= expected to find in stdout: =========
18151862 \\{s}
18161863 \\========= but stdout does not contain it: =====
......@@ -1823,7 +1870,7 @@ fn runCommand(
18231870 },
18241871 .expect_term => |expected_term| {
18251872 if (!termMatches(expected_term, generic_result.term)) {
1826 return step.fail("process {f} (expected {f})", .{
1873 return step.fail(maker, "process {f} (expected {f})", .{
18271874 fmtTerm(generic_result.term),
18281875 fmtTerm(expected_term),
18291876 });
......@@ -2069,21 +2116,23 @@ fn hasAnyOutputArgs(run: Run) bool {
20692116///
20702117/// Whenever a path is included in the argv of a child, it should be put through this function first
20712118/// to make sure the child doesn't see paths relative to a cwd other than its own.
2072fn convertPathArg(run: *Run, maker: *Maker, path: Path) []const u8 {
2073 const b = run.step.owner;
2119fn convertPathArg(run_index: Configuration.Step.Index, maker: *Maker, path: Path) ![]const u8 {
2120 const conf = &maker.scanned_config.configuration;
2121 const conf_step = run_index.ptr(conf);
2122 const conf_run = conf_step.extended.get(conf.extra).run;
20742123 const graph = maker.graph;
2075 const arena = graph.arena;
2124 const arena = graph.arena; // TODO don't leak into process arena
20762125
2077 const path_str = path.toString(arena) catch @panic("OOM");
2126 const path_str = try path.toString(arena);
20782127 if (Dir.path.isAbsolute(path_str)) {
20792128 // Absolute paths don't need changing.
20802129 return path_str;
20812130 }
20822131 const child_cwd_rel: []const u8 = rel: {
2083 const child_lazy_cwd = run.cwd orelse break :rel path_str;
2084 const child_cwd = child_lazy_cwd.getPath3(b, &run.step).toString(arena) catch @panic("OOM");
2132 const child_lazy_cwd = conf_run.cwd.value orelse break :rel path_str;
2133 const child_cwd = try maker.resolveLazyPathIndexAbs(arena, child_lazy_cwd, run_index);
20852134 // Convert it from relative to *our* cwd, to relative to the *child's* cwd.
2086 break :rel Dir.path.relative(arena, graph.cache.cwd, &graph.environ_map, child_cwd, path_str) catch @panic("OOM");
2135 break :rel try Dir.path.relative(arena, graph.cache.cwd, &graph.environ_map, child_cwd, path_str);
20872136 };
20882137 // Not every path can be made relative, e.g. if the path and the child cwd are on different
20892138 // disk designators on Windows. In that case, `relative` will return an absolute path which we can
......@@ -2094,10 +2143,10 @@ fn convertPathArg(run: *Run, maker: *Maker, path: Path) []const u8 {
20942143 // * On POSIX, the executable name cannot be a single component like 'foo'
20952144 // * Some executables might treat a leading '-' like a flag, which we must avoid
20962145 // There's no harm in it, so just *always* apply this prefix.
2097 return Dir.path.join(arena, &.{ ".", child_cwd_rel }) catch @panic("OOM");
2146 return Dir.path.join(arena, &.{ ".", child_cwd_rel });
20982147}
20992148
2100fn addPathForDynLibs(artifact: *Step.Compile) void {
2149fn addPathForDynLibs(artifact: Configuration.Step.Index) void {
21012150 if (true) @panic("TODO");
21022151 for (artifact.getCompileDependencies(true)) |compile| {
21032152 if (compile.root_module.resolved_target.?.result.os.tag == .windows and
......@@ -2127,13 +2176,13 @@ fn failForeign(
21272176 const host_name = try graph.host.result.zigTriple(process_arena);
21282177 const foreign_name = try exe.rootModuleTarget().zigTriple(process_arena);
21292178
2130 return step.fail(
2179 return step.fail(maker,
21312180 \\unable to spawn foreign binary '{s}' ({s}) on host system ({s})
21322181 \\ consider using {s} or enabling skip_foreign_checks in the Run step
21332182 , .{ argv0, foreign_name, host_name, suggested_flag });
21342183 },
21352184 else => {
2136 return step.fail("unable to spawn foreign binary '{s}'", .{argv0});
2185 return step.fail(maker, "unable to spawn foreign binary '{s}'", .{argv0});
21372186 },
21382187 }
21392188}
lib/std/Build/Cache.zig+4-1
......@@ -189,12 +189,15 @@ pub const File = struct {
189189pub const HashHelper = struct {
190190 hasher: Hasher = hasher_init,
191191
192 /// Record a slice of bytes as a dependency of the process being cached.
193192 pub fn addBytes(hh: *HashHelper, bytes: []const u8) void {
194193 hh.hasher.update(mem.asBytes(&bytes.len));
195194 hh.hasher.update(bytes);
196195 }
197196
197 pub fn addBytesZ(hh: *HashHelper, bytes: [:0]const u8) void {
198 hh.hasher.update(mem.absorbSentinel(u8, 0, bytes));
199 }
200
198201 pub fn addOptionalBytes(hh: *HashHelper, optional_bytes: ?[]const u8) void {
199202 hh.add(optional_bytes != null);
200203 hh.addBytes(optional_bytes orelse return);
lib/std/Build/Configuration.zig+2
......@@ -561,8 +561,10 @@ pub const Step = extern struct {
561561
562562 pub const Tag = enum(u3) {
563563 artifact,
564 /// `path` contains the file.
564565 path_file,
565566 path_directory,
567 /// `prefix` contains the string.
566568 string,
567569 file_content,
568570 output_file,