authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-26 03:58:39+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-10-05 20:52:25+01:00
log5ce962eb690aa094521b0ec5f3f466192c42318a
tree0c2075769c964afa603bec2f16513b2e7f3ba5fd
parentf60c045cef7bafdd3eac285f82d5a310daadaec5
signaturelock-open Commit is signed but in an unrecognized format.

incr-check: better progress output, support external executors

If no external executor is available for a successful binary, its execution is silently skipped. This allows the CI to test, to the fullest extent possible, incremental cross-compilation to targets whose binaries can't be executed on the host.

1 files changed, 67 insertions(+), 16 deletions(-)

tools/incr-check.zig+67-16
...@@ -55,9 +55,6 @@ pub fn main() !void {...@@ -55,9 +55,6 @@ pub fn main() !void {
55 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);55 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);
56 const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{});56 const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{});
5757
58 const child_prog_node = prog_node.start("zig build-exe", 0);
59 defer child_prog_node.end();
60
61 // Convert paths to be relative to the cwd of the subprocess.58 // Convert paths to be relative to the cwd of the subprocess.
62 const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe);59 const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe);
63 const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir|60 const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir|
...@@ -65,9 +62,18 @@ pub fn main() !void {...@@ -65,9 +62,18 @@ pub fn main() !void {
65 else62 else
66 null;63 null;
6764
65 const host = try std.zig.system.resolveTargetQuery(.{});
66
68 const debug_log_verbose = debug_zcu or debug_link;67 const debug_log_verbose = debug_zcu or debug_link;
6968
70 for (case.targets) |target| {69 for (case.targets) |target| {
70 const target_prog_node = node: {
71 var name_buf: [std.Progress.Node.max_name_len]u8 = undefined;
72 const name = std.fmt.bufPrint(&name_buf, "{s}-{s}", .{ target.query, @tagName(target.backend) }) catch &name_buf;
73 break :node prog_node.start(name, case.updates.len);
74 };
75 defer target_prog_node.end();
76
71 if (debug_log_verbose) {77 if (debug_log_verbose) {
72 std.log.scoped(.status).info("target: '{s}-{s}'", .{ target.query, @tagName(target.backend) });78 std.log.scoped(.status).info("target: '{s}-{s}'", .{ target.query, @tagName(target.backend) });
73 }79 }
...@@ -102,11 +108,14 @@ pub fn main() !void {...@@ -102,11 +108,14 @@ pub fn main() !void {
102 try child_args.appendSlice(arena, &.{ "--debug-log", "link", "--debug-log", "link_state", "--debug-log", "link_relocs" });108 try child_args.appendSlice(arena, &.{ "--debug-log", "link", "--debug-log", "link_state", "--debug-log", "link_relocs" });
103 }109 }
104110
111 const zig_prog_node = target_prog_node.start("zig build-exe", 0);
112 defer zig_prog_node.end();
113
105 var child = std.process.Child.init(child_args.items, arena);114 var child = std.process.Child.init(child_args.items, arena);
106 child.stdin_behavior = .Pipe;115 child.stdin_behavior = .Pipe;
107 child.stdout_behavior = .Pipe;116 child.stdout_behavior = .Pipe;
108 child.stderr_behavior = .Pipe;117 child.stderr_behavior = .Pipe;
109 child.progress_node = child_prog_node;118 child.progress_node = zig_prog_node;
110 child.cwd_dir = tmp_dir;119 child.cwd_dir = tmp_dir;
111 child.cwd = tmp_dir_path;120 child.cwd = tmp_dir_path;
112121
...@@ -131,6 +140,7 @@ pub fn main() !void {...@@ -131,6 +140,7 @@ pub fn main() !void {
131 var eval: Eval = .{140 var eval: Eval = .{
132 .arena = arena,141 .arena = arena,
133 .case = case,142 .case = case,
143 .host = host,
134 .target = target,144 .target = target,
135 .tmp_dir = tmp_dir,145 .tmp_dir = tmp_dir,
136 .tmp_dir_path = tmp_dir_path,146 .tmp_dir_path = tmp_dir_path,
...@@ -148,7 +158,7 @@ pub fn main() !void {...@@ -148,7 +158,7 @@ pub fn main() !void {
148 defer poller.deinit();158 defer poller.deinit();
149159
150 for (case.updates) |update| {160 for (case.updates) |update| {
151 var update_node = prog_node.start(update.name, 0);161 var update_node = target_prog_node.start(update.name, 0);
152 defer update_node.end();162 defer update_node.end();
153163
154 if (debug_log_verbose) {164 if (debug_log_verbose) {
...@@ -168,6 +178,7 @@ pub fn main() !void {...@@ -168,6 +178,7 @@ pub fn main() !void {
168178
169const Eval = struct {179const Eval = struct {
170 arena: Allocator,180 arena: Allocator,
181 host: std.Target,
171 case: Case,182 case: Case,
172 target: Case.Target,183 target: Case.Target,
173 tmp_dir: std.fs.Dir,184 tmp_dir: std.fs.Dir,
...@@ -270,14 +281,7 @@ const Eval = struct {...@@ -270,14 +281,7 @@ const Eval = struct {
270 const name = std.fs.path.stem(std.fs.path.basename(eval.case.root_source_file));281 const name = std.fs.path.stem(std.fs.path.basename(eval.case.root_source_file));
271 const bin_name = try std.zig.binNameAlloc(arena, .{282 const bin_name = try std.zig.binNameAlloc(arena, .{
272 .root_name = name,283 .root_name = name,
273 .target = try std.zig.system.resolveTargetQuery(try std.Build.parseTargetQuery(.{284 .target = eval.target.resolved,
274 .arch_os_abi = eval.target.query,
275 .object_format = switch (eval.target.backend) {
276 .sema => unreachable,
277 .selfhosted, .llvm => null,
278 .cbe => "c",
279 },
280 })),
281 .output_mode = .Exe,285 .output_mode = .Exe,
282 });286 });
283 const bin_path = try std.fs.path.join(arena, &.{ result_dir, bin_name });287 const bin_path = try std.fs.path.join(arena, &.{ result_dir, bin_name });
...@@ -346,9 +350,41 @@ const Eval = struct {...@@ -346,9 +350,41 @@ const Eval = struct {
346 },350 },
347 };351 };
348352
353 var argv_buf: [2][]const u8 = undefined;
354 const argv: []const []const u8, const ignore_stderr: bool = switch (std.zig.system.getExternalExecutor(
355 eval.host,
356 &eval.target.resolved,
357 .{ .link_libc = eval.target.backend == .cbe },
358 )) {
359 .bad_dl, .bad_os_or_cpu => {
360 // This binary cannot be executed on this host.
361 if (eval.allow_stderr) {
362 std.log.warn("skipping execution because host '{s}' cannot execute binaries for foreign target '{s}'", .{
363 try eval.host.zigTriple(eval.arena),
364 try eval.target.resolved.zigTriple(eval.arena),
365 });
366 }
367 return;
368 },
369 .native, .rosetta => argv: {
370 argv_buf[0] = binary_path;
371 break :argv .{ argv_buf[0..1], false };
372 },
373 .qemu, .wine, .wasmtime, .darling => |executor_cmd| argv: {
374 argv_buf[0] = executor_cmd;
375 argv_buf[1] = binary_path;
376 // Some executors (looking at you, Wine) like throwing some stderr in, just for fun.
377 // Therefore, we'll ignore stderr when using a foreign executor.
378 break :argv .{ argv_buf[0..2], true };
379 },
380 };
381
382 const run_prog_node = prog_node.start("run generated executable", 0);
383 defer run_prog_node.end();
384
349 const result = std.process.Child.run(.{385 const result = std.process.Child.run(.{
350 .allocator = eval.arena,386 .allocator = eval.arena,
351 .argv = &.{binary_path},387 .argv = argv,
352 .cwd_dir = eval.tmp_dir,388 .cwd_dir = eval.tmp_dir,
353 .cwd = eval.tmp_dir_path,389 .cwd = eval.tmp_dir_path,
354 }) catch |err| {390 }) catch |err| {
...@@ -356,7 +392,7 @@ const Eval = struct {...@@ -356,7 +392,7 @@ const Eval = struct {
356 update.name, binary_path, @errorName(err),392 update.name, binary_path, @errorName(err),
357 });393 });
358 };394 };
359 if (result.stderr.len != 0) {395 if (!ignore_stderr and result.stderr.len != 0) {
360 std.log.err("update '{s}': generated executable '{s}' had unexpected stderr:\n{s}", .{396 std.log.err("update '{s}': generated executable '{s}' had unexpected stderr:\n{s}", .{
361 update.name, binary_path, result.stderr,397 update.name, binary_path, result.stderr,
362 });398 });
...@@ -380,7 +416,7 @@ const Eval = struct {...@@ -380,7 +416,7 @@ const Eval = struct {
380 });416 });
381 },417 },
382 }418 }
383 if (result.stderr.len != 0) std.process.exit(1);419 if (!ignore_stderr and result.stderr.len != 0) std.process.exit(1);
384 }420 }
385421
386 fn requestUpdate(eval: *Eval) !void {422 fn requestUpdate(eval: *Eval) !void {
...@@ -468,6 +504,7 @@ const Case = struct {...@@ -468,6 +504,7 @@ const Case = struct {
468504
469 const Target = struct {505 const Target = struct {
470 query: []const u8,506 query: []const u8,
507 resolved: std.Target,
471 backend: Backend,508 backend: Backend,
472 const Backend = enum {509 const Backend = enum {
473 /// Run semantic analysis only. Runtime output will not be tested, but we still verify510 /// Run semantic analysis only. Runtime output will not be tested, but we still verify
...@@ -529,12 +566,26 @@ const Case = struct {...@@ -529,12 +566,26 @@ const Case = struct {
529 } else if (std.mem.eql(u8, key, "target")) {566 } else if (std.mem.eql(u8, key, "target")) {
530 const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse567 const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse
531 fatal("line {d}: target does not include backend", .{line_n});568 fatal("line {d}: target does not include backend", .{line_n});
569
532 const query = val[0..split_idx];570 const query = val[0..split_idx];
571
533 const backend_str = val[split_idx + 1 ..];572 const backend_str = val[split_idx + 1 ..];
534 const backend: Target.Backend = std.meta.stringToEnum(Target.Backend, backend_str) orelse573 const backend: Target.Backend = std.meta.stringToEnum(Target.Backend, backend_str) orelse
535 fatal("line {d}: invalid backend '{s}'", .{ line_n, backend_str });574 fatal("line {d}: invalid backend '{s}'", .{ line_n, backend_str });
575
576 const parsed_query = std.Build.parseTargetQuery(.{
577 .arch_os_abi = query,
578 .object_format = switch (backend) {
579 .sema, .selfhosted, .llvm => null,
580 .cbe => "c",
581 },
582 }) catch fatal("line {d}: invalid target query '{s}'", .{ line_n, query });
583
584 const resolved = try std.zig.system.resolveTargetQuery(parsed_query);
585
536 try targets.append(arena, .{586 try targets.append(arena, .{
537 .query = query,587 .query = query,
588 .resolved = resolved,
538 .backend = backend,589 .backend = backend,
539 });590 });
540 } else if (std.mem.eql(u8, key, "update")) {591 } else if (std.mem.eql(u8, key, "update")) {