| author | |
| committer | |
| log | ab46b8235450cd1d8cb1a483859c2265588a1863 |
| tree | 1dfa78312ed920a1356469464e9956e3801a3903 |
| parent | c6d0c68141ebd8059bf5e7cdc5f1b4117e62e64a |
4 files changed, 293 insertions(+), 0 deletions(-)
build.zig+9| ... | @@ -629,6 +629,15 @@ pub fn build(b: *std.Build) !void { | ... | @@ -629,6 +629,15 @@ pub fn build(b: *std.Build) !void { |
| 629 | .skip_llvm = skip_llvm, | 629 | .skip_llvm = skip_llvm, |
| 630 | .max_rss = 3_300_000_000, | 630 | .max_rss = 3_300_000_000, |
| 631 | })); | 631 | })); |
| 632 | test_step.dependOn(tests.addLinkTests(b, .{ | ||
| 633 | .test_target_filters = test_target_filters, | ||
| 634 | .test_filters = test_filters, | ||
| 635 | .optimize_modes = optimize_modes, | ||
| 636 | .skip_non_native = skip_non_native, | ||
| 637 | .skip_windows = skip_windows, | ||
| 638 | .skip_llvm = skip_llvm, | ||
| 639 | .max_rss = 100_000_000, | ||
| 640 | })); | ||
| 632 | test_step.dependOn(tests.addStackTraceTests(b, test_filters, skip_non_native)); | 641 | test_step.dependOn(tests.addStackTraceTests(b, test_filters, skip_non_native)); |
| 633 | test_step.dependOn(tests.addErrorTraceTests(b, test_filters, optimize_modes, skip_non_native)); | 642 | test_step.dependOn(tests.addErrorTraceTests(b, test_filters, optimize_modes, skip_non_native)); |
| 634 | test_step.dependOn(tests.addCliTests(b)); | 643 | test_step.dependOn(tests.addCliTests(b)); |
test/link.zig created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | pub fn addCases(cases: @import("tests.zig").LinkContext) void { | ||
| 2 | if (cases.addTestStep("static-lib-exports")) |name| { | ||
| 3 | const lib = cases.addStaticLibrary(.{ | ||
| 4 | .name = "lib", | ||
| 5 | .zig_source_bytes = | ||
| 6 | \\export fn foo() void {} | ||
| 7 | \\var bar: u32 = 1234; | ||
| 8 | \\comptime { @export(&bar, .{ .name = "bar", .linkage = .strong }); } | ||
| 9 | \\const baz: u64 = 5678; | ||
| 10 | \\comptime { @export(&baz, .{ .name = "baz", .linkage = .strong }); } | ||
| 11 | , | ||
| 12 | }); | ||
| 13 | cases.verifyObjdump(name, lib, &.{"--symbols"}, .{ .os = true }); | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | const std = @import("std"); | ||
test/src/Link.zig created+159| ... | @@ -0,0 +1,159 @@ | ||
| 1 | b: *Build, | ||
| 2 | step: *Step, | ||
| 3 | optimize: std.builtin.OptimizeMode, | ||
| 4 | target: std.Build.ResolvedTarget, | ||
| 5 | use_llvm: bool, | ||
| 6 | use_lld: bool, | ||
| 7 | link_libc: bool, | ||
| 8 | suffix: []const u8, | ||
| 9 | test_filters: []const []const u8, | ||
| 10 | max_rss: usize, | ||
| 11 | |||
| 12 | pub fn addTestStep(self: *const Link, prefix: []const u8) ?[]const u8 { | ||
| 13 | if (for (self.test_filters) |filter| { | ||
| 14 | if (std.mem.containsAtLeast(u8, prefix, 1, filter)) break false; | ||
| 15 | } else self.test_filters.len > 0) return null; | ||
| 16 | |||
| 17 | return std.fmt.allocPrint(self.b.allocator, "test-{s}", .{prefix}) catch @panic("OOM"); | ||
| 18 | } | ||
| 19 | |||
| 20 | pub fn addStaticLibrary(self: *const Link, overlay: OverlayOptions) *Step.Compile { | ||
| 21 | return self.b.addLibrary(.{ | ||
| 22 | .linkage = .static, | ||
| 23 | .name = overlay.name, | ||
| 24 | .root_module = self.createModule(overlay), | ||
| 25 | .use_llvm = self.use_llvm, | ||
| 26 | .use_lld = self.use_lld, | ||
| 27 | }); | ||
| 28 | } | ||
| 29 | |||
| 30 | // TODO: Use std.meta.FieldEnum on TargetQuery? | ||
| 31 | const SnapshotScope = packed struct { | ||
| 32 | arch: bool = false, | ||
| 33 | os: bool = false, | ||
| 34 | abi: bool = false, | ||
| 35 | optimize: bool = false, | ||
| 36 | use_llvm: bool = false, | ||
| 37 | use_lld: bool = false, | ||
| 38 | link_libc: bool = false, | ||
| 39 | }; | ||
| 40 | |||
| 41 | pub fn verifyObjdump( | ||
| 42 | self: *const Link, | ||
| 43 | name: []const u8, | ||
| 44 | compile: *Step.Compile, | ||
| 45 | args: []const []const u8, | ||
| 46 | scope: SnapshotScope, | ||
| 47 | ) void { | ||
| 48 | const snapshot_name = self.snapshotName(name, compile.name, scope) catch @panic("OOM"); | ||
| 49 | const run_step = Step.Run.create(self.b, self.b.fmt("objdump {s}", .{snapshot_name})); | ||
| 50 | run_step.addArgs(&.{ self.b.graph.zig_exe, "objdump" }); | ||
| 51 | run_step.addArtifactArg(compile); | ||
| 52 | run_step.addArgs(args); | ||
| 53 | run_step.addCheck(.{ .expect_term = .{ .exited = 0 } }); | ||
| 54 | |||
| 55 | const actual_path = run_step.captureStdOut(.{ .trim_whitespace = .none }); | ||
| 56 | const expected_path = self.b.path(self.b.pathJoin(&.{ "test/link/snapshots/", snapshot_name })); | ||
| 57 | |||
| 58 | const check_step = self.b.addCheckFile(actual_path, .{ | ||
| 59 | .expected_file = .{ | ||
| 60 | .file = expected_path, | ||
| 61 | .if_missing = .fail, | ||
| 62 | // TODO: Option to do UpdateSourceFiles if not matching / missing? | ||
| 63 | // TODO: Option to output to <name>-<self.suffix>.actual.dmp file? | ||
| 64 | }, | ||
| 65 | }); | ||
| 66 | |||
| 67 | self.step.dependOn(&check_step.step); | ||
| 68 | } | ||
| 69 | |||
| 70 | fn snapshotName( | ||
| 71 | self: *const Link, | ||
| 72 | test_name: []const u8, | ||
| 73 | compile_name: []const u8, | ||
| 74 | scope: SnapshotScope, | ||
| 75 | ) ![]const u8 { | ||
| 76 | var snapshot_name: std.Io.Writer.Allocating = .init(self.b.allocator); | ||
| 77 | const w = &snapshot_name.writer; | ||
| 78 | |||
| 79 | try w.print("{s}.{s}", .{ test_name, compile_name }); | ||
| 80 | if (scope.arch) try w.print("-{t}", .{self.target.result.cpu.arch}); | ||
| 81 | if (scope.os) try w.print("-{t}", .{self.target.result.os.tag}); | ||
| 82 | if (scope.abi) try w.print("-{t}", .{self.target.result.abi}); | ||
| 83 | if (scope.optimize) try w.print("-{t}", .{self.optimize}); | ||
| 84 | if (scope.use_llvm and self.use_llvm) try w.writeAll("-llvm"); | ||
| 85 | if (scope.use_lld and self.use_lld) try w.writeAll("-lld"); | ||
| 86 | if (scope.link_libc and self.link_libc) try w.writeAll("-libc"); | ||
| 87 | try w.writeAll(".dmp"); | ||
| 88 | |||
| 89 | return try snapshot_name.toOwnedSlice(); | ||
| 90 | } | ||
| 91 | |||
| 92 | fn createModule(self: *const Link, overlay: OverlayOptions) *Build.Module { | ||
| 93 | const write_files = self.b.addWriteFiles(); | ||
| 94 | |||
| 95 | const mod = self.b.createModule(.{ | ||
| 96 | .target = self.target, | ||
| 97 | .optimize = self.optimize, | ||
| 98 | .root_source_file = rsf: { | ||
| 99 | const bytes = overlay.zig_source_bytes orelse break :rsf null; | ||
| 100 | const name = self.b.fmt("{s}.zig", .{overlay.name}); | ||
| 101 | break :rsf write_files.add(name, bytes); | ||
| 102 | }, | ||
| 103 | .link_libc = self.link_libc, // TODO: Should this be in overlay instead? | ||
| 104 | .pic = overlay.pic, | ||
| 105 | .strip = overlay.strip, | ||
| 106 | }); | ||
| 107 | |||
| 108 | if (overlay.objcpp_source_bytes) |bytes| { | ||
| 109 | mod.addCSourceFile(.{ | ||
| 110 | .file = write_files.add("a.mm", bytes), | ||
| 111 | .flags = overlay.objcpp_source_flags, | ||
| 112 | }); | ||
| 113 | } | ||
| 114 | if (overlay.objc_source_bytes) |bytes| { | ||
| 115 | mod.addCSourceFile(.{ | ||
| 116 | .file = write_files.add("a.m", bytes), | ||
| 117 | .flags = overlay.objc_source_flags, | ||
| 118 | }); | ||
| 119 | } | ||
| 120 | if (overlay.cpp_source_bytes) |bytes| { | ||
| 121 | mod.addCSourceFile(.{ | ||
| 122 | .file = write_files.add("a.cpp", bytes), | ||
| 123 | .flags = overlay.cpp_source_flags, | ||
| 124 | }); | ||
| 125 | } | ||
| 126 | if (overlay.c_source_bytes) |bytes| { | ||
| 127 | mod.addCSourceFile(.{ | ||
| 128 | .file = write_files.add("a.c", bytes), | ||
| 129 | .flags = overlay.c_source_flags, | ||
| 130 | }); | ||
| 131 | } | ||
| 132 | if (overlay.asm_source_bytes) |bytes| { | ||
| 133 | mod.addAssemblyFile(write_files.add("a.s", bytes)); | ||
| 134 | } | ||
| 135 | |||
| 136 | return mod; | ||
| 137 | } | ||
| 138 | |||
| 139 | const OverlayOptions = struct { | ||
| 140 | name: []const u8, | ||
| 141 | asm_source_bytes: ?[]const u8 = null, | ||
| 142 | c_source_bytes: ?[]const u8 = null, | ||
| 143 | c_source_flags: []const []const u8 = &.{}, | ||
| 144 | cpp_source_bytes: ?[]const u8 = null, | ||
| 145 | cpp_source_flags: []const []const u8 = &.{}, | ||
| 146 | objc_source_bytes: ?[]const u8 = null, | ||
| 147 | objc_source_flags: []const []const u8 = &.{}, | ||
| 148 | objcpp_source_bytes: ?[]const u8 = null, | ||
| 149 | objcpp_source_flags: []const []const u8 = &.{}, | ||
| 150 | zig_source_bytes: ?[]const u8 = null, | ||
| 151 | pic: ?bool = null, | ||
| 152 | strip: ?bool = null, | ||
| 153 | }; | ||
| 154 | |||
| 155 | const std = @import("std"); | ||
| 156 | const Build = std.Build; | ||
| 157 | const Step = Build.Step; | ||
| 158 | |||
| 159 | const Link = @This(); | ||
test/tests.zig+108| ... | @@ -10,6 +10,7 @@ const error_traces = @import("error_traces.zig"); | ... | @@ -10,6 +10,7 @@ const error_traces = @import("error_traces.zig"); |
| 10 | const stack_traces = @import("stack_traces.zig"); | 10 | const stack_traces = @import("stack_traces.zig"); |
| 11 | const llvm_ir = @import("llvm_ir.zig"); | 11 | const llvm_ir = @import("llvm_ir.zig"); |
| 12 | const libc = @import("libc.zig"); | 12 | const libc = @import("libc.zig"); |
| 13 | const link = @import("link.zig"); | ||
| 13 | 14 | ||
| 14 | // Implementations | 15 | // Implementations |
| 15 | pub const ErrorTracesContext = @import("src/ErrorTrace.zig"); | 16 | pub const ErrorTracesContext = @import("src/ErrorTrace.zig"); |
| ... | @@ -17,6 +18,7 @@ pub const StackTracesContext = @import("src/StackTrace.zig"); | ... | @@ -17,6 +18,7 @@ pub const StackTracesContext = @import("src/StackTrace.zig"); |
| 17 | pub const DebuggerContext = @import("src/Debugger.zig"); | 18 | pub const DebuggerContext = @import("src/Debugger.zig"); |
| 18 | pub const LlvmIrContext = @import("src/LlvmIr.zig"); | 19 | pub const LlvmIrContext = @import("src/LlvmIr.zig"); |
| 19 | pub const LibcContext = @import("src/Libc.zig"); | 20 | pub const LibcContext = @import("src/Libc.zig"); |
| 21 | pub const LinkContext = @import("src/Link.zig"); | ||
| 20 | 22 | ||
| 21 | const ModuleTestTarget = struct { | 23 | const ModuleTestTarget = struct { |
| 22 | linkage: ?std.builtin.LinkMode = null, | 24 | linkage: ?std.builtin.LinkMode = null, |
| ... | @@ -2059,6 +2061,57 @@ const c_abi_targets = blk: { | ... | @@ -2059,6 +2061,57 @@ const c_abi_targets = blk: { |
| 2059 | }; | 2061 | }; |
| 2060 | }; | 2062 | }; |
| 2061 | 2063 | ||
| 2064 | const LinkTarget = struct { | ||
| 2065 | target: std.Target.Query = .{}, | ||
| 2066 | link_libc: bool = false, | ||
| 2067 | use_llvm: bool = false, | ||
| 2068 | use_lld: bool = false, | ||
| 2069 | }; | ||
| 2070 | |||
| 2071 | const link_targets = blk: { | ||
| 2072 | @setEvalBranchQuota(30000); | ||
| 2073 | break :blk [_]LinkTarget{ | ||
| 2074 | // Native Targets | ||
| 2075 | |||
| 2076 | // .{ | ||
| 2077 | // .use_llvm = true, | ||
| 2078 | // }, | ||
| 2079 | |||
| 2080 | // Windows Targets | ||
| 2081 | |||
| 2082 | .{ | ||
| 2083 | .target = .{ | ||
| 2084 | .cpu_arch = .x86_64, | ||
| 2085 | .os_tag = .windows, | ||
| 2086 | .abi = .gnu, | ||
| 2087 | }, | ||
| 2088 | }, | ||
| 2089 | .{ | ||
| 2090 | .target = .{ | ||
| 2091 | .cpu_arch = .x86_64, | ||
| 2092 | .os_tag = .windows, | ||
| 2093 | .abi = .gnu, | ||
| 2094 | }, | ||
| 2095 | .link_libc = true, | ||
| 2096 | }, | ||
| 2097 | .{ | ||
| 2098 | .target = .{ | ||
| 2099 | .cpu_arch = .x86_64, | ||
| 2100 | .os_tag = .windows, | ||
| 2101 | .abi = .msvc, | ||
| 2102 | }, | ||
| 2103 | }, | ||
| 2104 | .{ | ||
| 2105 | .target = .{ | ||
| 2106 | .cpu_arch = .x86_64, | ||
| 2107 | .os_tag = .windows, | ||
| 2108 | .abi = .msvc, | ||
| 2109 | }, | ||
| 2110 | .link_libc = true, | ||
| 2111 | }, | ||
| 2112 | }; | ||
| 2113 | }; | ||
| 2114 | |||
| 2062 | /// Unlike `test_targets` and `c_abi_targets`, these targets are just simple strings which we pass | 2115 | /// Unlike `test_targets` and `c_abi_targets`, these targets are just simple strings which we pass |
| 2063 | /// directly to `incr-check`. They include the target triple and the compiler backend. | 2116 | /// directly to `incr-check`. They include the target triple and the compiler backend. |
| 2064 | /// | 2117 | /// |
| ... | @@ -3083,6 +3136,61 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step { | ... | @@ -3083,6 +3136,61 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step { |
| 3083 | return step; | 3136 | return step; |
| 3084 | } | 3137 | } |
| 3085 | 3138 | ||
| 3139 | const LinkTestOptions = struct { | ||
| 3140 | test_target_filters: []const []const u8, | ||
| 3141 | test_filters: []const []const u8, | ||
| 3142 | optimize_modes: []const OptimizeMode, | ||
| 3143 | skip_non_native: bool, | ||
| 3144 | skip_windows: bool, | ||
| 3145 | skip_llvm: bool, | ||
| 3146 | max_rss: usize, | ||
| 3147 | }; | ||
| 3148 | |||
| 3149 | pub fn addLinkTests(b: *std.Build, options: LinkTestOptions) *Step { | ||
| 3150 | const step = b.step("test-link", "Run the linker tests"); | ||
| 3151 | |||
| 3152 | for (link_targets) |link_target| { | ||
| 3153 | if (options.skip_non_native and !link_target.target.isNative()) continue; | ||
| 3154 | if (options.skip_windows and link_target.target.os_tag == .windows) continue; | ||
| 3155 | |||
| 3156 | const resolved_target = b.resolveTargetQuery(link_target.target); | ||
| 3157 | const triple_txt = resolved_target.query.zigTriple(b.allocator) catch @panic("OOM"); | ||
| 3158 | const target = &resolved_target.result; | ||
| 3159 | |||
| 3160 | if (options.test_target_filters.len > 0) { | ||
| 3161 | for (options.test_target_filters) |filter| { | ||
| 3162 | if (std.mem.indexOf(u8, triple_txt, filter) != null) break; | ||
| 3163 | } else continue; | ||
| 3164 | } | ||
| 3165 | |||
| 3166 | for (options.optimize_modes) |optimize_mode| { | ||
| 3167 | const would_use_llvm = wouldUseLlvm(link_target.use_llvm, link_target.target, optimize_mode); | ||
| 3168 | if (options.skip_llvm and would_use_llvm) continue; | ||
| 3169 | if (link_target.link_libc and target.abi == .msvc and b.graph.host.result.os.tag != .windows) continue; | ||
| 3170 | |||
| 3171 | link.addCases(.{ | ||
| 3172 | .b = b, | ||
| 3173 | .step = step, | ||
| 3174 | .optimize = optimize_mode, | ||
| 3175 | .target = resolved_target, | ||
| 3176 | .suffix = std.fmt.allocPrint(b.allocator, "{s}-{t}{s}{s}{s}", .{ | ||
| 3177 | target.zigTriple(b.allocator) catch @panic("OOM"), | ||
| 3178 | optimize_mode, | ||
| 3179 | if (link_target.use_llvm) "-llvm" else "", | ||
| 3180 | if (link_target.use_lld) "-lld" else "", | ||
| 3181 | if (link_target.link_libc) "-libc" else "", | ||
| 3182 | }) catch @panic("OOM"), | ||
| 3183 | .use_llvm = link_target.use_llvm, | ||
| 3184 | .use_lld = link_target.use_lld, | ||
| 3185 | .link_libc = link_target.link_libc, | ||
| 3186 | .test_filters = options.test_filters, | ||
| 3187 | .max_rss = options.max_rss, | ||
| 3188 | }); | ||
| 3189 | } | ||
| 3190 | } | ||
| 3191 | return step; | ||
| 3192 | } | ||
| 3193 | |||
| 3086 | pub fn addCases( | 3194 | pub fn addCases( |
| 3087 | b: *std.Build, | 3195 | b: *std.Build, |
| 3088 | parent_step: *Step, | 3196 | parent_step: *Step, |