From cfa0b7cd9529beb987f35246663d335ba9ae6157 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Feb 2026 16:02:31 -0800 Subject: [PATCH] configure runner: serialization of Module --- lib/compiler/configure_runner.zig | 99 +++- lib/std/Build/Module.zig | 15 +- lib/std/builtin.zig | 2 +- lib/std/zig/Configuration.zig | 744 +++++++++++++++++++++++++++++- 4 files changed, 824 insertions(+), 36 deletions(-) diff --git a/lib/compiler/configure_runner.zig b/lib/compiler/configure_runner.zig index 64ebf844ae..b61d2b57fa 100644 --- a/lib/compiler/configure_runner.zig +++ b/lib/compiler/configure_runner.zig @@ -227,6 +227,9 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { const arena = graph.arena; const gpa = wc.gpa; + var module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty; + defer module_map.deinit(gpa); + // Starting from all top-level steps in `b`, traverse the entire step graph // and add all step dependencies implied by module graphs. const top_level_steps = b.top_level_steps.values(); @@ -360,7 +363,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .install_name = c.install_name != null, .entitlements = c.entitlements != null, }, - .root_module = try addModule(wc, c.root_module), + .root_module = try addModule(wc, &module_map, c.root_module), .root_name = try wc.addString(c.name), })); @@ -462,20 +465,97 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { }); } -fn addModule(wc: *Configuration.Wip, module: *std.Build.Module) !Configuration.Module { - _ = wc; - _ = module; - @panic("TODO"); +fn addModule( + wc: *Configuration.Wip, + module_map: *std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index), + m: *std.Build.Module, +) !Configuration.Module.Index { + if (module_map.get(m)) |index| return index; + + const gpa = wc.gpa; + const import_table: Configuration.ImportTable = @enumFromInt(wc.extra.items.len); + const import_table_extra_len = 1 + 2 * m.import_table.entries.len; + try wc.extra.ensureUnusedCapacity(gpa, import_table_extra_len); + wc.extra.items.len += import_table_extra_len; + wc.extra.appendAssumeCapacity(@intCast(m.import_table.entries.len)); + wc.extra.items[@intFromEnum(import_table)] = @intCast(m.import_table.entries.len); + for ( + m.import_table.keys(), + @intFromEnum(import_table) + 1.., + ) |mod_name, extra_index| { + wc.extra.items[extra_index] = @intFromEnum(try wc.addString(mod_name)); + } + for ( + m.import_table.values(), + @intFromEnum(import_table) + 1 + m.import_table.entries.len.., + ) |dep, extra_index| { + // TODO module dependencies can be cyclic + wc.extra.items[extra_index] = @intFromEnum(try addModule(wc, module_map, dep)); + } + + const module_index: Configuration.Module.Index = @enumFromInt(try wc.addExtra(@as(Configuration.Module, .{ + .flags = .{ + .optimize = .init(m.optimize), + .strip = .init(m.strip), + .unwind_tables = .init(m.unwind_tables), + .dwarf_format = .init(m.dwarf_format), + .single_threaded = .init(m.strip), + .stack_protector = .init(m.strip), + .stack_check = .init(m.strip), + .sanitize_c = .init(m.sanitize_c), + .sanitize_thread = .init(m.strip), + .fuzz = .init(m.strip), + .code_model = m.code_model, + .c_macros = m.c_macros.items.len != 0, + .include_dirs = m.include_dirs.items.len != 0, + .lib_paths = m.lib_paths.items.len != 0, + .rpaths = m.rpaths.items.len != 0, + .frameworks = m.frameworks.entries.len != 0, + .link_objects = m.link_objects.items.len != 0, + .export_symbol_names = m.export_symbol_names.len != 0, + }, + .flags2 = .{ + .valgrind = .init(m.strip), + .pic = .init(m.strip), + .red_zone = .init(m.strip), + .omit_frame_pointer = .init(m.strip), + .error_tracing = .init(m.strip), + .link_libc = .init(m.strip), + .link_libcpp = .init(m.strip), + .no_builtin = .init(m.strip), + }, + .owner = builderToPackage(m.owner), + .root_source_file = try addOptionalLazyPath(wc, m.root_source_file), + .import_table = import_table, + .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target), + }))); + + std.log.err("TODO serialize the trailing Module data", .{}); + + try module_map.putNoClobber(gpa, m, module_index); + + return module_index; +} + +fn addOptionalResolvedTarget( + wc: *Configuration.Wip, + optional_resolved_target: ?std.Build.ResolvedTarget, +) !Configuration.ResolvedTarget.OptionalIndex { + const resolved_target = optional_resolved_target orelse return .none; + // TODO dedupe + return @enumFromInt(try wc.addExtra(@as(Configuration.ResolvedTarget, .{ + .query = try wc.addTargetQuery(resolved_target.query), + .result = try wc.addTarget(resolved_target.result), + }))); } fn addOptionalLazyPath(wc: *Configuration.Wip, lp: ?std.Build.LazyPath) !Configuration.OptionalLazyPath { return @enumFromInt(switch (lp orelse return .none) { .src_path => |src_path| i: { - const owner = builderToPackage(src_path.owner); const sub_path = try wc.addString(src_path.sub_path); break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{ .flags = .{}, - .owner = owner, + .owner = builderToPackage(src_path.owner), .sub_path = sub_path, })); }, @@ -494,18 +574,17 @@ fn addOptionalLazyPath(wc: *Configuration.Wip, lp: ?std.Build.LazyPath) !Configu })); }, .dependency => |dependency| i: { - const owner = builderToPackage(dependency.dependency.builder); const sub_path = try wc.addString(dependency.sub_path); break :i try wc.addExtra(@as(Configuration.LazyPath.SourcePath, .{ .flags = .{}, - .owner = owner, + .owner = builderToPackage(dependency.dependency.builder), .sub_path = sub_path, })); }, }); } -fn builderToPackage(b: *std.Build) Configuration.Package { +fn builderToPackage(b: *std.Build) Configuration.Package.Index { _ = b; @panic("TODO"); } diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index 077c255a50..7b42c9a110 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -1,3 +1,11 @@ +const Module = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const LazyPath = std.Build.LazyPath; +const Step = std.Build.Step; +const ArrayList = std.ArrayList; + /// The one responsible for creating this module. owner: *std.Build, root_source_file: ?LazyPath, @@ -703,10 +711,3 @@ pub fn getGraph(root: *Module) Graph { root.cached_graph = result; return result; } - -const Module = @This(); -const std = @import("std"); -const assert = std.debug.assert; -const LazyPath = std.Build.LazyPath; -const Step = std.Build.Step; -const ArrayList = std.ArrayList; diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 97846ed035..80d8178a2e 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -93,7 +93,7 @@ pub const AtomicRmwOp = enum { /// /// This data structure is used by the Zig language code generation and /// therefore must be kept in sync with the compiler implementation. -pub const CodeModel = enum { +pub const CodeModel = enum(u4) { default, extreme, kernel, diff --git a/lib/std/zig/Configuration.zig b/lib/std/zig/Configuration.zig index 9009156191..61ac838b12 100644 --- a/lib/std/zig/Configuration.zig +++ b/lib/std/zig/Configuration.zig @@ -29,6 +29,7 @@ pub const Wip = struct { gpa: Allocator, string_table: StringTable = .empty, deps_table: DepsTable = .empty, + targets_table: TargetsTable = .empty, string_bytes: std.ArrayList(u8) = .empty, unlazy_deps: std.ArrayList(String) = .empty, @@ -37,6 +38,7 @@ pub const Wip = struct { extra: std.ArrayList(u32) = .empty, const DepsTable = std.HashMapUnmanaged(Deps, void, DepsTableContext, std.hash_map.default_max_load_percentage); + const TargetsTable = std.HashMapUnmanaged(TargetQuery.Index, void, TargetsTableContext, std.hash_map.default_max_load_percentage); const DepsTableContext = struct { extra: []const u32, @@ -56,6 +58,21 @@ pub const Wip = struct { } }; + const TargetsTableContext = struct { + extra: []const u32, + + pub fn eql(ctx: @This(), a: TargetQuery.Index, b: TargetQuery.Index) bool { + const slice_a = a.extraSlice(ctx.extra); + const slice_b = b.extraSlice(ctx.extra); + return std.mem.eql(u32, slice_a, slice_b); + } + + pub fn hash(ctx: @This(), key: TargetQuery.Index) u64 { + const slice = key.extraSlice(ctx.extra); + return std.hash_map.hashString(@ptrCast(slice)); + } + }; + const StringTable = std.HashMapUnmanaged(String, void, StringTableContext, std.hash_map.default_max_load_percentage); const StringTableContext = struct { bytes: []const u8, @@ -144,6 +161,157 @@ pub const Wip = struct { return new_off; } + pub fn addSemVer(wip: *Wip, sv: std.SemanticVersion) Allocator.Error!String { + var buffer: [256]u8 = undefined; + var writer: std.Io.Writer = .fixed(&buffer); + sv.format(&writer) catch return error.OutOfMemory; + return addString(wip, writer.buffered()); + } + + pub fn addTargetQuery(wip: *Wip, q: std.Target.Query) !TargetQuery.OptionalIndex { + if (q.isNative()) return .none; + const gpa = wip.gpa; + const cpu_name: ?String = switch (q.cpu_model) { + .native, .baseline, .determined_by_arch_os => null, + .explicit => |model| try wip.addString(model.name), + }; + const os_version_min: ?u32 = if (q.os_version_min) |ver| switch (ver) { + .none => null, + .semver => |sem_ver| @intFromEnum(try wip.addSemVer(sem_ver)), + .windows => |win_ver| @intFromEnum(win_ver), + } else null; + const os_version_max: ?u32 = if (q.os_version_max) |ver| switch (ver) { + .none => null, + .semver => |sem_ver| @intFromEnum(try wip.addSemVer(sem_ver)), + .windows => |win_ver| @intFromEnum(win_ver), + } else null; + const glibc_version: ?String = if (q.glibc_version) |sem_ver| try wip.addSemVer(sem_ver) else null; + const dynamic_linker: ?String = if (q.dynamic_linker) |*dl| + if (dl.get()) |s| try wip.addString(s) else .empty + else + null; + const cpu_features_add_empty = q.cpu_features_add.isEmpty(); + const cpu_features_sub_empty = q.cpu_features_sub.isEmpty(); + try wip.extra.ensureUnusedCapacity(gpa, @typeInfo(TargetQuery).@"struct".fields.len + 6 + + 2 * ((@sizeOf(std.Target.Cpu.Feature.Set) + 3) / 4)); + const result_index: TargetQuery.Index = @enumFromInt(wip.addExtraAssumeCapacity(@as(TargetQuery, .{ + .flags = .{ + .cpu_arch = .init(q.cpu_arch), + .cpu_model = .init(q.cpu_model), + .cpu_features_add = !cpu_features_add_empty, + .cpu_features_sub = !cpu_features_sub_empty, + .os_tag = .init(q.os_tag), + .abi = .init(q.abi), + .object_format = .init(q.ofmt), + .os_version_min = .init(q.os_version_min), + .os_version_max = .init(q.os_version_max), + .glibc_version = q.glibc_version != null, + .android_api_level = q.android_api_level != null, + .dynamic_linker = q.dynamic_linker != null, + }, + }))); + if (!cpu_features_add_empty) wip.extra.appendSliceAssumeCapacity(@ptrCast(&q.cpu_features_add.ints)); + if (!cpu_features_sub_empty) wip.extra.appendSliceAssumeCapacity(@ptrCast(&q.cpu_features_sub.ints)); + wip.addExtraOptionalStringAssumeCapacity(cpu_name); + if (os_version_min) |v| wip.extra.appendAssumeCapacity(v); + if (os_version_max) |v| wip.extra.appendAssumeCapacity(v); + wip.addExtraOptionalStringAssumeCapacity(glibc_version); + if (q.android_api_level) |x| wip.extra.appendAssumeCapacity(x); + wip.addExtraOptionalStringAssumeCapacity(dynamic_linker); + + // Deduplicate. + const gop = try wip.targets_table.getOrPutContext(gpa, result_index, @as(TargetsTableContext, .{ + .extra = wip.extra.items, + })); + if (gop.found_existing) { + wip.extra.items.len = @intFromEnum(result_index); + return .init(gop.key_ptr.*); + } else { + return .init(result_index); + } + } + + pub fn addTarget(wip: *Wip, t: std.Target) !TargetQuery.Index { + const gpa = wip.gpa; + const cpu_name: String = try wip.addString(t.cpu.model.name); + + const os_version_min: ?u32, const os_version_max: ?u32, const glibc_version: ?String, const android_api_level: ?u32 = switch (t.os.versionRange()) { + .none => .{ + null, + null, + null, + null, + }, + .semver => |range| .{ + @intFromEnum(try wip.addSemVer(range.min)), + @intFromEnum(try wip.addSemVer(range.max)), + null, + null, + }, + .hurd => |hurd| .{ + @intFromEnum(try wip.addSemVer(hurd.range.min)), + @intFromEnum(try wip.addSemVer(hurd.range.max)), + try wip.addSemVer(hurd.glibc), + null, + }, + .linux => |linux| .{ + @intFromEnum(try wip.addSemVer(linux.range.min)), + @intFromEnum(try wip.addSemVer(linux.range.max)), + try wip.addSemVer(linux.glibc), + linux.android, + }, + .windows => |range| .{ + @intFromEnum(range.min), + @intFromEnum(range.max), + null, + null, + }, + }; + const dynamic_linker: ?String = if (t.dynamic_linker.get()) |dl| try wip.addString(dl) else null; + const cpu_features_add_empty = t.cpu.features.isEmpty(); + const os_version: TargetQuery.OsVersion = switch (t.os.versionRange()) { + .none => .none, + .semver, .linux, .hurd => .semver, + .windows => .windows, + }; + try wip.extra.ensureUnusedCapacity(gpa, @typeInfo(TargetQuery).@"struct".fields.len + 6 + + 2 * ((@sizeOf(std.Target.Cpu.Feature.Set) + 3) / 4)); + const result_index: TargetQuery.Index = @enumFromInt(wip.addExtraAssumeCapacity(@as(TargetQuery, .{ + .flags = .{ + .cpu_arch = .init(t.cpu.arch), + .cpu_model = .explicit, + .cpu_features_add = !cpu_features_add_empty, + .cpu_features_sub = false, + .os_tag = .init(t.os.tag), + .abi = .init(t.abi), + .object_format = .init(t.ofmt), + .os_version_min = os_version, + .os_version_max = os_version, + .glibc_version = glibc_version != null, + .android_api_level = android_api_level != null, + .dynamic_linker = dynamic_linker != null, + }, + }))); + if (!cpu_features_add_empty) wip.extra.appendSliceAssumeCapacity(@ptrCast(&t.cpu.features.ints)); + wip.addExtraOptionalStringAssumeCapacity(cpu_name); + if (os_version_min) |v| wip.extra.appendAssumeCapacity(v); + if (os_version_max) |v| wip.extra.appendAssumeCapacity(v); + wip.addExtraOptionalStringAssumeCapacity(glibc_version); + if (android_api_level) |x| wip.extra.appendAssumeCapacity(x); + wip.addExtraOptionalStringAssumeCapacity(dynamic_linker); + + // Deduplicate. + const gop = try wip.targets_table.getOrPutContext(gpa, result_index, @as(TargetsTableContext, .{ + .extra = wip.extra.items, + })); + if (gop.found_existing) { + wip.extra.items.len = @intFromEnum(result_index); + return gop.key_ptr.*; + } else { + return result_index; + } + } + pub fn prepareDeps(wip: *Wip, n: usize) Allocator.Error![]u32 { const slice = try wip.extra.addManyAsSlice(wip.gpa, n + 1); slice[0] = @intCast(n); @@ -178,6 +346,11 @@ pub const Wip = struct { return result; } + fn addExtraOptionalStringAssumeCapacity(wip: *Wip, optional_string: ?String) void { + const string = optional_string orelse return; + wip.extra.appendAssumeCapacity(@intFromEnum(string)); + } + fn setExtra(wip: *Wip, index: usize, extra: anytype) void { const fields = @typeInfo(@TypeOf(extra)).@"struct".fields; var i = index; @@ -386,7 +559,7 @@ pub const Step = extern struct { flags3: Flags3, flags4: Flags4, - root_module: Module, + root_module: Module.Index, root_name: String, pub const ExpectedCompileErrors = enum(u3) { contains, exact, starts_with, stderr_contains, none }; @@ -466,19 +639,6 @@ pub const Step = extern struct { }; } }; - pub const DefaultingBool = enum(u2) { - false, - true, - default, - - pub fn init(b: ?bool) DefaultingBool { - return switch (b orelse return .default) { - false => .false, - true => .true, - }; - } - }; - pub const Subsystem = enum(u4) { console, windows, @@ -626,7 +786,7 @@ pub const LazyPath = enum(u32) { pub const SourcePath = struct { flags: Flags, - owner: Package, + owner: Package.Index, sub_path: String, pub const Flags = packed struct(u32) { @@ -662,11 +822,168 @@ pub const LazyPath = enum(u32) { }; }; -pub const Package = enum(u32) { - _, +pub const Package = extern struct { + hash: String, + build_root: OptionalString, + + pub const Index = enum(u32) { + root = maxInt(u32), + _, + }; }; -pub const Module = enum(u32) { +/// Trailing: +/// * c_macros: LengthPrefixedList(String), // if flag is set +/// * lib_paths: LengthPrefixedList(LazyPath), // if flag is set +/// * export_symbol_names: LengthPrefixedList(String), // if flag is set +/// * frameworks: FlagsPrefixedList(FrameworkFlags), // if flag is set +/// * include_dirs: UnionList(IncludeDir), // if flag is set +/// * rpaths: UnionList(RPath), // if flag is set +/// * link_objects: UnionList(LinkObject), // if flag is set +pub const Module = struct { + flags: Flags, + flags2: Flags2, + owner: Package.Index, + root_source_file: OptionalLazyPath, + import_table: ImportTable, + resolved_target: ResolvedTarget.OptionalIndex, + + pub const Optimize = enum(u3) { + debug, + safe, + fast, + small, + default, + + pub fn init(o: ?std.builtin.OptimizeMode) Optimize { + return switch (o orelse return .default) { + .Debug => .debug, + .ReleaseSafe => .safe, + .ReleaseFast => .fast, + .ReleaseSmall => .small, + }; + } + }; + + pub const UnwindTables = enum(u2) { + none, + sync, + async, + default, + + pub fn init(ut: ?std.builtin.UnwindTables) UnwindTables { + return switch (ut orelse return .default) { + .none => .none, + .sync => .sync, + .async => .async, + }; + } + }; + + pub const SanitizeC = enum(u2) { + off, + trap, + full, + default, + + pub fn init(sc: ?std.zig.SanitizeC) SanitizeC { + return switch (sc orelse return .default) { + .off => .off, + .trap => .trap, + .full => .full, + }; + } + }; + + pub const DwarfFormat = enum(u2) { + @"32", + @"64", + default, + + pub fn init(df: ?std.dwarf.Format) DwarfFormat { + return switch (df orelse return .default) { + .@"32" => .@"32", + .@"64" => .@"64", + }; + } + }; + + pub const Index = enum(u32) { + _, + }; + + pub const Flags = packed struct(u32) { + optimize: Optimize, + strip: DefaultingBool, + unwind_tables: UnwindTables, + dwarf_format: DwarfFormat, + single_threaded: DefaultingBool, + stack_protector: DefaultingBool, + stack_check: DefaultingBool, + sanitize_c: SanitizeC, + sanitize_thread: DefaultingBool, + fuzz: DefaultingBool, + code_model: std.builtin.CodeModel, + c_macros: bool, + include_dirs: bool, + lib_paths: bool, + rpaths: bool, + frameworks: bool, + link_objects: bool, + export_symbol_names: bool, + }; + + pub const Flags2 = packed struct(u32) { + valgrind: DefaultingBool, + pic: DefaultingBool, + red_zone: DefaultingBool, + omit_frame_pointer: DefaultingBool, + error_tracing: DefaultingBool, + link_libc: DefaultingBool, + link_libcpp: DefaultingBool, + no_builtin: DefaultingBool, + _: u16 = 0, + }; + + pub const IncludeDir = union(enum(u3)) { + path: LazyPath, + path_system: LazyPath, + path_after: LazyPath, + framework_path: LazyPath, + framework_path_system: LazyPath, + /// Always `Step.Tag.compile`. + other_step: Step.Index, + /// Always `Step.Tag.config_header`. + config_header_step: Step.Index, + embed_path: LazyPath, + }; + + pub const RPath = union(enum(u1)) { + lazy_path: LazyPath, + special: String, + }; + + pub const LinkObject = union(enum(u3)) { + static_path: LazyPath, + /// Always `Step.Tag.compile`. + other_step: Step.Index, + system_lib: SystemLib, + assembly_file: LazyPath, + c_source_file: CSourceFile.Index, + c_source_files: CSourceFiles.Index, + win32_resource_file: RcSourceFile.Index, + }; + + pub const FrameworkFlags = packed struct(u2) { + needed: bool, + weak: bool, + }; +}; + +/// Points into `extra`, first element is len, then: +/// * import_name: String, // for each len +/// * Module.Index, // for each len +pub const ImportTable = enum(u32) { _, }; @@ -734,6 +1051,397 @@ pub const String = enum(u32) { } }; +pub const DefaultingBool = enum(u2) { + false, + true, + default, + + pub fn init(b: ?bool) DefaultingBool { + return switch (b orelse return .default) { + false => .false, + true => .true, + }; + } +}; + +pub const SystemLib = struct { + name: String, + flags: Flags, + + pub const Index = enum(u32) { + _, + }; + + pub const UsePkgConfig = enum(u2) { no, yes, force }; + pub const LinkMode = enum { static, dynamic }; + + pub const Flags = packed struct(u32) { + needed: bool, + weak: bool, + use_pkg_config: UsePkgConfig, + preferred_link_mode: LinkMode, + search_strategy: SearchStrategy, + }; + + pub const SearchStrategy = enum(u2) { paths_first, mode_first, no_fallback }; +}; + +/// Trailing: +/// * flag: String, // for each flags_len +/// * sub_path: String, // for each files_len +pub const CSourceFiles = struct { + root: LazyPath, + files_len: u32, + flags: Flags, + + pub const Index = enum(u32) { + _, + }; + + pub const Flags = packed struct(u32) { + /// C compiler CLI flags. + flags_len: u29, + lang: OptionalCSourceLanguage, + }; +}; + +/// Trailing: +/// * flag: String, // for each flags_len +pub const CSourceFile = struct { + file: LazyPath, + flags: Flags, + + pub const Index = enum(u32) { + _, + }; + + pub const Flags = packed struct(u32) { + /// C compiler CLI flags. + flags_len: u29, + lang: OptionalCSourceLanguage, + }; +}; + +pub const OptionalCSourceLanguage = enum(u3) { + c, + cpp, + objective_c, + objective_cpp, + assembly, + assembly_with_preprocessor, + default, +}; + +pub const RcSourceFile = struct { + file: LazyPath, + /// Any option that rc.exe accepts will work here, with the exception of: + /// - `/fo`: The output filename is set by the build system + /// - `/p`: Only running the preprocessor is not supported in this context + /// - `/:no-preprocess` (non-standard option): Not supported in this context + /// - Any MUI-related option + /// https://learn.microsoft.com/en-us/windows/win32/menurc/using-rc-the-rc-command-line- + /// + /// Implicitly defined options: + /// /x (ignore the INCLUDE environment variable) + /// /D_DEBUG or /DNDEBUG depending on the optimization mode + flags: []const []const u8 = &.{}, + /// Include paths that may or may not exist yet and therefore need to be + /// specified as a LazyPath. Each path will be appended to the flags + /// as `/I `. + include_paths: []const LazyPath = &.{}, + + pub const Index = enum(u32) { + _, + }; +}; + +pub const ResolvedTarget = struct { + /// none indicates host. + query: TargetQuery.OptionalIndex, + /// defaults will be resolved. + result: TargetQuery.Index, + + pub const Index = enum(u32) { + _, + }; + + pub const OptionalIndex = enum(u32) { + none = maxInt(u32), + _, + }; +}; + +/// Trailing: +/// * cpu_features_add: std.Target.Feature.Set, // if flag set +/// * cpu_features_sub: std.Target.Feature.Set, // if flag set +/// * cpu_name: String, // if cpu_model is explicit +/// * os_version_min: WindowsVersion // if os_version_min is windows +/// * os_version_min: String // if os_version_min is semver +/// * os_version_max: WindowsVersion // if os_version_max is windows +/// * os_version_max: String // if os_version_max is semver +/// * glibc_version: String, // if flag is set +/// * android_api_level: u32, // if flag is set +/// * dynamic_linker: String, // if flag is set +pub const TargetQuery = struct { + flags: Flags, + + pub const Index = enum(u32) { + _, + + pub fn extraSlice(i: Index, extra: []const u32) []const u32 { + return extra[@intFromEnum(i)..][0..length(i, extra)]; + } + + pub fn length(i: Index, extra: []const u32) usize { + //const flags = getExtra(extra, @intFromEnum(i), TargetQuery).flags; + const flags: Flags = @bitCast(extra[@intFromEnum(i)]); + const feature_set_size: usize = (@sizeOf(std.Target.Cpu.Feature.Set) + 3) / 4; + return @typeInfo(TargetQuery).@"struct".fields.len + + (if (flags.cpu_features_add) feature_set_size else 0) + + (if (flags.cpu_features_sub) feature_set_size else 0) + + @intFromBool(flags.cpu_model == .explicit) + + @as(usize, switch (flags.os_version_min) { + .semver, .windows => 1, + else => 0, + }) + + @as(usize, switch (flags.os_version_max) { + .semver, .windows => 1, + else => 0, + }) + + @intFromBool(flags.glibc_version) + + @intFromBool(flags.android_api_level) + + @intFromBool(flags.dynamic_linker); + } + }; + + pub const OptionalIndex = enum(u32) { + none = maxInt(u32), + _, + + pub fn init(i: Index) OptionalIndex { + const result: OptionalIndex = @enumFromInt(@intFromEnum(i)); + assert(result != .none); + return result; + } + }; + + pub const CpuModel = enum(u2) { + native, + baseline, + determined_by_arch_os, + explicit, + + pub fn init(x: std.Target.Query.CpuModel) @This() { + return switch (x) { + .native => .native, + .baseline => .baseline, + .determined_by_arch_os => .determined_by_arch_os, + .explicit => .explicit, + }; + } + }; + pub const OsVersion = enum(u2) { + none, + semver, + windows, + default, + + pub fn init(x: ?std.Target.Query.OsVersion) @This() { + return switch (x orelse return .default) { + .none => .none, + .semver => .semver, + .windows => .windows, + }; + } + }; + pub const Abi = enum(u5) { + none, + gnu, + gnuabin32, + gnuabi64, + gnueabi, + gnueabihf, + gnuf32, + gnusf, + gnux32, + eabi, + eabihf, + ilp32, + android, + androideabi, + musl, + muslabin32, + muslabi64, + musleabi, + musleabihf, + muslf32, + muslsf, + muslx32, + msvc, + itanium, + simulator, + ohos, + ohoseabi, + + default, + + pub fn init(x: ?std.Target.Abi) @This() { + // TODO comptime assert the enums match + return @enumFromInt(@intFromEnum(x orelse return .default)); + } + }; + pub const CpuArch = enum(u6) { + aarch64, + aarch64_be, + alpha, + amdgcn, + arc, + arceb, + arm, + armeb, + avr, + bpfeb, + bpfel, + csky, + hexagon, + hppa, + hppa64, + kalimba, + kvx, + lanai, + loongarch32, + loongarch64, + m68k, + microblaze, + microblazeel, + mips, + mipsel, + mips64, + mips64el, + msp430, + nvptx, + nvptx64, + or1k, + powerpc, + powerpcle, + powerpc64, + powerpc64le, + propeller, + riscv32, + riscv32be, + riscv64, + riscv64be, + s390x, + sh, + sheb, + sparc, + sparc64, + spirv32, + spirv64, + thumb, + thumbeb, + ve, + wasm32, + wasm64, + x86_16, + x86, + x86_64, + xcore, + xtensa, + xtensaeb, + + default, + + pub fn init(x: ?std.Target.Cpu.Arch) @This() { + // TODO comptime assert the enums match + return @enumFromInt(@intFromEnum(x orelse return .default)); + } + }; + pub const OsTag = enum(u6) { + freestanding, + other, + contiki, + fuchsia, + hermit, + managarm, + haiku, + hurd, + illumos, + linux, + plan9, + rtems, + serenity, + dragonfly, + freebsd, + netbsd, + openbsd, + driverkit, + ios, + maccatalyst, + macos, + tvos, + visionos, + watchos, + windows, + uefi, + @"3ds", + ps3, + ps4, + ps5, + vita, + emscripten, + wasi, + amdhsa, + amdpal, + cuda, + mesa3d, + nvcl, + opencl, + opengl, + vulkan, + + default, + + pub fn init(x: ?std.Target.Os.Tag) @This() { + // TODO comptime assert the enums match + return @enumFromInt(@intFromEnum(x orelse return .default)); + } + }; + pub const ObjectFormat = enum(u4) { + c, + coff, + elf, + hex, + macho, + plan9, + raw, + spirv, + wasm, + + default, + + pub fn init(x: ?std.Target.ObjectFormat) @This() { + // TODO comptime assert the enums match + return @enumFromInt(@intFromEnum(x orelse return .default)); + } + }; + + pub const Flags = packed struct(u32) { + cpu_arch: CpuArch, + cpu_model: CpuModel, + cpu_features_add: bool, + cpu_features_sub: bool, + os_tag: OsTag, + abi: Abi, + object_format: ObjectFormat, + os_version_min: OsVersion, + os_version_max: OsVersion, + glibc_version: bool, + android_api_level: bool, + dynamic_linker: bool, + }; +}; + pub const LoadFileError = Io.File.Reader.Error || Allocator.Error || error{EndOfStream}; pub fn loadFile(arena: Allocator, io: Io, file: Io.File) LoadFileError!Configuration {