authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-14 10:30:15+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-08-14 10:30:15+02:00
log63cfe88f0c011895fdd573703ad6715ec91e3231
treeeba50dc1fc5a69b0d5b9ee8a54d79eccf91bfcc0
parent4e5b5356094a63a13955c757cb2713f120fa920e
parente0f7e43a540bb1c401f44505941ae1f4b3645587

Merge pull request 'build system: rework user input options' (#36503) from fix-build-system into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36503

6 files changed, 374 insertions(+), 663 deletions(-)

lib/compiler/Maker.zig+1-1
...@@ -3388,7 +3388,7 @@ pub fn generatedPath(maker: *const Maker, index: Configuration.GeneratedFileInde...@@ -3388,7 +3388,7 @@ pub fn generatedPath(maker: *const Maker, index: Configuration.GeneratedFileInde
3388pub fn packagePath(3388pub fn packagePath(
3389 maker: *const Maker,3389 maker: *const Maker,
3390 arena: Allocator,3390 arena: Allocator,
3391 inst_index: Configuration.PackageInstance.Index,3391 inst_index: Configuration.Package.Instance.Index,
3392 sub_path: []const u8,3392 sub_path: []const u8,
3393) Allocator.Error!Path {3393) Allocator.Error!Path {
3394 const c = &maker.scanned_config.configuration;3394 const c = &maker.scanned_config.configuration;
lib/compiler/Maker/ScannedConfig.zig-8
...@@ -116,14 +116,6 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {...@@ -116,14 +116,6 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void {
116 try sf.container.serializer.int(@backingInt(inst.package));116 try sf.container.serializer.int(@backingInt(inst.package));
117 }117 }
118118
119 var otf = try sf.beginTupleField("user_input_options", .{});
120 for (inst.user_input_options.slice(c)) |option| {
121 var osf = try otf.beginStructField(.{});
122 try sc.printStruct(&osf, Configuration.PackageInstance.UserInputOption, option.get(c));
123 try osf.end();
124 }
125 try otf.end();
126
127 var msf = try sf.beginStructField("modules", .{});119 var msf = try sf.beginStructField("modules", .{});
128 for (inst.modules.keys.slice(c), inst.modules.values.slice(c)) |key, value| {120 for (inst.modules.keys.slice(c), inst.modules.values.slice(c)) |key, value| {
129 var msf2 = try msf.beginStructField(key.slice(c), .{});121 var msf2 = try msf.beginStructField(key.slice(c), .{});
lib/compiler/configurer.zig+27-6
...@@ -86,11 +86,15 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -86,11 +86,15 @@ pub fn main(init: process.Init.Minimal) !void {
86 if (mem.findScalar(u8, option_contents, '=')) |name_end| {86 if (mem.findScalar(u8, option_contents, '=')) |name_end| {
87 const option_name = option_contents[0..name_end];87 const option_name = option_contents[0..name_end];
88 const option_value = option_contents[name_end + 1 ..];88 const option_value = option_contents[name_end + 1 ..];
89 if (try builder.addUserInputOption(option_name, option_value))89 if (try builder.addUserInputOption(option_name, option_value)) {
90 fatal(" access the help menu with 'zig build -h'", .{});90 log.info("to access the help menu: zig build -h", .{});
91 process.exit(1);
92 }
91 } else {93 } else {
92 if (try builder.addUserInputFlag(option_contents))94 if (try builder.addUserInputFlag(option_contents)) {
93 fatal(" access the help menu with 'zig build -h'", .{});95 log.info("to access the help menu: zig build -h", .{});
96 process.exit(1);
97 }
94 }98 }
95 } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| {99 } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| {
96 try graph.system_integration_options.put(arena, name, .user_enabled);100 try graph.system_integration_options.put(arena, name, .user_enabled);
...@@ -133,8 +137,25 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -133,8 +137,25 @@ pub fn main(init: process.Init.Minimal) !void {
133137
134 builder.runPackageScript(root);138 builder.runPackageScript(root);
135139
136 if (builder.validateUserInputDidItFail()) {140 // Even though the root package's user input options are not serialized,
137 fatal(" access the help menu with 'zig build -h'", .{});141 // this is done for consistency, since the rest of the dependency tree
142 // sorts user_input_options before calling validateUserInputDidItFail,
143 // which has user-visible behavior (the order of errors reported).
144 std.Build.PackageOptions.sort(&builder.user_input_options);
145
146 // Make sure the package actually provides all the arguments specified.
147 for (builder.user_input_options.keys()) |name| {
148 if (!builder.available_options_map.contains(name)) {
149 log.err("invalid option: {q}", .{name});
150 builder.invalid_user_input = true;
151 }
152 }
153 if (builder.invalid_user_input) {
154 for (builder.available_options_map.keys(), builder.available_options_map.values()) |name, *available| {
155 log.info("available option: {q}: {s}", .{ name, available.description });
156 }
157 log.info("to access the help menu: zig build -h", .{});
158 process.exit(1);
138 }159 }
139160
140 try Serialize.packageOptions(builder, &graph.wip_configuration);161 try Serialize.packageOptions(builder, &graph.wip_configuration);
lib/std/Build.zig+295-489
...@@ -9,7 +9,6 @@ const mem = std.mem;...@@ -9,7 +9,6 @@ const mem = std.mem;
9const panic = std.debug.panic;9const panic = std.debug.panic;
10const assert = std.debug.assert;10const assert = std.debug.assert;
11const log = std.log;11const log = std.log;
12const StringHashMap = std.StringHashMap;
13const Allocator = std.mem.Allocator;12const Allocator = std.mem.Allocator;
14const Target = std.Target;13const Target = std.Target;
15const process = std.process;14const process = std.process;
...@@ -32,9 +31,6 @@ graph: *Graph,...@@ -32,9 +31,6 @@ graph: *Graph,
32install_tls: Step.TopLevel,31install_tls: Step.TopLevel,
33uninstall_tls: Step.TopLevel,32uninstall_tls: Step.TopLevel,
34allocator: Allocator,33allocator: Allocator,
35user_input_options: UserInputOptionsMap,
36available_options_map: std.array_hash_map.String(AvailableOption) = .empty,
37invalid_user_input: bool,
38default_step: *Step,34default_step: *Step,
39top_level_steps: std.array_hash_map.String(*Step.TopLevel),35top_level_steps: std.array_hash_map.String(*Step.TopLevel),
40/// Path to the directory containing build.zig.36/// Path to the directory containing build.zig.
...@@ -45,6 +41,10 @@ debug_log_scopes: []const []const u8 = &.{},...@@ -45,6 +41,10 @@ debug_log_scopes: []const []const u8 = &.{},
45/// Set to 0 to disable stack collection.41/// Set to 0 to disable stack collection.
46debug_stack_frames_count: u8 = 8,42debug_stack_frames_count: u8 = 8,
4743
44user_input_options: PackageOptions.Map,
45available_options_map: std.array_hash_map.String(AvailableOption) = .empty,
46invalid_user_input: bool,
47
48dep_prefix: []const u8 = "",48dep_prefix: []const u8 = "",
4949
50modules: std.array_hash_map.String(*Module),50modules: std.array_hash_map.String(*Module),
...@@ -82,7 +82,7 @@ pub const Graph = struct {...@@ -82,7 +82,7 @@ pub const Graph = struct {
82 needed_lazy_dependencies: std.array_hash_map.String(void) = .empty,82 needed_lazy_dependencies: std.array_hash_map.String(void) = .empty,
83 /// Information about the native target. Computed before build() is invoked.83 /// Information about the native target. Computed before build() is invoked.
84 host: ResolvedTarget,84 host: ResolvedTarget,
85 dependency_cache: InitializedDepMap = .empty,85 dependency_cache: PackageInstanceMap = .empty,
86 allow_so_scripts: ?bool = null,86 allow_so_scripts: ?bool = null,
87 time_report: bool = false,87 time_report: bool = false,
88 verbose: bool = false,88 verbose: bool = false,
...@@ -238,65 +238,123 @@ pub const SystemLibraryMode = enum {...@@ -238,65 +238,123 @@ pub const SystemLibraryMode = enum {
238 declared_enabled,238 declared_enabled,
239};239};
240240
241const InitializedDepMap = std.HashMapUnmanaged(InitializedDepKey, *Dependency, InitializedDepContext, std.hash_map.default_max_load_percentage);241const PackageInstanceMap = std.array_hash_map.Custom(PackageInstanceKey, *Dependency, struct {
242const InitializedDepKey = struct {242 pub fn hash(_: @This(), k: PackageInstanceKey) u32 {
243 build_root_string: []const u8,
244 user_input_options: UserInputOptionsMap,
245};
246
247const InitializedDepContext = struct {
248 allocator: Allocator,
249
250 pub fn hash(ctx: @This(), k: InitializedDepKey) u64 {
251 var hasher = std.hash.Wyhash.init(0);243 var hasher = std.hash.Wyhash.init(0);
252 hasher.update(k.build_root_string);244 hasher.update(k.pkg_hash);
253 hashUserInputOptionsMap(ctx.allocator, k.user_input_options, &hasher);245 for (k.options.keys(), k.options.values()) |option_key, option_value| {
254 return hasher.final();246 hasher.update(option_key);
247 option_value.hash(&hasher);
248 }
249 return @truncate(hasher.final());
250 }
251
252 pub fn eql(_: @This(), a: PackageInstanceKey, b: PackageInstanceKey, _: usize) bool {
253 if (!mem.eql(u8, a.pkg_hash, b.pkg_hash)) return false;
254 if (a.options.count() != b.options.count()) return false;
255 for (
256 a.options.keys(),
257 b.options.keys(),
258 a.options.values(),
259 b.options.values(),
260 ) |a_key, b_key, a_val, b_val| {
261 if (!mem.eql(u8, a_key, b_key)) return false;
262 if (!a_val.eql(b_val)) return false;
263 }
264 return true;
255 }265 }
266}, true);
256267
257 pub fn eql(_: @This(), lhs: InitializedDepKey, rhs: InitializedDepKey) bool {268const PackageInstanceKey = struct {
258 if (!std.mem.eql(u8, lhs.build_root_string, rhs.build_root_string))269 pkg_hash: []const u8,
259 return false;270 options: *const PackageOptions.Map,
271};
260272
261 if (lhs.user_input_options.count() != rhs.user_input_options.count())273/// Build system implementation details.
262 return false;274pub const PackageOptions = struct {
275 pub const Map = std.array_hash_map.String(UserProvided);
276
277 pub const UserProvided = union(enum) {
278 flag: void,
279 scalar: []const u8,
280 list: std.ArrayList([]const u8),
281 map: std.array_hash_map.String(*const UserProvided),
282 lazy_path: LazyPath,
283 lazy_path_list: std.ArrayList(LazyPath),
284
285 fn eql(a: UserProvided, b: UserProvided) bool {
286 if (std.meta.activeTag(a) != b) return false;
287 return switch (a) {
288 .flag => true,
289 .scalar => |a_scalar| return mem.eql(u8, a_scalar, b.scalar),
290 .list => |a_list| {
291 if (a_list.items.len != b.list.items.len) return false;
292 for (a_list.items, b.list.items) |a_elem, b_elem| {
293 if (!mem.eql(u8, a_elem, b_elem))
294 return false;
295 }
296 return true;
297 },
298 .map => |a_map| {
299 if (a_map.count() != b.map.count()) return false;
300 for (a_map.keys(), a_map.values(), b.map.keys(), b.map.values()) |a_key, a_val, b_key, b_val| {
301 if (!mem.eql(u8, a_key, b_key)) return false;
302 if (!a_val.eql(b_val.*)) return false;
303 }
304 return true;
305 },
306 .lazy_path => |a_lazy_path| return a_lazy_path.eql(b.lazy_path),
307 .lazy_path_list => |a_lazy_path_list| {
308 if (a_lazy_path_list.items.len != b.lazy_path_list.items.len) return false;
309 for (a_lazy_path_list.items, b.lazy_path_list.items) |a_lp, b_lp| {
310 if (!a_lp.eql(b_lp)) return false;
311 }
312 return true;
313 },
314 };
315 }
316
317 fn hash(a: UserProvided, hasher: *std.hash.Wyhash) void {
318 hasher.update(&mem.toBytes(std.meta.activeTag(a)));
319 switch (a) {
320 .flag => {},
321 .scalar => |scalar| hasher.update(scalar),
322 .list => |*list| for (list.items) |elem| hasher.update(elem),
323 .map => |*map| for (map.keys(), map.values()) |key, val| {
324 hasher.update(key);
325 val.hash(hasher);
326 },
327 .lazy_path => |lp| lp.hash(hasher),
328 .lazy_path_list => |*list| for (list.items) |lp| lp.hash(hasher),
329 }
330 }
331 };
263332
264 var it = lhs.user_input_options.iterator();333 fn fromArgs(arena: Allocator, map: *PackageOptions.Map, args: anytype) void {
265 while (it.next()) |lhs_entry| {334 const args_info = @typeInfo(@TypeOf(args)).@"struct";
266 const rhs_value = rhs.user_input_options.get(lhs_entry.key_ptr.*) orelse return false;335 inline for (args_info.field_names, args_info.field_types) |field_name, field_type| {
267 if (!userValuesAreSame(lhs_entry.value_ptr.*.value, rhs_value.value))336 if (field_type == @TypeOf(null)) continue;
268 return false;337 addPackageOptionFromArg(arena, map, field_name, field_type, @field(args, field_name));
269 }338 }
339 }
270340
271 return true;341 pub fn sort(map: *Map) void {
342 map.sortUnstable(@as(struct {
343 keys: []const []const u8,
344 pub fn lessThan(this: @This(), a_index: usize, b_index: usize) bool {
345 return mem.lessThan(u8, this.keys[a_index], this.keys[b_index]);
346 }
347 }, .{ .keys = map.keys() }));
272 }348 }
273};349};
274350
275pub const UserInputOptionsMap = StringHashMap(UserInputOption);
276
277const AvailableOption = struct {351const AvailableOption = struct {
278 name: []const u8,
279 type_id: Configuration.AvailableOption.Type,352 type_id: Configuration.AvailableOption.Type,
280 description: []const u8,353 description: []const u8,
281 /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options354 /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options
282 enum_options: ?[]const []const u8,355 enum_options: ?[]const []const u8,
283};356};
284357
285pub const UserInputOption = struct {
286 name: []const u8,
287 value: UserValue,
288 used: bool,
289};
290
291pub const UserValue = union(enum) {
292 flag: void,
293 scalar: []const u8,
294 list: std.array_list.Managed([]const u8),
295 map: StringHashMap(*const UserValue),
296 lazy_path: LazyPath,
297 lazy_path_list: std.array_list.Managed(LazyPath),
298};
299
300/// Build system implementation detail.358/// Build system implementation detail.
301pub fn create(359pub fn create(
302 graph: *Graph,360 graph: *Graph,
...@@ -311,7 +369,7 @@ pub fn create(...@@ -311,7 +369,7 @@ pub fn create(
311 .root = root,369 .root = root,
312 .invalid_user_input = false,370 .invalid_user_input = false,
313 .allocator = arena,371 .allocator = arena,
314 .user_input_options = UserInputOptionsMap.init(arena),372 .user_input_options = .empty,
315 .top_level_steps = .{},373 .top_level_steps = .{},
316 .default_step = undefined,374 .default_step = undefined,
317 .install_tls = .{375 .install_tls = .{
...@@ -348,7 +406,7 @@ fn createChild(...@@ -348,7 +406,7 @@ fn createChild(
348 root: Cache.Path,406 root: Cache.Path,
349 pkg_hash: []const u8,407 pkg_hash: []const u8,
350 pkg_deps: AvailableDeps,408 pkg_deps: AvailableDeps,
351 user_input_options: UserInputOptionsMap,409 user_input_options: PackageOptions.Map,
352) error{OutOfMemory}!*Build {410) error{OutOfMemory}!*Build {
353 const arena = parent.graph.arena;411 const arena = parent.graph.arena;
354 const child = try arena.create(Build);412 const child = try arena.create(Build);
...@@ -390,167 +448,96 @@ fn createChild(...@@ -390,167 +448,96 @@ fn createChild(
390 return child;448 return child;
391}449}
392450
393fn userInputOptionsFromArgs(arena: Allocator, args: anytype) UserInputOptionsMap {451fn addPackageOptionFromArg(
394 var map = UserInputOptionsMap.init(arena);
395 const args_info = @typeInfo(@TypeOf(args)).@"struct";
396 inline for (args_info.field_names, args_info.field_types) |field_name, field_type| {
397 if (field_type == @TypeOf(null)) continue;
398 addUserInputOptionFromArg(arena, &map, field_name, field_type, @field(args, field_name));
399 }
400 return map;
401}
402
403fn addUserInputOptionFromArg(
404 arena: Allocator,452 arena: Allocator,
405 map: *UserInputOptionsMap,453 map: *PackageOptions.Map,
406 field_name: [:0]const u8,454 field_name: [:0]const u8,
407 comptime T: type,455 comptime T: type,
408 /// If null, the value won't be added, but `T` will still be type-checked.456 /// If null, the value won't be added, but `T` will still be type-checked.
409 maybe_value: ?T,457 maybe_value: ?T,
410) void {458) void {
459 map.ensureUnusedCapacity(arena, 2) catch @panic("OOM");
411 switch (T) {460 switch (T) {
412 Target.Query => return if (maybe_value) |v| {461 Target.Query => return if (maybe_value) |v| {
413 map.put(field_name, .{462 map.putAssumeCapacity(field_name, .{ .scalar = v.zigTriple(arena) catch @panic("OOM") });
414 .name = field_name,463 map.putAssumeCapacity("cpu", .{ .scalar = v.serializeCpuAlloc(arena) catch @panic("OOM") });
415 .value = .{ .scalar = v.zigTriple(arena) catch @panic("OOM") },
416 .used = false,
417 }) catch @panic("OOM");
418 map.put("cpu", .{
419 .name = "cpu",
420 .value = .{ .scalar = v.serializeCpuAlloc(arena) catch @panic("OOM") },
421 .used = false,
422 }) catch @panic("OOM");
423 },464 },
424 ResolvedTarget => return if (maybe_value) |v| {465 ResolvedTarget => return if (maybe_value) |v| {
425 map.put(field_name, .{466 map.putAssumeCapacity(field_name, .{ .scalar = v.query.zigTriple(arena) catch @panic("OOM") });
426 .name = field_name,467 map.putAssumeCapacity("cpu", .{ .scalar = v.query.serializeCpuAlloc(arena) catch @panic("OOM") });
427 .value = .{ .scalar = v.query.zigTriple(arena) catch @panic("OOM") },
428 .used = false,
429 }) catch @panic("OOM");
430 map.put("cpu", .{
431 .name = "cpu",
432 .value = .{ .scalar = v.query.serializeCpuAlloc(arena) catch @panic("OOM") },
433 .used = false,
434 }) catch @panic("OOM");
435 },468 },
436 std.zig.BuildId => return if (maybe_value) |v| {469 std.zig.BuildId => return if (maybe_value) |v| {
437 map.put(field_name, .{470 map.putAssumeCapacity(field_name, .{
438 .name = field_name,471 .scalar = std.fmt.allocPrint(arena, "{f}", .{v}) catch @panic("OOM"),
439 .value = .{ .scalar = std.fmt.allocPrint(arena, "{f}", .{v}) catch @panic("OOM") },472 });
440 .used = false,
441 }) catch @panic("OOM");
442 },473 },
443 LazyPath => return if (maybe_value) |v| {474 LazyPath => return if (maybe_value) |v| {
444 map.put(field_name, .{475 map.putAssumeCapacity(field_name, .{ .lazy_path = v.dupeInner(arena) });
445 .name = field_name,
446 .value = .{ .lazy_path = v.dupeInner(arena) },
447 .used = false,
448 }) catch @panic("OOM");
449 },476 },
450 []const LazyPath => return if (maybe_value) |v| {477 []const LazyPath => return if (maybe_value) |v| {
451 var list = std.array_list.Managed(LazyPath).initCapacity(arena, v.len) catch @panic("OOM");478 var list: std.ArrayList(LazyPath) = .empty;
452 for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(arena));479 const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM");
453 map.put(field_name, .{480 for (v, elems) |lp, *elem| elem.* = lp.dupeInner(arena);
454 .name = field_name,481 map.putAssumeCapacity(field_name, .{ .lazy_path_list = list });
455 .value = .{ .lazy_path_list = list },
456 .used = false,
457 }) catch @panic("OOM");
458 },482 },
459 []const u8 => return if (maybe_value) |v| {483 []const u8 => return if (maybe_value) |v| {
460 map.put(field_name, .{484 map.putAssumeCapacity(field_name, .{ .scalar = arena.dupe(u8, v) catch @panic("OOM") });
461 .name = field_name,
462 .value = .{ .scalar = arena.dupe(u8, v) catch @panic("OOM") },
463 .used = false,
464 }) catch @panic("OOM");
465 },485 },
466 []const []const u8 => return if (maybe_value) |v| {486 []const []const u8 => return if (maybe_value) |v| {
467 var list = std.array_list.Managed([]const u8).initCapacity(arena, v.len) catch @panic("OOM");487 var list: std.ArrayList([]const u8) = .empty;
468 for (v) |s| list.appendAssumeCapacity(arena.dupe(u8, s) catch @panic("OOM"));488 const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM");
469 map.put(field_name, .{489 for (v, elems) |s, *elem| elem.* = arena.dupe(u8, s) catch @panic("OOM");
470 .name = field_name,490 map.putAssumeCapacity(field_name, .{ .list = list });
471 .value = .{ .list = list },
472 .used = false,
473 }) catch @panic("OOM");
474 },491 },
475 else => switch (@typeInfo(T)) {492 else => switch (@typeInfo(T)) {
476 .bool => return if (maybe_value) |v| {493 .bool => return if (maybe_value) |v| {
477 map.put(field_name, .{494 map.putAssumeCapacity(field_name, .{ .scalar = if (v) "true" else "false" });
478 .name = field_name,
479 .value = .{ .scalar = if (v) "true" else "false" },
480 .used = false,
481 }) catch @panic("OOM");
482 },495 },
483 .@"enum", .enum_literal => return if (maybe_value) |v| {496 .@"enum", .enum_literal => return if (maybe_value) |v| {
484 map.put(field_name, .{497 map.putAssumeCapacity(field_name, .{ .scalar = @tagName(v) });
485 .name = field_name,
486 .value = .{ .scalar = @tagName(v) },
487 .used = false,
488 }) catch @panic("OOM");
489 },498 },
490 .comptime_int, .int => return if (maybe_value) |v| {499 .comptime_int, .int => return if (maybe_value) |v| {
491 map.put(field_name, .{500 map.putAssumeCapacity(field_name, .{
492 .name = field_name,501 .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM"),
493 .value = .{ .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM") },502 });
494 .used = false,
495 }) catch @panic("OOM");
496 },503 },
497 .comptime_float, .float => return if (maybe_value) |v| {504 .comptime_float, .float => return if (maybe_value) |v| {
498 map.put(field_name, .{505 map.putAssumeCapacity(field_name, .{
499 .name = field_name,506 .scalar = std.fmt.allocPrint(arena, "{x}", .{v}) catch @panic("OOM"),
500 .value = .{ .scalar = std.fmt.allocPrint(arena, "{x}", .{v}) catch @panic("OOM") },507 });
501 .used = false,
502 }) catch @panic("OOM");
503 },508 },
504 .pointer => |ptr_info| switch (ptr_info.size) {509 .pointer => |ptr_info| switch (ptr_info.size) {
505 .one => switch (@typeInfo(ptr_info.child)) {510 .one => switch (@typeInfo(ptr_info.child)) {
506 .array => |array_info| {511 .array => |array_info| return addPackageOptionFromArg(
507 addUserInputOptionFromArg(512 arena,
508 arena,513 map,
509 map,514 field_name,
510 field_name,515 @Pointer(.slice, .{ .@"const" = true }, array_info.child, null),
511 @Pointer(.slice, .{ .@"const" = true }, array_info.child, null),516 maybe_value orelse null,
512 maybe_value orelse null,517 ),
513 );
514 return;
515 },
516 else => {},518 else => {},
517 },519 },
518 .slice => switch (@typeInfo(ptr_info.child)) {520 .slice => switch (@typeInfo(ptr_info.child)) {
519 .@"enum" => return if (maybe_value) |v| {521 .@"enum" => return if (maybe_value) |v| {
520 var list = std.array_list.Managed([]const u8).initCapacity(arena, v.len) catch @panic("OOM");522 var list: std.ArrayList([]const u8) = .empty;
521 for (v) |tag| list.appendAssumeCapacity(@tagName(tag));523 const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM");
522 map.put(field_name, .{524 for (elems, v) |*elem, tag| elem.* = @tagName(tag);
523 .name = field_name,525 map.putAssumeCapacity(field_name, .{ .list = list });
524 .value = .{ .list = list },
525 .used = false,
526 }) catch @panic("OOM");
527 },
528 else => {
529 addUserInputOptionFromArg(
530 arena,
531 map,
532 field_name,
533 @Pointer(ptr_info.size, .{ .@"const" = true }, ptr_info.child, null),
534 maybe_value orelse null,
535 );
536 return;
537 },526 },
527 else => return addPackageOptionFromArg(
528 arena,
529 map,
530 field_name,
531 @Pointer(ptr_info.size, .{ .@"const" = true }, ptr_info.child, null),
532 maybe_value orelse null,
533 ),
538 },534 },
539 else => {},535 else => {},
540 },536 },
541 .null => unreachable,537 .null => unreachable,
542 .optional => |info| switch (@typeInfo(info.child)) {538 .optional => |info| switch (@typeInfo(info.child)) {
543 .optional => {},539 .optional => {},
544 else => {540 else => return addPackageOptionFromArg(arena, map, field_name, info.child, maybe_value orelse null),
545 addUserInputOptionFromArg(
546 arena,
547 map,
548 field_name,
549 info.child,
550 maybe_value orelse null,
551 );
552 return;
553 },
554 },541 },
555 else => {},542 else => {},
556 },543 },
...@@ -558,130 +545,6 @@ fn addUserInputOptionFromArg(...@@ -558,130 +545,6 @@ fn addUserInputOptionFromArg(
558 @compileError("option '" ++ field_name ++ "' has unsupported type: " ++ @typeName(T));545 @compileError("option '" ++ field_name ++ "' has unsupported type: " ++ @typeName(T));
559}546}
560547
561const OrderedUserValue = union(enum) {
562 flag: void,
563 scalar: []const u8,
564 list: std.array_list.Managed([]const u8),
565 map: std.array_list.Managed(Pair),
566 lazy_path: LazyPath,
567 lazy_path_list: std.array_list.Managed(LazyPath),
568
569 const Pair = struct {
570 name: []const u8,
571 value: OrderedUserValue,
572 fn lessThan(_: void, lhs: Pair, rhs: Pair) bool {
573 return std.ascii.lessThanIgnoreCase(lhs.name, rhs.name);
574 }
575 };
576
577 fn hash(val: OrderedUserValue, hasher: *std.hash.Wyhash) void {
578 hasher.update(&std.mem.toBytes(std.meta.activeTag(val)));
579 switch (val) {
580 .flag => {},
581 .scalar => |scalar| hasher.update(scalar),
582 // lists are already ordered
583 .list => |list| for (list.items) |list_entry|
584 hasher.update(list_entry),
585 .map => |map| for (map.items) |map_entry| {
586 hasher.update(map_entry.name);
587 map_entry.value.hash(hasher);
588 },
589 .lazy_path => |lp| hashLazyPath(lp, hasher),
590 .lazy_path_list => |lp_list| for (lp_list.items) |lp| {
591 hashLazyPath(lp, hasher);
592 },
593 }
594 }
595
596 fn hashLazyPath(lp: LazyPath, hasher: *std.hash.Wyhash) void {
597 switch (lp) {
598 .src_path => |sp| {
599 hasher.update(sp.owner.pkg_hash);
600 hasher.update(sp.sub_path);
601 },
602 .generated => |gen| {
603 hasher.update(@ptrCast(&gen.index));
604 hasher.update(@ptrCast(&gen.up));
605 hasher.update(gen.sub_path);
606 },
607 .cwd_relative => |rel_path| {
608 hasher.update(rel_path);
609 },
610 .relative => |r| {
611 hasher.update(@ptrCast(&r.base));
612 hasher.update(@ptrCast(&r.sub_path));
613 },
614 .dependency => |dep| {
615 hasher.update(dep.dependency.builder.pkg_hash);
616 hasher.update(dep.sub_path);
617 },
618 }
619 }
620
621 fn mapFromUnordered(allocator: Allocator, unordered: std.StringHashMap(*const UserValue)) std.array_list.Managed(Pair) {
622 var ordered = std.array_list.Managed(Pair).init(allocator);
623 var it = unordered.iterator();
624 while (it.next()) |entry| {
625 ordered.append(.{
626 .name = entry.key_ptr.*,
627 .value = OrderedUserValue.fromUnordered(allocator, entry.value_ptr.*.*),
628 }) catch @panic("OOM");
629 }
630
631 std.mem.sortUnstable(Pair, ordered.items, {}, Pair.lessThan);
632 return ordered;
633 }
634
635 fn fromUnordered(allocator: Allocator, unordered: UserValue) OrderedUserValue {
636 return switch (unordered) {
637 .flag => .{ .flag = {} },
638 .scalar => |scalar| .{ .scalar = scalar },
639 .list => |list| .{ .list = list },
640 .map => |map| .{ .map = OrderedUserValue.mapFromUnordered(allocator, map) },
641 .lazy_path => |lp| .{ .lazy_path = lp },
642 .lazy_path_list => |list| .{ .lazy_path_list = list },
643 };
644 }
645};
646
647const OrderedUserInputOption = struct {
648 name: []const u8,
649 value: OrderedUserValue,
650 used: bool,
651
652 fn hash(opt: OrderedUserInputOption, hasher: *std.hash.Wyhash) void {
653 hasher.update(opt.name);
654 opt.value.hash(hasher);
655 }
656
657 fn fromUnordered(allocator: Allocator, user_input_option: UserInputOption) OrderedUserInputOption {
658 return OrderedUserInputOption{
659 .name = user_input_option.name,
660 .used = user_input_option.used,
661 .value = OrderedUserValue.fromUnordered(allocator, user_input_option.value),
662 };
663 }
664
665 fn lessThan(_: void, lhs: OrderedUserInputOption, rhs: OrderedUserInputOption) bool {
666 return std.ascii.lessThanIgnoreCase(lhs.name, rhs.name);
667 }
668};
669
670// The hash should be consistent with the same values given a different order.
671// This function takes a user input map, orders it, then hashes the contents.
672fn hashUserInputOptionsMap(allocator: Allocator, user_input_options: UserInputOptionsMap, hasher: *std.hash.Wyhash) void {
673 var ordered = std.array_list.Managed(OrderedUserInputOption).init(allocator);
674 var it = user_input_options.iterator();
675 while (it.next()) |entry|
676 ordered.append(OrderedUserInputOption.fromUnordered(allocator, entry.value_ptr.*)) catch @panic("OOM");
677
678 std.mem.sortUnstable(OrderedUserInputOption, ordered.items, {}, OrderedUserInputOption.lessThan);
679
680 // juice it
681 for (ordered.items) |user_option|
682 user_option.hash(hasher);
683}
684
685/// Create a set of key-value pairs that can be converted into a Zig source548/// Create a set of key-value pairs that can be converted into a Zig source
686/// file and then inserted into a Zig compilation's module table for importing.549/// file and then inserted into a Zig compilation's module table for importing.
687///550///
...@@ -1107,31 +970,20 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1107,31 +970,20 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1107 const name = graph.dupeString(name_raw);970 const name = graph.dupeString(name_raw);
1108 const description = graph.dupeString(description_raw);971 const description = graph.dupeString(description_raw);
1109 const type_id = comptime typeToEnum(T);972 const type_id = comptime typeToEnum(T);
1110 const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: {973 const available_option: AvailableOption = .{
1111 const EnumType = if (type_id == .enum_list) @typeInfo(T).pointer.child else T;
1112 const field_names = @typeInfo(EnumType).@"enum".field_names;
1113 var options = std.array_list.Managed([]const u8).initCapacity(b.allocator, field_names.len) catch @panic("OOM");
1114
1115 inline for (field_names) |field_name| {
1116 options.appendAssumeCapacity(field_name);
1117 }
1118
1119 break :blk options.toOwnedSlice() catch @panic("OOM");
1120 } else null;
1121 const available_option = AvailableOption{
1122 .name = name,
1123 .type_id = type_id,974 .type_id = type_id,
1124 .description = description,975 .description = description,
1125 .enum_options = enum_options,976 .enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: {
977 const E = if (type_id == .enum_list) @typeInfo(T).pointer.child else T;
978 break :blk @typeInfo(E).@"enum".field_names;
979 } else null,
1126 };980 };
1127 if ((b.available_options_map.fetchPut(arena, name, available_option) catch @panic("OOM")) != null) {981 if ((b.available_options_map.fetchPut(arena, name, available_option) catch @panic("OOM")) != null) {
1128 panic("option {q} declared twice", .{name});982 panic("option {q} declared twice", .{name});
1129 }983 }
1130984 const user_provided = b.user_input_options.get(name) orelse return null;
1131 const option_ptr = b.user_input_options.getPtr(name) orelse return null;
1132 option_ptr.used = true;
1133 switch (type_id) {985 switch (type_id) {
1134 .bool => switch (option_ptr.value) {986 .bool => switch (user_provided) {
1135 .flag => return true,987 .flag => return true,
1136 .scalar => |s| {988 .scalar => |s| {
1137 if (mem.eql(u8, s, "true")) {989 if (mem.eql(u8, s, "true")) {
...@@ -1145,14 +997,14 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1145,14 +997,14 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1145 }997 }
1146 },998 },
1147 .list, .map, .lazy_path, .lazy_path_list => {999 .list, .map, .lazy_path, .lazy_path_list => {
1148 log.err("expected -D{s} to be a boolean; received: {t}", .{ name, option_ptr.value });1000 log.err("expected -D{s} to be a boolean; received: {t}", .{ name, user_provided });
1149 b.markInvalidUserInput();1001 b.markInvalidUserInput();
1150 return null;1002 return null;
1151 },1003 },
1152 },1004 },
1153 .int => switch (option_ptr.value) {1005 .int => switch (user_provided) {
1154 .flag, .list, .map, .lazy_path, .lazy_path_list => {1006 .flag, .list, .map, .lazy_path, .lazy_path_list => {
1155 log.err("expected -D{s} to be an integer; received: {t}", .{ name, option_ptr.value });1007 log.err("expected -D{s} to be an integer; received: {t}", .{ name, user_provided });
1156 b.markInvalidUserInput();1008 b.markInvalidUserInput();
1157 return null;1009 return null;
1158 },1010 },
...@@ -1172,9 +1024,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1172,9 +1024,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1172 return n;1024 return n;
1173 },1025 },
1174 },1026 },
1175 .float => switch (option_ptr.value) {1027 .float => switch (user_provided) {
1176 .flag, .map, .list, .lazy_path, .lazy_path_list => {1028 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1177 log.err("expected -D{s} to be a float; received: {t}", .{ name, option_ptr.value });1029 log.err("expected -D{s} to be a float; received: {t}", .{ name, user_provided });
1178 b.markInvalidUserInput();1030 b.markInvalidUserInput();
1179 return null;1031 return null;
1180 },1032 },
...@@ -1187,9 +1039,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1187,9 +1039,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1187 return n;1039 return n;
1188 },1040 },
1189 },1041 },
1190 .@"enum" => switch (option_ptr.value) {1042 .@"enum" => switch (user_provided) {
1191 .flag, .map, .list, .lazy_path, .lazy_path_list => {1043 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1192 log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value });1044 log.err("expected -D{s} to be an enum; received: {t}.", .{ name, user_provided });
1193 b.markInvalidUserInput();1045 b.markInvalidUserInput();
1194 return null;1046 return null;
1195 },1047 },
...@@ -1206,17 +1058,17 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1206,17 +1058,17 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1206 return null;1058 return null;
1207 },1059 },
1208 },1060 },
1209 .string => switch (option_ptr.value) {1061 .string => switch (user_provided) {
1210 .flag, .list, .map, .lazy_path, .lazy_path_list => {1062 .flag, .list, .map, .lazy_path, .lazy_path_list => {
1211 log.err("expected -D{s} to be a string; received: {t}", .{ name, option_ptr.value });1063 log.err("expected -D{s} to be a string; received: {t}", .{ name, user_provided });
1212 b.markInvalidUserInput();1064 b.markInvalidUserInput();
1213 return null;1065 return null;
1214 },1066 },
1215 .scalar => |s| return s,1067 .scalar => |s| return s,
1216 },1068 },
1217 .build_id => switch (option_ptr.value) {1069 .build_id => switch (user_provided) {
1218 .flag, .map, .list, .lazy_path, .lazy_path_list => {1070 .flag, .map, .list, .lazy_path, .lazy_path_list => {
1219 log.err("expected -D{s} to be an enum; received: {t}.", .{ name, option_ptr.value });1071 log.err("expected -D{s} to be an enum; received: {t}.", .{ name, user_provided });
1220 b.markInvalidUserInput();1072 b.markInvalidUserInput();
1221 return null;1073 return null;
1222 },1074 },
...@@ -1230,9 +1082,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1230,9 +1082,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1230 }1082 }
1231 },1083 },
1232 },1084 },
1233 .list => switch (option_ptr.value) {1085 .list => switch (user_provided) {
1234 .flag, .map, .lazy_path, .lazy_path_list => {1086 .flag, .map, .lazy_path, .lazy_path_list => {
1235 log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value });1087 log.err("expected -D{s} to be a list; received: {t}", .{ name, user_provided });
1236 b.markInvalidUserInput();1088 b.markInvalidUserInput();
1237 return null;1089 return null;
1238 },1090 },
...@@ -1241,9 +1093,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1241,9 +1093,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1241 },1093 },
1242 .list => |lst| return lst.items,1094 .list => |lst| return lst.items,
1243 },1095 },
1244 .enum_list => switch (option_ptr.value) {1096 .enum_list => switch (user_provided) {
1245 .flag, .map, .lazy_path, .lazy_path_list => {1097 .flag, .map, .lazy_path, .lazy_path_list => {
1246 log.err("expected -D{s} to be a list; received: {t}", .{ name, option_ptr.value });1098 log.err("expected -D{s} to be a list; received: {t}", .{ name, user_provided });
1247 b.markInvalidUserInput();1099 b.markInvalidUserInput();
1248 return null;1100 return null;
1249 },1101 },
...@@ -1283,16 +1135,16 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1283,16 +1135,16 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1283 return new_list;1135 return new_list;
1284 },1136 },
1285 },1137 },
1286 .lazy_path => switch (option_ptr.value) {1138 .lazy_path => switch (user_provided) {
1287 .scalar => |s| return .{ .cwd_relative = s },1139 .scalar => |s| return .{ .cwd_relative = s },
1288 .lazy_path => |lp| return lp,1140 .lazy_path => |lp| return lp,
1289 .flag, .map, .list, .lazy_path_list => {1141 .flag, .map, .list, .lazy_path_list => {
1290 log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value });1142 log.err("expected -D{s} to be a path; received: {t}", .{ name, user_provided });
1291 b.markInvalidUserInput();1143 b.markInvalidUserInput();
1292 return null;1144 return null;
1293 },1145 },
1294 },1146 },
1295 .lazy_path_list => switch (option_ptr.value) {1147 .lazy_path_list => switch (user_provided) {
1296 .scalar => |s| return arena.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"),1148 .scalar => |s| return arena.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"),
1297 .lazy_path => |lp| return arena.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"),1149 .lazy_path => |lp| return arena.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"),
1298 .list => |lst| {1150 .list => |lst| {
...@@ -1304,7 +1156,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw...@@ -1304,7 +1156,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1304 },1156 },
1305 .lazy_path_list => |lp_list| return lp_list.items,1157 .lazy_path_list => |lp_list| return lp_list.items,
1306 .flag, .map => {1158 .flag, .map => {
1307 log.err("expected -D{s} to be a path; received: {t}", .{ name, option_ptr.value });1159 log.err("expected -D{s} to be a path; received: {t}", .{ name, user_provided });
1308 b.markInvalidUserInput();1160 b.markInvalidUserInput();
1309 return null;1161 return null;
1310 },1162 },
...@@ -1497,88 +1349,63 @@ pub fn standardTargetOptionsQueryOnly(b: *Build, args: StandardTargetOptionsArgs...@@ -1497,88 +1349,63 @@ pub fn standardTargetOptionsQueryOnly(b: *Build, args: StandardTargetOptionsArgs
1497}1349}
14981350
1499/// Build system implementation detail.1351/// Build system implementation detail.
1500pub fn addUserInputOption(b: *Build, name_raw: []const u8, value_raw: []const u8) error{OutOfMemory}!bool {1352pub fn addUserInputOption(b: *Build, name: []const u8, value_raw: []const u8) error{OutOfMemory}!bool {
1501 const graph = b.graph;1353 const graph = b.graph;
1502 const arena = graph.arena;1354 const arena = graph.arena;
1503 const name = graph.dupeString(name_raw);
1504 const value = graph.dupeString(value_raw);1355 const value = graph.dupeString(value_raw);
1505 const gop = try b.user_input_options.getOrPut(name);1356 const gop = try b.user_input_options.getOrPut(arena, name);
1357
1506 if (!gop.found_existing) {1358 if (!gop.found_existing) {
1507 gop.value_ptr.* = UserInputOption{1359 gop.key_ptr.* = graph.dupeString(name);
1508 .name = name,1360 gop.value_ptr.* = .{ .scalar = value };
1509 .value = .{ .scalar = value },
1510 .used = false,
1511 };
1512 return false;1361 return false;
1513 }1362 }
15141363
1515 // option already exists1364 // Option already exists.
1516 switch (gop.value_ptr.value) {1365 switch (gop.value_ptr.*) {
1517 .scalar => |s| {1366 .scalar => |s| {
1518 // turn it into a list1367 // Turn it into a list.
1519 var list = std.array_list.Managed([]const u8).init(arena);1368 var list: std.ArrayList([]const u8) = .empty;
1520 try list.append(s);1369 (try list.addManyAsArray(arena, 2)).* = .{ s, value };
1521 try list.append(value);1370 gop.value_ptr.* = .{ .list = list };
1522 try b.user_input_options.put(name, .{
1523 .name = name,
1524 .value = .{ .list = list },
1525 .used = false,
1526 });
1527 },
1528 .list => |*list| {
1529 // append to the list
1530 try list.append(value);
1531 try b.user_input_options.put(name, .{
1532 .name = name,
1533 .value = .{ .list = list.* },
1534 .used = false,
1535 });
1536 },1371 },
1372 .list => |*list| try list.append(arena, value),
1537 .flag => {1373 .flag => {
1538 log.warn("option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });1374 log.err("option -D{s}={s} conflicts with flag -D{s}", .{ name, value, name });
1539 return true;1375 return true;
1540 },1376 },
1541 .map => |*map| {1377 .map => |*map| {
1542 _ = map;1378 _ = map;
1543 log.warn("TODO maps as command line arguments is not implemented yet.", .{});1379 unreachable; // TODO implement maps as command line arguments
1544 return true;
1545 },
1546 .lazy_path, .lazy_path_list => {
1547 log.warn("the lazy path value type isn't added from the CLI, but somehow {q} is a .{f}", .{
1548 name, std.zig.fmtId(@tagName(gop.value_ptr.value)),
1549 });
1550 return true;
1551 },1380 },
1381 .lazy_path => unreachable,
1382 .lazy_path_list => unreachable,
1552 }1383 }
1553 return false;1384 return false;
1554}1385}
15551386
1556/// Build system implementation detail.1387/// Build system implementation detail.
1557pub fn addUserInputFlag(b: *Build, name_raw: []const u8) error{OutOfMemory}!bool {1388pub fn addUserInputFlag(b: *Build, name: []const u8) error{OutOfMemory}!bool {
1558 const graph = b.graph;1389 const graph = b.graph;
1559 const name = graph.dupeString(name_raw);1390 const arena = graph.arena;
1560 const gop = try b.user_input_options.getOrPut(name);1391 const gop = try b.user_input_options.getOrPut(arena, name);
1561 if (!gop.found_existing) {1392 if (!gop.found_existing) {
1562 gop.value_ptr.* = .{1393 gop.key_ptr.* = graph.dupeString(name);
1563 .name = name,1394 gop.value_ptr.* = .{ .flag = {} };
1564 .value = .{ .flag = {} },
1565 .used = false,
1566 };
1567 return false;1395 return false;
1568 }1396 }
15691397 // Option already exists.
1570 // option already exists1398 switch (gop.value_ptr.*) {
1571 switch (gop.value_ptr.value) {
1572 .scalar => |s| {1399 .scalar => |s| {
1573 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });1400 log.err("flag -D{s} conflicts with option -D{s}={s}", .{ name, name, s });
1574 return true;1401 return true;
1575 },1402 },
1576 .list, .map, .lazy_path_list => {1403 .list, .map, .lazy_path_list => {
1577 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});1404 log.err("flag -D{s} conflicts with multiple options of the same name", .{name});
1578 return true;1405 return true;
1579 },1406 },
1580 .lazy_path => |lp| {1407 .lazy_path => |lp| {
1581 log.err("Flag '-D{s}' conflicts with option '-D{s}={f}'.", .{ name, name, lp });1408 log.err("flag -D{s} conflicts with option -D{s}={f}", .{ name, name, lp });
1582 return true;1409 return true;
1583 },1410 },
15841411
...@@ -1614,17 +1441,16 @@ fn markInvalidUserInput(b: *Build) void {...@@ -1614,17 +1441,16 @@ fn markInvalidUserInput(b: *Build) void {
1614 b.invalid_user_input = true;1441 b.invalid_user_input = true;
1615}1442}
16161443
1617/// Build system implementation detail.1444fn validateUserInputDidItFail(b: *Build) bool {
1618pub fn validateUserInputDidItFail(b: *Build) bool {1445 for (b.user_input_options.keys()) |name| {
1619 // Make sure all args are used.1446 if (!b.available_options_map.contains(name)) {
1620 var it = b.user_input_options.iterator();1447 for (b.available_options_map.keys(), b.available_options_map.values()) |available_name, *available| {
1621 while (it.next()) |entry| {1448 log.info("available option: {q}: {s}", .{ available_name, available.description });
1622 if (!entry.value_ptr.used) {1449 }
1623 log.err("invalid option: -D{s}", .{entry.key_ptr.*});1450 log.err("invalid option: {q}", .{name});
1624 b.markInvalidUserInput();1451 b.markInvalidUserInput();
1625 }1452 }
1626 }1453 }
1627
1628 return b.invalid_user_input;1454 return b.invalid_user_input;
1629}1455}
16301456
...@@ -2092,14 +1918,14 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c...@@ -2092,14 +1918,14 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c
2092 const pkg = @field(deps.packages, pkg_hash);1918 const pkg = @field(deps.packages, pkg_hash);
2093 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };1919 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };
2094 } else .{ "", deps.root_deps };1920 } else .{ "", deps.root_deps };
2095 if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) {1921 if (!mem.eql(u8, b_pkg_hash, b.pkg_hash)) {
2096 const build_zig_path = b.root.join(arena, "build.zig") catch @panic("OOM");1922 const build_zig_path = b.root.join(arena, "build.zig") catch @panic("OOM");
2097 panic("{} is not the struct that corresponds to {f}", .{1923 panic("{} is not the struct that corresponds to {f}", .{
2098 asking_build_zig, build_zig_path,1924 asking_build_zig, build_zig_path,
2099 });1925 });
2100 }1926 }
2101 comptime for (b_pkg_deps) |dep| {1927 comptime for (b_pkg_deps) |dep| {
2102 if (std.mem.eql(u8, dep[0], dep_name)) return dep[1];1928 if (mem.eql(u8, dep[0], dep_name)) return dep[1];
2103 };1929 };
21041930
2105 const full_path = b.root.join(arena, "build.zig.zon") catch @panic("OOM");1931 const full_path = b.root.join(arena, "build.zig.zon") catch @panic("OOM");
...@@ -2141,7 +1967,9 @@ pub fn dependencyLazy(b: *Build, name: []const u8, args: anytype) error{LazyDepe...@@ -2141,7 +1967,9 @@ pub fn dependencyLazy(b: *Build, name: []const u8, args: anytype) error{LazyDepe
2141 markNeededLazyDep(b, pkg_hash);1967 markNeededLazyDep(b, pkg_hash);
2142 return error.LazyDependencyNeeded;1968 return error.LazyDependencyNeeded;
2143 }1969 }
2144 return dependencyResolved(b, name, entry, userInputOptionsFromArgs(b.graph.arena, args));1970 var map: PackageOptions.Map = .empty;
1971 PackageOptions.fromArgs(b.graph.arena, &map, args);
1972 return dependencyResolved(b, name, entry, &map);
2145}1973}
21461974
2147pub const PackageEntry = struct {1975pub const PackageEntry = struct {
...@@ -2232,12 +2060,14 @@ pub inline fn lazyImport(...@@ -2232,12 +2060,14 @@ pub inline fn lazyImport(
2232 comptime unreachable; // Bad @dependencies source2060 comptime unreachable; // Bad @dependencies source
2233}2061}
22342062
2235fn pkgHashFromBuildZig(comptime build_zig: type) ?[]const u8 {2063inline fn pkgHashFromBuildZig(comptime build_zig: type) ?[]const u8 {
2236 const deps = @import("root").dependencies;2064 comptime {
2237 return comptime for (@typeInfo(deps.packages).@"struct".decl_names) |pkg_hash| {2065 const deps = @import("root").dependencies;
2238 const pkg = @field(deps.packages, pkg_hash);2066 return for (@typeInfo(deps.packages).@"struct".decl_names) |pkg_hash| {
2239 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == build_zig) break pkg_hash;2067 const pkg = @field(deps.packages, pkg_hash);
2240 } else null;2068 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == build_zig) break pkg_hash;
2069 } else null;
2070 }
2241}2071}
22422072
2243/// Build system implementation detail.2073/// Build system implementation detail.
...@@ -2251,114 +2081,37 @@ pub fn dependencyFromBuildZig(...@@ -2251,114 +2081,37 @@ pub fn dependencyFromBuildZig(
2251 const arena = b.graph.arena;2081 const arena = b.graph.arena;
22522082
2253 find_dep: {2083 find_dep: {
2254 const pkg_hash = comptime pkgHashFromBuildZig(build_zig) orelse break :find_dep;2084 const pkg_hash = pkgHashFromBuildZig(build_zig) orelse break :find_dep;
2255 const dep_name = for (b.available_deps) |dep| {2085 const dep_name = for (b.available_deps) |dep| {
2256 if (mem.eql(u8, dep[1], pkg_hash)) break dep[1];2086 if (mem.eql(u8, dep[1], pkg_hash)) break dep[1];
2257 } else break :find_dep;2087 } else break :find_dep;
2258 const entry = package_map.get(pkg_hash) orelse break :find_dep;2088 const entry = package_map.get(pkg_hash) orelse break :find_dep;
2259 return dependencyResolved(b, dep_name, entry, userInputOptionsFromArgs(arena, args));2089 var map: PackageOptions.Map = .empty;
2090 PackageOptions.fromArgs(arena, &map, args);
2091 return dependencyResolved(b, dep_name, entry, &map);
2260 }2092 }
22612093
2262 const full_path = b.root.join(arena, "build.zig.zon") catch @panic("OOM");2094 const full_path = b.root.join(arena, "build.zig.zon") catch @panic("OOM");
2263 panic("{} is not a build.zig struct of a dependency in {f}", .{ build_zig, full_path });2095 panic("{} is not a build.zig struct of a dependency in {f}", .{ build_zig, full_path });
2264}2096}
22652097
2266fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool {2098/// Takes ownership of `package_options`, which may be unsorted.
2267 if (std.meta.activeTag(lhs) != rhs) return false;
2268 switch (lhs) {
2269 .flag => {},
2270 .scalar => |lhs_scalar| {
2271 const rhs_scalar = rhs.scalar;
2272
2273 if (!std.mem.eql(u8, lhs_scalar, rhs_scalar))
2274 return false;
2275 },
2276 .list => |lhs_list| {
2277 const rhs_list = rhs.list;
2278
2279 if (lhs_list.items.len != rhs_list.items.len)
2280 return false;
2281
2282 for (lhs_list.items, rhs_list.items) |lhs_list_entry, rhs_list_entry| {
2283 if (!std.mem.eql(u8, lhs_list_entry, rhs_list_entry))
2284 return false;
2285 }
2286 },
2287 .map => |lhs_map| {
2288 const rhs_map = rhs.map;
2289
2290 if (lhs_map.count() != rhs_map.count())
2291 return false;
2292
2293 var lhs_it = lhs_map.iterator();
2294 while (lhs_it.next()) |lhs_entry| {
2295 const rhs_value = rhs_map.get(lhs_entry.key_ptr.*) orelse return false;
2296 if (!userValuesAreSame(lhs_entry.value_ptr.*.*, rhs_value.*))
2297 return false;
2298 }
2299 },
2300 .lazy_path => |lhs_lp| {
2301 const rhs_lp = rhs.lazy_path;
2302 return userLazyPathsAreTheSame(lhs_lp, rhs_lp);
2303 },
2304 .lazy_path_list => |lhs_lp_list| {
2305 const rhs_lp_list = rhs.lazy_path_list;
2306 if (lhs_lp_list.items.len != rhs_lp_list.items.len) return false;
2307 for (lhs_lp_list.items, rhs_lp_list.items) |lhs_lp, rhs_lp| {
2308 if (!userLazyPathsAreTheSame(lhs_lp, rhs_lp)) return false;
2309 }
2310 return true;
2311 },
2312 }
2313
2314 return true;
2315}
2316
2317fn userLazyPathsAreTheSame(lhs_lp: LazyPath, rhs_lp: LazyPath) bool {
2318 if (std.meta.activeTag(lhs_lp) != rhs_lp) return false;
2319 switch (lhs_lp) {
2320 .src_path => |lhs_sp| {
2321 const rhs_sp = rhs_lp.src_path;
2322
2323 if (lhs_sp.owner != rhs_sp.owner) return false;
2324 if (std.mem.eql(u8, lhs_sp.sub_path, rhs_sp.sub_path)) return false;
2325 },
2326 .generated => |*lhs_gen| {
2327 const rhs_gen = &rhs_lp.generated;
2328
2329 if (lhs_gen.index != rhs_gen.index) return false;
2330 if (lhs_gen.up != rhs_gen.up) return false;
2331 if (std.mem.eql(u8, lhs_gen.sub_path, rhs_gen.sub_path)) return false;
2332 },
2333 .cwd_relative => |lhs_rel_path| {
2334 const rhs_rel_path = rhs_lp.cwd_relative;
2335
2336 if (!std.mem.eql(u8, lhs_rel_path, rhs_rel_path)) return false;
2337 },
2338 .relative => |lhs| return lhs.eql(rhs_lp.relative),
2339 .dependency => |lhs_dep| {
2340 const rhs_dep = rhs_lp.dependency;
2341
2342 if (lhs_dep.dependency != rhs_dep.dependency) return false;
2343 if (!std.mem.eql(u8, lhs_dep.sub_path, rhs_dep.sub_path)) return false;
2344 },
2345 }
2346 return true;
2347}
2348
2349fn dependencyResolved(2099fn dependencyResolved(
2350 b: *Build,2100 b: *Build,
2351 name: []const u8,2101 name: []const u8,
2352 entry: PackageEntry,2102 entry: PackageEntry,
2353 user_input_options: UserInputOptionsMap,2103 package_options: *PackageOptions.Map,
2354) *Dependency {2104) *Dependency {
2355 const graph = b.graph;2105 const graph = b.graph;
2356 const io = graph.io;2106 const io = graph.io;
2357 const arena = graph.arena;2107 const arena = graph.arena;
2108
2109 PackageOptions.sort(package_options);
2110
2358 if (graph.dependency_cache.getContext(.{2111 if (graph.dependency_cache.getContext(.{
2359 .build_root_string = entry.build_root,2112 .pkg_hash = entry.hash,
2360 .user_input_options = user_input_options,2113 .options = package_options,
2361 }, .{ .allocator = arena })) |dep| return dep;2114 }, .{})) |dep| return dep;
23622115
2363 const dep_root: Cache.Path = .{2116 const dep_root: Cache.Path = .{
2364 .root_dir = .{2117 .root_dir = .{
...@@ -2368,7 +2121,7 @@ fn dependencyResolved(...@@ -2368,7 +2121,7 @@ fn dependencyResolved(
2368 },2121 },
2369 };2122 };
23702123
2371 const sub_builder = b.createChild(name, dep_root, entry.hash, entry.deps, user_input_options) catch @panic("OOM");2124 const sub_builder = b.createChild(name, dep_root, entry.hash, entry.deps, package_options.*) catch @panic("OOM");
2372 if (entry.run_build) |run_build| {2125 if (entry.run_build) |run_build| {
2373 run_build(sub_builder);2126 run_build(sub_builder);
23742127
...@@ -2381,9 +2134,9 @@ fn dependencyResolved(...@@ -2381,9 +2134,9 @@ fn dependencyResolved(
2381 dep.* = .{ .builder = sub_builder };2134 dep.* = .{ .builder = sub_builder };
23822135
2383 graph.dependency_cache.putContext(arena, .{2136 graph.dependency_cache.putContext(arena, .{
2384 .build_root_string = entry.build_root,2137 .pkg_hash = entry.hash,
2385 .user_input_options = user_input_options,2138 .options = &sub_builder.user_input_options,
2386 }, dep, .{ .allocator = arena }) catch @panic("OOM");2139 }, dep, .{}) catch @panic("OOM");
2387 return dep;2140 return dep;
2388}2141}
23892142
...@@ -2650,6 +2403,59 @@ pub const LazyPath = union(enum) {...@@ -2650,6 +2403,59 @@ pub const LazyPath = union(enum) {
2650 } },2403 } },
2651 };2404 };
2652 }2405 }
2406
2407 fn eql(a: LazyPath, b: LazyPath) bool {
2408 if (std.meta.activeTag(a) != b) return false;
2409 switch (a) {
2410 .src_path => |a_sp| {
2411 const b_sp = b.src_path;
2412 if (a_sp.owner != b_sp.owner) return false;
2413 if (mem.eql(u8, a_sp.sub_path, b_sp.sub_path)) return false;
2414 },
2415 .generated => |*a_gen| {
2416 const b_gen = &b.generated;
2417 if (a_gen.index != b_gen.index) return false;
2418 if (a_gen.up != b_gen.up) return false;
2419 if (mem.eql(u8, a_gen.sub_path, b_gen.sub_path)) return false;
2420 },
2421 .cwd_relative => |a_rel_path| {
2422 const b_rel_path = b.cwd_relative;
2423 if (!mem.eql(u8, a_rel_path, b_rel_path)) return false;
2424 },
2425 .relative => |a_relative| return a_relative.eql(b.relative),
2426 .dependency => |a_dep| {
2427 const b_dep = b.dependency;
2428 if (a_dep.dependency != b_dep.dependency) return false;
2429 if (!mem.eql(u8, a_dep.sub_path, b_dep.sub_path)) return false;
2430 },
2431 }
2432 return true;
2433 }
2434
2435 fn hash(lp: LazyPath, hasher: *std.hash.Wyhash) void {
2436 switch (lp) {
2437 .src_path => |sp| {
2438 hasher.update(sp.owner.pkg_hash);
2439 hasher.update(sp.sub_path);
2440 },
2441 .generated => |gen| {
2442 hasher.update(@ptrCast(&gen.index));
2443 hasher.update(@ptrCast(&gen.up));
2444 hasher.update(gen.sub_path);
2445 },
2446 .cwd_relative => |rel_path| {
2447 hasher.update(rel_path);
2448 },
2449 .relative => |r| {
2450 hasher.update(@ptrCast(&r.base));
2451 hasher.update(@ptrCast(&r.sub_path));
2452 },
2453 .dependency => |dep| {
2454 hasher.update(dep.dependency.builder.pkg_hash);
2455 hasher.update(dep.sub_path);
2456 },
2457 }
2458 }
2653};2459};
26542460
2655fn dumpBadDirnameHelp(2461fn dumpBadDirnameHelp(
lib/std/Build/Configuration.zig+36-110
...@@ -22,7 +22,7 @@ packages: []Package,...@@ -22,7 +22,7 @@ packages: []Package,
22/// Unlike `packages`, each item corresponds to a `std.Build`, which is a22/// Unlike `packages`, each item corresponds to a `std.Build`, which is a
23/// package that was instantiated by running its build script with specific23/// package that was instantiated by running its build script with specific
24/// input options.24/// input options.
25package_instances: []PackageInstance,25package_instances: []Package.Instance,
26extra: []u32,26extra: []u32,
27default_step: Step.Index,27default_step: Step.Index,
28generated_files_len: u32,28generated_files_len: u32,
...@@ -69,7 +69,7 @@ pub const Wip = struct {...@@ -69,7 +69,7 @@ pub const Wip = struct {
69 path_deps: std.ArrayList(PathDep) = .empty,69 path_deps: std.ArrayList(PathDep) = .empty,
70 search_prefixes: std.ArrayList(String) = .empty,70 search_prefixes: std.ArrayList(String) = .empty,
71 packages: std.ArrayList(Package) = .empty,71 packages: std.ArrayList(Package) = .empty,
72 package_instances: std.ArrayList(PackageInstance) = .empty,72 package_instances: std.ArrayList(Package.Instance) = .empty,
73 extra: std.ArrayList(u32) = .empty,73 extra: std.ArrayList(u32) = .empty,
74 next_generated_file_index: u32 = 0,74 next_generated_file_index: u32 = 0,
75 cache_poison: bool = false,75 cache_poison: bool = false,
...@@ -494,7 +494,7 @@ pub const AvailableOption = extern struct {...@@ -494,7 +494,7 @@ pub const AvailableOption = extern struct {
494494
495pub const Step = extern struct {495pub const Step = extern struct {
496 name: String,496 name: String,
497 owner: PackageInstance.Index,497 owner: Package.Instance.Index,
498 deps: Deps.Index,498 deps: Deps.Index,
499 max_rss: MaxRss,499 max_rss: MaxRss,
500 extended: Storage.Extended(Flags, union(Tag) {500 extended: Storage.Extended(Flags, union(Tag) {
...@@ -1543,7 +1543,7 @@ pub const LazyPath = union(@This().Tag) {...@@ -1543,7 +1543,7 @@ pub const LazyPath = union(@This().Tag) {
15431543
1544 pub const SourcePath = struct {1544 pub const SourcePath = struct {
1545 flags: @This().Flags = .{},1545 flags: @This().Flags = .{},
1546 owner: PackageInstance.Index,1546 owner: Package.Instance.Index,
1547 sub_path: String,1547 sub_path: String,
15481548
1549 pub const Flags = packed struct(u32) {1549 pub const Flags = packed struct(u32) {
...@@ -1674,122 +1674,48 @@ pub const Package = extern struct {...@@ -1674,122 +1674,48 @@ pub const Package = extern struct {
1674 };1674 };
1675 };1675 };
1676 };1676 };
1677};
1678
1679pub const PackageInstance = extern struct {
1680 package: Package.Index,
1681 user_input_options: UserInputOption.List.Index,
1682 modules: PublicModules,
16831677
1684 pub const UserInputOption = struct {1678 pub const Instance = extern struct {
1685 flags: Flags,1679 package: Package.Index,
1686 name: String,1680 modules: PublicModules,
1687 value: Storage.FlagUnion(.flags, .tag, UserValue),
16881681
1689 pub const Flags = packed struct(u32) {1682 pub const PublicModules = extern struct {
1690 tag: UserValue.Tag,1683 keys: StringList,
1691 used: bool,1684 values: Module.List.Index,
1692 _: u28 = 0,
1693 };1685 };
16941686
1695 pub const List = struct {1687 pub const Index = enum(u32) {
1696 options: Storage.LengthPrefixedList(UserInputOption.Index),1688 root,
16971689 _,
1698 pub const Index = enum(u32) {
1699 _,
17001690
1701 pub fn get(this: @This(), c: *const Configuration) List {1691 pub fn ptr(this: @This(), c: *const Configuration) *const Package.Instance {
1702 return extraData(c, List, @backingInt(this));1692 return &c.package_instances[@backingInt(this)];
1703 }1693 }
17041694
1705 pub fn slice(this: @This(), c: *const Configuration) []const UserInputOption.Index {1695 pub fn package(this: @This(), c: *const Configuration) Package.Index {
1706 return this.get(c).options.slice;1696 return this.ptr(c).package;
1707 }1697 }
1708 };
1709 };1698 };
17101699
1711 pub const Index = IndexType(@This());1700 pub const OptionalIndex = enum(u32) {
1712 };1701 root,
17131702 none = max_u32,
1714 pub const UserValue = union(Tag) {1703 _,
1715 flag,
1716 scalar: String,
1717 list: StringList,
1718 map: Map.Index,
1719 lazy_path: LazyPath.Index,
1720 lazy_path_list: Storage.LengthPrefixedList(LazyPath.Index),
1721
1722 pub const Standalone = struct {
1723 flags: Flags,
1724 value: Storage.FlagUnion(.flags, .tag, UserValue),
1725
1726 pub const Flags = packed struct(u32) {
1727 tag: UserValue.Tag,
1728 _: u29 = 0,
1729 };
1730
1731 pub const Index = IndexType(@This());
1732 };
17331704
1734 pub const Tag = enum(u3) {1705 pub fn init(i: Instance.Index) Instance.OptionalIndex {
1735 flag,1706 const result: Instance.OptionalIndex = @fromBackingInt(@intCast(@backingInt(i)));
1736 scalar,1707 assert(result != .none);
1737 list,1708 return result;
1738 map,1709 }
1739 lazy_path,
1740 lazy_path_list,
17411710
1742 pub fn init(uv: @typeInfo(std.Build.UserValue).@"union".tag_type.?) @This() {1711 pub fn unwrap(this: @This()) ?Instance.Index {
1743 return switch (uv) {1712 return switch (this) {
1744 inline else => |tag| @field(@This(), @tagName(tag)),1713 .none => null,
1714 .root => .root,
1715 _ => @fromBackingInt(@intCast(@backingInt(this))),
1745 };1716 };
1746 }1717 }
1747 };1718 };
1748
1749 pub const Map = struct {
1750 keys: StringList,
1751 values: Storage.LengthPrefixedList(UserValue.Standalone.Index),
1752
1753 pub const Index = IndexType(@This());
1754 };
1755 };
1756
1757 pub const PublicModules = extern struct {
1758 keys: StringList,
1759 values: Module.List.Index,
1760 };
1761
1762 pub const Index = enum(u32) {
1763 root,
1764 _,
1765
1766 pub fn ptr(this: @This(), c: *const Configuration) *const PackageInstance {
1767 return &c.package_instances[@backingInt(this)];
1768 }
1769
1770 pub fn package(this: @This(), c: *const Configuration) Package.Index {
1771 return this.ptr(c).package;
1772 }
1773 };
1774
1775 pub const OptionalIndex = enum(u32) {
1776 root,
1777 none = max_u32,
1778 _,
1779
1780 pub fn init(i: Index) OptionalIndex {
1781 const result: OptionalIndex = @fromBackingInt(@intCast(@backingInt(i)));
1782 assert(result != .none);
1783 return result;
1784 }
1785
1786 pub fn unwrap(this: @This()) ?Index {
1787 return switch (this) {
1788 .none => null,
1789 .root => .root,
1790 _ => @fromBackingInt(@intCast(@backingInt(this))),
1791 };
1792 }
1793 };1719 };
1794};1720};
17951721
...@@ -1797,7 +1723,7 @@ pub const Module = struct {...@@ -1797,7 +1723,7 @@ pub const Module = struct {
1797 flags: Flags,1723 flags: Flags,
1798 flags2: Flags2,1724 flags2: Flags2,
1799 import_table: ImportTable.Index,1725 import_table: ImportTable.Index,
1800 owner: PackageInstance.Index,1726 owner: Package.Instance.Index,
1801 root_source_file: LazyPath.OptionalIndex,1727 root_source_file: LazyPath.OptionalIndex,
1802 resolved_target: ResolvedTarget.OptionalIndex,1728 resolved_target: ResolvedTarget.OptionalIndex,
1803 c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String),1729 c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String),
...@@ -2040,7 +1966,7 @@ pub const OptionalStringList = enum(u32) {...@@ -2040,7 +1966,7 @@ pub const OptionalStringList = enum(u32) {
2040pub const PathDep = extern struct {1966pub const PathDep = extern struct {
2041 flags: Flags,1967 flags: Flags,
2042 sub: String,1968 sub: String,
2043 pkg: PackageInstance.OptionalIndex,1969 pkg: Package.Instance.OptionalIndex,
20441970
2045 pub const Flags = packed struct(u32) {1971 pub const Flags = packed struct(u32) {
2046 mode: Mode,1972 mode: Mode,
...@@ -3306,7 +3232,7 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration {...@@ -3306,7 +3232,7 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration {
3306 .available_options = try arena.alloc(AvailableOption, header.available_options_len),3232 .available_options = try arena.alloc(AvailableOption, header.available_options_len),
3307 .search_prefixes = try arena.alloc(String, header.search_prefixes_len),3233 .search_prefixes = try arena.alloc(String, header.search_prefixes_len),
3308 .packages = try arena.alloc(Package, header.packages_len),3234 .packages = try arena.alloc(Package, header.packages_len),
3309 .package_instances = try arena.alloc(PackageInstance, header.package_instances_len),3235 .package_instances = try arena.alloc(Package.Instance, header.package_instances_len),
3310 .extra = try arena.alloc(u32, header.extra_len),3236 .extra = try arena.alloc(u32, header.extra_len),
3311 .default_step = header.default_step,3237 .default_step = header.default_step,
3312 .generated_files_len = header.generated_files_len,3238 .generated_files_len = header.generated_files_len,
lib/std/Build/Serialize.zig+15-49
...@@ -40,12 +40,14 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi...@@ -40,12 +40,14 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi
40 // been created yet with packageInstanceFromBuilder.40 // been created yet with packageInstanceFromBuilder.
4141
42 s.package_instance_map.putAssumeCapacityNoClobber(b, {});42 s.package_instance_map.putAssumeCapacityNoClobber(b, {});
43 var iter = b.graph.dependency_cache.valueIterator();43 for (b.graph.dependency_cache.values()) |dep| {
44 while (iter.next()) |dep| s.package_instance_map.putAssumeCapacityNoClobber(dep.*.builder, {});44 s.package_instance_map.putAssumeCapacityNoClobber(dep.builder, {});
45 }
4546
46 try s.addPackageInstance(b);47 try s.addPackageInstance(b);
47 var iter2 = b.graph.dependency_cache.valueIterator();48 for (b.graph.dependency_cache.values()) |dep| {
48 while (iter2.next()) |dep| try s.addPackageInstance(dep.*.builder);49 try s.addPackageInstance(dep.builder);
50 }
4951
50 try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len);52 try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len);
51 for (53 for (
...@@ -814,51 +816,15 @@ fn addPackageInstance(s: *Serialize, b: *std.Build) Allocator.Error!void {...@@ -814,51 +816,15 @@ fn addPackageInstance(s: *Serialize, b: *std.Build) Allocator.Error!void {
814816
815 const index = s.package_instance_map.getIndex(b).?;817 const index = s.package_instance_map.getIndex(b).?;
816818
817 const options = try arena.alloc(819 const modules_values = try arena.alloc(Configuration.Module.Index, b.modules.count());
818 Configuration.PackageInstance.UserInputOption.Index,820 for (modules_values, b.modules.values()) |*dest_value, value| {
819 b.user_input_options.count(),821 dest_value.* = try s.addModule(value);
820 );
821
822 {
823 var i: usize = 0;
824 var iter = b.user_input_options.valueIterator();
825 while (iter.next()) |option| : (i += 1) {
826 options[i] = try wc.addExtra(Configuration.PackageInstance.UserInputOption, .{
827 .flags = .{
828 .tag = .init(option.value),
829 .used = option.used,
830 },
831 .name = try wc.addString(option.name),
832 .value = .{ .u = try s.makeUserValue(&option.value) },
833 });
834 }
835 }
836
837 const modules_keys = try arena.alloc(
838 []const u8,
839 b.modules.count(),
840 );
841 const modules_values = try arena.alloc(
842 Configuration.Module.Index,
843 b.modules.count(),
844 );
845
846 {
847 var i: usize = 0;
848 var iter = b.modules.iterator();
849 while (iter.next()) |entry| : (i += 1) {
850 modules_keys[i] = entry.key_ptr.*;
851 modules_values[i] = try s.addModule(entry.value_ptr.*);
852 }
853 }822 }
854823
855 wc.package_instances.items[index] = .{824 wc.package_instances.items[index] = .{
856 .package = s.packageFromHash(b.pkg_hash),825 .package = s.packageFromHash(b.pkg_hash),
857 .user_input_options = try wc.addDeduped(Configuration.PackageInstance.UserInputOption.List, .{
858 .options = .{ .slice = options },
859 }),
860 .modules = .{826 .modules = .{
861 .keys = try wc.addStringList(modules_keys),827 .keys = try wc.addStringList(b.modules.keys()),
862 .values = try wc.addDeduped(Configuration.Module.List, .{828 .values = try wc.addDeduped(Configuration.Module.List, .{
863 .modules = .{ .slice = modules_values },829 .modules = .{ .slice = modules_values },
864 }),830 }),
...@@ -866,7 +832,7 @@ fn addPackageInstance(s: *Serialize, b: *std.Build) Allocator.Error!void {...@@ -866,7 +832,7 @@ fn addPackageInstance(s: *Serialize, b: *std.Build) Allocator.Error!void {
866 };832 };
867}833}
868834
869fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocator.Error!Configuration.PackageInstance.UserValue {835fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocator.Error!Configuration.Package.Instance.UserValue {
870 const arena = s.arena;836 const arena = s.arena;
871 const wc = s.wc;837 const wc = s.wc;
872838
...@@ -876,7 +842,7 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato...@@ -876,7 +842,7 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato
876 .list => |list| .{ .list = try wc.addStringList(list.items) },842 .list => |list| .{ .list = try wc.addStringList(list.items) },
877 .map => |map| add: {843 .map => |map| add: {
878 const keys = try arena.alloc([]const u8, map.count());844 const keys = try arena.alloc([]const u8, map.count());
879 const values = try arena.alloc(Configuration.PackageInstance.UserValue.Standalone.Index, map.count());845 const values = try arena.alloc(Configuration.Package.Instance.UserValue.Standalone.Index, map.count());
880846
881 var i: usize = 0;847 var i: usize = 0;
882 var iter = map.iterator();848 var iter = map.iterator();
...@@ -885,13 +851,13 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato...@@ -885,13 +851,13 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato
885851
886 keys[i] = entry.key_ptr.*;852 keys[i] = entry.key_ptr.*;
887 values[i] = try wc.addDeduped(853 values[i] = try wc.addDeduped(
888 Configuration.PackageInstance.UserValue.Standalone,854 Configuration.Package.Instance.UserValue.Standalone,
889 .{ .flags = .{ .tag = value }, .value = .{ .u = value } },855 .{ .flags = .{ .tag = value }, .value = .{ .u = value } },
890 );856 );
891 }857 }
892858
893 break :add .{ .map = try wc.addDeduped(859 break :add .{ .map = try wc.addDeduped(
894 Configuration.PackageInstance.UserValue.Map,860 Configuration.Package.Instance.UserValue.Map,
895 .{861 .{
896 .keys = try wc.addStringList(keys),862 .keys = try wc.addStringList(keys),
897 .values = .{ .slice = values },863 .values = .{ .slice = values },
...@@ -907,7 +873,7 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato...@@ -907,7 +873,7 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato
907 };873 };
908}874}
909875
910fn packageInstanceFromBuilder(s: *Serialize, b: *std.Build) Configuration.PackageInstance.Index {876fn packageInstanceFromBuilder(s: *Serialize, b: *std.Build) Configuration.Package.Instance.Index {
911 if (b.pkg_hash.len == 0) return .root;877 if (b.pkg_hash.len == 0) return .root;
912 return @fromBackingInt(@intCast(s.package_instance_map.getIndex(b).?));878 return @fromBackingInt(@intCast(s.package_instance_map.getIndex(b).?));
913}879}