authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-23 21:01:42-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-26 19:08:50-04:00
log00b59eb18ae796ab2d5b20e91025811fdaae0b13
tree7841ca45a45288baee48baf7a259251decefa46e
parent146d0529ca5c0f0044ea28c0450250273862df2a

Runner: implement error reporting


15 files changed, 1043 insertions(+), 610 deletions(-)

lib/compiler/Maker.zig+2-4
...@@ -4125,8 +4125,7 @@ const Fork = struct {...@@ -4125,8 +4125,7 @@ const Fork = struct {
4125 fork.arena_allocator = .init(gpa);4125 fork.arena_allocator = .init(gpa);
4126 const arena = fork.arena_allocator.allocator();4126 const arena = fork.arena_allocator.allocator();
41274127
4128 var error_bundle: std.zig.ErrorBundle.Wip = undefined;4128 var error_bundle: std.zig.ErrorBundle.Wip = try .init(gpa);
4129 try error_bundle.init(gpa);
4130 defer error_bundle.deinit();4129 defer error_bundle.deinit();
41314130
4132 const manifest_path = try fork.path.join(arena, Package.Manifest.basename);4131 const manifest_path = try fork.path.join(arena, Package.Manifest.basename);
...@@ -4227,8 +4226,7 @@ fn loadManifest(...@@ -4227,8 +4226,7 @@ fn loadManifest(
4227 errdefer manifest.deinit(gpa);4226 errdefer manifest.deinit(gpa);
42284227
4229 if (manifest.errors.len > 0) {4228 if (manifest.errors.len > 0) {
4230 var wip_errors: std.zig.ErrorBundle.Wip = undefined;4229 var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa);
4231 try wip_errors.init(gpa);
4232 defer wip_errors.deinit();4230 defer wip_errors.deinit();
42334231
4234 const src_path = try wip_errors.addString(Package.Manifest.basename);4232 const src_path = try wip_errors.addString(Package.Manifest.basename);
lib/compiler/Maker/Fetch.zig+1-1
...@@ -527,7 +527,7 @@ pub fn run(f: *Fetch) RunError!void {...@@ -527,7 +527,7 @@ pub fn run(f: *Fetch) RunError!void {
527 const arena = f.arena.allocator();527 const arena = f.arena.allocator();
528 const gpa = f.arena.child_allocator;528 const gpa = f.arena.child_allocator;
529529
530 try eb.init(gpa);530 eb.* = try .init(gpa);
531531
532 // Check the global zig package cache to see if the hash already exists. If532 // Check the global zig package cache to see if the hash already exists. If
533 // so, load, parse, and validate the build.zig.zon file therein, and skip533 // so, load, parse, and validate the build.zig.zon file therein, and skip
lib/compiler/Maker/Step/Run.zig+6-3
...@@ -861,6 +861,7 @@ fn waitZigTest(...@@ -861,6 +861,7 @@ fn waitZigTest(
861 .{ builtin.zig_version_string, body },861 .{ builtin.zig_version_string, body },
862 );862 );
863 },863 },
864 .error_bundle => step.result_error_bundle = try std.zig.Server.allocErrorBundle(gpa, body),
864 .test_metadata => {865 .test_metadata => {
865 // `metadata` would only be populated if we'd already seen a `test_metadata`, but we866 // `metadata` would only be populated if we'd already seen a `test_metadata`, but we
866 // only request it once (and importantly, we don't re-request it if we kill and867 // only request it once (and importantly, we don't re-request it if we kill and
...@@ -920,10 +921,12 @@ fn waitZigTest(...@@ -920,10 +921,12 @@ fn waitZigTest(
920 const name = md.testName(tr_hdr.index);921 const name = md.testName(tr_hdr.index);
921 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");922 const stderr_bytes = std.mem.trim(u8, stderr.buffered(), "\n");
922 stderr.tossBuffered();923 stderr.tossBuffered();
923 if (stderr_bytes.len == 0) {924 if (stderr_bytes.len > 0) {
924 try step.addError(maker, "'{s}' failed without output", .{name});
925 } else {
926 try step.addError(maker, "'{s}' failed:\n{s}", .{ name, stderr_bytes });925 try step.addError(maker, "'{s}' failed:\n{s}", .{ name, stderr_bytes });
926 } else if (step.result_error_bundle.errorMessageCount() > 0) {
927 try step.addError(maker, "'{s}' errored", .{name});
928 } else {
929 try step.addError(maker, "'{s}' failed without output", .{name});
927 }930 }
928 } else if (leak_count > 0) {931 } else if (leak_count > 0) {
929 const name = md.testName(tr_hdr.index);932 const name = md.testName(tr_hdr.index);
lib/compiler/resinator/main.zig+3-6
...@@ -790,8 +790,7 @@ fn cliDiagnosticsToErrorBundle(...@@ -790,8 +790,7 @@ fn cliDiagnosticsToErrorBundle(
790) !ErrorBundle {790) !ErrorBundle {
791 @branchHint(.cold);791 @branchHint(.cold);
792792
793 var bundle: ErrorBundle.Wip = undefined;793 var bundle: ErrorBundle.Wip = try .init(gpa);
794 try bundle.init(gpa);
795 errdefer bundle.deinit();794 errdefer bundle.deinit();
796795
797 try bundle.addRootErrorMessage(.{796 try bundle.addRootErrorMessage(.{
...@@ -837,8 +836,7 @@ fn diagnosticsToErrorBundle(...@@ -837,8 +836,7 @@ fn diagnosticsToErrorBundle(
837) !ErrorBundle {836) !ErrorBundle {
838 @branchHint(.cold);837 @branchHint(.cold);
839838
840 var bundle: ErrorBundle.Wip = undefined;839 var bundle: ErrorBundle.Wip = try .init(gpa);
841 try bundle.init(gpa);
842 errdefer bundle.deinit();840 errdefer bundle.deinit();
843841
844 var msg_buf: std.Io.Writer.Allocating = .init(gpa);842 var msg_buf: std.Io.Writer.Allocating = .init(gpa);
...@@ -919,8 +917,7 @@ fn diagnosticsToErrorBundle(...@@ -919,8 +917,7 @@ fn diagnosticsToErrorBundle(
919917
920fn errorStringToErrorBundle(allocator: Allocator, comptime format: []const u8, args: anytype) !ErrorBundle {918fn errorStringToErrorBundle(allocator: Allocator, comptime format: []const u8, args: anytype) !ErrorBundle {
921 @branchHint(.cold);919 @branchHint(.cold);
922 var bundle: ErrorBundle.Wip = undefined;920 var bundle: ErrorBundle.Wip = try .init(allocator);
923 try bundle.init(allocator);
924 errdefer bundle.deinit();921 errdefer bundle.deinit();
925 try bundle.addRootErrorMessage(.{922 try bundle.addRootErrorMessage(.{
926 .msg = try bundle.printString(format, args),923 .msg = try bundle.printString(format, args),
lib/compiler/util.zig+1-2
...@@ -10,8 +10,7 @@ pub fn aroDiagnosticsToErrorBundle(...@@ -10,8 +10,7 @@ pub fn aroDiagnosticsToErrorBundle(
10) !ErrorBundle {10) !ErrorBundle {
11 @branchHint(.cold);11 @branchHint(.cold);
1212
13 var bundle: ErrorBundle.Wip = undefined;13 var bundle: ErrorBundle.Wip = try .init(gpa);
14 try bundle.init(gpa);
15 errdefer bundle.deinit();14 errdefer bundle.deinit();
1615
17 if (fail_msg) |msg| {16 if (fail_msg) |msg| {
lib/std/Io/Reader.zig+1-14
...@@ -849,27 +849,14 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {...@@ -849,27 +849,14 @@ pub fn peekDelimiterInclusive(r: *Reader, delimiter: u8) DelimiterError![]u8 {
849 }849 }
850 while (true) {850 while (true) {
851 const content_len = r.end - r.seek;851 const content_len = r.end - r.seek;
852 if (r.buffer.len - content_len == 0) break;
853 try fillMore(r);852 try fillMore(r);
853 if (r.buffer.len - content_len == 0) return error.StreamTooLong;
854 const seek = r.seek;854 const seek = r.seek;
855 const contents = r.buffer[0..r.end];855 const contents = r.buffer[0..r.end];
856 if (std.mem.findScalarPos(u8, contents, seek + content_len, delimiter)) |end| {856 if (std.mem.findScalarPos(u8, contents, seek + content_len, delimiter)) |end| {
857 return contents[seek .. end + 1];857 return contents[seek .. end + 1];
858 }858 }
859 }859 }
860 // It might or might not be end of stream. There is no more buffer space
861 // left to disambiguate. If `StreamTooLong` was added to `RebaseError` then
862 // this logic could be replaced by removing the exit condition from the
863 // above while loop. That error code would represent when `buffer` capacity
864 // is too small for an operation, replacing the current use of asserts.
865 var failing_writer = Writer.failing;
866 while (r.vtable.stream(r, &failing_writer, .limited(1))) |n| {
867 assert(n == 0);
868 } else |err| switch (err) {
869 error.WriteFailed => return error.StreamTooLong,
870 error.ReadFailed => |e| return e,
871 error.EndOfStream => |e| return e,
872 }
873}860}
874861
875/// Returns a slice of the next bytes of buffered data from the stream until862/// Returns a slice of the next bytes of buffered data from the stream until
lib/std/zig.zig+1-2
...@@ -727,8 +727,7 @@ pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *Io.File.Reader) ![...@@ -727,8 +727,7 @@ pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *Io.File.Reader) ![
727}727}
728728
729pub fn printAstErrorsToStderr(gpa: Allocator, io: Io, tree: Ast, path: []const u8, color: Color) !void {729pub fn printAstErrorsToStderr(gpa: Allocator, io: Io, tree: Ast, path: []const u8, color: Color) !void {
730 var wip_errors: ErrorBundle.Wip = undefined;730 var wip_errors: ErrorBundle.Wip = try .init(gpa);
731 try wip_errors.init(gpa);
732 defer wip_errors.deinit();731 defer wip_errors.deinit();
733732
734 try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);733 try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);
lib/std/zig/ErrorBundle.zig+7-4
...@@ -360,22 +360,25 @@ pub const Wip = struct {...@@ -360,22 +360,25 @@ pub const Wip = struct {
360 extra: std.ArrayList(u32),360 extra: std.ArrayList(u32),
361 root_list: std.ArrayList(MessageIndex),361 root_list: std.ArrayList(MessageIndex),
362362
363 pub fn init(wip: *Wip, gpa: Allocator) !void {363 pub fn init(gpa: Allocator) !Wip {
364 wip.* = .{364 var wip: Wip = .{
365 .gpa = gpa,365 .gpa = gpa,
366 .string_bytes = .empty,366 .string_bytes = .empty,
367 .extra = .empty,367 .extra = .empty,
368 .root_list = .empty,368 .root_list = .empty,
369 };369 };
370 errdefer wip.deinit();
370371
371 // So that 0 can be used to indicate a null string.372 // So that 0 can be used to indicate a null string.
372 try wip.string_bytes.append(gpa, 0);373 try wip.string_bytes.append(gpa, 0);
373374
374 assert(0 == try addExtra(wip, ErrorMessageList{375 assert(0 == try wip.addExtra(ErrorMessageList{
375 .len = 0,376 .len = 0,
376 .start = 0,377 .start = 0,
377 .compile_log_text = 0,378 .compile_log_text = 0,
378 }));379 }));
380
381 return wip;
379 }382 }
380383
381 pub fn deinit(wip: *Wip) void {384 pub fn deinit(wip: *Wip) void {
...@@ -773,7 +776,7 @@ pub const Wip = struct {...@@ -773,7 +776,7 @@ pub const Wip = struct {
773 u32 => @field(extra, field_name),776 u32 => @field(extra, field_name),
774 MessageIndex => @backingInt(@field(extra, field_name)),777 MessageIndex => @backingInt(@field(extra, field_name)),
775 SourceLocationIndex => @backingInt(@field(extra, field_name)),778 SourceLocationIndex => @backingInt(@field(extra, field_name)),
776 else => @compileError("bad field type"),779 else => @compileError("bad field type: " ++ @typeName(field_type)),
777 };780 };
778 i += 1;781 i += 1;
779 }782 }
src/Compilation.zig+3-6
...@@ -3838,8 +3838,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {...@@ -3838,8 +3838,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
3838 const gpa = comp.gpa;3838 const gpa = comp.gpa;
3839 const io = comp.io;3839 const io = comp.io;
38403840
3841 var bundle: ErrorBundle.Wip = undefined;3841 var bundle: ErrorBundle.Wip = try .init(gpa);
3842 try bundle.init(gpa);
3843 defer bundle.deinit();3842 defer bundle.deinit();
38443843
3845 for (comp.failed_c_objects.values()) |diag_bundle| {3844 for (comp.failed_c_objects.values()) |diag_bundle| {
...@@ -5442,8 +5441,7 @@ fn reportRetryableWin32ResourceError(...@@ -5442,8 +5441,7 @@ fn reportRetryableWin32ResourceError(
54425441
5443 win32_resource.status = .failure_retryable;5442 win32_resource.status = .failure_retryable;
54445443
5445 var bundle: ErrorBundle.Wip = undefined;5444 var bundle: ErrorBundle.Wip = try .init(comp.gpa);
5446 try bundle.init(comp.gpa);
5447 errdefer bundle.deinit();5445 errdefer bundle.deinit();
5448 try bundle.addRootErrorMessage(.{5446 try bundle.addRootErrorMessage(.{
5449 .msg = try bundle.printString("{s}", .{@errorName(err)}),5447 .msg = try bundle.printString("{s}", .{@errorName(err)}),
...@@ -6830,8 +6828,7 @@ fn failCObjWithOwnedDiagBundle(...@@ -6830,8 +6828,7 @@ fn failCObjWithOwnedDiagBundle(
68306828
6831fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } {6829fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) error{ OutOfMemory, AlreadyReported } {
6832 @branchHint(.cold);6830 @branchHint(.cold);
6833 var bundle: ErrorBundle.Wip = undefined;6831 var bundle: ErrorBundle.Wip = try .init(comp.gpa);
6834 try bundle.init(comp.gpa);
6835 errdefer bundle.deinit();6832 errdefer bundle.deinit();
6836 try bundle.addRootErrorMessage(.{6833 try bundle.addRootErrorMessage(.{
6837 .msg = try bundle.printString(format, args),6834 .msg = try bundle.printString(format, args),
src/Sema.zig+1-2
...@@ -2705,8 +2705,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg...@@ -2705,8 +2705,7 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg
2705 assert(sema.err == null);2705 assert(sema.err == null);
27062706
2707 if (build_options.enable_debug_extensions and comp.debug_compile_errors) {2707 if (build_options.enable_debug_extensions and comp.debug_compile_errors) {
2708 var wip_errors: std.zig.ErrorBundle.Wip = undefined;2708 var wip_errors = std.zig.ErrorBundle.Wip.init(gpa) catch @panic("out of memory");
2709 wip_errors.init(gpa) catch @panic("out of memory");
2710 Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory");2709 Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory");
2711 std.debug.print("compile error during Sema:\n", .{});2710 std.debug.print("compile error during Sema:\n", .{});
2712 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");2711 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");
src/fmt.zig+4-8
...@@ -130,8 +130,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !...@@ -130,8 +130,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !
130 defer zir.deinit(gpa);130 defer zir.deinit(gpa);
131131
132 if (zir.hasCompileErrors()) {132 if (zir.hasCompileErrors()) {
133 var wip_errors: std.zig.ErrorBundle.Wip = undefined;133 var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa);
134 try wip_errors.init(gpa);
135 defer wip_errors.deinit();134 defer wip_errors.deinit();
136 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");135 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");
137 var error_bundle = try wip_errors.toOwnedBundle("");136 var error_bundle = try wip_errors.toOwnedBundle("");
...@@ -144,8 +143,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !...@@ -144,8 +143,7 @@ pub fn run(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !
144 defer zoir.deinit(gpa);143 defer zoir.deinit(gpa);
145144
146 if (zoir.hasCompileErrors()) {145 if (zoir.hasCompileErrors()) {
147 var wip_errors: std.zig.ErrorBundle.Wip = undefined;146 var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa);
148 try wip_errors.init(gpa);
149 defer wip_errors.deinit();147 defer wip_errors.deinit();
150 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, "<stdin>");148 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, "<stdin>");
151 var error_bundle = try wip_errors.toOwnedBundle("");149 var error_bundle = try wip_errors.toOwnedBundle("");
...@@ -333,8 +331,7 @@ fn fmtPathFile(...@@ -333,8 +331,7 @@ fn fmtPathFile(
333 defer zir.deinit(gpa);331 defer zir.deinit(gpa);
334332
335 if (zir.hasCompileErrors()) {333 if (zir.hasCompileErrors()) {
336 var wip_errors: std.zig.ErrorBundle.Wip = undefined;334 var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa);
337 try wip_errors.init(gpa);
338 defer wip_errors.deinit();335 defer wip_errors.deinit();
339 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);336 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);
340 var error_bundle = try wip_errors.toOwnedBundle("");337 var error_bundle = try wip_errors.toOwnedBundle("");
...@@ -348,8 +345,7 @@ fn fmtPathFile(...@@ -348,8 +345,7 @@ fn fmtPathFile(
348 defer zoir.deinit(gpa);345 defer zoir.deinit(gpa);
349346
350 if (zoir.hasCompileErrors()) {347 if (zoir.hasCompileErrors()) {
351 var wip_errors: std.zig.ErrorBundle.Wip = undefined;348 var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa);
352 try wip_errors.init(gpa);
353 defer wip_errors.deinit();349 defer wip_errors.deinit();
354 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, file_path);350 try wip_errors.addZoirErrorMessages(zoir, tree, source_code, file_path);
355 var error_bundle = try wip_errors.toOwnedBundle("");351 var error_bundle = try wip_errors.toOwnedBundle("");
src/link.zig+1-2
...@@ -2586,8 +2586,7 @@ fn resolvePathInputLib(...@@ -2586,8 +2586,7 @@ fn resolvePathInputLib(
25862586
2587 const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items);2587 const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items);
2588 if (diags.hasErrors()) {2588 if (diags.hasErrors()) {
2589 var wip_errors: std.zig.ErrorBundle.Wip = undefined;2589 var wip_errors: std.zig.ErrorBundle.Wip = try .init(gpa);
2590 try wip_errors.init(gpa);
2591 defer wip_errors.deinit();2590 defer wip_errors.deinit();
25922591
2593 try diags.addMessagesToBundle(&wip_errors, null);2592 try diags.addMessagesToBundle(&wip_errors, null);
src/main.zig+4-8
...@@ -5659,8 +5659,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map:...@@ -5659,8 +5659,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map:
5659 const zir = try AstGen.generate(arena, tree);5659 const zir = try AstGen.generate(arena, tree);
56605660
5661 if (zir.hasCompileErrors()) {5661 if (zir.hasCompileErrors()) {
5662 var wip_errors: std.zig.ErrorBundle.Wip = undefined;5662 var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena);
5663 try wip_errors.init(arena);
5664 try wip_errors.addZirErrorMessages(zir, tree, source, display_path);5663 try wip_errors.addZirErrorMessages(zir, tree, source, display_path);
5665 var error_bundle = try wip_errors.toOwnedBundle("");5664 var error_bundle = try wip_errors.toOwnedBundle("");
5666 try error_bundle.renderToStderr(io, .{}, color);5665 try error_bundle.renderToStderr(io, .{}, color);
...@@ -5730,8 +5729,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map:...@@ -5730,8 +5729,7 @@ fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8, environ_map:
5730 .zon => {5729 .zon => {
5731 const zoir = try ZonGen.generate(arena, tree, .{});5730 const zoir = try ZonGen.generate(arena, tree, .{});
5732 if (zoir.hasCompileErrors()) {5731 if (zoir.hasCompileErrors()) {
5733 var wip_errors: std.zig.ErrorBundle.Wip = undefined;5732 var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena);
5734 try wip_errors.init(arena);
5735 try wip_errors.addZoirErrorMessages(zoir, tree, source, display_path);5733 try wip_errors.addZoirErrorMessages(zoir, tree, source, display_path);
5736 var error_bundle = try wip_errors.toOwnedBundle("");5734 var error_bundle = try wip_errors.toOwnedBundle("");
5737 error_bundle.renderToStderr(io, .{}, color) catch {};5735 error_bundle.renderToStderr(io, .{}, color) catch {};
...@@ -5824,8 +5822,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8, environ_map...@@ -5824,8 +5822,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8, environ_map
5824 const old_zir = try AstGen.generate(arena, old_tree);5822 const old_zir = try AstGen.generate(arena, old_tree);
58255823
5826 if (old_zir.loweringFailed()) {5824 if (old_zir.loweringFailed()) {
5827 var wip_errors: std.zig.ErrorBundle.Wip = undefined;5825 var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena);
5828 try wip_errors.init(arena);
5829 try wip_errors.addZirErrorMessages(old_zir, old_tree, old_source, old_source_path);5826 try wip_errors.addZirErrorMessages(old_zir, old_tree, old_source, old_source_path);
5830 var error_bundle = try wip_errors.toOwnedBundle("");5827 var error_bundle = try wip_errors.toOwnedBundle("");
5831 error_bundle.renderToStderr(io, .{}, color) catch {};5828 error_bundle.renderToStderr(io, .{}, color) catch {};
...@@ -5836,8 +5833,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8, environ_map...@@ -5836,8 +5833,7 @@ fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8, environ_map
5836 const new_zir = try AstGen.generate(arena, new_tree);5833 const new_zir = try AstGen.generate(arena, new_tree);
58375834
5838 if (new_zir.loweringFailed()) {5835 if (new_zir.loweringFailed()) {
5839 var wip_errors: std.zig.ErrorBundle.Wip = undefined;5836 var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena);
5840 try wip_errors.init(arena);
5841 try wip_errors.addZirErrorMessages(new_zir, new_tree, new_source, new_source_path);5837 try wip_errors.addZirErrorMessages(new_zir, new_tree, new_source, new_source_path);
5842 var error_bundle = try wip_errors.toOwnedBundle("");5838 var error_bundle = try wip_errors.toOwnedBundle("");
5843 error_bundle.renderToStderr(io, .{}, color) catch {};5839 error_bundle.renderToStderr(io, .{}, color) catch {};
test/src/Runner.zig+1007-546
...@@ -1,274 +1,5 @@...@@ -1,274 +1,5 @@
1const assert = std.debug.assert;
1const std = @import("std");2const std = @import("std");
2
3pub fn main(init: std.process.Init) !void {
4 const arena = init.arena.allocator();
5
6 var test_path_arg: ?[]const u8 = null;
7 var zig_path_arg: ?[]const u8 = null;
8 var lib_path_arg: ?[]const u8 = null;
9 var target_args: std.ArrayList([]const u8) = .empty;
10 defer target_args.deinit(init.gpa);
11 var keep_src = false;
12 var enable_qemu = false;
13 var enable_wine = false;
14 var enable_wasmtime = false;
15 var enable_darling = false;
16
17 var arg_it = try init.minimal.args.iterateAllocator(arena);
18 const self = arg_it.next().?;
19 while (arg_it.next()) |arg| {
20 if (std.mem.eql(u8, arg, "--listen=-")) {
21 var stdin_buffer: [512]u8 = undefined;
22 var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer);
23 var stdout_buffer: [512]u8 = undefined;
24 var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer);
25 var runner: Runner = .{
26 .gpa = init.gpa,
27 .arena = init.arena,
28 .io = init.io,
29 .prog_node = std.Progress.start(init.io, .{}),
30 .args = .{
31 .@"test" = null,
32 .manifest_file = null,
33 .zig_exe = null,
34 .lib_dir = null,
35 .src_dir = null,
36 .targets = .empty,
37 .target_bytes = .empty,
38 .enable_qemu = false,
39 .enable_wine = false,
40 .enable_wasmtime = false,
41 .enable_darling = false,
42 },
43 .host = try std.zig.system.resolveTargetQuery(init.io, .{}),
44 .server = .{
45 .in = &stdin.interface,
46 .out = &stdout.interface,
47 },
48 };
49 defer runner.deinit();
50 return runner.protocol();
51 } else if (std.mem.eql(u8, arg, "--zig")) {
52 zig_path_arg = arg_it.next() orelse fatal("missing arg after '{s}'", .{arg});
53 } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| {
54 zig_path_arg = zig_path;
55 } else if (std.mem.eql(u8, arg, "--lib")) {
56 lib_path_arg = arg_it.next() orelse fatal("missing arg after '{s}'", .{arg});
57 } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| {
58 lib_path_arg = lib_path;
59 } else if (std.mem.eql(u8, arg, "--target")) {
60 try target_args.append(
61 init.gpa,
62 try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after '{s}'", .{arg})),
63 );
64 } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| {
65 try target_args.append(init.gpa, try arena.dupe(u8, target));
66 } else if (std.mem.eql(u8, arg, "--keep-src")) {
67 keep_src = true;
68 } else if (std.mem.eql(u8, arg, "-fqemu")) {
69 enable_qemu = true;
70 } else if (std.mem.eql(u8, arg, "-fwine")) {
71 enable_wine = true;
72 } else if (std.mem.eql(u8, arg, "-fwasmtime")) {
73 enable_wasmtime = true;
74 } else if (std.mem.eql(u8, arg, "-fdarling")) {
75 enable_darling = true;
76 } else {
77 if (test_path_arg) |_| fatal("unknown arg '{s}'", .{arg});
78 test_path_arg = arg;
79 }
80 }
81
82 const test_path = test_path_arg orelse fatal("missing 'path/to/test'", .{});
83 const zig_path = zig_path_arg orelse fatal("missing '--zig=path/to/zig'", .{});
84 const lib_path = lib_path_arg orelse fatal("missing '--lib=path/to/lib'", .{});
85
86 const cwd: std.Io.Dir = .cwd();
87 const test_file = try cwd.openFile(init.io, test_path, .{
88 .allow_directory = true,
89 });
90 const zig_exe = try cwd.openFile(init.io, zig_path, .{});
91 defer zig_exe.close(init.io);
92 const lib_dir = try cwd.openDir(init.io, lib_path, .{});
93 defer lib_dir.close(init.io);
94
95 const src_dir_path = "src_" ++ std.fmt.hex(rand_int: {
96 var rand_int: u64 = undefined;
97 init.io.random(@ptrCast(&rand_int));
98 break :rand_int rand_int;
99 });
100 var src_dir = try cwd.createDirPathOpen(init.io, src_dir_path, .{});
101 defer {
102 src_dir.close(init.io);
103 if (!keep_src) cwd.deleteTree(init.io, src_dir_path) catch |err| {
104 std.log.warn("failed to delete tree '{s}': {t}", .{ src_dir_path, err });
105 };
106 }
107
108 var stdout_buffer: [512]u8 = undefined;
109 var stdout_fw: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer);
110 const stdout = &stdout_fw.interface;
111
112 var child = try std.process.spawn(init.io, .{
113 .argv = &.{ self, "--listen=-" },
114 .stdin = .pipe,
115 .stdout = .pipe,
116 .inherit_dirs = &.{ lib_dir, src_dir },
117 .inherit_files = &.{ zig_exe, test_file },
118 });
119
120 var child_reader_buffer: [512]u8 = undefined;
121 var child_reader = child.stdout.?.readerStreaming(init.io, &child_reader_buffer);
122 var child_writer_buffer: [512]u8 = undefined;
123 var child_writer = child.stdin.?.writerStreaming(init.io, &child_writer_buffer);
124 var client: std.zig.Client = .{
125 .in = &child_reader.interface,
126 .out = &child_writer.interface,
127 };
128 {
129 var args: std.ArrayList(u8) = .empty;
130 defer args.deinit(init.gpa);
131 const Arg = std.zig.Client.Message.Arg;
132
133 try args.append(init.gpa, @backingInt(@as(Arg, switch ((try test_file.stat(init.io)).kind) {
134 else => unreachable,
135 .file => .input_file,
136 .directory => .input_dir,
137 })));
138 try args.appendSlice(init.gpa, @ptrCast(&test_file.handle));
139
140 try args.append(init.gpa, @backingInt(Arg.prefix));
141 try args.appendSlice(init.gpa, "--zig=");
142 try args.append(init.gpa, 0);
143
144 try args.append(init.gpa, @backingInt(Arg.input_file));
145 try args.appendSlice(init.gpa, @ptrCast(&zig_exe.handle));
146
147 try args.append(init.gpa, @backingInt(Arg.prefix));
148 try args.appendSlice(init.gpa, "--lib=");
149 try args.append(init.gpa, 0);
150
151 try args.append(init.gpa, @backingInt(Arg.input_dir));
152 try args.appendSlice(init.gpa, @ptrCast(&lib_dir.handle));
153
154 try args.append(init.gpa, @backingInt(Arg.prefix));
155 try args.appendSlice(init.gpa, "--src=");
156 try args.append(init.gpa, 0);
157
158 try args.append(init.gpa, @backingInt(Arg.output_dir));
159 try args.appendSlice(init.gpa, @ptrCast(&src_dir.handle));
160
161 for (target_args.items) |target_arg| {
162 try args.append(init.gpa, @backingInt(Arg.string));
163 try args.appendSlice(init.gpa, "--target");
164 try args.append(init.gpa, 0);
165
166 try args.append(init.gpa, @backingInt(Arg.string));
167 try args.appendSlice(init.gpa, target_arg);
168 try args.append(init.gpa, 0);
169 }
170
171 if (enable_qemu) {
172 try args.append(init.gpa, @backingInt(Arg.string));
173 try args.appendSlice(init.gpa, "-fqemu");
174 try args.append(init.gpa, 0);
175 }
176 if (enable_wine) {
177 try args.append(init.gpa, @backingInt(Arg.string));
178 try args.appendSlice(init.gpa, "-fwine");
179 try args.append(init.gpa, 0);
180 }
181 if (enable_wasmtime) {
182 try args.append(init.gpa, @backingInt(Arg.string));
183 try args.appendSlice(init.gpa, "-fwasmtime");
184 try args.append(init.gpa, 0);
185 }
186 if (enable_darling) {
187 try args.append(init.gpa, @backingInt(Arg.string));
188 try args.appendSlice(init.gpa, "-fdarling");
189 try args.append(init.gpa, 0);
190 }
191
192 try client.serveMessageHeader(.{
193 .tag = .args,
194 .bytes_len = @intCast(args.items.len),
195 });
196 try client.out.writeAll(args.items);
197
198 var metadata: struct {
199 index: u32,
200 len: u32,
201 names: []u32,
202 expected_panic_msgs: []u32,
203 string_bytes: []u8,
204
205 fn name(m: *@This()) []const u8 {
206 return std.mem.sliceTo(m.string_bytes[m.names[m.index - 1]..], 0);
207 }
208
209 fn expectedPanicMsg(m: *@This()) ?[:0]const u8 {
210 return switch (m.expected_panic_msgs[m.index - 1]) {
211 0 => null,
212 else => |offset| m.string_bytes[offset..std.mem.findScalarPos(
213 u8,
214 m.string_bytes,
215 offset,
216 0,
217 ).? :0],
218 };
219 }
220
221 fn next(m: *@This()) ?u32 {
222 if (m.len - m.index == 0) return null;
223 defer m.index += 1;
224 return m.index;
225 }
226 } = .{ .index = 0, .len = 0, .names = &.{}, .expected_panic_msgs = &.{}, .string_bytes = &.{} };
227 try client.serveBodylessMessage(.query_test_metadata);
228 while (true) {
229 const hdr = try client.receiveMessage();
230 switch (hdr.tag) {
231 else => try client.in.discardAll(hdr.bytes_len),
232 .zig_version => {
233 const body = try arena.alloc(u8, hdr.bytes_len);
234 try client.in.readSliceAll(body);
235 if (!std.mem.eql(u8, @import("builtin").zig_version_string, body)) return fatal(
236 "zig version mismatch build runner vs compiler: '{s}' vs '{s}'",
237 .{ @import("builtin").zig_version_string, body },
238 );
239 },
240 .test_metadata => {
241 const tm_hdr =
242 try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little);
243 metadata = .{
244 .index = 0,
245 .len = tm_hdr.tests_len,
246 .names = try arena.alloc(u32, tm_hdr.tests_len),
247 .expected_panic_msgs = try arena.alloc(u32, tm_hdr.tests_len),
248 .string_bytes = try arena.alloc(u8, tm_hdr.string_bytes_len),
249 };
250 try client.in.readSliceEndian(u32, metadata.names, .little);
251 try client.in.readSliceEndian(u32, metadata.expected_panic_msgs, .little);
252 try client.in.readSliceAll(metadata.string_bytes);
253 try client.serveRunTest(metadata.next() orelse break);
254 },
255 .test_results => {
256 const tr = try client.in.takeStruct(std.zig.Server.Message.TestResults, .little);
257 try stdout.print("[{t}] {s}\n", .{ tr.flags.status, metadata.name() });
258 try stdout.flush();
259 try client.serveRunTest(metadata.next() orelse break);
260 },
261 }
262 }
263 try client.serveBodylessMessage(.exit);
264 }
265 _ = try child.wait(init.io);
266}
267fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
268 std.log.err(fmt, args);
269 std.process.exit(1);
270}
271
272const Runner = @This();3const Runner = @This();
2734
274gpa: std.mem.Allocator,5gpa: std.mem.Allocator,
...@@ -278,6 +9,7 @@ prog_node: std.Progress.Node,...@@ -278,6 +9,7 @@ prog_node: std.Progress.Node,
278args: Args,9args: Args,
279host: std.Target,10host: std.Target,
280server: std.zig.Server,11server: std.zig.Server,
12eb_wip: std.zig.ErrorBundle.Wip,
28113
282const Args = struct {14const Args = struct {
283 @"test": ?struct {15 @"test": ?struct {
...@@ -312,24 +44,34 @@ const Args = struct {...@@ -312,24 +44,34 @@ const Args = struct {
312fn deinit(runner: *Runner) void {44fn deinit(runner: *Runner) void {
313 runner.prog_node.end();45 runner.prog_node.end();
314 runner.args.deinit(runner.gpa);46 runner.args.deinit(runner.gpa);
47 runner.eb_wip.deinit();
315 runner.* = undefined;48 runner.* = undefined;
316}49}
31750
318pub fn protocol(runner: *Runner) !void {51const Fallible = error{AlreadyReported} || std.mem.Allocator.Error || std.Io.Cancelable;
52fn fail(runner: *Runner, comptime fmt: []const u8, args: anytype) Fallible {
53 return runner.failString(try runner.eb_wip.printString(fmt, args));
54}
55fn failString(runner: *Runner, msg: std.zig.ErrorBundle.String) Fallible {
56 try runner.eb_wip.addRootErrorMessage(.{
57 .msg = msg,
58 });
59 return error.AlreadyReported;
60}
61
62pub const ProtocolError = Fallible || std.Io.Reader.Error || std.Io.Writer.Error;
63pub fn runServer(runner: *Runner) ProtocolError {
319 try runner.server.serveStringMessage(.zig_version, @import("builtin").zig_version_string);64 try runner.server.serveStringMessage(.zig_version, @import("builtin").zig_version_string);
320 while (true) {65 while (true) {
321 _ = runner.arena.reset(.retain_capacity);66 _ = runner.arena.reset(.retain_capacity);
322 const arena = runner.arena.allocator();67 const arena = runner.arena.allocator();
323 const hdr = try runner.server.receiveMessage();68 const hdr = try runner.server.receiveMessage();
324 switch (hdr.tag) {69 switch (hdr.tag) {
325 else => {70 else => |tag| return runner.fail("unsupported message: {t}", .{tag}),
326 std.log.warn("unsupported message: {t}\n", .{hdr.tag});
327 std.process.exit(1);
328 },
329 .exit => std.process.exit(0),71 .exit => std.process.exit(0),
330 .args => {72 .args => {
331 const args_body = try arena.alloc(u8, hdr.bytes_len);73 const args_body = try arena.alloc(u8, hdr.bytes_len);
332 try runner.server.in.readSliceAll(args_body);74 runner.server.in.readSliceAll(args_body) catch unreachable;
333 var state: enum { positional, zig, lib, src, target } = .positional;75 var state: enum { positional, zig, lib, src, target } = .positional;
334 var input_dir: std.zig.Server.Message.InputDir = .cwd;76 var input_dir: std.zig.Server.Message.InputDir = .cwd;
335 var args_body_offset: usize = 0;77 var args_body_offset: usize = 0;
...@@ -392,32 +134,30 @@ pub fn protocol(runner: *Runner) !void {...@@ -392,32 +134,30 @@ pub fn protocol(runner: *Runner) !void {
392 };134 };
393 switch (state) {135 switch (state) {
394 .positional => {136 .positional => {
395 std.debug.assert(runner.args.@"test" == null);137 assert(runner.args.@"test" == null);
396 runner.args.@"test" = .{ .input_dir = input_dir, .dir = dir };138 runner.args.@"test" = .{ .input_dir = input_dir, .dir = dir };
397 std.debug.assert(runner.args.manifest_file == null);139 assert(runner.args.manifest_file == null);
398 const manifest_path = "manifest";140 const manifest_path = "manifest";
399 runner.args.manifest_file =141 runner.args.manifest_file = dir.openFile(
400 try dir.openFile(runner.io, manifest_path, .{});142 runner.io,
401143 manifest_path,
402 try runner.server.serveMessageHeader(.{144 .{},
403 .tag = .discovered_inputs,145 ) catch |err| switch (err) {
404 .bytes_len = @sizeOf(std.zig.Server.Message.InputDir) +146 error.Canceled => |e| return e,
405 manifest_path.len + 1,147 else => |e| return runner.fail(
406 });148 "unable to open manifest file: {t}",
407 try runner.server.out.writeInt(u32, @backingInt(149 .{e},
408 input_dir,150 ),
409 ), .little);151 };
410 try runner.server.out.writeAll(manifest_path);152 try runner.discoverInput(input_dir, manifest_path);
411 try runner.server.out.writeByte(0);
412 try runner.server.out.flush();
413 },153 },
414 .zig => unreachable,154 .zig => unreachable,
415 .lib => {155 .lib => {
416 std.debug.assert(runner.args.lib_dir == null);156 assert(runner.args.lib_dir == null);
417 runner.args.lib_dir = dir;157 runner.args.lib_dir = dir;
418 },158 },
419 .src => {159 .src => {
420 std.debug.assert(runner.args.src_dir == null);160 assert(runner.args.src_dir == null);
421 runner.args.src_dir = dir;161 runner.args.src_dir = dir;
422 },162 },
423 .target => unreachable,163 .target => unreachable,
...@@ -434,11 +174,11 @@ pub fn protocol(runner: *Runner) !void {...@@ -434,11 +174,11 @@ pub fn protocol(runner: *Runner) !void {
434 };174 };
435 switch (state) {175 switch (state) {
436 .positional => {176 .positional => {
437 std.debug.assert(runner.args.manifest_file == null);177 assert(runner.args.manifest_file == null);
438 runner.args.manifest_file = file;178 runner.args.manifest_file = file;
439 },179 },
440 .zig => {180 .zig => {
441 std.debug.assert(runner.args.zig_exe == null);181 assert(runner.args.zig_exe == null);
442 runner.args.zig_exe = file;182 runner.args.zig_exe = file;
443 },183 },
444 .lib => unreachable,184 .lib => unreachable,
...@@ -460,29 +200,199 @@ pub fn protocol(runner: *Runner) !void {...@@ -460,29 +200,199 @@ pub fn protocol(runner: *Runner) !void {
460 });200 });
461 },201 },
462 .run_test => {202 .run_test => {
463 const test_index = try runner.server.receiveBody_u32();203 const test_index = runner.server.receiveBody_u32() catch unreachable;
464 try runner.server.serveBodylessMessage(.test_started);204 try runner.server.serveBodylessMessage(.test_started);
465 try runner.server.serveTestResults(.{205 try runner.server.serveTestResults(.{
466 .index = test_index,206 .index = test_index,
467 .flags = .{207 .flags = .{
468 .status = if (runner.testOne(test_index)) .pass else |err| switch (err) {208 .status = if (runner.testOne(test_index))
469 else => status: {209 .pass
470 std.log.err("{t}\n", .{err});210 else |err| status: switch (err) {
471 if (@errorReturnTrace()) |trace| std.debug.dumpErrorReturnTrace(trace);211 error.Canceled,
212 error.OutOfMemory,
213 error.ReadFailed,
214 error.WriteFailed,
215 => |e| return e,
216 error.AlreadyReported => {
217 var eb = try runner.eb_wip.toOwnedBundle("");
218 defer eb.deinit(runner.gpa);
219 assert(eb.errorMessageCount() > 0); // already reported what?
220 try runner.server.serveErrorBundle(.error_bundle, eb);
221 const eb_wip: std.zig.ErrorBundle.Wip = try .init(runner.gpa);
222 runner.eb_wip.deinit();
223 runner.eb_wip = eb_wip;
472 break :status .fail;224 break :status .fail;
473 },225 },
474 error.SkipZigTest => .skip,226 error.SkipTest => .skip,
475 },227 },
476 .fuzz = false,228 .fuzz = false,
477 .log_err_count = 0,229 .log_err_count = 0,
478 .leak_count = 0,230 .leak_count = 0,
479 },231 },
480 });232 });
233 assert(runner.eb_wip.root_list.items.len == 0); // failed to report
481 },234 },
482 }235 }
483 }236 }
484}237}
485const DelimitedReader = struct {238
239fn discoverInput(
240 runner: *Runner,
241 dir: std.zig.Server.Message.InputDir,
242 sub_path: []const u8,
243) std.Io.Writer.Error!void {
244 try runner.server.serveMessageHeader(.{
245 .tag = .discovered_inputs,
246 .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) +
247 sub_path.len + 1),
248 });
249 try runner.server.out.writeInt(u32, @backingInt(dir), .little);
250 try runner.server.out.writeAll(sub_path);
251 try runner.server.out.writeByte(0);
252 try runner.server.out.flush();
253}
254
255pub const SourceReader = struct {
256 const Io = std.Io;
257
258 backing: *std.Io.Reader,
259 line_aw: std.Io.Writer.Allocating,
260 interface: std.Io.Reader,
261 line: u32,
262 eb_wip: *std.zig.ErrorBundle.Wip,
263 src_path: std.zig.ErrorBundle.String,
264
265 pub fn init(
266 reader: *Io.Reader,
267 gpa: std.mem.Allocator,
268 eb_wip: *std.zig.ErrorBundle.Wip,
269 src_path: std.zig.ErrorBundle.String,
270 ) SourceReader {
271 return .{
272 .backing = reader,
273 .line_aw = .init(gpa),
274 .interface = .{
275 .vtable = &.{
276 .stream = stream,
277 .discard = discard,
278 .readVec = readVec,
279 .rebase = rebase,
280 },
281 .buffer = &.{},
282 .seek = 0,
283 .end = 0,
284 },
285 .line = 0,
286 .eb_wip = eb_wip,
287 .src_path = src_path,
288 };
289 }
290 fn deinit(s: *SourceReader) void {
291 s.line_aw.deinit();
292 }
293 fn stream(r: *Io.Reader, _: *Io.Writer, _: Io.Limit) Io.Reader.StreamError!usize {
294 const s: *SourceReader = @fieldParentPtr("interface", r);
295 try s.bufferLine();
296 return 0;
297 }
298 fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize {
299 const s: *SourceReader = @fieldParentPtr("interface", r);
300 const remaining = r.buffered();
301 if (remaining.len > 0) {
302 const n = limit.minInt(remaining.len);
303 r.toss(n);
304 return n;
305 }
306 try s.bufferLine();
307 return 0;
308 }
309 fn readVec(r: *Io.Reader, _: [][]u8) Io.Reader.Error!usize {
310 const s: *SourceReader = @fieldParentPtr("interface", r);
311 try s.bufferLine();
312 return 0;
313 }
314 fn rebase(r: *Io.Reader, capacity: usize) Io.Reader.RebaseError!void {
315 const s: *SourceReader = @fieldParentPtr("interface", r);
316 defer s.updateBuffer();
317 s.line_aw.ensureUnusedCapacity(capacity) catch |err| switch (err) {
318 error.OutOfMemory => return error.ReadFailed,
319 };
320 }
321 fn updateBuffer(s: *SourceReader) void {
322 s.interface.buffer = s.line_aw.writer.buffer;
323 s.interface.end = s.line_aw.writer.end;
324 }
325 fn bufferLine(s: *SourceReader) Io.Reader.Error!void {
326 defer s.updateBuffer();
327 {
328 const written = s.line_aw.written();
329 const start = if (std.mem.findScalarLast(u8, written[0..s.interface.seek], '\n')) |newline|
330 newline + 1
331 else
332 0;
333 s.line += @intCast(std.mem.countScalar(u8, written[0..start], '\n'));
334 const preserve = written[start..];
335 @memmove(written[0..preserve.len], preserve);
336 s.line_aw.shrinkRetainingCapacity(preserve.len);
337 s.interface.seek -= start;
338 }
339 const n = s.backing.streamDelimiterEnding(&s.line_aw.writer, '\n') catch |err| switch (err) {
340 error.ReadFailed => |e| return e,
341 error.WriteFailed => return error.ReadFailed,
342 };
343 if (std.mem.endsWith(u8, s.line_aw.written(), "\r")) s.line_aw.writer.end -= 1;
344 const buffered = s.backing.buffered();
345 if (n == 0 and buffered.len == 0) return error.EndOfStream;
346 if (std.mem.startsWith(u8, buffered, "\n")) {
347 s.line_aw.writer.writeByte('\n') catch |err| switch (err) {
348 error.WriteFailed => return error.ReadFailed,
349 };
350 s.backing.toss(1);
351 }
352 }
353 fn fail(
354 s: *SourceReader,
355 src_loc: []const u8,
356 comptime fmt: []const u8,
357 args: anytype,
358 ) Fallible {
359 return s.failString(src_loc, try s.eb_wip.printString(fmt, args));
360 }
361 fn failString(
362 s: *SourceReader,
363 src_loc: []const u8,
364 msg: std.zig.ErrorBundle.String,
365 ) Fallible {
366 const line: u32, const column: u32, const source_line = src_loc: {
367 const pos = src_loc.ptr - s.interface.buffer.ptr;
368 const end = std.mem.findScalarPos(u8, s.interface.buffer, pos, '\n') orelse
369 s.interface.buffer.len;
370 const start = if (std.mem.findScalarLast(u8, s.interface.buffer[0..end], '\n')) |newline|
371 newline + 1
372 else
373 0;
374 break :src_loc .{
375 @intCast(s.line + std.mem.countScalar(u8, s.interface.buffer[0..start], '\n')),
376 @intCast(pos - start),
377 s.interface.buffer[start..end],
378 };
379 };
380 try s.eb_wip.addRootErrorMessage(.{
381 .msg = msg,
382 .src_loc = try s.eb_wip.addSourceLocation(.{
383 .src_path = s.src_path,
384 .line = line,
385 .column = column,
386 .span_start = column,
387 .span_main = column,
388 .span_end = @intCast(column + src_loc.len),
389 .source_line = try s.eb_wip.addString(source_line),
390 }),
391 });
392 return error.AlreadyReported;
393 }
394};
395pub const DelimitedReader = struct {
486 const Io = std.Io;396 const Io = std.Io;
487397
488 unlimited: *Io.Reader,398 unlimited: *Io.Reader,
...@@ -528,251 +438,376 @@ const DelimitedReader = struct {...@@ -528,251 +438,376 @@ const DelimitedReader = struct {
528 }438 }
529};439};
530440
531fn testOne(runner: *const Runner, test_index: u32) !void {441const Update = struct {
442 target_query: []const u8,
443 manifest_sr: SourceReader,
444 compiler: ?Compiler,
445 src_dir: std.Io.Dir,
446 prog_node: std.Progress.Node,
447 mtime: std.Io.Timestamp,
448 num: usize,
449 allow_skip: bool,
450};
451
452const TestError = error{
453 SkipTest,
454} || Fallible || std.Io.Reader.ShortError || std.Io.Writer.Error;
455fn testOne(runner: *Runner, test_index: u32) TestError!void {
532 const target_query =456 const target_query =
533 std.mem.sliceTo(runner.args.target_bytes.items[runner.args.targets.items[test_index]..], 0);457 std.mem.sliceTo(runner.args.target_bytes.items[runner.args.targets.items[test_index]..], 0);
534 const src_dir = try (runner.args.src_dir orelse return error.MissingSrcDir).createDirPathOpen(458 const manifest_file = runner.args.manifest_file orelse
535 runner.io,459 return runner.fail("missing \"path/to/test\" arg", .{});
536 target_query,
537 .{},
538 );
539 const manifest_file = runner.args.manifest_file orelse return error.MissingManifestArg;
540 var manifest_buffer: [512]u8 = undefined;460 var manifest_buffer: [512]u8 = undefined;
541 var manifest_fr = manifest_file.reader(runner.io, &manifest_buffer);461 var manifest_fr = manifest_file.reader(runner.io, &manifest_buffer);
542 var allow_skip = true;462 var update: Update = .{
463 .target_query = target_query,
464 .manifest_sr = .init(&manifest_fr.interface, runner.gpa, &runner.eb_wip, src_path_string: {
465 var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined;
466 const src_path = src_path: {
467 break :src_path path_buffer[0 .. std.Io.Dir.readLinkAbsolute(
468 runner.io,
469 try runner.arena.allocator().print("/proc/self/fd/{d}", .{manifest_file.handle}),
470 &path_buffer,
471 ) catch |err| switch (err) {
472 error.Canceled => |e| return e,
473 else => break :src_path "manifest",
474 }];
475 };
476 break :src_path_string try runner.eb_wip.addString(src_path);
477 }),
478 .compiler = null,
479 .src_dir = (runner.args.src_dir orelse
480 return runner.fail("missing \"--src=path/to/src\" arg", .{})).createDirPathOpen(
481 runner.io,
482 target_query,
483 .{},
484 ) catch |err| switch (err) {
485 error.Canceled => |e| return e,
486 else => |e| return runner.fail("unable to create {q}: {t}", .{ target_query, e }),
487 },
488 .prog_node = .none,
489 .mtime = .now(runner.io, .real),
490 .num = 0,
491 .allow_skip = true,
492 };
493 defer {
494 update.manifest_sr.deinit();
495 update.src_dir.close(runner.io);
496 update.prog_node.end();
497 }
543 var skip_delimiter = false;498 var skip_delimiter = false;
544 var compiler: ?Compiler = null;499 while (update.manifest_sr.interface.takeSentinel('\n')) |line| {
545 var update_prog_node: std.Progress.Node = .none;
546 defer update_prog_node.end();
547 var update_mtime = std.Io.Clock.real.now(runner.io);
548 var update_num: usize = 0;
549 while (manifest_fr.interface.takeSentinel('\n')) |untrimmed_line| {
550 const line = std.mem.trimEnd(u8, untrimmed_line, "\r");
551 var line_it = std.mem.tokenizeScalar(u8, line, ' ');500 var line_it = std.mem.tokenizeScalar(u8, line, ' ');
552 const cmd_str = std.mem.cutPrefix(u8, line_it.next() orelse continue, "#").?;501 const cmd_src_loc = line_it.next() orelse continue;
553 if (skip_delimiter) {502 if (skip_delimiter) {
554 std.debug.assert(std.mem.eql(u8, cmd_str, "}"));503 assert(std.mem.eql(u8, cmd_src_loc, "#}"));
555 skip_delimiter = false;504 skip_delimiter = false;
505 const unexpected = line_it.rest();
506 if (unexpected.len > 0)
507 return update.manifest_sr.fail(unexpected, "unexpected argument {q}", .{unexpected});
556 continue;508 continue;
557 }509 }
558 const cmd = std.meta.stringToEnum(510 const cmd = std.meta.stringToEnum(Command, std.mem.cutPrefix(u8, cmd_src_loc, "#") orelse
559 enum { todo, skip, exe, lib, obj, write, delete, update, check },511 return update.manifest_sr.fail(cmd_src_loc, "expected command", .{})) orelse
560 cmd_str,512 return update.manifest_sr.fail(cmd_src_loc, "unknown command", .{});
561 ) orelse return error.UnknownCommand;513 const arg = switch (cmd) {
562 var maybe_arg = line_it.next();514 .todo, .exe, .lib, .obj, .update => null,
515 .skip, .write, .delete, .check => line_it.next() orelse
516 return update.manifest_sr.fail(line_it.rest()[0..0], "missing argument", .{}),
517 };
563 var contents_file: ?std.Io.File = null;518 var contents_file: ?std.Io.File = null;
564 defer if (contents_file) |file| file.close(runner.io);519 defer if (contents_file) |file| file.close(runner.io);
565 var contents_buffer: [512]u8 = undefined;520 var contents_buffer: [512]u8 = undefined;
566 var contents_impl: union {521 var contents_impl: union(enum) {
522 ending,
567 dr: DelimitedReader,523 dr: DelimitedReader,
568 fr: std.Io.File.Reader,524 fr: std.Io.File.Reader,
569 } = undefined;525 } = .ending;
570 const contents_r: *std.Io.Reader = contents_r: switch (cmd) {526 const contents_r: *std.Io.Reader = contents_r: switch (cmd) {
571 .skip, .delete => {527 .skip, .delete => {
572 if (line_it.next()) |_| return error.UnexpectedArg;528 const unexpected = line_it.rest();
529 if (unexpected.len > 0) return update.manifest_sr.fail(
530 unexpected,
531 "unexpected argument {q}",
532 .{unexpected},
533 );
573 break :contents_r .ending;534 break :contents_r .ending;
574 },535 },
575 else => {536 else => {
576 const contents_path = contents_path: {537 const contents_path = line_it.rest();
577 const line_rest = line_it.rest();538 if (contents_path.len == 0) break :contents_r .ending;
578 if (line_rest.len > 0) break :contents_path line_rest;539
579 const contents_path = maybe_arg orelse break :contents_r .ending;
580 maybe_arg = null;
581 break :contents_path contents_path;
582 };
583 if (std.mem.eql(u8, contents_path, "{")) {540 if (std.mem.eql(u8, contents_path, "{")) {
584 contents_impl = .{ .dr = .init(&manifest_fr.interface, "#}", &contents_buffer) };541 contents_impl = .{
542 .dr = .init(&update.manifest_sr.interface, "#}", &contents_buffer),
543 };
585 skip_delimiter = true;544 skip_delimiter = true;
586 break :contents_r &contents_impl.dr.interface;545 break :contents_r &contents_impl.dr.interface;
587 }546 }
588547
589 const args_test = runner.args.@"test" orelse return error.ContentsFileInFileTest;548 const args_test = runner.args.@"test" orelse return update.manifest_sr.fail(
590 try runner.server.serveMessageHeader(.{549 contents_path,
591 .tag = .discovered_inputs,550 "contents file requires directory test",
592 .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) +551 .{},
593 contents_path.len + 1),552 );
594 });553 try runner.discoverInput(args_test.input_dir, contents_path);
595 try runner.server.out.writeInt(u32, @backingInt(args_test.input_dir), .little);
596 try runner.server.out.writeAll(contents_path);
597 try runner.server.out.writeByte(0);
598 try runner.server.out.flush();
599554
600 contents_file = try args_test.dir.openFile(runner.io, contents_path, .{});555 contents_file =
556 args_test.dir.openFile(runner.io, contents_path, .{}) catch |err| switch (err) {
557 error.Canceled => |e| return e,
558 else => |e| return update.manifest_sr.fail(
559 contents_path,
560 "unable to open contents file: {t}",
561 .{e},
562 ),
563 };
601 contents_impl = .{ .fr = contents_file.?.reader(runner.io, &contents_buffer) };564 contents_impl = .{ .fr = contents_file.?.reader(runner.io, &contents_buffer) };
602 break :contents_r &contents_impl.fr.interface;565 break :contents_r &contents_impl.fr.interface;
603 },566 },
604 };567 };
605568
606 var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined;569 runner.handleCommand(&update, cmd_src_loc, cmd, arg, contents_r) catch |err| switch (err) {
607 const cmd_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "#{t} {s}", .{570 error.Canceled,
608 cmd, maybe_arg orelse "",571 error.OutOfMemory,
609 }) catch &name_buffer, 0);572 error.AlreadyReported,
610 defer cmd_prog_node.end();573 error.SkipTest,
574 error.WriteFailed,
575 => |e| return e,
576 error.ReadFailed => switch (contents_impl) {
577 .ending => unreachable,
578 .dr => return runner.fail("unable to read manifest: {t}", .{manifest_fr.err.?}),
579 .fr => |fr| return runner.fail("unable to read contents file: {t}", .{fr.err.?}),
580 },
581 };
582 } else |err| switch (err) {
583 error.ReadFailed => |e| return e,
584 error.StreamTooLong => unreachable,
585 error.EndOfStream => if (skip_delimiter)
586 return runner.fail("manifest missing \"#}}\" delimiter", .{}),
587 }
588 if (update.compiler) |*comp| {
589 if (comp.state != .idle) return runner.fail("missing \"#check\"", .{});
590 try comp.exit();
591 }
592}
593const Command = enum { todo, skip, exe, lib, obj, write, delete, update, check };
594fn handleCommand(
595 runner: *Runner,
596 update: *Update,
597 cmd_src_loc: []const u8,
598 cmd: Command,
599 arg: ?[]const u8,
600 contents_r: *std.Io.Reader,
601) TestError!void {
602 var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined;
603 const cmd_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "#{t}{s}{s}", .{
604 cmd, if (arg) |_| " " else "", arg orelse "",
605 }) catch &name_buffer, 0);
606 defer cmd_prog_node.end();
611607
612 cmd: switch (cmd) {608 cmd: switch (cmd) {
613 .todo => _ = try contents_r.discardRemaining(),609 .todo => _ = contents_r.discardRemaining() catch |err| switch (err) {
614 .skip => if (!allow_skip)610 error.ReadFailed => {},
615 return error.NonInitialSkipCommand // #skip must appear first611 },
616 else if (std.mem.eql(u8, target_query, maybe_arg orelse return error.MissingArg))612 .skip => if (!update.allow_skip) return update.manifest_sr.fail(
617 return error.SkipZigTest,613 cmd_src_loc,
618 .exe, .lib, .obj => try runner.spawnCompiler(&compiler, src_dir, switch (cmd) {614 "\"#skip\" must appear before other commands",
619 else => unreachable,615 .{},
620 .exe => .Exe,616 ) else if (std.mem.eql(u8, update.target_query, arg.?))
621 .lib => .Lib,617 return error.SkipTest,
622 .obj => .Obj,618 .exe, .lib, .obj => try runner.spawnCompiler(&update.compiler, update.src_dir, switch (cmd) {
623 }, contents_r, target: {619 else => unreachable,
624 const backend_split = std.mem.findScalarLast(u8, target_query, '-') orelse620 .exe => .Exe,
625 return error.TargetMissingBackend;621 .lib => .Lib,
626 const mode_split =622 .obj => .Obj,
627 std.mem.findScalarLast(u8, target_query[0..backend_split], '-') orelse623 }, contents_r, target: {
628 return error.TargetMissingMode;624 const backend_split = std.mem.findScalarLast(u8, update.target_query, '-') orelse
629 const triple = target_query[0..mode_split];625 return runner.fail("target {q} missing query", .{update.target_query});
630 const backend = std.meta.stringToEnum(626 const mode_split =
631 Compiler.Target.Backend,627 std.mem.findScalarLast(u8, update.target_query[0..backend_split], '-') orelse
632 target_query[backend_split + 1 ..],628 return runner.fail("target {q} missing mode", .{update.target_query});
633 ) orelse return error.TargetMissingBackend;629 const triple = update.target_query[0..mode_split];
634 const mode = std.meta.stringToEnum(630 const backend_str = update.target_query[backend_split + 1 ..];
635 Compiler.Target.Mode,631 const backend = std.meta.stringToEnum(Compiler.Target.Backend, backend_str) orelse
636 target_query[mode_split + 1 .. backend_split],632 return runner.fail("target {q} unknown backend {q}", .{
637 ) orelse return error.TargetMissingMode;633 update.target_query, backend_str,
638 break :target .{634 });
639 .triple = triple,635 const mode_str = update.target_query[mode_split + 1 .. backend_split];
640 .resolved = try std.zig.system.resolveTargetQuery(636 const mode = std.meta.stringToEnum(Compiler.Target.Mode, mode_str) orelse
641 runner.io,637 return runner.fail("target {q} unknown mode {q}", .{ update.target_query, mode_str });
642 try std.Build.parseTargetQuery(.{638 break :target .{
643 .arch_os_abi = triple,639 .triple = triple,
644 .object_format = switch (backend) {640 .resolved = std.zig.system.resolveTargetQuery(
645 .sema, .selfhosted, .llvm => null,
646 .cbe => "c",
647 },
648 }),
649 ),
650 .mode = mode,
651 .backend = backend,
652 };
653 }),
654 .write => {
655 const file = try src_dir.createFile(
656 runner.io,641 runner.io,
657 maybe_arg orelse return error.MissingFileArg,642 std.Build.parseTargetQuery(.{
658 .{},643 .arch_os_abi = triple,
659 );644 .object_format = switch (backend) {
660 defer file.close(runner.io);645 .sema, .selfhosted, .llvm => null,
661 var fw = file.writer(runner.io, &.{});646 .cbe => "c",
662 _ = try contents_r.streamRemaining(&fw.interface);
663 try file.setTimestamps(runner.io, .{ .modify_timestamp = .{ .new = update_mtime } });
664 },
665 .delete => try src_dir.deleteFile(
666 runner.io,
667 maybe_arg orelse return error.MissingFileArg,
668 ),
669 .update => {
670 const comp = &(compiler orelse return error.MissingCompiler);
671 update_prog_node.end();
672 update_prog_node = runner.prog_node.start(
673 switch (try contents_r.readSliceShort(&name_buffer)) {
674 0 => switch (update_num) {
675 0 => "initial update",
676 else => |num| std.mem.print(&name_buffer, "update {d}", .{num}) catch
677 &name_buffer,
678 },647 },
679 else => |name_len| name_buffer[0..name_len],648 }) catch |err| switch (err) {
680 },649 error.ParseFailed => return runner.fail("unable to parse triple {q}", .{
681 0,650 triple,
682 );651 }),
683 _ = try contents_r.discardRemaining();
684 if (comp.state != .idle) return error.MissingCheck;
685 try comp.client.serveBodylessMessage(.update);
686 comp.state = .update;
687 update_mtime = update_mtime.addDuration(.fromSeconds(2));
688 update_num += 1;
689 },
690 .check => {
691 const check = std.meta.stringToEnum(
692 Compiler.Check,
693 maybe_arg orelse return error.MissingCheckArg,
694 ) orelse return error.UnknownCheck;
695 const expected = try contents_r.allocRemaining(runner.gpa, .unlimited);
696 defer runner.gpa.free(expected);
697
698 const comp = &(compiler orelse return error.MissingCompiler);
699 if (comp.state != .update) return error.MissingUpdate;
700 const stderr = comp.mr.reader(1);
701 while (try comp.receiveMessage()) |message| switch (message.tag) {
702 .config => {
703 var body_r: std.Io.Reader = .fixed(message.body);
704 comp.config = body_r.takeStruct(std.zig.Server.Message.Config, .little) catch
705 unreachable;
706 },
707 .emit_digest => {
708 var body_r: std.Io.Reader = .fixed(message.body);
709 _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch
710 unreachable;
711 const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch
712 unreachable;
713 try comp.checkSuccess(check, expected, src_dir, digest);
714 },652 },
715 .error_bundle => {653 ) catch |err| switch (err) {
716 const error_bundle =654 error.Canceled => |e| return e,
717 try std.zig.Server.allocErrorBundle(runner.arena.allocator(), message.body);655 else => |e| return runner.fail("unable to resolve target {q}: {t}", .{ triple, e }),
718 switch (check) {656 },
719 .errors => {657 .mode = mode,
720 var error_aw: std.Io.Writer.Allocating = .init(runner.gpa);658 .backend = backend,
721 defer error_aw.deinit();659 };
722 try error_bundle.renderToWriter(.{660 }),
723 .include_reference_trace = false,661 .write => {
724 .include_source_line = false,662 const file = update.src_dir.createFile(runner.io, arg.?, .{}) catch |err| switch (err) {
725 .include_log_text = true,663 error.Canceled => |e| return e,
726 }, &error_aw.writer);664 else => |e| return update.manifest_sr.fail(arg.?, "unable to create file: {t}", .{e}),
727 try std.testing.expectEqualStrings(expected, error_aw.written());665 };
728 },666 defer file.close(runner.io);
729 .stdout, .exit, .lldb => if (error_bundle.errorMessageCount() > 0) {667 var fw = file.writer(runner.io, &.{});
730 try error_bundle.renderToStderr(runner.io, .{}, .auto);668 _ = try contents_r.streamRemaining(&fw.interface);
731 fatal("unexpected compile errors", .{});669 file.setTimestamps(runner.io, .{ .modify_timestamp = .{
732 },670 .new = update.mtime,
733 }671 } }) catch |err| switch (err) {
734 comp.state = .idle;672 error.Canceled => |e| return e,
735 break :cmd;673 else => |e| return update.manifest_sr.fail(
674 arg.?,
675 "unable to update file timestamps: {t}",
676 .{e},
677 ),
678 };
679 },
680 .delete => update.src_dir.deleteFile(runner.io, arg.?) catch |err| switch (err) {
681 error.Canceled => |e| return e,
682 else => |e| return update.manifest_sr.fail(arg.?, "unable to delete file: {t}", .{e}),
683 },
684 .update => {
685 const comp = &(update.compiler orelse return update.manifest_sr.fail(
686 cmd_src_loc,
687 "missing \"#exe\", \"#lib\", or \"#obj\"",
688 .{},
689 ));
690 update.prog_node.end();
691 update.prog_node = runner.prog_node.start(
692 switch (try contents_r.readSliceShort(&name_buffer)) {
693 0 => switch (update.num) {
694 0 => "initial update",
695 else => |num| std.mem.print(&name_buffer, "update {d}", .{num}) catch
696 &name_buffer,
736 },697 },
737 else => {}, // Ignore other messages,698 else => |name_len| name_buffer[0..name_len],
699 },
700 0,
701 );
702 _ = try contents_r.discardRemaining();
703 if (comp.state != .idle)
704 return update.manifest_sr.fail(cmd_src_loc, "missing \"#check\"", .{});
705 try comp.client.serveBodylessMessage(.update);
706 comp.state = .update;
707 update.mtime = update.mtime.addDuration(.fromSeconds(2));
708 update.num += 1;
709 },
710 .check => {
711 const check = std.meta.stringToEnum(Compiler.Check, arg.?) orelse
712 return update.manifest_sr.fail(arg.?, "unknown check", .{});
713 const expected =
714 contents_r.allocRemaining(runner.gpa, .unlimited) catch |err| switch (err) {
715 error.OutOfMemory, error.ReadFailed => |e| return e,
716 error.StreamTooLong => unreachable, // .unlimited
738 };717 };
718 defer runner.gpa.free(expected);
739719
740 const buffered_stderr = stderr.buffered();720 const comp = &(update.compiler orelse return update.manifest_sr.fail(
741 if (buffered_stderr.len > 0) {721 cmd_src_loc,
742 if (comp.allow_compiler_stderr) {722 "missing \"#exe\", \"#lib\", or \"#obj\"",
743 std.log.info("stderr:\n{s}", .{buffered_stderr});723 .{},
744 } else {724 ));
745 fatal("unexpected stderr:\n{s}", .{buffered_stderr});725 if (comp.state != .update)
726 return update.manifest_sr.fail(cmd_src_loc, "missing \"#update\"", .{});
727 const stderr = comp.mr.reader(1);
728 while (try comp.receiveMessage()) |message| switch (message.tag) {
729 else => {}, // Ignore other messages,
730 .config => {
731 var body_r: std.Io.Reader = .fixed(message.body);
732 comp.config = body_r.takeStruct(std.zig.Server.Message.Config, .little) catch
733 unreachable;
734 },
735 .emit_digest => {
736 var body_r: std.Io.Reader = .fixed(message.body);
737 _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch
738 unreachable;
739 const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch
740 unreachable;
741 try comp.checkSuccess(check, expected, update.src_dir, digest);
742 },
743 .error_bundle => {
744 const eb = std.zig.Server.allocErrorBundle(
745 runner.arena.allocator(),
746 message.body,
747 ) catch |err| switch (err) {
748 error.OutOfMemory => |e| return e,
749 error.EndOfStream => return runner.fail("error bundle truncated", .{}),
750 };
751 switch (check) {
752 .errors => {
753 var error_aw: std.Io.Writer.Allocating = .init(runner.gpa);
754 defer error_aw.deinit();
755 try eb.renderToWriter(.{
756 .include_reference_trace = false,
757 .include_source_line = false,
758 .include_log_text = true,
759 }, &error_aw.writer);
760 std.testing.expectEqualStrings(
761 expected,
762 error_aw.written(),
763 ) catch |err| switch (err) {
764 error.TestExpectedEqual => return runner.fail(
765 "errors did not match expected",
766 .{},
767 ),
768 };
769 },
770 .stdout, .exit, .lldb => if (eb.errorMessageCount() > 0) {
771 eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) {
772 error.Canceled => {},
773 else => {},
774 };
775 return runner.fail("unexpected compile errors", .{});
776 },
746 }777 }
778 comp.state = .idle;
779 break :cmd;
780 },
781 };
782
783 const buffered_stderr = stderr.buffered();
784 if (buffered_stderr.len > 0) {
785 if (comp.allow_compiler_stderr) {
786 std.log.info("stderr:\n{s}", .{buffered_stderr});
787 } else {
788 return runner.fail("unexpected stderr:\n{s}", .{buffered_stderr});
747 }789 }
790 }
748791
749 try comp.exit();792 try comp.exit();
750 fatal("compiler failed to send terminating error_bundle", .{});793 return runner.fail("compiler failed to send terminating error_bundle", .{});
751 },794 },
752 }
753 std.debug.assert(try contents_r.discardRemaining() == 0);
754 switch (cmd) {
755 .todo, .skip => {},
756 else => allow_skip = false,
757 }
758 } else |err| switch (err) {
759 else => |e| return e,
760 error.EndOfStream => if (skip_delimiter) return error.MissingDelimiter,
761 }795 }
762 if (compiler) |*comp| {796 assert(try contents_r.discardRemaining() == 0);
763 if (comp.state != .idle) return error.MissingCheck;797 switch (cmd) {
764 try comp.exit();798 .todo, .skip => {},
799 else => update.allow_skip = false,
765 }800 }
766}801}
767802
768fn spawnCompiler(803fn spawnCompiler(
769 runner: *const Runner,804 runner: *Runner,
770 compiler: *?Compiler,805 compiler: *?Compiler,
771 src_dir: std.Io.Dir,806 src_dir: std.Io.Dir,
772 output_mode: std.lang.OutputMode,807 output_mode: std.lang.OutputMode,
773 args_r: *std.Io.Reader,808 args_r: *std.Io.Reader,
774 target: Compiler.Target,809 target: Compiler.Target,
775) !void {810) (Fallible || std.Io.Reader.ShortError)!void {
776 const gpa = runner.gpa;811 const gpa = runner.gpa;
777 const arena = runner.arena.allocator();812 const arena = runner.arena.allocator();
778 if (compiler.*) |*comp| {813 if (compiler.*) |*comp| {
...@@ -804,18 +839,20 @@ fn spawnCompiler(...@@ -804,18 +839,20 @@ fn spawnCompiler(
804 .selfhosted => try argv.append(gpa, "-fno-llvm"),839 .selfhosted => try argv.append(gpa, "-fno-llvm"),
805 .llvm => try argv.append(gpa, "-fllvm"),840 .llvm => try argv.append(gpa, "-fllvm"),
806 .cbe => try argv.append(gpa, "-ofmt=c"),841 .cbe => try argv.append(gpa, "-ofmt=c"),
807 } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0..try std.Io.Dir.readLinkAbsolute(842 } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. std.Io.Dir.readLinkAbsolute(
808 runner.io,843 runner.io,
809 try arena.print("/proc/self/fd/{d}", .{844 try arena.print("/proc/self/fd/{d}", .{(runner.args.lib_dir orelse
810 (runner.args.lib_dir orelse return error.MissingLibDir).handle,845 return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})).handle}),
811 }),
812 &path_buffer,846 &path_buffer,
813 )] });847 ) catch |err| switch (err) {
848 error.Canceled => |e| return e,
849 else => |e| return runner.fail("unable to get lib path: {t}", .{e}),
850 }] });
814 var need: usize = 1;851 var need: usize = 1;
815 var root_name: ?[]const u8 = null;852 var root_name: ?[]const u8 = null;
816 while (true) {853 while (true) {
817 const done = if (args_r.fill(need)) false else |err| switch (err) {854 const done = if (args_r.fill(need)) false else |err| switch (err) {
818 else => |e| return e,855 error.ReadFailed => |e| return e,
819 error.EndOfStream => true,856 error.EndOfStream => true,
820 };857 };
821 const buffered = args_r.buffered();858 const buffered = args_r.buffered();
...@@ -854,15 +891,19 @@ fn spawnCompiler(...@@ -854,15 +891,19 @@ fn spawnCompiler(
854 }) catch &name_buffer, 0);891 }) catch &name_buffer, 0);
855 errdefer comp_prog_node.end();892 errdefer comp_prog_node.end();
856893
857 const child = try std.process.spawn(runner.io, .{894 const child = std.process.spawn(runner.io, .{
858 .exe = .{ .file = runner.args.zig_exe orelse return error.MissingZigExe },895 .exe = .{ .file = runner.args.zig_exe orelse
896 return runner.fail("missing \"--zig=path/to/zig\" arg", .{}) },
859 .argv = argv.items,897 .argv = argv.items,
860 .cwd = .{ .dir = src_dir },898 .cwd = .{ .dir = src_dir },
861 .progress_node = comp_prog_node,899 .progress_node = comp_prog_node,
862 .stdin = .pipe,900 .stdin = .pipe,
863 .stdout = .pipe,901 .stdout = .pipe,
864 .stderr = .pipe,902 .stderr = .pipe,
865 });903 }) catch |err| switch (err) {
904 error.Canceled, error.OutOfMemory => |e| return e,
905 else => |e| return runner.fail("unable to spawn compiler: {t}", .{e}),
906 };
866 compiler.* = .{907 compiler.* = .{
867 .runner = runner,908 .runner = runner,
868 .prog_node = comp_prog_node,909 .prog_node = comp_prog_node,
...@@ -886,7 +927,7 @@ fn spawnCompiler(...@@ -886,7 +927,7 @@ fn spawnCompiler(
886 comp.client = .{ .in = comp.mr.reader(0), .out = &comp.fw.interface };927 comp.client = .{ .in = comp.mr.reader(0), .out = &comp.fw.interface };
887}928}
888const Compiler = struct {929const Compiler = struct {
889 runner: *const Runner,930 runner: *Runner,
890 prog_node: std.Progress.Node,931 prog_node: std.Progress.Node,
891 allow_compiler_stderr: bool,932 allow_compiler_stderr: bool,
892 output_mode: std.lang.OutputMode,933 output_mode: std.lang.OutputMode,
...@@ -927,7 +968,7 @@ const Compiler = struct {...@@ -927,7 +968,7 @@ const Compiler = struct {
927 };968 };
928 };969 };
929970
930 fn receiveMessage(comp: *Compiler) !?struct {971 fn receiveMessage(comp: *Compiler) Fallible!?struct {
931 tag: std.zig.Server.Message.Tag,972 tag: std.zig.Server.Message.Tag,
932 body: []const u8,973 body: []const u8,
933 } {974 } {
...@@ -935,9 +976,10 @@ const Compiler = struct {...@@ -935,9 +976,10 @@ const Compiler = struct {
935 &comp.mr,976 &comp.mr,
936 .none,977 .none,
937 ) catch |err| switch (err) {978 ) catch |err| switch (err) {
979 error.Canceled, error.OutOfMemory => |e| return e,
938 error.Timeout => unreachable,980 error.Timeout => unreachable,
939 error.EndOfStream => return null,981 error.EndOfStream => return null,
940 else => |e| return e,982 else => |e| return comp.runner.fail("unable to receive message: {t}", .{e}),
941 };983 };
942 const body = comp.client.in.take(header.bytes_len) catch unreachable;984 const body = comp.client.in.take(header.bytes_len) catch unreachable;
943 const stderr = comp.mr.reader(1);985 const stderr = comp.mr.reader(1);
...@@ -945,7 +987,9 @@ const Compiler = struct {...@@ -945,7 +987,9 @@ const Compiler = struct {
945 if (comp.allow_compiler_stderr) {987 if (comp.allow_compiler_stderr) {
946 std.log.info("{t} stderr:\n{s}", .{ header.tag, stderr.buffered() });988 std.log.info("{t} stderr:\n{s}", .{ header.tag, stderr.buffered() });
947 } else {989 } else {
948 fatal("{t} unexpected stderr:\n{s}", .{ header.tag, stderr.buffered() });990 return comp.runner.fail("{t} unexpected stderr:\n{s}", .{
991 header.tag, stderr.buffered(),
992 });
949 }993 }
950 stderr.tossBuffered();994 stderr.tossBuffered();
951 }995 }
...@@ -959,7 +1003,7 @@ const Compiler = struct {...@@ -959,7 +1003,7 @@ const Compiler = struct {
959 expected: []const u8,1003 expected: []const u8,
960 src_dir: std.Io.Dir,1004 src_dir: std.Io.Dir,
961 digest: *const std.Build.Cache.BinDigest,1005 digest: *const std.Build.Cache.BinDigest,
962 ) !void {1006 ) Fallible!void {
963 const runner = comp.runner;1007 const runner = comp.runner;
964 const gpa = runner.gpa;1008 const gpa = runner.gpa;
965 const arena = runner.arena.allocator();1009 const arena = runner.arena.allocator();
...@@ -1021,7 +1065,10 @@ const Compiler = struct {...@@ -1021,7 +1065,10 @@ const Compiler = struct {
1021 }) catch &name_buffer, 0);1065 }) catch &name_buffer, 0);
1022 defer run_prog_node.end();1066 defer run_prog_node.end();
10231067
1024 const bin_file = try src_dir.openFile(io, bin_path, .{});1068 const bin_file = src_dir.openFile(io, bin_path, .{}) catch |err| switch (err) {
1069 error.Canceled => |e| return e,
1070 else => |e| return runner.fail("unable to open {q}: {t}", .{ bin_path, e }),
1071 };
1025 defer bin_file.close(io);1072 defer bin_file.close(io);
1026 switch (check) {1073 switch (check) {
1027 .errors => unreachable,1074 .errors => unreachable,
...@@ -1033,13 +1080,14 @@ const Compiler = struct {...@@ -1033,13 +1080,14 @@ const Compiler = struct {
1033 .progress_node = run_prog_node,1080 .progress_node = run_prog_node,
1034 }) catch |err| if (executor) |_| {1081 }) catch |err| if (executor) |_| {
1035 // Chances are the foreign executor isn't available. Skip this evaluation.1082 // Chances are the foreign executor isn't available. Skip this evaluation.
1036 std.log.warn("skipping execution of '{s}' via executor for foreign target '{s}': {t}", .{1083 std.log.warn(
1037 bin_path,1084 "skipping execution of '{s}' via executor for foreign target '{s}': {t}",
1038 comp.target.triple,1085 .{ bin_path, comp.target.triple, err },
1039 err,1086 );
1040 });
1041 return;1087 return;
1042 } else fatal("failed to run the generated executable '{s}': {t}", .{ bin_path, err });1088 } else return runner.fail("unable to run the generated executable '{s}': {t}", .{
1089 bin_path, err,
1090 });
1043 defer {1091 defer {
1044 gpa.free(result.stdout);1092 gpa.free(result.stdout);
1045 gpa.free(result.stderr);1093 gpa.free(result.stderr);
...@@ -1048,20 +1096,46 @@ const Compiler = struct {...@@ -1048,20 +1096,46 @@ const Compiler = struct {
1048 .exited => |code| switch (check) {1096 .exited => |code| switch (check) {
1049 .errors, .lldb => unreachable,1097 .errors, .lldb => unreachable,
1050 .stdout => {1098 .stdout => {
1051 if (code != 0) fatal("generated executable '{s}' failed with code {d}", .{ bin_path, code });1099 if (code != 0) return runner.fail(
1052 try std.testing.expectEqualStrings(expected, result.stdout);1100 "generated executable '{s}' failed with code {d}",
1101 .{ bin_path, code },
1102 );
1103 std.testing.expectEqualStrings(
1104 expected,
1105 result.stdout,
1106 ) catch |err| switch (err) {
1107 error.TestExpectedEqual => return runner.fail(
1108 "stdout did not match expected",
1109 .{},
1110 ),
1111 };
1053 },1112 },
1054 .exit => {1113 .exit => {
1055 var actual_code_buffer: [std.fmt.count("{d}", .{std.math.maxInt(u8)})]u8 = undefined;1114 var actual_code_buffer: [std.fmt.count("{d}", .{std.math.maxInt(u8)})]u8 =
1056 try std.testing.expectEqualStrings(1115 undefined;
1116 std.testing.expectEqualStrings(
1057 expected,1117 expected,
1058 std.mem.print(&actual_code_buffer, "{d}", .{code}) catch unreachable,1118 std.mem.print(&actual_code_buffer, "{d}", .{code}) catch unreachable,
1059 );1119 ) catch |err| switch (err) {
1120 error.TestExpectedEqual => return runner.fail(
1121 "exit code did not match expected",
1122 .{},
1123 ),
1124 };
1060 },1125 },
1061 },1126 },
1062 .signal => |sig| fatal("generated executable '{s}' terminated with signal {t}", .{ bin_path, sig }),1127 .signal => |sig| return runner.fail(
1063 .stopped => |sig| fatal("generated executable '{s}' stopped with signal {t}", .{ bin_path, sig }),1128 "generated executable '{s}' terminated with signal {t}",
1064 .unknown => fatal("generated executable '{s}' terminated unexpectedly", .{bin_path}),1129 .{ bin_path, sig },
1130 ),
1131 .stopped => |sig| return runner.fail(
1132 "generated executable '{s}' stopped with signal {t}",
1133 .{ bin_path, sig },
1134 ),
1135 .unknown => return runner.fail(
1136 "generated executable '{s}' terminated unexpectedly",
1137 .{bin_path},
1138 ),
1065 }1139 }
1066 if (executor == null and result.stderr.len > 0) {1140 if (executor == null and result.stderr.len > 0) {
1067 std.log.err("generated executable '{s}' had unexpected stderr:\n{s}", .{1141 std.log.err("generated executable '{s}' had unexpected stderr:\n{s}", .{
...@@ -1073,12 +1147,12 @@ const Compiler = struct {...@@ -1073,12 +1147,12 @@ const Compiler = struct {
1073 }1147 }
1074 }1148 }
10751149
1076 fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) ![]const u8 {1150 fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) Fallible![]const u8 {
1077 const runner = parent_comp.runner;1151 const runner = parent_comp.runner;
1078 const arena = runner.arena.allocator();1152 const arena = runner.arena.allocator();
1079 var compiler: ?Compiler = null;1153 var compiler: ?Compiler = null;
1080 var args_r: std.Io.Reader = .fixed(c_path);1154 var args_r: std.Io.Reader = .fixed(c_path);
1081 try parent_comp.runner.spawnCompiler(&compiler, src_dir, parent_comp.output_mode, &args_r, .{1155 parent_comp.runner.spawnCompiler(&compiler, src_dir, parent_comp.output_mode, &args_r, .{
1082 .triple = parent_comp.target.triple,1156 .triple = parent_comp.target.triple,
1083 .resolved = resolved: {1157 .resolved = resolved: {
1084 var resolved = parent_comp.target.resolved;1158 var resolved = parent_comp.target.resolved;
...@@ -1087,12 +1161,18 @@ const Compiler = struct {...@@ -1087,12 +1161,18 @@ const Compiler = struct {
1087 },1161 },
1088 .mode = .whole,1162 .mode = .whole,
1089 .backend = null,1163 .backend = null,
1090 });1164 }) catch |err| switch (err) {
1165 error.Canceled, error.OutOfMemory, error.AlreadyReported => |e| return e,
1166 error.ReadFailed => unreachable, // .fixed
1167 };
1091 const comp = &compiler.?;1168 const comp = &compiler.?;
1092 try comp.client.serveBodylessMessage(.update);1169 comp.client.serveBodylessMessage(.update) catch |err| switch (err) {
1170 error.WriteFailed => return runner.fail("unable to send message: {t}", .{comp.fw.err.?}),
1171 };
1093 comp.state = .update;1172 comp.state = .update;
1094 var out_path: ?[]const u8 = null;1173 var out_path: ?[]const u8 = null;
1095 while (try comp.receiveMessage()) |message| switch (message.tag) {1174 while (try comp.receiveMessage()) |message| switch (message.tag) {
1175 else => {}, // Ignore other messages,
1096 .config => {1176 .config => {
1097 var body_r: std.Io.Reader = .fixed(message.body);1177 var body_r: std.Io.Reader = .fixed(message.body);
1098 comp.config =1178 comp.config =
...@@ -1108,40 +1188,421 @@ const Compiler = struct {...@@ -1108,40 +1188,421 @@ const Compiler = struct {
1108 out_path = try std.Io.Dir.path.join(arena, &.{ out_dir, comp.out_name });1188 out_path = try std.Io.Dir.path.join(arena, &.{ out_dir, comp.out_name });
1109 },1189 },
1110 .error_bundle => {1190 .error_bundle => {
1111 const error_bundle = try std.zig.Server.allocErrorBundle(arena, message.body);1191 const eb = std.zig.Server.allocErrorBundle(
11121192 runner.arena.allocator(),
1113 if (error_bundle.errorMessageCount() > 0) {1193 message.body,
1114 try error_bundle.renderToStderr(runner.io, .{}, .auto);1194 ) catch |err| switch (err) {
1115 fatal("unexpected compile errors", .{});1195 error.OutOfMemory => |e| return e,
1196 error.EndOfStream => return runner.fail("error bundle truncated", .{}),
1197 };
1198 if (eb.errorMessageCount() > 0) {
1199 try runner.eb_wip.addBundleAsRoots(eb);
1200 return runner.fail("unexpected compile errors", .{});
1116 }1201 }
1117 comp.state = .idle;1202 comp.state = .idle;
1118 break;1203 break;
1119 },1204 },
1120 else => {}, // Ignore other messages,
1121 };1205 };
1122 try comp.exit();1206 try comp.exit();
1123 return out_path.?;1207 return out_path.?;
1124 }1208 }
11251209
1126 fn exit(comp: *Compiler) !void {1210 fn exit(comp: *Compiler) Fallible!void {
1127 const io = comp.runner.io;1211 const runner = comp.runner;
1128 comp.client.serveBodylessMessage(.exit) catch |err| switch (err) {1212 comp.client.serveBodylessMessage(.exit) catch |err| switch (err) {
1129 error.WriteFailed => switch (comp.fw.err.?) {1213 error.WriteFailed => switch (comp.fw.err.?) {
1214 error.Canceled => |e| return e,
1130 error.BrokenPipe => {},1215 error.BrokenPipe => {},
1131 else => |e| fatal("failed to send exit: {t}", .{e}),1216 else => |e| return comp.runner.fail("unable to send exit: {t}", .{e}),
1132 },1217 },
1133 };1218 };
1134 comp.child.stdin.?.close(io);1219 comp.child.stdin.?.close(runner.io);
1135 comp.child.stdin = null;1220 comp.child.stdin = null;
1136 while (try comp.receiveMessage()) |_| {}1221 while (try comp.receiveMessage()) |_| {}
1137 if (comp.client.in.bufferedLen() > 0) return error.EndOfStream;1222 if (comp.client.in.bufferedLen() > 0) return runner.fail("unable to receive messages", .{});
1138 const term = comp.child.wait(io) catch |err| fatal("child process failed: {t}", .{err});1223 const term = comp.child.wait(runner.io) catch |err|
1224 return runner.fail("child process failed: {t}", .{err});
1139 comp.prog_node.end();1225 comp.prog_node.end();
1140 switch (term) {1226 switch (term) {
1141 .exited => |code| if (code != 0) fatal("compiler failed with code {d}", .{code}),1227 .exited => |code| if (code != 0)
1142 .signal => |sig| fatal("compiler terminated with signal {t}", .{sig}),1228 return runner.fail("compiler failed with code {d}", .{code}),
1143 .stopped => |sig| fatal("compiler stopped unexpectedly with signal {t}", .{sig}),1229 .signal => |sig| return runner.fail("compiler terminated with signal {t}", .{sig}),
1144 .unknown => fatal("compiler terminated unexpectedly", .{}),1230 .stopped => |sig| return runner.fail("compiler stopped unexpectedly with signal {t}", .{
1231 sig,
1232 }),
1233 .unknown => return runner.fail("compiler terminated unexpectedly", .{}),
1145 }1234 }
1146 }1235 }
1147};1236};
1237
1238const ClientArgs = struct {
1239 test_file: std.Io.File,
1240 test_kind: std.Io.File.Kind,
1241 zig_exe: std.Io.File,
1242 lib_dir: std.Io.Dir,
1243 src_dir: std.Io.Dir,
1244 targets: std.ArrayList([]const u8),
1245 keep_src: bool,
1246 enable_qemu: bool,
1247 enable_wine: bool,
1248 enable_wasmtime: bool,
1249 enable_darling: bool,
1250};
1251pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelable)!u8 {
1252 const fatal = struct {
1253 fn fatal(comptime fmt: []const u8, args: anytype) noreturn {
1254 std.log.err(fmt, args);
1255 std.process.exit(1);
1256 }
1257 }.fatal;
1258 const arena = init.arena.allocator();
1259
1260 var test_path_arg: ?[]const u8 = null;
1261 var zig_path_arg: ?[]const u8 = null;
1262 var lib_path_arg: ?[]const u8 = null;
1263 var args: ClientArgs = .{
1264 .test_file = undefined,
1265 .test_kind = undefined,
1266 .zig_exe = undefined,
1267 .lib_dir = undefined,
1268 .src_dir = undefined,
1269 .targets = .empty,
1270 .keep_src = false,
1271 .enable_qemu = false,
1272 .enable_wine = false,
1273 .enable_wasmtime = false,
1274 .enable_darling = false,
1275 };
1276 defer args.targets.deinit(init.gpa);
1277
1278 var arg_it = try init.minimal.args.iterateAllocator(arena);
1279 const self = arg_it.next().?;
1280 while (arg_it.next()) |arg| {
1281 if (std.mem.eql(u8, arg, "--listen=-")) {
1282 var stdin_buffer: [512]u8 = undefined;
1283 var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer);
1284 var stdout_buffer: [512]u8 = undefined;
1285 var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer);
1286 var runner: Runner = .{
1287 .gpa = init.gpa,
1288 .arena = init.arena,
1289 .io = init.io,
1290 .prog_node = std.Progress.start(init.io, .{}),
1291 .args = .{
1292 .@"test" = null,
1293 .manifest_file = null,
1294 .zig_exe = null,
1295 .lib_dir = null,
1296 .src_dir = null,
1297 .targets = .empty,
1298 .target_bytes = .empty,
1299 .enable_qemu = false,
1300 .enable_wine = false,
1301 .enable_wasmtime = false,
1302 .enable_darling = false,
1303 },
1304 .host = std.zig.system.resolveTargetQuery(init.io, .{}) catch |err| switch (err) {
1305 error.Canceled => |e| return e,
1306 else => |e| fatal("unable to resolve host target: {t}", .{e}),
1307 },
1308 .server = .{
1309 .in = &stdin.interface,
1310 .out = &stdout.interface,
1311 },
1312 .eb_wip = try .init(init.gpa),
1313 };
1314 defer runner.deinit();
1315 switch (runner.runServer()) {
1316 error.Canceled, error.OutOfMemory => |e| return e,
1317 error.ReadFailed => fatal("unable to receive message: {t}", .{stdin.err.?}),
1318 error.EndOfStream => fatal("no more messages", .{}),
1319 error.WriteFailed => fatal("unable to send message: {t}", .{stdout.err.?}),
1320 error.AlreadyReported => {
1321 var eb = try runner.eb_wip.toOwnedBundle("");
1322 defer eb.deinit(init.gpa);
1323 eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) {
1324 error.Canceled => |e| return e,
1325 else => {},
1326 };
1327 std.process.exit(1);
1328 },
1329 }
1330 } else if (std.mem.eql(u8, arg, "--zig")) {
1331 zig_path_arg = arg_it.next() orelse fatal("missing arg after '{s}'", .{arg});
1332 } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| {
1333 zig_path_arg = zig_path;
1334 } else if (std.mem.eql(u8, arg, "--lib")) {
1335 lib_path_arg = arg_it.next() orelse fatal("missing arg after '{s}'", .{arg});
1336 } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| {
1337 lib_path_arg = lib_path;
1338 } else if (std.mem.eql(u8, arg, "--target")) {
1339 try args.targets.append(
1340 init.gpa,
1341 try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after '{s}'", .{arg})),
1342 );
1343 } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| {
1344 try args.targets.append(init.gpa, try arena.dupe(u8, target));
1345 } else if (std.mem.eql(u8, arg, "--keep-src")) {
1346 args.keep_src = true;
1347 } else if (std.mem.eql(u8, arg, "-fqemu")) {
1348 args.enable_qemu = true;
1349 } else if (std.mem.eql(u8, arg, "-fwine")) {
1350 args.enable_wine = true;
1351 } else if (std.mem.eql(u8, arg, "-fwasmtime")) {
1352 args.enable_wasmtime = true;
1353 } else if (std.mem.eql(u8, arg, "-fdarling")) {
1354 args.enable_darling = true;
1355 } else {
1356 if (test_path_arg) |_| fatal("unknown arg '{s}'", .{arg});
1357 test_path_arg = arg;
1358 }
1359 }
1360
1361 const test_path = test_path_arg orelse fatal("missing \"path/to/test\" arg", .{});
1362 const zig_path = zig_path_arg orelse fatal("missing \"--zig=path/to/zig\" arg", .{});
1363 const lib_path = lib_path_arg orelse fatal("missing \"--lib=path/to/lib\" arg", .{});
1364
1365 const cwd: std.Io.Dir = .cwd();
1366 args.test_file = cwd.openFile(init.io, test_path, .{
1367 .allow_directory = true,
1368 }) catch |err| switch (err) {
1369 error.Canceled => |e| return e,
1370 else => |e| fatal("unable to open {q}: {t}", .{ test_path, e }),
1371 };
1372 defer args.test_file.close(init.io);
1373 args.test_kind = (args.test_file.stat(init.io) catch |err| switch (err) {
1374 error.Canceled => |e| return e,
1375 else => |e| fatal("unable to stat {q}: {t}", .{ test_path, e }),
1376 }).kind;
1377 args.zig_exe = cwd.openFile(init.io, zig_path, .{}) catch |err| switch (err) {
1378 error.Canceled => |e| return e,
1379 else => |e| fatal("unable to open {q}: {t}", .{ zig_path, e }),
1380 };
1381 defer args.zig_exe.close(init.io);
1382 args.lib_dir = cwd.openDir(init.io, lib_path, .{}) catch |err| switch (err) {
1383 error.Canceled => |e| return e,
1384 else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }),
1385 };
1386 defer args.lib_dir.close(init.io);
1387
1388 const src_dir_path = "src_" ++ std.fmt.hex(rand_int: {
1389 var rand_int: u64 = undefined;
1390 init.io.random(@ptrCast(&rand_int));
1391 break :rand_int rand_int;
1392 });
1393 args.src_dir = cwd.createDirPathOpen(init.io, src_dir_path, .{}) catch |err| switch (err) {
1394 error.Canceled => |e| return e,
1395 else => |e| fatal("unable to create {q}: {t}", .{ src_dir_path, e }),
1396 };
1397 defer {
1398 args.src_dir.close(init.io);
1399 if (!args.keep_src) cwd.deleteTree(init.io, src_dir_path) catch |err| {
1400 std.log.warn("unable to delete tree {q}: {t}", .{ src_dir_path, err });
1401 };
1402 }
1403
1404 var child = std.process.spawn(init.io, .{
1405 .argv = &.{ self, "--listen=-" },
1406 .stdin = .pipe,
1407 .stdout = .pipe,
1408 .inherit_dirs = &.{ args.lib_dir, args.src_dir },
1409 .inherit_files = &.{ args.zig_exe, args.test_file },
1410 }) catch |err| switch (err) {
1411 error.Canceled, error.OutOfMemory => |e| return e,
1412 else => |e| fatal("unable to spawn runner: {t}", .{e}),
1413 };
1414
1415 var child_reader_buffer: [512]u8 = undefined;
1416 var child_reader = child.stdout.?.readerStreaming(init.io, &child_reader_buffer);
1417 var child_writer_buffer: [512]u8 = undefined;
1418 var child_writer = child.stdin.?.writerStreaming(init.io, &child_writer_buffer);
1419 var client: std.zig.Client = .{
1420 .in = &child_reader.interface,
1421 .out = &child_writer.interface,
1422 };
1423 const success = runClient(init.gpa, arena, init.io, &client, &args) catch |err| switch (err) {
1424 error.OutOfMemory => |e| return e,
1425 error.ReadFailed => fatal("unable to receive message: {t}", .{child_reader.err.?}),
1426 error.EndOfStream => fatal("no more messages", .{}),
1427 error.WriteFailed => fatal("unable to send message: {t}", .{child_writer.err.?}),
1428 };
1429 _ = child.wait(init.io) catch |err| switch (err) {
1430 error.Canceled => |e| return e,
1431 else => |e| fatal("unable to wait for runner: {t}", .{e}),
1432 };
1433 return if (success) 0 else 1;
1434}
1435fn runClient(
1436 gpa: std.mem.Allocator,
1437 arena: std.mem.Allocator,
1438 io: std.Io,
1439 client: *std.zig.Client,
1440 args: *const ClientArgs,
1441) (std.mem.Allocator.Error || std.Io.Reader.Error || std.Io.Writer.Error)!bool {
1442 var stdout_buffer: [512]u8 = undefined;
1443 var stdout_fw: std.Io.File.Writer = .initStreaming(.stdout(), io, &stdout_buffer);
1444 const stdout = &stdout_fw.interface;
1445 var stderr_buffer: [512]u8 = undefined;
1446 var stderr_fw: std.Io.File.Writer = .initStreaming(.stderr(), io, &stderr_buffer);
1447 const stderr = &stderr_fw.interface;
1448
1449 var argv: std.ArrayList(u8) = .empty;
1450 defer argv.deinit(gpa);
1451 const Arg = std.zig.Client.Message.Arg;
1452
1453 try argv.append(gpa, @backingInt(@as(Arg, switch (args.test_kind) {
1454 else => unreachable,
1455 .file => .input_file,
1456 .directory => .input_dir,
1457 })));
1458 try argv.appendSlice(gpa, @ptrCast(&args.test_file.handle));
1459
1460 try argv.append(gpa, @backingInt(Arg.prefix));
1461 try argv.appendSlice(gpa, "--zig=");
1462 try argv.append(gpa, 0);
1463
1464 try argv.append(gpa, @backingInt(Arg.input_file));
1465 try argv.appendSlice(gpa, @ptrCast(&args.zig_exe.handle));
1466
1467 try argv.append(gpa, @backingInt(Arg.prefix));
1468 try argv.appendSlice(gpa, "--lib=");
1469 try argv.append(gpa, 0);
1470
1471 try argv.append(gpa, @backingInt(Arg.input_dir));
1472 try argv.appendSlice(gpa, @ptrCast(&args.lib_dir.handle));
1473
1474 try argv.append(gpa, @backingInt(Arg.prefix));
1475 try argv.appendSlice(gpa, "--src=");
1476 try argv.append(gpa, 0);
1477
1478 try argv.append(gpa, @backingInt(Arg.output_dir));
1479 try argv.appendSlice(gpa, @ptrCast(&args.src_dir.handle));
1480
1481 for (args.targets.items) |target| {
1482 try argv.append(gpa, @backingInt(Arg.string));
1483 try argv.appendSlice(gpa, "--target");
1484 try argv.append(gpa, 0);
1485
1486 try argv.append(gpa, @backingInt(Arg.string));
1487 try argv.appendSlice(gpa, target);
1488 try argv.append(gpa, 0);
1489 }
1490
1491 if (args.enable_qemu) {
1492 try argv.append(gpa, @backingInt(Arg.string));
1493 try argv.appendSlice(gpa, "-fqemu");
1494 try argv.append(gpa, 0);
1495 }
1496 if (args.enable_wine) {
1497 try argv.append(gpa, @backingInt(Arg.string));
1498 try argv.appendSlice(gpa, "-fwine");
1499 try argv.append(gpa, 0);
1500 }
1501 if (args.enable_wasmtime) {
1502 try argv.append(gpa, @backingInt(Arg.string));
1503 try argv.appendSlice(gpa, "-fwasmtime");
1504 try argv.append(gpa, 0);
1505 }
1506 if (args.enable_darling) {
1507 try argv.append(gpa, @backingInt(Arg.string));
1508 try argv.appendSlice(gpa, "-fdarling");
1509 try argv.append(gpa, 0);
1510 }
1511
1512 try client.serveMessageHeader(.{
1513 .tag = .args,
1514 .bytes_len = @intCast(argv.items.len),
1515 });
1516 try client.out.writeAll(argv.items);
1517
1518 var success = true;
1519 var metadata: struct {
1520 index: u32,
1521 len: u32,
1522 names: []u32,
1523 expected_panic_msgs: []u32,
1524 string_bytes: []u8,
1525
1526 fn name(m: *@This()) []const u8 {
1527 return std.mem.sliceTo(m.string_bytes[m.names[m.index - 1]..], 0);
1528 }
1529
1530 fn expectedPanicMsg(m: *@This()) ?[:0]const u8 {
1531 return switch (m.expected_panic_msgs[m.index - 1]) {
1532 0 => null,
1533 else => |offset| m.string_bytes[offset..std.mem.findScalarPos(
1534 u8,
1535 m.string_bytes,
1536 offset,
1537 0,
1538 ).? :0],
1539 };
1540 }
1541
1542 fn next(m: *@This()) ?u32 {
1543 if (m.len - m.index == 0) return null;
1544 defer m.index += 1;
1545 return m.index;
1546 }
1547 } = .{ .index = 0, .len = 0, .names = &.{}, .expected_panic_msgs = &.{}, .string_bytes = &.{} };
1548 try client.serveBodylessMessage(.query_test_metadata);
1549 while (true) {
1550 const hdr = try client.receiveMessage();
1551 switch (hdr.tag) {
1552 else => try client.in.discardAll(hdr.bytes_len),
1553 .zig_version => {
1554 const expected = @import("builtin").zig_version_string;
1555 const actual = try client.in.take(hdr.bytes_len);
1556 if (!std.mem.eql(u8, expected, actual)) {
1557 try stderr.print(
1558 "error: zig version mismatch compiler test runner vs compiler: {q} vs {q}\n",
1559 .{ expected, actual },
1560 );
1561 try stderr.flush();
1562 std.process.exit(1);
1563 }
1564 },
1565 .error_bundle => {
1566 const body = try arena.alloc(u8, hdr.bytes_len);
1567 try client.in.readSliceAll(body);
1568 const eb = std.zig.Server.allocErrorBundle(arena, body) catch |err| switch (err) {
1569 error.OutOfMemory => |e| return e,
1570 error.EndOfStream => {
1571 try stderr.writeAll("error bundle truncated\n");
1572 try stderr.flush();
1573 std.process.exit(1);
1574 },
1575 };
1576 success = false;
1577 eb.renderToStderr(io, .{}, .auto) catch {};
1578 },
1579 .test_metadata => {
1580 const tm_hdr =
1581 try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little);
1582 metadata = .{
1583 .index = 0,
1584 .len = tm_hdr.tests_len,
1585 .names = try arena.alloc(u32, tm_hdr.tests_len),
1586 .expected_panic_msgs = try arena.alloc(u32, tm_hdr.tests_len),
1587 .string_bytes = try arena.alloc(u8, tm_hdr.string_bytes_len),
1588 };
1589 try client.in.readSliceEndian(u32, metadata.names, .little);
1590 try client.in.readSliceEndian(u32, metadata.expected_panic_msgs, .little);
1591 try client.in.readSliceAll(metadata.string_bytes);
1592 try client.serveRunTest(metadata.next() orelse break);
1593 },
1594 .test_results => {
1595 const tr = try client.in.takeStruct(std.zig.Server.Message.TestResults, .little);
1596 switch (tr.flags.status) {
1597 .pass, .skip => {},
1598 .fail => success = false,
1599 }
1600 try stdout.print("[{t}] {s}\n", .{ tr.flags.status, metadata.name() });
1601 try stdout.flush();
1602 try client.serveRunTest(metadata.next() orelse break);
1603 },
1604 }
1605 }
1606 try client.serveBodylessMessage(.exit);
1607 return success;
1608}
tools/gen_spirv_spec.zig+1-2
...@@ -86,8 +86,7 @@ pub fn main(init: std.process.Init) !void {...@@ -86,8 +86,7 @@ pub fn main(init: std.process.Init) !void {
8686
87 var zir = try std.zig.AstGen.generate(arena, tree);87 var zir = try std.zig.AstGen.generate(arena, tree);
88 if (zir.hasCompileErrors()) {88 if (zir.hasCompileErrors()) {
89 var wip_errors: std.zig.ErrorBundle.Wip = undefined;89 var wip_errors: std.zig.ErrorBundle.Wip = try .init(arena);
90 try wip_errors.init(arena);
91 defer wip_errors.deinit();90 defer wip_errors.deinit();
92 try wip_errors.addZirErrorMessages(zir, tree, output, "");91 try wip_errors.addZirErrorMessages(zir, tree, output, "");
93 var error_bundle = try wip_errors.toOwnedBundle("");92 var error_bundle = try wip_errors.toOwnedBundle("");