diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index ea2a05c1bb8e19b598e899ef3107348bd0e70da9..51195aaf23bea8bb551d175b61cc57804b3a50e1 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -2226,7 +2226,7 @@ fn prepare(maker: *Maker, step_indices: []const Configuration.Step.Index) !void } } else { log.err("{s}{s}: this step declares an upper bound of {d} bytes of memory, exceeding the available {d} bytes of memory", .{ - conf_step.owner.depPrefixSlice(c), + conf_step.owner.package(c).depPrefixSlice(c), conf_step.name.slice(c), max_rss, maker.available_rss, @@ -3320,13 +3320,13 @@ pub fn generatedPath(maker: *const Maker, index: Configuration.GeneratedFileInde pub fn packagePath( maker: *const Maker, arena: Allocator, - package_index: Configuration.Package.Index, + inst_index: Configuration.PackageInstance.Index, sub_path: []const u8, ) Allocator.Error!Path { const c = &maker.scanned_config.configuration; const graph = maker.graph; - if (package_index == .root) return .{ + if (inst_index == .root) return .{ .root_dir = graph.build_root_directory, .sub_path = sub_path, }; @@ -3337,7 +3337,7 @@ pub fn packagePath( // construct a cwd relative path here. return .{ .root_dir = .cwd(), - .sub_path = try Dir.path.join(arena, &.{ package_index.ptr(c).root_path.slice(c), sub_path }), + .sub_path = try Dir.path.join(arena, &.{ inst_index.package(c).ptr(c).root_path.slice(c), sub_path }), }; } @@ -3967,7 +3967,7 @@ fn confPathDepToCachePath( .root_dir = graph.build_root_directory, .sub_path = switch (path_dep.pkg.unwrap().?) { .root => sub_path, - else => |index| try Dir.path.join(arena, &.{ index.ptr(c).root_path.slice(c), sub_path }), + else => |index| try Dir.path.join(arena, &.{ index.package(c).ptr(c).root_path.slice(c), sub_path }), }, }, .zig_lib => .{ diff --git a/lib/compiler/Maker/ScannedConfig.zig b/lib/compiler/Maker/ScannedConfig.zig index 168ae8b7f61ec27abf36a4d46c2e675e982b0061..6f6faa01b65362928a35bf2d96261166abf308c1 100644 --- a/lib/compiler/Maker/ScannedConfig.zig +++ b/lib/compiler/Maker/ScannedConfig.zig @@ -104,6 +104,38 @@ pub fn print(sc: *const ScannedConfig, w: *Writer) Writer.Error!void { try tf.end(); } + { + var tf = try s.beginTupleField("package_instances", .{}); + for (c.package_instances) |inst| { + var sf = try tf.beginStructField(.{}); + + try sf.fieldPrefix("package"); + if (std.enums.tagName(Configuration.Package.Index, inst.package)) |name| { + try sf.container.serializer.ident(name); + } else { + try sf.container.serializer.int(@backingInt(inst.package)); + } + + var otf = try sf.beginTupleField("user_input_options", .{}); + for (inst.user_input_options.slice(c)) |option| { + var osf = try otf.beginStructField(.{}); + try sc.printStruct(&osf, Configuration.PackageInstance.UserInputOption, option.get(c)); + try osf.end(); + } + try otf.end(); + + var msf = try sf.beginStructField("modules", .{}); + for (inst.modules.keys.slice(c), inst.modules.values.slice(c)) |key, value| { + var msf2 = try msf.beginStructField(key.slice(c), .{}); + try sc.printStruct(&msf2, Configuration.Module, value.get(c)); + try msf2.end(); + } + try msf.end(); + + try sf.end(); + } + } + try s.end(); } diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 97b80ff490a06211b4fe40c1e2852a087e59fea8..5a76ce30e3cb619e99d86f59b809aae29aa18c3b 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -272,7 +272,7 @@ const InitializedDepContext = struct { } }; -const UserInputOptionsMap = StringHashMap(UserInputOption); +pub const UserInputOptionsMap = StringHashMap(UserInputOption); const AvailableOption = struct { name: []const u8, @@ -282,13 +282,13 @@ const AvailableOption = struct { enum_options: ?[]const []const u8, }; -const UserInputOption = struct { +pub const UserInputOption = struct { name: []const u8, value: UserValue, used: bool, }; -const UserValue = union(enum) { +pub const UserValue = union(enum) { flag: void, scalar: []const u8, list: std.array_list.Managed([]const u8), diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 52c8187b44741880e3457f9a141e304df2019e13..a19bdd80ff6b931ad3ab7cac60f37454bf7eb62b 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -17,6 +17,12 @@ available_options: []AvailableOption, search_prefixes: []String, /// Index 0 always exists and is the root package. packages: []Package, +/// Index 0 always exists and is the root package instance. +/// +/// Unlike `packages`, each item corresponds to a `std.Build`, which is a +/// package that was instantiated by running its build script with specific +/// input options. +package_instances: []PackageInstance, extra: []u32, default_step: Step.Index, generated_files_len: u32, @@ -33,6 +39,7 @@ pub const Header = extern struct { available_options_len: u32, search_prefixes_len: u32, packages_len: u32, + package_instances_len: u32, extra_len: u32, default_step: Step.Index, @@ -62,6 +69,7 @@ pub const Wip = struct { path_deps: std.ArrayList(PathDep) = .empty, search_prefixes: std.ArrayList(String) = .empty, packages: std.ArrayList(Package) = .empty, + package_instances: std.ArrayList(PackageInstance) = .empty, extra: std.ArrayList(u32) = .empty, next_generated_file_index: u32 = 0, cache_poison: bool = false, @@ -144,6 +152,7 @@ pub const Wip = struct { wip.path_deps.deinit(gpa); wip.search_prefixes.deinit(gpa); wip.packages.deinit(gpa); + wip.package_instances.deinit(gpa); wip.extra.deinit(gpa); wip.* = undefined; } @@ -164,6 +173,7 @@ pub const Wip = struct { .available_options_len = @intCast(wip.available_options.items.len), .search_prefixes_len = @intCast(wip.search_prefixes.items.len), .packages_len = @intCast(wip.packages.items.len), + .package_instances_len = @intCast(wip.package_instances.items.len), .extra_len = @intCast(wip.extra.items.len), .default_step = static.default_step, @@ -182,6 +192,7 @@ pub const Wip = struct { @ptrCast(wip.available_options.items), @ptrCast(wip.search_prefixes.items), @ptrCast(wip.packages.items), + @ptrCast(wip.package_instances.items), @ptrCast(wip.extra.items), }; try w.writeVecAll(&buffers); @@ -483,7 +494,7 @@ pub const AvailableOption = extern struct { pub const Step = extern struct { name: String, - owner: Package.Index, + owner: PackageInstance.Index, deps: Deps.Index, max_rss: MaxRss, extended: Storage.Extended(Flags, union(Tag) { @@ -1515,13 +1526,7 @@ pub const LazyPath = union(@This().Tag) { }; /// An index into `extra`. - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) LazyPath { - return extraData(c, LazyPath, @backingInt(this)); - } - }; + pub const Index = IndexType(@This()); /// An index into `extra`, or `null`. pub const OptionalIndex = enum(u32) { @@ -1538,7 +1543,7 @@ pub const LazyPath = union(@This().Tag) { pub const SourcePath = struct { flags: @This().Flags = .{}, - owner: Package.Index, + owner: PackageInstance.Index, sub_path: String, pub const Flags = packed struct(u32) { @@ -1671,11 +1676,128 @@ pub const Package = extern struct { }; }; +pub const PackageInstance = extern struct { + package: Package.Index, + user_input_options: UserInputOption.List.Index, + modules: PublicModules, + + pub const UserInputOption = struct { + flags: Flags, + name: String, + value: Storage.FlagUnion(.flags, .tag, UserValue), + + pub const Flags = packed struct(u32) { + tag: UserValue.Tag, + used: bool, + _: u28 = 0, + }; + + pub const List = struct { + options: Storage.LengthPrefixedList(UserInputOption.Index), + + pub const Index = enum(u32) { + _, + + pub fn get(this: @This(), c: *const Configuration) List { + return extraData(c, List, @backingInt(this)); + } + + pub fn slice(this: @This(), c: *const Configuration) []const UserInputOption.Index { + return this.get(c).options.slice; + } + }; + }; + + pub const Index = IndexType(@This()); + }; + + pub const UserValue = union(Tag) { + flag, + scalar: String, + list: StringList, + map: Map.Index, + lazy_path: LazyPath.Index, + lazy_path_list: Storage.LengthPrefixedList(LazyPath.Index), + + pub const Standalone = struct { + flags: Flags, + value: Storage.FlagUnion(.flags, .tag, UserValue), + + pub const Flags = packed struct(u32) { + tag: UserValue.Tag, + _: u29 = 0, + }; + + pub const Index = IndexType(@This()); + }; + + pub const Tag = enum(u3) { + flag, + scalar, + list, + map, + lazy_path, + lazy_path_list, + + pub fn init(uv: @typeInfo(std.Build.UserValue).@"union".tag_type.?) @This() { + return switch (uv) { + inline else => |tag| @field(@This(), @tagName(tag)), + }; + } + }; + + pub const Map = struct { + keys: StringList, + values: Storage.LengthPrefixedList(UserValue.Standalone.Index), + + pub const Index = IndexType(@This()); + }; + }; + + pub const PublicModules = extern struct { + keys: StringList, + values: Module.List.Index, + }; + + pub const Index = enum(u32) { + root, + _, + + pub fn ptr(this: @This(), c: *const Configuration) *const PackageInstance { + return &c.package_instances[@backingInt(this)]; + } + + pub fn package(this: @This(), c: *const Configuration) Package.Index { + return this.ptr(c).package; + } + }; + + pub const OptionalIndex = enum(u32) { + root, + none = max_u32, + _, + + pub fn init(i: Index) OptionalIndex { + const result: OptionalIndex = @fromBackingInt(@intCast(@backingInt(i))); + assert(result != .none); + return result; + } + + pub fn unwrap(this: @This()) ?Index { + return switch (this) { + .none => null, + .root => .root, + _ => @fromBackingInt(@intCast(@backingInt(this))), + }; + } + }; +}; + pub const Module = struct { flags: Flags, flags2: Flags2, import_table: ImportTable.Index, - owner: Package.Index, + owner: PackageInstance.Index, root_source_file: LazyPath.OptionalIndex, resolved_target: ResolvedTarget.OptionalIndex, c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String), @@ -1746,14 +1868,6 @@ pub const Module = struct { } }; - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) Module { - return extraData(c, Module, @backingInt(this)); - } - }; - pub const Flags = packed struct(u32) { optimize: Optimize, strip: DefaultingBool, @@ -1824,6 +1938,24 @@ pub const Module = struct { _: u30 = 0, }; }; + + pub const Index = IndexType(@This()); + + pub const List = struct { + modules: Storage.LengthPrefixedList(Module.Index), + + pub const Index = enum(u32) { + _, + + pub fn get(this: @This(), c: *const Configuration) List { + return extraData(c, List, @backingInt(this)); + } + + pub fn slice(this: @This(), c: *const Configuration) []const Module.Index { + return this.get(c).modules.slice; + } + }; + }; }; pub const ImportTable = struct { @@ -1908,7 +2040,7 @@ pub const OptionalStringList = enum(u32) { pub const PathDep = extern struct { flags: Flags, sub: String, - pkg: Package.OptionalIndex, + pkg: PackageInstance.OptionalIndex, pub const Flags = packed struct(u32) { mode: Mode, @@ -2051,13 +2183,7 @@ pub const SystemLib = struct { name: String, flags: Flags, - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) SystemLib { - return extraData(c, SystemLib, @backingInt(this)); - } - }; + pub const Index = IndexType(@This()); pub const UsePkgConfig = enum(u2) { /// Don't use pkg-config, just pass -lfoo where foo is name. @@ -2090,13 +2216,7 @@ pub const CSourceFiles = struct { args: Storage.FlagList(.flags, .args_len, String), sub_paths: Storage.LengthPrefixedList(String), - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) CSourceFiles { - return extraData(c, CSourceFiles, @backingInt(this)); - } - }; + pub const Index = IndexType(@This()); pub const Flags = packed struct(u32) { /// C compiler CLI flags. @@ -2110,13 +2230,7 @@ pub const CSourceFile = struct { file: LazyPath.Index, args: Storage.FlagList(.flags, .args_len, String), - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) CSourceFile { - return extraData(c, CSourceFile, @backingInt(this)); - } - }; + pub const Index = IndexType(@This()); pub const Flags = packed struct(u32) { /// C compiler CLI flags. @@ -2131,13 +2245,7 @@ pub const RcSourceFile = struct { args: Storage.FlagList(.flags, .args_len, String), include_paths: Storage.FlagLengthPrefixedList(.flags, .include_paths, LazyPath.Index), - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) RcSourceFile { - return extraData(c, RcSourceFile, @backingInt(this)); - } - }; + pub const Index = IndexType(@This()); pub const Flags = packed struct(u32) { /// C compiler CLI flags. @@ -2176,13 +2284,7 @@ pub const ResolvedTarget = struct { /// defaults will be resolved. result: TargetQuery.Index, - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) ResolvedTarget { - return extraData(c, ResolvedTarget, @backingInt(this)); - } - }; + pub const Index = IndexType(@This()); pub const OptionalIndex = enum(u32) { none = max_u32, @@ -3204,6 +3306,7 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration { .available_options = try arena.alloc(AvailableOption, header.available_options_len), .search_prefixes = try arena.alloc(String, header.search_prefixes_len), .packages = try arena.alloc(Package, header.packages_len), + .package_instances = try arena.alloc(PackageInstance, header.package_instances_len), .extra = try arena.alloc(u32, header.extra_len), .default_step = header.default_step, .generated_files_len = header.generated_files_len, @@ -3218,6 +3321,7 @@ pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration { @ptrCast(result.available_options), @ptrCast(result.search_prefixes), @ptrCast(result.packages), + @ptrCast(result.package_instances), @ptrCast(result.extra), }; try reader.readVecAll(&vecs); diff --git a/lib/std/Build/Serialize.zig b/lib/std/Build/Serialize.zig index 94092f5f0fc58c1528ff7f779715bf7f52be7e41..0366d993f2dcdaeb82b8ebbfd421ed1187b0a47d 100644 --- a/lib/std/Build/Serialize.zig +++ b/lib/std/Build/Serialize.zig @@ -10,8 +10,10 @@ const log = std.log; arena: Allocator, wc: *Configuration.Wip, module_map: std.array_hash_map.Auto(*std.Build.Module, Configuration.Module.Index) = .empty, -/// Keyed by package hash. -package_map: std.array_hash_map.String(Configuration.Package.Index) = .empty, +/// Keyed by package hash. Index + 1 corresponds to `Configuration.packages` index. +package_map: std.array_hash_map.String(void) = .empty, +/// Index corresponds to `Configuration.package_instances` index. +package_instance_map: std.array_hash_map.Auto(*std.Build, void) = .empty, /// Index corresponds to `Configuration.steps` index. step_map: std.array_hash_map.Auto(*Step, void) = .empty, @@ -22,9 +24,28 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi var s: Serialize = .{ .wc = wc, .arena = arena }; - // Serialize all of the packages first to seed the package_map, which is - // later used in calls to packageFromHash. - try s.addRootPackage(b); + // Seed the package_map, which is later used when serializing package + // instances. + try s.traversePackages(b); + + // Next, seed the package_instance_map, which is later used in calls to + // packageInstanceFromBuilder. + + _ = try wc.package_instances.addManyAsSlice(gpa, 1 + b.graph.dependency_cache.count()); + try s.package_instance_map.ensureTotalCapacity(arena, 1 + b.graph.dependency_cache.count()); + + // As serializing package instances also requires serializing the public + // modules of each one, we must first allocate an index for each package + // instance. Otherwise, addModule may access a package instance that hasn't + // been created yet with packageInstanceFromBuilder. + + s.package_instance_map.putAssumeCapacityNoClobber(b, {}); + var iter = b.graph.dependency_cache.valueIterator(); + while (iter.next()) |dep| s.package_instance_map.putAssumeCapacityNoClobber(dep.*.builder, {}); + + try s.addPackageInstance(b); + var iter2 = b.graph.dependency_cache.valueIterator(); + while (iter2.next()) |dep| try s.addPackageInstance(dep.*.builder); try wc.path_deps.ensureTotalCapacityPrecise(gpa, graph.configure_dependencies.items.len); for ( @@ -49,10 +70,10 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi .relative => |r| try wc.addString(r.sub_path), }, .pkg = switch (src.lazy_path) { - .src_path => |sp| .init(s.packageFromHash(sp.owner.pkg_hash)), + .src_path => |sp| .init(s.packageInstanceFromBuilder(sp.owner)), .generated => unreachable, .cwd_relative, .relative => .none, - .dependency => |d| .init(s.packageFromHash(d.dependency.builder.pkg_hash)), + .dependency => |d| .init(s.packageInstanceFromBuilder(d.dependency.builder)), }, }; } @@ -89,7 +110,7 @@ pub fn write(b: *std.Build, wc: *Configuration.Wip, writer: *std.Io.Writer) !voi try wc.steps.ensureTotalCapacity(gpa, s.step_map.entries.capacity); wc.steps.appendAssumeCapacity(.{ .name = try wc.addString(step.name), - .owner = s.packageFromHash(step.owner.pkg_hash), + .owner = s.packageInstanceFromBuilder(step.owner), .deps = deps, .max_rss = .fromBytes(step.max_rss), .extended = @fromBackingInt(@intCast(switch (step.tag) { @@ -727,7 +748,7 @@ pub fn packageOptions(b: *std.Build, wc: *Configuration.Wip) Allocator.Error!voi } } -fn addRootPackage(s: *Serialize, b: *std.Build) Allocator.Error!void { +fn traversePackages(s: *Serialize, b: *std.Build) Allocator.Error!void { const arena = s.arena; const wc = s.wc; @@ -741,7 +762,7 @@ fn addRootPackage(s: *Serialize, b: *std.Build) Allocator.Error!void { const deps = try arena.alloc(Configuration.Package.Dep, b.available_deps.len); for (deps, b.available_deps) |*dest, src| dest.* = try s.makePackageDep("", src[0], src[1]); - wc.packages.items[0].deps = try wc.addExtra(Configuration.Package.Dep.List, .{ + wc.packages.items[0].deps = try wc.addDeduped(Configuration.Package.Dep.List, .{ .deps = .{ .slice = deps }, }); } @@ -750,18 +771,18 @@ fn makePackageDep(s: *Serialize, parent_dep_prefix: []const u8, name: []const u8 const arena = s.arena; const wc = s.wc; - if (s.package_map.get(hash)) |index| return .{ + if (s.package_map.getIndex(hash)) |index| return .{ .name = try wc.addString(name), - .package = index, + .package = @fromBackingInt(@intCast(index + 1)), }; + const index: Configuration.Package.Index = @fromBackingInt(@intCast(wc.packages.items.len)); + try s.package_map.put(arena, hash, {}); + const entry = std.Build.package_map.get(hash) orelse unreachable; const dep_prefix = try arena.print("{s}{s}.", .{ parent_dep_prefix, name }); - const index: Configuration.Package.Index = @fromBackingInt(@intCast(wc.packages.items.len)); - try s.package_map.put(arena, hash, index); - try wc.packages.append(wc.gpa, .{ .dep_prefix = try wc.addString(dep_prefix), .hash = try wc.addString(hash), @@ -772,7 +793,7 @@ fn makePackageDep(s: *Serialize, parent_dep_prefix: []const u8, name: []const u8 const deps = try arena.alloc(Configuration.Package.Dep, entry.deps.len); for (deps, entry.deps) |*dest, src| dest.* = try s.makePackageDep(dep_prefix, src[0], src[1]); - wc.packages.items[@backingInt(index)].deps = try wc.addExtra(Configuration.Package.Dep.List, .{ + wc.packages.items[@backingInt(index)].deps = try wc.addDeduped(Configuration.Package.Dep.List, .{ .deps = .{ .slice = deps }, }); @@ -784,7 +805,111 @@ fn makePackageDep(s: *Serialize, parent_dep_prefix: []const u8, name: []const u8 fn packageFromHash(s: *Serialize, pkg_hash: []const u8) Configuration.Package.Index { if (pkg_hash.len == 0) return .root; - return s.package_map.get(pkg_hash) orelse std.debug.panic("unrecognized package hash: {q}", .{pkg_hash}); + return @fromBackingInt(@intCast(s.package_map.getIndex(pkg_hash).? + 1)); +} + +fn addPackageInstance(s: *Serialize, b: *std.Build) Allocator.Error!void { + const arena = s.arena; + const wc = s.wc; + + const index = s.package_instance_map.getIndex(b).?; + + const options = try arena.alloc( + Configuration.PackageInstance.UserInputOption.Index, + b.user_input_options.count(), + ); + + { + var i: usize = 0; + var iter = b.user_input_options.valueIterator(); + while (iter.next()) |option| : (i += 1) { + options[i] = try wc.addExtra(Configuration.PackageInstance.UserInputOption, .{ + .flags = .{ + .tag = .init(option.value), + .used = option.used, + }, + .name = try wc.addString(option.name), + .value = .{ .u = try s.makeUserValue(&option.value) }, + }); + } + } + + const modules_keys = try arena.alloc( + []const u8, + b.modules.count(), + ); + const modules_values = try arena.alloc( + Configuration.Module.Index, + b.modules.count(), + ); + + { + var i: usize = 0; + var iter = b.modules.iterator(); + while (iter.next()) |entry| : (i += 1) { + modules_keys[i] = entry.key_ptr.*; + modules_values[i] = try s.addModule(entry.value_ptr.*); + } + } + + wc.package_instances.items[index] = .{ + .package = s.packageFromHash(b.pkg_hash), + .user_input_options = try wc.addDeduped(Configuration.PackageInstance.UserInputOption.List, .{ + .options = .{ .slice = options }, + }), + .modules = .{ + .keys = try wc.addStringList(modules_keys), + .values = try wc.addDeduped(Configuration.Module.List, .{ + .modules = .{ .slice = modules_values }, + }), + }, + }; +} + +fn makeUserValue(s: *Serialize, user_value: *const std.Build.UserValue) Allocator.Error!Configuration.PackageInstance.UserValue { + const arena = s.arena; + const wc = s.wc; + + return switch (user_value.*) { + .flag => .flag, + .scalar => |str| .{ .scalar = try wc.addString(str) }, + .list => |list| .{ .list = try wc.addStringList(list.items) }, + .map => |map| add: { + const keys = try arena.alloc([]const u8, map.count()); + const values = try arena.alloc(Configuration.PackageInstance.UserValue.Standalone.Index, map.count()); + + var i: usize = 0; + var iter = map.iterator(); + while (iter.next()) |entry| : (i += 1) { + const value = try s.makeUserValue(entry.value_ptr.*); + + keys[i] = entry.key_ptr.*; + values[i] = try wc.addDeduped( + Configuration.PackageInstance.UserValue.Standalone, + .{ .flags = .{ .tag = value }, .value = .{ .u = value } }, + ); + } + + break :add .{ .map = try wc.addDeduped( + Configuration.PackageInstance.UserValue.Map, + .{ + .keys = try wc.addStringList(keys), + .values = .{ .slice = values }, + }, + ) }; + }, + .lazy_path => |lp| .{ .lazy_path = try s.addLazyPath(lp) }, + .lazy_path_list => |lp_list| add: { + const paths = try arena.alloc(Configuration.LazyPath.Index, lp_list.items.len); + for (paths, lp_list.items) |*dest, src| dest.* = try s.addLazyPath(src); + break :add .{ .lazy_path_list = .{ .slice = paths } }; + }, + }; +} + +fn packageInstanceFromBuilder(s: *Serialize, b: *std.Build) Configuration.PackageInstance.Index { + if (b.pkg_hash.len == 0) return .root; + return @fromBackingInt(@intCast(s.package_instance_map.getIndex(b).?)); } fn addOptionalLazyPathEnum(s: *Serialize, lp: ?std.Build.LazyPath) !Configuration.LazyPath.OptionalIndex { @@ -793,7 +918,7 @@ fn addOptionalLazyPathEnum(s: *Serialize, lp: ?std.Build.LazyPath) !Configuratio .src_path => |src_path| i: { const sub_path = try wc.addString(src_path.sub_path); break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{ - .owner = s.packageFromHash(src_path.owner.pkg_hash), + .owner = s.packageInstanceFromBuilder(src_path.owner), .sub_path = sub_path, }); }, @@ -821,7 +946,7 @@ fn addOptionalLazyPathEnum(s: *Serialize, lp: ?std.Build.LazyPath) !Configuratio .dependency => |dependency| i: { const sub_path = try wc.addString(dependency.sub_path); break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{ - .owner = s.packageFromHash(dependency.dependency.builder.pkg_hash), + .owner = s.packageInstanceFromBuilder(dependency.dependency.builder), .sub_path = sub_path, }); }, @@ -1250,7 +1375,7 @@ fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index { const c_macros = try initStringList(s, m.c_macros.items); const export_symbol_names = try initStringList(s, m.export_symbol_names); - const module_index: Configuration.Module.Index = try wc.addExtra(Configuration.Module, .{ + const module_index: Configuration.Module.Index = try wc.addDeduped(Configuration.Module, .{ .flags = .{ .optimize = .init(m.optimize), .strip = .init(m.strip), @@ -1281,7 +1406,7 @@ fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index { .link_libcpp = .init(m.link_libcpp), .no_builtin = .init(m.no_builtin), }, - .owner = s.packageFromHash(m.owner.pkg_hash), + .owner = s.packageInstanceFromBuilder(m.owner), .root_source_file = try s.addOptionalLazyPathEnum(m.root_source_file), .import_table = .invalid, .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),