| author | |
| committer | |
| log | 7566a8fbd81a71d4b61022fd2b6f5f80a4679fb0 |
| tree | 2981d3abee93cd1c3e599ad9529cc9bf60b5807b |
| parent | 197fd41e27647d846b6e6bf6197bad0ab14634ae |
4 files changed, 215 insertions(+), 9 deletions(-)
test/link.zig+5-6| ... | @@ -4,6 +4,11 @@ pub const Case = struct { | ... | @@ -4,6 +4,11 @@ pub const Case = struct { |
| 4 | }; | 4 | }; |
| 5 | 5 | ||
| 6 | pub const cases = [_]Case{ | 6 | pub const cases = [_]Case{ |
| 7 | .{ | ||
| 8 | .build_root = "test/link", | ||
| 9 | .import = @import("link/link.zig"), | ||
| 10 | }, | ||
| 11 | |||
| 7 | .{ | 12 | .{ |
| 8 | .build_root = "test/link/bss", | 13 | .build_root = "test/link/bss", |
| 9 | .import = @import("link/bss/build.zig"), | 14 | .import = @import("link/bss/build.zig"), |
| ... | @@ -29,12 +34,6 @@ pub const cases = [_]Case{ | ... | @@ -29,12 +34,6 @@ pub const cases = [_]Case{ |
| 29 | .import = @import("link/glibc_compat/build.zig"), | 34 | .import = @import("link/glibc_compat/build.zig"), |
| 30 | }, | 35 | }, |
| 31 | 36 | ||
| 32 | // Elf Cases | ||
| 33 | .{ | ||
| 34 | .build_root = "test/link", | ||
| 35 | .import = @import("link/elf.zig"), | ||
| 36 | }, | ||
| 37 | |||
| 38 | // WASM Cases | 37 | // WASM Cases |
| 39 | // https://github.com/ziglang/zig/issues/16938 | 38 | // https://github.com/ziglang/zig/issues/16938 |
| 40 | //.{ | 39 | //.{ |
test/link/elf.zig+3-3| ... | @@ -2,9 +2,8 @@ | ... | @@ -2,9 +2,8 @@ |
| 2 | //! Currently, we support linking x86_64 Linux, but in the future we | 2 | //! Currently, we support linking x86_64 Linux, but in the future we |
| 3 | //! will progressively relax those to exercise more combinations. | 3 | //! will progressively relax those to exercise more combinations. |
| 4 | 4 | ||
| 5 | pub fn build(b: *Build) void { | 5 | pub fn testAll(b: *Build) *Step { |
| 6 | const elf_step = b.step("test-elf", "Run ELF tests"); | 6 | const elf_step = b.step("test-elf", "Run ELF tests"); |
| 7 | b.default_step = elf_step; | ||
| 8 | 7 | ||
| 9 | const default_target = CrossTarget{ | 8 | const default_target = CrossTarget{ |
| 10 | .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs | 9 | .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs |
| ... | @@ -123,6 +122,8 @@ pub fn build(b: *Build) void { | ... | @@ -123,6 +122,8 @@ pub fn build(b: *Build) void { |
| 123 | elf_step.dependOn(testZNow(b, .{ .target = glibc_target })); | 122 | elf_step.dependOn(testZNow(b, .{ .target = glibc_target })); |
| 124 | elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target })); | 123 | elf_step.dependOn(testZStackSize(b, .{ .target = glibc_target })); |
| 125 | elf_step.dependOn(testZText(b, .{ .target = glibc_target })); | 124 | elf_step.dependOn(testZText(b, .{ .target = glibc_target })); |
| 125 | |||
| 126 | return elf_step; | ||
| 126 | } | 127 | } |
| 127 | 128 | ||
| 128 | fn testAbsSymbols(b: *Build, opts: Options) *Step { | 129 | fn testAbsSymbols(b: *Build, opts: Options) *Step { |
| ... | @@ -3582,7 +3583,6 @@ const std = @import("std"); | ... | @@ -3582,7 +3583,6 @@ const std = @import("std"); |
| 3582 | const Build = std.Build; | 3583 | const Build = std.Build; |
| 3583 | const Compile = Step.Compile; | 3584 | const Compile = Step.Compile; |
| 3584 | const CrossTarget = std.zig.CrossTarget; | 3585 | const CrossTarget = std.zig.CrossTarget; |
| 3585 | const LazyPath = Build.LazyPath; | ||
| 3586 | const Run = Step.Run; | 3586 | const Run = Step.Run; |
| 3587 | const Step = Build.Step; | 3587 | const Step = Build.Step; |
| 3588 | const WriteFile = Step.WriteFile; | 3588 | const WriteFile = Step.WriteFile; |
test/link/link.zig created+114| ... | @@ -0,0 +1,114 @@ | ||
| 1 | pub fn build(b: *Build) void { | ||
| 2 | const test_step = b.step("test-link", "Run link tests"); | ||
| 3 | b.default_step = test_step; | ||
| 4 | |||
| 5 | test_step.dependOn(@import("elf.zig").testAll(b)); | ||
| 6 | test_step.dependOn(@import("macho.zig").testAll(b)); | ||
| 7 | } | ||
| 8 | |||
| 9 | pub const Options = struct { | ||
| 10 | target: CrossTarget, | ||
| 11 | optimize: std.builtin.OptimizeMode = .Debug, | ||
| 12 | use_llvm: bool = true, | ||
| 13 | use_lld: bool = false, | ||
| 14 | }; | ||
| 15 | |||
| 16 | pub fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { | ||
| 17 | const target = opts.target.zigTriple(b.allocator) catch @panic("OOM"); | ||
| 18 | const optimize = @tagName(opts.optimize); | ||
| 19 | const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm"; | ||
| 20 | const name = std.fmt.allocPrint(b.allocator, "test-" ++ prefix ++ "-{s}-{s}-{s}", .{ | ||
| 21 | target, | ||
| 22 | optimize, | ||
| 23 | use_llvm, | ||
| 24 | }) catch @panic("OOM"); | ||
| 25 | return b.step(name, ""); | ||
| 26 | } | ||
| 27 | |||
| 28 | pub fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile { | ||
| 29 | return b.addExecutable(.{ | ||
| 30 | .name = name, | ||
| 31 | .target = opts.target, | ||
| 32 | .optimize = opts.optimize, | ||
| 33 | .use_llvm = opts.use_llvm, | ||
| 34 | .use_lld = opts.use_lld, | ||
| 35 | }); | ||
| 36 | } | ||
| 37 | |||
| 38 | pub fn addObject(b: *Build, name: []const u8, opts: Options) *Compile { | ||
| 39 | return b.addObject(.{ | ||
| 40 | .name = name, | ||
| 41 | .target = opts.target, | ||
| 42 | .optimize = opts.optimize, | ||
| 43 | .use_llvm = opts.use_llvm, | ||
| 44 | .use_lld = opts.use_lld, | ||
| 45 | }); | ||
| 46 | } | ||
| 47 | |||
| 48 | pub fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile { | ||
| 49 | return b.addStaticLibrary(.{ | ||
| 50 | .name = name, | ||
| 51 | .target = opts.target, | ||
| 52 | .optimize = opts.optimize, | ||
| 53 | .use_llvm = opts.use_llvm, | ||
| 54 | .use_lld = opts.use_lld, | ||
| 55 | }); | ||
| 56 | } | ||
| 57 | |||
| 58 | pub fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile { | ||
| 59 | return b.addSharedLibrary(.{ | ||
| 60 | .name = name, | ||
| 61 | .target = opts.target, | ||
| 62 | .optimize = opts.optimize, | ||
| 63 | .use_llvm = opts.use_llvm, | ||
| 64 | .use_lld = opts.use_lld, | ||
| 65 | }); | ||
| 66 | } | ||
| 67 | |||
| 68 | pub fn addRunArtifact(comp: *Compile) *Run { | ||
| 69 | const b = comp.step.owner; | ||
| 70 | const run = b.addRunArtifact(comp); | ||
| 71 | run.skip_foreign_checks = true; | ||
| 72 | return run; | ||
| 73 | } | ||
| 74 | |||
| 75 | pub fn addZigSourceBytes(comp: *Compile, bytes: []const u8) void { | ||
| 76 | const b = comp.step.owner; | ||
| 77 | const file = WriteFile.create(b).add("a.zig", bytes); | ||
| 78 | file.addStepDependencies(&comp.step); | ||
| 79 | comp.root_src = file; | ||
| 80 | } | ||
| 81 | |||
| 82 | pub fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void { | ||
| 83 | const b = comp.step.owner; | ||
| 84 | const file = WriteFile.create(b).add("a.c", bytes); | ||
| 85 | comp.addCSourceFile(.{ .file = file, .flags = flags }); | ||
| 86 | } | ||
| 87 | |||
| 88 | pub fn addCppSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void { | ||
| 89 | const b = comp.step.owner; | ||
| 90 | const file = WriteFile.create(b).add("a.cpp", bytes); | ||
| 91 | comp.addCSourceFile(.{ .file = file, .flags = flags }); | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn addAsmSourceBytes(comp: *Compile, bytes: []const u8) void { | ||
| 95 | const b = comp.step.owner; | ||
| 96 | const actual_bytes = std.fmt.allocPrint(b.allocator, "{s}\n", .{bytes}) catch @panic("OOM"); | ||
| 97 | const file = WriteFile.create(b).add("a.s", actual_bytes); | ||
| 98 | comp.addAssemblyFile(file); | ||
| 99 | } | ||
| 100 | |||
| 101 | pub fn expectLinkErrors(comp: *Compile, test_step: *Step, expected_errors: Compile.ExpectedCompileErrors) void { | ||
| 102 | comp.expect_errors = expected_errors; | ||
| 103 | const bin_file = comp.getEmittedBin(); | ||
| 104 | bin_file.addStepDependencies(test_step); | ||
| 105 | } | ||
| 106 | |||
| 107 | const std = @import("std"); | ||
| 108 | |||
| 109 | const Build = std.Build; | ||
| 110 | const Compile = Step.Compile; | ||
| 111 | const CrossTarget = std.zig.CrossTarget; | ||
| 112 | const Run = Step.Run; | ||
| 113 | const Step = Build.Step; | ||
| 114 | const WriteFile = Step.WriteFile; | ||
test/link/macho.zig created+93| ... | @@ -0,0 +1,93 @@ | ||
| 1 | //! Here we test our MachO linker for correctness and functionality. | ||
| 2 | //! TODO migrate standalone tests from test/link/macho/* to here. | ||
| 3 | |||
| 4 | pub fn testAll(b: *Build) *Step { | ||
| 5 | const macho_step = b.step("test-macho", "Run MachO tests"); | ||
| 6 | |||
| 7 | const default_target = CrossTarget{ .os_tag = .macos }; | ||
| 8 | |||
| 9 | macho_step.dependOn(testResolvingBoundarySymbols(b, .{ .target = default_target })); | ||
| 10 | |||
| 11 | return macho_step; | ||
| 12 | } | ||
| 13 | |||
| 14 | fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step { | ||
| 15 | const test_step = addTestStep(b, "macho-resolving-boundary-symbols", opts); | ||
| 16 | |||
| 17 | const obj1 = addObject(b, "obj1", opts); | ||
| 18 | addCppSourceBytes(obj1, | ||
| 19 | \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST,__message_ptr"))) = "codebase"; | ||
| 20 | , &.{}); | ||
| 21 | |||
| 22 | const main_o = addObject(b, "main", opts); | ||
| 23 | addZigSourceBytes(main_o, | ||
| 24 | \\const std = @import("std"); | ||
| 25 | \\extern fn interop() [*:0]const u8; | ||
| 26 | \\pub fn main() !void { | ||
| 27 | \\ std.debug.print("All your {s} are belong to us.\n", .{ | ||
| 28 | \\ std.mem.span(interop()), | ||
| 29 | \\ }); | ||
| 30 | \\} | ||
| 31 | ); | ||
| 32 | |||
| 33 | { | ||
| 34 | const obj2 = addObject(b, "obj2", opts); | ||
| 35 | addCppSourceBytes(obj2, | ||
| 36 | \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__message_ptr"); | ||
| 37 | \\extern "C" const char* interop() { | ||
| 38 | \\ return message_pointer; | ||
| 39 | \\} | ||
| 40 | , &.{}); | ||
| 41 | |||
| 42 | const exe = addExecutable(b, "test", opts); | ||
| 43 | exe.addObject(obj1); | ||
| 44 | exe.addObject(obj2); | ||
| 45 | exe.addObject(main_o); | ||
| 46 | |||
| 47 | const run = addRunArtifact(exe); | ||
| 48 | run.expectStdErrEqual("All your codebase are belong to us.\n"); | ||
| 49 | test_step.dependOn(&run.step); | ||
| 50 | |||
| 51 | const check = exe.checkObject(); | ||
| 52 | check.checkInSymtab(); | ||
| 53 | check.checkNotPresent("section$start$__DATA_CONST$__message_ptr"); | ||
| 54 | test_step.dependOn(&check.step); | ||
| 55 | } | ||
| 56 | |||
| 57 | { | ||
| 58 | const obj3 = addObject(b, "obj3", opts); | ||
| 59 | addCppSourceBytes(obj3, | ||
| 60 | \\extern const char* message_pointer __asm("section$start$__DATA$__message_ptr"); | ||
| 61 | \\extern "C" const char* interop() { | ||
| 62 | \\ return message_pointer; | ||
| 63 | \\} | ||
| 64 | , &.{}); | ||
| 65 | |||
| 66 | const exe = addExecutable(b, "test", opts); | ||
| 67 | exe.addObject(obj1); | ||
| 68 | exe.addObject(obj3); | ||
| 69 | exe.addObject(main_o); | ||
| 70 | |||
| 71 | expectLinkErrors(exe, test_step, .{ .exact = &.{ | ||
| 72 | "section not found: __DATA,__message_ptr", | ||
| 73 | "note: while resolving section$start$__DATA$__message_ptr", | ||
| 74 | } }); | ||
| 75 | } | ||
| 76 | |||
| 77 | return test_step; | ||
| 78 | } | ||
| 79 | |||
| 80 | const addCppSourceBytes = link.addCppSourceBytes; | ||
| 81 | const addExecutable = link.addExecutable; | ||
| 82 | const addObject = link.addObject; | ||
| 83 | const addRunArtifact = link.addRunArtifact; | ||
| 84 | const addTestStep = link.addTestStep; | ||
| 85 | const addZigSourceBytes = link.addZigSourceBytes; | ||
| 86 | const expectLinkErrors = link.expectLinkErrors; | ||
| 87 | const link = @import("link.zig"); | ||
| 88 | const std = @import("std"); | ||
| 89 | |||
| 90 | const Build = std.Build; | ||
| 91 | const CrossTarget = std.zig.CrossTarget; | ||
| 92 | const Options = link.Options; | ||
| 93 | const Step = Build.Step; | ||