| ... | @@ -2,6 +2,7 @@ b: *Build, | ... | @@ -2,6 +2,7 @@ b: *Build, |
| 2 | step: *Step, | 2 | step: *Step, |
| 3 | optimize: std.builtin.OptimizeMode, | 3 | optimize: std.builtin.OptimizeMode, |
| 4 | target: std.Build.ResolvedTarget, | 4 | target: std.Build.ResolvedTarget, |
| | 5 | target_desc: []const u8, |
| 5 | use_llvm: bool, | 6 | use_llvm: bool, |
| 6 | use_lld: bool, | 7 | use_lld: bool, |
| 7 | link_libc: bool, | 8 | link_libc: bool, |
| ... | @@ -10,111 +11,168 @@ update_step: ?*Step.UpdateSourceFiles, | ... | @@ -10,111 +11,168 @@ update_step: ?*Step.UpdateSourceFiles, |
| 10 | updated_snapshots: std.StringArrayHashMapUnmanaged(void), | 11 | updated_snapshots: std.StringArrayHashMapUnmanaged(void), |
| 11 | max_rss: usize, | 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 | if (for (self.test_filters) |filter| { | 15 | if (for (self.test_filters) |filter| { |
| 15 | if (std.mem.containsAtLeast(u8, prefix, 1, filter)) break false; | 16 | if (std.mem.containsAtLeast(u8, prefix, 1, filter)) break false; |
| 16 | } else self.test_filters.len > 0) return null; | 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 | pub fn sourcePath(self: *const Link, sub_path: []const u8) std.Build.LazyPath { | 25 | pub fn sourcePath(self: *const Link, sub_path: []const u8) std.Build.LazyPath { |
| 21 | return self.b.path(self.b.pathJoin(&.{ "test/link", sub_path })); | 26 | return self.b.path(self.b.pathJoin(&.{ "test/link", sub_path })); |
| 22 | } | 27 | } |
| 23 | | 28 | |
| 24 | pub fn addLibrary( | 29 | pub const Case = struct { |
| 25 | self: *const Link, | 30 | ctx: *Link, |
| 26 | linkage: std.builtin.LinkMode, | 31 | prefix: []const u8, |
| 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 | } | | |
| 37 | | 32 | |
| 38 | pub fn addObject(self: *const Link, overlay: OverlayOptions) *Step.Compile { | 33 | fn resolveName(self: *const Case, overlay: *const OverlayOptions) []const u8 { |
| 39 | return self.b.addObject(.{ | 34 | if (!overlay.name_prefix and !overlay.name_target) |
| 40 | .name = overlay.name, | 35 | return 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 | } | | |
| 46 | | 36 | |
| 47 | const SnapshotScope = packed struct { | 37 | if (overlay.name_prefix == overlay.name_target) |
| 48 | arch: bool = false, | 38 | return self.ctx.b.fmt("{s}-{s}-{s}", .{ self.prefix, overlay.name, self.ctx.target_desc }) |
| 49 | os: bool = false, | 39 | else if (overlay.name_prefix) |
| 50 | abi: bool = false, | 40 | return self.ctx.b.fmt("{s}-{s}", .{ self.prefix, overlay.name }) |
| 51 | optimize: bool = false, | 41 | else |
| 52 | use_llvm: bool = false, | 42 | return self.ctx.b.fmt("{s}-{s}", .{ overlay.name, self.ctx.target_desc }); |
| 53 | use_lld: bool = false, | 43 | } |
| 54 | link_libc: bool = false, | | |
| 55 | }; | | |
| 56 | | 44 | |
| 57 | /// Verify the results of a `zig objdump` call against a snapshot, which | 45 | pub fn addLibrary( |
| 58 | /// contains the expected output. Snapshots alias between all build | 46 | self: *const Case, |
| 59 | /// configurations by default, but by specifying fields in `scope`, | 47 | linkage: std.builtin.LinkMode, |
| 60 | /// unique snapshot names are generated for each value of that field. | 48 | overlay: OverlayOptions, |
| 61 | pub fn verifyObjdump( | 49 | ) *Step.Compile { |
| 62 | self: *Link, | 50 | return self.ctx.b.addLibrary(.{ |
| 63 | prefix: []const u8, | 51 | .linkage = linkage, |
| 64 | compile: *Step.Compile, | 52 | .name = self.resolveName(&overlay), |
| 65 | args: []const []const u8, | 53 | .root_module = self.ctx.createModule(overlay), |
| 66 | scope: SnapshotScope, | 54 | .use_llvm = overlay.use_llvm orelse self.ctx.use_llvm, |
| 67 | ) void { | 55 | .use_lld = overlay.use_lld orelse self.ctx.use_lld, |
| 68 | const snapshot_name = self.snapshotName(prefix, compile.name, scope) catch @panic("OOM"); | 56 | }); |
| 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; | | |
| 76 | } | 57 | } |
| 77 | | 58 | |
| 78 | const run_step = Step.Run.create(self.b, self.b.fmt("objdump {s}", .{snapshot_name})); | 59 | pub fn addExecutable( |
| 79 | run_step.addArgs(&.{ self.b.graph.zig_exe, "objdump" }); | 60 | self: *const Case, |
| 80 | run_step.addArtifactArg(compile); | 61 | overlay: OverlayOptions, |
| 81 | run_step.addArgs(args); | 62 | ) *Step.Compile { |
| 82 | run_step.addCheck(.{ .expect_term = .{ .exited = 0 } }); | 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| { | 71 | pub fn addRunArtifact( |
| 85 | // Workaround for the build system not realizing objdump itself has changed | 72 | self: *const Case, |
| 86 | run_step.has_side_effects = true; | 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(.{}); | 81 | pub fn addObject( |
| 89 | update_step.addCopyFileToSource(snapshot_update_path, snapshot_sub_path); | 82 | self: *const Case, |
| 90 | } else { | 83 | overlay: OverlayOptions, |
| 91 | run_step.addCheck(.{ .snapshot = .{ .file = self.b.path(snapshot_sub_path) } }); | 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); | 93 | const SnapshotScope = struct { |
| 95 | } | 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( | 150 | fn snapshotName( |
| 98 | self: *const Link, | 151 | self: *const Case, |
| 99 | test_name: []const u8, | 152 | scope: SnapshotScope, |
| 100 | compile_name: []const u8, | 153 | ) ![]const u8 { |
| 101 | scope: SnapshotScope, | 154 | const ctx = self.ctx; |
| 102 | ) ![]const u8 { | 155 | var snapshot_name: std.Io.Writer.Allocating = .init(ctx.b.allocator); |
| 103 | var snapshot_name: std.Io.Writer.Allocating = .init(self.b.allocator); | 156 | const w = &snapshot_name.writer; |
| 104 | const w = &snapshot_name.writer; | 157 | |
| 105 | | 158 | try w.writeAll(self.prefix); |
| 106 | try w.print("{s}.{s}", .{ test_name, compile_name }); | 159 | if (scope.sub_name) |sub_name| { |
| 107 | if (scope.arch) try w.print("-{t}", .{self.target.result.cpu.arch}); | 160 | try w.writeByte('.'); |
| 108 | if (scope.os) try w.print("-{t}", .{self.target.result.os.tag}); | 161 | try w.writeAll(sub_name); |
| 109 | if (scope.abi) try w.print("-{t}", .{self.target.result.abi}); | 162 | } |
| 110 | if (scope.optimize) try w.print("-{t}", .{self.optimize}); | 163 | |
| 111 | if (scope.use_llvm) try w.writeAll(if (self.use_llvm) "-llvm" else "-no-llvm"); | 164 | if (scope.arch) try w.print("-{t}", .{ctx.target.result.cpu.arch}); |
| 112 | if (scope.use_lld) try w.writeAll(if (self.use_lld) "-lld" else "-no-lld"); | 165 | if (scope.os) try w.print("-{t}", .{ctx.target.result.os.tag}); |
| 113 | if (scope.link_libc) try w.writeAll(if (self.link_libc) "-libc" else "-no-libc"); | 166 | if (scope.abi) try w.print("-{t}", .{ctx.target.result.abi}); |
| 114 | try w.writeAll(".dmp"); | 167 | if (scope.optimize) try w.print("-{t}", .{ctx.optimize}); |
| 115 | | 168 | if (scope.use_llvm) try w.writeAll(if (ctx.use_llvm) "-llvm" else "-no-llvm"); |
| 116 | return try snapshot_name.toOwnedSlice(); | 169 | if (scope.use_lld) try w.writeAll(if (ctx.use_lld) "-lld" else "-no-lld"); |
| 117 | } | 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 | fn createModule(self: *const Link, overlay: OverlayOptions) *Build.Module { | 177 | fn createModule(self: *const Link, overlay: OverlayOptions) *Build.Module { |
| 120 | const write_files = self.b.addWriteFiles(); | 178 | const write_files = self.b.addWriteFiles(); |
| ... | @@ -165,6 +223,13 @@ fn createModule(self: *const Link, overlay: OverlayOptions) *Build.Module { | ... | @@ -165,6 +223,13 @@ fn createModule(self: *const Link, overlay: OverlayOptions) *Build.Module { |
| 165 | | 223 | |
| 166 | const OverlayOptions = struct { | 224 | const OverlayOptions = struct { |
| 167 | name: []const u8, | 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 | asm_source_bytes: ?[]const u8 = null, | 233 | asm_source_bytes: ?[]const u8 = null, |
| 169 | c_source_bytes: ?[]const u8 = null, | 234 | c_source_bytes: ?[]const u8 = null, |
| 170 | c_source_flags: []const []const u8 = &.{}, | 235 | c_source_flags: []const []const u8 = &.{}, |