zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob c8aa77ed (73582B) - Raw


      1 const builtin = @import("builtin");
      2 
      3 const std = @import("std");
      4 const Allocator = std.mem.Allocator;
      5 const Color = std.zig.Color;
      6 const Configuration = std.Build.Configuration;
      7 const File = std.Io.File;
      8 const Io = std.Io;
      9 const Step = std.Build.Step;
     10 const Writer = std.Io.Writer;
     11 const assert = std.debug.assert;
     12 const fatal = std.process.fatal;
     13 const fmt = std.fmt;
     14 const log = std.log;
     15 const mem = std.mem;
     16 const process = std.process;
     17 
     18 pub const root = @import("@build");
     19 pub const dependencies = @import("@dependencies");
     20 
     21 pub const std_options: std.Options = .{
     22     .side_channels_mitigations = .none,
     23     .http_disable_tls = true,
     24 };
     25 
     26 pub fn main(init: process.Init.Minimal) !void {
     27     var arena_allocator: std.heap.ArenaAllocator = .init(std.heap.page_allocator);
     28     defer arena_allocator.deinit();
     29     const arena = arena_allocator.allocator();
     30 
     31     // The configurer is always short-lived because all it does is serialize
     32     // the configuration, which is picked up by a separate maker process.
     33     var threaded: std.Io.Threaded = .init(arena, .{
     34         .environ = init.environ,
     35         .argv0 = .init(init.args),
     36     });
     37     defer threaded.deinit();
     38     const io = threaded.io();
     39 
     40     const args = try init.args.toSlice(arena);
     41 
     42     var arg_i: usize = 1; // Skip own executable name.
     43 
     44     const zig_exe = expectArgOrFatal(args, &arg_i, "--zig");
     45     const build_root_sub_path = expectArgOrFatal(args, &arg_i, "--build-root");
     46 
     47     var graph: std.Build.Graph = .{
     48         .io = io,
     49         .arena = arena,
     50         .environ_map = try init.environ.createMap(arena),
     51         // TODO get this from parent process instead
     52         .host = .{
     53             .query = .{},
     54             .result = try std.zig.system.resolveTargetQuery(io, .{}),
     55         },
     56         .generated_files = .empty,
     57         .zig_exe = zig_exe,
     58 
     59         // Created before running the user's configure script so that some things
     60         // can be added during script execution such as strings.
     61         //
     62         // Use of arena here is load-bearing because `std.Build.dupe` is
     63         // implemented by string internment, and then returning the interned
     64         // slice. When the string bytes array is reallocated, that reference
     65         // must stay alive.
     66         .wip_configuration = .init(arena),
     67     };
     68     assert(try graph.wip_configuration.addString("") == .empty);
     69     assert(try graph.wip_configuration.addString("root") == .root);
     70 
     71     const cwd: Io.Dir = .cwd();
     72 
     73     const build_root: std.Build.Cache.Path = .{
     74         .root_dir = .{
     75             .handle = try cwd.openDir(io, build_root_sub_path, .{}),
     76             .path = build_root_sub_path,
     77         },
     78     };
     79 
     80     const builder = try std.Build.create(&graph, build_root, dependencies.root_deps);
     81 
     82     var color: Color = .auto;
     83 
     84     while (nextArg(args, &arg_i)) |arg| {
     85         if (mem.cutPrefix(u8, arg, "-D")) |option_contents| {
     86             if (option_contents.len == 0)
     87                 fatalWithHint("expected option name after '-D'", .{});
     88             if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
     89                 const option_name = option_contents[0..name_end];
     90                 const option_value = option_contents[name_end + 1 ..];
     91                 if (try builder.addUserInputOption(option_name, option_value))
     92                     fatal("  access the help menu with 'zig build -h'", .{});
     93             } else {
     94                 if (try builder.addUserInputFlag(option_contents))
     95                     fatal("  access the help menu with 'zig build -h'", .{});
     96             }
     97         } else if (mem.cutPrefix(u8, arg, "-fsys=")) |name| {
     98             try graph.system_integration_options.put(arena, name, .user_enabled);
     99         } else if (mem.cutPrefix(u8, arg, "-fno-sys=")) |name| {
    100             try graph.system_integration_options.put(arena, name, .user_disabled);
    101         } else if (mem.eql(u8, arg, "--release")) {
    102             graph.release_mode = .any;
    103         } else if (mem.cutPrefix(u8, arg, "--release=")) |rest| {
    104             graph.release_mode = std.meta.stringToEnum(std.Build.ReleaseMode, rest) orelse {
    105                 fatalWithHint("expected --release=[off|any|fast|safe|small]; found: {s}", .{arg});
    106             };
    107         } else if (mem.cutPrefix(u8, arg, "--color=")) |rest| {
    108             color = std.meta.stringToEnum(Color, rest) orelse
    109                 fatalWithHint("expected --color=[auto|on|off]; found: {s}", .{arg});
    110         } else if (mem.eql(u8, arg, "--system")) {
    111             // The usage text shows another argument after this parameter
    112             // but it is handled by the parent process. The build runner
    113             // only sees this flag.
    114             graph.system_package_mode = true;
    115         } else if (mem.eql(u8, arg, "--verbose")) {
    116             graph.verbose = true;
    117         } else if (mem.cutPrefix(u8, arg, "--cache-poison=")) |rest| {
    118             graph.cache_poison = std.meta.stringToEnum(std.Build.Graph.CachePoison, rest) orelse
    119                 fatalWithHint("expected --cache-poison=[pure|poisoned|disallowed|ignored]; found: {s}", .{arg});
    120         } else {
    121             fatalWithHint("unrecognized argument: {s}", .{arg});
    122         }
    123     }
    124 
    125     const NO_COLOR = std.zig.EnvVar.NO_COLOR.isSet(&graph.environ_map);
    126     const CLICOLOR_FORCE = std.zig.EnvVar.CLICOLOR_FORCE.isSet(&graph.environ_map);
    127 
    128     graph.stderr_mode = switch (color) {
    129         .auto => try .detect(io, .stderr(), NO_COLOR, CLICOLOR_FORCE),
    130         .on => .escape_codes,
    131         .off => .no_color,
    132     };
    133 
    134     try builder.runBuild(root);
    135 
    136     if (builder.validateUserInputDidItFail()) {
    137         fatal("  access the help menu with 'zig build -h'", .{});
    138     }
    139 
    140     try serializePackageOptions(builder, &graph.wip_configuration);
    141     try serializeSystemIntegrationOptions(&graph, &graph.wip_configuration);
    142 
    143     var stdout_buffer: [1024]u8 = undefined;
    144     var file_writer = Io.File.stdout().writerStreaming(io, &stdout_buffer);
    145     serialize(builder, &graph.wip_configuration, &file_writer.interface) catch |err| switch (err) {
    146         error.WriteFailed => fatal("failed to write configuration output: {t}", .{file_writer.err.?}),
    147         error.OutOfMemory => |e| return e,
    148     };
    149     file_writer.flush() catch |err| fatal("failed to write configuration output: {t}", .{err});
    150 
    151     // This executable is short-lived and run in Debug mode, so we'd rather
    152     // have `zig build` run faster than catch resource leaks in the user's
    153     // build.zig script (or, frankly, this configure runner), therefore we call
    154     // exit directly here rather than cleanExit.
    155     process.exit(0);
    156 }
    157 
    158 const Serialize = struct {
    159     arena: Allocator,
    160     wc: *Configuration.Wip,
    161     module_map: std.AutoArrayHashMapUnmanaged(*std.Build.Module, Configuration.Module.Index) = .empty,
    162     package_map: std.AutoArrayHashMapUnmanaged(*std.Build, Configuration.Package.Index) = .empty,
    163     /// Index corresponds to `Configuration.steps` index.
    164     step_map: std.AutoArrayHashMapUnmanaged(*Step, void) = .empty,
    165 
    166     fn builderToPackage(s: *Serialize, b: *std.Build) !Configuration.Package.Index {
    167         if (b.pkg_hash.len == 0) return .root;
    168         const arena = s.arena;
    169         const wc = s.wc;
    170         const gop = try s.package_map.getOrPut(arena, b);
    171         if (!gop.found_existing) {
    172             gop.value_ptr.* = try wc.addExtra(Configuration.Package, .{
    173                 .hash = try wc.addString(b.pkg_hash),
    174                 .dep_prefix = try wc.addString(b.dep_prefix),
    175                 .root_path = try wc.addString(try b.root.toString(arena)),
    176             });
    177         }
    178         return gop.value_ptr.*;
    179     }
    180 
    181     fn addOptionalLazyPathEnum(s: *Serialize, lp: ?std.Build.LazyPath) !Configuration.LazyPath.OptionalIndex {
    182         const wc = s.wc;
    183         return @enumFromInt(switch (lp orelse return .none) {
    184             .src_path => |src_path| i: {
    185                 const sub_path = try wc.addString(src_path.sub_path);
    186                 break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{
    187                     .owner = try s.builderToPackage(src_path.owner),
    188                     .sub_path = sub_path,
    189                 });
    190             },
    191             .generated => |generated| i: {
    192                 const sub_path = try wc.addString(generated.sub_path);
    193                 break :i try wc.addExtraErased(Configuration.LazyPath.Generated, .{
    194                     .flags = .{ .up = @intCast(generated.up) },
    195                     .index = generated.index,
    196                     .sub_path = sub_path,
    197                 });
    198             },
    199             .cwd_relative => |cwd_relative_sub_path| i: {
    200                 const sub_path = try wc.addString(cwd_relative_sub_path);
    201                 break :i try wc.addExtraErased(Configuration.LazyPath.Relative, .{
    202                     .flags = .{ .base = .cwd },
    203                     .sub_path = sub_path,
    204                 });
    205             },
    206             .relative => |relative| i: {
    207                 break :i try wc.addExtraErased(Configuration.LazyPath.Relative, .{
    208                     .flags = .{ .base = relative.base },
    209                     .sub_path = relative.sub_path,
    210                 });
    211             },
    212             .dependency => |dependency| i: {
    213                 const sub_path = try wc.addString(dependency.sub_path);
    214                 break :i try wc.addExtraErased(Configuration.LazyPath.SourcePath, .{
    215                     .owner = try s.builderToPackage(dependency.dependency.builder),
    216                     .sub_path = sub_path,
    217                 });
    218             },
    219         });
    220     }
    221 
    222     fn addOptionalLazyPath(s: *Serialize, lp: ?std.Build.LazyPath) !?Configuration.LazyPath.Index {
    223         return (try addOptionalLazyPathEnum(s, lp)).unwrap();
    224     }
    225 
    226     fn addLazyPath(s: *Serialize, lp: std.Build.LazyPath) !Configuration.LazyPath.Index {
    227         return @enumFromInt(@intFromEnum(try addOptionalLazyPathEnum(s, lp)));
    228     }
    229 
    230     fn addOptionalSemVer(s: *Serialize, sem_ver: ?std.SemanticVersion) !?Configuration.String {
    231         return if (sem_ver) |sv| try s.wc.addSemVer(sv) else null;
    232     }
    233 
    234     fn addOptionalString(s: *Serialize, opt_slice: ?[]const u8) !?Configuration.String {
    235         return if (opt_slice) |slice| try s.wc.addString(slice) else null;
    236     }
    237 
    238     fn addSystemLib(s: *Serialize, sl: *const std.Build.Module.SystemLib) !Configuration.SystemLib.Index {
    239         const wc = s.wc;
    240         return try wc.addDeduped(Configuration.SystemLib, .{
    241             .flags = .{
    242                 .needed = sl.needed,
    243                 .weak = sl.weak,
    244                 .use_pkg_config = sl.use_pkg_config,
    245                 .preferred_link_mode = sl.preferred_link_mode,
    246                 .search_strategy = sl.search_strategy,
    247             },
    248             .name = try wc.addString(sl.name),
    249         });
    250     }
    251 
    252     fn addCSourceFile(s: *Serialize, csf: *const std.Build.Module.CSourceFile) !Configuration.CSourceFile.Index {
    253         const wc = s.wc;
    254         const args = try initStringList(s, csf.flags);
    255         return try wc.addExtra(Configuration.CSourceFile, .{
    256             .flags = .{
    257                 .args_len = @intCast(args.len),
    258                 .lang = .init(csf.language),
    259             },
    260             .file = try addLazyPath(s, csf.file),
    261             .args = .{ .slice = args },
    262         });
    263     }
    264 
    265     fn addCSourceFiles(s: *Serialize, csf: *const std.Build.Module.CSourceFiles) !Configuration.CSourceFiles.Index {
    266         const wc = s.wc;
    267         const sub_paths = try initStringList(s, csf.files);
    268         const args = try initStringList(s, csf.flags);
    269         return try wc.addExtra(Configuration.CSourceFiles, .{
    270             .flags = .{
    271                 .args_len = @intCast(args.len),
    272                 .lang = .init(csf.language),
    273             },
    274             .root = try addLazyPath(s, csf.root),
    275             .sub_paths = .{ .slice = sub_paths },
    276             .args = .{ .slice = args },
    277         });
    278     }
    279 
    280     fn addRcSourceFile(s: *Serialize, rsf: *const std.Build.Module.RcSourceFile) !Configuration.RcSourceFile.Index {
    281         const wc = s.wc;
    282         const include_paths = try initLazyPathList(s, rsf.include_paths);
    283         const args = try initStringList(s, rsf.flags);
    284         return try wc.addExtra(Configuration.RcSourceFile, .{
    285             .flags = .{
    286                 .args_len = @intCast(args.len),
    287                 .include_paths = include_paths.len != 0,
    288             },
    289             .file = try addLazyPath(s, rsf.file),
    290             .include_paths = .{ .slice = include_paths },
    291             .args = .{ .slice = args },
    292         });
    293     }
    294 
    295     fn addEnvironMap(s: *Serialize, opt_map: ?*std.process.Environ.Map) !?Configuration.EnvironMap.Index {
    296         const wc = s.wc;
    297         const map = opt_map orelse return null;
    298         return try wc.addDeduped(Configuration.EnvironMap, .{
    299             .keys = try wc.addStringList(map.array_hash_map.keys()),
    300             .values = try wc.addStringList(map.array_hash_map.values()),
    301         });
    302     }
    303 
    304     fn initArgsList(s: *Serialize, args: []const Step.Run.Arg) ![]const Configuration.Step.Run.Arg.Index {
    305         const wc = s.wc;
    306         const result = try s.arena.alloc(Configuration.Step.Run.Arg.Index, args.len);
    307         for (result, args) |*dest, src| {
    308             dest.* = try wc.addExtra(Configuration.Step.Run.Arg, switch (src) {
    309                 .artifact => |a| .{
    310                     .flags = .{
    311                         .tag = .artifact,
    312                         .prefix = a.prefix.len != 0,
    313                         .suffix = false,
    314                         .basename = false,
    315                         .path = false,
    316                         .producer = true,
    317                         .generated = false,
    318                         .dep_file = false,
    319                     },
    320                     .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
    321                     .suffix = .{ .value = null },
    322                     .basename = .{ .value = null },
    323                     .path = .{ .value = null },
    324                     .producer = .{ .value = stepIndex(s, &a.artifact.step) },
    325                     .generated = .{ .value = null },
    326                 },
    327                 .lazy_path => |a| .{
    328                     .flags = .{
    329                         .tag = .path_file,
    330                         .prefix = a.prefix.len != 0,
    331                         .suffix = false,
    332                         .basename = false,
    333                         .path = true,
    334                         .producer = false,
    335                         .generated = false,
    336                         .dep_file = false,
    337                     },
    338                     .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
    339                     .suffix = .{ .value = null },
    340                     .basename = .{ .value = null },
    341                     .path = .{ .value = try addLazyPath(s, a.lazy_path) },
    342                     .producer = .{ .value = null },
    343                     .generated = .{ .value = null },
    344                 },
    345                 .decorated_directory => |a| .{
    346                     .flags = .{
    347                         .tag = .path_directory,
    348                         .prefix = a.prefix.len != 0,
    349                         .suffix = a.suffix.len != 0,
    350                         .basename = false,
    351                         .path = true,
    352                         .producer = false,
    353                         .generated = false,
    354                         .dep_file = false,
    355                     },
    356                     .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
    357                     .suffix = .{ .value = if (a.suffix.len != 0) try wc.addString(a.suffix) else null },
    358                     .basename = .{ .value = null },
    359                     .path = .{ .value = try addLazyPath(s, a.lazy_path) },
    360                     .producer = .{ .value = null },
    361                     .generated = .{ .value = null },
    362                 },
    363                 .file_content => |a| .{
    364                     .flags = .{
    365                         .tag = .file_content,
    366                         .prefix = a.prefix.len != 0,
    367                         .suffix = false,
    368                         .basename = false,
    369                         .path = true,
    370                         .producer = false,
    371                         .generated = false,
    372                         .dep_file = false,
    373                     },
    374                     .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
    375                     .suffix = .{ .value = null },
    376                     .basename = .{ .value = null },
    377                     .path = .{ .value = try addLazyPath(s, a.lazy_path) },
    378                     .producer = .{ .value = null },
    379                     .generated = .{ .value = null },
    380                 },
    381                 .bytes => |a| .{
    382                     .flags = .{
    383                         .tag = .string,
    384                         .prefix = true,
    385                         .suffix = false,
    386                         .basename = false,
    387                         .path = false,
    388                         .producer = false,
    389                         .generated = false,
    390                         .dep_file = false,
    391                     },
    392                     .prefix = .{ .value = try wc.addString(a) },
    393                     .suffix = .{ .value = null },
    394                     .basename = .{ .value = null },
    395                     .path = .{ .value = null },
    396                     .producer = .{ .value = null },
    397                     .generated = .{ .value = null },
    398                 },
    399                 .output_file, .output_file_dep => |a, tag| .{
    400                     .flags = .{
    401                         .tag = .output_file,
    402                         .prefix = a.prefix.len != 0,
    403                         .suffix = false,
    404                         .basename = a.basename.len != 0,
    405                         .path = false,
    406                         .producer = false,
    407                         .generated = true,
    408                         .dep_file = tag == .output_file_dep,
    409                     },
    410                     .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
    411                     .suffix = .{ .value = null },
    412                     .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
    413                     .path = .{ .value = null },
    414                     .producer = .{ .value = null },
    415                     .generated = .{ .value = a.generated_file },
    416                 },
    417                 .output_directory => |a| .{
    418                     .flags = .{
    419                         .tag = .output_directory,
    420                         .prefix = a.prefix.len != 0,
    421                         .suffix = false,
    422                         .basename = a.basename.len != 0,
    423                         .path = false,
    424                         .producer = false,
    425                         .generated = true,
    426                         .dep_file = false,
    427                     },
    428                     .prefix = .{ .value = if (a.prefix.len != 0) try wc.addString(a.prefix) else null },
    429                     .suffix = .{ .value = null },
    430                     .basename = .{ .value = if (a.basename.len != 0) try wc.addString(a.basename) else null },
    431                     .path = .{ .value = null },
    432                     .producer = .{ .value = null },
    433                     .generated = .{ .value = a.generated_file },
    434                 },
    435                 .passthru => .{
    436                     .flags = .{
    437                         .tag = .passthru,
    438                         .prefix = false,
    439                         .suffix = false,
    440                         .basename = false,
    441                         .path = false,
    442                         .producer = false,
    443                         .generated = false,
    444                         .dep_file = false,
    445                     },
    446                     .prefix = .{ .value = null },
    447                     .suffix = .{ .value = null },
    448                     .basename = .{ .value = null },
    449                     .path = .{ .value = null },
    450                     .producer = .{ .value = null },
    451                     .generated = .{ .value = null },
    452                 },
    453             });
    454         }
    455         return result;
    456     }
    457 
    458     fn initIncludeDirList(
    459         s: *Serialize,
    460         list: []const std.Build.Module.IncludeDir,
    461     ) ![]const Configuration.Module.IncludeDir {
    462         const result = try s.arena.alloc(Configuration.Module.IncludeDir, list.len);
    463         for (result, list) |*dest, src| dest.* = switch (src) {
    464             .path => |lp| .{ .path = try addLazyPath(s, lp) },
    465             .path_system => |lp| .{ .path_system = try addLazyPath(s, lp) },
    466             .path_after => |lp| .{ .path_after = try addLazyPath(s, lp) },
    467             .framework_path => |lp| .{ .framework_path = try addLazyPath(s, lp) },
    468             .framework_path_system => |lp| .{ .framework_path_system = try addLazyPath(s, lp) },
    469             .embed_path => |lp| .{ .embed_path = try addLazyPath(s, lp) },
    470             .other_step => |cs| .{ .other_step = stepIndex(s, &cs.step) },
    471             .config_header_step => |chs| .{ .config_header_step = stepIndex(s, &chs.step) },
    472         };
    473         return result;
    474     }
    475 
    476     fn initLazyPathList(s: *Serialize, list: []const std.Build.LazyPath) ![]const Configuration.LazyPath.Index {
    477         const result = try s.arena.alloc(Configuration.LazyPath.Index, list.len);
    478         for (result, list) |*dest, src| dest.* = try addLazyPath(s, src);
    479         return result;
    480     }
    481 
    482     fn initStringList(s: *Serialize, list: []const []const u8) ![]const Configuration.String {
    483         const wc = s.wc;
    484         const result = try s.arena.alloc(Configuration.String, list.len);
    485         for (result, list) |*dest, src| dest.* = try wc.addString(src);
    486         return result;
    487     }
    488 
    489     fn initCopyList(s: *Serialize, list: []const Step.WriteFile.Copy) ![]const Configuration.Step.WriteFile.Copy {
    490         const result = try s.arena.alloc(Configuration.Step.WriteFile.Copy, list.len);
    491         for (result, list) |*dest, src| dest.* = .{
    492             .sub_path = src.sub_path,
    493             .src_file = try s.addLazyPath(src.src_file),
    494         };
    495         return result;
    496     }
    497 
    498     fn initOptionalStringList(s: *Serialize, list: []const ?[]const u8) ![]const Configuration.OptionalString {
    499         const wc = s.wc;
    500         const result = try s.arena.alloc(Configuration.OptionalString, list.len);
    501         for (result, list) |*dest, src| dest.* = try wc.addOptionalString(src);
    502         return result;
    503     }
    504 
    505     fn addModule(s: *Serialize, m: *std.Build.Module) !Configuration.Module.Index {
    506         if (s.module_map.get(m)) |index| return index;
    507 
    508         const wc = s.wc;
    509         const arena = s.arena;
    510 
    511         const rpaths = try arena.alloc(Configuration.Module.RPath, m.rpaths.items.len);
    512         for (rpaths, m.rpaths.items) |*dest, src| dest.* = switch (src) {
    513             .lazy_path => |lp| .{ .lazy_path = try addLazyPath(s, lp) },
    514             .special => |slice| .{ .special = try wc.addString(slice) },
    515         };
    516 
    517         const link_objects = try arena.alloc(Configuration.Module.LinkObject, m.link_objects.items.len);
    518         for (link_objects, m.link_objects.items) |*dest, *src| dest.* = switch (src.*) {
    519             .static_path => |lp| .{ .static_path = try addLazyPath(s, lp) },
    520             .other_step => |cs| .{ .other_step = stepIndex(s, &cs.step) },
    521             .system_lib => |*sl| .{ .system_lib = try addSystemLib(s, sl) },
    522             .assembly_file => |lp| .{ .assembly_file = try addLazyPath(s, lp) },
    523             .c_source_file => |csf| .{ .c_source_file = try addCSourceFile(s, csf) },
    524             .c_source_files => |csf| .{ .c_source_files = try addCSourceFiles(s, csf) },
    525             .win32_resource_file => |wrf| .{ .win32_resource_file = try addRcSourceFile(s, wrf) },
    526         };
    527 
    528         const frameworks = try arena.alloc(Configuration.Module.Framework, m.frameworks.entries.len);
    529         for (frameworks, m.frameworks.keys(), m.frameworks.values()) |*dest, name, options| dest.* = .{
    530             .flags = .{
    531                 .needed = options.needed,
    532                 .weak = options.weak,
    533             },
    534             .name = try wc.addString(name),
    535         };
    536 
    537         const lib_paths = try initLazyPathList(s, m.lib_paths.items);
    538         const c_macros = try initStringList(s, m.c_macros.items);
    539         const export_symbol_names = try initStringList(s, m.export_symbol_names);
    540 
    541         const module_index: Configuration.Module.Index = try wc.addExtra(Configuration.Module, .{
    542             .flags = .{
    543                 .optimize = .init(m.optimize),
    544                 .strip = .init(m.strip),
    545                 .unwind_tables = .init(m.unwind_tables),
    546                 .dwarf_format = .init(m.dwarf_format),
    547                 .single_threaded = .init(m.single_threaded),
    548                 .stack_protector = .init(m.stack_protector),
    549                 .stack_check = .init(m.stack_check),
    550                 .sanitize_c = .init(m.sanitize_c),
    551                 .sanitize_thread = .init(m.sanitize_thread),
    552                 .fuzz = .init(m.fuzz),
    553                 .code_model = m.code_model,
    554                 .c_macros = c_macros.len != 0,
    555                 .include_dirs = m.include_dirs.items.len != 0,
    556                 .lib_paths = lib_paths.len != 0,
    557                 .rpaths = rpaths.len != 0,
    558                 .frameworks = frameworks.len != 0,
    559                 .link_objects = link_objects.len != 0,
    560                 .export_symbol_names = export_symbol_names.len != 0,
    561             },
    562             .flags2 = .{
    563                 .valgrind = .init(m.valgrind),
    564                 .pic = .init(m.pic),
    565                 .red_zone = .init(m.red_zone),
    566                 .omit_frame_pointer = .init(m.omit_frame_pointer),
    567                 .error_tracing = .init(m.error_tracing),
    568                 .link_libc = .init(m.link_libc),
    569                 .link_libcpp = .init(m.link_libcpp),
    570                 .no_builtin = .init(m.no_builtin),
    571             },
    572             .owner = try s.builderToPackage(m.owner),
    573             .root_source_file = try s.addOptionalLazyPathEnum(m.root_source_file),
    574             .import_table = .invalid,
    575             .resolved_target = try addOptionalResolvedTarget(wc, m.resolved_target),
    576             .c_macros = .{ .slice = c_macros },
    577             .lib_paths = .{ .slice = lib_paths },
    578             .export_symbol_names = .{ .slice = export_symbol_names },
    579             .include_dirs = .init(try s.initIncludeDirList(m.include_dirs.items)),
    580             .rpaths = .init(rpaths),
    581             .link_objects = .init(link_objects),
    582             .frameworks = .{ .slice = frameworks },
    583         });
    584 
    585         // The import table is the only place that modules can form dependency
    586         // loops. Therefore, we populate the module indexes only after adding
    587         // the module to module_map.
    588         try s.module_map.putNoClobber(arena, m, module_index);
    589 
    590         var imports = try std.MultiArrayList(Configuration.ImportTable.Import).initCapacity(arena, m.import_table.entries.len);
    591         imports.len = m.import_table.entries.len;
    592         for (
    593             imports.items(.name),
    594             imports.items(.module),
    595             m.import_table.keys(),
    596             m.import_table.values(),
    597         ) |*dest_name, *dest_module, src_name, src_module| {
    598             dest_name.* = try wc.addString(src_name);
    599             dest_module.* = try addModule(s, src_module);
    600         }
    601 
    602         comptime assert(std.mem.eql(u8, @typeInfo(Configuration.Module).@"struct".fields[2].name, "import_table"));
    603         comptime assert(@typeInfo(Configuration.Module).@"struct".fields[2].type == Configuration.ImportTable.Index);
    604         assert(wc.extra.items[@intFromEnum(module_index) + 2] == @intFromEnum(Configuration.ImportTable.Index.invalid));
    605         const import_table_index = try wc.addDeduped(Configuration.ImportTable, .{
    606             .imports = .{ .mal = imports },
    607         });
    608         wc.extra.items[@intFromEnum(module_index) + 2] = @intFromEnum(import_table_index);
    609 
    610         return module_index;
    611     }
    612 
    613     fn stepIndex(s: *const Serialize, step: *Step) Configuration.Step.Index {
    614         return @enumFromInt(s.step_map.getIndex(step).?);
    615     }
    616 };
    617 
    618 fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
    619     const graph = b.graph;
    620     const arena = graph.arena;
    621     const gpa = wc.gpa;
    622 
    623     var s: Serialize = .{ .wc = wc, .arena = arena };
    624 
    625     // Starting from all top-level steps in `b`, traverse the entire step graph
    626     // and add all step dependencies implied by module graphs.
    627     const top_level_steps = b.top_level_steps.values();
    628     try s.step_map.ensureUnusedCapacity(arena, top_level_steps.len);
    629     for (top_level_steps) |tls| {
    630         s.step_map.putAssumeCapacityNoClobber(&tls.step, {});
    631     }
    632     {
    633         while (wc.steps.items.len < s.step_map.count()) {
    634             const step = s.step_map.keys()[wc.steps.items.len];
    635 
    636             // Set up any implied dependencies for this step. It's important that we do this first, so
    637             // that the loop below discovers steps implied by the module graph.
    638             try createModuleDependenciesForStep(step);
    639 
    640             try s.step_map.ensureUnusedCapacity(arena, step.dependencies.items.len);
    641             for (step.dependencies.items) |other_step| {
    642                 s.step_map.putAssumeCapacity(other_step, {});
    643             }
    644 
    645             // Add and then de-duplicate dependencies.
    646             const dep_steps = try arena.alloc(Configuration.Step.Index, step.dependencies.items.len);
    647             for (dep_steps, step.dependencies.items) |*dest, src|
    648                 dest.* = @enumFromInt(s.step_map.getIndex(src).?);
    649 
    650             const deps: Configuration.Deps.Index = try wc.addDeduped(Configuration.Deps, .{
    651                 .steps = .{ .slice = dep_steps },
    652             });
    653 
    654             try wc.steps.ensureTotalCapacity(gpa, s.step_map.entries.capacity);
    655             wc.steps.appendAssumeCapacity(.{
    656                 .name = try wc.addString(step.name),
    657                 .owner = try s.builderToPackage(step.owner),
    658                 .deps = deps,
    659                 .max_rss = .fromBytes(step.max_rss),
    660                 .extended = @enumFromInt(switch (step.tag) {
    661                     .top_level => e: {
    662                         const top_level: *Step.TopLevel = @fieldParentPtr("step", step);
    663                         break :e try wc.addExtraErased(Configuration.Step.TopLevel, .{
    664                             .description = try wc.addString(top_level.description),
    665                         });
    666                     },
    667                     .compile => e: {
    668                         const c: *Step.Compile = @fieldParentPtr("step", step);
    669                         const exec_cmd_args: []const ?[]const u8 = c.exec_cmd_args orelse &.{};
    670                         const installed_headers: []u32 = try arena.alloc(u32, c.installed_headers.items.len);
    671                         for (installed_headers, c.installed_headers.items) |*dst, src| switch (src) {
    672                             .file => |file| {
    673                                 dst.* = try wc.addExtraErased(Configuration.Step.Compile.InstalledHeader.File, .{
    674                                     .source = try s.addLazyPath(file.source),
    675                                     .dest_sub_path = try wc.addString(file.dest_rel_path),
    676                                 });
    677                             },
    678                             .directory => |directory| {
    679                                 const include_extensions = directory.options.include_extensions orelse &.{};
    680                                 dst.* = try wc.addExtraErased(Configuration.Step.Compile.InstalledHeader.Directory, .{
    681                                     .flags = .{
    682                                         .include_extensions = include_extensions.len != 0,
    683                                         .exclude_extensions = directory.options.exclude_extensions.len != 0,
    684                                     },
    685                                     .source = try s.addLazyPath(directory.source),
    686                                     .dest_sub_path = try wc.addString(directory.dest_rel_path),
    687                                     .exclude_extensions = .{ .slice = try s.initStringList(directory.options.exclude_extensions) },
    688                                     .include_extensions = .{ .slice = try s.initStringList(include_extensions) },
    689                                 });
    690                             },
    691                         };
    692 
    693                         break :e try wc.addExtraErased(Configuration.Step.Compile, .{
    694                             .flags = .{
    695                                 .filters_len = c.filters.len != 0,
    696                                 .exec_cmd_args_len = exec_cmd_args.len != 0,
    697                                 .installed_headers_len = installed_headers.len != 0,
    698                                 .force_undefined_symbols_len = c.force_undefined_symbols.entries.len != 0,
    699 
    700                                 .verbose_link = c.verbose_link,
    701                                 .verbose_cc = c.verbose_cc,
    702                                 .rdynamic = c.rdynamic,
    703                                 .import_memory = c.import_memory,
    704                                 .export_memory = c.export_memory,
    705                                 .import_symbols = c.import_symbols,
    706                                 .import_table = c.import_table,
    707                                 .export_table = c.export_table,
    708                                 .shared_memory = c.shared_memory,
    709                                 .link_eh_frame_hdr = c.link_eh_frame_hdr,
    710                                 .link_emit_relocs = c.link_emit_relocs,
    711                                 .link_function_sections = c.link_function_sections,
    712                                 .link_data_sections = c.link_data_sections,
    713                                 .linker_dynamicbase = c.linker_dynamicbase,
    714                                 .link_z_notext = c.link_z_notext,
    715                                 .link_z_relro = c.link_z_relro,
    716                                 .link_z_lazy = c.link_z_lazy,
    717                                 .link_z_defs = c.link_z_defs,
    718                                 .headerpad_max_install_names = c.headerpad_max_install_names,
    719                                 .dead_strip_dylibs = c.dead_strip_dylibs,
    720                                 .force_load_objc = c.force_load_objc,
    721                                 .discard_local_symbols = c.discard_local_symbols,
    722                                 .mingw_unicode_entry_point = c.mingw_unicode_entry_point,
    723                             },
    724                             .flags2 = .{
    725                                 .pie = .init(c.pie),
    726                                 .formatted_panics = .init(c.formatted_panics),
    727                                 .bundle_compiler_rt = .init(c.bundle_compiler_rt),
    728                                 .bundle_ubsan_rt = .init(c.bundle_ubsan_rt),
    729                                 .each_lib_rpath = .init(c.each_lib_rpath),
    730                                 .link_gc_sections = .init(c.link_gc_sections),
    731                                 .linker_allow_shlib_undefined = .init(c.linker_allow_shlib_undefined),
    732                                 .linker_allow_undefined_version = .init(c.linker_allow_undefined_version),
    733                                 .linker_enable_new_dtags = .init(c.linker_enable_new_dtags),
    734                                 .dll_export_fns = .init(c.dll_export_fns),
    735                                 .use_llvm = .init(c.use_llvm),
    736                                 .use_lld = .init(c.use_lld),
    737                                 .use_new_linker = .init(c.use_new_linker),
    738                                 .allow_so_scripts = .init(c.allow_so_scripts),
    739                                 .sanitize_coverage_trace_pc_guard = .init(c.sanitize_coverage_trace_pc_guard),
    740                                 .linkage = .init(c.linkage),
    741                             },
    742                             .flags3 = .{
    743                                 .is_linking_libc = c.is_linking_libc,
    744                                 .is_linking_libcpp = c.is_linking_libcpp,
    745                                 .version = c.version != null,
    746                                 .compress_debug_sections = c.compress_debug_sections,
    747                                 .initial_memory = c.initial_memory != null,
    748                                 .max_memory = c.max_memory != null,
    749                                 .kind = c.kind,
    750                                 .global_base = c.global_base != null,
    751                                 .test_runner = if (c.test_runner) |tr| switch (tr.mode) {
    752                                     .simple => .simple,
    753                                     .server => .server,
    754                                 } else .default,
    755                                 .wasi_exec_model = .init(c.wasi_exec_model),
    756                                 .win32_manifest = c.win32_manifest != null,
    757                                 .win32_module_definition = c.win32_module_definition != null,
    758                                 .zig_lib_dir = c.zig_lib_dir != null,
    759                                 .rc_includes = c.rc_includes,
    760                                 .image_base = c.image_base != null,
    761                                 .build_id = .init(c.build_id),
    762                                 .entry = switch (c.entry) {
    763                                     .default => .default,
    764                                     .disabled => .disabled,
    765                                     .enabled => .enabled,
    766                                     .symbol_name => .symbol_name,
    767                                 },
    768                                 .lto = .init(c.lto),
    769                                 .subsystem = .init(c.subsystem),
    770                             },
    771                             .flags4 = .{
    772                                 .libc_file = c.libc_file != null,
    773                                 .link_z_common_page_size = c.link_z_common_page_size != null,
    774                                 .link_z_max_page_size = c.link_z_max_page_size != null,
    775                                 .pagezero_size = c.pagezero_size != null,
    776                                 .stack_size = c.stack_size != null,
    777                                 .headerpad_size = c.headerpad_size != null,
    778                                 .error_limit = c.error_limit != null,
    779                                 .install_name = c.install_name != null,
    780                                 .entitlements = c.entitlements != null,
    781                                 .expect_errors = if (c.expect_errors) |x| switch (x) {
    782                                     .contains => .contains,
    783                                     .exact => .exact,
    784                                     .starts_with => .starts_with,
    785                                     .stderr_contains => .stderr_contains,
    786                                 } else .none,
    787                                 .linker_script = c.linker_script != null,
    788                                 .version_script = c.version_script != null,
    789                                 .emit_directory = c.emit_directory != .none,
    790                                 .generated_docs = c.generated_docs != .none,
    791                                 .generated_asm = c.generated_asm != .none,
    792                                 .generated_bin = c.generated_bin != .none,
    793                                 .generated_pdb = c.generated_pdb != .none,
    794                                 .generated_implib = c.generated_implib != .none,
    795                                 .generated_llvm_bc = c.generated_llvm_bc != .none,
    796                                 .generated_llvm_ir = c.generated_llvm_ir != .none,
    797                                 .generated_h = c.generated_h != .none,
    798                             },
    799                             .root_module = try s.addModule(c.root_module),
    800                             .root_name = try wc.addString(c.name),
    801                             .linker_script = .{ .value = try s.addOptionalLazyPath(c.linker_script) },
    802                             .version_script = .{ .value = try s.addOptionalLazyPath(c.version_script) },
    803                             .zig_lib_dir = .{ .value = try s.addOptionalLazyPath(c.zig_lib_dir) },
    804                             .libc_file = .{ .value = try s.addOptionalLazyPath(c.libc_file) },
    805                             .win32_manifest = .{ .value = try s.addOptionalLazyPath(c.win32_manifest) },
    806                             .win32_module_definition = .{ .value = try s.addOptionalLazyPath(c.win32_module_definition) },
    807                             .entitlements = .{ .value = try s.addOptionalLazyPath(c.entitlements) },
    808                             .version = .{ .value = try s.addOptionalSemVer(c.version) },
    809                             .install_name = .{ .value = try s.addOptionalString(c.install_name) },
    810                             .initial_memory = .{ .value = c.initial_memory },
    811                             .max_memory = .{ .value = c.max_memory },
    812                             .global_base = .{ .value = c.global_base },
    813                             .image_base = .{ .value = c.image_base },
    814                             .link_z_common_page_size = .{ .value = c.link_z_common_page_size },
    815                             .link_z_max_page_size = .{ .value = c.link_z_max_page_size },
    816                             .pagezero_size = .{ .value = c.pagezero_size },
    817                             .stack_size = .{ .value = c.stack_size },
    818                             .headerpad_size = .{ .value = c.headerpad_size },
    819                             .error_limit = .{ .value = c.error_limit },
    820                             .entry = .{ .value = switch (c.entry) {
    821                                 .symbol_name => |name| try wc.addString(name),
    822                                 .default, .disabled, .enabled => null,
    823                             } },
    824                             .build_id = .{ .value = if (c.build_id) |id| switch (id) {
    825                                 .hexstring => |*hexstring| try wc.addString(hexstring.toSlice()),
    826                                 .none, .fast, .uuid, .sha1, .md5 => null,
    827                             } else null },
    828                             .filters = .{ .slice = try s.initStringList(c.filters) },
    829                             .exec_cmd_args = .{ .slice = try s.initOptionalStringList(exec_cmd_args) },
    830                             .installed_headers = .initErased(installed_headers),
    831                             .force_undefined_symbols = .{ .slice = try s.initStringList(c.force_undefined_symbols.keys()) },
    832                             .expect_errors = .{ .u = if (c.expect_errors) |x| switch (x) {
    833                                 .contains => |slice| .{ .contains = try wc.addString(slice) },
    834                                 .exact => |exact| .{ .exact = .{ .slice = try s.initStringList(exact) } },
    835                                 .starts_with => |slice| .{ .starts_with = try wc.addString(slice) },
    836                                 .stderr_contains => |slice| .{ .stderr_contains = try wc.addString(slice) },
    837                             } else .none },
    838                             .test_runner = .{ .u = if (c.test_runner) |tr| switch (tr.mode) {
    839                                 .simple => .{ .simple = try s.addLazyPath(tr.path) },
    840                                 .server => .{ .server = try s.addLazyPath(tr.path) },
    841                             } else .default },
    842 
    843                             .emit_directory = .{ .value = c.emit_directory.unwrap() },
    844                             .generated_docs = .{ .value = c.generated_docs.unwrap() },
    845                             .generated_asm = .{ .value = c.generated_asm.unwrap() },
    846                             .generated_bin = .{ .value = c.generated_bin.unwrap() },
    847                             .generated_pdb = .{ .value = c.generated_pdb.unwrap() },
    848                             .generated_implib = .{ .value = c.generated_implib.unwrap() },
    849                             .generated_llvm_bc = .{ .value = c.generated_llvm_bc.unwrap() },
    850                             .generated_llvm_ir = .{ .value = c.generated_llvm_ir.unwrap() },
    851                             .generated_h = .{ .value = c.generated_h.unwrap() },
    852                         });
    853                     },
    854                     .install_artifact => e: {
    855                         const ia: *Step.InstallArtifact = @fieldParentPtr("step", step);
    856                         break :e try wc.addExtraErased(Configuration.Step.InstallArtifact, .{
    857                             .flags = .{
    858                                 .dylib_symlinks = ia.dylib_symlinks,
    859                                 .bin_dir = ia.dest_dir != null,
    860                                 .implib_dir = ia.implib_dir != null,
    861                                 .pdb_dir = ia.pdb_dir != null,
    862                                 .h_dir = ia.h_dir != null,
    863                                 .bin_sub_path = ia.dest_sub_path != null,
    864                             },
    865                             .bin_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.dest_dir) },
    866                             .implib_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.implib_dir) },
    867                             .pdb_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.pdb_dir) },
    868                             .h_dir = .{ .value = try addInstallDirDefaultNull(wc, ia.h_dir) },
    869                             .bin_sub_path = .{ .value = try s.addOptionalString(ia.dest_sub_path) },
    870                         });
    871                     },
    872                     .install_file => e: {
    873                         const sif: *Step.InstallFile = @fieldParentPtr("step", step);
    874                         break :e try wc.addExtraErased(Configuration.Step.InstallFile, .{
    875                             .source = try s.addLazyPath(sif.source),
    876                             .dest_dir = try addInstallDir(wc, sif.dir),
    877                             .dest_sub_path = try wc.addString(sif.dest_rel_path),
    878                         });
    879                     },
    880                     .install_dir => e: {
    881                         const sid: *Step.InstallDir = @fieldParentPtr("step", step);
    882                         const dest_sub_path: ?[]const u8 = if (sid.options.install_subdir.len != 0)
    883                             sid.options.install_subdir
    884                         else
    885                             null;
    886                         const include_extensions = sid.options.include_extensions orelse &.{};
    887                         break :e try wc.addExtraErased(Configuration.Step.InstallDir, .{
    888                             .flags = .{
    889                                 .dest_sub_path = dest_sub_path != null,
    890                                 .exclude_extensions = sid.options.exclude_extensions.len != 0,
    891                                 .include_extensions = include_extensions.len != 0,
    892                                 .include_extensions_active = sid.options.include_extensions != null,
    893                                 .blank_extensions = sid.options.blank_extensions.len != 0,
    894                             },
    895                             .source_dir = try s.addLazyPath(sid.options.source_dir),
    896                             .dest_dir = try addInstallDir(wc, sid.options.install_dir),
    897                             .dest_sub_path = .{ .value = try s.addOptionalString(dest_sub_path) },
    898                             .exclude_extensions = .{ .slice = try s.initStringList(sid.options.exclude_extensions) },
    899                             .include_extensions = .{ .slice = try s.initStringList(include_extensions) },
    900                             .blank_extensions = .{ .slice = try s.initStringList(sid.options.blank_extensions) },
    901                         });
    902                     },
    903                     .fail => e: {
    904                         const sf: *Step.Fail = @fieldParentPtr("step", step);
    905                         break :e try wc.addExtraErased(Configuration.Step.Fail, .{
    906                             .msg = sf.error_msg,
    907                         });
    908                     },
    909                     .find_program => @panic("TODO"),
    910                     .fmt => e: {
    911                         const sf: *Step.Fmt = @fieldParentPtr("step", step);
    912                         break :e try wc.addExtraErased(Configuration.Step.Fmt, .{
    913                             .flags = .{
    914                                 .paths = sf.paths.len != 0,
    915                                 .exclude_paths = sf.exclude_paths.len != 0,
    916                                 .check = sf.check,
    917                             },
    918                             .paths = .{ .slice = try s.initLazyPathList(sf.paths) },
    919                             .exclude_paths = .{ .slice = try s.initLazyPathList(sf.exclude_paths) },
    920                         });
    921                     },
    922                     .translate_c => e: {
    923                         const tc: *Step.TranslateC = @fieldParentPtr("step", step);
    924 
    925                         const system_libs = try arena.alloc(Configuration.SystemLib.Index, tc.system_libs.items.len);
    926                         for (system_libs, tc.system_libs.items) |*dest, *src| dest.* = try s.addSystemLib(src);
    927 
    928                         break :e try wc.addExtraErased(Configuration.Step.TranslateC, .{
    929                             .flags = .{
    930                                 .include_dirs = tc.include_dirs.items.len != 0,
    931                                 .system_libs = system_libs.len != 0,
    932                                 .c_macros = tc.c_macros.items.len != 0,
    933                                 .link_libc = tc.link_libc,
    934                                 .optimize = .init(tc.optimize),
    935                             },
    936                             .src_path = try s.addLazyPath(tc.source),
    937                             .output_file = tc.output_file,
    938                             .include_dirs = .init(try s.initIncludeDirList(tc.include_dirs.items)),
    939                             .system_libs = .{ .slice = system_libs },
    940                             .c_macros = .{ .slice = tc.c_macros.items },
    941                             .target = try addOptionalResolvedTarget(wc, tc.target),
    942                         });
    943                     },
    944                     .write_file => e: {
    945                         const wf: *Step.WriteFile = @fieldParentPtr("step", step);
    946 
    947                         const directories = try arena.alloc(
    948                             Configuration.Step.WriteFile.Directory,
    949                             wf.directories.items.len,
    950                         );
    951                         for (directories, wf.directories.items) |*dest, src| dest.* = .{
    952                             .sub_path = src.sub_path,
    953                             .src_path = try s.addLazyPath(src.src_path),
    954                             .exclude_extensions = src.exclude_extensions,
    955                             .include_extensions = src.include_extensions,
    956                         };
    957 
    958                         break :e try wc.addExtraErased(Configuration.Step.WriteFile, .{
    959                             .flags = .{
    960                                 .embeds = wf.embeds.items.len != 0,
    961                                 .copies = wf.copies.items.len != 0,
    962                                 .directories = directories.len != 0,
    963                                 .mode = switch (wf.mode) {
    964                                     .whole_cached => .whole_cached,
    965                                     .tmp => .tmp,
    966                                     .mutate => .mutate,
    967                                 },
    968                             },
    969                             .generated_directory = wf.generated_directory,
    970                             .embeds = .{ .slice = wf.embeds.items },
    971                             .copies = .{ .slice = try s.initCopyList(wf.copies.items) },
    972                             .directories = .{ .slice = directories },
    973                             .mutate_path = .{ .value = switch (wf.mode) {
    974                                 .mutate => |lp| try s.addLazyPath(lp),
    975                                 .whole_cached, .tmp => null,
    976                             } },
    977                         });
    978                     },
    979                     .update_source_files => e: {
    980                         const usf: *Step.UpdateSourceFiles = @fieldParentPtr("step", step);
    981                         break :e try wc.addExtraErased(Configuration.Step.UpdateSourceFiles, .{
    982                             .flags = .{
    983                                 .embeds = usf.embeds.items.len != 0,
    984                                 .copies = usf.copies.items.len != 0,
    985                             },
    986                             .embeds = .{ .slice = usf.embeds.items },
    987                             .copies = .{ .slice = try s.initCopyList(usf.copies.items) },
    988                         });
    989                     },
    990                     .run => e: {
    991                         const run: *Step.Run = @fieldParentPtr("step", step);
    992                         var expect_stderr_exact: ?Configuration.Bytes = null;
    993                         var expect_stdout_exact: ?Configuration.Bytes = null;
    994                         var expect_stderr_match: std.ArrayList(Configuration.Bytes) = .empty;
    995                         var expect_stdout_match: std.ArrayList(Configuration.Bytes) = .empty;
    996                         var expect_term: ?struct {
    997                             status: Configuration.Step.Run.ExpectTermStatus,
    998                             value: u32,
    999                         } = null;
   1000                         switch (run.stdio) {
   1001                             .check => |checks| for (checks.items) |check| switch (check) {
   1002                                 .expect_stderr_exact => |bytes| expect_stderr_exact = try wc.addBytes(bytes),
   1003                                 .expect_stdout_exact => |bytes| expect_stdout_exact = try wc.addBytes(bytes),
   1004                                 .expect_stderr_match => |bytes| {
   1005                                     try expect_stderr_match.append(arena, try wc.addBytes(bytes));
   1006                                 },
   1007                                 .expect_stdout_match => |bytes| {
   1008                                     try expect_stdout_match.append(arena, try wc.addBytes(bytes));
   1009                                 },
   1010                                 .expect_term => |t| expect_term = switch (t) {
   1011                                     .exited => |x| .{ .status = .exited, .value = x },
   1012                                     .signal => |x| .{ .status = .signal, .value = @intFromEnum(x) },
   1013                                     .stopped => |x| .{ .status = .stopped, .value = @intFromEnum(x) },
   1014                                     .unknown => |x| .{ .status = .unknown, .value = x },
   1015                                 },
   1016                             },
   1017                             else => {},
   1018                         }
   1019 
   1020                         break :e try wc.addExtraErased(Configuration.Step.Run, .{
   1021                             .flags = .{
   1022                                 .disable_zig_progress = run.disable_zig_progress,
   1023                                 .skip_foreign_checks = run.skip_foreign_checks,
   1024                                 .failing_to_execute_foreign_is_an_error = run.failing_to_execute_foreign_is_an_error,
   1025                                 .has_side_effects = run.has_side_effects,
   1026                                 .test_runner_mode = run.test_runner_mode,
   1027                                 .color = run.color,
   1028                                 .stdio = switch (run.stdio) {
   1029                                     .infer_from_args => .infer_from_args,
   1030                                     .inherit => .inherit,
   1031                                     .check => .check,
   1032                                     .zig_test => .zig_test,
   1033                                 },
   1034                                 .stdin = switch (run.stdin) {
   1035                                     .none => .none,
   1036                                     .bytes => .bytes,
   1037                                     .lazy_path => .lazy_path,
   1038                                 },
   1039                                 .stdout_trim_whitespace = if (run.captured_stdout) |cs| cs.trim_whitespace else .none,
   1040                                 .stderr_trim_whitespace = if (run.captured_stderr) |cs| cs.trim_whitespace else .none,
   1041                                 .stdio_limit = run.stdio_limit != .unlimited,
   1042                                 .producer = run.producer != null,
   1043                                 .cwd = run.cwd != null,
   1044                                 .captured_stdout = run.captured_stdout != null,
   1045                                 .captured_stderr = run.captured_stderr != null,
   1046                                 .environ_map = run.environ_map != null,
   1047                             },
   1048                             .flags2 = .{
   1049                                 .expect_stderr_exact = expect_stderr_exact != null,
   1050                                 .expect_stdout_exact = expect_stdout_exact != null,
   1051                                 .expect_stderr_match = expect_stderr_match.items.len != 0,
   1052                                 .expect_stdout_match = expect_stdout_match.items.len != 0,
   1053                                 .expect_term = expect_term != null,
   1054                                 .expect_term_status = if (expect_term) |t| t.status else .exited,
   1055                             },
   1056                             .file_inputs = .{ .slice = try s.initLazyPathList(run.file_inputs.items) },
   1057                             .args = .{ .slice = try s.initArgsList(run.argv.items) },
   1058                             .cwd = .{ .value = try s.addOptionalLazyPath(run.cwd) },
   1059                             .captured_stdout = .{ .value = if (run.captured_stdout) |cs| .{
   1060                                 .basename = try wc.addString(cs.output.basename),
   1061                                 .generated_file = cs.output.generated_file,
   1062                             } else null },
   1063                             .captured_stderr = .{ .value = if (run.captured_stderr) |cs| .{
   1064                                 .basename = try wc.addString(cs.output.basename),
   1065                                 .generated_file = cs.output.generated_file,
   1066                             } else null },
   1067                             .environ_map = .{ .value = try s.addEnvironMap(run.environ_map) },
   1068                             .expect_term_value = .{ .value = if (expect_term) |t| t.value else null },
   1069                             .stdio_limit = .{ .value = run.stdio_limit.toInt() },
   1070                             .producer = .{ .value = if (run.producer) |cs| s.stepIndex(&cs.step) else null },
   1071                             .expect_stderr_exact = .{ .value = if (expect_stderr_exact) |bytes| bytes else null },
   1072                             .expect_stdout_exact = .{ .value = if (expect_stdout_exact) |bytes| bytes else null },
   1073                             .expect_stderr_match = .{ .slice = expect_stderr_match.items },
   1074                             .expect_stdout_match = .{ .slice = expect_stdout_match.items },
   1075                             .stdin = .{ .u = switch (run.stdin) {
   1076                                 .none => .none,
   1077                                 .bytes => |bytes| .{ .bytes = try wc.addBytes(bytes) },
   1078                                 .lazy_path => |lp| .{ .lazy_path = try s.addLazyPath(lp) },
   1079                             } },
   1080                         });
   1081                     },
   1082                     .check_file => e: {
   1083                         const cf: *Step.CheckFile = @fieldParentPtr("step", step);
   1084                         break :e try wc.addExtraErased(Configuration.Step.CheckFile, .{
   1085                             .flags = .{
   1086                                 .expected_exact = cf.expected_exact != null,
   1087                                 .expected_matches = cf.expected_matches.len != 0,
   1088                                 .max_bytes = cf.max_bytes != null,
   1089                             },
   1090                             .file = try s.addLazyPath(cf.file),
   1091                             .expected_exact = .{ .value = cf.expected_exact },
   1092                             .expected_matches = .{ .slice = cf.expected_matches },
   1093                             .max_bytes = .{ .value = cf.max_bytes },
   1094                         });
   1095                     },
   1096                     .config_header => e: {
   1097                         const ch: *Step.ConfigHeader = @fieldParentPtr("step", step);
   1098                         const lazy_path: ?std.Build.LazyPath = ch.style.getPath();
   1099                         const pairs = try arena.alloc(Configuration.Step.ConfigHeader.Value.Pair, ch.values.count());
   1100                         for (pairs, ch.values.keys(), ch.values.values()) |*pair, key, value| pair.* = .{
   1101                             .key = try wc.addString(key),
   1102                             .index = switch (value) {
   1103                                 .undef => .undef,
   1104                                 .defined => .defined,
   1105                                 .boolean => |x| switch (x) {
   1106                                     false => .bool_false,
   1107                                     true => .bool_true,
   1108                                 },
   1109                                 .int => |x| switch (x) {
   1110                                     0 => .int_0,
   1111                                     1 => .int_1,
   1112                                     else => try wc.addExtra(Configuration.Step.ConfigHeader.Value, .initSigned(x)),
   1113                                 },
   1114                                 .ident => |x| try wc.addExtra(Configuration.Step.ConfigHeader.Value, .{
   1115                                     .flags = .{
   1116                                         .tag = .ident,
   1117                                         .small = 0,
   1118                                     },
   1119                                     .i64 = .{ .value = null },
   1120                                     .u64 = .{ .value = null },
   1121                                     .ident = .{ .value = try wc.addString(x) },
   1122                                     .string = .{ .value = null },
   1123                                 }),
   1124                                 .string => |x| try wc.addExtra(Configuration.Step.ConfigHeader.Value, .{
   1125                                     .flags = .{
   1126                                         .tag = .string,
   1127                                         .small = 0,
   1128                                     },
   1129                                     .i64 = .{ .value = null },
   1130                                     .u64 = .{ .value = null },
   1131                                     .ident = .{ .value = null },
   1132                                     .string = .{ .value = try wc.addString(x) },
   1133                                 }),
   1134                             },
   1135                         };
   1136                         break :e try wc.addExtraErased(Configuration.Step.ConfigHeader, .{
   1137                             .flags = .{
   1138                                 .template_file = lazy_path != null,
   1139                                 .style = .init(ch.style),
   1140                                 .input_size_limit = ch.input_size_limit != null,
   1141                                 .include_guard = ch.include_guard != .none,
   1142                             },
   1143                             .template_file = .{ .value = try s.addOptionalLazyPath(lazy_path) },
   1144                             .generated_dir = ch.generated_dir,
   1145                             .input_size_limit = .{ .value = ch.input_size_limit },
   1146                             .include_path = try wc.addString(ch.include_path),
   1147                             .include_guard = .{ .value = ch.include_guard.unwrap() },
   1148                             .values = .{ .slice = pairs },
   1149                         });
   1150                     },
   1151                     .obj_copy => e: {
   1152                         const oc: *Step.ObjCopy = @fieldParentPtr("step", step);
   1153 
   1154                         const debug_basename: ?Configuration.String = if (oc.debug_file) |df|
   1155                             df.basename.unwrap()
   1156                         else
   1157                             null;
   1158 
   1159                         const debug_file: ?Configuration.GeneratedFileIndex = if (oc.debug_file) |df|
   1160                             df.output_file
   1161                         else
   1162                             null;
   1163 
   1164                         const add_sections = try arena.alloc(
   1165                             Configuration.Step.ObjCopy.AddSection,
   1166                             oc.add_sections.items.len,
   1167                         );
   1168                         for (add_sections, oc.add_sections.items) |*dest, src| dest.* = .{
   1169                             .section_name = src.section_name,
   1170                             .file_path = try s.addLazyPath(src.file_path),
   1171                         };
   1172 
   1173                         break :e try wc.addExtraErased(Configuration.Step.ObjCopy, .{
   1174                             .flags = .{
   1175                                 .basename = oc.basename != .none,
   1176                                 .debug_file = debug_file != null,
   1177                                 .debug_basename = debug_basename != null,
   1178                                 .format = .init(oc.format),
   1179                                 .strip = oc.strip,
   1180                                 .compress_debug = oc.compress_debug,
   1181                                 .only_section = oc.only_section != .none,
   1182                                 .pad_to = oc.pad_to != null,
   1183                                 .add_section = add_sections.len != 0,
   1184                                 .update_section = oc.update_sections.items.len != 0,
   1185                             },
   1186                             .input_file = try s.addLazyPath(oc.input_file),
   1187                             .output_file = oc.output_file,
   1188                             .basename = .{ .value = oc.basename.unwrap() },
   1189                             .debug_file = .{ .value = debug_file },
   1190                             .debug_basename = .{ .value = debug_basename },
   1191                             .only_section = .{ .value = oc.only_section.unwrap() },
   1192                             .pad_to = .{ .value = oc.pad_to },
   1193                             .add_section = .{ .slice = add_sections },
   1194                             .update_section = .{ .slice = oc.update_sections.items },
   1195                         });
   1196                     },
   1197                     .options => e: {
   1198                         const so: *Step.Options = @fieldParentPtr("step", step);
   1199 
   1200                         const args = try arena.alloc(Configuration.Step.Options.Arg, so.args.items.len);
   1201                         for (args, so.args.items) |*dest, src| dest.* = .{
   1202                             .name = src.name,
   1203                             .path = try s.addLazyPath(src.path),
   1204                         };
   1205 
   1206                         break :e try wc.addExtraErased(Configuration.Step.Options, .{
   1207                             .flags = .{
   1208                                 .args = so.args.items.len != 0,
   1209                             },
   1210                             .generated_file = so.generated_file,
   1211                             .contents = try wc.addBytes(so.contents.items),
   1212                             .args = .{ .slice = args },
   1213                         });
   1214                     },
   1215                 }),
   1216             });
   1217         }
   1218     }
   1219 
   1220     try wc.unlazy_deps.ensureUnusedCapacity(gpa, graph.needed_lazy_dependencies.keys().len);
   1221     for (graph.needed_lazy_dependencies.keys()) |k| {
   1222         wc.unlazy_deps.appendAssumeCapacity(try wc.addString(k));
   1223     }
   1224 
   1225     try wc.write(writer, .{
   1226         .default_step = s.stepIndex(b.default_step),
   1227         .generated_files_len = @intCast(graph.generated_files.items.len),
   1228         .poisoned = switch (graph.cache_poison) {
   1229             .pure, .disallowed, .ignored => false,
   1230             .poisoned => true,
   1231         },
   1232     });
   1233 }
   1234 
   1235 fn addOptionalResolvedTarget(
   1236     wc: *Configuration.Wip,
   1237     optional_resolved_target: ?std.Build.ResolvedTarget,
   1238 ) !Configuration.ResolvedTarget.OptionalIndex {
   1239     const resolved_target = optional_resolved_target orelse return .none;
   1240     return .init(try wc.addDeduped(Configuration.ResolvedTarget, .{
   1241         .query = try wc.addTargetQuery(&resolved_target.query),
   1242         .result = try wc.addTarget(resolved_target.result),
   1243     }));
   1244 }
   1245 
   1246 fn addInstallDir(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !Configuration.InstallDestDir {
   1247     switch (install_dir orelse return .none) {
   1248         .prefix => return .prefix,
   1249         .lib => return .lib,
   1250         .bin => return .bin,
   1251         .header => return .header,
   1252         .custom => |sub_path| return .initCustom(try wc.addString(sub_path)),
   1253     }
   1254 }
   1255 
   1256 fn addInstallDirDefaultNull(wc: *Configuration.Wip, install_dir: ?std.Build.InstallDir) !?Configuration.InstallDestDir {
   1257     return try addInstallDir(wc, install_dir orelse return null);
   1258 }
   1259 
   1260 /// If the given `Step` is a `Step.Compile`, adds any dependencies for that step which
   1261 /// are implied by the module graph rooted at `step.cast(Step.Compile).?.root_module`.
   1262 fn createModuleDependenciesForStep(step: *Step) Allocator.Error!void {
   1263     const root_module = if (step.cast(Step.Compile)) |cs| root: {
   1264         break :root cs.root_module;
   1265     } else return; // not a compile step so no module dependencies
   1266 
   1267     // Starting from `root_module`, discover all modules in this graph.
   1268     const modules = root_module.getGraph().modules;
   1269 
   1270     // For each of those modules, set up the implied step dependencies.
   1271     for (modules) |mod| {
   1272         if (mod.root_source_file) |lp| lp.addStepDependencies(step);
   1273         for (mod.include_dirs.items) |include_dir| switch (include_dir) {
   1274             .path,
   1275             .path_system,
   1276             .path_after,
   1277             .framework_path,
   1278             .framework_path_system,
   1279             .embed_path,
   1280             => |lp| lp.addStepDependencies(step),
   1281 
   1282             .other_step => |other| {
   1283                 other.getEmittedIncludeTree().addStepDependencies(step);
   1284                 step.dependOn(&other.step);
   1285             },
   1286 
   1287             .config_header_step => |other| step.dependOn(&other.step),
   1288         };
   1289         for (mod.lib_paths.items) |lp| lp.addStepDependencies(step);
   1290         for (mod.rpaths.items) |rpath| switch (rpath) {
   1291             .lazy_path => |lp| lp.addStepDependencies(step),
   1292             .special => {},
   1293         };
   1294         for (mod.link_objects.items) |link_object| switch (link_object) {
   1295             .static_path,
   1296             .assembly_file,
   1297             => |lp| lp.addStepDependencies(step),
   1298             .other_step => |other| step.dependOn(&other.step),
   1299             .system_lib => {},
   1300             .c_source_file => |source| source.file.addStepDependencies(step),
   1301             .c_source_files => |source_files| source_files.root.addStepDependencies(step),
   1302             .win32_resource_file => |rc_source| {
   1303                 rc_source.file.addStepDependencies(step);
   1304                 for (rc_source.include_paths) |lp| lp.addStepDependencies(step);
   1305             },
   1306         };
   1307     }
   1308 }
   1309 
   1310 fn nextArg(args: []const [:0]const u8, idx: *usize) ?[:0]const u8 {
   1311     if (idx.* >= args.len) return null;
   1312     defer idx.* += 1;
   1313     return args[idx.*];
   1314 }
   1315 
   1316 fn expectArgOrFatal(args: []const [:0]const u8, index_ptr: *usize, first: []const u8) []const u8 {
   1317     const next_arg = nextArg(args, index_ptr) orelse fatal("missing {q} argument", .{first});
   1318     if (!mem.eql(u8, first, next_arg)) fatal("expected {q} instead of {q}", .{ first, next_arg });
   1319     const arg = nextArg(args, index_ptr) orelse fatal("expected argument after {q}", .{first});
   1320     return arg;
   1321 }
   1322 
   1323 const ErrorStyle = enum {
   1324     verbose,
   1325     minimal,
   1326     verbose_clear,
   1327     minimal_clear,
   1328     fn verboseContext(s: ErrorStyle) bool {
   1329         return switch (s) {
   1330             .verbose, .verbose_clear => true,
   1331             .minimal, .minimal_clear => false,
   1332         };
   1333     }
   1334     fn clearOnUpdate(s: ErrorStyle) bool {
   1335         return switch (s) {
   1336             .verbose, .minimal => false,
   1337             .verbose_clear, .minimal_clear => true,
   1338         };
   1339     }
   1340 };
   1341 const MultilineErrors = enum { indent, newline, none };
   1342 const Summary = enum { all, new, failures, line, none };
   1343 
   1344 fn fatalWithHint(comptime f: []const u8, args: anytype) noreturn {
   1345     log.info("to access the help menu: zig build -h", .{});
   1346     fatal(f, args);
   1347 }
   1348 
   1349 fn serializeSystemIntegrationOptions(graph: *std.Build.Graph, wc: *Configuration.Wip) Allocator.Error!void {
   1350     const gpa = wc.gpa;
   1351 
   1352     var bad = false;
   1353     try wc.system_integrations.ensureTotalCapacityPrecise(gpa, graph.system_integration_options.entries.len);
   1354     for (graph.system_integration_options.keys(), graph.system_integration_options.values()) |k, v| {
   1355         wc.system_integrations.appendAssumeCapacity(.{
   1356             .name = try wc.addString(k),
   1357             .status = switch (v) {
   1358                 .user_disabled, .user_enabled => x: {
   1359                     // The user tried to enable or disable a system library integration, but
   1360                     // the configure script did not recognize that option.
   1361                     log.err("system integration name not recognized by configure script: {s}", .{k});
   1362                     bad = true;
   1363                     break :x .disabled;
   1364                 },
   1365                 .declared_disabled => .disabled,
   1366                 .declared_enabled => .enabled,
   1367             },
   1368         });
   1369     }
   1370     if (bad) {
   1371         log.info("help menu contains available options: zig build -h", .{});
   1372         process.exit(1);
   1373     }
   1374 }
   1375 
   1376 fn serializePackageOptions(b: *std.Build, wc: *Configuration.Wip) Allocator.Error!void {
   1377     const gpa = wc.gpa;
   1378 
   1379     try wc.available_options.ensureTotalCapacityPrecise(gpa, b.available_options_map.count());
   1380     for (b.available_options_map.keys(), b.available_options_map.values()) |name, *opt| {
   1381         wc.available_options.appendAssumeCapacity(.{
   1382             .name = try wc.addString(name),
   1383             .description = try wc.addString(opt.description),
   1384             .type = opt.type_id,
   1385             .enum_options = if (opt.enum_options) |enum_vals| .init(try wc.addStringList(enum_vals)) else .none,
   1386         });
   1387     }
   1388 }