1const Configuration = @This();
2
3const std = @import("../std.zig");
4const builtin = @import("builtin");
5const Io = std.Io;
6const Allocator = std.mem.Allocator;
7const assert = std.debug.assert;
8const max_u32 = std.math.maxInt(u32);
9const native_endian = builtin.target.cpu.arch.endian();
10
11string_bytes: []u8,
12steps: []Step,
13path_deps: []PathDep,
14unlazy_deps: []String,
15system_integrations: []SystemIntegration,
16available_options: []AvailableOption,
17search_prefixes: []String,
18extra: []u32,
19default_step: Step.Index,
20generated_files_len: u32,
21poisoned: bool,
22
23/// The field order here matches `Configuration` which documents the order in
24/// the serialized format.
25pub const Header = extern struct {
26 string_bytes_len: u32,
27 steps_len: u32,
28 path_deps_len: u32,
29 unlazy_deps_len: u32,
30 system_integrations_len: u32,
31 available_options_len: u32,
32 search_prefixes_len: u32,
33 extra_len: u32,
34
35 default_step: Step.Index,
36 /// There is not actually any data stored for this - it just provides a way
37 /// for maker process to preallocate an array for these.
38 generated_files_len: u32,
39 flags: Flags,
40
41 pub const Flags = packed struct(u32) {
42 poisoned: bool,
43 _: u31 = 0,
44 };
45};
46
47pub const Wip = struct {
48 gpa: Allocator,
49 string_table: StringTable = .empty,
50 /// De-duplicates an array inside `extra`.
51 dedupe_table: DedupeTable = .empty,
52 targets_table: TargetsTable = .empty,
53
54 string_bytes: std.ArrayList(u8) = .empty,
55 unlazy_deps: std.ArrayList(String) = .empty,
56 system_integrations: std.ArrayList(SystemIntegration) = .empty,
57 available_options: std.ArrayList(AvailableOption) = .empty,
58 steps: std.ArrayList(Step) = .empty,
59 path_deps: std.ArrayList(PathDep) = .empty,
60 search_prefixes: std.ArrayList(String) = .empty,
61 extra: std.ArrayList(u32) = .empty,
62 next_generated_file_index: u32 = 0,
63 cache_poison: bool = false,
64
65 const DedupeTable = std.HashMapUnmanaged(ExtraSlice, void, ExtraSlice.Context, std.hash_map.default_max_load_percentage);
66 const TargetsTable = std.HashMapUnmanaged(TargetQuery.Index, void, TargetsTableContext, std.hash_map.default_max_load_percentage);
67
68 const ExtraSlice = struct {
69 index: u32,
70 len: u32,
71
72 const Context = struct {
73 extra: []const u32,
74
75 pub fn eql(ctx: @This(), a: ExtraSlice, b: ExtraSlice) bool {
76 const slice_a = ctx.extra[a.index..][0..a.len];
77 const slice_b = ctx.extra[b.index..][0..b.len];
78 return std.mem.eql(u32, slice_a, slice_b);
79 }
80
81 pub fn hash(ctx: @This(), key: ExtraSlice) u64 {
82 const slice = ctx.extra[key.index..][0..key.len];
83 return std.hash_map.hashString(@ptrCast(slice));
84 }
85 };
86 };
87
88 const TargetsTableContext = struct {
89 extra: []const u32,
90
91 pub fn eql(ctx: @This(), a: TargetQuery.Index, b: TargetQuery.Index) bool {
92 const slice_a = a.extraSlice(ctx.extra);
93 const slice_b = b.extraSlice(ctx.extra);
94 return std.mem.eql(u32, slice_a, slice_b);
95 }
96
97 pub fn hash(ctx: @This(), key: TargetQuery.Index) u64 {
98 const slice = key.extraSlice(ctx.extra);
99 return std.hash_map.hashString(@ptrCast(slice));
100 }
101 };
102
103 const StringTable = std.HashMapUnmanaged(String, void, StringTableContext, std.hash_map.default_max_load_percentage);
104 const StringTableContext = struct {
105 bytes: []const u8,
106
107 pub fn eql(_: @This(), a: String, b: String) bool {
108 return a == b;
109 }
110
111 pub fn hash(ctx: @This(), key: String) u64 {
112 return std.hash_map.hashString(std.mem.sliceTo(ctx.bytes[@backingInt(key)..], 0));
113 }
114 };
115
116 const StringTableIndexAdapter = struct {
117 bytes: []const u8,
118
119 pub fn eql(ctx: @This(), a: []const u8, b: String) bool {
120 return std.mem.eql(u8, a, std.mem.sliceTo(ctx.bytes[@backingInt(b)..], 0));
121 }
122
123 pub fn hash(_: @This(), adapted_key: []const u8) u64 {
124 assert(std.mem.findScalar(u8, adapted_key, 0) == null);
125 return std.hash_map.hashString(adapted_key);
126 }
127 };
128
129 pub fn init(gpa: Allocator) Wip {
130 return .{ .gpa = gpa };
131 }
132
133 pub fn deinit(wip: *Wip) void {
134 const gpa = wip.gpa;
135 wip.string_bytes.deinit(gpa);
136 wip.unlazy_deps.deinit(gpa);
137 wip.system_integrations.deinit(gpa);
138 wip.available_options.deinit(gpa);
139 wip.steps.deinit(gpa);
140 wip.path_deps.deinit(gpa);
141 wip.search_prefixes.deinit(gpa);
142 wip.extra.deinit(gpa);
143 wip.* = undefined;
144 }
145
146 pub const Static = struct {
147 default_step: Step.Index,
148 generated_files_len: u32,
149 poisoned: bool,
150 };
151
152 pub fn write(wip: *Wip, w: *Io.Writer, static: Static) Io.Writer.Error!void {
153 const header: Header = .{
154 .string_bytes_len = @intCast(wip.string_bytes.items.len),
155 .steps_len = @intCast(wip.steps.items.len),
156 .path_deps_len = @intCast(wip.path_deps.items.len),
157 .unlazy_deps_len = @intCast(wip.unlazy_deps.items.len),
158 .system_integrations_len = @intCast(wip.system_integrations.items.len),
159 .available_options_len = @intCast(wip.available_options.items.len),
160 .search_prefixes_len = @intCast(wip.search_prefixes.items.len),
161 .extra_len = @intCast(wip.extra.items.len),
162
163 .default_step = static.default_step,
164 .generated_files_len = static.generated_files_len,
165 .flags = .{
166 .poisoned = static.poisoned,
167 },
168 };
169 var buffers = [_][]const u8{
170 @ptrCast(&header),
171 wip.string_bytes.items,
172 @ptrCast(wip.steps.items),
173 @ptrCast(wip.path_deps.items),
174 @ptrCast(wip.unlazy_deps.items),
175 @ptrCast(wip.system_integrations.items),
176 @ptrCast(wip.available_options.items),
177 @ptrCast(wip.search_prefixes.items),
178 @ptrCast(wip.extra.items),
179 };
180 try w.writeVecAll(&buffers);
181 }
182
183 pub fn addString(wip: *Wip, bytes: []const u8) Allocator.Error!String {
184 const gpa = wip.gpa;
185 assert(std.mem.findScalar(u8, bytes, 0) == null);
186 const gop = try wip.string_table.getOrPutContextAdapted(
187 gpa,
188 @as([]const u8, bytes),
189 @as(StringTableIndexAdapter, .{ .bytes = wip.string_bytes.items }),
190 @as(StringTableContext, .{ .bytes = wip.string_bytes.items }),
191 );
192 if (gop.found_existing) return gop.key_ptr.*;
193
194 try wip.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1);
195 const new_off: String = @fromBackingInt(@intCast(wip.string_bytes.items.len));
196
197 wip.string_bytes.appendSliceAssumeCapacity(bytes);
198 wip.string_bytes.appendAssumeCapacity(0);
199
200 gop.key_ptr.* = new_off;
201
202 return new_off;
203 }
204
205 pub fn addOptionalString(wip: *Wip, bytes: ?[]const u8) Allocator.Error!OptionalString {
206 return .init(try addString(wip, bytes orelse return .none));
207 }
208
209 pub fn addStringList(wip: *Wip, list: []const []const u8) Allocator.Error!StringList {
210 // Increase size of extra to support the list. Add the string list
211 // there. Then check for duplicate, reverting list if already found.
212 const gpa = wip.gpa;
213 const revert_index: u32 = @intCast(wip.extra.items.len);
214 const added = try wip.extra.addManyAsSlice(gpa, list.len + 1);
215 added[0] = @intCast(list.len);
216 for (added[1..], list) |*d, s| d.* = @backingInt(try addString(wip, s));
217 const gop = try wip.dedupe_table.getOrPutContext(gpa, .{
218 .index = revert_index,
219 .len = @intCast(added.len),
220 }, @as(ExtraSlice.Context, .{ .extra = wip.extra.items }));
221
222 if (gop.found_existing) {
223 wip.extra.items.len = revert_index;
224 return @fromBackingInt(@intCast(gop.key_ptr.index));
225 }
226
227 return @fromBackingInt(@intCast(revert_index));
228 }
229
230 pub fn addBytes(wip: *Wip, bytes: []const u8) Allocator.Error!Bytes {
231 try wip.string_bytes.appendSlice(wip.gpa, bytes);
232 return .{
233 .index = @intCast(wip.string_bytes.items.len - bytes.len),
234 .len = @intCast(bytes.len),
235 };
236 }
237
238 pub fn addSemVer(wip: *Wip, sv: std.SemanticVersion) Allocator.Error!String {
239 var buffer: [256]u8 = undefined;
240 var writer: std.Io.Writer = .fixed(&buffer);
241 sv.format(&writer) catch return error.OutOfMemory;
242 return addString(wip, writer.buffered());
243 }
244
245 pub fn addTargetQuery(wip: *Wip, q: *const std.Target.Query) !TargetQuery.OptionalIndex {
246 if (q.isNative()) return .none;
247 const gpa = wip.gpa;
248 const cpu_name: ?String = switch (q.cpu_model) {
249 .native, .baseline, .determined_by_arch_os => null,
250 .explicit => |model| try wip.addString(model.name),
251 };
252 const os_version_min: TargetQuery.OsVersion = if (q.os_version_min) |ver| switch (ver) {
253 .none => .none,
254 .semver => |sem_ver| .{ .semver = try wip.addSemVer(sem_ver) },
255 .windows => |win_ver| .{ .windows = win_ver },
256 } else .default;
257 const os_version_max: TargetQuery.OsVersion = if (q.os_version_max) |ver| switch (ver) {
258 .none => .none,
259 .semver => |sem_ver| .{ .semver = try wip.addSemVer(sem_ver) },
260 .windows => |win_ver| .{ .windows = win_ver },
261 } else .default;
262 const glibc_version: ?String = if (q.glibc_version) |sem_ver| try wip.addSemVer(sem_ver) else null;
263 const dynamic_linker: ?String = if (q.dynamic_linker) |*dl|
264 if (dl.get()) |s| try wip.addString(s) else .empty
265 else
266 null;
267 const cpu_features_add_empty = q.cpu_features_add.isEmpty();
268 const cpu_features_sub_empty = q.cpu_features_sub.isEmpty();
269 const result_index: TargetQuery.Index = try wip.addExtra(TargetQuery, .{
270 .flags = .{
271 .cpu_arch = .init(q.cpu_arch),
272 .cpu_model = .init(q.cpu_model),
273 .cpu_features_add = !cpu_features_add_empty,
274 .cpu_features_sub = !cpu_features_sub_empty,
275 .os_tag = .init(q.os_tag),
276 .abi = .init(q.abi),
277 .object_format = .init(q.ofmt),
278 .os_version_min = os_version_min,
279 .os_version_max = os_version_max,
280 .glibc_version = glibc_version != null,
281 .android_api_level = q.android_api_level != null,
282 .dynamic_linker = dynamic_linker != null,
283 },
284 .cpu_features_add = .{ .value = if (cpu_features_add_empty) null else q.cpu_features_add },
285 .cpu_features_sub = .{ .value = if (cpu_features_sub_empty) null else q.cpu_features_sub },
286 .glibc_version = .{ .value = glibc_version },
287 .android_api_level = .{ .value = q.android_api_level },
288 .dynamic_linker = .{ .value = dynamic_linker },
289 .cpu_name = .{ .value = cpu_name },
290 .os_version_min = .{ .u = os_version_min },
291 .os_version_max = .{ .u = os_version_max },
292 });
293
294 // Deduplicate.
295 const gop = try wip.targets_table.getOrPutContext(gpa, result_index, @as(TargetsTableContext, .{
296 .extra = wip.extra.items,
297 }));
298 if (gop.found_existing) {
299 wip.extra.items.len = @backingInt(result_index);
300 return .init(gop.key_ptr.*);
301 } else {
302 return .init(result_index);
303 }
304 }
305
306 pub fn addTarget(wip: *Wip, t: std.Target) !TargetQuery.Index {
307 const gpa = wip.gpa;
308 const cpu_name: String = try wip.addString(t.cpu.model.name);
309
310 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()) {
311 .none => .{
312 .none,
313 .none,
314 null,
315 null,
316 },
317 .semver => |range| .{
318 .{ .semver = try wip.addSemVer(range.min) },
319 .{ .semver = try wip.addSemVer(range.max) },
320 null,
321 null,
322 },
323 .hurd => |hurd| .{
324 .{ .semver = try wip.addSemVer(hurd.range.min) },
325 .{ .semver = try wip.addSemVer(hurd.range.max) },
326 try wip.addSemVer(hurd.glibc),
327 null,
328 },
329 .linux => |linux| .{
330 .{ .semver = try wip.addSemVer(linux.range.min) },
331 .{ .semver = try wip.addSemVer(linux.range.max) },
332 try wip.addSemVer(linux.glibc),
333 linux.android,
334 },
335 .windows => |range| .{
336 .{ .windows = range.min },
337 .{ .windows = range.max },
338 null,
339 null,
340 },
341 };
342 const dynamic_linker: ?String = if (t.dynamic_linker.get()) |dl| try wip.addString(dl) else null;
343 const cpu_features_add_empty = t.cpu.features.isEmpty();
344 const result_index = try wip.addExtra(TargetQuery, .{
345 .flags = .{
346 .cpu_arch = .init(t.cpu.arch),
347 .cpu_model = .explicit,
348 .cpu_features_add = !cpu_features_add_empty,
349 .cpu_features_sub = false,
350 .os_tag = .init(t.os.tag),
351 .abi = .init(t.abi),
352 .object_format = .init(t.ofmt),
353 .os_version_min = os_version_min,
354 .os_version_max = os_version_max,
355 .glibc_version = glibc_version != null,
356 .android_api_level = android_api_level != null,
357 .dynamic_linker = dynamic_linker != null,
358 },
359 .cpu_features_add = .{ .value = if (cpu_features_add_empty) null else t.cpu.features },
360 .cpu_features_sub = .{ .value = null },
361 .glibc_version = .{ .value = glibc_version },
362 .android_api_level = .{ .value = android_api_level },
363 .dynamic_linker = .{ .value = dynamic_linker },
364 .cpu_name = .{ .value = cpu_name },
365 .os_version_min = .{ .u = os_version_min },
366 .os_version_max = .{ .u = os_version_max },
367 });
368
369 // Deduplicate.
370 const gop = try wip.targets_table.getOrPutContext(gpa, result_index, @as(TargetsTableContext, .{
371 .extra = wip.extra.items,
372 }));
373 if (gop.found_existing) {
374 wip.extra.items.len = @backingInt(result_index);
375 return gop.key_ptr.*;
376 } else {
377 return result_index;
378 }
379 }
380
381 pub fn addExtra(wip: *Wip, comptime T: type, v: T) Allocator.Error!T.Index {
382 const extra_len = Storage.extraLen(v);
383 try wip.extra.ensureUnusedCapacity(wip.gpa, extra_len);
384 return addExtraReserved(wip, T, v);
385 }
386
387 pub fn addExtraErased(wip: *Wip, comptime T: type, v: T) Allocator.Error!u32 {
388 const extra_len = Storage.extraLen(v);
389 try wip.extra.ensureUnusedCapacity(wip.gpa, extra_len);
390 return addExtraReservedErased(wip, T, v);
391 }
392
393 /// Same as `addExtra` but uses a hash map to possibly return an already
394 /// existing index instead of appending to `extra`.
395 pub fn addDeduped(wip: *Wip, comptime T: type, v: T) Allocator.Error!T.Index {
396 const gpa = wip.gpa;
397 const revert_index = wip.extra.items.len;
398 const upper_bound_len = Storage.extraLen(v);
399 try wip.extra.ensureUnusedCapacity(gpa, upper_bound_len);
400 try wip.dedupe_table.ensureUnusedCapacityContext(gpa, 1, @as(ExtraSlice.Context, .{
401 .extra = wip.extra.items,
402 }));
403 const new_index = addExtraReservedErased(wip, T, v);
404 const len: u32 = @intCast(wip.extra.items.len - new_index);
405 assert(len != 0);
406 const gop = wip.dedupe_table.getOrPutAssumeCapacityContext(.{
407 .index = new_index,
408 .len = len,
409 }, @as(ExtraSlice.Context, .{ .extra = wip.extra.items }));
410
411 if (gop.found_existing) {
412 wip.extra.items.len = revert_index;
413 return @fromBackingInt(@intCast(gop.key_ptr.index));
414 }
415
416 return @fromBackingInt(@intCast(new_index));
417 }
418
419 pub fn addExtraReserved(wip: *Wip, comptime T: type, v: T) T.Index {
420 return @fromBackingInt(@intCast(addExtraReservedErased(wip, T, v)));
421 }
422
423 pub fn addExtraReservedErased(wip: *Wip, comptime T: type, v: T) u32 {
424 const result: u32 = @intCast(wip.extra.items.len);
425 wip.extra.items.len = Storage.setExtra(wip.extra.allocatedSlice(), result, v);
426 return result;
427 }
428
429 fn addExtraOptionalStringAssumeCapacity(wip: *Wip, optional_string: ?String) void {
430 const string = optional_string orelse return;
431 wip.extra.appendAssumeCapacity(@backingInt(string));
432 }
433
434 pub fn addGeneratedFile(wip: *Wip) GeneratedFileIndex {
435 defer wip.next_generated_file_index += 1;
436 return @fromBackingInt(@intCast(wip.next_generated_file_index));
437 }
438
439 /// Returned slice expires upon next append to the configuration.
440 pub fn stringSlice(wip: *const Wip, s: String) [:0]const u8 {
441 const start_slice = wip.string_bytes.items[@backingInt(s)..];
442 return start_slice[0..std.mem.findScalar(u8, start_slice, 0).? :0];
443 }
444};
445
446pub const SystemIntegration = extern struct {
447 name: String,
448 status: Status,
449
450 pub const Status = enum(u32) {
451 disabled = 0,
452 enabled = 1,
453 };
454};
455
456pub const AvailableOption = extern struct {
457 name: String,
458 description: String,
459 type: Type,
460 /// If the `type_id` is `enum` or `enum_list` this provides the list of enum options
461 enum_options: OptionalStringList,
462
463 pub const Type = enum(u8) {
464 bool,
465 int,
466 float,
467 @"enum",
468 enum_list,
469 string,
470 list,
471 build_id,
472 lazy_path,
473 lazy_path_list,
474 };
475};
476
477pub const Step = extern struct {
478 name: String,
479 owner: Package.Index,
480 deps: Deps.Index,
481 max_rss: MaxRss,
482 extended: Storage.Extended(Flags, union(Tag) {
483 check_file: CheckFile,
484 compile: Compile,
485 config_header: ConfigHeader,
486 fail: Fail,
487 find_program: FindProgram,
488 fmt: Fmt,
489 install_artifact: InstallArtifact,
490 install_dir: InstallDir,
491 install_file: InstallFile,
492 obj_copy: ObjCopy,
493 options: Options,
494 run: Run,
495 top_level: TopLevel,
496 translate_c: TranslateC,
497 update_source_files: UpdateSourceFiles,
498 write_file: WriteFile,
499 }),
500
501 /// Points into `steps`.
502 pub const Index = enum(u32) {
503 _,
504
505 pub fn ptr(i: Index, c: *const Configuration) *const Step {
506 return &c.steps[@backingInt(i)];
507 }
508 };
509
510 /// Shared by all steps.
511 pub const Flags = packed struct(u32) {
512 tag: Tag,
513 _: u27 = 0,
514 };
515
516 pub const Tag = enum(u5) {
517 check_file,
518 compile,
519 config_header,
520 fail,
521 find_program,
522 fmt,
523 install_artifact,
524 install_dir,
525 install_file,
526 obj_copy,
527 options,
528 run,
529 top_level,
530 translate_c,
531 update_source_files,
532 write_file,
533 };
534
535 pub const TopLevel = struct {
536 flags: @This().Flags = .{},
537 description: String,
538
539 pub const Flags = packed struct(u32) {
540 tag: Tag = .top_level,
541 _: u27 = 0,
542 };
543 };
544
545 /// The first dependency step index will be the compile step whose
546 /// artifacts are being installed with this step.
547 pub const InstallArtifact = struct {
548 flags: @This().Flags,
549 bin_dir: Storage.FlagOptional(.flags, .bin_dir, InstallDestDir),
550 implib_dir: Storage.FlagOptional(.flags, .implib_dir, InstallDestDir),
551 pdb_dir: Storage.FlagOptional(.flags, .pdb_dir, InstallDestDir),
552 h_dir: Storage.FlagOptional(.flags, .h_dir, InstallDestDir),
553 bin_sub_path: Storage.FlagOptional(.flags, .bin_sub_path, String),
554
555 pub const Flags = packed struct(u32) {
556 tag: Tag = .install_artifact,
557 dylib_symlinks: bool,
558 bin_dir: bool,
559 implib_dir: bool,
560 pdb_dir: bool,
561 h_dir: bool,
562 bin_sub_path: bool,
563 _: u21 = 0,
564 };
565 };
566
567 pub const Run = struct {
568 flags: @This().Flags,
569 flags2: Flags2,
570 args: Storage.LengthPrefixedList(Arg.Index),
571 cwd: Storage.FlagOptional(.flags, .cwd, LazyPath.Index),
572 preopens: Storage.FlagLengthPrefixedList(.flags, .preopens, Preopen),
573 captured_stdout: Storage.FlagOptional(.flags, .captured_stdout, CapturedStream),
574 captured_stderr: Storage.FlagOptional(.flags, .captured_stderr, CapturedStream),
575 file_inputs: Storage.LengthPrefixedList(LazyPath.Index),
576 stdio_limit: Storage.FlagOptional(.flags, .stdio_limit, u64),
577 /// Always a compile step.
578 producer: Storage.FlagOptional(.flags, .producer, Step.Index),
579 /// First half is keys, second half is values.
580 environ_map: Storage.FlagOptional(.flags, .environ_map, EnvironMap.Index),
581 stdin: Storage.FlagUnion(.flags, .stdin, StdIn),
582 expect_stderr_exact: Storage.FlagOptional(.flags2, .expect_stderr_exact, Bytes),
583 expect_stdout_exact: Storage.FlagOptional(.flags2, .expect_stdout_exact, Bytes),
584 expect_stderr_match: Storage.FlagLengthPrefixedList(.flags2, .expect_stderr_match, Bytes),
585 expect_stdout_match: Storage.FlagLengthPrefixedList(.flags2, .expect_stdout_match, Bytes),
586 expect_term_value: Storage.FlagOptional(.flags2, .expect_term, u32),
587 expect_stdout_snapshot: Storage.FlagOptional(.flags2, .expect_stdout_snapshot, LazyPath.Index),
588 expect_stderr_snapshot: Storage.FlagOptional(.flags2, .expect_stderr_snapshot, LazyPath.Index),
589
590 pub const CapturedStream = extern struct {
591 generated_file: GeneratedFileIndex,
592 basename: String,
593 };
594
595 pub const Arg = struct {
596 flags: @This().Flags,
597 prefix: Storage.FlagOptional(.flags, .prefix, String),
598 suffix: Storage.FlagOptional(.flags, .suffix, String),
599 basename: Storage.FlagOptional(.flags, .basename, String),
600 path: Storage.FlagOptional(.flags, .path, LazyPath.Index),
601 /// Always a compile step.
602 producer: Storage.FlagOptional(.flags, .producer, Step.Index),
603 generated: Storage.FlagOptional(.flags, .generated, GeneratedFileIndex),
604
605 pub const Flags = packed struct(u32) {
606 tag: Arg.Tag,
607 prefix: bool,
608 suffix: bool,
609 basename: bool,
610 path: bool,
611 producer: bool,
612 generated: bool,
613 dep_file: bool,
614 make_absolute: bool,
615 _: u20 = 0,
616 };
617
618 pub const Tag = enum(u4) {
619 artifact,
620 /// `path` contains the file.
621 path_file,
622 path_directory,
623 /// `prefix` contains the string.
624 string,
625 file_content,
626 output_file,
627 output_directory,
628 passthru,
629 /// `prefix` contains the enabled string.
630 /// `suffix` contains the disabled string.
631 enable_darling,
632 enable_qemu,
633 enable_rosetta,
634 enable_wasmtime,
635 enable_wine,
636 };
637
638 pub const Index = IndexType(@This());
639 };
640
641 pub const Color = enum(u4) {
642 /// `CLICOLOR_FORCE` is set, and `NO_COLOR` is unset.
643 enable,
644 /// `NO_COLOR` is set, and `CLICOLOR_FORCE` is unset.
645 disable,
646 /// If the build runner is using color, equivalent to `.enable`. Otherwise, equivalent to `.disable`.
647 inherit,
648 /// If stderr is captured or checked, equivalent to `.disable`. Otherwise, equivalent to `.inherit`.
649 auto,
650 /// The build runner does not modify the `CLICOLOR_FORCE` or `NO_COLOR` environment variables.
651 /// They are treated like normal variables, so can be controlled through `setEnvironmentVariable`.
652 manual,
653 };
654
655 pub const Preopen = extern struct {
656 name: String,
657 path: LazyPath.Index,
658 };
659
660 pub const StdIn = union(@This().Tag) {
661 none: void,
662 bytes: Bytes,
663 lazy_path: LazyPath.Index,
664
665 pub const Tag = enum(u2) { none, bytes, lazy_path };
666 };
667 pub const TrimWhitespace = enum(u2) { none, all, leading, trailing };
668 pub const StdIo = enum(u2) { infer_from_args, inherit, check, zig_test };
669
670 pub const ExpectTermStatus = enum(u2) { exited, signal, stopped, unknown };
671
672 pub const Flags = packed struct(u32) {
673 tag: Tag = .run,
674 disable_zig_progress: bool,
675 skip_foreign_checks: bool,
676 failing_to_execute_foreign_is_an_error: bool,
677 has_side_effects: bool,
678 test_runner_mode: bool,
679 color: Color,
680 stdin: StdIn.Tag,
681 stdio: StdIo,
682 stdout_trim_whitespace: TrimWhitespace,
683 stderr_trim_whitespace: TrimWhitespace,
684 stdio_limit: bool,
685 producer: bool,
686 cwd: bool,
687 captured_stdout: bool,
688 captured_stderr: bool,
689 environ_map: bool,
690 preopens: bool,
691 _: u3 = 0,
692 };
693
694 pub const Flags2 = packed struct(u32) {
695 expect_stderr_exact: bool,
696 expect_stdout_exact: bool,
697 expect_stderr_match: bool,
698 expect_stdout_match: bool,
699 expect_term: bool,
700 expect_term_status: ExpectTermStatus,
701 expect_stdout_snapshot: bool,
702 expect_stderr_snapshot: bool,
703 _: u23 = 0,
704 };
705 };
706
707 pub const Compile = struct {
708 flags: @This().Flags,
709 flags2: Flags2,
710 flags3: Flags3,
711 flags4: Flags4,
712
713 root_module: Module.Index,
714 root_name: String,
715
716 filters: Storage.FlagLengthPrefixedList(.flags, .filters_len, String),
717 installed_headers: Storage.FlagLengthPrefixedList(.flags, .installed_headers_len, Storage.Extended(InstalledHeader.Flags, InstalledHeader)),
718 force_undefined_symbols: Storage.FlagLengthPrefixedList(.flags, .force_undefined_symbols_len, String),
719 expect_errors: Storage.FlagUnion(.flags4, .expect_errors, ExpectErrors),
720 linker_script: Storage.FlagOptional(.flags4, .linker_script, LazyPath.Index),
721 version_script: Storage.FlagOptional(.flags4, .version_script, LazyPath.Index),
722 zig_lib_dir: Storage.FlagOptional(.flags3, .zig_lib_dir, LazyPath.Index),
723 libc_file: Storage.FlagOptional(.flags4, .libc_file, LazyPath.Index),
724 win32_manifest: Storage.FlagOptional(.flags3, .win32_manifest, LazyPath.Index),
725 win32_module_definition: Storage.FlagOptional(.flags3, .win32_module_definition, LazyPath.Index),
726 entitlements: Storage.FlagOptional(.flags4, .entitlements, LazyPath.Index),
727 version: Storage.FlagOptional(.flags3, .version, String), // semantic version string
728 entry: Storage.EnumOptional(.flags3, .entry, .symbol_name, String),
729 install_name: Storage.FlagOptional(.flags4, .install_name, String),
730 initial_memory: Storage.FlagOptional(.flags3, .initial_memory, u64),
731 max_memory: Storage.FlagOptional(.flags3, .max_memory, u64),
732 global_base: Storage.FlagOptional(.flags3, .global_base, u64),
733 image_base: Storage.FlagOptional(.flags3, .image_base, u64),
734 link_z_common_page_size: Storage.FlagOptional(.flags4, .link_z_common_page_size, u64),
735 link_z_max_page_size: Storage.FlagOptional(.flags4, .link_z_max_page_size, u64),
736 pagezero_size: Storage.FlagOptional(.flags4, .pagezero_size, u64),
737 stack_size: Storage.FlagOptional(.flags4, .stack_size, u64),
738 headerpad_size: Storage.FlagOptional(.flags4, .headerpad_size, u32),
739 error_limit: Storage.FlagOptional(.flags4, .error_limit, u32),
740 build_id: Storage.EnumOptional(.flags3, .build_id, .hexstring, String),
741 test_runner: Storage.FlagUnion(.flags3, .test_runner, TestRunner),
742
743 emit_directory: Storage.FlagOptional(.flags4, .emit_directory, GeneratedFileIndex),
744 generated_docs: Storage.FlagOptional(.flags4, .generated_docs, GeneratedFileIndex),
745 generated_asm: Storage.FlagOptional(.flags4, .generated_asm, GeneratedFileIndex),
746 generated_bin: Storage.FlagOptional(.flags4, .generated_bin, GeneratedFileIndex),
747 generated_pdb: Storage.FlagOptional(.flags4, .generated_pdb, GeneratedFileIndex),
748 generated_implib: Storage.FlagOptional(.flags4, .generated_implib, GeneratedFileIndex),
749 generated_llvm_bc: Storage.FlagOptional(.flags4, .generated_llvm_bc, GeneratedFileIndex),
750 generated_llvm_ir: Storage.FlagOptional(.flags4, .generated_llvm_ir, GeneratedFileIndex),
751 generated_h: Storage.FlagOptional(.flags4, .generated_h, GeneratedFileIndex),
752
753 pub const InstalledHeader = union(@This().Tag) {
754 file: File,
755 directory: Directory,
756
757 pub const Flags = packed struct(u32) {
758 tag: InstalledHeader.Tag,
759 _: u24 = 0,
760 };
761
762 pub const Tag = enum(u8) {
763 file,
764 directory,
765 };
766
767 pub const File = struct {
768 flags: @This().Flags = .{},
769 source: LazyPath.Index,
770 dest_sub_path: String,
771
772 pub const Flags = packed struct(u32) {
773 tag: InstalledHeader.Tag = .file,
774 _: u24 = 0,
775 };
776 };
777
778 pub const Directory = struct {
779 flags: @This().Flags,
780 source: LazyPath.Index,
781 dest_sub_path: String,
782 exclude_extensions: Storage.FlagLengthPrefixedList(.flags, .exclude_extensions, String),
783 include_extensions: Storage.FlagLengthPrefixedList(.flags, .include_extensions, String),
784
785 pub const Flags = packed struct(u32) {
786 tag: InstalledHeader.Tag = .directory,
787 exclude_extensions: bool,
788 include_extensions: bool,
789 _: u22 = 0,
790 };
791 };
792 };
793 pub const ExpectErrors = union(@This().Tag) {
794 pub const Tag = enum(u3) { contains, exact, starts_with, stderr_contains, none };
795
796 contains: String,
797 exact: Storage.LengthPrefixedList(String),
798 starts_with: String,
799 stderr_contains: String,
800 none: void,
801 };
802 pub const TestRunner = union(@This().Tag) {
803 pub const Tag = enum(u2) { default, simple, server };
804
805 default: void,
806 simple: LazyPath.Index,
807 server: LazyPath.Index,
808 };
809 pub const Entry = enum(u2) { default, disabled, enabled, symbol_name };
810
811 pub const Lto = enum(u2) {
812 none,
813 full,
814 thin,
815 default,
816
817 pub fn init(lto: ?std.zig.LtoMode) Lto {
818 return switch (lto orelse return .default) {
819 .none => .none,
820 .full => .full,
821 .thin => .thin,
822 };
823 }
824 };
825
826 pub const BuildId = enum(u3) {
827 none,
828 fast,
829 uuid,
830 sha1,
831 md5,
832 hexstring,
833 default,
834
835 pub fn init(build_id: ?std.zig.BuildId) BuildId {
836 return switch (build_id orelse return .default) {
837 .none => .none,
838 .fast => .fast,
839 .uuid => .uuid,
840 .sha1 => .sha1,
841 .md5 => .md5,
842 .hexstring => .hexstring,
843 };
844 }
845
846 pub fn unwrap(this: @This(), hexstring: ?String, c: *const Configuration) ?std.zig.BuildId {
847 if (hexstring) |h| {
848 assert(this == .hexstring);
849 return .initHexString(h.slice(c));
850 }
851 return switch (this) {
852 .none => .none,
853 .fast => .fast,
854 .uuid => .uuid,
855 .sha1 => .sha1,
856 .md5 => .md5,
857 .hexstring => unreachable,
858 .default => null,
859 };
860 }
861 };
862 pub const WasiExecModel = enum(u2) {
863 default,
864 command,
865 reactor,
866
867 pub fn init(wasi_exec_model: ?std.builtin.WasiExecModel) WasiExecModel {
868 return switch (wasi_exec_model orelse return .default) {
869 .command => .command,
870 .reactor => .reactor,
871 };
872 }
873 };
874 pub const Linkage = enum(u2) {
875 static,
876 dynamic,
877 default,
878
879 pub fn init(link_mode: ?std.builtin.LinkMode) Linkage {
880 return switch (link_mode orelse return .default) {
881 .static => .static,
882 .dynamic => .dynamic,
883 };
884 }
885
886 pub fn unwrap(this: @This()) ?std.builtin.LinkMode {
887 return switch (this) {
888 .static => .static,
889 .dynamic => .dynamic,
890 .default => null,
891 };
892 }
893 };
894 pub const Kind = enum(u3) {
895 exe,
896 lib,
897 obj,
898 @"test",
899 test_obj,
900
901 pub fn isTest(kind: Kind) bool {
902 return switch (kind) {
903 .exe, .lib, .obj => false,
904 .@"test", .test_obj => true,
905 };
906 }
907
908 pub fn toOutputMode(kind: Kind) std.builtin.OutputMode {
909 return switch (kind) {
910 .exe, .@"test" => .Exe,
911 .lib => .Lib,
912 .obj, .test_obj => .Obj,
913 };
914 }
915 };
916 pub const Subsystem = enum(u4) {
917 console,
918 windows,
919 posix,
920 native,
921 efi_application,
922 efi_boot_service_driver,
923 efi_rom,
924 efi_runtime_driver,
925 default,
926
927 pub fn init(subsystem: ?std.zig.Subsystem) Subsystem {
928 return switch (subsystem orelse return .default) {
929 .console => .console,
930 .windows => .windows,
931 .posix => .posix,
932 .native => .native,
933 .efi_application => .efi_application,
934 .efi_boot_service_driver => .efi_boot_service_driver,
935 .efi_rom => .efi_rom,
936 .efi_runtime_driver => .efi_runtime_driver,
937 };
938 }
939 };
940
941 pub const Flags = packed struct(u32) {
942 tag: Tag = .compile,
943
944 filters_len: bool,
945 installed_headers_len: bool,
946 force_undefined_symbols_len: bool,
947
948 verbose_link: bool,
949 verbose_cc: bool,
950 rdynamic: bool,
951 import_memory: bool,
952 export_memory: bool,
953 import_symbols: bool,
954 import_table: bool,
955 export_table: bool,
956 growable_table: bool,
957 shared_memory: bool,
958 link_eh_frame_hdr: bool,
959 link_emit_relocs: bool,
960 link_function_sections: bool,
961 link_data_sections: bool,
962 linker_dynamicbase: bool,
963 link_z_notext: bool,
964 link_z_relro: bool,
965 link_z_lazy: bool,
966 link_z_defs: bool,
967 headerpad_max_install_names: bool,
968 dead_strip_dylibs: bool,
969 force_load_objc: bool,
970 discard_local_symbols: bool,
971 mingw_unicode_entry_point: bool,
972 };
973
974 pub const Flags2 = packed struct(u32) {
975 pie: DefaultingBool,
976 formatted_panics: DefaultingBool,
977 bundle_compiler_rt: DefaultingBool,
978 bundle_ubsan_rt: DefaultingBool,
979 each_lib_rpath: DefaultingBool,
980 link_gc_sections: DefaultingBool,
981 linker_allow_shlib_undefined: DefaultingBool,
982 linker_allow_undefined_version: DefaultingBool,
983 linker_enable_new_dtags: DefaultingBool,
984 dll_export_fns: DefaultingBool,
985 use_llvm: DefaultingBool,
986 use_lld: DefaultingBool,
987 use_new_linker: DefaultingBool,
988 allow_so_scripts: DefaultingBool,
989 sanitize_coverage_trace_pc_guard: DefaultingBool,
990 linkage: Linkage,
991 };
992
993 pub const Flags3 = packed struct(u32) {
994 version: bool,
995 initial_memory: bool,
996 max_memory: bool,
997 kind: Kind,
998 compress_debug_sections: std.zig.CompressDebugSections,
999 global_base: bool,
1000 test_runner: TestRunner.Tag,
1001 wasi_exec_model: WasiExecModel,
1002 win32_manifest: bool,
1003 win32_module_definition: bool,
1004 zig_lib_dir: bool,
1005 rc_includes: std.zig.RcIncludes,
1006 image_base: bool,
1007 build_id: BuildId,
1008 entry: Entry,
1009 lto: Lto,
1010 subsystem: Subsystem,
1011 _: u2 = 0,
1012 };
1013
1014 pub const Flags4 = packed struct(u32) {
1015 libc_file: bool,
1016 link_z_common_page_size: bool,
1017 link_z_max_page_size: bool,
1018 pagezero_size: bool,
1019 stack_size: bool,
1020 headerpad_size: bool,
1021 error_limit: bool,
1022 install_name: bool,
1023 entitlements: bool,
1024 expect_errors: ExpectErrors.Tag,
1025 linker_script: bool,
1026 version_script: bool,
1027 emit_directory: bool,
1028 generated_docs: bool,
1029 generated_asm: bool,
1030 generated_bin: bool,
1031 generated_pdb: bool,
1032 generated_implib: bool,
1033 generated_llvm_bc: bool,
1034 generated_llvm_ir: bool,
1035 generated_h: bool,
1036 incremental: DefaultingBool,
1037 _: u7 = 0,
1038 };
1039
1040 pub fn isDynamicLibrary(compile: *const Compile) bool {
1041 return compile.flags3.kind == .lib and compile.flags2.linkage == .dynamic;
1042 }
1043
1044 pub fn isStaticLibrary(compile: *const Compile) bool {
1045 return compile.flags3.kind == .lib and compile.flags2.linkage != .dynamic;
1046 }
1047
1048 pub fn producesImplib(compile: *const Compile, c: *const Configuration) bool {
1049 return isDll(compile, c);
1050 }
1051
1052 pub fn isDll(compile: *const Compile, c: *const Configuration) bool {
1053 return isDynamicLibrary(compile) and rootModuleTarget(compile, c).flags.os_tag == .windows;
1054 }
1055
1056 pub fn rootModuleTarget(compile: *const Compile, c: *const Configuration) TargetQuery {
1057 return compile.root_module.get(c).resolved_target.get(c).?.result.get(c);
1058 }
1059 };
1060
1061 pub const CheckFile = struct {
1062 flags: @This().Flags,
1063 file: LazyPath.Index,
1064 expected_exact: Storage.FlagOptional(.flags, .expected_exact, Bytes),
1065 expected_matches: Storage.FlagLengthPrefixedList(.flags, .expected_matches, Bytes),
1066 max_bytes: Storage.FlagOptional(.flags, .max_bytes, u32),
1067
1068 pub const Flags = packed struct(u32) {
1069 tag: Tag = .check_file,
1070 expected_exact: bool,
1071 expected_matches: bool,
1072 max_bytes: bool,
1073 _: u24 = 0,
1074 };
1075 };
1076
1077 pub const ConfigHeader = struct {
1078 flags: @This().Flags,
1079 template_file: Storage.FlagOptional(.flags, .template_file, LazyPath.Index),
1080 generated_dir: GeneratedFileIndex,
1081 input_size_limit: Storage.FlagOptional(.flags, .input_size_limit, u64),
1082 include_path: String,
1083 include_guard: Storage.FlagOptional(.flags, .include_guard, String),
1084 values: Storage.LengthPrefixedList(Value.Pair),
1085
1086 pub const Style = enum(u3) {
1087 autoconf_undef,
1088 autoconf_at,
1089 cmake,
1090 meson,
1091 blank,
1092 nasm,
1093
1094 pub fn init(s: std.Build.Step.ConfigHeader.Style) Style {
1095 return switch (s) {
1096 .autoconf_undef => .autoconf_undef,
1097 .autoconf_at => .autoconf_at,
1098 .cmake => .cmake,
1099 .meson => .meson,
1100 .blank => .blank,
1101 .nasm => .nasm,
1102 };
1103 }
1104 };
1105
1106 pub const Value = struct {
1107 flags: @This().Flags,
1108 i64: Storage.EnumOptional(.flags, .tag, .i64, i64),
1109 u64: Storage.EnumOptional(.flags, .tag, .u64, u64),
1110 ident: Storage.EnumOptional(.flags, .tag, .ident, String),
1111 string: Storage.EnumOptional(.flags, .tag, .string, String),
1112
1113 pub const Flags = packed struct(u32) {
1114 tag: Value.Tag,
1115 small: u29,
1116 };
1117
1118 pub const Tag = enum(u3) {
1119 ident,
1120 string,
1121 small_unsigned,
1122 small_signed,
1123 i64,
1124 u64,
1125 };
1126
1127 pub const Pair = extern struct {
1128 key: String,
1129 index: Value.Index,
1130 };
1131
1132 pub const Index = enum(u32) {
1133 int_0 = max_u32 - 5,
1134 int_1 = max_u32 - 4,
1135 bool_false = max_u32 - 3,
1136 bool_true = max_u32 - 2,
1137 undef = max_u32 - 1,
1138 defined = max_u32,
1139 _,
1140
1141 pub fn unpack(this: @This(), c: *const Configuration) Unpacked {
1142 return switch (this) {
1143 .int_0 => .{ .u64 = 0 },
1144 .int_1 => .{ .u64 = 1 },
1145 .bool_false => .{ .bool = false },
1146 .bool_true => .{ .bool = true },
1147 .undef => .undef,
1148 .defined => .defined,
1149 _ => {
1150 const value = extraData(c, Value, @backingInt(this));
1151 return switch (value.flags.tag) {
1152 .ident => .{ .ident = value.ident.value.?.slice(c) },
1153 .string => .{ .string = value.string.value.?.slice(c) },
1154 .small_unsigned => .{ .u64 = value.flags.small },
1155 .small_signed => .{ .i64 = @as(i29, @bitCast(value.flags.small)) },
1156 .i64 => .{ .i64 = value.i64.value.? },
1157 .u64 => .{ .u64 = value.u64.value.? },
1158 };
1159 },
1160 };
1161 }
1162 };
1163
1164 pub const Unpacked = union(enum) {
1165 bool: bool,
1166 undef,
1167 defined,
1168 i64: i64,
1169 u64: u64,
1170 ident: []const u8,
1171 string: []const u8,
1172 };
1173
1174 pub fn initSigned(x: i64) @This() {
1175 return switch (x) {
1176 0 => unreachable, // should have been an Index
1177 1 => unreachable, // should have been an Index
1178 2...std.math.maxInt(u29) => .{
1179 .flags = .{
1180 .tag = .small_unsigned,
1181 .small = @intCast(x),
1182 },
1183 .i64 = .{ .value = null },
1184 .u64 = .{ .value = null },
1185 .ident = .{ .value = null },
1186 .string = .{ .value = null },
1187 },
1188 std.math.minInt(i29)...-1 => .{
1189 .flags = .{
1190 .tag = .small_signed,
1191 .small = @bitCast(@as(i29, @intCast(x))),
1192 },
1193 .i64 = .{ .value = null },
1194 .u64 = .{ .value = null },
1195 .ident = .{ .value = null },
1196 .string = .{ .value = null },
1197 },
1198 else => .{
1199 .flags = .{
1200 .tag = .i64,
1201 .small = 0,
1202 },
1203 .i64 = .{ .value = x },
1204 .u64 = .{ .value = null },
1205 .ident = .{ .value = null },
1206 .string = .{ .value = null },
1207 },
1208 };
1209 }
1210 };
1211
1212 pub const Flags = packed struct(u32) {
1213 tag: Tag = .config_header,
1214 template_file: bool,
1215 style: Style,
1216 input_size_limit: bool,
1217 include_guard: bool,
1218 _: u21 = 0,
1219 };
1220 };
1221
1222 pub const Fail = struct {
1223 flags: @This().Flags = .{},
1224 msg: String,
1225
1226 pub const Flags = packed struct(u32) {
1227 tag: Tag = .fail,
1228 _: u27 = 0,
1229 };
1230 };
1231
1232 pub const Fmt = struct {
1233 flags: @This().Flags,
1234 paths: Storage.FlagLengthPrefixedList(.flags, .paths, LazyPath.Index),
1235 exclude_paths: Storage.FlagLengthPrefixedList(.flags, .exclude_paths, LazyPath.Index),
1236
1237 pub const Flags = packed struct(u32) {
1238 tag: Tag = .fmt,
1239 paths: bool,
1240 exclude_paths: bool,
1241 check: bool,
1242 _: u24 = 0,
1243 };
1244 };
1245
1246 pub const FindProgram = struct {
1247 flags: @This().Flags = .{},
1248 names: StringList,
1249 found_path: GeneratedFileIndex,
1250
1251 pub const Flags = packed struct(u32) {
1252 tag: Tag = .find_program,
1253 _: u27 = 0,
1254 };
1255 };
1256
1257 pub const InstallDir = struct {
1258 flags: @This().Flags,
1259 source_dir: LazyPath.Index,
1260 dest_dir: InstallDestDir,
1261 dest_sub_path: Storage.FlagOptional(.flags, .dest_sub_path, String),
1262 exclude_extensions: Storage.FlagLengthPrefixedList(.flags, .exclude_extensions, String),
1263 include_extensions: Storage.FlagLengthPrefixedList(.flags, .include_extensions, String),
1264 blank_extensions: Storage.FlagLengthPrefixedList(.flags, .blank_extensions, String),
1265
1266 pub const Flags = packed struct(u32) {
1267 tag: Tag = .install_dir,
1268 dest_sub_path: bool,
1269 exclude_extensions: bool,
1270 include_extensions: bool,
1271 include_extensions_active: bool,
1272 blank_extensions: bool,
1273 _: u22 = 0,
1274 };
1275 };
1276
1277 pub const InstallFile = struct {
1278 flags: @This().Flags = .{},
1279 source: LazyPath.Index,
1280 dest_dir: InstallDestDir,
1281 dest_sub_path: String,
1282
1283 pub const Flags = packed struct(u32) {
1284 tag: Tag = .install_file,
1285 _: u27 = 0,
1286 };
1287 };
1288
1289 pub const ObjCopy = struct {
1290 flags: @This().Flags,
1291 input_file: LazyPath.Index,
1292 output_file: GeneratedFileIndex,
1293 basename: Storage.FlagOptional(.flags, .basename, String),
1294 debug_file: Storage.FlagOptional(.flags, .debug_file, GeneratedFileIndex),
1295 debug_basename: Storage.FlagOptional(.flags, .debug_basename, String),
1296 only_section: Storage.FlagOptional(.flags, .only_section, String),
1297 pad_to: Storage.FlagOptional(.flags, .pad_to, u64),
1298 add_section: Storage.FlagLengthPrefixedList(.flags, .add_section, AddSection),
1299 update_section: Storage.FlagLengthPrefixedList(.flags, .update_section, UpdateSection),
1300
1301 pub const Format = enum(u2) {
1302 binary,
1303 hex,
1304 elf,
1305 default,
1306
1307 pub fn init(f: ?std.Build.Step.ObjCopy.Format) @This() {
1308 return switch (f orelse return .default) {
1309 .binary => .binary,
1310 .hex => .hex,
1311 .elf => .elf,
1312 };
1313 }
1314 };
1315
1316 pub const Strip = enum(u2) {
1317 none,
1318 debug,
1319 debug_and_symbols,
1320 };
1321
1322 pub const AddSection = extern struct {
1323 section_name: String,
1324 file_path: LazyPath.Index,
1325 };
1326
1327 pub const UpdateSection = extern struct {
1328 section_name: String,
1329 flags: @This().Flags,
1330
1331 pub const Flags = packed struct(u32) {
1332 section_flags: SectionFlags,
1333 alignment: Alignment,
1334 _: u17 = 0,
1335 };
1336 };
1337
1338 pub const SectionFlags = packed struct(u9) {
1339 /// add SHF_ALLOC
1340 alloc: bool = false,
1341 /// if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing
1342 contents: bool = false,
1343 /// if section is SHT_NOBITS, set SHT_PROGBITS, otherwise do nothing (same as contents)
1344 load: bool = false,
1345 /// readonly: clear default SHF_WRITE flag
1346 readonly: bool = false,
1347 /// add SHF_EXECINSTR
1348 code: bool = false,
1349 /// add SHF_EXCLUDE
1350 exclude: bool = false,
1351 /// add SHF_X86_64_LARGE. Fatal error if target is not x86_64
1352 large: bool = false,
1353 /// add SHF_MERGE
1354 merge: bool = false,
1355 /// add SHF_STRINGS
1356 strings: bool = false,
1357
1358 pub const default: @This() = .{};
1359 };
1360
1361 pub const Flags = packed struct(u32) {
1362 tag: Tag = .obj_copy,
1363 basename: bool,
1364 debug_file: bool,
1365 debug_basename: bool,
1366 format: Format,
1367 strip: Strip,
1368 compress_debug: bool,
1369 only_section: bool,
1370 pad_to: bool,
1371 add_section: bool,
1372 update_section: bool,
1373 _: u15 = 0,
1374 };
1375 };
1376
1377 pub const Options = struct {
1378 flags: @This().Flags,
1379 generated_file: GeneratedFileIndex,
1380 contents: Bytes,
1381 args: Storage.FlagLengthPrefixedList(.flags, .args, Arg),
1382
1383 pub const Arg = extern struct {
1384 name: String,
1385 path: LazyPath.Index,
1386 };
1387
1388 pub const Flags = packed struct(u32) {
1389 tag: Tag = .options,
1390 args: bool,
1391 _: u26 = 0,
1392 };
1393 };
1394
1395 pub const TranslateC = struct {
1396 flags: @This().Flags,
1397 src_path: LazyPath.Index,
1398 output_file: GeneratedFileIndex,
1399 include_dirs: Storage.UnionList(.flags, .include_dirs, Module.IncludeDir),
1400 system_libs: Storage.FlagLengthPrefixedList(.flags, .system_libs, SystemLib.Index),
1401 c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String),
1402 target: ResolvedTarget.OptionalIndex,
1403
1404 pub const Flags = packed struct(u32) {
1405 tag: Tag = .translate_c,
1406 include_dirs: bool,
1407 system_libs: bool,
1408 c_macros: bool,
1409 link_libc: bool,
1410 optimize: Module.Optimize,
1411 _: u20 = 0,
1412 };
1413 };
1414
1415 pub const UpdateSourceFiles = struct {
1416 flags: @This().Flags,
1417 embeds: Storage.FlagLengthPrefixedList(.flags, .embeds, Embed),
1418 copies: Storage.FlagLengthPrefixedList(.flags, .copies, Copy),
1419
1420 pub const Embed = WriteFile.Embed;
1421 pub const Copy = WriteFile.Copy;
1422
1423 pub const Flags = packed struct(u32) {
1424 tag: Tag = .update_source_files,
1425 embeds: bool,
1426 copies: bool,
1427 _: u25 = 0,
1428 };
1429 };
1430
1431 pub const WriteFile = struct {
1432 flags: @This().Flags,
1433 generated_directory: GeneratedFileIndex,
1434 embeds: Storage.FlagLengthPrefixedList(.flags, .embeds, Embed),
1435 copies: Storage.FlagLengthPrefixedList(.flags, .copies, Copy),
1436 directories: Storage.FlagLengthPrefixedList(.flags, .directories, Directory),
1437 mutate_path: Storage.EnumOptional(.flags, .mode, .mutate, LazyPath.Index),
1438
1439 pub const Embed = extern struct {
1440 sub_path: String,
1441 contents: Bytes,
1442 };
1443
1444 pub const Copy = extern struct {
1445 sub_path: String,
1446 src_file: LazyPath.Index,
1447 };
1448
1449 pub const Directory = extern struct {
1450 sub_path: String,
1451 src_path: LazyPath.Index,
1452 exclude_extensions: OptionalStringList,
1453 include_extensions: OptionalStringList,
1454 };
1455
1456 pub const Mode = enum(u2) {
1457 whole_cached,
1458 tmp,
1459 mutate,
1460 };
1461
1462 pub const Flags = packed struct(u32) {
1463 tag: Tag = .write_file,
1464 embeds: bool,
1465 copies: bool,
1466 directories: bool,
1467 mode: Mode,
1468 _: u22 = 0,
1469 };
1470 };
1471
1472 pub fn flags(s: *const Step, c: *const Configuration) Flags {
1473 return @bitCast(c.extra[@backingInt(s.extended)]);
1474 }
1475};
1476
1477pub const MaxRss = enum(u32) {
1478 none = 0,
1479 _,
1480
1481 pub fn toBytes(mr: MaxRss) u64 {
1482 const x: usize = @backingInt(mr);
1483 return x << 8;
1484 }
1485
1486 pub fn fromBytes(bytes: u64) MaxRss {
1487 return @fromBackingInt(@intCast(bytes >> 8));
1488 }
1489};
1490
1491pub const LazyPath = union(@This().Tag) {
1492 source_path: SourcePath,
1493 relative: Relative,
1494 generated: Generated,
1495
1496 pub const Tag = enum(u8) {
1497 /// A source file path relative to build root.
1498 source_path,
1499 /// Relative to the directory indicated in flags.
1500 relative,
1501 /// Path is available only after it is populated by its owning step.
1502 generated,
1503 };
1504
1505 pub const Flags = packed struct(u32) {
1506 tag: Tag,
1507 _: u24 = 0,
1508 };
1509
1510 /// An index into `extra`.
1511 pub const Index = IndexType(@This());
1512
1513 /// An index into `extra`, or `null`.
1514 pub const OptionalIndex = enum(u32) {
1515 none = max_u32,
1516 _,
1517
1518 pub fn unwrap(this: @This()) ?Index {
1519 return switch (this) {
1520 .none => null,
1521 else => @fromBackingInt(@intCast(@backingInt(this))),
1522 };
1523 }
1524 };
1525
1526 pub const SourcePath = struct {
1527 flags: @This().Flags = .{},
1528 owner: Package.Index,
1529 sub_path: String,
1530
1531 pub const Flags = packed struct(u32) {
1532 tag: Tag = .source_path,
1533 _: u24 = 0,
1534 };
1535 };
1536
1537 pub const Generated = struct {
1538 flags: @This().Flags = .{},
1539 index: GeneratedFileIndex,
1540 /// Applied after `up`.
1541 sub_path: String = .empty,
1542
1543 pub const Flags = packed struct(u32) {
1544 tag: Tag = .generated,
1545 /// The number of parent directories to go up.
1546 /// 0 means the generated file itself.
1547 /// 1 means the directory of the generated file.
1548 /// 2 means the parent of that directory, and so on.
1549 up: u24 = 0,
1550 };
1551 };
1552
1553 pub const Relative = struct {
1554 flags: @This().Flags,
1555 sub_path: String,
1556
1557 pub const Flags = packed struct(u32) {
1558 tag: Tag = .relative,
1559 base: Base,
1560 _: u16 = 0,
1561 };
1562
1563 pub const Base = enum(u8) {
1564 cwd,
1565 local_cache,
1566 global_cache,
1567 /// Must not be used with Relative since package index is missing.
1568 build_root,
1569 zig_exe,
1570 zig_lib,
1571 install_prefix,
1572 install_lib,
1573 install_bin,
1574 install_include,
1575 };
1576 };
1577};
1578
1579pub const GeneratedFileIndex = enum(u32) {
1580 _,
1581};
1582
1583pub const OptionalGeneratedFileIndex = enum(u32) {
1584 none = max_u32,
1585 _,
1586
1587 pub fn init(i: ?GeneratedFileIndex) OptionalGeneratedFileIndex {
1588 return @fromBackingInt(@intCast(@backingInt(i orelse return .none)));
1589 }
1590
1591 pub fn unwrap(this: @This()) ?GeneratedFileIndex {
1592 return switch (this) {
1593 .none => null,
1594 else => @fromBackingInt(@intCast(@backingInt(this))),
1595 };
1596 }
1597};
1598
1599pub const Package = struct {
1600 dep_prefix: String,
1601 hash: String,
1602 root_path: String,
1603
1604 pub const Index = enum(u32) {
1605 root = max_u32,
1606 _,
1607
1608 /// Returns `null` for root package.
1609 pub fn get(i: @This(), c: *const Configuration) ?Package {
1610 if (i == .root) return null;
1611 return extraData(c, Package, @backingInt(i));
1612 }
1613
1614 pub fn depPrefixSlice(i: @This(), c: *const Configuration) [:0]const u8 {
1615 const package = get(i, c) orelse return "";
1616 return package.dep_prefix.slice(c);
1617 }
1618 };
1619
1620 pub const OptionalIndex = enum(u32) {
1621 none = max_u32 - 1,
1622 root = max_u32,
1623 _,
1624
1625 pub fn init(i: Index) OptionalIndex {
1626 const result: OptionalIndex = @fromBackingInt(@intCast(@backingInt(i)));
1627 assert(result != .none);
1628 return result;
1629 }
1630
1631 pub fn unwrap(this: @This()) ?Index {
1632 return switch (this) {
1633 .none => null,
1634 .root => .root,
1635 _ => @fromBackingInt(@intCast(@backingInt(this))),
1636 };
1637 }
1638 };
1639};
1640
1641pub const Module = struct {
1642 flags: Flags,
1643 flags2: Flags2,
1644 import_table: ImportTable.Index,
1645 owner: Package.Index,
1646 root_source_file: LazyPath.OptionalIndex,
1647 resolved_target: ResolvedTarget.OptionalIndex,
1648 c_macros: Storage.FlagLengthPrefixedList(.flags, .c_macros, String),
1649 lib_paths: Storage.FlagLengthPrefixedList(.flags, .lib_paths, LazyPath.Index),
1650 export_symbol_names: Storage.FlagLengthPrefixedList(.flags, .export_symbol_names, String),
1651 include_dirs: Storage.UnionList(.flags, .include_dirs, IncludeDir),
1652 rpaths: Storage.UnionList(.flags, .rpaths, RPath),
1653 link_objects: Storage.UnionList(.flags, .link_objects, LinkObject),
1654 frameworks: Storage.FlagLengthPrefixedList(.flags, .frameworks, Framework),
1655
1656 pub const Optimize = enum(u3) {
1657 debug,
1658 safe,
1659 fast,
1660 small,
1661 default,
1662
1663 pub fn init(o: ?std.builtin.Optimize) Optimize {
1664 return switch (o orelse return .default) {
1665 .debug => .debug,
1666 .safe => .safe,
1667 .fast => .fast,
1668 .small => .small,
1669 };
1670 }
1671 };
1672
1673 pub const UnwindTables = enum(u2) {
1674 none,
1675 sync,
1676 async,
1677 default,
1678
1679 pub fn init(ut: ?std.builtin.UnwindTables) UnwindTables {
1680 return switch (ut orelse return .default) {
1681 .none => .none,
1682 .sync => .sync,
1683 .async => .async,
1684 };
1685 }
1686 };
1687
1688 pub const SanitizeC = enum(u2) {
1689 off,
1690 trap,
1691 full,
1692 default,
1693
1694 pub fn init(sc: ?std.zig.SanitizeC) SanitizeC {
1695 return switch (sc orelse return .default) {
1696 .off => .off,
1697 .trap => .trap,
1698 .full => .full,
1699 };
1700 }
1701 };
1702
1703 pub const DwarfFormat = enum(u2) {
1704 @"32",
1705 @"64",
1706 default,
1707
1708 pub fn init(df: ?std.dwarf.Format) DwarfFormat {
1709 return switch (df orelse return .default) {
1710 .@"32" => .@"32",
1711 .@"64" => .@"64",
1712 };
1713 }
1714 };
1715
1716 pub const Flags = packed struct(u32) {
1717 optimize: Optimize,
1718 strip: DefaultingBool,
1719 unwind_tables: UnwindTables,
1720 dwarf_format: DwarfFormat,
1721 single_threaded: DefaultingBool,
1722 stack_protector: DefaultingBool,
1723 stack_check: DefaultingBool,
1724 sanitize_c: SanitizeC,
1725 sanitize_thread: DefaultingBool,
1726 fuzz: DefaultingBool,
1727 code_model: std.builtin.CodeModel,
1728 c_macros: bool,
1729 include_dirs: bool,
1730 lib_paths: bool,
1731 rpaths: bool,
1732 frameworks: bool,
1733 link_objects: bool,
1734 export_symbol_names: bool,
1735 };
1736
1737 pub const Flags2 = packed struct(u32) {
1738 valgrind: DefaultingBool,
1739 pic: DefaultingBool,
1740 red_zone: DefaultingBool,
1741 omit_frame_pointer: DefaultingBool,
1742 error_tracing: DefaultingBool,
1743 link_libc: DefaultingBool,
1744 link_libcpp: DefaultingBool,
1745 no_builtin: DefaultingBool,
1746 _: u16 = 0,
1747 };
1748
1749 pub const IncludeDir = union(enum(u3)) {
1750 path: LazyPath.Index,
1751 path_system: LazyPath.Index,
1752 path_after: LazyPath.Index,
1753 framework_path: LazyPath.Index,
1754 framework_path_system: LazyPath.Index,
1755 /// Always `Step.Tag.config_header`.
1756 config_header_step: Step.Index,
1757 embed_path: LazyPath.Index,
1758 };
1759
1760 pub const RPath = union(enum(u1)) {
1761 lazy_path: LazyPath.Index,
1762 special: String,
1763 };
1764
1765 pub const LinkObject = union(enum(u3)) {
1766 static_path: LazyPath.Index,
1767 /// Always `Step.Tag.compile`.
1768 other_step: Step.Index,
1769 system_lib: SystemLib.Index,
1770 assembly_file: LazyPath.Index,
1771 c_source_file: CSourceFile.Index,
1772 c_source_files: CSourceFiles.Index,
1773 win32_resource_file: RcSourceFile.Index,
1774 };
1775
1776 pub const Framework = extern struct {
1777 flags: @This().Flags,
1778 name: String,
1779
1780 pub const Flags = packed struct(u32) {
1781 needed: bool,
1782 weak: bool,
1783 _: u30 = 0,
1784 };
1785 };
1786
1787 pub const Index = IndexType(@This());
1788};
1789
1790pub const ImportTable = struct {
1791 imports: Storage.MultiList(Import),
1792
1793 pub const Import = struct {
1794 name: String,
1795 module: Module.Index,
1796 };
1797
1798 /// Points into `extra`.
1799 pub const Index = enum(u32) {
1800 invalid = max_u32,
1801 _,
1802
1803 pub fn get(this: @This(), c: *const Configuration) ImportTable {
1804 return switch (this) {
1805 .invalid => unreachable,
1806 _ => extraData(c, ImportTable, @backingInt(this)),
1807 };
1808 }
1809 };
1810};
1811
1812pub const Deps = struct {
1813 steps: Storage.LengthPrefixedList(Step.Index),
1814
1815 pub const Index = enum(u32) {
1816 _,
1817
1818 pub fn get(this: @This(), c: *const Configuration) Deps {
1819 return extraData(c, Deps, @backingInt(this));
1820 }
1821
1822 pub fn slice(this: @This(), c: *const Configuration) []const Step.Index {
1823 return get(this, c).steps.slice;
1824 }
1825 };
1826};
1827
1828pub const EnvironMap = struct {
1829 keys: StringList,
1830 values: StringList,
1831
1832 pub const Index = IndexType(@This());
1833};
1834
1835/// Points into `extra`, where the first element is count of strings, following
1836/// elements is `String` per count.
1837///
1838/// Stored identically to `Deps`.
1839pub const StringList = enum(u32) {
1840 _,
1841
1842 pub fn slice(this: @This(), c: *const Configuration) []const String {
1843 const len = c.extra[@backingInt(this)];
1844 return @ptrCast(c.extra[@backingInt(this) + 1 ..][0..len]);
1845 }
1846};
1847
1848pub const OptionalStringList = enum(u32) {
1849 none = max_u32,
1850 _,
1851
1852 pub fn init(opt_string_list: ?StringList) OptionalStringList {
1853 const sl = opt_string_list orelse return .none;
1854 const result: OptionalStringList = @fromBackingInt(@intCast(@backingInt(sl)));
1855 assert(result != .none);
1856 return result;
1857 }
1858
1859 pub fn unwrap(this: @This()) ?StringList {
1860 if (this == .none) return null;
1861 return @fromBackingInt(@intCast(@backingInt(this)));
1862 }
1863
1864 pub fn slice(this: @This(), c: *const Configuration) ?[]const String {
1865 return (unwrap(this) orelse return null).slice(c);
1866 }
1867};
1868
1869pub const PathDep = extern struct {
1870 flags: Flags,
1871 sub: String,
1872 pkg: Package.OptionalIndex,
1873
1874 pub const Flags = packed struct(u32) {
1875 mode: Mode,
1876 base: LazyPath.Relative.Base,
1877 _: u16 = 0,
1878 };
1879
1880 pub const Mode = enum(u8) { directory, contents, metadata };
1881};
1882
1883pub const InstallDestDir = enum(u32) {
1884 none = max_u32 - 4,
1885 prefix = max_u32 - 3,
1886 lib = max_u32 - 2,
1887 bin = max_u32 - 1,
1888 header = max_u32,
1889 /// A `String` path relative to the prefix.
1890 _,
1891
1892 pub fn initCustom(sub_path: String) InstallDestDir {
1893 assert(@backingInt(sub_path) < @backingInt(InstallDestDir.none));
1894 return @fromBackingInt(@intCast(@backingInt(sub_path)));
1895 }
1896
1897 pub const Unpacked = union(enum) {
1898 prefix,
1899 lib,
1900 bin,
1901 header,
1902 sub_path: String,
1903 };
1904
1905 pub fn unpack(this: @This()) ?Unpacked {
1906 return switch (this) {
1907 .none => null,
1908 .prefix => .prefix,
1909 .lib => .lib,
1910 .bin => .bin,
1911 .header => .header,
1912 _ => .{ .sub_path = @fromBackingInt(@intCast(@backingInt(this))) },
1913 };
1914 }
1915};
1916
1917/// Points into `string_bytes`, null-terminated.
1918pub const OptionalString = enum(u32) {
1919 empty = 0,
1920 /// The string "root".
1921 root = 1,
1922 none = max_u32,
1923 _,
1924
1925 pub fn init(s: String) OptionalString {
1926 const result: OptionalString = @fromBackingInt(@intCast(@backingInt(s)));
1927 assert(result != .none);
1928 return result;
1929 }
1930
1931 pub fn unwrap(this: @This()) ?String {
1932 if (this == .none) return null;
1933 return @fromBackingInt(@intCast(@backingInt(this)));
1934 }
1935
1936 pub fn slice(this: @This(), c: *const Configuration) ?[:0]const u8 {
1937 return (unwrap(this) orelse return null).slice(c);
1938 }
1939};
1940
1941/// Points into `string_bytes`, null-terminated.
1942pub const String = enum(u32) {
1943 empty = 0,
1944 /// The string "root".
1945 root = 1,
1946 _,
1947
1948 pub fn slice(index: String, c: *const Configuration) [:0]const u8 {
1949 const start_slice = c.string_bytes[@backingInt(index)..];
1950 return start_slice[0..std.mem.findScalar(u8, start_slice, 0).? :0];
1951 }
1952};
1953
1954/// Arbitrary sequence of bytes that may contain null bytes.
1955pub const Bytes = extern struct {
1956 /// Points into `string_bytes`.
1957 index: u32,
1958 len: u32,
1959
1960 pub fn slice(bytes: Bytes, c: *const Configuration) []const u8 {
1961 return c.string_bytes[bytes.index..][0..bytes.len];
1962 }
1963};
1964
1965/// Stored as a power-of-two, with one special value to indicate none.
1966pub const Alignment = enum(u6) {
1967 @"1" = 0,
1968 @"2" = 1,
1969 @"4" = 2,
1970 @"8" = 3,
1971 @"16" = 4,
1972 @"32" = 5,
1973 @"64" = 6,
1974 none = std.math.maxInt(u6),
1975 _,
1976
1977 pub fn init(optional_alignment: ?std.mem.Alignment) @This() {
1978 const a = optional_alignment orelse return .none;
1979 return @fromBackingInt(@intCast(@backingInt(a)));
1980 }
1981
1982 pub fn toBytes(a: @This()) ?u64 {
1983 return switch (a) {
1984 .none => null,
1985 else => @as(u64, 1) << @backingInt(a),
1986 };
1987 }
1988};
1989
1990pub const DefaultingBool = enum(u2) {
1991 false,
1992 true,
1993 default,
1994
1995 pub fn init(b: ?bool) DefaultingBool {
1996 return switch (b orelse return .default) {
1997 false => .false,
1998 true => .true,
1999 };
2000 }
2001
2002 pub fn toBool(db: DefaultingBool) ?bool {
2003 return switch (db) {
2004 .false => false,
2005 .true => true,
2006 .default => null,
2007 };
2008 }
2009};
2010
2011pub const SystemLib = struct {
2012 name: String,
2013 flags: Flags,
2014
2015 pub const Index = IndexType(@This());
2016
2017 pub const UsePkgConfig = enum(u2) {
2018 /// Don't use pkg-config, just pass -lfoo where foo is name.
2019 no,
2020 /// Try to get information on how to link the library from pkg-config.
2021 /// If that fails, fall back to passing -lfoo where foo is name.
2022 yes,
2023 /// Try to get information on how to link the library from pkg-config.
2024 /// If that fails, error out.
2025 force,
2026 };
2027
2028 pub const LinkMode = std.builtin.LinkMode;
2029
2030 pub const Flags = packed struct(u32) {
2031 needed: bool,
2032 weak: bool,
2033 use_pkg_config: UsePkgConfig,
2034 preferred_link_mode: LinkMode,
2035 search_strategy: SearchStrategy,
2036 _: u25 = 0,
2037 };
2038
2039 pub const SearchStrategy = enum(u2) { paths_first, mode_first, no_fallback };
2040};
2041
2042pub const CSourceFiles = struct {
2043 flags: Flags,
2044 root: LazyPath.Index,
2045 args: Storage.FlagList(.flags, .args_len, String),
2046 sub_paths: Storage.LengthPrefixedList(String),
2047
2048 pub const Index = IndexType(@This());
2049
2050 pub const Flags = packed struct(u32) {
2051 /// C compiler CLI flags.
2052 args_len: u29,
2053 lang: OptionalCSourceLanguage,
2054 };
2055};
2056
2057pub const CSourceFile = struct {
2058 flags: Flags,
2059 file: LazyPath.Index,
2060 args: Storage.FlagList(.flags, .args_len, String),
2061
2062 pub const Index = IndexType(@This());
2063
2064 pub const Flags = packed struct(u32) {
2065 /// C compiler CLI flags.
2066 args_len: u29,
2067 lang: OptionalCSourceLanguage,
2068 };
2069};
2070
2071pub const RcSourceFile = struct {
2072 flags: Flags,
2073 file: LazyPath.Index,
2074 args: Storage.FlagList(.flags, .args_len, String),
2075 include_paths: Storage.FlagLengthPrefixedList(.flags, .include_paths, LazyPath.Index),
2076
2077 pub const Index = IndexType(@This());
2078
2079 pub const Flags = packed struct(u32) {
2080 /// C compiler CLI flags.
2081 args_len: u31,
2082 include_paths: bool,
2083 };
2084};
2085
2086pub const OptionalCSourceLanguage = enum(u3) {
2087 c,
2088 cpp,
2089 objective_c,
2090 objective_cpp,
2091 assembly,
2092 assembly_with_preprocessor,
2093
2094 default,
2095
2096 pub fn init(x: ?std.Build.Module.CSourceLanguage) @This() {
2097 return switch (x orelse return .default) {
2098 inline else => |tag| @field(@This(), @tagName(tag)),
2099 };
2100 }
2101
2102 pub fn get(this: @This()) ?std.Build.Module.CSourceLanguage {
2103 return switch (this) {
2104 inline else => |tag| @field(std.Build.Module.CSourceLanguage, @tagName(tag)),
2105 .default => null,
2106 };
2107 }
2108};
2109
2110pub const ResolvedTarget = struct {
2111 /// none indicates host.
2112 query: TargetQuery.OptionalIndex,
2113 /// defaults will be resolved.
2114 result: TargetQuery.Index,
2115
2116 pub const Index = IndexType(@This());
2117
2118 pub const OptionalIndex = enum(u32) {
2119 none = max_u32,
2120 _,
2121
2122 pub fn init(i: Index) OptionalIndex {
2123 const result: OptionalIndex = @fromBackingInt(@intCast(@backingInt(i)));
2124 assert(result != .none);
2125 return result;
2126 }
2127
2128 pub fn unwrap(this: @This()) ?Index {
2129 return switch (this) {
2130 .none => null,
2131 _ => @fromBackingInt(@intCast(@backingInt(this))),
2132 };
2133 }
2134
2135 pub fn get(this: @This(), c: *const Configuration) ?ResolvedTarget {
2136 return (unwrap(this) orelse return null).get(c);
2137 }
2138 };
2139
2140 pub fn unwrapQuery(rt: *const ResolvedTarget, c: *const Configuration) ?std.Target.Query {
2141 const tq = rt.query.get(c) orelse return null;
2142 const cpu_arch = tq.flags.cpu_arch.unwrap() orelse rt.result.get(c).flags.cpu_arch.unwrap().?;
2143 return .{
2144 .cpu_arch = cpu_arch,
2145 .cpu_model = switch (tq.flags.cpu_model) {
2146 .native => .native,
2147 .baseline => .baseline,
2148 .determined_by_arch_os => .determined_by_arch_os,
2149 .explicit => .{ .explicit = cpu_arch.parseCpuModel(tq.cpu_name.value.?.slice(c)).? },
2150 },
2151 .cpu_features_add = tq.cpu_features_add.value orelse .empty,
2152 .cpu_features_sub = tq.cpu_features_sub.value orelse .empty,
2153 .os_tag = tq.flags.os_tag.unwrap(),
2154 .os_version_min = tq.os_version_min.u.unwrap(c),
2155 .os_version_max = tq.os_version_max.u.unwrap(c),
2156 .glibc_version = if (tq.glibc_version.value) |s|
2157 std.SemanticVersion.parse(s.slice(c)) catch unreachable
2158 else
2159 null,
2160 .android_api_level = tq.android_api_level.value,
2161 .abi = tq.flags.abi.unwrap(),
2162 .dynamic_linker = if (tq.dynamic_linker.value) |s| .init(s.slice(c)) else null,
2163 .ofmt = tq.flags.object_format.unwrap(),
2164 };
2165 }
2166};
2167
2168pub const TargetQuery = struct {
2169 flags: Flags,
2170
2171 cpu_features_add: Storage.FlagOptional(.flags, .cpu_features_add, std.Target.Cpu.Feature.Set),
2172 cpu_features_sub: Storage.FlagOptional(.flags, .cpu_features_sub, std.Target.Cpu.Feature.Set),
2173 cpu_name: Storage.EnumOptional(.flags, .cpu_model, .explicit, String),
2174 os_version_min: Storage.FlagUnion(.flags, .os_version_min, OsVersion),
2175 os_version_max: Storage.FlagUnion(.flags, .os_version_max, OsVersion),
2176 glibc_version: Storage.FlagOptional(.flags, .glibc_version, String),
2177 android_api_level: Storage.FlagOptional(.flags, .android_api_level, u32),
2178 dynamic_linker: Storage.FlagOptional(.flags, .dynamic_linker, String),
2179
2180 pub const Index = enum(u32) {
2181 _,
2182
2183 pub fn extraSlice(i: Index, extra: []const u32) []const u32 {
2184 return extra[@backingInt(i)..][0..length(i, extra)];
2185 }
2186
2187 pub fn length(i: Index, extra: []const u32) usize {
2188 return Storage.dataLength(extra, @backingInt(i), TargetQuery);
2189 }
2190
2191 pub fn get(this: @This(), c: *const Configuration) TargetQuery {
2192 return extraData(c, TargetQuery, @backingInt(this));
2193 }
2194 };
2195
2196 pub const OptionalIndex = enum(u32) {
2197 none = max_u32,
2198 _,
2199
2200 pub fn init(i: Index) OptionalIndex {
2201 const result: OptionalIndex = @fromBackingInt(@intCast(@backingInt(i)));
2202 assert(result != .none);
2203 return result;
2204 }
2205
2206 pub fn unwrap(this: @This()) ?Index {
2207 return switch (this) {
2208 .none => null,
2209 _ => @fromBackingInt(@intCast(@backingInt(this))),
2210 };
2211 }
2212
2213 pub fn get(this: @This(), c: *const Configuration) ?TargetQuery {
2214 return (this.unwrap() orelse return null).get(c);
2215 }
2216 };
2217
2218 pub const CpuModel = enum(u2) {
2219 native,
2220 baseline,
2221 determined_by_arch_os,
2222 explicit,
2223
2224 pub fn init(x: std.Target.Query.CpuModel) @This() {
2225 return switch (x) {
2226 inline else => |_, tag| @field(@This(), @tagName(tag)),
2227 };
2228 }
2229 };
2230 pub const OsVersion = union(@This().Tag) {
2231 pub const Tag = enum(u2) { none, semver, windows, default };
2232
2233 none: void,
2234 semver: String,
2235 windows: std.Target.Os.WindowsVersion,
2236 default: void,
2237
2238 pub fn unwrap(this: @This(), c: *const Configuration) ?std.Target.Query.OsVersion {
2239 return switch (this) {
2240 .none => .none,
2241 .semver => |sv| .{ .semver = std.SemanticVersion.parse(sv.slice(c)) catch unreachable },
2242 .windows => |wv| .{ .windows = wv },
2243 .default => null,
2244 };
2245 }
2246 };
2247
2248 pub const Abi = enum(u5) {
2249 none,
2250 gnu,
2251 gnuabin32,
2252 gnuabi64,
2253 gnueabi,
2254 gnueabihf,
2255 gnuf32,
2256 gnusf,
2257 gnux32,
2258 eabi,
2259 eabihf,
2260 abin32,
2261 x32,
2262 ilp32,
2263 android,
2264 androideabi,
2265 musl,
2266 muslabin32,
2267 muslabi64,
2268 musleabi,
2269 musleabihf,
2270 muslf32,
2271 muslsf,
2272 muslx32,
2273 msvc,
2274 itanium,
2275 simulator,
2276 ohos,
2277 ohoseabi,
2278 call0,
2279
2280 default,
2281
2282 pub fn init(x: ?std.Target.Abi) @This() {
2283 return switch (x orelse return .default) {
2284 inline else => |tag| @field(@This(), @tagName(tag)),
2285 };
2286 }
2287
2288 pub fn unwrap(this: @This()) ?std.Target.Abi {
2289 return switch (this) {
2290 inline else => |tag| @field(std.Target.Abi, @tagName(tag)),
2291 .default => null,
2292 };
2293 }
2294 };
2295
2296 pub const CpuArch = enum(u6) {
2297 aarch64,
2298 aarch64_be,
2299 alpha,
2300 amdgcn,
2301 arc,
2302 arceb,
2303 arm,
2304 armeb,
2305 avr,
2306 bpfeb,
2307 bpfel,
2308 csky,
2309 ez80,
2310 hexagon,
2311 hppa,
2312 hppa64,
2313 kalimba,
2314 kvx,
2315 lanai,
2316 loongarch32,
2317 loongarch64,
2318 m68k,
2319 m88k,
2320 microblaze,
2321 microblazeel,
2322 mips,
2323 mipsel,
2324 mips64,
2325 mips64el,
2326 msp430,
2327 nvptx,
2328 nvptx64,
2329 or1k,
2330 powerpc,
2331 powerpcle,
2332 powerpc64,
2333 powerpc64le,
2334 propeller,
2335 riscv32,
2336 riscv32be,
2337 riscv64,
2338 riscv64be,
2339 s390x,
2340 sh,
2341 sheb,
2342 sparc,
2343 sparc64,
2344 spork8,
2345 spirv32,
2346 spirv64,
2347 thumb,
2348 thumbeb,
2349 ve,
2350 wasm32,
2351 wasm64,
2352 x86_16,
2353 x86,
2354 x86_64,
2355 xcore,
2356 xtensa,
2357 xtensaeb,
2358
2359 default,
2360
2361 pub fn init(x: ?std.Target.Cpu.Arch) @This() {
2362 return switch (x orelse return .default) {
2363 inline else => |tag| @field(@This(), @tagName(tag)),
2364 };
2365 }
2366
2367 pub fn unwrap(this: @This()) ?std.Target.Cpu.Arch {
2368 return switch (this) {
2369 inline else => |tag| @field(std.Target.Cpu.Arch, @tagName(tag)),
2370 .default => null,
2371 };
2372 }
2373 };
2374
2375 pub const OsTag = enum(u6) {
2376 freestanding,
2377 other,
2378 contiki,
2379 fuchsia,
2380 hermit,
2381 managarm,
2382 haiku,
2383 hurd,
2384 illumos,
2385 linux,
2386 plan9,
2387 rtems,
2388 serenity,
2389 dragonfly,
2390 freebsd,
2391 netbsd,
2392 openbsd,
2393 driverkit,
2394 ios,
2395 maccatalyst,
2396 macos,
2397 tvos,
2398 visionos,
2399 watchos,
2400 windows,
2401 uefi,
2402 @"3ds",
2403 wiiu,
2404 @"switch",
2405 gba,
2406 psx,
2407 ps3,
2408 ps4,
2409 ps5,
2410 psp,
2411 vita,
2412 emscripten,
2413 wasi,
2414 amdhsa,
2415 amdpal,
2416 cuda,
2417 mesa3d,
2418 nvcl,
2419 opencl,
2420 opengl,
2421 vulkan,
2422 tios,
2423 ashetos,
2424
2425 default,
2426
2427 pub fn init(x: ?std.Target.Os.Tag) @This() {
2428 return switch (x orelse return .default) {
2429 inline else => |tag| @field(@This(), @tagName(tag)),
2430 };
2431 }
2432
2433 pub fn unwrap(this: @This()) ?std.Target.Os.Tag {
2434 return switch (this) {
2435 inline else => |tag| @field(std.Target.Os.Tag, @tagName(tag)),
2436 .default => null,
2437 };
2438 }
2439 };
2440
2441 pub const ObjectFormat = enum(u4) {
2442 c,
2443 coff,
2444 elf,
2445 hex,
2446 macho,
2447 plan9,
2448 raw,
2449 spirv,
2450 wasm,
2451
2452 default,
2453
2454 pub fn init(x: ?std.Target.ObjectFormat) @This() {
2455 return switch (x orelse return .default) {
2456 inline else => |tag| @field(@This(), @tagName(tag)),
2457 };
2458 }
2459
2460 pub fn unwrap(this: @This()) ?std.Target.ObjectFormat {
2461 return switch (this) {
2462 inline else => |tag| @field(std.Target.ObjectFormat, @tagName(tag)),
2463 .default => null,
2464 };
2465 }
2466 };
2467
2468 pub const Flags = packed struct(u32) {
2469 cpu_arch: CpuArch,
2470 cpu_model: CpuModel,
2471 cpu_features_add: bool,
2472 cpu_features_sub: bool,
2473 os_tag: OsTag,
2474 abi: Abi,
2475 object_format: ObjectFormat,
2476 os_version_min: OsVersion.Tag,
2477 os_version_max: OsVersion.Tag,
2478 glibc_version: bool,
2479 android_api_level: bool,
2480 dynamic_linker: bool,
2481 };
2482
2483 pub fn unwrapTarget(tq: *const TargetQuery, c: *const Configuration) std.Target {
2484 const cpu_arch = tq.flags.cpu_arch.unwrap().?;
2485 const os_tag = tq.flags.os_tag.unwrap().?;
2486 return .{
2487 .cpu = .{
2488 .arch = cpu_arch,
2489 .model = cpu_arch.parseCpuModel(tq.cpu_name.value.?.slice(c)).?,
2490 .features = tq.cpu_features_add.value.?,
2491 },
2492 .os = .{
2493 .tag = os_tag,
2494 .version_range = switch (os_tag) {
2495 .linux => .{ .linux = .{
2496 .range = .{
2497 .min = tq.os_version_min.u.unwrap(c).?.semver,
2498 .max = tq.os_version_max.u.unwrap(c).?.semver,
2499 },
2500 .glibc = std.SemanticVersion.parse(tq.glibc_version.value.?.slice(c)) catch unreachable,
2501 .android = tq.android_api_level.value.?,
2502 } },
2503 .hurd => .{ .hurd = .{
2504 .range = .{
2505 .min = tq.os_version_min.u.unwrap(c).?.semver,
2506 .max = tq.os_version_max.u.unwrap(c).?.semver,
2507 },
2508 .glibc = std.SemanticVersion.parse(tq.glibc_version.value.?.slice(c)) catch unreachable,
2509 } },
2510 .windows => .{ .windows = .{
2511 .min = tq.os_version_min.u.unwrap(c).?.windows,
2512 .max = tq.os_version_max.u.unwrap(c).?.windows,
2513 } },
2514 else => switch (tq.os_version_min.u.unwrap(c).?) {
2515 .none => .{ .none = {} },
2516 .semver => |min| .{ .semver = .{
2517 .min = min,
2518 .max = tq.os_version_max.u.unwrap(c).?.semver,
2519 } },
2520 .windows => unreachable,
2521 },
2522 },
2523 },
2524 .abi = tq.flags.abi.unwrap().?,
2525 .ofmt = tq.flags.object_format.unwrap().?,
2526 .dynamic_linker = .init(if (tq.dynamic_linker.value) |s| s.slice(c) else null),
2527 };
2528 }
2529};
2530
2531pub const Storage = enum {
2532 flag_optional,
2533 enum_optional,
2534 extended,
2535 length_prefixed_list,
2536 flag_length_prefixed_list,
2537 union_list,
2538 flag_union,
2539 multi_list,
2540 flag_list,
2541
2542 /// The presence of the field is determined by a boolean within a packed
2543 /// struct.
2544 pub fn FlagOptional(
2545 comptime flags_arg: @EnumLiteral(),
2546 comptime flag_arg: @EnumLiteral(),
2547 comptime ValueArg: type,
2548 ) type {
2549 return struct {
2550 value: ?Value,
2551
2552 pub const storage: Storage = .flag_optional;
2553 pub const flags = flags_arg;
2554 pub const flag = flag_arg;
2555 pub const Value = ValueArg;
2556 };
2557 }
2558
2559 /// The type of the field is determined by an enum within a packed struct.
2560 pub fn FlagUnion(
2561 comptime flags_arg: @EnumLiteral(),
2562 comptime flag_arg: @EnumLiteral(),
2563 comptime UnionArg: type,
2564 ) type {
2565 return struct {
2566 u: Union,
2567
2568 pub const storage: Storage = .flag_union;
2569 pub const flags = flags_arg;
2570 pub const flag = flag_arg;
2571 pub const Union = UnionArg;
2572
2573 pub const Tag = @typeInfo(Union).@"union".tag_type.?;
2574 };
2575 }
2576
2577 /// The field is present if an enum tag from flags matches a specific value.
2578 pub fn EnumOptional(
2579 comptime flags_arg: @EnumLiteral(),
2580 comptime flag_arg: @EnumLiteral(),
2581 comptime tag_arg: @EnumLiteral(),
2582 comptime ValueArg: type,
2583 ) type {
2584 return struct {
2585 value: ?Value,
2586
2587 pub const storage: Storage = .enum_optional;
2588 pub const flags = flags_arg;
2589 pub const flag = flag_arg;
2590 pub const tag = tag_arg;
2591 pub const Value = ValueArg;
2592 };
2593 }
2594
2595 /// The field indexes into an auxilary buffer, with the first element being
2596 /// a packed struct that contains the tag.
2597 pub fn Extended(comptime BaseFlags: type, comptime U: type) type {
2598 return enum(u32) {
2599 _,
2600
2601 pub const storage: Storage = .extended;
2602
2603 pub fn tag(this: @This(), c: *const Configuration) @FieldType(BaseFlags, "tag") {
2604 const base_flags: BaseFlags = @bitCast(c.extra[@backingInt(this)]);
2605 return base_flags.tag;
2606 }
2607
2608 pub fn cast(this: @This(), c: *const Configuration, comptime S: type) ?S {
2609 const wanted_tag = blk: {
2610 const info = @typeInfo(S.Flags).@"struct";
2611 break :blk info.field_attrs[0].defaultValue(info.field_types[0]).?;
2612 };
2613 const base_flags: BaseFlags = @bitCast(c.extra[@backingInt(this)]);
2614 if (base_flags.tag != wanted_tag) return null;
2615 var i: usize = @backingInt(this);
2616 return data(c.extra, &i, S);
2617 }
2618
2619 pub fn get(this: @This(), buffer: []const u32) U {
2620 var i: usize = @backingInt(this);
2621 const base_flags: BaseFlags = @bitCast(buffer[i]);
2622 return switch (base_flags.tag) {
2623 inline else => |t| @unionInit(U, @tagName(t), data(buffer, &i, @FieldType(U, @tagName(t)))),
2624 };
2625 }
2626 };
2627 }
2628
2629 /// A field in flags determines whether the length is zero or nonzero. If
2630 /// the length is nonzero, then there is a length field followed by the
2631 /// list. The elements need well-defined memory layout but can otherwise be
2632 /// any multiple of u32 length. The length is the number of elements, not
2633 /// the number of u32s.
2634 pub fn FlagLengthPrefixedList(
2635 comptime flags_arg: @EnumLiteral(),
2636 comptime flag_arg: @EnumLiteral(),
2637 comptime ElemArg: type,
2638 ) type {
2639 return struct {
2640 slice: []const Elem,
2641
2642 pub const storage: Storage = .flag_length_prefixed_list;
2643 pub const flags = flags_arg;
2644 pub const flag = flag_arg;
2645 pub const Elem = ElemArg;
2646
2647 pub fn initErased(s: []const u32) @This() {
2648 return .{ .slice = @ptrCast(s) };
2649 }
2650 };
2651 }
2652
2653 /// The field contains a u32 length followed by that many items. Each
2654 /// element needs well-defined memory layout but can otherwise be any
2655 /// multiple of u32 length. The length is number of elements, not the
2656 /// number of u32s.
2657 pub fn LengthPrefixedList(comptime ElemArg: type) type {
2658 return struct {
2659 slice: []const Elem,
2660
2661 pub const storage: Storage = .length_prefixed_list;
2662 pub const Elem = ElemArg;
2663
2664 pub fn initErased(s: []const u32) @This() {
2665 return .{ .slice = @ptrCast(s) };
2666 }
2667 };
2668 }
2669
2670 /// The field is a list whose length is an integer inside flags.
2671 pub fn FlagList(
2672 comptime flags_arg: @EnumLiteral(),
2673 comptime flag_arg: @EnumLiteral(),
2674 comptime ElemArg: type,
2675 ) type {
2676 return struct {
2677 slice: []const Elem,
2678
2679 pub const storage: Storage = .flag_list;
2680 pub const flags = flags_arg;
2681 pub const flag = flag_arg;
2682 pub const Elem = ElemArg;
2683
2684 pub fn initErased(s: []const u32) @This() {
2685 return .{ .slice = @ptrCast(s) };
2686 }
2687 };
2688 }
2689
2690 /// The field contains a u32 length followed by that many items for the
2691 /// first field, that many items for the second field, etc.
2692 pub fn MultiList(comptime ElemArg: type) type {
2693 return struct {
2694 mal: std.MultiArrayList(Elem),
2695
2696 pub const storage: Storage = .multi_list;
2697 pub const Elem = ElemArg;
2698 };
2699 }
2700
2701 /// `UnionArg` is a tagged union with a small integer for the enum tag.
2702 ///
2703 /// A field in flags determines whether the metadata is present.
2704 ///
2705 /// The metadata is bit-packed consecutive packed struct which is the
2706 /// `UnionArg` enum tag combined with a "last" marker boolean field.
2707 /// When "last" is true, the element is the last one, providing
2708 /// the length of the list.
2709 ///
2710 /// Following is each element of the list; each bitcastable to u32.
2711 pub fn UnionList(
2712 comptime flags_arg: @EnumLiteral(),
2713 comptime flag_arg: @EnumLiteral(),
2714 comptime UnionArg: type,
2715 ) type {
2716 return struct {
2717 /// When serializing it is UnionArg slice pointer.
2718 /// When deserializing it is extra index of first UnionArg element.
2719 data: ?*const anyopaque,
2720 len: usize,
2721
2722 pub const storage: Storage = .union_list;
2723 pub const flags = flags_arg;
2724 pub const flag = flag_arg;
2725 pub const Union = UnionArg;
2726
2727 pub const Tag = @typeInfo(Union).@"union".tag_type.?;
2728 pub const MetaInt = @Int(.unsigned, @bitSizeOf(Tag) + 1);
2729 pub const Meta = packed struct(MetaInt) {
2730 tag: Tag,
2731 last: bool,
2732 };
2733
2734 /// Valid to call only when serializing.
2735 pub fn init(s: []const Union) @This() {
2736 return .{ .data = s.ptr, .len = s.len };
2737 }
2738
2739 /// Valid to call only when deserializing.
2740 pub fn slice(this: *const @This(), extra: []const u32) []const u32 {
2741 return extra[@intFromPtr(this.data)..][0..this.len];
2742 }
2743
2744 /// Valid to call only when deserializing.
2745 pub fn get(this: *const @This(), extra: []const u32, i: usize) Union {
2746 const elem = slice(this, extra)[i];
2747 return switch (this.tag(extra, i)) {
2748 inline else => |comptime_tag| @unionInit(Union, @tagName(comptime_tag), @fromBackingInt(@intCast(elem))),
2749 };
2750 }
2751
2752 /// Valid to call only when deserializing.
2753 pub fn tag(this: *const @This(), extra: []const u32, i: usize) Tag {
2754 const start = @intFromPtr(this.data);
2755 const meta_start = start - (this.len * @bitSizeOf(Meta) + 31) / 32;
2756 return loadBits(u32, extra[meta_start..], i * @bitSizeOf(Meta), Meta).tag;
2757 }
2758
2759 fn extraLen(len: usize) usize {
2760 return len + (len * @bitSizeOf(Meta) + 31) / 32;
2761 }
2762 };
2763 }
2764
2765 pub fn dataLength(buffer: []const u32, i: usize, comptime S: type) usize {
2766 var end = i;
2767 _ = data(buffer, &end, S);
2768 return end - i;
2769 }
2770
2771 pub fn data(buffer: []const u32, i: *usize, comptime T: type) T {
2772 switch (@typeInfo(T)) {
2773 .@"struct" => |info| {
2774 var result: T = undefined;
2775 inline for (info.field_names, info.field_types) |field_name, field_type| {
2776 @field(result, field_name) = dataField(buffer, i, &result, field_type);
2777 }
2778 return result;
2779 },
2780 .@"union" => |info| {
2781 const flags: T.Flags = @bitCast(buffer[i.*]);
2782 return switch (flags.tag) {
2783 inline else => |comptime_tag| @unionInit(
2784 T,
2785 @tagName(comptime_tag),
2786 data(buffer, i, info.field_types[@backingInt(comptime_tag)]),
2787 ),
2788 };
2789 },
2790 else => comptime unreachable,
2791 }
2792 }
2793
2794 fn dataField(buffer: []const u32, i: *usize, container: anytype, comptime Field: type) Field {
2795 switch (@typeInfo(Field)) {
2796 .void => return {},
2797 .int => |info| switch (info.bits) {
2798 32 => {
2799 defer i.* += 1;
2800 return buffer[i.*];
2801 },
2802 64 => {
2803 defer i.* += 2;
2804 return @bitCast(buffer[i.*..][0..2].*);
2805 },
2806 else => comptime unreachable,
2807 },
2808 .@"enum" => {
2809 defer i.* += 1;
2810 return @fromBackingInt(@intCast(buffer[i.*]));
2811 },
2812 .@"struct" => |info| switch (info.layout) {
2813 .@"packed" => switch (info.backing_integer.?) {
2814 u32 => {
2815 defer i.* += 1;
2816 return @bitCast(buffer[i.*]);
2817 },
2818 u64 => {
2819 defer i.* += 2;
2820 return @bitCast(buffer[i.*..][0..2].*);
2821 },
2822 else => comptime unreachable,
2823 },
2824 .auto => switch (Field) {
2825 std.Target.Cpu.Feature.Set => {
2826 const u32_count = (Field.usize_count * @sizeOf(usize)) / @sizeOf(u32);
2827 defer i.* += u32_count;
2828 return .{ .ints = @as(
2829 *align(@alignOf(u32)) const [Field.usize_count]usize,
2830 @ptrCast(buffer[i.*..][0..u32_count]),
2831 ).* };
2832 },
2833 else => switch (Field.storage) {
2834 .flag_optional => {
2835 const flags = @field(container, @tagName(Field.flags));
2836 const flag = @field(flags, @tagName(Field.flag));
2837 return .{
2838 .value = if (flag) dataField(buffer, i, container, Field.Value) else null,
2839 };
2840 },
2841 .flag_union => {
2842 const flags = @field(container, @tagName(Field.flags));
2843 const tag: Field.Tag = @field(flags, @tagName(Field.flag));
2844 return .{
2845 .u = switch (tag) {
2846 inline else => |comptime_tag| @unionInit(
2847 Field.Union,
2848 @tagName(comptime_tag),
2849 dataField(
2850 buffer,
2851 i,
2852 container,
2853 @typeInfo(Field.Union).@"union".field_types[@backingInt(comptime_tag)],
2854 ),
2855 ),
2856 },
2857 };
2858 },
2859 .enum_optional => {
2860 const flags = @field(container, @tagName(Field.flags));
2861 const tag = @field(flags, @tagName(Field.flag));
2862 const match = tag == Field.tag;
2863 return .{
2864 .value = if (match) dataField(buffer, i, container, Field.Value) else null,
2865 };
2866 },
2867 .extended => @compileError("unimplemented"),
2868 .length_prefixed_list => {
2869 const n = @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
2870 const data_start = i.* + 1;
2871 const buf_len = buffer[data_start - 1] * n;
2872 defer i.* = data_start + buf_len;
2873 return .{ .slice = @ptrCast(buffer[data_start..][0..buf_len]) };
2874 },
2875 .flag_length_prefixed_list => {
2876 const flags = @field(container, @tagName(Field.flags));
2877 const flag = @field(flags, @tagName(Field.flag));
2878 if (!flag) return .{ .slice = &.{} };
2879 const n = @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
2880 const data_start = i.* + 1;
2881 const buf_len = buffer[data_start - 1] * n;
2882 defer i.* = data_start + buf_len;
2883 return .{ .slice = @ptrCast(buffer[data_start..][0..buf_len]) };
2884 },
2885 .flag_list => {
2886 const flags = @field(container, @tagName(Field.flags));
2887 const len: u32 = @field(flags, @tagName(Field.flag));
2888 const data_start = i.*;
2889 defer i.* = data_start + len;
2890 return .{ .slice = @ptrCast(buffer[data_start..][0..len]) };
2891 },
2892 .multi_list => {
2893 const data_start = i.* + 1;
2894 const len = buffer[data_start - 1];
2895 defer i.* = data_start + len * @typeInfo(Field.Elem).@"struct".field_names.len;
2896 return .{ .mal = .{
2897 .bytes = @ptrCast(@constCast(buffer[data_start..][0..len])),
2898 .len = len,
2899 .capacity = len,
2900 } };
2901 },
2902 .union_list => {
2903 const flags = @field(container, @tagName(Field.flags));
2904 const flag = @field(flags, @tagName(Field.flag));
2905 if (!flag) return .{ .data = null, .len = 0 };
2906 const meta_start = i.*;
2907 const meta_buffer = buffer[meta_start..];
2908 var len: u32 = 0;
2909 var bit_offset: usize = 0;
2910 while (true) : (bit_offset += @bitSizeOf(Field.Meta)) {
2911 const meta = loadBits(u32, meta_buffer, bit_offset, Field.Meta);
2912 len += 1;
2913 if (meta.last) break;
2914 }
2915 const end = meta_start + Field.extraLen(len);
2916 i.* = end;
2917 return .{ .data = @ptrFromInt(end - len), .len = len };
2918 },
2919 },
2920 },
2921 .@"extern" => {
2922 const n = @divExact(@sizeOf(Field), @sizeOf(u32));
2923 defer i.* += n;
2924 const ptr: *align(@alignOf(u32)) const Field = @ptrCast(buffer[i.*..][0..n]);
2925 return ptr.*;
2926 },
2927 },
2928 else => comptime unreachable,
2929 }
2930 }
2931
2932 /// Returns new end index.
2933 fn setExtra(buffer: []u32, index: usize, extra: anytype) usize {
2934 const info = @typeInfo(@TypeOf(extra)).@"struct";
2935 var i = index;
2936 inline for (info.field_names, info.field_types) |field_name, field_type| {
2937 i += setExtraField(buffer, i, field_type, @field(extra, field_name));
2938 }
2939 return i;
2940 }
2941
2942 fn extraFieldLen(field: anytype) usize {
2943 const Field = @TypeOf(field);
2944 return switch (@typeInfo(Field)) {
2945 .void => 0,
2946 .int => |info| switch (info.bits) {
2947 32 => 1,
2948 64 => 2,
2949 else => comptime unreachable,
2950 },
2951 .@"enum" => 1,
2952 .@"struct" => |info| switch (info.layout) {
2953 .@"packed" => switch (info.backing_integer.?) {
2954 u32 => 1,
2955 u64 => 2,
2956 else => comptime unreachable,
2957 },
2958 .auto => switch (Field.storage) {
2959 .flag_optional, .enum_optional => (@sizeOf(Field.Value) + 3) / 4,
2960 .extended => 1,
2961 .length_prefixed_list,
2962 .flag_length_prefixed_list,
2963 .flag_list,
2964 => 1 + @divExact(@sizeOf(Field.Elem), @sizeOf(u32)) * field.slice.len,
2965 .multi_list => 1 + field.mal.len * @typeInfo(Field.Elem).@"struct".field_names.len,
2966 .union_list => Field.extraLen(field.len),
2967 .flag_union => switch (field.u) {
2968 inline else => |v| extraFieldLen(v),
2969 },
2970 },
2971 .@"extern" => @divExact(@sizeOf(Field), @sizeOf(u32)),
2972 },
2973 else => @compileError("bad type: " ++ @typeName(Field)),
2974 };
2975 }
2976
2977 fn extraLen(extra: anytype) usize {
2978 const field_names = @typeInfo(@TypeOf(extra)).@"struct".field_names;
2979 var i: usize = 0;
2980 inline for (field_names) |name| {
2981 i += Storage.extraFieldLen(@field(extra, name));
2982 }
2983 return i;
2984 }
2985
2986 inline fn setExtraField(buffer: []u32, i: usize, comptime Field: type, value: anytype) usize {
2987 switch (@typeInfo(Field)) {
2988 .void => return 0,
2989 .int => |info| switch (info.bits) {
2990 32 => {
2991 buffer[i] = value;
2992 return 1;
2993 },
2994 64 => {
2995 buffer[i..][0..2].* = @bitCast(value);
2996 return 2;
2997 },
2998 else => comptime unreachable,
2999 },
3000 .@"enum" => {
3001 buffer[i] = @backingInt(value);
3002 return 1;
3003 },
3004 .@"struct" => |info| switch (info.layout) {
3005 .@"packed" => switch (info.backing_integer.?) {
3006 u32 => {
3007 buffer[i] = @bitCast(value);
3008 return 1;
3009 },
3010 u64 => {
3011 buffer[i..][0..2].* = @bitCast(value);
3012 return 2;
3013 },
3014 else => comptime unreachable,
3015 },
3016 .auto => switch (Field) {
3017 std.Target.Cpu.Feature.Set => {
3018 const casted: []const u32 = @ptrCast(&value.ints);
3019 @memcpy(buffer[i..][0..casted.len], casted);
3020 return casted.len;
3021 },
3022 else => switch (Field.storage) {
3023 .flag_optional, .enum_optional => {
3024 return if (value.value) |v| setExtraField(buffer, i, Field.Value, v) else 0;
3025 },
3026 .flag_union => return switch (value.u) {
3027 inline else => |x| setExtraField(buffer, i, @TypeOf(x), x),
3028 },
3029 .extended => @compileError("unimplemented"),
3030 .flag_length_prefixed_list => {
3031 const len: u32 = @intCast(value.slice.len);
3032 if (len == 0) return 0; // Flag bit hides the length prefix.
3033 buffer[i] = len;
3034 const buf_len = len * @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
3035 @memcpy(buffer[i + 1 ..][0..buf_len], @as([]const u32, @ptrCast(value.slice)));
3036 return 1 + buf_len;
3037 },
3038 .length_prefixed_list => {
3039 const len: u32 = @intCast(value.slice.len);
3040 buffer[i] = len;
3041 const buf_len = len * @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
3042 @memcpy(buffer[i + 1 ..][0..buf_len], @as([]const u32, @ptrCast(value.slice)));
3043 return 1 + buf_len;
3044 },
3045 .flag_list => {
3046 const len: u32 = @intCast(value.slice.len);
3047 @memcpy(buffer[i..][0..len], @as([]const u32, @ptrCast(value.slice)));
3048 return len;
3049 },
3050 .multi_list => {
3051 const len: u32 = @intCast(value.mal.len);
3052 buffer[i] = len;
3053 const field_names = @typeInfo(Field.Elem).@"struct".field_names;
3054 inline for (0..field_names.len) |field_i| @memcpy(
3055 buffer[i + 1 + field_i * len ..][0..len],
3056 @as([]const u32, @ptrCast(value.mal.items(@fromBackingInt(@intCast(field_i))))),
3057 );
3058 return 1 + field_names.len * len;
3059 },
3060 .union_list => {
3061 if (value.len == 0) return 0;
3062 const Tag = @typeInfo(Field.Union).@"union".tag_type.?;
3063 const slice_ptr: [*]const Field.Union = @ptrCast(@alignCast(value.data));
3064 const slice = slice_ptr[0..value.len];
3065 const meta_buffer = buffer[i..][0 .. (slice.len * @bitSizeOf(Field.Meta) + 31) / 32];
3066 for (slice[0 .. slice.len - 1], 0..) |elem, elem_index| {
3067 const union_tag: Tag = elem;
3068 storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{
3069 .tag = union_tag,
3070 .last = false,
3071 }));
3072 } else {
3073 const elem_index = slice.len - 1;
3074 const elem = slice[elem_index];
3075 const union_tag: Tag = elem;
3076 storeBits(u32, meta_buffer, elem_index * @bitSizeOf(Field.Meta), @as(Field.Meta, .{
3077 .tag = union_tag,
3078 .last = true,
3079 }));
3080 }
3081 var total: usize = meta_buffer.len;
3082 for (i + meta_buffer.len.., slice) |elem_index, src| switch (src) {
3083 inline else => |x| total += setExtraField(buffer, elem_index, @TypeOf(x), x),
3084 };
3085 return total;
3086 },
3087 },
3088 },
3089 .@"extern" => {
3090 const n = @divExact(@sizeOf(Field), @sizeOf(u32));
3091 const ptr: *align(@alignOf(Field)) const [n]u32 = @ptrCast(&value);
3092 buffer[i..][0..n].* = ptr.*;
3093 return n;
3094 },
3095 },
3096 else => @compileError("bad field type: " ++ @typeName(Field)),
3097 }
3098 }
3099};
3100
3101fn IndexType(comptime T: type) type {
3102 return enum(u32) {
3103 _,
3104
3105 pub fn get(this: @This(), c: *const Configuration) T {
3106 return extraData(c, T, @backingInt(this));
3107 }
3108 };
3109}
3110
3111pub fn extraData(c: *const Configuration, comptime T: type, index: usize) T {
3112 var i: usize = index;
3113 return Storage.data(c.extra, &i, T);
3114}
3115
3116pub const LoadFileError = Io.File.Reader.Error || Allocator.Error || error{EndOfStream};
3117
3118pub fn loadFile(arena: Allocator, io: Io, file: Io.File) LoadFileError!Configuration {
3119 var buffer: [2000]u8 = undefined;
3120 var fr = file.reader(io, &buffer);
3121 return load(arena, &fr.interface) catch |err| switch (err) {
3122 error.ReadFailed => return fr.err.?,
3123 else => |e| return e,
3124 };
3125}
3126
3127pub const LoadError = Io.Reader.Error || Allocator.Error;
3128
3129pub fn load(arena: Allocator, reader: *Io.Reader) LoadError!Configuration {
3130 const header = try reader.takeStruct(Header, .native);
3131 const result: Configuration = .{
3132 .string_bytes = try arena.alloc(u8, header.string_bytes_len),
3133 .steps = try arena.alloc(Step, header.steps_len),
3134 .path_deps = try arena.alloc(PathDep, header.path_deps_len),
3135 .unlazy_deps = try arena.alloc(String, header.unlazy_deps_len),
3136 .system_integrations = try arena.alloc(SystemIntegration, header.system_integrations_len),
3137 .available_options = try arena.alloc(AvailableOption, header.available_options_len),
3138 .search_prefixes = try arena.alloc(String, header.search_prefixes_len),
3139 .extra = try arena.alloc(u32, header.extra_len),
3140 .default_step = header.default_step,
3141 .generated_files_len = header.generated_files_len,
3142 .poisoned = header.flags.poisoned,
3143 };
3144 var vecs = [_][]u8{
3145 result.string_bytes,
3146 @ptrCast(result.steps),
3147 @ptrCast(result.path_deps),
3148 @ptrCast(result.unlazy_deps),
3149 @ptrCast(result.system_integrations),
3150 @ptrCast(result.available_options),
3151 @ptrCast(result.search_prefixes),
3152 @ptrCast(result.extra),
3153 };
3154 try reader.readVecAll(&vecs);
3155 return result;
3156}
3157
3158/// Loads bits using native endianness when `value` spans multiple bytes.
3159/// On big endian architectures, `bit_offset` uses MSb 0 bit numbering.
3160/// On little endian architectures, `bit_offset` uses LSb 0 bit numbering.
3161/// See `storeBits`.
3162pub fn loadBits(comptime Int: type, buffer: []const Int, bit_offset: usize, comptime Result: type) Result {
3163 const index = bit_offset / @bitSizeOf(Int);
3164 const small_bit_offset = bit_offset % @bitSizeOf(Int);
3165 const ResultInt = @Int(.unsigned, @bitSizeOf(Result));
3166 switch (native_endian) {
3167 .little => {
3168 const result: ResultInt = @truncate(buffer[index] >> @intCast(small_bit_offset));
3169 const available_bits = @bitSizeOf(Int) - small_bit_offset;
3170 if (available_bits >= @bitSizeOf(ResultInt)) return @bitCast(result);
3171 const missing_bits = @bitSizeOf(ResultInt) - available_bits;
3172 const upper: ResultInt = @truncate(buffer[index + 1] & ((@as(usize, 1) << @intCast(missing_bits)) - 1));
3173 return @bitCast(result | (upper << @intCast(available_bits)));
3174 },
3175 .big => {
3176 const available_bits = @bitSizeOf(Int) - small_bit_offset;
3177 if (available_bits >= @bitSizeOf(ResultInt)) {
3178 const shift = available_bits - @bitSizeOf(ResultInt);
3179 const result: ResultInt = @truncate(buffer[index] >> @intCast(shift));
3180 return @bitCast(result);
3181 }
3182 const mask = (@as(Int, 1) << @intCast(available_bits)) - 1;
3183 const result: ResultInt = @intCast(buffer[index] & mask);
3184 const missing_bits = @bitSizeOf(ResultInt) - available_bits;
3185 const lower: ResultInt = @truncate(buffer[index + 1] >> @intCast(@bitSizeOf(Int) - missing_bits));
3186 return @bitCast((result << @intCast(missing_bits)) | lower);
3187 },
3188 }
3189}
3190
3191/// Store bits using native endianness when `value` spans multiple bytes.
3192/// On big endian architectures:
3193/// - For a given value, the bits of an earlier byte are more significant than the bits of subsequent bytes.
3194/// - `bit_offset` uses MSb 0 bit numbering.
3195/// On little endian architectures:
3196/// - For a given value, the bits of an earlier byte are less significant than the bits of subsequent bytes.
3197/// - `bit_offset` uses LSb 0 bit numbering.
3198pub fn storeBits(comptime Int: type, buffer: []Int, bit_offset: usize, value: anytype) void {
3199 const Value = @TypeOf(value);
3200 const ValueInt = @Int(.unsigned, @bitSizeOf(Value));
3201 const value_int: ValueInt = @bitCast(value);
3202 const index = bit_offset / @bitSizeOf(Int);
3203 const small_bit_offset = bit_offset % @bitSizeOf(Int);
3204 const available_bits = @bitSizeOf(Int) - small_bit_offset;
3205 if (available_bits >= @bitSizeOf(ValueInt)) {
3206 const shift = switch (native_endian) {
3207 .little => small_bit_offset,
3208 .big => available_bits - @bitSizeOf(ValueInt),
3209 };
3210 buffer[index] &= ~(((@as(Int, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(shift));
3211 buffer[index] |= @as(Int, value_int) << @intCast(shift);
3212 } else {
3213 const DoubleInt = @Int(.unsigned, @bitSizeOf(Int) * 2);
3214 const shift = switch (native_endian) {
3215 .little => small_bit_offset,
3216 .big => @bitSizeOf(DoubleInt) - small_bit_offset - @bitSizeOf(ValueInt),
3217 };
3218 const ptr: *align(@alignOf(Int)) DoubleInt = @ptrCast(buffer[index..][0..2]);
3219 ptr.* &= ~(((@as(DoubleInt, 1) << @intCast(@bitSizeOf(Value))) - 1) << @intCast(shift));
3220 ptr.* |= @as(DoubleInt, value_int) << @intCast(shift);
3221 }
3222}
3223
3224test "loadBits and storeBits" {
3225 var buffer: [2]u32 = switch (native_endian) {
3226 .little => .{
3227 //──┐ 0b100011 (end) ┌─┐ 0b100
3228 0b01111111000000001111111100000000,
3229 // n <── bit offset 0 ┘
3230 // ┌── 0b100011 (start)
3231 0b11111111000000001111111100000100,
3232 },
3233 .big => .{
3234 // ┌─┐ 0b100 ┌── 0b100011 (start)
3235 0b11111110000000001111111100000100,
3236 //└ bit offset 0 ──> n
3237 //──┐ 0b100011 (end)
3238 0b01111111000000001111111100000000,
3239 },
3240 };
3241
3242 try std.testing.expectEqual(0b100, loadBits(u32, &buffer, 6, u3));
3243 try std.testing.expectEqual(0b100011, loadBits(u32, &buffer, 29, u6));
3244
3245 storeBits(u32, &buffer, 0, @as(u1, 0b0));
3246 storeBits(u32, &buffer, 6, @as(u3, 0b010));
3247 storeBits(u32, &buffer, 29, @as(u6, 0b010110));
3248 storeBits(u32, &buffer, 40, @as(u17, 0b01110110011111110));
3249
3250 try std.testing.expectEqual(0b0, loadBits(u32, &buffer, 0, u1));
3251 try std.testing.expectEqual(0b010, loadBits(u32, &buffer, 6, u3));
3252 try std.testing.expectEqual(0b010110, loadBits(u32, &buffer, 29, u6));
3253 try std.testing.expectEqual(0b01110110011111110, loadBits(u32, &buffer, 40, u17));
3254
3255 // Test roundtripping of size/offset combinations
3256 inline for (1..32) |value_size| {
3257 for (0..64) |bit_offset| {
3258 if (value_size + bit_offset > @bitSizeOf(@TypeOf(buffer))) continue;
3259
3260 buffer = .{ 0, 0 };
3261
3262 const Value = @Int(.unsigned, value_size);
3263 const value: Value = @intCast((@as(u32, 1) << @intCast(@bitSizeOf(Value))) - 1);
3264 storeBits(u32, &buffer, bit_offset, value);
3265 std.testing.expectEqual(value, loadBits(u32, &buffer, bit_offset, Value)) catch |err| {
3266 std.debug.print("value size: {} bit offset: {}\n", .{ value_size, bit_offset });
3267 std.debug.print("buffer: {b:0>32} {b:0>32}\n", .{ buffer[0], buffer[1] });
3268 return err;
3269 };
3270 }
3271 }
3272}