zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit ac0b1bfda2c8ac6093b86a000c3515c57535ef07 (tree)
parent aec708ce25409f7e5aa9f39568cdf6281e857742
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Thu, 30 Apr 2026 18:08:57 -0700

build system: implement options

options which are passed to configurer and therefore observable by the
build script are added to the cache hash. A sorted list is hashed since
they are unordered.

Diffstat:
MBRANCH_TODO | 7+++++--
Mlib/compiler/configurer.zig | 63++++++++++++++++++++++-----------------------------------------
Mlib/std/Build.zig | 131++++++++++++++++++++++++++-----------------------------------------------------
Mlib/std/Build/Configuration.zig | 28+++++++++++++++++++++++++---
Msrc/main.zig | 63+++++++++++++++++++++++++++++++++++++++++++++------------------
5 files changed, 139 insertions(+), 153 deletions(-)

diff --git a/BRANCH_TODO b/BRANCH_TODO @@ -1,9 +1,9 @@ -* implement the build options * finish migrating the rest of the build steps * inspect b4ffb402c082605c4b324e88120306fc8fb3cf32 diff and apply changes as needed (merge conflict) * make zig-pkg path root configurable in maker (make sure --system still works) * eliminate calls to getPath, getPath2, getPath3 * [build system compile step data races with getGraph function](https://codeberg.org/ziglang/zig/issues/31397) +* test lazyImport * solve the TODOs added in this branch * get zig tests passing * test a bunch of third party projects / help people migrate @@ -16,6 +16,8 @@ * make addExtra return Index using reflection * refactor with DefaultingEnum +* implement {q} or delete {q} uses + ## Followup Issues * reduce the size of Maker.Step.Extended (make Run smaller) probably by using an arena per make * link_eh_frame_hdr should be DefaultingBool @@ -24,6 +26,7 @@ - but artifact install steps also add paths for dyn libs on windows * no more "artifact arg" to run step. if you want to run the post-install binary, get the lazy path from the install step. +* -D options which are files need to be accounted for in the configure cache ## Release Notes @@ -45,4 +48,4 @@ run_cmd.addPassthruArgs(); This removes a capability from build scripts since they can no longer observe those arguments. In exchange, it means that when changing those arguments, -build scripts need not be rebuilt from source. +build scripts no longer must be rebuilt from source. diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig @@ -64,22 +64,7 @@ pub fn main(init: process.Init.Minimal) !void { const builder = try std.Build.create(&graph, dependencies.root_deps); - var error_style: ErrorStyle = .verbose; - var multiline_errors: MultilineErrors = .indent; var color: Color = .auto; - - if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| { - if (std.meta.stringToEnum(ErrorStyle, str)) |style| { - error_style = style; - } - } - - if (std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| { - if (std.meta.stringToEnum(MultilineErrors, str)) |style| { - multiline_errors = style; - } - } - var arg_i: usize = 1; // Skip own executable name. while (nextArg(args, &arg_i)) |arg| { @@ -101,39 +86,20 @@ pub fn main(init: process.Init.Minimal) !void { try graph.system_integration_options.put(arena, name, .user_disabled); } else if (mem.eql(u8, arg, "--release")) { graph.release_mode = .any; - } else if (mem.cutPrefix(u8, arg, "--release=")) |text| { - graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse { - fatalWithHint("expected [off|any|fast|safe|small] in {q}, found {q}", .{ - arg, text, - }); - }; - } else if (mem.eql(u8, arg, "--color")) { - const next_arg = nextArg(args, &arg_i) orelse - fatalWithHint("expected [auto|on|off] after {q}", .{arg}); - color = std.meta.stringToEnum(Color, next_arg) orelse { - fatalWithHint("expected [auto|on|off] after {q}, found {q}", .{ - arg, next_arg, - }); - }; - } else if (mem.eql(u8, arg, "--error-style")) { - const next_arg = nextArg(args, &arg_i) orelse - fatalWithHint("expected style after {q}", .{arg}); - error_style = std.meta.stringToEnum(ErrorStyle, next_arg) orelse { - fatalWithHint("expected style after {q}, found {q}", .{ arg, next_arg }); - }; - } else if (mem.eql(u8, arg, "--multiline-errors")) { - const next_arg = nextArg(args, &arg_i) orelse - fatalWithHint("expected style after {q}", .{arg}); - multiline_errors = std.meta.stringToEnum(MultilineErrors, next_arg) orelse { - fatalWithHint("expected style after {q}, found {q}", .{ arg, next_arg }); + } else if (mem.cutPrefix(u8, arg, "--release=")) |rest| { + graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, rest) orelse { + fatalWithHint("expected --release=[off|any|fast|safe|small]; found: {s}", .{arg}); }; + } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| { + color = std.meta.stringToEnum(Color, rest) orelse + fatalWithHint("expected --color=[auto|on|off]; found: {s}", .{arg}); } else if (mem.eql(u8, arg, "--system")) { // The usage text shows another argument after this parameter // but it is handled by the parent process. The build runner // only sees this flag. graph.system_package_mode = true; } else { - fatalWithHint("unrecognized argument: {q}", .{arg}); + fatalWithHint("unrecognized argument: {s}", .{arg}); } } @@ -152,6 +118,7 @@ pub fn main(init: process.Init.Minimal) !void { fatal(" access the help menu with 'zig build -h'", .{}); } + try serializePackageOptions(builder, &graph.wip_configuration); try serializeSystemIntegrationOptions(&graph, &graph.wip_configuration); var stdout_buffer: [1024]u8 = undefined; @@ -1125,3 +1092,17 @@ fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration process.exit(1); } } + +fn serializePackageOptions(b: *std.Build, wc: *Configuration.Wip) Allocator.Error!void { + const gpa = wc.gpa; + + try wc.available_options.ensureTotalCapacityPrecise(gpa, b.available_options_map.count()); + for (b.available_options_map.keys(), b.available_options_map.values()) |name, *opt| { + wc.available_options.appendAssumeCapacity(.{ + .name = try wc.addString(name), + .description = try wc.addString(opt.description), + .type = opt.type_id, + .enum_options = if (opt.enum_options) |enum_vals| .init(try wc.addStringList(enum_vals)) else .none, + }); + } +} diff --git a/lib/std/Build.zig b/lib/std/Build.zig @@ -30,8 +30,7 @@ install_tls: Step.TopLevel, uninstall_tls: Step.TopLevel, allocator: Allocator, user_input_options: UserInputOptionsMap, -available_options_map: AvailableOptionsMap, -available_options_list: std.array_list.Managed(AvailableOption), +available_options_map: std.array_hash_map.String(AvailableOption) = .empty, invalid_user_input: bool, default_step: *Step, top_level_steps: std.StringArrayHashMapUnmanaged(*Step.TopLevel), @@ -180,11 +179,10 @@ pub const RunError = error{ } || std.process.SpawnError; const UserInputOptionsMap = StringHashMap(UserInputOption); -const AvailableOptionsMap = StringHashMap(AvailableOption); const AvailableOption = struct { name: []const u8, - type_id: TypeId, + type_id: Configuration.AvailableOption.Type, description: []const u8, /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options enum_options: ?[]const []const u8, @@ -205,19 +203,6 @@ const UserValue = union(enum) { lazy_path_list: std.array_list.Managed(LazyPath), }; -const TypeId = enum { - bool, - int, - float, - @"enum", - enum_list, - string, - list, - build_id, - lazy_path, - lazy_path_list, -}; - pub fn create( graph: *Graph, available_deps: AvailableDeps, @@ -230,8 +215,6 @@ pub fn create( .invalid_user_input = false, .allocator = arena, .user_input_options = UserInputOptionsMap.init(arena), - .available_options_map = AvailableOptionsMap.init(arena), - .available_options_list = std.array_list.Managed(AvailableOption).init(arena), .top_level_steps = .{}, .default_step = undefined, .install_prefix = undefined, @@ -292,8 +275,6 @@ fn createChild( .description = "Remove build artifacts from prefix path", }, .user_input_options = user_input_options, - .available_options_map = AvailableOptionsMap.init(allocator), - .available_options_list = std.array_list.Managed(AvailableOption).init(allocator), .invalid_user_input = false, .default_step = undefined, .top_level_steps = .{}, @@ -960,13 +941,14 @@ pub fn getUninstallStep(b: *Build) *Step { /// these options when calling the dependency's build.zig script as a function. /// `null` is returned when an option is left to default. pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { + const arena = b.allocator; const name = b.dupe(name_raw); const description = b.dupe(description_raw); const type_id = comptime typeToEnum(T); const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: { const EnumType = if (type_id == .enum_list) @typeInfo(T).pointer.child else T; const fields = comptime std.meta.fields(EnumType); - var options = std.array_list.Managed([]const u8).initCapacity(b.allocator, fields.len) catch @panic("OOM"); + var options = std.array_list.Managed([]const u8).initCapacity(arena, fields.len) catch @panic("OOM"); inline for (fields) |field| { options.appendAssumeCapacity(field.name); @@ -980,10 +962,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw .description = description, .enum_options = enum_options, }; - if ((b.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) { - panic("Option '{s}' declared twice", .{name}); + if ((b.available_options_map.fetchPut(arena, name, available_option) catch @panic("OOM")) != null) { + panic("option '{s}' declared twice", .{name}); } - b.available_options_list.append(available_option) catch @panic("OOM"); const option_ptr = b.user_input_options.getPtr(name) orelse return null; option_ptr.used = true; @@ -996,36 +977,32 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw } else if (mem.eql(u8, s, "false")) { return false; } else { - log.err("Expected -D{s} to be a boolean, but received '{s}'", .{ name, s }); + log.err("expected -D{s} to be a boolean; received: {s}", .{ name, s }); b.markInvalidUserInput(); return null; } }, .list, .map, .lazy_path, .lazy_path_list => { - log.err("Expected -D{s} to be a boolean, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be a boolean; received: {t}", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, }, .int => switch (option_ptr.value) { .flag, .list, .map, .lazy_path, .lazy_path_list => { - log.err("Expected -D{s} to be an integer, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be an integer; received: {t}", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, .scalar => |s| { const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { error.Overflow => { - log.err("-D{s} value {s} cannot fit into type {s}.", .{ name, s, @typeName(T) }); + log.err("-D{s} value {s} cannot fit into type {s}", .{ name, s, @typeName(T) }); b.markInvalidUserInput(); return null; }, else => { - log.err("Expected -D{s} to be an integer of type {s}.", .{ name, @typeName(T) }); + log.err("expected -D{s} to be an integer of type {s}", .{ name, @typeName(T) }); b.markInvalidUserInput(); return null; }, @@ -1035,15 +1012,13 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw }, .float => switch (option_ptr.value) { .flag, .map, .list, .lazy_path, .lazy_path_list => { - log.err("Expected -D{s} to be a float, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be a float; received: {t}", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, .scalar => |s| { const n = std.fmt.parseFloat(T, s) catch { - log.err("Expected -D{s} to be a float of type {s}.", .{ name, @typeName(T) }); + log.err("expected -D{s} to be a float of type {s}", .{ name, @typeName(T) }); b.markInvalidUserInput(); return null; }; @@ -1052,9 +1027,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw }, .@"enum" => switch (option_ptr.value) { .flag, .map, .list, .lazy_path, .lazy_path_list => { - log.err("Expected -D{s} to be an enum, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, @@ -1062,7 +1035,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw if (std.meta.stringToEnum(T, s)) |enum_lit| { return enum_lit; } else { - log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(T) }); + log.err("expected -D{s} to be of type {s}", .{ name, @typeName(T) }); b.markInvalidUserInput(); return null; } @@ -1070,9 +1043,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw }, .string => switch (option_ptr.value) { .flag, .list, .map, .lazy_path, .lazy_path_list => { - log.err("Expected -D{s} to be a string, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be a string; received: {t}", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, @@ -1080,9 +1051,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw }, .build_id => switch (option_ptr.value) { .flag, .map, .list, .lazy_path, .lazy_path_list => { - log.err("Expected -D{s} to be an enum, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, @@ -1090,7 +1059,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw if (std.zig.BuildId.parse(s)) |build_id| { return build_id; } else |err| { - log.err("unable to parse option '-D{s}': {t}", .{ name, err }); + log.err("failed to parse option -D{s}: {t}", .{ name, err }); b.markInvalidUserInput(); return null; } @@ -1098,42 +1067,38 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw }, .list => switch (option_ptr.value) { .flag, .map, .lazy_path, .lazy_path_list => { - log.err("Expected -D{s} to be a list, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, .scalar => |s| { - return b.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM"); + return arena.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM"); }, .list => |lst| return lst.items, }, .enum_list => switch (option_ptr.value) { .flag, .map, .lazy_path, .lazy_path_list => { - log.err("Expected -D{s} to be a list, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, .scalar => |s| { const Child = @typeInfo(T).pointer.child; const value = std.meta.stringToEnum(Child, s) orelse { - log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(Child) }); + log.err("expected -D{s} to be of type {s}", .{ name, @typeName(Child) }); b.markInvalidUserInput(); return null; }; - return b.allocator.dupe(Child, &[_]Child{value}) catch @panic("OOM"); + return arena.dupe(Child, &[_]Child{value}) catch @panic("OOM"); }, .list => |lst| { const Child = @typeInfo(T).pointer.child; - const new_list = b.allocator.alloc(Child, lst.items.len) catch @panic("OOM"); + const new_list = arena.alloc(Child, lst.items.len) catch @panic("OOM"); for (new_list, lst.items) |*new_item, str| { new_item.* = std.meta.stringToEnum(Child, str) orelse { - log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(Child) }); + log.err("expected -D{s} to be of type {s}", .{ name, @typeName(Child) }); b.markInvalidUserInput(); - b.allocator.free(new_list); + arena.free(new_list); return null; }; } @@ -1144,18 +1109,16 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw .scalar => |s| return .{ .cwd_relative = s }, .lazy_path => |lp| return lp, .flag, .map, .list, .lazy_path_list => { - log.err("Expected -D{s} to be a path, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, }, .lazy_path_list => switch (option_ptr.value) { - .scalar => |s| return b.allocator.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"), - .lazy_path => |lp| return b.allocator.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"), + .scalar => |s| return arena.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"), + .lazy_path => |lp| return arena.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"), .list => |lst| { - const new_list = b.allocator.alloc(LazyPath, lst.items.len) catch @panic("OOM"); + const new_list = arena.alloc(LazyPath, lst.items.len) catch @panic("OOM"); for (new_list, lst.items) |*new_item, str| { new_item.* = .{ .cwd_relative = str }; } @@ -1163,9 +1126,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw }, .lazy_path_list => |lp_list| return lp_list.items, .flag, .map => { - log.err("Expected -D{s} to be a path, but received a {s}.", .{ - name, @tagName(option_ptr.value), - }); + log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value }); b.markInvalidUserInput(); return null; }, @@ -1250,8 +1211,8 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile opts_copy.diagnostics = &diags; return std.Target.Query.parse(opts_copy) catch |err| switch (err) { error.UnknownCpuModel => { - std.debug.print("unknown CPU: '{s}'\navailable CPUs for architecture '{s}':\n", .{ - diags.cpu_name.?, @tagName(diags.arch.?), + std.debug.print("unknown CPU: '{s}'\navailable CPUs for architecture '{t}':\n", .{ + diags.cpu_name.?, diags.arch.?, }); for (diags.arch.?.allCpuModels()) |cpu| { std.debug.print(" {s}\n", .{cpu.name}); @@ -1261,11 +1222,10 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile error.UnknownCpuFeature => { std.debug.print( \\unknown CPU feature: '{s}' - \\available CPU features for architecture '{s}': + \\available CPU features for architecture '{t}': \\ , .{ - diags.unknown_feature_name.?, - @tagName(diags.arch.?), + diags.unknown_feature_name.?, diags.arch.?, }); for (diags.arch.?.allFeaturesList()) |feature| { std.debug.print(" {s}: {s}\n", .{ feature.name, feature.description }); @@ -1398,7 +1358,9 @@ pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8 return true; }, .lazy_path, .lazy_path_list => { - log.warn("the lazy path value type isn't added from the CLI, but somehow '{s}' is a .{f}", .{ name, std.zig.fmtId(@tagName(gop.value_ptr.value)) }); + log.warn("the lazy path value type isn't added from the CLI, but somehow '{s}' is a .{f}", .{ + name, std.zig.fmtId(@tagName(gop.value_ptr.value)), + }); return true; }, } @@ -1437,7 +1399,7 @@ pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool return false; } -fn typeToEnum(comptime T: type) TypeId { +fn typeToEnum(comptime T: type) Configuration.AvailableOption.Type { return switch (T) { std.zig.BuildId => .build_id, LazyPath => .lazy_path, @@ -1588,17 +1550,6 @@ pub fn path(b: *Build, sub_path: []const u8) LazyPath { } }; } -/// This is low-level implementation details of the build system, not meant to -/// be called by users' build scripts. Even in the build system itself it is a -/// code smell to call this function. -pub fn pathFromRoot(b: *Build, sub_path: []const u8) []u8 { - return b.pathResolve(&.{ b.build_root.path orelse ".", sub_path }); -} - -fn pathFromCwd(b: *Build, sub_path: []const u8) []u8 { - return b.pathResolve(&.{ b.graph.cache.cwd, sub_path }); -} - pub fn pathJoin(b: *Build, paths: []const []const u8) []u8 { return fs.path.join(b.allocator, paths) catch @panic("OOM"); } @@ -1792,7 +1743,9 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps }; } else .{ "", deps.root_deps }; if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) { - std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{ asking_build_zig, b.pathFromRoot("build.zig") }); + std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{ + asking_build_zig, b.pathFromRoot("build.zig"), + }); } comptime for (b_pkg_deps) |dep| { if (std.mem.eql(u8, dep[0], dep_name)) return dep[1]; diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig @@ -189,9 +189,24 @@ pub const Wip = struct { } pub fn addStringList(wip: *Wip, list: []const []const u8) Allocator.Error!StringList { - _ = wip; - _ = list; - @panic("TODO"); + // Increase size of extra to support the list. Add the string list + // there. Then check for duplicate, reverting list if already found. + const gpa = wip.gpa; + const revert_index: u32 = @intCast(wip.extra.items.len); + const added = try wip.extra.addManyAsSlice(gpa, list.len + 1); + added[0] = @intCast(list.len); + for (added[1..], list) |*d, s| d.* = @intFromEnum(try addString(wip, s)); + const gop = try wip.dedupe_table.getOrPutContext(gpa, .{ + .index = revert_index, + .len = @intCast(added.len), + }, @as(ExtraSlice.Context, .{ .extra = wip.extra.items })); + + if (gop.found_existing) { + wip.extra.items.len = revert_index; + return @enumFromInt(gop.key_ptr.index); + } + + return @enumFromInt(revert_index); } pub fn addBytes(wip: *Wip, bytes: []const u8) Allocator.Error!Bytes { @@ -1456,6 +1471,13 @@ pub const OptionalStringList = enum(u32) { none = max_u32, _, + pub fn init(opt_string_list: ?StringList) OptionalStringList { + const sl = opt_string_list orelse return .none; + const result: OptionalStringList = @enumFromInt(@intFromEnum(sl)); + assert(result != .none); + return result; + } + pub fn unwrap(this: @This()) ?StringList { if (this == .none) return null; return @enumFromInt(@intFromEnum(this)); diff --git a/src/main.zig b/src/main.zig @@ -4951,6 +4951,7 @@ fn cmdBuild( .ReleaseSafe; var configure_argv: std.ArrayList([]const u8) = .empty; var make_argv: std.ArrayList([]const u8) = .empty; + var cached_unordered_passthru_configure: std.ArrayList(u32) = .empty; var forks: std.ArrayList(Fork) = .empty; var reference_trace: ?u32 = null; var debug_compile_errors = false; @@ -4975,6 +4976,7 @@ fn cmdBuild( try configure_argv.ensureUnusedCapacity(arena, 16); try make_argv.ensureUnusedCapacity(arena, 16); + try cached_unordered_passthru_configure.ensureUnusedCapacity(arena, 16); _ = configure_argv.addOneAssumeCapacity(); // configurer executable _ = make_argv.addOneAssumeCapacity(); // maker executable @@ -5007,7 +5009,33 @@ fn cmdBuild( while (i < args.len) : (i += 1) { const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "--build-file")) { + try configure_argv.ensureUnusedCapacity(arena, 1); + + if (mem.startsWith(u8, arg, "-D") or + mem.startsWith(u8, arg, "-fsys=") or + mem.startsWith(u8, arg, "-fno-sys=") or + mem.startsWith(u8, arg, "--release=") or + mem.eql(u8, arg, "--release")) + { + try cached_unordered_passthru_configure.append(arena, @intCast(configure_argv.items.len)); + configure_argv.appendAssumeCapacity(arg); + continue; + } else if (mem.eql(u8, arg, "--system")) { + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); + i += 1; + system_pkg_dir_path = args[i]; + + try cached_unordered_passthru_configure.append(arena, @intCast(configure_argv.items.len)); + configure_argv.appendAssumeCapacity(arg); // Intentionally "--system" only; not the path. + continue; + } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| { + color = std.meta.stringToEnum(Color, rest) orelse + fatal("expected --color=[auto|on|off]; found: {s}", .{arg}); + + try cached_unordered_passthru_configure.append(arena, @intCast(configure_argv.items.len)); + configure_argv.appendAssumeCapacity(arg); + continue; + } else if (mem.eql(u8, arg, "--build-file")) { if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; build_file = args[i]; @@ -5058,12 +5086,6 @@ fn cmdBuild( .failed = false, }); continue; - } else if (mem.eql(u8, arg, "--system")) { - if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); - i += 1; - system_pkg_dir_path = args[i]; - try configure_argv.append(arena, "--system"); - continue; } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| { reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| { fatal("unable to parse reference_trace count '{s}': {t}", .{ num, err }); @@ -5122,14 +5144,6 @@ fn cmdBuild( verbose_llvm_bc = rest; } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { verbose_llvm_cpu_features = true; - } else if (mem.eql(u8, arg, "--color")) { - if (i + 1 >= args.len) fatal("expected [auto|on|off] after {s}", .{arg}); - i += 1; - color = std.meta.stringToEnum(Color, args[i]) orelse { - fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] }); - }; - try configure_argv.appendSlice(arena, &.{ arg, args[i] }); - continue; } else if (mem.cutPrefix(u8, arg, "-j")) |str| { const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| fatal("unable to parse jobs count {s}: {t}", .{ str, err }); @@ -5143,9 +5157,6 @@ fn cmdBuild( make_argv.items[argv_index_seed] = args[i]; continue; } else if (mem.eql(u8, arg, "--")) { - // The rest of the args are supposed to get passed onto - // build runner's `build.args` - try configure_argv.append(arena, "--have-run-args"); try make_argv.appendSlice(arena, args[i..]); break; } @@ -5212,6 +5223,22 @@ fn cmdBuild( defer config_man.deinit(); config_man.hash.addBytes(build_options.version); + const SortContext = struct { + list: []const []const u8, + fn lessThan(this: @This(), lhs: u32, rhs: u32) bool { + return mem.lessThan(u8, this.list[lhs], this.list[rhs]); + } + }; + mem.sortUnstable( + u32, + cached_unordered_passthru_configure.items, + @as(SortContext, .{ .list = configure_argv.items }), + SortContext.lessThan, + ); + for (cached_unordered_passthru_configure.items) |i| { + config_man.hash.addBytes(configure_argv.items[i]); + } + // Normally the build runner is compiled for the host target but here is // some code to help when debugging edits to the build runner so that you // can make sure it compiles successfully on other targets.