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* implement the build options
21* finish migrating the rest of the build steps
32* inspect b4ffb402c082605c4b324e88120306fc8fb3cf32 diff and apply changes as needed (merge conflict)
43* make zig-pkg path root configurable in maker (make sure --system still works)
54* eliminate calls to getPath, getPath2, getPath3
65* [build system compile step data races with getGraph function](https://codeberg.org/ziglang/zig/issues/31397)
6* test lazyImport
77* solve the TODOs added in this branch
88* get zig tests passing
99* test a bunch of third party projects / help people migrate
......@@ -16,6 +16,8 @@
1616* make addExtra return Index using reflection
1717* refactor with DefaultingEnum
1818
19* implement {q} or delete {q} uses
20
1921## Followup Issues
2022* reduce the size of Maker.Step.Extended (make Run smaller) probably by using an arena per make
2123* link_eh_frame_hdr should be DefaultingBool
......@@ -24,6 +26,7 @@
2426 - but artifact install steps also add paths for dyn libs on windows
2527* no more "artifact arg" to run step. if you want to run the post-install binary, get the lazy path
2628 from the install step.
29* -D options which are files need to be accounted for in the configure cache
2730
2831
2932## Release Notes
......@@ -45,4 +48,4 @@ run_cmd.addPassthruArgs();
4548
4649This removes a capability from build scripts since they can no longer observe
4750those 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 {
6464
6565 const builder = try std.Build.create(&graph, dependencies.root_deps);
6666
67 var error_style: ErrorStyle = .verbose;
68 var multiline_errors: MultilineErrors = .indent;
6967 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
8368 var arg_i: usize = 1; // Skip own executable name.
8469
8570 while (nextArg(args, &arg_i)) |arg| {
......@@ -101,39 +86,20 @@ pub fn main(init: process.Init.Minimal) !void {
10186 try graph.system_integration_options.put(arena, name, .user_disabled);
10287 } else if (mem.eql(u8, arg, "--release")) {
10388 graph.release_mode = .any;
104 } else if (mem.cutPrefix(u8, arg, "--release=")) |text| {
105 graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, text) orelse {
106 fatalWithHint("expected [off|any|fast|safe|small] in {q}, found {q}", .{
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 });
89 } else if (mem.cutPrefix(u8, arg, "--release=")) |rest| {
90 graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, rest) orelse {
91 fatalWithHint("expected --release=[off|any|fast|safe|small]; found: {s}", .{arg});
12992 };
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});
13096 } else if (mem.eql(u8, arg, "--system")) {
13197 // The usage text shows another argument after this parameter
13298 // but it is handled by the parent process. The build runner
13399 // only sees this flag.
134100 graph.system_package_mode = true;
135101 } else {
136 fatalWithHint("unrecognized argument: {q}", .{arg});
102 fatalWithHint("unrecognized argument: {s}", .{arg});
137103 }
138104 }
139105
......@@ -152,6 +118,7 @@ pub fn main(init: process.Init.Minimal) !void {
152118 fatal(" access the help menu with 'zig build -h'", .{});
153119 }
154120
121 try serializePackageOptions(builder, &graph.wip_configuration);
155122 try serializeSystemIntegrationOptions(&graph, &graph.wip_configuration);
156123
157124 var stdout_buffer: [1024]u8 = undefined;
......@@ -1125,3 +1092,17 @@ fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration
11251092 process.exit(1);
11261093 }
11271094}
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,
3030uninstall_tls: Step.TopLevel,
3131allocator: Allocator,
3232user_input_options: UserInputOptionsMap,
33available_options_map: AvailableOptionsMap,
34available_options_list: std.array_list.Managed(AvailableOption),
33available_options_map: std.array_hash_map.String(AvailableOption) = .empty,
3534invalid_user_input: bool,
3635default_step: *Step,
3736top_level_steps: std.StringArrayHashMapUnmanaged(*Step.TopLevel),
......@@ -180,11 +179,10 @@ pub const RunError = error{
180179} || std.process.SpawnError;
181180
182181const UserInputOptionsMap = StringHashMap(UserInputOption);
183const AvailableOptionsMap = StringHashMap(AvailableOption);
184182
185183const AvailableOption = struct {
186184 name: []const u8,
187 type_id: TypeId,
185 type_id: Configuration.AvailableOption.Type,
188186 description: []const u8,
189187 /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options
190188 enum_options: ?[]const []const u8,
......@@ -205,19 +203,6 @@ const UserValue = union(enum) {
205203 lazy_path_list: std.array_list.Managed(LazyPath),
206204};
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
221206pub fn create(
222207 graph: *Graph,
223208 available_deps: AvailableDeps,
......@@ -230,8 +215,6 @@ pub fn create(
230215 .invalid_user_input = false,
231216 .allocator = arena,
232217 .user_input_options = UserInputOptionsMap.init(arena),
233 .available_options_map = AvailableOptionsMap.init(arena),
234 .available_options_list = std.array_list.Managed(AvailableOption).init(arena),
235218 .top_level_steps = .{},
236219 .default_step = undefined,
237220 .install_prefix = undefined,
......@@ -292,8 +275,6 @@ fn createChild(
292275 .description = "Remove build artifacts from prefix path",
293276 },
294277 .user_input_options = user_input_options,
295 .available_options_map = AvailableOptionsMap.init(allocator),
296 .available_options_list = std.array_list.Managed(AvailableOption).init(allocator),
297278 .invalid_user_input = false,
298279 .default_step = undefined,
299280 .top_level_steps = .{},
......@@ -960,13 +941,14 @@ pub fn getUninstallStep(b: *Build) *Step {
960941/// these options when calling the dependency's build.zig script as a function.
961942/// `null` is returned when an option is left to default.
962943pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
944 const arena = b.allocator;
963945 const name = b.dupe(name_raw);
964946 const description = b.dupe(description_raw);
965947 const type_id = comptime typeToEnum(T);
966948 const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: {
967949 const EnumType = if (type_id == .enum_list) @typeInfo(T).pointer.child else T;
968950 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
971953 inline for (fields) |field| {
972954 options.appendAssumeCapacity(field.name);
......@@ -980,10 +962,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
980962 .description = description,
981963 .enum_options = enum_options,
982964 };
983 if ((b.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) {
984 panic("Option '{s}' declared twice", .{name});
965 if ((b.available_options_map.fetchPut(arena, name, available_option) catch @panic("OOM")) != null) {
966 panic("option '{s}' declared twice", .{name});
985967 }
986 b.available_options_list.append(available_option) catch @panic("OOM");
987968
988969 const option_ptr = b.user_input_options.getPtr(name) orelse return null;
989970 option_ptr.used = true;
......@@ -996,36 +977,32 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
996977 } else if (mem.eql(u8, s, "false")) {
997978 return false;
998979 } 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 });
1000981 b.markInvalidUserInput();
1001982 return null;
1002983 }
1003984 },
1004985 .list, .map, .lazy_path, .lazy_path_list => {
1005 log.err("Expected -D{s} to be a boolean, but received a {s}.", .{
1006 name, @tagName(option_ptr.value),
1007 });
986 log.err("expected -D{s} to be a boolean; received: {t}", .{ name, option_ptr.value });
1008987 b.markInvalidUserInput();
1009988 return null;
1010989 },
1011990 },
1012991 .int => switch (option_ptr.value) {
1013992 .flag, .list, .map, .lazy_path, .lazy_path_list => {
1014 log.err("Expected -D{s} to be an integer, but received a {s}.", .{
1015 name, @tagName(option_ptr.value),
1016 });
993 log.err("expected -D{s} to be an integer; received: {t}", .{ name, option_ptr.value });
1017994 b.markInvalidUserInput();
1018995 return null;
1019996 },
1020997 .scalar => |s| {
1021998 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
1022999 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) });
10241001 b.markInvalidUserInput();
10251002 return null;
10261003 },
10271004 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) });
10291006 b.markInvalidUserInput();
10301007 return null;
10311008 },
......@@ -1035,15 +1012,13 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
10351012 },
10361013 .float => switch (option_ptr.value) {
10371014 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1038 log.err("Expected -D{s} to be a float, but received a {s}.", .{
1039 name, @tagName(option_ptr.value),
1040 });
1015 log.err("expected -D{s} to be a float; received: {t}", .{ name, option_ptr.value });
10411016 b.markInvalidUserInput();
10421017 return null;
10431018 },
10441019 .scalar => |s| {
10451020 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) });
10471022 b.markInvalidUserInput();
10481023 return null;
10491024 };
......@@ -1052,9 +1027,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
10521027 },
10531028 .@"enum" => switch (option_ptr.value) {
10541029 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1055 log.err("Expected -D{s} to be an enum, but received a {s}.", .{
1056 name, @tagName(option_ptr.value),
1057 });
1030 log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value });
10581031 b.markInvalidUserInput();
10591032 return null;
10601033 },
......@@ -1062,7 +1035,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
10621035 if (std.meta.stringToEnum(T, s)) |enum_lit| {
10631036 return enum_lit;
10641037 } 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) });
10661039 b.markInvalidUserInput();
10671040 return null;
10681041 }
......@@ -1070,9 +1043,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
10701043 },
10711044 .string => switch (option_ptr.value) {
10721045 .flag, .list, .map, .lazy_path, .lazy_path_list => {
1073 log.err("Expected -D{s} to be a string, but received a {s}.", .{
1074 name, @tagName(option_ptr.value),
1075 });
1046 log.err("expected -D{s} to be a string; received: {t}", .{ name, option_ptr.value });
10761047 b.markInvalidUserInput();
10771048 return null;
10781049 },
......@@ -1080,9 +1051,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
10801051 },
10811052 .build_id => switch (option_ptr.value) {
10821053 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1083 log.err("Expected -D{s} to be an enum, but received a {s}.", .{
1084 name, @tagName(option_ptr.value),
1085 });
1054 log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value });
10861055 b.markInvalidUserInput();
10871056 return null;
10881057 },
......@@ -1090,7 +1059,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
10901059 if (std.zig.BuildId.parse(s)) |build_id| {
10911060 return build_id;
10921061 } 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 });
10941063 b.markInvalidUserInput();
10951064 return null;
10961065 }
......@@ -1098,42 +1067,38 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
10981067 },
10991068 .list => switch (option_ptr.value) {
11001069 .flag, .map, .lazy_path, .lazy_path_list => {
1101 log.err("Expected -D{s} to be a list, but received a {s}.", .{
1102 name, @tagName(option_ptr.value),
1103 });
1070 log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value });
11041071 b.markInvalidUserInput();
11051072 return null;
11061073 },
11071074 .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");
11091076 },
11101077 .list => |lst| return lst.items,
11111078 },
11121079 .enum_list => switch (option_ptr.value) {
11131080 .flag, .map, .lazy_path, .lazy_path_list => {
1114 log.err("Expected -D{s} to be a list, but received a {s}.", .{
1115 name, @tagName(option_ptr.value),
1116 });
1081 log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value });
11171082 b.markInvalidUserInput();
11181083 return null;
11191084 },
11201085 .scalar => |s| {
11211086 const Child = @typeInfo(T).pointer.child;
11221087 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) });
11241089 b.markInvalidUserInput();
11251090 return null;
11261091 };
1127 return b.allocator.dupe(Child, &[_]Child{value}) catch @panic("OOM");
1092 return arena.dupe(Child, &[_]Child{value}) catch @panic("OOM");
11281093 },
11291094 .list => |lst| {
11301095 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");
11321097 for (new_list, lst.items) |*new_item, str| {
11331098 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) });
11351100 b.markInvalidUserInput();
1136 b.allocator.free(new_list);
1101 arena.free(new_list);
11371102 return null;
11381103 };
11391104 }
......@@ -1144,18 +1109,16 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
11441109 .scalar => |s| return .{ .cwd_relative = s },
11451110 .lazy_path => |lp| return lp,
11461111 .flag, .map, .list, .lazy_path_list => {
1147 log.err("Expected -D{s} to be a path, but received a {s}.", .{
1148 name, @tagName(option_ptr.value),
1149 });
1112 log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value });
11501113 b.markInvalidUserInput();
11511114 return null;
11521115 },
11531116 },
11541117 .lazy_path_list => switch (option_ptr.value) {
1155 .scalar => |s| return b.allocator.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"),
1156 .lazy_path => |lp| return b.allocator.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"),
1118 .scalar => |s| return arena.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"),
1119 .lazy_path => |lp| return arena.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"),
11571120 .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");
11591122 for (new_list, lst.items) |*new_item, str| {
11601123 new_item.* = .{ .cwd_relative = str };
11611124 }
......@@ -1163,9 +1126,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
11631126 },
11641127 .lazy_path_list => |lp_list| return lp_list.items,
11651128 .flag, .map => {
1166 log.err("Expected -D{s} to be a path, but received a {s}.", .{
1167 name, @tagName(option_ptr.value),
1168 });
1129 log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value });
11691130 b.markInvalidUserInput();
11701131 return null;
11711132 },
......@@ -1250,8 +1211,8 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile
12501211 opts_copy.diagnostics = &diags;
12511212 return std.Target.Query.parse(opts_copy) catch |err| switch (err) {
12521213 error.UnknownCpuModel => {
1253 std.debug.print("unknown CPU: '{s}'\navailable CPUs for architecture '{s}':\n", .{
1254 diags.cpu_name.?, @tagName(diags.arch.?),
1214 std.debug.print("unknown CPU: '{s}'\navailable CPUs for architecture '{t}':\n", .{
1215 diags.cpu_name.?, diags.arch.?,
12551216 });
12561217 for (diags.arch.?.allCpuModels()) |cpu| {
12571218 std.debug.print(" {s}\n", .{cpu.name});
......@@ -1261,11 +1222,10 @@ pub fn parseTargetQuery(options: std.Target.Query.ParseOptions) error{ParseFaile
12611222 error.UnknownCpuFeature => {
12621223 std.debug.print(
12631224 \\unknown CPU feature: '{s}'
1264 \\available CPU features for architecture '{s}':
1225 \\available CPU features for architecture '{t}':
12651226 \\
12661227 , .{
1267 diags.unknown_feature_name.?,
1268 @tagName(diags.arch.?),
1228 diags.unknown_feature_name.?, diags.arch.?,
12691229 });
12701230 for (diags.arch.?.allFeaturesList()) |feature| {
12711231 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
13981358 return true;
13991359 },
14001360 .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 });
14021364 return true;
14031365 },
14041366 }
......@@ -1437,7 +1399,7 @@ pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool
14371399 return false;
14381400}
14391401
1440fn typeToEnum(comptime T: type) TypeId {
1402fn typeToEnum(comptime T: type) Configuration.AvailableOption.Type {
14411403 return switch (T) {
14421404 std.zig.BuildId => .build_id,
14431405 LazyPath => .lazy_path,
......@@ -1588,17 +1550,6 @@ pub fn path(b: *Build, sub_path: []const u8) LazyPath {
15881550 } };
15891551}
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
16021553pub fn pathJoin(b: *Build, paths: []const []const u8) []u8 {
16031554 return fs.path.join(b.allocator, paths) catch @panic("OOM");
16041555}
......@@ -1792,7 +1743,9 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c
17921743 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };
17931744 } else .{ "", deps.root_deps };
17941745 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 });
17961749 }
17971750 comptime for (b_pkg_deps) |dep| {
17981751 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 {
189189 }
190190
191191 pub fn addStringList(wip: *Wip, list: []const []const u8) Allocator.Error!StringList {
192 _ = wip;
193 _ = list;
194 @panic("TODO");
192 // Increase size of extra to support the list. Add the string list
193 // there. Then check for duplicate, reverting list if already found.
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);
195210 }
196211
197212 pub fn addBytes(wip: *Wip, bytes: []const u8) Allocator.Error!Bytes {
......@@ -1456,6 +1471,13 @@ pub const OptionalStringList = enum(u32) {
14561471 none = max_u32,
14571472 _,
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
14591481 pub fn unwrap(this: @This()) ?StringList {
14601482 if (this == .none) return null;
14611483 return @enumFromInt(@intFromEnum(this));
src/main.zig+45-18
......@@ -4951,6 +4951,7 @@ fn cmdBuild(
49514951 .ReleaseSafe;
49524952 var configure_argv: std.ArrayList([]const u8) = .empty;
49534953 var make_argv: std.ArrayList([]const u8) = .empty;
4954 var cached_unordered_passthru_configure: std.ArrayList(u32) = .empty;
49544955 var forks: std.ArrayList(Fork) = .empty;
49554956 var reference_trace: ?u32 = null;
49564957 var debug_compile_errors = false;
......@@ -4975,6 +4976,7 @@ fn cmdBuild(
49754976
49764977 try configure_argv.ensureUnusedCapacity(arena, 16);
49774978 try make_argv.ensureUnusedCapacity(arena, 16);
4979 try cached_unordered_passthru_configure.ensureUnusedCapacity(arena, 16);
49784980
49794981 _ = configure_argv.addOneAssumeCapacity(); // configurer executable
49804982 _ = make_argv.addOneAssumeCapacity(); // maker executable
......@@ -5007,7 +5009,33 @@ fn cmdBuild(
50075009 while (i < args.len) : (i += 1) {
50085010 const arg = args[i];
50095011 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")) {
50115039 if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
50125040 i += 1;
50135041 build_file = args[i];
......@@ -5058,12 +5086,6 @@ fn cmdBuild(
50585086 .failed = false,
50595087 });
50605088 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;
50675089 } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
50685090 reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
50695091 fatal("unable to parse reference_trace count '{s}': {t}", .{ num, err });
......@@ -5122,14 +5144,6 @@ fn cmdBuild(
51225144 verbose_llvm_bc = rest;
51235145 } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
51245146 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;
51335147 } else if (mem.cutPrefix(u8, arg, "-j")) |str| {
51345148 const num = std.fmt.parseUnsigned(u32, str, 10) catch |err|
51355149 fatal("unable to parse jobs count {s}: {t}", .{ str, err });
......@@ -5143,9 +5157,6 @@ fn cmdBuild(
51435157 make_argv.items[argv_index_seed] = args[i];
51445158 continue;
51455159 } 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");
51495160 try make_argv.appendSlice(arena, args[i..]);
51505161 break;
51515162 }
......@@ -5212,6 +5223,22 @@ fn cmdBuild(
52125223 defer config_man.deinit();
52135224 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
52155242 // Normally the build runner is compiled for the host target but here is
52165243 // some code to help when debugging edits to the build runner so that you
52175244 // can make sure it compiles successfully on other targets.