authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-13 06:31:29-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-26 19:08:50-04:00
log11958a306f30268b17454a25bc365e67aad84efd
tree014d51a6105cb1577763d4f9075a6f930c69e4c7
parent3cb5bbb36d09baf4f08464c1d047607a1f09a967

test: implement new runner


21 files changed, 1324 insertions(+), 171 deletions(-)

build.zig+21
......@@ -804,6 +804,27 @@ pub fn build(b: *std.Build) !void {
804804 .skip_wasm = skip_wasm,
805805 .max_rss = 4_300_000_000,
806806 })) |test_libc_nsz_step| test_step.dependOn(test_libc_nsz_step);
807
808 const runner = b.addExecutable(.{
809 .name = "runner",
810 .root_module = b.createModule(.{
811 .root_source_file = b.path("test/src/runner.zig"),
812 .target = b.graph.host,
813 }),
814 });
815 test_step.dependOn(try tests.addNewIncrementalTests(b, runner, .{
816 .test_filters = test_filters,
817 .test_target_filters = test_target_filters,
818 .skip_non_native = skip_non_native,
819 .skip_wasm = skip_wasm,
820 .skip_freebsd = skip_freebsd,
821 .skip_netbsd = skip_netbsd,
822 .skip_openbsd = skip_openbsd,
823 .skip_windows = skip_windows,
824 .skip_darwin = skip_darwin,
825 .skip_linux = skip_linux,
826 .skip_llvm = skip_llvm,
827 }));
807828}
808829
809830fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
lib/compiler/Maker/Step.zig+4-1
......@@ -270,7 +270,10 @@ pub fn make(
270270 .compile => break :t null,
271271 .run => {
272272 const run_flags: Configuration.Step.Run.Flags = @bitCast(flags);
273 if (run_flags.stdio == .zig_test) break :t null;
273 switch (run_flags.stdio) {
274 .infer_from_args, .inherit, .check => {},
275 .zig_test, .protocol => break :t null,
276 }
274277 },
275278 else => {},
276279 }
lib/compiler/Maker/Step/Run.zig+504-113
......@@ -73,18 +73,24 @@ pub fn make(
7373 man.hash.add(conf_run.flags.color);
7474 man.hash.add(conf_run.flags.disable_zig_progress);
7575
76 var any_dep_files = false;
76 var any_discovered_inputs = switch (conf_run.flags.stdio) {
77 .infer_from_args, .inherit, .check, .zig_test => false,
78 .protocol => true,
79 };
7780 var any_output_args = false;
7881 var any_cli_positionals = false;
7982
80 for (conf_run.args.slice) |arg_index| {
83 for (switch (conf_run.flags.stdio) {
84 .infer_from_args, .inherit, .check, .zig_test => conf_run.args.slice,
85 .protocol => conf_run.args.slice[0..1], // arguments communicated over protocol
86 }) |arg_index| {
8187 const arg = arg_index.get(conf);
8288 try argv_list.ensureUnusedCapacity(gpa, 1);
8389 switch (arg.flags.tag) {
8490 .string => {
85 const prefix = arg.prefix.value.?.slice(conf);
86 argv_list.appendAssumeCapacity(prefix);
87 man.hash.addBytesZ(prefix);
91 const string = arg.prefix.value.?.slice(conf);
92 argv_list.appendAssumeCapacity(string);
93 man.hash.addBytesZ(string);
8894 },
8995 .path_file => {
9096 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
......@@ -118,14 +124,14 @@ pub fn make(
118124 result.writer.writeAll(prefix) catch return error.OutOfMemory;
119125
120126 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
121 return step.fail(maker, "unable to open input file {f}: {t}", .{ file_path, err });
127 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
122128 defer file.close(io);
123129
124130 var file_reader = file.reader(io, &.{});
125131 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
126132 error.ReadFailed => switch (file_reader.err.?) {
127133 error.Canceled => |e| return e,
128 else => |e| return step.fail(maker, "failed to read from {f}: {t}", .{ file_path, e }),
134 else => |e| return step.fail(maker, "failed to read from {qf}: {t}", .{ file_path, e }),
129135 },
130136 error.WriteFailed => return error.OutOfMemory,
131137 };
......@@ -133,8 +139,8 @@ pub fn make(
133139
134140 argv_list.appendAssumeCapacity(result.written());
135141 man.hash.addBytesZ(prefix);
136 man.hash.addBytesZ(suffix);
137142 _ = try man.addInputPath(file_path, .{});
143 man.hash.addBytesZ(suffix);
138144 },
139145 .artifact => {
140146 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
......@@ -168,7 +174,7 @@ pub fn make(
168174 man.hash.addBytesZ(suffix);
169175 man.hash.add(arg.flags.dep_file);
170176
171 any_dep_files = any_dep_files or arg.flags.dep_file;
177 if (arg.flags.dep_file) any_discovered_inputs = true;
172178 any_output_args = true;
173179
174180 // Add a placeholder into the argument list because we need the
......@@ -176,9 +182,10 @@ pub fn make(
176182 // object directory is computed.
177183 try output_placeholders.append(gpa, .{
178184 .index = @intCast(argv_list.items.len),
185 .offset = 0,
179186 .arg_index = arg_index,
180187 });
181 argv_list.items.len += 1;
188 _ = argv_list.addOneAssumeCapacity();
182189 },
183190 .passthru => {
184191 any_cli_positionals = true;
......@@ -195,14 +202,285 @@ pub fn make(
195202 }
196203 }
197204
198 man.hash.add(conf_run.flags.test_runner_mode);
199 if (conf_run.flags.test_runner_mode) {
200 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
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 defer {
211 for (inherit_dirs.items, 0..) |inherit_dir, inherit_dir_index|
212 if (owned_dirs.isSet(inherit_dir_index)) inherit_dir.close(io);
213 for (inherit_files.items, 0..) |inherit_file, inherit_file_index|
214 if (owned_files.isSet(inherit_file_index)) inherit_file.close(io);
215 owned_dirs.deinit(gpa);
216 inherit_dirs.deinit(gpa);
217 owned_files.deinit(gpa);
218 inherit_files.deinit(gpa);
219 protocol_args.deinit(gpa);
220 }
221 switch (conf_run.flags.stdio) {
222 .infer_from_args, .inherit, .check => {},
223 .zig_test => {
224 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
225
226 try argv_list.ensureUnusedCapacity(gpa, 3);
227 argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string}));
228 argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed}));
229 argv_list.appendAssumeCapacity("--listen=-");
230 },
231 .protocol => for (conf_run.args.slice[1..]) |arg_index| {
232 const arg = arg_index.get(conf);
233 switch (arg.flags.tag) {
234 .string => {
235 const string = arg.prefix.value.?.slice(conf);
236
237 man.hash.addBytesZ(string);
238
239 try protocol_args.ensureUnusedCapacity(gpa, 1 + string.len + 1);
240 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string));
241 protocol_args.appendSliceAssumeCapacity(string);
242 protocol_args.appendAssumeCapacity(0);
243 },
244 .path_file => {
245 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
246 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
247 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
248
249 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
250 try inherit_files.ensureUnusedCapacity(gpa, 1);
251 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
252 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
253 inherit_files.appendAssumeCapacity(file);
254
255 man.hash.addBytesZ(prefix);
256 _ = try man.addInputPath(file_path, .{});
257 man.hash.addBytesZ(suffix);
258
259 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
260 1 + @sizeOf(Io.File.Handle) +
261 1 + suffix.len + 1);
262 if (prefix.len > 0) {
263 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
264 protocol_args.appendSliceAssumeCapacity(prefix);
265 protocol_args.appendAssumeCapacity(0);
266 }
267 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file));
268 const file_handle: *align(1) Io.File.Handle =
269 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
270 file_handle.* = file.handle;
271 if (suffix.len > 0) {
272 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
273 protocol_args.appendSliceAssumeCapacity(prefix);
274 protocol_args.appendAssumeCapacity(0);
275 }
276 },
277 .path_directory => {
278 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
279 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
280 const dir_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
281
282 const has_sub_path = dir_path.sub_path.len > 0;
283 try owned_dirs.resize(gpa, owned_dirs.bit_length + 1, has_sub_path);
284 try inherit_dirs.ensureUnusedCapacity(gpa, 1);
285 const dir = if (has_sub_path)
286 dir_path.root_dir.handle.openDir(io, dir_path.sub_path, .{
287 .iterate = true,
288 }) catch |err| return step.fail(maker, "unable to open input dir {qf}: {t}", .{ dir_path, err })
289 else
290 dir_path.root_dir.handle;
291 inherit_dirs.appendAssumeCapacity(dir);
292
293 man.hash.addBytesZ(prefix);
294 man.hash.addOptionalBytes(dir_path.root_dir.path);
295 man.hash.addBytes(dir_path.sub_path);
296 man.hash.addBytesZ(suffix);
297
298 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
299 1 + @sizeOf(Io.Dir.Handle) +
300 1 + suffix.len + 1);
301 if (prefix.len > 0) {
302 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
303 protocol_args.appendSliceAssumeCapacity(prefix);
304 protocol_args.appendAssumeCapacity(0);
305 }
306 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_dir));
307 const dir_handle: *align(1) Io.Dir.Handle =
308 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.Dir.Handle)));
309 dir_handle.* = dir.handle;
310 if (suffix.len > 0) {
311 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
312 protocol_args.appendSliceAssumeCapacity(prefix);
313 protocol_args.appendAssumeCapacity(0);
314 }
315 },
316 .file_content => {
317 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
318 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
319 const file_path = try maker.resolveLazyPathIndex(arena, arg.path.value.?, run_index);
320
321 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
322 try inherit_files.ensureUnusedCapacity(gpa, 1);
323 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
324 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
325 inherit_files.appendAssumeCapacity(file);
326
327 man.hash.addBytesZ(prefix);
328 _ = try man.addInputPath(file_path, .{});
329 man.hash.addBytesZ(suffix);
330
331 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
332 1 + @sizeOf(Io.File.Handle) +
333 1 + suffix.len + 1);
334 if (prefix.len > 0) {
335 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
336 protocol_args.appendSliceAssumeCapacity(prefix);
337 protocol_args.appendAssumeCapacity(0);
338 }
339 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file_content));
340 const file_handle: *align(1) Io.File.Handle =
341 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
342 file_handle.* = file.handle;
343 if (suffix.len > 0) {
344 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
345 protocol_args.appendSliceAssumeCapacity(prefix);
346 protocol_args.appendAssumeCapacity(0);
347 }
348 },
349 .artifact => {
350 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
351 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
352 const producer_index = arg.producer.value.?;
353 const producer_step = producer_index.ptr(conf);
354 const producer = producer_step.extended.get(conf.extra).compile;
355 const producer_make_comp_step = maker.stepByIndex(producer_index);
356 const producer_make_comp = &producer_make_comp_step.extended.compile;
357
358 const file_path = producer_make_comp.installed_path orelse
359 maker.generatedPath(producer.generated_bin.value.?);
360
361 try owned_files.resize(gpa, owned_files.bit_length + 1, true);
362 try inherit_files.ensureUnusedCapacity(gpa, 1);
363 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
364 return step.fail(maker, "unable to open input artifact {qf}: {t}", .{ file_path, err });
365 inherit_files.appendAssumeCapacity(file);
366
367 man.hash.addBytesZ(prefix);
368 _ = try man.addInputPath(file_path, .{});
369 man.hash.addBytesZ(suffix);
370
371 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
372 1 + @sizeOf(Io.File.Handle) +
373 1 + suffix.len + 1);
374 if (prefix.len > 0) {
375 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
376 protocol_args.appendSliceAssumeCapacity(prefix);
377 protocol_args.appendAssumeCapacity(0);
378 }
379 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.input_file));
380 const file_handle: *align(1) Io.File.Handle =
381 @ptrCast(protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle)));
382 file_handle.* = file.handle;
383 if (suffix.len > 0) {
384 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
385 protocol_args.appendSliceAssumeCapacity(prefix);
386 protocol_args.appendAssumeCapacity(0);
387 }
388 },
389 .output_file => {
390 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
391 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
392 const basename = arg.basename.value.?.slice(conf);
393
394 man.hash.addBytesZ(prefix);
395 man.hash.addBytesZ(basename);
396 man.hash.addBytesZ(suffix);
397 assert(!arg.flags.dep_file);
398
399 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
400 1 + @sizeOf(Io.File.Handle) +
401 1 + suffix.len + 1);
402 if (prefix.len > 0) {
403 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
404 protocol_args.appendSliceAssumeCapacity(prefix);
405 protocol_args.appendAssumeCapacity(0);
406 }
407 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.output_file));
408
409 // Add a placeholder into the argument list because we need the
410 // manifest hash to be updated with all arguments before the
411 // object directory is computed.
412 try owned_files.resize(gpa, owned_files.bit_length + 1, false);
413 try inherit_files.ensureUnusedCapacity(gpa, 1);
414 try output_placeholders.append(gpa, .{
415 .index = @intCast(inherit_files.items.len),
416 .offset = @intCast(protocol_args.items.len),
417 .arg_index = arg_index,
418 });
419 _ = inherit_files.addOneAssumeCapacity();
420
421 _ = protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.File.Handle));
422 if (suffix.len > 0) {
423 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
424 protocol_args.appendSliceAssumeCapacity(prefix);
425 protocol_args.appendAssumeCapacity(0);
426 }
427 },
428 .output_directory => {
429 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
430 const suffix = if (arg.suffix.value) |p| p.slice(conf) else "";
431 const basename = arg.basename.value.?.slice(conf);
432
433 man.hash.addBytesZ(prefix);
434 man.hash.addBytesZ(basename);
435 man.hash.addBytesZ(suffix);
436 assert(!arg.flags.dep_file);
437
438 try protocol_args.ensureUnusedCapacity(gpa, 1 + prefix.len + 1 +
439 1 + @sizeOf(Io.Dir.Handle) +
440 1 + suffix.len + 1);
441 if (prefix.len > 0) {
442 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.prefix));
443 protocol_args.appendSliceAssumeCapacity(prefix);
444 protocol_args.appendAssumeCapacity(0);
445 }
446 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.output_dir));
447
448 // Add a placeholder into the argument list because we need the
449 // manifest hash to be updated with all arguments before the
450 // object directory is computed.
451 try owned_dirs.resize(gpa, owned_dirs.bit_length + 1, false);
452 try inherit_dirs.ensureUnusedCapacity(gpa, 1);
453 try output_placeholders.append(gpa, .{
454 .index = @intCast(inherit_dirs.items.len),
455 .offset = @intCast(protocol_args.items.len),
456 .arg_index = arg_index,
457 });
458 _ = inherit_dirs.addOneAssumeCapacity();
201459
202 try argv_list.ensureUnusedCapacity(gpa, 3);
203 argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string}));
204 argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed}));
205 argv_list.appendAssumeCapacity("--listen=-");
460 _ = protocol_args.addManyAsArrayAssumeCapacity(@sizeOf(Io.Dir.Handle));
461 if (suffix.len > 0) {
462 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.suffix));
463 protocol_args.appendSliceAssumeCapacity(prefix);
464 protocol_args.appendAssumeCapacity(0);
465 }
466 },
467 .passthru => if (maker.run_args) |run_args| {
468 man.hash.addListOfBytes(run_args);
469
470 for (run_args) |run_arg| {
471 try protocol_args.ensureUnusedCapacity(gpa, 1 + run_arg.len + 1);
472 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string));
473 protocol_args.appendSliceAssumeCapacity(run_arg);
474 protocol_args.appendAssumeCapacity(0);
475 }
476 },
477 .enable_darling => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value),
478 .enable_qemu => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value),
479 .enable_rosetta => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value),
480 .enable_wasmtime => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value),
481 .enable_wine => try thirdPartyToggleProtocol(gpa, &man.hash, &protocol_args, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value),
482 }
483 },
206484 }
207485
208486 switch (conf_run.stdin.u) {
......@@ -227,7 +505,7 @@ pub fn make(
227505 }
228506
229507 switch (conf_run.flags.stdio) {
230 .infer_from_args, .inherit, .zig_test => {},
508 .infer_from_args, .inherit, .zig_test, .protocol => {},
231509 .check => {
232510 man.hash.addBytes(if (conf_run.expect_stderr_exact.value) |bytes| bytes.slice(conf) else "");
233511 man.hash.addBytes(if (conf_run.expect_stdout_exact.value) |bytes| bytes.slice(conf) else "");
......@@ -256,7 +534,7 @@ pub fn make(
256534 conf_run.captured_stdout.value == null and
257535 conf_run.captured_stderr.value == null,
258536 .inherit => true,
259 .check, .zig_test => false,
537 .check, .zig_test, .protocol => false,
260538 };
261539
262540 if (!has_side_effects and try step.cacheHitWatched(maker, &man, progress_node)) {
......@@ -268,13 +546,22 @@ pub fn make(
268546 return;
269547 }
270548
271 if (!any_dep_files) {
549 if (!any_discovered_inputs) {
272550 // We already know the final output paths; use them directly.
273551 const digest = if (has_side_effects) man.hash.final() else man.missDigestHex();
274552 const output_dir_path = "o" ++ Dir.path.sep_str ++ &digest;
275553 try populateGeneratedStdIo(maker, &conf_run, &digest);
276 try populateGeneratedPathsCreateDirs(arena, run_index, maker, output_dir_path, output_placeholders.items, argv_list.items);
277 try runCommand(arena, run, run_index, maker, progress_node, argv_list.items, has_side_effects, output_dir_path, null);
554 try populateGeneratedPathsCreateDirs(arena, run_index, maker, output_dir_path, output_placeholders.items, switch (conf_run.flags.stdio) {
555 .infer_from_args, .inherit, .check, .zig_test => .{ .argv = argv_list.items },
556 .protocol => .{ .protocol = .{
557 .owned_dirs = &owned_dirs,
558 .inherit_dirs = inherit_dirs.items,
559 .owned_files = &owned_files,
560 .inherit_files = inherit_files.items,
561 .args = protocol_args.items,
562 } },
563 });
564 try runCommand(arena, run, run_index, maker, progress_node, argv_list.items, inherit_dirs.items, inherit_files.items, protocol_args.items, has_side_effects, output_dir_path, null);
278565 if (!has_side_effects) try step.finalizeManifestAndWatch(maker, &man);
279566 return;
280567 }
......@@ -284,8 +571,17 @@ pub fn make(
284571 io.random(@ptrCast(&rand_int));
285572 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
286573
287 try populateGeneratedPathsCreateDirs(arena, run_index, maker, tmp_dir_path, output_placeholders.items, argv_list.items);
288 try runCommand(arena, run, run_index, maker, progress_node, argv_list.items, has_side_effects, tmp_dir_path, null);
574 try populateGeneratedPathsCreateDirs(arena, run_index, maker, tmp_dir_path, output_placeholders.items, switch (conf_run.flags.stdio) {
575 .infer_from_args, .inherit, .check, .zig_test => .{ .argv = argv_list.items },
576 .protocol => .{ .protocol = .{
577 .owned_dirs = &owned_dirs,
578 .inherit_dirs = inherit_dirs.items,
579 .owned_files = &owned_files,
580 .inherit_files = inherit_files.items,
581 .args = protocol_args.items,
582 } },
583 });
584 try runCommand(arena, run, run_index, maker, progress_node, argv_list.items, inherit_dirs.items, inherit_files.items, protocol_args.items, has_side_effects, tmp_dir_path, null);
289585
290586 for (output_placeholders.items) |placeholder| {
291587 const arg = placeholder.arg_index.get(conf);
......@@ -296,10 +592,10 @@ pub fn make(
296592 var diagnostic: Cache.DepTokenizer.Token = undefined;
297593 man.addInputDepFile(generated_path, &diagnostic) catch |err| switch (err) {
298594 error.OutOfMemory, error.Canceled => |e| return e,
299 error.InvalidDepFile => return step.fail(maker, "failed adding dep file {f} to cache: {f}", .{
595 error.InvalidDepFile => return step.fail(maker, "failed adding dep file {qf} to cache: {f}", .{
300596 generated_path, diagnostic,
301597 }),
302 else => |e| return step.fail(maker, "failed adding dep file {f} to cache: {t}", .{
598 else => |e| return step.fail(maker, "failed adding dep file {qf} to cache: {t}", .{
303599 generated_path, e,
304600 }),
305601 };
......@@ -307,13 +603,13 @@ pub fn make(
307603 var diagnostic: Cache.Manifest.AddDiscoveredDepFileDiagnostic = undefined;
308604 man.addDiscoveredDepFile(generated_path, &diagnostic) catch |err| switch (err) {
309605 error.OutOfMemory, error.Canceled => |e| return e,
310 error.InvalidDepFile => return step.fail(maker, "failed adding dep file {f} to cache: {f}", .{
606 error.InvalidDepFile => return step.fail(maker, "failed adding dep file {qf} to cache: {f}", .{
311607 generated_path, diagnostic.dep_tokenizer,
312608 }),
313 error.FileSystemFailure => return step.fail(maker, "failed adding a path from dep file {f} to cache: {f}", .{
609 error.FileSystemFailure => return step.fail(maker, "failed adding a path from dep file {qf} to cache: {f}", .{
314610 generated_path, diagnostic.add_discovered_path,
315611 }),
316 else => |e| return step.fail(maker, "failed adding dep file {f} to cache: {t}", .{
612 else => |e| return step.fail(maker, "failed adding dep file {qf} to cache: {t}", .{
317613 generated_path, e,
318614 }),
319615 };
......@@ -342,7 +638,7 @@ pub fn make(
342638 ) catch |err| switch (err) {
343639 error.DirNotEmpty => {
344640 dst_path.root_dir.handle.deleteTree(io, dst_path.sub_path) catch |del_err|
345 return step.fail(maker, "failed to remove tree {f}: {t}", .{ dst_path, del_err });
641 return step.fail(maker, "failed to remove tree {qf}: {t}", .{ dst_path, del_err });
346642
347643 Dir.rename(
348644 tmp_path.root_dir.handle,
......@@ -350,11 +646,11 @@ pub fn make(
350646 dst_path.root_dir.handle,
351647 dst_path.sub_path,
352648 io,
353 ) catch |retry_err| return step.fail(maker, "failed to rename directory {f} to {f}: {t}", .{
649 ) catch |retry_err| return step.fail(maker, "failed to rename directory {qf} to {qf}: {t}", .{
354650 tmp_path, dst_path, retry_err,
355651 });
356652 },
357 else => return step.fail(maker, "failed to rename directory {f} to {f}: {t}", .{
653 else => return step.fail(maker, "failed to rename directory {qf} to {qf}: {t}", .{
358654 tmp_path, dst_path, err,
359655 }),
360656 };
......@@ -386,19 +682,30 @@ fn thirdPartyToggle(
386682 enable: ?Configuration.String,
387683 disable: ?Configuration.String,
388684) void {
389 if (setting) {
390 if (enable) |string| {
391 const slice = string.slice(conf);
392 if (man_hash) |h| h.addBytesZ(slice);
393 argv_list.appendAssumeCapacity(slice);
394 }
395 } else {
396 if (disable) |string| {
397 const slice = string.slice(conf);
398 if (man_hash) |h| h.addBytesZ(slice);
399 argv_list.appendAssumeCapacity(slice);
400 }
401 }
685 const string = (if (setting) enable else disable) orelse return;
686 const slice = string.slice(conf);
687 if (man_hash) |h| h.addBytesZ(slice);
688 argv_list.appendAssumeCapacity(slice);
689}
690
691fn thirdPartyToggleProtocol(
692 gpa: Allocator,
693 man_hash: *Cache.HashHelper,
694 protocol_args: *std.ArrayList(u8),
695 conf: *const Configuration,
696 setting: bool,
697 enable: ?Configuration.String,
698 disable: ?Configuration.String,
699) Allocator.Error!void {
700 const string = (if (setting) enable else disable) orelse return;
701 const slice = string.slice(conf);
702
703 man_hash.addBytesZ(slice);
704
705 try protocol_args.ensureUnusedCapacity(gpa, 1 + slice.len + 1);
706 protocol_args.appendAssumeCapacity(@backingInt(std.zig.Client.Message.Arg.string));
707 protocol_args.appendSliceAssumeCapacity(slice);
708 protocol_args.appendAssumeCapacity(0);
402709}
403710
404711/// Reads stdout of a Zig test process until a termination condition is reached:
......@@ -412,6 +719,7 @@ fn waitZigTest(
412719 maker: *Maker,
413720 child: *process.Child,
414721 progress_node: std.Progress.Node,
722 protocol_args: []const u8,
415723 multi_reader: *Io.File.MultiReader,
416724 opt_metadata: *?TestMetadata,
417725 results: *Step.TestResults,
......@@ -444,6 +752,14 @@ fn waitZigTest(
444752 .out = &stdin_writer.interface,
445753 };
446754
755 if (protocol_args.len > 0) {
756 try client.serveMessageHeader(.{
757 .tag = .args,
758 .bytes_len = @intCast(protocol_args.len),
759 });
760 try client.out.writeAll(protocol_args);
761 }
762
447763 if (opt_metadata.*) |*md| {
448764 // Previous unit test process died or was killed; we're continuing where it left off
449765 requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err };
......@@ -492,6 +808,7 @@ fn waitZigTest(
492808 var body_r: std.Io.Reader = .fixed(body);
493809
494810 switch (header.tag) {
811 else => {}, // ignore other messages
495812 .zig_version => {
496813 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail(
497814 maker,
......@@ -583,7 +900,6 @@ fn waitZigTest(
583900
584901 requestNextTest(&client, md, &sub_prog_node) catch |err| return .{ .write_failed = err };
585902 },
586 else => {}, // ignore other messages
587903 }
588904 }
589905}
......@@ -820,6 +1136,7 @@ const FuzzTestRunner = struct {
8201136 }
8211137
8221138 switch (header.tag) {
1139 else => {}, // ignore other messages
8231140 .zig_version => {
8241141 if (!std.mem.eql(u8, builtin.zig_version_string, body)) return step.fail(
8251142 maker,
......@@ -882,7 +1199,6 @@ const FuzzTestRunner = struct {
8821199 f.pending_broadcasts.appendSliceAssumeCapacity(@ptrCast(&footer));
8831200 }
8841201 },
885 else => {}, // ignore other messages
8861202 }
8871203
8881204 instance.message.clearRetainingCapacity();
......@@ -1146,6 +1462,7 @@ fn evalZigTest(
11461462 maker: *Maker,
11471463 progress_node: std.Progress.Node,
11481464 spawn_options: process.SpawnOptions,
1465 protocol_args: []const u8,
11491466 fuzz_context: ?FuzzContext,
11501467) !void {
11511468 if (fuzz_context != null) {
......@@ -1194,6 +1511,7 @@ fn evalZigTest(
11941511 maker,
11951512 &child,
11961513 progress_node,
1514 protocol_args,
11971515 &multi_reader,
11981516 &test_metadata,
11991517 &test_results,
......@@ -1402,7 +1720,7 @@ fn evalGeneric(
14021720 var write_buffer: [1024]u8 = undefined;
14031721 var stdin_writer = child.stdin.?.writerStreaming(io, &write_buffer);
14041722 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
1405 error.ReadFailed => return step.fail(maker, "failed to read from {f}: {t}", .{
1723 error.ReadFailed => return step.fail(maker, "failed to read from {qf}: {t}", .{
14061724 path, file_reader.err.?,
14071725 }),
14081726 error.WriteFailed => return step.fail(maker, "failed to write to stdin: {t}", .{
......@@ -1490,6 +1808,7 @@ fn evalGeneric(
14901808
14911809const IndexedOutput = struct {
14921810 index: u32,
1811 offset: u32,
14931812 arg_index: Configuration.Step.Run.Arg.Index,
14941813};
14951814
......@@ -1516,13 +1835,16 @@ pub fn rerunInFuzzMode(
15161835 var argv_list: std.ArrayList([]const u8) = .empty;
15171836 defer argv_list.deinit(gpa);
15181837
1519 for (conf_run.args.slice) |arg_index| {
1838 for (switch (conf_run.flags.stdio) {
1839 .infer_from_args, .inherit, .check, .zig_test => conf_run.args.slice,
1840 .protocol => @panic("protocol fuzz unimplemented"),
1841 }) |arg_index| {
15201842 const arg = arg_index.get(conf);
15211843 try argv_list.ensureUnusedCapacity(gpa, 1);
15221844 switch (arg.flags.tag) {
15231845 .string => {
1524 const prefix = arg.prefix.value.?.slice(conf);
1525 argv_list.appendAssumeCapacity(prefix);
1846 const string = arg.prefix.value.?.slice(conf);
1847 argv_list.appendAssumeCapacity(string);
15261848 },
15271849 .path_file => {
15281850 const prefix = if (arg.prefix.value) |p| p.slice(conf) else "";
......@@ -1550,14 +1872,14 @@ pub fn rerunInFuzzMode(
15501872 result.writer.writeAll(prefix) catch return error.OutOfMemory;
15511873
15521874 const file = file_path.root_dir.handle.openFile(io, file_path.sub_path, .{}) catch |err|
1553 return step.fail(maker, "unable to open input file {f}: {t}", .{ file_path, err });
1875 return step.fail(maker, "unable to open input file {qf}: {t}", .{ file_path, err });
15541876 defer file.close(io);
15551877
15561878 var file_reader = file.reader(io, &.{});
15571879 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
15581880 error.ReadFailed => switch (file_reader.err.?) {
15591881 error.Canceled => |e| return e,
1560 else => |e| return step.fail(maker, "failed to read from {f}: {t}", .{ file_path, e }),
1882 else => |e| return step.fail(maker, "failed to read from {qf}: {t}", .{ file_path, e }),
15611883 },
15621884 error.WriteFailed => return error.OutOfMemory,
15631885 };
......@@ -1591,14 +1913,17 @@ pub fn rerunInFuzzMode(
15911913 .enable_wine => thirdPartyToggle(null, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value),
15921914 }
15931915 }
1594
1595 if (conf_run.flags.test_runner_mode) {
1596 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
1597
1598 try argv_list.ensureUnusedCapacity(gpa, 3);
1599 argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string}));
1600 argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed}));
1601 argv_list.appendAssumeCapacity("--listen=-");
1916 switch (conf_run.flags.stdio) {
1917 .infer_from_args, .inherit, .check => {},
1918 .zig_test => {
1919 const cache_dir_string = try convertPathArg(arena, run_index, maker, .{ .root_dir = cache_root }, false);
1920
1921 try argv_list.ensureUnusedCapacity(gpa, 3);
1922 argv_list.appendAssumeCapacity(try arena.print("--cache-dir={s}", .{cache_dir_string}));
1923 argv_list.appendAssumeCapacity(try arena.print("--seed=0x{x}", .{graph.random_seed}));
1924 argv_list.appendAssumeCapacity("--listen=-");
1925 },
1926 .protocol => unreachable,
16021927 }
16031928
16041929 step.clearFailedCommand(gpa);
......@@ -1607,7 +1932,7 @@ pub fn rerunInFuzzMode(
16071932 var rand_int: u64 = undefined;
16081933 io.random(@ptrCast(&rand_int));
16091934 const tmp_dir_path = "tmp" ++ Dir.path.sep_str ++ std.fmt.hex(rand_int);
1610 try runCommand(arena, run, run_index, maker, prog_node, argv_list.items, has_side_effects, tmp_dir_path, .{
1935 try runCommand(arena, run, run_index, maker, prog_node, argv_list.items, &.{}, &.{}, &.{}, has_side_effects, tmp_dir_path, .{
16111936 .fuzz = fuzz,
16121937 });
16131938}
......@@ -1633,7 +1958,16 @@ fn populateGeneratedPathsCreateDirs(
16331958 maker: *Maker,
16341959 output_dir_path: []const u8,
16351960 output_placeholders: []const IndexedOutput,
1636 argv: [][]const u8,
1961 populate: union(enum) {
1962 argv: [][]const u8,
1963 protocol: struct {
1964 owned_dirs: *std.bit_set.Dynamic,
1965 inherit_dirs: []Io.Dir,
1966 owned_files: *std.bit_set.Dynamic,
1967 inherit_files: []Io.File,
1968 args: []u8,
1969 },
1970 },
16371971) !void {
16381972 const step = maker.stepByIndex(run_index);
16391973 const conf = &maker.scanned_config.configuration;
......@@ -1653,16 +1987,52 @@ fn populateGeneratedPathsCreateDirs(
16531987 const create_path: Path = .{
16541988 .root_dir = cache_root,
16551989 .sub_path = switch (arg.flags.tag) {
1990 else => unreachable,
16561991 .output_file => Dir.path.dirname(generated_path.sub_path).?,
16571992 .output_directory => generated_path.sub_path,
1658 else => unreachable,
16591993 },
16601994 };
16611995 create_path.root_dir.handle.createDirPath(io, create_path.sub_path) catch |err|
1662 return step.fail(maker, "unable to make path {f}: {t}", .{ create_path, err });
1996 return step.fail(maker, "unable to make path {qf}: {t}", .{ create_path, err });
16631997
1664 const arg_output_path = try convertPathArg(arena, run_index, maker, generated_path, arg.flags.make_absolute);
1665 argv[placeholder.index] = try mem.concat(arena, u8, &.{ prefix, arg_output_path, suffix });
1998 switch (populate) {
1999 .argv => |argv| {
2000 const arg_output_path = try convertPathArg(arena, run_index, maker, generated_path, arg.flags.make_absolute);
2001 argv[placeholder.index] = try mem.concat(arena, u8, &.{ prefix, arg_output_path, suffix });
2002 },
2003 .protocol => |protocol| switch (arg.flags.tag) {
2004 else => unreachable,
2005 .output_file => {
2006 const file = generated_path.root_dir.handle.createFile(
2007 io,
2008 generated_path.sub_path,
2009 .{ .read = true },
2010 ) catch |err| return step.fail(maker, "unable to create output file {qf}: {t}", .{
2011 generated_path, err,
2012 });
2013 protocol.inherit_files[placeholder.index] = file;
2014 protocol.owned_files.set(placeholder.index);
2015 const file_handle: *align(1) Io.File.Handle =
2016 @ptrCast(protocol.args[placeholder.offset..][0..@sizeOf(Io.File.Handle)]);
2017 file_handle.* = file.handle;
2018 },
2019 .output_directory => {
2020 assert(generated_path.sub_path.len > 0);
2021 const dir = generated_path.root_dir.handle.openDir(
2022 io,
2023 generated_path.sub_path,
2024 .{ .iterate = true },
2025 ) catch |err| return step.fail(maker, "unable to open output dir {qf}: {t}", .{
2026 generated_path, err,
2027 });
2028 protocol.inherit_dirs[placeholder.index] = dir;
2029 protocol.owned_dirs.set(placeholder.index);
2030 const dir_handle: *align(1) Io.Dir.Handle =
2031 @ptrCast(protocol.args[placeholder.offset..][0..@sizeOf(Io.Dir.Handle)]);
2032 dir_handle.* = dir.handle;
2033 },
2034 },
2035 }
16662036 }
16672037}
16682038
......@@ -1711,6 +2081,9 @@ fn runCommand(
17112081 maker: *Maker,
17122082 progress_node: std.Progress.Node,
17132083 argv: []const []const u8,
2084 inherit_dirs: []const Io.Dir,
2085 inherit_files: []const Io.File,
2086 protocol_args: []const u8,
17142087 has_side_effects: bool,
17152088 output_dir_path: []const u8,
17162089 fuzz_context: ?FuzzContext,
......@@ -1777,10 +2150,14 @@ fn runCommand(
17772150 progress_node,
17782151 argv,
17792152 &environ_map,
2153 inherit_dirs,
2154 inherit_files,
2155 protocol_args,
17802156 has_side_effects,
17812157 fuzz_context,
17822158 ) catch |err| term: {
17832159 switch (err) {
2160 else => {},
17842161 error.InvalidExe, // cpu arch mismatch
17852162 error.FileNotFound, // can happen with a wrong dynamic linker path
17862163 => interpret: {
......@@ -1928,6 +2305,9 @@ fn runCommand(
19282305 progress_node,
19292306 interp_argv.items,
19302307 &environ_map,
2308 inherit_dirs,
2309 inherit_files,
2310 protocol_args,
19312311 has_side_effects,
19322312 fuzz_context,
19332313 ) catch |e| {
......@@ -1937,21 +2317,21 @@ fn runCommand(
19372317 };
19382318 },
19392319 error.MakeFailed, error.OutOfMemory, error.Canceled => |e| return e,
1940 else => {},
19412320 }
19422321 return step.fail(maker, "failed to spawn and capture stdio from {s}: {t}", .{ argv[0], err });
19432322 };
19442323
1945 const generic_result = opt_generic_result orelse {
1946 assert(conf_run.flags.stdio == .zig_test);
1947 // Specific errors have already been reported, and test results are populated. All we need
1948 // to do is report step failure if any test failed.
1949 if (!step.test_results.isSuccess()) return error.MakeFailed;
1950 return;
2324 const generic_result = switch (conf_run.flags.stdio) {
2325 .infer_from_args, .inherit, .check => opt_generic_result.?,
2326 .zig_test, .protocol => {
2327 assert(opt_generic_result == null);
2328 // Specific errors have already been reported, and test results are populated. All we need
2329 // to do is report step failure if any test failed.
2330 if (!step.test_results.isSuccess()) return error.MakeFailed;
2331 return;
2332 },
19512333 };
1952
19532334 assert(fuzz_context == null);
1954 assert(conf_run.flags.stdio != .zig_test);
19552335
19562336 // Capture stdout and stderr to GeneratedFile objects.
19572337 const Stream = struct {
......@@ -1978,7 +2358,7 @@ fn runCommand(
19782358
19792359 const sub_path_parent = output_path.dirname().?;
19802360 sub_path_parent.root_dir.handle.createDirPath(io, sub_path_parent.sub_path) catch |err|
1981 return step.fail(maker, "unable to make path {f}: {t}", .{ sub_path_parent, err });
2361 return step.fail(maker, "unable to make path {qf}: {t}", .{ sub_path_parent, err });
19822362
19832363 const data = switch (stream.trim_whitespace) {
19842364 .none => stream.bytes.?,
......@@ -1989,12 +2369,12 @@ fn runCommand(
19892369 output_path.root_dir.handle.writeFile(io, .{
19902370 .sub_path = output_path.sub_path,
19912371 .data = data,
1992 }) catch |err| return step.fail(maker, "unable to write file {f}: {t}", .{ output_path, err });
2372 }) catch |err| return step.fail(maker, "unable to write file {qf}: {t}", .{ output_path, err });
19932373 }
19942374 }
19952375
19962376 switch (conf_run.flags.stdio) {
1997 .zig_test => unreachable,
2377 .zig_test, .protocol => unreachable,
19982378 .check => {
19992379 if (conf_run.expect_stderr_exact.value) |bytes| {
20002380 const expected_bytes = bytes.slice(conf);
......@@ -2186,6 +2566,9 @@ fn spawnChildAndCollect(
21862566 progress_node: std.Progress.Node,
21872567 argv: []const []const u8,
21882568 environ_map: *EnvMap,
2569 inherit_dirs: []const Io.Dir,
2570 inherit_files: []const Io.File,
2571 protocol_args: []const u8,
21892572 has_side_effects: bool,
21902573 fuzz_context: ?FuzzContext,
21912574) !?EvalGenericResult {
......@@ -2199,7 +2582,10 @@ fn spawnChildAndCollect(
21992582
22002583 if (fuzz_context != null) {
22012584 assert(!has_side_effects);
2202 assert(conf_run.flags.stdio == .zig_test);
2585 switch (conf_run.flags.stdio) {
2586 .infer_from_args, .inherit, .check => unreachable,
2587 .zig_test, .protocol => {},
2588 }
22032589 }
22042590
22052591 const child_cwd: process.Child.Cwd = if (conf_run.cwd.value) |lazy_cwd|
......@@ -2237,20 +2623,22 @@ fn spawnChildAndCollect(
22372623 .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore,
22382624 .inherit => .inherit,
22392625 .check => .ignore,
2240 .zig_test => .pipe,
2626 .zig_test, .protocol => .pipe,
22412627 },
22422628 .stdout = if (conf_run.captured_stdout.value != null) .pipe else switch (conf_run.flags.stdio) {
22432629 .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .ignore,
22442630 .inherit => .inherit,
22452631 .check => if (checksContainStdout(&conf_run)) .pipe else .ignore,
2246 .zig_test => .pipe,
2632 .zig_test, .protocol => .pipe,
22472633 },
22482634 .stderr = if (conf_run.captured_stderr.value != null) .pipe else switch (conf_run.flags.stdio) {
22492635 .infer_from_args => if (maker.protocol_server == null and has_side_effects) .inherit else .pipe,
22502636 .inherit => if (maker.protocol_server == null) .inherit else .pipe,
22512637 .check => .pipe,
2252 .zig_test => .pipe,
2638 .zig_test, .protocol => .pipe,
22532639 },
2640 .inherit_dirs = inherit_dirs,
2641 .inherit_files = inherit_files,
22542642 };
22552643
22562644 if (maker.protocol_server != null) {
......@@ -2263,35 +2651,38 @@ fn spawnChildAndCollect(
22632651 assert(spawn_options.stderr != .inherit);
22642652 }
22652653
2266 if (conf_run.flags.stdio == .zig_test) {
2267 try setColorEnvironmentVariables(&conf_run, environ_map, graph.stderr_mode.?);
2268 const started: Io.Clock.Timestamp = .now(io, .awake);
2269 const result = evalZigTest(run, run_index, maker, progress_node, spawn_options, fuzz_context) catch |err| switch (err) {
2270 error.Canceled => |e| return e,
2271 else => |e| e,
2272 };
2273 step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds);
2274 try result;
2275 return null;
2276 } else {
2277 const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;
2278 if (!conf_run.flags.disable_zig_progress and !inherit) {
2279 spawn_options.progress_node = progress_node;
2280 }
2281 const terminal_mode: Io.Terminal.Mode = if (inherit) m: {
2282 const stderr = try io.lockStderr(&.{}, graph.stderr_mode);
2283 break :m stderr.terminal_mode;
2284 } else .no_color;
2285 defer if (inherit) io.unlockStderr();
2286 try setColorEnvironmentVariables(&conf_run, environ_map, terminal_mode);
2287
2288 const started: Io.Clock.Timestamp = .now(io, .awake);
2289 const result = evalGeneric(arena, run_index, maker, spawn_options) catch |err| switch (err) {
2290 error.Canceled => |e| return e,
2291 else => |e| e,
2292 };
2293 step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds);
2294 return try result;
2654 switch (conf_run.flags.stdio) {
2655 .infer_from_args, .inherit, .check => {
2656 const inherit = spawn_options.stdout == .inherit or spawn_options.stderr == .inherit;
2657 if (!conf_run.flags.disable_zig_progress and !inherit) {
2658 spawn_options.progress_node = progress_node;
2659 }
2660 const terminal_mode: Io.Terminal.Mode = if (inherit) m: {
2661 const stderr = try io.lockStderr(&.{}, graph.stderr_mode);
2662 break :m stderr.terminal_mode;
2663 } else .no_color;
2664 defer if (inherit) io.unlockStderr();
2665 try setColorEnvironmentVariables(&conf_run, environ_map, terminal_mode);
2666
2667 const started: Io.Clock.Timestamp = .now(io, .awake);
2668 const result = evalGeneric(arena, run_index, maker, spawn_options) catch |err| switch (err) {
2669 error.Canceled => |e| return e,
2670 else => |e| e,
2671 };
2672 step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds);
2673 return try result;
2674 },
2675 .zig_test, .protocol => {
2676 try setColorEnvironmentVariables(&conf_run, environ_map, graph.stderr_mode.?);
2677 const started: Io.Clock.Timestamp = .now(io, .awake);
2678 const result = evalZigTest(run, run_index, maker, progress_node, spawn_options, protocol_args, fuzz_context) catch |err| switch (err) {
2679 error.Canceled => |e| return e,
2680 else => |e| e,
2681 };
2682 step.result_duration_ns = @intCast(started.untilNow(io).raw.nanoseconds);
2683 try result;
2684 return null;
2685 },
22952686 }
22962687}
22972688
......@@ -2341,7 +2732,7 @@ fn setColorEnvironmentVariables(
23412732 .auto => {
23422733 const capture_stderr = conf_run.captured_stderr.value != null or switch (conf_run.flags.stdio) {
23432734 .check => checksContainStderr(conf_run),
2344 .infer_from_args, .inherit, .zig_test => false,
2735 .infer_from_args, .inherit, .zig_test, .protocol => false,
23452736 };
23462737 if (capture_stderr) {
23472738 continue :color .disable;
lib/std/Build.zig+1-4
......@@ -768,9 +768,8 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run {
768768
769769 const run_step = Step.Run.create(b, step_name);
770770 run_step.producer = exe;
771 run_step.addArtifactArg(exe);
771772 if (exe.kind == .@"test") {
772 run_step.addArtifactArg(exe);
773
774773 const test_server_mode: bool = s: {
775774 if (exe.test_runner) |r| break :s r.mode == .server;
776775 if (exe.use_llvm == false) {
......@@ -802,8 +801,6 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run {
802801 // communicate failure via its exit code.
803802 run_step.expectExitCode(0);
804803 }
805 } else {
806 run_step.addArtifactArg(exe);
807804 }
808805
809806 return run_step;
lib/std/Build/Configuration.zig+1-2
......@@ -665,7 +665,7 @@ pub const Step = extern struct {
665665 pub const Tag = enum(u2) { none, bytes, lazy_path };
666666 };
667667 pub const TrimWhitespace = enum(u2) { none, all, leading, trailing };
668 pub const StdIo = enum(u2) { infer_from_args, inherit, check, zig_test };
668 pub const StdIo = enum(u3) { infer_from_args, inherit, check, zig_test, protocol };
669669
670670 pub const ExpectTermStatus = enum(u2) { exited, signal, stopped, unknown };
671671
......@@ -675,7 +675,6 @@ pub const Step = extern struct {
675675 skip_foreign_checks: bool,
676676 failing_to_execute_foreign_is_an_error: bool,
677677 has_side_effects: bool,
678 test_runner_mode: bool,
679678 color: Color,
680679 stdin: StdIn.Tag,
681680 stdio: StdIo,
lib/std/Build/Serialize.zig+1-1
......@@ -462,13 +462,13 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi
462462 .skip_foreign_checks = run.skip_foreign_checks,
463463 .failing_to_execute_foreign_is_an_error = run.failing_to_execute_foreign_is_an_error,
464464 .has_side_effects = run.has_side_effects,
465 .test_runner_mode = run.test_runner_mode,
466465 .color = run.color,
467466 .stdio = switch (run.stdio) {
468467 .infer_from_args => .infer_from_args,
469468 .inherit => .inherit,
470469 .check => .check,
471470 .zig_test => .zig_test,
471 .protocol => .protocol,
472472 },
473473 .stdin = switch (run.stdin) {
474474 .none => .none,
lib/std/Build/Step/Run.zig+16-7
......@@ -92,7 +92,6 @@ captured_stdout: ?*CapturedStdIo,
9292captured_stderr: ?*CapturedStdIo,
9393
9494has_side_effects: bool,
95test_runner_mode: bool = false,
9695
9796/// If this Run step was produced by a Compile step, it is tracked here.
9897producer: ?*Step.Compile,
......@@ -127,7 +126,10 @@ pub const StdIo = union(enum) {
127126 check: std.ArrayList(Check),
128127 /// This Run step is running a zig unit test binary and will communicate
129128 /// extra metadata over the IPC protocol.
129 /// Deprecated, use `.protocol`.
130130 zig_test,
131 /// This Run step is running a binary that will communicate over the IPC protocol.
132 protocol,
131133
132134 pub const Check = union(enum) {
133135 expect_stderr_exact: []const u8,
......@@ -243,10 +245,13 @@ pub fn setName(run: *Run, name: []const u8) void {
243245 run.rename_step_with_output_arg = false;
244246}
245247
248/// Deprecated, use `enableProtocolMode`.
246249pub fn enableTestRunnerMode(run: *Run) void {
247 if (run.test_runner_mode) return;
248250 run.stdio = .zig_test;
249 run.test_runner_mode = true;
251}
252
253pub fn enableProtocolMode(run: *Run) void {
254 run.stdio = .protocol;
250255}
251256
252257pub const ArgOptions = struct {
......@@ -750,8 +755,10 @@ pub fn addCheck(run: *Run, new_check: StdIo.Check) void {
750755}
751756
752757pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath {
753 assert(run.stdio != .inherit);
754 assert(run.stdio != .zig_test);
758 switch (run.stdio) {
759 .infer_from_args, .check => {},
760 .inherit, .zig_test, .protocol => unreachable,
761 }
755762
756763 const b = run.step.owner;
757764 const graph = b.graph;
......@@ -771,8 +778,10 @@ pub fn captureStdErr(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPa
771778}
772779
773780pub fn captureStdOut(run: *Run, options: CapturedStdIo.Options) std.Build.LazyPath {
774 assert(run.stdio != .inherit);
775 assert(run.stdio != .zig_test);
781 switch (run.stdio) {
782 .infer_from_args, .check => {},
783 .inherit, .zig_test, .protocol => unreachable,
784 }
776785
777786 const b = run.step.owner;
778787 const graph = b.graph;
lib/std/Io/Reader/Limited.zig+2-4
......@@ -28,8 +28,7 @@ pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited {
2828fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
2929 const l: *Limited = @fieldParentPtr("interface", r);
3030 if (l.remaining == .nothing) return error.EndOfStream;
31 const combined_limit = limit.min(l.remaining);
32 const n = try l.unlimited.stream(w, combined_limit);
31 const n = try l.unlimited.stream(w, limit.min(l.remaining));
3332 l.remaining = l.remaining.subtract(n).?;
3433 return n;
3534}
......@@ -53,8 +52,7 @@ test stream {
5352fn discard(r: *Reader, limit: Limit) Reader.Error!usize {
5453 const l: *Limited = @fieldParentPtr("interface", r);
5554 if (l.remaining == .nothing) return error.EndOfStream;
56 const combined_limit = limit.min(l.remaining);
57 const n = try l.unlimited.discard(combined_limit);
55 const n = try l.unlimited.discard(limit.min(l.remaining));
5856 l.remaining = l.remaining.subtract(n).?;
5957 return n;
6058}
lib/std/Io/Threaded.zig+36-31
......@@ -4462,7 +4462,7 @@ fn dirCreateFilePosix(
44624462 var fl_flags: usize = fl: {
44634463 const syscall: Syscall = try .start();
44644464 while (true) {
4465 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));
4465 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(u32, 0));
44664466 switch (posix.errno(rc)) {
44674467 .SUCCESS => {
44684468 syscall.finish();
......@@ -5058,7 +5058,7 @@ fn dirOpenFilePosix(
50585058 var fl_flags: usize = fl: {
50595059 const syscall: Syscall = try .start();
50605060 while (true) {
5061 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));
5061 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(u32, 0));
50625062 switch (posix.errno(rc)) {
50635063 .SUCCESS => {
50645064 syscall.finish();
......@@ -15230,16 +15230,12 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
1523015230 const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null);
1523115231 for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeSentinel(u8, arg, 0)).ptr;
1523215232
15233 const prog_fileno = 3;
15234 comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno);
15235
1523615233 const env_block = env_block: {
15237 const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno;
1523815234 if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{
15239 .zig_progress_fd = prog_fd,
15235 .zig_progress_fd = prog_pipe[1],
1524015236 });
1524115237 break :env_block try t.environ.process_environ.createPosixBlock(arena, .{
15242 .zig_progress_fd = prog_fd,
15238 .zig_progress_fd = prog_pipe[1],
1524315239 });
1524415240 };
1524515241
......@@ -15267,23 +15263,42 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
1526715263 if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked;
1526815264 const ep1 = err_pipe[1];
1526915265
15270 setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
15271 setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
15272 setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
15273
15266 // Must happen befor clobbering fds below.
1527415267 switch (options.cwd) {
1527515268 .inherit => {},
15276 .dir => |cwd| {
15277 fchdir(cwd.handle) catch |err| forkBail(ep1, err);
15278 },
15279 .path => |cwd| {
15280 chdir(cwd) catch |err| forkBail(ep1, err);
15281 },
15269 .dir => |cwd| fchdir(cwd.handle) catch |err| forkBail(ep1, err),
15270 .path => |cwd| chdir(cwd) catch |err| forkBail(ep1, err),
1528215271 }
1528315272
15284 // Must happen after fchdir above, the cwd file descriptor might be
15285 // equal to prog_fileno and be clobbered by this dup2 call.
15286 if (prog_pipe[1] != -1) dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(ep1, err);
15273 for (options.inherit_dirs) |dir| switch (posix.errno(
15274 posix.system.fcntl(dir.handle, posix.F.SETFD, @as(u32, 0)),
15275 )) {
15276 .SUCCESS => {},
15277 else => |err| forkBail(ep1, posix.unexpectedErrno(err)),
15278 };
15279 for (options.inherit_files) |file| switch (posix.errno(
15280 posix.system.fcntl(file.handle, posix.F.SETFD, @as(u32, 0)),
15281 )) {
15282 .SUCCESS => {},
15283 else => |err| forkBail(ep1, posix.unexpectedErrno(err)),
15284 };
15285 if (prog_pipe[1] != -1) switch (posix.errno(
15286 posix.system.fcntl(prog_pipe[1], posix.F.SETFD, @as(u32, 0)),
15287 )) {
15288 .SUCCESS => {},
15289 else => |err| forkBail(ep1, posix.unexpectedErrno(err)),
15290 };
15291 for (
15292 [_]process.SpawnOptions.StdIo{ options.stdin, options.stdout, options.stderr },
15293 [_]posix.fd_t{ stdin_pipe[0], stdout_pipe[1], stderr_pipe[1] },
15294 [_]posix.fd_t{ posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO },
15295 ) |stdio, pipe_fd, std_fd| switch (stdio) {
15296 .pipe => dup2(pipe_fd, std_fd) catch |err| forkBail(ep1, err),
15297 .close => closeFd(std_fd),
15298 .inherit => {},
15299 .ignore => dup2(dev_null_fd, std_fd) catch |err| forkBail(ep1, err),
15300 .file => |file| dup2(file.handle, std_fd) catch |err| forkBail(ep1, err),
15301 };
1528715302
1528815303 if (options.gid) |gid| {
1528915304 switch (posix.errno(posix.system.setregid(gid, gid))) {
......@@ -15746,16 +15761,6 @@ fn destroyPipe(pipe: [2]posix.fd_t) void {
1574615761 if (pipe[0] != pipe[1]) closeFd(pipe[1]);
1574715762}
1574815763
15749fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
15750 switch (stdio) {
15751 .pipe => try dup2(pipe_fd, std_fileno),
15752 .close => closeFd(std_fileno),
15753 .inherit => {},
15754 .ignore => try dup2(dev_null_fd, std_fileno),
15755 .file => |file| try dup2(file.handle, std_fileno),
15756 }
15757}
15758
1575915764fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
1576015765 const t: *Threaded = @ptrCast(@alignCast(userdata));
1576115766
lib/std/bit_set.zig+2-7
......@@ -723,13 +723,8 @@ pub const Dynamic = struct {
723723 // That slot holds the size of the true allocation, which
724724 // is needed by Zig's allocator interface in case a shrink
725725 // fails.
726
727 // Don't modify this value. Ideally it would go in const data so
728 // modifications would cause a bus error, but the only way
729 // to discard a const qualifier is through intFromPtr, which
730 // cannot currently round trip at comptime.
731 var empty_masks_data = [_]MaskInt{ 0, undefined };
732 const empty_masks_ptr = empty_masks_data[1..2];
726 const empty_masks_data = [_]MaskInt{ 0, undefined };
727 const empty_masks_ptr = @constCast(empty_masks_data[1..2]);
733728
734729 /// Creates a bit set with no elements present.
735730 /// If bit_length is not zero, deinit must eventually be called.
lib/std/process.zig+2
......@@ -390,6 +390,8 @@ pub const SpawnOptions = struct {
390390 stdin: StdIo = .inherit,
391391 stdout: StdIo = .inherit,
392392 stderr: StdIo = .inherit,
393 inherit_dirs: []const Dir = &.{},
394 inherit_files: []const File = &.{},
393395
394396 /// Set to true to obtain rusage information for the child process.
395397 /// Depending on the target platform and implementation status, the
lib/std/zig/Client.zig+28
......@@ -60,6 +60,7 @@ pub const Message = struct {
6060 start_fuzzing,
6161 /// The message body has the same format as in Server.
6262 new_fuzz_input,
63 args,
6364
6465 /// Asks the server to run a list of steps.
6566 /// Body is a `BuildSteps`.
......@@ -69,6 +70,33 @@ pub const Message = struct {
6970 _,
7071 };
7172
73 pub const Arg = enum(u8) {
74 /// Trailing:
75 /// * string: [:0]const u8,
76 string,
77 /// Trailing:
78 /// * string: [:0]const u8,
79 prefix,
80 /// Trailing:
81 /// * string: [:0]const u8,
82 suffix,
83 /// Trailing:
84 /// * handle: Handle,
85 input_file,
86 /// Trailing:
87 /// * handle: Handle,
88 input_dir,
89 /// Trailing:
90 /// * handle: Handle,
91 input_file_content,
92 /// Trailing:
93 /// * handle: Handle,
94 output_file,
95 /// Trailing:
96 /// * handle: Handle,
97 output_dir,
98 };
99
72100 /// Trailing:
73101 /// * step_indices: [step_count]std.Build.Configuration.Step.Index,
74102 pub const BuildSteps = extern struct {
test/incremental2/change_panic_handler_explicit/0-main.zig created+46
......@@ -0,0 +1,46 @@
1pub fn main() !u8 {
2 var a: u8 = undefined;
3 a = 255;
4 _ = a + 1;
5 return 1;
6}
7const no_panic = std.debug.no_panic;
8pub const panic = struct {
9 pub const call = myPanic;
10 pub fn integerOverflow() noreturn {
11 @panic("integer overflow");
12 }
13 pub const sentinelMismatch = no_panic.sentinelMismatch;
14 pub const unwrapError = no_panic.unwrapError;
15 pub const outOfBounds = no_panic.outOfBounds;
16 pub const startGreaterThanEnd = no_panic.startGreaterThanEnd;
17 pub const inactiveUnionField = no_panic.inactiveUnionField;
18 pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder;
19 pub const reachedUnreachable = no_panic.reachedUnreachable;
20 pub const unwrapNull = no_panic.unwrapNull;
21 pub const castToNull = no_panic.castToNull;
22 pub const incorrectAlignment = no_panic.incorrectAlignment;
23 pub const invalidErrorCode = no_panic.invalidErrorCode;
24 pub const unexpectedErrorCode = no_panic.unexpectedErrorCode;
25 pub const integerOutOfBounds = no_panic.integerOutOfBounds;
26 pub const shlOverflow = no_panic.shlOverflow;
27 pub const shrOverflow = no_panic.shrOverflow;
28 pub const divideByZero = no_panic.divideByZero;
29 pub const exactDivisionRemainder = no_panic.exactDivisionRemainder;
30 pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds;
31 pub const corruptSwitch = no_panic.corruptSwitch;
32 pub const shiftRhsTooBig = no_panic.shiftRhsTooBig;
33 pub const invalidEnumValue = no_panic.invalidEnumValue;
34 pub const forLenMismatch = no_panic.forLenMismatch;
35 pub const copyLenMismatch = no_panic.copyLenMismatch;
36 pub const memcpyAlias = no_panic.memcpyAlias;
37 pub const noreturnReturned = no_panic.noreturnReturned;
38 pub const loadUninstantiableType = no_panic.loadUninstantiableType;
39};
40fn myPanic(msg: []const u8, _: ?usize) noreturn {
41 var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{});
42 stdout_writer.interface.print("panic message: {s}\n", .{msg}) catch {};
43 std.process.exit(0);
44}
45const std = @import("std");
46const io = std.Io.Threaded.global_single_threaded.io();
test/incremental2/change_panic_handler_explicit/1-main.zig created+46
......@@ -0,0 +1,46 @@
1pub fn main() !u8 {
2 var a: u8 = undefined;
3 a = 255;
4 _ = a + 1;
5 return 1;
6}
7const no_panic = std.debug.no_panic;
8pub const panic = struct {
9 pub const call = myPanic;
10 pub fn integerOverflow() noreturn {
11 @panic("integer overflow");
12 }
13 pub const sentinelMismatch = no_panic.sentinelMismatch;
14 pub const unwrapError = no_panic.unwrapError;
15 pub const outOfBounds = no_panic.outOfBounds;
16 pub const startGreaterThanEnd = no_panic.startGreaterThanEnd;
17 pub const inactiveUnionField = no_panic.inactiveUnionField;
18 pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder;
19 pub const reachedUnreachable = no_panic.reachedUnreachable;
20 pub const unwrapNull = no_panic.unwrapNull;
21 pub const castToNull = no_panic.castToNull;
22 pub const incorrectAlignment = no_panic.incorrectAlignment;
23 pub const invalidErrorCode = no_panic.invalidErrorCode;
24 pub const unexpectedErrorCode = no_panic.unexpectedErrorCode;
25 pub const integerOutOfBounds = no_panic.integerOutOfBounds;
26 pub const shlOverflow = no_panic.shlOverflow;
27 pub const shrOverflow = no_panic.shrOverflow;
28 pub const divideByZero = no_panic.divideByZero;
29 pub const exactDivisionRemainder = no_panic.exactDivisionRemainder;
30 pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds;
31 pub const corruptSwitch = no_panic.corruptSwitch;
32 pub const shiftRhsTooBig = no_panic.shiftRhsTooBig;
33 pub const invalidEnumValue = no_panic.invalidEnumValue;
34 pub const forLenMismatch = no_panic.forLenMismatch;
35 pub const copyLenMismatch = no_panic.copyLenMismatch;
36 pub const memcpyAlias = no_panic.memcpyAlias;
37 pub const noreturnReturned = no_panic.noreturnReturned;
38 pub const loadUninstantiableType = no_panic.loadUninstantiableType;
39};
40fn myPanic(msg: []const u8, _: ?usize) noreturn {
41 var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{});
42 stdout_writer.interface.print("new panic message: {s}\n", .{msg}) catch {};
43 std.process.exit(0);
44}
45const std = @import("std");
46const io = std.Io.Threaded.global_single_threaded.io();
test/incremental2/change_panic_handler_explicit/2-main.zig created+46
......@@ -0,0 +1,46 @@
1pub fn main() !u8 {
2 var a: u8 = undefined;
3 a = 255;
4 _ = a + 1;
5 return 1;
6}
7const no_panic = std.debug.no_panic;
8pub const panic = struct {
9 pub const call = myPanicNew;
10 pub fn integerOverflow() noreturn {
11 @panic("integer overflow");
12 }
13 pub const sentinelMismatch = no_panic.sentinelMismatch;
14 pub const unwrapError = no_panic.unwrapError;
15 pub const outOfBounds = no_panic.outOfBounds;
16 pub const startGreaterThanEnd = no_panic.startGreaterThanEnd;
17 pub const inactiveUnionField = no_panic.inactiveUnionField;
18 pub const sliceCastLenRemainder = no_panic.sliceCastLenRemainder;
19 pub const reachedUnreachable = no_panic.reachedUnreachable;
20 pub const unwrapNull = no_panic.unwrapNull;
21 pub const castToNull = no_panic.castToNull;
22 pub const incorrectAlignment = no_panic.incorrectAlignment;
23 pub const invalidErrorCode = no_panic.invalidErrorCode;
24 pub const unexpectedErrorCode = no_panic.unexpectedErrorCode;
25 pub const integerOutOfBounds = no_panic.integerOutOfBounds;
26 pub const shlOverflow = no_panic.shlOverflow;
27 pub const shrOverflow = no_panic.shrOverflow;
28 pub const divideByZero = no_panic.divideByZero;
29 pub const exactDivisionRemainder = no_panic.exactDivisionRemainder;
30 pub const integerPartOutOfBounds = no_panic.integerPartOutOfBounds;
31 pub const corruptSwitch = no_panic.corruptSwitch;
32 pub const shiftRhsTooBig = no_panic.shiftRhsTooBig;
33 pub const invalidEnumValue = no_panic.invalidEnumValue;
34 pub const forLenMismatch = no_panic.forLenMismatch;
35 pub const copyLenMismatch = no_panic.copyLenMismatch;
36 pub const memcpyAlias = no_panic.memcpyAlias;
37 pub const noreturnReturned = no_panic.noreturnReturned;
38 pub const loadUninstantiableType = no_panic.loadUninstantiableType;
39};
40fn myPanicNew(msg: []const u8, _: ?usize) noreturn {
41 var stdout_writer = std.Io.File.stdout().writerStreaming(io, &.{});
42 stdout_writer.interface.print("third panic message: {s}\n", .{msg}) catch {};
43 std.process.exit(0);
44}
45const std = @import("std");
46const io = std.Io.Threaded.global_single_threaded.io();
test/incremental2/change_panic_handler_explicit/manifest created+23
......@@ -0,0 +1,23 @@
1#exe {
2-Mroot=main.zig
3#}
4
5#write main.zig 0-main.zig
6#update
7#check stdout {
8panic message: integer overflow
9#}
10
11#write main.zig 1-main.zig
12#update {
13change the panic handler body#}
14#check stdout {
15new panic message: integer overflow
16#}
17
18#write main.zig 2-main.zig
19#update {
20change the panic handler function value#}
21#check stdout {
22third panic message: integer overflow
23#}
test/incremental2/hello created+28
......@@ -0,0 +1,28 @@
1#exe {
2-Mroot=main.zig
3#}
4
5#write main.zig {
6const std = @import("std");
7const io = std.Io.Threaded.global_single_threaded.io();
8pub fn main() !void {
9 try std.Io.File.stdout().writeStreamingAll(io, "good morning\n");
10}
11#}
12#update
13#check stdout {
14good morning
15#}
16
17#write main.zig {
18const std = @import("std");
19const io = std.Io.Threaded.global_single_threaded.io();
20pub fn main() !void {
21 try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n");
22}
23#}
24#update {
25change the string#}
26#check stdout {
27おはようございます
28#}
test/src/runner.zig created+279
......@@ -0,0 +1,279 @@
1const std = @import("std");
2const Args = struct {
3 test_dir: ?std.Io.Dir,
4 manifest_file: ?std.Io.File,
5 zig_file: ?std.Io.File,
6 lib_dir: ?std.Io.Dir,
7 src_dir: ?std.Io.Dir,
8};
9pub fn main(init: std.process.Init) !void {
10 var stdin_buffer: [512]u8 = undefined;
11 var stdout_buffer: [512]u8 = undefined;
12 var stderr_buffer: [512]u8 = undefined;
13 var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer);
14 var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer);
15 var stderr: std.Io.File.Writer = .initStreaming(.stderr(), init.io, &stderr_buffer);
16 var server: std.zig.Server = .{
17 .in = &stdin.interface,
18 .out = &stdout.interface,
19 };
20 try server.serveStringMessage(.zig_version, @import("builtin").zig_version_string);
21 var args: Args = .{
22 .test_dir = null,
23 .manifest_file = null,
24 .zig_file = null,
25 .lib_dir = null,
26 .src_dir = null,
27 };
28 while (true) {
29 const hdr = try server.receiveMessage();
30 switch (hdr.tag) {
31 else => {
32 std.debug.print("unsupported message: {t}\n", .{hdr.tag});
33 std.process.exit(1);
34 },
35 .exit => {
36 std.process.exit(0);
37 },
38 .args => {
39 const args_body = try init.arena.allocator().alloc(u8, hdr.bytes_len);
40 try server.in.readSliceAll(args_body);
41 var state: enum { positional, zig, lib, src } = .positional;
42 var args_body_offset: usize = 0;
43 while (args_body.len - args_body_offset > 0) {
44 const arg: std.zig.Client.Message.Arg =
45 @fromBackingInt(args_body[args_body_offset]);
46 args_body_offset += 1;
47 switch (arg) {
48 .string => {
49 const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?;
50 const string = args_body[args_body_offset..end];
51 args_body_offset = end + 1;
52 _ = string;
53 },
54 .prefix => {
55 const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?;
56 const string = args_body[args_body_offset..end];
57 args_body_offset = end + 1;
58 state = if (std.mem.eql(u8, string, "--zig="))
59 .zig
60 else if (std.mem.eql(u8, string, "--lib="))
61 .lib
62 else if (std.mem.eql(u8, string, "--src="))
63 .src
64 else
65 unreachable;
66 continue;
67 },
68 .suffix => unreachable,
69 .input_dir, .output_dir => {
70 const dir_handle: *align(1) const std.Io.Dir.Handle = @ptrCast(
71 args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)],
72 );
73 args_body_offset += @sizeOf(std.Io.Dir.Handle);
74 const dir: std.Io.Dir = .{
75 .handle = dir_handle.*,
76 };
77 switch (state) {
78 .positional => {
79 std.debug.assert(args.test_dir == null);
80 args.test_dir = dir;
81 std.debug.assert(args.manifest_file == null);
82 args.manifest_file = try dir.openFile(init.io, "manifest", .{});
83 },
84 .zig => unreachable,
85 .lib => {
86 std.debug.assert(args.lib_dir == null);
87 args.lib_dir = dir;
88 },
89 .src => {
90 std.debug.assert(args.src_dir == null);
91 args.src_dir = dir;
92 },
93 }
94 },
95 .input_file, .input_file_content, .output_file => {
96 const file_handle: *align(1) const std.Io.File.Handle = @ptrCast(
97 args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)],
98 );
99 args_body_offset += @sizeOf(std.Io.File.Handle);
100 const file: std.Io.File = .{
101 .handle = file_handle.*,
102 .flags = .{ .nonblocking = false },
103 };
104 switch (state) {
105 .positional => {
106 std.debug.assert(args.manifest_file == null);
107 args.manifest_file = file;
108 },
109 .zig => {
110 std.debug.assert(args.zig_file == null);
111 args.zig_file = file;
112 },
113 .lib, .src => unreachable,
114 }
115 },
116 }
117 state = .positional;
118 }
119 },
120 .query_test_metadata => try server.serveTestMetadata(.{
121 .names = &.{0},
122 .expected_panic_msgs = &.{0},
123 .string_bytes = "runner\x00",
124 }),
125 .run_test => {
126 const test_index = try server.receiveBody_u32();
127 try server.serveBodylessMessage(.test_started);
128 const test_result = runTest(init.io, &stderr.interface, &args);
129 try stderr.flush();
130 try server.serveTestResults(.{
131 .index = test_index,
132 .flags = .{
133 .status = if (test_result) .pass else |err| switch (err) {
134 else => status: {
135 if (@errorReturnTrace()) |trace| std.debug.dumpErrorReturnTrace(trace);
136 break :status .fail;
137 },
138 error.SkipZigTest => .skip,
139 },
140 .fuzz = false,
141 .log_err_count = 0,
142 .leak_count = 0,
143 },
144 });
145 },
146 }
147 }
148}
149fn runTest(io: std.Io, stderr: *std.Io.Writer, args: *const Args) anyerror!void {
150 const manifest_file = args.manifest_file orelse return error.MissingManifestArg;
151 const src_dir = args.src_dir orelse return error.MissingSrcDir;
152 var manifest_buffer: [512]u8 = undefined;
153 var manifest_fr = manifest_file.reader(io, &manifest_buffer);
154 var skip_delimiter = false;
155 var update_mtime = std.Io.Clock.real.now(io);
156 var update_num: usize = 0;
157 while (manifest_fr.interface.takeSentinel('\n')) |untrimmed_line| {
158 const line = std.mem.trimEnd(u8, untrimmed_line, "\r");
159 var line_it = std.mem.tokenizeScalar(u8, line, ' ');
160 const cmd_str = std.mem.cutPrefix(u8, line_it.next() orelse continue, "#").?;
161 if (skip_delimiter) {
162 std.debug.assert(std.mem.eql(u8, cmd_str, "}"));
163 skip_delimiter = false;
164 continue;
165 }
166 const cmd = std.meta.stringToEnum(
167 enum { obj, exe, lib, write, delete, update, check },
168 cmd_str,
169 ) orelse return error.UnknownCommand;
170 var maybe_arg = line_it.next();
171 var contents_file: ?std.Io.File = null;
172 defer if (contents_file) |file| file.close(io);
173 var contents_impl: union {
174 dr: DelimitedReader,
175 fr: std.Io.File.Reader,
176 } = undefined;
177 const contents_r: *std.Io.Reader = contents_r: {
178 switch (cmd) {
179 else => {},
180 .delete => break :contents_r .ending,
181 }
182 const contents_path = contents_path: {
183 const line_rest = line_it.rest();
184 if (line_rest.len > 0) break :contents_path line_rest;
185 const contents_path = maybe_arg orelse break :contents_r .ending;
186 maybe_arg = null;
187 break :contents_path contents_path;
188 };
189 if (std.mem.eql(u8, contents_path, "{")) {
190 contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &.{}) };
191 skip_delimiter = true;
192 break :contents_r &contents_impl.dr.interface;
193 }
194 const test_dir = args.test_dir orelse return error.ContentsFileInFileTest;
195 contents_file = try test_dir.openFile(io, contents_path, .{});
196 contents_impl = .{ .fr = contents_file.?.reader(io, &.{}) };
197 break :contents_r &contents_impl.fr.interface;
198 };
199 switch (cmd) {
200 else => {
201 try stderr.print("{t} {s} {{\n", .{ cmd, maybe_arg orelse "" });
202 _ = try contents_r.streamRemaining(stderr);
203 try stderr.writeAll("\n}\n");
204 },
205 .write => {
206 const file = try src_dir.createFile(io, maybe_arg orelse return error.MissingArg, .{});
207 defer file.close(io);
208 var fw = file.writer(io, &.{});
209 _ = try contents_r.streamRemaining(&fw.interface);
210 try file.setTimestamps(io, .{ .modify_timestamp = .{ .new = update_mtime } });
211 },
212 .delete => try src_dir.deleteFile(io, maybe_arg orelse return error.MissingArg),
213 .update => {
214 var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined;
215 const name = switch (try contents_r.readSliceShort(&name_buffer)) {
216 0 => switch (update_num) {
217 0 => "initial update",
218 else => |num| std.mem.print(&name_buffer, "update {d}", .{num}) catch
219 &name_buffer,
220 },
221 else => |name_len| name_buffer[0..name_len],
222 };
223 _ = try contents_r.discardRemaining();
224 try stderr.print("update({q})\n", .{name});
225 update_mtime = update_mtime.addDuration(.fromSeconds(2));
226 update_num += 1;
227 },
228 }
229 std.debug.assert(try contents_r.discardRemaining() == 0);
230 } else |err| switch (err) {
231 else => |e| return e,
232 error.EndOfStream => if (skip_delimiter) return error.MissingDelimiter,
233 }
234}
235const DelimitedReader = struct {
236 const Io = std.Io;
237
238 unlimited: *Io.Reader,
239 remaining: Io.Limit,
240 delimiter: []const u8,
241 interface: Io.Reader,
242
243 pub fn init(reader: *Io.Reader, delimiter: []const u8, buffer: []u8) DelimitedReader {
244 return .{
245 .unlimited = reader,
246 .remaining = .nothing,
247 .delimiter = delimiter,
248 .interface = .{
249 .vtable = &.{
250 .stream = stream,
251 .discard = discard,
252 },
253 .buffer = buffer,
254 .seek = 0,
255 .end = 0,
256 },
257 };
258 }
259 fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
260 const d: *DelimitedReader = @fieldParentPtr("interface", r);
261 const block = try d.unlimited.peekGreedy(d.delimiter.len);
262 d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse
263 block.len - d.delimiter.len + 1);
264 if (d.remaining == .nothing) return error.EndOfStream;
265 const n = try d.unlimited.stream(w, limit.min(d.remaining));
266 d.remaining = d.remaining.subtract(n).?;
267 return n;
268 }
269 fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize {
270 const d: *DelimitedReader = @fieldParentPtr("interface", r);
271 const block = try d.unlimited.peekGreedy(d.delimiter.len);
272 d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse
273 block.len - d.delimiter.len + 1);
274 if (d.remaining == .nothing) return error.EndOfStream;
275 const n = try d.unlimited.discard(limit.min(d.remaining));
276 d.remaining = d.remaining.subtract(n).?;
277 return n;
278 }
279};
test/standalone/build.zig+1
......@@ -44,6 +44,7 @@ pub fn build(b: *std.Build) void {
4444 "../../tools/generate_linux_syscalls.zig",
4545 "../../tools/process_headers.zig",
4646 "../../tools/migrate_langref.zig",
47 "../../tools/runner-check.zig",
4748 "../../tools/unskip_spirv_tests.zig",
4849 "../../tools/update-linux-headers.zig",
4950 "../../tools/update_clang_options.zig",
test/tests.zig+86-1
......@@ -3365,7 +3365,7 @@ pub fn addIncrementalTests(
33653365
33663366 var it = try dir.walk(b.graph.arena);
33673367 while (try it.next(io)) |entry| {
3368 if (std.mem.endsWith(u8, entry.basename, ".swp")) continue;
3368 if (isEditorFileName(entry.basename)) continue;
33693369
33703370 for (options.test_filters) |test_filter| {
33713371 if (std.mem.find(u8, entry.path, test_filter)) |_| break;
......@@ -3435,6 +3435,91 @@ pub fn addIncrementalTests(
34353435 }
34363436}
34373437
3438pub fn addNewIncrementalTests(
3439 b: *std.Build,
3440 runner: *std.Build.Step.Compile,
3441 options: IncrementalTestOptions,
3442) !*Step {
3443 const tests_step = b.step("test-new-incremental", "Run the new incremental compilation test cases");
3444
3445 const tests_path = b.path("test/incremental2");
3446 b.dependOnDirectoryContents(tests_path);
3447
3448 var tests_dir = try b.root.openDir(b.graph.io, "test/incremental2", .{ .iterate = true });
3449 defer tests_dir.close(b.graph.io);
3450 var test_it = tests_dir.iterate();
3451 while (try test_it.next(b.graph.io)) |@"test"| {
3452 const test_path = tests_path.path(b, @"test".name);
3453 switch (@"test".kind) {
3454 else => continue,
3455 .file => if (isEditorFileName(@"test".name)) continue,
3456 .directory => {},
3457 }
3458
3459 for (incremental_targets) |test_target| {
3460 const resolved_target = b.resolveTargetQuery(test_target.target);
3461
3462 if (options.skip_non_native and !isNative(&resolved_target, &b.graph.host.result))
3463 continue;
3464
3465 const target = &resolved_target.result;
3466
3467 if (options.skip_wasm and target.cpu.arch.isWasm()) continue;
3468
3469 if (options.skip_freebsd and target.os.tag == .freebsd) continue;
3470 if (options.skip_netbsd and target.os.tag == .netbsd) continue;
3471 if (options.skip_openbsd and target.os.tag == .openbsd) continue;
3472 if (options.skip_windows and target.os.tag == .windows) continue;
3473 if (options.skip_darwin and target.os.tag.isDarwin()) continue;
3474 if (options.skip_linux and target.os.tag == .linux) continue;
3475
3476 if (options.skip_llvm and test_target.backend == .llvm) continue;
3477
3478 const target_str = b.fmt("{s}-{t}", .{
3479 resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"),
3480 test_target.backend,
3481 });
3482
3483 if (options.test_target_filters.len > 0) {
3484 for (options.test_target_filters) |filter| {
3485 if (std.mem.find(u8, target_str, filter) != null) break;
3486 } else continue;
3487 }
3488
3489 const run = b.addRunArtifact(runner);
3490 run.setName(@"test".name);
3491 run.enableProtocolMode();
3492
3493 switch (@"test".kind) {
3494 else => continue,
3495 .file => run.addFileArg(test_path),
3496 .directory => run.addDirectoryArg(test_path),
3497 }
3498 run.addPrefixedFileArg("--zig=", .zig_exe);
3499 run.addPrefixedDirectoryArg("--lib=", .zig_lib);
3500 _ = run.addPrefixedOutputDirectoryArg("--src=", "src");
3501 run.addArgs(&.{ "--target", target_str });
3502
3503 run.addArg("--quiet"); // don't fill stderr telling us about skipped tests etc
3504
3505 run.addThirdPartyEnabledArgDarling(.{ .enabled = "-fdarling" });
3506 run.addThirdPartyEnabledArgQemu(.{ .enabled = "-fqemu" });
3507 run.addThirdPartyEnabledArgRosetta(.{ .enabled = "-frosetta" });
3508 run.addThirdPartyEnabledArgWasmtime(.{ .enabled = "-fwasmtime" });
3509 run.addThirdPartyEnabledArgWine(.{ .enabled = "-fwine" });
3510
3511 tests_step.dependOn(&run.step);
3512 }
3513 }
3514
3515 return tests_step;
3516}
3517
3518fn isEditorFileName(name: []const u8) bool {
3519 return (std.mem.startsWith(u8, name, "#") and std.mem.endsWith(u8, name, "#")) or
3520 std.mem.endsWith(u8, name, "~") or std.mem.endsWith(u8, name, ".swp");
3521}
3522
34383523pub fn addLlvmIrTests(b: *std.Build, options: LlvmIrContext.Options) ?*Step {
34393524 const step = b.step("test-llvm-ir", "Run the LLVM IR tests");
34403525
tools/runner-check.zig created+151
......@@ -0,0 +1,151 @@
1const std = @import("std");
2pub fn main(init: std.process.Init) !void {
3 const arena = init.arena.allocator();
4
5 var test_path_arg: ?[]const u8 = null;
6 var zig_path_arg: ?[]const u8 = null;
7 var lib_path_arg: ?[]const u8 = null;
8 var keep_src = false;
9
10 var arg_it = try init.minimal.args.iterateAllocator(arena);
11 std.debug.assert(arg_it.skip());
12 while (arg_it.next()) |arg| {
13 if (std.mem.eql(u8, arg, "--zig")) {
14 zig_path_arg = arg_it.next() orelse fail("missing arg after '{s}'", .{arg});
15 } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| {
16 zig_path_arg = zig_path;
17 } else if (std.mem.eql(u8, arg, "--lib")) {
18 lib_path_arg = arg_it.next() orelse fail("missing arg after '{s}'", .{arg});
19 } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| {
20 lib_path_arg = lib_path;
21 } else if (std.mem.eql(u8, arg, "--keep-src")) {
22 keep_src = true;
23 } else {
24 if (test_path_arg) |_| fail("unknown arg '{s}'", .{arg});
25 test_path_arg = arg;
26 }
27 }
28
29 const test_path = test_path_arg orelse fail("missing 'path/to/test'", .{});
30 const zig_path = zig_path_arg orelse fail("missing '--zig=path/to/zig'", .{});
31 const lib_path = lib_path_arg orelse fail("missing '--lib=path/to/lib'", .{});
32
33 const test_file = try std.Io.Dir.cwd().openFile(init.io, test_path, .{
34 .allow_directory = true,
35 });
36 const zig_file = try std.Io.Dir.cwd().openFile(init.io, zig_path, .{});
37 defer zig_file.close(init.io);
38 const lib_dir = try std.Io.Dir.cwd().openDir(init.io, lib_path, .{});
39 defer lib_dir.close(init.io);
40
41 const src_dir_path = "src_" ++ std.fmt.hex(rand_int: {
42 var rand_int: u64 = undefined;
43 init.io.random(@ptrCast(&rand_int));
44 break :rand_int rand_int;
45 });
46 var src_dir = try std.Io.Dir.cwd().createDirPathOpen(init.io, src_dir_path, .{});
47 defer {
48 src_dir.close(init.io);
49 if (!keep_src) std.Io.Dir.cwd().deleteTree(init.io, src_dir_path) catch |err| {
50 std.log.warn("failed to delete tree '{s}': {t}", .{ src_dir_path, err });
51 };
52 }
53
54 var child = try std.process.spawn(init.io, .{
55 .argv = &.{
56 zig_path,
57 "run",
58 try std.fs.path.resolveAlloc(arena, &.{ lib_path, "..", "test", "src", "runner.zig" }),
59 },
60 .stdin = .pipe,
61 .stdout = .pipe,
62 .inherit_dirs = &.{ lib_dir, src_dir },
63 .inherit_files = &.{ zig_file, test_file },
64 });
65
66 var stdout_buffer: [512]u8 = undefined;
67 var stdout_reader = child.stdout.?.readerStreaming(init.io, &stdout_buffer);
68 var stdin_buffer: [512]u8 = undefined;
69 var stdin_writer = child.stdin.?.writerStreaming(init.io, &stdin_buffer);
70 var client: std.zig.Client = .{
71 .in = &stdout_reader.interface,
72 .out = &stdin_writer.interface,
73 };
74 {
75 const Arg = std.zig.Client.Message.Arg;
76 var args: std.ArrayList(u8) = .initBuffer(try arena.alloc(u8, 0 +
77 @sizeOf(Arg) + @sizeOf(std.Io.File.Handle) +
78 @sizeOf(Arg) + "--zig=\x00".len +
79 @sizeOf(Arg) + @sizeOf(std.Io.File.Handle) +
80 @sizeOf(Arg) + "--lib=\x00".len +
81 @sizeOf(Arg) + @sizeOf(std.Io.Dir.Handle) +
82 @sizeOf(Arg) + "--src=\x00".len +
83 @sizeOf(Arg) + @sizeOf(std.Io.Dir.Handle)));
84
85 args.appendAssumeCapacity(@backingInt(@as(Arg, switch ((try test_file.stat(init.io)).kind) {
86 else => unreachable,
87 .file => .input_file,
88 .directory => .input_dir,
89 })));
90 args.appendSliceAssumeCapacity(@ptrCast(&test_file.handle));
91
92 args.appendAssumeCapacity(@backingInt(Arg.prefix));
93 args.appendSliceAssumeCapacity("--zig=");
94 args.appendAssumeCapacity(0);
95
96 args.appendAssumeCapacity(@backingInt(Arg.input_file));
97 args.appendSliceAssumeCapacity(@ptrCast(&zig_file.handle));
98
99 args.appendAssumeCapacity(@backingInt(Arg.prefix));
100 args.appendSliceAssumeCapacity("--lib=");
101 args.appendAssumeCapacity(0);
102
103 args.appendAssumeCapacity(@backingInt(Arg.input_dir));
104 args.appendSliceAssumeCapacity(@ptrCast(&lib_dir.handle));
105
106 args.appendAssumeCapacity(@backingInt(Arg.prefix));
107 args.appendSliceAssumeCapacity("--src=");
108 args.appendAssumeCapacity(0);
109
110 args.appendAssumeCapacity(@backingInt(Arg.output_dir));
111 args.appendSliceAssumeCapacity(@ptrCast(&src_dir.handle));
112
113 std.debug.assert(args.unusedCapacitySlice().len == 0);
114
115 try client.serveMessageHeader(.{
116 .tag = .args,
117 .bytes_len = @intCast(args.items.len),
118 });
119 try client.out.writeAll(args.items);
120
121 try client.serveBodylessMessage(.query_test_metadata);
122 while (true) {
123 const hdr = try client.receiveMessage();
124 switch (hdr.tag) {
125 else => try client.in.discardAll(hdr.bytes_len),
126 .zig_version => {
127 const body = try arena.alloc(u8, hdr.bytes_len);
128 try client.in.readSliceAll(body);
129 if (!std.mem.eql(u8, @import("builtin").zig_version_string, body)) return fail(
130 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
131 .{ @import("builtin").zig_version_string, body },
132 );
133 },
134 .test_metadata => {
135 try client.in.discardAll(hdr.bytes_len);
136 try client.serveRunTest(0);
137 },
138 .test_results => {
139 try client.in.discardAll(hdr.bytes_len);
140 break;
141 },
142 }
143 }
144 try client.serveBodylessMessage(.exit);
145 }
146 _ = try child.wait(init.io);
147}
148fn fail(comptime fmt: []const u8, args: anytype) noreturn {
149 std.log.err(fmt, args);
150 std.process.exit(1);
151}