| ... | ... | @@ -2,6 +2,7 @@ b: *Build, |
| 2 | 2 | step: *Step, |
| 3 | 3 | optimize: std.builtin.OptimizeMode, |
| 4 | 4 | target: std.Build.ResolvedTarget, |
| 5 | target_desc: []const u8, |
| 5 | 6 | use_llvm: bool, |
| 6 | 7 | use_lld: bool, |
| 7 | 8 | link_libc: bool, |
| ... | ... | @@ -10,111 +11,168 @@ update_step: ?*Step.UpdateSourceFiles, |
| 10 | 11 | updated_snapshots: std.StringArrayHashMapUnmanaged(void), |
| 11 | 12 | max_rss: usize, |
| 12 | 13 | |
| 13 | | pub fn includeTest(self: *const Link, prefix: []const u8) ?[]const u8 { |
| 14 | pub fn includeTest(self: *Link, prefix: []const u8) ?Case { |
| 14 | 15 | if (for (self.test_filters) |filter| { |
| 15 | 16 | if (std.mem.containsAtLeast(u8, prefix, 1, filter)) break false; |
| 16 | 17 | } else self.test_filters.len > 0) return null; |
| 17 | | return prefix; |
| 18 | |
| 19 | return .{ |
| 20 | .ctx = self, |
| 21 | .prefix = prefix, |
| 22 | }; |
| 18 | 23 | } |
| 19 | 24 | |
| 20 | 25 | pub fn sourcePath(self: *const Link, sub_path: []const u8) std.Build.LazyPath { |
| 21 | 26 | return self.b.path(self.b.pathJoin(&.{ "test/link", sub_path })); |
| 22 | 27 | } |
| 23 | 28 | |
| 24 | | pub fn addLibrary( |
| 25 | | self: *const Link, |
| 26 | | linkage: std.builtin.LinkMode, |
| 27 | | overlay: OverlayOptions, |
| 28 | | ) *Step.Compile { |
| 29 | | return self.b.addLibrary(.{ |
| 30 | | .linkage = linkage, |
| 31 | | .name = overlay.name, |
| 32 | | .root_module = self.createModule(overlay), |
| 33 | | .use_llvm = overlay.use_llvm orelse self.use_llvm, |
| 34 | | .use_lld = overlay.use_lld orelse self.use_lld, |
| 35 | | }); |
| 36 | | } |
| 29 | pub const Case = struct { |
| 30 | ctx: *Link, |
| 31 | prefix: []const u8, |
| 37 | 32 | |
| 38 | | pub fn addObject(self: *const Link, overlay: OverlayOptions) *Step.Compile { |
| 39 | | return self.b.addObject(.{ |
| 40 | | .name = overlay.name, |
| 41 | | .root_module = self.createModule(overlay), |
| 42 | | .use_llvm = overlay.use_llvm orelse self.use_llvm, |
| 43 | | .use_lld = overlay.use_lld orelse self.use_lld, |
| 44 | | }); |
| 45 | | } |
| 33 | fn resolveName(self: *const Case, overlay: *const OverlayOptions) []const u8 { |
| 34 | if (!overlay.name_prefix and !overlay.name_target) |
| 35 | return overlay.name; |
| 46 | 36 | |
| 47 | | const SnapshotScope = packed struct { |
| 48 | | arch: bool = false, |
| 49 | | os: bool = false, |
| 50 | | abi: bool = false, |
| 51 | | optimize: bool = false, |
| 52 | | use_llvm: bool = false, |
| 53 | | use_lld: bool = false, |
| 54 | | link_libc: bool = false, |
| 55 | | }; |
| 37 | if (overlay.name_prefix == overlay.name_target) |
| 38 | return self.ctx.b.fmt("{s}-{s}-{s}", .{ self.prefix, overlay.name, self.ctx.target_desc }) |
| 39 | else if (overlay.name_prefix) |
| 40 | return self.ctx.b.fmt("{s}-{s}", .{ self.prefix, overlay.name }) |
| 41 | else |
| 42 | return self.ctx.b.fmt("{s}-{s}", .{ overlay.name, self.ctx.target_desc }); |
| 43 | } |
| 56 | 44 | |
| 57 | | /// Verify the results of a `zig objdump` call against a snapshot, which |
| 58 | | /// contains the expected output. Snapshots alias between all build |
| 59 | | /// configurations by default, but by specifying fields in `scope`, |
| 60 | | /// unique snapshot names are generated for each value of that field. |
| 61 | | pub fn verifyObjdump( |
| 62 | | self: *Link, |
| 63 | | prefix: []const u8, |
| 64 | | compile: *Step.Compile, |
| 65 | | args: []const []const u8, |
| 66 | | scope: SnapshotScope, |
| 67 | | ) void { |
| 68 | | const snapshot_name = self.snapshotName(prefix, compile.name, scope) catch @panic("OOM"); |
| 69 | | const snapshot_sub_path = self.b.pathJoin(&.{ "test/link/snapshots/", snapshot_name }); |
| 70 | | |
| 71 | | // Many tests may read the same snapshot, so only use the first one to update. |
| 72 | | // If there are differences in output, they will show up on the next test run. |
| 73 | | if (self.update_step != null) { |
| 74 | | const gop = self.updated_snapshots.getOrPut(self.b.allocator, snapshot_sub_path) catch @panic("OOM"); |
| 75 | | if (gop.found_existing) return; |
| 45 | pub fn addLibrary( |
| 46 | self: *const Case, |
| 47 | linkage: std.builtin.LinkMode, |
| 48 | overlay: OverlayOptions, |
| 49 | ) *Step.Compile { |
| 50 | return self.ctx.b.addLibrary(.{ |
| 51 | .linkage = linkage, |
| 52 | .name = self.resolveName(&overlay), |
| 53 | .root_module = self.ctx.createModule(overlay), |
| 54 | .use_llvm = overlay.use_llvm orelse self.ctx.use_llvm, |
| 55 | .use_lld = overlay.use_lld orelse self.ctx.use_lld, |
| 56 | }); |
| 76 | 57 | } |
| 77 | 58 | |
| 78 | | const run_step = Step.Run.create(self.b, self.b.fmt("objdump {s}", .{snapshot_name})); |
| 79 | | run_step.addArgs(&.{ self.b.graph.zig_exe, "objdump" }); |
| 80 | | run_step.addArtifactArg(compile); |
| 81 | | run_step.addArgs(args); |
| 82 | | run_step.addCheck(.{ .expect_term = .{ .exited = 0 } }); |
| 59 | pub fn addExecutable( |
| 60 | self: *const Case, |
| 61 | overlay: OverlayOptions, |
| 62 | ) *Step.Compile { |
| 63 | return self.ctx.b.addExecutable(.{ |
| 64 | .name = self.resolveName(&overlay), |
| 65 | .root_module = self.ctx.createModule(overlay), |
| 66 | .use_llvm = overlay.use_llvm orelse self.ctx.use_llvm, |
| 67 | .use_lld = overlay.use_lld orelse self.ctx.use_lld, |
| 68 | }); |
| 69 | } |
| 83 | 70 | |
| 84 | | if (self.update_step) |update_step| { |
| 85 | | // Workaround for the build system not realizing objdump itself has changed |
| 86 | | run_step.has_side_effects = true; |
| 71 | pub fn addRunArtifact( |
| 72 | self: *const Case, |
| 73 | exe: *Step.Compile, |
| 74 | ) *Step.Run { |
| 75 | const run_step = self.ctx.b.addRunArtifact(exe); |
| 76 | run_step.skip_foreign_checks = true; |
| 77 | self.ctx.step.dependOn(&run_step.step); |
| 78 | return run_step; |
| 79 | } |
| 87 | 80 | |
| 88 | | const snapshot_update_path = run_step.captureStdOut(.{}); |
| 89 | | update_step.addCopyFileToSource(snapshot_update_path, snapshot_sub_path); |
| 90 | | } else { |
| 91 | | run_step.addCheck(.{ .snapshot = .{ .file = self.b.path(snapshot_sub_path) } }); |
| 81 | pub fn addObject( |
| 82 | self: *const Case, |
| 83 | overlay: OverlayOptions, |
| 84 | ) *Step.Compile { |
| 85 | return self.ctx.b.addObject(.{ |
| 86 | .name = self.resolveName(&overlay), |
| 87 | .root_module = self.ctx.createModule(overlay), |
| 88 | .use_llvm = overlay.use_llvm orelse self.ctx.use_llvm, |
| 89 | .use_lld = overlay.use_lld orelse self.ctx.use_lld, |
| 90 | }); |
| 92 | 91 | } |
| 93 | 92 | |
| 94 | | self.step.dependOn(&run_step.step); |
| 95 | | } |
| 93 | const SnapshotScope = struct { |
| 94 | /// If a test case has multiple verifyObjdump calls, `opt_sub_name` can |
| 95 | /// be used to differentiate them. |
| 96 | sub_name: ?[]const u8 = null, |
| 97 | arch: bool = false, |
| 98 | os: bool = false, |
| 99 | abi: bool = false, |
| 100 | optimize: bool = false, |
| 101 | use_llvm: bool = false, |
| 102 | use_lld: bool = false, |
| 103 | link_libc: bool = false, |
| 104 | }; |
| 105 | |
| 106 | /// Verify the results of a `zig objdump` call against a snapshot, which |
| 107 | /// contains the expected output. Snapshots alias between all build |
| 108 | /// configurations by default, but by specifying fields in `scope`, |
| 109 | /// unique snapshot names are generated for each value of that field. |
| 110 | /// |
| 111 | pub fn verifyObjdump( |
| 112 | self: *const Case, |
| 113 | compile: *Step.Compile, |
| 114 | args: []const []const u8, |
| 115 | scope: SnapshotScope, |
| 116 | ) void { |
| 117 | const ctx = self.ctx; |
| 118 | const snapshot_name = self.snapshotName(scope) catch @panic("OOM"); |
| 119 | const snapshot_sub_path = ctx.b.pathJoin(&.{ "test/link/snapshots/", snapshot_name }); |
| 120 | |
| 121 | // Many tests may read the same snapshot, so only use the first one to update. |
| 122 | // If there are differences in output, they will show up on the next test run. |
| 123 | if (ctx.update_step != null) { |
| 124 | const gop = ctx.updated_snapshots.getOrPut(ctx.b.allocator, snapshot_sub_path) catch @panic("OOM"); |
| 125 | if (gop.found_existing) return; |
| 126 | } |
| 127 | |
| 128 | const run_step = Step.Run.create(ctx.b, ctx.b.fmt( |
| 129 | "objdump {s} {s}", |
| 130 | .{ snapshot_name, ctx.target_desc }, |
| 131 | )); |
| 132 | run_step.addArgs(&.{ ctx.b.graph.zig_exe, "objdump" }); |
| 133 | run_step.addArtifactArg(compile); |
| 134 | run_step.addArgs(args); |
| 135 | run_step.addCheck(.{ .expect_term = .{ .exited = 0 } }); |
| 136 | |
| 137 | if (ctx.update_step) |update_step| { |
| 138 | // Workaround for the build system not realizing objdump itself has changed |
| 139 | run_step.has_side_effects = true; |
| 140 | |
| 141 | const snapshot_update_path = run_step.captureStdOut(.{}); |
| 142 | update_step.addCopyFileToSource(snapshot_update_path, snapshot_sub_path); |
| 143 | } else { |
| 144 | run_step.addCheck(.{ .snapshot = .{ .file = ctx.b.path(snapshot_sub_path) } }); |
| 145 | } |
| 146 | |
| 147 | ctx.step.dependOn(&run_step.step); |
| 148 | } |
| 96 | 149 | |
| 97 | | fn snapshotName( |
| 98 | | self: *const Link, |
| 99 | | test_name: []const u8, |
| 100 | | compile_name: []const u8, |
| 101 | | scope: SnapshotScope, |
| 102 | | ) ![]const u8 { |
| 103 | | var snapshot_name: std.Io.Writer.Allocating = .init(self.b.allocator); |
| 104 | | const w = &snapshot_name.writer; |
| 105 | | |
| 106 | | try w.print("{s}.{s}", .{ test_name, compile_name }); |
| 107 | | if (scope.arch) try w.print("-{t}", .{self.target.result.cpu.arch}); |
| 108 | | if (scope.os) try w.print("-{t}", .{self.target.result.os.tag}); |
| 109 | | if (scope.abi) try w.print("-{t}", .{self.target.result.abi}); |
| 110 | | if (scope.optimize) try w.print("-{t}", .{self.optimize}); |
| 111 | | if (scope.use_llvm) try w.writeAll(if (self.use_llvm) "-llvm" else "-no-llvm"); |
| 112 | | if (scope.use_lld) try w.writeAll(if (self.use_lld) "-lld" else "-no-lld"); |
| 113 | | if (scope.link_libc) try w.writeAll(if (self.link_libc) "-libc" else "-no-libc"); |
| 114 | | try w.writeAll(".dmp"); |
| 115 | | |
| 116 | | return try snapshot_name.toOwnedSlice(); |
| 117 | | } |
| 150 | fn snapshotName( |
| 151 | self: *const Case, |
| 152 | scope: SnapshotScope, |
| 153 | ) ![]const u8 { |
| 154 | const ctx = self.ctx; |
| 155 | var snapshot_name: std.Io.Writer.Allocating = .init(ctx.b.allocator); |
| 156 | const w = &snapshot_name.writer; |
| 157 | |
| 158 | try w.writeAll(self.prefix); |
| 159 | if (scope.sub_name) |sub_name| { |
| 160 | try w.writeByte('.'); |
| 161 | try w.writeAll(sub_name); |
| 162 | } |
| 163 | |
| 164 | if (scope.arch) try w.print("-{t}", .{ctx.target.result.cpu.arch}); |
| 165 | if (scope.os) try w.print("-{t}", .{ctx.target.result.os.tag}); |
| 166 | if (scope.abi) try w.print("-{t}", .{ctx.target.result.abi}); |
| 167 | if (scope.optimize) try w.print("-{t}", .{ctx.optimize}); |
| 168 | if (scope.use_llvm) try w.writeAll(if (ctx.use_llvm) "-llvm" else "-no-llvm"); |
| 169 | if (scope.use_lld) try w.writeAll(if (ctx.use_lld) "-lld" else "-no-lld"); |
| 170 | if (scope.link_libc) try w.writeAll(if (ctx.link_libc) "-libc" else "-no-libc"); |
| 171 | try w.writeAll(".dmp"); |
| 172 | |
| 173 | return try snapshot_name.toOwnedSlice(); |
| 174 | } |
| 175 | }; |
| 118 | 176 | |
| 119 | 177 | fn createModule(self: *const Link, overlay: OverlayOptions) *Build.Module { |
| 120 | 178 | const write_files = self.b.addWriteFiles(); |
| ... | ... | @@ -165,6 +223,13 @@ fn createModule(self: *const Link, overlay: OverlayOptions) *Build.Module { |
| 165 | 223 | |
| 166 | 224 | const OverlayOptions = struct { |
| 167 | 225 | name: []const u8, |
| 226 | /// Prefix the name with the test case prefix. |
| 227 | /// Unset if names with specific lengths are needed. |
| 228 | name_prefix: bool = true, |
| 229 | /// Prefix the name with `target_desc`. |
| 230 | /// Can be unset when the snapshot needs to contain the name, |
| 231 | /// so that snapshots can alias between targets. |
| 232 | name_target: bool = true, |
| 168 | 233 | asm_source_bytes: ?[]const u8 = null, |
| 169 | 234 | c_source_bytes: ?[]const u8 = null, |
| 170 | 235 | c_source_flags: []const []const u8 = &.{}, |