diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2024-09-26 03:58:39 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2024-10-05 20:52:25 +0100 |
| commit | 5ce962eb690aa094521b0ec5f3f466192c42318a (patch) | |
| tree | 0c2075769c964afa603bec2f16513b2e7f3ba5fd /tools | |
| parent | f60c045cef7bafdd3eac285f82d5a310daadaec5 (diff) | |
| download | zig-5ce962eb690aa094521b0ec5f3f466192c42318a.tar.gz zig-5ce962eb690aa094521b0ec5f3f466192c42318a.zip | |
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.
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/incr-check.zig | 83 |
1 files changed, 67 insertions, 16 deletions
diff --git a/tools/incr-check.zig b/tools/incr-check.zig index 0a3081d210..f33acc1e27 100644 --- a/tools/incr-check.zig +++ b/tools/incr-check.zig @@ -55,9 +55,6 @@ pub fn main() !void { const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int); const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{}); - const child_prog_node = prog_node.start("zig build-exe", 0); - defer child_prog_node.end(); - // Convert paths to be relative to the cwd of the subprocess. const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe); const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir| @@ -65,9 +62,18 @@ pub fn main() !void { else null; + const host = try std.zig.system.resolveTargetQuery(.{}); + const debug_log_verbose = debug_zcu or debug_link; for (case.targets) |target| { + const target_prog_node = node: { + var name_buf: [std.Progress.Node.max_name_len]u8 = undefined; + const name = std.fmt.bufPrint(&name_buf, "{s}-{s}", .{ target.query, @tagName(target.backend) }) catch &name_buf; + break :node prog_node.start(name, case.updates.len); + }; + defer target_prog_node.end(); + if (debug_log_verbose) { std.log.scoped(.status).info("target: '{s}-{s}'", .{ target.query, @tagName(target.backend) }); } @@ -102,11 +108,14 @@ pub fn main() !void { try child_args.appendSlice(arena, &.{ "--debug-log", "link", "--debug-log", "link_state", "--debug-log", "link_relocs" }); } + const zig_prog_node = target_prog_node.start("zig build-exe", 0); + defer zig_prog_node.end(); + var child = std.process.Child.init(child_args.items, arena); child.stdin_behavior = .Pipe; child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; - child.progress_node = child_prog_node; + child.progress_node = zig_prog_node; child.cwd_dir = tmp_dir; child.cwd = tmp_dir_path; @@ -131,6 +140,7 @@ pub fn main() !void { var eval: Eval = .{ .arena = arena, .case = case, + .host = host, .target = target, .tmp_dir = tmp_dir, .tmp_dir_path = tmp_dir_path, @@ -148,7 +158,7 @@ pub fn main() !void { defer poller.deinit(); for (case.updates) |update| { - var update_node = prog_node.start(update.name, 0); + var update_node = target_prog_node.start(update.name, 0); defer update_node.end(); if (debug_log_verbose) { @@ -168,6 +178,7 @@ pub fn main() !void { const Eval = struct { arena: Allocator, + host: std.Target, case: Case, target: Case.Target, tmp_dir: std.fs.Dir, @@ -270,14 +281,7 @@ const Eval = struct { const name = std.fs.path.stem(std.fs.path.basename(eval.case.root_source_file)); const bin_name = try std.zig.binNameAlloc(arena, .{ .root_name = name, - .target = try std.zig.system.resolveTargetQuery(try std.Build.parseTargetQuery(.{ - .arch_os_abi = eval.target.query, - .object_format = switch (eval.target.backend) { - .sema => unreachable, - .selfhosted, .llvm => null, - .cbe => "c", - }, - })), + .target = eval.target.resolved, .output_mode = .Exe, }); const bin_path = try std.fs.path.join(arena, &.{ result_dir, bin_name }); @@ -346,9 +350,41 @@ const Eval = struct { }, }; + var argv_buf: [2][]const u8 = undefined; + const argv: []const []const u8, const ignore_stderr: bool = switch (std.zig.system.getExternalExecutor( + eval.host, + &eval.target.resolved, + .{ .link_libc = eval.target.backend == .cbe }, + )) { + .bad_dl, .bad_os_or_cpu => { + // This binary cannot be executed on this host. + if (eval.allow_stderr) { + std.log.warn("skipping execution because host '{s}' cannot execute binaries for foreign target '{s}'", .{ + try eval.host.zigTriple(eval.arena), + try eval.target.resolved.zigTriple(eval.arena), + }); + } + return; + }, + .native, .rosetta => argv: { + argv_buf[0] = binary_path; + break :argv .{ argv_buf[0..1], false }; + }, + .qemu, .wine, .wasmtime, .darling => |executor_cmd| argv: { + argv_buf[0] = executor_cmd; + argv_buf[1] = binary_path; + // Some executors (looking at you, Wine) like throwing some stderr in, just for fun. + // Therefore, we'll ignore stderr when using a foreign executor. + break :argv .{ argv_buf[0..2], true }; + }, + }; + + const run_prog_node = prog_node.start("run generated executable", 0); + defer run_prog_node.end(); + const result = std.process.Child.run(.{ .allocator = eval.arena, - .argv = &.{binary_path}, + .argv = argv, .cwd_dir = eval.tmp_dir, .cwd = eval.tmp_dir_path, }) catch |err| { @@ -356,7 +392,7 @@ const Eval = struct { update.name, binary_path, @errorName(err), }); }; - if (result.stderr.len != 0) { + if (!ignore_stderr and result.stderr.len != 0) { std.log.err("update '{s}': generated executable '{s}' had unexpected stderr:\n{s}", .{ update.name, binary_path, result.stderr, }); @@ -380,7 +416,7 @@ const Eval = struct { }); }, } - if (result.stderr.len != 0) std.process.exit(1); + if (!ignore_stderr and result.stderr.len != 0) std.process.exit(1); } fn requestUpdate(eval: *Eval) !void { @@ -468,6 +504,7 @@ const Case = struct { const Target = struct { query: []const u8, + resolved: std.Target, backend: Backend, const Backend = enum { /// Run semantic analysis only. Runtime output will not be tested, but we still verify @@ -529,12 +566,26 @@ const Case = struct { } else if (std.mem.eql(u8, key, "target")) { const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse fatal("line {d}: target does not include backend", .{line_n}); + const query = val[0..split_idx]; + const backend_str = val[split_idx + 1 ..]; const backend: Target.Backend = std.meta.stringToEnum(Target.Backend, backend_str) orelse fatal("line {d}: invalid backend '{s}'", .{ line_n, backend_str }); + + const parsed_query = std.Build.parseTargetQuery(.{ + .arch_os_abi = query, + .object_format = switch (backend) { + .sema, .selfhosted, .llvm => null, + .cbe => "c", + }, + }) catch fatal("line {d}: invalid target query '{s}'", .{ line_n, query }); + + const resolved = try std.zig.system.resolveTargetQuery(parsed_query); + try targets.append(arena, .{ .query = query, + .resolved = resolved, .backend = backend, }); } else if (std.mem.eql(u8, key, "update")) { |
