diff --git a/BRANCH_TODO b/BRANCH_TODO index 90eec2c8c8840eae47fce9964ddedde0511ddb10..a289b85243cec7e2e58804ac3df39cb7586af758 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -1,4 +1,3 @@ -* rename std.zig.Configuration to std.Build.Configuration * replace union(@This().Tag) * replace b.dupe() with string internment * don't forget to add -listen arg back @@ -10,3 +9,4 @@ * get zig tests passing * test a bunch of third party projects / help people migrate * refactor with DefaultingEnum +* add flag for compiling maker in debug mode diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 8b3b8ec5fd5a3d561a58c7c3c949704f7b04a1d4..cfe9dfa946c2cedfa8981460673181ee807eb966 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -22,7 +22,7 @@ pub const Step = @import("Build/Step.zig"); pub const Module = @import("Build/Module.zig"); pub const abi = @import("Build/abi.zig"); /// The serialized output of configure phase ingested by make phase. -pub const Configuration = @import("zig/Configuration.zig"); +pub const Configuration = @import("Build/Configuration.zig"); /// Shared state among all Build instances. graph: *Graph, diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig new file mode 100644 index 0000000000000000000000000000000000000000..b01c6cd8bc59c77b912561696031b3316506d9e2 --- /dev/null +++ b/lib/std/Build/Configuration.zig @@ -0,0 +1,2527 @@ +const Configuration = @This(); + +const std = @import("../std.zig"); +const Io = std.Io; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const maxInt = std.math.maxInt; + +string_bytes: []u8, +steps: []Step, +path_deps_base: []Path.Base, +path_deps_sub: []String, +unlazy_deps: []String, +system_integrations: []SystemIntegration, +available_options: []AvailableOption, +extra: []u32, +default_step: Step.Index, +generated_files_len: u32, + +/// The field order here matches `Configuration` which documents the order in +/// the serialized format. +pub const Header = extern struct { + string_bytes_len: u32, + steps_len: u32, + path_deps_len: u32, + unlazy_deps_len: u32, + system_integrations_len: u32, + available_options_len: u32, + extra_len: u32, + + default_step: Step.Index, + /// There is not actually any data stored for this - it just provides a way + /// for maker process to preallocate an array for these. + generated_files_len: u32, +}; + +pub const Wip = struct { + gpa: Allocator, + string_table: StringTable = .empty, + /// De-duplicates an array inside `extra`. + dedupe_table: DedupeTable = .empty, + targets_table: TargetsTable = .empty, + + string_bytes: std.ArrayList(u8) = .empty, + unlazy_deps: std.ArrayList(String) = .empty, + system_integrations: std.ArrayList(SystemIntegration) = .empty, + available_options: std.ArrayList(AvailableOption) = .empty, + steps: std.ArrayList(Step) = .empty, + path_deps: std.MultiArrayList(Path) = .empty, + extra: std.ArrayList(u32) = .empty, + next_generated_file_index: u32 = 0, + + const DedupeTable = std.HashMapUnmanaged(ExtraSlice, void, ExtraSlice.Context, std.hash_map.default_max_load_percentage); + const TargetsTable = std.HashMapUnmanaged(TargetQuery.Index, void, TargetsTableContext, std.hash_map.default_max_load_percentage); + + const ExtraSlice = struct { + index: u32, + len: u32, + + const Context = struct { + extra: []const u32, + + pub fn eql(ctx: @This(), a: ExtraSlice, b: ExtraSlice) bool { + const slice_a = ctx.extra[a.index..][0..a.len]; + const slice_b = ctx.extra[b.index..][0..b.len]; + return std.mem.eql(u32, slice_a, slice_b); + } + + pub fn hash(ctx: @This(), key: ExtraSlice) u64 { + const slice = ctx.extra[key.index..][0..key.len]; + return std.hash_map.hashString(@ptrCast(slice)); + } + }; + }; + + 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, + + pub fn eql(_: @This(), a: String, b: String) bool { + return a == b; + } + + pub fn hash(ctx: @This(), key: String) u64 { + return std.hash_map.hashString(std.mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0)); + } + }; + + const StringTableIndexAdapter = struct { + bytes: []const u8, + + pub fn eql(ctx: @This(), a: []const u8, b: String) bool { + return std.mem.eql(u8, a, std.mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0)); + } + + pub fn hash(_: @This(), adapted_key: []const u8) u64 { + assert(std.mem.indexOfScalar(u8, adapted_key, 0) == null); + return std.hash_map.hashString(adapted_key); + } + }; + + pub fn init(gpa: Allocator) Wip { + return .{ .gpa = gpa }; + } + + pub fn deinit(wip: *Wip) void { + const gpa = wip.gpa; + wip.string_bytes.deinit(gpa); + wip.unlazy_deps.deinit(gpa); + wip.system_integrations.deinit(gpa); + wip.available_options.deinit(gpa); + wip.steps.deinit(gpa); + wip.path_deps.deinit(gpa); + wip.extra.deinit(gpa); + wip.* = undefined; + } + + pub const Static = struct { + default_step: Step.Index, + generated_files_len: u32, + }; + + pub fn write(wip: *Wip, w: *Io.Writer, static: Static) Io.Writer.Error!void { + const header: Header = .{ + .string_bytes_len = @intCast(wip.string_bytes.items.len), + .steps_len = @intCast(wip.steps.items.len), + .path_deps_len = @intCast(wip.path_deps.len), + .unlazy_deps_len = @intCast(wip.unlazy_deps.items.len), + .system_integrations_len = @intCast(wip.system_integrations.items.len), + .available_options_len = @intCast(wip.available_options.items.len), + .extra_len = @intCast(wip.extra.items.len), + + .default_step = static.default_step, + .generated_files_len = static.generated_files_len, + }; + var buffers = [_][]const u8{ + @ptrCast(&header), + wip.string_bytes.items, + @ptrCast(wip.steps.items), + @ptrCast(wip.path_deps.items(.base)), + @ptrCast(wip.path_deps.items(.sub)), + @ptrCast(wip.unlazy_deps.items), + @ptrCast(wip.system_integrations.items), + @ptrCast(wip.available_options.items), + @ptrCast(wip.extra.items), + }; + try w.writeVecAll(&buffers); + } + + pub fn addString(wip: *Wip, bytes: []const u8) Allocator.Error!String { + const gpa = wip.gpa; + assert(std.mem.indexOfScalar(u8, bytes, 0) == null); + const gop = try wip.string_table.getOrPutContextAdapted( + gpa, + @as([]const u8, bytes), + @as(StringTableIndexAdapter, .{ .bytes = wip.string_bytes.items }), + @as(StringTableContext, .{ .bytes = wip.string_bytes.items }), + ); + if (gop.found_existing) return gop.key_ptr.*; + + try wip.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1); + const new_off: String = @enumFromInt(wip.string_bytes.items.len); + + wip.string_bytes.appendSliceAssumeCapacity(bytes); + wip.string_bytes.appendAssumeCapacity(0); + + gop.key_ptr.* = new_off; + + return new_off; + } + + pub fn addOptionalString(wip: *Wip, bytes: ?[]const u8) Allocator.Error!OptionalString { + return .init(try addString(wip, bytes orelse return .none)); + } + + 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: TargetQuery.OsVersion = if (q.os_version_min) |ver| switch (ver) { + .none => .none, + .semver => |sem_ver| .{ .semver = try wip.addSemVer(sem_ver) }, + .windows => |win_ver| .{ .windows = win_ver }, + } else .default; + const os_version_max: TargetQuery.OsVersion = if (q.os_version_max) |ver| switch (ver) { + .none => .none, + .semver => |sem_ver| .{ .semver = try wip.addSemVer(sem_ver) }, + .windows => |win_ver| .{ .windows = win_ver }, + } else .default; + 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(); + const result_index: TargetQuery.Index = @enumFromInt(try wip.addExtra(@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 = os_version_min, + .os_version_max = os_version_max, + .glibc_version = glibc_version != null, + .android_api_level = q.android_api_level != null, + .dynamic_linker = dynamic_linker != null, + }, + .cpu_features_add = .{ .value = if (cpu_features_add_empty) null else q.cpu_features_add }, + .cpu_features_sub = .{ .value = if (cpu_features_sub_empty) null else q.cpu_features_sub }, + .glibc_version = .{ .value = glibc_version }, + .android_api_level = .{ .value = q.android_api_level }, + .dynamic_linker = .{ .value = dynamic_linker }, + .cpu_name = .{ .value = cpu_name }, + .os_version_min = .{ .u = os_version_min }, + .os_version_max = .{ .u = os_version_max }, + }))); + + // 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: TargetQuery.OsVersion, const os_version_max: TargetQuery.OsVersion, const glibc_version: ?String, const android_api_level: ?u32 = switch (t.os.versionRange()) { + .none => .{ + .none, + .none, + null, + null, + }, + .semver => |range| .{ + .{ .semver = try wip.addSemVer(range.min) }, + .{ .semver = try wip.addSemVer(range.max) }, + null, + null, + }, + .hurd => |hurd| .{ + .{ .semver = try wip.addSemVer(hurd.range.min) }, + .{ .semver = try wip.addSemVer(hurd.range.max) }, + try wip.addSemVer(hurd.glibc), + null, + }, + .linux => |linux| .{ + .{ .semver = try wip.addSemVer(linux.range.min) }, + .{ .semver = try wip.addSemVer(linux.range.max) }, + try wip.addSemVer(linux.glibc), + linux.android, + }, + .windows => |range| .{ + .{ .windows = range.min }, + .{ .windows = 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 result_index: TargetQuery.Index = @enumFromInt(try wip.addExtra(@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_min, + .os_version_max = os_version_max, + .glibc_version = glibc_version != null, + .android_api_level = android_api_level != null, + .dynamic_linker = dynamic_linker != null, + }, + .cpu_features_add = .{ .value = if (cpu_features_add_empty) null else t.cpu.features }, + .cpu_features_sub = .{ .value = null }, + .glibc_version = .{ .value = glibc_version }, + .android_api_level = .{ .value = android_api_level }, + .dynamic_linker = .{ .value = dynamic_linker }, + .cpu_name = .{ .value = cpu_name }, + .os_version_min = .{ .u = os_version_min }, + .os_version_max = .{ .u = os_version_max }, + }))); + + // 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 addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 { + const extra_len = Storage.extraLen(extra); + try wip.extra.ensureUnusedCapacity(wip.gpa, extra_len); + return addExtraAssumeCapacity(wip, extra); + } + + /// Same as `addExtra` but uses a hash map to possibly return an already + /// existing index instead of appending to `extra`. + pub fn addDeduped(wip: *Wip, extra: anytype) Allocator.Error!u32 { + const gpa = wip.gpa; + const revert_index = wip.extra.items.len; + const extra_len = Storage.extraLen(extra); + try wip.extra.ensureUnusedCapacity(gpa, extra_len); + const new_index = addExtraAssumeCapacity(wip, extra); + const len: u32 = @intCast(wip.extra.items.len - new_index); + + const gop = try wip.dedupe_table.getOrPutContext(gpa, .{ + .index = new_index, + .len = len, + }, @as(ExtraSlice.Context, .{ .extra = wip.extra.items })); + + if (gop.found_existing) { + wip.extra.items.len = revert_index; + return gop.key_ptr.index; + } + + return new_index; + } + + pub fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 { + const result: u32 = @intCast(wip.extra.items.len); + wip.extra.items.len = Storage.setExtra(wip.extra.allocatedSlice(), result, extra); + return result; + } + + fn addExtraOptionalStringAssumeCapacity(wip: *Wip, optional_string: ?String) void { + const string = optional_string orelse return; + wip.extra.appendAssumeCapacity(@intFromEnum(string)); + } + + pub fn addGeneratedFile(wip: *Wip) GeneratedFileIndex { + defer wip.next_generated_file_index += 1; + return @enumFromInt(wip.next_generated_file_index); + } +}; + +pub const SystemIntegration = extern struct { + name: String, + status: Status, + + pub const Status = enum(u32) { + disabled = 0, + enabled = 1, + }; +}; + +pub const AvailableOption = extern struct { + name: String, + description: String, + type: Type, + /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options + enum_options: OptionalStringList, + + pub const Type = enum(u8) { + bool, + int, + float, + @"enum", + enum_list, + string, + list, + build_id, + lazy_path, + lazy_path_list, + }; +}; + +pub const Step = extern struct { + name: String, + owner: Package.Index, + deps: Deps.Index, + max_rss: MaxRss, + extended: Storage.Extended(Flags, union(Tag) { + check_file: CheckFile, + check_object: CheckObject, + compile: Compile, + config_header: ConfigHeader, + fail: Fail, + fmt: Fmt, + install_artifact: InstallArtifact, + install_dir: InstallDir, + install_file: InstallFile, + objcopy: Objcopy, + options: Options, + remove_dir: RemoveDir, + run: Run, + top_level: TopLevel, + translate_c: TranslateC, + update_source_files: UpdateSourceFiles, + write_file: WriteFile, + }), + + /// Points into `steps`. + pub const Index = enum(u32) { + _, + + pub fn ptr(i: Index, c: *const Configuration) *const Step { + return &c.steps[@intFromEnum(i)]; + } + }; + + /// Shared by all steps. + pub const Flags = packed struct(u32) { + tag: Tag, + _: u27 = 0, + }; + + pub const Tag = enum(u5) { + check_file, + check_object, + compile, + config_header, + fail, + fmt, + install_artifact, + install_dir, + install_file, + objcopy, + options, + remove_dir, + run, + top_level, + translate_c, + update_source_files, + write_file, + }; + + pub const TopLevel = struct { + flags: @This().Flags = .{}, + description: String, + + pub const Flags = packed struct(u32) { + tag: Tag = .top_level, + _: u27 = 0, + }; + }; + + pub const InstallArtifact = struct { + flags: @This().Flags, + + dest_dir: InstallDestDir, + dest_sub_path: String, + emitted_bin: LazyPath.OptionalIndex, + + implib_dir: InstallDestDir, + emitted_implib: LazyPath.OptionalIndex, + + pdb_dir: InstallDestDir, + emitted_pdb: LazyPath.OptionalIndex, + + h_dir: InstallDestDir, + emitted_h: LazyPath.OptionalIndex, + + /// Always a compile step. + artifact: Step.Index, + + pub const Flags = packed struct(u32) { + tag: Tag = .install_artifact, + dylib_symlinks: bool, + _: u26 = 0, + }; + }; + + /// Trailing: + /// * LazyPath.Index for each file_inputs_len + /// * Arg for each args_len + /// * environ_map if corresponding flag is set + /// * stdin: Bytes, // if StdIn.bytes is chosen + /// * stdin: LazyPath.Index, // if StdIn.lazy_path is chosen + /// * checks: Checks, // if StdIo.check is chosen + /// * stdio_limit: u64, // if stdio_limit is set + /// * producer: Step.Index, // if producer is set. always compile step + pub const Run = struct { + flags: @This().Flags, + file_inputs_len: u32, + args_len: u32, + cwd: LazyPath.OptionalIndex, + captured_stdout: OptionalString, // basename + captured_stderr: OptionalString, // basename + + /// Trailing: + /// * String if prefix set + /// * String if suffix set + /// * String if basename set + /// * Step.Index which is always a compile step if tag is artifact + /// * LazyPath.Index if tag is path_file, path_directory, or file_content + pub const Arg = struct { + flags: Arg.Flags, + + pub const Flags = packed struct(u32) { + tag: Arg.Tag, + prefix: bool, + suffix: bool, + basename: bool, + /// Implies Tag is output_file + dep_file: bool, + _: u20 = 0, + }; + + pub const Tag = enum(u8) { + artifact, + path_file, + path_directory, + file_content, + bytes, + output_file, + output_directory, + cli_rest_positionals, + }; + }; + + pub const Color = enum(u4) { + /// `CLICOLOR_FORCE` is set, and `NO_COLOR` is unset. + enable, + /// `NO_COLOR` is set, and `CLICOLOR_FORCE` is unset. + disable, + /// If the build runner is using color, equivalent to `.enable`. Otherwise, equivalent to `.disable`. + inherit, + /// If stderr is captured or checked, equivalent to `.disable`. Otherwise, equivalent to `.inherit`. + auto, + /// The build runner does not modify the `CLICOLOR_FORCE` or `NO_COLOR` environment variables. + /// They are treated like normal variables, so can be controlled through `setEnvironmentVariable`. + manual, + }; + + pub const StdIn = enum(u2) { none, bytes, lazy_path }; + pub const TrimWhitespace = enum(u2) { none, all, leading, trailing }; + pub const StdIo = enum(u2) { infer_from_args, inherit, check, zig_test }; + + pub const Flags = packed struct(u32) { + tag: Tag = .run, + + disable_zig_progress: bool, + skip_foreign_checks: bool, + failing_to_execute_foreign_is_an_error: bool, + has_side_effects: bool, + test_runner_mode: bool, + color: Color, + stdin: StdIn, + stdio: StdIo, + stdout_trim_whitespace: TrimWhitespace, + stderr_trim_whitespace: TrimWhitespace, + stdio_limit: bool, + producer: bool, + _: u8 = 0, + }; + }; + + pub const Compile = struct { + flags: @This().Flags, + flags2: Flags2, + flags3: Flags3, + flags4: Flags4, + + root_module: Module.Index, + root_name: String, + + filters: Storage.FlagLengthPrefixedList(.flags, .filters_len, String), + exec_cmd_args: Storage.FlagLengthPrefixedList(.flags, .exec_cmd_args_len, OptionalString), + installed_headers: Storage.FlagLengthPrefixedList(.flags, .installed_headers_len, Storage.Extended(InstalledHeader.Flags, InstalledHeader)), + force_undefined_symbols: Storage.FlagLengthPrefixedList(.flags, .force_undefined_symbols_len, String), + expect_errors: Storage.FlagUnion(.flags4, .expect_errors, ExpectErrors), + linker_script: Storage.FlagOptional(.flags4, .linker_script, LazyPath.Index), + version_script: Storage.FlagOptional(.flags4, .version_script, LazyPath.Index), + zig_lib_dir: Storage.FlagOptional(.flags3, .zig_lib_dir, LazyPath.Index), + libc_file: Storage.FlagOptional(.flags4, .libc_file, LazyPath.Index), + win32_manifest: Storage.FlagOptional(.flags3, .win32_manifest, LazyPath.Index), + win32_module_definition: Storage.FlagOptional(.flags3, .win32_module_definition, LazyPath.Index), + entitlements: Storage.FlagOptional(.flags4, .entitlements, LazyPath.Index), + version: Storage.FlagOptional(.flags3, .version, String), // semantic version string + entry: Storage.EnumOptional(.flags3, .entry, .symbol_name, String), + install_name: Storage.FlagOptional(.flags4, .install_name, String), + initial_memory: Storage.FlagOptional(.flags3, .initial_memory, u64), + max_memory: Storage.FlagOptional(.flags3, .max_memory, u64), + global_base: Storage.FlagOptional(.flags3, .global_base, u64), + image_base: Storage.FlagOptional(.flags3, .image_base, u64), + link_z_common_page_size: Storage.FlagOptional(.flags4, .link_z_common_page_size, u64), + link_z_max_page_size: Storage.FlagOptional(.flags4, .link_z_max_page_size, u64), + pagezero_size: Storage.FlagOptional(.flags4, .pagezero_size, u64), + stack_size: Storage.FlagOptional(.flags4, .stack_size, u64), + headerpad_size: Storage.FlagOptional(.flags4, .headerpad_size, u32), + error_limit: Storage.FlagOptional(.flags4, .error_limit, u32), + build_id: Storage.EnumOptional(.flags3, .build_id, .hexstring, String), + test_runner: Storage.FlagUnion(.flags3, .test_runner, TestRunner), + + emit_directory: Storage.FlagOptional(.flags4, .emit_directory, GeneratedFileIndex), + generated_docs: Storage.FlagOptional(.flags4, .generated_docs, GeneratedFileIndex), + generated_asm: Storage.FlagOptional(.flags4, .generated_asm, GeneratedFileIndex), + generated_bin: Storage.FlagOptional(.flags4, .generated_bin, GeneratedFileIndex), + generated_pdb: Storage.FlagOptional(.flags4, .generated_pdb, GeneratedFileIndex), + generated_implib: Storage.FlagOptional(.flags4, .generated_implib, GeneratedFileIndex), + generated_llvm_bc: Storage.FlagOptional(.flags4, .generated_llvm_bc, GeneratedFileIndex), + generated_llvm_ir: Storage.FlagOptional(.flags4, .generated_llvm_ir, GeneratedFileIndex), + generated_h: Storage.FlagOptional(.flags4, .generated_h, GeneratedFileIndex), + + pub const InstalledHeader = union(@This().Tag) { + file: File, + directory: Directory, + + pub const Flags = packed struct(u32) { + tag: InstalledHeader.Tag, + _: u24 = 0, + }; + + pub const Tag = enum(u8) { + file, + directory, + }; + + pub const File = struct { + flags: @This().Flags = .{}, + source: LazyPath.Index, + dest_sub_path: String, + + pub const Flags = packed struct(u32) { + tag: InstalledHeader.Tag = .file, + _: u24 = 0, + }; + }; + + pub const Directory = struct { + flags: @This().Flags, + source: LazyPath.Index, + dest_sub_path: String, + exclude_extensions: Storage.FlagLengthPrefixedList(.flags, .exclude_extensions, String), + include_extensions: Storage.FlagLengthPrefixedList(.flags, .include_extensions, String), + + pub const Flags = packed struct(u32) { + tag: InstalledHeader.Tag = .directory, + exclude_extensions: bool, + include_extensions: bool, + _: u22 = 0, + }; + }; + }; + pub const ExpectErrors = union(@This().Tag) { + pub const Tag = enum(u3) { contains, exact, starts_with, stderr_contains, none }; + + contains: String, + exact: Storage.LengthPrefixedList(String), + starts_with: String, + stderr_contains: String, + none: void, + }; + pub const TestRunner = union(@This().Tag) { + pub const Tag = enum(u2) { default, simple, server }; + + default: void, + simple: LazyPath.Index, + server: LazyPath.Index, + }; + pub const Entry = enum(u2) { default, disabled, enabled, symbol_name }; + + pub const Lto = enum(u2) { + none, + full, + thin, + default, + + pub fn init(lto: ?std.zig.LtoMode) Lto { + return switch (lto orelse return .default) { + .none => .none, + .full => .full, + .thin => .thin, + }; + } + }; + + pub const BuildId = enum(u3) { + none, + fast, + uuid, + sha1, + md5, + hexstring, + default, + + pub fn init(build_id: ?std.zig.BuildId) BuildId { + return switch (build_id orelse return .default) { + .none => .none, + .fast => .fast, + .uuid => .uuid, + .sha1 => .sha1, + .md5 => .md5, + .hexstring => .hexstring, + }; + } + }; + pub const WasiExecModel = enum(u2) { + default, + command, + reactor, + + pub fn init(wasi_exec_model: ?std.builtin.WasiExecModel) WasiExecModel { + return switch (wasi_exec_model orelse return .default) { + .command => .command, + .reactor => .reactor, + }; + } + }; + pub const Linkage = enum(u2) { + static, + dynamic, + default, + + pub fn init(link_mode: ?std.builtin.LinkMode) Linkage { + return switch (link_mode orelse return .default) { + .static => .static, + .dynamic => .dynamic, + }; + } + }; + pub const Kind = enum(u3) { + exe, + lib, + obj, + @"test", + test_obj, + + pub fn isTest(kind: Kind) bool { + return switch (kind) { + .exe, .lib, .obj => false, + .@"test", .test_obj => true, + }; + } + }; + pub const Subsystem = enum(u4) { + console, + windows, + posix, + native, + efi_application, + efi_boot_service_driver, + efi_rom, + efi_runtime_driver, + default, + + pub fn init(subsystem: ?std.zig.Subsystem) Subsystem { + return switch (subsystem orelse return .default) { + .console => .console, + .windows => .windows, + .posix => .posix, + .native => .native, + .efi_application => .efi_application, + .efi_boot_service_driver => .efi_boot_service_driver, + .efi_rom => .efi_rom, + .efi_runtime_driver => .efi_runtime_driver, + }; + } + }; + + pub const Flags = packed struct(u32) { + tag: Tag = .compile, + + filters_len: bool, + exec_cmd_args_len: bool, + installed_headers_len: bool, + force_undefined_symbols_len: bool, + + verbose_link: bool, + verbose_cc: bool, + rdynamic: bool, + import_memory: bool, + export_memory: bool, + import_symbols: bool, + import_table: bool, + export_table: bool, + shared_memory: bool, + link_eh_frame_hdr: bool, + link_emit_relocs: bool, + link_function_sections: bool, + link_data_sections: bool, + linker_dynamicbase: bool, + link_z_notext: bool, + link_z_relro: bool, + link_z_lazy: bool, + link_z_defs: bool, + headerpad_max_install_names: bool, + dead_strip_dylibs: bool, + force_load_objc: bool, + discard_local_symbols: bool, + mingw_unicode_entry_point: bool, + }; + + pub const Flags2 = packed struct(u32) { + pie: DefaultingBool, + formatted_panics: DefaultingBool, + bundle_compiler_rt: DefaultingBool, + bundle_ubsan_rt: DefaultingBool, + each_lib_rpath: DefaultingBool, + link_gc_sections: DefaultingBool, + linker_allow_shlib_undefined: DefaultingBool, + linker_allow_undefined_version: DefaultingBool, + linker_enable_new_dtags: DefaultingBool, + dll_export_fns: DefaultingBool, + use_llvm: DefaultingBool, + use_lld: DefaultingBool, + use_new_linker: DefaultingBool, + allow_so_scripts: DefaultingBool, + sanitize_coverage_trace_pc_guard: DefaultingBool, + linkage: Linkage, + }; + + pub const Flags3 = packed struct(u32) { + is_linking_libc: bool, + is_linking_libcpp: bool, + version: bool, + initial_memory: bool, + max_memory: bool, + kind: Kind, + compress_debug_sections: std.zig.CompressDebugSections, + global_base: bool, + test_runner: TestRunner.Tag, + wasi_exec_model: WasiExecModel, + win32_manifest: bool, + win32_module_definition: bool, + zig_lib_dir: bool, + rc_includes: std.zig.RcIncludes, + image_base: bool, + build_id: BuildId, + entry: Entry, + lto: Lto, + subsystem: Subsystem, + }; + + pub const Flags4 = packed struct(u32) { + libc_file: bool, + link_z_common_page_size: bool, + link_z_max_page_size: bool, + pagezero_size: bool, + stack_size: bool, + headerpad_size: bool, + error_limit: bool, + install_name: bool, + entitlements: bool, + expect_errors: ExpectErrors.Tag, + linker_script: bool, + version_script: bool, + emit_directory: bool, + generated_docs: bool, + generated_asm: bool, + generated_bin: bool, + generated_pdb: bool, + generated_implib: bool, + generated_llvm_bc: bool, + generated_llvm_ir: bool, + generated_h: bool, + _: u9 = 0, + }; + + pub fn isDynamicLibrary(compile: *const Compile) bool { + return compile.flags3.kind == .lib and compile.flags2.linkage == .dynamic; + } + + pub fn isStaticLibrary(compile: *const Compile) bool { + return compile.flags3.kind == .lib and compile.flags2.linkage != .dynamic; + } + + pub fn producesImplib(compile: *const Compile, c: *const Configuration) bool { + return isDll(compile, c); + } + + pub fn isDll(compile: *const Compile, c: *const Configuration) bool { + return isDynamicLibrary(compile) and rootModuleTarget(compile, c).flags.os_tag == .windows; + } + + pub fn rootModuleTarget(compile: *const Compile, c: *const Configuration) TargetQuery { + return compile.root_module.get(c).resolved_target.get(c).?.result.get(c); + } + }; + + pub const CheckFile = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .check_file, + _: u27 = 0, + }; + }; + + pub const CheckObject = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .check_object, + _: u27 = 0, + }; + }; + + pub const ConfigHeader = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .config_header, + _: u27 = 0, + }; + }; + + pub const Fail = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .fail, + _: u27 = 0, + }; + }; + + pub const Fmt = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .fmt, + _: u27 = 0, + }; + }; + + pub const InstallDir = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .install_dir, + _: u27 = 0, + }; + }; + + pub const InstallFile = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .install_file, + _: u27 = 0, + }; + }; + + pub const Objcopy = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .objcopy, + _: u27 = 0, + }; + }; + + pub const Options = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .options, + _: u27 = 0, + }; + }; + + pub const RemoveDir = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .remove_dir, + _: u27 = 0, + }; + }; + + pub const TranslateC = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .translate_c, + _: u27 = 0, + }; + }; + + pub const UpdateSourceFiles = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .update_source_files, + _: u27 = 0, + }; + }; + + pub const WriteFile = struct { + flags: @This().Flags, + + pub const Flags = packed struct(u32) { + tag: Tag = .write_file, + _: u27 = 0, + }; + }; + + pub fn flags(s: *const Step, c: *const Configuration) Flags { + return @bitCast(c.extra[@intFromEnum(s.extended)]); + } +}; + +pub const MaxRss = enum(u32) { + none = 0, + _, + + pub fn toBytes(mr: MaxRss) usize { + const x: usize = @intFromEnum(mr); + return x << 8; + } + + pub fn fromBytes(bytes: usize) MaxRss { + return @enumFromInt(bytes >> 8); + } +}; + +pub const LazyPath = union(@This().Tag) { + source_path: SourcePath, + relative: Relative, + generated: Generated, + + pub const Tag = enum(u8) { + /// A source file path relative to build root. + source_path, + /// Relative to the directory indicated in flags. + relative, + /// Path is available only after it is populated by its owning step. + generated, + }; + + pub const Flags = packed struct(u32) { + tag: Tag, + _: u24 = 0, + }; + + /// An index into `extra`. + pub const Index = enum(u32) { + _, + + pub fn get(this: @This(), c: *const Configuration) LazyPath { + return extraData(c, LazyPath, @intFromEnum(this)); + } + }; + + /// An index into `extra`, or `null`. + pub const OptionalIndex = enum(u32) { + none = maxInt(u32), + _, + + pub fn unwrap(this: @This()) ?Index { + return switch (this) { + .none => null, + else => @enumFromInt(@intFromEnum(this)), + }; + } + }; + + pub const SourcePath = struct { + flags: @This().Flags, + owner: Package.Index, + sub_path: String, + + pub const Flags = packed struct(u32) { + tag: Tag = .source_path, + _: u24 = 0, + }; + }; + + pub const Generated = struct { + flags: @This().Flags = .{}, + index: GeneratedFileIndex, + /// Applied after `up`. + sub_path: String = .empty, + + pub const Flags = packed struct(u32) { + tag: Tag = .generated, + /// The number of parent directories to go up. + /// 0 means the generated file itself. + /// 1 means the directory of the generated file. + /// 2 means the parent of that directory, and so on. + up: u24 = 0, + }; + }; + + pub const Relative = struct { + flags: @This().Flags, + sub_path: String, + + pub const Flags = packed struct(u32) { + tag: Tag = .relative, + base: Path.Base, + _: u16 = 0, + }; + }; +}; + +pub const GeneratedFileIndex = enum(u32) { + _, +}; + +pub const OptionalGeneratedFileIndex = enum(u32) { + none = maxInt(u32), + _, + + pub fn init(i: ?GeneratedFileIndex) OptionalGeneratedFileIndex { + return @enumFromInt(@intFromEnum(i orelse return .none)); + } + + pub fn unwrap(this: @This()) ?GeneratedFileIndex { + return switch (this) { + .none => null, + else => @enumFromInt(@intFromEnum(this)), + }; + } +}; + +pub const Package = struct { + dep_prefix: String, + hash: String, + + pub const Index = enum(u32) { + root = maxInt(u32), + _, + + /// Returns `null` for root package. + pub fn get(i: @This(), c: *const Configuration) ?Package { + if (i == .root) return null; + return extraData(c, Package, @intFromEnum(i)); + } + + pub fn depPrefixSlice(i: @This(), c: *const Configuration) [:0]const u8 { + const package = get(i, c) orelse return ""; + return package.dep_prefix.slice(c); + } + }; +}; + +pub const Module = struct { + flags: Flags, + flags2: Flags2, + import_table: ImportTable.Index, + owner: Package.Index, + root_source_file: LazyPath.OptionalIndex, + resolved_target: ResolvedTarget.OptionalIndex, + c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String), + lib_paths: Storage.FlagLengthPrefixedList(.flags, .lib_paths, LazyPath.Index), + export_symbol_names: Storage.FlagLengthPrefixedList(.flags, .export_symbol_names, String), + include_dirs: Storage.UnionList(.flags, .include_dirs, IncludeDir), + rpaths: Storage.UnionList(.flags, .rpaths, RPath), + link_objects: Storage.UnionList(.flags, .link_objects, LinkObject), + frameworks: Storage.FlagLengthPrefixedList(.flags, .frameworks, Framework), + + 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 fn get(this: @This(), c: *const Configuration) Module { + return extraData(c, Module, @intFromEnum(this)); + } + }; + + 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.Index, + path_system: LazyPath.Index, + path_after: LazyPath.Index, + framework_path: LazyPath.Index, + framework_path_system: LazyPath.Index, + /// Always `Step.Tag.compile`. + other_step: Step.Index, + /// Always `Step.Tag.config_header`. + config_header_step: Step.Index, + embed_path: LazyPath.Index, + }; + + pub const RPath = union(enum(u1)) { + lazy_path: LazyPath.Index, + special: String, + }; + + pub const LinkObject = union(enum(u3)) { + static_path: LazyPath.Index, + /// Always `Step.Tag.compile`. + other_step: Step.Index, + system_lib: SystemLib.Index, + assembly_file: LazyPath.Index, + c_source_file: CSourceFile.Index, + c_source_files: CSourceFiles.Index, + win32_resource_file: RcSourceFile.Index, + }; + + pub const Framework = extern struct { + flags: @This().Flags, + name: String, + + pub const Flags = packed struct(u32) { + needed: bool, + weak: bool, + _: u30 = 0, + }; + }; +}; + +pub const ImportTable = struct { + imports: Storage.MultiList(Import), + + pub const Import = struct { + name: String, + module: Module.Index, + }; + + /// Points into `extra`. + pub const Index = enum(u32) { + invalid = maxInt(u32), + _, + + pub fn get(this: @This(), c: *const Configuration) ImportTable { + return switch (this) { + .invalid => unreachable, + _ => extraData(c, ImportTable, @intFromEnum(this)), + }; + } + }; +}; + +pub const Deps = struct { + steps: Storage.LengthPrefixedList(Step.Index), + + pub const Index = enum(u32) { + _, + + pub fn get(this: @This(), c: *const Configuration) Deps { + return extraData(c, Deps, @intFromEnum(this)); + } + + pub fn slice(this: @This(), c: *const Configuration) []const Step.Index { + return get(this, c).steps.slice; + } + }; +}; + +/// Points into `extra`, where the first element is count of strings, following +/// elements is `String` per count. +/// +/// Stored identically to `Deps`. +pub const OptionalStringList = enum(u32) { + none = maxInt(u32), + _, + + pub fn slice(osl: OptionalStringList, c: *const Configuration) ?[]const String { + const len = c.extra[@intFromEnum(osl)]; + return @ptrCast(c.extra[@intFromEnum(osl) + 1 ..][0..len]); + } +}; + +pub const Path = extern struct { + base: Base, + sub: String, + + pub const Base = enum(u8) { + cwd, + local_cache, + global_cache, + build_root, + }; + + pub fn toCachePath(path: Path, c: *const Configuration, arena: Allocator) std.Build.Cache.Path { + _ = c; + _ = arena; + _ = path; + @panic("TODO"); + } +}; + +pub const InstallDestDir = enum(u32) { + none = maxInt(u32) - 4, + prefix = maxInt(u32) - 3, + lib = maxInt(u32) - 2, + bin = maxInt(u32) - 1, + header = maxInt(u32), + /// A `String` path relative to the prefix. + _, + + pub fn initCustom(sub_path: String) InstallDestDir { + assert(@intFromEnum(sub_path) < @intFromEnum(InstallDestDir.none)); + return @enumFromInt(@intFromEnum(sub_path)); + } +}; + +/// Points into `string_bytes`, null-terminated. +pub const OptionalString = enum(u32) { + empty = 0, + /// The string "root". + root = 1, + none = maxInt(u32), + _, + + pub fn init(s: String) OptionalString { + const result: OptionalString = @enumFromInt(@intFromEnum(s)); + assert(result != .none); + return result; + } +}; + +/// Points into `string_bytes`, null-terminated. +pub const String = enum(u32) { + empty = 0, + /// The string "root". + root = 1, + _, + + pub fn slice(index: String, c: *const Configuration) [:0]const u8 { + const start_slice = c.string_bytes[@intFromEnum(index)..]; + return start_slice[0..std.mem.indexOfScalar(u8, start_slice, 0).? :0]; + } +}; + +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 fn toBool(db: DefaultingBool) ?bool { + return switch (db) { + .false => false, + .true => true, + .default => null, + }; + } +}; + +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, @intFromEnum(this)); + } + }; + + pub const UsePkgConfig = enum(u2) { + /// Don't use pkg-config, just pass -lfoo where foo is name. + no, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, fall back to passing -lfoo where foo is name. + yes, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, error out. + force, + }; + + pub const LinkMode = std.builtin.LinkMode; + + pub const Flags = packed struct(u32) { + needed: bool, + weak: bool, + use_pkg_config: UsePkgConfig, + preferred_link_mode: LinkMode, + search_strategy: SearchStrategy, + _: u25 = 0, + }; + + pub const SearchStrategy = enum(u2) { paths_first, mode_first, no_fallback }; +}; + +pub const CSourceFiles = struct { + flags: Flags, + root: LazyPath.Index, + 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, @intFromEnum(this)); + } + }; + + pub const Flags = packed struct(u32) { + /// C compiler CLI flags. + args_len: u29, + lang: OptionalCSourceLanguage, + }; +}; + +pub const CSourceFile = struct { + flags: Flags, + 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, @intFromEnum(this)); + } + }; + + pub const Flags = packed struct(u32) { + /// C compiler CLI flags. + args_len: u29, + lang: OptionalCSourceLanguage, + }; +}; + +pub const RcSourceFile = struct { + flags: Flags, + file: LazyPath.Index, + 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, @intFromEnum(this)); + } + }; + + pub const Flags = packed struct(u32) { + /// C compiler CLI flags. + args_len: u31, + include_paths: bool, + }; +}; + +pub const OptionalCSourceLanguage = enum(u3) { + c, + cpp, + objective_c, + objective_cpp, + assembly, + assembly_with_preprocessor, + default, + + pub fn init(x: ?std.Build.Module.CSourceLanguage) @This() { + return switch (x orelse return .default) { + .c => .c, + .cpp => .cpp, + .objective_c => .objective_c, + .objective_cpp => .objective_cpp, + .assembly => .assembly, + .assembly_with_preprocessor => .assembly_with_preprocessor, + }; + } + + pub fn get(this: @This()) ?std.Build.Module.CSourceLanguage { + return switch (this) { + .c => .c, + .cpp => .cpp, + .objective_c => .objective_c, + .objective_cpp => .objective_cpp, + .assembly => .assembly, + .assembly_with_preprocessor => .assembly_with_preprocessor, + .default => null, + }; + } +}; + +pub const ResolvedTarget = struct { + /// none indicates host. + query: TargetQuery.OptionalIndex, + /// 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, @intFromEnum(this)); + } + }; + + pub const OptionalIndex = enum(u32) { + none = maxInt(u32), + _, + + pub fn unwrap(this: @This()) ?Index { + return switch (this) { + .none => null, + _ => @enumFromInt(@intFromEnum(this)), + }; + } + + pub fn get(this: @This(), c: *const Configuration) ?ResolvedTarget { + return (unwrap(this) orelse return null).get(c); + } + }; +}; + +pub const TargetQuery = struct { + flags: Flags, + + cpu_features_add: Storage.FlagOptional(.flags, .cpu_features_add, std.Target.Cpu.Feature.Set), + cpu_features_sub: Storage.FlagOptional(.flags, .cpu_features_sub, std.Target.Cpu.Feature.Set), + cpu_name: Storage.EnumOptional(.flags, .cpu_model, .explicit, String), + os_version_min: Storage.FlagUnion(.flags, .os_version_min, OsVersion), + os_version_max: Storage.FlagUnion(.flags, .os_version_max, OsVersion), + glibc_version: Storage.FlagOptional(.flags, .glibc_version, String), + android_api_level: Storage.FlagOptional(.flags, .android_api_level, u32), + dynamic_linker: Storage.FlagOptional(.flags, .dynamic_linker, String), + + 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 { + return Storage.dataLength(extra, @intFromEnum(i), TargetQuery); + } + + pub fn get(this: @This(), c: *const Configuration) TargetQuery { + return extraData(c, TargetQuery, @intFromEnum(this)); + } + }; + + 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 fn unwrap(this: @This()) ?Index { + return switch (this) { + .none => null, + _ => @enumFromInt(@intFromEnum(this)), + }; + } + }; + + 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 = union(@This().Tag) { + pub const Tag = enum(u2) { none, semver, windows, default }; + + none: void, + semver: String, + windows: std.Target.Os.WindowsVersion, + default: void, + + 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 fn get(this: @This()) ?std.Target.ObjectFormat { + return switch (this) { + .c => .c, + .coff => .coff, + .elf => .elf, + .hex => .hex, + .macho => .macho, + .plan9 => .plan9, + .raw => .raw, + .spirv => .spirv, + .wasm => .wasm, + + .default => null, + }; + } + }; + + 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.Tag, + os_version_max: OsVersion.Tag, + glibc_version: bool, + android_api_level: bool, + dynamic_linker: bool, + }; +}; + +pub const Storage = enum { + flag_optional, + enum_optional, + extended, + length_prefixed_list, + flag_length_prefixed_list, + union_list, + flag_union, + multi_list, + flag_list, + + /// The presence of the field is determined by a boolean within a packed + /// struct. + pub fn FlagOptional( + comptime flags_arg: @EnumLiteral(), + comptime flag_arg: @EnumLiteral(), + comptime ValueArg: type, + ) type { + return struct { + value: ?Value, + + pub const storage: Storage = .flag_optional; + pub const flags = flags_arg; + pub const flag = flag_arg; + pub const Value = ValueArg; + }; + } + + /// The type of the field is determined by an enum within a packed struct. + pub fn FlagUnion( + comptime flags_arg: @EnumLiteral(), + comptime flag_arg: @EnumLiteral(), + comptime UnionArg: type, + ) type { + return struct { + u: Union, + + pub const storage: Storage = .flag_union; + pub const flags = flags_arg; + pub const flag = flag_arg; + pub const Union = UnionArg; + + pub const Tag = @typeInfo(Union).@"union".tag_type.?; + }; + } + + /// The field is present if an enum tag from flags matches a specific value. + pub fn EnumOptional( + comptime flags_arg: @EnumLiteral(), + comptime flag_arg: @EnumLiteral(), + comptime tag_arg: @EnumLiteral(), + comptime ValueArg: type, + ) type { + return struct { + value: ?Value, + + pub const storage: Storage = .enum_optional; + pub const flags = flags_arg; + pub const flag = flag_arg; + pub const tag = tag_arg; + pub const Value = ValueArg; + }; + } + + /// The field indexes into an auxilary buffer, with the first element being + /// a packed struct that contains the tag. + pub fn Extended(comptime BaseFlags: type, comptime U: type) type { + return enum(u32) { + _, + + pub const storage: Storage = .extended; + + pub fn get(this: @This(), buffer: []const u32) U { + var i: usize = @intFromEnum(this); + const base_flags: BaseFlags = @bitCast(buffer[i]); + return switch (base_flags.tag) { + inline else => |tag| @unionInit(U, @tagName(tag), data(buffer, &i, @FieldType(U, @tagName(tag)))), + }; + } + }; + } + + /// A field in flags determines whether the length is zero or nonzero. If the length is + /// nonzero, then there is a length field followed by the list. + pub fn FlagLengthPrefixedList( + comptime flags_arg: @EnumLiteral(), + comptime flag_arg: @EnumLiteral(), + comptime ElemArg: type, + ) type { + return struct { + slice: []const Elem, + + pub const storage: Storage = .flag_length_prefixed_list; + pub const flags = flags_arg; + pub const flag = flag_arg; + pub const Elem = ElemArg; + + pub fn initErased(s: []const u32) @This() { + return .{ .slice = @ptrCast(s) }; + } + }; + } + + /// The field contains a u32 length followed by that many items, each + /// element bitcastable to u32. + pub fn LengthPrefixedList(comptime ElemArg: type) type { + return struct { + slice: []const Elem, + + pub const storage: Storage = .length_prefixed_list; + pub const Elem = ElemArg; + + pub fn initErased(s: []const u32) @This() { + return .{ .slice = @ptrCast(s) }; + } + }; + } + + /// The field is a list whose length is an integer inside flags. + pub fn FlagList( + comptime flags_arg: @EnumLiteral(), + comptime flag_arg: @EnumLiteral(), + comptime ElemArg: type, + ) type { + return struct { + slice: []const Elem, + + pub const storage: Storage = .flag_list; + pub const flags = flags_arg; + pub const flag = flag_arg; + pub const Elem = ElemArg; + + pub fn initErased(s: []const u32) @This() { + return .{ .slice = @ptrCast(s) }; + } + }; + } + + /// The field contains a u32 length followed by that many items for the + /// first field, that many items for the second field, etc. + pub fn MultiList(comptime ElemArg: type) type { + return struct { + mal: std.MultiArrayList(Elem), + + pub const storage: Storage = .multi_list; + pub const Elem = ElemArg; + }; + } + + /// `UnionArg` is a tagged union with a small integer for the enum tag. + /// + /// A field in flags determines whether the metadata is present. + /// + /// The metadata is bit-packed consecutive packed struct which is the + /// `UnionArg` enum tag combined with a "last" marker boolean field. + /// When "last" is true, the element is the last one, providing + /// the length of the list. + /// + /// Following is each element of the list; each bitcastable to u32. + pub fn UnionList( + comptime flags_arg: @EnumLiteral(), + comptime flag_arg: @EnumLiteral(), + comptime UnionArg: type, + ) type { + return struct { + /// When serializing it is UnionArg slice pointer. + /// When deserializing it is extra index of first UnionArg element. + data: ?*const anyopaque, + len: usize, + + pub const storage: Storage = .union_list; + pub const flags = flags_arg; + pub const flag = flag_arg; + pub const Union = UnionArg; + + pub const Tag = @typeInfo(Union).@"union".tag_type.?; + pub const MetaInt = @Int(.unsigned, @bitSizeOf(Tag) + 1); + pub const Meta = packed struct(MetaInt) { + tag: Tag, + last: bool, + }; + + /// Valid to call only when serializing. + pub fn init(s: []const Union) @This() { + return .{ .data = s.ptr, .len = s.len }; + } + + /// Valid to call only when deserializing. + pub fn slice(this: *const @This(), extra: []const u32) []const u32 { + return extra[@intFromPtr(this.data)..][0..this.len]; + } + + /// Valid to call only when deserializing. + pub fn get(this: *const @This(), extra: []const u32, i: usize) Union { + const elem = slice(this, extra)[i]; + return switch (this.tag(extra, i)) { + inline else => |comptime_tag| @unionInit(Union, @tagName(comptime_tag), @enumFromInt(elem)), + }; + } + + /// Valid to call only when deserializing. + pub fn tag(this: *const @This(), extra: []const u32, i: usize) Tag { + _ = this; + _ = extra; + _ = i; + @panic("TODO implement UnionList.tag"); + } + + fn extraLen(len: usize) usize { + return len + (len * @bitSizeOf(Meta) + 31) / 32; + } + }; + } + + pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize { + var end = i; + _ = data(buffer, &end, S); + return end - i; + } + + pub fn data(buffer: []const u32, i: *usize, comptime T: type) T { + switch (@typeInfo(T)) { + .@"struct" => |info| { + var result: T = undefined; + inline for (info.fields) |field| { + @field(result, field.name) = dataField(buffer, i, &result, field.type); + } + return result; + }, + .@"union" => |info| { + const flags: T.Flags = @bitCast(buffer[i.*]); + return switch (flags.tag) { + inline else => |comptime_tag| @unionInit( + T, + @tagName(comptime_tag), + data(buffer, i, info.fields[@intFromEnum(comptime_tag)].type), + ), + }; + }, + else => comptime unreachable, + } + } + + fn dataField(buffer: []const u32, i: *usize, container: anytype, comptime Field: type) Field { + switch (@typeInfo(Field)) { + .void => return {}, + .int => |info| switch (info.bits) { + 32 => { + defer i.* += 1; + return buffer[i.*]; + }, + 64 => { + defer i.* += 2; + return @bitCast(buffer[i.*..][0..2].*); + }, + else => comptime unreachable, + }, + .@"enum" => { + defer i.* += 1; + return @enumFromInt(buffer[i.*]); + }, + .@"struct" => |info| switch (info.layout) { + .@"packed" => switch (info.backing_integer.?) { + u32 => { + defer i.* += 1; + return @bitCast(buffer[i.*]); + }, + u64 => { + defer i.* += 2; + return @bitCast(buffer[i.*..][0..2].*); + }, + else => comptime unreachable, + }, + .auto => switch (Field) { + std.Target.Cpu.Feature.Set => { + const u32_count = (Field.usize_count * @sizeOf(usize)) / @sizeOf(u32); + defer i.* += u32_count; + return .{ .ints = @as( + *align(@alignOf(u32)) const [Field.usize_count]usize, + @ptrCast(buffer[i.*..][0..u32_count]), + ).* }; + }, + else => switch (Field.storage) { + .flag_optional => { + const flags = @field(container, @tagName(Field.flags)); + const flag = @field(flags, @tagName(Field.flag)); + return .{ + .value = if (flag) dataField(buffer, i, container, Field.Value) else null, + }; + }, + .flag_union => { + const flags = @field(container, @tagName(Field.flags)); + const tag: Field.Tag = @field(flags, @tagName(Field.flag)); + return .{ + .u = switch (tag) { + inline else => |comptime_tag| @unionInit( + Field.Union, + @tagName(comptime_tag), + dataField( + buffer, + i, + container, + @typeInfo(Field.Union).@"union".fields[@intFromEnum(comptime_tag)].type, + ), + ), + }, + }; + }, + .enum_optional => { + const flags = @field(container, @tagName(Field.flags)); + const tag = @field(flags, @tagName(Field.flag)); + const match = tag == Field.tag; + return .{ + .value = if (match) dataField(buffer, i, container, Field.Value) else null, + }; + }, + .extended => @compileError("TODO"), + .length_prefixed_list => { + const data_start = i.* + 1; + const len = buffer[data_start - 1]; + defer i.* = data_start + len; + return .{ .slice = @ptrCast(buffer[data_start..][0..len]) }; + }, + .flag_length_prefixed_list => { + const flags = @field(container, @tagName(Field.flags)); + const flag = @field(flags, @tagName(Field.flag)); + if (!flag) return .{ .slice = &.{} }; + const data_start = i.* + 1; + const len = buffer[data_start - 1]; + defer i.* = data_start + len; + return .{ .slice = @ptrCast(buffer[data_start..][0..len]) }; + }, + .flag_list => { + const flags = @field(container, @tagName(Field.flags)); + const len: u32 = @field(flags, @tagName(Field.flag)); + const data_start = i.*; + defer i.* = data_start + len; + return .{ .slice = @ptrCast(buffer[data_start..][0..len]) }; + }, + .multi_list => { + const data_start = i.* + 1; + const len = buffer[data_start - 1]; + defer i.* = data_start + len * @typeInfo(Field.Elem).@"struct".fields.len; + return .{ .mal = .{ + .bytes = @ptrCast(@constCast(buffer[data_start..][0..len])), + .len = len, + .capacity = len, + } }; + }, + .union_list => { + const flags = @field(container, @tagName(Field.flags)); + const flag = @field(flags, @tagName(Field.flag)); + if (!flag) return .{ .data = null, .len = 0 }; + const meta_start = i.*; + const meta_buffer = buffer[meta_start..]; + var len: u32 = 0; + var bit_offset: usize = 0; + while (true) : (bit_offset += @bitSizeOf(Field.Meta)) { + const meta = loadBits(u32, meta_buffer, bit_offset, Field.Meta); + len += 1; + if (meta.last) break; + } + const end = meta_start + Field.extraLen(len); + i.* = end; + return .{ .data = @ptrFromInt(end - len), .len = len }; + }, + }, + }, + .@"extern" => comptime unreachable, + }, + else => comptime unreachable, + } + } + + /// Returns new end index. + fn setExtra(buffer: []u32, index: usize, extra: anytype) usize { + const fields = @typeInfo(@TypeOf(extra)).@"struct".fields; + var i = index; + inline for (fields) |field| { + i += setExtraField(buffer, i, field.type, @field(extra, field.name)); + } + return i; + } + + fn extraFieldLen(field: anytype) usize { + const Field = @TypeOf(field); + return switch (@typeInfo(Field)) { + .void => 0, + .int => |info| switch (info.bits) { + 32 => 1, + 64 => 2, + else => comptime unreachable, + }, + .@"enum" => 1, + .@"struct" => |info| switch (info.layout) { + .@"packed" => switch (info.backing_integer.?) { + u32 => 1, + u64 => 2, + else => comptime unreachable, + }, + .auto => switch (Field.storage) { + .flag_optional, .enum_optional, .extended => 1, + .length_prefixed_list, + .flag_length_prefixed_list, + .flag_list, + => 1 + @divExact(@sizeOf(Field.Elem), @sizeOf(u32)) * field.slice.len, + .multi_list => 1 + field.mal.len * @typeInfo(Field.Elem).@"struct".fields.len, + .union_list => Field.extraLen(field.len), + .flag_union => switch (field.u) { + inline else => |v| extraFieldLen(v), + }, + }, + .@"extern" => comptime unreachable, + }, + else => @compileError("bad type: " ++ @typeName(Field)), + }; + } + + fn extraLen(extra: anytype) usize { + const fields = @typeInfo(@TypeOf(extra)).@"struct".fields; + var i: usize = 0; + inline for (fields) |field| { + i += Storage.extraFieldLen(@field(extra, field.name)); + } + return i; + } + + inline fn setExtraField(buffer: []u32, i: usize, comptime Field: type, value: anytype) usize { + switch (@typeInfo(Field)) { + .void => return 0, + .int => |info| switch (info.bits) { + 32 => { + buffer[i] = value; + return 1; + }, + 64 => { + buffer[i..][0..2].* = @bitCast(value); + return 2; + }, + else => comptime unreachable, + }, + .@"enum" => { + buffer[i] = @intFromEnum(value); + return 1; + }, + .@"struct" => |info| switch (info.layout) { + .@"packed" => switch (info.backing_integer.?) { + u32 => { + buffer[i] = @bitCast(value); + return 1; + }, + u64 => { + buffer[i..][0..2].* = @bitCast(value); + return 2; + }, + else => comptime unreachable, + }, + .auto => switch (Field) { + std.Target.Cpu.Feature.Set => { + const casted: []const u32 = @ptrCast(&value.ints); + @memcpy(buffer[i..][0..casted.len], casted); + return casted.len; + }, + else => switch (Field.storage) { + .flag_optional, .enum_optional => { + return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0; + }, + .flag_union => return switch (value.u) { + inline else => |x| setExtraField(buffer, i, @TypeOf(x), x), + }, + .extended => @compileError("TODO"), + .flag_length_prefixed_list, .length_prefixed_list => { + const len: u32 = @intCast(value.slice.len); + if (len == 0) return 0; + buffer[i] = len; + @memcpy(buffer[i + 1 ..][0..len], @as([]const u32, @ptrCast(value.slice))); + return len + 1; + }, + .flag_list => { + const len: u32 = @intCast(value.slice.len); + @memcpy(buffer[i..][0..len], @as([]const u32, @ptrCast(value.slice))); + return len; + }, + .multi_list => { + const len: u32 = @intCast(value.mal.len); + if (len == 0) return 0; + buffer[i] = len; + const fields = @typeInfo(Field.Elem).@"struct".fields; + inline for (0..fields.len) |field_i| @memcpy( + buffer[i + 1 + field_i * len ..][0..len], + @as([]const u32, @ptrCast(value.mal.items(@enumFromInt(field_i)))), + ); + return 1 + fields.len * len; + }, + .union_list => { + if (value.len == 0) return 0; + const Tag = @typeInfo(Field.Union).@"union".tag_type.?; + const slice_ptr: [*]const Field.Union = @ptrCast(@alignCast(value.data)); + const slice = slice_ptr[0..value.len]; + const meta_buffer = buffer[i..][0 .. (slice.len * @bitSizeOf(Field.Meta) + 31) / 32]; + for (slice[0 .. slice.len - 1], 0..) |elem, elem_index| { + const union_tag: Tag = elem; + storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{ + .tag = union_tag, + .last = false, + })); + } else { + const elem_index = slice.len - 1; + const elem = slice[elem_index]; + const union_tag: Tag = elem; + storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{ + .tag = union_tag, + .last = true, + })); + } + var total: usize = meta_buffer.len; + for (i + meta_buffer.len.., slice) |elem_index, src| switch (src) { + inline else => |x| total += setExtraField(buffer, elem_index, @TypeOf(x), x), + }; + return total; + }, + }, + }, + .@"extern" => comptime unreachable, + }, + else => @compileError("bad field type: " ++ @typeName(Field)), + } + } +}; + +pub fn extraData(c: *const Configuration, comptime T: type, index: usize) T { + var i: usize = index; + return Storage.data(c.extra, &i, T); +} + +pub const LoadFileError = Io.File.Reader.Error || Allocator.Error || error{EndOfStream}; + +pub fn loadFile(arena: Allocator, io: Io, file: Io.File) LoadFileError!Configuration { + var buffer: [2000]u8 = undefined; + var fr = file.reader(io, &buffer); + return load(arena, &fr.interface) catch |err| switch (err) { + error.ReadFailed => return fr.err.?, + else => |e| return e, + }; +} + +pub const LoadError = Io.Reader.Error || Allocator.Error; + +pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration { + const header = try reader.takeStruct(Header, .little); + var result: Configuration = .{ + .string_bytes = try arena.alloc(u8, header.string_bytes_len), + .steps = try arena.alloc(Step, header.steps_len), + .path_deps_sub = try arena.alloc(String, header.path_deps_len), + .path_deps_base = try arena.alloc(Path.Base, header.path_deps_len), + .unlazy_deps = try arena.alloc(String, header.unlazy_deps_len), + .system_integrations = try arena.alloc(SystemIntegration, header.system_integrations_len), + .available_options = try arena.alloc(AvailableOption, header.available_options_len), + .extra = try arena.alloc(u32, header.extra_len), + .default_step = header.default_step, + .generated_files_len = header.generated_files_len, + }; + var vecs = [_][]u8{ + result.string_bytes, + @ptrCast(result.steps), + @ptrCast(result.path_deps_base), + @ptrCast(result.path_deps_sub), + @ptrCast(result.unlazy_deps), + @ptrCast(result.system_integrations), + @ptrCast(result.available_options), + @ptrCast(result.extra), + }; + try reader.readVecAll(&vecs); + return result; +} + +pub fn loadBits(comptime Int: type, buffer: []const Int, bit_offset: usize, comptime Result: type) Result { + const index = bit_offset / @bitSizeOf(Int); + const small_bit_offset = bit_offset % @bitSizeOf(Int); + const ResultInt = @Int(.unsigned, @bitSizeOf(Result)); + const result: ResultInt = @truncate(buffer[index] >> @intCast(small_bit_offset)); + const available_bits = @bitSizeOf(Int) - small_bit_offset; + if (available_bits >= @bitSizeOf(ResultInt)) return @bitCast(result); + const missing_bits = @bitSizeOf(ResultInt) - available_bits; + const upper: ResultInt = @truncate(buffer[index + 1] & ((@as(usize, 1) << @intCast(missing_bits)) - 1)); + return @bitCast(result | (upper << @intCast(available_bits))); +} + +pub fn storeBits(comptime Int: type, buffer: []Int, bit_offset: usize, value: anytype) void { + const Value = @TypeOf(value); + const ValueInt = @Int(.unsigned, @bitSizeOf(Value)); + const value_int: ValueInt = @bitCast(value); + const index = bit_offset / @bitSizeOf(Int); + const small_bit_offset = bit_offset % @bitSizeOf(Int); + const available_bits = @bitSizeOf(Int) - small_bit_offset; + if (available_bits >= @bitSizeOf(ValueInt)) { + buffer[index] &= ~(((@as(Int, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(small_bit_offset)); + buffer[index] |= @as(Int, value_int) << @intCast(small_bit_offset); + } else { + const DoubleInt = @Int(.unsigned, @bitSizeOf(Int) * 2); + const ptr: *align(@alignOf(Int)) DoubleInt = @ptrCast(buffer[index..][0..2]); + ptr.* &= ~(((@as(DoubleInt, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(small_bit_offset)); + ptr.* |= @as(DoubleInt, value_int) << @intCast(small_bit_offset); + } +} + +test "loadBits and storeBits" { + var buffer: [2]u32 = .{ + 0b01111111000000001111111100000000, + 0b11111111000000001111111100000100, + }; + try std.testing.expectEqual(0b100, loadBits(u32, &buffer, 6, u3)); + try std.testing.expectEqual(0b100011, loadBits(u32, &buffer, 29, u6)); + + storeBits(u32, &buffer, 6, @as(u3, 0b010)); + storeBits(u32, &buffer, 29, @as(u6, 0b010010)); + + try std.testing.expectEqual(0b010, loadBits(u32, &buffer, 6, u3)); + try std.testing.expectEqual(0b010010, loadBits(u32, &buffer, 29, u6)); +} diff --git a/lib/std/zig/Configuration.zig b/lib/std/zig/Configuration.zig deleted file mode 100644 index b01c6cd8bc59c77b912561696031b3316506d9e2..0000000000000000000000000000000000000000 --- a/lib/std/zig/Configuration.zig +++ /dev/null @@ -1,2527 +0,0 @@ -const Configuration = @This(); - -const std = @import("../std.zig"); -const Io = std.Io; -const Allocator = std.mem.Allocator; -const assert = std.debug.assert; -const maxInt = std.math.maxInt; - -string_bytes: []u8, -steps: []Step, -path_deps_base: []Path.Base, -path_deps_sub: []String, -unlazy_deps: []String, -system_integrations: []SystemIntegration, -available_options: []AvailableOption, -extra: []u32, -default_step: Step.Index, -generated_files_len: u32, - -/// The field order here matches `Configuration` which documents the order in -/// the serialized format. -pub const Header = extern struct { - string_bytes_len: u32, - steps_len: u32, - path_deps_len: u32, - unlazy_deps_len: u32, - system_integrations_len: u32, - available_options_len: u32, - extra_len: u32, - - default_step: Step.Index, - /// There is not actually any data stored for this - it just provides a way - /// for maker process to preallocate an array for these. - generated_files_len: u32, -}; - -pub const Wip = struct { - gpa: Allocator, - string_table: StringTable = .empty, - /// De-duplicates an array inside `extra`. - dedupe_table: DedupeTable = .empty, - targets_table: TargetsTable = .empty, - - string_bytes: std.ArrayList(u8) = .empty, - unlazy_deps: std.ArrayList(String) = .empty, - system_integrations: std.ArrayList(SystemIntegration) = .empty, - available_options: std.ArrayList(AvailableOption) = .empty, - steps: std.ArrayList(Step) = .empty, - path_deps: std.MultiArrayList(Path) = .empty, - extra: std.ArrayList(u32) = .empty, - next_generated_file_index: u32 = 0, - - const DedupeTable = std.HashMapUnmanaged(ExtraSlice, void, ExtraSlice.Context, std.hash_map.default_max_load_percentage); - const TargetsTable = std.HashMapUnmanaged(TargetQuery.Index, void, TargetsTableContext, std.hash_map.default_max_load_percentage); - - const ExtraSlice = struct { - index: u32, - len: u32, - - const Context = struct { - extra: []const u32, - - pub fn eql(ctx: @This(), a: ExtraSlice, b: ExtraSlice) bool { - const slice_a = ctx.extra[a.index..][0..a.len]; - const slice_b = ctx.extra[b.index..][0..b.len]; - return std.mem.eql(u32, slice_a, slice_b); - } - - pub fn hash(ctx: @This(), key: ExtraSlice) u64 { - const slice = ctx.extra[key.index..][0..key.len]; - return std.hash_map.hashString(@ptrCast(slice)); - } - }; - }; - - 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, - - pub fn eql(_: @This(), a: String, b: String) bool { - return a == b; - } - - pub fn hash(ctx: @This(), key: String) u64 { - return std.hash_map.hashString(std.mem.sliceTo(ctx.bytes[@intFromEnum(key)..], 0)); - } - }; - - const StringTableIndexAdapter = struct { - bytes: []const u8, - - pub fn eql(ctx: @This(), a: []const u8, b: String) bool { - return std.mem.eql(u8, a, std.mem.sliceTo(ctx.bytes[@intFromEnum(b)..], 0)); - } - - pub fn hash(_: @This(), adapted_key: []const u8) u64 { - assert(std.mem.indexOfScalar(u8, adapted_key, 0) == null); - return std.hash_map.hashString(adapted_key); - } - }; - - pub fn init(gpa: Allocator) Wip { - return .{ .gpa = gpa }; - } - - pub fn deinit(wip: *Wip) void { - const gpa = wip.gpa; - wip.string_bytes.deinit(gpa); - wip.unlazy_deps.deinit(gpa); - wip.system_integrations.deinit(gpa); - wip.available_options.deinit(gpa); - wip.steps.deinit(gpa); - wip.path_deps.deinit(gpa); - wip.extra.deinit(gpa); - wip.* = undefined; - } - - pub const Static = struct { - default_step: Step.Index, - generated_files_len: u32, - }; - - pub fn write(wip: *Wip, w: *Io.Writer, static: Static) Io.Writer.Error!void { - const header: Header = .{ - .string_bytes_len = @intCast(wip.string_bytes.items.len), - .steps_len = @intCast(wip.steps.items.len), - .path_deps_len = @intCast(wip.path_deps.len), - .unlazy_deps_len = @intCast(wip.unlazy_deps.items.len), - .system_integrations_len = @intCast(wip.system_integrations.items.len), - .available_options_len = @intCast(wip.available_options.items.len), - .extra_len = @intCast(wip.extra.items.len), - - .default_step = static.default_step, - .generated_files_len = static.generated_files_len, - }; - var buffers = [_][]const u8{ - @ptrCast(&header), - wip.string_bytes.items, - @ptrCast(wip.steps.items), - @ptrCast(wip.path_deps.items(.base)), - @ptrCast(wip.path_deps.items(.sub)), - @ptrCast(wip.unlazy_deps.items), - @ptrCast(wip.system_integrations.items), - @ptrCast(wip.available_options.items), - @ptrCast(wip.extra.items), - }; - try w.writeVecAll(&buffers); - } - - pub fn addString(wip: *Wip, bytes: []const u8) Allocator.Error!String { - const gpa = wip.gpa; - assert(std.mem.indexOfScalar(u8, bytes, 0) == null); - const gop = try wip.string_table.getOrPutContextAdapted( - gpa, - @as([]const u8, bytes), - @as(StringTableIndexAdapter, .{ .bytes = wip.string_bytes.items }), - @as(StringTableContext, .{ .bytes = wip.string_bytes.items }), - ); - if (gop.found_existing) return gop.key_ptr.*; - - try wip.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1); - const new_off: String = @enumFromInt(wip.string_bytes.items.len); - - wip.string_bytes.appendSliceAssumeCapacity(bytes); - wip.string_bytes.appendAssumeCapacity(0); - - gop.key_ptr.* = new_off; - - return new_off; - } - - pub fn addOptionalString(wip: *Wip, bytes: ?[]const u8) Allocator.Error!OptionalString { - return .init(try addString(wip, bytes orelse return .none)); - } - - 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: TargetQuery.OsVersion = if (q.os_version_min) |ver| switch (ver) { - .none => .none, - .semver => |sem_ver| .{ .semver = try wip.addSemVer(sem_ver) }, - .windows => |win_ver| .{ .windows = win_ver }, - } else .default; - const os_version_max: TargetQuery.OsVersion = if (q.os_version_max) |ver| switch (ver) { - .none => .none, - .semver => |sem_ver| .{ .semver = try wip.addSemVer(sem_ver) }, - .windows => |win_ver| .{ .windows = win_ver }, - } else .default; - 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(); - const result_index: TargetQuery.Index = @enumFromInt(try wip.addExtra(@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 = os_version_min, - .os_version_max = os_version_max, - .glibc_version = glibc_version != null, - .android_api_level = q.android_api_level != null, - .dynamic_linker = dynamic_linker != null, - }, - .cpu_features_add = .{ .value = if (cpu_features_add_empty) null else q.cpu_features_add }, - .cpu_features_sub = .{ .value = if (cpu_features_sub_empty) null else q.cpu_features_sub }, - .glibc_version = .{ .value = glibc_version }, - .android_api_level = .{ .value = q.android_api_level }, - .dynamic_linker = .{ .value = dynamic_linker }, - .cpu_name = .{ .value = cpu_name }, - .os_version_min = .{ .u = os_version_min }, - .os_version_max = .{ .u = os_version_max }, - }))); - - // 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: TargetQuery.OsVersion, const os_version_max: TargetQuery.OsVersion, const glibc_version: ?String, const android_api_level: ?u32 = switch (t.os.versionRange()) { - .none => .{ - .none, - .none, - null, - null, - }, - .semver => |range| .{ - .{ .semver = try wip.addSemVer(range.min) }, - .{ .semver = try wip.addSemVer(range.max) }, - null, - null, - }, - .hurd => |hurd| .{ - .{ .semver = try wip.addSemVer(hurd.range.min) }, - .{ .semver = try wip.addSemVer(hurd.range.max) }, - try wip.addSemVer(hurd.glibc), - null, - }, - .linux => |linux| .{ - .{ .semver = try wip.addSemVer(linux.range.min) }, - .{ .semver = try wip.addSemVer(linux.range.max) }, - try wip.addSemVer(linux.glibc), - linux.android, - }, - .windows => |range| .{ - .{ .windows = range.min }, - .{ .windows = 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 result_index: TargetQuery.Index = @enumFromInt(try wip.addExtra(@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_min, - .os_version_max = os_version_max, - .glibc_version = glibc_version != null, - .android_api_level = android_api_level != null, - .dynamic_linker = dynamic_linker != null, - }, - .cpu_features_add = .{ .value = if (cpu_features_add_empty) null else t.cpu.features }, - .cpu_features_sub = .{ .value = null }, - .glibc_version = .{ .value = glibc_version }, - .android_api_level = .{ .value = android_api_level }, - .dynamic_linker = .{ .value = dynamic_linker }, - .cpu_name = .{ .value = cpu_name }, - .os_version_min = .{ .u = os_version_min }, - .os_version_max = .{ .u = os_version_max }, - }))); - - // 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 addExtra(wip: *Wip, extra: anytype) Allocator.Error!u32 { - const extra_len = Storage.extraLen(extra); - try wip.extra.ensureUnusedCapacity(wip.gpa, extra_len); - return addExtraAssumeCapacity(wip, extra); - } - - /// Same as `addExtra` but uses a hash map to possibly return an already - /// existing index instead of appending to `extra`. - pub fn addDeduped(wip: *Wip, extra: anytype) Allocator.Error!u32 { - const gpa = wip.gpa; - const revert_index = wip.extra.items.len; - const extra_len = Storage.extraLen(extra); - try wip.extra.ensureUnusedCapacity(gpa, extra_len); - const new_index = addExtraAssumeCapacity(wip, extra); - const len: u32 = @intCast(wip.extra.items.len - new_index); - - const gop = try wip.dedupe_table.getOrPutContext(gpa, .{ - .index = new_index, - .len = len, - }, @as(ExtraSlice.Context, .{ .extra = wip.extra.items })); - - if (gop.found_existing) { - wip.extra.items.len = revert_index; - return gop.key_ptr.index; - } - - return new_index; - } - - pub fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 { - const result: u32 = @intCast(wip.extra.items.len); - wip.extra.items.len = Storage.setExtra(wip.extra.allocatedSlice(), result, extra); - return result; - } - - fn addExtraOptionalStringAssumeCapacity(wip: *Wip, optional_string: ?String) void { - const string = optional_string orelse return; - wip.extra.appendAssumeCapacity(@intFromEnum(string)); - } - - pub fn addGeneratedFile(wip: *Wip) GeneratedFileIndex { - defer wip.next_generated_file_index += 1; - return @enumFromInt(wip.next_generated_file_index); - } -}; - -pub const SystemIntegration = extern struct { - name: String, - status: Status, - - pub const Status = enum(u32) { - disabled = 0, - enabled = 1, - }; -}; - -pub const AvailableOption = extern struct { - name: String, - description: String, - type: Type, - /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options - enum_options: OptionalStringList, - - pub const Type = enum(u8) { - bool, - int, - float, - @"enum", - enum_list, - string, - list, - build_id, - lazy_path, - lazy_path_list, - }; -}; - -pub const Step = extern struct { - name: String, - owner: Package.Index, - deps: Deps.Index, - max_rss: MaxRss, - extended: Storage.Extended(Flags, union(Tag) { - check_file: CheckFile, - check_object: CheckObject, - compile: Compile, - config_header: ConfigHeader, - fail: Fail, - fmt: Fmt, - install_artifact: InstallArtifact, - install_dir: InstallDir, - install_file: InstallFile, - objcopy: Objcopy, - options: Options, - remove_dir: RemoveDir, - run: Run, - top_level: TopLevel, - translate_c: TranslateC, - update_source_files: UpdateSourceFiles, - write_file: WriteFile, - }), - - /// Points into `steps`. - pub const Index = enum(u32) { - _, - - pub fn ptr(i: Index, c: *const Configuration) *const Step { - return &c.steps[@intFromEnum(i)]; - } - }; - - /// Shared by all steps. - pub const Flags = packed struct(u32) { - tag: Tag, - _: u27 = 0, - }; - - pub const Tag = enum(u5) { - check_file, - check_object, - compile, - config_header, - fail, - fmt, - install_artifact, - install_dir, - install_file, - objcopy, - options, - remove_dir, - run, - top_level, - translate_c, - update_source_files, - write_file, - }; - - pub const TopLevel = struct { - flags: @This().Flags = .{}, - description: String, - - pub const Flags = packed struct(u32) { - tag: Tag = .top_level, - _: u27 = 0, - }; - }; - - pub const InstallArtifact = struct { - flags: @This().Flags, - - dest_dir: InstallDestDir, - dest_sub_path: String, - emitted_bin: LazyPath.OptionalIndex, - - implib_dir: InstallDestDir, - emitted_implib: LazyPath.OptionalIndex, - - pdb_dir: InstallDestDir, - emitted_pdb: LazyPath.OptionalIndex, - - h_dir: InstallDestDir, - emitted_h: LazyPath.OptionalIndex, - - /// Always a compile step. - artifact: Step.Index, - - pub const Flags = packed struct(u32) { - tag: Tag = .install_artifact, - dylib_symlinks: bool, - _: u26 = 0, - }; - }; - - /// Trailing: - /// * LazyPath.Index for each file_inputs_len - /// * Arg for each args_len - /// * environ_map if corresponding flag is set - /// * stdin: Bytes, // if StdIn.bytes is chosen - /// * stdin: LazyPath.Index, // if StdIn.lazy_path is chosen - /// * checks: Checks, // if StdIo.check is chosen - /// * stdio_limit: u64, // if stdio_limit is set - /// * producer: Step.Index, // if producer is set. always compile step - pub const Run = struct { - flags: @This().Flags, - file_inputs_len: u32, - args_len: u32, - cwd: LazyPath.OptionalIndex, - captured_stdout: OptionalString, // basename - captured_stderr: OptionalString, // basename - - /// Trailing: - /// * String if prefix set - /// * String if suffix set - /// * String if basename set - /// * Step.Index which is always a compile step if tag is artifact - /// * LazyPath.Index if tag is path_file, path_directory, or file_content - pub const Arg = struct { - flags: Arg.Flags, - - pub const Flags = packed struct(u32) { - tag: Arg.Tag, - prefix: bool, - suffix: bool, - basename: bool, - /// Implies Tag is output_file - dep_file: bool, - _: u20 = 0, - }; - - pub const Tag = enum(u8) { - artifact, - path_file, - path_directory, - file_content, - bytes, - output_file, - output_directory, - cli_rest_positionals, - }; - }; - - pub const Color = enum(u4) { - /// `CLICOLOR_FORCE` is set, and `NO_COLOR` is unset. - enable, - /// `NO_COLOR` is set, and `CLICOLOR_FORCE` is unset. - disable, - /// If the build runner is using color, equivalent to `.enable`. Otherwise, equivalent to `.disable`. - inherit, - /// If stderr is captured or checked, equivalent to `.disable`. Otherwise, equivalent to `.inherit`. - auto, - /// The build runner does not modify the `CLICOLOR_FORCE` or `NO_COLOR` environment variables. - /// They are treated like normal variables, so can be controlled through `setEnvironmentVariable`. - manual, - }; - - pub const StdIn = enum(u2) { none, bytes, lazy_path }; - pub const TrimWhitespace = enum(u2) { none, all, leading, trailing }; - pub const StdIo = enum(u2) { infer_from_args, inherit, check, zig_test }; - - pub const Flags = packed struct(u32) { - tag: Tag = .run, - - disable_zig_progress: bool, - skip_foreign_checks: bool, - failing_to_execute_foreign_is_an_error: bool, - has_side_effects: bool, - test_runner_mode: bool, - color: Color, - stdin: StdIn, - stdio: StdIo, - stdout_trim_whitespace: TrimWhitespace, - stderr_trim_whitespace: TrimWhitespace, - stdio_limit: bool, - producer: bool, - _: u8 = 0, - }; - }; - - pub const Compile = struct { - flags: @This().Flags, - flags2: Flags2, - flags3: Flags3, - flags4: Flags4, - - root_module: Module.Index, - root_name: String, - - filters: Storage.FlagLengthPrefixedList(.flags, .filters_len, String), - exec_cmd_args: Storage.FlagLengthPrefixedList(.flags, .exec_cmd_args_len, OptionalString), - installed_headers: Storage.FlagLengthPrefixedList(.flags, .installed_headers_len, Storage.Extended(InstalledHeader.Flags, InstalledHeader)), - force_undefined_symbols: Storage.FlagLengthPrefixedList(.flags, .force_undefined_symbols_len, String), - expect_errors: Storage.FlagUnion(.flags4, .expect_errors, ExpectErrors), - linker_script: Storage.FlagOptional(.flags4, .linker_script, LazyPath.Index), - version_script: Storage.FlagOptional(.flags4, .version_script, LazyPath.Index), - zig_lib_dir: Storage.FlagOptional(.flags3, .zig_lib_dir, LazyPath.Index), - libc_file: Storage.FlagOptional(.flags4, .libc_file, LazyPath.Index), - win32_manifest: Storage.FlagOptional(.flags3, .win32_manifest, LazyPath.Index), - win32_module_definition: Storage.FlagOptional(.flags3, .win32_module_definition, LazyPath.Index), - entitlements: Storage.FlagOptional(.flags4, .entitlements, LazyPath.Index), - version: Storage.FlagOptional(.flags3, .version, String), // semantic version string - entry: Storage.EnumOptional(.flags3, .entry, .symbol_name, String), - install_name: Storage.FlagOptional(.flags4, .install_name, String), - initial_memory: Storage.FlagOptional(.flags3, .initial_memory, u64), - max_memory: Storage.FlagOptional(.flags3, .max_memory, u64), - global_base: Storage.FlagOptional(.flags3, .global_base, u64), - image_base: Storage.FlagOptional(.flags3, .image_base, u64), - link_z_common_page_size: Storage.FlagOptional(.flags4, .link_z_common_page_size, u64), - link_z_max_page_size: Storage.FlagOptional(.flags4, .link_z_max_page_size, u64), - pagezero_size: Storage.FlagOptional(.flags4, .pagezero_size, u64), - stack_size: Storage.FlagOptional(.flags4, .stack_size, u64), - headerpad_size: Storage.FlagOptional(.flags4, .headerpad_size, u32), - error_limit: Storage.FlagOptional(.flags4, .error_limit, u32), - build_id: Storage.EnumOptional(.flags3, .build_id, .hexstring, String), - test_runner: Storage.FlagUnion(.flags3, .test_runner, TestRunner), - - emit_directory: Storage.FlagOptional(.flags4, .emit_directory, GeneratedFileIndex), - generated_docs: Storage.FlagOptional(.flags4, .generated_docs, GeneratedFileIndex), - generated_asm: Storage.FlagOptional(.flags4, .generated_asm, GeneratedFileIndex), - generated_bin: Storage.FlagOptional(.flags4, .generated_bin, GeneratedFileIndex), - generated_pdb: Storage.FlagOptional(.flags4, .generated_pdb, GeneratedFileIndex), - generated_implib: Storage.FlagOptional(.flags4, .generated_implib, GeneratedFileIndex), - generated_llvm_bc: Storage.FlagOptional(.flags4, .generated_llvm_bc, GeneratedFileIndex), - generated_llvm_ir: Storage.FlagOptional(.flags4, .generated_llvm_ir, GeneratedFileIndex), - generated_h: Storage.FlagOptional(.flags4, .generated_h, GeneratedFileIndex), - - pub const InstalledHeader = union(@This().Tag) { - file: File, - directory: Directory, - - pub const Flags = packed struct(u32) { - tag: InstalledHeader.Tag, - _: u24 = 0, - }; - - pub const Tag = enum(u8) { - file, - directory, - }; - - pub const File = struct { - flags: @This().Flags = .{}, - source: LazyPath.Index, - dest_sub_path: String, - - pub const Flags = packed struct(u32) { - tag: InstalledHeader.Tag = .file, - _: u24 = 0, - }; - }; - - pub const Directory = struct { - flags: @This().Flags, - source: LazyPath.Index, - dest_sub_path: String, - exclude_extensions: Storage.FlagLengthPrefixedList(.flags, .exclude_extensions, String), - include_extensions: Storage.FlagLengthPrefixedList(.flags, .include_extensions, String), - - pub const Flags = packed struct(u32) { - tag: InstalledHeader.Tag = .directory, - exclude_extensions: bool, - include_extensions: bool, - _: u22 = 0, - }; - }; - }; - pub const ExpectErrors = union(@This().Tag) { - pub const Tag = enum(u3) { contains, exact, starts_with, stderr_contains, none }; - - contains: String, - exact: Storage.LengthPrefixedList(String), - starts_with: String, - stderr_contains: String, - none: void, - }; - pub const TestRunner = union(@This().Tag) { - pub const Tag = enum(u2) { default, simple, server }; - - default: void, - simple: LazyPath.Index, - server: LazyPath.Index, - }; - pub const Entry = enum(u2) { default, disabled, enabled, symbol_name }; - - pub const Lto = enum(u2) { - none, - full, - thin, - default, - - pub fn init(lto: ?std.zig.LtoMode) Lto { - return switch (lto orelse return .default) { - .none => .none, - .full => .full, - .thin => .thin, - }; - } - }; - - pub const BuildId = enum(u3) { - none, - fast, - uuid, - sha1, - md5, - hexstring, - default, - - pub fn init(build_id: ?std.zig.BuildId) BuildId { - return switch (build_id orelse return .default) { - .none => .none, - .fast => .fast, - .uuid => .uuid, - .sha1 => .sha1, - .md5 => .md5, - .hexstring => .hexstring, - }; - } - }; - pub const WasiExecModel = enum(u2) { - default, - command, - reactor, - - pub fn init(wasi_exec_model: ?std.builtin.WasiExecModel) WasiExecModel { - return switch (wasi_exec_model orelse return .default) { - .command => .command, - .reactor => .reactor, - }; - } - }; - pub const Linkage = enum(u2) { - static, - dynamic, - default, - - pub fn init(link_mode: ?std.builtin.LinkMode) Linkage { - return switch (link_mode orelse return .default) { - .static => .static, - .dynamic => .dynamic, - }; - } - }; - pub const Kind = enum(u3) { - exe, - lib, - obj, - @"test", - test_obj, - - pub fn isTest(kind: Kind) bool { - return switch (kind) { - .exe, .lib, .obj => false, - .@"test", .test_obj => true, - }; - } - }; - pub const Subsystem = enum(u4) { - console, - windows, - posix, - native, - efi_application, - efi_boot_service_driver, - efi_rom, - efi_runtime_driver, - default, - - pub fn init(subsystem: ?std.zig.Subsystem) Subsystem { - return switch (subsystem orelse return .default) { - .console => .console, - .windows => .windows, - .posix => .posix, - .native => .native, - .efi_application => .efi_application, - .efi_boot_service_driver => .efi_boot_service_driver, - .efi_rom => .efi_rom, - .efi_runtime_driver => .efi_runtime_driver, - }; - } - }; - - pub const Flags = packed struct(u32) { - tag: Tag = .compile, - - filters_len: bool, - exec_cmd_args_len: bool, - installed_headers_len: bool, - force_undefined_symbols_len: bool, - - verbose_link: bool, - verbose_cc: bool, - rdynamic: bool, - import_memory: bool, - export_memory: bool, - import_symbols: bool, - import_table: bool, - export_table: bool, - shared_memory: bool, - link_eh_frame_hdr: bool, - link_emit_relocs: bool, - link_function_sections: bool, - link_data_sections: bool, - linker_dynamicbase: bool, - link_z_notext: bool, - link_z_relro: bool, - link_z_lazy: bool, - link_z_defs: bool, - headerpad_max_install_names: bool, - dead_strip_dylibs: bool, - force_load_objc: bool, - discard_local_symbols: bool, - mingw_unicode_entry_point: bool, - }; - - pub const Flags2 = packed struct(u32) { - pie: DefaultingBool, - formatted_panics: DefaultingBool, - bundle_compiler_rt: DefaultingBool, - bundle_ubsan_rt: DefaultingBool, - each_lib_rpath: DefaultingBool, - link_gc_sections: DefaultingBool, - linker_allow_shlib_undefined: DefaultingBool, - linker_allow_undefined_version: DefaultingBool, - linker_enable_new_dtags: DefaultingBool, - dll_export_fns: DefaultingBool, - use_llvm: DefaultingBool, - use_lld: DefaultingBool, - use_new_linker: DefaultingBool, - allow_so_scripts: DefaultingBool, - sanitize_coverage_trace_pc_guard: DefaultingBool, - linkage: Linkage, - }; - - pub const Flags3 = packed struct(u32) { - is_linking_libc: bool, - is_linking_libcpp: bool, - version: bool, - initial_memory: bool, - max_memory: bool, - kind: Kind, - compress_debug_sections: std.zig.CompressDebugSections, - global_base: bool, - test_runner: TestRunner.Tag, - wasi_exec_model: WasiExecModel, - win32_manifest: bool, - win32_module_definition: bool, - zig_lib_dir: bool, - rc_includes: std.zig.RcIncludes, - image_base: bool, - build_id: BuildId, - entry: Entry, - lto: Lto, - subsystem: Subsystem, - }; - - pub const Flags4 = packed struct(u32) { - libc_file: bool, - link_z_common_page_size: bool, - link_z_max_page_size: bool, - pagezero_size: bool, - stack_size: bool, - headerpad_size: bool, - error_limit: bool, - install_name: bool, - entitlements: bool, - expect_errors: ExpectErrors.Tag, - linker_script: bool, - version_script: bool, - emit_directory: bool, - generated_docs: bool, - generated_asm: bool, - generated_bin: bool, - generated_pdb: bool, - generated_implib: bool, - generated_llvm_bc: bool, - generated_llvm_ir: bool, - generated_h: bool, - _: u9 = 0, - }; - - pub fn isDynamicLibrary(compile: *const Compile) bool { - return compile.flags3.kind == .lib and compile.flags2.linkage == .dynamic; - } - - pub fn isStaticLibrary(compile: *const Compile) bool { - return compile.flags3.kind == .lib and compile.flags2.linkage != .dynamic; - } - - pub fn producesImplib(compile: *const Compile, c: *const Configuration) bool { - return isDll(compile, c); - } - - pub fn isDll(compile: *const Compile, c: *const Configuration) bool { - return isDynamicLibrary(compile) and rootModuleTarget(compile, c).flags.os_tag == .windows; - } - - pub fn rootModuleTarget(compile: *const Compile, c: *const Configuration) TargetQuery { - return compile.root_module.get(c).resolved_target.get(c).?.result.get(c); - } - }; - - pub const CheckFile = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .check_file, - _: u27 = 0, - }; - }; - - pub const CheckObject = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .check_object, - _: u27 = 0, - }; - }; - - pub const ConfigHeader = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .config_header, - _: u27 = 0, - }; - }; - - pub const Fail = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .fail, - _: u27 = 0, - }; - }; - - pub const Fmt = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .fmt, - _: u27 = 0, - }; - }; - - pub const InstallDir = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .install_dir, - _: u27 = 0, - }; - }; - - pub const InstallFile = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .install_file, - _: u27 = 0, - }; - }; - - pub const Objcopy = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .objcopy, - _: u27 = 0, - }; - }; - - pub const Options = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .options, - _: u27 = 0, - }; - }; - - pub const RemoveDir = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .remove_dir, - _: u27 = 0, - }; - }; - - pub const TranslateC = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .translate_c, - _: u27 = 0, - }; - }; - - pub const UpdateSourceFiles = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .update_source_files, - _: u27 = 0, - }; - }; - - pub const WriteFile = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .write_file, - _: u27 = 0, - }; - }; - - pub fn flags(s: *const Step, c: *const Configuration) Flags { - return @bitCast(c.extra[@intFromEnum(s.extended)]); - } -}; - -pub const MaxRss = enum(u32) { - none = 0, - _, - - pub fn toBytes(mr: MaxRss) usize { - const x: usize = @intFromEnum(mr); - return x << 8; - } - - pub fn fromBytes(bytes: usize) MaxRss { - return @enumFromInt(bytes >> 8); - } -}; - -pub const LazyPath = union(@This().Tag) { - source_path: SourcePath, - relative: Relative, - generated: Generated, - - pub const Tag = enum(u8) { - /// A source file path relative to build root. - source_path, - /// Relative to the directory indicated in flags. - relative, - /// Path is available only after it is populated by its owning step. - generated, - }; - - pub const Flags = packed struct(u32) { - tag: Tag, - _: u24 = 0, - }; - - /// An index into `extra`. - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) LazyPath { - return extraData(c, LazyPath, @intFromEnum(this)); - } - }; - - /// An index into `extra`, or `null`. - pub const OptionalIndex = enum(u32) { - none = maxInt(u32), - _, - - pub fn unwrap(this: @This()) ?Index { - return switch (this) { - .none => null, - else => @enumFromInt(@intFromEnum(this)), - }; - } - }; - - pub const SourcePath = struct { - flags: @This().Flags, - owner: Package.Index, - sub_path: String, - - pub const Flags = packed struct(u32) { - tag: Tag = .source_path, - _: u24 = 0, - }; - }; - - pub const Generated = struct { - flags: @This().Flags = .{}, - index: GeneratedFileIndex, - /// Applied after `up`. - sub_path: String = .empty, - - pub const Flags = packed struct(u32) { - tag: Tag = .generated, - /// The number of parent directories to go up. - /// 0 means the generated file itself. - /// 1 means the directory of the generated file. - /// 2 means the parent of that directory, and so on. - up: u24 = 0, - }; - }; - - pub const Relative = struct { - flags: @This().Flags, - sub_path: String, - - pub const Flags = packed struct(u32) { - tag: Tag = .relative, - base: Path.Base, - _: u16 = 0, - }; - }; -}; - -pub const GeneratedFileIndex = enum(u32) { - _, -}; - -pub const OptionalGeneratedFileIndex = enum(u32) { - none = maxInt(u32), - _, - - pub fn init(i: ?GeneratedFileIndex) OptionalGeneratedFileIndex { - return @enumFromInt(@intFromEnum(i orelse return .none)); - } - - pub fn unwrap(this: @This()) ?GeneratedFileIndex { - return switch (this) { - .none => null, - else => @enumFromInt(@intFromEnum(this)), - }; - } -}; - -pub const Package = struct { - dep_prefix: String, - hash: String, - - pub const Index = enum(u32) { - root = maxInt(u32), - _, - - /// Returns `null` for root package. - pub fn get(i: @This(), c: *const Configuration) ?Package { - if (i == .root) return null; - return extraData(c, Package, @intFromEnum(i)); - } - - pub fn depPrefixSlice(i: @This(), c: *const Configuration) [:0]const u8 { - const package = get(i, c) orelse return ""; - return package.dep_prefix.slice(c); - } - }; -}; - -pub const Module = struct { - flags: Flags, - flags2: Flags2, - import_table: ImportTable.Index, - owner: Package.Index, - root_source_file: LazyPath.OptionalIndex, - resolved_target: ResolvedTarget.OptionalIndex, - c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String), - lib_paths: Storage.FlagLengthPrefixedList(.flags, .lib_paths, LazyPath.Index), - export_symbol_names: Storage.FlagLengthPrefixedList(.flags, .export_symbol_names, String), - include_dirs: Storage.UnionList(.flags, .include_dirs, IncludeDir), - rpaths: Storage.UnionList(.flags, .rpaths, RPath), - link_objects: Storage.UnionList(.flags, .link_objects, LinkObject), - frameworks: Storage.FlagLengthPrefixedList(.flags, .frameworks, Framework), - - 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 fn get(this: @This(), c: *const Configuration) Module { - return extraData(c, Module, @intFromEnum(this)); - } - }; - - 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.Index, - path_system: LazyPath.Index, - path_after: LazyPath.Index, - framework_path: LazyPath.Index, - framework_path_system: LazyPath.Index, - /// Always `Step.Tag.compile`. - other_step: Step.Index, - /// Always `Step.Tag.config_header`. - config_header_step: Step.Index, - embed_path: LazyPath.Index, - }; - - pub const RPath = union(enum(u1)) { - lazy_path: LazyPath.Index, - special: String, - }; - - pub const LinkObject = union(enum(u3)) { - static_path: LazyPath.Index, - /// Always `Step.Tag.compile`. - other_step: Step.Index, - system_lib: SystemLib.Index, - assembly_file: LazyPath.Index, - c_source_file: CSourceFile.Index, - c_source_files: CSourceFiles.Index, - win32_resource_file: RcSourceFile.Index, - }; - - pub const Framework = extern struct { - flags: @This().Flags, - name: String, - - pub const Flags = packed struct(u32) { - needed: bool, - weak: bool, - _: u30 = 0, - }; - }; -}; - -pub const ImportTable = struct { - imports: Storage.MultiList(Import), - - pub const Import = struct { - name: String, - module: Module.Index, - }; - - /// Points into `extra`. - pub const Index = enum(u32) { - invalid = maxInt(u32), - _, - - pub fn get(this: @This(), c: *const Configuration) ImportTable { - return switch (this) { - .invalid => unreachable, - _ => extraData(c, ImportTable, @intFromEnum(this)), - }; - } - }; -}; - -pub const Deps = struct { - steps: Storage.LengthPrefixedList(Step.Index), - - pub const Index = enum(u32) { - _, - - pub fn get(this: @This(), c: *const Configuration) Deps { - return extraData(c, Deps, @intFromEnum(this)); - } - - pub fn slice(this: @This(), c: *const Configuration) []const Step.Index { - return get(this, c).steps.slice; - } - }; -}; - -/// Points into `extra`, where the first element is count of strings, following -/// elements is `String` per count. -/// -/// Stored identically to `Deps`. -pub const OptionalStringList = enum(u32) { - none = maxInt(u32), - _, - - pub fn slice(osl: OptionalStringList, c: *const Configuration) ?[]const String { - const len = c.extra[@intFromEnum(osl)]; - return @ptrCast(c.extra[@intFromEnum(osl) + 1 ..][0..len]); - } -}; - -pub const Path = extern struct { - base: Base, - sub: String, - - pub const Base = enum(u8) { - cwd, - local_cache, - global_cache, - build_root, - }; - - pub fn toCachePath(path: Path, c: *const Configuration, arena: Allocator) std.Build.Cache.Path { - _ = c; - _ = arena; - _ = path; - @panic("TODO"); - } -}; - -pub const InstallDestDir = enum(u32) { - none = maxInt(u32) - 4, - prefix = maxInt(u32) - 3, - lib = maxInt(u32) - 2, - bin = maxInt(u32) - 1, - header = maxInt(u32), - /// A `String` path relative to the prefix. - _, - - pub fn initCustom(sub_path: String) InstallDestDir { - assert(@intFromEnum(sub_path) < @intFromEnum(InstallDestDir.none)); - return @enumFromInt(@intFromEnum(sub_path)); - } -}; - -/// Points into `string_bytes`, null-terminated. -pub const OptionalString = enum(u32) { - empty = 0, - /// The string "root". - root = 1, - none = maxInt(u32), - _, - - pub fn init(s: String) OptionalString { - const result: OptionalString = @enumFromInt(@intFromEnum(s)); - assert(result != .none); - return result; - } -}; - -/// Points into `string_bytes`, null-terminated. -pub const String = enum(u32) { - empty = 0, - /// The string "root". - root = 1, - _, - - pub fn slice(index: String, c: *const Configuration) [:0]const u8 { - const start_slice = c.string_bytes[@intFromEnum(index)..]; - return start_slice[0..std.mem.indexOfScalar(u8, start_slice, 0).? :0]; - } -}; - -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 fn toBool(db: DefaultingBool) ?bool { - return switch (db) { - .false => false, - .true => true, - .default => null, - }; - } -}; - -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, @intFromEnum(this)); - } - }; - - pub const UsePkgConfig = enum(u2) { - /// Don't use pkg-config, just pass -lfoo where foo is name. - no, - /// Try to get information on how to link the library from pkg-config. - /// If that fails, fall back to passing -lfoo where foo is name. - yes, - /// Try to get information on how to link the library from pkg-config. - /// If that fails, error out. - force, - }; - - pub const LinkMode = std.builtin.LinkMode; - - pub const Flags = packed struct(u32) { - needed: bool, - weak: bool, - use_pkg_config: UsePkgConfig, - preferred_link_mode: LinkMode, - search_strategy: SearchStrategy, - _: u25 = 0, - }; - - pub const SearchStrategy = enum(u2) { paths_first, mode_first, no_fallback }; -}; - -pub const CSourceFiles = struct { - flags: Flags, - root: LazyPath.Index, - 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, @intFromEnum(this)); - } - }; - - pub const Flags = packed struct(u32) { - /// C compiler CLI flags. - args_len: u29, - lang: OptionalCSourceLanguage, - }; -}; - -pub const CSourceFile = struct { - flags: Flags, - 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, @intFromEnum(this)); - } - }; - - pub const Flags = packed struct(u32) { - /// C compiler CLI flags. - args_len: u29, - lang: OptionalCSourceLanguage, - }; -}; - -pub const RcSourceFile = struct { - flags: Flags, - file: LazyPath.Index, - 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, @intFromEnum(this)); - } - }; - - pub const Flags = packed struct(u32) { - /// C compiler CLI flags. - args_len: u31, - include_paths: bool, - }; -}; - -pub const OptionalCSourceLanguage = enum(u3) { - c, - cpp, - objective_c, - objective_cpp, - assembly, - assembly_with_preprocessor, - default, - - pub fn init(x: ?std.Build.Module.CSourceLanguage) @This() { - return switch (x orelse return .default) { - .c => .c, - .cpp => .cpp, - .objective_c => .objective_c, - .objective_cpp => .objective_cpp, - .assembly => .assembly, - .assembly_with_preprocessor => .assembly_with_preprocessor, - }; - } - - pub fn get(this: @This()) ?std.Build.Module.CSourceLanguage { - return switch (this) { - .c => .c, - .cpp => .cpp, - .objective_c => .objective_c, - .objective_cpp => .objective_cpp, - .assembly => .assembly, - .assembly_with_preprocessor => .assembly_with_preprocessor, - .default => null, - }; - } -}; - -pub const ResolvedTarget = struct { - /// none indicates host. - query: TargetQuery.OptionalIndex, - /// 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, @intFromEnum(this)); - } - }; - - pub const OptionalIndex = enum(u32) { - none = maxInt(u32), - _, - - pub fn unwrap(this: @This()) ?Index { - return switch (this) { - .none => null, - _ => @enumFromInt(@intFromEnum(this)), - }; - } - - pub fn get(this: @This(), c: *const Configuration) ?ResolvedTarget { - return (unwrap(this) orelse return null).get(c); - } - }; -}; - -pub const TargetQuery = struct { - flags: Flags, - - cpu_features_add: Storage.FlagOptional(.flags, .cpu_features_add, std.Target.Cpu.Feature.Set), - cpu_features_sub: Storage.FlagOptional(.flags, .cpu_features_sub, std.Target.Cpu.Feature.Set), - cpu_name: Storage.EnumOptional(.flags, .cpu_model, .explicit, String), - os_version_min: Storage.FlagUnion(.flags, .os_version_min, OsVersion), - os_version_max: Storage.FlagUnion(.flags, .os_version_max, OsVersion), - glibc_version: Storage.FlagOptional(.flags, .glibc_version, String), - android_api_level: Storage.FlagOptional(.flags, .android_api_level, u32), - dynamic_linker: Storage.FlagOptional(.flags, .dynamic_linker, String), - - 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 { - return Storage.dataLength(extra, @intFromEnum(i), TargetQuery); - } - - pub fn get(this: @This(), c: *const Configuration) TargetQuery { - return extraData(c, TargetQuery, @intFromEnum(this)); - } - }; - - 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 fn unwrap(this: @This()) ?Index { - return switch (this) { - .none => null, - _ => @enumFromInt(@intFromEnum(this)), - }; - } - }; - - 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 = union(@This().Tag) { - pub const Tag = enum(u2) { none, semver, windows, default }; - - none: void, - semver: String, - windows: std.Target.Os.WindowsVersion, - default: void, - - 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 fn get(this: @This()) ?std.Target.ObjectFormat { - return switch (this) { - .c => .c, - .coff => .coff, - .elf => .elf, - .hex => .hex, - .macho => .macho, - .plan9 => .plan9, - .raw => .raw, - .spirv => .spirv, - .wasm => .wasm, - - .default => null, - }; - } - }; - - 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.Tag, - os_version_max: OsVersion.Tag, - glibc_version: bool, - android_api_level: bool, - dynamic_linker: bool, - }; -}; - -pub const Storage = enum { - flag_optional, - enum_optional, - extended, - length_prefixed_list, - flag_length_prefixed_list, - union_list, - flag_union, - multi_list, - flag_list, - - /// The presence of the field is determined by a boolean within a packed - /// struct. - pub fn FlagOptional( - comptime flags_arg: @EnumLiteral(), - comptime flag_arg: @EnumLiteral(), - comptime ValueArg: type, - ) type { - return struct { - value: ?Value, - - pub const storage: Storage = .flag_optional; - pub const flags = flags_arg; - pub const flag = flag_arg; - pub const Value = ValueArg; - }; - } - - /// The type of the field is determined by an enum within a packed struct. - pub fn FlagUnion( - comptime flags_arg: @EnumLiteral(), - comptime flag_arg: @EnumLiteral(), - comptime UnionArg: type, - ) type { - return struct { - u: Union, - - pub const storage: Storage = .flag_union; - pub const flags = flags_arg; - pub const flag = flag_arg; - pub const Union = UnionArg; - - pub const Tag = @typeInfo(Union).@"union".tag_type.?; - }; - } - - /// The field is present if an enum tag from flags matches a specific value. - pub fn EnumOptional( - comptime flags_arg: @EnumLiteral(), - comptime flag_arg: @EnumLiteral(), - comptime tag_arg: @EnumLiteral(), - comptime ValueArg: type, - ) type { - return struct { - value: ?Value, - - pub const storage: Storage = .enum_optional; - pub const flags = flags_arg; - pub const flag = flag_arg; - pub const tag = tag_arg; - pub const Value = ValueArg; - }; - } - - /// The field indexes into an auxilary buffer, with the first element being - /// a packed struct that contains the tag. - pub fn Extended(comptime BaseFlags: type, comptime U: type) type { - return enum(u32) { - _, - - pub const storage: Storage = .extended; - - pub fn get(this: @This(), buffer: []const u32) U { - var i: usize = @intFromEnum(this); - const base_flags: BaseFlags = @bitCast(buffer[i]); - return switch (base_flags.tag) { - inline else => |tag| @unionInit(U, @tagName(tag), data(buffer, &i, @FieldType(U, @tagName(tag)))), - }; - } - }; - } - - /// A field in flags determines whether the length is zero or nonzero. If the length is - /// nonzero, then there is a length field followed by the list. - pub fn FlagLengthPrefixedList( - comptime flags_arg: @EnumLiteral(), - comptime flag_arg: @EnumLiteral(), - comptime ElemArg: type, - ) type { - return struct { - slice: []const Elem, - - pub const storage: Storage = .flag_length_prefixed_list; - pub const flags = flags_arg; - pub const flag = flag_arg; - pub const Elem = ElemArg; - - pub fn initErased(s: []const u32) @This() { - return .{ .slice = @ptrCast(s) }; - } - }; - } - - /// The field contains a u32 length followed by that many items, each - /// element bitcastable to u32. - pub fn LengthPrefixedList(comptime ElemArg: type) type { - return struct { - slice: []const Elem, - - pub const storage: Storage = .length_prefixed_list; - pub const Elem = ElemArg; - - pub fn initErased(s: []const u32) @This() { - return .{ .slice = @ptrCast(s) }; - } - }; - } - - /// The field is a list whose length is an integer inside flags. - pub fn FlagList( - comptime flags_arg: @EnumLiteral(), - comptime flag_arg: @EnumLiteral(), - comptime ElemArg: type, - ) type { - return struct { - slice: []const Elem, - - pub const storage: Storage = .flag_list; - pub const flags = flags_arg; - pub const flag = flag_arg; - pub const Elem = ElemArg; - - pub fn initErased(s: []const u32) @This() { - return .{ .slice = @ptrCast(s) }; - } - }; - } - - /// The field contains a u32 length followed by that many items for the - /// first field, that many items for the second field, etc. - pub fn MultiList(comptime ElemArg: type) type { - return struct { - mal: std.MultiArrayList(Elem), - - pub const storage: Storage = .multi_list; - pub const Elem = ElemArg; - }; - } - - /// `UnionArg` is a tagged union with a small integer for the enum tag. - /// - /// A field in flags determines whether the metadata is present. - /// - /// The metadata is bit-packed consecutive packed struct which is the - /// `UnionArg` enum tag combined with a "last" marker boolean field. - /// When "last" is true, the element is the last one, providing - /// the length of the list. - /// - /// Following is each element of the list; each bitcastable to u32. - pub fn UnionList( - comptime flags_arg: @EnumLiteral(), - comptime flag_arg: @EnumLiteral(), - comptime UnionArg: type, - ) type { - return struct { - /// When serializing it is UnionArg slice pointer. - /// When deserializing it is extra index of first UnionArg element. - data: ?*const anyopaque, - len: usize, - - pub const storage: Storage = .union_list; - pub const flags = flags_arg; - pub const flag = flag_arg; - pub const Union = UnionArg; - - pub const Tag = @typeInfo(Union).@"union".tag_type.?; - pub const MetaInt = @Int(.unsigned, @bitSizeOf(Tag) + 1); - pub const Meta = packed struct(MetaInt) { - tag: Tag, - last: bool, - }; - - /// Valid to call only when serializing. - pub fn init(s: []const Union) @This() { - return .{ .data = s.ptr, .len = s.len }; - } - - /// Valid to call only when deserializing. - pub fn slice(this: *const @This(), extra: []const u32) []const u32 { - return extra[@intFromPtr(this.data)..][0..this.len]; - } - - /// Valid to call only when deserializing. - pub fn get(this: *const @This(), extra: []const u32, i: usize) Union { - const elem = slice(this, extra)[i]; - return switch (this.tag(extra, i)) { - inline else => |comptime_tag| @unionInit(Union, @tagName(comptime_tag), @enumFromInt(elem)), - }; - } - - /// Valid to call only when deserializing. - pub fn tag(this: *const @This(), extra: []const u32, i: usize) Tag { - _ = this; - _ = extra; - _ = i; - @panic("TODO implement UnionList.tag"); - } - - fn extraLen(len: usize) usize { - return len + (len * @bitSizeOf(Meta) + 31) / 32; - } - }; - } - - pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize { - var end = i; - _ = data(buffer, &end, S); - return end - i; - } - - pub fn data(buffer: []const u32, i: *usize, comptime T: type) T { - switch (@typeInfo(T)) { - .@"struct" => |info| { - var result: T = undefined; - inline for (info.fields) |field| { - @field(result, field.name) = dataField(buffer, i, &result, field.type); - } - return result; - }, - .@"union" => |info| { - const flags: T.Flags = @bitCast(buffer[i.*]); - return switch (flags.tag) { - inline else => |comptime_tag| @unionInit( - T, - @tagName(comptime_tag), - data(buffer, i, info.fields[@intFromEnum(comptime_tag)].type), - ), - }; - }, - else => comptime unreachable, - } - } - - fn dataField(buffer: []const u32, i: *usize, container: anytype, comptime Field: type) Field { - switch (@typeInfo(Field)) { - .void => return {}, - .int => |info| switch (info.bits) { - 32 => { - defer i.* += 1; - return buffer[i.*]; - }, - 64 => { - defer i.* += 2; - return @bitCast(buffer[i.*..][0..2].*); - }, - else => comptime unreachable, - }, - .@"enum" => { - defer i.* += 1; - return @enumFromInt(buffer[i.*]); - }, - .@"struct" => |info| switch (info.layout) { - .@"packed" => switch (info.backing_integer.?) { - u32 => { - defer i.* += 1; - return @bitCast(buffer[i.*]); - }, - u64 => { - defer i.* += 2; - return @bitCast(buffer[i.*..][0..2].*); - }, - else => comptime unreachable, - }, - .auto => switch (Field) { - std.Target.Cpu.Feature.Set => { - const u32_count = (Field.usize_count * @sizeOf(usize)) / @sizeOf(u32); - defer i.* += u32_count; - return .{ .ints = @as( - *align(@alignOf(u32)) const [Field.usize_count]usize, - @ptrCast(buffer[i.*..][0..u32_count]), - ).* }; - }, - else => switch (Field.storage) { - .flag_optional => { - const flags = @field(container, @tagName(Field.flags)); - const flag = @field(flags, @tagName(Field.flag)); - return .{ - .value = if (flag) dataField(buffer, i, container, Field.Value) else null, - }; - }, - .flag_union => { - const flags = @field(container, @tagName(Field.flags)); - const tag: Field.Tag = @field(flags, @tagName(Field.flag)); - return .{ - .u = switch (tag) { - inline else => |comptime_tag| @unionInit( - Field.Union, - @tagName(comptime_tag), - dataField( - buffer, - i, - container, - @typeInfo(Field.Union).@"union".fields[@intFromEnum(comptime_tag)].type, - ), - ), - }, - }; - }, - .enum_optional => { - const flags = @field(container, @tagName(Field.flags)); - const tag = @field(flags, @tagName(Field.flag)); - const match = tag == Field.tag; - return .{ - .value = if (match) dataField(buffer, i, container, Field.Value) else null, - }; - }, - .extended => @compileError("TODO"), - .length_prefixed_list => { - const data_start = i.* + 1; - const len = buffer[data_start - 1]; - defer i.* = data_start + len; - return .{ .slice = @ptrCast(buffer[data_start..][0..len]) }; - }, - .flag_length_prefixed_list => { - const flags = @field(container, @tagName(Field.flags)); - const flag = @field(flags, @tagName(Field.flag)); - if (!flag) return .{ .slice = &.{} }; - const data_start = i.* + 1; - const len = buffer[data_start - 1]; - defer i.* = data_start + len; - return .{ .slice = @ptrCast(buffer[data_start..][0..len]) }; - }, - .flag_list => { - const flags = @field(container, @tagName(Field.flags)); - const len: u32 = @field(flags, @tagName(Field.flag)); - const data_start = i.*; - defer i.* = data_start + len; - return .{ .slice = @ptrCast(buffer[data_start..][0..len]) }; - }, - .multi_list => { - const data_start = i.* + 1; - const len = buffer[data_start - 1]; - defer i.* = data_start + len * @typeInfo(Field.Elem).@"struct".fields.len; - return .{ .mal = .{ - .bytes = @ptrCast(@constCast(buffer[data_start..][0..len])), - .len = len, - .capacity = len, - } }; - }, - .union_list => { - const flags = @field(container, @tagName(Field.flags)); - const flag = @field(flags, @tagName(Field.flag)); - if (!flag) return .{ .data = null, .len = 0 }; - const meta_start = i.*; - const meta_buffer = buffer[meta_start..]; - var len: u32 = 0; - var bit_offset: usize = 0; - while (true) : (bit_offset += @bitSizeOf(Field.Meta)) { - const meta = loadBits(u32, meta_buffer, bit_offset, Field.Meta); - len += 1; - if (meta.last) break; - } - const end = meta_start + Field.extraLen(len); - i.* = end; - return .{ .data = @ptrFromInt(end - len), .len = len }; - }, - }, - }, - .@"extern" => comptime unreachable, - }, - else => comptime unreachable, - } - } - - /// Returns new end index. - fn setExtra(buffer: []u32, index: usize, extra: anytype) usize { - const fields = @typeInfo(@TypeOf(extra)).@"struct".fields; - var i = index; - inline for (fields) |field| { - i += setExtraField(buffer, i, field.type, @field(extra, field.name)); - } - return i; - } - - fn extraFieldLen(field: anytype) usize { - const Field = @TypeOf(field); - return switch (@typeInfo(Field)) { - .void => 0, - .int => |info| switch (info.bits) { - 32 => 1, - 64 => 2, - else => comptime unreachable, - }, - .@"enum" => 1, - .@"struct" => |info| switch (info.layout) { - .@"packed" => switch (info.backing_integer.?) { - u32 => 1, - u64 => 2, - else => comptime unreachable, - }, - .auto => switch (Field.storage) { - .flag_optional, .enum_optional, .extended => 1, - .length_prefixed_list, - .flag_length_prefixed_list, - .flag_list, - => 1 + @divExact(@sizeOf(Field.Elem), @sizeOf(u32)) * field.slice.len, - .multi_list => 1 + field.mal.len * @typeInfo(Field.Elem).@"struct".fields.len, - .union_list => Field.extraLen(field.len), - .flag_union => switch (field.u) { - inline else => |v| extraFieldLen(v), - }, - }, - .@"extern" => comptime unreachable, - }, - else => @compileError("bad type: " ++ @typeName(Field)), - }; - } - - fn extraLen(extra: anytype) usize { - const fields = @typeInfo(@TypeOf(extra)).@"struct".fields; - var i: usize = 0; - inline for (fields) |field| { - i += Storage.extraFieldLen(@field(extra, field.name)); - } - return i; - } - - inline fn setExtraField(buffer: []u32, i: usize, comptime Field: type, value: anytype) usize { - switch (@typeInfo(Field)) { - .void => return 0, - .int => |info| switch (info.bits) { - 32 => { - buffer[i] = value; - return 1; - }, - 64 => { - buffer[i..][0..2].* = @bitCast(value); - return 2; - }, - else => comptime unreachable, - }, - .@"enum" => { - buffer[i] = @intFromEnum(value); - return 1; - }, - .@"struct" => |info| switch (info.layout) { - .@"packed" => switch (info.backing_integer.?) { - u32 => { - buffer[i] = @bitCast(value); - return 1; - }, - u64 => { - buffer[i..][0..2].* = @bitCast(value); - return 2; - }, - else => comptime unreachable, - }, - .auto => switch (Field) { - std.Target.Cpu.Feature.Set => { - const casted: []const u32 = @ptrCast(&value.ints); - @memcpy(buffer[i..][0..casted.len], casted); - return casted.len; - }, - else => switch (Field.storage) { - .flag_optional, .enum_optional => { - return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0; - }, - .flag_union => return switch (value.u) { - inline else => |x| setExtraField(buffer, i, @TypeOf(x), x), - }, - .extended => @compileError("TODO"), - .flag_length_prefixed_list, .length_prefixed_list => { - const len: u32 = @intCast(value.slice.len); - if (len == 0) return 0; - buffer[i] = len; - @memcpy(buffer[i + 1 ..][0..len], @as([]const u32, @ptrCast(value.slice))); - return len + 1; - }, - .flag_list => { - const len: u32 = @intCast(value.slice.len); - @memcpy(buffer[i..][0..len], @as([]const u32, @ptrCast(value.slice))); - return len; - }, - .multi_list => { - const len: u32 = @intCast(value.mal.len); - if (len == 0) return 0; - buffer[i] = len; - const fields = @typeInfo(Field.Elem).@"struct".fields; - inline for (0..fields.len) |field_i| @memcpy( - buffer[i + 1 + field_i * len ..][0..len], - @as([]const u32, @ptrCast(value.mal.items(@enumFromInt(field_i)))), - ); - return 1 + fields.len * len; - }, - .union_list => { - if (value.len == 0) return 0; - const Tag = @typeInfo(Field.Union).@"union".tag_type.?; - const slice_ptr: [*]const Field.Union = @ptrCast(@alignCast(value.data)); - const slice = slice_ptr[0..value.len]; - const meta_buffer = buffer[i..][0 .. (slice.len * @bitSizeOf(Field.Meta) + 31) / 32]; - for (slice[0 .. slice.len - 1], 0..) |elem, elem_index| { - const union_tag: Tag = elem; - storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{ - .tag = union_tag, - .last = false, - })); - } else { - const elem_index = slice.len - 1; - const elem = slice[elem_index]; - const union_tag: Tag = elem; - storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{ - .tag = union_tag, - .last = true, - })); - } - var total: usize = meta_buffer.len; - for (i + meta_buffer.len.., slice) |elem_index, src| switch (src) { - inline else => |x| total += setExtraField(buffer, elem_index, @TypeOf(x), x), - }; - return total; - }, - }, - }, - .@"extern" => comptime unreachable, - }, - else => @compileError("bad field type: " ++ @typeName(Field)), - } - } -}; - -pub fn extraData(c: *const Configuration, comptime T: type, index: usize) T { - var i: usize = index; - return Storage.data(c.extra, &i, T); -} - -pub const LoadFileError = Io.File.Reader.Error || Allocator.Error || error{EndOfStream}; - -pub fn loadFile(arena: Allocator, io: Io, file: Io.File) LoadFileError!Configuration { - var buffer: [2000]u8 = undefined; - var fr = file.reader(io, &buffer); - return load(arena, &fr.interface) catch |err| switch (err) { - error.ReadFailed => return fr.err.?, - else => |e| return e, - }; -} - -pub const LoadError = Io.Reader.Error || Allocator.Error; - -pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration { - const header = try reader.takeStruct(Header, .little); - var result: Configuration = .{ - .string_bytes = try arena.alloc(u8, header.string_bytes_len), - .steps = try arena.alloc(Step, header.steps_len), - .path_deps_sub = try arena.alloc(String, header.path_deps_len), - .path_deps_base = try arena.alloc(Path.Base, header.path_deps_len), - .unlazy_deps = try arena.alloc(String, header.unlazy_deps_len), - .system_integrations = try arena.alloc(SystemIntegration, header.system_integrations_len), - .available_options = try arena.alloc(AvailableOption, header.available_options_len), - .extra = try arena.alloc(u32, header.extra_len), - .default_step = header.default_step, - .generated_files_len = header.generated_files_len, - }; - var vecs = [_][]u8{ - result.string_bytes, - @ptrCast(result.steps), - @ptrCast(result.path_deps_base), - @ptrCast(result.path_deps_sub), - @ptrCast(result.unlazy_deps), - @ptrCast(result.system_integrations), - @ptrCast(result.available_options), - @ptrCast(result.extra), - }; - try reader.readVecAll(&vecs); - return result; -} - -pub fn loadBits(comptime Int: type, buffer: []const Int, bit_offset: usize, comptime Result: type) Result { - const index = bit_offset / @bitSizeOf(Int); - const small_bit_offset = bit_offset % @bitSizeOf(Int); - const ResultInt = @Int(.unsigned, @bitSizeOf(Result)); - const result: ResultInt = @truncate(buffer[index] >> @intCast(small_bit_offset)); - const available_bits = @bitSizeOf(Int) - small_bit_offset; - if (available_bits >= @bitSizeOf(ResultInt)) return @bitCast(result); - const missing_bits = @bitSizeOf(ResultInt) - available_bits; - const upper: ResultInt = @truncate(buffer[index + 1] & ((@as(usize, 1) << @intCast(missing_bits)) - 1)); - return @bitCast(result | (upper << @intCast(available_bits))); -} - -pub fn storeBits(comptime Int: type, buffer: []Int, bit_offset: usize, value: anytype) void { - const Value = @TypeOf(value); - const ValueInt = @Int(.unsigned, @bitSizeOf(Value)); - const value_int: ValueInt = @bitCast(value); - const index = bit_offset / @bitSizeOf(Int); - const small_bit_offset = bit_offset % @bitSizeOf(Int); - const available_bits = @bitSizeOf(Int) - small_bit_offset; - if (available_bits >= @bitSizeOf(ValueInt)) { - buffer[index] &= ~(((@as(Int, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(small_bit_offset)); - buffer[index] |= @as(Int, value_int) << @intCast(small_bit_offset); - } else { - const DoubleInt = @Int(.unsigned, @bitSizeOf(Int) * 2); - const ptr: *align(@alignOf(Int)) DoubleInt = @ptrCast(buffer[index..][0..2]); - ptr.* &= ~(((@as(DoubleInt, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(small_bit_offset)); - ptr.* |= @as(DoubleInt, value_int) << @intCast(small_bit_offset); - } -} - -test "loadBits and storeBits" { - var buffer: [2]u32 = .{ - 0b01111111000000001111111100000000, - 0b11111111000000001111111100000100, - }; - try std.testing.expectEqual(0b100, loadBits(u32, &buffer, 6, u3)); - try std.testing.expectEqual(0b100011, loadBits(u32, &buffer, 29, u6)); - - storeBits(u32, &buffer, 6, @as(u3, 0b010)); - storeBits(u32, &buffer, 29, @as(u6, 0b010010)); - - try std.testing.expectEqual(0b010, loadBits(u32, &buffer, 6, u3)); - try std.testing.expectEqual(0b010010, loadBits(u32, &buffer, 29, u6)); -}