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:
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.