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
33883388pub fn packagePath(
33893389 maker: *const Maker,
33903390 arena: Allocator,
3391 inst_index: Configuration.PackageInstance.Index,
3391 inst_index: Configuration.Package.Instance.Index,
33923392 sub_path: []const u8,
33933393) Allocator.Error!Path {
33943394 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 {
116116 try sf.container.serializer.int(@backingInt(inst.package));
117117 }
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
127119 var msf = try sf.beginStructField("modules", .{});
128120 for (inst.modules.keys.slice(c), inst.modules.values.slice(c)) |key, value| {
129121 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 {
8686 if (mem.findScalar(u8, option_contents, '=')) |name_end| {
8787 const option_name = option_contents[0..name_end];
8888 const option_value = option_contents[name_end + 1 ..];
89 if (try builder.addUserInputOption(option_name, option_value))
90 fatal(" access the help menu with 'zig build -h'", .{});
89 if (try builder.addUserInputOption(option_name, option_value)) {
90 log.info("to access the help menu: zig build -h", .{});
91 process.exit(1);
92 }
9193 } else {
92 if (try builder.addUserInputFlag(option_contents))
93 fatal(" access the help menu with 'zig build -h'", .{});
94 if (try builder.addUserInputFlag(option_contents)) {
95 log.info("to access the help menu: zig build -h", .{});
96 process.exit(1);
97 }
9498 }
9599 } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| {
96100 try graph.system_integration_options.put(arena, name, .user_enabled);
......@@ -133,8 +137,25 @@ pub fn main(init: process.Init.Minimal) !void {
133137
134138 builder.runPackageScript(root);
135139
136 if (builder.validateUserInputDidItFail()) {
137 fatal(" access the help menu with 'zig build -h'", .{});
140 // Even though the root package's user input options are not serialized,
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);
138159 }
139160
140161 try Serialize.packageOptions(builder, &graph.wip_configuration);
lib/std/Build.zig+295-489
......@@ -9,7 +9,6 @@ const mem = std.mem;
99const panic = std.debug.panic;
1010const assert = std.debug.assert;
1111const log = std.log;
12const StringHashMap = std.StringHashMap;
1312const Allocator = std.mem.Allocator;
1413const Target = std.Target;
1514const process = std.process;
......@@ -32,9 +31,6 @@ graph: *Graph,
3231install_tls: Step.TopLevel,
3332uninstall_tls: Step.TopLevel,
3433allocator: Allocator,
35user_input_options: UserInputOptionsMap,
36available_options_map: std.array_hash_map.String(AvailableOption) = .empty,
37invalid_user_input: bool,
3834default_step: *Step,
3935top_level_steps: std.array_hash_map.String(*Step.TopLevel),
4036/// Path to the directory containing build.zig.
......@@ -45,6 +41,10 @@ debug_log_scopes: []const []const u8 = &.{},
4541/// Set to 0 to disable stack collection.
4642debug_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
4848dep_prefix: []const u8 = "",
4949
5050modules: std.array_hash_map.String(*Module),
......@@ -82,7 +82,7 @@ pub const Graph = struct {
8282 needed_lazy_dependencies: std.array_hash_map.String(void) = .empty,
8383 /// Information about the native target. Computed before build() is invoked.
8484 host: ResolvedTarget,
85 dependency_cache: InitializedDepMap = .empty,
85 dependency_cache: PackageInstanceMap = .empty,
8686 allow_so_scripts: ?bool = null,
8787 time_report: bool = false,
8888 verbose: bool = false,
......@@ -238,65 +238,123 @@ pub const SystemLibraryMode = enum {
238238 declared_enabled,
239239};
240240
241const InitializedDepMap = std.HashMapUnmanaged(InitializedDepKey, *Dependency, InitializedDepContext, std.hash_map.default_max_load_percentage);
242const InitializedDepKey = struct {
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 {
241const PackageInstanceMap = std.array_hash_map.Custom(PackageInstanceKey, *Dependency, struct {
242 pub fn hash(_: @This(), k: PackageInstanceKey) u32 {
251243 var hasher = std.hash.Wyhash.init(0);
252 hasher.update(k.build_root_string);
253 hashUserInputOptionsMap(ctx.allocator, k.user_input_options, &hasher);
254 return hasher.final();
244 hasher.update(k.pkg_hash);
245 for (k.options.keys(), k.options.values()) |option_key, option_value| {
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;
255265 }
266}, true);
256267
257 pub fn eql(_: @This(), lhs: InitializedDepKey, rhs: InitializedDepKey) bool {
258 if (!std.mem.eql(u8, lhs.build_root_string, rhs.build_root_string))
259 return false;
268const PackageInstanceKey = struct {
269 pkg_hash: []const u8,
270 options: *const PackageOptions.Map,
271};
260272
261 if (lhs.user_input_options.count() != rhs.user_input_options.count())
262 return false;
273/// Build system implementation details.
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();
265 while (it.next()) |lhs_entry| {
266 const rhs_value = rhs.user_input_options.get(lhs_entry.key_ptr.*) orelse return false;
267 if (!userValuesAreSame(lhs_entry.value_ptr.*.value, rhs_value.value))
268 return false;
333 fn fromArgs(arena: Allocator, map: *PackageOptions.Map, args: anytype) void {
334 const args_info = @typeInfo(@TypeOf(args)).@"struct";
335 inline for (args_info.field_names, args_info.field_types) |field_name, field_type| {
336 if (field_type == @TypeOf(null)) continue;
337 addPackageOptionFromArg(arena, map, field_name, field_type, @field(args, field_name));
269338 }
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() }));
272348 }
273349};
274350
275pub const UserInputOptionsMap = StringHashMap(UserInputOption);
276
277351const AvailableOption = struct {
278 name: []const u8,
279352 type_id: Configuration.AvailableOption.Type,
280353 description: []const u8,
281354 /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options
282355 enum_options: ?[]const []const u8,
283356};
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
300358/// Build system implementation detail.
301359pub fn create(
302360 graph: *Graph,
......@@ -311,7 +369,7 @@ pub fn create(
311369 .root = root,
312370 .invalid_user_input = false,
313371 .allocator = arena,
314 .user_input_options = UserInputOptionsMap.init(arena),
372 .user_input_options = .empty,
315373 .top_level_steps = .{},
316374 .default_step = undefined,
317375 .install_tls = .{
......@@ -348,7 +406,7 @@ fn createChild(
348406 root: Cache.Path,
349407 pkg_hash: []const u8,
350408 pkg_deps: AvailableDeps,
351 user_input_options: UserInputOptionsMap,
409 user_input_options: PackageOptions.Map,
352410) error{OutOfMemory}!*Build {
353411 const arena = parent.graph.arena;
354412 const child = try arena.create(Build);
......@@ -390,167 +448,96 @@ fn createChild(
390448 return child;
391449}
392450
393fn userInputOptionsFromArgs(arena: Allocator, args: anytype) UserInputOptionsMap {
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(
451fn addPackageOptionFromArg(
404452 arena: Allocator,
405 map: *UserInputOptionsMap,
453 map: *PackageOptions.Map,
406454 field_name: [:0]const u8,
407455 comptime T: type,
408456 /// If null, the value won't be added, but `T` will still be type-checked.
409457 maybe_value: ?T,
410458) void {
459 map.ensureUnusedCapacity(arena, 2) catch @panic("OOM");
411460 switch (T) {
412461 Target.Query => return if (maybe_value) |v| {
413 map.put(field_name, .{
414 .name = field_name,
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");
462 map.putAssumeCapacity(field_name, .{ .scalar = v.zigTriple(arena) catch @panic("OOM") });
463 map.putAssumeCapacity("cpu", .{ .scalar = v.serializeCpuAlloc(arena) catch @panic("OOM") });
423464 },
424465 ResolvedTarget => return if (maybe_value) |v| {
425 map.put(field_name, .{
426 .name = field_name,
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");
466 map.putAssumeCapacity(field_name, .{ .scalar = v.query.zigTriple(arena) catch @panic("OOM") });
467 map.putAssumeCapacity("cpu", .{ .scalar = v.query.serializeCpuAlloc(arena) catch @panic("OOM") });
435468 },
436469 std.zig.BuildId => return if (maybe_value) |v| {
437 map.put(field_name, .{
438 .name = field_name,
439 .value = .{ .scalar = std.fmt.allocPrint(arena, "{f}", .{v}) catch @panic("OOM") },
440 .used = false,
441 }) catch @panic("OOM");
470 map.putAssumeCapacity(field_name, .{
471 .scalar = std.fmt.allocPrint(arena, "{f}", .{v}) catch @panic("OOM"),
472 });
442473 },
443474 LazyPath => return if (maybe_value) |v| {
444 map.put(field_name, .{
445 .name = field_name,
446 .value = .{ .lazy_path = v.dupeInner(arena) },
447 .used = false,
448 }) catch @panic("OOM");
475 map.putAssumeCapacity(field_name, .{ .lazy_path = v.dupeInner(arena) });
449476 },
450477 []const LazyPath => return if (maybe_value) |v| {
451 var list = std.array_list.Managed(LazyPath).initCapacity(arena, v.len) catch @panic("OOM");
452 for (v) |lp| list.appendAssumeCapacity(lp.dupeInner(arena));
453 map.put(field_name, .{
454 .name = field_name,
455 .value = .{ .lazy_path_list = list },
456 .used = false,
457 }) catch @panic("OOM");
478 var list: std.ArrayList(LazyPath) = .empty;
479 const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM");
480 for (v, elems) |lp, *elem| elem.* = lp.dupeInner(arena);
481 map.putAssumeCapacity(field_name, .{ .lazy_path_list = list });
458482 },
459483 []const u8 => return if (maybe_value) |v| {
460 map.put(field_name, .{
461 .name = field_name,
462 .value = .{ .scalar = arena.dupe(u8, v) catch @panic("OOM") },
463 .used = false,
464 }) catch @panic("OOM");
484 map.putAssumeCapacity(field_name, .{ .scalar = arena.dupe(u8, v) catch @panic("OOM") });
465485 },
466486 []const []const u8 => return if (maybe_value) |v| {
467 var list = std.array_list.Managed([]const u8).initCapacity(arena, v.len) catch @panic("OOM");
468 for (v) |s| list.appendAssumeCapacity(arena.dupe(u8, s) catch @panic("OOM"));
469 map.put(field_name, .{
470 .name = field_name,
471 .value = .{ .list = list },
472 .used = false,
473 }) catch @panic("OOM");
487 var list: std.ArrayList([]const u8) = .empty;
488 const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM");
489 for (v, elems) |s, *elem| elem.* = arena.dupe(u8, s) catch @panic("OOM");
490 map.putAssumeCapacity(field_name, .{ .list = list });
474491 },
475492 else => switch (@typeInfo(T)) {
476493 .bool => return if (maybe_value) |v| {
477 map.put(field_name, .{
478 .name = field_name,
479 .value = .{ .scalar = if (v) "true" else "false" },
480 .used = false,
481 }) catch @panic("OOM");
494 map.putAssumeCapacity(field_name, .{ .scalar = if (v) "true" else "false" });
482495 },
483496 .@"enum", .enum_literal => return if (maybe_value) |v| {
484 map.put(field_name, .{
485 .name = field_name,
486 .value = .{ .scalar = @tagName(v) },
487 .used = false,
488 }) catch @panic("OOM");
497 map.putAssumeCapacity(field_name, .{ .scalar = @tagName(v) });
489498 },
490499 .comptime_int, .int => return if (maybe_value) |v| {
491 map.put(field_name, .{
492 .name = field_name,
493 .value = .{ .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM") },
494 .used = false,
495 }) catch @panic("OOM");
500 map.putAssumeCapacity(field_name, .{
501 .scalar = std.fmt.allocPrint(arena, "{d}", .{v}) catch @panic("OOM"),
502 });
496503 },
497504 .comptime_float, .float => return if (maybe_value) |v| {
498 map.put(field_name, .{
499 .name = field_name,
500 .value = .{ .scalar = std.fmt.allocPrint(arena, "{x}", .{v}) catch @panic("OOM") },
501 .used = false,
502 }) catch @panic("OOM");
505 map.putAssumeCapacity(field_name, .{
506 .scalar = std.fmt.allocPrint(arena, "{x}", .{v}) catch @panic("OOM"),
507 });
503508 },
504509 .pointer => |ptr_info| switch (ptr_info.size) {
505510 .one => switch (@typeInfo(ptr_info.child)) {
506 .array => |array_info| {
507 addUserInputOptionFromArg(
508 arena,
509 map,
510 field_name,
511 @Pointer(.slice, .{ .@"const" = true }, array_info.child, null),
512 maybe_value orelse null,
513 );
514 return;
515 },
511 .array => |array_info| return addPackageOptionFromArg(
512 arena,
513 map,
514 field_name,
515 @Pointer(.slice, .{ .@"const" = true }, array_info.child, null),
516 maybe_value orelse null,
517 ),
516518 else => {},
517519 },
518520 .slice => switch (@typeInfo(ptr_info.child)) {
519521 .@"enum" => return if (maybe_value) |v| {
520 var list = std.array_list.Managed([]const u8).initCapacity(arena, v.len) catch @panic("OOM");
521 for (v) |tag| list.appendAssumeCapacity(@tagName(tag));
522 map.put(field_name, .{
523 .name = field_name,
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;
522 var list: std.ArrayList([]const u8) = .empty;
523 const elems = list.addManyAsSlice(arena, v.len) catch @panic("OOM");
524 for (elems, v) |*elem, tag| elem.* = @tagName(tag);
525 map.putAssumeCapacity(field_name, .{ .list = list });
537526 },
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 ),
538534 },
539535 else => {},
540536 },
541537 .null => unreachable,
542538 .optional => |info| switch (@typeInfo(info.child)) {
543539 .optional => {},
544 else => {
545 addUserInputOptionFromArg(
546 arena,
547 map,
548 field_name,
549 info.child,
550 maybe_value orelse null,
551 );
552 return;
553 },
540 else => return addPackageOptionFromArg(arena, map, field_name, info.child, maybe_value orelse null),
554541 },
555542 else => {},
556543 },
......@@ -558,130 +545,6 @@ fn addUserInputOptionFromArg(
558545 @compileError("option '" ++ field_name ++ "' has unsupported type: " ++ @typeName(T));
559546}
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
685548/// Create a set of key-value pairs that can be converted into a Zig source
686549/// file and then inserted into a Zig compilation's module table for importing.
687550///
......@@ -1107,31 +970,20 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1107970 const name = graph.dupeString(name_raw);
1108971 const description = graph.dupeString(description_raw);
1109972 const type_id = comptime typeToEnum(T);
1110 const enum_options = if (type_id == .@"enum" or type_id == .enum_list) blk: {
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,
973 const available_option: AvailableOption = .{
1123974 .type_id = type_id,
1124975 .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,
1126980 };
1127981 if ((b.available_options_map.fetchPut(arena, name, available_option) catch @panic("OOM")) != null) {
1128982 panic("option {q} declared twice", .{name});
1129983 }
1130
1131 const option_ptr = b.user_input_options.getPtr(name) orelse return null;
1132 option_ptr.used = true;
984 const user_provided = b.user_input_options.get(name) orelse return null;
1133985 switch (type_id) {
1134 .bool => switch (option_ptr.value) {
986 .bool => switch (user_provided) {
1135987 .flag => return true,
1136988 .scalar => |s| {
1137989 if (mem.eql(u8, s, "true")) {
......@@ -1145,14 +997,14 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
1145997 }
1146998 },
1147999 .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 });
11491001 b.markInvalidUserInput();
11501002 return null;
11511003 },
11521004 },
1153 .int => switch (option_ptr.value) {
1005 .int => switch (user_provided) {
11541006 .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 });
11561008 b.markInvalidUserInput();
11571009 return null;
11581010 },
......@@ -1172,9 +1024,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
11721024 return n;
11731025 },
11741026 },
1175 .float => switch (option_ptr.value) {
1027 .float => switch (user_provided) {
11761028 .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 });
11781030 b.markInvalidUserInput();
11791031 return null;
11801032 },
......@@ -1187,9 +1039,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
11871039 return n;
11881040 },
11891041 },
1190 .@"enum" => switch (option_ptr.value) {
1042 .@"enum" => switch (user_provided) {
11911043 .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 });
11931045 b.markInvalidUserInput();
11941046 return null;
11951047 },
......@@ -1206,17 +1058,17 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
12061058 return null;
12071059 },
12081060 },
1209 .string => switch (option_ptr.value) {
1061 .string => switch (user_provided) {
12101062 .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 });
12121064 b.markInvalidUserInput();
12131065 return null;
12141066 },
12151067 .scalar => |s| return s,
12161068 },
1217 .build_id => switch (option_ptr.value) {
1069 .build_id => switch (user_provided) {
12181070 .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 });
12201072 b.markInvalidUserInput();
12211073 return null;
12221074 },
......@@ -1230,9 +1082,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
12301082 }
12311083 },
12321084 },
1233 .list => switch (option_ptr.value) {
1085 .list => switch (user_provided) {
12341086 .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 });
12361088 b.markInvalidUserInput();
12371089 return null;
12381090 },
......@@ -1241,9 +1093,9 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
12411093 },
12421094 .list => |lst| return lst.items,
12431095 },
1244 .enum_list => switch (option_ptr.value) {
1096 .enum_list => switch (user_provided) {
12451097 .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 });
12471099 b.markInvalidUserInput();
12481100 return null;
12491101 },
......@@ -1283,16 +1135,16 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
12831135 return new_list;
12841136 },
12851137 },
1286 .lazy_path => switch (option_ptr.value) {
1138 .lazy_path => switch (user_provided) {
12871139 .scalar => |s| return .{ .cwd_relative = s },
12881140 .lazy_path => |lp| return lp,
12891141 .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 });
12911143 b.markInvalidUserInput();
12921144 return null;
12931145 },
12941146 },
1295 .lazy_path_list => switch (option_ptr.value) {
1147 .lazy_path_list => switch (user_provided) {
12961148 .scalar => |s| return arena.dupe(LazyPath, &[_]LazyPath{.{ .cwd_relative = s }}) catch @panic("OOM"),
12971149 .lazy_path => |lp| return arena.dupe(LazyPath, &[_]LazyPath{lp}) catch @panic("OOM"),
12981150 .list => |lst| {
......@@ -1304,7 +1156,7 @@ pub fn option(b: *Build, comptime T: type, name_raw: []const u8, description_raw
13041156 },
13051157 .lazy_path_list => |lp_list| return lp_list.items,
13061158 .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 });
13081160 b.markInvalidUserInput();
13091161 return null;
13101162 },
......@@ -1497,88 +1349,63 @@ pub fn standardTargetOptionsQueryOnly(b: *Build, args: StandardTargetOptionsArgs
14971349}
14981350
14991351/// 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 {
15011353 const graph = b.graph;
15021354 const arena = graph.arena;
1503 const name = graph.dupeString(name_raw);
15041355 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
15061358 if (!gop.found_existing) {
1507 gop.value_ptr.* = UserInputOption{
1508 .name = name,
1509 .value = .{ .scalar = value },
1510 .used = false,
1511 };
1359 gop.key_ptr.* = graph.dupeString(name);
1360 gop.value_ptr.* = .{ .scalar = value };
15121361 return false;
15131362 }
15141363
1515 // option already exists
1516 switch (gop.value_ptr.value) {
1364 // Option already exists.
1365 switch (gop.value_ptr.*) {
15171366 .scalar => |s| {
1518 // turn it into a list
1519 var list = std.array_list.Managed([]const u8).init(arena);
1520 try list.append(s);
1521 try list.append(value);
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 });
1367 // Turn it into a list.
1368 var list: std.ArrayList([]const u8) = .empty;
1369 (try list.addManyAsArray(arena, 2)).* = .{ s, value };
1370 gop.value_ptr.* = .{ .list = list };
15361371 },
1372 .list => |*list| try list.append(arena, value),
15371373 .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 });
15391375 return true;
15401376 },
15411377 .map => |*map| {
15421378 _ = map;
1543 log.warn("TODO maps as command line arguments is not implemented yet.", .{});
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;
1379 unreachable; // TODO implement maps as command line arguments
15511380 },
1381 .lazy_path => unreachable,
1382 .lazy_path_list => unreachable,
15521383 }
15531384 return false;
15541385}
15551386
15561387/// 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 {
15581389 const graph = b.graph;
1559 const name = graph.dupeString(name_raw);
1560 const gop = try b.user_input_options.getOrPut(name);
1390 const arena = graph.arena;
1391 const gop = try b.user_input_options.getOrPut(arena, name);
15611392 if (!gop.found_existing) {
1562 gop.value_ptr.* = .{
1563 .name = name,
1564 .value = .{ .flag = {} },
1565 .used = false,
1566 };
1393 gop.key_ptr.* = graph.dupeString(name);
1394 gop.value_ptr.* = .{ .flag = {} };
15671395 return false;
15681396 }
1569
1570 // option already exists
1571 switch (gop.value_ptr.value) {
1397 // Option already exists.
1398 switch (gop.value_ptr.*) {
15721399 .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 });
15741401 return true;
15751402 },
15761403 .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});
15781405 return true;
15791406 },
15801407 .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 });
15821409 return true;
15831410 },
15841411
......@@ -1614,17 +1441,16 @@ fn markInvalidUserInput(b: *Build) void {
16141441 b.invalid_user_input = true;
16151442}
16161443
1617/// Build system implementation detail.
1618pub fn validateUserInputDidItFail(b: *Build) bool {
1619 // Make sure all args are used.
1620 var it = b.user_input_options.iterator();
1621 while (it.next()) |entry| {
1622 if (!entry.value_ptr.used) {
1623 log.err("invalid option: -D{s}", .{entry.key_ptr.*});
1444fn validateUserInputDidItFail(b: *Build) bool {
1445 for (b.user_input_options.keys()) |name| {
1446 if (!b.available_options_map.contains(name)) {
1447 for (b.available_options_map.keys(), b.available_options_map.values()) |available_name, *available| {
1448 log.info("available option: {q}: {s}", .{ available_name, available.description });
1449 }
1450 log.err("invalid option: {q}", .{name});
16241451 b.markInvalidUserInput();
16251452 }
16261453 }
1627
16281454 return b.invalid_user_input;
16291455}
16301456
......@@ -2092,14 +1918,14 @@ inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, c
20921918 const pkg = @field(deps.packages, pkg_hash);
20931919 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };
20941920 } 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)) {
20961922 const build_zig_path = b.root.join(arena, "build.zig") catch @panic("OOM");
20971923 panic("{} is not the struct that corresponds to {f}", .{
20981924 asking_build_zig, build_zig_path,
20991925 });
21001926 }
21011927 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];
21031929 };
21041930
21051931 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
21411967 markNeededLazyDep(b, pkg_hash);
21421968 return error.LazyDependencyNeeded;
21431969 }
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);
21451973}
21461974
21471975pub const PackageEntry = struct {
......@@ -2232,12 +2060,14 @@ pub inline fn lazyImport(
22322060 comptime unreachable; // Bad @dependencies source
22332061}
22342062
2235fn pkgHashFromBuildZig(comptime build_zig: type) ?[]const u8 {
2236 const deps = @import("root").dependencies;
2237 return comptime for (@typeInfo(deps.packages).@"struct".decl_names) |pkg_hash| {
2238 const pkg = @field(deps.packages, pkg_hash);
2239 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == build_zig) break pkg_hash;
2240 } else null;
2063inline fn pkgHashFromBuildZig(comptime build_zig: type) ?[]const u8 {
2064 comptime {
2065 const deps = @import("root").dependencies;
2066 return for (@typeInfo(deps.packages).@"struct".decl_names) |pkg_hash| {
2067 const pkg = @field(deps.packages, pkg_hash);
2068 if (@hasDecl(pkg, "build_zig") and pkg.build_zig == build_zig) break pkg_hash;
2069 } else null;
2070 }
22412071}
22422072
22432073/// Build system implementation detail.
......@@ -2251,114 +2081,37 @@ pub fn dependencyFromBuildZig(
22512081 const arena = b.graph.arena;
22522082
22532083 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;
22552085 const dep_name = for (b.available_deps) |dep| {
22562086 if (mem.eql(u8, dep[1], pkg_hash)) break dep[1];
22572087 } else break :find_dep;
22582088 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);
22602092 }
22612093
22622094 const full_path = b.root.join(arena, "build.zig.zon") catch @panic("OOM");
22632095 panic("{} is not a build.zig struct of a dependency in {f}", .{ build_zig, full_path });
22642096}
22652097
2266fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool {
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
2098/// Takes ownership of `package_options`, which may be unsorted.
23492099fn dependencyResolved(
23502100 b: *Build,
23512101 name: []const u8,
23522102 entry: PackageEntry,
2353 user_input_options: UserInputOptionsMap,
2103 package_options: *PackageOptions.Map,
23542104) *Dependency {
23552105 const graph = b.graph;
23562106 const io = graph.io;
23572107 const arena = graph.arena;
2108
2109 PackageOptions.sort(package_options);
2110
23582111 if (graph.dependency_cache.getContext(.{
2359 .build_root_string = entry.build_root,
2360 .user_input_options = user_input_options,
2361 }, .{ .allocator = arena })) |dep| return dep;
2112 .pkg_hash = entry.hash,
2113 .options = package_options,
2114 }, .{})) |dep| return dep;
23622115
23632116 const dep_root: Cache.Path = .{
23642117 .root_dir = .{
......@@ -2368,7 +2121,7 @@ fn dependencyResolved(
23682121 },
23692122 };
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");
23722125 if (entry.run_build) |run_build| {
23732126 run_build(sub_builder);
23742127
......@@ -2381,9 +2134,9 @@ fn dependencyResolved(
23812134 dep.* = .{ .builder = sub_builder };
23822135
23832136 graph.dependency_cache.putContext(arena, .{
2384 .build_root_string = entry.build_root,
2385 .user_input_options = user_input_options,
2386 }, dep, .{ .allocator = arena }) catch @panic("OOM");
2137 .pkg_hash = entry.hash,
2138 .options = &sub_builder.user_input_options,
2139 }, dep, .{}) catch @panic("OOM");
23872140 return dep;
23882141}
23892142
......@@ -2650,6 +2403,59 @@ pub const LazyPath = union(enum) {
26502403 } },
26512404 };
26522405 }
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 }
26532459};
26542460
26552461fn dumpBadDirnameHelp(
lib/std/Build/Configuration.zig+36-110
......@@ -22,7 +22,7 @@ packages: []Package,
2222/// Unlike `packages`, each item corresponds to a `std.Build`, which is a
2323/// package that was instantiated by running its build script with specific
2424/// input options.
25package_instances: []PackageInstance,
25package_instances: []Package.Instance,
2626extra: []u32,
2727default_step: Step.Index,
2828generated_files_len: u32,
......@@ -69,7 +69,7 @@ pub const Wip = struct {
6969 path_deps: std.ArrayList(PathDep) = .empty,
7070 search_prefixes: std.ArrayList(String) = .empty,
7171 packages: std.ArrayList(Package) = .empty,
72 package_instances: std.ArrayList(PackageInstance) = .empty,
72 package_instances: std.ArrayList(Package.Instance) = .empty,
7373 extra: std.ArrayList(u32) = .empty,
7474 next_generated_file_index: u32 = 0,
7575 cache_poison: bool = false,
......@@ -494,7 +494,7 @@ pub const AvailableOption = extern struct {
494494
495495pub const Step = extern struct {
496496 name: String,
497 owner: PackageInstance.Index,
497 owner: Package.Instance.Index,
498498 deps: Deps.Index,
499499 max_rss: MaxRss,
500500 extended: Storage.Extended(Flags, union(Tag) {
......@@ -1543,7 +1543,7 @@ pub const LazyPath = union(@This().Tag) {
15431543
15441544 pub const SourcePath = struct {
15451545 flags: @This().Flags = .{},
1546 owner: PackageInstance.Index,
1546 owner: Package.Instance.Index,
15471547 sub_path: String,
15481548
15491549 pub const Flags = packed struct(u32) {
......@@ -1674,122 +1674,48 @@ pub const Package = extern struct {
16741674 };
16751675 };
16761676 };
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 {
1685 flags: Flags,
1686 name: String,
1687 value: Storage.FlagUnion(.flags, .tag, UserValue),
1678 pub const Instance = extern struct {
1679 package: Package.Index,
1680 modules: PublicModules,
16881681
1689 pub const Flags = packed struct(u32) {
1690 tag: UserValue.Tag,
1691 used: bool,
1692 _: u28 = 0,
1682 pub const PublicModules = extern struct {
1683 keys: StringList,
1684 values: Module.List.Index,
16931685 };
16941686
1695 pub const List = struct {
1696 options: Storage.LengthPrefixedList(UserInputOption.Index),
1697
1698 pub const Index = enum(u32) {
1699 _,
1687 pub const Index = enum(u32) {
1688 root,
1689 _,
17001690
1701 pub fn get(this: @This(), c: *const Configuration) List {
1702 return extraData(c, List, @backingInt(this));
1703 }
1691 pub fn ptr(this: @This(), c: *const Configuration) *const Package.Instance {
1692 return &c.package_instances[@backingInt(this)];
1693 }
17041694
1705 pub fn slice(this: @This(), c: *const Configuration) []const UserInputOption.Index {
1706 return this.get(c).options.slice;
1707 }
1708 };
1695 pub fn package(this: @This(), c: *const Configuration) Package.Index {
1696 return this.ptr(c).package;
1697 }
17091698 };
17101699
1711 pub const Index = IndexType(@This());
1712 };
1713
1714 pub const UserValue = union(Tag) {
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 };
1700 pub const OptionalIndex = enum(u32) {
1701 root,
1702 none = max_u32,
1703 _,
17331704
1734 pub const Tag = enum(u3) {
1735 flag,
1736 scalar,
1737 list,
1738 map,
1739 lazy_path,
1740 lazy_path_list,
1705 pub fn init(i: Instance.Index) Instance.OptionalIndex {
1706 const result: Instance.OptionalIndex = @fromBackingInt(@intCast(@backingInt(i)));
1707 assert(result != .none);
1708 return result;
1709 }
17411710
1742 pub fn init(uv: @typeInfo(std.Build.UserValue).@"union".tag_type.?) @This() {
1743 return switch (uv) {
1744 inline else => |tag| @field(@This(), @tagName(tag)),
1711 pub fn unwrap(this: @This()) ?Instance.Index {
1712 return switch (this) {
1713 .none => null,
1714 .root => .root,
1715 _ => @fromBackingInt(@intCast(@backingInt(this))),
17451716 };
17461717 }
17471718 };
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 }
17931719 };
17941720};
17951721
......@@ -1797,7 +1723,7 @@ pub const Module = struct {
17971723 flags: Flags,
17981724 flags2: Flags2,
17991725 import_table: ImportTable.Index,
1800 owner: PackageInstance.Index,
1726 owner: Package.Instance.Index,
18011727 root_source_file: LazyPath.OptionalIndex,
18021728 resolved_target: ResolvedTarget.OptionalIndex,
18031729 c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String),
......@@ -2040,7 +1966,7 @@ pub const OptionalStringList = enum(u32) {
20401966pub const PathDep = extern struct {
20411967 flags: Flags,
20421968 sub: String,
2043 pkg: PackageInstance.OptionalIndex,
1969 pkg: Package.Instance.OptionalIndex,
20441970
20451971 pub const Flags = packed struct(u32) {
20461972 mode: Mode,
......@@ -3306,7 +3232,7 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration {
33063232 .available_options = try arena.alloc(AvailableOption, header.available_options_len),
33073233 .search_prefixes = try arena.alloc(String, header.search_prefixes_len),
33083234 .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),
33103236 .extra = try arena.alloc(u32, header.extra_len),
33113237 .default_step = header.default_step,
33123238 .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
4040 // been created yet with packageInstanceFromBuilder.
4141
4242 s.package_instance_map.putAssumeCapacityNoClobber(b, {});
43 var iter = b.graph.dependency_cache.valueIterator();
44 while (iter.next()) |dep| s.package_instance_map.putAssumeCapacityNoClobber(dep.*.builder, {});
43 for (b.graph.dependency_cache.values()) |dep| {
44 s.package_instance_map.putAssumeCapacityNoClobber(dep.builder, {});
45 }
4546
4647 try s.addPackageInstance(b);
47 var iter2 = b.graph.dependency_cache.valueIterator();
48 while (iter2.next()) |dep| try s.addPackageInstance(dep.*.builder);
48 for (b.graph.dependency_cache.values()) |dep| {
49 try s.addPackageInstance(dep.builder);
50 }
4951
5052 try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len);
5153 for (
......@@ -814,51 +816,15 @@ fn addPackageInstance(s: *Serialize, b: *std.Build) Allocator.Error!void {
814816
815817 const index = s.package_instance_map.getIndex(b).?;
816818
817 const options = try arena.alloc(
818 Configuration.PackageInstance.UserInputOption.Index,
819 b.user_input_options.count(),
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 }
819 const modules_values = try arena.alloc(Configuration.Module.Index, b.modules.count());
820 for (modules_values, b.modules.values()) |*dest_value, value| {
821 dest_value.* = try s.addModule(value);
853822 }
854823
855824 wc.package_instances.items[index] = .{
856825 .package = s.packageFromHash(b.pkg_hash),
857 .user_input_options = try wc.addDeduped(Configuration.PackageInstance.UserInputOption.List, .{
858 .options = .{ .slice = options },
859 }),
860826 .modules = .{
861 .keys = try wc.addStringList(modules_keys),
827 .keys = try wc.addStringList(b.modules.keys()),
862828 .values = try wc.addDeduped(Configuration.Module.List, .{
863829 .modules = .{ .slice = modules_values },
864830 }),
......@@ -866,7 +832,7 @@ fn addPackageInstance(s: *Serialize, b: *std.Build) Allocator.Error!void {
866832 };
867833}
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 {
870836 const arena = s.arena;
871837 const wc = s.wc;
872838
......@@ -876,7 +842,7 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato
876842 .list => |list| .{ .list = try wc.addStringList(list.items) },
877843 .map => |map| add: {
878844 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
881847 var i: usize = 0;
882848 var iter = map.iterator();
......@@ -885,13 +851,13 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato
885851
886852 keys[i] = entry.key_ptr.*;
887853 values[i] = try wc.addDeduped(
888 Configuration.PackageInstance.UserValue.Standalone,
854 Configuration.Package.Instance.UserValue.Standalone,
889855 .{ .flags = .{ .tag = value }, .value = .{ .u = value } },
890856 );
891857 }
892858
893859 break :add .{ .map = try wc.addDeduped(
894 Configuration.PackageInstance.UserValue.Map,
860 Configuration.Package.Instance.UserValue.Map,
895861 .{
896862 .keys = try wc.addStringList(keys),
897863 .values = .{ .slice = values },
......@@ -907,7 +873,7 @@ fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocato
907873 };
908874}
909875
910fn packageInstanceFromBuilder(s: *Serialize, b: *std.Build) Configuration.PackageInstance.Index {
876fn packageInstanceFromBuilder(s: *Serialize, b: *std.Build) Configuration.Package.Instance.Index {
911877 if (b.pkg_hash.len == 0) return .root;
912878 return @fromBackingInt(@intCast(s.package_instance_map.getIndex(b).?));
913879}