| ... | @@ -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, .{}); |
| 57 | | 57 | |
| 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 | else | 62 | else |
| 66 | null; | 63 | null; |
| 67 | | 64 | |
| | 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; |
| 69 | | 68 | |
| 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 | } |
| 104 | | 110 | |
| | 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; |
| 112 | | 121 | |
| ... | @@ -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(); |
| 149 | | 159 | |
| 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(); |
| 153 | | 163 | |
| 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 { |
| 168 | | 178 | |
| 169 | const Eval = struct { | 179 | const 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 | }; |
| 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 | 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 | } |
| 385 | | 421 | |
| 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 { |
| 468 | | 504 | |
| 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 verify | 510 | /// 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, '-') orelse | 567 | 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) orelse | 573 | 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")) { |