authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-26 04:21:58+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-10-05 20:52:26+01:00
logdfc0a27090b1f15aae429801cdded14ab0a80595
treef37b49f99b77fde2d80fba12fb81ded57c7b38d7
parent5ce962eb690aa094521b0ec5f3f466192c42318a
signaturelock-open Commit is signed but in an unrecognized format.

incr-check: clean up temporary directory by default

The new `--preserve-tmp` flag can be used to preserve the temporary directory for debugging purposes.

1 files changed, 64 insertions(+), 30 deletions(-)

tools/incr-check.zig+64-30
...@@ -1,11 +1,12 @@...@@ -1,11 +1,12 @@
1const std = @import("std");1const std = @import("std");
2const fatal = std.process.fatal;
3const Allocator = std.mem.Allocator;2const Allocator = std.mem.Allocator;
4const Cache = std.Build.Cache;3const Cache = std.Build.Cache;
54
6const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-zcu] [--debug-link] [--zig-cc-binary /path/to/zig]";5const usage = "usage: incr-check <zig binary path> <input file> [--zig-lib-dir lib] [--debug-zcu] [--debug-link] [--preserve-tmp] [--zig-cc-binary /path/to/zig]";
76
8pub fn main() !void {7pub fn main() !void {
8 const fatal = std.process.fatal;
9
9 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);10 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
10 defer arena_instance.deinit();11 defer arena_instance.deinit();
11 const arena = arena_instance.allocator();12 const arena = arena_instance.allocator();
...@@ -16,6 +17,7 @@ pub fn main() !void {...@@ -16,6 +17,7 @@ pub fn main() !void {
16 var opt_cc_zig: ?[]const u8 = null;17 var opt_cc_zig: ?[]const u8 = null;
17 var debug_zcu = false;18 var debug_zcu = false;
18 var debug_link = false;19 var debug_link = false;
20 var preserve_tmp = false;
1921
20 var arg_it = try std.process.argsWithAllocator(arena);22 var arg_it = try std.process.argsWithAllocator(arena);
21 _ = arg_it.skip();23 _ = arg_it.skip();
...@@ -27,6 +29,8 @@ pub fn main() !void {...@@ -27,6 +29,8 @@ pub fn main() !void {
27 debug_zcu = true;29 debug_zcu = true;
28 } else if (std.mem.eql(u8, arg, "--debug-link")) {30 } else if (std.mem.eql(u8, arg, "--debug-link")) {
29 debug_link = true;31 debug_link = true;
32 } else if (std.mem.eql(u8, arg, "--preserve-tmp")) {
33 preserve_tmp = true;
30 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {34 } else if (std.mem.eql(u8, arg, "--zig-cc-binary")) {
31 opt_cc_zig = arg_it.next() orelse fatal("expect arg after '--zig-cc-binary'\n{s}", .{usage});35 opt_cc_zig = arg_it.next() orelse fatal("expect arg after '--zig-cc-binary'\n{s}", .{usage});
32 } else {36 } else {
...@@ -48,12 +52,29 @@ pub fn main() !void {...@@ -48,12 +52,29 @@ pub fn main() !void {
48 const input_file_bytes = try std.fs.cwd().readFileAlloc(arena, input_file_name, std.math.maxInt(u32));52 const input_file_bytes = try std.fs.cwd().readFileAlloc(arena, input_file_name, std.math.maxInt(u32));
49 const case = try Case.parse(arena, input_file_bytes);53 const case = try Case.parse(arena, input_file_bytes);
5054
55 // Check now: if there are any targets using the `cbe` backend, we need the lib dir.
56 if (opt_lib_dir == null) {
57 for (case.targets) |target| {
58 if (target.backend == .cbe) {
59 fatal("'--zig-lib-dir' requried when using backend 'cbe'", .{});
60 }
61 }
62 }
63
51 const prog_node = std.Progress.start(.{});64 const prog_node = std.Progress.start(.{});
52 defer prog_node.end();65 defer prog_node.end();
5366
54 const rand_int = std.crypto.random.int(u64);67 const rand_int = std.crypto.random.int(u64);
55 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);68 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);
56 const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{});69 var tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{});
70 defer {
71 tmp_dir.close();
72 if (!preserve_tmp) {
73 std.fs.cwd().deleteTree(tmp_dir_path) catch |err| {
74 std.log.warn("failed to delete tree '{s}': {s}", .{ tmp_dir_path, @errorName(err) });
75 };
76 }
77 }
5778
58 // Convert paths to be relative to the cwd of the subprocess.79 // Convert paths to be relative to the cwd of the subprocess.
59 const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe);80 const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe);
...@@ -132,7 +153,7 @@ pub fn main() !void {...@@ -132,7 +153,7 @@ pub fn main() !void {
132 "-target",153 "-target",
133 target.query,154 target.query,
134 "-I",155 "-I",
135 opt_resolved_lib_dir orelse fatal("'--zig-lib-dir' required when using backend 'cbe'", .{}),156 opt_resolved_lib_dir.?, // verified earlier
136 "-o",157 "-o",
137 });158 });
138 }159 }
...@@ -146,6 +167,7 @@ pub fn main() !void {...@@ -146,6 +167,7 @@ pub fn main() !void {
146 .tmp_dir_path = tmp_dir_path,167 .tmp_dir_path = tmp_dir_path,
147 .child = &child,168 .child = &child,
148 .allow_stderr = debug_log_verbose,169 .allow_stderr = debug_log_verbose,
170 .preserve_tmp_on_fatal = preserve_tmp,
149 .cc_child_args = &cc_child_args,171 .cc_child_args = &cc_child_args,
150 };172 };
151173
...@@ -172,7 +194,7 @@ pub fn main() !void {...@@ -172,7 +194,7 @@ pub fn main() !void {
172194
173 try eval.end(&poller);195 try eval.end(&poller);
174196
175 waitChild(&child);197 waitChild(&child, &eval);
176 }198 }
177}199}
178200
...@@ -185,6 +207,7 @@ const Eval = struct {...@@ -185,6 +207,7 @@ const Eval = struct {
185 tmp_dir_path: []const u8,207 tmp_dir_path: []const u8,
186 child: *std.process.Child,208 child: *std.process.Child,
187 allow_stderr: bool,209 allow_stderr: bool,
210 preserve_tmp_on_fatal: bool,
188 /// When `target.backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary.211 /// When `target.backend == .cbe`, this contains the first few arguments to `zig cc` to build the generated binary.
189 /// The arguments `out.c in.c` must be appended before spawning the subprocess.212 /// The arguments `out.c in.c` must be appended before spawning the subprocess.
190 cc_child_args: *std.ArrayListUnmanaged([]const u8),213 cc_child_args: *std.ArrayListUnmanaged([]const u8),
...@@ -199,12 +222,12 @@ const Eval = struct {...@@ -199,12 +222,12 @@ const Eval = struct {
199 .sub_path = full_contents.name,222 .sub_path = full_contents.name,
200 .data = full_contents.bytes,223 .data = full_contents.bytes,
201 }) catch |err| {224 }) catch |err| {
202 fatal("failed to update '{s}': {s}", .{ full_contents.name, @errorName(err) });225 eval.fatal("failed to update '{s}': {s}", .{ full_contents.name, @errorName(err) });
203 };226 };
204 }227 }
205 for (update.deletes) |doomed_name| {228 for (update.deletes) |doomed_name| {
206 eval.tmp_dir.deleteFile(doomed_name) catch |err| {229 eval.tmp_dir.deleteFile(doomed_name) catch |err| {
207 fatal("failed to delete '{s}': {s}", .{ doomed_name, @errorName(err) });230 eval.fatal("failed to delete '{s}': {s}", .{ doomed_name, @errorName(err) });
208 };231 };
209 }232 }
210 }233 }
...@@ -246,7 +269,7 @@ const Eval = struct {...@@ -246,7 +269,7 @@ const Eval = struct {
246 if (eval.allow_stderr) {269 if (eval.allow_stderr) {
247 std.log.info("error_bundle included stderr:\n{s}", .{stderr_data});270 std.log.info("error_bundle included stderr:\n{s}", .{stderr_data});
248 } else {271 } else {
249 fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data});272 eval.fatal("error_bundle included unexpected stderr:\n{s}", .{stderr_data});
250 }273 }
251 }274 }
252 if (result_error_bundle.errorMessageCount() != 0) {275 if (result_error_bundle.errorMessageCount() != 0) {
...@@ -265,7 +288,7 @@ const Eval = struct {...@@ -265,7 +288,7 @@ const Eval = struct {
265 if (eval.allow_stderr) {288 if (eval.allow_stderr) {
266 std.log.info("emit_digest included stderr:\n{s}", .{stderr_data});289 std.log.info("emit_digest included stderr:\n{s}", .{stderr_data});
267 } else {290 } else {
268 fatal("emit_digest included unexpected stderr:\n{s}", .{stderr_data});291 eval.fatal("emit_digest included unexpected stderr:\n{s}", .{stderr_data});
269 }292 }
270 }293 }
271294
...@@ -302,16 +325,15 @@ const Eval = struct {...@@ -302,16 +325,15 @@ const Eval = struct {
302 if (eval.allow_stderr) {325 if (eval.allow_stderr) {
303 std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr_data });326 std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr_data });
304 } else {327 } else {
305 fatal("update '{s}' failed:\n{s}", .{ update.name, stderr_data });328 eval.fatal("update '{s}' failed:\n{s}", .{ update.name, stderr_data });
306 }329 }
307 }330 }
308331
309 waitChild(eval.child);332 waitChild(eval.child, eval);
310 fatal("update '{s}': compiler failed to send error_bundle or emit_bin_path", .{update.name});333 eval.fatal("update '{s}': compiler failed to send error_bundle or emit_bin_path", .{update.name});
311 }334 }
312335
313 fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void {336 fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void {
314 _ = eval;
315 switch (update.outcome) {337 switch (update.outcome) {
316 .unknown => return,338 .unknown => return,
317 .compile_errors => |expected_errors| {339 .compile_errors => |expected_errors| {
...@@ -323,7 +345,7 @@ const Eval = struct {...@@ -323,7 +345,7 @@ const Eval = struct {
323 .stdout, .exit_code => {345 .stdout, .exit_code => {
324 const color: std.zig.Color = .auto;346 const color: std.zig.Color = .auto;
325 error_bundle.renderToStdErr(color.renderOptions());347 error_bundle.renderToStdErr(color.renderOptions());
326 fatal("update '{s}': unexpected compile errors", .{update.name});348 eval.fatal("update '{s}': unexpected compile errors", .{update.name});
327 },349 },
328 }350 }
329 }351 }
...@@ -331,7 +353,7 @@ const Eval = struct {...@@ -331,7 +353,7 @@ const Eval = struct {
331 fn checkSuccessOutcome(eval: *Eval, update: Case.Update, opt_emitted_path: ?[]const u8, prog_node: std.Progress.Node) !void {353 fn checkSuccessOutcome(eval: *Eval, update: Case.Update, opt_emitted_path: ?[]const u8, prog_node: std.Progress.Node) !void {
332 switch (update.outcome) {354 switch (update.outcome) {
333 .unknown => return,355 .unknown => return,
334 .compile_errors => fatal("expected compile errors but compilation incorrectly succeeded", .{}),356 .compile_errors => eval.fatal("expected compile errors but compilation incorrectly succeeded", .{}),
335 .stdout, .exit_code => {},357 .stdout, .exit_code => {},
336 }358 }
337 const emitted_path = opt_emitted_path orelse {359 const emitted_path = opt_emitted_path orelse {
...@@ -388,7 +410,7 @@ const Eval = struct {...@@ -388,7 +410,7 @@ const Eval = struct {
388 .cwd_dir = eval.tmp_dir,410 .cwd_dir = eval.tmp_dir,
389 .cwd = eval.tmp_dir_path,411 .cwd = eval.tmp_dir_path,
390 }) catch |err| {412 }) catch |err| {
391 fatal("update '{s}': failed to run the generated executable '{s}': {s}", .{413 eval.fatal("update '{s}': failed to run the generated executable '{s}': {s}", .{
392 update.name, binary_path, @errorName(err),414 update.name, binary_path, @errorName(err),
393 });415 });
394 };416 };
...@@ -402,7 +424,7 @@ const Eval = struct {...@@ -402,7 +424,7 @@ const Eval = struct {
402 .unknown, .compile_errors => unreachable,424 .unknown, .compile_errors => unreachable,
403 .stdout => |expected_stdout| {425 .stdout => |expected_stdout| {
404 if (code != 0) {426 if (code != 0) {
405 fatal("update '{s}': generated executable '{s}' failed with code {d}", .{427 eval.fatal("update '{s}': generated executable '{s}' failed with code {d}", .{
406 update.name, binary_path, code,428 update.name, binary_path, code,
407 });429 });
408 }430 }
...@@ -411,7 +433,7 @@ const Eval = struct {...@@ -411,7 +433,7 @@ const Eval = struct {
411 .exit_code => |expected_code| try std.testing.expectEqual(expected_code, result.term.Exited),433 .exit_code => |expected_code| try std.testing.expectEqual(expected_code, result.term.Exited),
412 },434 },
413 .Signal, .Stopped, .Unknown => {435 .Signal, .Stopped, .Unknown => {
414 fatal("update '{s}': generated executable '{s}' terminated unexpectedly", .{436 eval.fatal("update '{s}': generated executable '{s}' terminated unexpectedly", .{
415 update.name, binary_path,437 update.name, binary_path,
416 });438 });
417 },439 },
...@@ -428,7 +450,7 @@ const Eval = struct {...@@ -428,7 +450,7 @@ const Eval = struct {
428 }450 }
429451
430 fn end(eval: *Eval, poller: *Poller) !void {452 fn end(eval: *Eval, poller: *Poller) !void {
431 requestExit(eval.child);453 requestExit(eval.child, eval);
432454
433 const Header = std.zig.Server.Message.Header;455 const Header = std.zig.Server.Message.Header;
434 const stdout = poller.fifo(.stdout);456 const stdout = poller.fifo(.stdout);
...@@ -448,7 +470,7 @@ const Eval = struct {...@@ -448,7 +470,7 @@ const Eval = struct {
448470
449 if (stderr.readableLength() > 0) {471 if (stderr.readableLength() > 0) {
450 const stderr_data = try stderr.toOwnedSlice();472 const stderr_data = try stderr.toOwnedSlice();
451 fatal("unexpected stderr:\n{s}", .{stderr_data});473 eval.fatal("unexpected stderr:\n{s}", .{stderr_data});
452 }474 }
453 }475 }
454476
...@@ -468,7 +490,7 @@ const Eval = struct {...@@ -468,7 +490,7 @@ const Eval = struct {
468 .cwd = eval.tmp_dir_path,490 .cwd = eval.tmp_dir_path,
469 .progress_node = child_prog_node,491 .progress_node = child_prog_node,
470 }) catch |err| {492 }) catch |err| {
471 fatal("update '{s}': failed to spawn zig cc for '{s}': {s}", .{493 eval.fatal("update '{s}': failed to spawn zig cc for '{s}': {s}", .{
472 update.name, c_path, @errorName(err),494 update.name, c_path, @errorName(err),
473 });495 });
474 };496 };
...@@ -479,7 +501,7 @@ const Eval = struct {...@@ -479,7 +501,7 @@ const Eval = struct {
479 update.name, result.stderr,501 update.name, result.stderr,
480 });502 });
481 }503 }
482 fatal("update '{s}': zig cc for '{s}' failed with code {d}", .{504 eval.fatal("update '{s}': zig cc for '{s}' failed with code {d}", .{
483 update.name, c_path, code,505 update.name, c_path, code,
484 });506 });
485 },507 },
...@@ -489,12 +511,22 @@ const Eval = struct {...@@ -489,12 +511,22 @@ const Eval = struct {
489 update.name, result.stderr,511 update.name, result.stderr,
490 });512 });
491 }513 }
492 fatal("update '{s}': zig cc for '{s}' terminated unexpectedly", .{514 eval.fatal("update '{s}': zig cc for '{s}' terminated unexpectedly", .{
493 update.name, c_path,515 update.name, c_path,
494 });516 });
495 },517 },
496 }518 }
497 }519 }
520
521 fn fatal(eval: *Eval, comptime fmt: []const u8, args: anytype) noreturn {
522 eval.tmp_dir.close();
523 if (!eval.preserve_tmp_on_fatal) {
524 std.fs.cwd().deleteTree(eval.tmp_dir_path) catch |err| {
525 std.log.warn("failed to delete tree '{s}': {s}", .{ eval.tmp_dir_path, @errorName(err) });
526 };
527 }
528 std.process.fatal(fmt, args);
529 }
498};530};
499531
500const Case = struct {532const Case = struct {
...@@ -550,6 +582,8 @@ const Case = struct {...@@ -550,6 +582,8 @@ const Case = struct {
550 };582 };
551583
552 fn parse(arena: Allocator, bytes: []const u8) !Case {584 fn parse(arena: Allocator, bytes: []const u8) !Case {
585 const fatal = std.process.fatal;
586
553 var targets: std.ArrayListUnmanaged(Target) = .empty;587 var targets: std.ArrayListUnmanaged(Target) = .empty;
554 var updates: std.ArrayListUnmanaged(Update) = .empty;588 var updates: std.ArrayListUnmanaged(Update) = .empty;
555 var changes: std.ArrayListUnmanaged(FullContents) = .empty;589 var changes: std.ArrayListUnmanaged(FullContents) = .empty;
...@@ -656,7 +690,7 @@ const Case = struct {...@@ -656,7 +690,7 @@ const Case = struct {
656 }690 }
657};691};
658692
659fn requestExit(child: *std.process.Child) void {693fn requestExit(child: *std.process.Child, eval: *Eval) void {
660 if (child.stdin == null) return;694 if (child.stdin == null) return;
661695
662 const header: std.zig.Client.Message.Header = .{696 const header: std.zig.Client.Message.Header = .{
...@@ -665,7 +699,7 @@ fn requestExit(child: *std.process.Child) void {...@@ -665,7 +699,7 @@ fn requestExit(child: *std.process.Child) void {
665 };699 };
666 child.stdin.?.writeAll(std.mem.asBytes(&header)) catch |err| switch (err) {700 child.stdin.?.writeAll(std.mem.asBytes(&header)) catch |err| switch (err) {
667 error.BrokenPipe => {},701 error.BrokenPipe => {},
668 else => fatal("failed to send exit: {s}", .{@errorName(err)}),702 else => eval.fatal("failed to send exit: {s}", .{@errorName(err)}),
669 };703 };
670704
671 // Send EOF to stdin.705 // Send EOF to stdin.
...@@ -673,11 +707,11 @@ fn requestExit(child: *std.process.Child) void {...@@ -673,11 +707,11 @@ fn requestExit(child: *std.process.Child) void {
673 child.stdin = null;707 child.stdin = null;
674}708}
675709
676fn waitChild(child: *std.process.Child) void {710fn waitChild(child: *std.process.Child, eval: *Eval) void {
677 requestExit(child);711 requestExit(child, eval);
678 const term = child.wait() catch |err| fatal("child process failed: {s}", .{@errorName(err)});712 const term = child.wait() catch |err| eval.fatal("child process failed: {s}", .{@errorName(err)});
679 switch (term) {713 switch (term) {
680 .Exited => |code| if (code != 0) fatal("compiler failed with code {d}", .{code}),714 .Exited => |code| if (code != 0) eval.fatal("compiler failed with code {d}", .{code}),
681 .Signal, .Stopped, .Unknown => fatal("compiler terminated unexpectedly", .{}),715 .Signal, .Stopped, .Unknown => eval.fatal("compiler terminated unexpectedly", .{}),
682 }716 }
683}717}