Commit c594f8dc26

Andrew Kelley <andrew@ziglang.org>
2020-08-05 00:32:16
stage2 tests: support the -Denable-qemu options and friends
1 parent 02d09d1
Changed files (4)
lib
src-self-hosted
test
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;
 
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(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.
+                            },
+                        }
 
-                        const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
-                        defer allocator.free(exe_path);
+                        try module.makeBinFileExecutable();
 
                         break :x try std.ChildProcess.exec(.{
                             .allocator = allocator,
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",
-        );
-    }
 }
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);