| author | |
| committer | |
| log | 809fcd4910552b57e0a61567fd5706719315bb22 |
| tree | bbfc7d3401a9fc9359eef9417572591fad493770 |
| parent | 203bbaee07dfd8e67ea102dbb29afb75be246e53 |
64 files changed, 2587 insertions(+), 2711 deletions(-)
build.zig+8-10| ... | @@ -793,7 +793,7 @@ pub fn build(b: *std.Build) !void { | ... | @@ -793,7 +793,7 @@ pub fn build(b: *std.Build) !void { |
| 793 | const runner = b.addExecutable(.{ | 793 | const runner = b.addExecutable(.{ |
| 794 | .name = "runner", | 794 | .name = "runner", |
| 795 | .root_module = b.createModule(.{ | 795 | .root_module = b.createModule(.{ |
| 796 | .root_source_file = b.path("test/src/Runner.zig"), | 796 | .root_source_file = b.path("test/Runner.zig"), |
| 797 | .target = b.graph.host, | 797 | .target = b.graph.host, |
| 798 | }), | 798 | }), |
| 799 | }); | 799 | }); |
| ... | @@ -813,17 +813,15 @@ pub fn build(b: *std.Build) !void { | ... | @@ -813,17 +813,15 @@ pub fn build(b: *std.Build) !void { |
| 813 | .lldb = b.option([]const u8, "lldb", "path to lldb binary"), | 813 | .lldb = b.option([]const u8, "lldb", "path to lldb binary"), |
| 814 | }; | 814 | }; |
| 815 | 815 | ||
| 816 | const test_incremental_step = b.step("test-incremental", "Run the incremental compilation tests"); | ||
| 817 | try tests.addRunnerTests(b, runner, test_incremental_step, "test/incremental", tests.incremental_matrix, runner_opts); | ||
| 818 | test_step.dependOn(test_incremental_step); | ||
| 819 | |||
| 820 | const test_debugger_step = b.step("test-debugger", "Run the debugger tests"); | 816 | const test_debugger_step = b.step("test-debugger", "Run the debugger tests"); |
| 821 | if (runner_opts.gdb == null and runner_opts.lldb == null) { | 817 | if (runner_opts.gdb != null or runner_opts.lldb != null) { |
| 822 | test_debugger_step.dependOn(&b.addFail("test-debugger requires -Dgdb and/or -Dlldb").step); | 818 | try tests.addRunnerTests(b, runner, test_debugger_step, "test/debugger", @import("test/debugger/matrix.zon"), runner_opts); |
| 823 | } else { | ||
| 824 | try tests.addRunnerTests(b, runner, test_debugger_step, "test/debugger", tests.debugger_matrix, runner_opts); | ||
| 825 | test_step.dependOn(test_debugger_step); | 819 | test_step.dependOn(test_debugger_step); |
| 826 | } | 820 | } else test_debugger_step.dependOn(&b.addFail("test-debugger requires -Dgdb and/or -Dlldb").step); |
| 821 | |||
| 822 | const test_incremental_step = b.step("test-incremental", "Run the incremental compilation tests"); | ||
| 823 | try tests.addRunnerTests(b, runner, test_incremental_step, "test/incremental", @import("test/incremental/matrix.zon"), runner_opts); | ||
| 824 | test_step.dependOn(test_incremental_step); | ||
| 827 | } | 825 | } |
| 828 | 826 | ||
| 829 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | 827 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { |
lib/compiler/Maker/Package/Manifest.zig+3-89| ... | @@ -494,95 +494,9 @@ const Parse = struct { | ... | @@ -494,95 +494,9 @@ const Parse = struct { |
| 494 | }; | 494 | }; |
| 495 | switch (result) { | 495 | switch (result) { |
| 496 | .success => {}, | 496 | .success => {}, |
| 497 | .failure => |err| try p.appendStrLitError(err, token, bytes, offset), | 497 | .failure => |err| try p.appendErrorOff(token, @intCast(offset + err.offset()), "{f}", .{ |
| 498 | } | 498 | err.fmt(bytes[offset..]), |
| 499 | } | 499 | }), |
| 500 | |||
| 501 | /// TODO: try to DRY this with AstGen.failWithStrLitError | ||
| 502 | fn appendStrLitError( | ||
| 503 | p: *Parse, | ||
| 504 | err: std.zig.string_literal.Error, | ||
| 505 | token: Ast.TokenIndex, | ||
| 506 | bytes: []const u8, | ||
| 507 | offset: u32, | ||
| 508 | ) Allocator.Error!void { | ||
| 509 | const raw_string = bytes[offset..]; | ||
| 510 | switch (err) { | ||
| 511 | .invalid_escape_character => |bad_index| { | ||
| 512 | try p.appendErrorOff( | ||
| 513 | token, | ||
| 514 | offset + @as(u32, @intCast(bad_index)), | ||
| 515 | "invalid escape character: '{c}'", | ||
| 516 | .{raw_string[bad_index]}, | ||
| 517 | ); | ||
| 518 | }, | ||
| 519 | .expected_hex_digit => |bad_index| { | ||
| 520 | try p.appendErrorOff( | ||
| 521 | token, | ||
| 522 | offset + @as(u32, @intCast(bad_index)), | ||
| 523 | "expected hex digit, found '{c}'", | ||
| 524 | .{raw_string[bad_index]}, | ||
| 525 | ); | ||
| 526 | }, | ||
| 527 | .empty_unicode_escape_sequence => |bad_index| { | ||
| 528 | try p.appendErrorOff( | ||
| 529 | token, | ||
| 530 | offset + @as(u32, @intCast(bad_index)), | ||
| 531 | "empty unicode escape sequence", | ||
| 532 | .{}, | ||
| 533 | ); | ||
| 534 | }, | ||
| 535 | .expected_hex_digit_or_rbrace => |bad_index| { | ||
| 536 | try p.appendErrorOff( | ||
| 537 | token, | ||
| 538 | offset + @as(u32, @intCast(bad_index)), | ||
| 539 | "expected hex digit or '}}', found '{c}'", | ||
| 540 | .{raw_string[bad_index]}, | ||
| 541 | ); | ||
| 542 | }, | ||
| 543 | .invalid_unicode_codepoint => |bad_index| { | ||
| 544 | try p.appendErrorOff( | ||
| 545 | token, | ||
| 546 | offset + @as(u32, @intCast(bad_index)), | ||
| 547 | "unicode escape does not correspond to a unicode code point", | ||
| 548 | .{}, | ||
| 549 | ); | ||
| 550 | }, | ||
| 551 | .expected_lbrace => |bad_index| { | ||
| 552 | try p.appendErrorOff( | ||
| 553 | token, | ||
| 554 | offset + @as(u32, @intCast(bad_index)), | ||
| 555 | "expected '{{', found '{c}", | ||
| 556 | .{raw_string[bad_index]}, | ||
| 557 | ); | ||
| 558 | }, | ||
| 559 | .expected_rbrace => |bad_index| { | ||
| 560 | try p.appendErrorOff( | ||
| 561 | token, | ||
| 562 | offset + @as(u32, @intCast(bad_index)), | ||
| 563 | "expected '}}', found '{c}", | ||
| 564 | .{raw_string[bad_index]}, | ||
| 565 | ); | ||
| 566 | }, | ||
| 567 | .expected_single_quote => |bad_index| { | ||
| 568 | try p.appendErrorOff( | ||
| 569 | token, | ||
| 570 | offset + @as(u32, @intCast(bad_index)), | ||
| 571 | "expected single quote ('), found '{c}", | ||
| 572 | .{raw_string[bad_index]}, | ||
| 573 | ); | ||
| 574 | }, | ||
| 575 | .invalid_character => |bad_index| { | ||
| 576 | try p.appendErrorOff( | ||
| 577 | token, | ||
| 578 | offset + @as(u32, @intCast(bad_index)), | ||
| 579 | "invalid byte in string or character literal: '{c}'", | ||
| 580 | .{raw_string[bad_index]}, | ||
| 581 | ); | ||
| 582 | }, | ||
| 583 | .empty_char_literal => { | ||
| 584 | try p.appendErrorOff(token, offset, "empty character literal", .{}); | ||
| 585 | }, | ||
| 586 | } | 500 | } |
| 587 | } | 501 | } |
| 588 | 502 |
lib/std/zig/string_literal.zig+17-1| ... | @@ -38,6 +38,11 @@ pub const Error = union(enum) { | ... | @@ -38,6 +38,11 @@ pub const Error = union(enum) { |
| 38 | invalid_character: usize, | 38 | invalid_character: usize, |
| 39 | /// `''`. Not returned for string literals. | 39 | /// `''`. Not returned for string literals. |
| 40 | empty_char_literal, | 40 | empty_char_literal, |
| 41 | /// The input ended before the end of the literal. | ||
| 42 | end_of_input: usize, | ||
| 43 | /// The literal ended before the end of the input. There was an otherwise successful | ||
| 44 | /// parse of a prefix of the input of length given by this payload written to the writer. | ||
| 45 | end_of_literal: usize, | ||
| 41 | 46 | ||
| 42 | const FormatMessage = struct { | 47 | const FormatMessage = struct { |
| 43 | err: Error, | 48 | err: Error, |
| ... | @@ -83,6 +88,12 @@ pub const Error = union(enum) { | ... | @@ -83,6 +88,12 @@ pub const Error = union(enum) { |
| 83 | .empty_char_literal => try writer.writeAll( | 88 | .empty_char_literal => try writer.writeAll( |
| 84 | "empty character literal", | 89 | "empty character literal", |
| 85 | ), | 90 | ), |
| 91 | .end_of_input => try writer.writeAll( | ||
| 92 | "expected end of string literal", | ||
| 93 | ), | ||
| 94 | .end_of_literal => try writer.writeAll( | ||
| 95 | "unexpected end of string literal", | ||
| 96 | ), | ||
| 86 | } | 97 | } |
| 87 | } | 98 | } |
| 88 | 99 | ||
| ... | @@ -104,6 +115,8 @@ pub const Error = union(enum) { | ... | @@ -104,6 +115,8 @@ pub const Error = union(enum) { |
| 104 | .expected_rbrace, | 115 | .expected_rbrace, |
| 105 | .expected_single_quote, | 116 | .expected_single_quote, |
| 106 | .invalid_character, | 117 | .invalid_character, |
| 118 | .end_of_input, | ||
| 119 | .end_of_literal, | ||
| 107 | => |n| n, | 120 | => |n| n, |
| 108 | .empty_char_literal => 0, | 121 | .empty_char_literal => 0, |
| 109 | }; | 122 | }; |
| ... | @@ -339,13 +352,16 @@ pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result { | ... | @@ -339,13 +352,16 @@ pub fn parseWrite(writer: *Writer, bytes: []const u8) Writer.Error!Result { |
| 339 | try writer.writeAll(buf[0..len]); | 352 | try writer.writeAll(buf[0..len]); |
| 340 | } else { | 353 | } else { |
| 341 | try writer.writeByte(@as(u8, @intCast(codepoint))); | 354 | try writer.writeByte(@as(u8, @intCast(codepoint))); |
| 355 | if (index == bytes.len) return .{ .failure = .{ .end_of_input = index } }; | ||
| 342 | } | 356 | } |
| 343 | }, | 357 | }, |
| 344 | .failure => |err| return .{ .failure = err }, | 358 | .failure => |err| return .{ .failure = err }, |
| 345 | } | 359 | } |
| 346 | }, | 360 | }, |
| 347 | '\n' => return .{ .failure = .{ .invalid_character = index } }, | 361 | '\n' => return .{ .failure = .{ .invalid_character = index } }, |
| 348 | '"' => return .success, | 362 | '"' => return if (index < bytes.len - 1) .{ |
| 363 | .failure = .{ .end_of_literal = index }, | ||
| 364 | } else .success, | ||
| 349 | else => { | 365 | else => { |
| 350 | try writer.writeByte(b); | 366 | try writer.writeByte(b); |
| 351 | index += 1; | 367 | index += 1; |
test/Runner.zig created+2240| ... | @@ -0,0 +1,2240 @@ | ||
| 1 | const assert = std.debug.assert; | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const std = @import("std"); | ||
| 4 | const Runner = @This(); | ||
| 5 | |||
| 6 | gpa: std.mem.Allocator, | ||
| 7 | arena: *std.heap.ArenaAllocator, | ||
| 8 | environ_map: std.process.Environ.Map, | ||
| 9 | io: std.Io, | ||
| 10 | prog_node: std.Progress.Node, | ||
| 11 | args: Args, | ||
| 12 | host: std.Target, | ||
| 13 | server: std.zig.Server, | ||
| 14 | eb_wip: std.zig.ErrorBundle.Wip, | ||
| 15 | |||
| 16 | const Args = struct { | ||
| 17 | @"test": ?struct { | ||
| 18 | input_dir: std.zig.Server.Message.InputDir, | ||
| 19 | dir: std.Io.Dir, | ||
| 20 | }, | ||
| 21 | manifest_file: ?std.Io.File, | ||
| 22 | zig_exe: ?std.Io.File, | ||
| 23 | lib_dir: ?std.Io.Dir, | ||
| 24 | src_dir: ?std.Io.Dir, | ||
| 25 | targets: std.ArrayList(u32), | ||
| 26 | target_bytes: std.ArrayList(u8), | ||
| 27 | |||
| 28 | libc_runtimes_dir: ?std.Io.Dir, | ||
| 29 | enable_darling: bool, | ||
| 30 | enable_qemu: bool, | ||
| 31 | enable_rosetta: bool, | ||
| 32 | enable_wasmtime: bool, | ||
| 33 | enable_wine: bool, | ||
| 34 | |||
| 35 | gdb_exe: ?std.Io.File, | ||
| 36 | lldb_exe: ?std.Io.File, | ||
| 37 | |||
| 38 | quiet: bool, | ||
| 39 | |||
| 40 | fn deinit(args: *Args, gpa: std.mem.Allocator) void { | ||
| 41 | args.targets.deinit(gpa); | ||
| 42 | args.target_bytes.deinit(gpa); | ||
| 43 | args.* = undefined; | ||
| 44 | } | ||
| 45 | |||
| 46 | fn addTarget(args: *Args, gpa: std.mem.Allocator, target: []const u8) std.mem.Allocator.Error!void { | ||
| 47 | try args.target_bytes.ensureUnusedCapacity(gpa, target.len + 1); | ||
| 48 | try args.targets.append(gpa, @intCast(args.target_bytes.items.len)); | ||
| 49 | args.target_bytes.appendSliceAssumeCapacity(target); | ||
| 50 | args.target_bytes.appendAssumeCapacity(0); | ||
| 51 | } | ||
| 52 | }; | ||
| 53 | |||
| 54 | fn deinit(runner: *Runner) void { | ||
| 55 | runner.environ_map.deinit(); | ||
| 56 | runner.prog_node.end(); | ||
| 57 | runner.args.deinit(runner.gpa); | ||
| 58 | runner.eb_wip.deinit(); | ||
| 59 | runner.* = undefined; | ||
| 60 | } | ||
| 61 | |||
| 62 | const FailError = error{AlreadyReported} || std.mem.Allocator.Error || std.Io.Cancelable; | ||
| 63 | fn fail(runner: *Runner, comptime fmt: []const u8, args: anytype) FailError { | ||
| 64 | return runner.failString(try runner.eb_wip.printString(fmt, args)); | ||
| 65 | } | ||
| 66 | fn failString(runner: *Runner, msg: std.zig.ErrorBundle.String) FailError { | ||
| 67 | try runner.eb_wip.addRootErrorMessage(.{ .msg = msg }); | ||
| 68 | return error.AlreadyReported; | ||
| 69 | } | ||
| 70 | |||
| 71 | pub const ProtocolError = error{ | ||
| 72 | ServerReadFailed, | ||
| 73 | ServerWriteFailed, | ||
| 74 | ServerEndOfStream, | ||
| 75 | } || FailError; | ||
| 76 | pub fn runServer(runner: *Runner) ProtocolError { | ||
| 77 | runner.server.serveStringMessage(.zig_version, builtin.zig_version_string) catch |err| switch (err) { | ||
| 78 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 79 | }; | ||
| 80 | while (true) { | ||
| 81 | _ = runner.arena.reset(.retain_capacity); | ||
| 82 | const arena = runner.arena.allocator(); | ||
| 83 | const hdr = runner.server.receiveMessage() catch |err| switch (err) { | ||
| 84 | error.ReadFailed => return error.ServerReadFailed, | ||
| 85 | error.EndOfStream => return error.ServerEndOfStream, | ||
| 86 | }; | ||
| 87 | switch (hdr.tag) { | ||
| 88 | else => |tag| return runner.fail("unsupported message: {t}", .{tag}), | ||
| 89 | .exit => std.process.exit(0), | ||
| 90 | .args => { | ||
| 91 | const args_body = try arena.alloc(u8, hdr.bytes_len); | ||
| 92 | runner.server.in.readSliceAll(args_body) catch unreachable; | ||
| 93 | const State = enum { | ||
| 94 | positional, | ||
| 95 | @"--zig", | ||
| 96 | @"--lib", | ||
| 97 | @"--src", | ||
| 98 | @"--target", | ||
| 99 | @"--libc-runtimes", | ||
| 100 | @"--gdb", | ||
| 101 | @"--lldb", | ||
| 102 | }; | ||
| 103 | var state: State = .positional; | ||
| 104 | const state_expected: std.enums.EnumArray( | ||
| 105 | State, | ||
| 106 | enum { path, dir, file, string }, | ||
| 107 | ) = .init(.{ | ||
| 108 | .positional = .path, | ||
| 109 | .@"--zig" = .file, | ||
| 110 | .@"--lib" = .dir, | ||
| 111 | .@"--src" = .dir, | ||
| 112 | .@"--target" = .string, | ||
| 113 | .@"--libc-runtimes" = .dir, | ||
| 114 | .@"--gdb" = .file, | ||
| 115 | .@"--lldb" = .file, | ||
| 116 | }); | ||
| 117 | var input_dir: std.zig.Server.Message.InputDir = .cwd; | ||
| 118 | var args_body_offset: usize = 0; | ||
| 119 | while (args_body.len - args_body_offset > 0) { | ||
| 120 | const arg: std.zig.Client.Message.Arg = | ||
| 121 | @fromBackingInt(args_body[args_body_offset]); | ||
| 122 | args_body_offset += 1; | ||
| 123 | arg: switch (arg) { | ||
| 124 | .string => { | ||
| 125 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 126 | const string = args_body[args_body_offset..end]; | ||
| 127 | args_body_offset = end + 1; | ||
| 128 | switch (state) { | ||
| 129 | .positional => if (std.mem.eql(u8, string, "--target")) { | ||
| 130 | state = .@"--target"; | ||
| 131 | continue; | ||
| 132 | } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { | ||
| 133 | try runner.args.addTarget(runner.gpa, target); | ||
| 134 | } else if (std.mem.eql(u8, string, "--libc-runtimes")) { | ||
| 135 | state = .@"--libc-runtimes"; | ||
| 136 | continue; | ||
| 137 | } else if (std.mem.eql(u8, string, "-fdarling")) { | ||
| 138 | runner.args.enable_darling = true; | ||
| 139 | } else if (std.mem.eql(u8, string, "-fqemu")) { | ||
| 140 | runner.args.enable_qemu = true; | ||
| 141 | } else if (std.mem.eql(u8, string, "-frosetta")) { | ||
| 142 | runner.args.enable_rosetta = true; | ||
| 143 | } else if (std.mem.eql(u8, string, "-fwasmtime")) { | ||
| 144 | runner.args.enable_wasmtime = true; | ||
| 145 | } else if (std.mem.eql(u8, string, "-fwine")) { | ||
| 146 | runner.args.enable_wine = true; | ||
| 147 | } else if (std.mem.eql(u8, string, "--gdb")) { | ||
| 148 | state = .@"--gdb"; | ||
| 149 | continue; | ||
| 150 | } else if (std.mem.eql(u8, string, "--lldb")) { | ||
| 151 | state = .@"--lldb"; | ||
| 152 | continue; | ||
| 153 | } else if (std.mem.eql(u8, string, "--quiet")) { | ||
| 154 | runner.args.quiet = true; | ||
| 155 | }, | ||
| 156 | .@"--target" => try runner.args.addTarget(runner.gpa, string), | ||
| 157 | else => return runner.fail( | ||
| 158 | "\"{t}\" expected {t}, got {t}", | ||
| 159 | .{ state, state_expected.get(state), arg }, | ||
| 160 | ), | ||
| 161 | } | ||
| 162 | }, | ||
| 163 | .prefix => { | ||
| 164 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 165 | const string = args_body[args_body_offset..end]; | ||
| 166 | args_body_offset = end + 1; | ||
| 167 | assert(state == .positional); | ||
| 168 | state = if (std.mem.eql(u8, string, "--zig=")) | ||
| 169 | .@"--zig" | ||
| 170 | else if (std.mem.eql(u8, string, "--lib=")) | ||
| 171 | .@"--lib" | ||
| 172 | else if (std.mem.eql(u8, string, "--src=")) | ||
| 173 | .@"--src" | ||
| 174 | else if (std.mem.eql(u8, string, "--libc-runtimes=")) | ||
| 175 | .@"--libc-runtimes" | ||
| 176 | else if (std.mem.eql(u8, string, "--gdb=")) | ||
| 177 | .@"--gdb" | ||
| 178 | else if (std.mem.eql(u8, string, "--lldb=")) | ||
| 179 | .@"--lldb" | ||
| 180 | else | ||
| 181 | return runner.fail("unsupported arg prefix: {q}", .{string}); | ||
| 182 | continue; | ||
| 183 | }, | ||
| 184 | .suffix => { | ||
| 185 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 186 | const string = args_body[args_body_offset..end]; | ||
| 187 | args_body_offset = end + 1; | ||
| 188 | return runner.fail("unsupported arg suffix: {q}", .{string}); | ||
| 189 | }, | ||
| 190 | .input_dir => { | ||
| 191 | input_dir = @fromBackingInt(@backingInt(input_dir) + 1); | ||
| 192 | continue :arg .output_dir; | ||
| 193 | }, | ||
| 194 | .output_dir => { | ||
| 195 | const dir_handle: *align(1) const std.Io.Dir.Handle = @ptrCast( | ||
| 196 | args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)], | ||
| 197 | ); | ||
| 198 | args_body_offset += @sizeOf(std.Io.Dir.Handle); | ||
| 199 | const dir = runner.io.vtable.inheritParentDir( | ||
| 200 | runner.io.userdata, | ||
| 201 | dir_handle.*, | ||
| 202 | ) catch |err| switch (err) { | ||
| 203 | error.Canceled => |e| return e, | ||
| 204 | else => |e| return runner.fail( | ||
| 205 | "unable to inherit parent dir: {t}", | ||
| 206 | .{e}, | ||
| 207 | ), | ||
| 208 | }; | ||
| 209 | switch (state) { | ||
| 210 | .positional => { | ||
| 211 | if (runner.args.@"test" != null or | ||
| 212 | runner.args.manifest_file != null) return runner.fail( | ||
| 213 | "{t} specified multiple times", | ||
| 214 | .{state}, | ||
| 215 | ); | ||
| 216 | runner.args.@"test" = .{ .input_dir = input_dir, .dir = dir }; | ||
| 217 | const manifest_path = "manifest"; | ||
| 218 | runner.args.manifest_file = dir.openFile( | ||
| 219 | runner.io, | ||
| 220 | manifest_path, | ||
| 221 | .{}, | ||
| 222 | ) catch |err| switch (err) { | ||
| 223 | error.Canceled => |e| return e, | ||
| 224 | else => |e| return runner.fail( | ||
| 225 | "unable to open manifest file: {t}", | ||
| 226 | .{e}, | ||
| 227 | ), | ||
| 228 | }; | ||
| 229 | try runner.discoverInput(input_dir, manifest_path); | ||
| 230 | }, | ||
| 231 | .@"--lib" => { | ||
| 232 | if (runner.args.lib_dir != null) return runner.fail( | ||
| 233 | "\"{t}\" specified multiple times", | ||
| 234 | .{state}, | ||
| 235 | ); | ||
| 236 | runner.args.lib_dir = dir; | ||
| 237 | }, | ||
| 238 | .@"--src" => { | ||
| 239 | if (runner.args.src_dir != null) return runner.fail( | ||
| 240 | "\"{t}\" specified multiple times", | ||
| 241 | .{state}, | ||
| 242 | ); | ||
| 243 | runner.args.src_dir = dir; | ||
| 244 | }, | ||
| 245 | .@"--libc-runtimes" => { | ||
| 246 | if (runner.args.libc_runtimes_dir != null) return runner.fail( | ||
| 247 | "\"{t}\" specified multiple times", | ||
| 248 | .{state}, | ||
| 249 | ); | ||
| 250 | runner.args.libc_runtimes_dir = dir; | ||
| 251 | }, | ||
| 252 | else => return runner.fail( | ||
| 253 | "\"{t}\" expected {t}, got {t}", | ||
| 254 | .{ state, state_expected.get(state), arg }, | ||
| 255 | ), | ||
| 256 | } | ||
| 257 | }, | ||
| 258 | .input_file, .input_file_content, .output_file => { | ||
| 259 | const file_handle: *align(1) const std.Io.File.Handle = @ptrCast( | ||
| 260 | args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)], | ||
| 261 | ); | ||
| 262 | args_body_offset += @sizeOf(std.Io.File.Handle); | ||
| 263 | const file = runner.io.vtable.inheritParentFile( | ||
| 264 | runner.io.userdata, | ||
| 265 | file_handle.*, | ||
| 266 | .{ .nonblocking = false }, | ||
| 267 | ) catch |err| switch (err) { | ||
| 268 | error.Canceled => |e| return e, | ||
| 269 | else => |e| return runner.fail( | ||
| 270 | "unable to inherit parent file: {t}", | ||
| 271 | .{e}, | ||
| 272 | ), | ||
| 273 | }; | ||
| 274 | switch (state) { | ||
| 275 | .positional => { | ||
| 276 | if (runner.args.@"test" != null or | ||
| 277 | runner.args.manifest_file != null) return runner.fail( | ||
| 278 | "{t} specified multiple times", | ||
| 279 | .{state}, | ||
| 280 | ); | ||
| 281 | runner.args.manifest_file = file; | ||
| 282 | }, | ||
| 283 | .@"--zig" => { | ||
| 284 | if (runner.args.zig_exe != null) return runner.fail( | ||
| 285 | "\"{t}\" specified multiple times", | ||
| 286 | .{state}, | ||
| 287 | ); | ||
| 288 | runner.args.zig_exe = file; | ||
| 289 | }, | ||
| 290 | .@"--gdb" => { | ||
| 291 | if (runner.args.gdb_exe != null) return runner.fail( | ||
| 292 | "\"{t}\" specified multiple times", | ||
| 293 | .{state}, | ||
| 294 | ); | ||
| 295 | runner.args.gdb_exe = file; | ||
| 296 | }, | ||
| 297 | .@"--lldb" => { | ||
| 298 | if (runner.args.lldb_exe != null) return runner.fail( | ||
| 299 | "\"{t}\" specified multiple times", | ||
| 300 | .{state}, | ||
| 301 | ); | ||
| 302 | runner.args.lldb_exe = file; | ||
| 303 | }, | ||
| 304 | else => return runner.fail( | ||
| 305 | "\"{t}\" expected {t}, got {t}", | ||
| 306 | .{ state, state_expected.get(state), arg }, | ||
| 307 | ), | ||
| 308 | } | ||
| 309 | }, | ||
| 310 | } | ||
| 311 | state = .positional; | ||
| 312 | } | ||
| 313 | }, | ||
| 314 | .query_test_metadata => { | ||
| 315 | const expected_panic_msgs = try arena.alloc(u32, runner.args.targets.items.len); | ||
| 316 | @memset(expected_panic_msgs, 0); | ||
| 317 | runner.server.serveTestMetadata(.{ | ||
| 318 | .names = runner.args.targets.items, | ||
| 319 | .expected_panic_msgs = expected_panic_msgs, | ||
| 320 | .string_bytes = runner.args.target_bytes.items, | ||
| 321 | }) catch |err| switch (err) { | ||
| 322 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 323 | }; | ||
| 324 | }, | ||
| 325 | .run_test => { | ||
| 326 | const test_index = runner.server.receiveBody_u32() catch unreachable; | ||
| 327 | runner.server.serveBodylessMessage(.test_started) catch |err| switch (err) { | ||
| 328 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 329 | }; | ||
| 330 | runner.server.serveTestResults(.{ | ||
| 331 | .index = test_index, | ||
| 332 | .flags = .{ | ||
| 333 | .status = if (runner.testOne(test_index)) | ||
| 334 | .pass | ||
| 335 | else |err| status: switch (err) { | ||
| 336 | error.Canceled, | ||
| 337 | error.OutOfMemory, | ||
| 338 | error.ServerWriteFailed, | ||
| 339 | => |e| return e, | ||
| 340 | error.AlreadyReported => { | ||
| 341 | var eb = try runner.eb_wip.toOwnedBundle(""); | ||
| 342 | defer eb.deinit(runner.gpa); | ||
| 343 | assert(eb.errorMessageCount() > 0); // already reported what? | ||
| 344 | runner.server.serveErrorBundle( | ||
| 345 | .error_bundle, | ||
| 346 | eb, | ||
| 347 | ) catch |server_err| switch (server_err) { | ||
| 348 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 349 | }; | ||
| 350 | const eb_wip: std.zig.ErrorBundle.Wip = try .init(runner.gpa); | ||
| 351 | runner.eb_wip.deinit(); | ||
| 352 | runner.eb_wip = eb_wip; | ||
| 353 | break :status .fail; | ||
| 354 | }, | ||
| 355 | error.SkipTest => .skip, | ||
| 356 | }, | ||
| 357 | .fuzz = false, | ||
| 358 | .log_err_count = 0, | ||
| 359 | .leak_count = 0, | ||
| 360 | }, | ||
| 361 | }) catch |err| switch (err) { | ||
| 362 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 363 | }; | ||
| 364 | assert(runner.eb_wip.root_list.items.len == 0); // failed to report | ||
| 365 | }, | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | |||
| 370 | fn discoverInput( | ||
| 371 | runner: *Runner, | ||
| 372 | dir: std.zig.Server.Message.InputDir, | ||
| 373 | sub_path: []const u8, | ||
| 374 | ) error{ServerWriteFailed}!void { | ||
| 375 | return runner.discoverInputInner(dir, sub_path) catch |err| switch (err) { | ||
| 376 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 377 | }; | ||
| 378 | } | ||
| 379 | fn discoverInputInner( | ||
| 380 | runner: *Runner, | ||
| 381 | dir: std.zig.Server.Message.InputDir, | ||
| 382 | sub_path: []const u8, | ||
| 383 | ) std.Io.Writer.Error!void { | ||
| 384 | try runner.server.serveMessageHeader(.{ | ||
| 385 | .tag = .discovered_inputs, | ||
| 386 | .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + | ||
| 387 | sub_path.len + 1), | ||
| 388 | }); | ||
| 389 | try runner.server.out.writeInt(u32, @backingInt(dir), .little); | ||
| 390 | try runner.server.out.writeAll(sub_path); | ||
| 391 | try runner.server.out.writeByte(0); | ||
| 392 | try runner.server.out.flush(); | ||
| 393 | } | ||
| 394 | |||
| 395 | pub const SourceReader = struct { | ||
| 396 | const Io = std.Io; | ||
| 397 | |||
| 398 | backing: *std.Io.Reader, | ||
| 399 | line_aw: std.Io.Writer.Allocating, | ||
| 400 | interface: std.Io.Reader, | ||
| 401 | line: u32, | ||
| 402 | eb_wip: *std.zig.ErrorBundle.Wip, | ||
| 403 | src_path: std.zig.ErrorBundle.String, | ||
| 404 | |||
| 405 | pub fn init( | ||
| 406 | reader: *Io.Reader, | ||
| 407 | gpa: std.mem.Allocator, | ||
| 408 | eb_wip: *std.zig.ErrorBundle.Wip, | ||
| 409 | src_path: std.zig.ErrorBundle.String, | ||
| 410 | ) SourceReader { | ||
| 411 | return .{ | ||
| 412 | .backing = reader, | ||
| 413 | .line_aw = .init(gpa), | ||
| 414 | .interface = .{ | ||
| 415 | .vtable = &.{ | ||
| 416 | .stream = stream, | ||
| 417 | .discard = discard, | ||
| 418 | .readVec = readVec, | ||
| 419 | .rebase = rebase, | ||
| 420 | }, | ||
| 421 | .buffer = &.{}, | ||
| 422 | .seek = 0, | ||
| 423 | .end = 0, | ||
| 424 | }, | ||
| 425 | .line = 0, | ||
| 426 | .eb_wip = eb_wip, | ||
| 427 | .src_path = src_path, | ||
| 428 | }; | ||
| 429 | } | ||
| 430 | fn deinit(s: *SourceReader) void { | ||
| 431 | s.line_aw.deinit(); | ||
| 432 | } | ||
| 433 | fn stream(r: *Io.Reader, _: *Io.Writer, _: Io.Limit) Io.Reader.StreamError!usize { | ||
| 434 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 435 | try s.bufferLine(); | ||
| 436 | return 0; | ||
| 437 | } | ||
| 438 | fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { | ||
| 439 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 440 | const remaining = r.buffered(); | ||
| 441 | if (remaining.len > 0) { | ||
| 442 | const n = limit.minInt(remaining.len); | ||
| 443 | r.toss(n); | ||
| 444 | return n; | ||
| 445 | } | ||
| 446 | try s.bufferLine(); | ||
| 447 | return 0; | ||
| 448 | } | ||
| 449 | fn readVec(r: *Io.Reader, _: [][]u8) Io.Reader.Error!usize { | ||
| 450 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 451 | try s.bufferLine(); | ||
| 452 | return 0; | ||
| 453 | } | ||
| 454 | fn rebase(r: *Io.Reader, capacity: usize) Io.Reader.RebaseError!void { | ||
| 455 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 456 | defer s.updateBuffer(); | ||
| 457 | s.line_aw.ensureUnusedCapacity(capacity) catch |err| switch (err) { | ||
| 458 | error.OutOfMemory => return error.ReadFailed, | ||
| 459 | }; | ||
| 460 | } | ||
| 461 | fn updateBuffer(s: *SourceReader) void { | ||
| 462 | s.interface.buffer = s.line_aw.writer.buffer; | ||
| 463 | s.interface.end = s.line_aw.writer.end; | ||
| 464 | } | ||
| 465 | fn bufferLine(s: *SourceReader) Io.Reader.Error!void { | ||
| 466 | defer s.updateBuffer(); | ||
| 467 | { | ||
| 468 | const written = s.line_aw.written(); | ||
| 469 | const start = if (std.mem.findScalarLast(u8, written[0..s.interface.seek], '\n')) |newline| | ||
| 470 | newline + 1 | ||
| 471 | else | ||
| 472 | 0; | ||
| 473 | s.line += @intCast(std.mem.countScalar(u8, written[0..start], '\n')); | ||
| 474 | const preserve = written[start..]; | ||
| 475 | @memmove(written[0..preserve.len], preserve); | ||
| 476 | s.line_aw.shrinkRetainingCapacity(preserve.len); | ||
| 477 | s.interface.seek -= start; | ||
| 478 | } | ||
| 479 | const n = s.backing.streamDelimiterEnding(&s.line_aw.writer, '\n') catch |err| switch (err) { | ||
| 480 | error.ReadFailed => |e| return e, | ||
| 481 | error.WriteFailed => return error.ReadFailed, | ||
| 482 | }; | ||
| 483 | if (std.mem.endsWith(u8, s.line_aw.written(), "\r")) s.line_aw.writer.end -= 1; | ||
| 484 | const buffered = s.backing.buffered(); | ||
| 485 | if (n == 0 and buffered.len == 0) return error.EndOfStream; | ||
| 486 | if (std.mem.startsWith(u8, buffered, "\n")) { | ||
| 487 | s.line_aw.writer.writeByte('\n') catch |err| switch (err) { | ||
| 488 | error.WriteFailed => return error.ReadFailed, | ||
| 489 | }; | ||
| 490 | s.backing.toss(1); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | fn fail( | ||
| 494 | s: *SourceReader, | ||
| 495 | src_loc: []const u8, | ||
| 496 | comptime fmt: []const u8, | ||
| 497 | args: anytype, | ||
| 498 | ) FailError { | ||
| 499 | return s.failString(src_loc, try s.eb_wip.printString(fmt, args)); | ||
| 500 | } | ||
| 501 | fn failString( | ||
| 502 | s: *SourceReader, | ||
| 503 | src_loc: []const u8, | ||
| 504 | msg: std.zig.ErrorBundle.String, | ||
| 505 | ) FailError { | ||
| 506 | try s.eb_wip.addRootErrorMessage(.{ .msg = msg, .src_loc = try s.srcLoc(src_loc) }); | ||
| 507 | return error.AlreadyReported; | ||
| 508 | } | ||
| 509 | fn srcLoc( | ||
| 510 | s: *SourceReader, | ||
| 511 | src_loc: []const u8, | ||
| 512 | ) std.mem.Allocator.Error!std.zig.ErrorBundle.SourceLocationIndex { | ||
| 513 | const line: u32, const column: u32, const source_line = src_loc: { | ||
| 514 | const buffer = s.interface.buffer[0..s.interface.end]; | ||
| 515 | const pos = src_loc.ptr - buffer.ptr; | ||
| 516 | const end = std.mem.findScalarPos(u8, buffer, pos, '\n') orelse buffer.len; | ||
| 517 | const start = | ||
| 518 | if (std.mem.findScalarLast(u8, buffer[0..end], '\n')) |newline| newline + 1 else 0; | ||
| 519 | break :src_loc .{ | ||
| 520 | @intCast(s.line + std.mem.countScalar(u8, buffer[0..start], '\n')), | ||
| 521 | @intCast(pos - start), | ||
| 522 | buffer[start..end], | ||
| 523 | }; | ||
| 524 | }; | ||
| 525 | return s.eb_wip.addSourceLocation(.{ | ||
| 526 | .src_path = s.src_path, | ||
| 527 | .line = line, | ||
| 528 | .column = column, | ||
| 529 | .span_start = column, | ||
| 530 | .span_main = column, | ||
| 531 | .span_end = @intCast(column + src_loc.len), | ||
| 532 | .source_line = try s.eb_wip.addString(source_line), | ||
| 533 | }); | ||
| 534 | } | ||
| 535 | }; | ||
| 536 | pub const DelimitedReader = struct { | ||
| 537 | const Io = std.Io; | ||
| 538 | |||
| 539 | unlimited: *Io.Reader, | ||
| 540 | remaining: Io.Limit, | ||
| 541 | delimiter: []const u8, | ||
| 542 | interface: Io.Reader, | ||
| 543 | |||
| 544 | pub fn init(reader: *Io.Reader, delimiter: []const u8, buffer: []u8) DelimitedReader { | ||
| 545 | return .{ | ||
| 546 | .unlimited = reader, | ||
| 547 | .remaining = .nothing, | ||
| 548 | .delimiter = delimiter, | ||
| 549 | .interface = .{ | ||
| 550 | .vtable = &.{ | ||
| 551 | .stream = stream, | ||
| 552 | .discard = discard, | ||
| 553 | }, | ||
| 554 | .buffer = buffer, | ||
| 555 | .seek = 0, | ||
| 556 | .end = 0, | ||
| 557 | }, | ||
| 558 | }; | ||
| 559 | } | ||
| 560 | fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { | ||
| 561 | const d: *DelimitedReader = @fieldParentPtr("interface", r); | ||
| 562 | const block = try d.unlimited.peekGreedy(d.delimiter.len); | ||
| 563 | d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse | ||
| 564 | block.len - d.delimiter.len + 1); | ||
| 565 | if (d.remaining == .nothing) return error.EndOfStream; | ||
| 566 | const n = try d.unlimited.stream(w, limit.min(d.remaining)); | ||
| 567 | d.remaining = d.remaining.subtract(n).?; | ||
| 568 | return n; | ||
| 569 | } | ||
| 570 | fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { | ||
| 571 | const d: *DelimitedReader = @fieldParentPtr("interface", r); | ||
| 572 | const block = try d.unlimited.peekGreedy(d.delimiter.len); | ||
| 573 | d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse | ||
| 574 | block.len - d.delimiter.len + 1); | ||
| 575 | if (d.remaining == .nothing) return error.EndOfStream; | ||
| 576 | const n = try d.unlimited.discard(limit.min(d.remaining)); | ||
| 577 | d.remaining = d.remaining.subtract(n).?; | ||
| 578 | return n; | ||
| 579 | } | ||
| 580 | }; | ||
| 581 | |||
| 582 | const Update = struct { | ||
| 583 | target_query: []const u8, | ||
| 584 | manifest_sr: SourceReader, | ||
| 585 | compiler: ?Compiler, | ||
| 586 | src_dir: std.Io.Dir, | ||
| 587 | eb_wip: *std.zig.ErrorBundle.Wip, | ||
| 588 | cmd_src_loc: std.zig.ErrorBundle.SourceLocationIndex, | ||
| 589 | src_loc: ?std.zig.ErrorBundle.SourceLocationIndex, | ||
| 590 | num: usize, | ||
| 591 | name: []const u8, | ||
| 592 | prog_node: std.Progress.Node, | ||
| 593 | mtime: std.Io.Timestamp, | ||
| 594 | allow_skip: bool, | ||
| 595 | |||
| 596 | fn checkMatches( | ||
| 597 | update: *const Update, | ||
| 598 | what: []const u8, | ||
| 599 | expected: []const u8, | ||
| 600 | actual: []const u8, | ||
| 601 | ) FailError!void { | ||
| 602 | var expected_match_it = std.mem.splitSequence(u8, expected, "#..."); | ||
| 603 | var pos: usize = 0; | ||
| 604 | while (expected_match_it.next()) |expected_match| : (pos += expected_match.len) { | ||
| 605 | var context_len: usize = expected_match.len; | ||
| 606 | pos = while (true) : (context_len -= 1) break std.mem.findPos( | ||
| 607 | u8, | ||
| 608 | actual, | ||
| 609 | pos, | ||
| 610 | expected_match[0..context_len], | ||
| 611 | ) orelse continue; | ||
| 612 | const actual_match = actual[pos..]; | ||
| 613 | std.testing.expectEqualStrings( | ||
| 614 | expected_match, | ||
| 615 | actual_match[0..@min(actual_match.len, expected_match.len)], | ||
| 616 | ) catch |err| switch (err) { | ||
| 617 | error.TestExpectedEqual => { | ||
| 618 | std.testing.failPrint("\n=============== full output: =========\n", .{}); | ||
| 619 | std.testing.failPrintWithVisibleNewlines(actual_match); | ||
| 620 | std.testing.failPrint("\n======================================\n", .{}); | ||
| 621 | return update.fail("{s} did not match expected", .{what}); | ||
| 622 | }, | ||
| 623 | }; | ||
| 624 | } | ||
| 625 | } | ||
| 626 | |||
| 627 | fn fail(update: *const Update, comptime fmt: []const u8, args: anytype) FailError { | ||
| 628 | return update.failString(try update.eb_wip.printString(fmt, args)); | ||
| 629 | } | ||
| 630 | fn failString(update: *const Update, msg: std.zig.ErrorBundle.String) FailError { | ||
| 631 | var notes_buffer: [1]std.zig.ErrorBundle.ErrorMessage = undefined; | ||
| 632 | var notes_len: u32 = 0; | ||
| 633 | if (update.src_loc) |src_loc| { | ||
| 634 | notes_buffer[notes_len] = .{ | ||
| 635 | .msg = try update.eb_wip.addString("after this update"), | ||
| 636 | .src_loc = src_loc, | ||
| 637 | }; | ||
| 638 | notes_len += 1; | ||
| 639 | } | ||
| 640 | try update.eb_wip.addRootErrorMessageWithNotes(.{ | ||
| 641 | .msg = msg, | ||
| 642 | .src_loc = update.cmd_src_loc, | ||
| 643 | .notes_len = 1, | ||
| 644 | }, notes_buffer[0..notes_len]); | ||
| 645 | return error.AlreadyReported; | ||
| 646 | } | ||
| 647 | }; | ||
| 648 | |||
| 649 | const TestError = error{ | ||
| 650 | SkipTest, | ||
| 651 | } || FailError; | ||
| 652 | fn testOne(runner: *Runner, test_index: u32) (error{ServerWriteFailed} || TestError)!void { | ||
| 653 | const target_query = | ||
| 654 | std.mem.sliceTo(runner.args.target_bytes.items[runner.args.targets.items[test_index]..], 0); | ||
| 655 | const manifest_file = runner.args.manifest_file orelse | ||
| 656 | return runner.fail("missing \"path/to/test\" arg", .{}); | ||
| 657 | var manifest_buffer: [512]u8 = undefined; | ||
| 658 | var manifest_fr = manifest_file.reader(runner.io, &manifest_buffer); | ||
| 659 | var update: Update = .{ | ||
| 660 | .target_query = target_query, | ||
| 661 | .manifest_sr = .init(&manifest_fr.interface, runner.gpa, &runner.eb_wip, src_path_string: { | ||
| 662 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 663 | const src_path = src_path: { | ||
| 664 | break :src_path path_buffer[0 .. manifest_file.realPath( | ||
| 665 | runner.io, | ||
| 666 | &path_buffer, | ||
| 667 | ) catch |err| switch (err) { | ||
| 668 | error.Canceled => |e| return e, | ||
| 669 | else => break :src_path "manifest", | ||
| 670 | }]; | ||
| 671 | }; | ||
| 672 | break :src_path_string try runner.eb_wip.addString(src_path); | ||
| 673 | }), | ||
| 674 | .compiler = null, | ||
| 675 | .src_dir = (runner.args.src_dir orelse | ||
| 676 | return runner.fail("missing \"--src=path/to/src\" arg", .{})).createDirPathOpen( | ||
| 677 | runner.io, | ||
| 678 | target_query, | ||
| 679 | .{}, | ||
| 680 | ) catch |err| switch (err) { | ||
| 681 | error.Canceled => |e| return e, | ||
| 682 | else => |e| return runner.fail("unable to create {q}: {t}", .{ target_query, e }), | ||
| 683 | }, | ||
| 684 | .eb_wip = &runner.eb_wip, | ||
| 685 | .cmd_src_loc = undefined, | ||
| 686 | .src_loc = null, | ||
| 687 | .num = 0, | ||
| 688 | .name = undefined, | ||
| 689 | .prog_node = .none, | ||
| 690 | .mtime = .now(runner.io, .real), | ||
| 691 | .allow_skip = true, | ||
| 692 | }; | ||
| 693 | defer { | ||
| 694 | update.manifest_sr.deinit(); | ||
| 695 | update.src_dir.close(runner.io); | ||
| 696 | update.prog_node.end(); | ||
| 697 | } | ||
| 698 | |||
| 699 | // hack around `std.Io.Reader` interface bugs by ensuring the | ||
| 700 | // buffer is larger than the longest supported manifest line | ||
| 701 | try update.manifest_sr.line_aw.ensureTotalCapacityPrecise(512); | ||
| 702 | update.manifest_sr.updateBuffer(); | ||
| 703 | |||
| 704 | var skip_delimiter = false; | ||
| 705 | while (update.manifest_sr.interface.takeSentinel('\n')) |line| { | ||
| 706 | var line_it = std.mem.tokenizeScalar(u8, line, ' '); | ||
| 707 | const cmd_src_loc = line_it.next() orelse continue; | ||
| 708 | if (skip_delimiter) { | ||
| 709 | assert(std.mem.eql(u8, cmd_src_loc, "#}")); | ||
| 710 | skip_delimiter = false; | ||
| 711 | const unexpected = line_it.rest(); | ||
| 712 | if (unexpected.len > 0) | ||
| 713 | return update.manifest_sr.fail(unexpected, "unexpected argument {q}", .{unexpected}); | ||
| 714 | continue; | ||
| 715 | } | ||
| 716 | update.cmd_src_loc = try update.manifest_sr.srcLoc(cmd_src_loc); | ||
| 717 | const cmd = std.meta.stringToEnum(Command, std.mem.cutPrefix(u8, cmd_src_loc, "#") orelse | ||
| 718 | return update.manifest_sr.fail(cmd_src_loc, "expected command", .{})) orelse | ||
| 719 | return update.manifest_sr.fail(cmd_src_loc, "unknown command", .{}); | ||
| 720 | const arg = switch (cmd) { | ||
| 721 | .todo, .exe, .lib, .obj, .update => null, | ||
| 722 | .skip, .write, .delete, .check => line_it.next() orelse | ||
| 723 | return update.manifest_sr.fail(line_it.rest()[0..0], "missing argument", .{}), | ||
| 724 | }; | ||
| 725 | var contents_file: ?std.Io.File = null; | ||
| 726 | defer if (contents_file) |file| file.close(runner.io); | ||
| 727 | var contents_buffer: [512]u8 = undefined; | ||
| 728 | var contents_impl: union(enum) { | ||
| 729 | r: std.Io.Reader, | ||
| 730 | dr: DelimitedReader, | ||
| 731 | fr: std.Io.File.Reader, | ||
| 732 | } = undefined; | ||
| 733 | const contents_r: *std.Io.Reader = contents_r: switch (cmd) { | ||
| 734 | .skip, .delete => { | ||
| 735 | const unexpected = line_it.rest(); | ||
| 736 | if (unexpected.len > 0) return update.manifest_sr.fail( | ||
| 737 | unexpected, | ||
| 738 | "unexpected argument {q}", | ||
| 739 | .{unexpected}, | ||
| 740 | ); | ||
| 741 | contents_impl = .{ .r = .ending_instance }; | ||
| 742 | break :contents_r &contents_impl.r; | ||
| 743 | }, | ||
| 744 | else => { | ||
| 745 | const contents = line_it.rest(); | ||
| 746 | if (contents.len == 0) { | ||
| 747 | contents_impl = .{ .r = .ending_instance }; | ||
| 748 | break :contents_r &contents_impl.r; | ||
| 749 | } | ||
| 750 | contents: switch (contents[0]) { | ||
| 751 | else => return update.manifest_sr.fail(contents, "invalid contents syntax", .{}), | ||
| 752 | '"' => { | ||
| 753 | if (contents.len < 2 or contents[contents.len - 1] != '"') continue :contents 0; | ||
| 754 | var contents_fw: std.Io.Writer = .fixed(&contents_buffer); | ||
| 755 | switch (std.zig.string_literal.parseWrite( | ||
| 756 | &contents_fw, | ||
| 757 | contents, | ||
| 758 | ) catch |err| switch (err) { | ||
| 759 | error.WriteFailed => return update.manifest_sr.fail( | ||
| 760 | contents, | ||
| 761 | "contents string too long, use inline or file syntax instead", | ||
| 762 | .{}, | ||
| 763 | ), | ||
| 764 | }) { | ||
| 765 | .success => { | ||
| 766 | contents_impl = .{ .r = .fixed(contents_fw.buffered()) }; | ||
| 767 | break :contents_r &contents_impl.r; | ||
| 768 | }, | ||
| 769 | .failure => |err| return update.manifest_sr.fail( | ||
| 770 | contents[err.offset()..][0..0], | ||
| 771 | "{f}", | ||
| 772 | .{err.fmt(contents)}, | ||
| 773 | ), | ||
| 774 | } | ||
| 775 | }, | ||
| 776 | '{' => { | ||
| 777 | if (contents.len != 1) continue :contents 0; | ||
| 778 | |||
| 779 | contents_impl = .{ | ||
| 780 | .dr = .init(&update.manifest_sr.interface, "#}", &contents_buffer), | ||
| 781 | }; | ||
| 782 | skip_delimiter = true; | ||
| 783 | break :contents_r &contents_impl.dr.interface; | ||
| 784 | }, | ||
| 785 | '@' => { | ||
| 786 | const args_test = runner.args.@"test" orelse return update.manifest_sr.fail( | ||
| 787 | contents, | ||
| 788 | "contents file requires directory test", | ||
| 789 | .{}, | ||
| 790 | ); | ||
| 791 | const contents_path = contents[1..]; | ||
| 792 | try runner.discoverInput(args_test.input_dir, contents_path); | ||
| 793 | |||
| 794 | contents_file = args_test.dir.openFile( | ||
| 795 | runner.io, | ||
| 796 | contents_path, | ||
| 797 | .{}, | ||
| 798 | ) catch |err| switch (err) { | ||
| 799 | error.Canceled => |e| return e, | ||
| 800 | else => |e| return update.manifest_sr.fail( | ||
| 801 | contents_path, | ||
| 802 | "unable to open contents file: {t}", | ||
| 803 | .{e}, | ||
| 804 | ), | ||
| 805 | }; | ||
| 806 | contents_impl = .{ .fr = contents_file.?.reader(runner.io, &contents_buffer) }; | ||
| 807 | break :contents_r &contents_impl.fr.interface; | ||
| 808 | }, | ||
| 809 | } | ||
| 810 | }, | ||
| 811 | }; | ||
| 812 | |||
| 813 | runner.handleCommand(&update, cmd_src_loc, cmd, arg, contents_r) catch |err| switch (err) { | ||
| 814 | error.Canceled, | ||
| 815 | error.OutOfMemory, | ||
| 816 | error.AlreadyReported, | ||
| 817 | error.SkipTest, | ||
| 818 | => |e| return e, | ||
| 819 | error.ContentsReadFailed => switch (contents_impl) { | ||
| 820 | .r => unreachable, | ||
| 821 | .dr => switch (manifest_fr.err orelse return error.OutOfMemory) { | ||
| 822 | error.Canceled => |e| return e, | ||
| 823 | else => |e| return update.fail("unable to read manifest: {t}", .{e}), | ||
| 824 | }, | ||
| 825 | .fr => |fr| switch (fr.err.?) { | ||
| 826 | error.Canceled => |e| return e, | ||
| 827 | else => |e| return update.fail("unable to read contents file: {t}", .{e}), | ||
| 828 | }, | ||
| 829 | }, | ||
| 830 | }; | ||
| 831 | } else |err| switch (err) { | ||
| 832 | error.ReadFailed => switch (manifest_fr.err orelse return error.OutOfMemory) { | ||
| 833 | error.Canceled => |e| return e, | ||
| 834 | else => |e| return update.fail("unable to read manifest: {t}", .{e}), | ||
| 835 | }, | ||
| 836 | error.StreamTooLong => unreachable, | ||
| 837 | error.EndOfStream => { | ||
| 838 | const final_line = update.manifest_sr.interface.buffered(); | ||
| 839 | switch (final_line.len) { | ||
| 840 | 0 => if (skip_delimiter) return update.manifest_sr.fail( | ||
| 841 | final_line[0..0], | ||
| 842 | "missing \"#}}\" delimiter", | ||
| 843 | .{}, | ||
| 844 | ), | ||
| 845 | else => return update.manifest_sr.fail( | ||
| 846 | final_line[final_line.len..], | ||
| 847 | "missing trailing newline", | ||
| 848 | .{}, | ||
| 849 | ), | ||
| 850 | } | ||
| 851 | }, | ||
| 852 | } | ||
| 853 | if (update.compiler) |*comp| { | ||
| 854 | if (comp.state != .idle) return update.fail("missing \"#check\"", .{}); | ||
| 855 | try comp.exit(); | ||
| 856 | } | ||
| 857 | } | ||
| 858 | const Command = enum { todo, skip, exe, lib, obj, write, delete, update, check }; | ||
| 859 | fn handleCommand( | ||
| 860 | runner: *Runner, | ||
| 861 | update: *Update, | ||
| 862 | cmd_src_loc: []const u8, | ||
| 863 | cmd: Command, | ||
| 864 | arg: ?[]const u8, | ||
| 865 | contents_r: *std.Io.Reader, | ||
| 866 | ) (error{ContentsReadFailed} || TestError)!void { | ||
| 867 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 868 | const cmd_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "#{t}{s}{s}", .{ | ||
| 869 | cmd, if (arg) |_| " " else "", arg orelse "", | ||
| 870 | }) catch &name_buffer, 0); | ||
| 871 | defer cmd_prog_node.end(); | ||
| 872 | |||
| 873 | cmd: switch (cmd) { | ||
| 874 | .todo => _ = contents_r.discardRemaining() catch |err| switch (err) { | ||
| 875 | error.ReadFailed => {}, | ||
| 876 | }, | ||
| 877 | .skip => if (!update.allow_skip) return update.manifest_sr.fail( | ||
| 878 | cmd_src_loc, | ||
| 879 | "\"#skip\" must appear before other commands", | ||
| 880 | .{}, | ||
| 881 | ) else if (std.mem.eql(u8, update.target_query, arg.?)) return error.SkipTest, | ||
| 882 | .exe, .lib, .obj => runner.spawnCompiler(&update.compiler, update.src_dir, switch (cmd) { | ||
| 883 | else => unreachable, | ||
| 884 | .exe => .Exe, | ||
| 885 | .lib => .Lib, | ||
| 886 | .obj => .Obj, | ||
| 887 | }, contents_r, target: { | ||
| 888 | var component_it = std.mem.splitBackwardsScalar(u8, update.target_query, '-'); | ||
| 889 | const pic_str = component_it.next() orelse | ||
| 890 | return update.fail("target {q} missing pic", .{update.target_query}); | ||
| 891 | const pic = std.meta.stringToEnum(Compiler.Target.Pic, pic_str) orelse | ||
| 892 | return update.fail("target {q} unknown pic {q}", .{ update.target_query, pic_str }); | ||
| 893 | const linker_str = component_it.next() orelse | ||
| 894 | return update.fail("target {q} missing linker", .{update.target_query}); | ||
| 895 | const linker = std.meta.stringToEnum(Compiler.Target.Linker, linker_str) orelse | ||
| 896 | return update.fail("target {q} unknown linker {q}", .{ | ||
| 897 | update.target_query, linker_str, | ||
| 898 | }); | ||
| 899 | const backend_str = component_it.next() orelse | ||
| 900 | return update.fail("target {q} missing backend", .{update.target_query}); | ||
| 901 | const backend = std.meta.stringToEnum(Compiler.Target.Backend, backend_str) orelse | ||
| 902 | return update.fail("target {q} unknown backend {q}", .{ | ||
| 903 | update.target_query, backend_str, | ||
| 904 | }); | ||
| 905 | const cache_mode_str = component_it.next() orelse | ||
| 906 | return update.fail("target {q} missing cache mode", .{update.target_query}); | ||
| 907 | const cache_mode = std.meta.stringToEnum(Compiler.Target.CacheMode, cache_mode_str) orelse | ||
| 908 | return update.fail("target {q} unknown cache mode {q}", .{ | ||
| 909 | update.target_query, cache_mode_str, | ||
| 910 | }); | ||
| 911 | const triple = component_it.rest(); | ||
| 912 | break :target .{ | ||
| 913 | .triple = triple, | ||
| 914 | .resolved = std.zig.system.resolveTargetQuery( | ||
| 915 | runner.io, | ||
| 916 | std.Build.parseTargetQuery(.{ | ||
| 917 | .arch_os_abi = triple, | ||
| 918 | .object_format = switch (backend) { | ||
| 919 | .sema, .selfhosted, .llvm => null, | ||
| 920 | .cbe => "c", | ||
| 921 | }, | ||
| 922 | }) catch |err| switch (err) { | ||
| 923 | error.ParseFailed => return update.fail("unable to parse triple {q}", .{ | ||
| 924 | triple, | ||
| 925 | }), | ||
| 926 | }, | ||
| 927 | ) catch |err| switch (err) { | ||
| 928 | error.Canceled => |e| return e, | ||
| 929 | else => |e| return update.fail("unable to resolve target {q}: {t}", .{ triple, e }), | ||
| 930 | }, | ||
| 931 | .cache_mode = cache_mode, | ||
| 932 | .backend = backend, | ||
| 933 | .linker = linker, | ||
| 934 | .pic = pic, | ||
| 935 | }; | ||
| 936 | }) catch |err| switch (err) { | ||
| 937 | error.Canceled, error.OutOfMemory, error.AlreadyReported, error.SkipTest => |e| return e, | ||
| 938 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 939 | }, | ||
| 940 | .write => { | ||
| 941 | const file = update.src_dir.createFile(runner.io, arg.?, .{}) catch |err| switch (err) { | ||
| 942 | error.Canceled => |e| return e, | ||
| 943 | else => |e| return update.manifest_sr.fail(arg.?, "unable to create file: {t}", .{e}), | ||
| 944 | }; | ||
| 945 | defer file.close(runner.io); | ||
| 946 | var fw_buffer: [512]u8 = undefined; | ||
| 947 | var fw = file.writer(runner.io, &fw_buffer); | ||
| 948 | _ = contents_r.streamRemaining(&fw.interface) catch |err| switch (err) { | ||
| 949 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 950 | error.WriteFailed => switch (fw.err.?) { | ||
| 951 | error.Canceled => |e| return e, | ||
| 952 | else => |e| return update.manifest_sr.fail( | ||
| 953 | arg.?, | ||
| 954 | "unable to write file: {t}", | ||
| 955 | .{e}, | ||
| 956 | ), | ||
| 957 | }, | ||
| 958 | }; | ||
| 959 | fw.flush() catch |err| switch (err) { | ||
| 960 | error.Canceled => |e| return e, | ||
| 961 | else => |e| return update.manifest_sr.fail( | ||
| 962 | arg.?, | ||
| 963 | "unable to write file: {t}", | ||
| 964 | .{e}, | ||
| 965 | ), | ||
| 966 | }; | ||
| 967 | file.setTimestamps(runner.io, .{ .modify_timestamp = .{ | ||
| 968 | .new = update.mtime, | ||
| 969 | } }) catch |err| switch (err) { | ||
| 970 | error.Canceled => |e| return e, | ||
| 971 | else => |e| return update.manifest_sr.fail( | ||
| 972 | arg.?, | ||
| 973 | "unable to update file timestamps: {t}", | ||
| 974 | .{e}, | ||
| 975 | ), | ||
| 976 | }; | ||
| 977 | }, | ||
| 978 | .delete => update.src_dir.deleteFile(runner.io, arg.?) catch |err| switch (err) { | ||
| 979 | error.Canceled => |e| return e, | ||
| 980 | else => |e| return update.manifest_sr.fail(arg.?, "unable to delete file: {t}", .{e}), | ||
| 981 | }, | ||
| 982 | .update => { | ||
| 983 | update.src_loc = update.cmd_src_loc; | ||
| 984 | const comp = &(update.compiler orelse return update.manifest_sr.fail( | ||
| 985 | cmd_src_loc, | ||
| 986 | "missing \"#exe\", \"#lib\", or \"#obj\"", | ||
| 987 | .{}, | ||
| 988 | )); | ||
| 989 | update.prog_node.end(); | ||
| 990 | const arena = runner.arena.allocator(); | ||
| 991 | update.name = switch (contents_r.readSliceShort(&name_buffer) catch |err| switch (err) { | ||
| 992 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 993 | }) { | ||
| 994 | 0 => switch (update.num) { | ||
| 995 | 0 => "initial update", | ||
| 996 | else => |num| try arena.print("update {d}", .{num}), | ||
| 997 | }, | ||
| 998 | else => |name_len| try arena.dupe(u8, name_buffer[0..name_len]), | ||
| 999 | }; | ||
| 1000 | update.prog_node = runner.prog_node.start(update.name, 0); | ||
| 1001 | _ = contents_r.discardRemaining() catch |err| switch (err) { | ||
| 1002 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 1003 | }; | ||
| 1004 | if (comp.state != .idle) | ||
| 1005 | return update.manifest_sr.fail(cmd_src_loc, "missing \"#check\"", .{}); | ||
| 1006 | comp.client.serveBodylessMessage(.update) catch |err| switch (err) { | ||
| 1007 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 1008 | error.Canceled => |e| return e, | ||
| 1009 | else => |e| return update.fail("unable to send message to compiler: {t}", .{e}), | ||
| 1010 | }, | ||
| 1011 | }; | ||
| 1012 | comp.state = .update; | ||
| 1013 | update.mtime = update.mtime.addDuration(.fromSeconds(2)); | ||
| 1014 | update.num += 1; | ||
| 1015 | }, | ||
| 1016 | .check => { | ||
| 1017 | const check = std.meta.stringToEnum(Compiler.Check, arg.?) orelse | ||
| 1018 | return update.manifest_sr.fail(arg.?, "unknown check", .{}); | ||
| 1019 | const expected = | ||
| 1020 | contents_r.allocRemaining(runner.gpa, .unlimited) catch |err| switch (err) { | ||
| 1021 | error.OutOfMemory => |e| return e, | ||
| 1022 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 1023 | error.StreamTooLong => unreachable, // .unlimited | ||
| 1024 | }; | ||
| 1025 | defer runner.gpa.free(expected); | ||
| 1026 | |||
| 1027 | const comp = &(update.compiler orelse return update.manifest_sr.fail( | ||
| 1028 | cmd_src_loc, | ||
| 1029 | "missing \"#exe\", \"#lib\", or \"#obj\"", | ||
| 1030 | .{}, | ||
| 1031 | )); | ||
| 1032 | if (comp.state != .update) | ||
| 1033 | return update.manifest_sr.fail(cmd_src_loc, "missing \"#update\"", .{}); | ||
| 1034 | const stderr = comp.mr.reader(1); | ||
| 1035 | while (try comp.receiveMessage()) |message| switch (message.tag) { | ||
| 1036 | else => {}, // Ignore other messages, | ||
| 1037 | .config => { | ||
| 1038 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 1039 | comp.config = body_r.takeStruct(std.zig.Server.Message.Config, .little) catch | ||
| 1040 | unreachable; | ||
| 1041 | }, | ||
| 1042 | .emit_digest => { | ||
| 1043 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 1044 | _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch | ||
| 1045 | unreachable; | ||
| 1046 | const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch | ||
| 1047 | unreachable; | ||
| 1048 | try comp.checkSuccess(update, check, expected, digest); | ||
| 1049 | }, | ||
| 1050 | .error_bundle => { | ||
| 1051 | const eb = std.zig.Server.allocErrorBundle( | ||
| 1052 | runner.arena.allocator(), | ||
| 1053 | message.body, | ||
| 1054 | ) catch |err| switch (err) { | ||
| 1055 | error.OutOfMemory => |e| return e, | ||
| 1056 | error.EndOfStream => return update.fail("error bundle truncated", .{}), | ||
| 1057 | }; | ||
| 1058 | switch (check) { | ||
| 1059 | .errors => { | ||
| 1060 | var error_aw: std.Io.Writer.Allocating = .init(runner.gpa); | ||
| 1061 | defer error_aw.deinit(); | ||
| 1062 | eb.renderToWriter(.{ | ||
| 1063 | .include_reference_trace = false, | ||
| 1064 | .include_source_line = false, | ||
| 1065 | .include_log_text = true, | ||
| 1066 | }, &error_aw.writer) catch |err| switch (err) { | ||
| 1067 | error.WriteFailed => return error.OutOfMemory, | ||
| 1068 | }; | ||
| 1069 | const actual = error_aw.written(); | ||
| 1070 | std.mem.replaceScalar(u8, actual, '\\', '/'); | ||
| 1071 | std.testing.expectEqualStrings(expected, actual) catch |err| switch (err) { | ||
| 1072 | error.TestExpectedEqual => return update.fail( | ||
| 1073 | "errors did not match expected", | ||
| 1074 | .{}, | ||
| 1075 | ), | ||
| 1076 | }; | ||
| 1077 | }, | ||
| 1078 | .stdout, .exit, .gdb, .lldb => if (eb.errorMessageCount() > 0) { | ||
| 1079 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { | ||
| 1080 | error.Canceled => {}, | ||
| 1081 | else => {}, | ||
| 1082 | }; | ||
| 1083 | return update.fail("unexpected compile errors", .{}); | ||
| 1084 | }, | ||
| 1085 | } | ||
| 1086 | comp.state = .idle; | ||
| 1087 | break :cmd; | ||
| 1088 | }, | ||
| 1089 | }; | ||
| 1090 | |||
| 1091 | const stderr_buffered = stderr.buffered(); | ||
| 1092 | if (stderr_buffered.len > 0) { | ||
| 1093 | if (comp.allow_compiler_stderr) { | ||
| 1094 | std.log.info("#update {q} stderr:\n{s}", .{ update.name, stderr_buffered }); | ||
| 1095 | } else return update.fail("unexpected stderr:\n{s}", .{stderr_buffered}); | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | try comp.exit(); | ||
| 1099 | return update.fail("compiler failed to send terminating error_bundle", .{}); | ||
| 1100 | }, | ||
| 1101 | } | ||
| 1102 | assert(contents_r.discardRemaining() catch |err| switch (err) { | ||
| 1103 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 1104 | } == 0); | ||
| 1105 | switch (cmd) { | ||
| 1106 | .todo, .skip => {}, | ||
| 1107 | else => update.allow_skip = false, | ||
| 1108 | } | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | fn spawnCompiler( | ||
| 1112 | runner: *Runner, | ||
| 1113 | compiler: *?Compiler, | ||
| 1114 | src_dir: std.Io.Dir, | ||
| 1115 | output_mode: std.lang.OutputMode, | ||
| 1116 | args_r: *std.Io.Reader, | ||
| 1117 | target: Compiler.Target, | ||
| 1118 | ) (std.Io.Reader.ShortError || TestError)!void { | ||
| 1119 | const gpa = runner.gpa; | ||
| 1120 | const arena = runner.arena.allocator(); | ||
| 1121 | if (compiler.*) |*comp| { | ||
| 1122 | try comp.exit(); | ||
| 1123 | compiler.* = null; | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | var argv: std.ArrayList([]const u8) = .empty; | ||
| 1127 | defer argv.deinit(gpa); | ||
| 1128 | try argv.appendSlice(gpa, &.{ | ||
| 1129 | "zig", | ||
| 1130 | switch (output_mode) { | ||
| 1131 | .Obj => "build-obj", | ||
| 1132 | .Exe => "build-exe", | ||
| 1133 | .Lib => "build-lib", | ||
| 1134 | }, | ||
| 1135 | "-target", | ||
| 1136 | target.triple, | ||
| 1137 | "--cache-dir", | ||
| 1138 | ".zig-cache", | ||
| 1139 | }); | ||
| 1140 | switch (target.cache_mode) { | ||
| 1141 | .whole => {}, | ||
| 1142 | .incremental => try argv.append(gpa, "-fincremental"), | ||
| 1143 | } | ||
| 1144 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 1145 | if (target.backend) |b| switch (b) { | ||
| 1146 | .sema => try argv.append(gpa, "-fno-emit-bin"), | ||
| 1147 | .selfhosted => try argv.append(gpa, "-fno-llvm"), | ||
| 1148 | .llvm => try argv.append(gpa, "-fllvm"), | ||
| 1149 | .cbe => try argv.append(gpa, "-ofmt=c"), | ||
| 1150 | } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse | ||
| 1151 | return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})) | ||
| 1152 | .realPath(runner.io, &path_buffer) catch |err| switch (err) { | ||
| 1153 | error.Canceled => |e| return e, | ||
| 1154 | else => |e| return runner.fail("unable to get lib path: {t}", .{e}), | ||
| 1155 | }] }); | ||
| 1156 | switch (target.linker) { | ||
| 1157 | .lld => try argv.appendSlice(gpa, &.{ "-flld", "-fno-new-linker" }), | ||
| 1158 | .old => try argv.appendSlice(gpa, &.{ "-fno-lld", "-fno-new-linker" }), | ||
| 1159 | .new => try argv.appendSlice(gpa, &.{ "-fno-lld", "-fnew-linker" }), | ||
| 1160 | } | ||
| 1161 | switch (target.pic) { | ||
| 1162 | .nopic => try argv.appendSlice(gpa, &.{ "-fno-PIC", "-fno-PIE" }), | ||
| 1163 | .pic => try argv.appendSlice(gpa, &.{ "-fPIC", "-fno-PIE" }), | ||
| 1164 | .pie => try argv.appendSlice(gpa, &.{ "-fPIC", "-fPIE" }), | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | var need: usize = 1; | ||
| 1168 | var state: enum { positional, @"--name" } = .positional; | ||
| 1169 | var root_name: ?[]const u8 = null; | ||
| 1170 | while (true) { | ||
| 1171 | const done = if (args_r.fill(need)) false else |err| switch (err) { | ||
| 1172 | error.ReadFailed => |e| return e, | ||
| 1173 | error.EndOfStream => true, | ||
| 1174 | }; | ||
| 1175 | const buffered = args_r.buffered(); | ||
| 1176 | const start = std.mem.findNone(u8, buffered, "\n ") orelse buffered.len; | ||
| 1177 | const end = std.mem.findAnyPos(u8, buffered, start, "\n ") orelse buffered.len; | ||
| 1178 | if (end - start > 0) { | ||
| 1179 | const arg = try arena.dupe(u8, buffered[start..end]); | ||
| 1180 | switch (state) { | ||
| 1181 | .positional => if (std.mem.eql(u8, arg, "--name")) { | ||
| 1182 | state = .@"--name"; | ||
| 1183 | } else if (std.mem.cutPrefix(u8, arg, "-M")) |module| { | ||
| 1184 | var module_name_it = std.mem.splitScalar(u8, module, '='); | ||
| 1185 | const module_name = module_name_it.next().?; | ||
| 1186 | root_name = root_name orelse module_name; | ||
| 1187 | }, | ||
| 1188 | .@"--name" => { | ||
| 1189 | root_name = arg; | ||
| 1190 | state = .positional; | ||
| 1191 | }, | ||
| 1192 | } | ||
| 1193 | try argv.append(gpa, arg); | ||
| 1194 | args_r.toss(end); | ||
| 1195 | need = 1; | ||
| 1196 | } else { | ||
| 1197 | args_r.toss(start); | ||
| 1198 | need = buffered.len - start + 1; | ||
| 1199 | } | ||
| 1200 | if (done) break; | ||
| 1201 | } | ||
| 1202 | try argv.append(gpa, "--listen=-"); | ||
| 1203 | |||
| 1204 | const out_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ | ||
| 1205 | .root_name = root_name orelse "root", | ||
| 1206 | .cpu_arch = target.resolved.cpu.arch, | ||
| 1207 | .os_tag = target.resolved.os.tag, | ||
| 1208 | .ofmt = target.resolved.ofmt, | ||
| 1209 | .abi = target.resolved.abi, | ||
| 1210 | .output_mode = output_mode, | ||
| 1211 | }); | ||
| 1212 | |||
| 1213 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 1214 | const comp_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "compile {s}", .{ | ||
| 1215 | out_name, | ||
| 1216 | }) catch &name_buffer, 0); | ||
| 1217 | errdefer comp_prog_node.end(); | ||
| 1218 | |||
| 1219 | const child = std.process.spawn(runner.io, .{ | ||
| 1220 | .exe = .{ .file = runner.args.zig_exe orelse | ||
| 1221 | return runner.fail("missing \"--zig=path/to/zig\" arg", .{}) }, | ||
| 1222 | .argv = argv.items, | ||
| 1223 | .cwd = .{ .dir = src_dir }, | ||
| 1224 | .progress_node = comp_prog_node, | ||
| 1225 | .stdin = .pipe, | ||
| 1226 | .stdout = .pipe, | ||
| 1227 | .stderr = .pipe, | ||
| 1228 | }) catch |err| switch (err) { | ||
| 1229 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1230 | error.OperationUnsupported => return error.SkipTest, | ||
| 1231 | else => |e| return runner.fail("unable to spawn compiler: {t}", .{e}), | ||
| 1232 | }; | ||
| 1233 | compiler.* = .{ | ||
| 1234 | .runner = runner, | ||
| 1235 | .prog_node = comp_prog_node, | ||
| 1236 | .allow_compiler_stderr = true, | ||
| 1237 | .output_mode = output_mode, | ||
| 1238 | .state = .idle, | ||
| 1239 | .child = child, | ||
| 1240 | .fw = undefined, | ||
| 1241 | .mr_buffer = undefined, | ||
| 1242 | .mr = undefined, | ||
| 1243 | .client = undefined, | ||
| 1244 | .target = target, | ||
| 1245 | .out_name = out_name, | ||
| 1246 | .config = null, | ||
| 1247 | }; | ||
| 1248 | const comp = &compiler.*.?; | ||
| 1249 | comp.fw = child.stdin.?.writerStreaming(runner.io, &.{}); | ||
| 1250 | comp.mr.init(gpa, runner.io, comp.mr_buffer.toStreams(), &.{ | ||
| 1251 | child.stdout.?, child.stderr.?, | ||
| 1252 | }); | ||
| 1253 | comp.client = .{ .in = comp.mr.reader(0), .out = &comp.fw.interface }; | ||
| 1254 | } | ||
| 1255 | const Compiler = struct { | ||
| 1256 | runner: *Runner, | ||
| 1257 | prog_node: std.Progress.Node, | ||
| 1258 | allow_compiler_stderr: bool, | ||
| 1259 | output_mode: std.lang.OutputMode, | ||
| 1260 | state: enum { idle, update }, | ||
| 1261 | child: std.process.Child, | ||
| 1262 | fw: std.Io.File.Writer, | ||
| 1263 | mr_buffer: std.Io.File.MultiReader.Buffer(2), | ||
| 1264 | mr: std.Io.File.MultiReader, | ||
| 1265 | client: std.zig.Client, | ||
| 1266 | target: Target, | ||
| 1267 | out_name: []const u8, | ||
| 1268 | config: ?std.zig.Server.Message.Config, | ||
| 1269 | |||
| 1270 | const Target = struct { | ||
| 1271 | triple: []const u8, | ||
| 1272 | resolved: std.Target, | ||
| 1273 | cache_mode: CacheMode, | ||
| 1274 | backend: ?Backend, | ||
| 1275 | linker: Linker, | ||
| 1276 | pic: Pic, | ||
| 1277 | |||
| 1278 | const CacheMode = enum { whole, incremental }; | ||
| 1279 | |||
| 1280 | const Backend = enum { | ||
| 1281 | /// Run semantic analysis only. Runtime output will not be tested, but we still verify | ||
| 1282 | /// that compilation succeeds. Corresponds to `-fno-emit-bin`. | ||
| 1283 | sema, | ||
| 1284 | /// Use the self-hosted code generation backend for this target. | ||
| 1285 | /// Corresponds to `-fno-llvm -fno-lld`. | ||
| 1286 | selfhosted, | ||
| 1287 | /// Use the LLVM backend. | ||
| 1288 | /// Corresponds to `-fllvm -flld`. | ||
| 1289 | llvm, | ||
| 1290 | /// Use the C backend. The output is compiled with `zig cc`. | ||
| 1291 | /// Corresponds to `-ofmt=c`. | ||
| 1292 | cbe, | ||
| 1293 | }; | ||
| 1294 | |||
| 1295 | const Linker = enum { old, new, lld }; | ||
| 1296 | |||
| 1297 | const Pic = enum { nopic, pic, pie }; | ||
| 1298 | }; | ||
| 1299 | |||
| 1300 | fn receiveMessage(comp: *Compiler) FailError!?struct { | ||
| 1301 | tag: std.zig.Server.Message.Tag, | ||
| 1302 | body: []const u8, | ||
| 1303 | } { | ||
| 1304 | const header = comp.client.receiveMessageWithMultiReader( | ||
| 1305 | &comp.mr, | ||
| 1306 | .none, | ||
| 1307 | ) catch |err| switch (err) { | ||
| 1308 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1309 | error.Timeout => unreachable, | ||
| 1310 | error.EndOfStream => return null, | ||
| 1311 | else => |e| return comp.runner.fail("unable to receive message: {t}", .{e}), | ||
| 1312 | }; | ||
| 1313 | const body = comp.client.in.take(header.bytes_len) catch unreachable; | ||
| 1314 | const stderr = comp.mr.reader(1); | ||
| 1315 | if (stderr.bufferedLen() > 0) { | ||
| 1316 | if (comp.allow_compiler_stderr) { | ||
| 1317 | std.log.info("{t} stderr:\n{s}", .{ header.tag, stderr.buffered() }); | ||
| 1318 | } else { | ||
| 1319 | return comp.runner.fail("{t} unexpected stderr:\n{s}", .{ | ||
| 1320 | header.tag, stderr.buffered(), | ||
| 1321 | }); | ||
| 1322 | } | ||
| 1323 | stderr.tossBuffered(); | ||
| 1324 | } | ||
| 1325 | return .{ .tag = header.tag, .body = body }; | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | const Check = enum { errors, stdout, exit, gdb, lldb }; | ||
| 1329 | fn checkSuccess( | ||
| 1330 | comp: *Compiler, | ||
| 1331 | update: *const Update, | ||
| 1332 | check: Check, | ||
| 1333 | expected: []const u8, | ||
| 1334 | digest: *const std.Build.Cache.BinDigest, | ||
| 1335 | ) TestError!void { | ||
| 1336 | const runner = comp.runner; | ||
| 1337 | const gpa = runner.gpa; | ||
| 1338 | const arena = runner.arena.allocator(); | ||
| 1339 | const io = runner.io; | ||
| 1340 | const target = &comp.target.resolved; | ||
| 1341 | const config = &comp.config.?; | ||
| 1342 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 1343 | |||
| 1344 | // After a failure while running a script, the debugger starts accepting commands from stdin, and | ||
| 1345 | // because it is empty, the debugger exits normally with status 0. Choose a non-zero status to | ||
| 1346 | // return from the debugger script instead to detect it running to completion and indicate success. | ||
| 1347 | const debug_success = 99; | ||
| 1348 | |||
| 1349 | const tmp_dir_path = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ "tmp"; | ||
| 1350 | update.src_dir.createDirPath(io, tmp_dir_path) catch |err| switch (err) { | ||
| 1351 | error.Canceled => |e| return e, | ||
| 1352 | else => |e| return update.fail("unable to create dir path {q}: {t}", .{ | ||
| 1353 | tmp_dir_path, e, | ||
| 1354 | }), | ||
| 1355 | }; | ||
| 1356 | |||
| 1357 | var argv: std.ArrayList([]const u8) = .initBuffer(try arena.alloc([]const u8, 5 + 4)); | ||
| 1358 | var environ_map: std.process.Environ.Map = .init(gpa); | ||
| 1359 | defer environ_map.deinit(); | ||
| 1360 | try environ_map.putAll(&runner.environ_map); | ||
| 1361 | var script_buffer: [512]u8 = undefined; | ||
| 1362 | const script_path, const script_file, var script_fw = script: switch (check) { | ||
| 1363 | .errors => return, | ||
| 1364 | .stdout, .exit => .{ undefined, null, undefined }, | ||
| 1365 | .gdb => { | ||
| 1366 | const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "gdb"; | ||
| 1367 | argv.appendSliceAssumeCapacity(&.{ "gdb", "--batch", "--command", script_path, "--args" }); | ||
| 1368 | const script_file = | ||
| 1369 | update.src_dir.createFile(io, script_path, .{}) catch |err| switch (err) { | ||
| 1370 | error.Canceled => |e| return e, | ||
| 1371 | else => |e| return update.fail("unable to create debugger script {q}: {t}", .{ | ||
| 1372 | script_path, e, | ||
| 1373 | }), | ||
| 1374 | }; | ||
| 1375 | errdefer script_file.close(io); | ||
| 1376 | var script_fw = script_file.writer(io, &script_buffer); | ||
| 1377 | script_fw.interface.writeAll( | ||
| 1378 | \\set remotetimeout 0 | ||
| 1379 | \\ | ||
| 1380 | \\ | ||
| 1381 | ) catch |err| switch (err) { | ||
| 1382 | error.WriteFailed => switch (script_fw.err.?) { | ||
| 1383 | error.Canceled => |e| return e, | ||
| 1384 | else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ | ||
| 1385 | script_path, e, | ||
| 1386 | }), | ||
| 1387 | }, | ||
| 1388 | }; | ||
| 1389 | break :script .{ script_path, script_file, script_fw }; | ||
| 1390 | }, | ||
| 1391 | .lldb => { | ||
| 1392 | const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "lldb"; | ||
| 1393 | argv.appendSliceAssumeCapacity(&.{ "lldb", "--batch", "--source", script_path, "--" }); | ||
| 1394 | try environ_map.put("LANG", "C.UTF-8"); // affects output formatting | ||
| 1395 | const script_file = | ||
| 1396 | update.src_dir.createFile(io, script_path, .{}) catch |err| switch (err) { | ||
| 1397 | error.Canceled => |e| return e, | ||
| 1398 | else => |e| return update.fail("unable to create debugger script {q}: {t}", .{ | ||
| 1399 | script_path, e, | ||
| 1400 | }), | ||
| 1401 | }; | ||
| 1402 | errdefer script_file.close(io); | ||
| 1403 | var script_fw = script_file.writer(io, &script_buffer); | ||
| 1404 | script_fw.interface.writeAll( | ||
| 1405 | \\settings set frame-format 'frame #${frame.index}:{ ${module.file.basename}{\`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${line.file.basename}:${line.number}{:${line.column}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\n' | ||
| 1406 | \\settings set plugin.process.gdb-remote.packet-timeout 0 | ||
| 1407 | \\settings set stop-line-count-after 0 | ||
| 1408 | \\settings set stop-line-count-before 0 | ||
| 1409 | \\ | ||
| 1410 | ) catch |err| switch (err) { | ||
| 1411 | error.WriteFailed => switch (script_fw.err.?) { | ||
| 1412 | error.Canceled => |e| return e, | ||
| 1413 | else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ | ||
| 1414 | script_path, e, | ||
| 1415 | }), | ||
| 1416 | }, | ||
| 1417 | }; | ||
| 1418 | break :script .{ script_path, script_file, script_fw }; | ||
| 1419 | }, | ||
| 1420 | }; | ||
| 1421 | defer if (script_file) |file| file.close(io); | ||
| 1422 | |||
| 1423 | const out_dir_path = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ | ||
| 1424 | "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); | ||
| 1425 | const out_path = try std.Io.Dir.path.join(arena, &.{ out_dir_path, comp.out_name }); | ||
| 1426 | const bin_path = switch (comp.target.backend.?) { | ||
| 1427 | .sema => return, | ||
| 1428 | .selfhosted, .llvm => out_path, | ||
| 1429 | .cbe => try comp.compileC(update.src_dir, out_path), | ||
| 1430 | }; | ||
| 1431 | |||
| 1432 | const need_cross_libc = | ||
| 1433 | target.os.tag == .linux and config.flags.link_libc and config.flags.link_mode == .dynamic; | ||
| 1434 | const use_executor = use_executor: switch (std.zig.system.getExternalExecutor(io, target, .{ | ||
| 1435 | .host_cpu_arch = runner.host.cpu.arch, | ||
| 1436 | .host_os_tag = runner.host.os.tag, | ||
| 1437 | .qemu_fixes_dl = need_cross_libc and runner.args.libc_runtimes_dir != null, | ||
| 1438 | .link_mode = config.flags.link_mode, | ||
| 1439 | .link_libc = config.flags.link_libc, | ||
| 1440 | })) { | ||
| 1441 | .bad_dl, .bad_os_or_cpu => { | ||
| 1442 | // This binary cannot be executed on this host. | ||
| 1443 | if (!runner.args.quiet) std.log.warn("skipping execution because host {q} cannot " ++ | ||
| 1444 | "execute binaries for foreign target {q}", .{ | ||
| 1445 | try runner.host.zigTriple(arena), comp.target.triple, | ||
| 1446 | }); | ||
| 1447 | return; | ||
| 1448 | }, | ||
| 1449 | .native => false, | ||
| 1450 | .darling => |executor| if (runner.args.enable_darling) { | ||
| 1451 | argv.appendAssumeCapacity(executor); | ||
| 1452 | break :use_executor true; | ||
| 1453 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1454 | .qemu => |executor| if (runner.args.enable_qemu) { | ||
| 1455 | argv.appendAssumeCapacity(executor); | ||
| 1456 | if (need_cross_libc) { | ||
| 1457 | const libc_runtimes_dir = runner.args.libc_runtimes_dir orelse | ||
| 1458 | continue :use_executor .bad_os_or_cpu; | ||
| 1459 | const libc_runtimes_path = path_buffer[0 .. libc_runtimes_dir.realPath( | ||
| 1460 | io, | ||
| 1461 | &path_buffer, | ||
| 1462 | ) catch |err| switch (err) { | ||
| 1463 | error.Canceled => |e| return e, | ||
| 1464 | else => |e| return update.fail("unable to get libc runtimes path: {t}", .{e}), | ||
| 1465 | }]; | ||
| 1466 | argv.appendSliceAssumeCapacity(&.{ "-L", try std.Io.Dir.path.join(arena, &.{ | ||
| 1467 | libc_runtimes_path, | ||
| 1468 | try if (target.isGnuLibC()) std.zig.target.glibcRuntimeTriple( | ||
| 1469 | arena, | ||
| 1470 | target.cpu.arch, | ||
| 1471 | target.os.tag, | ||
| 1472 | target.abi, | ||
| 1473 | ) else if (target.isMuslLibC()) std.zig.target.muslRuntimeTriple( | ||
| 1474 | arena, | ||
| 1475 | target.cpu.arch, | ||
| 1476 | target.abi, | ||
| 1477 | ) else unreachable, | ||
| 1478 | }) }); | ||
| 1479 | } | ||
| 1480 | break :use_executor true; | ||
| 1481 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1482 | .rosetta => if (runner.args.enable_rosetta) | ||
| 1483 | true | ||
| 1484 | else | ||
| 1485 | continue :use_executor .bad_os_or_cpu, | ||
| 1486 | .wine => |executor| if (runner.args.enable_wine) { | ||
| 1487 | if (!environ_map.contains("WINEDEBUG")) try environ_map.put("WINEDEBUG", "-all"); | ||
| 1488 | argv.appendAssumeCapacity(executor); | ||
| 1489 | break :use_executor true; | ||
| 1490 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1491 | .wasmtime => |executor| if (runner.args.enable_wasmtime) { | ||
| 1492 | if (!environ_map.contains("WASMTIME_BACKTRACE_DETAILS")) | ||
| 1493 | try environ_map.put("WASMTIME_BACKTRACE_DETAILS", "1"); | ||
| 1494 | argv.appendSliceAssumeCapacity(&.{ executor, "--dir=.", "-Sinherit-env" }); | ||
| 1495 | break :use_executor true; | ||
| 1496 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1497 | }; | ||
| 1498 | argv.appendAssumeCapacity(bin_path); | ||
| 1499 | |||
| 1500 | if (!runner.args.quiet) std.log.info("{f}", .{std.zig.SubprocessCommand{ | ||
| 1501 | .argv = argv.items, | ||
| 1502 | .cwd = cwd_path: { | ||
| 1503 | break :cwd_path path_buffer[0 .. update.src_dir.realPath( | ||
| 1504 | io, | ||
| 1505 | &path_buffer, | ||
| 1506 | ) catch |err| switch (err) { | ||
| 1507 | error.Canceled => |e| return e, | ||
| 1508 | else => break :cwd_path null, | ||
| 1509 | }]; | ||
| 1510 | }, | ||
| 1511 | .parent_env = &runner.environ_map, | ||
| 1512 | .child_env = &environ_map, | ||
| 1513 | }}); | ||
| 1514 | |||
| 1515 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 1516 | const run_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "run {s}", .{ | ||
| 1517 | comp.out_name, | ||
| 1518 | }) catch &name_buffer, 0); | ||
| 1519 | defer run_prog_node.end(); | ||
| 1520 | |||
| 1521 | const bin_file = update.src_dir.openFile(io, bin_path, .{}) catch |err| switch (err) { | ||
| 1522 | error.Canceled => |e| return e, | ||
| 1523 | else => |e| return update.fail("unable to open {q}: {t}", .{ bin_path, e }), | ||
| 1524 | }; | ||
| 1525 | defer bin_file.close(io); | ||
| 1526 | const result = std.process.run(gpa, io, .{ | ||
| 1527 | .exe = exe: switch (check) { | ||
| 1528 | .errors => unreachable, | ||
| 1529 | .stdout, .exit => if (use_executor) .search else .{ .file = bin_file }, | ||
| 1530 | .gdb, .lldb => { | ||
| 1531 | if (use_executor) { | ||
| 1532 | if (!runner.args.quiet) std.log.warn( | ||
| 1533 | "skipping debugging of {q} via executor for foreign target {q}", | ||
| 1534 | .{ bin_path, comp.target.triple }, | ||
| 1535 | ); | ||
| 1536 | return; | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | var line_it = std.mem.splitScalar(u8, expected, '\n'); | ||
| 1540 | while (line_it.next()) |line| if (std.mem.cutPrefix(u8, line, switch (check) { | ||
| 1541 | else => unreachable, | ||
| 1542 | .gdb => "(gdb) ", | ||
| 1543 | .lldb => "(lldb) ", | ||
| 1544 | })) |cmd| script_fw.interface.print( | ||
| 1545 | \\{s} | ||
| 1546 | \\ | ||
| 1547 | , .{cmd}) catch |err| switch (err) { | ||
| 1548 | error.WriteFailed => switch (script_fw.err.?) { | ||
| 1549 | error.Canceled => |e| return e, | ||
| 1550 | else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ | ||
| 1551 | script_path, e, | ||
| 1552 | }), | ||
| 1553 | }, | ||
| 1554 | }; | ||
| 1555 | script_fw.interface.print( | ||
| 1556 | \\ | ||
| 1557 | \\quit {d} | ||
| 1558 | \\ | ||
| 1559 | , .{debug_success}) catch |err| switch (err) { | ||
| 1560 | error.WriteFailed => switch (script_fw.err.?) { | ||
| 1561 | error.Canceled => |e| return e, | ||
| 1562 | else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ | ||
| 1563 | script_path, e, | ||
| 1564 | }), | ||
| 1565 | }, | ||
| 1566 | }; | ||
| 1567 | script_fw.flush() catch |err| switch (err) { | ||
| 1568 | error.Canceled => |e| return e, | ||
| 1569 | else => |e| return update.fail("unable to write debugger script {q}: {t}", .{ | ||
| 1570 | script_path, e, | ||
| 1571 | }), | ||
| 1572 | }; | ||
| 1573 | break :exe .{ .file = switch (check) { | ||
| 1574 | else => unreachable, | ||
| 1575 | .gdb => runner.args.gdb_exe, | ||
| 1576 | .lldb => runner.args.lldb_exe, | ||
| 1577 | } orelse { | ||
| 1578 | if (!runner.args.quiet) std.log.warn( | ||
| 1579 | "skipping debugging of {q} because of missing \"--{t}=path/to/{[1]t}\" arg", | ||
| 1580 | .{ bin_path, check }, | ||
| 1581 | ); | ||
| 1582 | return; | ||
| 1583 | } }; | ||
| 1584 | }, | ||
| 1585 | }, | ||
| 1586 | .argv = argv.items, | ||
| 1587 | .cwd = .{ .dir = update.src_dir }, | ||
| 1588 | .environ_map = &environ_map, | ||
| 1589 | .progress_node = run_prog_node, | ||
| 1590 | }) catch |err| if (use_executor) { | ||
| 1591 | // Chances are the foreign executor isn't available. Skip this evaluation. | ||
| 1592 | if (!runner.args.quiet) std.log.warn( | ||
| 1593 | "skipping execution of {q} via executor for foreign target {q}: {t}", | ||
| 1594 | .{ bin_path, comp.target.triple, err }, | ||
| 1595 | ); | ||
| 1596 | return; | ||
| 1597 | } else return update.fail("unable to run the generated executable {q}: {t}", .{ | ||
| 1598 | bin_path, err, | ||
| 1599 | }); | ||
| 1600 | defer { | ||
| 1601 | gpa.free(result.stdout); | ||
| 1602 | gpa.free(result.stderr); | ||
| 1603 | } | ||
| 1604 | switch (result.term) { | ||
| 1605 | .exited => |code| switch (check) { | ||
| 1606 | .errors => unreachable, | ||
| 1607 | .stdout => { | ||
| 1608 | if (code != 0) return update.fail( | ||
| 1609 | "generated executable {q} failed with code {d}", | ||
| 1610 | .{ bin_path, code }, | ||
| 1611 | ); | ||
| 1612 | std.testing.expectEqualStrings( | ||
| 1613 | expected, | ||
| 1614 | result.stdout, | ||
| 1615 | ) catch |err| switch (err) { | ||
| 1616 | error.TestExpectedEqual => return update.fail( | ||
| 1617 | "stdout did not match expected", | ||
| 1618 | .{}, | ||
| 1619 | ), | ||
| 1620 | }; | ||
| 1621 | }, | ||
| 1622 | .exit => { | ||
| 1623 | var actual_code_buffer: [std.fmt.count("{d}", .{std.math.maxInt(u8)})]u8 = | ||
| 1624 | undefined; | ||
| 1625 | std.testing.expectEqualStrings( | ||
| 1626 | expected, | ||
| 1627 | std.mem.print(&actual_code_buffer, "{d}", .{code}) catch unreachable, | ||
| 1628 | ) catch |err| switch (err) { | ||
| 1629 | error.TestExpectedEqual => return update.fail( | ||
| 1630 | "exit code did not match expected", | ||
| 1631 | .{}, | ||
| 1632 | ), | ||
| 1633 | }; | ||
| 1634 | }, | ||
| 1635 | .gdb, .lldb => { | ||
| 1636 | if (code != debug_success) return update.fail( | ||
| 1637 | "debugging {q} failed with code {d}", | ||
| 1638 | .{ bin_path, code }, | ||
| 1639 | ); | ||
| 1640 | try update.checkMatches("stdout", expected, result.stdout); | ||
| 1641 | }, | ||
| 1642 | }, | ||
| 1643 | .signal => |sig| return update.fail( | ||
| 1644 | "generated executable {q} terminated with signal {t}", | ||
| 1645 | .{ bin_path, sig }, | ||
| 1646 | ), | ||
| 1647 | .stopped => |sig| return update.fail( | ||
| 1648 | "generated executable {q} stopped with signal {t}", | ||
| 1649 | .{ bin_path, sig }, | ||
| 1650 | ), | ||
| 1651 | .unknown => return update.fail( | ||
| 1652 | "generated executable {q} terminated unexpectedly", | ||
| 1653 | .{bin_path}, | ||
| 1654 | ), | ||
| 1655 | } | ||
| 1656 | if (!use_executor and result.stderr.len > 0) { | ||
| 1657 | std.log.err("#update {q} generated executable {q} had unexpected stderr:\n{s}", .{ | ||
| 1658 | update.name, bin_path, result.stderr, | ||
| 1659 | }); | ||
| 1660 | } | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) TestError![]const u8 { | ||
| 1664 | const runner = parent_comp.runner; | ||
| 1665 | const arena = runner.arena.allocator(); | ||
| 1666 | var compiler: ?Compiler = null; | ||
| 1667 | var args_r: std.Io.Reader = .fixed(c_path); | ||
| 1668 | parent_comp.runner.spawnCompiler(&compiler, src_dir, parent_comp.output_mode, &args_r, .{ | ||
| 1669 | .triple = parent_comp.target.triple, | ||
| 1670 | .resolved = resolved: { | ||
| 1671 | var resolved = parent_comp.target.resolved; | ||
| 1672 | resolved.ofmt = .default(resolved.os.tag, resolved.cpu.arch); | ||
| 1673 | break :resolved resolved; | ||
| 1674 | }, | ||
| 1675 | .cache_mode = .whole, | ||
| 1676 | .backend = null, | ||
| 1677 | .linker = .lld, | ||
| 1678 | .pic = parent_comp.target.pic, | ||
| 1679 | }) catch |err| switch (err) { | ||
| 1680 | error.Canceled, error.OutOfMemory, error.AlreadyReported, error.SkipTest => |e| return e, | ||
| 1681 | error.ReadFailed => unreachable, // .fixed | ||
| 1682 | }; | ||
| 1683 | const comp = &compiler.?; | ||
| 1684 | comp.client.serveBodylessMessage(.update) catch |err| switch (err) { | ||
| 1685 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 1686 | error.Canceled => |e| return e, | ||
| 1687 | else => |e| return runner.fail("unable to send message: {t}", .{e}), | ||
| 1688 | }, | ||
| 1689 | }; | ||
| 1690 | comp.state = .update; | ||
| 1691 | var out_path: ?[]const u8 = null; | ||
| 1692 | while (try comp.receiveMessage()) |message| switch (message.tag) { | ||
| 1693 | else => {}, // Ignore other messages, | ||
| 1694 | .config => { | ||
| 1695 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 1696 | comp.config = | ||
| 1697 | body_r.takeStruct(std.zig.Server.Message.Config, .little) catch unreachable; | ||
| 1698 | }, | ||
| 1699 | .emit_digest => { | ||
| 1700 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 1701 | _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; | ||
| 1702 | const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch unreachable; | ||
| 1703 | |||
| 1704 | const out_dir = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ | ||
| 1705 | "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); | ||
| 1706 | out_path = try std.Io.Dir.path.join(arena, &.{ out_dir, comp.out_name }); | ||
| 1707 | }, | ||
| 1708 | .error_bundle => { | ||
| 1709 | const eb = std.zig.Server.allocErrorBundle( | ||
| 1710 | runner.arena.allocator(), | ||
| 1711 | message.body, | ||
| 1712 | ) catch |err| switch (err) { | ||
| 1713 | error.OutOfMemory => |e| return e, | ||
| 1714 | error.EndOfStream => return runner.fail("error bundle truncated", .{}), | ||
| 1715 | }; | ||
| 1716 | if (eb.errorMessageCount() > 0) { | ||
| 1717 | try runner.eb_wip.addBundleAsRoots(eb); | ||
| 1718 | return runner.fail("unexpected compile errors", .{}); | ||
| 1719 | } | ||
| 1720 | comp.state = .idle; | ||
| 1721 | break; | ||
| 1722 | }, | ||
| 1723 | }; | ||
| 1724 | try comp.exit(); | ||
| 1725 | return out_path.?; | ||
| 1726 | } | ||
| 1727 | |||
| 1728 | fn exit(comp: *Compiler) FailError!void { | ||
| 1729 | const runner = comp.runner; | ||
| 1730 | comp.client.serveBodylessMessage(.exit) catch |err| switch (err) { | ||
| 1731 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 1732 | error.Canceled => |e| return e, | ||
| 1733 | error.BrokenPipe => {}, | ||
| 1734 | else => |e| return comp.runner.fail("unable to send exit: {t}", .{e}), | ||
| 1735 | }, | ||
| 1736 | }; | ||
| 1737 | comp.child.stdin.?.close(runner.io); | ||
| 1738 | comp.child.stdin = null; | ||
| 1739 | while (try comp.receiveMessage()) |_| {} | ||
| 1740 | if (comp.client.in.bufferedLen() > 0) return runner.fail("unable to receive messages", .{}); | ||
| 1741 | const term = comp.child.wait(runner.io) catch |err| | ||
| 1742 | return runner.fail("child process failed: {t}", .{err}); | ||
| 1743 | comp.prog_node.end(); | ||
| 1744 | switch (term) { | ||
| 1745 | .exited => |code| if (code != 0) | ||
| 1746 | return runner.fail("compiler failed with code {d}", .{code}), | ||
| 1747 | .signal => |sig| return runner.fail("compiler terminated with signal {t}", .{sig}), | ||
| 1748 | .stopped => |sig| return runner.fail("compiler stopped unexpectedly with signal {t}", .{ | ||
| 1749 | sig, | ||
| 1750 | }), | ||
| 1751 | .unknown => return runner.fail("compiler terminated unexpectedly", .{}), | ||
| 1752 | } | ||
| 1753 | } | ||
| 1754 | }; | ||
| 1755 | |||
| 1756 | const ClientArgs = struct { | ||
| 1757 | test_file: std.Io.File, | ||
| 1758 | test_kind: std.Io.File.Kind, | ||
| 1759 | zig_exe: std.Io.File, | ||
| 1760 | lib_dir: std.Io.Dir, | ||
| 1761 | src_dir: std.Io.Dir, | ||
| 1762 | targets: std.ArrayList([]const u8), | ||
| 1763 | |||
| 1764 | libc_runtimes_dir: ?std.Io.Dir, | ||
| 1765 | enable_darling: bool, | ||
| 1766 | enable_qemu: bool, | ||
| 1767 | enable_rosetta: bool, | ||
| 1768 | enable_wine: bool, | ||
| 1769 | enable_wasmtime: bool, | ||
| 1770 | |||
| 1771 | gdb_exe: ?std.Io.File, | ||
| 1772 | lldb_exe: ?std.Io.File, | ||
| 1773 | |||
| 1774 | quiet: bool, | ||
| 1775 | keep_src: bool, | ||
| 1776 | }; | ||
| 1777 | pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelable)!u8 { | ||
| 1778 | const fatal = struct { | ||
| 1779 | fn fatal(comptime fmt: []const u8, args: anytype) noreturn { | ||
| 1780 | std.log.err(fmt, args); | ||
| 1781 | std.process.exit(1); | ||
| 1782 | } | ||
| 1783 | }.fatal; | ||
| 1784 | const arena = init.arena.allocator(); | ||
| 1785 | |||
| 1786 | var test_path_arg: ?[]const u8 = null; | ||
| 1787 | var zig_path_arg: ?[]const u8 = null; | ||
| 1788 | var lib_path_arg: ?[]const u8 = null; | ||
| 1789 | var libc_runtimes_path_arg: ?[]const u8 = null; | ||
| 1790 | var gdb_path_arg: ?[]const u8 = null; | ||
| 1791 | var lldb_path_arg: ?[]const u8 = null; | ||
| 1792 | var args: ClientArgs = .{ | ||
| 1793 | .test_file = undefined, | ||
| 1794 | .test_kind = undefined, | ||
| 1795 | .zig_exe = undefined, | ||
| 1796 | .lib_dir = undefined, | ||
| 1797 | .src_dir = undefined, | ||
| 1798 | .targets = .empty, | ||
| 1799 | |||
| 1800 | .libc_runtimes_dir = null, | ||
| 1801 | .enable_darling = false, | ||
| 1802 | .enable_qemu = false, | ||
| 1803 | .enable_rosetta = false, | ||
| 1804 | .enable_wine = false, | ||
| 1805 | .enable_wasmtime = false, | ||
| 1806 | |||
| 1807 | .gdb_exe = null, | ||
| 1808 | .lldb_exe = null, | ||
| 1809 | |||
| 1810 | .quiet = false, | ||
| 1811 | .keep_src = false, | ||
| 1812 | }; | ||
| 1813 | defer args.targets.deinit(init.gpa); | ||
| 1814 | |||
| 1815 | var arg_it = try init.minimal.args.iterateAllocator(arena); | ||
| 1816 | const self = arg_it.next().?; | ||
| 1817 | while (arg_it.next()) |arg| { | ||
| 1818 | if (std.mem.eql(u8, arg, "--listen=-")) { | ||
| 1819 | var stdin_buffer: [512]u8 = undefined; | ||
| 1820 | var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer); | ||
| 1821 | var stdout_buffer: [512]u8 = undefined; | ||
| 1822 | var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer); | ||
| 1823 | var runner: Runner = .{ | ||
| 1824 | .gpa = init.gpa, | ||
| 1825 | .arena = init.arena, | ||
| 1826 | .environ_map = init.minimal.environ.createMap(init.gpa) catch |err| switch (err) { | ||
| 1827 | error.OutOfMemory => |e| return e, | ||
| 1828 | else => |e| fatal("unable to get environment: {t}", .{e}), | ||
| 1829 | }, | ||
| 1830 | .io = init.io, | ||
| 1831 | .prog_node = std.Progress.start(init.io, .{}), | ||
| 1832 | .args = .{ | ||
| 1833 | .@"test" = null, | ||
| 1834 | .manifest_file = null, | ||
| 1835 | .zig_exe = null, | ||
| 1836 | .lib_dir = null, | ||
| 1837 | .src_dir = null, | ||
| 1838 | .targets = .empty, | ||
| 1839 | .target_bytes = .empty, | ||
| 1840 | |||
| 1841 | .libc_runtimes_dir = null, | ||
| 1842 | .enable_darling = false, | ||
| 1843 | .enable_qemu = false, | ||
| 1844 | .enable_rosetta = false, | ||
| 1845 | .enable_wasmtime = false, | ||
| 1846 | .enable_wine = false, | ||
| 1847 | |||
| 1848 | .gdb_exe = null, | ||
| 1849 | .lldb_exe = null, | ||
| 1850 | |||
| 1851 | .quiet = false, | ||
| 1852 | }, | ||
| 1853 | .host = std.zig.system.resolveTargetQuery(init.io, .{}) catch |err| switch (err) { | ||
| 1854 | error.Canceled => |e| return e, | ||
| 1855 | else => |e| fatal("unable to resolve host target: {t}", .{e}), | ||
| 1856 | }, | ||
| 1857 | .server = .{ | ||
| 1858 | .in = &stdin.interface, | ||
| 1859 | .out = &stdout.interface, | ||
| 1860 | }, | ||
| 1861 | .eb_wip = try .init(init.gpa), | ||
| 1862 | }; | ||
| 1863 | defer runner.deinit(); | ||
| 1864 | switch (runner.runServer()) { | ||
| 1865 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1866 | error.ServerReadFailed => switch (stdin.err.?) { | ||
| 1867 | error.Canceled => |e| return e, | ||
| 1868 | else => |e| fatal("unable to receive message: {t}", .{e}), | ||
| 1869 | }, | ||
| 1870 | error.ServerWriteFailed => switch (stdout.err.?) { | ||
| 1871 | error.Canceled => |e| return e, | ||
| 1872 | else => |e| fatal("unable to send message: {t}", .{e}), | ||
| 1873 | }, | ||
| 1874 | error.ServerEndOfStream => fatal("no more messages", .{}), | ||
| 1875 | error.AlreadyReported => { | ||
| 1876 | var eb = try runner.eb_wip.toOwnedBundle(""); | ||
| 1877 | defer eb.deinit(init.gpa); | ||
| 1878 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { | ||
| 1879 | error.Canceled => |e| return e, | ||
| 1880 | else => {}, | ||
| 1881 | }; | ||
| 1882 | std.process.exit(1); | ||
| 1883 | }, | ||
| 1884 | } | ||
| 1885 | } else if (std.mem.eql(u8, arg, "--zig")) { | ||
| 1886 | zig_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1887 | } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| { | ||
| 1888 | zig_path_arg = zig_path; | ||
| 1889 | } else if (std.mem.eql(u8, arg, "--lib")) { | ||
| 1890 | lib_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1891 | } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| { | ||
| 1892 | lib_path_arg = lib_path; | ||
| 1893 | } else if (std.mem.eql(u8, arg, "--target")) { | ||
| 1894 | try args.targets.append( | ||
| 1895 | init.gpa, | ||
| 1896 | try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after {q}", .{arg})), | ||
| 1897 | ); | ||
| 1898 | } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| { | ||
| 1899 | try args.targets.append(init.gpa, try arena.dupe(u8, target)); | ||
| 1900 | } else if (std.mem.eql(u8, arg, "--libc-runtimes")) { | ||
| 1901 | libc_runtimes_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1902 | } else if (std.mem.cutPrefix(u8, arg, "--libc-runtimes=")) |libc_runtimes_path| { | ||
| 1903 | libc_runtimes_path_arg = libc_runtimes_path; | ||
| 1904 | } else if (std.mem.eql(u8, arg, "-fdarling")) { | ||
| 1905 | args.enable_darling = true; | ||
| 1906 | } else if (std.mem.eql(u8, arg, "-fqemu")) { | ||
| 1907 | args.enable_qemu = true; | ||
| 1908 | } else if (std.mem.eql(u8, arg, "-frosetta")) { | ||
| 1909 | args.enable_rosetta = true; | ||
| 1910 | } else if (std.mem.eql(u8, arg, "-fwine")) { | ||
| 1911 | args.enable_wine = true; | ||
| 1912 | } else if (std.mem.eql(u8, arg, "-fwasmtime")) { | ||
| 1913 | args.enable_wasmtime = true; | ||
| 1914 | } else if (std.mem.eql(u8, arg, "--gdb")) { | ||
| 1915 | gdb_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1916 | } else if (std.mem.cutPrefix(u8, arg, "--gdb=")) |gdb_path| { | ||
| 1917 | gdb_path_arg = gdb_path; | ||
| 1918 | } else if (std.mem.eql(u8, arg, "--lldb")) { | ||
| 1919 | lldb_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1920 | } else if (std.mem.cutPrefix(u8, arg, "--lldb=")) |lldb_path| { | ||
| 1921 | lldb_path_arg = lldb_path; | ||
| 1922 | } else if (std.mem.eql(u8, arg, "--quiet")) { | ||
| 1923 | args.quiet = true; | ||
| 1924 | } else if (std.mem.eql(u8, arg, "--keep-src")) { | ||
| 1925 | args.keep_src = true; | ||
| 1926 | } else { | ||
| 1927 | if (test_path_arg) |_| fatal("unexpected arg {q}", .{arg}); | ||
| 1928 | test_path_arg = arg; | ||
| 1929 | } | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | const test_path = test_path_arg orelse fatal("missing \"path/to/test\" arg", .{}); | ||
| 1933 | const zig_path = zig_path_arg orelse fatal("missing \"--zig=path/to/zig\" arg", .{}); | ||
| 1934 | const lib_path = lib_path_arg orelse fatal("missing \"--lib=path/to/lib\" arg", .{}); | ||
| 1935 | |||
| 1936 | const cwd: std.Io.Dir = .cwd(); | ||
| 1937 | args.test_file = cwd.openFile(init.io, test_path, .{ | ||
| 1938 | .allow_directory = true, | ||
| 1939 | }) catch |err| switch (err) { | ||
| 1940 | error.Canceled => |e| return e, | ||
| 1941 | else => |e| fatal("unable to open {q}: {t}", .{ test_path, e }), | ||
| 1942 | }; | ||
| 1943 | defer args.test_file.close(init.io); | ||
| 1944 | args.test_kind = (args.test_file.stat(init.io) catch |err| switch (err) { | ||
| 1945 | error.Canceled => |e| return e, | ||
| 1946 | else => |e| fatal("unable to stat {q}: {t}", .{ test_path, e }), | ||
| 1947 | }).kind; | ||
| 1948 | args.zig_exe = cwd.openFile(init.io, zig_path, .{}) catch |err| switch (err) { | ||
| 1949 | error.Canceled => |e| return e, | ||
| 1950 | else => |e| fatal("unable to open {q}: {t}", .{ zig_path, e }), | ||
| 1951 | }; | ||
| 1952 | defer args.zig_exe.close(init.io); | ||
| 1953 | args.lib_dir = cwd.openDir(init.io, lib_path, .{}) catch |err| switch (err) { | ||
| 1954 | error.Canceled => |e| return e, | ||
| 1955 | else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), | ||
| 1956 | }; | ||
| 1957 | defer args.lib_dir.close(init.io); | ||
| 1958 | if (libc_runtimes_path_arg) |libc_runtimes_path| args.libc_runtimes_dir = cwd.openDir( | ||
| 1959 | init.io, | ||
| 1960 | libc_runtimes_path, | ||
| 1961 | .{}, | ||
| 1962 | ) catch |err| switch (err) { | ||
| 1963 | error.Canceled => |e| return e, | ||
| 1964 | else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), | ||
| 1965 | }; | ||
| 1966 | defer if (args.libc_runtimes_dir) |libc_runtimes_dir| libc_runtimes_dir.close(init.io); | ||
| 1967 | if (gdb_path_arg) |gdb_path| args.gdb_exe = cwd.openFile(init.io, gdb_path, .{}) catch |err| switch (err) { | ||
| 1968 | error.Canceled => |e| return e, | ||
| 1969 | else => |e| fatal("unable to open {q}: {t}", .{ gdb_path, e }), | ||
| 1970 | }; | ||
| 1971 | defer if (args.gdb_exe) |gdb_exe| gdb_exe.close(init.io); | ||
| 1972 | if (lldb_path_arg) |lldb_path| args.lldb_exe = cwd.openFile(init.io, lldb_path, .{}) catch |err| switch (err) { | ||
| 1973 | error.Canceled => |e| return e, | ||
| 1974 | else => |e| fatal("unable to open {q}: {t}", .{ lldb_path, e }), | ||
| 1975 | }; | ||
| 1976 | defer if (args.lldb_exe) |lldb_exe| lldb_exe.close(init.io); | ||
| 1977 | |||
| 1978 | const src_dir_path = "src_" ++ std.fmt.hex(rand_int: { | ||
| 1979 | var rand_int: u64 = undefined; | ||
| 1980 | init.io.random(@ptrCast(&rand_int)); | ||
| 1981 | break :rand_int rand_int; | ||
| 1982 | }); | ||
| 1983 | args.src_dir = cwd.createDirPathOpen(init.io, src_dir_path, .{}) catch |err| switch (err) { | ||
| 1984 | error.Canceled => |e| return e, | ||
| 1985 | else => |e| fatal("unable to create {q}: {t}", .{ src_dir_path, e }), | ||
| 1986 | }; | ||
| 1987 | defer { | ||
| 1988 | args.src_dir.close(init.io); | ||
| 1989 | if (!args.keep_src) cwd.deleteTree(init.io, src_dir_path) catch |err| { | ||
| 1990 | std.log.warn("unable to delete tree {q}: {t}", .{ src_dir_path, err }); | ||
| 1991 | }; | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | var inherit_dirs: std.ArrayList(std.Io.Dir) = .initBuffer(try arena.alloc(std.Io.Dir, 3)); | ||
| 1995 | var inherit_files: std.ArrayList(std.Io.File) = .initBuffer(try arena.alloc(std.Io.File, 4)); | ||
| 1996 | inherit_files.appendSliceAssumeCapacity(&.{ args.test_file, args.zig_exe }); | ||
| 1997 | inherit_dirs.appendSliceAssumeCapacity(&.{ args.lib_dir, args.src_dir }); | ||
| 1998 | if (args.libc_runtimes_dir) |libc_runtimes_dir| inherit_dirs.appendAssumeCapacity(libc_runtimes_dir); | ||
| 1999 | if (args.gdb_exe) |gdb_exe| inherit_files.appendAssumeCapacity(gdb_exe); | ||
| 2000 | if (args.lldb_exe) |lldb_exe| inherit_files.appendAssumeCapacity(lldb_exe); | ||
| 2001 | var child = std.process.spawn(init.io, .{ | ||
| 2002 | .argv = &.{ self, "--listen=-" }, | ||
| 2003 | .stdin = .pipe, | ||
| 2004 | .stdout = .pipe, | ||
| 2005 | .inherit_dirs = inherit_dirs.items, | ||
| 2006 | .inherit_files = inherit_files.items, | ||
| 2007 | }) catch |err| switch (err) { | ||
| 2008 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 2009 | else => |e| fatal("unable to spawn runner: {t}", .{e}), | ||
| 2010 | }; | ||
| 2011 | |||
| 2012 | var child_reader_buffer: [512]u8 = undefined; | ||
| 2013 | var child_reader = child.stdout.?.readerStreaming(init.io, &child_reader_buffer); | ||
| 2014 | var child_writer_buffer: [512]u8 = undefined; | ||
| 2015 | var child_writer = child.stdin.?.writerStreaming(init.io, &child_writer_buffer); | ||
| 2016 | var client: std.zig.Client = .{ | ||
| 2017 | .in = &child_reader.interface, | ||
| 2018 | .out = &child_writer.interface, | ||
| 2019 | }; | ||
| 2020 | const success = runClient(init.gpa, arena, init.io, &client, &args) catch |err| switch (err) { | ||
| 2021 | error.OutOfMemory => |e| return e, | ||
| 2022 | error.ReadFailed => switch (child_reader.err.?) { | ||
| 2023 | error.Canceled => |e| return e, | ||
| 2024 | else => |e| fatal("unable to receive message: {t}", .{e}), | ||
| 2025 | }, | ||
| 2026 | error.WriteFailed => switch (child_writer.err.?) { | ||
| 2027 | error.Canceled => |e| return e, | ||
| 2028 | else => |e| fatal("unable to send message: {t}", .{e}), | ||
| 2029 | }, | ||
| 2030 | error.EndOfStream => fatal("no more messages", .{}), | ||
| 2031 | }; | ||
| 2032 | _ = child.wait(init.io) catch |err| switch (err) { | ||
| 2033 | error.Canceled => |e| return e, | ||
| 2034 | else => |e| fatal("unable to wait for runner: {t}", .{e}), | ||
| 2035 | }; | ||
| 2036 | return if (success) 0 else 1; | ||
| 2037 | } | ||
| 2038 | fn runClient( | ||
| 2039 | gpa: std.mem.Allocator, | ||
| 2040 | arena: std.mem.Allocator, | ||
| 2041 | io: std.Io, | ||
| 2042 | client: *std.zig.Client, | ||
| 2043 | args: *const ClientArgs, | ||
| 2044 | ) (std.mem.Allocator.Error || std.Io.Reader.Error || std.Io.Writer.Error)!bool { | ||
| 2045 | var stdout_buffer: [512]u8 = undefined; | ||
| 2046 | var stdout_fw: std.Io.File.Writer = .initStreaming(.stdout(), io, &stdout_buffer); | ||
| 2047 | const stdout = &stdout_fw.interface; | ||
| 2048 | var stderr_buffer: [512]u8 = undefined; | ||
| 2049 | var stderr_fw: std.Io.File.Writer = .initStreaming(.stderr(), io, &stderr_buffer); | ||
| 2050 | const stderr = &stderr_fw.interface; | ||
| 2051 | |||
| 2052 | var argv: std.ArrayList(u8) = .empty; | ||
| 2053 | defer argv.deinit(gpa); | ||
| 2054 | const Arg = std.zig.Client.Message.Arg; | ||
| 2055 | |||
| 2056 | try argv.append(gpa, @backingInt(@as(Arg, switch (args.test_kind) { | ||
| 2057 | else => unreachable, | ||
| 2058 | .file => .input_file, | ||
| 2059 | .directory => .input_dir, | ||
| 2060 | }))); | ||
| 2061 | try argv.appendSlice(gpa, @ptrCast(&args.test_file.handle)); | ||
| 2062 | |||
| 2063 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 2064 | try argv.appendSlice(gpa, "--zig="); | ||
| 2065 | try argv.append(gpa, 0); | ||
| 2066 | try argv.append(gpa, @backingInt(Arg.input_file)); | ||
| 2067 | try argv.appendSlice(gpa, @ptrCast(&args.zig_exe.handle)); | ||
| 2068 | |||
| 2069 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 2070 | try argv.appendSlice(gpa, "--lib="); | ||
| 2071 | try argv.append(gpa, 0); | ||
| 2072 | try argv.append(gpa, @backingInt(Arg.input_dir)); | ||
| 2073 | try argv.appendSlice(gpa, @ptrCast(&args.lib_dir.handle)); | ||
| 2074 | |||
| 2075 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 2076 | try argv.appendSlice(gpa, "--src="); | ||
| 2077 | try argv.append(gpa, 0); | ||
| 2078 | try argv.append(gpa, @backingInt(Arg.output_dir)); | ||
| 2079 | try argv.appendSlice(gpa, @ptrCast(&args.src_dir.handle)); | ||
| 2080 | |||
| 2081 | for (args.targets.items) |target| { | ||
| 2082 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2083 | try argv.appendSlice(gpa, "--target"); | ||
| 2084 | try argv.append(gpa, 0); | ||
| 2085 | |||
| 2086 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2087 | try argv.appendSlice(gpa, target); | ||
| 2088 | try argv.append(gpa, 0); | ||
| 2089 | } | ||
| 2090 | |||
| 2091 | if (args.libc_runtimes_dir) |libc_runtimes_dir| { | ||
| 2092 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 2093 | try argv.appendSlice(gpa, "--libc-runtimes="); | ||
| 2094 | try argv.append(gpa, 0); | ||
| 2095 | try argv.append(gpa, @backingInt(Arg.input_dir)); | ||
| 2096 | try argv.appendSlice(gpa, @ptrCast(&libc_runtimes_dir.handle)); | ||
| 2097 | } | ||
| 2098 | if (args.enable_darling) { | ||
| 2099 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2100 | try argv.appendSlice(gpa, "-fdarling"); | ||
| 2101 | try argv.append(gpa, 0); | ||
| 2102 | } | ||
| 2103 | if (args.enable_qemu) { | ||
| 2104 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2105 | try argv.appendSlice(gpa, "-fqemu"); | ||
| 2106 | try argv.append(gpa, 0); | ||
| 2107 | } | ||
| 2108 | if (args.enable_rosetta) { | ||
| 2109 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2110 | try argv.appendSlice(gpa, "-frosetta"); | ||
| 2111 | try argv.append(gpa, 0); | ||
| 2112 | } | ||
| 2113 | if (args.enable_wine) { | ||
| 2114 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2115 | try argv.appendSlice(gpa, "-fwine"); | ||
| 2116 | try argv.append(gpa, 0); | ||
| 2117 | } | ||
| 2118 | if (args.enable_wasmtime) { | ||
| 2119 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2120 | try argv.appendSlice(gpa, "-fwasmtime"); | ||
| 2121 | try argv.append(gpa, 0); | ||
| 2122 | } | ||
| 2123 | |||
| 2124 | if (args.gdb_exe) |gdb_exe| { | ||
| 2125 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 2126 | try argv.appendSlice(gpa, "--gdb="); | ||
| 2127 | try argv.append(gpa, 0); | ||
| 2128 | try argv.append(gpa, @backingInt(Arg.input_file)); | ||
| 2129 | try argv.appendSlice(gpa, @ptrCast(&gdb_exe.handle)); | ||
| 2130 | } | ||
| 2131 | if (args.lldb_exe) |lldb_exe| { | ||
| 2132 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 2133 | try argv.appendSlice(gpa, "--lldb="); | ||
| 2134 | try argv.append(gpa, 0); | ||
| 2135 | try argv.append(gpa, @backingInt(Arg.input_file)); | ||
| 2136 | try argv.appendSlice(gpa, @ptrCast(&lldb_exe.handle)); | ||
| 2137 | } | ||
| 2138 | |||
| 2139 | if (args.quiet) { | ||
| 2140 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2141 | try argv.appendSlice(gpa, "--quiet"); | ||
| 2142 | try argv.append(gpa, 0); | ||
| 2143 | } | ||
| 2144 | |||
| 2145 | try client.serveMessageHeader(.{ | ||
| 2146 | .tag = .args, | ||
| 2147 | .bytes_len = @intCast(argv.items.len), | ||
| 2148 | }); | ||
| 2149 | try client.out.writeAll(argv.items); | ||
| 2150 | |||
| 2151 | var success = true; | ||
| 2152 | var metadata: struct { | ||
| 2153 | index: u32, | ||
| 2154 | len: u32, | ||
| 2155 | names: []u32, | ||
| 2156 | expected_panic_msgs: []u32, | ||
| 2157 | string_bytes: []u8, | ||
| 2158 | |||
| 2159 | fn name(m: *@This()) []const u8 { | ||
| 2160 | return std.mem.sliceTo(m.string_bytes[m.names[m.index - 1]..], 0); | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | fn expectedPanicMsg(m: *@This()) ?[:0]const u8 { | ||
| 2164 | return switch (m.expected_panic_msgs[m.index - 1]) { | ||
| 2165 | 0 => null, | ||
| 2166 | else => |offset| m.string_bytes[offset..std.mem.findScalarPos( | ||
| 2167 | u8, | ||
| 2168 | m.string_bytes, | ||
| 2169 | offset, | ||
| 2170 | 0, | ||
| 2171 | ).? :0], | ||
| 2172 | }; | ||
| 2173 | } | ||
| 2174 | |||
| 2175 | fn next(m: *@This()) ?u32 { | ||
| 2176 | if (m.len - m.index == 0) return null; | ||
| 2177 | defer m.index += 1; | ||
| 2178 | return m.index; | ||
| 2179 | } | ||
| 2180 | } = .{ .index = 0, .len = 0, .names = &.{}, .expected_panic_msgs = &.{}, .string_bytes = &.{} }; | ||
| 2181 | try client.serveBodylessMessage(.query_test_metadata); | ||
| 2182 | while (true) { | ||
| 2183 | const hdr = try client.receiveMessage(); | ||
| 2184 | switch (hdr.tag) { | ||
| 2185 | else => try client.in.discardAll(hdr.bytes_len), | ||
| 2186 | .zig_version => { | ||
| 2187 | const actual_version = try client.in.take(hdr.bytes_len); | ||
| 2188 | if (!std.mem.eql(u8, builtin.zig_version_string, actual_version)) { | ||
| 2189 | try stderr.print( | ||
| 2190 | "error: zig version mismatch compiler test runner vs compiler: {q} vs {q}\n", | ||
| 2191 | .{ builtin.zig_version_string, actual_version }, | ||
| 2192 | ); | ||
| 2193 | try stderr.flush(); | ||
| 2194 | std.process.exit(1); | ||
| 2195 | } | ||
| 2196 | }, | ||
| 2197 | .error_bundle => { | ||
| 2198 | const body = try arena.alloc(u8, hdr.bytes_len); | ||
| 2199 | try client.in.readSliceAll(body); | ||
| 2200 | const eb = std.zig.Server.allocErrorBundle(arena, body) catch |err| switch (err) { | ||
| 2201 | error.OutOfMemory => |e| return e, | ||
| 2202 | error.EndOfStream => { | ||
| 2203 | try stderr.writeAll("error bundle truncated\n"); | ||
| 2204 | try stderr.flush(); | ||
| 2205 | std.process.exit(1); | ||
| 2206 | }, | ||
| 2207 | }; | ||
| 2208 | success = false; | ||
| 2209 | eb.renderToStderr(io, .{}, .auto) catch {}; | ||
| 2210 | }, | ||
| 2211 | .test_metadata => { | ||
| 2212 | const tm_hdr = | ||
| 2213 | try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little); | ||
| 2214 | metadata = .{ | ||
| 2215 | .index = 0, | ||
| 2216 | .len = tm_hdr.tests_len, | ||
| 2217 | .names = try arena.alloc(u32, tm_hdr.tests_len), | ||
| 2218 | .expected_panic_msgs = try arena.alloc(u32, tm_hdr.tests_len), | ||
| 2219 | .string_bytes = try arena.alloc(u8, tm_hdr.string_bytes_len), | ||
| 2220 | }; | ||
| 2221 | try client.in.readSliceEndian(u32, metadata.names, .little); | ||
| 2222 | try client.in.readSliceEndian(u32, metadata.expected_panic_msgs, .little); | ||
| 2223 | try client.in.readSliceAll(metadata.string_bytes); | ||
| 2224 | try client.serveRunTest(metadata.next() orelse break); | ||
| 2225 | }, | ||
| 2226 | .test_results => { | ||
| 2227 | const tr = try client.in.takeStruct(std.zig.Server.Message.TestResults, .little); | ||
| 2228 | switch (tr.flags.status) { | ||
| 2229 | .pass, .skip => {}, | ||
| 2230 | .fail => success = false, | ||
| 2231 | } | ||
| 2232 | try stdout.print("[{t}] {s}\n", .{ tr.flags.status, metadata.name() }); | ||
| 2233 | try stdout.flush(); | ||
| 2234 | try client.serveRunTest(metadata.next() orelse break); | ||
| 2235 | }, | ||
| 2236 | } | ||
| 2237 | } | ||
| 2238 | try client.serveBodylessMessage(.exit); | ||
| 2239 | return success; | ||
| 2240 | } | ||
test/debugger/basic/manifest+2-2| ... | @@ -3,6 +3,6 @@ | ... | @@ -3,6 +3,6 @@ |
| 3 | -Mroot=basic.zig | 3 | -Mroot=basic.zig |
| 4 | #} | 4 | #} |
| 5 | 5 | ||
| 6 | #write basic.zig basic.zig | 6 | #write basic.zig @basic.zig |
| 7 | #update | 7 | #update |
| 8 | #check lldb lldb | 8 | #check lldb @lldb |
test/debugger/enums+1-3| ... | @@ -1,6 +1,4 @@ | ... | @@ -1,6 +1,4 @@ |
| 1 | #todo { | 1 | #todo "passes, but prints errors" |
| 2 | passes, but prints errors | ||
| 3 | #} | ||
| 4 | #skip x86_64-linux-none-whole-selfhosted-new-nopic | 2 | #skip x86_64-linux-none-whole-selfhosted-new-nopic |
| 5 | #skip x86_64-linux-none-whole-selfhosted-new-pic | 3 | #skip x86_64-linux-none-whole-selfhosted-new-pic |
| 6 | #skip x86_64-linux-none-incremental-selfhosted-new-nopic | 4 | #skip x86_64-linux-none-incremental-selfhosted-new-nopic |
test/debugger/errors+1-3| ... | @@ -1,6 +1,4 @@ | ... | @@ -1,6 +1,4 @@ |
| 1 | #todo { | 1 | #todo "passes, but prints errors" |
| 2 | passes, but prints errors | ||
| 3 | #} | ||
| 4 | #skip x86_64-linux-none-whole-selfhosted-new-nopic | 2 | #skip x86_64-linux-none-whole-selfhosted-new-nopic |
| 5 | #skip x86_64-linux-none-whole-selfhosted-new-pic | 3 | #skip x86_64-linux-none-whole-selfhosted-new-pic |
| 6 | #skip x86_64-linux-none-incremental-selfhosted-new-nopic | 4 | #skip x86_64-linux-none-incremental-selfhosted-new-nopic |
test/debugger/inline_call/manifest+6-8| ... | @@ -1,6 +1,4 @@ | ... | @@ -1,6 +1,4 @@ |
| 1 | #todo { | 1 | #todo "implement inline line info in lldb" |
| 2 | implement inline line info in lldb | ||
| 3 | #} | ||
| 4 | #skip x86_64-linux-none-incremental-selfhosted-new-nopic | 2 | #skip x86_64-linux-none-incremental-selfhosted-new-nopic |
| 5 | #skip x86_64-linux-none-incremental-selfhosted-new-pic | 3 | #skip x86_64-linux-none-incremental-selfhosted-new-pic |
| 6 | 4 | ||
| ... | @@ -10,9 +8,9 @@ implement inline line info in lldb | ... | @@ -10,9 +8,9 @@ implement inline line info in lldb |
| 10 | -Mmodule=mod0.zig | 8 | -Mmodule=mod0.zig |
| 11 | #} | 9 | #} |
| 12 | 10 | ||
| 13 | #write root0.zig root0.zig | 11 | #write root0.zig @root0.zig |
| 14 | #write root1.zig root1.zig | 12 | #write root1.zig @root1.zig |
| 15 | #write mod0.zig mod0.zig | 13 | #write mod0.zig @mod0.zig |
| 16 | #write mod1.zig mod1.zig | 14 | #write mod1.zig @mod1.zig |
| 17 | #update | 15 | #update |
| 18 | #check lldb lldb | 16 | #check lldb @lldb |
test/debugger/matrix.zon created+68| ... | @@ -0,0 +1,68 @@ | ||
| 1 | .{ | ||
| 2 | .{ | ||
| 3 | .target = .{ | ||
| 4 | .cpu_arch = .x86_64, | ||
| 5 | .os_tag = .linux, | ||
| 6 | .abi = .none, | ||
| 7 | }, | ||
| 8 | .cache_mode = .whole, | ||
| 9 | .backend = .selfhosted, | ||
| 10 | .linker = .old, | ||
| 11 | .pic = .nopic, | ||
| 12 | }, | ||
| 13 | .{ | ||
| 14 | .target = .{ | ||
| 15 | .cpu_arch = .x86_64, | ||
| 16 | .os_tag = .linux, | ||
| 17 | .abi = .none, | ||
| 18 | }, | ||
| 19 | .cache_mode = .whole, | ||
| 20 | .backend = .selfhosted, | ||
| 21 | .linker = .old, | ||
| 22 | .pic = .pic, | ||
| 23 | }, | ||
| 24 | .{ | ||
| 25 | .target = .{ | ||
| 26 | .cpu_arch = .x86_64, | ||
| 27 | .os_tag = .linux, | ||
| 28 | .abi = .none, | ||
| 29 | }, | ||
| 30 | .cache_mode = .whole, | ||
| 31 | .backend = .selfhosted, | ||
| 32 | .linker = .new, | ||
| 33 | .pic = .nopic, | ||
| 34 | }, | ||
| 35 | .{ | ||
| 36 | .target = .{ | ||
| 37 | .cpu_arch = .x86_64, | ||
| 38 | .os_tag = .linux, | ||
| 39 | .abi = .none, | ||
| 40 | }, | ||
| 41 | .cache_mode = .whole, | ||
| 42 | .backend = .selfhosted, | ||
| 43 | .linker = .new, | ||
| 44 | .pic = .pic, | ||
| 45 | }, | ||
| 46 | .{ | ||
| 47 | .target = .{ | ||
| 48 | .cpu_arch = .x86_64, | ||
| 49 | .os_tag = .linux, | ||
| 50 | .abi = .none, | ||
| 51 | }, | ||
| 52 | .cache_mode = .incremental, | ||
| 53 | .backend = .selfhosted, | ||
| 54 | .linker = .new, | ||
| 55 | .pic = .nopic, | ||
| 56 | }, | ||
| 57 | .{ | ||
| 58 | .target = .{ | ||
| 59 | .cpu_arch = .x86_64, | ||
| 60 | .os_tag = .linux, | ||
| 61 | .abi = .none, | ||
| 62 | }, | ||
| 63 | .cache_mode = .incremental, | ||
| 64 | .backend = .selfhosted, | ||
| 65 | .linker = .new, | ||
| 66 | .pic = .pic, | ||
| 67 | }, | ||
| 68 | } | ||
test/debugger/multi_array_list/manifest+2-2| ... | @@ -3,6 +3,6 @@ | ... | @@ -3,6 +3,6 @@ |
| 3 | -Mroot=main.zig | 3 | -Mroot=main.zig |
| 4 | #} | 4 | #} |
| 5 | 5 | ||
| 6 | #write main.zig main.zig | 6 | #write main.zig @main.zig |
| 7 | #update | 7 | #update |
| 8 | #check lldb lldb | 8 | #check lldb @lldb |
test/debugger/step_single_stmt_loops/manifest+2-2| ... | @@ -3,6 +3,6 @@ | ... | @@ -3,6 +3,6 @@ |
| 3 | -Mroot=step_single_stmt_loops.zig | 3 | -Mroot=step_single_stmt_loops.zig |
| 4 | #} | 4 | #} |
| 5 | 5 | ||
| 6 | #write step_single_stmt_loops.zig step_single_stmt_loops.zig | 6 | #write step_single_stmt_loops.zig @step_single_stmt_loops.zig |
| 7 | #update | 7 | #update |
| 8 | #check lldb lldb | 8 | #check lldb @lldb |
test/debugger/unions+1-3| ... | @@ -1,6 +1,4 @@ | ... | @@ -1,6 +1,4 @@ |
| 1 | #todo { | 1 | #todo "passes, but prints errors" |
| 2 | passes, but prints errors | ||
| 3 | #} | ||
| 4 | #skip x86_64-linux-none-whole-selfhosted-new-nopic | 2 | #skip x86_64-linux-none-whole-selfhosted-new-nopic |
| 5 | #skip x86_64-linux-none-whole-selfhosted-new-pic | 3 | #skip x86_64-linux-none-whole-selfhosted-new-pic |
| 6 | #skip x86_64-linux-none-incremental-selfhosted-new-nopic | 4 | #skip x86_64-linux-none-incremental-selfhosted-new-nopic |
test/incremental/add_decl+5-10| ... | @@ -24,8 +24,7 @@ const foo = "good morning\n"; | ... | @@ -24,8 +24,7 @@ const foo = "good morning\n"; |
| 24 | const bar = "good evening\n"; | 24 | const bar = "good evening\n"; |
| 25 | const io = std.Io.Threaded.global_single_threaded.io(); | 25 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 26 | #} | 26 | #} |
| 27 | #update { | 27 | #update "add new declaration" |
| 28 | add new declaration#} | ||
| 29 | #check stdout { | 28 | #check stdout { |
| 30 | good morning | 29 | good morning |
| 31 | #} | 30 | #} |
| ... | @@ -39,8 +38,7 @@ const foo = "good morning\n"; | ... | @@ -39,8 +38,7 @@ const foo = "good morning\n"; |
| 39 | const bar = "good evening\n"; | 38 | const bar = "good evening\n"; |
| 40 | const io = std.Io.Threaded.global_single_threaded.io(); | 39 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 41 | #} | 40 | #} |
| 42 | #update { | 41 | #update "reference new declaration" |
| 43 | reference new declaration#} | ||
| 44 | #check stdout { | 42 | #check stdout { |
| 45 | good evening | 43 | good evening |
| 46 | #} | 44 | #} |
| ... | @@ -54,8 +52,7 @@ const foo = "good morning\n"; | ... | @@ -54,8 +52,7 @@ const foo = "good morning\n"; |
| 54 | const bar = "good evening\n"; | 52 | const bar = "good evening\n"; |
| 55 | const io = std.Io.Threaded.global_single_threaded.io(); | 53 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 56 | #} | 54 | #} |
| 57 | #update { | 55 | #update "reference missing declaration" |
| 58 | reference missing declaration#} | ||
| 59 | #check errors { | 56 | #check errors { |
| 60 | main.zig:3:52: error: use of undeclared identifier 'qux' | 57 | main.zig:3:52: error: use of undeclared identifier 'qux' |
| 61 | #} | 58 | #} |
| ... | @@ -70,8 +67,7 @@ const bar = "good evening\n"; | ... | @@ -70,8 +67,7 @@ const bar = "good evening\n"; |
| 70 | const qux = "good night\n"; | 67 | const qux = "good night\n"; |
| 71 | const io = std.Io.Threaded.global_single_threaded.io(); | 68 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 72 | #} | 69 | #} |
| 73 | #update { | 70 | #update "add missing declaration" |
| 74 | add missing declaration#} | ||
| 75 | #check stdout { | 71 | #check stdout { |
| 76 | good night | 72 | good night |
| 77 | #} | 73 | #} |
| ... | @@ -84,8 +80,7 @@ pub fn main() !void { | ... | @@ -84,8 +80,7 @@ pub fn main() !void { |
| 84 | const qux = "good night\n"; | 80 | const qux = "good night\n"; |
| 85 | const io = std.Io.Threaded.global_single_threaded.io(); | 81 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 86 | #} | 82 | #} |
| 87 | #update { | 83 | #update "remove unused declarations" |
| 88 | remove unused declarations#} | ||
| 89 | #check stdout { | 84 | #check stdout { |
| 90 | good night | 85 | good night |
| 91 | #} | 86 | #} |
test/incremental/add_decl_namespaced+5-10| ... | @@ -24,8 +24,7 @@ const foo = "good morning\n"; | ... | @@ -24,8 +24,7 @@ const foo = "good morning\n"; |
| 24 | const bar = "good evening\n"; | 24 | const bar = "good evening\n"; |
| 25 | const io = std.Io.Threaded.global_single_threaded.io(); | 25 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 26 | #} | 26 | #} |
| 27 | #update { | 27 | #update "add new declaration" |
| 28 | add new declaration#} | ||
| 29 | #check stdout { | 28 | #check stdout { |
| 30 | good morning | 29 | good morning |
| 31 | #} | 30 | #} |
| ... | @@ -39,8 +38,7 @@ const foo = "good morning\n"; | ... | @@ -39,8 +38,7 @@ const foo = "good morning\n"; |
| 39 | const bar = "good evening\n"; | 38 | const bar = "good evening\n"; |
| 40 | const io = std.Io.Threaded.global_single_threaded.io(); | 39 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 41 | #} | 40 | #} |
| 42 | #update { | 41 | #update "reference new declaration" |
| 43 | reference new declaration#} | ||
| 44 | #check stdout { | 42 | #check stdout { |
| 45 | good evening | 43 | good evening |
| 46 | #} | 44 | #} |
| ... | @@ -54,8 +52,7 @@ const foo = "good morning\n"; | ... | @@ -54,8 +52,7 @@ const foo = "good morning\n"; |
| 54 | const bar = "good evening\n"; | 52 | const bar = "good evening\n"; |
| 55 | const io = std.Io.Threaded.global_single_threaded.io(); | 53 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 56 | #} | 54 | #} |
| 57 | #update { | 55 | #update "reference missing declaration" |
| 58 | reference missing declaration#} | ||
| 59 | #check errors { | 56 | #check errors { |
| 60 | main.zig:3:59: error: root source file struct 'main' has no member named 'qux' | 57 | main.zig:3:59: error: root source file struct 'main' has no member named 'qux' |
| 61 | main.zig:1:1: note: struct declared here | 58 | main.zig:1:1: note: struct declared here |
| ... | @@ -71,8 +68,7 @@ const bar = "good evening\n"; | ... | @@ -71,8 +68,7 @@ const bar = "good evening\n"; |
| 71 | const qux = "good night\n"; | 68 | const qux = "good night\n"; |
| 72 | const io = std.Io.Threaded.global_single_threaded.io(); | 69 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 73 | #} | 70 | #} |
| 74 | #update { | 71 | #update "add missing declaration" |
| 75 | add missing declaration#} | ||
| 76 | #check stdout { | 72 | #check stdout { |
| 77 | good night | 73 | good night |
| 78 | #} | 74 | #} |
| ... | @@ -85,8 +81,7 @@ pub fn main() !void { | ... | @@ -85,8 +81,7 @@ pub fn main() !void { |
| 85 | const qux = "good night\n"; | 81 | const qux = "good night\n"; |
| 86 | const io = std.Io.Threaded.global_single_threaded.io(); | 82 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 87 | #} | 83 | #} |
| 88 | #update { | 84 | #update "remove unused declarations" |
| 89 | remove unused declarations#} | ||
| 90 | #check stdout { | 85 | #check stdout { |
| 91 | good night | 86 | good night |
| 92 | #} | 87 | #} |
test/incremental/add_decls_around_bad_field_expr+1-2| ... | @@ -32,8 +32,7 @@ pub fn main() void { | ... | @@ -32,8 +32,7 @@ pub fn main() void { |
| 32 | _ = @as(@This(), undefined); | 32 | _ = @as(@This(), undefined); |
| 33 | } | 33 | } |
| 34 | #} | 34 | #} |
| 35 | #update { | 35 | #update "add more decls around error" |
| 36 | add more decls around error#} | ||
| 37 | #check errors { | 36 | #check errors { |
| 38 | main.zig:4:15: error: root source file struct 'main' has no member named 'does_not_exist' | 37 | main.zig:4:15: error: root source file struct 'main' has no member named 'does_not_exist' |
| 39 | main.zig:1:1: note: struct declared here | 38 | main.zig:1:1: note: struct declared here |
test/incremental/add_field_and_nested_struct_uses_changed_decl+3-6| ... | @@ -14,8 +14,7 @@ const S = struct { | ... | @@ -14,8 +14,7 @@ const S = struct { |
| 14 | }; | 14 | }; |
| 15 | #} | 15 | #} |
| 16 | #update | 16 | #update |
| 17 | #check stdout { | 17 | #check stdout |
| 18 | #} | ||
| 19 | 18 | ||
| 20 | #write main.zig { | 19 | #write main.zig { |
| 21 | pub fn main() void { | 20 | pub fn main() void { |
| ... | @@ -29,7 +28,5 @@ const S = struct { | ... | @@ -29,7 +28,5 @@ const S = struct { |
| 29 | bar: u32, | 28 | bar: u32, |
| 30 | }; | 29 | }; |
| 31 | #} | 30 | #} |
| 32 | #update { | 31 | #update "add field to outer struct, change decl used by inner struct" |
| 33 | add field to outer struct, change decl used by inner struct#} | 32 | #check stdout |
| 34 | #check stdout { | ||
| 35 | #} |
test/incremental/add_remove_struct_fields/manifest+7-10| ... | @@ -2,30 +2,27 @@ | ... | @@ -2,30 +2,27 @@ |
| 2 | -Mroot=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | 3 | #} |
| 4 | 4 | ||
| 5 | #write main.zig 0/main.zig | 5 | #write main.zig @0/main.zig |
| 6 | #update | 6 | #update |
| 7 | #check stdout { | 7 | #check stdout { |
| 8 | 1 100 | 8 | 1 100 |
| 9 | #} | 9 | #} |
| 10 | 10 | ||
| 11 | #write main.zig 1/main.zig | 11 | #write main.zig @1/main.zig |
| 12 | #update { | 12 | #update "add a field" |
| 13 | add a field#} | ||
| 14 | #check stdout { | 13 | #check stdout { |
| 15 | 2 100 | 14 | 2 100 |
| 16 | #} | 15 | #} |
| 17 | 16 | ||
| 18 | #write main.zig 2/main.zig | 17 | #write main.zig @2/main.zig |
| 19 | #update { | 18 | #update "remove all fields" |
| 20 | remove all fields#} | ||
| 21 | #check errors { | 19 | #check errors { |
| 22 | main.zig:15:24: error: no field named 'x' in struct 'main.S' | 20 | main.zig:15:24: error: no field named 'x' in struct 'main.S' |
| 23 | main.zig:1:11: note: struct declared here | 21 | main.zig:1:11: note: struct declared here |
| 24 | #} | 22 | #} |
| 25 | 23 | ||
| 26 | #write main.zig 3/main.zig | 24 | #write main.zig @3/main.zig |
| 27 | #update { | 25 | #update "remove reference to non-existent field" |
| 28 | remove reference to non-existent field#} | ||
| 29 | #check stdout { | 26 | #check stdout { |
| 30 | 0 <no fields> | 27 | 0 <no fields> |
| 31 | #} | 28 | #} |
test/incremental/add_remove_toplevel_fields/manifest+7-10| ... | @@ -2,30 +2,27 @@ | ... | @@ -2,30 +2,27 @@ |
| 2 | -Mroot=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | 3 | #} |
| 4 | 4 | ||
| 5 | #write main.zig 0/main.zig | 5 | #write main.zig @0/main.zig |
| 6 | #update | 6 | #update |
| 7 | #check stdout { | 7 | #check stdout { |
| 8 | 1 100 | 8 | 1 100 |
| 9 | #} | 9 | #} |
| 10 | 10 | ||
| 11 | #write main.zig 1/main.zig | 11 | #write main.zig @1/main.zig |
| 12 | #update { | 12 | #update "add a field" |
| 13 | add a field#} | ||
| 14 | #check stdout { | 13 | #check stdout { |
| 15 | 2 100 | 14 | 2 100 |
| 16 | #} | 15 | #} |
| 17 | 16 | ||
| 18 | #write main.zig 2/main.zig | 17 | #write main.zig @2/main.zig |
| 19 | #update { | 18 | #update "remove all fields" |
| 20 | remove all fields#} | ||
| 21 | #check errors { | 19 | #check errors { |
| 22 | main.zig:15:24: error: no field named 'x' in struct 'main' | 20 | main.zig:15:24: error: no field named 'x' in struct 'main' |
| 23 | main.zig:1:1: note: struct declared here | 21 | main.zig:1:1: note: struct declared here |
| 24 | #} | 22 | #} |
| 25 | 23 | ||
| 26 | #write main.zig 3/main.zig | 24 | #write main.zig @3/main.zig |
| 27 | #update { | 25 | #update "remove reference to non-existent field" |
| 28 | remove reference to non-existent field#} | ||
| 29 | #check stdout { | 26 | #check stdout { |
| 30 | 0 <no fields> | 27 | 0 <no fields> |
| 31 | #} | 28 | #} |
test/incremental/analysis_error_and_syntax_error+6-12| ... | @@ -16,8 +16,7 @@ main.zig:2:5: error: uh oh | ... | @@ -16,8 +16,7 @@ main.zig:2:5: error: uh oh |
| 16 | pub fn main() !void { | 16 | pub fn main() !void { |
| 17 | @compileError("uh oh"); | 17 | @compileError("uh oh"); |
| 18 | #} | 18 | #} |
| 19 | #update { | 19 | #update "add parse error" |
| 20 | add parse error#} | ||
| 21 | #check errors { | 20 | #check errors { |
| 22 | main.zig:3:1: error: expected statement, found 'EOF' | 21 | main.zig:3:1: error: expected statement, found 'EOF' |
| 23 | #} | 22 | #} |
| ... | @@ -27,8 +26,7 @@ pub fn main() !void { | ... | @@ -27,8 +26,7 @@ pub fn main() !void { |
| 27 | @compileError("uh oh"); | 26 | @compileError("uh oh"); |
| 28 | } | 27 | } |
| 29 | #} | 28 | #} |
| 30 | #update { | 29 | #update "fix parse error" |
| 31 | fix parse error#} | ||
| 32 | #check errors { | 30 | #check errors { |
| 33 | main.zig:2:5: error: uh oh | 31 | main.zig:2:5: error: uh oh |
| 34 | #} | 32 | #} |
| ... | @@ -37,8 +35,7 @@ main.zig:2:5: error: uh oh | ... | @@ -37,8 +35,7 @@ main.zig:2:5: error: uh oh |
| 37 | pub fn main() !void { | 35 | pub fn main() !void { |
| 38 | @compileError("uh oh"); | 36 | @compileError("uh oh"); |
| 39 | #} | 37 | #} |
| 40 | #update { | 38 | #update "add parse error again" |
| 41 | add parse error again#} | ||
| 42 | #check errors { | 39 | #check errors { |
| 43 | main.zig:3:1: error: expected statement, found 'EOF' | 40 | main.zig:3:1: error: expected statement, found 'EOF' |
| 44 | #} | 41 | #} |
| ... | @@ -47,8 +44,7 @@ main.zig:3:1: error: expected statement, found 'EOF' | ... | @@ -47,8 +44,7 @@ main.zig:3:1: error: expected statement, found 'EOF' |
| 47 | pub fn main() !void { | 44 | pub fn main() !void { |
| 48 | //@compileError("uh oh"); | 45 | //@compileError("uh oh"); |
| 49 | #} | 46 | #} |
| 50 | #update { | 47 | #update "comment @compileError call" |
| 51 | comment @compileError call#} | ||
| 52 | #check errors { | 48 | #check errors { |
| 53 | main.zig:3:1: error: expected statement, found 'EOF' | 49 | main.zig:3:1: error: expected statement, found 'EOF' |
| 54 | #} | 50 | #} |
| ... | @@ -58,7 +54,5 @@ pub fn main() !void { | ... | @@ -58,7 +54,5 @@ pub fn main() !void { |
| 58 | //@compileError("uh oh"); | 54 | //@compileError("uh oh"); |
| 59 | } | 55 | } |
| 60 | #} | 56 | #} |
| 61 | #update { | 57 | #update "fix parse error again" |
| 62 | fix parse error again#} | 58 | #check stdout |
| 63 | #check stdout { | ||
| 64 | #} |
test/incremental/bad_import+2-4| ... | @@ -26,8 +26,7 @@ comptime { | ... | @@ -26,8 +26,7 @@ comptime { |
| 26 | _ = @import("this_is/not_real.zig"); | 26 | _ = @import("this_is/not_real.zig"); |
| 27 | } | 27 | } |
| 28 | #} | 28 | #} |
| 29 | #update { | 29 | #update "change bad import" |
| 30 | change bad import#} | ||
| 31 | #check errors { | 30 | #check errors { |
| 32 | this_is/not_real.zig:1:1: error: unable to load "not_real.zig": FileNotFound | 31 | this_is/not_real.zig:1:1: error: unable to load "not_real.zig": FileNotFound |
| 33 | foo.zig:2:17: note: file imported here | 32 | foo.zig:2:17: note: file imported here |
| ... | @@ -41,8 +40,7 @@ pub fn main() !void { | ... | @@ -41,8 +40,7 @@ pub fn main() !void { |
| 41 | const std = @import("std"); | 40 | const std = @import("std"); |
| 42 | const io = std.Io.Threaded.global_single_threaded.io(); | 41 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 43 | #} | 42 | #} |
| 44 | #update { | 43 | #update "remove import of 'foo.zig'" |
| 45 | remove import of 'foo.zig'#} | ||
| 46 | #check stdout { | 44 | #check stdout { |
| 47 | success | 45 | success |
| 48 | #} | 46 | #} |
test/incremental/change_bitpack_backing_int+3-6| ... | @@ -38,8 +38,7 @@ comptime { | ... | @@ -38,8 +38,7 @@ comptime { |
| 38 | const std = @import("std"); | 38 | const std = @import("std"); |
| 39 | const io = std.Io.Threaded.global_single_threaded.io(); | 39 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 40 | #} | 40 | #} |
| 41 | #update { | 41 | #update "make backing types signed" |
| 42 | make backing types signed#} | ||
| 43 | #check errors { | 42 | #check errors { |
| 44 | main.zig:5:5: error: found compile log statement | 43 | main.zig:5:5: error: found compile log statement |
| 45 | main.zig:8:5: note: also here | 44 | main.zig:8:5: note: also here |
| ... | @@ -62,8 +61,7 @@ comptime { | ... | @@ -62,8 +61,7 @@ comptime { |
| 62 | const std = @import("std"); | 61 | const std = @import("std"); |
| 63 | const io = std.Io.Threaded.global_single_threaded.io(); | 62 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 64 | #} | 63 | #} |
| 65 | #update { | 64 | #update "make backing types too small" |
| 66 | make backing types too small#} | ||
| 67 | #check errors { | 65 | #check errors { |
| 68 | main.zig:1:20: error: backing integer bit width does not match total bit width of fields | 66 | main.zig:1:20: error: backing integer bit width does not match total bit width of fields |
| 69 | main.zig:1:27: note: backing integer 'i5' has bit width '5' | 67 | main.zig:1:27: note: backing integer 'i5' has bit width '5' |
| ... | @@ -87,8 +85,7 @@ comptime { | ... | @@ -87,8 +85,7 @@ comptime { |
| 87 | const std = @import("std"); | 85 | const std = @import("std"); |
| 88 | const io = std.Io.Threaded.global_single_threaded.io(); | 86 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 89 | #} | 87 | #} |
| 90 | #update { | 88 | #update "make backing types too big" |
| 91 | make backing types too big#} | ||
| 92 | #check errors { | 89 | #check errors { |
| 93 | main.zig:1:20: error: backing integer bit width does not match total bit width of fields | 90 | main.zig:1:20: error: backing integer bit width does not match total bit width of fields |
| 94 | main.zig:1:27: note: backing integer 'u10' has bit width '10' | 91 | main.zig:1:27: note: backing integer 'u10' has bit width '10' |
test/incremental/change_container_kind+8-16| ... | @@ -12,8 +12,7 @@ pub fn main() void { | ... | @@ -12,8 +12,7 @@ pub fn main() void { |
| 12 | #write foo.zig { | 12 | #write foo.zig { |
| 13 | pub const T = struct { x: u8 }; | 13 | pub const T = struct { x: u8 }; |
| 14 | #} | 14 | #} |
| 15 | #update { | 15 | #update "T is struct" |
| 16 | T is struct#} | ||
| 17 | #check errors { | 16 | #check errors { |
| 18 | main.zig:3:5: error: found compile log statement | 17 | main.zig:3:5: error: found compile log statement |
| 19 | 18 | ||
| ... | @@ -25,8 +24,7 @@ Compile Log Output: | ... | @@ -25,8 +24,7 @@ Compile Log Output: |
| 25 | #write foo.zig { | 24 | #write foo.zig { |
| 26 | pub const T = union {}; | 25 | pub const T = union {}; |
| 27 | #} | 26 | #} |
| 28 | #update { | 27 | #update "T is union" |
| 29 | T is union#} | ||
| 30 | #check errors { | 28 | #check errors { |
| 31 | main.zig:3:5: error: found compile log statement | 29 | main.zig:3:5: error: found compile log statement |
| 32 | 30 | ||
| ... | @@ -38,8 +36,7 @@ Compile Log Output: | ... | @@ -38,8 +36,7 @@ Compile Log Output: |
| 38 | #write foo.zig { | 36 | #write foo.zig { |
| 39 | pub const T = enum {}; | 37 | pub const T = enum {}; |
| 40 | #} | 38 | #} |
| 41 | #update { | 39 | #update "T is enum" |
| 42 | T is enum#} | ||
| 43 | #check errors { | 40 | #check errors { |
| 44 | main.zig:3:5: error: found compile log statement | 41 | main.zig:3:5: error: found compile log statement |
| 45 | 42 | ||
| ... | @@ -51,8 +48,7 @@ Compile Log Output: | ... | @@ -51,8 +48,7 @@ Compile Log Output: |
| 51 | #write foo.zig { | 48 | #write foo.zig { |
| 52 | pub const T = opaque {}; | 49 | pub const T = opaque {}; |
| 53 | #} | 50 | #} |
| 54 | #update { | 51 | #update "T is opaque" |
| 55 | T is opaque#} | ||
| 56 | #check errors { | 52 | #check errors { |
| 57 | main.zig:3:5: error: found compile log statement | 53 | main.zig:3:5: error: found compile log statement |
| 58 | 54 | ||
| ... | @@ -64,8 +60,7 @@ Compile Log Output: | ... | @@ -64,8 +60,7 @@ Compile Log Output: |
| 64 | #write foo.zig { | 60 | #write foo.zig { |
| 65 | pub const T = packed struct {}; | 61 | pub const T = packed struct {}; |
| 66 | #} | 62 | #} |
| 67 | #update { | 63 | #update "T is packed struct" |
| 68 | T is packed struct#} | ||
| 69 | #check errors { | 64 | #check errors { |
| 70 | main.zig:3:5: error: found compile log statement | 65 | main.zig:3:5: error: found compile log statement |
| 71 | 66 | ||
| ... | @@ -77,8 +72,7 @@ Compile Log Output: | ... | @@ -77,8 +72,7 @@ Compile Log Output: |
| 77 | #write foo.zig { | 72 | #write foo.zig { |
| 78 | pub const T = packed union { x: void }; // packed unions need at least 1 field | 73 | pub const T = packed union { x: void }; // packed unions need at least 1 field |
| 79 | #} | 74 | #} |
| 80 | #update { | 75 | #update "T is packed union" |
| 81 | T is packed union#} | ||
| 82 | #check errors { | 76 | #check errors { |
| 83 | main.zig:3:5: error: found compile log statement | 77 | main.zig:3:5: error: found compile log statement |
| 84 | 78 | ||
| ... | @@ -90,8 +84,7 @@ Compile Log Output: | ... | @@ -90,8 +84,7 @@ Compile Log Output: |
| 90 | #write foo.zig { | 84 | #write foo.zig { |
| 91 | pub const T = extern struct {}; | 85 | pub const T = extern struct {}; |
| 92 | #} | 86 | #} |
| 93 | #update { | 87 | #update "T is extern struct" |
| 94 | T is extern struct#} | ||
| 95 | #check errors { | 88 | #check errors { |
| 96 | main.zig:3:5: error: found compile log statement | 89 | main.zig:3:5: error: found compile log statement |
| 97 | 90 | ||
| ... | @@ -103,8 +96,7 @@ Compile Log Output: | ... | @@ -103,8 +96,7 @@ Compile Log Output: |
| 103 | #write foo.zig { | 96 | #write foo.zig { |
| 104 | pub const T = extern union { x: void }; // extern unions need at least 1 field | 97 | pub const T = extern union { x: void }; // extern unions need at least 1 field |
| 105 | #} | 98 | #} |
| 106 | #update { | 99 | #update "T is extern union" |
| 107 | T is extern union#} | ||
| 108 | #check errors { | 100 | #check errors { |
| 109 | main.zig:3:5: error: found compile log statement | 101 | main.zig:3:5: error: found compile log statement |
| 110 | 102 |
test/incremental/change_embed_file+5-10| ... | @@ -21,15 +21,13 @@ Hello, World! | ... | @@ -21,15 +21,13 @@ Hello, World! |
| 21 | #write string.txt { | 21 | #write string.txt { |
| 22 | Hello again, World! | 22 | Hello again, World! |
| 23 | #} | 23 | #} |
| 24 | #update { | 24 | #update "change file contents" |
| 25 | change file contents#} | ||
| 26 | #check stdout { | 25 | #check stdout { |
| 27 | Hello again, World! | 26 | Hello again, World! |
| 28 | #} | 27 | #} |
| 29 | 28 | ||
| 30 | #delete string.txt | 29 | #delete string.txt |
| 31 | #update { | 30 | #update "delete file" |
| 32 | delete file#} | ||
| 33 | #check errors { | 31 | #check errors { |
| 34 | main.zig:2:27: error: failed opening "string.txt": FileNotFound | 32 | main.zig:2:27: error: failed opening "string.txt": FileNotFound |
| 35 | #} | 33 | #} |
| ... | @@ -42,8 +40,7 @@ pub fn main() !void { | ... | @@ -42,8 +40,7 @@ pub fn main() !void { |
| 42 | } | 40 | } |
| 43 | const io = std.Io.Threaded.global_single_threaded.io(); | 41 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 44 | #} | 42 | #} |
| 45 | #update { | 43 | #update "remove reference to file" |
| 46 | remove reference to file#} | ||
| 47 | #check stdout { | 44 | #check stdout { |
| 48 | a hardcoded string | 45 | a hardcoded string |
| 49 | #} | 46 | #} |
| ... | @@ -56,8 +53,7 @@ pub fn main() !void { | ... | @@ -56,8 +53,7 @@ pub fn main() !void { |
| 56 | } | 53 | } |
| 57 | const io = std.Io.Threaded.global_single_threaded.io(); | 54 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 58 | #} | 55 | #} |
| 59 | #update { | 56 | #update "re-introduce reference to file" |
| 60 | re-introduce reference to file#} | ||
| 61 | #check errors { | 57 | #check errors { |
| 62 | main.zig:2:27: error: failed opening "string.txt": FileNotFound | 58 | main.zig:2:27: error: failed opening "string.txt": FileNotFound |
| 63 | #} | 59 | #} |
| ... | @@ -65,8 +61,7 @@ main.zig:2:27: error: failed opening "string.txt": FileNotFound | ... | @@ -65,8 +61,7 @@ main.zig:2:27: error: failed opening "string.txt": FileNotFound |
| 65 | #write string.txt { | 61 | #write string.txt { |
| 66 | We're back, World! | 62 | We're back, World! |
| 67 | #} | 63 | #} |
| 68 | #update { | 64 | #update "recreate file" |
| 69 | recreate file#} | ||
| 70 | #check stdout { | 65 | #check stdout { |
| 71 | We're back, World! | 66 | We're back, World! |
| 72 | #} | 67 | #} |
test/incremental/change_enum_tag_type/manifest+9-13| ... | @@ -2,29 +2,26 @@ | ... | @@ -2,29 +2,26 @@ |
| 2 | -Mroot=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | 3 | #} |
| 4 | 4 | ||
| 5 | #write main.zig 0/main.zig | 5 | #write main.zig @0/main.zig |
| 6 | #update | 6 | #update |
| 7 | #check stdout { | 7 | #check stdout { |
| 8 | a | 8 | a |
| 9 | #} | 9 | #} |
| 10 | 10 | ||
| 11 | #write main.zig 1/main.zig | 11 | #write main.zig @1/main.zig |
| 12 | #update { | 12 | #update "too many enum fields" |
| 13 | too many enum fields#} | ||
| 14 | #check errors { | 13 | #check errors { |
| 15 | main.zig:7:5: error: enum tag value '4' too large for type 'u2' | 14 | main.zig:7:5: error: enum tag value '4' too large for type 'u2' |
| 16 | #} | 15 | #} |
| 17 | 16 | ||
| 18 | #write main.zig 2/main.zig | 17 | #write main.zig @2/main.zig |
| 19 | #update { | 18 | #update "increase tag size" |
| 20 | increase tag size#} | ||
| 21 | #check stdout { | 19 | #check stdout { |
| 22 | a | 20 | a |
| 23 | #} | 21 | #} |
| 24 | 22 | ||
| 25 | #write main.zig 3/main.zig | 23 | #write main.zig @3/main.zig |
| 26 | #update { | 24 | #update "specify tag directly" |
| 27 | specify tag directly#} | ||
| 28 | #check errors { | 25 | #check errors { |
| 29 | main.zig:9:5: error: found compile log statement | 26 | main.zig:9:5: error: found compile log statement |
| 30 | 27 | ||
| ... | @@ -32,9 +29,8 @@ Compile Log Output: | ... | @@ -32,9 +29,8 @@ Compile Log Output: |
| 32 | @as(type, u3) | 29 | @as(type, u3) |
| 33 | #} | 30 | #} |
| 34 | 31 | ||
| 35 | #write main.zig 4/main.zig | 32 | #write main.zig @4/main.zig |
| 36 | #update { | 33 | #update "change directly-specified tag type" |
| 37 | change directly-specified tag type#} | ||
| 38 | #check errors { | 34 | #check errors { |
| 39 | main.zig:9:5: error: found compile log statement | 35 | main.zig:9:5: error: found compile log statement |
| 40 | 36 |
test/incremental/change_exports/manifest+14-21| ... | @@ -2,50 +2,43 @@ | ... | @@ -2,50 +2,43 @@ |
| 2 | -Mroot=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | 3 | #} |
| 4 | 4 | ||
| 5 | #write main.zig 0/main.zig | 5 | #write main.zig @0/main.zig |
| 6 | #update | 6 | #update |
| 7 | #check stdout { | 7 | #check stdout { |
| 8 | 123 | 8 | 123 |
| 9 | #} | 9 | #} |
| 10 | 10 | ||
| 11 | #write main.zig 1/main.zig | 11 | #write main.zig @1/main.zig |
| 12 | #update { | 12 | #update "add conflict" |
| 13 | add conflict#} | ||
| 14 | #check errors { | 13 | #check errors { |
| 15 | main.zig:6:5: error: exported symbol collision: foo | 14 | main.zig:6:5: error: exported symbol collision: foo |
| 16 | main.zig:1:1: note: other symbol here | 15 | main.zig:1:1: note: other symbol here |
| 17 | #} | 16 | #} |
| 18 | 17 | ||
| 19 | #write main.zig 2/main.zig | 18 | #write main.zig @2/main.zig |
| 20 | #update { | 19 | #update "resolve conflict" |
| 21 | resolve conflict#} | ||
| 22 | #check stdout { | 20 | #check stdout { |
| 23 | 123 456 | 21 | 123 456 |
| 24 | #} | 22 | #} |
| 25 | 23 | ||
| 26 | #write main.zig 3/main.zig | 24 | #write main.zig @3/main.zig |
| 27 | #update { | 25 | #update "put exports in decl" |
| 28 | put exports in decl#} | ||
| 29 | #check stdout { | 26 | #check stdout { |
| 30 | 123 456 | 27 | 123 456 |
| 31 | #} | 28 | #} |
| 32 | 29 | ||
| 33 | #write main.zig 4/main.zig | 30 | #write main.zig @4/main.zig |
| 34 | #update { | 31 | #update "remove reference to exporting decl" |
| 35 | remove reference to exporting decl#} | 32 | #check stdout |
| 36 | #check stdout { | ||
| 37 | #} | ||
| 38 | 33 | ||
| 39 | #write main.zig 5/main.zig | 34 | #write main.zig @5/main.zig |
| 40 | #update { | 35 | #update "mark consts as export" |
| 41 | mark consts as export#} | ||
| 42 | #check stdout { | 36 | #check stdout { |
| 43 | 123 456 | 37 | 123 456 |
| 44 | #} | 38 | #} |
| 45 | 39 | ||
| 46 | #write main.zig 6/main.zig | 40 | #write main.zig @6/main.zig |
| 47 | #update { | 41 | #update "reintroduce reference to exporting decl, introducing conflict" |
| 48 | reintroduce reference to exporting decl, introducing conflict#} | ||
| 49 | #check errors { | 42 | #check errors { |
| 50 | main.zig:5:5: error: exported symbol collision: bar | 43 | main.zig:5:5: error: exported symbol collision: bar |
| 51 | main.zig:2:1: note: other symbol here | 44 | main.zig:2:1: note: other symbol here |
test/incremental/change_fn_type+2-4| ... | @@ -29,8 +29,7 @@ fn foo(x: i64) !void { | ... | @@ -29,8 +29,7 @@ fn foo(x: i64) !void { |
| 29 | const std = @import("std"); | 29 | const std = @import("std"); |
| 30 | const io = std.Io.Threaded.global_single_threaded.io(); | 30 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 31 | #} | 31 | #} |
| 32 | #update { | 32 | #update "change function type" |
| 33 | change function type#} | ||
| 34 | #check stdout { | 33 | #check stdout { |
| 35 | 123 | 34 | 123 |
| 36 | #} | 35 | #} |
| ... | @@ -46,8 +45,7 @@ fn foo(x: i64) !void { | ... | @@ -46,8 +45,7 @@ fn foo(x: i64) !void { |
| 46 | const std = @import("std"); | 45 | const std = @import("std"); |
| 47 | const io = std.Io.Threaded.global_single_threaded.io(); | 46 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 48 | #} | 47 | #} |
| 49 | #update { | 48 | #update "change function argument" |
| 50 | change function argument#} | ||
| 51 | #check stdout { | 49 | #check stdout { |
| 52 | -42 | 50 | -42 |
| 53 | #} | 51 | #} |
test/incremental/change_generic_line_number+1-2| ... | @@ -39,8 +39,7 @@ pub fn main() !void { | ... | @@ -39,8 +39,7 @@ pub fn main() !void { |
| 39 | try Printer("bar\n").print(); | 39 | try Printer("bar\n").print(); |
| 40 | } | 40 | } |
| 41 | #} | 41 | #} |
| 42 | #update { | 42 | #update "change line number" |
| 43 | change line number#} | ||
| 44 | #check stdout { | 43 | #check stdout { |
| 45 | foo | 44 | foo |
| 46 | bar | 45 | bar |
test/incremental/change_line_number+1-2| ... | @@ -22,8 +22,7 @@ pub fn main() !void { | ... | @@ -22,8 +22,7 @@ pub fn main() !void { |
| 22 | } | 22 | } |
| 23 | const io = std.Io.Threaded.global_single_threaded.io(); | 23 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 24 | #} | 24 | #} |
| 25 | #update { | 25 | #update "change line number" |
| 26 | change line number#} | ||
| 27 | #check stdout { | 26 | #check stdout { |
| 28 | foo | 27 | foo |
| 29 | #} | 28 | #} |
test/incremental/change_module+4-8| ... | @@ -41,8 +41,7 @@ comptime { | ... | @@ -41,8 +41,7 @@ comptime { |
| 41 | //_ = @import("other.zig"); | 41 | //_ = @import("other.zig"); |
| 42 | } | 42 | } |
| 43 | #} | 43 | #} |
| 44 | #update { | 44 | #update "change module of other.zig" |
| 45 | change module of other.zig#} | ||
| 46 | #check errors { | 45 | #check errors { |
| 47 | other.zig:2:5: error: found compile log statement | 46 | other.zig:2:5: error: found compile log statement |
| 48 | 47 | ||
| ... | @@ -61,8 +60,7 @@ comptime { | ... | @@ -61,8 +60,7 @@ comptime { |
| 61 | _ = @import("other.zig"); | 60 | _ = @import("other.zig"); |
| 62 | } | 61 | } |
| 63 | #} | 62 | #} |
| 64 | #update { | 63 | #update "put other.zig in both modules" |
| 65 | put other.zig in both modules#} | ||
| 66 | #check errors { | 64 | #check errors { |
| 67 | other.zig:1:1: error: file exists in modules 'root' and 'foo' | 65 | other.zig:1:1: error: file exists in modules 'root' and 'foo' |
| 68 | other.zig:1:1: note: files must belong to only one module | 66 | other.zig:1:1: note: files must belong to only one module |
| ... | @@ -81,7 +79,5 @@ comptime { | ... | @@ -81,7 +79,5 @@ comptime { |
| 81 | //_ = @import("other.zig"); | 79 | //_ = @import("other.zig"); |
| 82 | } | 80 | } |
| 83 | #} | 81 | #} |
| 84 | #update { | 82 | #update "put other.zig in no modules" |
| 85 | put other.zig in no modules#} | 83 | #check stdout |
| 86 | #check stdout { | ||
| 87 | #} |
test/incremental/change_panic_handler+2-4| ... | @@ -39,8 +39,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { | ... | @@ -39,8 +39,7 @@ fn myPanic(msg: []const u8, _: ?usize) noreturn { |
| 39 | const std = @import("std"); | 39 | const std = @import("std"); |
| 40 | const io = std.Io.Threaded.global_single_threaded.io(); | 40 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 41 | #} | 41 | #} |
| 42 | #update { | 42 | #update "change the panic handler body" |
| 43 | change the panic handler body#} | ||
| 44 | #check stdout { | 43 | #check stdout { |
| 45 | new panic message: integer overflow | 44 | new panic message: integer overflow |
| 46 | #} | 45 | #} |
| ... | @@ -61,8 +60,7 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn { | ... | @@ -61,8 +60,7 @@ fn myPanicNew(msg: []const u8, _: ?usize) noreturn { |
| 61 | const std = @import("std"); | 60 | const std = @import("std"); |
| 62 | const io = std.Io.Threaded.global_single_threaded.io(); | 61 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 63 | #} | 62 | #} |
| 64 | #update { | 63 | #update "change the panic handler function value" |
| 65 | change the panic handler function value#} | ||
| 66 | #check stdout { | 64 | #check stdout { |
| 67 | third panic message: integer overflow | 65 | third panic message: integer overflow |
| 68 | #} | 66 | #} |
test/incremental/change_panic_handler_explicit/manifest+5-7| ... | @@ -2,22 +2,20 @@ | ... | @@ -2,22 +2,20 @@ |
| 2 | -Mroot=main.zig | 2 | -Mroot=main.zig |
| 3 | #} | 3 | #} |
| 4 | 4 | ||
| 5 | #write main.zig 0/main.zig | 5 | #write main.zig @0/main.zig |
| 6 | #update | 6 | #update |
| 7 | #check stdout { | 7 | #check stdout { |
| 8 | panic message: integer overflow | 8 | panic message: integer overflow |
| 9 | #} | 9 | #} |
| 10 | 10 | ||
| 11 | #write main.zig 1/main.zig | 11 | #write main.zig @1/main.zig |
| 12 | #update { | 12 | #update "change the panic handler body" |
| 13 | change the panic handler body#} | ||
| 14 | #check stdout { | 13 | #check stdout { |
| 15 | new panic message: integer overflow | 14 | new panic message: integer overflow |
| 16 | #} | 15 | #} |
| 17 | 16 | ||
| 18 | #write main.zig 2/main.zig | 17 | #write main.zig @2/main.zig |
| 19 | #update { | 18 | #update "change the panic handler function value" |
| 20 | change the panic handler function value#} | ||
| 21 | #check stdout { | 19 | #check stdout { |
| 22 | third panic message: integer overflow | 20 | third panic message: integer overflow |
| 23 | #} | 21 | #} |
test/incremental/change_reify_struct_field_type+3-6| ... | @@ -13,8 +13,7 @@ const Enum = enum { foo, bar }; | ... | @@ -13,8 +13,7 @@ const Enum = enum { foo, bar }; |
| 13 | const std = @import("std"); | 13 | const std = @import("std"); |
| 14 | #} | 14 | #} |
| 15 | #update | 15 | #update |
| 16 | #check stdout { | 16 | #check stdout "foo" |
| 17 | foo#} | ||
| 18 | 17 | ||
| 19 | #write main.zig { | 18 | #write main.zig { |
| 20 | fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{Enum}, &.{.{}})) Enum { | 19 | fn getEnum(s: @Struct(.auto, null, &.{"field"}, &.{Enum}, &.{.{}})) Enum { |
| ... | @@ -26,7 +25,5 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -26,7 +25,5 @@ pub fn main(init: std.process.Init) !void { |
| 26 | const Enum = enum { foo, bar }; | 25 | const Enum = enum { foo, bar }; |
| 27 | const std = @import("std"); | 26 | const std = @import("std"); |
| 28 | #} | 27 | #} |
| 29 | #update { | 28 | #update "change field type" |
| 30 | change field type#} | 29 | #check stdout "bar" |
| 31 | #check stdout { | ||
| 32 | bar#} |
test/incremental/change_shift_op+1-2| ... | @@ -29,8 +29,7 @@ fn foo(x: u16) !void { | ... | @@ -29,8 +29,7 @@ fn foo(x: u16) !void { |
| 29 | const std = @import("std"); | 29 | const std = @import("std"); |
| 30 | const io = std.Io.Threaded.global_single_threaded.io(); | 30 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 31 | #} | 31 | #} |
| 32 | #update { | 32 | #update "change to right shift" |
| 33 | change to right shift#} | ||
| 34 | #check stdout { | 33 | #check stdout { |
| 35 | 0x130 | 34 | 0x130 |
| 36 | #} | 35 | #} |
test/incremental/change_struct_same_fields+2-4| ... | @@ -39,8 +39,7 @@ fn foo(val: *const S) !void { | ... | @@ -39,8 +39,7 @@ fn foo(val: *const S) !void { |
| 39 | const std = @import("std"); | 39 | const std = @import("std"); |
| 40 | const io = std.Io.Threaded.global_single_threaded.io(); | 40 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 41 | #} | 41 | #} |
| 42 | #update { | 42 | #update "change struct layout" |
| 43 | change struct layout#} | ||
| 44 | #check stdout { | 43 | #check stdout { |
| 45 | 100 200 | 44 | 100 200 |
| 46 | #} | 45 | #} |
| ... | @@ -61,8 +60,7 @@ fn foo(val: *const S) !void { | ... | @@ -61,8 +60,7 @@ fn foo(val: *const S) !void { |
| 61 | const std = @import("std"); | 60 | const std = @import("std"); |
| 62 | const io = std.Io.Threaded.global_single_threaded.io(); | 61 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 63 | #} | 62 | #} |
| 64 | #update { | 63 | #update "change values" |
| 65 | change values#} | ||
| 66 | #check stdout { | 64 | #check stdout { |
| 67 | 1234 5678 | 65 | 1234 5678 |
| 68 | #} | 66 | #} |
test/incremental/change_union_tag_type+2-4| ... | @@ -29,8 +29,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -29,8 +29,7 @@ pub fn main(init: std.process.Init) !void { |
| 29 | } | 29 | } |
| 30 | const std = @import("std"); | 30 | const std = @import("std"); |
| 31 | #} | 31 | #} |
| 32 | #update { | 32 | #update "change tag type" |
| 33 | change tag type#} | ||
| 34 | #check errors { | 33 | #check errors { |
| 35 | main.zig:3:24: error: no field named 'a' in enum 'main.B' | 34 | main.zig:3:24: error: no field named 'a' in enum 'main.B' |
| 36 | main.zig:2:11: note: enum declared here | 35 | main.zig:2:11: note: enum declared here |
| ... | @@ -47,8 +46,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -47,8 +46,7 @@ pub fn main(init: std.process.Init) !void { |
| 47 | } | 46 | } |
| 48 | const std = @import("std"); | 47 | const std = @import("std"); |
| 49 | #} | 48 | #} |
| 50 | #update { | 49 | #update "change field name to match new tag type" |
| 51 | change field name to match new tag type#} | ||
| 52 | #check stdout { | 50 | #check stdout { |
| 53 | b | 51 | b |
| 54 | #} | 52 | #} |
test/incremental/change_zon_file+5-10| ... | @@ -21,15 +21,13 @@ Hello, World! | ... | @@ -21,15 +21,13 @@ Hello, World! |
| 21 | #write message.zon { | 21 | #write message.zon { |
| 22 | "Hello again, World!\n" | 22 | "Hello again, World!\n" |
| 23 | #} | 23 | #} |
| 24 | #update { | 24 | #update "change ZON file contents" |
| 25 | change ZON file contents#} | ||
| 26 | #check stdout { | 25 | #check stdout { |
| 27 | Hello again, World! | 26 | Hello again, World! |
| 28 | #} | 27 | #} |
| 29 | 28 | ||
| 30 | #delete message.zon | 29 | #delete message.zon |
| 31 | #update { | 30 | #update "delete file" |
| 32 | delete file#} | ||
| 33 | #check errors { | 31 | #check errors { |
| 34 | message.zon:1:1: error: unable to load "message.zon": FileNotFound | 32 | message.zon:1:1: error: unable to load "message.zon": FileNotFound |
| 35 | main.zig:2:37: note: file imported here | 33 | main.zig:2:37: note: file imported here |
| ... | @@ -43,8 +41,7 @@ pub fn main() !void { | ... | @@ -43,8 +41,7 @@ pub fn main() !void { |
| 43 | } | 41 | } |
| 44 | const io = std.Io.Threaded.global_single_threaded.io(); | 42 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 45 | #} | 43 | #} |
| 46 | #update { | 44 | #update "remove reference to ZON file" |
| 47 | remove reference to ZON file#} | ||
| 48 | #check errors { | 45 | #check errors { |
| 49 | message.zon:1:1: error: unable to load "message.zon": FileNotFound | 46 | message.zon:1:1: error: unable to load "message.zon": FileNotFound |
| 50 | main.zig:2:37: note: file imported here | 47 | main.zig:2:37: note: file imported here |
| ... | @@ -53,8 +50,7 @@ main.zig:2:37: note: file imported here | ... | @@ -53,8 +50,7 @@ main.zig:2:37: note: file imported here |
| 53 | #write message.zon { | 50 | #write message.zon { |
| 54 | "We're back, World!\n" | 51 | "We're back, World!\n" |
| 55 | #} | 52 | #} |
| 56 | #update { | 53 | #update "recreate ZON file" |
| 57 | recreate ZON file#} | ||
| 58 | #check stdout { | 54 | #check stdout { |
| 59 | a hardcoded string | 55 | a hardcoded string |
| 60 | #} | 56 | #} |
| ... | @@ -67,8 +63,7 @@ pub fn main() !void { | ... | @@ -67,8 +63,7 @@ pub fn main() !void { |
| 67 | } | 63 | } |
| 68 | const io = std.Io.Threaded.global_single_threaded.io(); | 64 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 69 | #} | 65 | #} |
| 70 | #update { | 66 | #update "re-introduce reference to ZON file" |
| 71 | re-introduce reference to ZON file#} | ||
| 72 | #check stdout { | 67 | #check stdout { |
| 73 | We're back, World! | 68 | We're back, World! |
| 74 | #} | 69 | #} |
test/incremental/change_zon_file_no_result_type+1-2| ... | @@ -26,8 +26,7 @@ Hello, World! | ... | @@ -26,8 +26,7 @@ Hello, World! |
| 26 | .b_number = 0, | 26 | .b_number = 0, |
| 27 | } | 27 | } |
| 28 | #} | 28 | #} |
| 29 | #update { | 29 | #update "change ZON file" |
| 30 | change ZON file#} | ||
| 31 | #check stdout { | 30 | #check stdout { |
| 32 | Hello again, World! | 31 | Hello again, World! |
| 33 | #} | 32 | #} |
test/incremental/compile_error_then_log+2-4| ... | @@ -11,8 +11,7 @@ comptime { | ... | @@ -11,8 +11,7 @@ comptime { |
| 11 | @compileLog("this is a log"); | 11 | @compileLog("this is a log"); |
| 12 | } | 12 | } |
| 13 | #} | 13 | #} |
| 14 | #update { | 14 | #update "compile error" |
| 15 | compile error#} | ||
| 16 | #check errors { | 15 | #check errors { |
| 17 | main.zig:3:5: error: this is an error | 16 | main.zig:3:5: error: this is an error |
| 18 | 17 | ||
| ... | @@ -29,8 +28,7 @@ comptime { | ... | @@ -29,8 +28,7 @@ comptime { |
| 29 | @compileLog("this is a log"); | 28 | @compileLog("this is a log"); |
| 30 | } | 29 | } |
| 31 | #} | 30 | #} |
| 32 | #update { | 31 | #update "remove the compile error" |
| 33 | remove the compile error#} | ||
| 34 | #check errors { | 32 | #check errors { |
| 35 | main.zig:6:5: error: found compile log statement | 33 | main.zig:6:5: error: found compile log statement |
| 36 | 34 |
test/incremental/compile_log+3-6| ... | @@ -9,8 +9,7 @@ pub fn main() !void { | ... | @@ -9,8 +9,7 @@ pub fn main() !void { |
| 9 | } | 9 | } |
| 10 | const io = std.Io.Threaded.global_single_threaded.io(); | 10 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 11 | #} | 11 | #} |
| 12 | #update { | 12 | #update "no compile log" |
| 13 | no compile log#} | ||
| 14 | #check stdout { | 13 | #check stdout { |
| 15 | Hello, World! | 14 | Hello, World! |
| 16 | #} | 15 | #} |
| ... | @@ -23,8 +22,7 @@ pub fn main() !void { | ... | @@ -23,8 +22,7 @@ pub fn main() !void { |
| 23 | } | 22 | } |
| 24 | const io = std.Io.Threaded.global_single_threaded.io(); | 23 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 25 | #} | 24 | #} |
| 26 | #update { | 25 | #update "add compile log" |
| 27 | add compile log#} | ||
| 28 | #check errors { | 26 | #check errors { |
| 29 | main.zig:4:5: error: found compile log statement | 27 | main.zig:4:5: error: found compile log statement |
| 30 | 28 | ||
| ... | @@ -39,8 +37,7 @@ pub fn main() !void { | ... | @@ -39,8 +37,7 @@ pub fn main() !void { |
| 39 | } | 37 | } |
| 40 | const io = std.Io.Threaded.global_single_threaded.io(); | 38 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 41 | #} | 39 | #} |
| 42 | #update { | 40 | #update "remove compile log" |
| 43 | remove compile log#} | ||
| 44 | #check stdout { | 41 | #check stdout { |
| 45 | Hello, World! | 42 | Hello, World! |
| 46 | #} | 43 | #} |
test/incremental/delete_comptime_decls+1-2| ... | @@ -44,8 +44,7 @@ comptime { | ... | @@ -44,8 +44,7 @@ comptime { |
| 44 | _ = y; | 44 | _ = y; |
| 45 | } | 45 | } |
| 46 | #} | 46 | #} |
| 47 | #update { | 47 | #update "delete and modify comptime decls" |
| 48 | delete and modify comptime decls#} | ||
| 49 | #check errors { | 48 | #check errors { |
| 50 | main.zig:6:16: error: slice of null pointer | 49 | main.zig:6:16: error: slice of null pointer |
| 51 | #} | 50 | #} |
test/incremental/dependency_on_type_of_inferred_global+2-4| ... | @@ -15,8 +15,7 @@ comptime { | ... | @@ -15,8 +15,7 @@ comptime { |
| 15 | pub fn main() void {} | 15 | pub fn main() void {} |
| 16 | #} | 16 | #} |
| 17 | #update | 17 | #update |
| 18 | #check stdout { | 18 | #check stdout |
| 19 | #} | ||
| 20 | 19 | ||
| 21 | #write main.zig { | 20 | #write main.zig { |
| 22 | const foo = @as(u16, 123); | 21 | const foo = @as(u16, 123); |
| ... | @@ -30,8 +29,7 @@ comptime { | ... | @@ -30,8 +29,7 @@ comptime { |
| 30 | } | 29 | } |
| 31 | pub fn main() void {} | 30 | pub fn main() void {} |
| 32 | #} | 31 | #} |
| 33 | #update { | 32 | #update "change the type" |
| 34 | change the type#} | ||
| 35 | #check errors { | 33 | #check errors { |
| 36 | main.zig:8:37: error: reached unreachable code | 34 | main.zig:8:37: error: reached unreachable code |
| 37 | #} | 35 | #} |
test/incremental/do_nothing+4-9| ... | @@ -1,6 +1,4 @@ | ... | @@ -1,6 +1,4 @@ |
| 1 | #todo { | 1 | #todo "it'd be great if we could actually check that no analysis happened!" |
| 2 | it'd be great if we could actually check that no analysis happened! | ||
| 3 | #} | ||
| 4 | #exe { | 2 | #exe { |
| 5 | -Mroot=main.zig | 3 | -Mroot=main.zig |
| 6 | #} | 4 | #} |
| ... | @@ -25,8 +23,7 @@ const O = opaque { | ... | @@ -25,8 +23,7 @@ const O = opaque { |
| 25 | }; | 23 | }; |
| 26 | #} | 24 | #} |
| 27 | #update | 25 | #update |
| 28 | #check stdout { | 26 | #check stdout |
| 29 | #} | ||
| 30 | 27 | ||
| 31 | #write main.zig { | 28 | #write main.zig { |
| 32 | pub fn main() void { | 29 | pub fn main() void { |
| ... | @@ -47,7 +44,5 @@ const O = opaque { | ... | @@ -47,7 +44,5 @@ const O = opaque { |
| 47 | } | 44 | } |
| 48 | }; | 45 | }; |
| 49 | #} | 46 | #} |
| 50 | #update { | 47 | #update "do literally nothing" |
| 51 | do literally nothing#} | 48 | #check stdout |
| 52 | #check stdout { | ||
| 53 | #} |
test/incremental/fix_astgen_failure+4-8| ... | @@ -12,8 +12,7 @@ pub fn hello() !void { | ... | @@ -12,8 +12,7 @@ pub fn hello() !void { |
| 12 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); | 12 | try std.Io.File.stdout().writeStreamingAll(io, "Hello, World!\n"); |
| 13 | } | 13 | } |
| 14 | #} | 14 | #} |
| 15 | #update { | 15 | #update "error" |
| 16 | error#} | ||
| 17 | #check errors { | 16 | #check errors { |
| 18 | foo.zig:2:9: error: use of undeclared identifier 'std' | 17 | foo.zig:2:9: error: use of undeclared identifier 'std' |
| 19 | #} | 18 | #} |
| ... | @@ -25,8 +24,7 @@ pub fn hello() !void { | ... | @@ -25,8 +24,7 @@ pub fn hello() !void { |
| 25 | } | 24 | } |
| 26 | const io = std.Io.Threaded.global_single_threaded.io(); | 25 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 27 | #} | 26 | #} |
| 28 | #update { | 27 | #update "fix the error" |
| 29 | fix the error#} | ||
| 30 | #check stdout { | 28 | #check stdout { |
| 31 | Hello, World! | 29 | Hello, World! |
| 32 | #} | 30 | #} |
| ... | @@ -38,8 +36,7 @@ pub fn hello() !void { | ... | @@ -38,8 +36,7 @@ pub fn hello() !void { |
| 38 | } | 36 | } |
| 39 | const io = std.Io.Threaded.global_single_threaded.io(); | 37 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 40 | #} | 38 | #} |
| 41 | #update { | 39 | #update "add new error" |
| 42 | add new error#} | ||
| 43 | #check errors { | 40 | #check errors { |
| 44 | foo.zig:3:52: error: use of undeclared identifier 'hello_str' | 41 | foo.zig:3:52: error: use of undeclared identifier 'hello_str' |
| 45 | #} | 42 | #} |
| ... | @@ -52,8 +49,7 @@ pub fn hello() !void { | ... | @@ -52,8 +49,7 @@ pub fn hello() !void { |
| 52 | } | 49 | } |
| 53 | const io = std.Io.Threaded.global_single_threaded.io(); | 50 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 54 | #} | 51 | #} |
| 55 | #update { | 52 | #update "fix the new error" |
| 56 | fix the new error#} | ||
| 57 | #check stdout { | 53 | #check stdout { |
| 58 | Hello, World! Again! | 54 | Hello, World! Again! |
| 59 | #} | 55 | #} |
test/incremental/function_becomes_inline+3-6| ... | @@ -12,8 +12,7 @@ fn foo() !void { | ... | @@ -12,8 +12,7 @@ fn foo() !void { |
| 12 | const std = @import("std"); | 12 | const std = @import("std"); |
| 13 | const io = std.Io.Threaded.global_single_threaded.io(); | 13 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 14 | #} | 14 | #} |
| 15 | #update { | 15 | #update "non-inline version" |
| 16 | non-inline version#} | ||
| 17 | #check stdout { | 16 | #check stdout { |
| 18 | Hello, World! | 17 | Hello, World! |
| 19 | #} | 18 | #} |
| ... | @@ -28,8 +27,7 @@ inline fn foo() !void { | ... | @@ -28,8 +27,7 @@ inline fn foo() !void { |
| 28 | const std = @import("std"); | 27 | const std = @import("std"); |
| 29 | const io = std.Io.Threaded.global_single_threaded.io(); | 28 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 30 | #} | 29 | #} |
| 31 | #update { | 30 | #update "make function inline" |
| 32 | make function inline#} | ||
| 33 | #check stdout { | 31 | #check stdout { |
| 34 | Hello, World! | 32 | Hello, World! |
| 35 | #} | 33 | #} |
| ... | @@ -44,8 +42,7 @@ inline fn foo() !void { | ... | @@ -44,8 +42,7 @@ inline fn foo() !void { |
| 44 | const std = @import("std"); | 42 | const std = @import("std"); |
| 45 | const io = std.Io.Threaded.global_single_threaded.io(); | 43 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 46 | #} | 44 | #} |
| 47 | #update { | 45 | #update "change string" |
| 48 | change string#} | ||
| 49 | #check stdout { | 46 | #check stdout { |
| 50 | Hello, `inline` World! | 47 | Hello, `inline` World! |
| 51 | #} | 48 | #} |
test/incremental/hello+1-2| ... | @@ -21,8 +21,7 @@ pub fn main() !void { | ... | @@ -21,8 +21,7 @@ pub fn main() !void { |
| 21 | try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n"); | 21 | try std.Io.File.stdout().writeStreamingAll(io, "おはようございます\n"); |
| 22 | } | 22 | } |
| 23 | #} | 23 | #} |
| 24 | #update { | 24 | #update "change the string" |
| 25 | change the string#} | ||
| 26 | #check stdout { | 25 | #check stdout { |
| 27 | おはようございます | 26 | おはようございます |
| 28 | #} | 27 | #} |
test/incremental/increase_array_field_length+1-2| ... | @@ -35,8 +35,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -35,8 +35,7 @@ pub fn main(init: std.process.Init) !void { |
| 35 | } | 35 | } |
| 36 | const std = @import("std"); | 36 | const std = @import("std"); |
| 37 | #} | 37 | #} |
| 38 | #update { | 38 | #update "increase array length" |
| 39 | increase array length#} | ||
| 40 | #check stdout { | 39 | #check stdout { |
| 41 | arr[999] = 234 | 40 | arr[999] = 234 |
| 42 | #} | 41 | #} |
test/incremental/make_decl_pub+1-2| ... | @@ -28,8 +28,7 @@ pub fn hello() !void { | ... | @@ -28,8 +28,7 @@ pub fn hello() !void { |
| 28 | } | 28 | } |
| 29 | const io = std.Io.Threaded.global_single_threaded.io(); | 29 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 30 | #} | 30 | #} |
| 31 | #update { | 31 | #update "make hello pub" |
| 32 | make hello pub#} | ||
| 33 | #check stdout { | 32 | #check stdout { |
| 34 | Hello, World! | 33 | Hello, World! |
| 35 | #} | 34 | #} |
test/incremental/matrix.zon created+61| ... | @@ -0,0 +1,61 @@ | ||
| 1 | .{ | ||
| 2 | // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others | ||
| 3 | // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang | ||
| 4 | // or LLVM). We just have a couple here to make sure that it works. | ||
| 5 | .{ | ||
| 6 | .target = .{ | ||
| 7 | .cpu_arch = .x86_64, | ||
| 8 | .os_tag = .linux, | ||
| 9 | .abi = .musl, | ||
| 10 | }, | ||
| 11 | .cache_mode = .incremental, | ||
| 12 | .backend = .cbe, | ||
| 13 | .linker = .old, | ||
| 14 | .pic = .nopic, | ||
| 15 | }, | ||
| 16 | .{ | ||
| 17 | .target = .{ | ||
| 18 | .cpu_arch = .x86_64, | ||
| 19 | .os_tag = .linux, | ||
| 20 | .abi = .none, | ||
| 21 | }, | ||
| 22 | .cache_mode = .incremental, | ||
| 23 | .backend = .llvm, | ||
| 24 | .linker = .lld, | ||
| 25 | .pic = .nopic, | ||
| 26 | }, | ||
| 27 | |||
| 28 | .{ | ||
| 29 | .target = .{ | ||
| 30 | .cpu_arch = .x86_64, | ||
| 31 | .os_tag = .linux, | ||
| 32 | .abi = .none, | ||
| 33 | }, | ||
| 34 | .cache_mode = .incremental, | ||
| 35 | .backend = .selfhosted, | ||
| 36 | .linker = .new, | ||
| 37 | .pic = .nopic, | ||
| 38 | }, | ||
| 39 | .{ | ||
| 40 | .target = .{ | ||
| 41 | .cpu_arch = .x86_64, | ||
| 42 | .os_tag = .windows, | ||
| 43 | .abi = .none, | ||
| 44 | }, | ||
| 45 | .cache_mode = .incremental, | ||
| 46 | .backend = .selfhosted, | ||
| 47 | .linker = .old, | ||
| 48 | .pic = .pic, | ||
| 49 | }, | ||
| 50 | .{ | ||
| 51 | .target = .{ | ||
| 52 | .cpu_arch = .wasm32, | ||
| 53 | .os_tag = .wasi, | ||
| 54 | .abi = .none, | ||
| 55 | }, | ||
| 56 | .cache_mode = .incremental, | ||
| 57 | .backend = .selfhosted, | ||
| 58 | .linker = .old, | ||
| 59 | .pic = .nopic, | ||
| 60 | }, | ||
| 61 | } | ||
test/incremental/modify_inline_fn+1-2| ... | @@ -29,8 +29,7 @@ inline fn getStr() []const u8 { | ... | @@ -29,8 +29,7 @@ inline fn getStr() []const u8 { |
| 29 | } | 29 | } |
| 30 | const io = std.Io.Threaded.global_single_threaded.io(); | 30 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 31 | #} | 31 | #} |
| 32 | #update { | 32 | #update "change the string" |
| 33 | change the string#} | ||
| 34 | #check stdout { | 33 | #check stdout { |
| 35 | bar | 34 | bar |
| 36 | #} | 35 | #} |
test/incremental/move_src+1-2| ... | @@ -36,8 +36,7 @@ fn bar() u32 { | ... | @@ -36,8 +36,7 @@ fn bar() u32 { |
| 36 | } | 36 | } |
| 37 | const io = std.Io.Threaded.global_single_threaded.io(); | 37 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 38 | #} | 38 | #} |
| 39 | #update { | 39 | #update "add newline" |
| 40 | add newline#} | ||
| 41 | #check stdout { | 40 | #check stdout { |
| 42 | 8 123 | 41 | 8 123 |
| 43 | #} | 42 | #} |
test/incremental/pointer_to_decl_with_astgen_error+4-8| ... | @@ -9,8 +9,7 @@ pub fn main() void { | ... | @@ -9,8 +9,7 @@ pub fn main() void { |
| 9 | } | 9 | } |
| 10 | #} | 10 | #} |
| 11 | #update | 11 | #update |
| 12 | #check stdout { | 12 | #check stdout |
| 13 | #} | ||
| 14 | 13 | ||
| 15 | #write main.zig { | 14 | #write main.zig { |
| 16 | const foo = something; | 15 | const foo = something; |
| ... | @@ -18,8 +17,7 @@ pub fn main() void { | ... | @@ -18,8 +17,7 @@ pub fn main() void { |
| 18 | _ = &foo; | 17 | _ = &foo; |
| 19 | } | 18 | } |
| 20 | #} | 19 | #} |
| 21 | #update { | 20 | #update "introduce use of undeclared identifier" |
| 22 | introduce use of undeclared identifier#} | ||
| 23 | #check errors { | 21 | #check errors { |
| 24 | main.zig:1:13: error: use of undeclared identifier 'something' | 22 | main.zig:1:13: error: use of undeclared identifier 'something' |
| 25 | #} | 23 | #} |
| ... | @@ -30,7 +28,5 @@ pub fn main() void { | ... | @@ -30,7 +28,5 @@ pub fn main() void { |
| 30 | _ = &foo; | 28 | _ = &foo; |
| 31 | } | 29 | } |
| 32 | #} | 30 | #} |
| 33 | #update { | 31 | #update "revert use of undeclared identifier" |
| 34 | revert use of undeclared identifier#} | 32 | #check stdout |
| 35 | #check stdout { | ||
| 36 | #} |
test/incremental/recursive_function_becomes_non_recursive+1-2| ... | @@ -31,8 +31,7 @@ fn foo(recurse: bool) !void { | ... | @@ -31,8 +31,7 @@ fn foo(recurse: bool) !void { |
| 31 | const std = @import("std"); | 31 | const std = @import("std"); |
| 32 | const io = std.Io.Threaded.global_single_threaded.io(); | 32 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 33 | #} | 33 | #} |
| 34 | #update { | 34 | #update "eliminate recursion and change argument" |
| 35 | eliminate recursion and change argument#} | ||
| 36 | #check stdout { | 35 | #check stdout { |
| 37 | x==1 | 36 | x==1 |
| 38 | #} | 37 | #} |
test/incremental/remove_enum_field+1-2| ... | @@ -31,8 +31,7 @@ pub fn main() !void { | ... | @@ -31,8 +31,7 @@ pub fn main() !void { |
| 31 | const std = @import("std"); | 31 | const std = @import("std"); |
| 32 | const io = std.Io.Threaded.global_single_threaded.io(); | 32 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 33 | #} | 33 | #} |
| 34 | #update { | 34 | #update "remove enum field" |
| 35 | remove enum field#} | ||
| 36 | #check errors { | 35 | #check errors { |
| 37 | main.zig:7:68: error: enum 'main.MyEnum' has no member named 'foo' | 36 | main.zig:7:68: error: enum 'main.MyEnum' has no member named 'foo' |
| 38 | main.zig:1:16: note: enum declared here | 37 | main.zig:1:16: note: enum declared here |
test/incremental/remove_function_with_changed_callee+3-6| ... | @@ -28,8 +28,7 @@ pub fn main() !void { | ... | @@ -28,8 +28,7 @@ pub fn main() !void { |
| 28 | } | 28 | } |
| 29 | #} | 29 | #} |
| 30 | #update | 30 | #update |
| 31 | #check stdout { | 31 | #check stdout "0" |
| 32 | 0#} | ||
| 33 | 32 | ||
| 34 | #write main.zig { | 33 | #write main.zig { |
| 35 | //! This update must preserve `bar` `InternPool.Index` value, while changing its wasm function type. | 34 | //! This update must preserve `bar` `InternPool.Index` value, while changing its wasm function type. |
| ... | @@ -54,7 +53,5 @@ pub fn main() !void { | ... | @@ -54,7 +53,5 @@ pub fn main() !void { |
| 54 | try bar(.one); | 53 | try bar(.one); |
| 55 | } | 54 | } |
| 56 | #} | 55 | #} |
| 57 | #update { | 56 | #update "remove foo and change bar wasm function type" |
| 58 | remove foo and change bar wasm function type#} | 57 | #check stdout "1" |
| 59 | #check stdout { | ||
| 60 | 1#} |
test/incremental/remove_invalid_union_backing_enum+2-4| ... | @@ -33,7 +33,5 @@ pub fn main() void { | ... | @@ -33,7 +33,5 @@ pub fn main() void { |
| 33 | _ = u; | 33 | _ = u; |
| 34 | } | 34 | } |
| 35 | #} | 35 | #} |
| 36 | #update { | 36 | #update "remove invalid backing enum" |
| 37 | remove invalid backing enum#} | 37 | #check stdout |
| 38 | #check stdout { | ||
| 39 | #} |
test/incremental/struct_reification_becomes_anon_init+1-2| ... | @@ -23,8 +23,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -23,8 +23,7 @@ pub fn main(init: std.process.Init) !void { |
| 23 | } | 23 | } |
| 24 | const std = @import("std"); | 24 | const std = @import("std"); |
| 25 | #} | 25 | #} |
| 26 | #update { | 26 | #update "change to `@TypeOf` anonymous init" |
| 27 | change to `@TypeOf` anonymous init#} | ||
| 28 | #check stdout { | 27 | #check stdout { |
| 29 | anon_field | 28 | anon_field |
| 30 | #} | 29 | #} |
test/incremental/tag_name+5-10| ... | @@ -11,8 +11,7 @@ pub fn main() !void { | ... | @@ -11,8 +11,7 @@ pub fn main() !void { |
| 11 | } | 11 | } |
| 12 | #} | 12 | #} |
| 13 | #update | 13 | #update |
| 14 | #check stdout { | 14 | #check stdout "first" |
| 15 | first#} | ||
| 16 | 15 | ||
| 17 | #write main.zig { | 16 | #write main.zig { |
| 18 | const std = @import("std"); | 17 | const std = @import("std"); |
| ... | @@ -22,10 +21,8 @@ pub fn main() !void { | ... | @@ -22,10 +21,8 @@ pub fn main() !void { |
| 22 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); | 21 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); |
| 23 | } | 22 | } |
| 24 | #} | 23 | #} |
| 25 | #update { | 24 | #update "no change" |
| 26 | no change#} | 25 | #check stdout "first" |
| 27 | #check stdout { | ||
| 28 | first#} | ||
| 29 | 26 | ||
| 30 | #write main.zig { | 27 | #write main.zig { |
| 31 | const std = @import("std"); | 28 | const std = @import("std"); |
| ... | @@ -35,7 +32,5 @@ pub fn main() !void { | ... | @@ -35,7 +32,5 @@ pub fn main() !void { |
| 35 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); | 32 | try std.Io.File.stdout().writeStreamingAll(io, @tagName(some_enum)); |
| 36 | } | 33 | } |
| 37 | #} | 34 | #} |
| 38 | #update { | 35 | #update "swap fields" |
| 39 | swap fields#} | 36 | #check stdout "first" |
| 40 | #check stdout { | ||
| 41 | first#} |
test/incremental/temporary_analysis_error_in_generic_signature+2-4| ... | @@ -33,8 +33,7 @@ fn bar(comptime x: u8) @This().FooAlias { | ... | @@ -33,8 +33,7 @@ fn bar(comptime x: u8) @This().FooAlias { |
| 33 | } | 33 | } |
| 34 | const std = @import("std"); | 34 | const std = @import("std"); |
| 35 | #} | 35 | #} |
| 36 | #update { | 36 | #update "change generic signature to use non-existent member" |
| 37 | change generic signature to use non-existent member#} | ||
| 38 | #check errors { | 37 | #check errors { |
| 39 | main.zig:8:31: error: root source file struct 'main' has no member named 'FooAlias' | 38 | main.zig:8:31: error: root source file struct 'main' has no member named 'FooAlias' |
| 40 | main.zig:1:1: note: struct declared here | 39 | main.zig:1:1: note: struct declared here |
| ... | @@ -54,8 +53,7 @@ fn bar(comptime x: u8) @This().FooAlias { | ... | @@ -54,8 +53,7 @@ fn bar(comptime x: u8) @This().FooAlias { |
| 54 | } | 53 | } |
| 55 | const std = @import("std"); | 54 | const std = @import("std"); |
| 56 | #} | 55 | #} |
| 57 | #update { | 56 | #update "add that member, fixing the error" |
| 58 | add that member, fixing the error#} | ||
| 59 | #check stdout { | 57 | #check stdout { |
| 60 | Z | 58 | Z |
| 61 | #} | 59 | #} |
test/incremental/temporary_parse_error+4-8| ... | @@ -7,14 +7,12 @@ const std = @import("std"); | ... | @@ -7,14 +7,12 @@ const std = @import("std"); |
| 7 | pub fn main() !void {} | 7 | pub fn main() !void {} |
| 8 | #} | 8 | #} |
| 9 | #update | 9 | #update |
| 10 | #check stdout { | 10 | #check stdout |
| 11 | #} | ||
| 12 | 11 | ||
| 13 | #write main.zig { | 12 | #write main.zig { |
| 14 | pub fn main() !void { | 13 | pub fn main() !void { |
| 15 | #} | 14 | #} |
| 16 | #update { | 15 | #update "introduce parse error" |
| 17 | introduce parse error#} | ||
| 18 | #check errors { | 16 | #check errors { |
| 19 | main.zig:2:1: error: expected statement, found 'EOF' | 17 | main.zig:2:1: error: expected statement, found 'EOF' |
| 20 | #} | 18 | #} |
| ... | @@ -22,7 +20,5 @@ main.zig:2:1: error: expected statement, found 'EOF' | ... | @@ -22,7 +20,5 @@ main.zig:2:1: error: expected statement, found 'EOF' |
| 22 | #write main.zig { | 20 | #write main.zig { |
| 23 | pub fn main() !void {} | 21 | pub fn main() !void {} |
| 24 | #} | 22 | #} |
| 25 | #update { | 23 | #update "fix parse error" |
| 26 | fix parse error#} | 24 | #check stdout |
| 27 | #check stdout { | ||
| 28 | #} |
test/incremental/type_becomes_comptime_only+5-10| ... | @@ -14,8 +14,7 @@ pub fn main() void { | ... | @@ -14,8 +14,7 @@ pub fn main() void { |
| 14 | } | 14 | } |
| 15 | #} | 15 | #} |
| 16 | #update | 16 | #update |
| 17 | #check stdout { | 17 | #check stdout |
| 18 | #} | ||
| 19 | 18 | ||
| 20 | #write main.zig { | 19 | #write main.zig { |
| 21 | const SomeType = comptime_int; | 20 | const SomeType = comptime_int; |
| ... | @@ -28,10 +27,8 @@ pub fn main() void { | ... | @@ -28,10 +27,8 @@ pub fn main() void { |
| 28 | s.foo(); | 27 | s.foo(); |
| 29 | } | 28 | } |
| 30 | #} | 29 | #} |
| 31 | #update { | 30 | #update "make S comptime-only" |
| 32 | make S comptime-only#} | 31 | #check stdout |
| 33 | #check stdout { | ||
| 34 | #} | ||
| 35 | 32 | ||
| 36 | #write main.zig { | 33 | #write main.zig { |
| 37 | const SomeType = u16; | 34 | const SomeType = u16; |
| ... | @@ -44,7 +41,5 @@ pub fn main() void { | ... | @@ -44,7 +41,5 @@ pub fn main() void { |
| 44 | s.foo(); | 41 | s.foo(); |
| 45 | } | 42 | } |
| 46 | #} | 43 | #} |
| 47 | #update { | 44 | #update "make S runtime again" |
| 48 | make S runtime again#} | 45 | #check stdout |
| 49 | #check stdout { | ||
| 50 | #} |
test/incremental/type_dependency_loop+7-14| ... | @@ -24,10 +24,8 @@ pub fn main() void { | ... | @@ -24,10 +24,8 @@ pub fn main() void { |
| 24 | _ = B; | 24 | _ = B; |
| 25 | } | 25 | } |
| 26 | #} | 26 | #} |
| 27 | #update { | 27 | #update "remove reference to dependency loop" |
| 28 | remove reference to dependency loop#} | 28 | #check stdout |
| 29 | #check stdout { | ||
| 30 | #} | ||
| 31 | 29 | ||
| 32 | #write main.zig { | 30 | #write main.zig { |
| 33 | pub const A = struct { b: B }; | 31 | pub const A = struct { b: B }; |
| ... | @@ -36,10 +34,8 @@ pub fn main() void { | ... | @@ -36,10 +34,8 @@ pub fn main() void { |
| 36 | _ = B; | 34 | _ = B; |
| 37 | } | 35 | } |
| 38 | #} | 36 | #} |
| 39 | #update { | 37 | #update "change dependency loop without fixing it" |
| 40 | change dependency loop without fixing it#} | 38 | #check stdout |
| 41 | #check stdout { | ||
| 42 | #} | ||
| 43 | 39 | ||
| 44 | #write main.zig { | 40 | #write main.zig { |
| 45 | pub const A = struct { b: B }; | 41 | pub const A = struct { b: B }; |
| ... | @@ -48,8 +44,7 @@ pub fn main() void { | ... | @@ -48,8 +44,7 @@ pub fn main() void { |
| 48 | _ = @as(B, undefined); | 44 | _ = @as(B, undefined); |
| 49 | } | 45 | } |
| 50 | #} | 46 | #} |
| 51 | #update { | 47 | #update "reference dependency loop again" |
| 52 | reference dependency loop again#} | ||
| 53 | #check errors { | 48 | #check errors { |
| 54 | error: dependency loop with length 2 | 49 | error: dependency loop with length 2 |
| 55 | main.zig:2:43: note: type 'main.B' depends on type 'main.A' for alignment query here | 50 | main.zig:2:43: note: type 'main.B' depends on type 'main.A' for alignment query here |
| ... | @@ -64,7 +59,5 @@ pub fn main() void { | ... | @@ -64,7 +59,5 @@ pub fn main() void { |
| 64 | _ = @as(B, undefined); | 59 | _ = @as(B, undefined); |
| 65 | } | 60 | } |
| 66 | #} | 61 | #} |
| 67 | #update { | 62 | #update "fix dependency loop" |
| 68 | fix dependency loop#} | 63 | #check stdout |
| 69 | #check stdout { | ||
| 70 | #} |
test/incremental/unreferenced_error+3-6| ... | @@ -23,8 +23,7 @@ pub fn main() !void { | ... | @@ -23,8 +23,7 @@ pub fn main() !void { |
| 23 | const a = @compileError("bad a"); | 23 | const a = @compileError("bad a"); |
| 24 | const io = std.Io.Threaded.global_single_threaded.io(); | 24 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 25 | #} | 25 | #} |
| 26 | #update { | 26 | #update "introduce compile error" |
| 27 | introduce compile error#} | ||
| 28 | #check errors { | 27 | #check errors { |
| 29 | main.zig:5:11: error: bad a | 28 | main.zig:5:11: error: bad a |
| 30 | #} | 29 | #} |
| ... | @@ -38,8 +37,7 @@ const a = @compileError("bad a"); | ... | @@ -38,8 +37,7 @@ const a = @compileError("bad a"); |
| 38 | const b = "Hi there!\n"; | 37 | const b = "Hi there!\n"; |
| 39 | const io = std.Io.Threaded.global_single_threaded.io(); | 38 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 40 | #} | 39 | #} |
| 41 | #update { | 40 | #update "remove error reference" |
| 42 | remove error reference#} | ||
| 43 | #check stdout { | 41 | #check stdout { |
| 44 | Hi there! | 42 | Hi there! |
| 45 | #} | 43 | #} |
| ... | @@ -53,8 +51,7 @@ const a = "Back to a\n"; | ... | @@ -53,8 +51,7 @@ const a = "Back to a\n"; |
| 53 | const b = @compileError("bad b"); | 51 | const b = @compileError("bad b"); |
| 54 | const io = std.Io.Threaded.global_single_threaded.io(); | 52 | const io = std.Io.Threaded.global_single_threaded.io(); |
| 55 | #} | 53 | #} |
| 56 | #update { | 54 | #update "introduce and remove reference to error" |
| 57 | introduce and remove reference to error#} | ||
| 58 | #check stdout { | 55 | #check stdout { |
| 59 | Back to a | 56 | Back to a |
| 60 | #} | 57 | #} |
test/src/Runner.zig deleted-2141| ... | @@ -1,2141 +0,0 @@ | ||
| 1 | const assert = std.debug.assert; | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const std = @import("std"); | ||
| 4 | const Runner = @This(); | ||
| 5 | |||
| 6 | gpa: std.mem.Allocator, | ||
| 7 | arena: *std.heap.ArenaAllocator, | ||
| 8 | environ_map: std.process.Environ.Map, | ||
| 9 | io: std.Io, | ||
| 10 | prog_node: std.Progress.Node, | ||
| 11 | args: Args, | ||
| 12 | host: std.Target, | ||
| 13 | server: std.zig.Server, | ||
| 14 | eb_wip: std.zig.ErrorBundle.Wip, | ||
| 15 | |||
| 16 | const Args = struct { | ||
| 17 | @"test": ?struct { | ||
| 18 | input_dir: std.zig.Server.Message.InputDir, | ||
| 19 | dir: std.Io.Dir, | ||
| 20 | }, | ||
| 21 | manifest_file: ?std.Io.File, | ||
| 22 | zig_exe: ?std.Io.File, | ||
| 23 | lib_dir: ?std.Io.Dir, | ||
| 24 | src_dir: ?std.Io.Dir, | ||
| 25 | targets: std.ArrayList(u32), | ||
| 26 | target_bytes: std.ArrayList(u8), | ||
| 27 | |||
| 28 | libc_runtimes_dir: ?std.Io.Dir, | ||
| 29 | enable_darling: bool, | ||
| 30 | enable_qemu: bool, | ||
| 31 | enable_rosetta: bool, | ||
| 32 | enable_wasmtime: bool, | ||
| 33 | enable_wine: bool, | ||
| 34 | |||
| 35 | gdb_exe: ?std.Io.File, | ||
| 36 | lldb_exe: ?std.Io.File, | ||
| 37 | |||
| 38 | quiet: bool, | ||
| 39 | |||
| 40 | fn deinit(args: *Args, gpa: std.mem.Allocator) void { | ||
| 41 | args.targets.deinit(gpa); | ||
| 42 | args.target_bytes.deinit(gpa); | ||
| 43 | args.* = undefined; | ||
| 44 | } | ||
| 45 | |||
| 46 | fn addTarget(args: *Args, gpa: std.mem.Allocator, target: []const u8) std.mem.Allocator.Error!void { | ||
| 47 | try args.target_bytes.ensureUnusedCapacity(gpa, target.len + 1); | ||
| 48 | try args.targets.append(gpa, @intCast(args.target_bytes.items.len)); | ||
| 49 | args.target_bytes.appendSliceAssumeCapacity(target); | ||
| 50 | args.target_bytes.appendAssumeCapacity(0); | ||
| 51 | } | ||
| 52 | }; | ||
| 53 | |||
| 54 | fn deinit(runner: *Runner) void { | ||
| 55 | runner.environ_map.deinit(); | ||
| 56 | runner.prog_node.end(); | ||
| 57 | runner.args.deinit(runner.gpa); | ||
| 58 | runner.eb_wip.deinit(); | ||
| 59 | runner.* = undefined; | ||
| 60 | } | ||
| 61 | |||
| 62 | const FailError = error{AlreadyReported} || std.mem.Allocator.Error || std.Io.Cancelable; | ||
| 63 | fn fail(runner: *Runner, comptime fmt: []const u8, args: anytype) FailError { | ||
| 64 | return runner.failString(try runner.eb_wip.printString(fmt, args)); | ||
| 65 | } | ||
| 66 | fn failString(runner: *Runner, msg: std.zig.ErrorBundle.String) FailError { | ||
| 67 | try runner.eb_wip.addRootErrorMessage(.{ | ||
| 68 | .msg = msg, | ||
| 69 | }); | ||
| 70 | return error.AlreadyReported; | ||
| 71 | } | ||
| 72 | |||
| 73 | pub const ProtocolError = error{ | ||
| 74 | ServerReadFailed, | ||
| 75 | ServerWriteFailed, | ||
| 76 | ServerEndOfStream, | ||
| 77 | } || FailError; | ||
| 78 | pub fn runServer(runner: *Runner) ProtocolError { | ||
| 79 | runner.server.serveStringMessage(.zig_version, builtin.zig_version_string) catch |err| switch (err) { | ||
| 80 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 81 | }; | ||
| 82 | while (true) { | ||
| 83 | _ = runner.arena.reset(.retain_capacity); | ||
| 84 | const arena = runner.arena.allocator(); | ||
| 85 | const hdr = runner.server.receiveMessage() catch |err| switch (err) { | ||
| 86 | error.ReadFailed => return error.ServerReadFailed, | ||
| 87 | error.EndOfStream => return error.ServerEndOfStream, | ||
| 88 | }; | ||
| 89 | switch (hdr.tag) { | ||
| 90 | else => |tag| return runner.fail("unsupported message: {t}", .{tag}), | ||
| 91 | .exit => std.process.exit(0), | ||
| 92 | .args => { | ||
| 93 | const args_body = try arena.alloc(u8, hdr.bytes_len); | ||
| 94 | runner.server.in.readSliceAll(args_body) catch unreachable; | ||
| 95 | const State = enum { | ||
| 96 | positional, | ||
| 97 | @"--zig", | ||
| 98 | @"--lib", | ||
| 99 | @"--src", | ||
| 100 | @"--target", | ||
| 101 | @"--libc-runtimes", | ||
| 102 | @"--gdb", | ||
| 103 | @"--lldb", | ||
| 104 | }; | ||
| 105 | var state: State = .positional; | ||
| 106 | const state_expected: std.enums.EnumArray( | ||
| 107 | State, | ||
| 108 | enum { path, dir, file, string }, | ||
| 109 | ) = .init(.{ | ||
| 110 | .positional = .path, | ||
| 111 | .@"--zig" = .file, | ||
| 112 | .@"--lib" = .dir, | ||
| 113 | .@"--src" = .dir, | ||
| 114 | .@"--target" = .string, | ||
| 115 | .@"--libc-runtimes" = .dir, | ||
| 116 | .@"--gdb" = .file, | ||
| 117 | .@"--lldb" = .file, | ||
| 118 | }); | ||
| 119 | var input_dir: std.zig.Server.Message.InputDir = .cwd; | ||
| 120 | var args_body_offset: usize = 0; | ||
| 121 | while (args_body.len - args_body_offset > 0) { | ||
| 122 | const arg: std.zig.Client.Message.Arg = | ||
| 123 | @fromBackingInt(args_body[args_body_offset]); | ||
| 124 | args_body_offset += 1; | ||
| 125 | arg: switch (arg) { | ||
| 126 | .string => { | ||
| 127 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 128 | const string = args_body[args_body_offset..end]; | ||
| 129 | args_body_offset = end + 1; | ||
| 130 | switch (state) { | ||
| 131 | .positional => if (std.mem.eql(u8, string, "--target")) { | ||
| 132 | state = .@"--target"; | ||
| 133 | continue; | ||
| 134 | } else if (std.mem.cutPrefix(u8, string, "--target=")) |target| { | ||
| 135 | try runner.args.addTarget(runner.gpa, target); | ||
| 136 | } else if (std.mem.eql(u8, string, "--libc-runtimes")) { | ||
| 137 | state = .@"--libc-runtimes"; | ||
| 138 | continue; | ||
| 139 | } else if (std.mem.eql(u8, string, "-fdarling")) { | ||
| 140 | runner.args.enable_darling = true; | ||
| 141 | } else if (std.mem.eql(u8, string, "-fqemu")) { | ||
| 142 | runner.args.enable_qemu = true; | ||
| 143 | } else if (std.mem.eql(u8, string, "-frosetta")) { | ||
| 144 | runner.args.enable_rosetta = true; | ||
| 145 | } else if (std.mem.eql(u8, string, "-fwasmtime")) { | ||
| 146 | runner.args.enable_wasmtime = true; | ||
| 147 | } else if (std.mem.eql(u8, string, "-fwine")) { | ||
| 148 | runner.args.enable_wine = true; | ||
| 149 | } else if (std.mem.eql(u8, string, "--gdb")) { | ||
| 150 | state = .@"--gdb"; | ||
| 151 | continue; | ||
| 152 | } else if (std.mem.eql(u8, string, "--lldb")) { | ||
| 153 | state = .@"--lldb"; | ||
| 154 | continue; | ||
| 155 | } else if (std.mem.eql(u8, string, "--quiet")) { | ||
| 156 | runner.args.quiet = true; | ||
| 157 | }, | ||
| 158 | .@"--target" => try runner.args.addTarget(runner.gpa, string), | ||
| 159 | else => return runner.fail( | ||
| 160 | "\"{t}\" expected {t}, got {t}", | ||
| 161 | .{ state, state_expected.get(state), arg }, | ||
| 162 | ), | ||
| 163 | } | ||
| 164 | }, | ||
| 165 | .prefix => { | ||
| 166 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 167 | const string = args_body[args_body_offset..end]; | ||
| 168 | args_body_offset = end + 1; | ||
| 169 | assert(state == .positional); | ||
| 170 | state = if (std.mem.eql(u8, string, "--zig=")) | ||
| 171 | .@"--zig" | ||
| 172 | else if (std.mem.eql(u8, string, "--lib=")) | ||
| 173 | .@"--lib" | ||
| 174 | else if (std.mem.eql(u8, string, "--src=")) | ||
| 175 | .@"--src" | ||
| 176 | else if (std.mem.eql(u8, string, "--libc-runtimes=")) | ||
| 177 | .@"--libc-runtimes" | ||
| 178 | else if (std.mem.eql(u8, string, "--gdb=")) | ||
| 179 | .@"--gdb" | ||
| 180 | else if (std.mem.eql(u8, string, "--lldb=")) | ||
| 181 | .@"--lldb" | ||
| 182 | else | ||
| 183 | return runner.fail("unsupported arg prefix: {q}", .{string}); | ||
| 184 | continue; | ||
| 185 | }, | ||
| 186 | .suffix => { | ||
| 187 | const end = std.mem.findScalarPos(u8, args_body, args_body_offset, 0).?; | ||
| 188 | const string = args_body[args_body_offset..end]; | ||
| 189 | args_body_offset = end + 1; | ||
| 190 | return runner.fail("unsupported arg suffix: {q}", .{string}); | ||
| 191 | }, | ||
| 192 | .input_dir => { | ||
| 193 | input_dir = @fromBackingInt(@backingInt(input_dir) + 1); | ||
| 194 | continue :arg .output_dir; | ||
| 195 | }, | ||
| 196 | .output_dir => { | ||
| 197 | const dir_handle: *align(1) const std.Io.Dir.Handle = @ptrCast( | ||
| 198 | args_body[args_body_offset..][0..@sizeOf(std.Io.Dir.Handle)], | ||
| 199 | ); | ||
| 200 | args_body_offset += @sizeOf(std.Io.Dir.Handle); | ||
| 201 | const dir = runner.io.vtable.inheritParentDir( | ||
| 202 | runner.io.userdata, | ||
| 203 | dir_handle.*, | ||
| 204 | ) catch |err| switch (err) { | ||
| 205 | error.Canceled => |e| return e, | ||
| 206 | else => |e| return runner.fail( | ||
| 207 | "unable to inherit parent dir: {t}", | ||
| 208 | .{e}, | ||
| 209 | ), | ||
| 210 | }; | ||
| 211 | switch (state) { | ||
| 212 | .positional => { | ||
| 213 | if (runner.args.@"test" != null or | ||
| 214 | runner.args.manifest_file != null) return runner.fail( | ||
| 215 | "{t} specified multiple times", | ||
| 216 | .{state}, | ||
| 217 | ); | ||
| 218 | runner.args.@"test" = .{ .input_dir = input_dir, .dir = dir }; | ||
| 219 | const manifest_path = "manifest"; | ||
| 220 | runner.args.manifest_file = dir.openFile( | ||
| 221 | runner.io, | ||
| 222 | manifest_path, | ||
| 223 | .{}, | ||
| 224 | ) catch |err| switch (err) { | ||
| 225 | error.Canceled => |e| return e, | ||
| 226 | else => |e| return runner.fail( | ||
| 227 | "unable to open manifest file: {t}", | ||
| 228 | .{e}, | ||
| 229 | ), | ||
| 230 | }; | ||
| 231 | try runner.discoverInput(input_dir, manifest_path); | ||
| 232 | }, | ||
| 233 | .@"--lib" => { | ||
| 234 | if (runner.args.lib_dir != null) return runner.fail( | ||
| 235 | "\"{t}\" specified multiple times", | ||
| 236 | .{state}, | ||
| 237 | ); | ||
| 238 | runner.args.lib_dir = dir; | ||
| 239 | }, | ||
| 240 | .@"--src" => { | ||
| 241 | if (runner.args.src_dir != null) return runner.fail( | ||
| 242 | "\"{t}\" specified multiple times", | ||
| 243 | .{state}, | ||
| 244 | ); | ||
| 245 | runner.args.src_dir = dir; | ||
| 246 | }, | ||
| 247 | .@"--libc-runtimes" => { | ||
| 248 | if (runner.args.libc_runtimes_dir != null) return runner.fail( | ||
| 249 | "\"{t}\" specified multiple times", | ||
| 250 | .{state}, | ||
| 251 | ); | ||
| 252 | runner.args.libc_runtimes_dir = dir; | ||
| 253 | }, | ||
| 254 | else => return runner.fail( | ||
| 255 | "\"{t}\" expected {t}, got {t}", | ||
| 256 | .{ state, state_expected.get(state), arg }, | ||
| 257 | ), | ||
| 258 | } | ||
| 259 | }, | ||
| 260 | .input_file, .input_file_content, .output_file => { | ||
| 261 | const file_handle: *align(1) const std.Io.File.Handle = @ptrCast( | ||
| 262 | args_body[args_body_offset..][0..@sizeOf(std.Io.File.Handle)], | ||
| 263 | ); | ||
| 264 | args_body_offset += @sizeOf(std.Io.File.Handle); | ||
| 265 | const file = runner.io.vtable.inheritParentFile( | ||
| 266 | runner.io.userdata, | ||
| 267 | file_handle.*, | ||
| 268 | .{ .nonblocking = false }, | ||
| 269 | ) catch |err| switch (err) { | ||
| 270 | error.Canceled => |e| return e, | ||
| 271 | else => |e| return runner.fail( | ||
| 272 | "unable to inherit parent file: {t}", | ||
| 273 | .{e}, | ||
| 274 | ), | ||
| 275 | }; | ||
| 276 | switch (state) { | ||
| 277 | .positional => { | ||
| 278 | if (runner.args.@"test" != null or | ||
| 279 | runner.args.manifest_file != null) return runner.fail( | ||
| 280 | "{t} specified multiple times", | ||
| 281 | .{state}, | ||
| 282 | ); | ||
| 283 | runner.args.manifest_file = file; | ||
| 284 | }, | ||
| 285 | .@"--zig" => { | ||
| 286 | if (runner.args.zig_exe != null) return runner.fail( | ||
| 287 | "\"{t}\" specified multiple times", | ||
| 288 | .{state}, | ||
| 289 | ); | ||
| 290 | runner.args.zig_exe = file; | ||
| 291 | }, | ||
| 292 | .@"--gdb" => { | ||
| 293 | if (runner.args.gdb_exe != null) return runner.fail( | ||
| 294 | "\"{t}\" specified multiple times", | ||
| 295 | .{state}, | ||
| 296 | ); | ||
| 297 | runner.args.gdb_exe = file; | ||
| 298 | }, | ||
| 299 | .@"--lldb" => { | ||
| 300 | if (runner.args.lldb_exe != null) return runner.fail( | ||
| 301 | "\"{t}\" specified multiple times", | ||
| 302 | .{state}, | ||
| 303 | ); | ||
| 304 | runner.args.lldb_exe = file; | ||
| 305 | }, | ||
| 306 | else => return runner.fail( | ||
| 307 | "\"{t}\" expected {t}, got {t}", | ||
| 308 | .{ state, state_expected.get(state), arg }, | ||
| 309 | ), | ||
| 310 | } | ||
| 311 | }, | ||
| 312 | } | ||
| 313 | state = .positional; | ||
| 314 | } | ||
| 315 | }, | ||
| 316 | .query_test_metadata => { | ||
| 317 | const expected_panic_msgs = try arena.alloc(u32, runner.args.targets.items.len); | ||
| 318 | @memset(expected_panic_msgs, 0); | ||
| 319 | runner.server.serveTestMetadata(.{ | ||
| 320 | .names = runner.args.targets.items, | ||
| 321 | .expected_panic_msgs = expected_panic_msgs, | ||
| 322 | .string_bytes = runner.args.target_bytes.items, | ||
| 323 | }) catch |err| switch (err) { | ||
| 324 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 325 | }; | ||
| 326 | }, | ||
| 327 | .run_test => { | ||
| 328 | const test_index = runner.server.receiveBody_u32() catch unreachable; | ||
| 329 | runner.server.serveBodylessMessage(.test_started) catch |err| switch (err) { | ||
| 330 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 331 | }; | ||
| 332 | runner.server.serveTestResults(.{ | ||
| 333 | .index = test_index, | ||
| 334 | .flags = .{ | ||
| 335 | .status = if (runner.testOne(test_index)) | ||
| 336 | .pass | ||
| 337 | else |err| status: switch (err) { | ||
| 338 | error.Canceled, | ||
| 339 | error.OutOfMemory, | ||
| 340 | error.ServerWriteFailed, | ||
| 341 | => |e| return e, | ||
| 342 | error.AlreadyReported => { | ||
| 343 | var eb = try runner.eb_wip.toOwnedBundle(""); | ||
| 344 | defer eb.deinit(runner.gpa); | ||
| 345 | assert(eb.errorMessageCount() > 0); // already reported what? | ||
| 346 | runner.server.serveErrorBundle( | ||
| 347 | .error_bundle, | ||
| 348 | eb, | ||
| 349 | ) catch |server_err| switch (server_err) { | ||
| 350 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 351 | }; | ||
| 352 | const eb_wip: std.zig.ErrorBundle.Wip = try .init(runner.gpa); | ||
| 353 | runner.eb_wip.deinit(); | ||
| 354 | runner.eb_wip = eb_wip; | ||
| 355 | break :status .fail; | ||
| 356 | }, | ||
| 357 | error.SkipTest => .skip, | ||
| 358 | }, | ||
| 359 | .fuzz = false, | ||
| 360 | .log_err_count = 0, | ||
| 361 | .leak_count = 0, | ||
| 362 | }, | ||
| 363 | }) catch |err| switch (err) { | ||
| 364 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 365 | }; | ||
| 366 | assert(runner.eb_wip.root_list.items.len == 0); // failed to report | ||
| 367 | }, | ||
| 368 | } | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | fn discoverInput( | ||
| 373 | runner: *Runner, | ||
| 374 | dir: std.zig.Server.Message.InputDir, | ||
| 375 | sub_path: []const u8, | ||
| 376 | ) error{ServerWriteFailed}!void { | ||
| 377 | return runner.discoverInputInner(dir, sub_path) catch |err| switch (err) { | ||
| 378 | error.WriteFailed => return error.ServerWriteFailed, | ||
| 379 | }; | ||
| 380 | } | ||
| 381 | fn discoverInputInner( | ||
| 382 | runner: *Runner, | ||
| 383 | dir: std.zig.Server.Message.InputDir, | ||
| 384 | sub_path: []const u8, | ||
| 385 | ) std.Io.Writer.Error!void { | ||
| 386 | try runner.server.serveMessageHeader(.{ | ||
| 387 | .tag = .discovered_inputs, | ||
| 388 | .bytes_len = @intCast(@sizeOf(std.zig.Server.Message.InputDir) + | ||
| 389 | sub_path.len + 1), | ||
| 390 | }); | ||
| 391 | try runner.server.out.writeInt(u32, @backingInt(dir), .little); | ||
| 392 | try runner.server.out.writeAll(sub_path); | ||
| 393 | try runner.server.out.writeByte(0); | ||
| 394 | try runner.server.out.flush(); | ||
| 395 | } | ||
| 396 | |||
| 397 | pub const SourceReader = struct { | ||
| 398 | const Io = std.Io; | ||
| 399 | |||
| 400 | backing: *std.Io.Reader, | ||
| 401 | line_aw: std.Io.Writer.Allocating, | ||
| 402 | interface: std.Io.Reader, | ||
| 403 | line: u32, | ||
| 404 | eb_wip: *std.zig.ErrorBundle.Wip, | ||
| 405 | src_path: std.zig.ErrorBundle.String, | ||
| 406 | |||
| 407 | pub fn init( | ||
| 408 | reader: *Io.Reader, | ||
| 409 | gpa: std.mem.Allocator, | ||
| 410 | eb_wip: *std.zig.ErrorBundle.Wip, | ||
| 411 | src_path: std.zig.ErrorBundle.String, | ||
| 412 | ) SourceReader { | ||
| 413 | return .{ | ||
| 414 | .backing = reader, | ||
| 415 | .line_aw = .init(gpa), | ||
| 416 | .interface = .{ | ||
| 417 | .vtable = &.{ | ||
| 418 | .stream = stream, | ||
| 419 | .discard = discard, | ||
| 420 | .readVec = readVec, | ||
| 421 | .rebase = rebase, | ||
| 422 | }, | ||
| 423 | .buffer = &.{}, | ||
| 424 | .seek = 0, | ||
| 425 | .end = 0, | ||
| 426 | }, | ||
| 427 | .line = 0, | ||
| 428 | .eb_wip = eb_wip, | ||
| 429 | .src_path = src_path, | ||
| 430 | }; | ||
| 431 | } | ||
| 432 | fn deinit(s: *SourceReader) void { | ||
| 433 | s.line_aw.deinit(); | ||
| 434 | } | ||
| 435 | fn stream(r: *Io.Reader, _: *Io.Writer, _: Io.Limit) Io.Reader.StreamError!usize { | ||
| 436 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 437 | try s.bufferLine(); | ||
| 438 | return 0; | ||
| 439 | } | ||
| 440 | fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { | ||
| 441 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 442 | const remaining = r.buffered(); | ||
| 443 | if (remaining.len > 0) { | ||
| 444 | const n = limit.minInt(remaining.len); | ||
| 445 | r.toss(n); | ||
| 446 | return n; | ||
| 447 | } | ||
| 448 | try s.bufferLine(); | ||
| 449 | return 0; | ||
| 450 | } | ||
| 451 | fn readVec(r: *Io.Reader, _: [][]u8) Io.Reader.Error!usize { | ||
| 452 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 453 | try s.bufferLine(); | ||
| 454 | return 0; | ||
| 455 | } | ||
| 456 | fn rebase(r: *Io.Reader, capacity: usize) Io.Reader.RebaseError!void { | ||
| 457 | const s: *SourceReader = @fieldParentPtr("interface", r); | ||
| 458 | defer s.updateBuffer(); | ||
| 459 | s.line_aw.ensureUnusedCapacity(capacity) catch |err| switch (err) { | ||
| 460 | error.OutOfMemory => return error.ReadFailed, | ||
| 461 | }; | ||
| 462 | } | ||
| 463 | fn updateBuffer(s: *SourceReader) void { | ||
| 464 | s.interface.buffer = s.line_aw.writer.buffer; | ||
| 465 | s.interface.end = s.line_aw.writer.end; | ||
| 466 | } | ||
| 467 | fn bufferLine(s: *SourceReader) Io.Reader.Error!void { | ||
| 468 | defer s.updateBuffer(); | ||
| 469 | { | ||
| 470 | const written = s.line_aw.written(); | ||
| 471 | const start = if (std.mem.findScalarLast(u8, written[0..s.interface.seek], '\n')) |newline| | ||
| 472 | newline + 1 | ||
| 473 | else | ||
| 474 | 0; | ||
| 475 | s.line += @intCast(std.mem.countScalar(u8, written[0..start], '\n')); | ||
| 476 | const preserve = written[start..]; | ||
| 477 | @memmove(written[0..preserve.len], preserve); | ||
| 478 | s.line_aw.shrinkRetainingCapacity(preserve.len); | ||
| 479 | s.interface.seek -= start; | ||
| 480 | } | ||
| 481 | const n = s.backing.streamDelimiterEnding(&s.line_aw.writer, '\n') catch |err| switch (err) { | ||
| 482 | error.ReadFailed => |e| return e, | ||
| 483 | error.WriteFailed => return error.ReadFailed, | ||
| 484 | }; | ||
| 485 | if (std.mem.endsWith(u8, s.line_aw.written(), "\r")) s.line_aw.writer.end -= 1; | ||
| 486 | const buffered = s.backing.buffered(); | ||
| 487 | if (n == 0 and buffered.len == 0) return error.EndOfStream; | ||
| 488 | if (std.mem.startsWith(u8, buffered, "\n")) { | ||
| 489 | s.line_aw.writer.writeByte('\n') catch |err| switch (err) { | ||
| 490 | error.WriteFailed => return error.ReadFailed, | ||
| 491 | }; | ||
| 492 | s.backing.toss(1); | ||
| 493 | } | ||
| 494 | } | ||
| 495 | fn fail( | ||
| 496 | s: *SourceReader, | ||
| 497 | src_loc: []const u8, | ||
| 498 | comptime fmt: []const u8, | ||
| 499 | args: anytype, | ||
| 500 | ) FailError { | ||
| 501 | return s.failString(src_loc, try s.eb_wip.printString(fmt, args)); | ||
| 502 | } | ||
| 503 | fn failString( | ||
| 504 | s: *SourceReader, | ||
| 505 | src_loc: []const u8, | ||
| 506 | msg: std.zig.ErrorBundle.String, | ||
| 507 | ) FailError { | ||
| 508 | const line: u32, const column: u32, const source_line = src_loc: { | ||
| 509 | const pos = src_loc.ptr - s.interface.buffer.ptr; | ||
| 510 | const end = std.mem.findScalarPos(u8, s.interface.buffer, pos, '\n') orelse | ||
| 511 | s.interface.buffer.len; | ||
| 512 | const start = if (std.mem.findScalarLast(u8, s.interface.buffer[0..end], '\n')) |newline| | ||
| 513 | newline + 1 | ||
| 514 | else | ||
| 515 | 0; | ||
| 516 | break :src_loc .{ | ||
| 517 | @intCast(s.line + std.mem.countScalar(u8, s.interface.buffer[0..start], '\n')), | ||
| 518 | @intCast(pos - start), | ||
| 519 | s.interface.buffer[start..end], | ||
| 520 | }; | ||
| 521 | }; | ||
| 522 | try s.eb_wip.addRootErrorMessage(.{ | ||
| 523 | .msg = msg, | ||
| 524 | .src_loc = try s.eb_wip.addSourceLocation(.{ | ||
| 525 | .src_path = s.src_path, | ||
| 526 | .line = line, | ||
| 527 | .column = column, | ||
| 528 | .span_start = column, | ||
| 529 | .span_main = column, | ||
| 530 | .span_end = @intCast(column + src_loc.len), | ||
| 531 | .source_line = try s.eb_wip.addString(source_line), | ||
| 532 | }), | ||
| 533 | }); | ||
| 534 | return error.AlreadyReported; | ||
| 535 | } | ||
| 536 | }; | ||
| 537 | pub const DelimitedReader = struct { | ||
| 538 | const Io = std.Io; | ||
| 539 | |||
| 540 | unlimited: *Io.Reader, | ||
| 541 | remaining: Io.Limit, | ||
| 542 | delimiter: []const u8, | ||
| 543 | interface: Io.Reader, | ||
| 544 | |||
| 545 | pub fn init(reader: *Io.Reader, delimiter: []const u8, buffer: []u8) DelimitedReader { | ||
| 546 | return .{ | ||
| 547 | .unlimited = reader, | ||
| 548 | .remaining = .nothing, | ||
| 549 | .delimiter = delimiter, | ||
| 550 | .interface = .{ | ||
| 551 | .vtable = &.{ | ||
| 552 | .stream = stream, | ||
| 553 | .discard = discard, | ||
| 554 | }, | ||
| 555 | .buffer = buffer, | ||
| 556 | .seek = 0, | ||
| 557 | .end = 0, | ||
| 558 | }, | ||
| 559 | }; | ||
| 560 | } | ||
| 561 | fn stream(r: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { | ||
| 562 | const d: *DelimitedReader = @fieldParentPtr("interface", r); | ||
| 563 | const block = try d.unlimited.peekGreedy(d.delimiter.len); | ||
| 564 | d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse | ||
| 565 | block.len - d.delimiter.len + 1); | ||
| 566 | if (d.remaining == .nothing) return error.EndOfStream; | ||
| 567 | const n = try d.unlimited.stream(w, limit.min(d.remaining)); | ||
| 568 | d.remaining = d.remaining.subtract(n).?; | ||
| 569 | return n; | ||
| 570 | } | ||
| 571 | fn discard(r: *Io.Reader, limit: Io.Limit) Io.Reader.Error!usize { | ||
| 572 | const d: *DelimitedReader = @fieldParentPtr("interface", r); | ||
| 573 | const block = try d.unlimited.peekGreedy(d.delimiter.len); | ||
| 574 | d.remaining = .limited(std.mem.findPos(u8, block, d.remaining.toInt().?, d.delimiter) orelse | ||
| 575 | block.len - d.delimiter.len + 1); | ||
| 576 | if (d.remaining == .nothing) return error.EndOfStream; | ||
| 577 | const n = try d.unlimited.discard(limit.min(d.remaining)); | ||
| 578 | d.remaining = d.remaining.subtract(n).?; | ||
| 579 | return n; | ||
| 580 | } | ||
| 581 | }; | ||
| 582 | |||
| 583 | const Update = struct { | ||
| 584 | target_query: []const u8, | ||
| 585 | manifest_sr: SourceReader, | ||
| 586 | compiler: ?Compiler, | ||
| 587 | src_dir: std.Io.Dir, | ||
| 588 | prog_node: std.Progress.Node, | ||
| 589 | mtime: std.Io.Timestamp, | ||
| 590 | num: usize, | ||
| 591 | allow_skip: bool, | ||
| 592 | }; | ||
| 593 | |||
| 594 | const TestError = error{ | ||
| 595 | SkipTest, | ||
| 596 | } || FailError; | ||
| 597 | fn testOne(runner: *Runner, test_index: u32) (error{ServerWriteFailed} || TestError)!void { | ||
| 598 | const target_query = | ||
| 599 | std.mem.sliceTo(runner.args.target_bytes.items[runner.args.targets.items[test_index]..], 0); | ||
| 600 | const manifest_file = runner.args.manifest_file orelse | ||
| 601 | return runner.fail("missing \"path/to/test\" arg", .{}); | ||
| 602 | var manifest_buffer: [512]u8 = undefined; | ||
| 603 | var manifest_fr = manifest_file.reader(runner.io, &manifest_buffer); | ||
| 604 | var update: Update = .{ | ||
| 605 | .target_query = target_query, | ||
| 606 | .manifest_sr = .init(&manifest_fr.interface, runner.gpa, &runner.eb_wip, src_path_string: { | ||
| 607 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 608 | const src_path = src_path: { | ||
| 609 | break :src_path path_buffer[0 .. manifest_file.realPath( | ||
| 610 | runner.io, | ||
| 611 | &path_buffer, | ||
| 612 | ) catch |err| switch (err) { | ||
| 613 | error.Canceled => |e| return e, | ||
| 614 | else => break :src_path "manifest", | ||
| 615 | }]; | ||
| 616 | }; | ||
| 617 | break :src_path_string try runner.eb_wip.addString(src_path); | ||
| 618 | }), | ||
| 619 | .compiler = null, | ||
| 620 | .src_dir = (runner.args.src_dir orelse | ||
| 621 | return runner.fail("missing \"--src=path/to/src\" arg", .{})).createDirPathOpen( | ||
| 622 | runner.io, | ||
| 623 | target_query, | ||
| 624 | .{}, | ||
| 625 | ) catch |err| switch (err) { | ||
| 626 | error.Canceled => |e| return e, | ||
| 627 | else => |e| return runner.fail("unable to create {q}: {t}", .{ target_query, e }), | ||
| 628 | }, | ||
| 629 | .prog_node = .none, | ||
| 630 | .mtime = .now(runner.io, .real), | ||
| 631 | .num = 0, | ||
| 632 | .allow_skip = true, | ||
| 633 | }; | ||
| 634 | defer { | ||
| 635 | update.manifest_sr.deinit(); | ||
| 636 | update.src_dir.close(runner.io); | ||
| 637 | update.prog_node.end(); | ||
| 638 | } | ||
| 639 | |||
| 640 | // hack around `std.Io.Reader` interface bugs by ensuring the | ||
| 641 | // buffer is larger than the longest supported manifest line | ||
| 642 | try update.manifest_sr.line_aw.ensureTotalCapacityPrecise(512); | ||
| 643 | update.manifest_sr.updateBuffer(); | ||
| 644 | |||
| 645 | var skip_delimiter = false; | ||
| 646 | while (update.manifest_sr.interface.takeSentinel('\n')) |line| { | ||
| 647 | var line_it = std.mem.tokenizeScalar(u8, line, ' '); | ||
| 648 | const cmd_src_loc = line_it.next() orelse continue; | ||
| 649 | if (skip_delimiter) { | ||
| 650 | assert(std.mem.eql(u8, cmd_src_loc, "#}")); | ||
| 651 | skip_delimiter = false; | ||
| 652 | const unexpected = line_it.rest(); | ||
| 653 | if (unexpected.len > 0) | ||
| 654 | return update.manifest_sr.fail(unexpected, "unexpected argument {q}", .{unexpected}); | ||
| 655 | continue; | ||
| 656 | } | ||
| 657 | const cmd = std.meta.stringToEnum(Command, std.mem.cutPrefix(u8, cmd_src_loc, "#") orelse | ||
| 658 | return update.manifest_sr.fail(cmd_src_loc, "expected command", .{})) orelse | ||
| 659 | return update.manifest_sr.fail(cmd_src_loc, "unknown command", .{}); | ||
| 660 | const arg = switch (cmd) { | ||
| 661 | .todo, .exe, .lib, .obj, .update => null, | ||
| 662 | .skip, .write, .delete, .check => line_it.next() orelse | ||
| 663 | return update.manifest_sr.fail(line_it.rest()[0..0], "missing argument", .{}), | ||
| 664 | }; | ||
| 665 | var contents_file: ?std.Io.File = null; | ||
| 666 | defer if (contents_file) |file| file.close(runner.io); | ||
| 667 | var contents_buffer: [512]u8 = undefined; | ||
| 668 | var contents_impl: union(enum) { | ||
| 669 | ending: std.Io.Reader, | ||
| 670 | dr: DelimitedReader, | ||
| 671 | fr: std.Io.File.Reader, | ||
| 672 | } = undefined; | ||
| 673 | const contents_r: *std.Io.Reader = contents_r: switch (cmd) { | ||
| 674 | .skip, .delete => { | ||
| 675 | const unexpected = line_it.rest(); | ||
| 676 | if (unexpected.len > 0) return update.manifest_sr.fail( | ||
| 677 | unexpected, | ||
| 678 | "unexpected argument {q}", | ||
| 679 | .{unexpected}, | ||
| 680 | ); | ||
| 681 | contents_impl = .{ .ending = .ending_instance }; | ||
| 682 | break :contents_r &contents_impl.ending; | ||
| 683 | }, | ||
| 684 | else => { | ||
| 685 | const contents_path = line_it.rest(); | ||
| 686 | if (contents_path.len == 0) { | ||
| 687 | contents_impl = .{ .ending = .ending_instance }; | ||
| 688 | break :contents_r &contents_impl.ending; | ||
| 689 | } | ||
| 690 | |||
| 691 | if (std.mem.eql(u8, contents_path, "{")) { | ||
| 692 | contents_impl = .{ | ||
| 693 | .dr = .init(&update.manifest_sr.interface, "#}", &contents_buffer), | ||
| 694 | }; | ||
| 695 | skip_delimiter = true; | ||
| 696 | break :contents_r &contents_impl.dr.interface; | ||
| 697 | } | ||
| 698 | |||
| 699 | const args_test = runner.args.@"test" orelse return update.manifest_sr.fail( | ||
| 700 | contents_path, | ||
| 701 | "contents file requires directory test", | ||
| 702 | .{}, | ||
| 703 | ); | ||
| 704 | try runner.discoverInput(args_test.input_dir, contents_path); | ||
| 705 | |||
| 706 | contents_file = | ||
| 707 | args_test.dir.openFile(runner.io, contents_path, .{}) catch |err| switch (err) { | ||
| 708 | error.Canceled => |e| return e, | ||
| 709 | else => |e| return update.manifest_sr.fail( | ||
| 710 | contents_path, | ||
| 711 | "unable to open contents file: {t}", | ||
| 712 | .{e}, | ||
| 713 | ), | ||
| 714 | }; | ||
| 715 | contents_impl = .{ .fr = contents_file.?.reader(runner.io, &contents_buffer) }; | ||
| 716 | break :contents_r &contents_impl.fr.interface; | ||
| 717 | }, | ||
| 718 | }; | ||
| 719 | |||
| 720 | runner.handleCommand(&update, cmd_src_loc, cmd, arg, contents_r) catch |err| switch (err) { | ||
| 721 | error.Canceled, | ||
| 722 | error.OutOfMemory, | ||
| 723 | error.AlreadyReported, | ||
| 724 | error.SkipTest, | ||
| 725 | => |e| return e, | ||
| 726 | error.ContentsReadFailed => switch (contents_impl) { | ||
| 727 | .ending => unreachable, | ||
| 728 | .dr => switch (manifest_fr.err orelse return error.OutOfMemory) { | ||
| 729 | error.Canceled => |e| return e, | ||
| 730 | else => |e| return runner.fail("unable to read manifest: {t}", .{e}), | ||
| 731 | }, | ||
| 732 | .fr => |fr| switch (fr.err.?) { | ||
| 733 | error.Canceled => |e| return e, | ||
| 734 | else => |e| return runner.fail("unable to read contents file: {t}", .{e}), | ||
| 735 | }, | ||
| 736 | }, | ||
| 737 | }; | ||
| 738 | } else |err| switch (err) { | ||
| 739 | error.ReadFailed => switch (manifest_fr.err orelse return error.OutOfMemory) { | ||
| 740 | error.Canceled => |e| return e, | ||
| 741 | else => |e| return runner.fail("unable to read manifest: {t}", .{e}), | ||
| 742 | }, | ||
| 743 | error.StreamTooLong => unreachable, | ||
| 744 | error.EndOfStream => if (skip_delimiter) | ||
| 745 | return runner.fail("manifest missing \"#}}\" delimiter", .{}), | ||
| 746 | } | ||
| 747 | if (update.compiler) |*comp| { | ||
| 748 | if (comp.state != .idle) return runner.fail("missing \"#check\"", .{}); | ||
| 749 | try comp.exit(); | ||
| 750 | } | ||
| 751 | } | ||
| 752 | const Command = enum { todo, skip, exe, lib, obj, write, delete, update, check }; | ||
| 753 | fn handleCommand( | ||
| 754 | runner: *Runner, | ||
| 755 | update: *Update, | ||
| 756 | cmd_src_loc: []const u8, | ||
| 757 | cmd: Command, | ||
| 758 | arg: ?[]const u8, | ||
| 759 | contents_r: *std.Io.Reader, | ||
| 760 | ) (error{ContentsReadFailed} || TestError)!void { | ||
| 761 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 762 | const cmd_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "#{t}{s}{s}", .{ | ||
| 763 | cmd, if (arg) |_| " " else "", arg orelse "", | ||
| 764 | }) catch &name_buffer, 0); | ||
| 765 | defer cmd_prog_node.end(); | ||
| 766 | |||
| 767 | cmd: switch (cmd) { | ||
| 768 | .todo => _ = contents_r.discardRemaining() catch |err| switch (err) { | ||
| 769 | error.ReadFailed => {}, | ||
| 770 | }, | ||
| 771 | .skip => if (!update.allow_skip) return update.manifest_sr.fail( | ||
| 772 | cmd_src_loc, | ||
| 773 | "\"#skip\" must appear before other commands", | ||
| 774 | .{}, | ||
| 775 | ) else if (std.mem.eql(u8, update.target_query, arg.?)) return error.SkipTest, | ||
| 776 | .exe, .lib, .obj => runner.spawnCompiler(&update.compiler, update.src_dir, switch (cmd) { | ||
| 777 | else => unreachable, | ||
| 778 | .exe => .Exe, | ||
| 779 | .lib => .Lib, | ||
| 780 | .obj => .Obj, | ||
| 781 | }, contents_r, target: { | ||
| 782 | var component_it = std.mem.splitBackwardsScalar(u8, update.target_query, '-'); | ||
| 783 | const pic_str = component_it.next() orelse | ||
| 784 | return runner.fail("target {q} missing pic", .{update.target_query}); | ||
| 785 | const pic = std.meta.stringToEnum(Compiler.Target.Pic, pic_str) orelse | ||
| 786 | return runner.fail("target {q} unknown pic {q}", .{ update.target_query, pic_str }); | ||
| 787 | const linker_str = component_it.next() orelse | ||
| 788 | return runner.fail("target {q} missing linker", .{update.target_query}); | ||
| 789 | const linker = std.meta.stringToEnum(Compiler.Target.Linker, linker_str) orelse | ||
| 790 | return runner.fail("target {q} unknown linker {q}", .{ update.target_query, linker_str }); | ||
| 791 | const backend_str = component_it.next() orelse | ||
| 792 | return runner.fail("target {q} missing backend", .{update.target_query}); | ||
| 793 | const backend = std.meta.stringToEnum(Compiler.Target.Backend, backend_str) orelse | ||
| 794 | return runner.fail("target {q} unknown backend {q}", .{ update.target_query, backend_str }); | ||
| 795 | const cache_mode_str = component_it.next() orelse | ||
| 796 | return runner.fail("target {q} missing cache mode", .{update.target_query}); | ||
| 797 | const cache_mode = std.meta.stringToEnum(Compiler.Target.CacheMode, cache_mode_str) orelse | ||
| 798 | return runner.fail("target {q} unknown cache mode {q}", .{ update.target_query, cache_mode_str }); | ||
| 799 | const triple = component_it.rest(); | ||
| 800 | break :target .{ | ||
| 801 | .triple = triple, | ||
| 802 | .resolved = std.zig.system.resolveTargetQuery( | ||
| 803 | runner.io, | ||
| 804 | std.Build.parseTargetQuery(.{ | ||
| 805 | .arch_os_abi = triple, | ||
| 806 | .object_format = switch (backend) { | ||
| 807 | .sema, .selfhosted, .llvm => null, | ||
| 808 | .cbe => "c", | ||
| 809 | }, | ||
| 810 | }) catch |err| switch (err) { | ||
| 811 | error.ParseFailed => return runner.fail("unable to parse triple {q}", .{ | ||
| 812 | triple, | ||
| 813 | }), | ||
| 814 | }, | ||
| 815 | ) catch |err| switch (err) { | ||
| 816 | error.Canceled => |e| return e, | ||
| 817 | else => |e| return runner.fail("unable to resolve target {q}: {t}", .{ triple, e }), | ||
| 818 | }, | ||
| 819 | .cache_mode = cache_mode, | ||
| 820 | .backend = backend, | ||
| 821 | .linker = linker, | ||
| 822 | .pic = pic, | ||
| 823 | }; | ||
| 824 | }) catch |err| switch (err) { | ||
| 825 | error.Canceled, error.OutOfMemory, error.AlreadyReported, error.SkipTest => |e| return e, | ||
| 826 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 827 | }, | ||
| 828 | .write => { | ||
| 829 | const file = update.src_dir.createFile(runner.io, arg.?, .{}) catch |err| switch (err) { | ||
| 830 | error.Canceled => |e| return e, | ||
| 831 | else => |e| return update.manifest_sr.fail(arg.?, "unable to create file: {t}", .{e}), | ||
| 832 | }; | ||
| 833 | defer file.close(runner.io); | ||
| 834 | var fw_buffer: [512]u8 = undefined; | ||
| 835 | var fw = file.writer(runner.io, &fw_buffer); | ||
| 836 | _ = contents_r.streamRemaining(&fw.interface) catch |err| switch (err) { | ||
| 837 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 838 | error.WriteFailed => switch (fw.err.?) { | ||
| 839 | error.Canceled => |e| return e, | ||
| 840 | else => |e| return update.manifest_sr.fail( | ||
| 841 | arg.?, | ||
| 842 | "unable to write file: {t}", | ||
| 843 | .{e}, | ||
| 844 | ), | ||
| 845 | }, | ||
| 846 | }; | ||
| 847 | fw.flush() catch |err| switch (err) { | ||
| 848 | error.Canceled => |e| return e, | ||
| 849 | else => |e| return update.manifest_sr.fail( | ||
| 850 | arg.?, | ||
| 851 | "unable to write file: {t}", | ||
| 852 | .{e}, | ||
| 853 | ), | ||
| 854 | }; | ||
| 855 | file.setTimestamps(runner.io, .{ .modify_timestamp = .{ | ||
| 856 | .new = update.mtime, | ||
| 857 | } }) catch |err| switch (err) { | ||
| 858 | error.Canceled => |e| return e, | ||
| 859 | else => |e| return update.manifest_sr.fail( | ||
| 860 | arg.?, | ||
| 861 | "unable to update file timestamps: {t}", | ||
| 862 | .{e}, | ||
| 863 | ), | ||
| 864 | }; | ||
| 865 | }, | ||
| 866 | .delete => update.src_dir.deleteFile(runner.io, arg.?) catch |err| switch (err) { | ||
| 867 | error.Canceled => |e| return e, | ||
| 868 | else => |e| return update.manifest_sr.fail(arg.?, "unable to delete file: {t}", .{e}), | ||
| 869 | }, | ||
| 870 | .update => { | ||
| 871 | const comp = &(update.compiler orelse return update.manifest_sr.fail( | ||
| 872 | cmd_src_loc, | ||
| 873 | "missing \"#exe\", \"#lib\", or \"#obj\"", | ||
| 874 | .{}, | ||
| 875 | )); | ||
| 876 | update.prog_node.end(); | ||
| 877 | update.prog_node = runner.prog_node.start( | ||
| 878 | switch (contents_r.readSliceShort(&name_buffer) catch |err| switch (err) { | ||
| 879 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 880 | }) { | ||
| 881 | 0 => switch (update.num) { | ||
| 882 | 0 => "initial update", | ||
| 883 | else => |num| std.mem.print(&name_buffer, "update {d}", .{num}) catch | ||
| 884 | &name_buffer, | ||
| 885 | }, | ||
| 886 | else => |name_len| name_buffer[0..name_len], | ||
| 887 | }, | ||
| 888 | 0, | ||
| 889 | ); | ||
| 890 | _ = contents_r.discardRemaining() catch |err| switch (err) { | ||
| 891 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 892 | }; | ||
| 893 | if (comp.state != .idle) | ||
| 894 | return update.manifest_sr.fail(cmd_src_loc, "missing \"#check\"", .{}); | ||
| 895 | comp.client.serveBodylessMessage(.update) catch |err| switch (err) { | ||
| 896 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 897 | error.Canceled => |e| return e, | ||
| 898 | else => |e| return runner.fail("unable to send message to compiler: {t}", .{e}), | ||
| 899 | }, | ||
| 900 | }; | ||
| 901 | comp.state = .update; | ||
| 902 | update.mtime = update.mtime.addDuration(.fromSeconds(2)); | ||
| 903 | update.num += 1; | ||
| 904 | }, | ||
| 905 | .check => { | ||
| 906 | const check = std.meta.stringToEnum(Compiler.Check, arg.?) orelse | ||
| 907 | return update.manifest_sr.fail(arg.?, "unknown check", .{}); | ||
| 908 | const expected = | ||
| 909 | contents_r.allocRemaining(runner.gpa, .unlimited) catch |err| switch (err) { | ||
| 910 | error.OutOfMemory => |e| return e, | ||
| 911 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 912 | error.StreamTooLong => unreachable, // .unlimited | ||
| 913 | }; | ||
| 914 | defer runner.gpa.free(expected); | ||
| 915 | |||
| 916 | const comp = &(update.compiler orelse return update.manifest_sr.fail( | ||
| 917 | cmd_src_loc, | ||
| 918 | "missing \"#exe\", \"#lib\", or \"#obj\"", | ||
| 919 | .{}, | ||
| 920 | )); | ||
| 921 | if (comp.state != .update) | ||
| 922 | return update.manifest_sr.fail(cmd_src_loc, "missing \"#update\"", .{}); | ||
| 923 | const stderr = comp.mr.reader(1); | ||
| 924 | while (try comp.receiveMessage()) |message| switch (message.tag) { | ||
| 925 | else => {}, // Ignore other messages, | ||
| 926 | .config => { | ||
| 927 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 928 | comp.config = body_r.takeStruct(std.zig.Server.Message.Config, .little) catch | ||
| 929 | unreachable; | ||
| 930 | }, | ||
| 931 | .emit_digest => { | ||
| 932 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 933 | _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch | ||
| 934 | unreachable; | ||
| 935 | const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch | ||
| 936 | unreachable; | ||
| 937 | try comp.checkSuccess(check, expected, update.src_dir, digest); | ||
| 938 | }, | ||
| 939 | .error_bundle => { | ||
| 940 | const eb = std.zig.Server.allocErrorBundle( | ||
| 941 | runner.arena.allocator(), | ||
| 942 | message.body, | ||
| 943 | ) catch |err| switch (err) { | ||
| 944 | error.OutOfMemory => |e| return e, | ||
| 945 | error.EndOfStream => return runner.fail("error bundle truncated", .{}), | ||
| 946 | }; | ||
| 947 | switch (check) { | ||
| 948 | .errors => { | ||
| 949 | var error_aw: std.Io.Writer.Allocating = .init(runner.gpa); | ||
| 950 | defer error_aw.deinit(); | ||
| 951 | eb.renderToWriter(.{ | ||
| 952 | .include_reference_trace = false, | ||
| 953 | .include_source_line = false, | ||
| 954 | .include_log_text = true, | ||
| 955 | }, &error_aw.writer) catch |err| switch (err) { | ||
| 956 | error.WriteFailed => return error.OutOfMemory, | ||
| 957 | }; | ||
| 958 | const actual = error_aw.written(); | ||
| 959 | std.mem.replaceScalar(u8, actual, '\\', '/'); | ||
| 960 | std.testing.expectEqualStrings(expected, actual) catch |err| switch (err) { | ||
| 961 | error.TestExpectedEqual => return runner.fail( | ||
| 962 | "errors did not match expected", | ||
| 963 | .{}, | ||
| 964 | ), | ||
| 965 | }; | ||
| 966 | }, | ||
| 967 | .stdout, .exit, .gdb, .lldb => if (eb.errorMessageCount() > 0) { | ||
| 968 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { | ||
| 969 | error.Canceled => {}, | ||
| 970 | else => {}, | ||
| 971 | }; | ||
| 972 | return runner.fail("unexpected compile errors", .{}); | ||
| 973 | }, | ||
| 974 | } | ||
| 975 | comp.state = .idle; | ||
| 976 | break :cmd; | ||
| 977 | }, | ||
| 978 | }; | ||
| 979 | |||
| 980 | const buffered_stderr = stderr.buffered(); | ||
| 981 | if (buffered_stderr.len > 0) { | ||
| 982 | if (comp.allow_compiler_stderr) { | ||
| 983 | std.log.info("stderr:\n{s}", .{buffered_stderr}); | ||
| 984 | } else { | ||
| 985 | return runner.fail("unexpected stderr:\n{s}", .{buffered_stderr}); | ||
| 986 | } | ||
| 987 | } | ||
| 988 | |||
| 989 | try comp.exit(); | ||
| 990 | return runner.fail("compiler failed to send terminating error_bundle", .{}); | ||
| 991 | }, | ||
| 992 | } | ||
| 993 | assert(contents_r.discardRemaining() catch |err| switch (err) { | ||
| 994 | error.ReadFailed => return error.ContentsReadFailed, | ||
| 995 | } == 0); | ||
| 996 | switch (cmd) { | ||
| 997 | .todo, .skip => {}, | ||
| 998 | else => update.allow_skip = false, | ||
| 999 | } | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | fn spawnCompiler( | ||
| 1003 | runner: *Runner, | ||
| 1004 | compiler: *?Compiler, | ||
| 1005 | src_dir: std.Io.Dir, | ||
| 1006 | output_mode: std.lang.OutputMode, | ||
| 1007 | args_r: *std.Io.Reader, | ||
| 1008 | target: Compiler.Target, | ||
| 1009 | ) (std.Io.Reader.ShortError || TestError)!void { | ||
| 1010 | const gpa = runner.gpa; | ||
| 1011 | const arena = runner.arena.allocator(); | ||
| 1012 | if (compiler.*) |*comp| { | ||
| 1013 | try comp.exit(); | ||
| 1014 | compiler.* = null; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | var argv: std.ArrayList([]const u8) = .empty; | ||
| 1018 | defer argv.deinit(gpa); | ||
| 1019 | try argv.appendSlice(gpa, &.{ | ||
| 1020 | "zig", | ||
| 1021 | switch (output_mode) { | ||
| 1022 | .Obj => "build-obj", | ||
| 1023 | .Exe => "build-exe", | ||
| 1024 | .Lib => "build-lib", | ||
| 1025 | }, | ||
| 1026 | "-target", | ||
| 1027 | target.triple, | ||
| 1028 | "--cache-dir", | ||
| 1029 | ".zig-cache", | ||
| 1030 | }); | ||
| 1031 | switch (target.cache_mode) { | ||
| 1032 | .whole => {}, | ||
| 1033 | .incremental => try argv.append(gpa, "-fincremental"), | ||
| 1034 | } | ||
| 1035 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 1036 | if (target.backend) |b| switch (b) { | ||
| 1037 | .sema => try argv.append(gpa, "-fno-emit-bin"), | ||
| 1038 | .selfhosted => try argv.append(gpa, "-fno-llvm"), | ||
| 1039 | .llvm => try argv.append(gpa, "-fllvm"), | ||
| 1040 | .cbe => try argv.append(gpa, "-ofmt=c"), | ||
| 1041 | } else try argv.appendSlice(gpa, &.{ "-I", path_buffer[0 .. (runner.args.lib_dir orelse | ||
| 1042 | return runner.fail("\"--lib=path/to/lib\" arg required with cbe", .{})) | ||
| 1043 | .realPath(runner.io, &path_buffer) catch |err| switch (err) { | ||
| 1044 | error.Canceled => |e| return e, | ||
| 1045 | else => |e| return runner.fail("unable to get lib path: {t}", .{e}), | ||
| 1046 | }] }); | ||
| 1047 | switch (target.linker) { | ||
| 1048 | .lld => try argv.appendSlice(gpa, &.{ "-flld", "-fno-new-linker" }), | ||
| 1049 | .old => try argv.appendSlice(gpa, &.{ "-fno-lld", "-fno-new-linker" }), | ||
| 1050 | .new => try argv.appendSlice(gpa, &.{ "-fno-lld", "-fnew-linker" }), | ||
| 1051 | } | ||
| 1052 | switch (target.pic) { | ||
| 1053 | .nopic => try argv.appendSlice(gpa, &.{ "-fno-PIC", "-fno-PIE" }), | ||
| 1054 | .pic => try argv.appendSlice(gpa, &.{ "-fPIC", "-fno-PIE" }), | ||
| 1055 | .pie => try argv.appendSlice(gpa, &.{ "-fPIC", "-fPIE" }), | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | var need: usize = 1; | ||
| 1059 | var state: enum { positional, @"--name" } = .positional; | ||
| 1060 | var root_name: ?[]const u8 = null; | ||
| 1061 | while (true) { | ||
| 1062 | const done = if (args_r.fill(need)) false else |err| switch (err) { | ||
| 1063 | error.ReadFailed => |e| return e, | ||
| 1064 | error.EndOfStream => true, | ||
| 1065 | }; | ||
| 1066 | const buffered = args_r.buffered(); | ||
| 1067 | const start = std.mem.findNone(u8, buffered, "\n ") orelse buffered.len; | ||
| 1068 | const end = std.mem.findAnyPos(u8, buffered, start, "\n ") orelse buffered.len; | ||
| 1069 | if (end - start > 0) { | ||
| 1070 | const arg = try arena.dupe(u8, buffered[start..end]); | ||
| 1071 | switch (state) { | ||
| 1072 | .positional => if (std.mem.eql(u8, arg, "--name")) { | ||
| 1073 | state = .@"--name"; | ||
| 1074 | } else if (std.mem.cutPrefix(u8, arg, "-M")) |module| { | ||
| 1075 | var module_name_it = std.mem.splitScalar(u8, module, '='); | ||
| 1076 | const module_name = module_name_it.next().?; | ||
| 1077 | root_name = root_name orelse module_name; | ||
| 1078 | }, | ||
| 1079 | .@"--name" => { | ||
| 1080 | root_name = arg; | ||
| 1081 | state = .positional; | ||
| 1082 | }, | ||
| 1083 | } | ||
| 1084 | try argv.append(gpa, arg); | ||
| 1085 | args_r.toss(end); | ||
| 1086 | need = 1; | ||
| 1087 | } else { | ||
| 1088 | args_r.toss(start); | ||
| 1089 | need = buffered.len - start + 1; | ||
| 1090 | } | ||
| 1091 | if (done) break; | ||
| 1092 | } | ||
| 1093 | try argv.append(gpa, "--listen=-"); | ||
| 1094 | |||
| 1095 | const out_name = try std.zig.EmitArtifact.bin.cacheName(arena, .{ | ||
| 1096 | .root_name = root_name orelse "root", | ||
| 1097 | .cpu_arch = target.resolved.cpu.arch, | ||
| 1098 | .os_tag = target.resolved.os.tag, | ||
| 1099 | .ofmt = target.resolved.ofmt, | ||
| 1100 | .abi = target.resolved.abi, | ||
| 1101 | .output_mode = output_mode, | ||
| 1102 | }); | ||
| 1103 | |||
| 1104 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 1105 | const comp_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "compile {s}", .{ | ||
| 1106 | out_name, | ||
| 1107 | }) catch &name_buffer, 0); | ||
| 1108 | errdefer comp_prog_node.end(); | ||
| 1109 | |||
| 1110 | const child = std.process.spawn(runner.io, .{ | ||
| 1111 | .exe = .{ .file = runner.args.zig_exe orelse | ||
| 1112 | return runner.fail("missing \"--zig=path/to/zig\" arg", .{}) }, | ||
| 1113 | .argv = argv.items, | ||
| 1114 | .cwd = .{ .dir = src_dir }, | ||
| 1115 | .progress_node = comp_prog_node, | ||
| 1116 | .stdin = .pipe, | ||
| 1117 | .stdout = .pipe, | ||
| 1118 | .stderr = .pipe, | ||
| 1119 | }) catch |err| switch (err) { | ||
| 1120 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1121 | error.OperationUnsupported => return error.SkipTest, | ||
| 1122 | else => |e| return runner.fail("unable to spawn compiler: {t}", .{e}), | ||
| 1123 | }; | ||
| 1124 | compiler.* = .{ | ||
| 1125 | .runner = runner, | ||
| 1126 | .prog_node = comp_prog_node, | ||
| 1127 | .allow_compiler_stderr = true, | ||
| 1128 | .output_mode = output_mode, | ||
| 1129 | .state = .idle, | ||
| 1130 | .child = child, | ||
| 1131 | .fw = undefined, | ||
| 1132 | .mr_buffer = undefined, | ||
| 1133 | .mr = undefined, | ||
| 1134 | .client = undefined, | ||
| 1135 | .target = target, | ||
| 1136 | .out_name = out_name, | ||
| 1137 | .config = null, | ||
| 1138 | }; | ||
| 1139 | const comp = &compiler.*.?; | ||
| 1140 | comp.fw = child.stdin.?.writerStreaming(runner.io, &.{}); | ||
| 1141 | comp.mr.init(gpa, runner.io, comp.mr_buffer.toStreams(), &.{ | ||
| 1142 | child.stdout.?, child.stderr.?, | ||
| 1143 | }); | ||
| 1144 | comp.client = .{ .in = comp.mr.reader(0), .out = &comp.fw.interface }; | ||
| 1145 | } | ||
| 1146 | const Compiler = struct { | ||
| 1147 | runner: *Runner, | ||
| 1148 | prog_node: std.Progress.Node, | ||
| 1149 | allow_compiler_stderr: bool, | ||
| 1150 | output_mode: std.lang.OutputMode, | ||
| 1151 | state: enum { idle, update }, | ||
| 1152 | child: std.process.Child, | ||
| 1153 | fw: std.Io.File.Writer, | ||
| 1154 | mr_buffer: std.Io.File.MultiReader.Buffer(2), | ||
| 1155 | mr: std.Io.File.MultiReader, | ||
| 1156 | client: std.zig.Client, | ||
| 1157 | target: Target, | ||
| 1158 | out_name: []const u8, | ||
| 1159 | config: ?std.zig.Server.Message.Config, | ||
| 1160 | |||
| 1161 | const Target = struct { | ||
| 1162 | triple: []const u8, | ||
| 1163 | resolved: std.Target, | ||
| 1164 | cache_mode: CacheMode, | ||
| 1165 | backend: ?Backend, | ||
| 1166 | linker: Linker, | ||
| 1167 | pic: Pic, | ||
| 1168 | |||
| 1169 | const CacheMode = enum { whole, incremental }; | ||
| 1170 | |||
| 1171 | const Backend = enum { | ||
| 1172 | /// Run semantic analysis only. Runtime output will not be tested, but we still verify | ||
| 1173 | /// that compilation succeeds. Corresponds to `-fno-emit-bin`. | ||
| 1174 | sema, | ||
| 1175 | /// Use the self-hosted code generation backend for this target. | ||
| 1176 | /// Corresponds to `-fno-llvm -fno-lld`. | ||
| 1177 | selfhosted, | ||
| 1178 | /// Use the LLVM backend. | ||
| 1179 | /// Corresponds to `-fllvm -flld`. | ||
| 1180 | llvm, | ||
| 1181 | /// Use the C backend. The output is compiled with `zig cc`. | ||
| 1182 | /// Corresponds to `-ofmt=c`. | ||
| 1183 | cbe, | ||
| 1184 | }; | ||
| 1185 | |||
| 1186 | const Linker = enum { old, new, lld }; | ||
| 1187 | |||
| 1188 | const Pic = enum { nopic, pic, pie }; | ||
| 1189 | }; | ||
| 1190 | |||
| 1191 | fn receiveMessage(comp: *Compiler) FailError!?struct { | ||
| 1192 | tag: std.zig.Server.Message.Tag, | ||
| 1193 | body: []const u8, | ||
| 1194 | } { | ||
| 1195 | const header = comp.client.receiveMessageWithMultiReader( | ||
| 1196 | &comp.mr, | ||
| 1197 | .none, | ||
| 1198 | ) catch |err| switch (err) { | ||
| 1199 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1200 | error.Timeout => unreachable, | ||
| 1201 | error.EndOfStream => return null, | ||
| 1202 | else => |e| return comp.runner.fail("unable to receive message: {t}", .{e}), | ||
| 1203 | }; | ||
| 1204 | const body = comp.client.in.take(header.bytes_len) catch unreachable; | ||
| 1205 | const stderr = comp.mr.reader(1); | ||
| 1206 | if (stderr.bufferedLen() > 0) { | ||
| 1207 | if (comp.allow_compiler_stderr) { | ||
| 1208 | std.log.info("{t} stderr:\n{s}", .{ header.tag, stderr.buffered() }); | ||
| 1209 | } else { | ||
| 1210 | return comp.runner.fail("{t} unexpected stderr:\n{s}", .{ | ||
| 1211 | header.tag, stderr.buffered(), | ||
| 1212 | }); | ||
| 1213 | } | ||
| 1214 | stderr.tossBuffered(); | ||
| 1215 | } | ||
| 1216 | return .{ .tag = header.tag, .body = body }; | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | const Check = enum { errors, stdout, exit, gdb, lldb }; | ||
| 1220 | fn checkSuccess( | ||
| 1221 | comp: *Compiler, | ||
| 1222 | check: Check, | ||
| 1223 | expected: []const u8, | ||
| 1224 | src_dir: std.Io.Dir, | ||
| 1225 | digest: *const std.Build.Cache.BinDigest, | ||
| 1226 | ) TestError!void { | ||
| 1227 | const runner = comp.runner; | ||
| 1228 | const gpa = runner.gpa; | ||
| 1229 | const arena = runner.arena.allocator(); | ||
| 1230 | const io = runner.io; | ||
| 1231 | const target = &comp.target.resolved; | ||
| 1232 | const config = &comp.config.?; | ||
| 1233 | var path_buffer: [std.Io.Dir.max_path_bytes]u8 = undefined; | ||
| 1234 | |||
| 1235 | // After a failure while running a script, the debugger starts accepting commands from stdin, and | ||
| 1236 | // because it is empty, the debugger exits normally with status 0. Choose a non-zero status to | ||
| 1237 | // return from the debugger script instead to detect it running to completion and indicate success. | ||
| 1238 | const debug_success = 99; | ||
| 1239 | |||
| 1240 | const tmp_dir_path = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ "tmp"; | ||
| 1241 | src_dir.createDirPath(io, tmp_dir_path) catch |err| switch (err) { | ||
| 1242 | error.Canceled => |e| return e, | ||
| 1243 | else => |e| return runner.fail("unable to create dir path {q}: {t}", .{ | ||
| 1244 | tmp_dir_path, e, | ||
| 1245 | }), | ||
| 1246 | }; | ||
| 1247 | |||
| 1248 | var argv: std.ArrayList([]const u8) = .initBuffer(try arena.alloc([]const u8, 5 + 4)); | ||
| 1249 | var environ_map: std.process.Environ.Map = .init(gpa); | ||
| 1250 | defer environ_map.deinit(); | ||
| 1251 | try environ_map.putAll(&runner.environ_map); | ||
| 1252 | var script_buffer: [512]u8 = undefined; | ||
| 1253 | const script_path, const script_file, var script_fw = script: switch (check) { | ||
| 1254 | .errors => return, | ||
| 1255 | .stdout, .exit => .{ undefined, null, undefined }, | ||
| 1256 | .gdb => { | ||
| 1257 | const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "gdb"; | ||
| 1258 | argv.appendSliceAssumeCapacity(&.{ "gdb", "--batch", "--command", script_path, "--args" }); | ||
| 1259 | const script_file = src_dir.createFile(io, script_path, .{}) catch |err| switch (err) { | ||
| 1260 | error.Canceled => |e| return e, | ||
| 1261 | else => |e| return runner.fail("unable to create debugger script {q}: {t}", .{ | ||
| 1262 | script_path, e, | ||
| 1263 | }), | ||
| 1264 | }; | ||
| 1265 | errdefer script_file.close(io); | ||
| 1266 | var script_fw = script_file.writer(io, &script_buffer); | ||
| 1267 | script_fw.interface.writeAll( | ||
| 1268 | \\set remotetimeout 0 | ||
| 1269 | \\ | ||
| 1270 | \\ | ||
| 1271 | ) catch |err| switch (err) { | ||
| 1272 | error.WriteFailed => switch (script_fw.err.?) { | ||
| 1273 | error.Canceled => |e| return e, | ||
| 1274 | else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ script_path, e }), | ||
| 1275 | }, | ||
| 1276 | }; | ||
| 1277 | break :script .{ script_path, script_file, script_fw }; | ||
| 1278 | }, | ||
| 1279 | .lldb => { | ||
| 1280 | const script_path = tmp_dir_path ++ std.Io.Dir.path.sep_str ++ "lldb"; | ||
| 1281 | argv.appendSliceAssumeCapacity(&.{ "lldb", "--batch", "--source", script_path, "--" }); | ||
| 1282 | try environ_map.put("LANG", "C.UTF-8"); // affects output formatting | ||
| 1283 | const script_file = src_dir.createFile(io, script_path, .{}) catch |err| switch (err) { | ||
| 1284 | error.Canceled => |e| return e, | ||
| 1285 | else => |e| return runner.fail("unable to create debugger script {q}: {t}", .{ | ||
| 1286 | script_path, | ||
| 1287 | e, | ||
| 1288 | }), | ||
| 1289 | }; | ||
| 1290 | errdefer script_file.close(io); | ||
| 1291 | var script_fw = script_file.writer(io, &script_buffer); | ||
| 1292 | script_fw.interface.writeAll( | ||
| 1293 | \\settings set frame-format 'frame #${frame.index}:{ ${module.file.basename}{\`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${line.file.basename}:${line.number}{:${line.column}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\n' | ||
| 1294 | \\settings set plugin.process.gdb-remote.packet-timeout 0 | ||
| 1295 | \\settings set stop-line-count-after 0 | ||
| 1296 | \\settings set stop-line-count-before 0 | ||
| 1297 | \\ | ||
| 1298 | ) catch |err| switch (err) { | ||
| 1299 | error.WriteFailed => switch (script_fw.err.?) { | ||
| 1300 | error.Canceled => |e| return e, | ||
| 1301 | else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ script_path, e }), | ||
| 1302 | }, | ||
| 1303 | }; | ||
| 1304 | break :script .{ script_path, script_file, script_fw }; | ||
| 1305 | }, | ||
| 1306 | }; | ||
| 1307 | defer if (script_file) |file| file.close(io); | ||
| 1308 | |||
| 1309 | const out_dir_path = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ | ||
| 1310 | "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); | ||
| 1311 | const out_path = try std.Io.Dir.path.join(arena, &.{ out_dir_path, comp.out_name }); | ||
| 1312 | const bin_path = switch (comp.target.backend.?) { | ||
| 1313 | .sema => return, | ||
| 1314 | .selfhosted, .llvm => out_path, | ||
| 1315 | .cbe => try comp.compileC(src_dir, out_path), | ||
| 1316 | }; | ||
| 1317 | |||
| 1318 | const need_cross_libc = | ||
| 1319 | target.os.tag == .linux and config.flags.link_libc and config.flags.link_mode == .dynamic; | ||
| 1320 | const use_executor = use_executor: switch (std.zig.system.getExternalExecutor(io, target, .{ | ||
| 1321 | .host_cpu_arch = runner.host.cpu.arch, | ||
| 1322 | .host_os_tag = runner.host.os.tag, | ||
| 1323 | .qemu_fixes_dl = need_cross_libc and runner.args.libc_runtimes_dir != null, | ||
| 1324 | .link_mode = config.flags.link_mode, | ||
| 1325 | .link_libc = config.flags.link_libc, | ||
| 1326 | })) { | ||
| 1327 | .bad_dl, .bad_os_or_cpu => { | ||
| 1328 | // This binary cannot be executed on this host. | ||
| 1329 | if (!runner.args.quiet) std.log.warn("skipping execution because host {q} cannot " ++ | ||
| 1330 | "execute binaries for foreign target {q}", .{ | ||
| 1331 | try runner.host.zigTriple(arena), comp.target.triple, | ||
| 1332 | }); | ||
| 1333 | return; | ||
| 1334 | }, | ||
| 1335 | .native => false, | ||
| 1336 | .darling => |executor| if (runner.args.enable_darling) { | ||
| 1337 | argv.appendAssumeCapacity(executor); | ||
| 1338 | break :use_executor true; | ||
| 1339 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1340 | .qemu => |executor| if (runner.args.enable_qemu) { | ||
| 1341 | argv.appendAssumeCapacity(executor); | ||
| 1342 | if (need_cross_libc) { | ||
| 1343 | const libc_runtimes_dir = runner.args.libc_runtimes_dir orelse | ||
| 1344 | continue :use_executor .bad_os_or_cpu; | ||
| 1345 | const libc_runtimes_path = path_buffer[0 .. libc_runtimes_dir.realPath( | ||
| 1346 | io, | ||
| 1347 | &path_buffer, | ||
| 1348 | ) catch |err| switch (err) { | ||
| 1349 | error.Canceled => |e| return e, | ||
| 1350 | else => |e| return runner.fail("unable to get libc runtimes path: {t}", .{e}), | ||
| 1351 | }]; | ||
| 1352 | argv.appendSliceAssumeCapacity(&.{ "-L", try std.Io.Dir.path.join(arena, &.{ | ||
| 1353 | libc_runtimes_path, | ||
| 1354 | try if (target.isGnuLibC()) std.zig.target.glibcRuntimeTriple( | ||
| 1355 | arena, | ||
| 1356 | target.cpu.arch, | ||
| 1357 | target.os.tag, | ||
| 1358 | target.abi, | ||
| 1359 | ) else if (target.isMuslLibC()) std.zig.target.muslRuntimeTriple( | ||
| 1360 | arena, | ||
| 1361 | target.cpu.arch, | ||
| 1362 | target.abi, | ||
| 1363 | ) else unreachable, | ||
| 1364 | }) }); | ||
| 1365 | } | ||
| 1366 | break :use_executor true; | ||
| 1367 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1368 | .rosetta => if (runner.args.enable_rosetta) | ||
| 1369 | true | ||
| 1370 | else | ||
| 1371 | continue :use_executor .bad_os_or_cpu, | ||
| 1372 | .wine => |executor| if (runner.args.enable_wine) { | ||
| 1373 | if (!environ_map.contains("WINEDEBUG")) try environ_map.put("WINEDEBUG", "-all"); | ||
| 1374 | argv.appendAssumeCapacity(executor); | ||
| 1375 | break :use_executor true; | ||
| 1376 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1377 | .wasmtime => |executor| if (runner.args.enable_wasmtime) { | ||
| 1378 | if (!environ_map.contains("WASMTIME_BACKTRACE_DETAILS")) | ||
| 1379 | try environ_map.put("WASMTIME_BACKTRACE_DETAILS", "1"); | ||
| 1380 | argv.appendSliceAssumeCapacity(&.{ executor, "--dir=.", "-Sinherit-env" }); | ||
| 1381 | break :use_executor true; | ||
| 1382 | } else continue :use_executor .bad_os_or_cpu, | ||
| 1383 | }; | ||
| 1384 | argv.appendAssumeCapacity(bin_path); | ||
| 1385 | |||
| 1386 | if (!runner.args.quiet) std.log.info("{f}", .{std.zig.SubprocessCommand{ | ||
| 1387 | .argv = argv.items, | ||
| 1388 | .cwd = cwd_path: { | ||
| 1389 | break :cwd_path path_buffer[0 .. src_dir.realPath(io, &path_buffer) catch |err| switch (err) { | ||
| 1390 | error.Canceled => |e| return e, | ||
| 1391 | else => break :cwd_path null, | ||
| 1392 | }]; | ||
| 1393 | }, | ||
| 1394 | .parent_env = &runner.environ_map, | ||
| 1395 | .child_env = &environ_map, | ||
| 1396 | }}); | ||
| 1397 | |||
| 1398 | var name_buffer: [std.Progress.Node.max_name_len]u8 = undefined; | ||
| 1399 | const run_prog_node = runner.prog_node.start(std.mem.print(&name_buffer, "run {s}", .{ | ||
| 1400 | comp.out_name, | ||
| 1401 | }) catch &name_buffer, 0); | ||
| 1402 | defer run_prog_node.end(); | ||
| 1403 | |||
| 1404 | const bin_file = src_dir.openFile(io, bin_path, .{}) catch |err| switch (err) { | ||
| 1405 | error.Canceled => |e| return e, | ||
| 1406 | else => |e| return runner.fail("unable to open {q}: {t}", .{ bin_path, e }), | ||
| 1407 | }; | ||
| 1408 | defer bin_file.close(io); | ||
| 1409 | const result = std.process.run(gpa, io, .{ | ||
| 1410 | .exe = exe: switch (check) { | ||
| 1411 | .errors => unreachable, | ||
| 1412 | .stdout, .exit => if (use_executor) .search else .{ .file = bin_file }, | ||
| 1413 | .gdb, .lldb => { | ||
| 1414 | if (use_executor) { | ||
| 1415 | if (!runner.args.quiet) std.log.warn( | ||
| 1416 | "skipping debugging of {q} via executor for foreign target {q}", | ||
| 1417 | .{ bin_path, comp.target.triple }, | ||
| 1418 | ); | ||
| 1419 | return; | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | var line_it = std.mem.splitScalar(u8, expected, '\n'); | ||
| 1423 | while (line_it.next()) |line| if (std.mem.cutPrefix(u8, line, switch (check) { | ||
| 1424 | else => unreachable, | ||
| 1425 | .gdb => "(gdb) ", | ||
| 1426 | .lldb => "(lldb) ", | ||
| 1427 | })) |cmd| script_fw.interface.print( | ||
| 1428 | \\{s} | ||
| 1429 | \\ | ||
| 1430 | , .{cmd}) catch |err| switch (err) { | ||
| 1431 | error.WriteFailed => switch (script_fw.err.?) { | ||
| 1432 | error.Canceled => |e| return e, | ||
| 1433 | else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ | ||
| 1434 | script_path, e, | ||
| 1435 | }), | ||
| 1436 | }, | ||
| 1437 | }; | ||
| 1438 | script_fw.interface.print( | ||
| 1439 | \\ | ||
| 1440 | \\quit {d} | ||
| 1441 | \\ | ||
| 1442 | , .{debug_success}) catch |err| switch (err) { | ||
| 1443 | error.WriteFailed => switch (script_fw.err.?) { | ||
| 1444 | error.Canceled => |e| return e, | ||
| 1445 | else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ | ||
| 1446 | script_path, e, | ||
| 1447 | }), | ||
| 1448 | }, | ||
| 1449 | }; | ||
| 1450 | script_fw.flush() catch |err| switch (err) { | ||
| 1451 | error.Canceled => |e| return e, | ||
| 1452 | else => |e| return runner.fail("unable to write debugger script {q}: {t}", .{ | ||
| 1453 | script_path, e, | ||
| 1454 | }), | ||
| 1455 | }; | ||
| 1456 | break :exe .{ .file = switch (check) { | ||
| 1457 | else => unreachable, | ||
| 1458 | .gdb => runner.args.gdb_exe, | ||
| 1459 | .lldb => runner.args.lldb_exe, | ||
| 1460 | } orelse { | ||
| 1461 | if (!runner.args.quiet) std.log.warn( | ||
| 1462 | "skipping debugging of {q} because of missing \"--{t}=path/to/{[1]t}\" arg", | ||
| 1463 | .{ bin_path, check }, | ||
| 1464 | ); | ||
| 1465 | return; | ||
| 1466 | } }; | ||
| 1467 | }, | ||
| 1468 | }, | ||
| 1469 | .argv = argv.items, | ||
| 1470 | .cwd = .{ .dir = src_dir }, | ||
| 1471 | .environ_map = &environ_map, | ||
| 1472 | .progress_node = run_prog_node, | ||
| 1473 | }) catch |err| if (use_executor) { | ||
| 1474 | // Chances are the foreign executor isn't available. Skip this evaluation. | ||
| 1475 | if (!runner.args.quiet) std.log.warn( | ||
| 1476 | "skipping execution of {q} via executor for foreign target {q}: {t}", | ||
| 1477 | .{ bin_path, comp.target.triple, err }, | ||
| 1478 | ); | ||
| 1479 | return; | ||
| 1480 | } else return runner.fail("unable to run the generated executable {q}: {t}", .{ | ||
| 1481 | bin_path, err, | ||
| 1482 | }); | ||
| 1483 | defer { | ||
| 1484 | gpa.free(result.stdout); | ||
| 1485 | gpa.free(result.stderr); | ||
| 1486 | } | ||
| 1487 | switch (result.term) { | ||
| 1488 | .exited => |code| switch (check) { | ||
| 1489 | .errors => unreachable, | ||
| 1490 | .stdout => { | ||
| 1491 | if (code != 0) return runner.fail( | ||
| 1492 | "generated executable {q} failed with code {d}", | ||
| 1493 | .{ bin_path, code }, | ||
| 1494 | ); | ||
| 1495 | std.testing.expectEqualStrings( | ||
| 1496 | expected, | ||
| 1497 | result.stdout, | ||
| 1498 | ) catch |err| switch (err) { | ||
| 1499 | error.TestExpectedEqual => return runner.fail("stdout did not match expected", .{}), | ||
| 1500 | }; | ||
| 1501 | }, | ||
| 1502 | .exit => { | ||
| 1503 | var actual_code_buffer: [std.fmt.count("{d}", .{std.math.maxInt(u8)})]u8 = undefined; | ||
| 1504 | std.testing.expectEqualStrings( | ||
| 1505 | expected, | ||
| 1506 | std.mem.print(&actual_code_buffer, "{d}", .{code}) catch unreachable, | ||
| 1507 | ) catch |err| switch (err) { | ||
| 1508 | error.TestExpectedEqual => return runner.fail( | ||
| 1509 | "exit code did not match expected", | ||
| 1510 | .{}, | ||
| 1511 | ), | ||
| 1512 | }; | ||
| 1513 | }, | ||
| 1514 | .gdb, .lldb => { | ||
| 1515 | if (code != debug_success) return runner.fail( | ||
| 1516 | "debugging {q} failed with code {d}", | ||
| 1517 | .{ bin_path, code }, | ||
| 1518 | ); | ||
| 1519 | var expected_match_it = std.mem.splitSequence(u8, expected, "#..."); | ||
| 1520 | var pos: usize = 0; | ||
| 1521 | while (expected_match_it.next()) |expected_match| : (pos += expected_match.len) { | ||
| 1522 | var context_len: usize = expected_match.len; | ||
| 1523 | pos = while (true) : (context_len -= 1) break std.mem.findPos( | ||
| 1524 | u8, | ||
| 1525 | result.stdout, | ||
| 1526 | pos, | ||
| 1527 | expected_match[0..context_len], | ||
| 1528 | ) orelse continue; | ||
| 1529 | const actual_match = result.stdout[pos..]; | ||
| 1530 | std.testing.expectEqualStrings( | ||
| 1531 | expected_match, | ||
| 1532 | actual_match[0..@min(actual_match.len, expected_match.len)], | ||
| 1533 | ) catch |err| switch (err) { | ||
| 1534 | error.TestExpectedEqual => { | ||
| 1535 | std.testing.failPrint("\n=============== full output: =========\n", .{}); | ||
| 1536 | std.testing.failPrintWithVisibleNewlines(actual_match); | ||
| 1537 | std.testing.failPrint("\n======================================\n", .{}); | ||
| 1538 | return runner.fail("stdout did not match expected", .{}); | ||
| 1539 | }, | ||
| 1540 | }; | ||
| 1541 | } | ||
| 1542 | }, | ||
| 1543 | }, | ||
| 1544 | .signal => |sig| return runner.fail( | ||
| 1545 | "generated executable {q} terminated with signal {t}", | ||
| 1546 | .{ bin_path, sig }, | ||
| 1547 | ), | ||
| 1548 | .stopped => |sig| return runner.fail( | ||
| 1549 | "generated executable {q} stopped with signal {t}", | ||
| 1550 | .{ bin_path, sig }, | ||
| 1551 | ), | ||
| 1552 | .unknown => return runner.fail( | ||
| 1553 | "generated executable {q} terminated unexpectedly", | ||
| 1554 | .{bin_path}, | ||
| 1555 | ), | ||
| 1556 | } | ||
| 1557 | if (!use_executor and result.stderr.len > 0) { | ||
| 1558 | std.log.err("generated executable {q} had unexpected stderr:\n{s}", .{ | ||
| 1559 | bin_path, result.stderr, | ||
| 1560 | }); | ||
| 1561 | } | ||
| 1562 | } | ||
| 1563 | |||
| 1564 | fn compileC(parent_comp: *Compiler, src_dir: std.Io.Dir, c_path: []const u8) TestError![]const u8 { | ||
| 1565 | const runner = parent_comp.runner; | ||
| 1566 | const arena = runner.arena.allocator(); | ||
| 1567 | var compiler: ?Compiler = null; | ||
| 1568 | var args_r: std.Io.Reader = .fixed(c_path); | ||
| 1569 | parent_comp.runner.spawnCompiler(&compiler, src_dir, parent_comp.output_mode, &args_r, .{ | ||
| 1570 | .triple = parent_comp.target.triple, | ||
| 1571 | .resolved = resolved: { | ||
| 1572 | var resolved = parent_comp.target.resolved; | ||
| 1573 | resolved.ofmt = .default(resolved.os.tag, resolved.cpu.arch); | ||
| 1574 | break :resolved resolved; | ||
| 1575 | }, | ||
| 1576 | .cache_mode = .whole, | ||
| 1577 | .backend = null, | ||
| 1578 | .linker = .lld, | ||
| 1579 | .pic = parent_comp.target.pic, | ||
| 1580 | }) catch |err| switch (err) { | ||
| 1581 | error.Canceled, error.OutOfMemory, error.AlreadyReported, error.SkipTest => |e| return e, | ||
| 1582 | error.ReadFailed => unreachable, // .fixed | ||
| 1583 | }; | ||
| 1584 | const comp = &compiler.?; | ||
| 1585 | comp.client.serveBodylessMessage(.update) catch |err| switch (err) { | ||
| 1586 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 1587 | error.Canceled => |e| return e, | ||
| 1588 | else => |e| return runner.fail("unable to send message: {t}", .{e}), | ||
| 1589 | }, | ||
| 1590 | }; | ||
| 1591 | comp.state = .update; | ||
| 1592 | var out_path: ?[]const u8 = null; | ||
| 1593 | while (try comp.receiveMessage()) |message| switch (message.tag) { | ||
| 1594 | else => {}, // Ignore other messages, | ||
| 1595 | .config => { | ||
| 1596 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 1597 | comp.config = | ||
| 1598 | body_r.takeStruct(std.zig.Server.Message.Config, .little) catch unreachable; | ||
| 1599 | }, | ||
| 1600 | .emit_digest => { | ||
| 1601 | var body_r: std.Io.Reader = .fixed(message.body); | ||
| 1602 | _ = body_r.takeStruct(std.zig.Server.Message.EmitDigest, .little) catch unreachable; | ||
| 1603 | const digest = body_r.takeArray(std.Build.Cache.bin_digest_len) catch unreachable; | ||
| 1604 | |||
| 1605 | const out_dir = ".zig-cache" ++ std.Io.Dir.path.sep_str ++ | ||
| 1606 | "o" ++ std.Io.Dir.path.sep_str ++ std.Build.Cache.binToHex(digest.*); | ||
| 1607 | out_path = try std.Io.Dir.path.join(arena, &.{ out_dir, comp.out_name }); | ||
| 1608 | }, | ||
| 1609 | .error_bundle => { | ||
| 1610 | const eb = std.zig.Server.allocErrorBundle( | ||
| 1611 | runner.arena.allocator(), | ||
| 1612 | message.body, | ||
| 1613 | ) catch |err| switch (err) { | ||
| 1614 | error.OutOfMemory => |e| return e, | ||
| 1615 | error.EndOfStream => return runner.fail("error bundle truncated", .{}), | ||
| 1616 | }; | ||
| 1617 | if (eb.errorMessageCount() > 0) { | ||
| 1618 | try runner.eb_wip.addBundleAsRoots(eb); | ||
| 1619 | return runner.fail("unexpected compile errors", .{}); | ||
| 1620 | } | ||
| 1621 | comp.state = .idle; | ||
| 1622 | break; | ||
| 1623 | }, | ||
| 1624 | }; | ||
| 1625 | try comp.exit(); | ||
| 1626 | return out_path.?; | ||
| 1627 | } | ||
| 1628 | |||
| 1629 | fn exit(comp: *Compiler) FailError!void { | ||
| 1630 | const runner = comp.runner; | ||
| 1631 | comp.client.serveBodylessMessage(.exit) catch |err| switch (err) { | ||
| 1632 | error.WriteFailed => switch (comp.fw.err.?) { | ||
| 1633 | error.Canceled => |e| return e, | ||
| 1634 | error.BrokenPipe => {}, | ||
| 1635 | else => |e| return comp.runner.fail("unable to send exit: {t}", .{e}), | ||
| 1636 | }, | ||
| 1637 | }; | ||
| 1638 | comp.child.stdin.?.close(runner.io); | ||
| 1639 | comp.child.stdin = null; | ||
| 1640 | while (try comp.receiveMessage()) |_| {} | ||
| 1641 | if (comp.client.in.bufferedLen() > 0) return runner.fail("unable to receive messages", .{}); | ||
| 1642 | const term = comp.child.wait(runner.io) catch |err| | ||
| 1643 | return runner.fail("child process failed: {t}", .{err}); | ||
| 1644 | comp.prog_node.end(); | ||
| 1645 | switch (term) { | ||
| 1646 | .exited => |code| if (code != 0) | ||
| 1647 | return runner.fail("compiler failed with code {d}", .{code}), | ||
| 1648 | .signal => |sig| return runner.fail("compiler terminated with signal {t}", .{sig}), | ||
| 1649 | .stopped => |sig| return runner.fail("compiler stopped unexpectedly with signal {t}", .{ | ||
| 1650 | sig, | ||
| 1651 | }), | ||
| 1652 | .unknown => return runner.fail("compiler terminated unexpectedly", .{}), | ||
| 1653 | } | ||
| 1654 | } | ||
| 1655 | }; | ||
| 1656 | |||
| 1657 | const ClientArgs = struct { | ||
| 1658 | test_file: std.Io.File, | ||
| 1659 | test_kind: std.Io.File.Kind, | ||
| 1660 | zig_exe: std.Io.File, | ||
| 1661 | lib_dir: std.Io.Dir, | ||
| 1662 | src_dir: std.Io.Dir, | ||
| 1663 | targets: std.ArrayList([]const u8), | ||
| 1664 | |||
| 1665 | libc_runtimes_dir: ?std.Io.Dir, | ||
| 1666 | enable_darling: bool, | ||
| 1667 | enable_qemu: bool, | ||
| 1668 | enable_rosetta: bool, | ||
| 1669 | enable_wine: bool, | ||
| 1670 | enable_wasmtime: bool, | ||
| 1671 | |||
| 1672 | gdb_exe: ?std.Io.File, | ||
| 1673 | lldb_exe: ?std.Io.File, | ||
| 1674 | |||
| 1675 | quiet: bool, | ||
| 1676 | keep_src: bool, | ||
| 1677 | }; | ||
| 1678 | pub fn main(init: std.process.Init) (std.mem.Allocator.Error || std.Io.Cancelable)!u8 { | ||
| 1679 | const fatal = struct { | ||
| 1680 | fn fatal(comptime fmt: []const u8, args: anytype) noreturn { | ||
| 1681 | std.log.err(fmt, args); | ||
| 1682 | std.process.exit(1); | ||
| 1683 | } | ||
| 1684 | }.fatal; | ||
| 1685 | const arena = init.arena.allocator(); | ||
| 1686 | |||
| 1687 | var test_path_arg: ?[]const u8 = null; | ||
| 1688 | var zig_path_arg: ?[]const u8 = null; | ||
| 1689 | var lib_path_arg: ?[]const u8 = null; | ||
| 1690 | var libc_runtimes_path_arg: ?[]const u8 = null; | ||
| 1691 | var gdb_path_arg: ?[]const u8 = null; | ||
| 1692 | var lldb_path_arg: ?[]const u8 = null; | ||
| 1693 | var args: ClientArgs = .{ | ||
| 1694 | .test_file = undefined, | ||
| 1695 | .test_kind = undefined, | ||
| 1696 | .zig_exe = undefined, | ||
| 1697 | .lib_dir = undefined, | ||
| 1698 | .src_dir = undefined, | ||
| 1699 | .targets = .empty, | ||
| 1700 | |||
| 1701 | .libc_runtimes_dir = null, | ||
| 1702 | .enable_darling = false, | ||
| 1703 | .enable_qemu = false, | ||
| 1704 | .enable_rosetta = false, | ||
| 1705 | .enable_wine = false, | ||
| 1706 | .enable_wasmtime = false, | ||
| 1707 | |||
| 1708 | .gdb_exe = null, | ||
| 1709 | .lldb_exe = null, | ||
| 1710 | |||
| 1711 | .quiet = false, | ||
| 1712 | .keep_src = false, | ||
| 1713 | }; | ||
| 1714 | defer args.targets.deinit(init.gpa); | ||
| 1715 | |||
| 1716 | var arg_it = try init.minimal.args.iterateAllocator(arena); | ||
| 1717 | const self = arg_it.next().?; | ||
| 1718 | while (arg_it.next()) |arg| { | ||
| 1719 | if (std.mem.eql(u8, arg, "--listen=-")) { | ||
| 1720 | var stdin_buffer: [512]u8 = undefined; | ||
| 1721 | var stdin: std.Io.File.Reader = .initStreaming(.stdin(), init.io, &stdin_buffer); | ||
| 1722 | var stdout_buffer: [512]u8 = undefined; | ||
| 1723 | var stdout: std.Io.File.Writer = .initStreaming(.stdout(), init.io, &stdout_buffer); | ||
| 1724 | var runner: Runner = .{ | ||
| 1725 | .gpa = init.gpa, | ||
| 1726 | .arena = init.arena, | ||
| 1727 | .environ_map = init.minimal.environ.createMap(init.gpa) catch |err| switch (err) { | ||
| 1728 | error.OutOfMemory => |e| return e, | ||
| 1729 | else => |e| fatal("unable to get environment: {t}", .{e}), | ||
| 1730 | }, | ||
| 1731 | .io = init.io, | ||
| 1732 | .prog_node = std.Progress.start(init.io, .{}), | ||
| 1733 | .args = .{ | ||
| 1734 | .@"test" = null, | ||
| 1735 | .manifest_file = null, | ||
| 1736 | .zig_exe = null, | ||
| 1737 | .lib_dir = null, | ||
| 1738 | .src_dir = null, | ||
| 1739 | .targets = .empty, | ||
| 1740 | .target_bytes = .empty, | ||
| 1741 | |||
| 1742 | .libc_runtimes_dir = null, | ||
| 1743 | .enable_darling = false, | ||
| 1744 | .enable_qemu = false, | ||
| 1745 | .enable_rosetta = false, | ||
| 1746 | .enable_wasmtime = false, | ||
| 1747 | .enable_wine = false, | ||
| 1748 | |||
| 1749 | .gdb_exe = null, | ||
| 1750 | .lldb_exe = null, | ||
| 1751 | |||
| 1752 | .quiet = false, | ||
| 1753 | }, | ||
| 1754 | .host = std.zig.system.resolveTargetQuery(init.io, .{}) catch |err| switch (err) { | ||
| 1755 | error.Canceled => |e| return e, | ||
| 1756 | else => |e| fatal("unable to resolve host target: {t}", .{e}), | ||
| 1757 | }, | ||
| 1758 | .server = .{ | ||
| 1759 | .in = &stdin.interface, | ||
| 1760 | .out = &stdout.interface, | ||
| 1761 | }, | ||
| 1762 | .eb_wip = try .init(init.gpa), | ||
| 1763 | }; | ||
| 1764 | defer runner.deinit(); | ||
| 1765 | switch (runner.runServer()) { | ||
| 1766 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1767 | error.ServerReadFailed => switch (stdin.err.?) { | ||
| 1768 | error.Canceled => |e| return e, | ||
| 1769 | else => |e| fatal("unable to receive message: {t}", .{e}), | ||
| 1770 | }, | ||
| 1771 | error.ServerWriteFailed => switch (stdout.err.?) { | ||
| 1772 | error.Canceled => |e| return e, | ||
| 1773 | else => |e| fatal("unable to send message: {t}", .{e}), | ||
| 1774 | }, | ||
| 1775 | error.ServerEndOfStream => fatal("no more messages", .{}), | ||
| 1776 | error.AlreadyReported => { | ||
| 1777 | var eb = try runner.eb_wip.toOwnedBundle(""); | ||
| 1778 | defer eb.deinit(init.gpa); | ||
| 1779 | eb.renderToStderr(runner.io, .{}, .auto) catch |err| switch (err) { | ||
| 1780 | error.Canceled => |e| return e, | ||
| 1781 | else => {}, | ||
| 1782 | }; | ||
| 1783 | std.process.exit(1); | ||
| 1784 | }, | ||
| 1785 | } | ||
| 1786 | } else if (std.mem.eql(u8, arg, "--zig")) { | ||
| 1787 | zig_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1788 | } else if (std.mem.cutPrefix(u8, arg, "--zig=")) |zig_path| { | ||
| 1789 | zig_path_arg = zig_path; | ||
| 1790 | } else if (std.mem.eql(u8, arg, "--lib")) { | ||
| 1791 | lib_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1792 | } else if (std.mem.cutPrefix(u8, arg, "--lib=")) |lib_path| { | ||
| 1793 | lib_path_arg = lib_path; | ||
| 1794 | } else if (std.mem.eql(u8, arg, "--target")) { | ||
| 1795 | try args.targets.append( | ||
| 1796 | init.gpa, | ||
| 1797 | try arena.dupe(u8, arg_it.next() orelse fatal("missing arg after {q}", .{arg})), | ||
| 1798 | ); | ||
| 1799 | } else if (std.mem.cutPrefix(u8, arg, "--target=")) |target| { | ||
| 1800 | try args.targets.append(init.gpa, try arena.dupe(u8, target)); | ||
| 1801 | } else if (std.mem.eql(u8, arg, "--libc-runtimes")) { | ||
| 1802 | libc_runtimes_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1803 | } else if (std.mem.cutPrefix(u8, arg, "--libc-runtimes=")) |libc_runtimes_path| { | ||
| 1804 | libc_runtimes_path_arg = libc_runtimes_path; | ||
| 1805 | } else if (std.mem.eql(u8, arg, "-fdarling")) { | ||
| 1806 | args.enable_darling = true; | ||
| 1807 | } else if (std.mem.eql(u8, arg, "-fqemu")) { | ||
| 1808 | args.enable_qemu = true; | ||
| 1809 | } else if (std.mem.eql(u8, arg, "-frosetta")) { | ||
| 1810 | args.enable_rosetta = true; | ||
| 1811 | } else if (std.mem.eql(u8, arg, "-fwine")) { | ||
| 1812 | args.enable_wine = true; | ||
| 1813 | } else if (std.mem.eql(u8, arg, "-fwasmtime")) { | ||
| 1814 | args.enable_wasmtime = true; | ||
| 1815 | } else if (std.mem.eql(u8, arg, "--gdb")) { | ||
| 1816 | gdb_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1817 | } else if (std.mem.cutPrefix(u8, arg, "--gdb=")) |gdb_path| { | ||
| 1818 | gdb_path_arg = gdb_path; | ||
| 1819 | } else if (std.mem.eql(u8, arg, "--lldb")) { | ||
| 1820 | lldb_path_arg = arg_it.next() orelse fatal("missing arg after {q}", .{arg}); | ||
| 1821 | } else if (std.mem.cutPrefix(u8, arg, "--lldb=")) |lldb_path| { | ||
| 1822 | lldb_path_arg = lldb_path; | ||
| 1823 | } else if (std.mem.eql(u8, arg, "--quiet")) { | ||
| 1824 | args.quiet = true; | ||
| 1825 | } else if (std.mem.eql(u8, arg, "--keep-src")) { | ||
| 1826 | args.keep_src = true; | ||
| 1827 | } else { | ||
| 1828 | if (test_path_arg) |_| fatal("unexpected arg {q}", .{arg}); | ||
| 1829 | test_path_arg = arg; | ||
| 1830 | } | ||
| 1831 | } | ||
| 1832 | |||
| 1833 | const test_path = test_path_arg orelse fatal("missing \"path/to/test\" arg", .{}); | ||
| 1834 | const zig_path = zig_path_arg orelse fatal("missing \"--zig=path/to/zig\" arg", .{}); | ||
| 1835 | const lib_path = lib_path_arg orelse fatal("missing \"--lib=path/to/lib\" arg", .{}); | ||
| 1836 | |||
| 1837 | const cwd: std.Io.Dir = .cwd(); | ||
| 1838 | args.test_file = cwd.openFile(init.io, test_path, .{ | ||
| 1839 | .allow_directory = true, | ||
| 1840 | }) catch |err| switch (err) { | ||
| 1841 | error.Canceled => |e| return e, | ||
| 1842 | else => |e| fatal("unable to open {q}: {t}", .{ test_path, e }), | ||
| 1843 | }; | ||
| 1844 | defer args.test_file.close(init.io); | ||
| 1845 | args.test_kind = (args.test_file.stat(init.io) catch |err| switch (err) { | ||
| 1846 | error.Canceled => |e| return e, | ||
| 1847 | else => |e| fatal("unable to stat {q}: {t}", .{ test_path, e }), | ||
| 1848 | }).kind; | ||
| 1849 | args.zig_exe = cwd.openFile(init.io, zig_path, .{}) catch |err| switch (err) { | ||
| 1850 | error.Canceled => |e| return e, | ||
| 1851 | else => |e| fatal("unable to open {q}: {t}", .{ zig_path, e }), | ||
| 1852 | }; | ||
| 1853 | defer args.zig_exe.close(init.io); | ||
| 1854 | args.lib_dir = cwd.openDir(init.io, lib_path, .{}) catch |err| switch (err) { | ||
| 1855 | error.Canceled => |e| return e, | ||
| 1856 | else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), | ||
| 1857 | }; | ||
| 1858 | defer args.lib_dir.close(init.io); | ||
| 1859 | if (libc_runtimes_path_arg) |libc_runtimes_path| args.libc_runtimes_dir = cwd.openDir( | ||
| 1860 | init.io, | ||
| 1861 | libc_runtimes_path, | ||
| 1862 | .{}, | ||
| 1863 | ) catch |err| switch (err) { | ||
| 1864 | error.Canceled => |e| return e, | ||
| 1865 | else => |e| fatal("unable to open {q}: {t}", .{ lib_path, e }), | ||
| 1866 | }; | ||
| 1867 | defer if (args.libc_runtimes_dir) |libc_runtimes_dir| libc_runtimes_dir.close(init.io); | ||
| 1868 | if (gdb_path_arg) |gdb_path| args.gdb_exe = cwd.openFile(init.io, gdb_path, .{}) catch |err| switch (err) { | ||
| 1869 | error.Canceled => |e| return e, | ||
| 1870 | else => |e| fatal("unable to open {q}: {t}", .{ gdb_path, e }), | ||
| 1871 | }; | ||
| 1872 | defer if (args.gdb_exe) |gdb_exe| gdb_exe.close(init.io); | ||
| 1873 | if (lldb_path_arg) |lldb_path| args.lldb_exe = cwd.openFile(init.io, lldb_path, .{}) catch |err| switch (err) { | ||
| 1874 | error.Canceled => |e| return e, | ||
| 1875 | else => |e| fatal("unable to open {q}: {t}", .{ lldb_path, e }), | ||
| 1876 | }; | ||
| 1877 | defer if (args.lldb_exe) |lldb_exe| lldb_exe.close(init.io); | ||
| 1878 | |||
| 1879 | const src_dir_path = "src_" ++ std.fmt.hex(rand_int: { | ||
| 1880 | var rand_int: u64 = undefined; | ||
| 1881 | init.io.random(@ptrCast(&rand_int)); | ||
| 1882 | break :rand_int rand_int; | ||
| 1883 | }); | ||
| 1884 | args.src_dir = cwd.createDirPathOpen(init.io, src_dir_path, .{}) catch |err| switch (err) { | ||
| 1885 | error.Canceled => |e| return e, | ||
| 1886 | else => |e| fatal("unable to create {q}: {t}", .{ src_dir_path, e }), | ||
| 1887 | }; | ||
| 1888 | defer { | ||
| 1889 | args.src_dir.close(init.io); | ||
| 1890 | if (!args.keep_src) cwd.deleteTree(init.io, src_dir_path) catch |err| { | ||
| 1891 | std.log.warn("unable to delete tree {q}: {t}", .{ src_dir_path, err }); | ||
| 1892 | }; | ||
| 1893 | } | ||
| 1894 | |||
| 1895 | var inherit_dirs: std.ArrayList(std.Io.Dir) = .initBuffer(try arena.alloc(std.Io.Dir, 3)); | ||
| 1896 | var inherit_files: std.ArrayList(std.Io.File) = .initBuffer(try arena.alloc(std.Io.File, 4)); | ||
| 1897 | inherit_files.appendSliceAssumeCapacity(&.{ args.test_file, args.zig_exe }); | ||
| 1898 | inherit_dirs.appendSliceAssumeCapacity(&.{ args.lib_dir, args.src_dir }); | ||
| 1899 | if (args.libc_runtimes_dir) |libc_runtimes_dir| inherit_dirs.appendAssumeCapacity(libc_runtimes_dir); | ||
| 1900 | if (args.gdb_exe) |gdb_exe| inherit_files.appendAssumeCapacity(gdb_exe); | ||
| 1901 | if (args.lldb_exe) |lldb_exe| inherit_files.appendAssumeCapacity(lldb_exe); | ||
| 1902 | var child = std.process.spawn(init.io, .{ | ||
| 1903 | .argv = &.{ self, "--listen=-" }, | ||
| 1904 | .stdin = .pipe, | ||
| 1905 | .stdout = .pipe, | ||
| 1906 | .inherit_dirs = inherit_dirs.items, | ||
| 1907 | .inherit_files = inherit_files.items, | ||
| 1908 | }) catch |err| switch (err) { | ||
| 1909 | error.Canceled, error.OutOfMemory => |e| return e, | ||
| 1910 | else => |e| fatal("unable to spawn runner: {t}", .{e}), | ||
| 1911 | }; | ||
| 1912 | |||
| 1913 | var child_reader_buffer: [512]u8 = undefined; | ||
| 1914 | var child_reader = child.stdout.?.readerStreaming(init.io, &child_reader_buffer); | ||
| 1915 | var child_writer_buffer: [512]u8 = undefined; | ||
| 1916 | var child_writer = child.stdin.?.writerStreaming(init.io, &child_writer_buffer); | ||
| 1917 | var client: std.zig.Client = .{ | ||
| 1918 | .in = &child_reader.interface, | ||
| 1919 | .out = &child_writer.interface, | ||
| 1920 | }; | ||
| 1921 | const success = runClient(init.gpa, arena, init.io, &client, &args) catch |err| switch (err) { | ||
| 1922 | error.OutOfMemory => |e| return e, | ||
| 1923 | error.ReadFailed => switch (child_reader.err.?) { | ||
| 1924 | error.Canceled => |e| return e, | ||
| 1925 | else => |e| fatal("unable to receive message: {t}", .{e}), | ||
| 1926 | }, | ||
| 1927 | error.WriteFailed => switch (child_writer.err.?) { | ||
| 1928 | error.Canceled => |e| return e, | ||
| 1929 | else => |e| fatal("unable to send message: {t}", .{e}), | ||
| 1930 | }, | ||
| 1931 | error.EndOfStream => fatal("no more messages", .{}), | ||
| 1932 | }; | ||
| 1933 | _ = child.wait(init.io) catch |err| switch (err) { | ||
| 1934 | error.Canceled => |e| return e, | ||
| 1935 | else => |e| fatal("unable to wait for runner: {t}", .{e}), | ||
| 1936 | }; | ||
| 1937 | return if (success) 0 else 1; | ||
| 1938 | } | ||
| 1939 | fn runClient( | ||
| 1940 | gpa: std.mem.Allocator, | ||
| 1941 | arena: std.mem.Allocator, | ||
| 1942 | io: std.Io, | ||
| 1943 | client: *std.zig.Client, | ||
| 1944 | args: *const ClientArgs, | ||
| 1945 | ) (std.mem.Allocator.Error || std.Io.Reader.Error || std.Io.Writer.Error)!bool { | ||
| 1946 | var stdout_buffer: [512]u8 = undefined; | ||
| 1947 | var stdout_fw: std.Io.File.Writer = .initStreaming(.stdout(), io, &stdout_buffer); | ||
| 1948 | const stdout = &stdout_fw.interface; | ||
| 1949 | var stderr_buffer: [512]u8 = undefined; | ||
| 1950 | var stderr_fw: std.Io.File.Writer = .initStreaming(.stderr(), io, &stderr_buffer); | ||
| 1951 | const stderr = &stderr_fw.interface; | ||
| 1952 | |||
| 1953 | var argv: std.ArrayList(u8) = .empty; | ||
| 1954 | defer argv.deinit(gpa); | ||
| 1955 | const Arg = std.zig.Client.Message.Arg; | ||
| 1956 | |||
| 1957 | try argv.append(gpa, @backingInt(@as(Arg, switch (args.test_kind) { | ||
| 1958 | else => unreachable, | ||
| 1959 | .file => .input_file, | ||
| 1960 | .directory => .input_dir, | ||
| 1961 | }))); | ||
| 1962 | try argv.appendSlice(gpa, @ptrCast(&args.test_file.handle)); | ||
| 1963 | |||
| 1964 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 1965 | try argv.appendSlice(gpa, "--zig="); | ||
| 1966 | try argv.append(gpa, 0); | ||
| 1967 | try argv.append(gpa, @backingInt(Arg.input_file)); | ||
| 1968 | try argv.appendSlice(gpa, @ptrCast(&args.zig_exe.handle)); | ||
| 1969 | |||
| 1970 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 1971 | try argv.appendSlice(gpa, "--lib="); | ||
| 1972 | try argv.append(gpa, 0); | ||
| 1973 | try argv.append(gpa, @backingInt(Arg.input_dir)); | ||
| 1974 | try argv.appendSlice(gpa, @ptrCast(&args.lib_dir.handle)); | ||
| 1975 | |||
| 1976 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 1977 | try argv.appendSlice(gpa, "--src="); | ||
| 1978 | try argv.append(gpa, 0); | ||
| 1979 | try argv.append(gpa, @backingInt(Arg.output_dir)); | ||
| 1980 | try argv.appendSlice(gpa, @ptrCast(&args.src_dir.handle)); | ||
| 1981 | |||
| 1982 | for (args.targets.items) |target| { | ||
| 1983 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1984 | try argv.appendSlice(gpa, "--target"); | ||
| 1985 | try argv.append(gpa, 0); | ||
| 1986 | |||
| 1987 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 1988 | try argv.appendSlice(gpa, target); | ||
| 1989 | try argv.append(gpa, 0); | ||
| 1990 | } | ||
| 1991 | |||
| 1992 | if (args.libc_runtimes_dir) |libc_runtimes_dir| { | ||
| 1993 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 1994 | try argv.appendSlice(gpa, "--libc-runtimes="); | ||
| 1995 | try argv.append(gpa, 0); | ||
| 1996 | try argv.append(gpa, @backingInt(Arg.input_dir)); | ||
| 1997 | try argv.appendSlice(gpa, @ptrCast(&libc_runtimes_dir.handle)); | ||
| 1998 | } | ||
| 1999 | if (args.enable_darling) { | ||
| 2000 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2001 | try argv.appendSlice(gpa, "-fdarling"); | ||
| 2002 | try argv.append(gpa, 0); | ||
| 2003 | } | ||
| 2004 | if (args.enable_qemu) { | ||
| 2005 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2006 | try argv.appendSlice(gpa, "-fqemu"); | ||
| 2007 | try argv.append(gpa, 0); | ||
| 2008 | } | ||
| 2009 | if (args.enable_rosetta) { | ||
| 2010 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2011 | try argv.appendSlice(gpa, "-frosetta"); | ||
| 2012 | try argv.append(gpa, 0); | ||
| 2013 | } | ||
| 2014 | if (args.enable_wine) { | ||
| 2015 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2016 | try argv.appendSlice(gpa, "-fwine"); | ||
| 2017 | try argv.append(gpa, 0); | ||
| 2018 | } | ||
| 2019 | if (args.enable_wasmtime) { | ||
| 2020 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2021 | try argv.appendSlice(gpa, "-fwasmtime"); | ||
| 2022 | try argv.append(gpa, 0); | ||
| 2023 | } | ||
| 2024 | |||
| 2025 | if (args.gdb_exe) |gdb_exe| { | ||
| 2026 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 2027 | try argv.appendSlice(gpa, "--gdb="); | ||
| 2028 | try argv.append(gpa, 0); | ||
| 2029 | try argv.append(gpa, @backingInt(Arg.input_file)); | ||
| 2030 | try argv.appendSlice(gpa, @ptrCast(&gdb_exe.handle)); | ||
| 2031 | } | ||
| 2032 | if (args.lldb_exe) |lldb_exe| { | ||
| 2033 | try argv.append(gpa, @backingInt(Arg.prefix)); | ||
| 2034 | try argv.appendSlice(gpa, "--lldb="); | ||
| 2035 | try argv.append(gpa, 0); | ||
| 2036 | try argv.append(gpa, @backingInt(Arg.input_file)); | ||
| 2037 | try argv.appendSlice(gpa, @ptrCast(&lldb_exe.handle)); | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | if (args.quiet) { | ||
| 2041 | try argv.append(gpa, @backingInt(Arg.string)); | ||
| 2042 | try argv.appendSlice(gpa, "--quiet"); | ||
| 2043 | try argv.append(gpa, 0); | ||
| 2044 | } | ||
| 2045 | |||
| 2046 | try client.serveMessageHeader(.{ | ||
| 2047 | .tag = .args, | ||
| 2048 | .bytes_len = @intCast(argv.items.len), | ||
| 2049 | }); | ||
| 2050 | try client.out.writeAll(argv.items); | ||
| 2051 | |||
| 2052 | var success = true; | ||
| 2053 | var metadata: struct { | ||
| 2054 | index: u32, | ||
| 2055 | len: u32, | ||
| 2056 | names: []u32, | ||
| 2057 | expected_panic_msgs: []u32, | ||
| 2058 | string_bytes: []u8, | ||
| 2059 | |||
| 2060 | fn name(m: *@This()) []const u8 { | ||
| 2061 | return std.mem.sliceTo(m.string_bytes[m.names[m.index - 1]..], 0); | ||
| 2062 | } | ||
| 2063 | |||
| 2064 | fn expectedPanicMsg(m: *@This()) ?[:0]const u8 { | ||
| 2065 | return switch (m.expected_panic_msgs[m.index - 1]) { | ||
| 2066 | 0 => null, | ||
| 2067 | else => |offset| m.string_bytes[offset..std.mem.findScalarPos( | ||
| 2068 | u8, | ||
| 2069 | m.string_bytes, | ||
| 2070 | offset, | ||
| 2071 | 0, | ||
| 2072 | ).? :0], | ||
| 2073 | }; | ||
| 2074 | } | ||
| 2075 | |||
| 2076 | fn next(m: *@This()) ?u32 { | ||
| 2077 | if (m.len - m.index == 0) return null; | ||
| 2078 | defer m.index += 1; | ||
| 2079 | return m.index; | ||
| 2080 | } | ||
| 2081 | } = .{ .index = 0, .len = 0, .names = &.{}, .expected_panic_msgs = &.{}, .string_bytes = &.{} }; | ||
| 2082 | try client.serveBodylessMessage(.query_test_metadata); | ||
| 2083 | while (true) { | ||
| 2084 | const hdr = try client.receiveMessage(); | ||
| 2085 | switch (hdr.tag) { | ||
| 2086 | else => try client.in.discardAll(hdr.bytes_len), | ||
| 2087 | .zig_version => { | ||
| 2088 | const actual_version = try client.in.take(hdr.bytes_len); | ||
| 2089 | if (!std.mem.eql(u8, builtin.zig_version_string, actual_version)) { | ||
| 2090 | try stderr.print( | ||
| 2091 | "error: zig version mismatch compiler test runner vs compiler: {q} vs {q}\n", | ||
| 2092 | .{ builtin.zig_version_string, actual_version }, | ||
| 2093 | ); | ||
| 2094 | try stderr.flush(); | ||
| 2095 | std.process.exit(1); | ||
| 2096 | } | ||
| 2097 | }, | ||
| 2098 | .error_bundle => { | ||
| 2099 | const body = try arena.alloc(u8, hdr.bytes_len); | ||
| 2100 | try client.in.readSliceAll(body); | ||
| 2101 | const eb = std.zig.Server.allocErrorBundle(arena, body) catch |err| switch (err) { | ||
| 2102 | error.OutOfMemory => |e| return e, | ||
| 2103 | error.EndOfStream => { | ||
| 2104 | try stderr.writeAll("error bundle truncated\n"); | ||
| 2105 | try stderr.flush(); | ||
| 2106 | std.process.exit(1); | ||
| 2107 | }, | ||
| 2108 | }; | ||
| 2109 | success = false; | ||
| 2110 | eb.renderToStderr(io, .{}, .auto) catch {}; | ||
| 2111 | }, | ||
| 2112 | .test_metadata => { | ||
| 2113 | const tm_hdr = | ||
| 2114 | try client.in.takeStruct(std.zig.Server.Message.TestMetadata, .little); | ||
| 2115 | metadata = .{ | ||
| 2116 | .index = 0, | ||
| 2117 | .len = tm_hdr.tests_len, | ||
| 2118 | .names = try arena.alloc(u32, tm_hdr.tests_len), | ||
| 2119 | .expected_panic_msgs = try arena.alloc(u32, tm_hdr.tests_len), | ||
| 2120 | .string_bytes = try arena.alloc(u8, tm_hdr.string_bytes_len), | ||
| 2121 | }; | ||
| 2122 | try client.in.readSliceEndian(u32, metadata.names, .little); | ||
| 2123 | try client.in.readSliceEndian(u32, metadata.expected_panic_msgs, .little); | ||
| 2124 | try client.in.readSliceAll(metadata.string_bytes); | ||
| 2125 | try client.serveRunTest(metadata.next() orelse break); | ||
| 2126 | }, | ||
| 2127 | .test_results => { | ||
| 2128 | const tr = try client.in.takeStruct(std.zig.Server.Message.TestResults, .little); | ||
| 2129 | switch (tr.flags.status) { | ||
| 2130 | .pass, .skip => {}, | ||
| 2131 | .fail => success = false, | ||
| 2132 | } | ||
| 2133 | try stdout.print("[{t}] {s}\n", .{ tr.flags.status, metadata.name() }); | ||
| 2134 | try stdout.flush(); | ||
| 2135 | try client.serveRunTest(metadata.next() orelse break); | ||
| 2136 | }, | ||
| 2137 | } | ||
| 2138 | } | ||
| 2139 | try client.serveBodylessMessage(.exit); | ||
| 2140 | return success; | ||
| 2141 | } | ||
test/tests.zig+9-137| ... | @@ -2201,137 +2201,6 @@ const RunnerTarget = struct { | ... | @@ -2201,137 +2201,6 @@ const RunnerTarget = struct { |
| 2201 | pic: enum { nopic, pic, pie }, | 2201 | pic: enum { nopic, pic, pie }, |
| 2202 | }; | 2202 | }; |
| 2203 | 2203 | ||
| 2204 | pub const incremental_matrix: []const RunnerTarget = &.{ | ||
| 2205 | // Avoid adding more CBE or LLVM targets without good reason: they're a lot slower than others | ||
| 2206 | // to run due to the output (C source code or LLVM IR) being built non-incrementally (by Clang | ||
| 2207 | // or LLVM). We just have a couple here to make sure that it works. | ||
| 2208 | .{ | ||
| 2209 | .target = .{ | ||
| 2210 | .cpu_arch = .x86_64, | ||
| 2211 | .os_tag = .linux, | ||
| 2212 | .abi = .musl, | ||
| 2213 | }, | ||
| 2214 | .cache_mode = .incremental, | ||
| 2215 | .backend = .cbe, | ||
| 2216 | .linker = .old, | ||
| 2217 | .pic = .nopic, | ||
| 2218 | }, | ||
| 2219 | .{ | ||
| 2220 | .target = .{ | ||
| 2221 | .cpu_arch = .x86_64, | ||
| 2222 | .os_tag = .linux, | ||
| 2223 | .abi = .none, | ||
| 2224 | }, | ||
| 2225 | .cache_mode = .incremental, | ||
| 2226 | .backend = .llvm, | ||
| 2227 | .linker = .lld, | ||
| 2228 | .pic = .nopic, | ||
| 2229 | }, | ||
| 2230 | |||
| 2231 | .{ | ||
| 2232 | .target = .{ | ||
| 2233 | .cpu_arch = .x86_64, | ||
| 2234 | .os_tag = .linux, | ||
| 2235 | .abi = .none, | ||
| 2236 | }, | ||
| 2237 | .cache_mode = .incremental, | ||
| 2238 | .backend = .selfhosted, | ||
| 2239 | .linker = .new, | ||
| 2240 | .pic = .nopic, | ||
| 2241 | }, | ||
| 2242 | .{ | ||
| 2243 | .target = .{ | ||
| 2244 | .cpu_arch = .x86_64, | ||
| 2245 | .os_tag = .windows, | ||
| 2246 | .abi = .none, | ||
| 2247 | }, | ||
| 2248 | .cache_mode = .incremental, | ||
| 2249 | .backend = .selfhosted, | ||
| 2250 | .linker = .old, | ||
| 2251 | .pic = .pic, | ||
| 2252 | }, | ||
| 2253 | .{ | ||
| 2254 | .target = .{ | ||
| 2255 | .cpu_arch = .wasm32, | ||
| 2256 | .os_tag = .wasi, | ||
| 2257 | .abi = .none, | ||
| 2258 | }, | ||
| 2259 | .cache_mode = .incremental, | ||
| 2260 | .backend = .selfhosted, | ||
| 2261 | .linker = .old, | ||
| 2262 | .pic = .nopic, | ||
| 2263 | }, | ||
| 2264 | }; | ||
| 2265 | |||
| 2266 | pub const debugger_matrix: []const RunnerTarget = &.{ | ||
| 2267 | .{ | ||
| 2268 | .target = .{ | ||
| 2269 | .cpu_arch = .x86_64, | ||
| 2270 | .os_tag = .linux, | ||
| 2271 | .abi = .none, | ||
| 2272 | }, | ||
| 2273 | .cache_mode = .whole, | ||
| 2274 | .backend = .selfhosted, | ||
| 2275 | .linker = .old, | ||
| 2276 | .pic = .nopic, | ||
| 2277 | }, | ||
| 2278 | .{ | ||
| 2279 | .target = .{ | ||
| 2280 | .cpu_arch = .x86_64, | ||
| 2281 | .os_tag = .linux, | ||
| 2282 | .abi = .none, | ||
| 2283 | }, | ||
| 2284 | .cache_mode = .whole, | ||
| 2285 | .backend = .selfhosted, | ||
| 2286 | .linker = .old, | ||
| 2287 | .pic = .pic, | ||
| 2288 | }, | ||
| 2289 | .{ | ||
| 2290 | .target = .{ | ||
| 2291 | .cpu_arch = .x86_64, | ||
| 2292 | .os_tag = .linux, | ||
| 2293 | .abi = .none, | ||
| 2294 | }, | ||
| 2295 | .cache_mode = .whole, | ||
| 2296 | .backend = .selfhosted, | ||
| 2297 | .linker = .new, | ||
| 2298 | .pic = .nopic, | ||
| 2299 | }, | ||
| 2300 | .{ | ||
| 2301 | .target = .{ | ||
| 2302 | .cpu_arch = .x86_64, | ||
| 2303 | .os_tag = .linux, | ||
| 2304 | .abi = .none, | ||
| 2305 | }, | ||
| 2306 | .cache_mode = .whole, | ||
| 2307 | .backend = .selfhosted, | ||
| 2308 | .linker = .new, | ||
| 2309 | .pic = .pic, | ||
| 2310 | }, | ||
| 2311 | .{ | ||
| 2312 | .target = .{ | ||
| 2313 | .cpu_arch = .x86_64, | ||
| 2314 | .os_tag = .linux, | ||
| 2315 | .abi = .none, | ||
| 2316 | }, | ||
| 2317 | .cache_mode = .incremental, | ||
| 2318 | .backend = .selfhosted, | ||
| 2319 | .linker = .new, | ||
| 2320 | .pic = .nopic, | ||
| 2321 | }, | ||
| 2322 | .{ | ||
| 2323 | .target = .{ | ||
| 2324 | .cpu_arch = .x86_64, | ||
| 2325 | .os_tag = .linux, | ||
| 2326 | .abi = .none, | ||
| 2327 | }, | ||
| 2328 | .cache_mode = .incremental, | ||
| 2329 | .backend = .selfhosted, | ||
| 2330 | .linker = .new, | ||
| 2331 | .pic = .pic, | ||
| 2332 | }, | ||
| 2333 | }; | ||
| 2334 | |||
| 2335 | pub fn addStackTraceTests(b: *std.Build, options: StackTracesContext.Options) *Step { | 2204 | pub fn addStackTraceTests(b: *std.Build, options: StackTracesContext.Options) *Step { |
| 2336 | const step = b.step("test-stack-traces", "Run the stack trace tests"); | 2205 | const step = b.step("test-stack-traces", "Run the stack trace tests"); |
| 2337 | 2206 | ||
| ... | @@ -3224,21 +3093,24 @@ pub fn addRunnerTests( | ... | @@ -3224,21 +3093,24 @@ pub fn addRunnerTests( |
| 3224 | defer tests_dir.close(b.graph.io); | 3093 | defer tests_dir.close(b.graph.io); |
| 3225 | var test_it = tests_dir.iterate(); | 3094 | var test_it = tests_dir.iterate(); |
| 3226 | while (try test_it.next(b.graph.io)) |@"test"| { | 3095 | while (try test_it.next(b.graph.io)) |@"test"| { |
| 3227 | for (options.test_filters) |filter| { | ||
| 3228 | if (std.mem.find(u8, @"test".name, filter) != null) break; | ||
| 3229 | } else if (options.test_filters.len > 0) continue; | ||
| 3230 | |||
| 3231 | const test_path = tests_path.path(b, @"test".name); | ||
| 3232 | switch (@"test".kind) { | 3096 | switch (@"test".kind) { |
| 3233 | else => continue, | 3097 | else => continue, |
| 3234 | .file => if (isEditorFileName(@"test".name)) continue, | 3098 | .file => { |
| 3099 | if (std.mem.eql(u8, @"test".name, "matrix.zon")) continue; | ||
| 3100 | if (isEditorFileName(@"test".name)) continue; | ||
| 3101 | }, | ||
| 3235 | .directory => {}, | 3102 | .directory => {}, |
| 3236 | } | 3103 | } |
| 3237 | 3104 | ||
| 3105 | for (options.test_filters) |filter| { | ||
| 3106 | if (std.mem.find(u8, @"test".name, filter) != null) break; | ||
| 3107 | } else if (options.test_filters.len > 0) continue; | ||
| 3108 | |||
| 3238 | const run = b.addRunArtifact(runner); | 3109 | const run = b.addRunArtifact(runner); |
| 3239 | run.setName(@"test".name); | 3110 | run.setName(@"test".name); |
| 3240 | run.enableProtocolMode(); | 3111 | run.enableProtocolMode(); |
| 3241 | 3112 | ||
| 3113 | const test_path = tests_path.path(b, @"test".name); | ||
| 3242 | switch (@"test".kind) { | 3114 | switch (@"test".kind) { |
| 3243 | else => continue, | 3115 | else => continue, |
| 3244 | .file => run.addFileArg(test_path), | 3116 | .file => run.addFileArg(test_path), |