mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 22:35:24 +00:00
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.
This commit is contained in:
parent
f60c045cef
commit
5ce962eb69
@ -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")) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user