Commit 38edef35bf

Jakub Konka <kubkon@jakubkonka.com>
2022-05-28 11:44:53
test: introduce link(er) tests - builds on standalone tests
1 parent 74ed7c1
Changed files (43)
lib
std
test
lib/std/build/RunStep.zig
@@ -149,6 +149,7 @@ fn make(step: *Step) !void {
     const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root;
 
     var argv_list = ArrayList([]const u8).init(self.builder.allocator);
+
     for (self.argv.items) |arg| {
         switch (arg) {
             .bytes => |bytes| try argv_list.append(bytes),
test/link/bss/build.zig
@@ -0,0 +1,14 @@
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: *Builder) void {
+    const mode = b.standardReleaseOptions();
+    const test_step = b.step("test", "Test");
+
+    const exe = b.addExecutable("bss", "main.zig");
+    b.default_step.dependOn(&exe.step);
+    exe.setBuildMode(mode);
+
+    const run = exe.run();
+    run.expectStdOutEqual("0, 1, 0\n");
+    test_step.dependOn(&run.step);
+}
test/link/bss/main.zig
@@ -0,0 +1,13 @@
+const std = @import("std");
+
+// Stress test zerofill layout
+var buffer: [0x1000000]u64 = undefined;
+
+pub fn main() anyerror!void {
+    buffer[0x10] = 1;
+    try std.io.getStdOut().writer().print("{d}, {d}, {d}\n", .{
+        buffer[0],
+        buffer[0x10],
+        buffer[0x1000000 - 1],
+    });
+}
test/standalone/link_common_symbols/a.c → test/link/common_symbols/a.c
File renamed without changes
test/standalone/link_common_symbols/b.c → test/link/common_symbols/b.c
File renamed without changes
test/standalone/link_common_symbols/build.zig → test/link/common_symbols/build.zig
File renamed without changes
test/standalone/link_common_symbols/c.c → test/link/common_symbols/c.c
File renamed without changes
test/standalone/link_common_symbols/main.zig → test/link/common_symbols/main.zig
File renamed without changes
test/standalone/link_common_symbols_alignment/a.c → test/link/common_symbols_alignment/a.c
File renamed without changes
test/standalone/link_common_symbols_alignment/build.zig → test/link/common_symbols_alignment/build.zig
File renamed without changes
test/standalone/link_common_symbols_alignment/main.zig → test/link/common_symbols_alignment/main.zig
File renamed without changes
test/link/dylib/a.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+char world[] = "world";
+
+char* hello() {
+  return "Hello";
+}
test/link/dylib/build.zig
@@ -0,0 +1,29 @@
+const std = @import("std");
+const Builder = std.build.Builder;
+
+pub fn build(b: *Builder) void {
+    const mode = b.standardReleaseOptions();
+
+    const test_step = b.step("test", "Test");
+
+    const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
+    dylib.setBuildMode(mode);
+    dylib.addCSourceFile("a.c", &.{});
+    dylib.linkLibC();
+    dylib.install();
+
+    const exe = b.addExecutable("main", null);
+    exe.setBuildMode(mode);
+    exe.addCSourceFile("main.c", &.{});
+    exe.linkSystemLibrary("a");
+    exe.linkLibC();
+    exe.addLibraryPath(b.pathFromRoot("zig-out/lib/"));
+    exe.addRPath(b.pathFromRoot("zig-out/lib"));
+
+    const run = exe.run();
+    run.cwd = b.pathFromRoot(".");
+    run.expectStdOutEqual("Hello world");
+
+    test_step.dependOn(b.getInstallStep());
+    test_step.dependOn(&run.step);
+}
test/link/dylib/main.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+char* hello();
+extern char world[];
+
+int main() {
+  printf("%s %s", hello(), world);
+  return 0;
+}
test/standalone/link_frameworks/build.zig → test/link/frameworks/build.zig
@@ -1,19 +1,8 @@
 const std = @import("std");
 const Builder = std.build.Builder;
-const CrossTarget = std.zig.CrossTarget;
-
-fn isRunnableTarget(t: CrossTarget) bool {
-    // TODO I think we might be able to run this on Linux via Darling.
-    // Add a check for that here, and return true if Darling is available.
-    if (t.isNative() and t.getOsTag() == .macos)
-        return true
-    else
-        return false;
-}
 
 pub fn build(b: *Builder) void {
     const mode = b.standardReleaseOptions();
-    const target = b.standardTargetOptions(.{});
 
     const test_step = b.step("test", "Test the program");
 
@@ -21,14 +10,11 @@ pub fn build(b: *Builder) void {
     b.default_step.dependOn(&exe.step);
     exe.addCSourceFile("main.c", &[0][]const u8{});
     exe.setBuildMode(mode);
-    exe.setTarget(target);
     exe.linkLibC();
     // TODO when we figure out how to ship framework stubs for cross-compilation,
     // populate paths to the sysroot here.
     exe.linkFramework("Cocoa");
 
-    if (isRunnableTarget(target)) {
-        const run_cmd = exe.run();
-        test_step.dependOn(&run_cmd.step);
-    }
+    const run_cmd = exe.run();
+    test_step.dependOn(&run_cmd.step);
 }
test/standalone/link_frameworks/main.c → test/link/frameworks/main.c
File renamed without changes
test/standalone/link_interdependent_static_c_libs/a.c → test/link/interdependent_static_c_libs/a.c
File renamed without changes
test/standalone/link_interdependent_static_c_libs/a.h → test/link/interdependent_static_c_libs/a.h
File renamed without changes
test/standalone/link_interdependent_static_c_libs/b.c → test/link/interdependent_static_c_libs/b.c
File renamed without changes
test/standalone/link_interdependent_static_c_libs/b.h → test/link/interdependent_static_c_libs/b.h
File renamed without changes
test/standalone/link_interdependent_static_c_libs/build.zig → test/link/interdependent_static_c_libs/build.zig
File renamed without changes
test/standalone/link_interdependent_static_c_libs/main.zig → test/link/interdependent_static_c_libs/main.zig
File renamed without changes
test/standalone/objc/build.zig → test/link/objc/build.zig
@@ -1,19 +1,8 @@
 const std = @import("std");
 const Builder = std.build.Builder;
-const CrossTarget = std.zig.CrossTarget;
-
-fn isRunnableTarget(t: CrossTarget) bool {
-    // TODO I think we might be able to run this on Linux via Darling.
-    // Add a check for that here, and return true if Darling is available.
-    if (t.isNative() and t.getOsTag() == .macos)
-        return true
-    else
-        return false;
-}
 
 pub fn build(b: *Builder) void {
     const mode = b.standardReleaseOptions();
-    const target = b.standardTargetOptions(.{});
 
     const test_step = b.step("test", "Test the program");
 
@@ -23,14 +12,11 @@ pub fn build(b: *Builder) void {
     exe.addCSourceFile("Foo.m", &[0][]const u8{});
     exe.addCSourceFile("test.m", &[0][]const u8{});
     exe.setBuildMode(mode);
-    exe.setTarget(target);
     exe.linkLibC();
     // TODO when we figure out how to ship framework stubs for cross-compilation,
     // populate paths to the sysroot here.
     exe.linkFramework("Foundation");
 
-    if (isRunnableTarget(target)) {
-        const run_cmd = exe.run();
-        test_step.dependOn(&run_cmd.step);
-    }
+    const run_cmd = exe.run();
+    test_step.dependOn(&run_cmd.step);
 }
test/standalone/objc/Foo.h → test/link/objc/Foo.h
File renamed without changes
test/standalone/objc/Foo.m → test/link/objc/Foo.m
File renamed without changes
test/standalone/objc/test.m → test/link/objc/test.m
File renamed without changes
test/standalone/objcpp/build.zig → test/link/objcpp/build.zig
@@ -1,19 +1,8 @@
 const std = @import("std");
 const Builder = std.build.Builder;
-const CrossTarget = std.zig.CrossTarget;
-
-fn isRunnableTarget(t: CrossTarget) bool {
-    // TODO I think we might be able to run this on Linux via Darling.
-    // Add a check for that here, and return true if Darling is available.
-    if (t.isNative() and t.getOsTag() == .macos)
-        return true
-    else
-        return false;
-}
 
 pub fn build(b: *Builder) void {
     const mode = b.standardReleaseOptions();
-    const target = b.standardTargetOptions(.{});
 
     const test_step = b.step("test", "Test the program");
 
@@ -23,14 +12,13 @@ pub fn build(b: *Builder) void {
     exe.addCSourceFile("Foo.mm", &[0][]const u8{});
     exe.addCSourceFile("test.mm", &[0][]const u8{});
     exe.setBuildMode(mode);
-    exe.setTarget(target);
     exe.linkLibCpp();
     // TODO when we figure out how to ship framework stubs for cross-compilation,
     // populate paths to the sysroot here.
     exe.linkFramework("Foundation");
 
-    if (isRunnableTarget(target)) {
-        const run_cmd = exe.run();
-        test_step.dependOn(&run_cmd.step);
-    }
+    const run_cmd = exe.run();
+    run_cmd.expectStdOutEqual("Hello from C++ and Zig");
+
+    test_step.dependOn(&run_cmd.step);
 }
test/standalone/objcpp/Foo.h → test/link/objcpp/Foo.h
File renamed without changes
test/standalone/objcpp/Foo.mm → test/link/objcpp/Foo.mm
File renamed without changes
test/standalone/objcpp/test.mm → test/link/objcpp/test.mm
File renamed without changes
test/standalone/link_static_lib_as_system_lib/a.c → test/link/static_lib_as_system_lib/a.c
File renamed without changes
test/standalone/link_static_lib_as_system_lib/a.h → test/link/static_lib_as_system_lib/a.h
File renamed without changes
test/standalone/link_static_lib_as_system_lib/build.zig → test/link/static_lib_as_system_lib/build.zig
File renamed without changes
test/standalone/link_static_lib_as_system_lib/main.zig → test/link/static_lib_as_system_lib/main.zig
File renamed without changes
test/link/tls/a.c
@@ -0,0 +1,5 @@
+_Thread_local int a;
+
+int getA() {
+  return a;
+}
test/standalone/link_import_tls_dylib/build.zig → test/link/tls/build.zig
@@ -6,10 +6,12 @@ pub fn build(b: *Builder) void {
     const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
     lib.setBuildMode(mode);
     lib.addCSourceFile("a.c", &.{});
+    lib.linkLibC();
 
     const test_exe = b.addTest("main.zig");
     test_exe.setBuildMode(mode);
     test_exe.linkLibrary(lib);
+    test_exe.linkLibC();
 
     const test_step = b.step("test", "Test it");
     test_step.dependOn(&test_exe.step);
test/link/tls/main.zig
@@ -0,0 +1,15 @@
+const std = @import("std");
+
+extern threadlocal var a: i32;
+extern fn getA() i32;
+
+fn getA2() i32 {
+    return a;
+}
+
+test {
+    a = 2;
+    try std.testing.expect(getA() == 2);
+    try std.testing.expect(2 == getA2());
+    try std.testing.expect(getA() == getA2());
+}
test/link.zig
@@ -0,0 +1,52 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const tests = @import("tests.zig");
+
+pub fn addCases(cases: *tests.StandaloneContext) void {
+    cases.addBuildFile("test/link/bss/build.zig", .{
+        .build_modes = false, // we only guarantee zerofill for undefined in Debug
+    });
+
+    cases.addBuildFile("test/link/dylib/build.zig", .{
+        .build_modes = true,
+    });
+
+    cases.addBuildFile("test/link/common_symbols/build.zig", .{
+        .build_modes = true,
+    });
+
+    cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{
+        .build_modes = true,
+    });
+
+    cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{
+        .build_modes = true,
+    });
+
+    cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{
+        .build_modes = true,
+    });
+
+    cases.addBuildFile("test/link/tls/build.zig", .{
+        .build_modes = true,
+    });
+
+    if (builtin.os.tag == .macos) {
+        cases.addBuildFile("test/link/frameworks/build.zig", .{
+            .build_modes = true,
+            .requires_macos_sdk = true,
+        });
+
+        // Try to build and run an Objective-C executable.
+        cases.addBuildFile("test/link/objc/build.zig", .{
+            .build_modes = true,
+            .requires_macos_sdk = true,
+        });
+
+        // Try to build and run an Objective-C++ executable.
+        cases.addBuildFile("test/link/objcpp/build.zig", .{
+            .build_modes = true,
+            .requires_macos_sdk = true,
+        });
+    }
+}
test/standalone.zig
@@ -13,31 +13,12 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
     cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{});
     cases.addBuildFile("test/standalone/shared_library/build.zig", .{});
     cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{});
-    if (builtin.os.tag == .macos) {
-        // Zig's macOS linker does not yet support LTO for LLVM IR files:
-        // https://github.com/ziglang/zig/issues/8680
-        cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{
-            .build_modes = false,
-            .cross_targets = true,
-        });
-    } else {
-        cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{
-            .build_modes = true,
-            .cross_targets = true,
-        });
-    }
+    cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{
+        .build_modes = true,
+        .cross_targets = true,
+    });
     cases.addBuildFile("test/standalone/global_linkage/build.zig", .{});
     cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{});
-    cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig", .{});
-    cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig", .{});
-    cases.addBuildFile("test/standalone/link_common_symbols/build.zig", .{});
-    cases.addBuildFile("test/standalone/link_frameworks/build.zig", .{
-        .requires_macos_sdk = true,
-    });
-    cases.addBuildFile("test/standalone/link_common_symbols_alignment/build.zig", .{});
-    if (builtin.os.tag == .macos) {
-        cases.addBuildFile("test/standalone/link_import_tls_dylib/build.zig", .{});
-    }
     cases.addBuildFile("test/standalone/issue_339/build.zig", .{});
     cases.addBuildFile("test/standalone/issue_8550/build.zig", .{});
     cases.addBuildFile("test/standalone/issue_794/build.zig", .{});
@@ -69,16 +50,6 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
     if (builtin.os.tag == .linux) {
         cases.addBuildFile("test/standalone/pie/build.zig", .{});
     }
-    // Try to build and run an Objective-C executable.
-    cases.addBuildFile("test/standalone/objc/build.zig", .{
-        .build_modes = true,
-        .requires_macos_sdk = true,
-    });
-    // Try to build and run an Objective-C++ executable.
-    cases.addBuildFile("test/standalone/objcpp/build.zig", .{
-        .build_modes = true,
-        .requires_macos_sdk = true,
-    });
 
     // Ensure the development tools are buildable.
     cases.add("tools/gen_spirv_spec.zig");
test/tests.zig
@@ -21,6 +21,7 @@ const assemble_and_link = @import("assemble_and_link.zig");
 const translate_c = @import("translate_c.zig");
 const run_translated_c = @import("run_translated_c.zig");
 const gen_h = @import("gen_h.zig");
+const link = @import("link.zig");
 
 // Implementations
 pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext;
@@ -479,6 +480,27 @@ pub fn addStandaloneTests(
     return cases.step;
 }
 
+pub fn addLinkTests(
+    b: *build.Builder,
+    test_filter: ?[]const u8,
+    modes: []const Mode,
+    enable_macos_sdk: bool,
+) *build.Step {
+    const cases = b.allocator.create(StandaloneContext) catch unreachable;
+    cases.* = StandaloneContext{
+        .b = b,
+        .step = b.step("test-link", "Run the linker tests"),
+        .test_index = 0,
+        .test_filter = test_filter,
+        .modes = modes,
+        .skip_non_native = true,
+        .enable_macos_sdk = enable_macos_sdk,
+        .target = .{},
+    };
+    link.addCases(cases);
+    return cases.step;
+}
+
 pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
     _ = test_filter;
     _ = modes;
build.zig
@@ -489,6 +489,7 @@ pub fn build(b: *Builder) !void {
 
     toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
     toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes, skip_non_native, enable_macos_sdk, target));
+    toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk));
     toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
     toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes));
     toolchain_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));