authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-25 20:21:07-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-26 19:08:50-04:00
logaa3dd5d5d9a08191a0ebb3af639f35402d58ee87
tree90fee63761c8811baae58062a5f0cf0a96bb3986
parente0beb0222312a5c4737ccfe76e75e414f594bd9a

Maker.Step.Run: fix resource deallocation order on windows

The inherited file descriptors need to be closed before attempting to rename the output directory.

1 files changed, 549 insertions(+), 543 deletions(-)

lib/compiler/Maker/Step/Run.zig+549-543
...@@ -55,531 +55,564 @@ pub fn make(...@@ -55,531 +55,564 @@ pub fn make(
55 var man = graph.cache.obtain();55 var man = graph.cache.obtain();
56 defer man.deinit();56 defer man.deinit();
5757
58 if (conf_run.environ_map.value) |environ_map_index| {58 var tmp_dir_path: ["tmp".len + Dir.path.sep_str.len + std.fmt.hex(@as(u64, 0)).len]u8 = undefined;
59 const environ_map = environ_map_index.get(conf);59
60 for (environ_map.keys.slice(conf), environ_map.values.slice(conf)) |key, value| {60 const has_side_effects = has_side_effects: {
61 man.hash.addBytesZ(key.slice(conf));61 if (conf_run.environ_map.value) |environ_map_index| {
62 man.hash.addBytesZ(value.slice(conf));62 const environ_map = environ_map_index.get(conf);
63 for (environ_map.keys.slice(conf), environ_map.values.slice(conf)) |key, value| {
64 man.hash.addBytesZ(key.slice(conf));
65 man.hash.addBytesZ(value.slice(conf));
66 }
63 }67 }
64 }
6568
66 for (conf_run.preopens.slice) |preopen| {69 for (conf_run.preopens.slice) |preopen| {
67 man.hash.addBytesZ(preopen.name.slice(conf));70 man.hash.addBytesZ(preopen.name.slice(conf));
68 const cwd_path = try maker.resolveLazyPathIndex(arena, preopen.path, run_index);71 const cwd_path = try maker.resolveLazyPathIndex(arena, preopen.path, run_index);
69 man.hash.addBytes(try cwd_path.toString(arena));72 man.hash.addBytes(try cwd_path.toString(arena));
70 }73 }
7174
72 man.hash.add(graph.fuzzing);75 man.hash.add(graph.fuzzing);
73 man.hash.add(conf_run.flags.color);76 man.hash.add(conf_run.flags.color);
74 man.hash.add(conf_run.flags.disable_zig_progress);77 man.hash.add(conf_run.flags.disable_zig_progress);
7578
76 var any_discovered_inputs = switch (conf_run.flags.stdio) {79 var any_discovered_inputs = switch (conf_run.flags.stdio) {
77 .infer_from_args, .inherit, .check, .zig_test => false,80 .infer_from_args, .inherit, .check, .zig_test => false,
78 .protocol => true,81 .protocol => true,
79 };82 };
80 var any_output_args = false;83 var any_output_args = false;
81 var any_cli_positionals = false;84 var any_cli_positionals = false;
85
86 for (switch (conf_run.flags.stdio) {
87 .infer_from_args, .inherit, .check, .zig_test => conf_run.args.slice,
88 .protocol => conf_run.args.slice[0..1], // arguments communicated over protocol
89 }) |arg_index| {
90 const arg = arg_index.get(conf);
91 try argv_list.ensureUnusedCapacity(gpa, 1);
92 switch (arg.flags.tag) {
93 .string => {
94 const string = arg.prefix.value.?.slice(conf);
95 argv_list.appendAssumeCapacity(string);
96 man.hash.addBytesZ(string);
97 },
98 .path_file => {
99 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
100 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
101 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
102 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
103 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
104 }));
105 man.hash.add(arg.flags.make_absolute);
106 man.hash.addBytesZ(prefix);
107 man.hash.addBytesZ(suffix);
108 _ = try man.addInputPath(file_path, .{});
109 },
110 .path_directory => {
111 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
112 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
113 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
114 const resolved_arg = try mem.concat(arena, u8, &.{
115 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
116 });
117 argv_list.appendAssumeCapacity(resolved_arg);
118 man.hash.add(arg.flags.make_absolute);
119 man.hash.addBytes(resolved_arg);
120 },
121 .file_content => {
122 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
123 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
124 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
125
126 var result: std.Io.Writer.Allocating = .init(arena);
127 result.writer.writeAll(prefix) catch return error.OutOfMemory;
128
129 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
130 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
131 defer file.close(io);
132
133 var file_reader = file.reader(io, &.{});
134 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
135 error.ReadFailed => switch (file_reader.err.?) {
136 error.Canceled => |e| return e,
137 else => |e| return step.fail(maker, "failed to read from {qf}: {t}", .{ file_path, e }),
138 },
139 error.WriteFailed => return error.OutOfMemory,
140 };
141 result.writer.writeAll(suffix) catch return error.OutOfMemory;
82142
83 for (switch (conf_run.flags.stdio) {143 argv_list.appendAssumeCapacity(result.written());
84 .infer_from_args, .inherit, .check, .zig_test => conf_run.args.slice,144 man.hash.addBytesZ(prefix);
85 .protocol => conf_run.args.slice[0..1], // arguments communicated over protocol145 _ = try man.addInputPath(file_path, .{});
86 }) |arg_index| {146 man.hash.addBytesZ(suffix);
87 const arg = arg_index.get(conf);147 },
88 try argv_list.ensureUnusedCapacity(gpa, 1);148 .artifact => {
89 switch (arg.flags.tag) {149 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
90 .string => {150 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
91 const string = arg.prefix.value.?.slice(conf);151 const producer_index = arg.producer.value.?;
92 argv_list.appendAssumeCapacity(string);152 const producer_step = producer_index.ptr(conf);
93 man.hash.addBytesZ(string);153 const producer = producer_step.extended.get(conf.extra).compile;
94 },154 const producer_make_comp_step = maker.stepByIndex(producer_index);
95 .path_file => {155 const producer_make_comp = &producer_make_comp_step.extended.compile;
96 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";156
97 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";157 const file_path = producer_make_comp.installed_path orelse
98 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);158 maker.generatedPath(producer.generated_bin.value.?);
99 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{159
100 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,160 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
101 }));161 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
102 man.hash.add(arg.flags.make_absolute);162 }));
103 man.hash.addBytesZ(prefix);163
104 man.hash.addBytesZ(suffix);164 man.hash.add(arg.flags.make_absolute);
105 _ = try man.addInputPath(file_path, .{});165 man.hash.addBytesZ(prefix);
106 },166 man.hash.addBytesZ(suffix);
107 .path_directory => {167 _ = try man.addInputPath(file_path, .{});
108 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";168 },
109 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";169 .output_file, .output_directory => {
110 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);170 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
111 const resolved_arg = try mem.concat(arena, u8, &.{171 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
112 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,172 const basename = arg.basename.value.?.slice(conf);
113 });173
114 argv_list.appendAssumeCapacity(resolved_arg);174 man.hash.add(arg.flags.make_absolute);
115 man.hash.add(arg.flags.make_absolute);175 man.hash.addBytesZ(prefix);
116 man.hash.addBytes(resolved_arg);176 man.hash.addBytesZ(basename);
177 man.hash.addBytesZ(suffix);
178 man.hash.add(arg.flags.dep_file);
179
180 if (arg.flags.dep_file) any_discovered_inputs = true;
181 any_output_args = true;
182
183 // Add a placeholder into the argument list because we need the
184 // manifest hash to be updated with all arguments before the
185 // object directory is computed.
186 try output_placeholders.append(gpa, .{
187 .index = @intCast(argv_list.items.len),
188 .offset = 0,
189 .arg_index = arg_index,
190 });
191 _ = argv_list.addOneAssumeCapacity();
192 },
193 .passthru => {
194 any_cli_positionals = true;
195 if (maker.run_args) |run_args| {
196 try argv_list.appendSlice(gpa, run_args);
197 man.hash.addListOfBytes(run_args);
198 }
199 },
200 .enable_darling => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value),
201 .enable_qemu => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value),
202 .enable_rosetta => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value),
203 .enable_wasmtime => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value),
204 .enable_wine => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value),
205 }
206 }
207
208 var owned_dirs: std.bit_set.Dynamic = .{};
209 var inherit_dirs: std.ArrayList(Io.Dir) = .empty;
210 var owned_files: std.bit_set.Dynamic = .{};
211 var inherit_files: std.ArrayList(Io.File) = .empty;
212 var protocol_args: std.ArrayList(u8) = .empty;
213 var input_dirs: std.ArrayList(Cache.Path) = .empty;
214 defer {
215 for (inherit_dirs.items, 0..) |inherit_dir, inherit_dir_index|
216 if (owned_dirs.isSet(inherit_dir_index)) inherit_dir.close(io);
217 for (inherit_files.items, 0..) |inherit_file, inherit_file_index|
218 if (owned_files.isSet(inherit_file_index)) inherit_file.close(io);
219 owned_dirs.deinit(gpa);
220 inherit_dirs.deinit(gpa);
221 owned_files.deinit(gpa);
222 inherit_files.deinit(gpa);
223 protocol_args.deinit(gpa);
224 input_dirs.deinit(gpa);
225 }
226 switch (conf_run.flags.stdio) {
227 .infer_from_args, .inherit, .check => {},
228 .zig_test => {
229 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
230
231 try argv_list.ensureUnusedCapacity(gpa, 3);
232 argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string}));
233 argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed}));
234 argv_list.appendAssumeCapacity("--listen=-");
117 },235 },
118 .file_content => {236 .protocol => {
119 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";237 argv_list.appendAssumeCapacity("--listen=-");
120 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";238 try input_dirs.append(gpa, if (conf_run.cwd.value) |lazy_cwd|
121 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);239 try maker.resolveLazyPathIndex(arena, lazy_cwd, run_index)
240 else
241 .cwd());
242 for (conf_run.args.slice[1..]) |arg_index| {
243 const arg = arg_index.get(conf);
244 switch (arg.flags.tag) {
245 .string => {
246 const string = arg.prefix.value.?.slice(conf);
122247
123 var result: std.Io.Writer.Allocating = .init(arena);248 man.hash.addBytesZ(string);
124 result.writer.writeAll(prefix) catch return error.OutOfMemory;
125249
126 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|250 try protocol_args.ensureUnusedCapacity(gpa, 1 + string.len + 1);
127 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });251 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string));
128 defer file.close(io);252 protocol_args.appendSliceAssumeCapacity(string);
253 protocol_args.appendAssumeCapacity(0);
254 },
255 .path_file => {
256 const lazy_path = arg.path.value.?.get(conf);
257 switch (lazy_path) {
258 else => {},
259 .relative => |relative| switch (relative.flags.base) {
260 else => {},
261 .libc_runtimes => if (graph.libc_runtimes_dir == null) continue,
262 },
263 }
129264
130 var file_reader = file.reader(io, &.{});265 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
131 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {266 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
132 error.ReadFailed => switch (file_reader.err.?) {267 const file_path = try maker.resolveLazyPath(arena, lazy_path, run_index);
133 error.Canceled => |e| return e,268
134 else => |e| return step.fail(maker, "failed to read from {qf}: {t}", .{ file_path, e }),269 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
135 },270 try inherit_files.ensureUnusedCapacity(gpa, 1);
136 error.WriteFailed => return error.OutOfMemory,271 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
137 };272 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
138 result.writer.writeAll(suffix) catch return error.OutOfMemory;273 inherit_files.appendAssumeCapacity(file);
274
275 man.hash.addBytesZ(prefix);
276 _ = try man.addInputPath(file_path, .{});
277 man.hash.addBytesZ(suffix);
278
279 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
280 1 + @sizeOf(Io.File.Handle) +
281 1 + suffix.len + 1);
282 if (prefix.len > 0) {
283 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
284 protocol_args.appendSliceAssumeCapacity(prefix);
285 protocol_args.appendAssumeCapacity(0);
286 }
287 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file));
288 const file_handle: *align(1) Io.File.Handle =
289 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
290 file_handle.* = file.handle;
291 if (suffix.len > 0) {
292 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
293 protocol_args.appendSliceAssumeCapacity(prefix);
294 protocol_args.appendAssumeCapacity(0);
295 }
296 },
297 .path_directory => {
298 const lazy_path = arg.path.value.?.get(conf);
299 switch (lazy_path) {
300 else => {},
301 .relative => |relative| switch (relative.flags.base) {
302 else => {},
303 .libc_runtimes => if (graph.libc_runtimes_dir == null) continue,
304 },
305 }
139306
140 argv_list.appendAssumeCapacity(result.written());307 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
141 man.hash.addBytesZ(prefix);308 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
142 _ = try man.addInputPath(file_path, .{});309 const dir_path = try maker.resolveLazyPath(arena, lazy_path, run_index);
143 man.hash.addBytesZ(suffix);310 try input_dirs.append(gpa, dir_path);
311
312 const has_sub_path = dir_path.sub_path.len > 0;
313 try owned_dirs.resize(gpa, owned_dirs.bit_length + 1, has_sub_path);
314 try inherit_dirs.ensureUnusedCapacity(gpa, 1);
315 const dir = if (has_sub_path)
316 dir_path.root_dir.handle.openDir(io, dir_path.sub_path, .{
317 .iterate = true,
318 }) catch |err| return step.fail(maker, "unable to open input dir {qf}: {t}", .{ dir_path, err })
319 else
320 dir_path.root_dir.handle;
321 inherit_dirs.appendAssumeCapacity(dir);
322
323 man.hash.addBytesZ(prefix);
324 man.hash.addOptionalBytes(dir_path.root_dir.path);
325 man.hash.addBytes(dir_path.sub_path);
326 man.hash.addBytesZ(suffix);
327
328 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
329 1 + @sizeOf(Io.Dir.Handle) +
330 1 + suffix.len + 1);
331 if (prefix.len > 0) {
332 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
333 protocol_args.appendSliceAssumeCapacity(prefix);
334 protocol_args.appendAssumeCapacity(0);
335 }
336 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_dir));
337 const dir_handle: *align(1) Io.Dir.Handle =
338 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.Dir.Handle)));
339 dir_handle.* = dir.handle;
340 if (suffix.len > 0) {
341 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
342 protocol_args.appendSliceAssumeCapacity(prefix);
343 protocol_args.appendAssumeCapacity(0);
344 }
345 },
346 .file_content => {
347 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
348 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
349 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
350
351 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
352 try inherit_files.ensureUnusedCapacity(gpa, 1);
353 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
354 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
355 inherit_files.appendAssumeCapacity(file);
356
357 man.hash.addBytesZ(prefix);
358 _ = try man.addInputPath(file_path, .{});
359 man.hash.addBytesZ(suffix);
360
361 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
362 1 + @sizeOf(Io.File.Handle) +
363 1 + suffix.len + 1);
364 if (prefix.len > 0) {
365 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
366 protocol_args.appendSliceAssumeCapacity(prefix);
367 protocol_args.appendAssumeCapacity(0);
368 }
369 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file_content));
370 const file_handle: *align(1) Io.File.Handle =
371 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
372 file_handle.* = file.handle;
373 if (suffix.len > 0) {
374 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
375 protocol_args.appendSliceAssumeCapacity(prefix);
376 protocol_args.appendAssumeCapacity(0);
377 }
378 },
379 .artifact => {
380 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
381 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
382 const producer_index = arg.producer.value.?;
383 const producer_step = producer_index.ptr(conf);
384 const producer = producer_step.extended.get(conf.extra).compile;
385 const producer_make_comp_step = maker.stepByIndex(producer_index);
386 const producer_make_comp = &producer_make_comp_step.extended.compile;
387
388 const file_path = producer_make_comp.installed_path orelse
389 maker.generatedPath(producer.generated_bin.value.?);
390
391 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
392 try inherit_files.ensureUnusedCapacity(gpa, 1);
393 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
394 return step.fail(maker, "unable to open input artifact {qf}: {t}", .{ file_path, err });
395 inherit_files.appendAssumeCapacity(file);
396
397 man.hash.addBytesZ(prefix);
398 _ = try man.addInputPath(file_path, .{});
399 man.hash.addBytesZ(suffix);
400
401 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
402 1 + @sizeOf(Io.File.Handle) +
403 1 + suffix.len + 1);
404 if (prefix.len > 0) {
405 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
406 protocol_args.appendSliceAssumeCapacity(prefix);
407 protocol_args.appendAssumeCapacity(0);
408 }
409 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file));
410 const file_handle: *align(1) Io.File.Handle =
411 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
412 file_handle.* = file.handle;
413 if (suffix.len > 0) {
414 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
415 protocol_args.appendSliceAssumeCapacity(prefix);
416 protocol_args.appendAssumeCapacity(0);
417 }
418 },
419 .output_file => {
420 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
421 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
422 const basename = arg.basename.value.?.slice(conf);
423
424 man.hash.addBytesZ(prefix);
425 man.hash.addBytesZ(basename);
426 man.hash.addBytesZ(suffix);
427 assert(!arg.flags.dep_file);
428
429 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
430 1 + @sizeOf(Io.File.Handle) +
431 1 + suffix.len + 1);
432 if (prefix.len > 0) {
433 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
434 protocol_args.appendSliceAssumeCapacity(prefix);
435 protocol_args.appendAssumeCapacity(0);
436 }
437 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.output_file));
438
439 // Add a placeholder into the argument list because we need the
440 // manifest hash to be updated with all arguments before the
441 // object directory is computed.
442 try owned_files.resize(gpa, owned_files.bit_length + 1, false);
443 try inherit_files.ensureUnusedCapacity(gpa, 1);
444 try output_placeholders.append(gpa, .{
445 .index = @intCast(inherit_files.items.len),
446 .offset = @intCast(protocol_args.items.len),
447 .arg_index = arg_index,
448 });
449 _ = inherit_files.addOneAssumeCapacity();
450
451 _ = protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle));
452 if (suffix.len > 0) {
453 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
454 protocol_args.appendSliceAssumeCapacity(prefix);
455 protocol_args.appendAssumeCapacity(0);
456 }
457 },
458 .output_directory => {
459 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
460 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
461 const basename = arg.basename.value.?.slice(conf);
462
463 man.hash.addBytesZ(prefix);
464 man.hash.addBytesZ(basename);
465 man.hash.addBytesZ(suffix);
466 assert(!arg.flags.dep_file);
467
468 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
469 1 + @sizeOf(Io.Dir.Handle) +
470 1 + suffix.len + 1);
471 if (prefix.len > 0) {
472 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
473 protocol_args.appendSliceAssumeCapacity(prefix);
474 protocol_args.appendAssumeCapacity(0);
475 }
476 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.output_dir));
477
478 // Add a placeholder into the argument list because we need the
479 // manifest hash to be updated with all arguments before the
480 // object directory is computed.
481 try owned_dirs.resize(gpa, owned_dirs.bit_length + 1, false);
482 try inherit_dirs.ensureUnusedCapacity(gpa, 1);
483 try output_placeholders.append(gpa, .{
484 .index = @intCast(inherit_dirs.items.len),
485 .offset = @intCast(protocol_args.items.len),
486 .arg_index = arg_index,
487 });
488 _ = inherit_dirs.addOneAssumeCapacity();
489
490 _ = protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.Dir.Handle));
491 if (suffix.len > 0) {
492 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
493 protocol_args.appendSliceAssumeCapacity(prefix);
494 protocol_args.appendAssumeCapacity(0);
495 }
496 },
497 .passthru => if (maker.run_args) |run_args| {
498 man.hash.addListOfBytes(run_args);
499
500 for (run_args) |run_arg| {
501 try protocol_args.ensureUnusedCapacity(gpa, 1 + run_arg.len + 1);
502 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string));
503 protocol_args.appendSliceAssumeCapacity(run_arg);
504 protocol_args.appendAssumeCapacity(0);
505 }
506 },
507 .enable_darling => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value),
508 .enable_qemu => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value),
509 .enable_rosetta => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value),
510 .enable_wasmtime => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value),
511 .enable_wine => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value),
512 }
513 }
144 },514 },
145 .artifact => {515 }
146 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
147 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
148 const producer_index = arg.producer.value.?;
149 const producer_step = producer_index.ptr(conf);
150 const producer = producer_step.extended.get(conf.extra).compile;
151 const producer_make_comp_step = maker.stepByIndex(producer_index);
152 const producer_make_comp = &producer_make_comp_step.extended.compile;
153
154 const file_path = producer_make_comp.installed_path orelse
155 maker.generatedPath(producer.generated_bin.value.?);
156
157 argv_list.appendAssumeCapacity(try mem.concat(arena, u8, &.{
158 prefix, try convertPathArg(arena, run_index, maker, file_path, arg.flags.make_absolute), suffix,
159 }));
160516
161 man.hash.add(arg.flags.make_absolute);517 switch (conf_run.stdin.u) {
162 man.hash.addBytesZ(prefix);518 .bytes => |bytes| {
163 man.hash.addBytesZ(suffix);519 man.hash.addBytes(bytes.slice(conf));
164 _ = try man.addInputPath(file_path, .{});
165 },
166 .output_file, .output_directory => {
167 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
168 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
169 const basename = arg.basename.value.?.slice(conf);
170
171 man.hash.add(arg.flags.make_absolute);
172 man.hash.addBytesZ(prefix);
173 man.hash.addBytesZ(basename);
174 man.hash.addBytesZ(suffix);
175 man.hash.add(arg.flags.dep_file);
176
177 if (arg.flags.dep_file) any_discovered_inputs = true;
178 any_output_args = true;
179
180 // Add a placeholder into the argument list because we need the
181 // manifest hash to be updated with all arguments before the
182 // object directory is computed.
183 try output_placeholders.append(gpa, .{
184 .index = @intCast(argv_list.items.len),
185 .offset = 0,
186 .arg_index = arg_index,
187 });
188 _ = argv_list.addOneAssumeCapacity();
189 },520 },
190 .passthru => {521 .lazy_path => |lazy_path| {
191 any_cli_positionals = true;522 const file_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index);
192 if (maker.run_args) |run_args| {523 _ = try man.addInputPath(file_path, .{});
193 try argv_list.appendSlice(gpa, run_args);
194 man.hash.addListOfBytes(run_args);
195 }
196 },524 },
197 .enable_darling => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value),525 .none => {},
198 .enable_qemu => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value),
199 .enable_rosetta => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value),
200 .enable_wasmtime => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value),
201 .enable_wine => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value),
202 }526 }
203 }
204
205 var owned_dirs: std.bit_set.Dynamic = .{};
206 var inherit_dirs: std.ArrayList(Io.Dir) = .empty;
207 var owned_files: std.bit_set.Dynamic = .{};
208 var inherit_files: std.ArrayList(Io.File) = .empty;
209 var protocol_args: std.ArrayList(u8) = .empty;
210 var input_dirs: std.ArrayList(Cache.Path) = .empty;
211 defer {
212 for (inherit_dirs.items, 0..) |inherit_dir, inherit_dir_index|
213 if (owned_dirs.isSet(inherit_dir_index)) inherit_dir.close(io);
214 for (inherit_files.items, 0..) |inherit_file, inherit_file_index|
215 if (owned_files.isSet(inherit_file_index)) inherit_file.close(io);
216 owned_dirs.deinit(gpa);
217 inherit_dirs.deinit(gpa);
218 owned_files.deinit(gpa);
219 inherit_files.deinit(gpa);
220 protocol_args.deinit(gpa);
221 input_dirs.deinit(gpa);
222 }
223 switch (conf_run.flags.stdio) {
224 .infer_from_args, .inherit, .check => {},
225 .zig_test => {
226 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
227527
228 try argv_list.ensureUnusedCapacity(gpa, 3);528 if (conf_run.captured_stdout.value) |captured| {
229 argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string}));529 man.hash.addBytes(captured.basename.slice(conf));
230 argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed}));530 man.hash.add(conf_run.flags.stdout_trim_whitespace);
231 argv_list.appendAssumeCapacity("--listen=-");531 }
232 },
233 .protocol => {
234 argv_list.appendAssumeCapacity("--listen=-");
235 try input_dirs.append(gpa, if (conf_run.cwd.value) |lazy_cwd|
236 try maker.resolveLazyPathIndex(arena, lazy_cwd, run_index)
237 else
238 .cwd());
239 for (conf_run.args.slice[1..]) |arg_index| {
240 const arg = arg_index.get(conf);
241 switch (arg.flags.tag) {
242 .string => {
243 const string = arg.prefix.value.?.slice(conf);
244
245 man.hash.addBytesZ(string);
246
247 try protocol_args.ensureUnusedCapacity(gpa, 1 + string.len + 1);
248 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string));
249 protocol_args.appendSliceAssumeCapacity(string);
250 protocol_args.appendAssumeCapacity(0);
251 },
252 .path_file => {
253 const lazy_path = arg.path.value.?.get(conf);
254 switch (lazy_path) {
255 else => {},
256 .relative => |relative| switch (relative.flags.base) {
257 else => {},
258 .libc_runtimes => if (graph.libc_runtimes_dir == null) continue,
259 },
260 }
261
262 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
263 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
264 const file_path = try maker.resolveLazyPath(arena, lazy_path, run_index);
265
266 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
267 try inherit_files.ensureUnusedCapacity(gpa, 1);
268 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
269 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
270 inherit_files.appendAssumeCapacity(file);
271
272 man.hash.addBytesZ(prefix);
273 _ = try man.addInputPath(file_path, .{});
274 man.hash.addBytesZ(suffix);
275
276 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
277 1 + @sizeOf(Io.File.Handle) +
278 1 + suffix.len + 1);
279 if (prefix.len > 0) {
280 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
281 protocol_args.appendSliceAssumeCapacity(prefix);
282 protocol_args.appendAssumeCapacity(0);
283 }
284 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file));
285 const file_handle: *align(1) Io.File.Handle =
286 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
287 file_handle.* = file.handle;
288 if (suffix.len > 0) {
289 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
290 protocol_args.appendSliceAssumeCapacity(prefix);
291 protocol_args.appendAssumeCapacity(0);
292 }
293 },
294 .path_directory => {
295 const lazy_path = arg.path.value.?.get(conf);
296 switch (lazy_path) {
297 else => {},
298 .relative => |relative| switch (relative.flags.base) {
299 else => {},
300 .libc_runtimes => if (graph.libc_runtimes_dir == null) continue,
301 },
302 }
303
304 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
305 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
306 const dir_path = try maker.resolveLazyPath(arena, lazy_path, run_index);
307 try input_dirs.append(gpa, dir_path);
308
309 const has_sub_path = dir_path.sub_path.len > 0;
310 try owned_dirs.resize(gpa, owned_dirs.bit_length + 1, has_sub_path);
311 try inherit_dirs.ensureUnusedCapacity(gpa, 1);
312 const dir = if (has_sub_path)
313 dir_path.root_dir.handle.openDir(io, dir_path.sub_path, .{
314 .iterate = true,
315 }) catch |err| return step.fail(maker, "unable to open input dir {qf}: {t}", .{ dir_path, err })
316 else
317 dir_path.root_dir.handle;
318 inherit_dirs.appendAssumeCapacity(dir);
319
320 man.hash.addBytesZ(prefix);
321 man.hash.addOptionalBytes(dir_path.root_dir.path);
322 man.hash.addBytes(dir_path.sub_path);
323 man.hash.addBytesZ(suffix);
324
325 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
326 1 + @sizeOf(Io.Dir.Handle) +
327 1 + suffix.len + 1);
328 if (prefix.len > 0) {
329 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
330 protocol_args.appendSliceAssumeCapacity(prefix);
331 protocol_args.appendAssumeCapacity(0);
332 }
333 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_dir));
334 const dir_handle: *align(1) Io.Dir.Handle =
335 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.Dir.Handle)));
336 dir_handle.* = dir.handle;
337 if (suffix.len > 0) {
338 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
339 protocol_args.appendSliceAssumeCapacity(prefix);
340 protocol_args.appendAssumeCapacity(0);
341 }
342 },
343 .file_content => {
344 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
345 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
346 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
347
348 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
349 try inherit_files.ensureUnusedCapacity(gpa, 1);
350 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
351 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
352 inherit_files.appendAssumeCapacity(file);
353
354 man.hash.addBytesZ(prefix);
355 _ = try man.addInputPath(file_path, .{});
356 man.hash.addBytesZ(suffix);
357
358 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
359 1 + @sizeOf(Io.File.Handle) +
360 1 + suffix.len + 1);
361 if (prefix.len > 0) {
362 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
363 protocol_args.appendSliceAssumeCapacity(prefix);
364 protocol_args.appendAssumeCapacity(0);
365 }
366 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file_content));
367 const file_handle: *align(1) Io.File.Handle =
368 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
369 file_handle.* = file.handle;
370 if (suffix.len > 0) {
371 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
372 protocol_args.appendSliceAssumeCapacity(prefix);
373 protocol_args.appendAssumeCapacity(0);
374 }
375 },
376 .artifact => {
377 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
378 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
379 const producer_index = arg.producer.value.?;
380 const producer_step = producer_index.ptr(conf);
381 const producer = producer_step.extended.get(conf.extra).compile;
382 const producer_make_comp_step = maker.stepByIndex(producer_index);
383 const producer_make_comp = &producer_make_comp_step.extended.compile;
384
385 const file_path = producer_make_comp.installed_path orelse
386 maker.generatedPath(producer.generated_bin.value.?);
387
388 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
389 try inherit_files.ensureUnusedCapacity(gpa, 1);
390 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
391 return step.fail(maker, "unable to open input artifact {qf}: {t}", .{ file_path, err });
392 inherit_files.appendAssumeCapacity(file);
393
394 man.hash.addBytesZ(prefix);
395 _ = try man.addInputPath(file_path, .{});
396 man.hash.addBytesZ(suffix);
397
398 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
399 1 + @sizeOf(Io.File.Handle) +
400 1 + suffix.len + 1);
401 if (prefix.len > 0) {
402 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
403 protocol_args.appendSliceAssumeCapacity(prefix);
404 protocol_args.appendAssumeCapacity(0);
405 }
406 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file));
407 const file_handle: *align(1) Io.File.Handle =
408 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
409 file_handle.* = file.handle;
410 if (suffix.len > 0) {
411 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
412 protocol_args.appendSliceAssumeCapacity(prefix);
413 protocol_args.appendAssumeCapacity(0);
414 }
415 },
416 .output_file => {
417 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
418 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
419 const basename = arg.basename.value.?.slice(conf);
420
421 man.hash.addBytesZ(prefix);
422 man.hash.addBytesZ(basename);
423 man.hash.addBytesZ(suffix);
424 assert(!arg.flags.dep_file);
425
426 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
427 1 + @sizeOf(Io.File.Handle) +
428 1 + suffix.len + 1);
429 if (prefix.len > 0) {
430 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
431 protocol_args.appendSliceAssumeCapacity(prefix);
432 protocol_args.appendAssumeCapacity(0);
433 }
434 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.output_file));
435
436 // Add a placeholder into the argument list because we need the
437 // manifest hash to be updated with all arguments before the
438 // object directory is computed.
439 try owned_files.resize(gpa, owned_files.bit_length + 1, false);
440 try inherit_files.ensureUnusedCapacity(gpa, 1);
441 try output_placeholders.append(gpa, .{
442 .index = @intCast(inherit_files.items.len),
443 .offset = @intCast(protocol_args.items.len),
444 .arg_index = arg_index,
445 });
446 _ = inherit_files.addOneAssumeCapacity();
447
448 _ = protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle));
449 if (suffix.len > 0) {
450 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
451 protocol_args.appendSliceAssumeCapacity(prefix);
452 protocol_args.appendAssumeCapacity(0);
453 }
454 },
455 .output_directory => {
456 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
457 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
458 const basename = arg.basename.value.?.slice(conf);
459
460 man.hash.addBytesZ(prefix);
461 man.hash.addBytesZ(basename);
462 man.hash.addBytesZ(suffix);
463 assert(!arg.flags.dep_file);
464
465 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
466 1 + @sizeOf(Io.Dir.Handle) +
467 1 + suffix.len + 1);
468 if (prefix.len > 0) {
469 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
470 protocol_args.appendSliceAssumeCapacity(prefix);
471 protocol_args.appendAssumeCapacity(0);
472 }
473 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.output_dir));
474
475 // Add a placeholder into the argument list because we need the
476 // manifest hash to be updated with all arguments before the
477 // object directory is computed.
478 try owned_dirs.resize(gpa, owned_dirs.bit_length + 1, false);
479 try inherit_dirs.ensureUnusedCapacity(gpa, 1);
480 try output_placeholders.append(gpa, .{
481 .index = @intCast(inherit_dirs.items.len),
482 .offset = @intCast(protocol_args.items.len),
483 .arg_index = arg_index,
484 });
485 _ = inherit_dirs.addOneAssumeCapacity();
486532
487 _ = protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.Dir.Handle));533 if (conf_run.captured_stderr.value) |captured| {
488 if (suffix.len > 0) {534 man.hash.addBytes(captured.basename.slice(conf));
489 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));535 man.hash.add(conf_run.flags.stderr_trim_whitespace);
490 protocol_args.appendSliceAssumeCapacity(prefix);536 }
491 protocol_args.appendAssumeCapacity(0);
492 }
493 },
494 .passthru => if (maker.run_args) |run_args| {
495 man.hash.addListOfBytes(run_args);
496537
497 for (run_args) |run_arg| {538 switch (conf_run.flags.stdio) {
498 try protocol_args.ensureUnusedCapacity(gpa, 1 + run_arg.len + 1);539 .infer_from_args, .inherit, .zig_test, .protocol => {},
499 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string));540 .check => {
500 protocol_args.appendSliceAssumeCapacity(run_arg);541 man.hash.addBytes(if (conf_run.expect_stderr_exact.value) |bytes| bytes.slice(conf) else "");
501 protocol_args.appendAssumeCapacity(0);542 man.hash.addBytes(if (conf_run.expect_stdout_exact.value) |bytes| bytes.slice(conf) else "");
502 }543 for (conf_run.expect_stderr_match.slice) |bytes| man.hash.addBytes(bytes.slice(conf));
503 },544 for (conf_run.expect_stdout_match.slice) |bytes| man.hash.addBytes(bytes.slice(conf));
504 .enable_darling => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value),545 man.hash.add(conf_run.flags2.expect_term_status);
505 .enable_qemu => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value),546 man.hash.addOptional(conf_run.expect_term_value.value);
506 .enable_rosetta => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value),547 },
507 .enable_wasmtime => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value),548 }
508 .enable_wine => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value),
509 }
510 }
511 },
512 }
513549
514 switch (conf_run.stdin.u) {550 for (conf_run.file_inputs.slice) |lazy_path| {
515 .bytes => |bytes| {
516 man.hash.addBytes(bytes.slice(conf));
517 },
518 .lazy_path => |lazy_path| {
519 const file_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index);551 const file_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index);
520 _ = try man.addInputPath(file_path, .{});552 _ = try man.addInputPath(file_path, .{});
521 },553 }
522 .none => {},
523 }
524
525 if (conf_run.captured_stdout.value) |captured| {
526 man.hash.addBytes(captured.basename.slice(conf));
527 man.hash.add(conf_run.flags.stdout_trim_whitespace);
528 }
529
530 if (conf_run.captured_stderr.value) |captured| {
531 man.hash.addBytes(captured.basename.slice(conf));
532 man.hash.add(conf_run.flags.stderr_trim_whitespace);
533 }
534554
535 switch (conf_run.flags.stdio) {555 if (conf_run.cwd.value) |lazy_path| {
536 .infer_from_args, .inherit, .zig_test, .protocol => {},556 const cwd_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index);
537 .check => {557 _ = man.hash.addBytes(try cwd_path.toString(arena));
538 man.hash.addBytes(if (conf_run.expect_stderr_exact.value) |bytes| bytes.slice(conf) else "");558 }
539 man.hash.addBytes(if (conf_run.expect_stdout_exact.value) |bytes| bytes.slice(conf) else "");
540 for (conf_run.expect_stderr_match.slice) |bytes| man.hash.addBytes(bytes.slice(conf));
541 for (conf_run.expect_stdout_match.slice) |bytes| man.hash.addBytes(bytes.slice(conf));
542 man.hash.add(conf_run.flags2.expect_term_status);
543 man.hash.addOptional(conf_run.expect_term_value.value);
544 },
545 }
546559
547 for (conf_run.file_inputs.slice) |lazy_path| {560 // Whether the Run step has side effects *other than* updating the output arguments.
548 const file_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index);561 // When fuzzing we need to always run the test runner to populate fuzz_tests.
549 _ = try man.addInputPath(file_path, .{});562 const has_side_effects = graph.fuzzing or conf_run.flags.has_side_effects or any_cli_positionals or
550 }563 switch (conf_run.flags.stdio) {
564 .infer_from_args => !any_output_args and
565 conf_run.captured_stdout.value == null and
566 conf_run.captured_stderr.value == null,
567 .inherit => true,
568 .check, .zig_test, .protocol => false,
569 };
551570
552 if (conf_run.cwd.value) |lazy_path| {571 if (!has_side_effects and try step.cacheHitWatched(maker, &man, progress_node)) {
553 const cwd_path = try maker.resolveLazyPathIndex(arena, lazy_path, run_index);572 // Cache hit; skip running command.
554 _ = man.hash.addBytes(try cwd_path.toString(arena));573 const digest = man.hitDigestHex();
555 }574 try populateGeneratedStdIo(maker, &conf_run, &digest);
575 try populateGeneratedPaths(maker, output_placeholders.items, &digest);
576 step.result_cached = true;
577 return;
578 }
556579
557 // Whether the Run step has side effects *other than* updating the output arguments.580 if (!any_discovered_inputs) {
558 // When fuzzing we need to always run the test runner to populate fuzz_tests.581 // We already know the final output paths; use them directly.
559 const has_side_effects = graph.fuzzing or conf_run.flags.has_side_effects or any_cli_positionals or582 const digest = if (has_side_effects) man.hash.final() else man.missDigestHex();
560 switch (conf_run.flags.stdio) {583 const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest;
561 .infer_from_args => !any_output_args and584 try populateGeneratedStdIo(maker, &conf_run, &digest);
562 conf_run.captured_stdout.value == null and585 try populateGeneratedPathsCreateDirs(arena, run_index, maker, output_dir_path, output_placeholders.items, switch (conf_run.flags.stdio) {
563 conf_run.captured_stderr.value == null,586 .infer_from_args, .inherit, .check, .zig_test => .{ .argv = argv_list.items },
564 .inherit => true,587 .protocol => .{ .protocol = .{
565 .check, .zig_test, .protocol => false,588 .owned_dirs = &owned_dirs,
566 };589 .inherit_dirs = inherit_dirs.items,
590 .owned_files = &owned_files,
591 .inherit_files = inherit_files.items,
592 .args = protocol_args.items,
593 } },
594 });
595 runCommand(arena, run, run_index, maker, progress_node, argv_list.items, &.{}, &.{}, &.{}, &.{}, null, has_side_effects, output_dir_path, null) catch |err| switch (err) {
596 else => |e| return e,
597 error.MakeFailed, error.MakeSkipped => |e| {
598 try step.setWatchInputsFromManifest(maker, &man);
599 return e;
600 },
601 };
602 if (has_side_effects) {
603 try step.setWatchInputsFromManifest(maker, &man);
604 } else {
605 try step.finalizeManifestAndWatch(maker, &man);
606 }
607 return;
608 }
567609
568 if (!has_side_effects and try step.cacheHitWatched(maker, &man, progress_node)) {610 // We do not know the final output paths yet; use temporary directory to run the command.
569 // Cache hit; skip running command.611 var rand_int: u64 = undefined;
570 const digest = man.hitDigestHex();612 io.random(@ptrCast(&rand_int));
571 try populateGeneratedStdIo(maker, &conf_run, &digest);613 tmp_dir_path = ("tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int)).*;
572 try populateGeneratedPaths(maker, output_placeholders.items, &digest);
573 step.result_cached = true;
574 return;
575 }
576614
577 if (!any_discovered_inputs) {615 try populateGeneratedPathsCreateDirs(arena, run_index, maker, &tmp_dir_path, output_placeholders.items, switch (conf_run.flags.stdio) {
578 // We already know the final output paths; use them directly.
579 const digest = if (has_side_effects) man.hash.final() else man.missDigestHex();
580 const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest;
581 try populateGeneratedStdIo(maker, &conf_run, &digest);
582 try populateGeneratedPathsCreateDirs(arena, run_index, maker, output_dir_path, output_placeholders.items, switch (conf_run.flags.stdio) {
583 .infer_from_args, .inherit, .check, .zig_test => .{ .argv = argv_list.items },616 .infer_from_args, .inherit, .check, .zig_test => .{ .argv = argv_list.items },
584 .protocol => .{ .protocol = .{617 .protocol => .{ .protocol = .{
585 .owned_dirs = &owned_dirs,618 .owned_dirs = &owned_dirs,
...@@ -589,57 +622,30 @@ pub fn make(...@@ -589,57 +622,30 @@ pub fn make(
589 .args = protocol_args.items,622 .args = protocol_args.items,
590 } },623 } },
591 });624 });
592 runCommand(arena, run, run_index, maker, progress_node, argv_list.items, &.{}, &.{}, &.{}, &.{}, null, has_side_effects, output_dir_path, null) catch |err| switch (err) {625 runCommand(
626 arena,
627 run,
628 run_index,
629 maker,
630 progress_node,
631 argv_list.items,
632 inherit_dirs.items,
633 inherit_files.items,
634 protocol_args.items,
635 input_dirs.items,
636 &man,
637 has_side_effects,
638 &tmp_dir_path,
639 null,
640 ) catch |err| switch (err) {
593 else => |e| return e,641 else => |e| return e,
594 error.MakeFailed, error.MakeSkipped => |e| {642 error.MakeFailed, error.MakeSkipped => |e| {
595 try step.setWatchInputsFromManifest(maker, &man);643 try step.setWatchInputsFromManifest(maker, &man);
596 return e;644 return e;
597 },645 },
598 };646 };
599 if (has_side_effects) {
600 try step.setWatchInputsFromManifest(maker, &man);
601 } else {
602 try step.finalizeManifestAndWatch(maker, &man);
603 }
604 return;
605 }
606647
607 // We do not know the final output paths yet; use temporary directory to run the command.648 break :has_side_effects has_side_effects;
608 var rand_int: u64 = undefined;
609 io.random(@ptrCast(&rand_int));
610 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
611
612 try populateGeneratedPathsCreateDirs(arena, run_index, maker, tmp_dir_path, output_placeholders.items, switch (conf_run.flags.stdio) {
613 .infer_from_args, .inherit, .check, .zig_test => .{ .argv = argv_list.items },
614 .protocol => .{ .protocol = .{
615 .owned_dirs = &owned_dirs,
616 .inherit_dirs = inherit_dirs.items,
617 .owned_files = &owned_files,
618 .inherit_files = inherit_files.items,
619 .args = protocol_args.items,
620 } },
621 });
622 runCommand(
623 arena,
624 run,
625 run_index,
626 maker,
627 progress_node,
628 argv_list.items,
629 inherit_dirs.items,
630 inherit_files.items,
631 protocol_args.items,
632 input_dirs.items,
633 &man,
634 has_side_effects,
635 tmp_dir_path,
636 null,
637 ) catch |err| switch (err) {
638 else => |e| return e,
639 error.MakeFailed, error.MakeSkipped => |e| {
640 try step.setWatchInputsFromManifest(maker, &man);
641 return e;
642 },
643 };649 };
644650
645 for (output_placeholders.items) |placeholder| {651 for (output_placeholders.items) |placeholder| {
...@@ -686,7 +692,7 @@ pub fn make(...@@ -686,7 +692,7 @@ pub fn make(
686692
687 if (any_output) {693 if (any_output) {
688 // Rename into place.694 // Rename into place.
689 const tmp_path: Path = .{ .root_dir = cache_root, .sub_path = tmp_dir_path };695 const tmp_path: Path = .{ .root_dir = cache_root, .sub_path = &tmp_dir_path };
690 const dst_path: Path = .{ .root_dir = cache_root, .sub_path = "o" ++ Dir.path.sep_str ++ &digest };696 const dst_path: Path = .{ .root_dir = cache_root, .sub_path = "o" ++ Dir.path.sep_str ++ &digest };
691 Dir.rename(697 Dir.rename(
692 tmp_path.root_dir.handle,698 tmp_path.root_dir.handle,