diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index 869471d4e921856cee4fd870c299a95aa0fd5965..d0d36f62ce2c0d36aac774392edce182ed621d4a 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -2119,7 +2119,7 @@ fn markFailedStepsDirty(maker: *Maker) void { for (all_steps) |step_index| { const step = maker.stepByIndex(step_index); switch (step.state) { - .dependency_failure, .failure, .skipped => _ = maker.invalidateResult(step), + .dependency_failure, .dependency_skipped, .failure, .skipped => _ = maker.invalidateResult(step), else => continue, } } @@ -2334,7 +2334,7 @@ fn makeSteps( .precheck_unstarted => unreachable, .precheck_started => unreachable, .precheck_done => unreachable, - .dependency_failure => pending_count += 1, + .dependency_failure, .dependency_skipped => pending_count += 1, .success => success_count += 1, .skipped, .skipped_oom => skipped_count += 1, .failure => { @@ -2580,10 +2580,14 @@ fn makeStep( .failure, .dependency_failure, - .skipped_oom, => break .dependency_failure, - .success, .skipped => {}, + .dependency_skipped, + .skipped_oom, + .skipped, + => break .dependency_skipped, + + .success => {}, } } else if (Step.make(step_index, maker, step_prog_node)) state: { break :state .success; @@ -2602,11 +2606,12 @@ fn makeStep( .failure, .dependency_failure, + .dependency_skipped, .skipped_oom, + .skipped, => false, .success, - .skipped, => true, }; @@ -2623,7 +2628,7 @@ fn makeStep( .precheck_done => unreachable, .success => .success, .failure, .dependency_failure => .failure, - .skipped => .skipped, + .dependency_skipped, .skipped => .skipped, .skipped_oom => .skipped_oom, }; serveBuildStepCompleted( @@ -2777,6 +2782,12 @@ fn printStepStatus(maker: *Maker, step_index: Configuration.Step.Index, stderr: try stderr.setColor(.reset); }, + .dependency_skipped => { + try stderr.setColor(.dim); + try writer.writeAll(" transitive skip\n"); + try stderr.setColor(.reset); + }, + .success => { try stderr.setColor(.green); if (s.result_cached) { @@ -3023,6 +3034,7 @@ fn constructGraphAndCheckForDependencyLoop( // These don't happen until we actually run the step graph. .dependency_failure => unreachable, + .dependency_skipped => unreachable, .success => unreachable, .failure => unreachable, .skipped => unreachable, diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index b8c5992cce243ed5b7f5fb0cdfc1b6b9d92f7fdb..181cdf2bd75fc678fc45d72cbab16a9e9bbeecd1 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -163,6 +163,9 @@ pub const State = enum { /// be re-evaluated. precheck_done, dependency_failure, + /// Handled exactly the same as `dependency_failure` except communicates + /// that the dependency didn't fail but rather was skipped. + dependency_skipped, success, failure, /// This state indicates that the step did not complete, however, it also did not fail, diff --git a/lib/compiler/Maker/Step/Run.zig b/lib/compiler/Maker/Step/Run.zig index 7f74e76954f44e5b965955d5ff5113d07428b6a7..85087be4d06ff29c71eabebd303e9ba6077dd1f9 100644 --- a/lib/compiler/Maker/Step/Run.zig +++ b/lib/compiler/Maker/Step/Run.zig @@ -187,6 +187,11 @@ pub fn make( man.hash.addListOfBytes(run_args); } }, + .enable_darling => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value), + .enable_qemu => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value), + .enable_rosetta => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value), + .enable_wasmtime => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value), + .enable_wine => thirdPartyToggle(&man.hash, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value), } } @@ -351,6 +356,29 @@ pub fn make( step.clearFailedCommand(gpa); } +fn thirdPartyToggle( + man_hash: ?*Cache.HashHelper, + argv_list: *std.ArrayList([]const u8), + conf: *const Configuration, + setting: bool, + enable: ?Configuration.String, + disable: ?Configuration.String, +) void { + if (setting) { + if (enable) |string| { + const slice = string.slice(conf); + if (man_hash) |h| h.addBytesZ(slice); + argv_list.appendAssumeCapacity(slice); + } + } else { + if (disable) |string| { + const slice = string.slice(conf); + if (man_hash) |h| h.addBytesZ(slice); + argv_list.appendAssumeCapacity(slice); + } + } +} + /// Reads stdout of a Zig test process until a termination condition is reached: /// * A write fails, indicating the child unexpectedly closed stdin /// * A test (or a response from the test runner) times out @@ -1535,6 +1563,11 @@ pub fn rerunInFuzzMode( .output_file => unreachable, .output_directory => unreachable, .passthru => unreachable, + .enable_darling => thirdPartyToggle(null, &argv_list, conf, graph.enable_darling, arg.prefix.value, arg.suffix.value), + .enable_qemu => thirdPartyToggle(null, &argv_list, conf, graph.enable_qemu, arg.prefix.value, arg.suffix.value), + .enable_rosetta => thirdPartyToggle(null, &argv_list, conf, graph.enable_rosetta, arg.prefix.value, arg.suffix.value), + .enable_wasmtime => thirdPartyToggle(null, &argv_list, conf, graph.enable_wasmtime, arg.prefix.value, arg.suffix.value), + .enable_wine => thirdPartyToggle(null, &argv_list, conf, graph.enable_wine, arg.prefix.value, arg.suffix.value), } } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index eed57a0b15b47b23b54b412c4305f12020b9773c..0258ce941c5fbb32bb162095d2ebec28262f6fc6 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -45,17 +45,6 @@ debug_log_scopes: []const []const u8 = &.{}, /// Set to 0 to disable stack collection. debug_stack_frames_count: u8 = 8, -/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. -enable_darling: bool = false, -/// Use system QEMU installation to run cross compiled foreign architecture build artifacts. -enable_qemu: bool = false, -/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS. -enable_rosetta: bool = false, -/// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts. -enable_wasmtime: bool = false, -/// Use system Wine installation to run cross compiled Windows build artifacts. -enable_wine: bool = false, - dep_prefix: []const u8 = "", modules: std.array_hash_map.String(*Module), @@ -388,11 +377,6 @@ fn createChild( .default_step = undefined, .top_level_steps = .{}, .debug_log_scopes = parent.debug_log_scopes, - .enable_darling = parent.enable_darling, - .enable_qemu = parent.enable_qemu, - .enable_rosetta = parent.enable_rosetta, - .enable_wasmtime = parent.enable_wasmtime, - .enable_wine = parent.enable_wine, .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), .modules = .empty, .named_writefiles = .empty, diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 8337da6d2f9d44f6f3b0a96241b4478f55afe3b0..fb5b04dc0039b28850dd11817b73c2f2b597da3d 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -626,6 +626,13 @@ pub const Step = extern struct { output_file, output_directory, passthru, + /// `prefix` contains the enabled string. + /// `suffix` contains the disabled string. + enable_darling, + enable_qemu, + enable_rosetta, + enable_wasmtime, + enable_wine, }; pub const Index = IndexType(@This()); diff --git a/lib/std/Build/Serialize.zig b/lib/std/Build/Serialize.zig index c3e9443181565196d6d922ea97f6e94f386b0f8a..995a69da87f9a6becfdb35f61683178b5e046c8b 100644 --- a/lib/std/Build/Serialize.zig +++ b/lib/std/Build/Serialize.zig @@ -1017,6 +1017,101 @@ fn initArgsList(s: *Serialize, args: []const Step.Run.Arg) ![]const Configuratio .producer = .{ .value = null }, .generated = .{ .value = null }, }, + .enable_darling => |a| .{ + .flags = .{ + .tag = .enable_darling, + .prefix = a.enabled != null, + .suffix = a.disabled != null, + .basename = false, + .path = false, + .producer = false, + .generated = false, + .dep_file = false, + .make_absolute = false, + }, + .prefix = .{ .value = try s.addOptionalString(a.enabled) }, + .suffix = .{ .value = try s.addOptionalString(a.disabled) }, + .basename = .{ .value = null }, + .path = .{ .value = null }, + .producer = .{ .value = null }, + .generated = .{ .value = null }, + }, + .enable_qemu => |a| .{ + .flags = .{ + .tag = .enable_qemu, + .prefix = a.enabled != null, + .suffix = a.disabled != null, + .basename = false, + .path = false, + .producer = false, + .generated = false, + .dep_file = false, + .make_absolute = false, + }, + .prefix = .{ .value = try s.addOptionalString(a.enabled) }, + .suffix = .{ .value = try s.addOptionalString(a.disabled) }, + .basename = .{ .value = null }, + .path = .{ .value = null }, + .producer = .{ .value = null }, + .generated = .{ .value = null }, + }, + .enable_rosetta => |a| .{ + .flags = .{ + .tag = .enable_rosetta, + .prefix = a.enabled != null, + .suffix = a.disabled != null, + .basename = false, + .path = false, + .producer = false, + .generated = false, + .dep_file = false, + .make_absolute = false, + }, + .prefix = .{ .value = try s.addOptionalString(a.enabled) }, + .suffix = .{ .value = try s.addOptionalString(a.disabled) }, + .basename = .{ .value = null }, + .path = .{ .value = null }, + .producer = .{ .value = null }, + .generated = .{ .value = null }, + }, + .enable_wasmtime => |a| .{ + .flags = .{ + .tag = .enable_wasmtime, + .prefix = a.enabled != null, + .suffix = a.disabled != null, + .basename = false, + .path = false, + .producer = false, + .generated = false, + .dep_file = false, + .make_absolute = false, + }, + .prefix = .{ .value = try s.addOptionalString(a.enabled) }, + .suffix = .{ .value = try s.addOptionalString(a.disabled) }, + .basename = .{ .value = null }, + .path = .{ .value = null }, + .producer = .{ .value = null }, + .generated = .{ .value = null }, + }, + .enable_wine => |a| .{ + .flags = .{ + .tag = .enable_wine, + .prefix = a.enabled != null, + .suffix = a.disabled != null, + .basename = false, + .path = false, + .producer = false, + .generated = false, + .dep_file = false, + .make_absolute = false, + }, + .prefix = .{ .value = try s.addOptionalString(a.enabled) }, + .suffix = .{ .value = try s.addOptionalString(a.disabled) }, + .basename = .{ .value = null }, + .path = .{ .value = null }, + .producer = .{ .value = null }, + .generated = .{ .value = null }, + }, }); } return result; diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 9e1f3ca7047585a13fcae9b77de0afb6c45f9eee..7d271e10e1461606574b96a6966332975b39b706 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -68,9 +68,11 @@ rename_step_with_output_arg: bool, /// executed binary will not fail the build if the binary cannot be executed /// due to being for a foreign binary to the host system which is running the /// build graph. +/// /// Command-line arguments such as -fqemu and -fwasmtime may affect whether a /// binary is detected as foreign, as well as system configuration such as /// Rosetta (macOS) and binfmt_misc (Linux). +/// /// If this Run step is considered to have side-effects, then this flag does /// nothing. skip_foreign_checks: bool, @@ -149,6 +151,19 @@ pub const Arg = union(enum) { output_directory: *Output, /// The arguments passed after "--" on the "zig build" CLI. passthru, + + enable_darling: ToggleFlags, + enable_qemu: ToggleFlags, + enable_rosetta: ToggleFlags, + enable_wasmtime: ToggleFlags, + enable_wine: ToggleFlags, +}; + +pub const ToggleFlags = struct { + /// The string to pass when enabled, or null to omit the arg. + enabled: ?[]const u8 = null, + /// The string to pass when disabled, or null to omit the arg. + disabled: ?[]const u8 = null, }; pub const DecoratedArtifact = struct { @@ -576,6 +591,46 @@ pub fn addPassthruArgs(run: *Run) void { run.argv.append(arena, .passthru) catch @panic("OOM"); } +/// Appends a custom string to the command line depending on the `-fdarling` +/// value passed to `zig build`. +pub fn addThirdPartyEnabledArgDarling(run: *Run, toggle_flags: ToggleFlags) void { + const graph = run.step.owner.graph; + const arena = graph.arena; + run.argv.append(arena, .{ .enable_darling = toggle_flags }) catch @panic("OOM"); +} + +/// Appends a custom string to the command line depending on the `-fqemu` +/// value passed to `zig build`. +pub fn addThirdPartyEnabledArgQemu(run: *Run, toggle_flags: ToggleFlags) void { + const graph = run.step.owner.graph; + const arena = graph.arena; + run.argv.append(arena, .{ .enable_qemu = toggle_flags }) catch @panic("OOM"); +} + +/// Appends a custom string to the command line depending on the `-frosetta` +/// value passed to `zig build`. +pub fn addThirdPartyEnabledArgRosetta(run: *Run, toggle_flags: ToggleFlags) void { + const graph = run.step.owner.graph; + const arena = graph.arena; + run.argv.append(arena, .{ .enable_rosetta = toggle_flags }) catch @panic("OOM"); +} + +/// Appends a custom string to the command line depending on the `-fwasmtime` +/// value passed to `zig build`. +pub fn addThirdPartyEnabledArgWasmtime(run: *Run, toggle_flags: ToggleFlags) void { + const graph = run.step.owner.graph; + const arena = graph.arena; + run.argv.append(arena, .{ .enable_wasmtime = toggle_flags }) catch @panic("OOM"); +} + +/// Appends a custom string to the command line depending on the `-fwine` +/// value passed to `zig build`. +pub fn addThirdPartyEnabledArgWine(run: *Run, toggle_flags: ToggleFlags) void { + const graph = run.step.owner.graph; + const arena = graph.arena; + run.argv.append(arena, .{ .enable_wine = toggle_flags }) catch @panic("OOM"); +} + pub fn setStdIn(run: *Run, stdin: StdIn) void { switch (stdin) { .lazy_path => |lazy_path| lazy_path.addStepDependencies(&run.step), diff --git a/test/error_traces.zig b/test/error_traces.zig index 071e4e8df53b7f017f988f6759db5adc30cebef0..5f84202fcfa5268094a5b625c10e30cda2bc3725 100644 --- a/test/error_traces.zig +++ b/test/error_traces.zig @@ -1,7 +1,10 @@ const std = @import("std"); +const Context = @import("tests.zig").ErrorTracesContext; -pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target.Os.Tag) void { +pub fn addCases(cases: *Context, params: *const Context.CaseParameters, target: *const std.Target) void { cases.addCase(.{ + .params = params, + .target = target, .name = "return", .source = \\pub fn main() !void { @@ -17,6 +20,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "try return", .source = \\fn foo() !void { @@ -44,6 +49,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }, }); cases.addCase(.{ + .params = params, + .target = target, .name = "non-error return pops error trace", .source = \\fn bar() !void { @@ -70,6 +77,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "continue in while loop", .source = \\fn foo() !void { @@ -93,6 +102,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "for loop pops error return trace", .source = \\fn foo() !void { return error.FooError; } @@ -123,6 +134,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "implicit continue in for loop pops stale error return trace", .source = \\fn foo() !void { return error.FooError; } @@ -154,6 +167,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "while loop pops error return trace", .source = \\fn foo() !void { return error.FooError; } @@ -186,6 +201,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "implicit continue in while loop pops stale error return trace", .source = \\fn foo() !void { return error.FooError; } @@ -219,6 +236,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "try return + handled catch/if-else", .source = \\fn foo() !void { @@ -251,6 +270,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "break from inline loop pops error return trace", .source = \\fn foo() !void { return error.FooBar; } @@ -276,6 +297,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "catch and re-throw error", .source = \\fn foo() !void { @@ -304,6 +327,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "errors stored in var do not contribute to error trace", .source = \\fn foo() !void { @@ -328,6 +353,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "error stored in const has trace preserved for duration of block", .source = \\fn foo() !void { return error.TheSkyIsFalling; } @@ -376,6 +403,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "error passed to function has its trace preserved for duration of the call", .source = \\pub fn expectError(expected_error: anyerror, actual_error: anyerror!void) !void { @@ -418,6 +447,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "try return from within catch", .source = \\fn foo() !void { @@ -455,6 +486,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "try return from within if-else", .source = \\fn foo() !void { @@ -492,6 +525,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "try try return return", .source = \\fn foo() !void { @@ -534,6 +569,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "error union switch with call operand", .source = \\pub fn main() !void { @@ -579,6 +616,8 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "trace through inline call", // The main function has two inline calls to ensure // that inlinees in PDBs are properly deduplicated. @@ -595,7 +634,7 @@ pub fn addCases(cases: *@import("tests.zig").ErrorTracesContext, os: std.Target. \\} , .expect_error = "ThisIsSoSad", - .expect_trace = switch (os) { + .expect_trace = switch (target.os.tag) { // LLVM doesn't emit column info in the binary annotations for inlinee callees in PDBs, // so our expected result is slightly different for Windows than on other operating // systems. diff --git a/test/src/ErrorTrace.zig b/test/src/ErrorTrace.zig index b0ce8b05bb39687429ffb8eec893f254d4c4813a..7fda389dc6c295a6c950f95a6551945eab878643 100644 --- a/test/src/ErrorTrace.zig +++ b/test/src/ErrorTrace.zig @@ -1,11 +1,81 @@ +const ErrorTrace = @This(); + +const builtin = @import("builtin"); + +const std = @import("std"); +const Step = std.Build.Step; +const OptimizeMode = std.lang.Optimize; +const mem = std.mem; + +const error_traces_cases = @import("../error_traces.zig"); + b: *std.Build, step: *Step, test_filters: []const []const u8, -targets: []const std.Build.ResolvedTarget, +skip_non_native: bool, optimize_modes: []const OptimizeMode, convert_exe: *std.Build.Step.Compile, +pub const CaseParameters = @import("StackTrace.zig").CaseParameters; + +const param_sets = [_]CaseParameters{ + .{}, + .{ + .link_libc = true, + }, + .{ + .use_llvm = true, + .use_lld = true, + }, + .{ + .pie = true, + }, + .{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .windows, + .abi = .msvc, + }, + }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .gnu, + }, + }, + .{ + .target = .{ + .cpu_arch = .x86, + .os_tag = .windows, + .abi = .msvc, + }, + }, + .{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .macos, + }, + }, + .{ + .target = .{ + .cpu_arch = .s390x, + .os_tag = .linux, + .abi = .none, + }, + }, + .{ + .target = .{ + .cpu_arch = .loongarch32, + .os_tag = .linux, + .abi = .none, + }, + }, +}; + pub const Case = struct { + params: *const CaseParameters, + target: *const std.Target, name: []const u8, source: []const u8, expect_error: []const u8, @@ -22,50 +92,47 @@ pub const Case = struct { pub const Backend = enum { llvm, selfhosted }; }; -pub fn addCase(self: *ErrorTrace, case: Case) void { - for (self.targets) |*target| { - const triple: ?[]const u8 = if (target.query.isNative()) null else t: { - break :t target.query.zigTriple(self.b.graph.arena) catch @panic("OOM"); - }; +pub fn addCases(self: *ErrorTrace) void { + const b = self.b; + + for (¶m_sets) |*params| { + const resolved_target = b.resolveTargetQuery(params.target); + + if (self.skip_non_native and !resolved_target.query.isNative()) continue; + + // To avoid redundant testing, skip cross-compilation targets matching the host. + if (resolved_target.result.os.tag == builtin.target.os.tag and + resolved_target.result.cpu.arch == builtin.target.cpu.arch) + { + continue; + } + for (self.optimize_modes) |optimize| { - self.addCaseConfig(case, target, triple, optimize, .llvm); - } - if (shouldTestNonLlvm(&target.result)) { - for (self.optimize_modes) |optimize| { - self.addCaseConfig(case, target, triple, optimize, .selfhosted); - } - } + if (optimize == params.optimize) break; + } else return; + + error_traces_cases.addCases(self, params, &resolved_target.result); } } -fn shouldTestNonLlvm(target: *const std.Target) bool { - if (comptime builtin.cpu.arch.endian() == .big) return false; // https://github.com/ziglang/zig/issues/25961 - return switch (target.cpu.arch) { - .x86_64 => switch (target.ofmt) { - .elf => !target.os.tag.isBSD() and target.os.tag != .illumos, - else => false, - }, - else => false, - }; -} - -fn addCaseConfig( - self: *ErrorTrace, - case: Case, - target: *const std.Build.ResolvedTarget, - triple: ?[]const u8, - optimize: OptimizeMode, - backend: Case.Backend, -) void { +/// Called from test/error_traces.zig +pub fn addCase(self: *ErrorTrace, case: Case) void { const b = self.b; + const params = case.params; + const target = case.target; + const target_query = params.target; + + const triple: ?[]const u8 = if (target_query.isNative()) null else t: { + break :t target_query.zigTriple(self.b.graph.arena) catch @panic("OOM"); + }; const error_tracing: bool = tracing: { - if (optimize == .debug) break :tracing true; - if (backend != .llvm) break :tracing true; - if (optimize == .small) break :tracing false; + if (params.optimize == .debug) break :tracing true; + if (params.use_llvm == false) break :tracing true; + if (params.optimize == .small) break :tracing false; for (case.disable_trace_optimized) |disable| { const d_arch, const d_os = disable; - if (target.result.cpu.arch == d_arch and target.result.os.tag == d_os) { + if (target.cpu.arch == d_arch and target.os.tag == d_os) { // This particular configuration cannot do error tracing in optimized LLVM builds. break :tracing false; } @@ -73,12 +140,19 @@ fn addCaseConfig( break :tracing true; }; - const annotated_case_name = b.fmt("check {s} ({s}{s}{s} {s})", .{ + const backend_string = if (params.use_llvm == true) + "-llvm" + else if (params.use_llvm == false) + "-selfhosted" + else + ""; + + const annotated_case_name = b.fmt("check {s} ({s}{s}{t}{s})", .{ case.name, triple orelse "", if (triple != null) " " else "", - @tagName(optimize), - @tagName(backend), + params.optimize, + backend_string, }); if (self.test_filters.len > 0) { for (self.test_filters) |test_filter| { @@ -92,19 +166,18 @@ fn addCaseConfig( .name = "test", .root_module = b.createModule(.{ .root_source_file = source_zig, - .optimize = optimize, - .target = target.*, + .optimize = params.optimize, + .target = .{ .result = target.*, .query = target_query }, .error_tracing = error_tracing, .strip = false, }), - .use_llvm = switch (backend) { - .llvm => true, - .selfhosted => false, - }, + .use_llvm = params.use_llvm, + .use_lld = params.use_lld, }); exe.bundle_ubsan_rt = false; const run = b.addRunArtifact(exe); + run.skip_foreign_checks = true; run.removeEnvironmentVariable("CLICOLOR_FORCE"); run.setEnvironmentVariable("NO_COLOR", "1"); run.expectExitCode(1); @@ -116,16 +189,10 @@ fn addCaseConfig( }; const check_run = b.addRunArtifact(self.convert_exe); + check_run.skip_foreign_checks = true; check_run.setName(annotated_case_name); check_run.addFileArg(run.captureStdErr(.{})); check_run.expectStdOutEqual(expected_stderr); self.step.dependOn(&check_run.step); } - -const ErrorTrace = @This(); -const std = @import("std"); -const builtin = @import("builtin"); -const Step = std.Build.Step; -const OptimizeMode = std.builtin.OptimizeMode; -const mem = std.mem; diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig index 23938cbf1ade2733f8220bbb03612c183ad7e27a..c4e4a5fe391a4141fe167681183b761c8fbf806b 100644 --- a/test/src/StackTrace.zig +++ b/test/src/StackTrace.zig @@ -1,10 +1,91 @@ +const StackTrace = @This(); + +const builtin = @import("builtin"); + +const std = @import("std"); +const Step = std.Build.Step; +const OptimizeMode = std.lang.Optimize; +const mem = std.mem; + +const stack_traces_cases = @import("../stack_traces.zig"); + b: *std.Build, step: *Step, test_filters: []const []const u8, -targets: []const std.Build.ResolvedTarget, +skip_non_native: bool, convert_exe: *std.Build.Step.Compile, +pub const CaseParameters = struct { + target: std.Target.Query = .{}, + optimize: std.builtin.OptimizeMode = .debug, + link_libc: ?bool = null, + use_llvm: ?bool = null, + use_lld: ?bool = null, + pie: ?bool = null, + /// To enable this coverage, one of two things needs to happen: + /// * The compiler needs to gain the ability to strip only debug info (not symbols) + /// * `std.Build.Step.ObjCopy` needs to be un-regressed + strip: ?bool = false, +}; + +const param_sets = [_]CaseParameters{ + .{}, + .{ + .link_libc = true, + }, + .{ + .use_llvm = true, + .use_lld = true, + }, + .{ + .pie = true, + }, + .{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .windows, + .abi = .msvc, + }, + }, + .{ + .target = .{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .gnu, + }, + }, + .{ + .target = .{ + .cpu_arch = .x86, + .os_tag = .windows, + .abi = .msvc, + }, + }, + .{ + .target = .{ + .cpu_arch = .aarch64, + .os_tag = .macos, + }, + }, + .{ + .target = .{ + .cpu_arch = .s390x, + .os_tag = .linux, + .abi = .none, + }, + }, + .{ + .target = .{ + .cpu_arch = .loongarch32, + .os_tag = .linux, + .abi = .none, + }, + }, +}; + const Config = struct { + params: *const CaseParameters, + target: *const std.Target, name: []const u8, source: []const u8, /// Whether this test case expects to have unwind tables / frame pointers. @@ -26,42 +107,37 @@ const Config = struct { expect_strip: []const u8, }; +pub fn addCases(self: *StackTrace) void { + const b = self.b; + + for (¶m_sets) |*params| { + const resolved_target = b.resolveTargetQuery(params.target); + + if (self.skip_non_native and !resolved_target.query.isNative()) continue; + + // To avoid redundant testing, skip cross-compilation targets matching the host. + if (resolved_target.result.os.tag == builtin.target.os.tag and + resolved_target.result.cpu.arch == builtin.target.cpu.arch) + { + continue; + } + + stack_traces_cases.addCases(self, params, &resolved_target.result); + } +} + +/// Called from test/stack_traces.zig pub fn addCase(self: *StackTrace, config: Config) void { - for (self.targets) |*target| { - addCaseTarget( - self, - config, - target, - if (target.query.isNative()) null else t: { - break :t target.query.zigTriple(self.b.graph.arena) catch @panic("OOM"); - }, - ); - } -} -fn addCaseTarget( - self: *StackTrace, - config: Config, - target: *const std.Build.ResolvedTarget, - triple: ?[]const u8, -) void { - const both_backends = b: { - if (comptime builtin.cpu.arch.endian() == .big) break :b false; // https://github.com/ziglang/zig/issues/25961 - break :b switch (target.result.cpu.arch) { - .x86_64 => switch (target.result.ofmt) { - .elf => !target.result.os.tag.isBSD() and target.result.os.tag != .illumos, - else => false, - }, - else => false, - }; + const params = config.params; + const target = config.target; + const target_query = config.params.target; + + const triple: ?[]const u8 = if (target_query.isNative()) null else t: { + break :t target_query.zigTriple(self.b.graph.arena) catch @panic("OOM"); }; - const both_pie = switch (target.result.os.tag) { - .fuchsia => false, - else => true, - }; - const both_libc = !std.os.targetRequiresLibC(&target.result); // See `std.debug.StackIterator.fp_usability` logic. - const fp_usability: enum { useless, unsafe, safe, ideal } = switch (target.result.cpu.arch) { + const fp_usability: enum { useless, unsafe, safe, ideal } = switch (target.cpu.arch) { .alpha, .csky, .microblaze, @@ -83,20 +159,15 @@ fn addCaseTarget( .sparc, .sparc64, => .ideal, - .aarch64 => if (target.result.os.tag.isDarwin()) .safe else .unsafe, + .aarch64 => if (target.os.tag.isDarwin()) .safe else .unsafe, else => .unsafe, }; - const supports_unwind_tables = switch (target.result.os.tag) { + const supports_unwind_tables = switch (target.os.tag) { // x86-windows just has no way to do stack unwinding other then using frame pointers. - .windows => target.result.cpu.arch != .x86, + .windows => target.cpu.arch != .x86, else => true, }; - const use_llvm_vals: []const bool = if (both_backends) &.{ true, false } else &.{true}; - const pie_vals: []const ?bool = if (both_pie) &.{ true, false } else &.{null}; - const link_libc_vals: []const ?bool = if (both_libc) &.{ true, false } else &.{null}; - const strip_debug_vals: []const bool = &.{ true, false }; - const UnwindInfo = packed struct(u2) { tables: bool, fp: bool, @@ -126,43 +197,33 @@ fn addCaseTarget( }, }; - for (use_llvm_vals) |use_llvm| { - for (pie_vals) |pie| { - for (link_libc_vals) |link_libc| { - for (strip_debug_vals) |strip_debug| { - for (unwind_info_vals) |unwind_info| { - if (unwind_info.tables and !supports_unwind_tables) continue; - self.addCaseInstance( - target, - triple, - config.name, - config.source, - use_llvm, - pie, - link_libc, - strip_debug, - !unwind_info.tables and supports_unwind_tables, - !unwind_info.fp, - config.expect_panic, - if (strip_debug) config.expect_strip else config.expect, - ); - } - } - } - } + for (unwind_info_vals) |unwind_info| { + if (unwind_info.tables and !supports_unwind_tables) continue; + const strip = params.strip orelse switch (params.optimize) { + .debug, .fast, .safe => false, + .small => true, + }; + self.addCaseInstance( + .{ .result = target.*, .query = target_query }, + triple, + config.name, + config.source, + params, + !unwind_info.tables and supports_unwind_tables, + !unwind_info.fp, + config.expect_panic, + if (strip) config.expect_strip else config.expect, + ); } } fn addCaseInstance( self: *StackTrace, - target: *const std.Build.ResolvedTarget, + resolved_target: std.Build.ResolvedTarget, triple: ?[]const u8, name: []const u8, source: []const u8, - use_llvm: bool, - pie: ?bool, - link_libc: ?bool, - strip_debug: bool, + params: *const CaseParameters, strip_unwind: bool, omit_frame_pointer: bool, expect_panic: bool, @@ -170,13 +231,6 @@ fn addCaseInstance( ) void { const b = self.b; - if (strip_debug) { - // To enable this coverage, one of two things needs to happen: - // * The compiler needs to gain the ability to strip only debug info (not symbols) - // * `std.Build.Step.ObjCopy` needs to be un-regressed - return; - } - if (strip_unwind) { // To enable this coverage, `std.Build.Step.ObjCopy` needs to be un-regressed and gain the // ability to remove individual sections. `-fno-unwind-tables` is insufficient because it @@ -187,14 +241,28 @@ fn addCaseInstance( return; } + const backend_string = if (params.use_llvm == true) + " llvm" + else if (params.use_llvm == false) + " selfhosted" + else + ""; + + const strip_string = if (params.strip == true) + " strip" + else if (params.strip == false) + " unstripped" + else + ""; + const annotated_case_name = b.fmt("check {s} ({s}{s}{s}{s}{s}{s}{s}{s})", .{ name, triple orelse "", if (triple != null) " " else "", - if (use_llvm) "llvm" else "selfhosted", - if (pie == true) " pie" else "", - if (link_libc == true) " libc" else "", - if (strip_debug) " strip" else "", + backend_string, + if (params.pie == true) " pie" else "", + if (params.link_libc == true) " libc" else "", + strip_string, if (strip_unwind) " no_unwind" else "", if (omit_frame_pointer) " no_fp" else "", }); @@ -211,24 +279,26 @@ fn addCaseInstance( .root_module = b.createModule(.{ .root_source_file = source_zig, .optimize = .Debug, - .target = target.*, + .target = resolved_target, .omit_frame_pointer = omit_frame_pointer, - .link_libc = link_libc, + .link_libc = params.link_libc, .unwind_tables = if (strip_unwind) .none else null, // make panics single-threaded so that they don't include a thread ID .single_threaded = expect_panic, }), - .use_llvm = use_llvm, + .use_llvm = params.use_llvm, + .use_lld = params.use_lld, }); - exe.pie = pie; + exe.pie = params.pie; exe.bundle_ubsan_rt = false; const run = b.addRunArtifact(exe); + run.skip_foreign_checks = true; run.removeEnvironmentVariable("CLICOLOR_FORCE"); run.setEnvironmentVariable("NO_COLOR", "1"); run.addCheck(.{ .expect_term = term: { if (!expect_panic) break :term .{ .exited = 0 }; - if (target.result.os.tag == .windows) break :term .{ .exited = 3 }; + if (resolved_target.result.os.tag == .windows) break :term .{ .exited = 3 }; break :term .{ .signal = @fromBackingInt(@intCast(6)) }; } }); run.expectStdOutEqual(""); @@ -241,10 +311,3 @@ fn addCaseInstance( self.step.dependOn(&check_run.step); } - -const StackTrace = @This(); -const std = @import("std"); -const builtin = @import("builtin"); -const Step = std.Build.Step; -const OptimizeMode = std.builtin.OptimizeMode; -const mem = std.mem; diff --git a/test/stack_traces.zig b/test/stack_traces.zig index 82d7d67863c209f4a5e3825f0eeb2cb100e50ced..352950af2b307d0fb1d7a8027358c76fe064db56 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -1,7 +1,10 @@ const std = @import("std"); +const Context = @import("tests.zig").StackTracesContext; -pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target.Os.Tag) void { +pub fn addCases(cases: *Context, params: *const Context.CaseParameters, target: *const std.Target) void { cases.addCase(.{ + .params = params, + .target = target, .name = "simple panic", .source = \\pub fn main() void { @@ -33,6 +36,8 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "simple panic with no unwind strategy", .source = \\pub fn main() void { @@ -50,6 +55,8 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "dump current trace", .source = \\pub fn main() void { @@ -89,6 +96,8 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "dump current trace with no unwind strategy", .source = \\pub fn main() void { @@ -114,6 +123,8 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "dump captured trace", .source = \\pub fn main() void { @@ -155,6 +166,8 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "dump captured trace with no unwind strategy", .source = \\pub fn main() void { @@ -180,6 +193,8 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "dump captured trace on thread", .source = \\pub fn main() !void { @@ -225,6 +240,8 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. }); cases.addCase(.{ + .params = params, + .target = target, .name = "simple inline panic", // The main function has two inline calls to ensure // that inlinees in PDBs are properly deduplicated. @@ -240,7 +257,7 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. , .unwind = .any, .expect_panic = true, - .expect = switch (os) { + .expect = switch (target.os.tag) { // LLVM doesn't emit column info in the binary annotations for inlinee callees in PDBs, // so the first location has only a row. .windows => @@ -262,7 +279,7 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. \\ ^ , }, - .expect_strip = switch (os) { + .expect_strip = switch (target.os.tag) { .windows => \\panic: oh no \\???:?:?: [address] in source.foo @@ -279,6 +296,8 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. // Make sure all inline calls are resolved and in the right order! cases.addCase(.{ + .params = params, + .target = target, .name = "nested inline panic", .source = \\pub fn main() void { @@ -298,7 +317,7 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. .unwind = .any, .expect_panic = true, // This switch serves a similar purpose as in "inline panic". - .expect = switch (os) { + .expect = switch (target.os.tag) { .windows => \\panic: oh no \\source.zig:11: [address] in baz @@ -322,7 +341,7 @@ pub fn addCases(cases: *@import("tests.zig").StackTracesContext, os: std.Target. \\ ^ , }, - .expect_strip = switch (os) { + .expect_strip = switch (target.os.tag) { .windows => \\panic: oh no \\???:?:?: [address] in baz diff --git a/test/standalone/build.zig.zon b/test/standalone/build.zig.zon index 190394704ae48cd28406511353905a2f8675dd63..e134d00bd7d5977cda3341787a3dfefca86b4b7c 100644 --- a/test/standalone/build.zig.zon +++ b/test/standalone/build.zig.zon @@ -187,9 +187,6 @@ .posix = .{ .path = "posix", }, - .debug_io_color = .{ - .path = "debug_io_color", - }, .elf2 = .{ .path = "elf2", }, diff --git a/test/standalone/debug_io_color/build.zig b/test/standalone/debug_io_color/build.zig deleted file mode 100644 index 22ce7c8c22f9ab3299bf14e07e7c0b7a23f367c6..0000000000000000000000000000000000000000 --- a/test/standalone/debug_io_color/build.zig +++ /dev/null @@ -1,95 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test"); - b.default_step = test_step; - - // Most targets handle color the same way, regardless of whether libc is linked. - const native_target = b.graph.host; - addTestCases(test_step, native_target, false); - addTestCases(test_step, native_target, true); - - // WASI behaves differently depending on whether libc is linked. - if (b.enable_wasmtime) { - const wasi_target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .wasi }); - addTestCases(test_step, wasi_target, false); - addTestCases(test_step, wasi_target, true); - } -} - -fn addTestCases( - test_step: *std.Build.Step, - target: std.Build.ResolvedTarget, - link_libc: bool, -) void { - const b = test_step.owner; - const exe = b.addExecutable(.{ - .name = b.fmt("{s}{s}", .{ @tagName(target.result.os.tag), if (link_libc) "-libc" else "" }), - .root_module = b.createModule(.{ - .root_source_file = b.path("main.zig"), - .target = target, - .link_libc = link_libc, - }), - }); - - // Should reflect 'std.process.Environ.Block' and 'std.Io.Threaded.init_single_threaded'. - const debug_io_can_read_environ = switch (target.result.os.tag) { - .windows => true, - .wasi, .emscripten => link_libc, - .freestanding, .other => false, - else => true, - }; - - // Don't forget to account for whether the build process's stderr supports color. - const parent_stderr_color_enabled = (std.Io.Terminal.Mode.detect(b.graph.io, .stderr(), false, false) catch unreachable) != .no_color; - - _ = addTestCase(test_step, exe, "neither", .inherit, .manual, parent_stderr_color_enabled); - _ = addTestCase(test_step, exe, "neither", .redirect, .manual, false); - _ = addTestCase(test_step, exe, "no_color", .inherit, .disable, if (debug_io_can_read_environ) false else parent_stderr_color_enabled); - _ = addTestCase(test_step, exe, "no_color", .redirect, .disable, false); - _ = addTestCase(test_step, exe, "clicolor_force", .inherit, .enable, if (debug_io_can_read_environ) true else parent_stderr_color_enabled); - _ = addTestCase(test_step, exe, "clicolor_force", .redirect, .enable, debug_io_can_read_environ); - - const both = addTestCase(test_step, exe, "both", .inherit, .manual, if (debug_io_can_read_environ) false else parent_stderr_color_enabled); - both.setEnvironmentVariable("NO_COLOR", "1"); - both.setEnvironmentVariable("CLICOLOR_FORCE", "1"); - - const both_redirected = addTestCase(test_step, exe, "both", .redirect, .manual, false); - both_redirected.setEnvironmentVariable("NO_COLOR", "1"); - both_redirected.setEnvironmentVariable("CLICOLOR_FORCE", "1"); -} - -fn addTestCase( - test_step: *std.Build.Step, - exe: *std.Build.Step.Compile, - test_case_name: []const u8, - stderr: enum { inherit, redirect }, - run_step_color: std.Build.Step.Run.Color, - expected_color_enabled: bool, -) *std.Build.Step.Run { - const b = test_step.owner; - const step_name = b.fmt("{s} {s}{s}", .{ - exe.name, - test_case_name, - if (stderr == .redirect) "-redirect" else "", - }); - const run_exe = b.addRunArtifact(exe); - run_exe.setName(b.fmt("run {s}", .{step_name})); - - run_exe.failing_to_execute_foreign_is_an_error = false; - if (stderr == .redirect) run_exe.expectStdErrMatch(""); - - run_exe.clearEnvironment(); - run_exe.color = run_step_color; - - // Build system quirk: Currently, Run step stdout checks will also redirect stderr, so as a - // workaround we use a CheckFile step instead. We must also mark the Run step as having side - // effects, to ensure the parent stderr is inherited when not explicitly redirected. - run_exe.has_side_effects = true; - const stdout = run_exe.captureStdOut(.{}); - const check_file = b.addCheckFile(stdout, .{ .expected_exact = if (expected_color_enabled) "true" else "false" }); - check_file.setName(b.fmt("check {s}", .{step_name})); - test_step.dependOn(&check_file.step); - - return run_exe; -} diff --git a/test/standalone/debug_io_color/main.zig b/test/standalone/debug_io_color/main.zig deleted file mode 100644 index d9627f61792dbf38b9acf6b0e54f2ea54a138fe1..0000000000000000000000000000000000000000 --- a/test/standalone/debug_io_color/main.zig +++ /dev/null @@ -1,7 +0,0 @@ -const std = @import("std"); - -pub fn main() !void { - const stderr = std.debug.lockStderr(&.{}); - defer std.debug.unlockStderr(); - try std.Io.File.stdout().writeStreamingAll(std.Options.debug_io, if (stderr.terminal_mode != .no_color) "true" else "false"); -} diff --git a/test/tests.zig b/test/tests.zig index e2f62400d1d0efa4d40966263e64d4d80e0f2306..fbb16c5a2fe759dd4840d4670670983898985276 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -6,8 +6,6 @@ const OptimizeMode = std.builtin.OptimizeMode; const Step = std.Build.Step; // Cases -const error_traces = @import("error_traces.zig"); -const stack_traces = @import("stack_traces.zig"); const llvm_ir = @import("llvm_ir.zig"); const libc = @import("libc.zig"); const link = @import("link.zig"); @@ -2381,59 +2379,7 @@ pub fn isNative(actual_target: *const std.Build.ResolvedTarget, host: *const std return true; } -/// For stack trace tests, we only test native by default, because external executors are pretty -/// unreliable at stack tracing. However, if there's a 32-bit equivalent target which the host can -/// trivially run, we may as well at least test that! -fn nativeAndCompatible32bit(b: *std.Build, skip_non_native: bool) []const std.Build.ResolvedTarget { - const host = b.graph.host.result; - const only_native = (&b.graph.host)[0..1]; - if (skip_non_native) return only_native; - const arch32 = compatible32bitArch(&b.graph.host.result) orelse return only_native; - return b.graph.arena.dupe(std.Build.ResolvedTarget, &.{ - b.graph.host, - b.resolveTargetQuery(.{ .cpu_arch = arch32, .os_tag = host.os.tag }), - }) catch @panic("OOM"); -} - -fn wineAndCompatible32bit(b: *std.Build, skip_non_native: bool) []const std.Build.ResolvedTarget { - var targets: std.ArrayList(std.Build.ResolvedTarget) = .empty; - - const host = b.graph.host.result; - - targets.append(b.graph.arena, b.resolveTargetQuery(.{ - .cpu_arch = host.cpu.arch, - .os_tag = .windows, - })) catch @panic("OOM"); - if (!skip_non_native) { - if (compatible32bitArch(&b.graph.host.result)) |arch| { - targets.append(b.graph.arena, b.resolveTargetQuery(.{ - .cpu_arch = arch, - .os_tag = .windows, - })) catch @panic("OOM"); - } - } - - return targets.toOwnedSlice(b.graph.arena) catch @panic("OOM"); -} - -fn darlingTargets(b: *std.Build) []const std.Build.ResolvedTarget { - var targets: std.ArrayList(std.Build.ResolvedTarget) = .empty; - - const host = b.graph.host.result; - - targets.append(b.graph.arena, b.resolveTargetQuery(.{ - .cpu_arch = host.cpu.arch, - .os_tag = .macos, - })) catch @panic("OOM"); - - return targets.toOwnedSlice(b.graph.arena) catch @panic("OOM"); -} - -pub fn addStackTraceTests( - b: *std.Build, - test_filters: []const []const u8, - skip_non_native: bool, -) *Step { +pub fn addStackTraceTests(b: *std.Build, test_filters: []const []const u8, skip_non_native: bool) *Step { const step = b.step("test-stack-traces", "Run the stack trace tests"); const convert_exe = b.addExecutable(.{ @@ -2445,39 +2391,15 @@ pub fn addStackTraceTests( }), }); - const host_cases = b.allocator.create(StackTracesContext) catch @panic("OOM"); - host_cases.* = .{ + const stack_traces_context = b.allocator.create(StackTracesContext) catch @panic("OOM"); + stack_traces_context.* = .{ .b = b, .step = step, .test_filters = test_filters, - .targets = nativeAndCompatible32bit(b, skip_non_native), + .skip_non_native = skip_non_native, .convert_exe = convert_exe, }; - stack_traces.addCases(host_cases, b.graph.host.result.os.tag); - - if (b.enable_wine) { - const wine_cases = b.allocator.create(StackTracesContext) catch @panic("OOM"); - wine_cases.* = .{ - .b = b, - .step = step, - .test_filters = test_filters, - .targets = wineAndCompatible32bit(b, skip_non_native), - .convert_exe = convert_exe, - }; - stack_traces.addCases(wine_cases, .windows); - } - - if (b.enable_darling) { - const darling_cases = b.allocator.create(StackTracesContext) catch @panic("OOM"); - darling_cases.* = .{ - .b = b, - .step = step, - .test_filters = test_filters, - .targets = darlingTargets(b), - .convert_exe = convert_exe, - }; - stack_traces.addCases(darling_cases, .macos); - } + stack_traces_context.addCases(); return step; } @@ -2499,52 +2421,20 @@ pub fn addErrorTraceTests( }), }); - const host_cases = b.allocator.create(ErrorTracesContext) catch @panic("OOM"); - host_cases.* = .{ + const error_traces_context = b.allocator.create(ErrorTracesContext) catch @panic("OOM"); + error_traces_context.* = .{ .b = b, .step = step, .test_filters = test_filters, - .targets = nativeAndCompatible32bit(b, skip_non_native), + .skip_non_native = skip_non_native, .optimize_modes = optimize_modes, .convert_exe = convert_exe, }; - error_traces.addCases(host_cases, b.graph.host.result.os.tag); - - if (b.enable_wine) { - const wine_cases = b.allocator.create(ErrorTracesContext) catch @panic("OOM"); - wine_cases.* = .{ - .b = b, - .step = step, - .test_filters = test_filters, - .targets = wineAndCompatible32bit(b, skip_non_native), - .optimize_modes = optimize_modes, - .convert_exe = convert_exe, - }; - error_traces.addCases(wine_cases, .windows); - } - - if (b.enable_darling) { - const darling_cases = b.allocator.create(ErrorTracesContext) catch @panic("OOM"); - darling_cases.* = .{ - .b = b, - .step = step, - .test_filters = test_filters, - .targets = darlingTargets(b), - .optimize_modes = optimize_modes, - .convert_exe = convert_exe, - }; - error_traces.addCases(darling_cases, .macos); - } + error_traces_context.addCases(); return step; } -fn compilerHasPackageManager(b: *std.Build) bool { - // We can only use dependencies if the compiler was built with support for package management. - // (zig2 doesn't support it, but we still need to construct a build graph to build stage3.) - return b.available_deps.len != 0; -} - pub fn addStandaloneTests( b: *std.Build, optimize_modes: []const OptimizeMode, @@ -2553,21 +2443,19 @@ pub fn addStandaloneTests( enable_symlinks_windows: bool, ) *Step { const step = b.step("test-standalone", "Run the standalone tests"); - if (compilerHasPackageManager(b)) { - const test_cases_dep_name = "standalone_test_cases"; - const test_cases_dep = b.dependency(test_cases_dep_name, .{ - .enable_ios_sdk = enable_ios_sdk, - .enable_macos_sdk = enable_macos_sdk, - .enable_symlinks_windows = enable_symlinks_windows, - .simple_skip_debug = mem.findScalar(OptimizeMode, optimize_modes, .debug) == null, - .simple_skip_release_safe = mem.findScalar(OptimizeMode, optimize_modes, .safe) == null, - .simple_skip_release_fast = mem.findScalar(OptimizeMode, optimize_modes, .fast) == null, - .simple_skip_release_small = mem.findScalar(OptimizeMode, optimize_modes, .small) == null, - }); - const test_cases_dep_step = test_cases_dep.builder.default_step; - test_cases_dep_step.name = b.graph.dupeString(test_cases_dep_name); - step.dependOn(test_cases_dep.builder.default_step); - } + const test_cases_dep_name = "standalone_test_cases"; + const test_cases_dep = b.dependency(test_cases_dep_name, .{ + .enable_ios_sdk = enable_ios_sdk, + .enable_macos_sdk = enable_macos_sdk, + .enable_symlinks_windows = enable_symlinks_windows, + .simple_skip_debug = mem.findScalar(OptimizeMode, optimize_modes, .debug) == null, + .simple_skip_release_safe = mem.findScalar(OptimizeMode, optimize_modes, .safe) == null, + .simple_skip_release_fast = mem.findScalar(OptimizeMode, optimize_modes, .fast) == null, + .simple_skip_release_small = mem.findScalar(OptimizeMode, optimize_modes, .small) == null, + }); + const test_cases_dep_step = test_cases_dep.builder.default_step; + test_cases_dep_step.name = b.graph.dupeString(test_cases_dep_name); + step.dependOn(test_cases_dep.builder.default_step); return step; } @@ -3417,10 +3305,11 @@ pub fn addIncrementalTests(b: *std.Build, test_step: *Step, test_filters: []cons run.addArg("--quiet"); // don't fill stderr telling us about skipped tests etc - if (b.enable_qemu) run.addArg("-fqemu"); - if (b.enable_wine) run.addArg("-fwine"); - if (b.enable_wasmtime) run.addArg("-fwasmtime"); - if (b.enable_darling) run.addArg("-fdarling"); + run.addThirdPartyEnabledArgDarling(.{ .enabled = "-fdarling" }); + run.addThirdPartyEnabledArgQemu(.{ .enabled = "-fqemu" }); + run.addThirdPartyEnabledArgRosetta(.{ .enabled = "-frosetta" }); + run.addThirdPartyEnabledArgWasmtime(.{ .enabled = "-fwasmtime" }); + run.addThirdPartyEnabledArgWine(.{ .enabled = "-fwine" }); run.addCheck(.{ .expect_term = .{ .exited = 0 } }); test_step.dependOn(&run.step);