| ... | @@ -2,14 +2,41 @@ const std = @import("std"); | ... | @@ -2,14 +2,41 @@ const std = @import("std"); |
| 2 | const fatal = std.process.fatal; | 2 | const fatal = std.process.fatal; |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | | 4 | |
| | 5 | const usage = "usage: incr-check <zig binary path> <input file> [-fno-emit-bin] [--zig-lib-dir lib]"; |
| | 6 | |
| 5 | pub fn main() !void { | 7 | pub fn main() !void { |
| 6 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); | 8 | var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); |
| 7 | defer arena_instance.deinit(); | 9 | defer arena_instance.deinit(); |
| 8 | const arena = arena_instance.allocator(); | 10 | const arena = arena_instance.allocator(); |
| 9 | | 11 | |
| 10 | const args = try std.process.argsAlloc(arena); | 12 | var opt_zig_exe: ?[]const u8 = null; |
| 11 | const zig_exe = args[1]; | 13 | var opt_input_file_name: ?[]const u8 = null; |
| 12 | const input_file_name = args[2]; | 14 | var opt_lib_dir: ?[]const u8 = null; |
| | 15 | var no_bin = false; |
| | 16 | |
| | 17 | var arg_it = try std.process.argsWithAllocator(arena); |
| | 18 | _ = arg_it.skip(); |
| | 19 | while (arg_it.next()) |arg| { |
| | 20 | if (arg.len > 0 and arg[0] == '-') { |
| | 21 | if (std.mem.eql(u8, arg, "-fno-emit-bin")) { |
| | 22 | no_bin = true; |
| | 23 | } else if (std.mem.eql(u8, arg, "--zig-lib-dir")) { |
| | 24 | opt_lib_dir = arg_it.next() orelse fatal("expected arg after '--zig-lib-dir'\n{s}", .{usage}); |
| | 25 | } else { |
| | 26 | fatal("unknown option '{s}'\n{s}", .{ arg, usage }); |
| | 27 | } |
| | 28 | continue; |
| | 29 | } |
| | 30 | if (opt_zig_exe == null) { |
| | 31 | opt_zig_exe = arg; |
| | 32 | } else if (opt_input_file_name == null) { |
| | 33 | opt_input_file_name = arg; |
| | 34 | } else { |
| | 35 | fatal("unknown argument '{s}'\n{s}", .{ arg, usage }); |
| | 36 | } |
| | 37 | } |
| | 38 | const zig_exe = opt_zig_exe orelse fatal("missing path to zig\n{s}", .{usage}); |
| | 39 | const input_file_name = opt_input_file_name orelse fatal("missing input file\n{s}", .{usage}); |
| 13 | | 40 | |
| 14 | const input_file_bytes = try std.fs.cwd().readFileAlloc(arena, input_file_name, std.math.maxInt(u32)); | 41 | const input_file_bytes = try std.fs.cwd().readFileAlloc(arena, input_file_name, std.math.maxInt(u32)); |
| 15 | const case = try Case.parse(arena, input_file_bytes); | 42 | const case = try Case.parse(arena, input_file_bytes); |
| ... | @@ -24,13 +51,12 @@ pub fn main() !void { | ... | @@ -24,13 +51,12 @@ pub fn main() !void { |
| 24 | const child_prog_node = prog_node.start("zig build-exe", 0); | 51 | const child_prog_node = prog_node.start("zig build-exe", 0); |
| 25 | defer child_prog_node.end(); | 52 | defer child_prog_node.end(); |
| 26 | | 53 | |
| 27 | var child = std.process.Child.init(&.{ | 54 | var child_args: std.ArrayListUnmanaged([]const u8) = .{}; |
| | 55 | try child_args.appendSlice(arena, &.{ |
| 28 | // Convert incr-check-relative path to subprocess-relative path. | 56 | // Convert incr-check-relative path to subprocess-relative path. |
| 29 | try std.fs.path.relative(arena, tmp_dir_path, zig_exe), | 57 | try std.fs.path.relative(arena, tmp_dir_path, zig_exe), |
| 30 | "build-exe", | 58 | "build-exe", |
| 31 | case.root_source_file, | 59 | case.root_source_file, |
| 32 | "-fno-llvm", | | |
| 33 | "-fno-lld", | | |
| 34 | "-fincremental", | 60 | "-fincremental", |
| 35 | "-target", | 61 | "-target", |
| 36 | case.target_query, | 62 | case.target_query, |
| ... | @@ -39,8 +65,17 @@ pub fn main() !void { | ... | @@ -39,8 +65,17 @@ pub fn main() !void { |
| 39 | "--global-cache-dir", | 65 | "--global-cache-dir", |
| 40 | ".global_cache", | 66 | ".global_cache", |
| 41 | "--listen=-", | 67 | "--listen=-", |
| 42 | }, arena); | 68 | }); |
| | 69 | if (opt_lib_dir) |lib_dir| { |
| | 70 | try child_args.appendSlice(arena, &.{ "--zig-lib-dir", lib_dir }); |
| | 71 | } |
| | 72 | if (no_bin) { |
| | 73 | try child_args.append(arena, "-fno-emit-bin"); |
| | 74 | } else { |
| | 75 | try child_args.appendSlice(arena, &.{ "-fno-llvm", "-fno-lld" }); |
| | 76 | } |
| 43 | | 77 | |
| | 78 | var child = std.process.Child.init(child_args.items, arena); |
| 44 | child.stdin_behavior = .Pipe; | 79 | child.stdin_behavior = .Pipe; |
| 45 | child.stdout_behavior = .Pipe; | 80 | child.stdout_behavior = .Pipe; |
| 46 | child.stderr_behavior = .Pipe; | 81 | child.stderr_behavior = .Pipe; |
| ... | @@ -65,6 +100,8 @@ pub fn main() !void { | ... | @@ -65,6 +100,8 @@ pub fn main() !void { |
| 65 | defer poller.deinit(); | 100 | defer poller.deinit(); |
| 66 | | 101 | |
| 67 | for (case.updates) |update| { | 102 | for (case.updates) |update| { |
| | 103 | var update_node = prog_node.start(update.name, 0); |
| | 104 | defer update_node.end(); |
| 68 | eval.write(update); | 105 | eval.write(update); |
| 69 | try eval.requestUpdate(); | 106 | try eval.requestUpdate(); |
| 70 | try eval.check(&poller, update); | 107 | try eval.check(&poller, update); |
| ... | @@ -138,7 +175,17 @@ const Eval = struct { | ... | @@ -138,7 +175,17 @@ const Eval = struct { |
| 138 | const stderr_data = try stderr.toOwnedSlice(); | 175 | const stderr_data = try stderr.toOwnedSlice(); |
| 139 | fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data}); | 176 | fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data}); |
| 140 | } | 177 | } |
| 141 | try eval.checkErrorOutcome(update, result_error_bundle); | 178 | if (result_error_bundle.errorMessageCount() == 0) { |
| | 179 | // Empty bundle indicates successful update in a `-fno-emit-bin` build. |
| | 180 | // We can't do a full success check since we don't have a binary, but let's |
| | 181 | // at least check that no errors were expected. |
| | 182 | switch (update.outcome) { |
| | 183 | .unknown, .stdout, .exit_code => {}, |
| | 184 | .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}), |
| | 185 | } |
| | 186 | } else { |
| | 187 | try eval.checkErrorOutcome(update, result_error_bundle); |
| | 188 | } |
| 142 | // This message indicates the end of the update. | 189 | // This message indicates the end of the update. |
| 143 | stdout.discard(body.len); | 190 | stdout.discard(body.len); |
| 144 | return; | 191 | return; |
| ... | @@ -357,6 +404,11 @@ const Case = struct { | ... | @@ -357,6 +404,11 @@ const Case = struct { |
| 357 | fatal("line {d}: bad string literal: {s}", .{ line_n, @errorName(err) }); | 404 | fatal("line {d}: bad string literal: {s}", .{ line_n, @errorName(err) }); |
| 358 | }, | 405 | }, |
| 359 | }; | 406 | }; |
| | 407 | } else if (std.mem.eql(u8, key, "expect_error")) { |
| | 408 | if (updates.items.len == 0) fatal("line {d}: expect directive before update", .{line_n}); |
| | 409 | const last_update = &updates.items[updates.items.len - 1]; |
| | 410 | if (last_update.outcome != .unknown) fatal("line {d}: conflicting expect directive", .{line_n}); |
| | 411 | last_update.outcome = .{ .compile_errors = &.{} }; |
| 360 | } else { | 412 | } else { |
| 361 | fatal("line {d}: unrecognized key '{s}'", .{ line_n, key }); | 413 | fatal("line {d}: unrecognized key '{s}'", .{ line_n, key }); |
| 362 | } | 414 | } |