| ... | ... | @@ -55,9 +55,6 @@ pub fn main() !void { |
| 55 | 55 | const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int); |
| 56 | 56 | const tmp_dir = try std.fs.cwd().makeOpenPath(tmp_dir_path, .{}); |
| 57 | 57 | |
| 58 | | const child_prog_node = prog_node.start("zig build-exe", 0); |
| 59 | | defer child_prog_node.end(); |
| 60 | | |
| 61 | 58 | // Convert paths to be relative to the cwd of the subprocess. |
| 62 | 59 | const resolved_zig_exe = try std.fs.path.relative(arena, tmp_dir_path, zig_exe); |
| 63 | 60 | const opt_resolved_lib_dir = if (opt_lib_dir) |lib_dir| |
| ... | ... | @@ -65,9 +62,18 @@ pub fn main() !void { |
| 65 | 62 | else |
| 66 | 63 | null; |
| 67 | 64 | |
| 65 | const host = try std.zig.system.resolveTargetQuery(.{}); |
| 66 | |
| 68 | 67 | const debug_log_verbose = debug_zcu or debug_link; |
| 69 | 68 | |
| 70 | 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 | 77 | if (debug_log_verbose) { |
| 72 | 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 | 108 | try child_args.appendSlice(arena, &.{ "--debug-log", "link", "--debug-log", "link_state", "--debug-log", "link_relocs" }); |
| 103 | 109 | } |
| 104 | 110 | |
| 111 | const zig_prog_node = target_prog_node.start("zig build-exe", 0); |
| 112 | defer zig_prog_node.end(); |
| 113 | |
| 105 | 114 | var child = std.process.Child.init(child_args.items, arena); |
| 106 | 115 | child.stdin_behavior = .Pipe; |
| 107 | 116 | child.stdout_behavior = .Pipe; |
| 108 | 117 | child.stderr_behavior = .Pipe; |
| 109 | | child.progress_node = child_prog_node; |
| 118 | child.progress_node = zig_prog_node; |
| 110 | 119 | child.cwd_dir = tmp_dir; |
| 111 | 120 | child.cwd = tmp_dir_path; |
| 112 | 121 | |
| ... | ... | @@ -131,6 +140,7 @@ pub fn main() !void { |
| 131 | 140 | var eval: Eval = .{ |
| 132 | 141 | .arena = arena, |
| 133 | 142 | .case = case, |
| 143 | .host = host, |
| 134 | 144 | .target = target, |
| 135 | 145 | .tmp_dir = tmp_dir, |
| 136 | 146 | .tmp_dir_path = tmp_dir_path, |
| ... | ... | @@ -148,7 +158,7 @@ pub fn main() !void { |
| 148 | 158 | defer poller.deinit(); |
| 149 | 159 | |
| 150 | 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 | 162 | defer update_node.end(); |
| 153 | 163 | |
| 154 | 164 | if (debug_log_verbose) { |
| ... | ... | @@ -168,6 +178,7 @@ pub fn main() !void { |
| 168 | 178 | |
| 169 | 179 | const Eval = struct { |
| 170 | 180 | arena: Allocator, |
| 181 | host: std.Target, |
| 171 | 182 | case: Case, |
| 172 | 183 | target: Case.Target, |
| 173 | 184 | tmp_dir: std.fs.Dir, |
| ... | ... | @@ -270,14 +281,7 @@ const Eval = struct { |
| 270 | 281 | const name = std.fs.path.stem(std.fs.path.basename(eval.case.root_source_file)); |
| 271 | 282 | const bin_name = try std.zig.binNameAlloc(arena, .{ |
| 272 | 283 | .root_name = name, |
| 273 | | .target = try std.zig.system.resolveTargetQuery(try std.Build.parseTargetQuery(.{ |
| 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 | | })), |
| 284 | .target = eval.target.resolved, |
| 281 | 285 | .output_mode = .Exe, |
| 282 | 286 | }); |
| 283 | 287 | const bin_path = try std.fs.path.join(arena, &.{ result_dir, bin_name }); |
| ... | ... | @@ -346,9 +350,41 @@ const Eval = struct { |
| 346 | 350 | }, |
| 347 | 351 | }; |
| 348 | 352 | |
| 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 | 385 | const result = std.process.Child.run(.{ |
| 350 | 386 | .allocator = eval.arena, |
| 351 | | .argv = &.{binary_path}, |
| 387 | .argv = argv, |
| 352 | 388 | .cwd_dir = eval.tmp_dir, |
| 353 | 389 | .cwd = eval.tmp_dir_path, |
| 354 | 390 | }) catch |err| { |
| ... | ... | @@ -356,7 +392,7 @@ const Eval = struct { |
| 356 | 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 | 396 | std.log.err("update '{s}': generated executable '{s}' had unexpected stderr:\n{s}", .{ |
| 361 | 397 | update.name, binary_path, result.stderr, |
| 362 | 398 | }); |
| ... | ... | @@ -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 | } |
| 385 | 421 | |
| 386 | 422 | fn requestUpdate(eval: *Eval) !void { |
| ... | ... | @@ -468,6 +504,7 @@ const Case = struct { |
| 468 | 504 | |
| 469 | 505 | const Target = struct { |
| 470 | 506 | query: []const u8, |
| 507 | resolved: std.Target, |
| 471 | 508 | backend: Backend, |
| 472 | 509 | const Backend = enum { |
| 473 | 510 | /// Run semantic analysis only. Runtime output will not be tested, but we still verify |
| ... | ... | @@ -529,12 +566,26 @@ const Case = struct { |
| 529 | 566 | } else if (std.mem.eql(u8, key, "target")) { |
| 530 | 567 | const split_idx = std.mem.lastIndexOfScalar(u8, val, '-') orelse |
| 531 | 568 | fatal("line {d}: target does not include backend", .{line_n}); |
| 569 | |
| 532 | 570 | const query = val[0..split_idx]; |
| 571 | |
| 533 | 572 | const backend_str = val[split_idx + 1 ..]; |
| 534 | 573 | const backend: Target.Backend = std.meta.stringToEnum(Target.Backend, backend_str) orelse |
| 535 | 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 | 586 | try targets.append(arena, .{ |
| 537 | 587 | .query = query, |
| 588 | .resolved = resolved, |
| 538 | 589 | .backend = backend, |
| 539 | 590 | }); |
| 540 | 591 | } else if (std.mem.eql(u8, key, "update")) { |