Commit 1142e05343

Andrew Kelley <andrew@ziglang.org>
2023-03-08 04:23:11
re-enable macho linker tests
1 parent 9a9b008
Changed files (25)
test
link
macho
bugs
13056
13457
dead_strip
dead_strip_dylibs
dylib
empty
entry
headerpad
linksection
needed_framework
needed_library
objc
objcpp
pagezero
search_strategy
stack_size
strict_validation
tls
unwind_info
uuid
weak_framework
weak_library
test/link/macho/bugs/13056/build.zig
@@ -1,20 +1,28 @@
 const std = @import("std");
 
+pub const requires_macos_sdk = true;
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
+
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     const target: std.zig.CrossTarget = .{ .os_tag = .macos };
     const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
     const sdk = std.zig.system.darwin.getDarwinSDK(b.allocator, target_info.target) orelse
         @panic("macOS SDK is required to run the test");
 
-    const test_step = b.step("test", "Test the program");
-
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
     });
-    b.default_step.dependOn(&exe.step);
     exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable);
     exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable);
     exe.addCSourceFile("test.cpp", &.{
test/link/macho/bugs/13457/build.zig
@@ -1,10 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
+
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
-    const test_step = b.step("test", "Test the program");
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const exe = b.addExecutable(.{
         .name = "test",
@@ -16,5 +25,6 @@ pub fn build(b: *std.Build) void {
     const run = b.addRunArtifact(exe);
     run.skip_foreign_checks = true;
     run.expectStdOutEqual("");
+
     test_step.dependOn(&run.step);
 }
test/link/macho/dead_strip/build.zig
@@ -1,15 +1,17 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const optimize: std.builtin.OptimizeMode = .Debug;
     const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const test_step = b.step("test", "Test the program");
-    test_step.dependOn(b.getInstallStep());
+    b.default_step = test_step;
 
     {
         // Without -dead_strip, we expect `iAmUnused` symbol present
-        const exe = createScenario(b, optimize, target);
+        const exe = createScenario(b, optimize, target, "no-gc");
 
         const check = exe.checkObject(.macho);
         check.checkInSymtab();
@@ -22,7 +24,7 @@ pub fn build(b: *std.Build) void {
 
     {
         // With -dead_strip, no `iAmUnused` symbol should be present
-        const exe = createScenario(b, optimize, target);
+        const exe = createScenario(b, optimize, target, "yes-gc");
         exe.link_gc_sections = true;
 
         const check = exe.checkObject(.macho);
@@ -39,9 +41,10 @@ fn createScenario(
     b: *std.Build,
     optimize: std.builtin.OptimizeMode,
     target: std.zig.CrossTarget,
+    name: []const u8,
 ) *std.Build.CompileStep {
     const exe = b.addExecutable(.{
-        .name = "test",
+        .name = name,
         .optimize = optimize,
         .target = target,
     });
test/link/macho/dead_strip_dylibs/build.zig
@@ -1,14 +1,22 @@
 const std = @import("std");
 
+pub const requires_macos_sdk = true;
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     {
         // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable
-        const exe = createScenario(b, optimize);
+        const exe = createScenario(b, optimize, "no-dead-strip");
 
         const check = exe.checkObject(.macho);
         check.checkStart("cmd LOAD_DYLIB");
@@ -25,18 +33,22 @@ pub fn build(b: *std.Build) void {
 
     {
         // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable
-        const exe = createScenario(b, optimize);
+        const exe = createScenario(b, optimize, "yes-dead-strip");
         exe.dead_strip_dylibs = true;
 
-        const run_cmd = exe.run();
-        run_cmd.expected_term = .{ .Exited = @bitCast(u8, @as(i8, -2)) }; // should fail
+        const run_cmd = b.addRunArtifact(exe);
+        run_cmd.expectExitCode(@bitCast(u8, @as(i8, -2))); // should fail
         test_step.dependOn(&run_cmd.step);
     }
 }
 
-fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
+fn createScenario(
+    b: *std.Build,
+    optimize: std.builtin.OptimizeMode,
+    name: []const u8,
+) *std.Build.CompileStep {
     const exe = b.addExecutable(.{
-        .name = "test",
+        .name = name,
         .optimize = optimize,
     });
     exe.addCSourceFile("main.c", &[0][]const u8{});
test/link/macho/dylib/build.zig
@@ -1,11 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const dylib = b.addSharedLibrary(.{
         .name = "a",
@@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void {
     });
     dylib.addCSourceFile("a.c", &.{});
     dylib.linkLibC();
-    dylib.install();
 
     const check_dylib = dylib.checkObject(.macho);
     check_dylib.checkStart("cmd ID_DYLIB");
@@ -33,9 +40,9 @@ pub fn build(b: *std.Build) void {
     });
     exe.addCSourceFile("main.c", &.{});
     exe.linkSystemLibrary("a");
+    exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
+    exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
     exe.linkLibC();
-    exe.addLibraryPath(b.pathFromRoot("zig-out/lib/"));
-    exe.addRPath(b.pathFromRoot("zig-out/lib"));
 
     const check_exe = exe.checkObject(.macho);
     check_exe.checkStart("cmd LOAD_DYLIB");
@@ -45,10 +52,12 @@ pub fn build(b: *std.Build) void {
     check_exe.checkNext("compatibility version 10000");
 
     check_exe.checkStart("cmd RPATH");
-    check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable);
+    // TODO check this (perhaps with `checkNextFileSource(dylib.getOutputDirectorySource())`)
+    //check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{
+    //    b.pathFromRoot("zig-out/lib"),
+    //}) catch unreachable);
 
     const run = check_exe.runAndCompare();
-    run.cwd = b.pathFromRoot(".");
     run.expectStdOutEqual("Hello world");
     test_step.dependOn(&run.step);
 }
test/link/macho/empty/build.zig
@@ -1,11 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const exe = b.addExecutable(.{
         .name = "test",
test/link/macho/entry/build.zig
@@ -1,11 +1,18 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     const exe = b.addExecutable(.{
         .name = "main",
         .optimize = optimize,
test/link/macho/headerpad/build.zig
@@ -1,12 +1,20 @@
 const std = @import("std");
 const builtin = @import("builtin");
 
+pub const requires_symlinks = true;
+pub const requires_macos_sdk = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     {
         // Test -headerpad_max_install_names
         const exe = simpleExe(b, optimize);
test/link/macho/linksection/build.zig
@@ -1,11 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target = std.zig.CrossTarget{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target = std.zig.CrossTarget{ .os_tag = .macos };
 
     const obj = b.addObject(.{
         .name = "test",
test/link/macho/needed_framework/build.zig
@@ -1,11 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+pub const requires_macos_sdk = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     // -dead_strip_dylibs
     // -needed_framework Cocoa
     const exe = b.addExecutable(.{
test/link/macho/needed_library/build.zig
@@ -1,11 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const dylib = b.addSharedLibrary(.{
         .name = "a",
@@ -15,7 +23,6 @@ pub fn build(b: *std.Build) void {
     });
     dylib.addCSourceFile("a.c", &.{});
     dylib.linkLibC();
-    dylib.install();
 
     // -dead_strip_dylibs
     // -needed-la
@@ -27,8 +34,8 @@ pub fn build(b: *std.Build) void {
     exe.addCSourceFile("main.c", &[0][]const u8{});
     exe.linkLibC();
     exe.linkSystemLibraryNeeded("a");
-    exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
-    exe.addRPath(b.pathFromRoot("zig-out/lib"));
+    exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
+    exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
     exe.dead_strip_dylibs = true;
 
     const check = exe.checkObject(.macho);
test/link/macho/objc/build.zig
@@ -1,10 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+pub const requires_macos_sdk = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
test/link/macho/objcpp/build.zig
@@ -1,10 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+pub const requires_macos_sdk = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
test/link/macho/pagezero/build.zig
@@ -1,11 +1,13 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    const optimize: std.builtin.OptimizeMode = .Debug;
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     {
         const exe = b.addExecutable(.{
test/link/macho/search_strategy/build.zig
@@ -1,35 +1,41 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     {
         // -search_dylibs_first
-        const exe = createScenario(b, optimize, target);
+        const exe = createScenario(b, optimize, target, "search_dylibs_first");
         exe.search_strategy = .dylibs_first;
 
         const check = exe.checkObject(.macho);
         check.checkStart("cmd LOAD_DYLIB");
-        check.checkNext("name @rpath/liba.dylib");
+        check.checkNext("name @rpath/libsearch_dylibs_first.dylib");
 
         const run = check.runAndCompare();
-        run.cwd = b.pathFromRoot(".");
         run.expectStdOutEqual("Hello world");
         test_step.dependOn(&run.step);
     }
 
     {
         // -search_paths_first
-        const exe = createScenario(b, optimize, target);
+        const exe = createScenario(b, optimize, target, "search_paths_first");
         exe.search_strategy = .paths_first;
 
         const run = b.addRunArtifact(exe);
         run.skip_foreign_checks = true;
-        run.cwd = b.pathFromRoot(".");
         run.expectStdOutEqual("Hello world");
         test_step.dependOn(&run.step);
     }
@@ -39,9 +45,10 @@ fn createScenario(
     b: *std.Build,
     optimize: std.builtin.OptimizeMode,
     target: std.zig.CrossTarget,
+    name: []const u8,
 ) *std.Build.CompileStep {
     const static = b.addStaticLibrary(.{
-        .name = "a",
+        .name = name,
         .optimize = optimize,
         .target = target,
     });
@@ -50,10 +57,9 @@ fn createScenario(
     static.override_dest_dir = std.Build.InstallDir{
         .custom = "static",
     };
-    static.install();
 
     const dylib = b.addSharedLibrary(.{
-        .name = "a",
+        .name = name,
         .version = .{ .major = 1, .minor = 0 },
         .optimize = optimize,
         .target = target,
@@ -63,18 +69,17 @@ fn createScenario(
     dylib.override_dest_dir = std.Build.InstallDir{
         .custom = "dynamic",
     };
-    dylib.install();
 
     const exe = b.addExecutable(.{
-        .name = "main",
+        .name = name,
         .optimize = optimize,
         .target = target,
     });
     exe.addCSourceFile("main.c", &.{});
-    exe.linkSystemLibraryName("a");
+    exe.linkSystemLibraryName(name);
     exe.linkLibC();
-    exe.addLibraryPath(b.pathFromRoot("zig-out/static"));
-    exe.addLibraryPath(b.pathFromRoot("zig-out/dynamic"));
-    exe.addRPath(b.pathFromRoot("zig-out/dynamic"));
+    exe.addLibraryPathDirectorySource(static.getOutputDirectorySource());
+    exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
+    exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
     return exe;
 }
test/link/macho/stack_size/build.zig
@@ -1,11 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const exe = b.addExecutable(.{
         .name = "main",
test/link/macho/strict_validation/build.zig
@@ -1,12 +1,20 @@
 const std = @import("std");
 const builtin = @import("builtin");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const exe = b.addExecutable(.{
         .name = "main",
test/link/macho/tls/build.zig
@@ -1,7 +1,18 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
+
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const lib = b.addSharedLibrary(.{
@@ -21,6 +32,5 @@ pub fn build(b: *std.Build) void {
     test_exe.linkLibrary(lib);
     test_exe.linkLibC();
 
-    const test_step = b.step("test", "Test it");
     test_step.dependOn(&test_exe.step);
 }
test/link/macho/unwind_info/build.zig
@@ -1,14 +1,23 @@
 const std = @import("std");
 const builtin = @import("builtin");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
-    testUnwindInfo(b, test_step, optimize, target, false);
-    testUnwindInfo(b, test_step, optimize, target, true);
+    testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip");
+    testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip");
 }
 
 fn testUnwindInfo(
@@ -17,8 +26,9 @@ fn testUnwindInfo(
     optimize: std.builtin.OptimizeMode,
     target: std.zig.CrossTarget,
     dead_strip: bool,
+    name: []const u8,
 ) void {
-    const exe = createScenario(b, optimize, target);
+    const exe = createScenario(b, optimize, target, name);
     exe.link_gc_sections = dead_strip;
 
     const check = exe.checkObject(.macho);
@@ -54,9 +64,10 @@ fn createScenario(
     b: *std.Build,
     optimize: std.builtin.OptimizeMode,
     target: std.zig.CrossTarget,
+    name: []const u8,
 ) *std.Build.CompileStep {
     const exe = b.addExecutable(.{
-        .name = "test",
+        .name = name,
         .optimize = optimize,
         .target = target,
     });
test/link/macho/uuid/build.zig
@@ -3,11 +3,14 @@ const CompileStep = std.Build.CompileStep;
 const FileSource = std.Build.FileSource;
 const Step = std.Build.Step;
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
     const test_step = b.step("test", "Test");
-    test_step.dependOn(b.getInstallStep());
+    b.default_step = test_step;
 
-    // We force cross-compilation to ensure we always pick a generic CPU with constant set of CPU features.
+    // We force cross-compilation to ensure we always pick a generic CPU with
+    // constant set of CPU features.
     const aarch64_macos = std.zig.CrossTarget{
         .cpu_arch = .aarch64,
         .os_tag = .macos,
test/link/macho/weak_framework/build.zig
@@ -1,11 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+pub const requires_macos_sdk = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
 
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
     const exe = b.addExecutable(.{
         .name = "test",
         .optimize = optimize,
test/link/macho/weak_library/build.zig
@@ -1,11 +1,19 @@
 const std = @import("std");
 
+pub const requires_symlinks = true;
+
 pub fn build(b: *std.Build) void {
-    const optimize = b.standardOptimizeOption(.{});
-    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
+    const test_step = b.step("test", "Test it");
+    b.default_step = test_step;
 
-    const test_step = b.step("test", "Test the program");
-    test_step.dependOn(b.getInstallStep());
+    add(b, test_step, .Debug);
+    add(b, test_step, .ReleaseFast);
+    add(b, test_step, .ReleaseSmall);
+    add(b, test_step, .ReleaseSafe);
+}
+
+fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void {
+    const target: std.zig.CrossTarget = .{ .os_tag = .macos };
 
     const dylib = b.addSharedLibrary(.{
         .name = "a",
@@ -25,8 +33,8 @@ pub fn build(b: *std.Build) void {
     exe.addCSourceFile("main.c", &[0][]const u8{});
     exe.linkLibC();
     exe.linkSystemLibraryWeak("a");
-    exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
-    exe.addRPath(b.pathFromRoot("zig-out/lib"));
+    exe.addLibraryPathDirectorySource(dylib.getOutputDirectorySource());
+    exe.addRPathDirectorySource(dylib.getOutputDirectorySource());
 
     const check = exe.checkObject(.macho);
     check.checkStart("cmd LOAD_WEAK_DYLIB");
test/link.zig
@@ -76,122 +76,103 @@ pub const cases = [_]Case{
     },
 
     // Mach-O Cases
-    //    cases.addBuildFile("test/link/macho/bugs/13056/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_macos_sdk = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/bugs/13457/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{
-    //        .build_modes = false,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_macos_sdk = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/dylib/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/empty/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/entry/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/headerpad/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_macos_sdk = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/linksection/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_macos_sdk = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/needed_library/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/objc/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_macos_sdk = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/objcpp/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_macos_sdk = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/pagezero/build.zig", .{
-    //        .build_modes = false,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/search_strategy/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/stack_size/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/strict_validation/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/tls/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/unwind_info/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/uuid/build.zig", .{
-    //        .build_modes = false,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/weak_library/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_symlinks = true,
-    //    });
-    //
-    //    cases.addBuildFile("test/link/macho/weak_framework/build.zig", .{
-    //        .build_modes = true,
-    //        .requires_macos_sdk = true,
-    //        .requires_symlinks = true,
-    //    });
+    .{
+        .build_root = "test/link/macho/bugs/13056",
+        .import = @import("link/macho/bugs/13056/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/bugs/13457",
+        .import = @import("link/macho/bugs/13457/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/bugs/13457",
+        .import = @import("link/macho/bugs/13457/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/dead_strip",
+        .import = @import("link/macho/dead_strip/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/dead_strip_dylibs",
+        .import = @import("link/macho/dead_strip_dylibs/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/dylib",
+        .import = @import("link/macho/dylib/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/empty",
+        .import = @import("link/macho/empty/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/entry",
+        .import = @import("link/macho/entry/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/headerpad",
+        .import = @import("link/macho/headerpad/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/linksection",
+        .import = @import("link/macho/linksection/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/needed_framework",
+        .import = @import("link/macho/needed_framework/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/needed_library",
+        .import = @import("link/macho/needed_library/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/objc",
+        .import = @import("link/macho/objc/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/objcpp",
+        .import = @import("link/macho/objcpp/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/pagezero",
+        .import = @import("link/macho/pagezero/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/search_strategy",
+        .import = @import("link/macho/search_strategy/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/stack_size",
+        .import = @import("link/macho/stack_size/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/strict_validation",
+        .import = @import("link/macho/strict_validation/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/tls",
+        .import = @import("link/macho/tls/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/unwind_info",
+        .import = @import("link/macho/unwind_info/build.zig"),
+    },
+    // TODO: re-enable this test. It currently has some incompatibilities with
+    // the new build system API. In particular, it depends on installing the build
+    // artifacts, which should be unnecessary, and it has a custom build step that
+    // prints directly to stderr instead of failing the step with an error message.
+    //.{
+    //    .build_root = "test/link/macho/uuid",
+    //    .import = @import("link/macho/uuid/build.zig"),
+    //},
+
+    .{
+        .build_root = "test/link/macho/weak_library",
+        .import = @import("link/macho/weak_library/build.zig"),
+    },
+    .{
+        .build_root = "test/link/macho/weak_framework",
+        .import = @import("link/macho/weak_framework/build.zig"),
+    },
 };
 
 const std = @import("std");
test/tests.zig
@@ -590,23 +590,25 @@ pub fn addStandaloneTests(
 
 pub fn addLinkTests(
     b: *std.Build,
-    test_filter: ?[]const u8,
-    optimize_modes: []const OptimizeMode,
     enable_macos_sdk: bool,
     omit_stage2: bool,
     enable_symlinks_windows: bool,
 ) *Step {
-    _ = test_filter;
-    _ = optimize_modes;
-    _ = enable_macos_sdk;
-    _ = enable_symlinks_windows;
-
     const step = b.step("test-link", "Run the linker tests");
+    const omit_symlinks = builtin.os.tag == .windows and !enable_symlinks_windows;
 
     inline for (link.cases) |link_test| {
         const requires_stage2 = @hasDecl(link_test.import, "requires_stage2") and
             link_test.import.requires_stage2;
-        if (!requires_stage2 or !omit_stage2) {
+        const requires_symlinks = @hasDecl(link_test.import, "requires_symlinks") and
+            link_test.import.requires_symlinks;
+        const requires_macos_sdk = @hasDecl(link_test.import, "requires_macos_sdk") and
+            link_test.import.requires_macos_sdk;
+        const bad =
+            (requires_stage2 and omit_stage2) or
+            (requires_symlinks and omit_symlinks) or
+            (requires_macos_sdk and !enable_macos_sdk);
+        if (!bad) {
             const dep = b.anonymousDependency(link_test.build_root, link_test.import, .{});
             const dep_step = dep.builder.default_step;
             assert(mem.startsWith(u8, dep.builder.dep_prefix, "test."));
build.zig
@@ -454,7 +454,7 @@ pub fn build(b: *std.Build) !void {
     //    enable_symlinks_windows,
     //));
     test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
-    test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
+    test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
     test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));
     test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes));
     test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));