| author | |
| committer | |
| log | ede5dcffea5a3a5fc9fd14e4e180464633402fae |
| tree | dcf88812197be81cdb36b747d2b2b55c56d2192b |
| parent | ef5f8bd7c62f929b5cc210caa816ce4a8c8f8538 |
std.Build.addTest creates a CompileStep as before, however, this kind of
step no longer actually runs the unit tests. Instead it only compiles
it, and one must additionally create a RunStep from the CompileStep in
order to actually run the tests.
RunStep gains integration with the default test runner, which now
supports the standard --listen=- argument in order to communicate over
stdin and stdout. It also reports test statistics; how many passed,
failed, and leaked, as well as directly associating the relevant stderr
with the particular test name that failed.
This separation of CompileStep and RunStep means that
`CompileStep.Kind.test_exe` is no longer needed, and therefore has been
removed in this commit.
* build runner: show unit test statistics in build summary
* added Step.writeManifest since many steps want to treat it as a
warning and emit the same message if it fails.
* RunStep: fixed error message that prints the failed command printing
the original argv and not the adjusted argv in case an interpreter
was used.
* RunStep: fixed not passing the command line arguments to the
interpreter.
* move src/Server.zig to std.zig.Server so that the default test runner
can use it.
* the simpler test runner function which is used by work-in-progress
backends now no longer prints to stderr, which is necessary in order
for the build runner to not print the stderr as a warning message.30 files changed, 780 insertions(+), 373 deletions(-)
CMakeLists.txt+1-1| ... | ... | @@ -518,6 +518,7 @@ set(ZIG_STAGE2_SOURCES |
| 518 | 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" |
| 519 | 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" |
| 520 | 520 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" |
| 521 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig" | |
| 521 | 522 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" |
| 522 | 523 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" |
| 523 | 524 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" |
| ... | ... | @@ -623,7 +624,6 @@ set(ZIG_STAGE2_SOURCES |
| 623 | 624 | "${CMAKE_SOURCE_DIR}/src/print_targets.zig" |
| 624 | 625 | "${CMAKE_SOURCE_DIR}/src/print_zir.zig" |
| 625 | 626 | "${CMAKE_SOURCE_DIR}/src/register_manager.zig" |
| 626 | "${CMAKE_SOURCE_DIR}/src/Server.zig" | |
| 627 | 627 | "${CMAKE_SOURCE_DIR}/src/target.zig" |
| 628 | 628 | "${CMAKE_SOURCE_DIR}/src/tracy.zig" |
| 629 | 629 | "${CMAKE_SOURCE_DIR}/src/translate_c.zig" |
lib/build_runner.zig+57-2| ... | ... | @@ -416,6 +416,12 @@ fn runStepNames( |
| 416 | 416 | } |
| 417 | 417 | assert(run.memory_blocked_steps.items.len == 0); |
| 418 | 418 | |
| 419 | var test_skip_count: usize = 0; | |
| 420 | var test_fail_count: usize = 0; | |
| 421 | var test_pass_count: usize = 0; | |
| 422 | var test_leak_count: usize = 0; | |
| 423 | var test_count: usize = 0; | |
| 424 | ||
| 419 | 425 | var success_count: usize = 0; |
| 420 | 426 | var skipped_count: usize = 0; |
| 421 | 427 | var failure_count: usize = 0; |
| ... | ... | @@ -425,6 +431,12 @@ fn runStepNames( |
| 425 | 431 | defer compile_error_steps.deinit(gpa); |
| 426 | 432 | |
| 427 | 433 | for (step_stack.keys()) |s| { |
| 434 | test_fail_count += s.test_results.fail_count; | |
| 435 | test_skip_count += s.test_results.skip_count; | |
| 436 | test_leak_count += s.test_results.leak_count; | |
| 437 | test_pass_count += s.test_results.passCount(); | |
| 438 | test_count += s.test_results.test_count; | |
| 439 | ||
| 428 | 440 | switch (s.state) { |
| 429 | 441 | .precheck_unstarted => unreachable, |
| 430 | 442 | .precheck_started => unreachable, |
| ... | ... | @@ -468,6 +480,11 @@ fn runStepNames( |
| 468 | 480 | if (skipped_count > 0) stderr.writer().print("; {d} skipped", .{skipped_count}) catch {}; |
| 469 | 481 | if (failure_count > 0) stderr.writer().print("; {d} failed", .{failure_count}) catch {}; |
| 470 | 482 | |
| 483 | if (test_count > 0) stderr.writer().print("; {d}/{d} tests passed", .{ test_pass_count, test_count }) catch {}; | |
| 484 | if (test_skip_count > 0) stderr.writer().print("; {d} skipped", .{test_skip_count}) catch {}; | |
| 485 | if (test_fail_count > 0) stderr.writer().print("; {d} failed", .{test_fail_count}) catch {}; | |
| 486 | if (test_leak_count > 0) stderr.writer().print("; {d} leaked", .{test_leak_count}) catch {}; | |
| 487 | ||
| 471 | 488 | if (run.enable_summary == null) { |
| 472 | 489 | ttyconf.setColor(stderr, .Dim) catch {}; |
| 473 | 490 | stderr.writeAll(" (disable with -fno-summary)") catch {}; |
| ... | ... | @@ -566,6 +583,13 @@ fn printTreeStep( |
| 566 | 583 | try ttyconf.setColor(stderr, .Green); |
| 567 | 584 | if (s.result_cached) { |
| 568 | 585 | try stderr.writeAll(" cached"); |
| 586 | } else if (s.test_results.test_count > 0) { | |
| 587 | const pass_count = s.test_results.passCount(); | |
| 588 | try stderr.writer().print(" {d} passed", .{pass_count}); | |
| 589 | if (s.test_results.skip_count > 0) { | |
| 590 | try ttyconf.setColor(stderr, .Yellow); | |
| 591 | try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count}); | |
| 592 | } | |
| 569 | 593 | } else { |
| 570 | 594 | try stderr.writeAll(" success"); |
| 571 | 595 | } |
| ... | ... | @@ -609,15 +633,46 @@ fn printTreeStep( |
| 609 | 633 | }, |
| 610 | 634 | |
| 611 | 635 | .failure => { |
| 612 | try ttyconf.setColor(stderr, .Red); | |
| 613 | 636 | if (s.result_error_bundle.errorMessageCount() > 0) { |
| 637 | try ttyconf.setColor(stderr, .Red); | |
| 614 | 638 | try stderr.writer().print(" {d} errors\n", .{ |
| 615 | 639 | s.result_error_bundle.errorMessageCount(), |
| 616 | 640 | }); |
| 641 | try ttyconf.setColor(stderr, .Reset); | |
| 642 | } else if (!s.test_results.isSuccess()) { | |
| 643 | try stderr.writer().print(" {d}/{d} passed", .{ | |
| 644 | s.test_results.passCount(), s.test_results.test_count, | |
| 645 | }); | |
| 646 | if (s.test_results.fail_count > 0) { | |
| 647 | try stderr.writeAll(", "); | |
| 648 | try ttyconf.setColor(stderr, .Red); | |
| 649 | try stderr.writer().print("{d} failed", .{ | |
| 650 | s.test_results.fail_count, | |
| 651 | }); | |
| 652 | try ttyconf.setColor(stderr, .Reset); | |
| 653 | } | |
| 654 | if (s.test_results.skip_count > 0) { | |
| 655 | try stderr.writeAll(", "); | |
| 656 | try ttyconf.setColor(stderr, .Yellow); | |
| 657 | try stderr.writer().print("{d} skipped", .{ | |
| 658 | s.test_results.skip_count, | |
| 659 | }); | |
| 660 | try ttyconf.setColor(stderr, .Reset); | |
| 661 | } | |
| 662 | if (s.test_results.leak_count > 0) { | |
| 663 | try stderr.writeAll(", "); | |
| 664 | try ttyconf.setColor(stderr, .Red); | |
| 665 | try stderr.writer().print("{d} leaked", .{ | |
| 666 | s.test_results.leak_count, | |
| 667 | }); | |
| 668 | try ttyconf.setColor(stderr, .Reset); | |
| 669 | } | |
| 670 | try stderr.writeAll("\n"); | |
| 617 | 671 | } else { |
| 672 | try ttyconf.setColor(stderr, .Red); | |
| 618 | 673 | try stderr.writeAll(" failure\n"); |
| 674 | try ttyconf.setColor(stderr, .Reset); | |
| 619 | 675 | } |
| 620 | try ttyconf.setColor(stderr, .Reset); | |
| 621 | 676 | }, |
| 622 | 677 | } |
| 623 | 678 |
lib/std/Build.zig+4-6| ... | ... | @@ -531,7 +531,6 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { |
| 531 | 531 | |
| 532 | 532 | pub const TestOptions = struct { |
| 533 | 533 | name: []const u8 = "test", |
| 534 | kind: CompileStep.Kind = .@"test", | |
| 535 | 534 | root_source_file: FileSource, |
| 536 | 535 | target: CrossTarget = .{}, |
| 537 | 536 | optimize: std.builtin.Mode = .Debug, |
| ... | ... | @@ -542,7 +541,7 @@ pub const TestOptions = struct { |
| 542 | 541 | pub fn addTest(b: *Build, options: TestOptions) *CompileStep { |
| 543 | 542 | return CompileStep.create(b, .{ |
| 544 | 543 | .name = options.name, |
| 545 | .kind = options.kind, | |
| 544 | .kind = .@"test", | |
| 546 | 545 | .root_source_file = options.root_source_file, |
| 547 | 546 | .target = options.target, |
| 548 | 547 | .optimize = options.optimize, |
| ... | ... | @@ -626,16 +625,15 @@ pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { |
| 626 | 625 | /// Creates a `RunStep` with an executable built with `addExecutable`. |
| 627 | 626 | /// Add command line arguments with methods of `RunStep`. |
| 628 | 627 | pub fn addRunArtifact(b: *Build, exe: *CompileStep) *RunStep { |
| 629 | assert(exe.kind == .exe or exe.kind == .test_exe); | |
| 630 | ||
| 631 | 628 | // It doesn't have to be native. We catch that if you actually try to run it. |
| 632 | 629 | // Consider that this is declarative; the run step may not be run unless a user |
| 633 | 630 | // option is supplied. |
| 634 | 631 | const run_step = RunStep.create(b, b.fmt("run {s}", .{exe.name})); |
| 635 | 632 | run_step.addArtifactArg(exe); |
| 636 | 633 | |
| 637 | if (exe.kind == .test_exe) { | |
| 638 | run_step.addArg(b.zig_exe); | |
| 634 | if (exe.kind == .@"test") { | |
| 635 | run_step.stdio = .zig_test; | |
| 636 | run_step.addArgs(&.{"--listen=-"}); | |
| 639 | 637 | } |
| 640 | 638 | |
| 641 | 639 | if (exe.vcpkg_bin_path) |path| { |
lib/std/Build/CompileStep.zig+7-86| ... | ... | @@ -289,7 +289,6 @@ pub const Kind = enum { |
| 289 | 289 | lib, |
| 290 | 290 | obj, |
| 291 | 291 | @"test", |
| 292 | test_exe, | |
| 293 | 292 | }; |
| 294 | 293 | |
| 295 | 294 | pub const Linkage = enum { dynamic, static }; |
| ... | ... | @@ -328,7 +327,7 @@ pub fn create(owner: *std.Build, options: Options) *CompileStep { |
| 328 | 327 | .exe => "zig build-exe", |
| 329 | 328 | .lib => "zig build-lib", |
| 330 | 329 | .obj => "zig build-obj", |
| 331 | .test_exe, .@"test" => "zig test", | |
| 330 | .@"test" => "zig test", | |
| 332 | 331 | }, |
| 333 | 332 | name_adjusted, |
| 334 | 333 | @tagName(options.optimize), |
| ... | ... | @@ -410,7 +409,7 @@ fn computeOutFileNames(self: *CompileStep) void { |
| 410 | 409 | .output_mode = switch (self.kind) { |
| 411 | 410 | .lib => .Lib, |
| 412 | 411 | .obj => .Obj, |
| 413 | .exe, .@"test", .test_exe => .Exe, | |
| 412 | .exe, .@"test" => .Exe, | |
| 414 | 413 | }, |
| 415 | 414 | .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { |
| 416 | 415 | .dynamic => .Dynamic, |
| ... | ... | @@ -621,7 +620,7 @@ pub fn producesPdbFile(self: *CompileStep) bool { |
| 621 | 620 | if (!self.target.isWindows() and !self.target.isUefi()) return false; |
| 622 | 621 | if (self.target.getObjectFormat() == .c) return false; |
| 623 | 622 | if (self.strip == true) return false; |
| 624 | return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; | |
| 623 | return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test"; | |
| 625 | 624 | } |
| 626 | 625 | |
| 627 | 626 | pub fn linkLibC(self: *CompileStep) void { |
| ... | ... | @@ -850,19 +849,19 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { |
| 850 | 849 | |
| 851 | 850 | pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { |
| 852 | 851 | const b = self.step.owner; |
| 853 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 852 | assert(self.kind == .@"test"); | |
| 854 | 853 | self.name_prefix = b.dupe(text); |
| 855 | 854 | } |
| 856 | 855 | |
| 857 | 856 | pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { |
| 858 | 857 | const b = self.step.owner; |
| 859 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 858 | assert(self.kind == .@"test"); | |
| 860 | 859 | self.filter = if (text) |t| b.dupe(t) else null; |
| 861 | 860 | } |
| 862 | 861 | |
| 863 | 862 | pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { |
| 864 | 863 | const b = self.step.owner; |
| 865 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 864 | assert(self.kind == .@"test"); | |
| 866 | 865 | self.test_runner = if (path) |p| b.dupePath(p) else null; |
| 867 | 866 | } |
| 868 | 867 | |
| ... | ... | @@ -938,7 +937,7 @@ pub fn getOutputLibSource(self: *CompileStep) FileSource { |
| 938 | 937 | /// Returns the generated header file. |
| 939 | 938 | /// This function can only be called for libraries or object files which have `emit_h` set. |
| 940 | 939 | pub fn getOutputHSource(self: *CompileStep) FileSource { |
| 941 | assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); | |
| 940 | assert(self.kind != .exe and self.kind != .@"test"); | |
| 942 | 941 | assert(self.emit_h); |
| 943 | 942 | return .{ .generated = &self.output_h_path_source }; |
| 944 | 943 | } |
| ... | ... | @@ -1243,7 +1242,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 1243 | 1242 | .exe => "build-exe", |
| 1244 | 1243 | .obj => "build-obj", |
| 1245 | 1244 | .@"test" => "test", |
| 1246 | .test_exe => "test", | |
| 1247 | 1245 | }; |
| 1248 | 1246 | try zig_args.append(cmd); |
| 1249 | 1247 | |
| ... | ... | @@ -1293,7 +1291,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 1293 | 1291 | |
| 1294 | 1292 | .other_step => |other| switch (other.kind) { |
| 1295 | 1293 | .exe => @panic("Cannot link with an executable build artifact"), |
| 1296 | .test_exe => @panic("Cannot link with an executable build artifact"), | |
| 1297 | 1294 | .@"test" => @panic("Cannot link with a test"), |
| 1298 | 1295 | .obj => { |
| 1299 | 1296 | try zig_args.append(other.getOutputSource().getPath(b)); |
| ... | ... | @@ -1661,83 +1658,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 1661 | 1658 | try zig_args.append("--test-cmd-bin"); |
| 1662 | 1659 | } |
| 1663 | 1660 | } |
| 1664 | } else { | |
| 1665 | const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; | |
| 1666 | ||
| 1667 | switch (b.host.getExternalExecutor(self.target_info, .{ | |
| 1668 | .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, | |
| 1669 | .link_libc = transitive_deps.is_linking_libc, | |
| 1670 | })) { | |
| 1671 | .native => {}, | |
| 1672 | .bad_dl, .bad_os_or_cpu => { | |
| 1673 | try zig_args.append("--test-no-exec"); | |
| 1674 | }, | |
| 1675 | .rosetta => if (b.enable_rosetta) { | |
| 1676 | try zig_args.append("--test-cmd-bin"); | |
| 1677 | } else { | |
| 1678 | try zig_args.append("--test-no-exec"); | |
| 1679 | }, | |
| 1680 | .qemu => |bin_name| ok: { | |
| 1681 | if (b.enable_qemu) qemu: { | |
| 1682 | const glibc_dir_arg = if (need_cross_glibc) | |
| 1683 | b.glibc_runtimes_dir orelse break :qemu | |
| 1684 | else | |
| 1685 | null; | |
| 1686 | try zig_args.append("--test-cmd"); | |
| 1687 | try zig_args.append(bin_name); | |
| 1688 | if (glibc_dir_arg) |dir| { | |
| 1689 | // TODO look into making this a call to `linuxTriple`. This | |
| 1690 | // needs the directory to be called "i686" rather than | |
| 1691 | // "x86" which is why we do it manually here. | |
| 1692 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | |
| 1693 | const cpu_arch = self.target.getCpuArch(); | |
| 1694 | const os_tag = self.target.getOsTag(); | |
| 1695 | const abi = self.target.getAbi(); | |
| 1696 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | |
| 1697 | "i686" | |
| 1698 | else | |
| 1699 | @tagName(cpu_arch); | |
| 1700 | const full_dir = try std.fmt.allocPrint(b.allocator, fmt_str, .{ | |
| 1701 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | |
| 1702 | }); | |
| 1703 | ||
| 1704 | try zig_args.append("--test-cmd"); | |
| 1705 | try zig_args.append("-L"); | |
| 1706 | try zig_args.append("--test-cmd"); | |
| 1707 | try zig_args.append(full_dir); | |
| 1708 | } | |
| 1709 | try zig_args.append("--test-cmd-bin"); | |
| 1710 | break :ok; | |
| 1711 | } | |
| 1712 | try zig_args.append("--test-no-exec"); | |
| 1713 | }, | |
| 1714 | .wine => |bin_name| if (b.enable_wine) { | |
| 1715 | try zig_args.append("--test-cmd"); | |
| 1716 | try zig_args.append(bin_name); | |
| 1717 | try zig_args.append("--test-cmd-bin"); | |
| 1718 | } else { | |
| 1719 | try zig_args.append("--test-no-exec"); | |
| 1720 | }, | |
| 1721 | .wasmtime => |bin_name| if (b.enable_wasmtime) { | |
| 1722 | try zig_args.append("--test-cmd"); | |
| 1723 | try zig_args.append(bin_name); | |
| 1724 | try zig_args.append("--test-cmd"); | |
| 1725 | try zig_args.append("--dir=."); | |
| 1726 | try zig_args.append("--test-cmd-bin"); | |
| 1727 | } else { | |
| 1728 | try zig_args.append("--test-no-exec"); | |
| 1729 | }, | |
| 1730 | .darling => |bin_name| if (b.enable_darling) { | |
| 1731 | try zig_args.append("--test-cmd"); | |
| 1732 | try zig_args.append(bin_name); | |
| 1733 | try zig_args.append("--test-cmd-bin"); | |
| 1734 | } else { | |
| 1735 | try zig_args.append("--test-no-exec"); | |
| 1736 | }, | |
| 1737 | } | |
| 1738 | 1661 | } |
| 1739 | } else if (self.kind == .test_exe) { | |
| 1740 | try zig_args.append("--test-no-exec"); | |
| 1741 | 1662 | } |
| 1742 | 1663 | |
| 1743 | 1664 | try self.appendModuleArgs(&zig_args); |
lib/std/Build/InstallArtifactStep.zig+2-3| ... | ... | @@ -32,12 +32,11 @@ pub fn create(owner: *std.Build, artifact: *CompileStep) *InstallArtifactStep { |
| 32 | 32 | .artifact = artifact, |
| 33 | 33 | .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { |
| 34 | 34 | .obj => @panic("Cannot install a .obj build artifact."), |
| 35 | .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."), | |
| 36 | .exe, .test_exe => InstallDir{ .bin = {} }, | |
| 35 | .exe, .@"test" => InstallDir{ .bin = {} }, | |
| 37 | 36 | .lib => InstallDir{ .lib = {} }, |
| 38 | 37 | }, |
| 39 | 38 | .pdb_dir = if (artifact.producesPdbFile()) blk: { |
| 40 | if (artifact.kind == .exe or artifact.kind == .test_exe) { | |
| 39 | if (artifact.kind == .exe or artifact.kind == .@"test") { | |
| 41 | 40 | break :blk InstallDir{ .bin = {} }; |
| 42 | 41 | } else { |
| 43 | 42 | break :blk InstallDir{ .lib = {} }; |
lib/std/Build/RunStep.zig+296-59| ... | ... | @@ -92,6 +92,9 @@ pub const StdIo = union(enum) { |
| 92 | 92 | /// Note that an explicit check for exit code 0 needs to be added to this |
| 93 | 93 | /// list if such a check is desireable. |
| 94 | 94 | check: std.ArrayList(Check), |
| 95 | /// This RunStep is running a zig unit test binary and will communicate | |
| 96 | /// extra metadata over the IPC protocol. | |
| 97 | zig_test, | |
| 95 | 98 | |
| 96 | 99 | pub const Check = union(enum) { |
| 97 | 100 | expect_stderr_exact: []const u8, |
| ... | ... | @@ -324,6 +327,7 @@ fn hasSideEffects(self: RunStep) bool { |
| 324 | 327 | .infer_from_args => !self.hasAnyOutputArgs(), |
| 325 | 328 | .inherit => true, |
| 326 | 329 | .check => false, |
| 330 | .zig_test => false, | |
| 327 | 331 | }; |
| 328 | 332 | } |
| 329 | 333 | |
| ... | ... | @@ -366,11 +370,6 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { |
| 366 | 370 | } |
| 367 | 371 | |
| 368 | 372 | fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 369 | // Unfortunately we have no way to collect progress from arbitrary programs. | |
| 370 | // Perhaps in the future Zig could offer some kind of opt-in IPC mechanism that | |
| 371 | // processes could use to supply progress updates. | |
| 372 | _ = prog_node; | |
| 373 | ||
| 374 | 373 | const b = step.owner; |
| 375 | 374 | const arena = b.allocator; |
| 376 | 375 | const self = @fieldParentPtr(RunStep, "step", step); |
| ... | ... | @@ -439,7 +438,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 439 | 438 | hashStdIo(&man.hash, self.stdio); |
| 440 | 439 | |
| 441 | 440 | if (has_side_effects) { |
| 442 | try runCommand(self, argv_list.items, has_side_effects, null); | |
| 441 | try runCommand(self, argv_list.items, has_side_effects, null, prog_node); | |
| 443 | 442 | return; |
| 444 | 443 | } |
| 445 | 444 | |
| ... | ... | @@ -492,8 +491,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 492 | 491 | argv_list.items[placeholder.index] = cli_arg; |
| 493 | 492 | } |
| 494 | 493 | |
| 495 | try runCommand(self, argv_list.items, has_side_effects, &digest); | |
| 496 | try man.writeManifest(); | |
| 494 | try runCommand(self, argv_list.items, has_side_effects, &digest, prog_node); | |
| 495 | ||
| 496 | try step.writeManifest(&man); | |
| 497 | 497 | } |
| 498 | 498 | |
| 499 | 499 | fn formatTerm( |
| ... | ... | @@ -546,6 +546,7 @@ fn runCommand( |
| 546 | 546 | argv: []const []const u8, |
| 547 | 547 | has_side_effects: bool, |
| 548 | 548 | digest: ?*const [std.Build.Cache.hex_digest_len]u8, |
| 549 | prog_node: *std.Progress.Node, | |
| 549 | 550 | ) !void { |
| 550 | 551 | const step = &self.step; |
| 551 | 552 | const b = step.owner; |
| ... | ... | @@ -554,7 +555,15 @@ fn runCommand( |
| 554 | 555 | try step.handleChildProcUnsupported(self.cwd, argv); |
| 555 | 556 | try Step.handleVerbose(step.owner, self.cwd, argv); |
| 556 | 557 | |
| 557 | const result = spawnChildAndCollect(self, argv, has_side_effects) catch |err| term: { | |
| 558 | const allow_skip = switch (self.stdio) { | |
| 559 | .check, .zig_test => self.skip_foreign_checks, | |
| 560 | else => false, | |
| 561 | }; | |
| 562 | ||
| 563 | var interp_argv = std.ArrayList([]const u8).init(b.allocator); | |
| 564 | defer interp_argv.deinit(); | |
| 565 | ||
| 566 | const result = spawnChildAndCollect(self, argv, has_side_effects, prog_node) catch |err| term: { | |
| 558 | 567 | // InvalidExe: cpu arch mismatch |
| 559 | 568 | // FileNotFound: can happen with a wrong dynamic linker path |
| 560 | 569 | if (err == error.InvalidExe or err == error.FileNotFound) interpret: { |
| ... | ... | @@ -566,10 +575,10 @@ fn runCommand( |
| 566 | 575 | .artifact => |exe| exe, |
| 567 | 576 | else => break :interpret, |
| 568 | 577 | }; |
| 569 | if (exe.kind != .exe) break :interpret; | |
| 570 | ||
| 571 | var interp_argv = std.ArrayList([]const u8).init(b.allocator); | |
| 572 | defer interp_argv.deinit(); | |
| 578 | switch (exe.kind) { | |
| 579 | .exe, .@"test" => {}, | |
| 580 | else => break :interpret, | |
| 581 | } | |
| 573 | 582 | |
| 574 | 583 | const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc; |
| 575 | 584 | switch (b.host.getExternalExecutor(exe.target_info, .{ |
| ... | ... | @@ -577,14 +586,13 @@ fn runCommand( |
| 577 | 586 | .link_libc = exe.is_linking_libc, |
| 578 | 587 | })) { |
| 579 | 588 | .native, .rosetta => { |
| 580 | if (self.stdio == .check and self.skip_foreign_checks) | |
| 581 | return error.MakeSkipped; | |
| 582 | ||
| 589 | if (allow_skip) return error.MakeSkipped; | |
| 583 | 590 | break :interpret; |
| 584 | 591 | }, |
| 585 | 592 | .wine => |bin_name| { |
| 586 | 593 | if (b.enable_wine) { |
| 587 | 594 | try interp_argv.append(bin_name); |
| 595 | try interp_argv.appendSlice(argv); | |
| 588 | 596 | } else { |
| 589 | 597 | return failForeign(self, "-fwine", argv[0], exe); |
| 590 | 598 | } |
| ... | ... | @@ -617,6 +625,8 @@ fn runCommand( |
| 617 | 625 | try interp_argv.append("-L"); |
| 618 | 626 | try interp_argv.append(full_dir); |
| 619 | 627 | } |
| 628 | ||
| 629 | try interp_argv.appendSlice(argv); | |
| 620 | 630 | } else { |
| 621 | 631 | return failForeign(self, "-fqemu", argv[0], exe); |
| 622 | 632 | } |
| ... | ... | @@ -624,6 +634,7 @@ fn runCommand( |
| 624 | 634 | .darling => |bin_name| { |
| 625 | 635 | if (b.enable_darling) { |
| 626 | 636 | try interp_argv.append(bin_name); |
| 637 | try interp_argv.appendSlice(argv); | |
| 627 | 638 | } else { |
| 628 | 639 | return failForeign(self, "-fdarling", argv[0], exe); |
| 629 | 640 | } |
| ... | ... | @@ -632,13 +643,15 @@ fn runCommand( |
| 632 | 643 | if (b.enable_wasmtime) { |
| 633 | 644 | try interp_argv.append(bin_name); |
| 634 | 645 | try interp_argv.append("--dir=."); |
| 646 | try interp_argv.append(argv[0]); | |
| 647 | try interp_argv.append("--"); | |
| 648 | try interp_argv.appendSlice(argv[1..]); | |
| 635 | 649 | } else { |
| 636 | 650 | return failForeign(self, "-fwasmtime", argv[0], exe); |
| 637 | 651 | } |
| 638 | 652 | }, |
| 639 | 653 | .bad_dl => |foreign_dl| { |
| 640 | if (self.stdio == .check and self.skip_foreign_checks) | |
| 641 | return error.MakeSkipped; | |
| 654 | if (allow_skip) return error.MakeSkipped; | |
| 642 | 655 | |
| 643 | 656 | const host_dl = b.host.dynamic_linker.get() orelse "(none)"; |
| 644 | 657 | |
| ... | ... | @@ -650,8 +663,7 @@ fn runCommand( |
| 650 | 663 | , .{ host_dl, foreign_dl }); |
| 651 | 664 | }, |
| 652 | 665 | .bad_os_or_cpu => { |
| 653 | if (self.stdio == .check and self.skip_foreign_checks) | |
| 654 | return error.MakeSkipped; | |
| 666 | if (allow_skip) return error.MakeSkipped; | |
| 655 | 667 | |
| 656 | 668 | const host_name = try b.host.target.zigTriple(b.allocator); |
| 657 | 669 | const foreign_name = try exe.target.zigTriple(b.allocator); |
| ... | ... | @@ -667,11 +679,9 @@ fn runCommand( |
| 667 | 679 | RunStep.addPathForDynLibsInternal(&self.step, b, exe); |
| 668 | 680 | } |
| 669 | 681 | |
| 670 | try interp_argv.append(argv[0]); | |
| 671 | ||
| 672 | 682 | try Step.handleVerbose(step.owner, self.cwd, interp_argv.items); |
| 673 | 683 | |
| 674 | break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects) catch |e| { | |
| 684 | break :term spawnChildAndCollect(self, interp_argv.items, has_side_effects, prog_node) catch |e| { | |
| 675 | 685 | return step.fail("unable to spawn {s}: {s}", .{ |
| 676 | 686 | interp_argv.items[0], @errorName(e), |
| 677 | 687 | }); |
| ... | ... | @@ -683,6 +693,7 @@ fn runCommand( |
| 683 | 693 | |
| 684 | 694 | step.result_duration_ns = result.elapsed_ns; |
| 685 | 695 | step.result_peak_rss = result.peak_rss; |
| 696 | step.test_results = result.stdio.test_results; | |
| 686 | 697 | |
| 687 | 698 | // Capture stdout and stderr to GeneratedFile objects. |
| 688 | 699 | const Stream = struct { |
| ... | ... | @@ -693,13 +704,13 @@ fn runCommand( |
| 693 | 704 | for ([_]Stream{ |
| 694 | 705 | .{ |
| 695 | 706 | .captured = self.captured_stdout, |
| 696 | .is_null = result.stdout_null, | |
| 697 | .bytes = result.stdout, | |
| 707 | .is_null = result.stdio.stdout_null, | |
| 708 | .bytes = result.stdio.stdout, | |
| 698 | 709 | }, |
| 699 | 710 | .{ |
| 700 | 711 | .captured = self.captured_stderr, |
| 701 | .is_null = result.stderr_null, | |
| 702 | .bytes = result.stderr, | |
| 712 | .is_null = result.stdio.stderr_null, | |
| 713 | .bytes = result.stdio.stderr, | |
| 703 | 714 | }, |
| 704 | 715 | }) |stream| { |
| 705 | 716 | if (stream.captured) |output| { |
| ... | ... | @@ -724,11 +735,13 @@ fn runCommand( |
| 724 | 735 | } |
| 725 | 736 | } |
| 726 | 737 | |
| 738 | const final_argv = if (interp_argv.items.len == 0) argv else interp_argv.items; | |
| 739 | ||
| 727 | 740 | switch (self.stdio) { |
| 728 | 741 | .check => |checks| for (checks.items) |check| switch (check) { |
| 729 | 742 | .expect_stderr_exact => |expected_bytes| { |
| 730 | assert(!result.stderr_null); | |
| 731 | if (!mem.eql(u8, expected_bytes, result.stderr)) { | |
| 743 | assert(!result.stdio.stderr_null); | |
| 744 | if (!mem.eql(u8, expected_bytes, result.stdio.stderr)) { | |
| 732 | 745 | return step.fail( |
| 733 | 746 | \\ |
| 734 | 747 | \\========= expected this stderr: ========= |
| ... | ... | @@ -739,14 +752,14 @@ fn runCommand( |
| 739 | 752 | \\{s} |
| 740 | 753 | , .{ |
| 741 | 754 | expected_bytes, |
| 742 | result.stderr, | |
| 743 | try Step.allocPrintCmd(arena, self.cwd, argv), | |
| 755 | result.stdio.stderr, | |
| 756 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | |
| 744 | 757 | }); |
| 745 | 758 | } |
| 746 | 759 | }, |
| 747 | 760 | .expect_stderr_match => |match| { |
| 748 | assert(!result.stderr_null); | |
| 749 | if (mem.indexOf(u8, result.stderr, match) == null) { | |
| 761 | assert(!result.stdio.stderr_null); | |
| 762 | if (mem.indexOf(u8, result.stdio.stderr, match) == null) { | |
| 750 | 763 | return step.fail( |
| 751 | 764 | \\ |
| 752 | 765 | \\========= expected to find in stderr: ========= |
| ... | ... | @@ -757,14 +770,14 @@ fn runCommand( |
| 757 | 770 | \\{s} |
| 758 | 771 | , .{ |
| 759 | 772 | match, |
| 760 | result.stderr, | |
| 761 | try Step.allocPrintCmd(arena, self.cwd, argv), | |
| 773 | result.stdio.stderr, | |
| 774 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | |
| 762 | 775 | }); |
| 763 | 776 | } |
| 764 | 777 | }, |
| 765 | 778 | .expect_stdout_exact => |expected_bytes| { |
| 766 | assert(!result.stdout_null); | |
| 767 | if (!mem.eql(u8, expected_bytes, result.stdout)) { | |
| 779 | assert(!result.stdio.stdout_null); | |
| 780 | if (!mem.eql(u8, expected_bytes, result.stdio.stdout)) { | |
| 768 | 781 | return step.fail( |
| 769 | 782 | \\ |
| 770 | 783 | \\========= expected this stdout: ========= |
| ... | ... | @@ -775,14 +788,14 @@ fn runCommand( |
| 775 | 788 | \\{s} |
| 776 | 789 | , .{ |
| 777 | 790 | expected_bytes, |
| 778 | result.stdout, | |
| 779 | try Step.allocPrintCmd(arena, self.cwd, argv), | |
| 791 | result.stdio.stdout, | |
| 792 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | |
| 780 | 793 | }); |
| 781 | 794 | } |
| 782 | 795 | }, |
| 783 | 796 | .expect_stdout_match => |match| { |
| 784 | assert(!result.stdout_null); | |
| 785 | if (mem.indexOf(u8, result.stdout, match) == null) { | |
| 797 | assert(!result.stdio.stdout_null); | |
| 798 | if (mem.indexOf(u8, result.stdio.stdout, match) == null) { | |
| 786 | 799 | return step.fail( |
| 787 | 800 | \\ |
| 788 | 801 | \\========= expected to find in stdout: ========= |
| ... | ... | @@ -793,8 +806,8 @@ fn runCommand( |
| 793 | 806 | \\{s} |
| 794 | 807 | , .{ |
| 795 | 808 | match, |
| 796 | result.stdout, | |
| 797 | try Step.allocPrintCmd(arena, self.cwd, argv), | |
| 809 | result.stdio.stdout, | |
| 810 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | |
| 798 | 811 | }); |
| 799 | 812 | } |
| 800 | 813 | }, |
| ... | ... | @@ -803,33 +816,46 @@ fn runCommand( |
| 803 | 816 | return step.fail("the following command {} (expected {}):\n{s}", .{ |
| 804 | 817 | fmtTerm(result.term), |
| 805 | 818 | fmtTerm(expected_term), |
| 806 | try Step.allocPrintCmd(arena, self.cwd, argv), | |
| 819 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | |
| 807 | 820 | }); |
| 808 | 821 | } |
| 809 | 822 | }, |
| 810 | 823 | }, |
| 824 | .zig_test => { | |
| 825 | const expected_term: std.process.Child.Term = .{ .Exited = 0 }; | |
| 826 | if (!termMatches(expected_term, result.term)) { | |
| 827 | return step.fail("the following command {} (expected {}):\n{s}", .{ | |
| 828 | fmtTerm(result.term), | |
| 829 | fmtTerm(expected_term), | |
| 830 | try Step.allocPrintCmd(arena, self.cwd, final_argv), | |
| 831 | }); | |
| 832 | } | |
| 833 | if (!result.stdio.test_results.isSuccess()) { | |
| 834 | return step.fail( | |
| 835 | "the following test command failed:\n{s}", | |
| 836 | .{try Step.allocPrintCmd(arena, self.cwd, final_argv)}, | |
| 837 | ); | |
| 838 | } | |
| 839 | }, | |
| 811 | 840 | else => { |
| 812 | try step.handleChildProcessTerm(result.term, self.cwd, argv); | |
| 841 | try step.handleChildProcessTerm(result.term, self.cwd, final_argv); | |
| 813 | 842 | }, |
| 814 | 843 | } |
| 815 | 844 | } |
| 816 | 845 | |
| 817 | 846 | const ChildProcResult = struct { |
| 818 | // These use boolean flags instead of optionals as a workaround for | |
| 819 | // https://github.com/ziglang/zig/issues/14783 | |
| 820 | stdout: []const u8, | |
| 821 | stderr: []const u8, | |
| 822 | stdout_null: bool, | |
| 823 | stderr_null: bool, | |
| 824 | 847 | term: std.process.Child.Term, |
| 825 | 848 | elapsed_ns: u64, |
| 826 | 849 | peak_rss: usize, |
| 850 | ||
| 851 | stdio: StdIoResult, | |
| 827 | 852 | }; |
| 828 | 853 | |
| 829 | 854 | fn spawnChildAndCollect( |
| 830 | 855 | self: *RunStep, |
| 831 | 856 | argv: []const []const u8, |
| 832 | 857 | has_side_effects: bool, |
| 858 | prog_node: *std.Progress.Node, | |
| 833 | 859 | ) !ChildProcResult { |
| 834 | 860 | const b = self.step.owner; |
| 835 | 861 | const arena = b.allocator; |
| ... | ... | @@ -848,16 +874,19 @@ fn spawnChildAndCollect( |
| 848 | 874 | .infer_from_args => if (has_side_effects) .Inherit else .Close, |
| 849 | 875 | .inherit => .Inherit, |
| 850 | 876 | .check => .Close, |
| 877 | .zig_test => .Pipe, | |
| 851 | 878 | }; |
| 852 | 879 | child.stdout_behavior = switch (self.stdio) { |
| 853 | 880 | .infer_from_args => if (has_side_effects) .Inherit else .Ignore, |
| 854 | 881 | .inherit => .Inherit, |
| 855 | 882 | .check => |checks| if (checksContainStdout(checks.items)) .Pipe else .Ignore, |
| 883 | .zig_test => .Pipe, | |
| 856 | 884 | }; |
| 857 | 885 | child.stderr_behavior = switch (self.stdio) { |
| 858 | 886 | .infer_from_args => if (has_side_effects) .Inherit else .Pipe, |
| 859 | 887 | .inherit => .Inherit, |
| 860 | 888 | .check => .Pipe, |
| 889 | .zig_test => .Pipe, | |
| 861 | 890 | }; |
| 862 | 891 | if (self.captured_stdout != null) child.stdout_behavior = .Pipe; |
| 863 | 892 | if (self.captured_stderr != null) child.stderr_behavior = .Pipe; |
| ... | ... | @@ -871,6 +900,219 @@ fn spawnChildAndCollect( |
| 871 | 900 | }); |
| 872 | 901 | var timer = try std.time.Timer.start(); |
| 873 | 902 | |
| 903 | const result = if (self.stdio == .zig_test) | |
| 904 | evalZigTest(self, &child, prog_node) | |
| 905 | else | |
| 906 | evalGeneric(self, &child); | |
| 907 | ||
| 908 | const term = try child.wait(); | |
| 909 | const elapsed_ns = timer.read(); | |
| 910 | ||
| 911 | return .{ | |
| 912 | .stdio = try result, | |
| 913 | .term = term, | |
| 914 | .elapsed_ns = elapsed_ns, | |
| 915 | .peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0, | |
| 916 | }; | |
| 917 | } | |
| 918 | ||
| 919 | const StdIoResult = struct { | |
| 920 | // These use boolean flags instead of optionals as a workaround for | |
| 921 | // https://github.com/ziglang/zig/issues/14783 | |
| 922 | stdout: []const u8, | |
| 923 | stderr: []const u8, | |
| 924 | stdout_null: bool, | |
| 925 | stderr_null: bool, | |
| 926 | test_results: Step.TestResults, | |
| 927 | }; | |
| 928 | ||
| 929 | fn evalZigTest( | |
| 930 | self: *RunStep, | |
| 931 | child: *std.process.Child, | |
| 932 | prog_node: *std.Progress.Node, | |
| 933 | ) !StdIoResult { | |
| 934 | const gpa = self.step.owner.allocator; | |
| 935 | const arena = self.step.owner.allocator; | |
| 936 | ||
| 937 | var poller = std.io.poll(gpa, enum { stdout, stderr }, .{ | |
| 938 | .stdout = child.stdout.?, | |
| 939 | .stderr = child.stderr.?, | |
| 940 | }); | |
| 941 | defer poller.deinit(); | |
| 942 | ||
| 943 | try sendMessage(child.stdin.?, .query_test_metadata); | |
| 944 | ||
| 945 | const Header = std.zig.Server.Message.Header; | |
| 946 | ||
| 947 | const stdout = poller.fifo(.stdout); | |
| 948 | const stderr = poller.fifo(.stderr); | |
| 949 | ||
| 950 | var fail_count: u32 = 0; | |
| 951 | var skip_count: u32 = 0; | |
| 952 | var leak_count: u32 = 0; | |
| 953 | var test_count: u32 = 0; | |
| 954 | ||
| 955 | var metadata: ?TestMetadata = null; | |
| 956 | ||
| 957 | var sub_prog_node: ?std.Progress.Node = null; | |
| 958 | defer if (sub_prog_node) |*n| n.end(); | |
| 959 | ||
| 960 | poll: while (try poller.poll()) { | |
| 961 | while (true) { | |
| 962 | const buf = stdout.readableSlice(0); | |
| 963 | assert(stdout.readableLength() == buf.len); | |
| 964 | if (buf.len < @sizeOf(Header)) continue :poll; | |
| 965 | const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); | |
| 966 | const header_and_msg_len = header.bytes_len + @sizeOf(Header); | |
| 967 | if (buf.len < header_and_msg_len) continue :poll; | |
| 968 | const body = buf[@sizeOf(Header)..][0..header.bytes_len]; | |
| 969 | switch (header.tag) { | |
| 970 | .zig_version => { | |
| 971 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | |
| 972 | return self.step.fail( | |
| 973 | "zig version mismatch build runner vs compiler: '{s}' vs '{s}'", | |
| 974 | .{ builtin.zig_version_string, body }, | |
| 975 | ); | |
| 976 | } | |
| 977 | }, | |
| 978 | .test_metadata => { | |
| 979 | const TmHdr = std.zig.Server.Message.TestMetadata; | |
| 980 | const tm_hdr = @ptrCast(*align(1) const TmHdr, body); | |
| 981 | test_count = tm_hdr.tests_len; | |
| 982 | ||
| 983 | const names_bytes = body[@sizeOf(TmHdr)..][0 .. test_count * @sizeOf(u32)]; | |
| 984 | const async_frame_lens_bytes = body[@sizeOf(TmHdr) + names_bytes.len ..][0 .. test_count * @sizeOf(u32)]; | |
| 985 | const expected_panic_msgs_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len ..][0 .. test_count * @sizeOf(u32)]; | |
| 986 | const string_bytes = body[@sizeOf(TmHdr) + names_bytes.len + async_frame_lens_bytes.len + expected_panic_msgs_bytes.len ..][0..tm_hdr.string_bytes_len]; | |
| 987 | ||
| 988 | const names = std.mem.bytesAsSlice(u32, names_bytes); | |
| 989 | const async_frame_lens = std.mem.bytesAsSlice(u32, async_frame_lens_bytes); | |
| 990 | const expected_panic_msgs = std.mem.bytesAsSlice(u32, expected_panic_msgs_bytes); | |
| 991 | const names_aligned = try arena.alloc(u32, names.len); | |
| 992 | for (names_aligned, names) |*dest, src| dest.* = src; | |
| 993 | ||
| 994 | const async_frame_lens_aligned = try arena.alloc(u32, async_frame_lens.len); | |
| 995 | for (async_frame_lens_aligned, async_frame_lens) |*dest, src| dest.* = src; | |
| 996 | ||
| 997 | const expected_panic_msgs_aligned = try arena.alloc(u32, expected_panic_msgs.len); | |
| 998 | for (expected_panic_msgs_aligned, expected_panic_msgs) |*dest, src| dest.* = src; | |
| 999 | ||
| 1000 | prog_node.setEstimatedTotalItems(names.len); | |
| 1001 | metadata = .{ | |
| 1002 | .string_bytes = try arena.dupe(u8, string_bytes), | |
| 1003 | .names = names_aligned, | |
| 1004 | .async_frame_lens = async_frame_lens_aligned, | |
| 1005 | .expected_panic_msgs = expected_panic_msgs_aligned, | |
| 1006 | .next_index = 0, | |
| 1007 | .prog_node = prog_node, | |
| 1008 | }; | |
| 1009 | ||
| 1010 | try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); | |
| 1011 | }, | |
| 1012 | .test_results => { | |
| 1013 | const md = metadata.?; | |
| 1014 | ||
| 1015 | const TrHdr = std.zig.Server.Message.TestResults; | |
| 1016 | const tr_hdr = @ptrCast(*align(1) const TrHdr, body); | |
| 1017 | fail_count += @boolToInt(tr_hdr.flags.fail); | |
| 1018 | skip_count += @boolToInt(tr_hdr.flags.skip); | |
| 1019 | leak_count += @boolToInt(tr_hdr.flags.leak); | |
| 1020 | ||
| 1021 | if (tr_hdr.flags.fail or tr_hdr.flags.leak) { | |
| 1022 | const name = std.mem.sliceTo(md.string_bytes[md.names[tr_hdr.index]..], 0); | |
| 1023 | const msg = std.mem.trim(u8, stderr.readableSlice(0), "\n"); | |
| 1024 | const label = if (tr_hdr.flags.fail) "failed" else "leaked"; | |
| 1025 | if (msg.len > 0) { | |
| 1026 | try self.step.addError("'{s}' {s}: {s}", .{ name, label, msg }); | |
| 1027 | } else { | |
| 1028 | try self.step.addError("'{s}' {s}", .{ name, label }); | |
| 1029 | } | |
| 1030 | stderr.discard(msg.len); | |
| 1031 | } | |
| 1032 | ||
| 1033 | try requestNextTest(child.stdin.?, &metadata.?, &sub_prog_node); | |
| 1034 | }, | |
| 1035 | else => {}, // ignore other messages | |
| 1036 | } | |
| 1037 | stdout.discard(header_and_msg_len); | |
| 1038 | } | |
| 1039 | } | |
| 1040 | ||
| 1041 | if (stderr.readableLength() > 0) { | |
| 1042 | const msg = std.mem.trim(u8, try stderr.toOwnedSlice(), "\n"); | |
| 1043 | if (msg.len > 0) try self.step.result_error_msgs.append(arena, msg); | |
| 1044 | } | |
| 1045 | ||
| 1046 | // Send EOF to stdin. | |
| 1047 | child.stdin.?.close(); | |
| 1048 | child.stdin = null; | |
| 1049 | ||
| 1050 | return .{ | |
| 1051 | .stdout = &.{}, | |
| 1052 | .stderr = &.{}, | |
| 1053 | .stdout_null = true, | |
| 1054 | .stderr_null = true, | |
| 1055 | .test_results = .{ | |
| 1056 | .test_count = test_count, | |
| 1057 | .fail_count = fail_count, | |
| 1058 | .skip_count = skip_count, | |
| 1059 | .leak_count = leak_count, | |
| 1060 | }, | |
| 1061 | }; | |
| 1062 | } | |
| 1063 | ||
| 1064 | const TestMetadata = struct { | |
| 1065 | names: []const u32, | |
| 1066 | async_frame_lens: []const u32, | |
| 1067 | expected_panic_msgs: []const u32, | |
| 1068 | string_bytes: []const u8, | |
| 1069 | next_index: u32, | |
| 1070 | prog_node: *std.Progress.Node, | |
| 1071 | ||
| 1072 | fn testName(tm: TestMetadata, index: u32) []const u8 { | |
| 1073 | return std.mem.sliceTo(tm.string_bytes[tm.names[index]..], 0); | |
| 1074 | } | |
| 1075 | }; | |
| 1076 | ||
| 1077 | fn requestNextTest(in: fs.File, metadata: *TestMetadata, sub_prog_node: *?std.Progress.Node) !void { | |
| 1078 | while (metadata.next_index < metadata.names.len) { | |
| 1079 | const i = metadata.next_index; | |
| 1080 | metadata.next_index += 1; | |
| 1081 | ||
| 1082 | if (metadata.async_frame_lens[i] != 0) continue; | |
| 1083 | if (metadata.expected_panic_msgs[i] != 0) continue; | |
| 1084 | ||
| 1085 | const name = metadata.testName(i); | |
| 1086 | if (sub_prog_node.*) |*n| n.end(); | |
| 1087 | sub_prog_node.* = metadata.prog_node.start(name, 0); | |
| 1088 | ||
| 1089 | try sendRunTestMessage(in, i); | |
| 1090 | return; | |
| 1091 | } else { | |
| 1092 | try sendMessage(in, .exit); | |
| 1093 | } | |
| 1094 | } | |
| 1095 | ||
| 1096 | fn sendMessage(file: std.fs.File, tag: std.zig.Client.Message.Tag) !void { | |
| 1097 | const header: std.zig.Client.Message.Header = .{ | |
| 1098 | .tag = tag, | |
| 1099 | .bytes_len = 0, | |
| 1100 | }; | |
| 1101 | try file.writeAll(std.mem.asBytes(&header)); | |
| 1102 | } | |
| 1103 | ||
| 1104 | fn sendRunTestMessage(file: std.fs.File, index: u32) !void { | |
| 1105 | const header: std.zig.Client.Message.Header = .{ | |
| 1106 | .tag = .run_test, | |
| 1107 | .bytes_len = 4, | |
| 1108 | }; | |
| 1109 | const full_msg = std.mem.asBytes(&header) ++ std.mem.asBytes(&index); | |
| 1110 | try file.writeAll(full_msg); | |
| 1111 | } | |
| 1112 | ||
| 1113 | fn evalGeneric(self: *RunStep, child: *std.process.Child) !StdIoResult { | |
| 1114 | const arena = self.step.owner.allocator; | |
| 1115 | ||
| 874 | 1116 | if (self.stdin) |stdin| { |
| 875 | 1117 | child.stdin.?.writeAll(stdin) catch |err| { |
| 876 | 1118 | return self.step.fail("unable to write stdin: {s}", .{@errorName(err)}); |
| ... | ... | @@ -925,17 +1167,12 @@ fn spawnChildAndCollect( |
| 925 | 1167 | } |
| 926 | 1168 | } |
| 927 | 1169 | |
| 928 | const term = try child.wait(); | |
| 929 | const elapsed_ns = timer.read(); | |
| 930 | ||
| 931 | 1170 | return .{ |
| 932 | 1171 | .stdout = stdout_bytes, |
| 933 | 1172 | .stderr = stderr_bytes, |
| 934 | 1173 | .stdout_null = stdout_null, |
| 935 | 1174 | .stderr_null = stderr_null, |
| 936 | .term = term, | |
| 937 | .elapsed_ns = elapsed_ns, | |
| 938 | .peak_rss = child.resource_usage_statistics.getMaxRss() orelse 0, | |
| 1175 | .test_results = .{}, | |
| 939 | 1176 | }; |
| 940 | 1177 | } |
| 941 | 1178 | |
| ... | ... | @@ -966,7 +1203,7 @@ fn failForeign( |
| 966 | 1203 | exe: *CompileStep, |
| 967 | 1204 | ) error{ MakeFailed, MakeSkipped, OutOfMemory } { |
| 968 | 1205 | switch (self.stdio) { |
| 969 | .check => { | |
| 1206 | .check, .zig_test => { | |
| 970 | 1207 | if (self.skip_foreign_checks) |
| 971 | 1208 | return error.MakeSkipped; |
| 972 | 1209 | |
| ... | ... | @@ -987,7 +1224,7 @@ fn failForeign( |
| 987 | 1224 | |
| 988 | 1225 | fn hashStdIo(hh: *std.Build.Cache.HashHelper, stdio: StdIo) void { |
| 989 | 1226 | switch (stdio) { |
| 990 | .infer_from_args, .inherit => {}, | |
| 1227 | .infer_from_args, .inherit, .zig_test => {}, | |
| 991 | 1228 | .check => |checks| for (checks.items) |check| { |
| 992 | 1229 | hh.add(@as(std.meta.Tag(StdIo.Check), check)); |
| 993 | 1230 | switch (check) { |
lib/std/Build/Step.zig+30-3| ... | ... | @@ -35,11 +35,27 @@ result_cached: bool, |
| 35 | 35 | result_duration_ns: ?u64, |
| 36 | 36 | /// 0 means unavailable or not reported. |
| 37 | 37 | result_peak_rss: usize, |
| 38 | test_results: TestResults, | |
| 38 | 39 | |
| 39 | 40 | /// The return addresss associated with creation of this step that can be useful |
| 40 | 41 | /// to print along with debugging messages. |
| 41 | 42 | debug_stack_trace: [n_debug_stack_frames]usize, |
| 42 | 43 | |
| 44 | pub const TestResults = struct { | |
| 45 | fail_count: u32 = 0, | |
| 46 | skip_count: u32 = 0, | |
| 47 | leak_count: u32 = 0, | |
| 48 | test_count: u32 = 0, | |
| 49 | ||
| 50 | pub fn isSuccess(tr: TestResults) bool { | |
| 51 | return tr.fail_count == 0 and tr.leak_count == 0; | |
| 52 | } | |
| 53 | ||
| 54 | pub fn passCount(tr: TestResults) u32 { | |
| 55 | return tr.test_count - tr.fail_count - tr.skip_count; | |
| 56 | } | |
| 57 | }; | |
| 58 | ||
| 43 | 59 | pub const MakeFn = *const fn (self: *Step, prog_node: *std.Progress.Node) anyerror!void; |
| 44 | 60 | |
| 45 | 61 | const n_debug_stack_frames = 4; |
| ... | ... | @@ -134,6 +150,7 @@ pub fn init(options: Options) Step { |
| 134 | 150 | .result_cached = false, |
| 135 | 151 | .result_duration_ns = null, |
| 136 | 152 | .result_peak_rss = 0, |
| 153 | .test_results = .{}, | |
| 137 | 154 | }; |
| 138 | 155 | } |
| 139 | 156 | |
| ... | ... | @@ -152,6 +169,10 @@ pub fn make(s: *Step, prog_node: *std.Progress.Node) error{ MakeFailed, MakeSkip |
| 152 | 169 | }, |
| 153 | 170 | }; |
| 154 | 171 | |
| 172 | if (!s.test_results.isSuccess()) { | |
| 173 | return error.MakeFailed; | |
| 174 | } | |
| 175 | ||
| 155 | 176 | if (s.max_rss != 0 and s.result_peak_rss > s.max_rss) { |
| 156 | 177 | const msg = std.fmt.allocPrint(arena, "memory usage peaked at {d} bytes, exceeding the declared upper bound of {d}", .{ |
| 157 | 178 | s.result_peak_rss, s.max_rss, |
| ... | ... | @@ -346,9 +367,7 @@ pub fn evalZigProcess( |
| 346 | 367 | s.result_cached = ebp_hdr.flags.cache_hit; |
| 347 | 368 | result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]); |
| 348 | 369 | }, |
| 349 | _ => { | |
| 350 | // Unrecognized message. | |
| 351 | }, | |
| 370 | else => {}, // ignore other messages | |
| 352 | 371 | } |
| 353 | 372 | stdout.discard(header_and_msg_len); |
| 354 | 373 | } |
| ... | ... | @@ -475,3 +494,11 @@ fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyer |
| 475 | 494 | const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; |
| 476 | 495 | return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path }); |
| 477 | 496 | } |
| 497 | ||
| 498 | pub fn writeManifest(s: *Step, man: *std.Build.Cache.Manifest) !void { | |
| 499 | if (s.test_results.isSuccess()) { | |
| 500 | man.writeManifest() catch |err| { | |
| 501 | try s.addError("unable to write cache manifest: {s}", .{@errorName(err)}); | |
| 502 | }; | |
| 503 | } | |
| 504 | } |
lib/std/Build/WriteFileStep.zig+1-1| ... | ... | @@ -282,7 +282,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 282 | 282 | }); |
| 283 | 283 | } |
| 284 | 284 | |
| 285 | try man.writeManifest(); | |
| 285 | try step.writeManifest(&man); | |
| 286 | 286 | } |
| 287 | 287 | |
| 288 | 288 | const std = @import("../std.zig"); |
lib/std/zig/Client.zig+7| ... | ... | @@ -26,6 +26,13 @@ pub const Message = struct { |
| 26 | 26 | /// swap. |
| 27 | 27 | /// No body. |
| 28 | 28 | hot_update, |
| 29 | /// Ask the test runner for metadata about all the unit tests that can | |
| 30 | /// be run. Server will respond with a `test_metadata` message. | |
| 31 | /// No body. | |
| 32 | query_test_metadata, | |
| 33 | /// Ask the test runner to run a particular test. | |
| 34 | /// The message body is a u32 test index. | |
| 35 | run_test, | |
| 29 | 36 | |
| 30 | 37 | _, |
| 31 | 38 | }; |
lib/std/zig/Server.zig+200| ... | ... | @@ -1,3 +1,7 @@ |
| 1 | in: std.fs.File, | |
| 2 | out: std.fs.File, | |
| 3 | receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), | |
| 4 | ||
| 1 | 5 | pub const Message = struct { |
| 2 | 6 | pub const Header = extern struct { |
| 3 | 7 | tag: Tag, |
| ... | ... | @@ -14,6 +18,11 @@ pub const Message = struct { |
| 14 | 18 | progress, |
| 15 | 19 | /// Body is a EmitBinPath. |
| 16 | 20 | emit_bin_path, |
| 21 | /// Body is a TestMetadata | |
| 22 | test_metadata, | |
| 23 | /// Body is a TestResults | |
| 24 | test_results, | |
| 25 | ||
| 17 | 26 | _, |
| 18 | 27 | }; |
| 19 | 28 | |
| ... | ... | @@ -26,6 +35,33 @@ pub const Message = struct { |
| 26 | 35 | string_bytes_len: u32, |
| 27 | 36 | }; |
| 28 | 37 | |
| 38 | /// Trailing: | |
| 39 | /// * name: [tests_len]u32 | |
| 40 | /// - null-terminated string_bytes index | |
| 41 | /// * async_frame_len: [tests_len]u32, | |
| 42 | /// - 0 means not async | |
| 43 | /// * expected_panic_msg: [tests_len]u32, | |
| 44 | /// - null-terminated string_bytes index | |
| 45 | /// - 0 means does not expect pani | |
| 46 | /// * string_bytes: [string_bytes_len]u8, | |
| 47 | pub const TestMetadata = extern struct { | |
| 48 | string_bytes_len: u32, | |
| 49 | tests_len: u32, | |
| 50 | }; | |
| 51 | ||
| 52 | pub const TestResults = extern struct { | |
| 53 | index: u32, | |
| 54 | flags: Flags, | |
| 55 | ||
| 56 | pub const Flags = packed struct(u8) { | |
| 57 | fail: bool, | |
| 58 | skip: bool, | |
| 59 | leak: bool, | |
| 60 | ||
| 61 | reserved: u5 = 0, | |
| 62 | }; | |
| 63 | }; | |
| 64 | ||
| 29 | 65 | /// Trailing: |
| 30 | 66 | /// * the file system path the emitted binary can be found |
| 31 | 67 | pub const EmitBinPath = extern struct { |
| ... | ... | @@ -37,3 +73,167 @@ pub const Message = struct { |
| 37 | 73 | }; |
| 38 | 74 | }; |
| 39 | 75 | }; |
| 76 | ||
| 77 | pub const Options = struct { | |
| 78 | gpa: Allocator, | |
| 79 | in: std.fs.File, | |
| 80 | out: std.fs.File, | |
| 81 | zig_version: []const u8, | |
| 82 | }; | |
| 83 | ||
| 84 | pub fn init(options: Options) !Server { | |
| 85 | var s: Server = .{ | |
| 86 | .in = options.in, | |
| 87 | .out = options.out, | |
| 88 | .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa), | |
| 89 | }; | |
| 90 | try s.serveStringMessage(.zig_version, options.zig_version); | |
| 91 | return s; | |
| 92 | } | |
| 93 | ||
| 94 | pub fn deinit(s: *Server) void { | |
| 95 | s.receive_fifo.deinit(); | |
| 96 | s.* = undefined; | |
| 97 | } | |
| 98 | ||
| 99 | pub fn receiveMessage(s: *Server) !InMessage.Header { | |
| 100 | const Header = InMessage.Header; | |
| 101 | const fifo = &s.receive_fifo; | |
| 102 | ||
| 103 | while (true) { | |
| 104 | const buf = fifo.readableSlice(0); | |
| 105 | assert(fifo.readableLength() == buf.len); | |
| 106 | if (buf.len >= @sizeOf(Header)) { | |
| 107 | const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); | |
| 108 | ||
| 109 | if (buf.len - @sizeOf(Header) >= header.bytes_len) { | |
| 110 | const result = header.*; | |
| 111 | fifo.discard(@sizeOf(Header)); | |
| 112 | return result; | |
| 113 | } else { | |
| 114 | const needed = header.bytes_len - (buf.len - @sizeOf(Header)); | |
| 115 | const write_buffer = try fifo.writableWithSize(needed); | |
| 116 | const amt = try s.in.read(write_buffer); | |
| 117 | fifo.update(amt); | |
| 118 | continue; | |
| 119 | } | |
| 120 | } | |
| 121 | ||
| 122 | const write_buffer = try fifo.writableWithSize(256); | |
| 123 | const amt = try s.in.read(write_buffer); | |
| 124 | fifo.update(amt); | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | pub fn receiveBody_u32(s: *Server) !u32 { | |
| 129 | const fifo = &s.receive_fifo; | |
| 130 | const buf = fifo.readableSlice(0); | |
| 131 | const result = @ptrCast(*align(1) const u32, buf[0..4]).*; | |
| 132 | fifo.discard(4); | |
| 133 | return result; | |
| 134 | } | |
| 135 | ||
| 136 | pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { | |
| 137 | return s.serveMessage(.{ | |
| 138 | .tag = tag, | |
| 139 | .bytes_len = @intCast(u32, msg.len), | |
| 140 | }, &.{msg}); | |
| 141 | } | |
| 142 | ||
| 143 | pub fn serveMessage( | |
| 144 | s: *const Server, | |
| 145 | header: OutMessage.Header, | |
| 146 | bufs: []const []const u8, | |
| 147 | ) !void { | |
| 148 | var iovecs: [10]std.os.iovec_const = undefined; | |
| 149 | iovecs[0] = .{ | |
| 150 | .iov_base = @ptrCast([*]const u8, &header), | |
| 151 | .iov_len = @sizeOf(OutMessage.Header), | |
| 152 | }; | |
| 153 | for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { | |
| 154 | iovec.* = .{ | |
| 155 | .iov_base = buf.ptr, | |
| 156 | .iov_len = buf.len, | |
| 157 | }; | |
| 158 | } | |
| 159 | try s.out.writevAll(iovecs[0 .. bufs.len + 1]); | |
| 160 | } | |
| 161 | ||
| 162 | pub fn serveEmitBinPath( | |
| 163 | s: *Server, | |
| 164 | fs_path: []const u8, | |
| 165 | header: OutMessage.EmitBinPath, | |
| 166 | ) !void { | |
| 167 | try s.serveMessage(.{ | |
| 168 | .tag = .emit_bin_path, | |
| 169 | .bytes_len = @intCast(u32, fs_path.len + @sizeOf(OutMessage.EmitBinPath)), | |
| 170 | }, &.{ | |
| 171 | std.mem.asBytes(&header), | |
| 172 | fs_path, | |
| 173 | }); | |
| 174 | } | |
| 175 | ||
| 176 | pub fn serveTestResults( | |
| 177 | s: *Server, | |
| 178 | msg: OutMessage.TestResults, | |
| 179 | ) !void { | |
| 180 | try s.serveMessage(.{ | |
| 181 | .tag = .test_results, | |
| 182 | .bytes_len = @intCast(u32, @sizeOf(OutMessage.TestResults)), | |
| 183 | }, &.{ | |
| 184 | std.mem.asBytes(&msg), | |
| 185 | }); | |
| 186 | } | |
| 187 | ||
| 188 | pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { | |
| 189 | const eb_hdr: OutMessage.ErrorBundle = .{ | |
| 190 | .extra_len = @intCast(u32, error_bundle.extra.len), | |
| 191 | .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len), | |
| 192 | }; | |
| 193 | const bytes_len = @sizeOf(OutMessage.ErrorBundle) + | |
| 194 | 4 * error_bundle.extra.len + error_bundle.string_bytes.len; | |
| 195 | try s.serveMessage(.{ | |
| 196 | .tag = .error_bundle, | |
| 197 | .bytes_len = @intCast(u32, bytes_len), | |
| 198 | }, &.{ | |
| 199 | std.mem.asBytes(&eb_hdr), | |
| 200 | // TODO: implement @ptrCast between slices changing the length | |
| 201 | std.mem.sliceAsBytes(error_bundle.extra), | |
| 202 | error_bundle.string_bytes, | |
| 203 | }); | |
| 204 | } | |
| 205 | ||
| 206 | pub const TestMetadata = struct { | |
| 207 | names: []const u32, | |
| 208 | async_frame_sizes: []const u32, | |
| 209 | expected_panic_msgs: []const u32, | |
| 210 | string_bytes: []const u8, | |
| 211 | }; | |
| 212 | ||
| 213 | pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { | |
| 214 | const header: OutMessage.TestMetadata = .{ | |
| 215 | .tests_len = @intCast(u32, test_metadata.names.len), | |
| 216 | .string_bytes_len = @intCast(u32, test_metadata.string_bytes.len), | |
| 217 | }; | |
| 218 | const bytes_len = @sizeOf(OutMessage.TestMetadata) + | |
| 219 | 3 * 4 * test_metadata.names.len + test_metadata.string_bytes.len; | |
| 220 | return s.serveMessage(.{ | |
| 221 | .tag = .test_metadata, | |
| 222 | .bytes_len = @intCast(u32, bytes_len), | |
| 223 | }, &.{ | |
| 224 | std.mem.asBytes(&header), | |
| 225 | // TODO: implement @ptrCast between slices changing the length | |
| 226 | std.mem.sliceAsBytes(test_metadata.names), | |
| 227 | std.mem.sliceAsBytes(test_metadata.async_frame_sizes), | |
| 228 | std.mem.sliceAsBytes(test_metadata.expected_panic_msgs), | |
| 229 | test_metadata.string_bytes, | |
| 230 | }); | |
| 231 | } | |
| 232 | ||
| 233 | const OutMessage = std.zig.Server.Message; | |
| 234 | const InMessage = std.zig.Client.Message; | |
| 235 | ||
| 236 | const Server = @This(); | |
| 237 | const std = @import("std"); | |
| 238 | const Allocator = std.mem.Allocator; | |
| 239 | const assert = std.debug.assert; |
lib/test_runner.zig+127-45| ... | ... | @@ -8,14 +8,130 @@ pub const std_options = struct { |
| 8 | 8 | }; |
| 9 | 9 | |
| 10 | 10 | var log_err_count: usize = 0; |
| 11 | var cmdline_buffer: [4096]u8 = undefined; | |
| 12 | var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer); | |
| 11 | 13 | |
| 12 | 14 | pub fn main() void { |
| 13 | if (builtin.zig_backend != .stage1 and | |
| 14 | builtin.zig_backend != .stage2_llvm and | |
| 15 | builtin.zig_backend != .stage2_c) | |
| 15 | if (builtin.zig_backend == .stage2_wasm or | |
| 16 | builtin.zig_backend == .stage2_x86_64 or | |
| 17 | builtin.zig_backend == .stage2_aarch64) | |
| 16 | 18 | { |
| 17 | return main2() catch @panic("test failure"); | |
| 19 | return mainSimple() catch @panic("test failure"); | |
| 20 | } | |
| 21 | ||
| 22 | const args = std.process.argsAlloc(fba.allocator()) catch | |
| 23 | @panic("unable to parse command line args"); | |
| 24 | ||
| 25 | var listen = false; | |
| 26 | ||
| 27 | for (args[1..]) |arg| { | |
| 28 | if (std.mem.eql(u8, arg, "--listen=-")) { | |
| 29 | listen = true; | |
| 30 | } else { | |
| 31 | @panic("unrecognized command line argument"); | |
| 32 | } | |
| 33 | } | |
| 34 | ||
| 35 | if (listen) { | |
| 36 | return mainServer(); | |
| 37 | } else { | |
| 38 | return mainTerminal(); | |
| 39 | } | |
| 40 | } | |
| 41 | ||
| 42 | fn mainServer() void { | |
| 43 | return mainServerFallible() catch @panic("internal test runner failure"); | |
| 44 | } | |
| 45 | ||
| 46 | fn mainServerFallible() !void { | |
| 47 | var server = try std.zig.Server.init(.{ | |
| 48 | .gpa = fba.allocator(), | |
| 49 | .in = std.io.getStdIn(), | |
| 50 | .out = std.io.getStdOut(), | |
| 51 | .zig_version = builtin.zig_version_string, | |
| 52 | }); | |
| 53 | defer server.deinit(); | |
| 54 | ||
| 55 | while (true) { | |
| 56 | const hdr = try server.receiveMessage(); | |
| 57 | switch (hdr.tag) { | |
| 58 | .exit => { | |
| 59 | return std.process.exit(0); | |
| 60 | }, | |
| 61 | .query_test_metadata => { | |
| 62 | std.testing.allocator_instance = .{}; | |
| 63 | defer if (std.testing.allocator_instance.deinit()) { | |
| 64 | @panic("internal test runner memory leak"); | |
| 65 | }; | |
| 66 | ||
| 67 | var string_bytes: std.ArrayListUnmanaged(u8) = .{}; | |
| 68 | defer string_bytes.deinit(std.testing.allocator); | |
| 69 | try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null. | |
| 70 | ||
| 71 | const test_fns = builtin.test_functions; | |
| 72 | const names = try std.testing.allocator.alloc(u32, test_fns.len); | |
| 73 | defer std.testing.allocator.free(names); | |
| 74 | const async_frame_sizes = try std.testing.allocator.alloc(u32, test_fns.len); | |
| 75 | defer std.testing.allocator.free(async_frame_sizes); | |
| 76 | const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len); | |
| 77 | defer std.testing.allocator.free(expected_panic_msgs); | |
| 78 | ||
| 79 | for (test_fns, names, async_frame_sizes, expected_panic_msgs) |test_fn, *name, *async_frame_size, *expected_panic_msg| { | |
| 80 | name.* = @intCast(u32, string_bytes.items.len); | |
| 81 | try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1); | |
| 82 | string_bytes.appendSliceAssumeCapacity(test_fn.name); | |
| 83 | string_bytes.appendAssumeCapacity(0); | |
| 84 | ||
| 85 | async_frame_size.* = @intCast(u32, test_fn.async_frame_size orelse 0); | |
| 86 | expected_panic_msg.* = 0; | |
| 87 | } | |
| 88 | ||
| 89 | try server.serveTestMetadata(.{ | |
| 90 | .names = names, | |
| 91 | .async_frame_sizes = async_frame_sizes, | |
| 92 | .expected_panic_msgs = expected_panic_msgs, | |
| 93 | .string_bytes = string_bytes.items, | |
| 94 | }); | |
| 95 | }, | |
| 96 | ||
| 97 | .run_test => { | |
| 98 | std.testing.allocator_instance = .{}; | |
| 99 | const index = try server.receiveBody_u32(); | |
| 100 | const test_fn = builtin.test_functions[index]; | |
| 101 | if (test_fn.async_frame_size != null) | |
| 102 | @panic("TODO test runner implement async tests"); | |
| 103 | var fail = false; | |
| 104 | var skip = false; | |
| 105 | var leak = false; | |
| 106 | test_fn.func() catch |err| switch (err) { | |
| 107 | error.SkipZigTest => skip = true, | |
| 108 | else => { | |
| 109 | fail = true; | |
| 110 | if (@errorReturnTrace()) |trace| { | |
| 111 | std.debug.dumpStackTrace(trace.*); | |
| 112 | } | |
| 113 | }, | |
| 114 | }; | |
| 115 | leak = std.testing.allocator_instance.deinit(); | |
| 116 | try server.serveTestResults(.{ | |
| 117 | .index = index, | |
| 118 | .flags = .{ | |
| 119 | .fail = fail, | |
| 120 | .skip = skip, | |
| 121 | .leak = leak, | |
| 122 | }, | |
| 123 | }); | |
| 124 | }, | |
| 125 | ||
| 126 | else => { | |
| 127 | std.debug.print("unsupported message: {x}", .{@enumToInt(hdr.tag)}); | |
| 128 | std.process.exit(1); | |
| 129 | }, | |
| 130 | } | |
| 18 | 131 | } |
| 132 | } | |
| 133 | ||
| 134 | fn mainTerminal() void { | |
| 19 | 135 | const test_fn_list = builtin.test_functions; |
| 20 | 136 | var ok_count: usize = 0; |
| 21 | 137 | var skip_count: usize = 0; |
| ... | ... | @@ -118,51 +234,17 @@ pub fn log( |
| 118 | 234 | } |
| 119 | 235 | } |
| 120 | 236 | |
| 121 | pub fn main2() anyerror!void { | |
| 122 | var skipped: usize = 0; | |
| 123 | var failed: usize = 0; | |
| 124 | // Simpler main(), exercising fewer language features, so that stage2 can handle it. | |
| 237 | /// Simpler main(), exercising fewer language features, so that | |
| 238 | /// work-in-progress backends can handle it. | |
| 239 | pub fn mainSimple() anyerror!void { | |
| 240 | //const stderr = std.io.getStdErr(); | |
| 125 | 241 | for (builtin.test_functions) |test_fn| { |
| 126 | 242 | test_fn.func() catch |err| { |
| 127 | 243 | if (err != error.SkipZigTest) { |
| 128 | failed += 1; | |
| 129 | } else { | |
| 130 | skipped += 1; | |
| 244 | //stderr.writeAll(test_fn.name) catch {}; | |
| 245 | //stderr.writeAll("\n") catch {}; | |
| 246 | return err; | |
| 131 | 247 | } |
| 132 | 248 | }; |
| 133 | 249 | } |
| 134 | if (builtin.zig_backend == .stage2_wasm or | |
| 135 | builtin.zig_backend == .stage2_x86_64 or | |
| 136 | builtin.zig_backend == .stage2_aarch64 or | |
| 137 | builtin.zig_backend == .stage2_llvm or | |
| 138 | builtin.zig_backend == .stage2_c) | |
| 139 | { | |
| 140 | const passed = builtin.test_functions.len - skipped - failed; | |
| 141 | const stderr = std.io.getStdErr(); | |
| 142 | writeInt(stderr, passed) catch {}; | |
| 143 | stderr.writeAll(" passed; ") catch {}; | |
| 144 | writeInt(stderr, skipped) catch {}; | |
| 145 | stderr.writeAll(" skipped; ") catch {}; | |
| 146 | writeInt(stderr, failed) catch {}; | |
| 147 | stderr.writeAll(" failed.\n") catch {}; | |
| 148 | } | |
| 149 | if (failed != 0) { | |
| 150 | return error.TestsFailed; | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | fn writeInt(stderr: std.fs.File, int: usize) anyerror!void { | |
| 155 | const base = 10; | |
| 156 | var buf: [100]u8 = undefined; | |
| 157 | var a: usize = int; | |
| 158 | var index: usize = buf.len; | |
| 159 | while (true) { | |
| 160 | const digit = a % base; | |
| 161 | index -= 1; | |
| 162 | buf[index] = std.fmt.digitToChar(@intCast(u8, digit), .lower); | |
| 163 | a /= base; | |
| 164 | if (a == 0) break; | |
| 165 | } | |
| 166 | const slice = buf[index..]; | |
| 167 | try stderr.writeAll(slice); | |
| 168 | 250 | } |
src/Server.zig deleted-113| ... | ... | @@ -1,113 +0,0 @@ |
| 1 | in: std.fs.File, | |
| 2 | out: std.fs.File, | |
| 3 | receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), | |
| 4 | ||
| 5 | pub const Options = struct { | |
| 6 | gpa: Allocator, | |
| 7 | in: std.fs.File, | |
| 8 | out: std.fs.File, | |
| 9 | }; | |
| 10 | ||
| 11 | pub fn init(options: Options) !Server { | |
| 12 | var s: Server = .{ | |
| 13 | .in = options.in, | |
| 14 | .out = options.out, | |
| 15 | .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa), | |
| 16 | }; | |
| 17 | try s.serveStringMessage(.zig_version, build_options.version); | |
| 18 | return s; | |
| 19 | } | |
| 20 | ||
| 21 | pub fn deinit(s: *Server) void { | |
| 22 | s.receive_fifo.deinit(); | |
| 23 | s.* = undefined; | |
| 24 | } | |
| 25 | ||
| 26 | pub fn receiveMessage(s: *Server) !InMessage.Header { | |
| 27 | const Header = InMessage.Header; | |
| 28 | const fifo = &s.receive_fifo; | |
| 29 | ||
| 30 | while (true) { | |
| 31 | const buf = fifo.readableSlice(0); | |
| 32 | assert(fifo.readableLength() == buf.len); | |
| 33 | if (buf.len >= @sizeOf(Header)) { | |
| 34 | const header = @ptrCast(*align(1) const Header, buf[0..@sizeOf(Header)]); | |
| 35 | if (header.bytes_len != 0) | |
| 36 | return error.InvalidClientMessage; | |
| 37 | const result = header.*; | |
| 38 | fifo.discard(@sizeOf(Header)); | |
| 39 | return result; | |
| 40 | } | |
| 41 | ||
| 42 | const write_buffer = try fifo.writableWithSize(256); | |
| 43 | const amt = try s.in.read(write_buffer); | |
| 44 | fifo.update(amt); | |
| 45 | } | |
| 46 | } | |
| 47 | ||
| 48 | pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { | |
| 49 | return s.serveMessage(.{ | |
| 50 | .tag = tag, | |
| 51 | .bytes_len = @intCast(u32, msg.len), | |
| 52 | }, &.{msg}); | |
| 53 | } | |
| 54 | ||
| 55 | pub fn serveMessage( | |
| 56 | s: *const Server, | |
| 57 | header: OutMessage.Header, | |
| 58 | bufs: []const []const u8, | |
| 59 | ) !void { | |
| 60 | var iovecs: [10]std.os.iovec_const = undefined; | |
| 61 | iovecs[0] = .{ | |
| 62 | .iov_base = @ptrCast([*]const u8, &header), | |
| 63 | .iov_len = @sizeOf(OutMessage.Header), | |
| 64 | }; | |
| 65 | for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { | |
| 66 | iovec.* = .{ | |
| 67 | .iov_base = buf.ptr, | |
| 68 | .iov_len = buf.len, | |
| 69 | }; | |
| 70 | } | |
| 71 | try s.out.writevAll(iovecs[0 .. bufs.len + 1]); | |
| 72 | } | |
| 73 | ||
| 74 | pub fn serveEmitBinPath( | |
| 75 | s: *Server, | |
| 76 | fs_path: []const u8, | |
| 77 | header: std.zig.Server.Message.EmitBinPath, | |
| 78 | ) !void { | |
| 79 | try s.serveMessage(.{ | |
| 80 | .tag = .emit_bin_path, | |
| 81 | .bytes_len = @intCast(u32, fs_path.len + @sizeOf(std.zig.Server.Message.EmitBinPath)), | |
| 82 | }, &.{ | |
| 83 | std.mem.asBytes(&header), | |
| 84 | fs_path, | |
| 85 | }); | |
| 86 | } | |
| 87 | ||
| 88 | pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { | |
| 89 | const eb_hdr: std.zig.Server.Message.ErrorBundle = .{ | |
| 90 | .extra_len = @intCast(u32, error_bundle.extra.len), | |
| 91 | .string_bytes_len = @intCast(u32, error_bundle.string_bytes.len), | |
| 92 | }; | |
| 93 | const bytes_len = @sizeOf(std.zig.Server.Message.ErrorBundle) + | |
| 94 | 4 * error_bundle.extra.len + error_bundle.string_bytes.len; | |
| 95 | try s.serveMessage(.{ | |
| 96 | .tag = .error_bundle, | |
| 97 | .bytes_len = @intCast(u32, bytes_len), | |
| 98 | }, &.{ | |
| 99 | std.mem.asBytes(&eb_hdr), | |
| 100 | // TODO: implement @ptrCast between slices changing the length | |
| 101 | std.mem.sliceAsBytes(error_bundle.extra), | |
| 102 | error_bundle.string_bytes, | |
| 103 | }); | |
| 104 | } | |
| 105 | ||
| 106 | const OutMessage = std.zig.Server.Message; | |
| 107 | const InMessage = std.zig.Client.Message; | |
| 108 | ||
| 109 | const Server = @This(); | |
| 110 | const std = @import("std"); | |
| 111 | const build_options = @import("build_options"); | |
| 112 | const Allocator = std.mem.Allocator; | |
| 113 | const assert = std.debug.assert; |
src/main.zig+5-16| ... | ... | @@ -10,6 +10,7 @@ const ArrayList = std.ArrayList; |
| 10 | 10 | const Ast = std.zig.Ast; |
| 11 | 11 | const warn = std.log.warn; |
| 12 | 12 | const ThreadPool = std.Thread.Pool; |
| 13 | const cleanExit = std.process.cleanExit; | |
| 13 | 14 | |
| 14 | 15 | const tracy = @import("tracy.zig"); |
| 15 | 16 | const Compilation = @import("Compilation.zig"); |
| ... | ... | @@ -26,7 +27,7 @@ const target_util = @import("target.zig"); |
| 26 | 27 | const crash_report = @import("crash_report.zig"); |
| 27 | 28 | const Module = @import("Module.zig"); |
| 28 | 29 | const AstGen = @import("AstGen.zig"); |
| 29 | const Server = @import("Server.zig"); | |
| 30 | const Server = std.zig.Server; | |
| 30 | 31 | |
| 31 | 32 | pub const std_options = struct { |
| 32 | 33 | pub const wasiCwd = wasi_cwd; |
| ... | ... | @@ -3545,6 +3546,7 @@ fn serve( |
| 3545 | 3546 | .gpa = gpa, |
| 3546 | 3547 | .in = in, |
| 3547 | 3548 | .out = out, |
| 3549 | .zig_version = build_options.version, | |
| 3548 | 3550 | }); |
| 3549 | 3551 | defer server.deinit(); |
| 3550 | 3552 | |
| ... | ... | @@ -3656,8 +3658,8 @@ fn serve( |
| 3656 | 3658 | ); |
| 3657 | 3659 | } |
| 3658 | 3660 | }, |
| 3659 | _ => { | |
| 3660 | @panic("TODO unrecognized message from client"); | |
| 3661 | else => { | |
| 3662 | fatal("unrecognized message from client: 0x{x}", .{@enumToInt(hdr.tag)}); | |
| 3661 | 3663 | }, |
| 3662 | 3664 | } |
| 3663 | 3665 | } |
| ... | ... | @@ -5624,19 +5626,6 @@ fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.Nat |
| 5624 | 5626 | return std.zig.system.NativeTargetInfo.detect(cross_target); |
| 5625 | 5627 | } |
| 5626 | 5628 | |
| 5627 | /// Indicate that we are now terminating with a successful exit code. | |
| 5628 | /// In debug builds, this is a no-op, so that the calling code's | |
| 5629 | /// cleanup mechanisms are tested and so that external tools that | |
| 5630 | /// check for resource leaks can be accurate. In release builds, this | |
| 5631 | /// calls exit(0), and does not return. | |
| 5632 | pub fn cleanExit() void { | |
| 5633 | if (builtin.mode == .Debug) { | |
| 5634 | return; | |
| 5635 | } else { | |
| 5636 | process.exit(0); | |
| 5637 | } | |
| 5638 | } | |
| 5639 | ||
| 5640 | 5629 | const usage_ast_check = |
| 5641 | 5630 | \\Usage: zig ast-check [file] |
| 5642 | 5631 | \\ |
src/objcopy.zig+5-4| ... | ... | @@ -8,8 +8,8 @@ const assert = std.debug.assert; |
| 8 | 8 | |
| 9 | 9 | const main = @import("main.zig"); |
| 10 | 10 | const fatal = main.fatal; |
| 11 | const cleanExit = main.cleanExit; | |
| 12 | const Server = @import("Server.zig"); | |
| 11 | const Server = std.zig.Server; | |
| 12 | const build_options = @import("build_options"); | |
| 13 | 13 | |
| 14 | 14 | pub fn cmdObjCopy( |
| 15 | 15 | gpa: Allocator, |
| ... | ... | @@ -116,6 +116,7 @@ pub fn cmdObjCopy( |
| 116 | 116 | .gpa = gpa, |
| 117 | 117 | .in = std.io.getStdIn(), |
| 118 | 118 | .out = std.io.getStdOut(), |
| 119 | .zig_version = build_options.version, | |
| 119 | 120 | }); |
| 120 | 121 | defer server.deinit(); |
| 121 | 122 | |
| ... | ... | @@ -124,7 +125,7 @@ pub fn cmdObjCopy( |
| 124 | 125 | const hdr = try server.receiveMessage(); |
| 125 | 126 | switch (hdr.tag) { |
| 126 | 127 | .exit => { |
| 127 | return cleanExit(); | |
| 128 | return std.process.cleanExit(); | |
| 128 | 129 | }, |
| 129 | 130 | .update => { |
| 130 | 131 | if (seen_update) { |
| ... | ... | @@ -144,7 +145,7 @@ pub fn cmdObjCopy( |
| 144 | 145 | } |
| 145 | 146 | } |
| 146 | 147 | } |
| 147 | return cleanExit(); | |
| 148 | return std.process.cleanExit(); | |
| 148 | 149 | } |
| 149 | 150 | |
| 150 | 151 | const usage = |
test/link/common_symbols/build.zig+1-1| ... | ... | @@ -24,5 +24,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 24 | 24 | }); |
| 25 | 25 | test_exe.linkLibrary(lib_a); |
| 26 | 26 | |
| 27 | test_step.dependOn(&test_exe.step); | |
| 27 | test_step.dependOn(&test_exe.run().step); | |
| 28 | 28 | } |
test/link/common_symbols_alignment/build.zig+1-1| ... | ... | @@ -24,5 +24,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 24 | 24 | }); |
| 25 | 25 | test_exe.linkLibrary(lib_a); |
| 26 | 26 | |
| 27 | test_step.dependOn(&test_exe.step); | |
| 27 | test_step.dependOn(&test_exe.run().step); | |
| 28 | 28 | } |
test/link/interdependent_static_c_libs/build.zig+1-1| ... | ... | @@ -35,5 +35,5 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 35 | 35 | test_exe.linkLibrary(lib_b); |
| 36 | 36 | test_exe.addIncludePath("."); |
| 37 | 37 | |
| 38 | test_step.dependOn(&test_exe.step); | |
| 38 | test_step.dependOn(&test_exe.run().step); | |
| 39 | 39 | } |
test/link/macho/tls/build.zig+4-1| ... | ... | @@ -32,5 +32,8 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize |
| 32 | 32 | test_exe.linkLibrary(lib); |
| 33 | 33 | test_exe.linkLibC(); |
| 34 | 34 | |
| 35 | test_step.dependOn(&test_exe.step); | |
| 35 | const run = test_exe.run(); | |
| 36 | run.skip_foreign_checks = true; | |
| 37 | ||
| 38 | test_step.dependOn(&run.step); | |
| 36 | 39 | } |
test/src/Cases.zig+4-7| ... | ... | @@ -547,15 +547,12 @@ pub fn lowerToBuildSteps( |
| 547 | 547 | parent_step.dependOn(&artifact.step); |
| 548 | 548 | }, |
| 549 | 549 | .Execution => |expected_stdout| { |
| 550 | if (case.is_test) { | |
| 551 | parent_step.dependOn(&artifact.step); | |
| 552 | } else { | |
| 553 | const run = b.addRunArtifact(artifact); | |
| 554 | run.skip_foreign_checks = true; | |
| 550 | const run = b.addRunArtifact(artifact); | |
| 551 | run.skip_foreign_checks = true; | |
| 552 | if (!case.is_test) { | |
| 555 | 553 | run.expectStdOutEqual(expected_stdout); |
| 556 | ||
| 557 | parent_step.dependOn(&run.step); | |
| 558 | 554 | } |
| 555 | parent_step.dependOn(&run.step); | |
| 559 | 556 | }, |
| 560 | 557 | .Header => @panic("TODO"), |
| 561 | 558 | } |
test/standalone/emit_asm_and_bin/build.zig+1-1| ... | ... | @@ -11,5 +11,5 @@ pub fn build(b: *std.Build) void { |
| 11 | 11 | main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; |
| 12 | 12 | main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; |
| 13 | 13 | |
| 14 | test_step.dependOn(&main.step); | |
| 14 | test_step.dependOn(&main.run().step); | |
| 15 | 15 | } |
test/standalone/global_linkage/build.zig+1-1| ... | ... | @@ -28,5 +28,5 @@ pub fn build(b: *std.Build) void { |
| 28 | 28 | main.linkLibrary(obj1); |
| 29 | 29 | main.linkLibrary(obj2); |
| 30 | 30 | |
| 31 | test_step.dependOn(&main.step); | |
| 31 | test_step.dependOn(&main.run().step); | |
| 32 | 32 | } |
test/standalone/issue_13970/build.zig+3-3| ... | ... | @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { |
| 17 | 17 | test2.setTestRunner("src/main.zig"); |
| 18 | 18 | test3.setTestRunner("src/main.zig"); |
| 19 | 19 | |
| 20 | test_step.dependOn(&test1.step); | |
| 21 | test_step.dependOn(&test2.step); | |
| 22 | test_step.dependOn(&test3.step); | |
| 20 | test_step.dependOn(&test1.run().step); | |
| 21 | test_step.dependOn(&test2.run().step); | |
| 22 | test_step.dependOn(&test3.run().step); | |
| 23 | 23 | } |
test/standalone/main_pkg_path/build.zig+1-1| ... | ... | @@ -9,5 +9,5 @@ pub fn build(b: *std.Build) void { |
| 9 | 9 | }); |
| 10 | 10 | test_exe.setMainPkgPath("."); |
| 11 | 11 | |
| 12 | test_step.dependOn(&test_exe.step); | |
| 12 | test_step.dependOn(&test_exe.run().step); | |
| 13 | 13 | } |
test/standalone/options/build.zig+1-1| ... | ... | @@ -20,5 +20,5 @@ pub fn build(b: *std.Build) void { |
| 20 | 20 | options.addOption([]const u8, "string", b.option([]const u8, "string", "s").?); |
| 21 | 21 | |
| 22 | 22 | const test_step = b.step("test", "Run unit tests"); |
| 23 | test_step.dependOn(&main.step); | |
| 23 | test_step.dependOn(&main.run().step); | |
| 24 | 24 | } |
test/standalone/pie/build.zig+1-1| ... | ... | @@ -17,5 +17,5 @@ pub fn build(b: *std.Build) void { |
| 17 | 17 | }); |
| 18 | 18 | main.pie = true; |
| 19 | 19 | |
| 20 | test_step.dependOn(&main.step); | |
| 20 | test_step.dependOn(&main.run().step); | |
| 21 | 21 | } |
test/standalone/static_c_lib/build.zig+1-1| ... | ... | @@ -21,5 +21,5 @@ pub fn build(b: *std.Build) void { |
| 21 | 21 | test_exe.linkLibrary(foo); |
| 22 | 22 | test_exe.addIncludePath("."); |
| 23 | 23 | |
| 24 | test_step.dependOn(&test_exe.step); | |
| 24 | test_step.dependOn(&test_exe.run().step); | |
| 25 | 25 | } |
test/standalone/test_runner_module_imports/build.zig+1-1| ... | ... | @@ -15,5 +15,5 @@ pub fn build(b: *std.Build) void { |
| 15 | 15 | t.addModule("module2", module2); |
| 16 | 16 | |
| 17 | 17 | const test_step = b.step("test", "Run unit tests"); |
| 18 | test_step.dependOn(&t.step); | |
| 18 | test_step.dependOn(&t.run().step); | |
| 19 | 19 | } |
test/standalone/test_runner_path/build.zig-1| ... | ... | @@ -8,7 +8,6 @@ pub fn build(b: *std.Build) void { |
| 8 | 8 | |
| 9 | 9 | const test_exe = b.addTest(.{ |
| 10 | 10 | .root_source_file = .{ .path = "test.zig" }, |
| 11 | .kind = .test_exe, | |
| 12 | 11 | }); |
| 13 | 12 | test_exe.test_runner = "test_runner.zig"; |
| 14 | 13 |
test/standalone/use_alias/build.zig+1-1| ... | ... | @@ -12,5 +12,5 @@ pub fn build(b: *std.Build) void { |
| 12 | 12 | }); |
| 13 | 13 | main.addIncludePath("."); |
| 14 | 14 | |
| 15 | test_step.dependOn(&main.step); | |
| 15 | test_step.dependOn(&main.run().step); | |
| 16 | 16 | } |
test/tests.zig+16-11| ... | ... | @@ -596,7 +596,7 @@ pub fn addStandaloneTests( |
| 596 | 596 | }); |
| 597 | 597 | if (case.link_libc) exe.linkLibC(); |
| 598 | 598 | |
| 599 | step.dependOn(&exe.step); | |
| 599 | step.dependOn(&exe.run().step); | |
| 600 | 600 | } |
| 601 | 601 | } |
| 602 | 602 | } |
| ... | ... | @@ -981,14 +981,6 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { |
| 981 | 981 | }); |
| 982 | 982 | const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; |
| 983 | 983 | const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; |
| 984 | these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ | |
| 985 | options.name, | |
| 986 | triple_prefix, | |
| 987 | @tagName(test_target.optimize_mode), | |
| 988 | libc_prefix, | |
| 989 | single_threaded_txt, | |
| 990 | backend_txt, | |
| 991 | })); | |
| 992 | 984 | these_tests.single_threaded = test_target.single_threaded; |
| 993 | 985 | these_tests.setFilter(options.test_filter); |
| 994 | 986 | if (test_target.link_libc) { |
| ... | ... | @@ -1014,7 +1006,18 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { |
| 1014 | 1006 | }, |
| 1015 | 1007 | }; |
| 1016 | 1008 | |
| 1017 | step.dependOn(&these_tests.step); | |
| 1009 | const run = these_tests.run(); | |
| 1010 | run.skip_foreign_checks = true; | |
| 1011 | run.setName(b.fmt("run test {s}-{s}-{s}-{s}-{s}-{s}", .{ | |
| 1012 | options.name, | |
| 1013 | triple_prefix, | |
| 1014 | @tagName(test_target.optimize_mode), | |
| 1015 | libc_prefix, | |
| 1016 | single_threaded_txt, | |
| 1017 | backend_txt, | |
| 1018 | })); | |
| 1019 | ||
| 1020 | step.dependOn(&run.step); | |
| 1018 | 1021 | } |
| 1019 | 1022 | return step; |
| 1020 | 1023 | } |
| ... | ... | @@ -1053,7 +1056,9 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S |
| 1053 | 1056 | @tagName(optimize_mode), |
| 1054 | 1057 | })); |
| 1055 | 1058 | |
| 1056 | step.dependOn(&test_step.step); | |
| 1059 | const run = test_step.run(); | |
| 1060 | run.skip_foreign_checks = true; | |
| 1061 | step.dependOn(&run.step); | |
| 1057 | 1062 | } |
| 1058 | 1063 | } |
| 1059 | 1064 | return step; |