authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-30 18:08:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-25 18:54:35-07:00
logac0b1bfda2c8ac6093b86a000c3515c57535ef07
tree72e79a0fd19c200a45d1ab3bab9b4314e2c1b11e
parentaec708ce25409f7e5aa9f39568cdf6281e857742

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.

5 files changed, 139 insertions(+), 153 deletions(-)

BRANCH_TODO+5-2
...@@ -1,9 +1,9 @@...@@ -1,9 +1,9 @@
1* implement the build options
2* finish migrating the rest of the build steps1* finish migrating the rest of the build steps
3* inspect b4ffb402c082605c4b324e88120306fc8fb3cf32 diff and apply changes as needed (merge conflict)2* inspect b4ffb402c082605c4b324e88120306fc8fb3cf32 diff and apply changes as needed (merge conflict)
4* make zig-pkg path root configurable in maker (make sure --system still works)3* make zig-pkg path root configurable in maker (make sure --system still works)
5* eliminate calls to getPath, getPath2, getPath34* eliminate calls to getPath, getPath2, getPath3
6* [build system compile step data races with getGraph function](https://codeberg.org/ziglang/zig/issues/31397)5* [build system compile step data races with getGraph function](https://codeberg.org/ziglang/zig/issues/31397)
6* test lazyImport
7* solve the TODOs added in this branch7* solve the TODOs added in this branch
8* get zig tests passing8* get zig tests passing
9* test a bunch of third party projects / help people migrate9* test a bunch of third party projects / help people migrate
...@@ -16,6 +16,8 @@...@@ -16,6 +16,8 @@
16* make addExtra return Index using reflection16* make addExtra return Index using reflection
17* refactor with DefaultingEnum17* refactor with DefaultingEnum
1818
19* implement {q} or delete {q} uses
20
19## Followup Issues21## Followup Issues
20* reduce the size of Maker.Step.Extended (make Run smaller) probably by using an arena per make22* reduce the size of Maker.Step.Extended (make Run smaller) probably by using an arena per make
21* link_eh_frame_hdr should be DefaultingBool23* link_eh_frame_hdr should be DefaultingBool
...@@ -24,6 +26,7 @@...@@ -24,6 +26,7 @@
24 - but artifact install steps also add paths for dyn libs on windows26 - but artifact install steps also add paths for dyn libs on windows
25* no more "artifact arg" to run step. if you want to run the post-install binary, get the lazy path27* no more "artifact arg" to run step. if you want to run the post-install binary, get the lazy path
26 from the install step.28 from the install step.
29* -D options which are files need to be accounted for in the configure cache
2730
2831
29## Release Notes32## Release Notes
...@@ -45,4 +48,4 @@ run_cmd.addPassthruArgs();...@@ -45,4 +48,4 @@ run_cmd.addPassthruArgs();
4548
46This removes a capability from build scripts since they can no longer observe49This removes a capability from build scripts since they can no longer observe
47those arguments. In exchange, it means that when changing those arguments,50those arguments. In exchange, it means that when changing those arguments,
48build scripts need not be rebuilt from source.51build scripts no longer must be rebuilt from source.
lib/compiler/configurer.zig+22-41
...@@ -64,22 +64,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -64,22 +64,7 @@ pub fn main(init: process.Init.Minimal) !void {
6464
65 const builder = try std.Build.create(&graph, dependencies.root_deps);65 const builder = try std.Build.create(&graph, dependencies.root_deps);
6666
67 var error_style: ErrorStyle = .verbose;
68 var multiline_errors: MultilineErrors = .indent;
69 var color: Color = .auto;67 var color: Color = .auto;
70
71 if (std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(&graph.environ_map)) |str| {
72 if (std.meta.stringToEnum(ErrorStyle, str)) |style| {
73 error_style = style;
74 }
75 }
76
77 if (std.zig.EnvVar.ZIG_BUILD_MULTILINE_ERRORS.get(&graph.environ_map)) |str| {
78 if (std.meta.stringToEnum(MultilineErrors, str)) |style| {
79 multiline_errors = style;
80 }
81 }
82
83 var arg_i: usize = 1; // Skip own executable name.68 var arg_i: usize = 1; // Skip own executable name.
8469
85 while (nextArg(args, &arg_i)) |arg| {70 while (nextArg(args, &arg_i)) |arg| {
...@@ -101,39 +86,20 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -101,39 +86,20 @@ pub fn main(init: process.Init.Minimal) !void {
101 try graph.system_integration_options.put(arena, name, .user_disabled);86 try graph.system_integration_options.put(arena, name, .user_disabled);
102 } else if (mem.eql(u8, arg, "--release")) {87 } else if (mem.eql(u8, arg, "--release")) {
103 graph.release_mode = .any;88 graph.release_mode = .any;
104 } else if (mem.cutPrefix(u8, arg, "--release=")) |text| {89 } else if (mem.cutPrefix(u8, arg, "--release=")) |rest| {
105 graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse {90 graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, rest) orelse {
106 fatalWithHint("expected [off|any|fast|safe|small] in {q}, found {q}", .{91 fatalWithHint("expected --release=[off|any|fast|safe|small]; found: {s}", .{arg});
107 arg, text,
108 });
109 };
110 } else if (mem.eql(u8, arg, "--color")) {
111 const next_arg = nextArg(args, &arg_i) orelse
112 fatalWithHint("expected [auto|on|off] after {q}", .{arg});
113 color = std.meta.stringToEnum(Color, next_arg) orelse {
114 fatalWithHint("expected [auto|on|off] after {q}, found {q}", .{
115 arg, next_arg,
116 });
117 };
118 } else if (mem.eql(u8, arg, "--error-style")) {
119 const next_arg = nextArg(args, &arg_i) orelse
120 fatalWithHint("expected style after {q}", .{arg});
121 error_style = std.meta.stringToEnum(ErrorStyle, next_arg) orelse {
122 fatalWithHint("expected style after {q}, found {q}", .{ arg, next_arg });
123 };
124 } else if (mem.eql(u8, arg, "--multiline-errors")) {
125 const next_arg = nextArg(args, &arg_i) orelse
126 fatalWithHint("expected style after {q}", .{arg});
127 multiline_errors = std.meta.stringToEnum(MultilineErrors, next_arg) orelse {
128 fatalWithHint("expected style after {q}, found {q}", .{ arg, next_arg });
129 };92 };
93 } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| {
94 color = std.meta.stringToEnum(Color, rest) orelse
95 fatalWithHint("expected --color=[auto|on|off]; found: {s}", .{arg});
130 } else if (mem.eql(u8, arg, "--system")) {96 } else if (mem.eql(u8, arg, "--system")) {
131 // The usage text shows another argument after this parameter97 // The usage text shows another argument after this parameter
132 // but it is handled by the parent process. The build runner98 // but it is handled by the parent process. The build runner
133 // only sees this flag.99 // only sees this flag.
134 graph.system_package_mode = true;100 graph.system_package_mode = true;
135 } else {101 } else {
136 fatalWithHint("unrecognized argument: {q}", .{arg});102 fatalWithHint("unrecognized argument: {s}", .{arg});
137 }103 }
138 }104 }
139105
...@@ -152,6 +118,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -152,6 +118,7 @@ pub fn main(init: process.Init.Minimal) !void {
152 fatal(" access the help menu with 'zig build -h'", .{});118 fatal(" access the help menu with 'zig build -h'", .{});
153 }119 }
154120
121 try serializePackageOptions(builder, &graph.wip_configuration);
155 try serializeSystemIntegrationOptions(&graph, &graph.wip_configuration);122 try serializeSystemIntegrationOptions(&graph, &graph.wip_configuration);
156123
157 var stdout_buffer: [1024]u8 = undefined;124 var stdout_buffer: [1024]u8 = undefined;
...@@ -1125,3 +1092,17 @@ fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration...@@ -1125,3 +1092,17 @@ fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration
1125 process.exit(1);1092 process.exit(1);
1126 }1093 }
1127}1094}
1095
1096fn serializePackageOptions(b: *std.Build, wc: *Configuration.Wip) Allocator.Error!void {
1097 const gpa = wc.gpa;
1098
1099 try wc.available_options.ensureTotalCapacityPrecise(gpa, b.available_options_map.count());
1100 for (b.available_options_map.keys(), b.available_options_map.values()) |name, *opt| {
1101 wc.available_options.appendAssumeCapacity(.{
1102 .name = try wc.addString(name),
1103 .description = try wc.addString(opt.description),
1104 .type = opt.type_id,
1105 .enum_options = if (opt.enum_options) |enum_vals| .init(try wc.addStringList(enum_vals)) else .none,
1106 });
1107 }
1108}
lib/std/Build.zig+42-89
...@@ -30,8 +30,7 @@ install_tls: Step.TopLevel,...@@ -30,8 +30,7 @@ install_tls: Step.TopLevel,
30uninstall_tls: Step.TopLevel,30uninstall_tls: Step.TopLevel,
31allocator: Allocator,31allocator: Allocator,
32user_input_options: UserInputOptionsMap,32user_input_options: UserInputOptionsMap,
33available_options_map: AvailableOptionsMap,33available_options_map: std.array_hash_map.String(AvailableOption) = .empty,
34available_options_list: std.array_list.Managed(AvailableOption),
35invalid_user_input: bool,34invalid_user_input: bool,
36default_step: *Step,35default_step: *Step,
37top_level_steps: std.StringArrayHashMapUnmanaged(*Step.TopLevel),36top_level_steps: std.StringArrayHashMapUnmanaged(*Step.TopLevel),
...@@ -180,11 +179,10 @@ pub const RunError = error{...@@ -180,11 +179,10 @@ pub const RunError = error{
180} || std.process.SpawnError;179} || std.process.SpawnError;
181180
182const UserInputOptionsMap = StringHashMap(UserInputOption);181const UserInputOptionsMap = StringHashMap(UserInputOption);
183const AvailableOptionsMap = StringHashMap(AvailableOption);
184182
185const AvailableOption = struct {183const AvailableOption = struct {
186 name: []const u8,184 name: []const u8,
187 type_id: TypeId,185 type_id: Configuration.AvailableOption.Type,
188 description: []const u8,186 description: []const u8,
189 /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options187 /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options
190 enum_options: ?[]const []const u8,188 enum_options: ?[]const []const u8,
...@@ -205,19 +203,6 @@ const UserValue = union(enum) {...@@ -205,19 +203,6 @@ const UserValue = union(enum) {
205 lazy_path_list: std.array_list.Managed(LazyPath),203 lazy_path_list: std.array_list.Managed(LazyPath),
206};204};
207205
208const TypeId = enum {
209 bool,
210 int,
211 float,
212 @"enum",
213 enum_list,
214 string,
215 list,
216 build_id,
217 lazy_path,
218 lazy_path_list,
219};
220
221pub fn create(206pub fn create(
222 graph: *Graph,207 graph: *Graph,
223 available_deps: AvailableDeps,208 available_deps: AvailableDeps,
...@@ -230,8 +215,6 @@ pub fn create(...@@ -230,8 +215,6 @@ pub fn create(
230 .invalid_user_input = false,215 .invalid_user_input = false,
231 .allocator = arena,216 .allocator = arena,
232 .user_input_options = UserInputOptionsMap.init(arena),217 .user_input_options = UserInputOptionsMap.init(arena),
233 .available_options_map = AvailableOptionsMap.init(arena),
234 .available_options_list = std.array_list.Managed(AvailableOption).init(arena),
235 .top_level_steps = .{},218 .top_level_steps = .{},
236 .default_step = undefined,219 .default_step = undefined,
237 .install_prefix = undefined,220 .install_prefix = undefined,
...@@ -292,8 +275,6 @@ fn createChild(...@@ -292,8 +275,6 @@ fn createChild(
292 .description = "Remove build artifacts from prefix path",275 .description = "Remove build artifacts from prefix path",
293 },276 },
294 .user_input_options = user_input_options,277 .user_input_options = user_input_options,
295 .available_options_map = AvailableOptionsMap.init(allocator),
296 .available_options_list = std.array_list.Managed(AvailableOption).init(allocator),
297 .invalid_user_input = false,278 .invalid_user_input = false,
298 .default_step = undefined,279 .default_step = undefined,
299 .top_level_steps = .{},280 .top_level_steps = .{},
...@@ -960,13 +941,14 @@ pub fn getUninstallStep(b: *Build) *Step {...@@ -960,13 +941,14 @@ pub fn getUninstallStep(b: *Build) *Step {
960/// these options when calling the dependency's build.zig script as a function.941/// these options when calling the dependency's build.zig script as a function.
961/// `null` is returned when an option is left to default.942/// `null` is returned when an option is left to default.
962pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {943pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
944 const arena = b.allocator;
963 const name = b.dupe(name_raw);945 const name = b.dupe(name_raw);
964 const description = b.dupe(description_raw);946 const description = b.dupe(description_raw);
965 const type_id = comptime typeToEnum(T);947 const type_id = comptime typeToEnum(T);
966 const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: {948 const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: {
967 const EnumType = if (type_id == .enum_list) @typeInfo(T).pointer.child else T;949 const EnumType = if (type_id == .enum_list) @typeInfo(T).pointer.child else T;
968 const fields = comptime std.meta.fields(EnumType);950 const fields = comptime std.meta.fields(EnumType);
969 var options = std.array_list.Managed([]const u8).initCapacity(b.allocator, fields.len) catch @panic("OOM");951 var options = std.array_list.Managed([]const u8).initCapacity(arena, fields.len) catch @panic("OOM");
970952
971 inline for (fields) |field| {953 inline for (fields) |field| {
972 options.appendAssumeCapacity(field.name);954 options.appendAssumeCapacity(field.name);
...@@ -980,10 +962,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -980,10 +962,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
980 .description = description,962 .description = description,
981 .enum_options = enum_options,963 .enum_options = enum_options,
982 };964 };
983 if ((b.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) {965 if ((b.available_options_map.fetchPut(arena, name, available_option) catch @panic("OOM")) != null) {
984 panic("Option '{s}' declared twice", .{name});966 panic("option '{s}' declared twice", .{name});
985 }967 }
986 b.available_options_list.append(available_option) catch @panic("OOM");
987968
988 const option_ptr = b.user_input_options.getPtr(name) orelse return null;969 const option_ptr = b.user_input_options.getPtr(name) orelse return null;
989 option_ptr.used = true;970 option_ptr.used = true;
...@@ -996,36 +977,32 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -996,36 +977,32 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
996 } else if (mem.eql(u8, s, "false")) {977 } else if (mem.eql(u8, s, "false")) {
997 return false;978 return false;
998 } else {979 } else {
999 log.err("Expected -D{s} to be a boolean, but received '{s}'", .{ name, s });980 log.err("expected -D{s} to be a boolean; received: {s}", .{ name, s });
1000 b.markInvalidUserInput();981 b.markInvalidUserInput();
1001 return null;982 return null;
1002 }983 }
1003 },984 },
1004 .list, .map, .lazy_path, .lazy_path_list => {985 .list, .map, .lazy_path, .lazy_path_list => {
1005 log.err("Expected -D{s} to be a boolean, but received a {s}.", .{986 log.err("expected -D{s} to be a boolean; received: {t}", .{ name, option_ptr.value });
1006 name, @tagName(option_ptr.value),
1007 });
1008 b.markInvalidUserInput();987 b.markInvalidUserInput();
1009 return null;988 return null;
1010 },989 },
1011 },990 },
1012 .int => switch (option_ptr.value) {991 .int => switch (option_ptr.value) {
1013 .flag, .list, .map, .lazy_path, .lazy_path_list => {992 .flag, .list, .map, .lazy_path, .lazy_path_list => {
1014 log.err("Expected -D{s} to be an integer, but received a {s}.", .{993 log.err("expected -D{s} to be an integer; received: {t}", .{ name, option_ptr.value });
1015 name, @tagName(option_ptr.value),
1016 });
1017 b.markInvalidUserInput();994 b.markInvalidUserInput();
1018 return null;995 return null;
1019 },996 },
1020 .scalar => |s| {997 .scalar => |s| {
1021 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {998 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
1022 error.Overflow => {999 error.Overflow => {
1023 log.err("-D{s} value {s} cannot fit into type {s}.", .{ name, s, @typeName(T) });1000 log.err("-D{s} value {s} cannot fit into type {s}", .{ name, s, @typeName(T) });
1024 b.markInvalidUserInput();1001 b.markInvalidUserInput();
1025 return null;1002 return null;
1026 },1003 },
1027 else => {1004 else => {
1028 log.err("Expected -D{s} to be an integer of type {s}.", .{ name, @typeName(T) });1005 log.err("expected -D{s} to be an integer of type {s}", .{ name, @typeName(T) });
1029 b.markInvalidUserInput();1006 b.markInvalidUserInput();
1030 return null;1007 return null;
1031 },1008 },
...@@ -1035,15 +1012,13 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1035,15 +1012,13 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1035 },1012 },
1036 .float => switch (option_ptr.value) {1013 .float => switch (option_ptr.value) {
1037 .flag, .map, .list, .lazy_path, .lazy_path_list => {1014 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1038 log.err("Expected -D{s} to be a float, but received a {s}.", .{1015 log.err("expected -D{s} to be a float; received: {t}", .{ name, option_ptr.value });
1039 name, @tagName(option_ptr.value),
1040 });
1041 b.markInvalidUserInput();1016 b.markInvalidUserInput();
1042 return null;1017 return null;
1043 },1018 },
1044 .scalar => |s| {1019 .scalar => |s| {
1045 const n = std.fmt.parseFloat(T, s) catch {1020 const n = std.fmt.parseFloat(T, s) catch {
1046 log.err("Expected -D{s} to be a float of type {s}.", .{ name, @typeName(T) });1021 log.err("expected -D{s} to be a float of type {s}", .{ name, @typeName(T) });
1047 b.markInvalidUserInput();1022 b.markInvalidUserInput();
1048 return null;1023 return null;
1049 };1024 };
...@@ -1052,9 +1027,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1052,9 +1027,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1052 },1027 },
1053 .@"enum" => switch (option_ptr.value) {1028 .@"enum" => switch (option_ptr.value) {
1054 .flag, .map, .list, .lazy_path, .lazy_path_list => {1029 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1055 log.err("Expected -D{s} to be an enum, but received a {s}.", .{1030 log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value });
1056 name, @tagName(option_ptr.value),
1057 });
1058 b.markInvalidUserInput();1031 b.markInvalidUserInput();
1059 return null;1032 return null;
1060 },1033 },
...@@ -1062,7 +1035,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1062,7 +1035,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1062 if (std.meta.stringToEnum(T, s)) |enum_lit| {1035 if (std.meta.stringToEnum(T, s)) |enum_lit| {
1063 return enum_lit;1036 return enum_lit;
1064 } else {1037 } else {
1065 log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(T) });1038 log.err("expected -D{s} to be of type {s}", .{ name, @typeName(T) });
1066 b.markInvalidUserInput();1039 b.markInvalidUserInput();
1067 return null;1040 return null;
1068 }1041 }
...@@ -1070,9 +1043,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1070,9 +1043,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1070 },1043 },
1071 .string => switch (option_ptr.value) {1044 .string => switch (option_ptr.value) {
1072 .flag, .list, .map, .lazy_path, .lazy_path_list => {1045 .flag, .list, .map, .lazy_path, .lazy_path_list => {
1073 log.err("Expected -D{s} to be a string, but received a {s}.", .{1046 log.err("expected -D{s} to be a string; received: {t}", .{ name, option_ptr.value });
1074 name, @tagName(option_ptr.value),
1075 });
1076 b.markInvalidUserInput();1047 b.markInvalidUserInput();
1077 return null;1048 return null;
1078 },1049 },
...@@ -1080,9 +1051,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1080,9 +1051,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1080 },1051 },
1081 .build_id => switch (option_ptr.value) {1052 .build_id => switch (option_ptr.value) {
1082 .flag, .map, .list, .lazy_path, .lazy_path_list => {1053 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1083 log.err("Expected -D{s} to be an enum, but received a {s}.", .{1054 log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value });
1084 name, @tagName(option_ptr.value),
1085 });
1086 b.markInvalidUserInput();1055 b.markInvalidUserInput();
1087 return null;1056 return null;
1088 },1057 },
...@@ -1090,7 +1059,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1090,7 +1059,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1090 if (std.zig.BuildId.parse(s)) |build_id| {1059 if (std.zig.BuildId.parse(s)) |build_id| {
1091 return build_id;1060 return build_id;
1092 } else |err| {1061 } else |err| {
1093 log.err("unable to parse option '-D{s}': {t}", .{ name, err });1062 log.err("failed to parse option -D{s}: {t}", .{ name, err });
1094 b.markInvalidUserInput();1063 b.markInvalidUserInput();
1095 return null;1064 return null;
1096 }1065 }
...@@ -1098,42 +1067,38 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1098,42 +1067,38 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1098 },1067 },
1099 .list => switch (option_ptr.value) {1068 .list => switch (option_ptr.value) {
1100 .flag, .map, .lazy_path, .lazy_path_list => {1069 .flag, .map, .lazy_path, .lazy_path_list => {
1101 log.err("Expected -D{s} to be a list, but received a {s}.", .{1070 log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value });
1102 name, @tagName(option_ptr.value),
1103 });
1104 b.markInvalidUserInput();1071 b.markInvalidUserInput();
1105 return null;1072 return null;
1106 },1073 },
1107 .scalar => |s| {1074 .scalar => |s| {
1108 return b.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM");1075 return arena.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM");
1109 },1076 },
1110 .list => |lst| return lst.items,1077 .list => |lst| return lst.items,
1111 },1078 },
1112 .enum_list => switch (option_ptr.value) {1079 .enum_list => switch (option_ptr.value) {
1113 .flag, .map, .lazy_path, .lazy_path_list => {1080 .flag, .map, .lazy_path, .lazy_path_list => {
1114 log.err("Expected -D{s} to be a list, but received a {s}.", .{1081 log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value });
1115 name, @tagName(option_ptr.value),
1116 });
1117 b.markInvalidUserInput();1082 b.markInvalidUserInput();
1118 return null;1083 return null;
1119 },1084 },
1120 .scalar => |s| {1085 .scalar => |s| {
1121 const Child = @typeInfo(T).pointer.child;1086 const Child = @typeInfo(T).pointer.child;
1122 const value = std.meta.stringToEnum(Child, s) orelse {1087 const value = std.meta.stringToEnum(Child, s) orelse {
1123 log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(Child) });1088 log.err("expected -D{s} to be of type {s}", .{ name, @typeName(Child) });
1124 b.markInvalidUserInput();1089 b.markInvalidUserInput();
1125 return null;1090 return null;
1126 };1091 };
1127 return b.allocator.dupe(Child, &[_]Child{value}) catch @panic("OOM");1092 return arena.dupe(Child, &[_]Child{value}) catch @panic("OOM");
1128 },1093 },
1129 .list => |lst| {1094 .list => |lst| {
1130 const Child = @typeInfo(T).pointer.child;1095 const Child = @typeInfo(T).pointer.child;
1131 const new_list = b.allocator.alloc(Child, lst.items.len) catch @panic("OOM");1096 const new_list = arena.alloc(Child, lst.items.len) catch @panic("OOM");
1132 for (new_list, lst.items) |*new_item, str| {1097 for (new_list, lst.items) |*new_item, str| {
1133 new_item.* = std.meta.stringToEnum(Child, str) orelse {1098 new_item.* = std.meta.stringToEnum(Child, str) orelse {
1134 log.err("Expected -D{s} to be of type {s}.", .{ name, @typeName(Child) });1099 log.err("expected -D{s} to be of type {s}", .{ name, @typeName(Child) });
1135 b.markInvalidUserInput();1100 b.markInvalidUserInput();
1136 b.allocator.free(new_list);1101 arena.free(new_list);
1137 return null;1102 return null;
1138 };1103 };
1139 }1104 }
...@@ -1144,18 +1109,16 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1144,18 +1109,16 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1144 .scalar => |s| return .{ .cwd_relative = s },1109 .scalar => |s| return .{ .cwd_relative = s },
1145 .lazy_path => |lp| return lp,1110 .lazy_path => |lp| return lp,
1146 .flag, .map, .list, .lazy_path_list => {1111 .flag, .map, .list, .lazy_path_list => {
1147 log.err("Expected -D{s} to be a path, but received a {s}.", .{1112 log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value });
1148 name, @tagName(option_ptr.value),
1149 });
1150 b.markInvalidUserInput();1113 b.markInvalidUserInput();
1151 return null;1114 return null;
1152 },1115 },
1153 },1116 },
1154 .lazy_path_list => switch (option_ptr.value) {1117 .lazy_path_list => switch (option_ptr.value) {
1155 .scalar => |s| return b.allocator.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"),1118 .scalar => |s| return arena.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"),
1156 .lazy_path => |lp| return b.allocator.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"),1119 .lazy_path => |lp| return arena.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"),
1157 .list => |lst| {1120 .list => |lst| {
1158 const new_list = b.allocator.alloc(LazyPath, lst.items.len) catch @panic("OOM");1121 const new_list = arena.alloc(LazyPath, lst.items.len) catch @panic("OOM");
1159 for (new_list, lst.items) |*new_item, str| {1122 for (new_list, lst.items) |*new_item, str| {
1160 new_item.* = .{ .cwd_relative = str };1123 new_item.* = .{ .cwd_relative = str };
1161 }1124 }
...@@ -1163,9 +1126,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1163,9 +1126,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1163 },1126 },
1164 .lazy_path_list => |lp_list| return lp_list.items,1127 .lazy_path_list => |lp_list| return lp_list.items,
1165 .flag, .map => {1128 .flag, .map => {
1166 log.err("Expected -D{s} to be a path, but received a {s}.", .{1129 log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value });
1167 name, @tagName(option_ptr.value),
1168 });
1169 b.markInvalidUserInput();1130 b.markInvalidUserInput();
1170 return null;1131 return null;
1171 },1132 },
...@@ -1250,8 +1211,8 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile...@@ -1250,8 +1211,8 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile
1250 opts_copy.diagnostics = &diags;1211 opts_copy.diagnostics = &diags;
1251 return std.Target.Query.parse(opts_copy) catch |err| switch (err) {1212 return std.Target.Query.parse(opts_copy) catch |err| switch (err) {
1252 error.UnknownCpuModel => {1213 error.UnknownCpuModel => {
1253 std.debug.print("unknown CPU: '{s}'\navailable CPUs for architecture '{s}':\n", .{1214 std.debug.print("unknown CPU: '{s}'\navailable CPUs for architecture '{t}':\n", .{
1254 diags.cpu_name.?, @tagName(diags.arch.?),1215 diags.cpu_name.?, diags.arch.?,
1255 });1216 });
1256 for (diags.arch.?.allCpuModels()) |cpu| {1217 for (diags.arch.?.allCpuModels()) |cpu| {
1257 std.debug.print(" {s}\n", .{cpu.name});1218 std.debug.print(" {s}\n", .{cpu.name});
...@@ -1261,11 +1222,10 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile...@@ -1261,11 +1222,10 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile
1261 error.UnknownCpuFeature => {1222 error.UnknownCpuFeature => {
1262 std.debug.print(1223 std.debug.print(
1263 \\unknown CPU feature: '{s}'1224 \\unknown CPU feature: '{s}'
1264 \\available CPU features for architecture '{s}':1225 \\available CPU features for architecture '{t}':
1265 \\1226 \\
1266 , .{1227 , .{
1267 diags.unknown_feature_name.?,1228 diags.unknown_feature_name.?, diags.arch.?,
1268 @tagName(diags.arch.?),
1269 });1229 });
1270 for (diags.arch.?.allFeaturesList()) |feature| {1230 for (diags.arch.?.allFeaturesList()) |feature| {
1271 std.debug.print(" {s}: {s}\n", .{ feature.name, feature.description });1231 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...@@ -1398,7 +1358,9 @@ pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8
1398 return true;1358 return true;
1399 },1359 },
1400 .lazy_path, .lazy_path_list => {1360 .lazy_path, .lazy_path_list => {
1401 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)) });1361 log.warn("the lazy path value type isn't added from the CLI, but somehow '{s}' is a .{f}", .{
1362 name, std.zig.fmtId(@tagName(gop.value_ptr.value)),
1363 });
1402 return true;1364 return true;
1403 },1365 },
1404 }1366 }
...@@ -1437,7 +1399,7 @@ pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool...@@ -1437,7 +1399,7 @@ pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool
1437 return false;1399 return false;
1438}1400}
14391401
1440fn typeToEnum(comptime T: type) TypeId {1402fn typeToEnum(comptime T: type) Configuration.AvailableOption.Type {
1441 return switch (T) {1403 return switch (T) {
1442 std.zig.BuildId => .build_id,1404 std.zig.BuildId => .build_id,
1443 LazyPath => .lazy_path,1405 LazyPath => .lazy_path,
...@@ -1588,17 +1550,6 @@ pub fn path(b: *Build, sub_path: []const u8) LazyPath {...@@ -1588,17 +1550,6 @@ pub fn path(b: *Build, sub_path: []const u8) LazyPath {
1588 } };1550 } };
1589}1551}
15901552
1591/// This is low-level implementation details of the build system, not meant to
1592/// be called by users' build scripts. Even in the build system itself it is a
1593/// code smell to call this function.
1594pub fn pathFromRoot(b: *Build, sub_path: []const u8) []u8 {
1595 return b.pathResolve(&.{ b.build_root.path orelse ".", sub_path });
1596}
1597
1598fn pathFromCwd(b: *Build, sub_path: []const u8) []u8 {
1599 return b.pathResolve(&.{ b.graph.cache.cwd, sub_path });
1600}
1601
1602pub fn pathJoin(b: *Build, paths: []const []const u8) []u8 {1553pub fn pathJoin(b: *Build, paths: []const []const u8) []u8 {
1603 return fs.path.join(b.allocator, paths) catch @panic("OOM");1554 return fs.path.join(b.allocator, paths) catch @panic("OOM");
1604}1555}
...@@ -1792,7 +1743,9 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c...@@ -1792,7 +1743,9 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c
1792 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };1743 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };
1793 } else .{ "", deps.root_deps };1744 } else .{ "", deps.root_deps };
1794 if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) {1745 if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) {
1795 std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{ asking_build_zig, b.pathFromRoot("build.zig") });1746 std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{
1747 asking_build_zig, b.pathFromRoot("build.zig"),
1748 });
1796 }1749 }
1797 comptime for (b_pkg_deps) |dep| {1750 comptime for (b_pkg_deps) |dep| {
1798 if (std.mem.eql(u8, dep[0], dep_name)) return dep[1];1751 if (std.mem.eql(u8, dep[0], dep_name)) return dep[1];
lib/std/Build/Configuration.zig+25-3
...@@ -189,9 +189,24 @@ pub const Wip = struct {...@@ -189,9 +189,24 @@ pub const Wip = struct {
189 }189 }
190190
191 pub fn addStringList(wip: *Wip, list: []const []const u8) Allocator.Error!StringList {191 pub fn addStringList(wip: *Wip, list: []const []const u8) Allocator.Error!StringList {
192 _ = wip;192 // Increase size of extra to support the list. Add the string list
193 _ = list;193 // there. Then check for duplicate, reverting list if already found.
194 @panic("TODO");194 const gpa = wip.gpa;
195 const revert_index: u32 = @intCast(wip.extra.items.len);
196 const added = try wip.extra.addManyAsSlice(gpa, list.len + 1);
197 added[0] = @intCast(list.len);
198 for (added[1..], list) |*d, s| d.* = @intFromEnum(try addString(wip, s));
199 const gop = try wip.dedupe_table.getOrPutContext(gpa, .{
200 .index = revert_index,
201 .len = @intCast(added.len),
202 }, @as(ExtraSlice.Context, .{ .extra = wip.extra.items }));
203
204 if (gop.found_existing) {
205 wip.extra.items.len = revert_index;
206 return @enumFromInt(gop.key_ptr.index);
207 }
208
209 return @enumFromInt(revert_index);
195 }210 }
196211
197 pub fn addBytes(wip: *Wip, bytes: []const u8) Allocator.Error!Bytes {212 pub fn addBytes(wip: *Wip, bytes: []const u8) Allocator.Error!Bytes {
...@@ -1456,6 +1471,13 @@ pub const OptionalStringList = enum(u32) {...@@ -1456,6 +1471,13 @@ pub const OptionalStringList = enum(u32) {
1456 none = max_u32,1471 none = max_u32,
1457 _,1472 _,
14581473
1474 pub fn init(opt_string_list: ?StringList) OptionalStringList {
1475 const sl = opt_string_list orelse return .none;
1476 const result: OptionalStringList = @enumFromInt(@intFromEnum(sl));
1477 assert(result != .none);
1478 return result;
1479 }
1480
1459 pub fn unwrap(this: @This()) ?StringList {1481 pub fn unwrap(this: @This()) ?StringList {
1460 if (this == .none) return null;1482 if (this == .none) return null;
1461 return @enumFromInt(@intFromEnum(this));1483 return @enumFromInt(@intFromEnum(this));
src/main.zig+45-18
...@@ -4951,6 +4951,7 @@ fn cmdBuild(...@@ -4951,6 +4951,7 @@ fn cmdBuild(
4951 .ReleaseSafe;4951 .ReleaseSafe;
4952 var configure_argv: std.ArrayList([]const u8) = .empty;4952 var configure_argv: std.ArrayList([]const u8) = .empty;
4953 var make_argv: std.ArrayList([]const u8) = .empty;4953 var make_argv: std.ArrayList([]const u8) = .empty;
4954 var cached_unordered_passthru_configure: std.ArrayList(u32) = .empty;
4954 var forks: std.ArrayList(Fork) = .empty;4955 var forks: std.ArrayList(Fork) = .empty;
4955 var reference_trace: ?u32 = null;4956 var reference_trace: ?u32 = null;
4956 var debug_compile_errors = false;4957 var debug_compile_errors = false;
...@@ -4975,6 +4976,7 @@ fn cmdBuild(...@@ -4975,6 +4976,7 @@ fn cmdBuild(
49754976
4976 try configure_argv.ensureUnusedCapacity(arena, 16);4977 try configure_argv.ensureUnusedCapacity(arena, 16);
4977 try make_argv.ensureUnusedCapacity(arena, 16);4978 try make_argv.ensureUnusedCapacity(arena, 16);
4979 try cached_unordered_passthru_configure.ensureUnusedCapacity(arena, 16);
49784980
4979 _ = configure_argv.addOneAssumeCapacity(); // configurer executable4981 _ = configure_argv.addOneAssumeCapacity(); // configurer executable
4980 _ = make_argv.addOneAssumeCapacity(); // maker executable4982 _ = make_argv.addOneAssumeCapacity(); // maker executable
...@@ -5007,7 +5009,33 @@ fn cmdBuild(...@@ -5007,7 +5009,33 @@ fn cmdBuild(
5007 while (i < args.len) : (i += 1) {5009 while (i < args.len) : (i += 1) {
5008 const arg = args[i];5010 const arg = args[i];
5009 if (mem.startsWith(u8, arg, "-")) {5011 if (mem.startsWith(u8, arg, "-")) {
5010 if (mem.eql(u8, arg, "--build-file")) {5012 try configure_argv.ensureUnusedCapacity(arena, 1);
5013
5014 if (mem.startsWith(u8, arg, "-D") or
5015 mem.startsWith(u8, arg, "-fsys=") or
5016 mem.startsWith(u8, arg, "-fno-sys=") or
5017 mem.startsWith(u8, arg, "--release=") or
5018 mem.eql(u8, arg, "--release"))
5019 {
5020 try cached_unordered_passthru_configure.append(arena, @intCast(configure_argv.items.len));
5021 configure_argv.appendAssumeCapacity(arg);
5022 continue;
5023 } else if (mem.eql(u8, arg, "--system")) {
5024 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5025 i += 1;
5026 system_pkg_dir_path = args[i];
5027
5028 try cached_unordered_passthru_configure.append(arena, @intCast(configure_argv.items.len));
5029 configure_argv.appendAssumeCapacity(arg); // Intentionally "--system" only; not the path.
5030 continue;
5031 } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| {
5032 color = std.meta.stringToEnum(Color, rest) orelse
5033 fatal("expected --color=[auto|on|off]; found: {s}", .{arg});
5034
5035 try cached_unordered_passthru_configure.append(arena, @intCast(configure_argv.items.len));
5036 configure_argv.appendAssumeCapacity(arg);
5037 continue;
5038 } else if (mem.eql(u8, arg, "--build-file")) {
5011 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});5039 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5012 i += 1;5040 i += 1;
5013 build_file = args[i];5041 build_file = args[i];
...@@ -5058,12 +5086,6 @@ fn cmdBuild(...@@ -5058,12 +5086,6 @@ fn cmdBuild(
5058 .failed = false,5086 .failed = false,
5059 });5087 });
5060 continue;5088 continue;
5061 } else if (mem.eql(u8, arg, "--system")) {
5062 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
5063 i += 1;
5064 system_pkg_dir_path = args[i];
5065 try configure_argv.append(arena, "--system");
5066 continue;
5067 } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {5089 } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
5068 reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {5090 reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
5069 fatal("unable to parse reference_trace count '{s}': {t}", .{ num, err });5091 fatal("unable to parse reference_trace count '{s}': {t}", .{ num, err });
...@@ -5122,14 +5144,6 @@ fn cmdBuild(...@@ -5122,14 +5144,6 @@ fn cmdBuild(
5122 verbose_llvm_bc = rest;5144 verbose_llvm_bc = rest;
5123 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {5145 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
5124 verbose_llvm_cpu_features = true;5146 verbose_llvm_cpu_features = true;
5125 } else if (mem.eql(u8, arg, "--color")) {
5126 if (i + 1 >= args.len) fatal("expected [auto|on|off] after {s}", .{arg});
5127 i += 1;
5128 color = std.meta.stringToEnum(Color, args[i]) orelse {
5129 fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] });
5130 };
5131 try configure_argv.appendSlice(arena, &.{ arg, args[i] });
5132 continue;
5133 } else if (mem.cutPrefix(u8, arg, "-j")) |str| {5147 } else if (mem.cutPrefix(u8, arg, "-j")) |str| {
5134 const num = std.fmt.parseUnsigned(u32, str, 10) catch |err|5148 const num = std.fmt.parseUnsigned(u32, str, 10) catch |err|
5135 fatal("unable to parse jobs count {s}: {t}", .{ str, err });5149 fatal("unable to parse jobs count {s}: {t}", .{ str, err });
...@@ -5143,9 +5157,6 @@ fn cmdBuild(...@@ -5143,9 +5157,6 @@ fn cmdBuild(
5143 make_argv.items[argv_index_seed] = args[i];5157 make_argv.items[argv_index_seed] = args[i];
5144 continue;5158 continue;
5145 } else if (mem.eql(u8, arg, "--")) {5159 } else if (mem.eql(u8, arg, "--")) {
5146 // The rest of the args are supposed to get passed onto
5147 // build runner's `build.args`
5148 try configure_argv.append(arena, "--have-run-args");
5149 try make_argv.appendSlice(arena, args[i..]);5160 try make_argv.appendSlice(arena, args[i..]);
5150 break;5161 break;
5151 }5162 }
...@@ -5212,6 +5223,22 @@ fn cmdBuild(...@@ -5212,6 +5223,22 @@ fn cmdBuild(
5212 defer config_man.deinit();5223 defer config_man.deinit();
5213 config_man.hash.addBytes(build_options.version);5224 config_man.hash.addBytes(build_options.version);
52145225
5226 const SortContext = struct {
5227 list: []const []const u8,
5228 fn lessThan(this: @This(), lhs: u32, rhs: u32) bool {
5229 return mem.lessThan(u8, this.list[lhs], this.list[rhs]);
5230 }
5231 };
5232 mem.sortUnstable(
5233 u32,
5234 cached_unordered_passthru_configure.items,
5235 @as(SortContext, .{ .list = configure_argv.items }),
5236 SortContext.lessThan,
5237 );
5238 for (cached_unordered_passthru_configure.items) |i| {
5239 config_man.hash.addBytes(configure_argv.items[i]);
5240 }
5241
5215 // Normally the build runner is compiled for the host target but here is5242 // Normally the build runner is compiled for the host target but here is
5216 // some code to help when debugging edits to the build runner so that you5243 // some code to help when debugging edits to the build runner so that you
5217 // can make sure it compiles successfully on other targets.5244 // can make sure it compiles successfully on other targets.