diff --git a/build.zig b/build.zig index 51089664ad..7e9920bda2 100644 --- a/build.zig +++ b/build.zig @@ -107,6 +107,12 @@ pub fn build(b: *Builder) !void { const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false; const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc"); + + test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); + test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); + test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled); + test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir); + const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); test_stage2_step.dependOn(&test_stage2.step); test_step.dependOn(test_stage2_step); diff --git a/lib/std/build.zig b/lib/std/build.zig index c85534ba7d..8bfd230263 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1857,10 +1857,10 @@ pub const LibExeObjStep = struct { zig_args.append(builder.zig_exe) catch unreachable; const cmd = switch (self.kind) { - Kind.Lib => "build-lib", - Kind.Exe => "build-exe", - Kind.Obj => "build-obj", - Kind.Test => "test", + .Lib => "build-lib", + .Exe => "build-exe", + .Obj => "build-obj", + .Test => "test", }; zig_args.append(cmd) catch unreachable; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 6c30ef162f..e075de23bb 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -4,6 +4,11 @@ const Module = @import("Module.zig"); const Allocator = std.mem.Allocator; const zir = @import("zir.zig"); const Package = @import("Package.zig"); +const build_options = @import("build_options"); +const enable_qemu: bool = build_options.enable_qemu; +const enable_wine: bool = build_options.enable_wine; +const enable_wasmtime: bool = build_options.enable_wasmtime; +const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir; const cheader = @embedFile("cbe.h"); @@ -401,8 +406,6 @@ pub const TestContext = struct { const root_node = try progress.start("tests", self.cases.items.len); defer root_node.end(); - const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{}); - for (self.cases.items) |case| { std.testing.base_allocator_instance.reset(); @@ -415,13 +418,19 @@ pub const TestContext = struct { progress.initial_delay_ns = 0; progress.refresh_rate_ns = 0; - const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target); - try self.runOneCase(std.testing.allocator, &prg_node, case, info.target); + try self.runOneCase(std.testing.allocator, &prg_node, case); try std.testing.allocator_instance.validate(); } } - fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case, target: std.Target) !void { + fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void { + const target_info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target); + const target = target_info.target; + + var arena_allocator = std.heap.ArenaAllocator.init(allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); @@ -429,8 +438,7 @@ pub const TestContext = struct { const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); defer root_pkg.destroy(); - const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null); - defer allocator.free(bin_name); + const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null); var module = try Module.init(allocator, .{ .root_name = "test_case", @@ -485,8 +493,7 @@ pub const TestContext = struct { // incremental updates var file = try tmp.dir.openFile(bin_name, .{ .read = true }); defer file.close(); - var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!"); - defer allocator.free(out); + var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!"); if (expected_output.len != out.len) { std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out }); @@ -533,8 +540,7 @@ pub const TestContext = struct { var test_node = update_node.start("assert", null); test_node.activate(); defer test_node.end(); - var handled_errors = try allocator.alloc(bool, e.len); - defer allocator.free(handled_errors); + var handled_errors = try arena.alloc(bool, e.len); for (handled_errors) |*h| { h.* = false; } @@ -569,10 +575,56 @@ pub const TestContext = struct { exec_node.activate(); defer exec_node.end(); - try module.makeBinFileExecutable(); + var argv = std.ArrayList([]const u8).init(allocator); + defer argv.deinit(); - const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name}); - defer allocator.free(exe_path); + const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name}); + + switch (case.target.getExternalExecutor()) { + .native => try argv.append(exe_path), + .unavailable => return, // No executor available; pass test. + + .qemu => |qemu_bin_name| { + if (enable_qemu) qemu: { + // TODO Ability for test cases to specify whether to link libc. + const need_cross_glibc = false; // target.isGnuLibC() and self.is_linking_libc; + const glibc_dir_arg = if (need_cross_glibc) + glibc_multi_install_dir orelse break :qemu + else + null; + try argv.append(qemu_bin_name); + if (glibc_dir_arg) |dir| { + const linux_triple = try target.linuxTriple(arena); + const full_dir = try std.fs.path.join(arena, &[_][]const u8{ + dir, + linux_triple, + }); + + try argv.append("-L"); + try argv.append(full_dir); + } + try argv.append(exe_path); + } + return; // QEMU not available; pass test. + }, + + .wine => |wine_bin_name| if (enable_wine) { + try argv.append(wine_bin_name); + try argv.append(exe_path); + } else { + return; // Wine not available; pass test. + }, + + .wasmtime => |wasmtime_bin_name| if (enable_wasmtime) { + try argv.append(wasmtime_bin_name); + try argv.append("--dir=."); + try argv.append(exe_path); + } else { + return; // wasmtime not available; pass test. + }, + } + + try module.makeBinFileExecutable(); break :x try std.ChildProcess.exec(.{ .allocator = allocator, diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index b124a4ef2d..1e4db06572 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -7,7 +7,7 @@ const linux_x64 = std.zig.CrossTarget{ .os_tag = .linux, }; -const riscv64 = std.zig.CrossTarget{ +const linux_riscv64 = std.zig.CrossTarget{ .cpu_arch = .riscv64, .os_tag = .linux, }; @@ -123,6 +123,42 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); } + + { + var case = ctx.exe("hello world", linux_riscv64); + // Regular old hello world + case.addCompareOutput( + \\export fn _start() noreturn { + \\ print(); + \\ + \\ exit(); + \\} + \\ + \\fn print() void { + \\ asm volatile ("ecall" + \\ : + \\ : [number] "{a7}" (64), + \\ [arg1] "{a0}" (1), + \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{a2}" ("Hello, World!\n".len) + \\ : "rcx", "r11", "memory" + \\ ); + \\ return; + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("ecall" + \\ : + \\ : [number] "{a7}" (94), + \\ [arg1] "{a0}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "Hello, World!\n", + ); + } { var case = ctx.exe("adding numbers at comptime", linux_x64); @@ -403,40 +439,4 @@ pub fn addCases(ctx: *TestContext) !void { "", ); } - - { - var case = ctx.exe("hello world with updates", riscv64); - // Regular old hello world - case.addCompareOutput( - \\export fn _start() noreturn { - \\ print(); - \\ - \\ exit(); - \\} - \\ - \\fn print() void { - \\ asm volatile ("ecall" - \\ : - \\ : [number] "{a7}" (64), - \\ [arg1] "{a0}" (1), - \\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")), - \\ [arg3] "{a2}" ("Hello, World!\n".len) - \\ : "rcx", "r11", "memory" - \\ ); - \\ return; - \\} - \\ - \\fn exit() noreturn { - \\ asm volatile ("ecall" - \\ : - \\ : [number] "{a7}" (94), - \\ [arg1] "{a0}" (0) - \\ : "rcx", "r11", "memory" - \\ ); - \\ unreachable; - \\} - , - "Hello, World!\n", - ); - } }