master
   1const builtin = @import("builtin");
   2const native_os = builtin.os.tag;
   3
   4const std = @import("std");
   5const Io = std.Io;
   6const assert = std.debug.assert;
   7const fs = std.fs;
   8const mem = std.mem;
   9const process = std.process;
  10const Allocator = mem.Allocator;
  11const Ast = std.zig.Ast;
  12const Color = std.zig.Color;
  13const warn = std.log.warn;
  14const ThreadPool = std.Thread.Pool;
  15const cleanExit = std.process.cleanExit;
  16const Cache = std.Build.Cache;
  17const Path = std.Build.Cache.Path;
  18const Directory = std.Build.Cache.Directory;
  19const EnvVar = std.zig.EnvVar;
  20const LibCInstallation = std.zig.LibCInstallation;
  21const AstGen = std.zig.AstGen;
  22const ZonGen = std.zig.ZonGen;
  23const Server = std.zig.Server;
  24
  25const tracy = @import("tracy.zig");
  26const Compilation = @import("Compilation.zig");
  27const link = @import("link.zig");
  28const Package = @import("Package.zig");
  29const build_options = @import("build_options");
  30const introspect = @import("introspect.zig");
  31const wasi_libc = @import("libs/wasi_libc.zig");
  32const target_util = @import("target.zig");
  33const crash_report = @import("crash_report.zig");
  34const Zcu = @import("Zcu.zig");
  35const mingw = @import("libs/mingw.zig");
  36const dev = @import("dev.zig");
  37
  38test {
  39    _ = Package;
  40    _ = @import("codegen.zig");
  41}
  42
  43const thread_stack_size = 60 << 20;
  44
  45pub const std_options: std.Options = .{
  46    .wasiCwd = wasi_cwd,
  47    .logFn = log,
  48
  49    .log_level = switch (builtin.mode) {
  50        .Debug => .debug,
  51        .ReleaseSafe, .ReleaseFast => .info,
  52        .ReleaseSmall => .err,
  53    },
  54};
  55
  56pub const panic = crash_report.panic;
  57pub const debug = crash_report.debug;
  58
  59var wasi_preopens: fs.wasi.Preopens = undefined;
  60pub fn wasi_cwd() std.os.wasi.fd_t {
  61    // Expect the first preopen to be current working directory.
  62    const cwd_fd: std.posix.fd_t = 3;
  63    assert(mem.eql(u8, wasi_preopens.names[cwd_fd], "."));
  64    return cwd_fd;
  65}
  66
  67const fatal = std.process.fatal;
  68
  69/// This can be global since stdin is a singleton.
  70var stdin_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
  71/// This can be global since stdout is a singleton.
  72var stdout_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
  73
  74/// Shaming all the locations that inappropriately use an O(N) search algorithm.
  75/// Please delete this and fix the compilation errors!
  76pub const @"bad O(N)" = void;
  77
  78const normal_usage =
  79    \\Usage: zig [command] [options]
  80    \\
  81    \\Commands:
  82    \\
  83    \\  build            Build project from build.zig
  84    \\  fetch            Copy a package into global cache and print its hash
  85    \\  init             Initialize a Zig package in the current directory
  86    \\
  87    \\  build-exe        Create executable from source or object files
  88    \\  build-lib        Create library from source or object files
  89    \\  build-obj        Create object from source or object files
  90    \\  test             Perform unit testing
  91    \\  test-obj         Create object for unit testing
  92    \\  run              Create executable and run immediately
  93    \\
  94    \\  ast-check        Look for simple compile errors in any set of files
  95    \\  fmt              Reformat Zig source into canonical form
  96    \\  reduce           Minimize a bug report
  97    \\  translate-c      Convert C code to Zig code
  98    \\
  99    \\  ar               Use Zig as a drop-in archiver
 100    \\  cc               Use Zig as a drop-in C compiler
 101    \\  c++              Use Zig as a drop-in C++ compiler
 102    \\  dlltool          Use Zig as a drop-in dlltool.exe
 103    \\  lib              Use Zig as a drop-in lib.exe
 104    \\  ranlib           Use Zig as a drop-in ranlib
 105    \\  objcopy          Use Zig as a drop-in objcopy
 106    \\  rc               Use Zig as a drop-in rc.exe
 107    \\
 108    \\  env              Print lib path, std path, cache directory, and version
 109    \\  help             Print this help and exit
 110    \\  std              View standard library documentation in a browser
 111    \\  libc             Display native libc paths file or validate one
 112    \\  targets          List available compilation targets
 113    \\  version          Print version number and exit
 114    \\  zen              Print Zen of Zig and exit
 115    \\
 116    \\General Options:
 117    \\
 118    \\  -h, --help       Print command-specific usage
 119    \\
 120;
 121
 122const debug_usage = normal_usage ++
 123    \\
 124    \\Debug Commands:
 125    \\
 126    \\  changelist       Compute mappings from old ZIR to new ZIR
 127    \\  dump-zir         Dump a file containing cached ZIR
 128    \\  detect-cpu       Compare Zig's CPU feature detection vs LLVM
 129    \\  llvm-ints        Dump a list of LLVMABIAlignmentOfType for all integers
 130    \\
 131;
 132
 133const usage = if (build_options.enable_debug_extensions) debug_usage else normal_usage;
 134
 135var log_scopes: std.ArrayList([]const u8) = .empty;
 136
 137pub fn log(
 138    comptime level: std.log.Level,
 139    comptime scope: @EnumLiteral(),
 140    comptime format: []const u8,
 141    args: anytype,
 142) void {
 143    // Hide debug messages unless:
 144    // * logging enabled with `-Dlog`.
 145    // * the --debug-log arg for the scope has been provided
 146    if (@intFromEnum(level) > @intFromEnum(std.options.log_level) or
 147        @intFromEnum(level) > @intFromEnum(std.log.Level.info))
 148    {
 149        if (!build_options.enable_logging) return;
 150
 151        const scope_name = @tagName(scope);
 152        for (log_scopes.items) |log_scope| {
 153            if (mem.eql(u8, log_scope, scope_name))
 154                break;
 155        } else return;
 156    }
 157
 158    // Otherwise, use the default implementation.
 159    std.log.defaultLog(level, scope, format, args);
 160}
 161
 162var debug_allocator: std.heap.DebugAllocator(.{
 163    .stack_trace_frames = build_options.mem_leak_frames,
 164}) = .init;
 165
 166pub fn main() anyerror!void {
 167    const gpa, const is_debug = gpa: {
 168        if (build_options.debug_gpa) break :gpa .{ debug_allocator.allocator(), true };
 169        if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false };
 170        if (builtin.link_libc) {
 171            // We would prefer to use raw libc allocator here, but cannot use
 172            // it if it won't support the alignment we need.
 173            if (@alignOf(std.c.max_align_t) < @max(@alignOf(i128), std.atomic.cache_line)) {
 174                break :gpa .{ std.heap.c_allocator, false };
 175            }
 176            break :gpa .{ std.heap.raw_c_allocator, false };
 177        }
 178        break :gpa switch (builtin.mode) {
 179            .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
 180            .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },
 181        };
 182    };
 183    defer if (is_debug) {
 184        _ = debug_allocator.deinit();
 185    };
 186    var arena_instance = std.heap.ArenaAllocator.init(gpa);
 187    defer arena_instance.deinit();
 188    const arena = arena_instance.allocator();
 189
 190    const args = try process.argsAlloc(arena);
 191
 192    if (args.len > 0) crash_report.zig_argv0 = args[0];
 193
 194    if (tracy.enable_allocation) {
 195        var gpa_tracy = tracy.tracyAllocator(gpa);
 196        return mainArgs(gpa_tracy.allocator(), arena, args);
 197    }
 198
 199    if (native_os == .wasi) {
 200        wasi_preopens = try fs.wasi.preopensAlloc(arena);
 201    }
 202
 203    return mainArgs(gpa, arena, args);
 204}
 205
 206fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
 207    const tr = tracy.trace(@src());
 208    defer tr.end();
 209
 210    if (args.len <= 1) {
 211        std.log.info("{s}", .{usage});
 212        fatal("expected command argument", .{});
 213    }
 214
 215    if (process.can_execv and std.posix.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) {
 216        dev.check(.cc_command);
 217        // In this case we have accidentally invoked ourselves as "the system C compiler"
 218        // to figure out where libc is installed. This is essentially infinite recursion
 219        // via child process execution due to the CC environment variable pointing to Zig.
 220        // Here we ignore the CC environment variable and exec `cc` as a child process.
 221        // However it's possible Zig is installed as *that* C compiler as well, which is
 222        // why we have this additional environment variable here to check.
 223        var env_map = try process.getEnvMap(arena);
 224
 225        const inf_loop_env_key = "ZIG_IS_TRYING_TO_NOT_CALL_ITSELF";
 226        if (env_map.get(inf_loop_env_key) != null) {
 227            fatal("The compilation links against libc, but Zig is unable to provide a libc " ++
 228                "for this operating system, and no --libc " ++
 229                "parameter was provided, so Zig attempted to invoke the system C compiler " ++
 230                "in order to determine where libc is installed. However the system C " ++
 231                "compiler is `zig cc`, so no libc installation was found.", .{});
 232        }
 233        try env_map.put(inf_loop_env_key, "1");
 234
 235        // Some programs such as CMake will strip the `cc` and subsequent args from the
 236        // CC environment variable. We detect and support this scenario here because of
 237        // the ZIG_IS_DETECTING_LIBC_PATHS environment variable.
 238        if (mem.eql(u8, args[1], "cc")) {
 239            return process.execve(arena, args[1..], &env_map);
 240        } else {
 241            const modified_args = try arena.dupe([]const u8, args);
 242            modified_args[0] = "cc";
 243            return process.execve(arena, modified_args, &env_map);
 244        }
 245    }
 246
 247    var threaded: Io.Threaded = .init(gpa);
 248    defer threaded.deinit();
 249    const io = threaded.io();
 250
 251    const cmd = args[1];
 252    const cmd_args = args[2..];
 253    if (mem.eql(u8, cmd, "build-exe")) {
 254        dev.check(.build_exe_command);
 255        return buildOutputType(gpa, arena, io, args, .{ .build = .Exe });
 256    } else if (mem.eql(u8, cmd, "build-lib")) {
 257        dev.check(.build_lib_command);
 258        return buildOutputType(gpa, arena, io, args, .{ .build = .Lib });
 259    } else if (mem.eql(u8, cmd, "build-obj")) {
 260        dev.check(.build_obj_command);
 261        return buildOutputType(gpa, arena, io, args, .{ .build = .Obj });
 262    } else if (mem.eql(u8, cmd, "test")) {
 263        dev.check(.test_command);
 264        return buildOutputType(gpa, arena, io, args, .zig_test);
 265    } else if (mem.eql(u8, cmd, "test-obj")) {
 266        dev.check(.test_command);
 267        return buildOutputType(gpa, arena, io, args, .zig_test_obj);
 268    } else if (mem.eql(u8, cmd, "run")) {
 269        dev.check(.run_command);
 270        return buildOutputType(gpa, arena, io, args, .run);
 271    } else if (mem.eql(u8, cmd, "dlltool") or
 272        mem.eql(u8, cmd, "ranlib") or
 273        mem.eql(u8, cmd, "lib") or
 274        mem.eql(u8, cmd, "ar"))
 275    {
 276        dev.check(.ar_command);
 277        return process.exit(try llvmArMain(arena, args));
 278    } else if (mem.eql(u8, cmd, "build")) {
 279        dev.check(.build_command);
 280        return cmdBuild(gpa, arena, io, cmd_args);
 281    } else if (mem.eql(u8, cmd, "clang") or
 282        mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
 283    {
 284        dev.check(.clang_command);
 285        return process.exit(try clangMain(arena, args));
 286    } else if (mem.eql(u8, cmd, "ld.lld") or
 287        mem.eql(u8, cmd, "lld-link") or
 288        mem.eql(u8, cmd, "wasm-ld"))
 289    {
 290        dev.check(.lld_linker);
 291        return process.exit(try lldMain(arena, args, true));
 292    } else if (mem.eql(u8, cmd, "cc")) {
 293        dev.check(.cc_command);
 294        return buildOutputType(gpa, arena, io, args, .cc);
 295    } else if (mem.eql(u8, cmd, "c++")) {
 296        dev.check(.cc_command);
 297        return buildOutputType(gpa, arena, io, args, .cpp);
 298    } else if (mem.eql(u8, cmd, "translate-c")) {
 299        dev.check(.translate_c_command);
 300        return buildOutputType(gpa, arena, io, args, .translate_c);
 301    } else if (mem.eql(u8, cmd, "rc")) {
 302        const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration");
 303        return jitCmd(gpa, arena, io, cmd_args, .{
 304            .cmd_name = "resinator",
 305            .root_src_path = "resinator/main.zig",
 306            .depend_on_aro = true,
 307            .prepend_zig_lib_dir_path = true,
 308            .server = use_server,
 309        });
 310    } else if (mem.eql(u8, cmd, "fmt")) {
 311        dev.check(.fmt_command);
 312        return @import("fmt.zig").run(gpa, arena, io, cmd_args);
 313    } else if (mem.eql(u8, cmd, "objcopy")) {
 314        return jitCmd(gpa, arena, io, cmd_args, .{
 315            .cmd_name = "objcopy",
 316            .root_src_path = "objcopy.zig",
 317        });
 318    } else if (mem.eql(u8, cmd, "fetch")) {
 319        return cmdFetch(gpa, arena, io, cmd_args);
 320    } else if (mem.eql(u8, cmd, "libc")) {
 321        return jitCmd(gpa, arena, io, cmd_args, .{
 322            .cmd_name = "libc",
 323            .root_src_path = "libc.zig",
 324            .prepend_zig_lib_dir_path = true,
 325        });
 326    } else if (mem.eql(u8, cmd, "std")) {
 327        return jitCmd(gpa, arena, io, cmd_args, .{
 328            .cmd_name = "std",
 329            .root_src_path = "std-docs.zig",
 330            .prepend_zig_lib_dir_path = true,
 331            .prepend_zig_exe_path = true,
 332            .prepend_global_cache_path = true,
 333        });
 334    } else if (mem.eql(u8, cmd, "init")) {
 335        return cmdInit(gpa, arena, cmd_args);
 336    } else if (mem.eql(u8, cmd, "targets")) {
 337        dev.check(.targets_command);
 338        const host = std.zig.resolveTargetQueryOrFatal(io, .{});
 339        var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
 340        try @import("print_targets.zig").cmdTargets(arena, cmd_args, &stdout_writer.interface, &host);
 341        return stdout_writer.interface.flush();
 342    } else if (mem.eql(u8, cmd, "version")) {
 343        dev.check(.version_command);
 344        try fs.File.stdout().writeAll(build_options.version ++ "\n");
 345        return;
 346    } else if (mem.eql(u8, cmd, "env")) {
 347        dev.check(.env_command);
 348        const host = std.zig.resolveTargetQueryOrFatal(io, .{});
 349        var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
 350        try @import("print_env.zig").cmdEnv(
 351            arena,
 352            &stdout_writer.interface,
 353            args,
 354            if (native_os == .wasi) wasi_preopens,
 355            &host,
 356        );
 357        return stdout_writer.interface.flush();
 358    } else if (mem.eql(u8, cmd, "reduce")) {
 359        return jitCmd(gpa, arena, io, cmd_args, .{
 360            .cmd_name = "reduce",
 361            .root_src_path = "reduce.zig",
 362        });
 363    } else if (mem.eql(u8, cmd, "zen")) {
 364        dev.check(.zen_command);
 365        return fs.File.stdout().writeAll(info_zen);
 366    } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
 367        dev.check(.help_command);
 368        return fs.File.stdout().writeAll(usage);
 369    } else if (mem.eql(u8, cmd, "ast-check")) {
 370        return cmdAstCheck(arena, io, cmd_args);
 371    } else if (mem.eql(u8, cmd, "detect-cpu")) {
 372        return cmdDetectCpu(io, cmd_args);
 373    } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "changelist")) {
 374        return cmdChangelist(arena, io, cmd_args);
 375    } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "dump-zir")) {
 376        return cmdDumpZir(arena, io, cmd_args);
 377    } else if (build_options.enable_debug_extensions and mem.eql(u8, cmd, "llvm-ints")) {
 378        return cmdDumpLlvmInts(gpa, arena, cmd_args);
 379    } else {
 380        std.log.info("{s}", .{usage});
 381        fatal("unknown command: {s}", .{args[1]});
 382    }
 383}
 384
 385const usage_build_generic =
 386    \\Usage: zig build-exe   [options] [files]
 387    \\       zig build-lib   [options] [files]
 388    \\       zig build-obj   [options] [files]
 389    \\       zig test        [options] [files]
 390    \\       zig run         [options] [files] [-- [args]]
 391    \\       zig translate-c [options] [file]
 392    \\
 393    \\Supported file types:
 394    \\                         .zig    Zig source code
 395    \\                           .o    ELF object file
 396    \\                           .o    Mach-O (macOS) object file
 397    \\                           .o    WebAssembly object file
 398    \\                         .obj    COFF (Windows) object file
 399    \\                         .lib    COFF (Windows) static library
 400    \\                           .a    ELF static library
 401    \\                           .a    Mach-O (macOS) static library
 402    \\                           .a    WebAssembly static library
 403    \\                          .so    ELF shared object (dynamic link)
 404    \\                         .dll    Windows Dynamic Link Library
 405    \\                       .dylib    Mach-O (macOS) dynamic library
 406    \\                         .tbd    (macOS) text-based dylib definition
 407    \\                           .s    Target-specific assembly source code
 408    \\                           .S    Assembly with C preprocessor (requires LLVM extensions)
 409    \\                           .c    C source code (requires LLVM extensions)
 410    \\        .cxx .cc .C .cpp .c++    C++ source code (requires LLVM extensions)
 411    \\                           .m    Objective-C source code (requires LLVM extensions)
 412    \\                          .mm    Objective-C++ source code (requires LLVM extensions)
 413    \\                          .bc    LLVM IR Module (requires LLVM extensions)
 414    \\
 415    \\General Options:
 416    \\  -h, --help                Print this help and exit
 417    \\  --color [auto|off|on]     Enable or disable colored error messages
 418    \\  -j<N>                     Limit concurrent jobs (default is to use all CPU cores)
 419    \\  -fincremental             Enable incremental compilation
 420    \\  -fno-incremental          Disable incremental compilation
 421    \\  -femit-bin[=path]         (default) Output machine code
 422    \\  -fno-emit-bin             Do not output machine code
 423    \\  -femit-asm[=path]         Output .s (assembly code)
 424    \\  -fno-emit-asm             (default) Do not output .s (assembly code)
 425    \\  -femit-llvm-ir[=path]     Produce a .ll file with optimized LLVM IR (requires LLVM extensions)
 426    \\  -fno-emit-llvm-ir         (default) Do not produce a .ll file with optimized LLVM IR
 427    \\  -femit-llvm-bc[=path]     Produce an optimized LLVM module as a .bc file (requires LLVM extensions)
 428    \\  -fno-emit-llvm-bc         (default) Do not produce an optimized LLVM module as a .bc file
 429    \\  -femit-h[=path]           Generate a C header file (.h)
 430    \\  -fno-emit-h               (default) Do not generate a C header file (.h)
 431    \\  -femit-docs[=path]        Create a docs/ dir with html documentation
 432    \\  -fno-emit-docs            (default) Do not produce docs/ dir with html documentation
 433    \\  -femit-implib[=path]      (default) Produce an import .lib when building a Windows DLL
 434    \\  -fno-emit-implib          Do not produce an import .lib when building a Windows DLL
 435    \\  --show-builtin            Output the source of @import("builtin") then exit
 436    \\  --cache-dir [path]        Override the local cache directory
 437    \\  --global-cache-dir [path] Override the global cache directory
 438    \\  --zig-lib-dir [path]      Override path to Zig installation lib directory
 439    \\
 440    \\Global Compile Options:
 441    \\  --name [name]             Compilation unit name (not a file path)
 442    \\  --libc [file]             Provide a file which specifies libc paths
 443    \\  -x language               Treat subsequent input files as having type <language>
 444    \\  --dep [[import=]name]     Add an entry to the next module's import table
 445    \\  -M[name][=src]            Create a module based on the current per-module settings.
 446    \\                            The first module is the main module.
 447    \\                            "std" can be configured by omitting src
 448    \\                            After a -M argument, per-module settings are reset.
 449    \\  --error-limit [num]       Set the maximum amount of distinct error values
 450    \\  -fllvm                    Force using LLVM as the codegen backend
 451    \\  -fno-llvm                 Prevent using LLVM as the codegen backend
 452    \\  -flibllvm                 Force using the LLVM API in the codegen backend
 453    \\  -fno-libllvm              Prevent using the LLVM API in the codegen backend
 454    \\  -fclang                   Force using Clang as the C/C++ compilation backend
 455    \\  -fno-clang                Prevent using Clang as the C/C++ compilation backend
 456    \\  -fPIE                     Force-enable Position Independent Executable
 457    \\  -fno-PIE                  Force-disable Position Independent Executable
 458    \\  -flto                     Force-enable Link Time Optimization (requires LLVM extensions)
 459    \\  -fno-lto                  Force-disable Link Time Optimization
 460    \\  -fdll-export-fns          Mark exported functions as DLL exports (Windows)
 461    \\  -fno-dll-export-fns       Force-disable marking exported functions as DLL exports
 462    \\  -freference-trace[=num]   Show num lines of reference trace per compile error
 463    \\  -fno-reference-trace      Disable reference trace
 464    \\  -ffunction-sections       Places each function in a separate section
 465    \\  -fno-function-sections    All functions go into same section
 466    \\  -fdata-sections           Places each data in a separate section
 467    \\  -fno-data-sections        All data go into same section
 468    \\  -fformatted-panics        Enable formatted safety panics
 469    \\  -fno-formatted-panics     Disable formatted safety panics
 470    \\  -fstructured-cfg          (SPIR-V) force SPIR-V kernels to use structured control flow
 471    \\  -fno-structured-cfg       (SPIR-V) force SPIR-V kernels to not use structured control flow
 472    \\  -mexec-model=[value]      (WASI) Execution model
 473    \\  -municode                 (Windows) Use wmain/wWinMain as entry point
 474    \\  --time-report             Send timing diagnostics to '--listen' clients
 475    \\
 476    \\Per-Module Compile Options:
 477    \\  -target [name]            <arch><sub>-<os>-<abi> see the targets command
 478    \\  -O [mode]                 Choose what to optimize for
 479    \\    Debug                   (default) Optimizations off, safety on
 480    \\    ReleaseFast             Optimize for performance, safety off
 481    \\    ReleaseSafe             Optimize for performance, safety on
 482    \\    ReleaseSmall            Optimize for small binary, safety off
 483    \\  -ofmt=[fmt]               Override target object format
 484    \\    elf                     Executable and Linking Format
 485    \\    c                       C source code
 486    \\    wasm                    WebAssembly
 487    \\    coff                    Common Object File Format (Windows)
 488    \\    macho                   macOS relocatables
 489    \\    spirv                   Standard, Portable Intermediate Representation V (SPIR-V)
 490    \\    plan9                   Plan 9 from Bell Labs object format
 491    \\    hex  (planned feature)  Intel IHEX
 492    \\    raw  (planned feature)  Dump machine code directly
 493    \\  -mcpu [cpu]               Specify target CPU and feature set
 494    \\  -mcmodel=[model]          Limit range of code and data virtual addresses
 495    \\    default
 496    \\    extreme
 497    \\    kernel
 498    \\    large
 499    \\    medany
 500    \\    medium
 501    \\    medlow
 502    \\    medmid
 503    \\    normal
 504    \\    small
 505    \\    tiny
 506    \\  -mred-zone                Force-enable the "red-zone"
 507    \\  -mno-red-zone             Force-disable the "red-zone"
 508    \\  -fomit-frame-pointer      Omit the stack frame pointer
 509    \\  -fno-omit-frame-pointer   Store the stack frame pointer
 510    \\  -fPIC                     Force-enable Position Independent Code
 511    \\  -fno-PIC                  Force-disable Position Independent Code
 512    \\  -fstack-check             Enable stack probing in unsafe builds
 513    \\  -fno-stack-check          Disable stack probing in safe builds
 514    \\  -fstack-protector         Enable stack protection in unsafe builds
 515    \\  -fno-stack-protector      Disable stack protection in safe builds
 516    \\  -fvalgrind                Include valgrind client requests in release builds
 517    \\  -fno-valgrind             Omit valgrind client requests in debug builds
 518    \\  -fsanitize-c[=mode]       Enable C undefined behavior detection in unsafe builds
 519    \\    trap                    Insert trap instructions on undefined behavior
 520    \\    full                    (Default) Insert runtime calls on undefined behavior
 521    \\  -fno-sanitize-c           Disable C undefined behavior detection in safe builds
 522    \\  -fsanitize-thread         Enable Thread Sanitizer
 523    \\  -fno-sanitize-thread      Disable Thread Sanitizer
 524    \\  -ffuzz                    Enable fuzz testing instrumentation
 525    \\  -fno-fuzz                 Disable fuzz testing instrumentation
 526    \\  -fbuiltin                 Enable implicit builtin knowledge of functions
 527    \\  -fno-builtin              Disable implicit builtin knowledge of functions
 528    \\  -funwind-tables           Always produce unwind table entries for all functions
 529    \\  -fasync-unwind-tables     Always produce asynchronous unwind table entries for all functions
 530    \\  -fno-unwind-tables        Never produce unwind table entries
 531    \\  -ferror-tracing           Enable error tracing in release builds
 532    \\  -fno-error-tracing        Disable error tracing in debug builds
 533    \\  -fsingle-threaded         Code assumes there is only one thread
 534    \\  -fno-single-threaded      Code may not assume there is only one thread
 535    \\  -fstrip                   Omit debug symbols
 536    \\  -fno-strip                Keep debug symbols
 537    \\  -idirafter [dir]          Add directory to AFTER include search path
 538    \\  -isystem  [dir]           Add directory to SYSTEM include search path
 539    \\  -I[dir]                   Add directory to include search path
 540    \\  --embed-dir=[dir]         Add directory to embed search path
 541    \\  -D[macro]=[value]         Define C [macro] to [value] (1 if [value] omitted)
 542    \\  -cflags [flags] --        Set extra flags for the next positional C source files
 543    \\  -rcflags [flags] --       Set extra flags for the next positional .rc source files
 544    \\  -rcincludes=[type]        Set the type of includes to use when compiling .rc source files
 545    \\    any                     (default) Use msvc if available, fall back to gnu
 546    \\    msvc                    Use msvc include paths (must be present on the system)
 547    \\    gnu                     Use mingw include paths (distributed with Zig)
 548    \\    none                    Do not use any autodetected include paths
 549    \\
 550    \\Global Link Options:
 551    \\  -T[script], --script [script]  Use a custom linker script
 552    \\  --version-script [path]        Provide a version .map file
 553    \\  --undefined-version            Allow version scripts to refer to undefined symbols
 554    \\  --no-undefined-version         (default) Disallow version scripts from referring to undefined symbols
 555    \\  --enable-new-dtags             Use the new behavior for dynamic tags (RUNPATH)
 556    \\  --disable-new-dtags            Use the old behavior for dynamic tags (RPATH)
 557    \\  --dynamic-linker [path]        Set the dynamic interpreter path (usually ld.so)
 558    \\  --no-dynamic-linker            Do not set any dynamic interpreter path
 559    \\  --sysroot [path]               Set the system root directory (usually /)
 560    \\  --version [ver]                Dynamic library semver
 561    \\  -fentry                        Enable entry point with default symbol name
 562    \\  -fentry=[name]                 Override the entry point symbol name
 563    \\  -fno-entry                     Do not output any entry point
 564    \\  --force_undefined [name]       Specify the symbol must be defined for the link to succeed
 565    \\  -fsoname[=name]                Override the default SONAME value
 566    \\  -fno-soname                    Disable emitting a SONAME
 567    \\  -flld                          Force using LLD as the linker
 568    \\  -fno-lld                       Prevent using LLD as the linker
 569    \\  -fcompiler-rt                  Always include compiler-rt symbols in output
 570    \\  -fno-compiler-rt               Prevent including compiler-rt symbols in output
 571    \\  -fubsan-rt                     Always include ubsan-rt symbols in the output
 572    \\  -fno-ubsan-rt                  Prevent including ubsan-rt symbols in the output
 573    \\  -rdynamic                      Add all symbols to the dynamic symbol table
 574    \\  -feach-lib-rpath               Ensure adding rpath for each used dynamic library
 575    \\  -fno-each-lib-rpath            Prevent adding rpath for each used dynamic library
 576    \\  -fallow-shlib-undefined        Allows undefined symbols in shared libraries
 577    \\  -fno-allow-shlib-undefined     Disallows undefined symbols in shared libraries
 578    \\  -fallow-so-scripts             Allows .so files to be GNU ld scripts
 579    \\  -fno-allow-so-scripts          (default) .so files must be ELF files
 580    \\  --build-id[=style]             At a minor link-time expense, embeds a build ID in binaries
 581    \\      fast                       8-byte non-cryptographic hash (COFF, ELF, WASM)
 582    \\      sha1, tree                 20-byte cryptographic hash (ELF, WASM)
 583    \\      md5                        16-byte cryptographic hash (ELF)
 584    \\      uuid                       16-byte random UUID (ELF, WASM)
 585    \\      0x[hexstring]              Constant ID, maximum 32 bytes (ELF, WASM)
 586    \\      none                     (default) No build ID
 587    \\  --eh-frame-hdr                 Enable C++ exception handling by passing --eh-frame-hdr to linker
 588    \\  --no-eh-frame-hdr              Disable C++ exception handling by passing --no-eh-frame-hdr to linker
 589    \\  --emit-relocs                  Enable output of relocation sections for post build tools
 590    \\  -z [arg]                       Set linker extension flags
 591    \\    nodelete                     Indicate that the object cannot be deleted from a process
 592    \\    notext                       Permit read-only relocations in read-only segments
 593    \\    defs                         Force a fatal error if any undefined symbols remain
 594    \\    undefs                       Reverse of -z defs
 595    \\    origin                       Indicate that the object must have its origin processed
 596    \\    nocopyreloc                  Disable the creation of copy relocations
 597    \\    now                          (default) Force all relocations to be processed on load
 598    \\    lazy                         Don't force all relocations to be processed on load
 599    \\    relro                        (default) Force all relocations to be read-only after processing
 600    \\    norelro                      Don't force all relocations to be read-only after processing
 601    \\    common-page-size=[bytes]     Set the common page size for ELF binaries
 602    \\    max-page-size=[bytes]        Set the max page size for ELF binaries
 603    \\  -dynamic                       Force output to be dynamically linked
 604    \\  -static                        Force output to be statically linked
 605    \\  -Bsymbolic                     Bind global references locally
 606    \\  --compress-debug-sections=[e]  Debug section compression settings
 607    \\      none                       No compression
 608    \\      zlib                       Compression with deflate/inflate
 609    \\      zstd                       Compression with zstandard
 610    \\  --gc-sections                  Force removal of functions and data that are unreachable by the entry point or exported symbols
 611    \\  --no-gc-sections               Don't force removal of unreachable functions and data
 612    \\  --sort-section=[value]         Sort wildcard section patterns by 'name' or 'alignment'
 613    \\  --subsystem [subsystem]        (Windows) /SUBSYSTEM:<subsystem> to the linker
 614    \\  --stack [size]                 Override default stack size
 615    \\  --image-base [addr]            Set base address for executable image
 616    \\  -install_name=[value]          (Darwin) add dylib's install name
 617    \\  --entitlements [path]          (Darwin) add path to entitlements file for embedding in code signature
 618    \\  -pagezero_size [value]         (Darwin) size of the __PAGEZERO segment in hexadecimal notation
 619    \\  -headerpad [value]             (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
 620    \\  -headerpad_max_install_names   (Darwin) set enough space as if all paths were MAXPATHLEN
 621    \\  -dead_strip                    (Darwin) remove functions and data that are unreachable by the entry point or exported symbols
 622    \\  -dead_strip_dylibs             (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
 623    \\  -ObjC                          (Darwin) force load all members of static archives that implement an Objective-C class or category
 624    \\  --import-memory                (WebAssembly) import memory from the environment
 625    \\  --export-memory                (WebAssembly) export memory to the host (Default unless --import-memory used)
 626    \\  --import-symbols               (WebAssembly) import missing symbols from the host environment
 627    \\  --import-table                 (WebAssembly) import function table from the host environment
 628    \\  --export-table                 (WebAssembly) export function table to the host environment
 629    \\  --initial-memory=[bytes]       (WebAssembly) initial size of the linear memory
 630    \\  --max-memory=[bytes]           (WebAssembly) maximum size of the linear memory
 631    \\  --shared-memory                (WebAssembly) use shared linear memory
 632    \\  --global-base=[addr]           (WebAssembly) where to start to place global data
 633    \\
 634    \\Per-Module Link Options:
 635    \\  -l[lib], --library [lib]       Link against system library (only if actually used)
 636    \\  -needed-l[lib],                Link against system library (even if unused)
 637    \\    --needed-library [lib]
 638    \\  -weak-l[lib]                   link against system library marking it and all
 639    \\    -weak_library [lib]          referenced symbols as weak
 640    \\  -L[d], --library-directory [d] Add a directory to the library search path
 641    \\  -search_paths_first            For each library search path, check for dynamic
 642    \\                                 lib then static lib before proceeding to next path.
 643    \\  -search_paths_first_static     For each library search path, check for static
 644    \\                                 lib then dynamic lib before proceeding to next path.
 645    \\  -search_dylibs_first           Search for dynamic libs in all library search
 646    \\                                 paths, then static libs.
 647    \\  -search_static_first           Search for static libs in all library search
 648    \\                                 paths, then dynamic libs.
 649    \\  -search_dylibs_only            Only search for dynamic libs.
 650    \\  -search_static_only            Only search for static libs.
 651    \\  -rpath [path]                  Add directory to the runtime library search path
 652    \\  -framework [name]              (Darwin) link against framework
 653    \\  -needed_framework [name]       (Darwin) link against framework (even if unused)
 654    \\  -needed_library [lib]          (Darwin) link against system library (even if unused)
 655    \\  -weak_framework [name]         (Darwin) link against framework and mark it and all referenced symbols as weak
 656    \\  -F[dir]                        (Darwin) add search path for frameworks
 657    \\  --export=[value]               (WebAssembly) Force a symbol to be exported
 658    \\
 659    \\Test Options:
 660    \\  --test-filter [text]           Skip tests that do not match any filter
 661    \\  --test-cmd [arg]               Specify test execution command one arg at a time
 662    \\  --test-cmd-bin                 Appends test binary path to test cmd args
 663    \\  --test-no-exec                 Compiles test binary without running it
 664    \\  --test-runner [path]           Specify a custom test runner
 665    \\
 666    \\Debug Options (Zig Compiler Development):
 667    \\  -fopt-bisect-limit=[limit]   Only run [limit] first LLVM optimization passes
 668    \\  -fstack-report               Print stack size diagnostics
 669    \\  --verbose-link               Display linker invocations
 670    \\  --verbose-cc                 Display C compiler invocations
 671    \\  --verbose-air                Enable compiler debug output for Zig AIR
 672    \\  --verbose-intern-pool        Enable compiler debug output for InternPool
 673    \\  --verbose-generic-instances  Enable compiler debug output for generic instance generation
 674    \\  --verbose-llvm-ir[=path]     Enable compiler debug output for unoptimized LLVM IR
 675    \\  --verbose-llvm-bc=[path]     Enable compiler debug output for unoptimized LLVM BC
 676    \\  --verbose-cimport            Enable compiler debug output for C imports
 677    \\  --verbose-llvm-cpu-features  Enable compiler debug output for LLVM CPU features
 678    \\  --debug-log [scope]          Enable printing debug/info log messages for scope
 679    \\  --debug-compile-errors       Crash with helpful diagnostics at the first compile error
 680    \\  --debug-link-snapshot        Enable dumping of the linker's state in JSON format
 681    \\  --debug-rt                   Debug compiler runtime libraries
 682    \\  --debug-incremental          Enable incremental compilation debug features
 683    \\
 684;
 685
 686const SOName = union(enum) {
 687    no,
 688    yes_default_value,
 689    yes: []const u8,
 690};
 691
 692const EmitBin = union(enum) {
 693    no,
 694    yes_default_path,
 695    yes: []const u8,
 696    yes_a_out,
 697};
 698
 699const Emit = union(enum) {
 700    no,
 701    yes_default_path,
 702    yes: []const u8,
 703
 704    const OutputToCacheReason = enum { listen, @"zig run", @"zig test" };
 705    fn resolve(emit: Emit, default_basename: []const u8, output_to_cache: ?OutputToCacheReason) Compilation.CreateOptions.Emit {
 706        return switch (emit) {
 707            .no => .no,
 708            .yes_default_path => if (output_to_cache != null) .yes_cache else .{ .yes_path = default_basename },
 709            .yes => |path| if (output_to_cache) |reason| {
 710                switch (reason) {
 711                    .listen => fatal("--listen incompatible with explicit output path '{s}'", .{path}),
 712                    .@"zig run", .@"zig test" => fatal(
 713                        "'{s}' with explicit output path '{s}' requires explicit '-femit-bin=path' or '-fno-emit-bin'",
 714                        .{ @tagName(reason), path },
 715                    ),
 716                }
 717            } else e: {
 718                // If there's a dirname, check that dir exists. This will give a more descriptive error than `Compilation` otherwise would.
 719                if (fs.path.dirname(path)) |dir_path| {
 720                    var dir = fs.cwd().openDir(dir_path, .{}) catch |err| {
 721                        fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) });
 722                    };
 723                    dir.close();
 724                }
 725                break :e .{ .yes_path = path };
 726            },
 727        };
 728    }
 729};
 730
 731const ArgMode = union(enum) {
 732    build: std.builtin.OutputMode,
 733    cc,
 734    cpp,
 735    translate_c,
 736    zig_test,
 737    zig_test_obj,
 738    run,
 739};
 740
 741const Listen = union(enum) {
 742    none,
 743    stdio: if (dev.env.supports(.stdio_listen)) void else noreturn,
 744    ip4: if (dev.env.supports(.network_listen)) Io.net.Ip4Address else noreturn,
 745};
 746
 747const ArgsIterator = struct {
 748    resp_file: ?ArgIteratorResponseFile = null,
 749    args: []const []const u8,
 750    i: usize = 0,
 751    fn next(it: *@This()) ?[]const u8 {
 752        if (it.i >= it.args.len) {
 753            if (it.resp_file) |*resp| return resp.next();
 754            return null;
 755        }
 756        defer it.i += 1;
 757        return it.args[it.i];
 758    }
 759    fn nextOrFatal(it: *@This()) []const u8 {
 760        if (it.i >= it.args.len) {
 761            if (it.resp_file) |*resp| if (resp.next()) |ret| return ret;
 762            fatal("expected parameter after {s}", .{it.args[it.i - 1]});
 763        }
 764        defer it.i += 1;
 765        return it.args[it.i];
 766    }
 767};
 768
 769/// Similar to `link.Framework` except it doesn't store yet unresolved
 770/// path to the framework.
 771const Framework = struct {
 772    needed: bool = false,
 773    weak: bool = false,
 774};
 775
 776const CliModule = struct {
 777    root_path: []const u8,
 778    root_src_path: []const u8,
 779    cc_argv: []const []const u8,
 780    inherited: Package.Module.CreateOptions.Inherited,
 781    target_arch_os_abi: ?[]const u8,
 782    target_mcpu: ?[]const u8,
 783
 784    deps: []const Dep,
 785    resolved: ?*Package.Module,
 786
 787    c_source_files_start: usize,
 788    c_source_files_end: usize,
 789    rc_source_files_start: usize,
 790    rc_source_files_end: usize,
 791
 792    const Dep = struct {
 793        key: []const u8,
 794        value: []const u8,
 795    };
 796};
 797
 798fn buildOutputType(
 799    gpa: Allocator,
 800    arena: Allocator,
 801    io: Io,
 802    all_args: []const []const u8,
 803    arg_mode: ArgMode,
 804) !void {
 805    var provided_name: ?[]const u8 = null;
 806    var root_src_file: ?[]const u8 = null;
 807    var version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 };
 808    var have_version = false;
 809    var compatibility_version: ?std.SemanticVersion = null;
 810    var function_sections = false;
 811    var data_sections = false;
 812    var listen: Listen = .none;
 813    var debug_compile_errors = false;
 814    var debug_incremental = false;
 815    var verbose_link = (native_os != .wasi or builtin.link_libc) and
 816        EnvVar.ZIG_VERBOSE_LINK.isSet();
 817    var verbose_cc = (native_os != .wasi or builtin.link_libc) and
 818        EnvVar.ZIG_VERBOSE_CC.isSet();
 819    var verbose_air = false;
 820    var verbose_intern_pool = false;
 821    var verbose_generic_instances = false;
 822    var verbose_llvm_ir: ?[]const u8 = null;
 823    var verbose_llvm_bc: ?[]const u8 = null;
 824    var verbose_cimport = false;
 825    var verbose_llvm_cpu_features = false;
 826    var time_report = false;
 827    var stack_report = false;
 828    var show_builtin = false;
 829    var emit_bin: EmitBin = .yes_default_path;
 830    var emit_asm: Emit = .no;
 831    var emit_llvm_ir: Emit = .no;
 832    var emit_llvm_bc: Emit = .no;
 833    var emit_docs: Emit = .no;
 834    var emit_implib: Emit = .yes_default_path;
 835    var emit_implib_arg_provided = false;
 836    var target_arch_os_abi: ?[]const u8 = null;
 837    var target_mcpu: ?[]const u8 = null;
 838    var emit_h: Emit = .no;
 839    var soname: SOName = undefined;
 840    var want_compiler_rt: ?bool = null;
 841    var want_ubsan_rt: ?bool = null;
 842    var linker_script: ?[]const u8 = null;
 843    var version_script: ?[]const u8 = null;
 844    var linker_repro: ?bool = null;
 845    var linker_allow_undefined_version: bool = false;
 846    var linker_enable_new_dtags: ?bool = null;
 847    var disable_c_depfile = false;
 848    var linker_sort_section: ?link.File.Lld.Elf.SortSection = null;
 849    var linker_gc_sections: ?bool = null;
 850    var linker_compress_debug_sections: ?std.zig.CompressDebugSections = null;
 851    var linker_allow_shlib_undefined: ?bool = null;
 852    var allow_so_scripts: bool = false;
 853    var linker_bind_global_refs_locally: ?bool = null;
 854    var linker_import_symbols: bool = false;
 855    var linker_import_table: bool = false;
 856    var linker_export_table: bool = false;
 857    var linker_initial_memory: ?u64 = null;
 858    var linker_max_memory: ?u64 = null;
 859    var linker_global_base: ?u64 = null;
 860    var linker_print_gc_sections: bool = false;
 861    var linker_print_icf_sections: bool = false;
 862    var linker_print_map: bool = false;
 863    var llvm_opt_bisect_limit: c_int = -1;
 864    var linker_z_nocopyreloc = false;
 865    var linker_z_nodelete = false;
 866    var linker_z_notext = false;
 867    var linker_z_defs = false;
 868    var linker_z_origin = false;
 869    var linker_z_now = true;
 870    var linker_z_relro = true;
 871    var linker_z_common_page_size: ?u64 = null;
 872    var linker_z_max_page_size: ?u64 = null;
 873    var linker_tsaware = false;
 874    var linker_nxcompat = false;
 875    var linker_dynamicbase = true;
 876    var linker_optimization: ?[]const u8 = null;
 877    var linker_module_definition_file: ?[]const u8 = null;
 878    var test_no_exec = false;
 879    var entry: Compilation.CreateOptions.Entry = .default;
 880    var force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .empty;
 881    var stack_size: ?u64 = null;
 882    var image_base: ?u64 = null;
 883    var link_eh_frame_hdr = false;
 884    var link_emit_relocs = false;
 885    var build_id: ?std.zig.BuildId = null;
 886    var runtime_args_start: ?usize = null;
 887    var test_filters: std.ArrayList([]const u8) = .empty;
 888    var test_runner_path: ?[]const u8 = null;
 889    var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena);
 890    var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
 891    var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
 892    var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
 893    var subsystem: ?std.zig.Subsystem = null;
 894    var major_subsystem_version: ?u16 = null;
 895    var minor_subsystem_version: ?u16 = null;
 896    var mingw_unicode_entry_point: bool = false;
 897    var enable_link_snapshots: bool = false;
 898    var debug_compiler_runtime_libs = false;
 899    var install_name: ?[]const u8 = null;
 900    var hash_style: link.File.Lld.Elf.HashStyle = .both;
 901    var entitlements: ?[]const u8 = null;
 902    var pagezero_size: ?u64 = null;
 903    var lib_search_strategy: link.UnresolvedInput.SearchStrategy = .paths_first;
 904    var lib_preferred_mode: std.builtin.LinkMode = .dynamic;
 905    var headerpad_size: ?u32 = null;
 906    var headerpad_max_install_names: bool = false;
 907    var dead_strip_dylibs: bool = false;
 908    var force_load_objc: bool = false;
 909    var discard_local_symbols: bool = false;
 910    var contains_res_file: bool = false;
 911    var reference_trace: ?u32 = null;
 912    var pdb_out_path: ?[]const u8 = null;
 913    var error_limit: ?Zcu.ErrorInt = null;
 914    // These are before resolving sysroot.
 915    var extra_cflags: std.ArrayList([]const u8) = .empty;
 916    var extra_rcflags: std.ArrayList([]const u8) = .empty;
 917    var symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty;
 918    var rc_includes: std.zig.RcIncludes = .any;
 919    var manifest_file: ?[]const u8 = null;
 920    var linker_export_symbol_names: std.ArrayList([]const u8) = .empty;
 921
 922    // Tracks the position in c_source_files which have already their owner populated.
 923    var c_source_files_owner_index: usize = 0;
 924    // Tracks the position in rc_source_files which have already their owner populated.
 925    var rc_source_files_owner_index: usize = 0;
 926
 927    // null means replace with the test executable binary
 928    var test_exec_args: std.ArrayList(?[]const u8) = .empty;
 929
 930    // These get set by CLI flags and then snapshotted when a `-M` flag is
 931    // encountered.
 932    var mod_opts: Package.Module.CreateOptions.Inherited = .{};
 933
 934    // These get appended to by CLI flags and then slurped when a `-M` flag
 935    // is encountered.
 936    var cssan: ClangSearchSanitizer = .{};
 937    var cc_argv: std.ArrayList([]const u8) = .empty;
 938    var deps: std.ArrayList(CliModule.Dep) = .empty;
 939
 940    // Contains every module specified via -M. The dependencies are added
 941    // after argument parsing is completed. We use a StringArrayHashMap to make
 942    // error output consistent. "root" is special.
 943    var create_module: CreateModule = .{
 944        // Populated just before the call to `createModule`.
 945        .dirs = undefined,
 946        .object_format = null,
 947        .dynamic_linker = null,
 948        .modules = .{},
 949        .opts = .{
 950            .is_test = switch (arg_mode) {
 951                .zig_test, .zig_test_obj => true,
 952                .build, .cc, .cpp, .translate_c, .run => false,
 953            },
 954            // Populated while parsing CLI args.
 955            .output_mode = undefined,
 956            // Populated in the call to `createModule` for the root module.
 957            .resolved_target = undefined,
 958            .have_zcu = false,
 959            // Populated just before the call to `createModule`.
 960            .emit_llvm_ir = undefined,
 961            // Populated just before the call to `createModule`.
 962            .emit_llvm_bc = undefined,
 963            // Populated just before the call to `createModule`.
 964            .emit_bin = undefined,
 965            // Populated just before the call to `createModule`.
 966            .any_c_source_files = undefined,
 967        },
 968        // Populated in the call to `createModule` for the root module.
 969        .resolved_options = undefined,
 970
 971        .cli_link_inputs = .empty,
 972        .windows_libs = .empty,
 973        .link_inputs = .empty,
 974
 975        .c_source_files = .{},
 976        .rc_source_files = .{},
 977
 978        .llvm_m_args = .{},
 979        .sysroot = null,
 980        .lib_directories = .{}, // populated by createModule()
 981        .lib_dir_args = .{}, // populated from CLI arg parsing
 982        .libc_installation = null,
 983        .want_native_include_dirs = false,
 984        .frameworks = .{},
 985        .framework_dirs = .{},
 986        .rpath_list = .{},
 987        .each_lib_rpath = null,
 988        .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena),
 989        .native_system_include_paths = &.{},
 990    };
 991    defer create_module.link_inputs.deinit(gpa);
 992
 993    // before arg parsing, check for the NO_COLOR and CLICOLOR_FORCE environment variables
 994    // if set, default the color setting to .off or .on, respectively
 995    // explicit --color arguments will still override this setting.
 996    // Disable color on WASI per https://github.com/WebAssembly/WASI/issues/162
 997    var color: Color = if (native_os == .wasi or EnvVar.NO_COLOR.isSet())
 998        .off
 999    else if (EnvVar.CLICOLOR_FORCE.isSet())
1000        .on
1001    else
1002        .auto;
1003    var n_jobs: ?u32 = null;
1004
1005    switch (arg_mode) {
1006        .build, .translate_c, .zig_test, .zig_test_obj, .run => {
1007            switch (arg_mode) {
1008                .build => |m| {
1009                    create_module.opts.output_mode = m;
1010                },
1011                .translate_c => {
1012                    emit_bin = .no;
1013                    create_module.opts.output_mode = .Obj;
1014                },
1015                .zig_test, .run => {
1016                    create_module.opts.output_mode = .Exe;
1017                },
1018                .zig_test_obj => {
1019                    create_module.opts.output_mode = .Obj;
1020                },
1021                else => unreachable,
1022            }
1023
1024            soname = .yes_default_value;
1025
1026            var args_iter = ArgsIterator{
1027                .args = all_args[2..],
1028            };
1029
1030            var file_ext: ?Compilation.FileExt = null;
1031            args_loop: while (args_iter.next()) |arg| {
1032                if (mem.cutPrefix(u8, arg, "@")) |resp_file_path| {
1033                    // This is a "compiler response file". We must parse the file and treat its
1034                    // contents as command line parameters.
1035                    args_iter.resp_file = initArgIteratorResponseFile(arena, resp_file_path) catch |err| {
1036                        fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) });
1037                    };
1038                } else if (mem.startsWith(u8, arg, "-")) {
1039                    if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
1040                        try fs.File.stdout().writeAll(usage_build_generic);
1041                        return cleanExit();
1042                    } else if (mem.eql(u8, arg, "--")) {
1043                        if (arg_mode == .run) {
1044                            // args_iter.i is 1, referring the next arg after "--" in ["--", ...]
1045                            // Add +2 to the index so it is relative to all_args
1046                            runtime_args_start = args_iter.i + 2;
1047                            break :args_loop;
1048                        } else {
1049                            fatal("unexpected end-of-parameter mark: --", .{});
1050                        }
1051                    } else if (mem.eql(u8, arg, "--dep")) {
1052                        const next_arg = args_iter.nextOrFatal();
1053                        const key, const value = mem.cutScalar(u8, next_arg, '=') orelse .{ next_arg, next_arg };
1054                        if (mem.eql(u8, key, "std") and !mem.eql(u8, value, "std")) {
1055                            fatal("unable to import as '{s}': conflicts with builtin module", .{
1056                                key,
1057                            });
1058                        }
1059                        for ([_][]const u8{ "root", "builtin" }) |name| {
1060                            if (mem.eql(u8, key, name)) {
1061                                fatal("unable to import as '{s}': conflicts with builtin module", .{
1062                                    key,
1063                                });
1064                            }
1065                        }
1066                        try deps.append(arena, .{
1067                            .key = key,
1068                            .value = value,
1069                        });
1070                    } else if (mem.cutPrefix(u8, arg, "-M")) |rest| {
1071                        const mod_name, const root_src_orig = mem.cutScalar(u8, rest, '=') orelse .{ rest, null };
1072                        try handleModArg(
1073                            arena,
1074                            mod_name,
1075                            root_src_orig,
1076                            &create_module,
1077                            &mod_opts,
1078                            &cc_argv,
1079                            &target_arch_os_abi,
1080                            &target_mcpu,
1081                            &deps,
1082                            &c_source_files_owner_index,
1083                            &rc_source_files_owner_index,
1084                            &cssan,
1085                        );
1086                    } else if (mem.eql(u8, arg, "--error-limit")) {
1087                        const next_arg = args_iter.nextOrFatal();
1088                        error_limit = std.fmt.parseUnsigned(Zcu.ErrorInt, next_arg, 0) catch |err| {
1089                            fatal("unable to parse error limit '{s}': {s}", .{ next_arg, @errorName(err) });
1090                        };
1091                    } else if (mem.eql(u8, arg, "-cflags")) {
1092                        extra_cflags.shrinkRetainingCapacity(0);
1093                        while (true) {
1094                            const next_arg = args_iter.next() orelse {
1095                                fatal("expected -- after -cflags", .{});
1096                            };
1097                            if (mem.eql(u8, next_arg, "--")) break;
1098                            try extra_cflags.append(arena, next_arg);
1099                        }
1100                    } else if (mem.eql(u8, arg, "-rcincludes")) {
1101                        rc_includes = parseRcIncludes(args_iter.nextOrFatal());
1102                    } else if (mem.cutPrefix(u8, arg, "-rcincludes=")) |rest| {
1103                        rc_includes = parseRcIncludes(rest);
1104                    } else if (mem.eql(u8, arg, "-rcflags")) {
1105                        extra_rcflags.shrinkRetainingCapacity(0);
1106                        while (true) {
1107                            const next_arg = args_iter.next() orelse {
1108                                fatal("expected -- after -rcflags", .{});
1109                            };
1110                            if (mem.eql(u8, next_arg, "--")) break;
1111                            try extra_rcflags.append(arena, next_arg);
1112                        }
1113                    } else if (mem.eql(u8, arg, "-fstructured-cfg")) {
1114                        mod_opts.structured_cfg = true;
1115                    } else if (mem.eql(u8, arg, "-fno-structured-cfg")) {
1116                        mod_opts.structured_cfg = false;
1117                    } else if (mem.eql(u8, arg, "--color")) {
1118                        const next_arg = args_iter.next() orelse {
1119                            fatal("expected [auto|on|off] after --color", .{});
1120                        };
1121                        color = std.meta.stringToEnum(Color, next_arg) orelse {
1122                            fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
1123                        };
1124                    } else if (mem.cutPrefix(u8, arg, "-j")) |str| {
1125                        const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| {
1126                            fatal("unable to parse jobs count '{s}': {s}", .{
1127                                str, @errorName(err),
1128                            });
1129                        };
1130                        if (num < 1) {
1131                            fatal("number of jobs must be at least 1\n", .{});
1132                        }
1133                        n_jobs = num;
1134                    } else if (mem.eql(u8, arg, "--subsystem")) {
1135                        subsystem = try parseSubsystem(args_iter.nextOrFatal());
1136                    } else if (mem.eql(u8, arg, "-O")) {
1137                        mod_opts.optimize_mode = parseOptimizeMode(args_iter.nextOrFatal());
1138                    } else if (mem.cutPrefix(u8, arg, "-fentry=")) |rest| {
1139                        entry = .{ .named = rest };
1140                    } else if (mem.eql(u8, arg, "--force_undefined")) {
1141                        try force_undefined_symbols.put(arena, args_iter.nextOrFatal(), {});
1142                    } else if (mem.eql(u8, arg, "--discard-all")) {
1143                        discard_local_symbols = true;
1144                    } else if (mem.eql(u8, arg, "--stack")) {
1145                        stack_size = parseStackSize(args_iter.nextOrFatal());
1146                    } else if (mem.eql(u8, arg, "--image-base")) {
1147                        image_base = parseImageBase(args_iter.nextOrFatal());
1148                    } else if (mem.eql(u8, arg, "--name")) {
1149                        provided_name = args_iter.nextOrFatal();
1150                        if (!mem.eql(u8, provided_name.?, fs.path.basename(provided_name.?)))
1151                            fatal("invalid package name '{s}': cannot contain folder separators", .{provided_name.?});
1152                    } else if (mem.eql(u8, arg, "-rpath")) {
1153                        try create_module.rpath_list.append(arena, args_iter.nextOrFatal());
1154                    } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
1155                        try create_module.lib_dir_args.append(arena, args_iter.nextOrFatal());
1156                    } else if (mem.eql(u8, arg, "-F")) {
1157                        try create_module.framework_dirs.append(arena, args_iter.nextOrFatal());
1158                    } else if (mem.eql(u8, arg, "-framework")) {
1159                        try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{});
1160                    } else if (mem.eql(u8, arg, "-weak_framework")) {
1161                        try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .weak = true });
1162                    } else if (mem.eql(u8, arg, "-needed_framework")) {
1163                        try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{ .needed = true });
1164                    } else if (mem.eql(u8, arg, "-install_name")) {
1165                        install_name = args_iter.nextOrFatal();
1166                    } else if (mem.cutPrefix(u8, arg, "--compress-debug-sections=")) |param| {
1167                        linker_compress_debug_sections = std.meta.stringToEnum(std.zig.CompressDebugSections, param) orelse {
1168                            fatal("expected --compress-debug-sections=[none|zlib|zstd], found '{s}'", .{param});
1169                        };
1170                    } else if (mem.eql(u8, arg, "--compress-debug-sections")) {
1171                        linker_compress_debug_sections = .zlib;
1172                    } else if (mem.eql(u8, arg, "-pagezero_size")) {
1173                        const next_arg = args_iter.nextOrFatal();
1174                        pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
1175                            fatal("unable to parse pagezero size'{s}': {s}", .{ next_arg, @errorName(err) });
1176                        };
1177                    } else if (mem.eql(u8, arg, "-search_paths_first")) {
1178                        lib_search_strategy = .paths_first;
1179                        lib_preferred_mode = .dynamic;
1180                    } else if (mem.eql(u8, arg, "-search_paths_first_static")) {
1181                        lib_search_strategy = .paths_first;
1182                        lib_preferred_mode = .static;
1183                    } else if (mem.eql(u8, arg, "-search_dylibs_first")) {
1184                        lib_search_strategy = .mode_first;
1185                        lib_preferred_mode = .dynamic;
1186                    } else if (mem.eql(u8, arg, "-search_static_first")) {
1187                        lib_search_strategy = .mode_first;
1188                        lib_preferred_mode = .static;
1189                    } else if (mem.eql(u8, arg, "-search_dylibs_only")) {
1190                        lib_search_strategy = .no_fallback;
1191                        lib_preferred_mode = .dynamic;
1192                    } else if (mem.eql(u8, arg, "-search_static_only")) {
1193                        lib_search_strategy = .no_fallback;
1194                        lib_preferred_mode = .static;
1195                    } else if (mem.eql(u8, arg, "-headerpad")) {
1196                        const next_arg = args_iter.nextOrFatal();
1197                        headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
1198                            fatal("unable to parse headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });
1199                        };
1200                    } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
1201                        headerpad_max_install_names = true;
1202                    } else if (mem.eql(u8, arg, "-dead_strip")) {
1203                        linker_gc_sections = true;
1204                    } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
1205                        dead_strip_dylibs = true;
1206                    } else if (mem.eql(u8, arg, "-ObjC")) {
1207                        force_load_objc = true;
1208                    } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
1209                        linker_script = args_iter.nextOrFatal();
1210                    } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
1211                        version_script = args_iter.nextOrFatal();
1212                    } else if (mem.eql(u8, arg, "--undefined-version")) {
1213                        linker_allow_undefined_version = true;
1214                    } else if (mem.eql(u8, arg, "--no-undefined-version")) {
1215                        linker_allow_undefined_version = false;
1216                    } else if (mem.eql(u8, arg, "--enable-new-dtags")) {
1217                        linker_enable_new_dtags = true;
1218                    } else if (mem.eql(u8, arg, "--disable-new-dtags")) {
1219                        linker_enable_new_dtags = false;
1220                    } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
1221                        // We don't know whether this library is part of libc
1222                        // or libc++ until we resolve the target, so we append
1223                        // to the list for now.
1224                        try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1225                            .name = args_iter.nextOrFatal(),
1226                            .query = .{
1227                                .needed = false,
1228                                .weak = false,
1229                                .preferred_mode = lib_preferred_mode,
1230                                .search_strategy = lib_search_strategy,
1231                                .allow_so_scripts = allow_so_scripts,
1232                            },
1233                        } });
1234                    } else if (mem.eql(u8, arg, "--needed-library") or
1235                        mem.eql(u8, arg, "-needed-l") or
1236                        mem.eql(u8, arg, "-needed_library"))
1237                    {
1238                        const next_arg = args_iter.nextOrFatal();
1239                        try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1240                            .name = next_arg,
1241                            .query = .{
1242                                .needed = true,
1243                                .weak = false,
1244                                .preferred_mode = lib_preferred_mode,
1245                                .search_strategy = lib_search_strategy,
1246                                .allow_so_scripts = allow_so_scripts,
1247                            },
1248                        } });
1249                    } else if (mem.eql(u8, arg, "-weak_library") or mem.eql(u8, arg, "-weak-l")) {
1250                        try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1251                            .name = args_iter.nextOrFatal(),
1252                            .query = .{
1253                                .needed = false,
1254                                .weak = true,
1255                                .preferred_mode = lib_preferred_mode,
1256                                .search_strategy = lib_search_strategy,
1257                                .allow_so_scripts = allow_so_scripts,
1258                            },
1259                        } });
1260                    } else if (mem.eql(u8, arg, "-D")) {
1261                        try cc_argv.appendSlice(arena, &.{ arg, args_iter.nextOrFatal() });
1262                    } else if (mem.eql(u8, arg, "-I")) {
1263                        try cssan.addIncludePath(arena, &cc_argv, .I, arg, args_iter.nextOrFatal(), false);
1264                    } else if (mem.cutPrefix(u8, arg, "--embed-dir=")) |rest| {
1265                        try cssan.addIncludePath(arena, &cc_argv, .embed_dir, arg, rest, true);
1266                    } else if (mem.eql(u8, arg, "-isystem")) {
1267                        try cssan.addIncludePath(arena, &cc_argv, .isystem, arg, args_iter.nextOrFatal(), false);
1268                    } else if (mem.eql(u8, arg, "-iwithsysroot")) {
1269                        try cssan.addIncludePath(arena, &cc_argv, .iwithsysroot, arg, args_iter.nextOrFatal(), false);
1270                    } else if (mem.eql(u8, arg, "-idirafter")) {
1271                        try cssan.addIncludePath(arena, &cc_argv, .idirafter, arg, args_iter.nextOrFatal(), false);
1272                    } else if (mem.eql(u8, arg, "-iframework")) {
1273                        const path = args_iter.nextOrFatal();
1274                        try cssan.addIncludePath(arena, &cc_argv, .iframework, arg, path, false);
1275                        try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F
1276                    } else if (mem.eql(u8, arg, "-iframeworkwithsysroot")) {
1277                        const path = args_iter.nextOrFatal();
1278                        try cssan.addIncludePath(arena, &cc_argv, .iframeworkwithsysroot, arg, path, false);
1279                        try create_module.framework_dirs.append(arena, path); // Forward to the backend as -F
1280                    } else if (mem.eql(u8, arg, "--version")) {
1281                        const next_arg = args_iter.nextOrFatal();
1282                        version = std.SemanticVersion.parse(next_arg) catch |err| {
1283                            fatal("unable to parse --version '{s}': {s}", .{ next_arg, @errorName(err) });
1284                        };
1285                        have_version = true;
1286                    } else if (mem.eql(u8, arg, "-target")) {
1287                        target_arch_os_abi = args_iter.nextOrFatal();
1288                    } else if (mem.eql(u8, arg, "-mcpu")) {
1289                        target_mcpu = args_iter.nextOrFatal();
1290                    } else if (mem.eql(u8, arg, "-mcmodel")) {
1291                        mod_opts.code_model = parseCodeModel(args_iter.nextOrFatal());
1292                    } else if (mem.cutPrefix(u8, arg, "-mcmodel=")) |rest| {
1293                        mod_opts.code_model = parseCodeModel(rest);
1294                    } else if (mem.cutPrefix(u8, arg, "-ofmt=")) |rest| {
1295                        create_module.object_format = rest;
1296                    } else if (mem.cutPrefix(u8, arg, "-mcpu=")) |rest| {
1297                        target_mcpu = rest;
1298                    } else if (mem.cutPrefix(u8, arg, "-O")) |rest| {
1299                        mod_opts.optimize_mode = parseOptimizeMode(rest);
1300                    } else if (mem.eql(u8, arg, "--dynamic-linker")) {
1301                        create_module.dynamic_linker = args_iter.nextOrFatal();
1302                    } else if (mem.eql(u8, arg, "--no-dynamic-linker")) {
1303                        create_module.dynamic_linker = "";
1304                    } else if (mem.eql(u8, arg, "--sysroot")) {
1305                        const next_arg = args_iter.nextOrFatal();
1306                        create_module.sysroot = next_arg;
1307                        try cc_argv.appendSlice(arena, &.{ "-isysroot", next_arg });
1308                    } else if (mem.eql(u8, arg, "--libc")) {
1309                        create_module.libc_paths_file = args_iter.nextOrFatal();
1310                    } else if (mem.eql(u8, arg, "--test-filter")) {
1311                        try test_filters.append(arena, args_iter.nextOrFatal());
1312                    } else if (mem.eql(u8, arg, "--test-runner")) {
1313                        test_runner_path = args_iter.nextOrFatal();
1314                    } else if (mem.eql(u8, arg, "--test-cmd")) {
1315                        try test_exec_args.append(arena, args_iter.nextOrFatal());
1316                    } else if (mem.eql(u8, arg, "--cache-dir")) {
1317                        override_local_cache_dir = args_iter.nextOrFatal();
1318                    } else if (mem.eql(u8, arg, "--global-cache-dir")) {
1319                        override_global_cache_dir = args_iter.nextOrFatal();
1320                    } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
1321                        override_lib_dir = args_iter.nextOrFatal();
1322                    } else if (mem.eql(u8, arg, "--debug-log")) {
1323                        if (!build_options.enable_logging) {
1324                            warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
1325                            _ = args_iter.nextOrFatal();
1326                        } else {
1327                            try log_scopes.append(arena, args_iter.nextOrFatal());
1328                        }
1329                    } else if (mem.eql(u8, arg, "--listen")) {
1330                        const next_arg = args_iter.nextOrFatal();
1331                        if (mem.eql(u8, next_arg, "-")) {
1332                            dev.check(.stdio_listen);
1333                            listen = .stdio;
1334                        } else {
1335                            dev.check(.network_listen);
1336                            // example: --listen 127.0.0.1:9000
1337                            const host, const port_text = mem.cutScalar(u8, next_arg, ':') orelse .{ next_arg, "14735" };
1338                            const port = std.fmt.parseInt(u16, port_text, 10) catch |err|
1339                                fatal("invalid port number: '{s}': {s}", .{ port_text, @errorName(err) });
1340                            listen = .{ .ip4 = Io.net.Ip4Address.parse(host, port) catch |err|
1341                                fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }) };
1342                        }
1343                    } else if (mem.eql(u8, arg, "--listen=-")) {
1344                        dev.check(.stdio_listen);
1345                        listen = .stdio;
1346                    } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
1347                        if (!build_options.enable_link_snapshots) {
1348                            warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});
1349                        } else {
1350                            enable_link_snapshots = true;
1351                        }
1352                    } else if (mem.eql(u8, arg, "--debug-rt")) {
1353                        debug_compiler_runtime_libs = true;
1354                    } else if (mem.eql(u8, arg, "--debug-incremental")) {
1355                        if (build_options.enable_debug_extensions) {
1356                            debug_incremental = true;
1357                        } else {
1358                            warn("Zig was compiled without debug extensions. --debug-incremental has no effect.", .{});
1359                        }
1360                    } else if (mem.eql(u8, arg, "-fincremental")) {
1361                        dev.check(.incremental);
1362                        create_module.opts.incremental = true;
1363                    } else if (mem.eql(u8, arg, "-fno-incremental")) {
1364                        create_module.opts.incremental = false;
1365                    } else if (mem.eql(u8, arg, "--entitlements")) {
1366                        entitlements = args_iter.nextOrFatal();
1367                    } else if (mem.eql(u8, arg, "-fcompiler-rt")) {
1368                        want_compiler_rt = true;
1369                    } else if (mem.eql(u8, arg, "-fno-compiler-rt")) {
1370                        want_compiler_rt = false;
1371                    } else if (mem.eql(u8, arg, "-fubsan-rt")) {
1372                        want_ubsan_rt = true;
1373                    } else if (mem.eql(u8, arg, "-fno-ubsan-rt")) {
1374                        want_ubsan_rt = false;
1375                    } else if (mem.eql(u8, arg, "-feach-lib-rpath")) {
1376                        create_module.each_lib_rpath = true;
1377                    } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) {
1378                        create_module.each_lib_rpath = false;
1379                    } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
1380                        try test_exec_args.append(arena, null);
1381                    } else if (mem.eql(u8, arg, "--test-no-exec")) {
1382                        test_no_exec = true;
1383                    } else if (mem.eql(u8, arg, "--time-report")) {
1384                        time_report = true;
1385                    } else if (mem.eql(u8, arg, "-fstack-report")) {
1386                        stack_report = true;
1387                    } else if (mem.eql(u8, arg, "-fPIC")) {
1388                        mod_opts.pic = true;
1389                    } else if (mem.eql(u8, arg, "-fno-PIC")) {
1390                        mod_opts.pic = false;
1391                    } else if (mem.eql(u8, arg, "-fPIE")) {
1392                        create_module.opts.pie = true;
1393                    } else if (mem.eql(u8, arg, "-fno-PIE")) {
1394                        create_module.opts.pie = false;
1395                    } else if (mem.eql(u8, arg, "-flto")) {
1396                        create_module.opts.lto = .full;
1397                    } else if (mem.cutPrefix(u8, arg, "-flto=")) |mode| {
1398                        if (mem.eql(u8, mode, "full")) {
1399                            create_module.opts.lto = .full;
1400                        } else if (mem.eql(u8, mode, "thin")) {
1401                            create_module.opts.lto = .thin;
1402                        } else {
1403                            fatal("Invalid -flto mode: '{s}'. Must be 'full'or 'thin'.", .{mode});
1404                        }
1405                    } else if (mem.eql(u8, arg, "-fno-lto")) {
1406                        create_module.opts.lto = .none;
1407                    } else if (mem.eql(u8, arg, "-funwind-tables")) {
1408                        mod_opts.unwind_tables = .sync;
1409                    } else if (mem.eql(u8, arg, "-fasync-unwind-tables")) {
1410                        mod_opts.unwind_tables = .async;
1411                    } else if (mem.eql(u8, arg, "-fno-unwind-tables")) {
1412                        mod_opts.unwind_tables = .none;
1413                    } else if (mem.eql(u8, arg, "-fstack-check")) {
1414                        mod_opts.stack_check = true;
1415                    } else if (mem.eql(u8, arg, "-fno-stack-check")) {
1416                        mod_opts.stack_check = false;
1417                    } else if (mem.eql(u8, arg, "-fstack-protector")) {
1418                        mod_opts.stack_protector = Compilation.default_stack_protector_buffer_size;
1419                    } else if (mem.eql(u8, arg, "-fno-stack-protector")) {
1420                        mod_opts.stack_protector = 0;
1421                    } else if (mem.eql(u8, arg, "-mred-zone")) {
1422                        mod_opts.red_zone = true;
1423                    } else if (mem.eql(u8, arg, "-mno-red-zone")) {
1424                        mod_opts.red_zone = false;
1425                    } else if (mem.eql(u8, arg, "-fomit-frame-pointer")) {
1426                        mod_opts.omit_frame_pointer = true;
1427                    } else if (mem.eql(u8, arg, "-fno-omit-frame-pointer")) {
1428                        mod_opts.omit_frame_pointer = false;
1429                    } else if (mem.eql(u8, arg, "-fsanitize-c")) {
1430                        mod_opts.sanitize_c = .full;
1431                    } else if (mem.cutPrefix(u8, arg, "-fsanitize-c=")) |mode| {
1432                        if (mem.eql(u8, mode, "trap")) {
1433                            mod_opts.sanitize_c = .trap;
1434                        } else if (mem.eql(u8, mode, "full")) {
1435                            mod_opts.sanitize_c = .full;
1436                        } else {
1437                            fatal("Invalid -fsanitize-c mode: '{s}'. Must be 'trap' or 'full'.", .{mode});
1438                        }
1439                    } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
1440                        mod_opts.sanitize_c = .off;
1441                    } else if (mem.eql(u8, arg, "-fvalgrind")) {
1442                        mod_opts.valgrind = true;
1443                    } else if (mem.eql(u8, arg, "-fno-valgrind")) {
1444                        mod_opts.valgrind = false;
1445                    } else if (mem.eql(u8, arg, "-fsanitize-thread")) {
1446                        mod_opts.sanitize_thread = true;
1447                    } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {
1448                        mod_opts.sanitize_thread = false;
1449                    } else if (mem.eql(u8, arg, "-ffuzz")) {
1450                        mod_opts.fuzz = true;
1451                    } else if (mem.eql(u8, arg, "-fno-fuzz")) {
1452                        mod_opts.fuzz = false;
1453                    } else if (mem.eql(u8, arg, "-fllvm")) {
1454                        create_module.opts.use_llvm = true;
1455                    } else if (mem.eql(u8, arg, "-fno-llvm")) {
1456                        create_module.opts.use_llvm = false;
1457                    } else if (mem.eql(u8, arg, "-flibllvm")) {
1458                        create_module.opts.use_lib_llvm = true;
1459                    } else if (mem.eql(u8, arg, "-fno-libllvm")) {
1460                        create_module.opts.use_lib_llvm = false;
1461                    } else if (mem.eql(u8, arg, "-flld")) {
1462                        create_module.opts.use_lld = true;
1463                    } else if (mem.eql(u8, arg, "-fno-lld")) {
1464                        create_module.opts.use_lld = false;
1465                    } else if (mem.eql(u8, arg, "-fnew-linker")) {
1466                        create_module.opts.use_new_linker = true;
1467                    } else if (mem.eql(u8, arg, "-fno-new-linker")) {
1468                        create_module.opts.use_new_linker = false;
1469                    } else if (mem.eql(u8, arg, "-fclang")) {
1470                        create_module.opts.use_clang = true;
1471                    } else if (mem.eql(u8, arg, "-fno-clang")) {
1472                        create_module.opts.use_clang = false;
1473                    } else if (mem.eql(u8, arg, "-fsanitize-coverage-trace-pc-guard")) {
1474                        create_module.opts.san_cov_trace_pc_guard = true;
1475                    } else if (mem.eql(u8, arg, "-fno-sanitize-coverage-trace-pc-guard")) {
1476                        create_module.opts.san_cov_trace_pc_guard = false;
1477                    } else if (mem.eql(u8, arg, "-freference-trace")) {
1478                        reference_trace = 256;
1479                    } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
1480                        reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
1481                            fatal("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
1482                        };
1483                    } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
1484                        reference_trace = null;
1485                    } else if (mem.eql(u8, arg, "-ferror-tracing")) {
1486                        mod_opts.error_tracing = true;
1487                    } else if (mem.eql(u8, arg, "-fno-error-tracing")) {
1488                        mod_opts.error_tracing = false;
1489                    } else if (mem.eql(u8, arg, "-rdynamic")) {
1490                        create_module.opts.rdynamic = true;
1491                    } else if (mem.eql(u8, arg, "-fsoname")) {
1492                        soname = .yes_default_value;
1493                    } else if (mem.cutPrefix(u8, arg, "-fsoname=")) |rest| {
1494                        soname = .{ .yes = rest };
1495                    } else if (mem.eql(u8, arg, "-fno-soname")) {
1496                        soname = .no;
1497                    } else if (mem.eql(u8, arg, "-femit-bin")) {
1498                        emit_bin = .yes_default_path;
1499                    } else if (mem.cutPrefix(u8, arg, "-femit-bin=")) |rest| {
1500                        emit_bin = .{ .yes = rest };
1501                    } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
1502                        emit_bin = .no;
1503                    } else if (mem.eql(u8, arg, "-femit-h")) {
1504                        emit_h = .yes_default_path;
1505                    } else if (mem.cutPrefix(u8, arg, "-femit-h=")) |rest| {
1506                        emit_h = .{ .yes = rest };
1507                    } else if (mem.eql(u8, arg, "-fno-emit-h")) {
1508                        emit_h = .no;
1509                    } else if (mem.eql(u8, arg, "-femit-asm")) {
1510                        emit_asm = .yes_default_path;
1511                    } else if (mem.cutPrefix(u8, arg, "-femit-asm=")) |rest| {
1512                        emit_asm = .{ .yes = rest };
1513                    } else if (mem.eql(u8, arg, "-fno-emit-asm")) {
1514                        emit_asm = .no;
1515                    } else if (mem.eql(u8, arg, "-femit-llvm-ir")) {
1516                        emit_llvm_ir = .yes_default_path;
1517                    } else if (mem.cutPrefix(u8, arg, "-femit-llvm-ir=")) |rest| {
1518                        emit_llvm_ir = .{ .yes = rest };
1519                    } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
1520                        emit_llvm_ir = .no;
1521                    } else if (mem.eql(u8, arg, "-femit-llvm-bc")) {
1522                        emit_llvm_bc = .yes_default_path;
1523                    } else if (mem.cutPrefix(u8, arg, "-femit-llvm-bc=")) |rest| {
1524                        emit_llvm_bc = .{ .yes = rest };
1525                    } else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) {
1526                        emit_llvm_bc = .no;
1527                    } else if (mem.eql(u8, arg, "-femit-docs")) {
1528                        emit_docs = .yes_default_path;
1529                    } else if (mem.cutPrefix(u8, arg, "-femit-docs=")) |rest| {
1530                        emit_docs = .{ .yes = rest };
1531                    } else if (mem.eql(u8, arg, "-fno-emit-docs")) {
1532                        emit_docs = .no;
1533                    } else if (mem.eql(u8, arg, "-femit-implib")) {
1534                        emit_implib = .yes_default_path;
1535                        emit_implib_arg_provided = true;
1536                    } else if (mem.cutPrefix(u8, arg, "-femit-implib=")) |rest| {
1537                        emit_implib = .{ .yes = rest };
1538                        emit_implib_arg_provided = true;
1539                    } else if (mem.eql(u8, arg, "-fno-emit-implib")) {
1540                        emit_implib = .no;
1541                        emit_implib_arg_provided = true;
1542                    } else if (mem.eql(u8, arg, "-dynamic")) {
1543                        create_module.opts.link_mode = .dynamic;
1544                        lib_preferred_mode = .dynamic;
1545                        lib_search_strategy = .mode_first;
1546                    } else if (mem.eql(u8, arg, "-static")) {
1547                        create_module.opts.link_mode = .static;
1548                        lib_preferred_mode = .static;
1549                        lib_search_strategy = .no_fallback;
1550                    } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
1551                        create_module.opts.dll_export_fns = true;
1552                    } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
1553                        create_module.opts.dll_export_fns = false;
1554                    } else if (mem.eql(u8, arg, "--show-builtin")) {
1555                        show_builtin = true;
1556                        emit_bin = .no;
1557                    } else if (mem.eql(u8, arg, "-fstrip")) {
1558                        mod_opts.strip = true;
1559                    } else if (mem.eql(u8, arg, "-fno-strip")) {
1560                        mod_opts.strip = false;
1561                    } else if (mem.eql(u8, arg, "-gdwarf32")) {
1562                        create_module.opts.debug_format = .{ .dwarf = .@"32" };
1563                    } else if (mem.eql(u8, arg, "-gdwarf64")) {
1564                        create_module.opts.debug_format = .{ .dwarf = .@"64" };
1565                    } else if (mem.eql(u8, arg, "-fformatted-panics")) {
1566                        // Remove this after 0.15.0 is tagged.
1567                        warn("-fformatted-panics is deprecated and does nothing", .{});
1568                    } else if (mem.eql(u8, arg, "-fno-formatted-panics")) {
1569                        // Remove this after 0.15.0 is tagged.
1570                        warn("-fno-formatted-panics is deprecated and does nothing", .{});
1571                    } else if (mem.eql(u8, arg, "-fsingle-threaded")) {
1572                        mod_opts.single_threaded = true;
1573                    } else if (mem.eql(u8, arg, "-fno-single-threaded")) {
1574                        mod_opts.single_threaded = false;
1575                    } else if (mem.eql(u8, arg, "-ffunction-sections")) {
1576                        function_sections = true;
1577                    } else if (mem.eql(u8, arg, "-fno-function-sections")) {
1578                        function_sections = false;
1579                    } else if (mem.eql(u8, arg, "-fdata-sections")) {
1580                        data_sections = true;
1581                    } else if (mem.eql(u8, arg, "-fno-data-sections")) {
1582                        data_sections = false;
1583                    } else if (mem.eql(u8, arg, "-fbuiltin")) {
1584                        mod_opts.no_builtin = false;
1585                    } else if (mem.eql(u8, arg, "-fno-builtin")) {
1586                        mod_opts.no_builtin = true;
1587                    } else if (mem.cutPrefix(u8, arg, "-fopt-bisect-limit=")) |next_arg| {
1588                        llvm_opt_bisect_limit = std.fmt.parseInt(c_int, next_arg, 0) catch |err|
1589                            fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
1590                    } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
1591                        link_eh_frame_hdr = true;
1592                    } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {
1593                        link_eh_frame_hdr = false;
1594                    } else if (mem.eql(u8, arg, "--dynamicbase")) {
1595                        linker_dynamicbase = true;
1596                    } else if (mem.eql(u8, arg, "--no-dynamicbase")) {
1597                        linker_dynamicbase = false;
1598                    } else if (mem.eql(u8, arg, "--emit-relocs")) {
1599                        link_emit_relocs = true;
1600                    } else if (mem.eql(u8, arg, "-fallow-shlib-undefined")) {
1601                        linker_allow_shlib_undefined = true;
1602                    } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {
1603                        linker_allow_shlib_undefined = false;
1604                    } else if (mem.eql(u8, arg, "-fallow-so-scripts")) {
1605                        allow_so_scripts = true;
1606                    } else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
1607                        allow_so_scripts = false;
1608                    } else if (mem.eql(u8, arg, "-z")) {
1609                        const z_arg = args_iter.nextOrFatal();
1610                        if (mem.eql(u8, z_arg, "nodelete")) {
1611                            linker_z_nodelete = true;
1612                        } else if (mem.eql(u8, z_arg, "notext")) {
1613                            linker_z_notext = true;
1614                        } else if (mem.eql(u8, z_arg, "defs")) {
1615                            linker_z_defs = true;
1616                        } else if (mem.eql(u8, z_arg, "undefs")) {
1617                            linker_z_defs = false;
1618                        } else if (mem.eql(u8, z_arg, "origin")) {
1619                            linker_z_origin = true;
1620                        } else if (mem.eql(u8, z_arg, "nocopyreloc")) {
1621                            linker_z_nocopyreloc = true;
1622                        } else if (mem.eql(u8, z_arg, "now")) {
1623                            linker_z_now = true;
1624                        } else if (mem.eql(u8, z_arg, "lazy")) {
1625                            linker_z_now = false;
1626                        } else if (mem.eql(u8, z_arg, "relro")) {
1627                            linker_z_relro = true;
1628                        } else if (mem.eql(u8, z_arg, "norelro")) {
1629                            linker_z_relro = false;
1630                        } else if (prefixedIntArg(z_arg, "common-page-size=")) |int| {
1631                            linker_z_common_page_size = int;
1632                        } else if (prefixedIntArg(z_arg, "max-page-size=")) |int| {
1633                            linker_z_max_page_size = int;
1634                        } else {
1635                            fatal("unsupported linker extension flag: -z {s}", .{z_arg});
1636                        }
1637                    } else if (mem.eql(u8, arg, "--import-memory")) {
1638                        create_module.opts.import_memory = true;
1639                    } else if (mem.eql(u8, arg, "-fentry")) {
1640                        switch (entry) {
1641                            .default, .disabled => entry = .enabled,
1642                            .enabled, .named => {},
1643                        }
1644                    } else if (mem.eql(u8, arg, "-fno-entry")) {
1645                        entry = .disabled;
1646                    } else if (mem.eql(u8, arg, "--export-memory")) {
1647                        create_module.opts.export_memory = true;
1648                    } else if (mem.eql(u8, arg, "--import-symbols")) {
1649                        linker_import_symbols = true;
1650                    } else if (mem.eql(u8, arg, "--import-table")) {
1651                        linker_import_table = true;
1652                    } else if (mem.eql(u8, arg, "--export-table")) {
1653                        linker_export_table = true;
1654                    } else if (prefixedIntArg(arg, "--initial-memory=")) |int| {
1655                        linker_initial_memory = int;
1656                    } else if (prefixedIntArg(arg, "--max-memory=")) |int| {
1657                        linker_max_memory = int;
1658                    } else if (mem.eql(u8, arg, "--shared-memory")) {
1659                        create_module.opts.shared_memory = true;
1660                    } else if (prefixedIntArg(arg, "--global-base=")) |int| {
1661                        linker_global_base = int;
1662                    } else if (mem.cutPrefix(u8, arg, "--export=")) |rest| {
1663                        try linker_export_symbol_names.append(arena, rest);
1664                    } else if (mem.eql(u8, arg, "-Bsymbolic")) {
1665                        linker_bind_global_refs_locally = true;
1666                    } else if (mem.eql(u8, arg, "--gc-sections")) {
1667                        linker_gc_sections = true;
1668                    } else if (mem.eql(u8, arg, "--no-gc-sections")) {
1669                        linker_gc_sections = false;
1670                    } else if (mem.eql(u8, arg, "--build-id")) {
1671                        build_id = .fast;
1672                    } else if (mem.cutPrefix(u8, arg, "--build-id=")) |style| {
1673                        build_id = std.zig.BuildId.parse(style) catch |err| {
1674                            fatal("unable to parse --build-id style '{s}': {s}", .{
1675                                style, @errorName(err),
1676                            });
1677                        };
1678                    } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
1679                        if (build_options.enable_debug_extensions) {
1680                            debug_compile_errors = true;
1681                        } else {
1682                            warn("Zig was compiled without debug extensions. --debug-compile-errors has no effect.", .{});
1683                        }
1684                    } else if (mem.eql(u8, arg, "--verbose-link")) {
1685                        verbose_link = true;
1686                    } else if (mem.eql(u8, arg, "--verbose-cc")) {
1687                        verbose_cc = true;
1688                    } else if (mem.eql(u8, arg, "--verbose-air")) {
1689                        verbose_air = true;
1690                    } else if (mem.eql(u8, arg, "--verbose-intern-pool")) {
1691                        verbose_intern_pool = true;
1692                    } else if (mem.eql(u8, arg, "--verbose-generic-instances")) {
1693                        verbose_generic_instances = true;
1694                    } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
1695                        verbose_llvm_ir = "-";
1696                    } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-ir=")) |rest| {
1697                        verbose_llvm_ir = rest;
1698                    } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-bc=")) |rest| {
1699                        verbose_llvm_bc = rest;
1700                    } else if (mem.eql(u8, arg, "--verbose-cimport")) {
1701                        verbose_cimport = true;
1702                    } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
1703                        verbose_llvm_cpu_features = true;
1704                    } else if (mem.cutPrefix(u8, arg, "-T")) |rest| {
1705                        linker_script = rest;
1706                    } else if (mem.cutPrefix(u8, arg, "-L")) |rest| {
1707                        try create_module.lib_dir_args.append(arena, rest);
1708                    } else if (mem.cutPrefix(u8, arg, "-F")) |rest| {
1709                        try create_module.framework_dirs.append(arena, rest);
1710                    } else if (mem.cutPrefix(u8, arg, "-l")) |name| {
1711                        // We don't know whether this library is part of libc
1712                        // or libc++ until we resolve the target, so we append
1713                        // to the list for now.
1714                        try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1715                            .name = name,
1716                            .query = .{
1717                                .needed = false,
1718                                .weak = false,
1719                                .preferred_mode = lib_preferred_mode,
1720                                .search_strategy = lib_search_strategy,
1721                                .allow_so_scripts = allow_so_scripts,
1722                            },
1723                        } });
1724                    } else if (mem.cutPrefix(u8, arg, "-needed-l")) |name| {
1725                        try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1726                            .name = name,
1727                            .query = .{
1728                                .needed = true,
1729                                .weak = false,
1730                                .preferred_mode = lib_preferred_mode,
1731                                .search_strategy = lib_search_strategy,
1732                                .allow_so_scripts = allow_so_scripts,
1733                            },
1734                        } });
1735                    } else if (mem.cutPrefix(u8, arg, "-weak-l")) |name| {
1736                        try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1737                            .name = name,
1738                            .query = .{
1739                                .needed = false,
1740                                .weak = true,
1741                                .preferred_mode = lib_preferred_mode,
1742                                .search_strategy = lib_search_strategy,
1743                                .allow_so_scripts = allow_so_scripts,
1744                            },
1745                        } });
1746                    } else if (mem.startsWith(u8, arg, "-D")) {
1747                        try cc_argv.append(arena, arg);
1748                    } else if (mem.cutPrefix(u8, arg, "-I")) |rest| {
1749                        try cssan.addIncludePath(arena, &cc_argv, .I, arg, rest, true);
1750                    } else if (mem.cutPrefix(u8, arg, "-x")) |rest| {
1751                        const lang = if (rest.len == 0) args_iter.nextOrFatal() else rest;
1752                        if (mem.eql(u8, lang, "none")) {
1753                            file_ext = null;
1754                        } else if (Compilation.LangToExt.get(lang)) |got_ext| {
1755                            file_ext = got_ext;
1756                        } else {
1757                            fatal("language not recognized: '{s}'", .{lang});
1758                        }
1759                    } else if (mem.cutPrefix(u8, arg, "-mexec-model=")) |rest| {
1760                        create_module.opts.wasi_exec_model = parseWasiExecModel(rest);
1761                    } else if (mem.eql(u8, arg, "-municode")) {
1762                        mingw_unicode_entry_point = true;
1763                    } else {
1764                        fatal("unrecognized parameter: '{s}'", .{arg});
1765                    }
1766                } else switch (file_ext orelse Compilation.classifyFileExt(arg)) {
1767                    .shared_library, .object, .static_library => {
1768                        try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1769                            .path = Path.initCwd(arg),
1770                            .query = .{
1771                                .preferred_mode = lib_preferred_mode,
1772                                .search_strategy = lib_search_strategy,
1773                                .allow_so_scripts = allow_so_scripts,
1774                            },
1775                        } });
1776                        // We do not set `any_dyn_libs` yet because a .so file
1777                        // may actually resolve to a GNU ld script which ends
1778                        // up being a static library.
1779                    },
1780                    .res => {
1781                        try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1782                            .path = Path.initCwd(arg),
1783                            .query = .{
1784                                .preferred_mode = lib_preferred_mode,
1785                                .search_strategy = lib_search_strategy,
1786                                .allow_so_scripts = allow_so_scripts,
1787                            },
1788                        } });
1789                        contains_res_file = true;
1790                    },
1791                    .manifest => {
1792                        if (manifest_file) |other| {
1793                            fatal("only one manifest file can be specified, found '{s}' after '{s}'", .{ arg, other });
1794                        } else manifest_file = arg;
1795                    },
1796                    .assembly, .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .ll, .bc, .m, .mm => {
1797                        dev.check(.c_compiler);
1798                        try create_module.c_source_files.append(arena, .{
1799                            // Populated after module creation.
1800                            .owner = undefined,
1801                            .src_path = arg,
1802                            .extra_flags = try arena.dupe([]const u8, extra_cflags.items),
1803                            // duped when parsing the args.
1804                            .ext = file_ext,
1805                        });
1806                    },
1807                    .rc => {
1808                        dev.check(.win32_resource);
1809                        try create_module.rc_source_files.append(arena, .{
1810                            // Populated after module creation.
1811                            .owner = undefined,
1812                            .src_path = arg,
1813                            .extra_flags = try arena.dupe([]const u8, extra_rcflags.items),
1814                        });
1815                    },
1816                    .zig => {
1817                        if (root_src_file) |other| {
1818                            fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other });
1819                        } else root_src_file = arg;
1820                    },
1821                    .def, .unknown => {
1822                        if (std.ascii.eqlIgnoreCase(".xml", fs.path.extension(arg))) {
1823                            warn("embedded manifest files must have the extension '.manifest'", .{});
1824                        }
1825                        fatal("unrecognized file extension of parameter '{s}'", .{arg});
1826                    },
1827                }
1828            }
1829        },
1830        .cc, .cpp => {
1831            dev.check(.cc_command);
1832
1833            emit_h = .no;
1834            soname = .no;
1835            create_module.opts.ensure_libc_on_non_freestanding = true;
1836            create_module.opts.ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
1837            create_module.want_native_include_dirs = true;
1838            // Clang's driver enables this switch unconditionally.
1839            // Disabling the emission of .eh_frame_hdr can unexpectedly break
1840            // some functionality that depend on it, such as C++ exceptions and
1841            // DWARF-based stack traces.
1842            link_eh_frame_hdr = true;
1843            allow_so_scripts = true;
1844
1845            const COutMode = enum {
1846                link,
1847                object,
1848                assembly,
1849                preprocessor,
1850            };
1851            var c_out_mode: ?COutMode = null;
1852            var out_path: ?[]const u8 = null;
1853            var is_shared_lib = false;
1854            var preprocessor_args = std.array_list.Managed([]const u8).init(arena);
1855            var linker_args = std.array_list.Managed([]const u8).init(arena);
1856            var it = ClangArgIterator.init(arena, all_args);
1857            var emit_llvm = false;
1858            var needed = false;
1859            var must_link = false;
1860            var file_ext: ?Compilation.FileExt = null;
1861            while (it.has_next) {
1862                it.next() catch |err| {
1863                    fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
1864                };
1865                switch (it.zig_equivalent) {
1866                    .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
1867                    .o => {
1868                        // We handle -o /dev/null equivalent to -fno-emit-bin because
1869                        // otherwise our atomic rename into place will fail. This also
1870                        // makes Zig do less work, avoiding pointless file system operations.
1871                        if (mem.eql(u8, it.only_arg, "/dev/null")) {
1872                            emit_bin = .no;
1873                        } else {
1874                            out_path = it.only_arg;
1875                        }
1876                    },
1877                    .c, .r => c_out_mode = .object, // -c or -r
1878                    .asm_only => c_out_mode = .assembly, // -S
1879                    .preprocess_only => c_out_mode = .preprocessor, // -E
1880                    .emit_llvm => emit_llvm = true,
1881                    .x => {
1882                        const lang = mem.sliceTo(it.only_arg, 0);
1883                        if (mem.eql(u8, lang, "none")) {
1884                            file_ext = null;
1885                        } else if (Compilation.LangToExt.get(lang)) |got_ext| {
1886                            file_ext = got_ext;
1887                        } else {
1888                            fatal("language not recognized: '{s}'", .{lang});
1889                        }
1890                    },
1891                    .other => {
1892                        try cc_argv.appendSlice(arena, it.other_args);
1893                    },
1894                    .positional => switch (file_ext orelse Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0))) {
1895                        .assembly, .assembly_with_cpp, .c, .cpp, .ll, .bc, .h, .hpp, .hm, .hmm, .m, .mm => {
1896                            try create_module.c_source_files.append(arena, .{
1897                                // Populated after module creation.
1898                                .owner = undefined,
1899                                .src_path = it.only_arg,
1900                                .ext = file_ext, // duped while parsing the args.
1901                            });
1902                        },
1903                        .unknown, .object, .static_library, .shared_library => {
1904                            try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1905                                .path = Path.initCwd(it.only_arg),
1906                                .query = .{
1907                                    .must_link = must_link,
1908                                    .needed = needed,
1909                                    .preferred_mode = lib_preferred_mode,
1910                                    .search_strategy = lib_search_strategy,
1911                                    .allow_so_scripts = allow_so_scripts,
1912                                },
1913                            } });
1914                            // We do not set `any_dyn_libs` yet because a .so file
1915                            // may actually resolve to a GNU ld script which ends
1916                            // up being a static library.
1917                        },
1918                        .res => {
1919                            try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
1920                                .path = Path.initCwd(it.only_arg),
1921                                .query = .{
1922                                    .must_link = must_link,
1923                                    .needed = needed,
1924                                    .preferred_mode = lib_preferred_mode,
1925                                    .search_strategy = lib_search_strategy,
1926                                    .allow_so_scripts = allow_so_scripts,
1927                                },
1928                            } });
1929                            contains_res_file = true;
1930                        },
1931                        .manifest => {
1932                            if (manifest_file) |other| {
1933                                fatal("only one manifest file can be specified, found '{s}' after previously specified manifest '{s}'", .{ it.only_arg, other });
1934                            } else manifest_file = it.only_arg;
1935                        },
1936                        .def => {
1937                            linker_module_definition_file = it.only_arg;
1938                        },
1939                        .rc => {
1940                            try create_module.rc_source_files.append(arena, .{
1941                                // Populated after module creation.
1942                                .owner = undefined,
1943                                .src_path = it.only_arg,
1944                            });
1945                        },
1946                        .zig => {
1947                            if (root_src_file) |other| {
1948                                fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
1949                            } else root_src_file = it.only_arg;
1950                        },
1951                    },
1952                    .l => {
1953                        // -l
1954                        // We don't know whether this library is part of libc or libc++ until
1955                        // we resolve the target, so we simply append to the list for now.
1956                        if (mem.startsWith(u8, it.only_arg, ":")) {
1957                            // -l :path/to/filename is used when callers need
1958                            // more control over what's in the resulting
1959                            // binary: no extra rpaths and DSO filename exactly
1960                            // as provided. CGo compilation depends on this.
1961                            try create_module.cli_link_inputs.append(arena, .{ .dso_exact = .{
1962                                .name = it.only_arg,
1963                            } });
1964                        } else {
1965                            try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
1966                                .name = it.only_arg,
1967                                .query = .{
1968                                    .must_link = must_link,
1969                                    .needed = needed,
1970                                    .weak = false,
1971                                    .preferred_mode = lib_preferred_mode,
1972                                    .search_strategy = lib_search_strategy,
1973                                    .allow_so_scripts = allow_so_scripts,
1974                                },
1975                            } });
1976                        }
1977                    },
1978                    .ignore => {},
1979                    .driver_punt => {
1980                        // Never mind what we're doing, just pass the args directly. For example --help.
1981                        return process.exit(try clangMain(arena, all_args));
1982                    },
1983                    .pic => mod_opts.pic = true,
1984                    .no_pic => mod_opts.pic = false,
1985                    .pie => create_module.opts.pie = true,
1986                    .no_pie => create_module.opts.pie = false,
1987                    .lto => {
1988                        if (mem.eql(u8, it.only_arg, "flto") or
1989                            mem.eql(u8, it.only_arg, "auto") or
1990                            mem.eql(u8, it.only_arg, "full") or
1991                            mem.eql(u8, it.only_arg, "jobserver"))
1992                        {
1993                            create_module.opts.lto = .full;
1994                        } else if (mem.eql(u8, it.only_arg, "thin")) {
1995                            create_module.opts.lto = .thin;
1996                        } else {
1997                            fatal("Invalid -flto mode: '{s}'. Must be 'auto', 'full', 'thin', or 'jobserver'.", .{it.only_arg});
1998                        }
1999                    },
2000                    .no_lto => create_module.opts.lto = .none,
2001                    .red_zone => mod_opts.red_zone = true,
2002                    .no_red_zone => mod_opts.red_zone = false,
2003                    .omit_frame_pointer => mod_opts.omit_frame_pointer = true,
2004                    .no_omit_frame_pointer => mod_opts.omit_frame_pointer = false,
2005                    .function_sections => function_sections = true,
2006                    .no_function_sections => function_sections = false,
2007                    .data_sections => data_sections = true,
2008                    .no_data_sections => data_sections = false,
2009                    .builtin => mod_opts.no_builtin = false,
2010                    .no_builtin => mod_opts.no_builtin = true,
2011                    .color_diagnostics => color = .on,
2012                    .no_color_diagnostics => color = .off,
2013                    .stack_check => mod_opts.stack_check = true,
2014                    .no_stack_check => mod_opts.stack_check = false,
2015                    .stack_protector => {
2016                        if (mod_opts.stack_protector == null) {
2017                            mod_opts.stack_protector = Compilation.default_stack_protector_buffer_size;
2018                        }
2019                    },
2020                    .no_stack_protector => mod_opts.stack_protector = 0,
2021                    // The way these unwind table options are processed in GCC and Clang is crazy
2022                    // convoluted, and we also don't know the target triple here, so this is all
2023                    // best-effort.
2024                    .unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {
2025                        .none => {
2026                            mod_opts.unwind_tables = .sync;
2027                        },
2028                        .sync, .async => {},
2029                    } else {
2030                        mod_opts.unwind_tables = .sync;
2031                    },
2032                    .no_unwind_tables => mod_opts.unwind_tables = .none,
2033                    .asynchronous_unwind_tables => mod_opts.unwind_tables = .async,
2034                    .no_asynchronous_unwind_tables => if (mod_opts.unwind_tables) |uwt| switch (uwt) {
2035                        .none, .sync => {},
2036                        .async => {
2037                            mod_opts.unwind_tables = .sync;
2038                        },
2039                    } else {
2040                        mod_opts.unwind_tables = .sync;
2041                    },
2042                    .nostdlib => {
2043                        create_module.opts.ensure_libc_on_non_freestanding = false;
2044                        create_module.opts.ensure_libcpp_on_non_freestanding = false;
2045                    },
2046                    .nostdlib_cpp => create_module.opts.ensure_libcpp_on_non_freestanding = false,
2047                    .shared => {
2048                        create_module.opts.link_mode = .dynamic;
2049                        is_shared_lib = true;
2050                    },
2051                    .rdynamic => create_module.opts.rdynamic = true,
2052                    .wp => {
2053                        var split_it = mem.splitScalar(u8, it.only_arg, ',');
2054                        while (split_it.next()) |preprocessor_arg| {
2055                            if (preprocessor_arg.len >= 3 and
2056                                preprocessor_arg[0] == '-' and
2057                                preprocessor_arg[2] != '-')
2058                            {
2059                                if (mem.indexOfScalar(u8, preprocessor_arg, '=')) |equals_pos| {
2060                                    const key = preprocessor_arg[0..equals_pos];
2061                                    const value = preprocessor_arg[equals_pos + 1 ..];
2062                                    try preprocessor_args.append(key);
2063                                    try preprocessor_args.append(value);
2064                                    continue;
2065                                }
2066                            }
2067                            try preprocessor_args.append(preprocessor_arg);
2068                        }
2069                    },
2070                    .wl => {
2071                        var split_it = mem.splitScalar(u8, it.only_arg, ',');
2072                        while (split_it.next()) |linker_arg| {
2073                            // Handle nested-joined args like `-Wl,-rpath=foo`.
2074                            // Must be prefixed with 1 or 2 dashes.
2075                            if (linker_arg.len >= 3 and
2076                                linker_arg[0] == '-' and
2077                                linker_arg[2] != '-')
2078                            {
2079                                if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| {
2080                                    const key = linker_arg[0..equals_pos];
2081                                    const value = linker_arg[equals_pos + 1 ..];
2082                                    if (mem.eql(u8, key, "--build-id")) {
2083                                        build_id = std.zig.BuildId.parse(value) catch |err| {
2084                                            fatal("unable to parse --build-id style '{s}': {s}", .{
2085                                                value, @errorName(err),
2086                                            });
2087                                        };
2088                                        continue;
2089                                    } else if (mem.eql(u8, key, "--sort-common")) {
2090                                        // this ignores --sort=common=<anything>; ignoring plain --sort-common
2091                                        // is done below.
2092                                        continue;
2093                                    }
2094                                    try linker_args.append(key);
2095                                    try linker_args.append(value);
2096                                    continue;
2097                                }
2098                            }
2099                            if (mem.eql(u8, linker_arg, "--build-id")) {
2100                                build_id = .fast;
2101                            } else if (mem.eql(u8, linker_arg, "--as-needed")) {
2102                                needed = false;
2103                            } else if (mem.eql(u8, linker_arg, "--no-as-needed")) {
2104                                needed = true;
2105                            } else if (mem.eql(u8, linker_arg, "-no-pie")) {
2106                                create_module.opts.pie = false;
2107                            } else if (mem.eql(u8, linker_arg, "--sort-common")) {
2108                                // from ld.lld(1): --sort-common is ignored for GNU compatibility,
2109                                // this ignores plain --sort-common
2110                            } else if (mem.eql(u8, linker_arg, "--whole-archive") or
2111                                mem.eql(u8, linker_arg, "-whole-archive"))
2112                            {
2113                                must_link = true;
2114                            } else if (mem.eql(u8, linker_arg, "--no-whole-archive") or
2115                                mem.eql(u8, linker_arg, "-no-whole-archive"))
2116                            {
2117                                must_link = false;
2118                            } else if (mem.eql(u8, linker_arg, "-Bdynamic") or
2119                                mem.eql(u8, linker_arg, "-dy") or
2120                                mem.eql(u8, linker_arg, "-call_shared"))
2121                            {
2122                                lib_search_strategy = .no_fallback;
2123                                lib_preferred_mode = .dynamic;
2124                            } else if (mem.eql(u8, linker_arg, "-Bstatic") or
2125                                mem.eql(u8, linker_arg, "-dn") or
2126                                mem.eql(u8, linker_arg, "-non_shared") or
2127                                mem.eql(u8, linker_arg, "-static"))
2128                            {
2129                                lib_search_strategy = .no_fallback;
2130                                lib_preferred_mode = .static;
2131                            } else if (mem.eql(u8, linker_arg, "-search_paths_first")) {
2132                                lib_search_strategy = .paths_first;
2133                                lib_preferred_mode = .dynamic;
2134                            } else if (mem.eql(u8, linker_arg, "-search_dylibs_first")) {
2135                                lib_search_strategy = .mode_first;
2136                                lib_preferred_mode = .dynamic;
2137                            } else {
2138                                try linker_args.append(linker_arg);
2139                            }
2140                        }
2141                    },
2142                    .san_cov_trace_pc_guard => create_module.opts.san_cov_trace_pc_guard = true,
2143                    .san_cov => {
2144                        var split_it = mem.splitScalar(u8, it.only_arg, ',');
2145                        while (split_it.next()) |san_arg| {
2146                            if (std.mem.eql(u8, san_arg, "trace-pc-guard")) {
2147                                create_module.opts.san_cov_trace_pc_guard = true;
2148                            }
2149                        }
2150                        try cc_argv.appendSlice(arena, it.other_args);
2151                    },
2152                    .no_san_cov => {
2153                        var split_it = mem.splitScalar(u8, it.only_arg, ',');
2154                        while (split_it.next()) |san_arg| {
2155                            if (std.mem.eql(u8, san_arg, "trace-pc-guard")) {
2156                                create_module.opts.san_cov_trace_pc_guard = false;
2157                            }
2158                        }
2159                        try cc_argv.appendSlice(arena, it.other_args);
2160                    },
2161                    .optimize => {
2162                        // Alright, what release mode do they want?
2163                        const level = if (it.only_arg.len >= 1 and it.only_arg[0] == 'O') it.only_arg[1..] else it.only_arg;
2164                        if (mem.eql(u8, level, "s") or
2165                            mem.eql(u8, level, "z"))
2166                        {
2167                            mod_opts.optimize_mode = .ReleaseSmall;
2168                        } else if (mem.eql(u8, level, "1") or
2169                            mem.eql(u8, level, "2") or
2170                            mem.eql(u8, level, "3") or
2171                            mem.eql(u8, level, "4") or
2172                            mem.eql(u8, level, "fast"))
2173                        {
2174                            mod_opts.optimize_mode = .ReleaseFast;
2175                        } else if (mem.eql(u8, level, "g") or
2176                            mem.eql(u8, level, "0"))
2177                        {
2178                            mod_opts.optimize_mode = .Debug;
2179                        } else {
2180                            try cc_argv.appendSlice(arena, it.other_args);
2181                        }
2182                    },
2183                    .debug => {
2184                        mod_opts.strip = false;
2185                        if (mem.eql(u8, it.only_arg, "g")) {
2186                            // We handled with strip = false above.
2187                        } else if (mem.eql(u8, it.only_arg, "g1") or
2188                            mem.eql(u8, it.only_arg, "gline-tables-only"))
2189                        {
2190                            // We handled with strip = false above. but we also want reduced debug info.
2191                            try cc_argv.append(arena, "-gline-tables-only");
2192                        } else {
2193                            try cc_argv.appendSlice(arena, it.other_args);
2194                        }
2195                    },
2196                    .gdwarf32 => {
2197                        mod_opts.strip = false;
2198                        create_module.opts.debug_format = .{ .dwarf = .@"32" };
2199                    },
2200                    .gdwarf64 => {
2201                        mod_opts.strip = false;
2202                        create_module.opts.debug_format = .{ .dwarf = .@"64" };
2203                    },
2204                    .sanitize, .no_sanitize => |t| {
2205                        const enable = t == .sanitize;
2206                        var san_it = std.mem.splitScalar(u8, it.only_arg, ',');
2207                        var recognized_any = false;
2208                        while (san_it.next()) |sub_arg| {
2209                            if (mem.eql(u8, sub_arg, "undefined")) {
2210                                mod_opts.sanitize_c = if (enable) .full else .off;
2211                                recognized_any = true;
2212                            } else if (mem.eql(u8, sub_arg, "thread")) {
2213                                mod_opts.sanitize_thread = enable;
2214                                recognized_any = true;
2215                            } else if (mem.eql(u8, sub_arg, "fuzzer") or mem.eql(u8, sub_arg, "fuzzer-no-link")) {
2216                                mod_opts.fuzz = enable;
2217                                recognized_any = true;
2218                            }
2219                        }
2220                        if (!recognized_any) {
2221                            try cc_argv.appendSlice(arena, it.other_args);
2222                        }
2223                    },
2224                    .sanitize_trap, .no_sanitize_trap => |t| {
2225                        const enable = t == .sanitize_trap;
2226                        var san_it = std.mem.splitScalar(u8, it.only_arg, ',');
2227                        var recognized_any = false;
2228                        while (san_it.next()) |sub_arg| {
2229                            // This logic doesn't match Clang 1:1, but it's probably good enough, and avoids
2230                            // significantly complicating the resolution of the options.
2231                            if (mem.eql(u8, sub_arg, "undefined")) {
2232                                if (mod_opts.sanitize_c) |sc| switch (sc) {
2233                                    .off => if (enable) {
2234                                        mod_opts.sanitize_c = .trap;
2235                                    },
2236                                    .trap => if (!enable) {
2237                                        mod_opts.sanitize_c = .full;
2238                                    },
2239                                    .full => if (enable) {
2240                                        mod_opts.sanitize_c = .trap;
2241                                    },
2242                                } else {
2243                                    if (enable) {
2244                                        mod_opts.sanitize_c = .trap;
2245                                    } else {
2246                                        // This means we were passed `-fno-sanitize-trap=undefined` and nothing else. In
2247                                        // this case, ideally, we should use whatever value `sanitize_c` resolves to by
2248                                        // default, except change `trap` to `full`. However, we don't yet know what
2249                                        // `sanitize_c` will resolve to! So we either have to pick `off` or `full`.
2250                                        //
2251                                        // `full` has the potential to be problematic if `optimize_mode` turns out to
2252                                        // be `ReleaseFast`/`ReleaseSmall` because the user will get a slower and larger
2253                                        // binary than expected. On the other hand, if `optimize_mode` turns out to be
2254                                        // `Debug`/`ReleaseSafe`, `off` would mean UBSan would unexpectedly be disabled.
2255                                        //
2256                                        // `off` seems very slightly less bad, so let's go with that.
2257                                        mod_opts.sanitize_c = .off;
2258                                    }
2259                                }
2260                                recognized_any = true;
2261                            }
2262                        }
2263                        if (!recognized_any) {
2264                            try cc_argv.appendSlice(arena, it.other_args);
2265                        }
2266                    },
2267                    .linker_script => linker_script = it.only_arg,
2268                    .verbose => {
2269                        verbose_link = true;
2270                        // Have Clang print more infos, some tools such as CMake
2271                        // parse this to discover any implicit include and
2272                        // library dir to look-up into.
2273                        try cc_argv.append(arena, "-v");
2274                    },
2275                    .dry_run => {
2276                        // This flag means "dry run". Clang will not actually output anything
2277                        // to the file system.
2278                        verbose_link = true;
2279                        disable_c_depfile = true;
2280                        try cc_argv.append(arena, "-###");
2281                    },
2282                    .for_linker => try linker_args.append(it.only_arg),
2283                    .linker_input_z => {
2284                        try linker_args.append("-z");
2285                        try linker_args.append(it.only_arg);
2286                    },
2287                    .lib_dir => try create_module.lib_dir_args.append(arena, it.only_arg),
2288                    .mcpu => target_mcpu = it.only_arg,
2289                    .m => try create_module.llvm_m_args.append(arena, it.only_arg),
2290                    .dep_file => {
2291                        disable_c_depfile = true;
2292                        try cc_argv.appendSlice(arena, it.other_args);
2293                    },
2294                    .dep_file_to_stdout => { // -M, -MM
2295                        // "Like -MD, but also implies -E and writes to stdout by default"
2296                        // "Like -MMD, but also implies -E and writes to stdout by default"
2297                        c_out_mode = .preprocessor;
2298                        disable_c_depfile = true;
2299                        try cc_argv.appendSlice(arena, it.other_args);
2300                    },
2301                    .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg),
2302                    .framework => try create_module.frameworks.put(arena, it.only_arg, .{}),
2303                    .nostdlibinc => create_module.want_native_include_dirs = false,
2304                    .strip => mod_opts.strip = true,
2305                    .exec_model => {
2306                        create_module.opts.wasi_exec_model = parseWasiExecModel(it.only_arg);
2307                    },
2308                    .sysroot => {
2309                        create_module.sysroot = it.only_arg;
2310                    },
2311                    .entry => {
2312                        entry = .{ .named = it.only_arg };
2313                    },
2314                    .force_undefined_symbol => {
2315                        try force_undefined_symbols.put(arena, it.only_arg, {});
2316                    },
2317                    .force_load_objc => force_load_objc = true,
2318                    .mingw_unicode_entry_point => mingw_unicode_entry_point = true,
2319                    .weak_library => try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2320                        .name = it.only_arg,
2321                        .query = .{
2322                            .needed = false,
2323                            .weak = true,
2324                            .preferred_mode = lib_preferred_mode,
2325                            .search_strategy = lib_search_strategy,
2326                            .allow_so_scripts = allow_so_scripts,
2327                        },
2328                    } }),
2329                    .weak_framework => try create_module.frameworks.put(arena, it.only_arg, .{ .weak = true }),
2330                    .headerpad_max_install_names => headerpad_max_install_names = true,
2331                    .compress_debug_sections => {
2332                        if (it.only_arg.len == 0) {
2333                            linker_compress_debug_sections = .zlib;
2334                        } else {
2335                            linker_compress_debug_sections = std.meta.stringToEnum(std.zig.CompressDebugSections, it.only_arg) orelse {
2336                                fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{it.only_arg});
2337                            };
2338                        }
2339                    },
2340                    .install_name => {
2341                        install_name = it.only_arg;
2342                    },
2343                    .undefined => {
2344                        if (mem.eql(u8, "dynamic_lookup", it.only_arg)) {
2345                            linker_allow_shlib_undefined = true;
2346                        } else if (mem.eql(u8, "error", it.only_arg)) {
2347                            linker_allow_shlib_undefined = false;
2348                        } else {
2349                            fatal("unsupported -undefined option '{s}'", .{it.only_arg});
2350                        }
2351                    },
2352                    .rtlib => {
2353                        // Unlike Clang, we support `none` for explicitly omitting compiler-rt.
2354                        if (mem.eql(u8, "none", it.only_arg)) {
2355                            want_compiler_rt = false;
2356                        } else if (mem.eql(u8, "compiler-rt", it.only_arg) or
2357                            mem.eql(u8, "libgcc", it.only_arg))
2358                        {
2359                            want_compiler_rt = true;
2360                        } else {
2361                            // Note that we don't support `platform`.
2362                            fatal("unsupported -rtlib option '{s}'", .{it.only_arg});
2363                        }
2364                    },
2365                    .static => {
2366                        create_module.opts.link_mode = .static;
2367                        lib_preferred_mode = .static;
2368                        lib_search_strategy = .no_fallback;
2369                    },
2370                    .dynamic => {
2371                        create_module.opts.link_mode = .dynamic;
2372                        lib_preferred_mode = .dynamic;
2373                        lib_search_strategy = .mode_first;
2374                    },
2375                }
2376            }
2377            // Parse linker args.
2378            var linker_args_it = ArgsIterator{
2379                .args = linker_args.items,
2380            };
2381            while (linker_args_it.next()) |arg| {
2382                if (mem.eql(u8, arg, "-soname") or
2383                    mem.eql(u8, arg, "--soname"))
2384                {
2385                    const name = linker_args_it.nextOrFatal();
2386                    soname = .{ .yes = name };
2387                    // Use it as --name.
2388                    // Example: libsoundio.so.2
2389                    var prefix: usize = 0;
2390                    if (mem.startsWith(u8, name, "lib")) {
2391                        prefix = 3;
2392                    }
2393                    var end: usize = name.len;
2394                    if (mem.endsWith(u8, name, ".so")) {
2395                        end -= 3;
2396                    } else {
2397                        var found_digit = false;
2398                        while (end > 0 and std.ascii.isDigit(name[end - 1])) {
2399                            found_digit = true;
2400                            end -= 1;
2401                        }
2402                        if (found_digit and end > 0 and name[end - 1] == '.') {
2403                            end -= 1;
2404                        } else {
2405                            end = name.len;
2406                        }
2407                        if (mem.endsWith(u8, name[prefix..end], ".so")) {
2408                            end -= 3;
2409                        }
2410                    }
2411                    provided_name = name[prefix..end];
2412                } else if (mem.eql(u8, arg, "-rpath") or mem.eql(u8, arg, "--rpath") or mem.eql(u8, arg, "-R")) {
2413                    try create_module.rpath_list.append(arena, linker_args_it.nextOrFatal());
2414                } else if (mem.eql(u8, arg, "--subsystem")) {
2415                    subsystem = try parseSubsystem(linker_args_it.nextOrFatal());
2416                } else if (mem.eql(u8, arg, "-I") or
2417                    mem.eql(u8, arg, "--dynamic-linker") or
2418                    mem.eql(u8, arg, "-dynamic-linker"))
2419                {
2420                    create_module.dynamic_linker = linker_args_it.nextOrFatal();
2421                } else if (mem.eql(u8, arg, "-I") or
2422                    mem.eql(u8, arg, "--no-dynamic-linker") or
2423                    mem.eql(u8, arg, "-no-dynamic-linker"))
2424                {
2425                    create_module.dynamic_linker = "";
2426                } else if (mem.eql(u8, arg, "-E") or
2427                    mem.eql(u8, arg, "--export-dynamic") or
2428                    mem.eql(u8, arg, "-export-dynamic"))
2429                {
2430                    create_module.opts.rdynamic = true;
2431                } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) {
2432                    version_script = linker_args_it.nextOrFatal();
2433                } else if (mem.eql(u8, arg, "--undefined-version")) {
2434                    linker_allow_undefined_version = true;
2435                } else if (mem.eql(u8, arg, "--no-undefined-version")) {
2436                    linker_allow_undefined_version = false;
2437                } else if (mem.eql(u8, arg, "--enable-new-dtags")) {
2438                    linker_enable_new_dtags = true;
2439                } else if (mem.eql(u8, arg, "--disable-new-dtags")) {
2440                    linker_enable_new_dtags = false;
2441                } else if (mem.eql(u8, arg, "-O")) {
2442                    linker_optimization = linker_args_it.nextOrFatal();
2443                } else if (mem.cutPrefix(u8, arg, "-O")) |rest| {
2444                    linker_optimization = rest;
2445                } else if (mem.eql(u8, arg, "-pagezero_size")) {
2446                    const next_arg = linker_args_it.nextOrFatal();
2447                    pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| {
2448                        fatal("unable to parse pagezero size '{s}': {s}", .{ next_arg, @errorName(err) });
2449                    };
2450                } else if (mem.eql(u8, arg, "-headerpad")) {
2451                    const next_arg = linker_args_it.nextOrFatal();
2452                    headerpad_size = std.fmt.parseUnsigned(u32, eatIntPrefix(next_arg, 16), 16) catch |err| {
2453                        fatal("unable to parse  headerpad size '{s}': {s}", .{ next_arg, @errorName(err) });
2454                    };
2455                } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
2456                    headerpad_max_install_names = true;
2457                } else if (mem.eql(u8, arg, "-dead_strip")) {
2458                    linker_gc_sections = true;
2459                } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
2460                    dead_strip_dylibs = true;
2461                } else if (mem.eql(u8, arg, "-ObjC")) {
2462                    force_load_objc = true;
2463                } else if (mem.eql(u8, arg, "--no-undefined")) {
2464                    linker_z_defs = true;
2465                } else if (mem.eql(u8, arg, "--gc-sections")) {
2466                    linker_gc_sections = true;
2467                } else if (mem.eql(u8, arg, "--no-gc-sections")) {
2468                    linker_gc_sections = false;
2469                } else if (mem.eql(u8, arg, "--print-gc-sections")) {
2470                    linker_print_gc_sections = true;
2471                } else if (mem.eql(u8, arg, "--print-icf-sections")) {
2472                    linker_print_icf_sections = true;
2473                } else if (mem.eql(u8, arg, "--print-map")) {
2474                    linker_print_map = true;
2475                } else if (mem.eql(u8, arg, "--sort-section")) {
2476                    const arg1 = linker_args_it.nextOrFatal();
2477                    linker_sort_section = std.meta.stringToEnum(link.File.Lld.Elf.SortSection, arg1) orelse {
2478                        fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1});
2479                    };
2480                } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
2481                    mem.eql(u8, arg, "-allow-shlib-undefined"))
2482                {
2483                    linker_allow_shlib_undefined = true;
2484                } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
2485                    mem.eql(u8, arg, "-no-allow-shlib-undefined"))
2486                {
2487                    linker_allow_shlib_undefined = false;
2488                } else if (mem.eql(u8, arg, "-Bsymbolic")) {
2489                    linker_bind_global_refs_locally = true;
2490                } else if (mem.eql(u8, arg, "--import-memory")) {
2491                    create_module.opts.import_memory = true;
2492                } else if (mem.eql(u8, arg, "--export-memory")) {
2493                    create_module.opts.export_memory = true;
2494                } else if (mem.eql(u8, arg, "--import-symbols")) {
2495                    linker_import_symbols = true;
2496                } else if (mem.eql(u8, arg, "--import-table")) {
2497                    linker_import_table = true;
2498                } else if (mem.eql(u8, arg, "--export-table")) {
2499                    linker_export_table = true;
2500                } else if (mem.eql(u8, arg, "--no-entry")) {
2501                    entry = .disabled;
2502                } else if (mem.eql(u8, arg, "--initial-memory")) {
2503                    const next_arg = linker_args_it.nextOrFatal();
2504                    linker_initial_memory = std.fmt.parseUnsigned(u32, next_arg, 10) catch |err| {
2505                        fatal("unable to parse initial memory size '{s}': {s}", .{ next_arg, @errorName(err) });
2506                    };
2507                } else if (mem.eql(u8, arg, "--max-memory")) {
2508                    const next_arg = linker_args_it.nextOrFatal();
2509                    linker_max_memory = std.fmt.parseUnsigned(u32, next_arg, 10) catch |err| {
2510                        fatal("unable to parse max memory size '{s}': {s}", .{ next_arg, @errorName(err) });
2511                    };
2512                } else if (mem.eql(u8, arg, "--shared-memory")) {
2513                    create_module.opts.shared_memory = true;
2514                } else if (mem.eql(u8, arg, "--global-base")) {
2515                    const next_arg = linker_args_it.nextOrFatal();
2516                    linker_global_base = std.fmt.parseUnsigned(u32, next_arg, 10) catch |err| {
2517                        fatal("unable to parse global base '{s}': {s}", .{ next_arg, @errorName(err) });
2518                    };
2519                } else if (mem.eql(u8, arg, "--export")) {
2520                    try linker_export_symbol_names.append(arena, linker_args_it.nextOrFatal());
2521                } else if (mem.eql(u8, arg, "--compress-debug-sections")) {
2522                    const arg1 = linker_args_it.nextOrFatal();
2523                    linker_compress_debug_sections = std.meta.stringToEnum(std.zig.CompressDebugSections, arg1) orelse {
2524                        fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{arg1});
2525                    };
2526                } else if (mem.cutPrefix(u8, arg, "-z")) |z_rest| {
2527                    const z_arg = if (z_rest.len == 0) linker_args_it.nextOrFatal() else z_rest;
2528                    if (mem.eql(u8, z_arg, "nodelete")) {
2529                        linker_z_nodelete = true;
2530                    } else if (mem.eql(u8, z_arg, "notext")) {
2531                        linker_z_notext = true;
2532                    } else if (mem.eql(u8, z_arg, "defs")) {
2533                        linker_z_defs = true;
2534                    } else if (mem.eql(u8, z_arg, "undefs")) {
2535                        linker_z_defs = false;
2536                    } else if (mem.eql(u8, z_arg, "origin")) {
2537                        linker_z_origin = true;
2538                    } else if (mem.eql(u8, z_arg, "nocopyreloc")) {
2539                        linker_z_nocopyreloc = true;
2540                    } else if (mem.eql(u8, z_arg, "noexecstack")) {
2541                        // noexecstack is the default when linking with LLD
2542                    } else if (mem.eql(u8, z_arg, "now")) {
2543                        linker_z_now = true;
2544                    } else if (mem.eql(u8, z_arg, "lazy")) {
2545                        linker_z_now = false;
2546                    } else if (mem.eql(u8, z_arg, "relro")) {
2547                        linker_z_relro = true;
2548                    } else if (mem.eql(u8, z_arg, "norelro")) {
2549                        linker_z_relro = false;
2550                    } else if (mem.cutPrefix(u8, z_arg, "stack-size=")) |rest| {
2551                        stack_size = parseStackSize(rest);
2552                    } else if (prefixedIntArg(z_arg, "common-page-size=")) |int| {
2553                        linker_z_common_page_size = int;
2554                    } else if (prefixedIntArg(z_arg, "max-page-size=")) |int| {
2555                        linker_z_max_page_size = int;
2556                    } else {
2557                        fatal("unsupported linker extension flag: -z {s}", .{z_arg});
2558                    }
2559                } else if (mem.eql(u8, arg, "--major-image-version")) {
2560                    const major = linker_args_it.nextOrFatal();
2561                    version.major = std.fmt.parseUnsigned(u32, major, 10) catch |err| {
2562                        fatal("unable to parse major image version '{s}': {s}", .{ major, @errorName(err) });
2563                    };
2564                    have_version = true;
2565                } else if (mem.eql(u8, arg, "--minor-image-version")) {
2566                    const minor = linker_args_it.nextOrFatal();
2567                    version.minor = std.fmt.parseUnsigned(u32, minor, 10) catch |err| {
2568                        fatal("unable to parse minor image version '{s}': {s}", .{ minor, @errorName(err) });
2569                    };
2570                    have_version = true;
2571                } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) {
2572                    entry = .{ .named = linker_args_it.nextOrFatal() };
2573                } else if (mem.eql(u8, arg, "-u")) {
2574                    try force_undefined_symbols.put(arena, linker_args_it.nextOrFatal(), {});
2575                } else if (mem.eql(u8, arg, "-x") or mem.eql(u8, arg, "--discard-all")) {
2576                    discard_local_symbols = true;
2577                } else if (mem.eql(u8, arg, "--stack") or mem.eql(u8, arg, "-stack_size")) {
2578                    stack_size = parseStackSize(linker_args_it.nextOrFatal());
2579                } else if (mem.eql(u8, arg, "--image-base")) {
2580                    image_base = parseImageBase(linker_args_it.nextOrFatal());
2581                } else if (mem.eql(u8, arg, "--enable-auto-image-base") or
2582                    mem.eql(u8, arg, "--disable-auto-image-base"))
2583                {
2584                    // `--enable-auto-image-base` is a flag that binutils added in ~2000 for MinGW.
2585                    // It does a hash of the file and uses that as part of the image base value.
2586                    // Presumably the idea was to avoid DLLs needing to be relocated when loaded.
2587                    // This is practically irrelevant today as all PEs produced since Windows Vista
2588                    // have ASLR enabled by default anyway, and Windows 10+ has Mandatory ASLR which
2589                    // doesn't even care what the PE file wants and relocates it anyway.
2590                    //
2591                    // Unfortunately, Libtool hardcodes usage of this archaic flag when targeting
2592                    // MinGW, so to make `zig cc` for that use case work, accept and ignore the
2593                    // flag, and warn the user that it has no effect.
2594                    warn("auto-image-base options are unimplemented and ignored", .{});
2595                } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
2596                    linker_script = linker_args_it.nextOrFatal();
2597                } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
2598                    link_eh_frame_hdr = true;
2599                } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {
2600                    link_eh_frame_hdr = false;
2601                } else if (mem.eql(u8, arg, "--tsaware")) {
2602                    linker_tsaware = true;
2603                } else if (mem.eql(u8, arg, "--nxcompat")) {
2604                    linker_nxcompat = true;
2605                } else if (mem.eql(u8, arg, "--dynamicbase")) {
2606                    linker_dynamicbase = true;
2607                } else if (mem.eql(u8, arg, "--no-dynamicbase")) {
2608                    linker_dynamicbase = false;
2609                } else if (mem.eql(u8, arg, "--high-entropy-va")) {
2610                    // This option does not do anything.
2611                } else if (mem.eql(u8, arg, "--export-all-symbols")) {
2612                    create_module.opts.rdynamic = true;
2613                } else if (mem.eql(u8, arg, "--color-diagnostics") or
2614                    mem.eql(u8, arg, "--color-diagnostics=always"))
2615                {
2616                    color = .on;
2617                } else if (mem.eql(u8, arg, "--no-color-diagnostics") or
2618                    mem.eql(u8, arg, "--color-diagnostics=never"))
2619                {
2620                    color = .off;
2621                } else if (mem.eql(u8, arg, "-s") or mem.eql(u8, arg, "--strip-all") or
2622                    mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--strip-debug"))
2623                {
2624                    // -s, --strip-all             Strip all symbols
2625                    // -S, --strip-debug           Strip debugging symbols
2626                    mod_opts.strip = true;
2627                } else if (mem.eql(u8, arg, "--start-group") or
2628                    mem.eql(u8, arg, "--end-group"))
2629                {
2630                    // We don't need to care about these because these args are
2631                    // for resolving circular dependencies but our linker takes
2632                    // care of this without explicit args.
2633                } else if (mem.eql(u8, arg, "--major-os-version") or
2634                    mem.eql(u8, arg, "--minor-os-version"))
2635                {
2636                    // This option does not do anything.
2637                    _ = linker_args_it.nextOrFatal();
2638                } else if (mem.eql(u8, arg, "--major-subsystem-version")) {
2639                    const major = linker_args_it.nextOrFatal();
2640                    major_subsystem_version = std.fmt.parseUnsigned(u16, major, 10) catch |err| {
2641                        fatal("unable to parse major subsystem version '{s}': {s}", .{
2642                            major, @errorName(err),
2643                        });
2644                    };
2645                } else if (mem.eql(u8, arg, "--minor-subsystem-version")) {
2646                    const minor = linker_args_it.nextOrFatal();
2647                    minor_subsystem_version = std.fmt.parseUnsigned(u16, minor, 10) catch |err| {
2648                        fatal("unable to parse minor subsystem version '{s}': {s}", .{
2649                            minor, @errorName(err),
2650                        });
2651                    };
2652                } else if (mem.eql(u8, arg, "-framework")) {
2653                    try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{});
2654                } else if (mem.eql(u8, arg, "-weak_framework")) {
2655                    try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .weak = true });
2656                } else if (mem.eql(u8, arg, "-needed_framework")) {
2657                    try create_module.frameworks.put(arena, linker_args_it.nextOrFatal(), .{ .needed = true });
2658                } else if (mem.eql(u8, arg, "-needed_library")) {
2659                    try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2660                        .name = linker_args_it.nextOrFatal(),
2661                        .query = .{
2662                            .weak = false,
2663                            .needed = true,
2664                            .preferred_mode = lib_preferred_mode,
2665                            .search_strategy = lib_search_strategy,
2666                            .allow_so_scripts = allow_so_scripts,
2667                        },
2668                    } });
2669                } else if (mem.cutPrefix(u8, arg, "-weak-l")) |rest| {
2670                    try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2671                        .name = rest,
2672                        .query = .{
2673                            .weak = true,
2674                            .needed = false,
2675                            .preferred_mode = lib_preferred_mode,
2676                            .search_strategy = lib_search_strategy,
2677                            .allow_so_scripts = allow_so_scripts,
2678                        },
2679                    } });
2680                } else if (mem.eql(u8, arg, "-weak_library")) {
2681                    try create_module.cli_link_inputs.append(arena, .{ .name_query = .{
2682                        .name = linker_args_it.nextOrFatal(),
2683                        .query = .{
2684                            .weak = true,
2685                            .needed = false,
2686                            .preferred_mode = lib_preferred_mode,
2687                            .search_strategy = lib_search_strategy,
2688                            .allow_so_scripts = allow_so_scripts,
2689                        },
2690                    } });
2691                } else if (mem.eql(u8, arg, "-compatibility_version")) {
2692                    const compat_version = linker_args_it.nextOrFatal();
2693                    compatibility_version = std.SemanticVersion.parse(compat_version) catch |err| {
2694                        fatal("unable to parse -compatibility_version '{s}': {s}", .{ compat_version, @errorName(err) });
2695                    };
2696                } else if (mem.eql(u8, arg, "-current_version")) {
2697                    const curr_version = linker_args_it.nextOrFatal();
2698                    version = std.SemanticVersion.parse(curr_version) catch |err| {
2699                        fatal("unable to parse -current_version '{s}': {s}", .{ curr_version, @errorName(err) });
2700                    };
2701                    have_version = true;
2702                } else if (mem.eql(u8, arg, "--out-implib") or
2703                    mem.eql(u8, arg, "-implib"))
2704                {
2705                    emit_implib = .{ .yes = linker_args_it.nextOrFatal() };
2706                    emit_implib_arg_provided = true;
2707                } else if (mem.eql(u8, arg, "-Brepro") or mem.eql(u8, arg, "/Brepro")) {
2708                    linker_repro = true;
2709                } else if (mem.eql(u8, arg, "-undefined")) {
2710                    const lookup_type = linker_args_it.nextOrFatal();
2711                    if (mem.eql(u8, "dynamic_lookup", lookup_type)) {
2712                        linker_allow_shlib_undefined = true;
2713                    } else if (mem.eql(u8, "error", lookup_type)) {
2714                        linker_allow_shlib_undefined = false;
2715                    } else {
2716                        fatal("unsupported -undefined option '{s}'", .{lookup_type});
2717                    }
2718                } else if (mem.eql(u8, arg, "-install_name")) {
2719                    install_name = linker_args_it.nextOrFatal();
2720                } else if (mem.eql(u8, arg, "-force_load")) {
2721                    try create_module.cli_link_inputs.append(arena, .{ .path_query = .{
2722                        .path = Path.initCwd(linker_args_it.nextOrFatal()),
2723                        .query = .{
2724                            .must_link = true,
2725                            .preferred_mode = .static,
2726                            .search_strategy = .no_fallback,
2727                        },
2728                    } });
2729                } else if (mem.eql(u8, arg, "-hash-style") or
2730                    mem.eql(u8, arg, "--hash-style"))
2731                {
2732                    const next_arg = linker_args_it.nextOrFatal();
2733                    hash_style = std.meta.stringToEnum(link.File.Lld.Elf.HashStyle, next_arg) orelse {
2734                        fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{
2735                            next_arg,
2736                        });
2737                    };
2738                } else if (mem.eql(u8, arg, "-wrap")) {
2739                    const next_arg = linker_args_it.nextOrFatal();
2740                    try symbol_wrap_set.put(arena, next_arg, {});
2741                } else if (mem.startsWith(u8, arg, "/subsystem:")) {
2742                    var split_it = mem.splitBackwardsScalar(u8, arg, ':');
2743                    subsystem = try parseSubsystem(split_it.first());
2744                } else if (mem.startsWith(u8, arg, "/implib:")) {
2745                    var split_it = mem.splitBackwardsScalar(u8, arg, ':');
2746                    emit_implib = .{ .yes = split_it.first() };
2747                    emit_implib_arg_provided = true;
2748                } else if (mem.startsWith(u8, arg, "/pdb:")) {
2749                    var split_it = mem.splitBackwardsScalar(u8, arg, ':');
2750                    pdb_out_path = split_it.first();
2751                } else if (mem.startsWith(u8, arg, "/version:")) {
2752                    var split_it = mem.splitBackwardsScalar(u8, arg, ':');
2753                    const version_arg = split_it.first();
2754                    version = std.SemanticVersion.parse(version_arg) catch |err| {
2755                        fatal("unable to parse /version '{s}': {s}", .{ arg, @errorName(err) });
2756                    };
2757                    have_version = true;
2758                } else if (mem.eql(u8, arg, "-V")) {
2759                    warn("ignoring request for supported emulations: unimplemented", .{});
2760                } else if (mem.eql(u8, arg, "-v")) {
2761                    try fs.File.stdout().writeAll("zig ld " ++ build_options.version ++ "\n");
2762                } else if (mem.eql(u8, arg, "--version")) {
2763                    try fs.File.stdout().writeAll("zig ld " ++ build_options.version ++ "\n");
2764                    process.exit(0);
2765                } else {
2766                    fatal("unsupported linker arg: {s}", .{arg});
2767                }
2768            }
2769
2770            // Parse preprocessor args.
2771            var preprocessor_args_it = ArgsIterator{
2772                .args = preprocessor_args.items,
2773            };
2774            while (preprocessor_args_it.next()) |arg| {
2775                if (mem.eql(u8, arg, "-MD") or mem.eql(u8, arg, "-MMD") or mem.eql(u8, arg, "-MT")) {
2776                    disable_c_depfile = true;
2777                    const cc_arg = try std.fmt.allocPrint(arena, "-Wp,{s},{s}", .{ arg, preprocessor_args_it.nextOrFatal() });
2778                    try cc_argv.append(arena, cc_arg);
2779                } else {
2780                    fatal("unsupported preprocessor arg: {s}", .{arg});
2781                }
2782            }
2783
2784            if (mod_opts.sanitize_c) |wsc| {
2785                if (wsc != .off and mod_opts.optimize_mode == .ReleaseFast) {
2786                    mod_opts.optimize_mode = .ReleaseSafe;
2787                }
2788            }
2789
2790            // precompiled header syntax: "zig cc -x c-header test.h -o test.pch"
2791            const emit_pch = ((file_ext == .h or file_ext == .hpp or file_ext == .hm or file_ext == .hmm) and c_out_mode == null);
2792            if (emit_pch)
2793                c_out_mode = .preprocessor;
2794
2795            switch (c_out_mode orelse .link) {
2796                .link => {
2797                    create_module.opts.output_mode = if (is_shared_lib) .Lib else .Exe;
2798                    if (emit_bin != .no) {
2799                        emit_bin = if (out_path) |p| .{ .yes = p } else .yes_a_out;
2800                    }
2801                    if (emit_llvm) {
2802                        fatal("-emit-llvm cannot be used when linking", .{});
2803                    }
2804                },
2805                .object => {
2806                    create_module.opts.output_mode = .Obj;
2807                    if (emit_llvm) {
2808                        emit_bin = .no;
2809                        if (out_path) |p| {
2810                            emit_llvm_bc = .{ .yes = p };
2811                        } else {
2812                            emit_llvm_bc = .yes_default_path;
2813                        }
2814                    } else {
2815                        if (out_path) |p| {
2816                            emit_bin = .{ .yes = p };
2817                        } else {
2818                            emit_bin = .yes_default_path;
2819                        }
2820                    }
2821                },
2822                .assembly => {
2823                    create_module.opts.output_mode = .Obj;
2824                    emit_bin = .no;
2825                    if (emit_llvm) {
2826                        if (out_path) |p| {
2827                            emit_llvm_ir = .{ .yes = p };
2828                        } else {
2829                            emit_llvm_ir = .yes_default_path;
2830                        }
2831                    } else {
2832                        if (out_path) |p| {
2833                            emit_asm = .{ .yes = p };
2834                        } else {
2835                            emit_asm = .yes_default_path;
2836                        }
2837                    }
2838                },
2839                .preprocessor => {
2840                    create_module.opts.output_mode = .Obj;
2841                    // An error message is generated when there is more than 1 C source file.
2842                    if (create_module.c_source_files.items.len != 1) {
2843                        // For example `zig cc` and no args should print the "no input files" message.
2844                        return process.exit(try clangMain(arena, all_args));
2845                    }
2846                    if (emit_pch) {
2847                        emit_bin = if (out_path) |p| .{ .yes = p } else .yes_default_path;
2848                        clang_preprocessor_mode = .pch;
2849                    } else {
2850                        // If the output path is "-" (stdout), then we need to emit the preprocessed output to stdout
2851                        // like "clang -E main.c -o -" does.
2852                        if (out_path != null and !mem.eql(u8, out_path.?, "-")) {
2853                            emit_bin = .{ .yes = out_path.? };
2854                            clang_preprocessor_mode = .yes;
2855                        } else {
2856                            emit_bin = .no;
2857                            clang_preprocessor_mode = .stdout;
2858                        }
2859                    }
2860                },
2861            }
2862            if (create_module.c_source_files.items.len == 0 and
2863                !anyObjectLinkInputs(create_module.cli_link_inputs.items) and
2864                root_src_file == null)
2865            {
2866                // For example `zig cc` and no args should print the "no input files" message.
2867                // There could be other reasons to punt to clang, for example, --help.
2868                return process.exit(try clangMain(arena, all_args));
2869            }
2870        },
2871    }
2872
2873    if (arg_mode == .zig_test_obj and !test_no_exec and listen == .none) {
2874        fatal("test-obj requires --test-no-exec", .{});
2875    }
2876
2877    if (time_report and listen == .none) {
2878        fatal("--time-report requires --listen", .{});
2879    }
2880
2881    if (arg_mode == .translate_c and create_module.c_source_files.items.len != 1) {
2882        fatal("translate-c expects exactly 1 source file (found {d})", .{create_module.c_source_files.items.len});
2883    }
2884
2885    if (show_builtin and root_src_file == null) {
2886        // Without this, there will be no main module created and no zig
2887        // compilation unit, and therefore also no builtin.zig contents
2888        // created.
2889        root_src_file = "builtin.zig";
2890    }
2891
2892    implicit_root_mod: {
2893        const src_path = b: {
2894            if (root_src_file) |src_path| {
2895                if (create_module.modules.count() != 0) {
2896                    fatal("main module provided both by '-M{s}={s}{c}{s}' and by positional argument '{s}'", .{
2897                        create_module.modules.keys()[0],
2898                        create_module.modules.values()[0].root_path,
2899                        fs.path.sep,
2900                        create_module.modules.values()[0].root_src_path,
2901                        src_path,
2902                    });
2903                }
2904                create_module.opts.have_zcu = true;
2905                break :b src_path;
2906            }
2907
2908            if (create_module.modules.count() != 0)
2909                break :implicit_root_mod;
2910
2911            if (create_module.c_source_files.items.len >= 1)
2912                break :b create_module.c_source_files.items[0].src_path;
2913
2914            for (create_module.cli_link_inputs.items) |unresolved_link_input| switch (unresolved_link_input) {
2915                // Intentionally includes dynamic libraries provided by file path.
2916                .path_query => |pq| break :b pq.path.sub_path,
2917                else => continue,
2918            };
2919
2920            if (emit_bin == .yes)
2921                break :b emit_bin.yes;
2922
2923            if (create_module.rc_source_files.items.len >= 1)
2924                break :b create_module.rc_source_files.items[0].src_path;
2925
2926            if (arg_mode == .run)
2927                fatal("`zig run` expects at least one positional argument", .{});
2928
2929            fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{});
2930
2931            break :implicit_root_mod;
2932        };
2933
2934        // See duplicate logic: ModCreationGlobalFlags
2935        if (mod_opts.single_threaded == false)
2936            create_module.opts.any_non_single_threaded = true;
2937        if (mod_opts.sanitize_thread == true)
2938            create_module.opts.any_sanitize_thread = true;
2939        if (mod_opts.sanitize_c) |sc| switch (sc) {
2940            .off => {},
2941            .trap => if (create_module.opts.any_sanitize_c == .off) {
2942                create_module.opts.any_sanitize_c = .trap;
2943            },
2944            .full => create_module.opts.any_sanitize_c = .full,
2945        };
2946        if (mod_opts.fuzz == true)
2947            create_module.opts.any_fuzz = true;
2948        if (mod_opts.unwind_tables) |uwt| switch (uwt) {
2949            .none => {},
2950            .sync, .async => create_module.opts.any_unwind_tables = true,
2951        };
2952        if (mod_opts.strip == false)
2953            create_module.opts.any_non_stripped = true;
2954        if (mod_opts.error_tracing == true)
2955            create_module.opts.any_error_tracing = true;
2956
2957        const name = switch (arg_mode) {
2958            .zig_test => "test",
2959            .build, .cc, .cpp, .translate_c, .zig_test_obj, .run => fs.path.stem(fs.path.basename(src_path)),
2960        };
2961
2962        try create_module.modules.put(arena, name, .{
2963            .root_path = fs.path.dirname(src_path) orelse ".",
2964            .root_src_path = fs.path.basename(src_path),
2965            .cc_argv = try cc_argv.toOwnedSlice(arena),
2966            .inherited = mod_opts,
2967            .target_arch_os_abi = target_arch_os_abi,
2968            .target_mcpu = target_mcpu,
2969            .deps = try deps.toOwnedSlice(arena),
2970            .resolved = null,
2971            .c_source_files_start = c_source_files_owner_index,
2972            .c_source_files_end = create_module.c_source_files.items.len,
2973            .rc_source_files_start = rc_source_files_owner_index,
2974            .rc_source_files_end = create_module.rc_source_files.items.len,
2975        });
2976        cssan.reset();
2977        mod_opts = .{};
2978        target_arch_os_abi = null;
2979        target_mcpu = null;
2980        c_source_files_owner_index = create_module.c_source_files.items.len;
2981        rc_source_files_owner_index = create_module.rc_source_files.items.len;
2982    }
2983
2984    if (!create_module.opts.have_zcu and create_module.opts.is_test) {
2985        fatal("`zig test` expects a zig source file argument", .{});
2986    }
2987
2988    if (c_source_files_owner_index != create_module.c_source_files.items.len) {
2989        fatal("C source file '{s}' has no parent module", .{
2990            create_module.c_source_files.items[c_source_files_owner_index].src_path,
2991        });
2992    }
2993
2994    if (rc_source_files_owner_index != create_module.rc_source_files.items.len) {
2995        fatal("resource file '{s}' has no parent module", .{
2996            create_module.rc_source_files.items[rc_source_files_owner_index].src_path,
2997        });
2998    }
2999
3000    const self_exe_path = switch (native_os) {
3001        .wasi => {},
3002        else => fs.selfExePathAlloc(arena) catch |err| {
3003            fatal("unable to find zig self exe path: {s}", .{@errorName(err)});
3004        },
3005    };
3006
3007    // This `init` calls `fatal` on error.
3008    var dirs: Compilation.Directories = .init(
3009        arena,
3010        override_lib_dir,
3011        override_global_cache_dir,
3012        s: {
3013            if (override_local_cache_dir) |p| break :s .{ .override = p };
3014            break :s switch (arg_mode) {
3015                .run => .global,
3016                else => .search,
3017            };
3018        },
3019        if (native_os == .wasi) wasi_preopens,
3020        self_exe_path,
3021    );
3022    defer dirs.deinit();
3023
3024    if (linker_optimization) |o| {
3025        warn("ignoring deprecated linker optimization setting '{s}'", .{o});
3026    }
3027
3028    create_module.dirs = dirs;
3029    create_module.opts.emit_llvm_ir = emit_llvm_ir != .no;
3030    create_module.opts.emit_llvm_bc = emit_llvm_bc != .no;
3031    create_module.opts.emit_bin = emit_bin != .no;
3032    create_module.opts.any_c_source_files = create_module.c_source_files.items.len != 0;
3033
3034    const main_mod = try createModule(gpa, arena, io, &create_module, 0, null, color);
3035    for (create_module.modules.keys(), create_module.modules.values()) |key, cli_mod| {
3036        if (cli_mod.resolved == null)
3037            fatal("module '{s}' declared but not used", .{key});
3038    }
3039
3040    // When you're testing std, the main module is std, and we need to avoid duplicating the module.
3041    const main_mod_is_std = main_mod.root.root == .zig_lib and
3042        mem.eql(u8, main_mod.root.sub_path, "std") and
3043        mem.eql(u8, main_mod.root_src_path, "std.zig");
3044
3045    const std_mod = m: {
3046        if (main_mod_is_std) break :m main_mod;
3047        if (create_module.modules.get("std")) |cli_mod| break :m cli_mod.resolved.?;
3048        break :m null;
3049    };
3050
3051    const root_mod = switch (arg_mode) {
3052        .zig_test, .zig_test_obj => root_mod: {
3053            const test_mod = if (test_runner_path) |test_runner| test_mod: {
3054                const test_mod = try Package.Module.create(arena, .{
3055                    .paths = .{
3056                        .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(test_runner) orelse "."}),
3057                        .root_src_path = fs.path.basename(test_runner),
3058                    },
3059                    .fully_qualified_name = "root",
3060                    .cc_argv = &.{},
3061                    .inherited = .{},
3062                    .global = create_module.resolved_options,
3063                    .parent = main_mod,
3064                });
3065                test_mod.deps = try main_mod.deps.clone(arena);
3066                break :test_mod test_mod;
3067            } else try Package.Module.create(arena, .{
3068                .paths = .{
3069                    .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"),
3070                    .root_src_path = "test_runner.zig",
3071                },
3072                .fully_qualified_name = "root",
3073                .cc_argv = &.{},
3074                .inherited = .{},
3075                .global = create_module.resolved_options,
3076                .parent = main_mod,
3077            });
3078
3079            break :root_mod test_mod;
3080        },
3081        else => main_mod,
3082    };
3083
3084    const target = &main_mod.resolved_target.result;
3085
3086    if (target.cpu.arch == .arc or target.cpu.arch.isNvptx()) {
3087        if (emit_bin != .no and create_module.resolved_options.use_llvm) {
3088            fatal("cannot emit {s} binary with the LLVM backend; only '-femit-asm' is supported", .{
3089                @tagName(target.cpu.arch),
3090            });
3091        }
3092    }
3093
3094    if (target.os.tag == .windows and major_subsystem_version == null and minor_subsystem_version == null) {
3095        major_subsystem_version, minor_subsystem_version = switch (target.os.version_range.windows.min) {
3096            .nt4 => .{ 4, 0 },
3097            .win2k => .{ 5, 0 },
3098            .xp => if (target.cpu.arch == .x86_64) .{ 5, 2 } else .{ 5, 1 },
3099            .ws2003 => .{ 5, 2 },
3100            else => .{ null, null },
3101        };
3102    }
3103
3104    if (target.ofmt != .coff) {
3105        if (manifest_file != null) {
3106            fatal("manifest file is not allowed unless the target object format is coff (Windows/UEFI)", .{});
3107        }
3108        if (create_module.rc_source_files.items.len != 0) {
3109            fatal("rc files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
3110        }
3111        if (contains_res_file) {
3112            fatal("res files are not allowed unless the target object format is coff (Windows/UEFI)", .{});
3113        }
3114    }
3115
3116    var resolved_frameworks = std.array_list.Managed(Compilation.Framework).init(arena);
3117
3118    if (create_module.frameworks.keys().len > 0) {
3119        var test_path = std.array_list.Managed(u8).init(gpa);
3120        defer test_path.deinit();
3121
3122        var checked_paths = std.array_list.Managed(u8).init(gpa);
3123        defer checked_paths.deinit();
3124
3125        var failed_frameworks = std.array_list.Managed(struct {
3126            name: []const u8,
3127            checked_paths: []const u8,
3128        }).init(arena);
3129
3130        framework: for (create_module.frameworks.keys(), create_module.frameworks.values()) |framework_name, info| {
3131            checked_paths.clearRetainingCapacity();
3132
3133            for (create_module.framework_dirs.items) |framework_dir_path| {
3134                if (try accessFrameworkPath(
3135                    &test_path,
3136                    &checked_paths,
3137                    framework_dir_path,
3138                    framework_name,
3139                )) {
3140                    const path = Path.initCwd(try arena.dupe(u8, test_path.items));
3141                    try resolved_frameworks.append(.{
3142                        .needed = info.needed,
3143                        .weak = info.weak,
3144                        .path = path,
3145                    });
3146                    continue :framework;
3147                }
3148            }
3149
3150            try failed_frameworks.append(.{
3151                .name = framework_name,
3152                .checked_paths = try arena.dupe(u8, checked_paths.items),
3153            });
3154        }
3155
3156        if (failed_frameworks.items.len > 0) {
3157            for (failed_frameworks.items) |f| {
3158                const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths;
3159                std.log.err("unable to find framework '{s}'. searched paths: {s}", .{
3160                    f.name, searched_paths,
3161                });
3162            }
3163            process.exit(1);
3164        }
3165    }
3166    // After this point, resolved_frameworks is used instead of frameworks.
3167
3168    if (create_module.resolved_options.output_mode == .Obj and target.ofmt == .coff) {
3169        const total_obj_count = create_module.c_source_files.items.len +
3170            @intFromBool(root_src_file != null) +
3171            create_module.rc_source_files.items.len +
3172            link.countObjectInputs(create_module.link_inputs.items);
3173        if (total_obj_count > 1) {
3174            fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)});
3175        }
3176    }
3177
3178    var cleanup_emit_bin_dir: ?fs.Dir = null;
3179    defer if (cleanup_emit_bin_dir) |*dir| dir.close();
3180
3181    // For `zig run` and `zig test`, we don't want to put the binary in the cwd by default. So, if
3182    // the binary is requested with no explicit path (as is the default), we emit to the cache.
3183    const output_to_cache: ?Emit.OutputToCacheReason = switch (listen) {
3184        .stdio, .ip4 => .listen,
3185        .none => if (arg_mode == .run and emit_bin == .yes_default_path)
3186            .@"zig run"
3187        else if (arg_mode == .zig_test and emit_bin == .yes_default_path)
3188            .@"zig test"
3189        else
3190            null,
3191    };
3192    const optional_version = if (have_version) version else null;
3193
3194    const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name;
3195
3196    const resolved_soname: ?[]const u8 = switch (soname) {
3197        .yes => |explicit| explicit,
3198        .no => null,
3199        .yes_default_value => if (create_module.resolved_options.output_mode == .Lib and
3200            create_module.resolved_options.link_mode == .dynamic and target.ofmt == .elf)
3201            if (have_version)
3202                try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major })
3203            else
3204                try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name})
3205        else
3206            null,
3207    };
3208
3209    const emit_bin_resolved: Compilation.CreateOptions.Emit = switch (emit_bin) {
3210        .no => .no,
3211        .yes_default_path => emit: {
3212            if (output_to_cache != null) break :emit .yes_cache;
3213            const name = switch (clang_preprocessor_mode) {
3214                .pch => try std.fmt.allocPrint(arena, "{s}.pch", .{root_name}),
3215                else => try std.zig.binNameAlloc(arena, .{
3216                    .root_name = root_name,
3217                    .target = target,
3218                    .output_mode = create_module.resolved_options.output_mode,
3219                    .link_mode = create_module.resolved_options.link_mode,
3220                    .version = optional_version,
3221                }),
3222            };
3223            break :emit .{ .yes_path = name };
3224        },
3225        .yes => |path| if (output_to_cache != null) {
3226            assert(output_to_cache == .listen); // there was an explicit bin path
3227            fatal("--listen incompatible with explicit output path '{s}'", .{path});
3228        } else emit: {
3229            // If there's a dirname, check that dir exists. This will give a more descriptive error than `Compilation` otherwise would.
3230            if (fs.path.dirname(path)) |dir_path| {
3231                var dir = fs.cwd().openDir(dir_path, .{}) catch |err| {
3232                    fatal("unable to open output directory '{s}': {s}", .{ dir_path, @errorName(err) });
3233                };
3234                dir.close();
3235            }
3236            break :emit .{ .yes_path = path };
3237        },
3238        .yes_a_out => emit: {
3239            assert(output_to_cache == null);
3240            break :emit .{ .yes_path = switch (target.ofmt) {
3241                .coff => "a.exe",
3242                else => "a.out",
3243            } };
3244        },
3245    };
3246
3247    const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name});
3248    const emit_h_resolved = emit_h.resolve(default_h_basename, output_to_cache);
3249
3250    const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name});
3251    const emit_asm_resolved = emit_asm.resolve(default_asm_basename, output_to_cache);
3252
3253    const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name});
3254    const emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename, output_to_cache);
3255
3256    const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});
3257    const emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename, output_to_cache);
3258
3259    const emit_docs_resolved = emit_docs.resolve("docs", output_to_cache);
3260
3261    const is_exe_or_dyn_lib = switch (create_module.resolved_options.output_mode) {
3262        .Obj => false,
3263        .Lib => create_module.resolved_options.link_mode == .dynamic,
3264        .Exe => true,
3265    };
3266    // Note that cmake when targeting Windows will try to execute
3267    // zig cc to make an executable and output an implib too.
3268    const implib_eligible = is_exe_or_dyn_lib and
3269        emit_bin_resolved != .no and target.os.tag == .windows;
3270    if (!implib_eligible) {
3271        if (!emit_implib_arg_provided) {
3272            emit_implib = .no;
3273        } else if (emit_implib != .no) {
3274            fatal("the argument -femit-implib is allowed only when building a Windows DLL", .{});
3275        }
3276    }
3277    const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name});
3278    const emit_implib_resolved: Compilation.CreateOptions.Emit = switch (emit_implib) {
3279        .no => .no,
3280        .yes => emit_implib.resolve(default_implib_basename, output_to_cache),
3281        .yes_default_path => emit: {
3282            if (output_to_cache != null) break :emit .yes_cache;
3283            const p = try fs.path.join(arena, &.{
3284                fs.path.dirname(emit_bin_resolved.yes_path) orelse ".",
3285                default_implib_basename,
3286            });
3287            break :emit .{ .yes_path = p };
3288        },
3289    };
3290
3291    var thread_pool: ThreadPool = undefined;
3292    try thread_pool.init(.{
3293        .allocator = gpa,
3294        .n_jobs = @min(@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
3295        .track_ids = true,
3296        .stack_size = thread_stack_size,
3297    });
3298    defer thread_pool.deinit();
3299
3300    for (create_module.c_source_files.items) |*src| {
3301        dev.check(.c_compiler);
3302        if (!mem.eql(u8, src.src_path, "-")) continue;
3303
3304        const ext = src.ext orelse
3305            fatal("-E or -x is required when reading from a non-regular file", .{});
3306
3307        // "-" is stdin. Dump it to a real file.
3308        const sep = fs.path.sep_str;
3309        const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{
3310            std.crypto.random.int(u64), ext.canonicalName(target),
3311        });
3312        try dirs.local_cache.handle.makePath("tmp");
3313
3314        // Note that in one of the happy paths, execve() is used to switch to
3315        // clang in which case any cleanup logic that exists for this temporary
3316        // file will not run and this temp file will be leaked. The filename
3317        // will be a hash of its contents — so multiple invocations of
3318        // `zig cc -` will result in the same temp file name.
3319        var f = try dirs.local_cache.handle.createFile(dump_path, .{});
3320        defer f.close();
3321
3322        // Re-using the hasher from Cache, since the functional requirements
3323        // for the hashing algorithm here and in the cache are the same.
3324        // We are providing our own cache key, because this file has nothing
3325        // to do with the cache manifest.
3326        var file_writer = f.writer(&.{});
3327        var buffer: [1000]u8 = undefined;
3328        var hasher = file_writer.interface.hashed(Cache.Hasher.init("0123456789abcdef"), &buffer);
3329        var stdin_reader = fs.File.stdin().readerStreaming(io, &.{});
3330        _ = hasher.writer.sendFileAll(&stdin_reader, .unlimited) catch |err| switch (err) {
3331            error.WriteFailed => fatal("failed to write {s}: {t}", .{ dump_path, file_writer.err.? }),
3332            else => fatal("failed to pipe stdin to {s}: {t}", .{ dump_path, err }),
3333        };
3334        try hasher.writer.flush();
3335
3336        const bin_digest: Cache.BinDigest = hasher.hasher.finalResult();
3337
3338        const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{
3339            &bin_digest, ext.canonicalName(target),
3340        });
3341        try dirs.local_cache.handle.rename(dump_path, sub_path);
3342
3343        // Convert `sub_path` to be relative to current working directory.
3344        src.src_path = try dirs.local_cache.join(arena, &.{sub_path});
3345    }
3346
3347    if (build_options.have_llvm and emit_asm_resolved != .no) {
3348        // LLVM has no way to set this non-globally.
3349        const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" };
3350        @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv);
3351    }
3352
3353    const clang_passthrough_mode = switch (arg_mode) {
3354        .cc, .cpp, .translate_c => true,
3355        else => false,
3356    };
3357
3358    const incremental = create_module.resolved_options.incremental;
3359    if (debug_incremental and !incremental) {
3360        fatal("--debug-incremental requires -fincremental", .{});
3361    }
3362
3363    if (incremental and create_module.resolved_options.use_llvm) {
3364        warn("-fincremental is currently unsupported by the LLVM backend; crashes or miscompilations are likely", .{});
3365    }
3366
3367    const cache_mode: Compilation.CacheMode = b: {
3368        // Once incremental compilation is the default, we'll want some smarter logic here,
3369        // considering things like the backend in use and whether there's a ZCU.
3370        if (output_to_cache == null) break :b .none;
3371        if (incremental) break :b .incremental;
3372        break :b .whole;
3373    };
3374
3375    process.raiseFileDescriptorLimit();
3376
3377    var file_system_inputs: std.ArrayList(u8) = .empty;
3378    defer file_system_inputs.deinit(gpa);
3379
3380    // Deduplicate rpath entries
3381    var rpath_dedup = std.StringArrayHashMapUnmanaged(void){};
3382    for (create_module.rpath_list.items) |rpath| {
3383        try rpath_dedup.put(arena, rpath, {});
3384    }
3385    create_module.rpath_list.clearRetainingCapacity();
3386    try create_module.rpath_list.appendSlice(arena, rpath_dedup.keys());
3387
3388    var create_diag: Compilation.CreateDiagnostic = undefined;
3389    const comp = Compilation.create(gpa, arena, io, &create_diag, .{
3390        .dirs = dirs,
3391        .thread_pool = &thread_pool,
3392        .self_exe_path = switch (native_os) {
3393            .wasi => null,
3394            else => self_exe_path,
3395        },
3396        .config = create_module.resolved_options,
3397        .root_name = root_name,
3398        .sysroot = create_module.sysroot,
3399        .main_mod = main_mod,
3400        .root_mod = root_mod,
3401        .std_mod = std_mod,
3402        .emit_bin = emit_bin_resolved,
3403        .emit_h = emit_h_resolved,
3404        .emit_asm = emit_asm_resolved,
3405        .emit_llvm_ir = emit_llvm_ir_resolved,
3406        .emit_llvm_bc = emit_llvm_bc_resolved,
3407        .emit_docs = emit_docs_resolved,
3408        .emit_implib = emit_implib_resolved,
3409        .lib_directories = create_module.lib_directories.items,
3410        .rpath_list = create_module.rpath_list.items,
3411        .symbol_wrap_set = symbol_wrap_set,
3412        .c_source_files = create_module.c_source_files.items,
3413        .rc_source_files = create_module.rc_source_files.items,
3414        .manifest_file = manifest_file,
3415        .rc_includes = rc_includes,
3416        .mingw_unicode_entry_point = mingw_unicode_entry_point,
3417        .link_inputs = create_module.link_inputs.items,
3418        .framework_dirs = create_module.framework_dirs.items,
3419        .frameworks = resolved_frameworks.items,
3420        .windows_lib_names = create_module.windows_libs.keys(),
3421        .want_compiler_rt = want_compiler_rt,
3422        .want_ubsan_rt = want_ubsan_rt,
3423        .hash_style = hash_style,
3424        .linker_script = linker_script,
3425        .version_script = version_script,
3426        .linker_allow_undefined_version = linker_allow_undefined_version,
3427        .linker_enable_new_dtags = linker_enable_new_dtags,
3428        .disable_c_depfile = disable_c_depfile,
3429        .soname = resolved_soname,
3430        .linker_sort_section = linker_sort_section,
3431        .linker_gc_sections = linker_gc_sections,
3432        .linker_repro = linker_repro,
3433        .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
3434        .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
3435        .linker_import_symbols = linker_import_symbols,
3436        .linker_import_table = linker_import_table,
3437        .linker_export_table = linker_export_table,
3438        .linker_initial_memory = linker_initial_memory,
3439        .linker_max_memory = linker_max_memory,
3440        .linker_print_gc_sections = linker_print_gc_sections,
3441        .linker_print_icf_sections = linker_print_icf_sections,
3442        .linker_print_map = linker_print_map,
3443        .llvm_opt_bisect_limit = llvm_opt_bisect_limit,
3444        .linker_global_base = linker_global_base,
3445        .linker_export_symbol_names = linker_export_symbol_names.items,
3446        .linker_z_nocopyreloc = linker_z_nocopyreloc,
3447        .linker_z_nodelete = linker_z_nodelete,
3448        .linker_z_notext = linker_z_notext,
3449        .linker_z_defs = linker_z_defs,
3450        .linker_z_origin = linker_z_origin,
3451        .linker_z_now = linker_z_now,
3452        .linker_z_relro = linker_z_relro,
3453        .linker_z_common_page_size = linker_z_common_page_size,
3454        .linker_z_max_page_size = linker_z_max_page_size,
3455        .linker_tsaware = linker_tsaware,
3456        .linker_nxcompat = linker_nxcompat,
3457        .linker_dynamicbase = linker_dynamicbase,
3458        .linker_compress_debug_sections = linker_compress_debug_sections,
3459        .linker_module_definition_file = linker_module_definition_file,
3460        .major_subsystem_version = major_subsystem_version,
3461        .minor_subsystem_version = minor_subsystem_version,
3462        .link_eh_frame_hdr = link_eh_frame_hdr,
3463        .link_emit_relocs = link_emit_relocs,
3464        .entry = entry,
3465        .force_undefined_symbols = force_undefined_symbols,
3466        .stack_size = stack_size,
3467        .image_base = image_base,
3468        .function_sections = function_sections,
3469        .data_sections = data_sections,
3470        .clang_passthrough_mode = clang_passthrough_mode,
3471        .clang_preprocessor_mode = clang_preprocessor_mode,
3472        .version = optional_version,
3473        .compatibility_version = compatibility_version,
3474        .libc_installation = if (create_module.libc_installation) |*lci| lci else null,
3475        .verbose_cc = verbose_cc,
3476        .verbose_link = verbose_link,
3477        .verbose_air = verbose_air,
3478        .verbose_intern_pool = verbose_intern_pool,
3479        .verbose_generic_instances = verbose_generic_instances,
3480        .verbose_llvm_ir = verbose_llvm_ir,
3481        .verbose_llvm_bc = verbose_llvm_bc,
3482        .verbose_cimport = verbose_cimport,
3483        .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
3484        .time_report = time_report,
3485        .stack_report = stack_report,
3486        .build_id = build_id,
3487        .test_filters = test_filters.items,
3488        .test_runner_path = test_runner_path,
3489        .cache_mode = cache_mode,
3490        .subsystem = subsystem,
3491        .debug_compile_errors = debug_compile_errors,
3492        .debug_incremental = debug_incremental,
3493        .enable_link_snapshots = enable_link_snapshots,
3494        .install_name = install_name,
3495        .entitlements = entitlements,
3496        .pagezero_size = pagezero_size,
3497        .headerpad_size = headerpad_size,
3498        .headerpad_max_install_names = headerpad_max_install_names,
3499        .dead_strip_dylibs = dead_strip_dylibs,
3500        .force_load_objc = force_load_objc,
3501        .discard_local_symbols = discard_local_symbols,
3502        .reference_trace = reference_trace,
3503        .pdb_out_path = pdb_out_path,
3504        .error_limit = error_limit,
3505        .native_system_include_paths = create_module.native_system_include_paths,
3506        // Any leftover C compilation args (such as -I) apply globally rather
3507        // than to any particular module. This feature can greatly reduce CLI
3508        // noise when --search-prefix and -M are combined.
3509        .global_cc_argv = try cc_argv.toOwnedSlice(arena),
3510        .file_system_inputs = &file_system_inputs,
3511        .debug_compiler_runtime_libs = debug_compiler_runtime_libs,
3512    }) catch |err| switch (err) {
3513        error.CreateFail => switch (create_diag) {
3514            .cross_libc_unavailable => {
3515                // We can emit a more informative error for this.
3516                const triple_name = try target.zigTriple(arena);
3517                std.log.err("unable to provide libc for target '{s}'", .{triple_name});
3518
3519                for (std.zig.target.available_libcs) |t| {
3520                    if (t.arch == target.cpu.arch and t.os == target.os.tag) {
3521                        // If there's a `glibc_min`, there's also an `os_ver`.
3522                        if (t.glibc_min) |glibc_min| {
3523                            std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}.{d}.{d}", .{
3524                                @tagName(t.arch),
3525                                @tagName(t.os),
3526                                t.os_ver.?,
3527                                @tagName(t.abi),
3528                                glibc_min.major,
3529                                glibc_min.minor,
3530                            });
3531                        } else if (t.os_ver) |os_ver| {
3532                            std.log.info("zig can provide libc for related target {s}-{s}.{f}-{s}", .{
3533                                @tagName(t.arch),
3534                                @tagName(t.os),
3535                                os_ver,
3536                                @tagName(t.abi),
3537                            });
3538                        } else {
3539                            std.log.info("zig can provide libc for related target {s}-{s}-{s}", .{
3540                                @tagName(t.arch),
3541                                @tagName(t.os),
3542                                @tagName(t.abi),
3543                            });
3544                        }
3545                    }
3546                }
3547                process.exit(1);
3548            },
3549            else => fatal("{f}", .{create_diag}),
3550        },
3551        else => fatal("failed to create compilation: {s}", .{@errorName(err)}),
3552    };
3553    var comp_destroyed = false;
3554    defer if (!comp_destroyed) comp.destroy();
3555
3556    if (show_builtin) {
3557        const builtin_opts = comp.root_mod.getBuiltinOptions(comp.config);
3558        const source = try builtin_opts.generate(arena);
3559        return fs.File.stdout().writeAll(source);
3560    }
3561    switch (listen) {
3562        .none => {},
3563        .stdio => {
3564            var stdin_reader = fs.File.stdin().reader(io, &stdin_buffer);
3565            var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
3566            try serve(
3567                comp,
3568                &stdin_reader.interface,
3569                &stdout_writer.interface,
3570                test_exec_args.items,
3571                self_exe_path,
3572                arg_mode,
3573                all_args,
3574                runtime_args_start,
3575            );
3576            return cleanExit();
3577        },
3578        .ip4 => |ip4_addr| {
3579            const addr: Io.net.IpAddress = .{ .ip4 = ip4_addr };
3580
3581            var server = try addr.listen(io, .{
3582                .reuse_address = true,
3583            });
3584            defer server.deinit(io);
3585
3586            var stream = try server.accept(io);
3587            defer stream.close(io);
3588
3589            var input = stream.reader(io, &stdin_buffer);
3590            var output = stream.writer(io, &stdout_buffer);
3591
3592            try serve(
3593                comp,
3594                &input.interface,
3595                &output.interface,
3596                test_exec_args.items,
3597                self_exe_path,
3598                arg_mode,
3599                all_args,
3600                runtime_args_start,
3601            );
3602            return cleanExit();
3603        },
3604    }
3605
3606    {
3607        const root_prog_node = std.Progress.start(.{
3608            .disable_printing = (color == .off),
3609        });
3610        defer root_prog_node.end();
3611
3612        if (arg_mode == .translate_c) {
3613            return cmdTranslateC(comp, arena, null, null, root_prog_node);
3614        }
3615
3616        updateModule(comp, color, root_prog_node) catch |err| switch (err) {
3617            error.CompileErrorsReported => {
3618                assert(listen == .none);
3619                saveState(comp, incremental);
3620                process.exit(1);
3621            },
3622            else => |e| return e,
3623        };
3624    }
3625    try comp.makeBinFileExecutable();
3626    saveState(comp, incremental);
3627
3628    if (switch (arg_mode) {
3629        .run => true,
3630        .zig_test => !test_no_exec,
3631        else => false,
3632    }) {
3633        dev.checkAny(&.{ .run_command, .test_command });
3634
3635        if (test_exec_args.items.len == 0 and target.ofmt == .c and emit_bin_resolved != .no) {
3636            // Default to using `zig run` to execute the produced .c code from `zig test`.
3637            try test_exec_args.appendSlice(arena, &.{ self_exe_path, "run" });
3638            if (dirs.zig_lib.path) |p| {
3639                try test_exec_args.appendSlice(arena, &.{ "-I", p });
3640            }
3641
3642            if (create_module.resolved_options.link_libc) {
3643                try test_exec_args.append(arena, "-lc");
3644            } else if (target.os.tag == .windows) {
3645                try test_exec_args.appendSlice(arena, &.{
3646                    "--subsystem", "console",
3647                    "-lkernel32",  "-lntdll",
3648                });
3649            }
3650
3651            const first_cli_mod = create_module.modules.values()[0];
3652            if (first_cli_mod.target_arch_os_abi) |triple| {
3653                try test_exec_args.appendSlice(arena, &.{ "-target", triple });
3654            }
3655            if (first_cli_mod.target_mcpu) |mcpu| {
3656                try test_exec_args.append(arena, try std.fmt.allocPrint(arena, "-mcpu={s}", .{mcpu}));
3657            }
3658            if (create_module.dynamic_linker) |dl| {
3659                if (dl.len > 0) {
3660                    try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl });
3661                } else {
3662                    try test_exec_args.append(arena, "--no-dynamic-linker");
3663                }
3664            }
3665            try test_exec_args.append(arena, null); // placeholder for the path of the emitted C source file
3666        }
3667
3668        try runOrTest(
3669            comp,
3670            gpa,
3671            arena,
3672            io,
3673            test_exec_args.items,
3674            self_exe_path,
3675            arg_mode,
3676            target,
3677            &comp_destroyed,
3678            all_args,
3679            runtime_args_start,
3680            create_module.resolved_options.link_libc,
3681        );
3682    }
3683
3684    // Skip resource deallocation in release builds; let the OS do it.
3685    return cleanExit();
3686}
3687
3688const CreateModule = struct {
3689    dirs: Compilation.Directories,
3690    modules: std.StringArrayHashMapUnmanaged(CliModule),
3691    opts: Compilation.Config.Options,
3692    dynamic_linker: ?[]const u8,
3693    object_format: ?[]const u8,
3694    /// undefined until createModule() for the root module is called.
3695    resolved_options: Compilation.Config,
3696
3697    /// This one is used while collecting CLI options. The set of libs is used
3698    /// directly after computing the target and used to compute link_libc,
3699    /// link_libcpp, and then the libraries are filtered into
3700    /// `unresolved_link_inputs` and `windows_libs`.
3701    cli_link_inputs: std.ArrayList(link.UnresolvedInput),
3702    windows_libs: std.StringArrayHashMapUnmanaged(void),
3703    /// The local variable `unresolved_link_inputs` is fed into library
3704    /// resolution, mutating the input array, and producing this data as
3705    /// output. Allocated with gpa.
3706    link_inputs: std.ArrayList(link.Input),
3707
3708    c_source_files: std.ArrayList(Compilation.CSourceFile),
3709    rc_source_files: std.ArrayList(Compilation.RcSourceFile),
3710
3711    /// e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
3712    /// This array is populated by zig cc frontend and then has to be converted to zig-style
3713    /// CPU features.
3714    llvm_m_args: std.ArrayList([]const u8),
3715    sysroot: ?[]const u8,
3716    lib_directories: std.ArrayList(Directory),
3717    lib_dir_args: std.ArrayList([]const u8),
3718    libc_installation: ?LibCInstallation,
3719    want_native_include_dirs: bool,
3720    frameworks: std.StringArrayHashMapUnmanaged(Framework),
3721    native_system_include_paths: []const []const u8,
3722    framework_dirs: std.ArrayList([]const u8),
3723    rpath_list: std.ArrayList([]const u8),
3724    each_lib_rpath: ?bool,
3725    libc_paths_file: ?[]const u8,
3726};
3727
3728fn createModule(
3729    gpa: Allocator,
3730    arena: Allocator,
3731    io: Io,
3732    create_module: *CreateModule,
3733    index: usize,
3734    parent: ?*Package.Module,
3735    color: std.zig.Color,
3736) Allocator.Error!*Package.Module {
3737    const cli_mod = &create_module.modules.values()[index];
3738    if (cli_mod.resolved) |m| return m;
3739
3740    const name = create_module.modules.keys()[index];
3741
3742    cli_mod.inherited.resolved_target = t: {
3743        // If the target is not overridden, use the parent's target. Of course,
3744        // if this is the root module then we need to proceed to resolve the
3745        // target.
3746        if (cli_mod.target_arch_os_abi == null and cli_mod.target_mcpu == null) {
3747            if (parent) |p| break :t p.resolved_target;
3748        }
3749
3750        var target_parse_options: std.Target.Query.ParseOptions = .{
3751            .arch_os_abi = cli_mod.target_arch_os_abi orelse "native",
3752            .cpu_features = cli_mod.target_mcpu,
3753            .dynamic_linker = create_module.dynamic_linker,
3754            .object_format = create_module.object_format,
3755        };
3756
3757        // Before passing the mcpu string in for parsing, we convert any -m flags that were
3758        // passed in via zig cc to zig-style.
3759        if (create_module.llvm_m_args.items.len != 0) {
3760            // If this returns null, we let it fall through to the case below which will
3761            // run the full parse function and do proper error handling.
3762            if (std.Target.Query.parseCpuArch(target_parse_options)) |cpu_arch| {
3763                var llvm_to_zig_name = std.StringHashMap([]const u8).init(gpa);
3764                defer llvm_to_zig_name.deinit();
3765
3766                for (cpu_arch.allFeaturesList()) |feature| {
3767                    const llvm_name = feature.llvm_name orelse continue;
3768                    try llvm_to_zig_name.put(llvm_name, feature.name);
3769                }
3770
3771                var mcpu_buffer = std.array_list.Managed(u8).init(gpa);
3772                defer mcpu_buffer.deinit();
3773
3774                try mcpu_buffer.appendSlice(cli_mod.target_mcpu orelse "baseline");
3775
3776                for (create_module.llvm_m_args.items) |llvm_m_arg| {
3777                    if (mem.cutPrefix(u8, llvm_m_arg, "mno-")) |llvm_name| {
3778                        const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
3779                            fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
3780                                @tagName(cpu_arch), llvm_name,
3781                            });
3782                        };
3783                        try mcpu_buffer.append('-');
3784                        try mcpu_buffer.appendSlice(zig_name);
3785                    } else if (mem.cutPrefix(u8, llvm_m_arg, "m")) |llvm_name| {
3786                        const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
3787                            fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
3788                                @tagName(cpu_arch), llvm_name,
3789                            });
3790                        };
3791                        try mcpu_buffer.append('+');
3792                        try mcpu_buffer.appendSlice(zig_name);
3793                    } else {
3794                        unreachable;
3795                    }
3796                }
3797
3798                const adjusted_target_mcpu = try arena.dupe(u8, mcpu_buffer.items);
3799                std.log.debug("adjusted target_mcpu: {s}", .{adjusted_target_mcpu});
3800                target_parse_options.cpu_features = adjusted_target_mcpu;
3801            }
3802        }
3803
3804        const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
3805        const target = std.zig.resolveTargetQueryOrFatal(io, target_query);
3806        break :t .{
3807            .result = target,
3808            .is_native_os = target_query.isNativeOs(),
3809            .is_native_abi = target_query.isNativeAbi(),
3810            .is_explicit_dynamic_linker = target_query.dynamic_linker != null,
3811        };
3812    };
3813
3814    if (parent == null) {
3815        // This block is for initializing the fields of
3816        // `Compilation.Config.Options` that require knowledge of the
3817        // target (which was just now resolved for the root module above).
3818        const resolved_target = &cli_mod.inherited.resolved_target.?;
3819        create_module.opts.resolved_target = resolved_target.*;
3820        create_module.opts.root_optimize_mode = cli_mod.inherited.optimize_mode;
3821        create_module.opts.root_strip = cli_mod.inherited.strip;
3822        create_module.opts.root_error_tracing = cli_mod.inherited.error_tracing;
3823        const target = &resolved_target.result;
3824
3825        // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
3826        // We need to know whether the set of system libraries contains anything besides these
3827        // to decide whether to trigger native path detection logic.
3828        // Preserves linker input order.
3829        var unresolved_link_inputs: std.ArrayList(link.UnresolvedInput) = .empty;
3830        defer unresolved_link_inputs.deinit(gpa);
3831        try unresolved_link_inputs.ensureUnusedCapacity(gpa, create_module.cli_link_inputs.items.len);
3832        var any_name_queries_remaining = false;
3833        for (create_module.cli_link_inputs.items) |cli_link_input| switch (cli_link_input) {
3834            .name_query => |nq| {
3835                const lib_name = nq.name;
3836
3837                if (std.zig.target.isLibCLibName(target, lib_name)) {
3838                    create_module.opts.link_libc = true;
3839                    continue;
3840                }
3841                if (std.zig.target.isLibCxxLibName(target, lib_name)) {
3842                    create_module.opts.link_libcpp = true;
3843                    continue;
3844                }
3845
3846                switch (target_util.classifyCompilerRtLibName(lib_name)) {
3847                    .none => {},
3848                    .only_libunwind, .both => {
3849                        create_module.opts.link_libunwind = true;
3850                        continue;
3851                    },
3852                    .only_compiler_rt => continue,
3853                }
3854
3855                if (target.isMinGW()) {
3856                    const exists = mingw.libExists(arena, target, create_module.dirs.zig_lib, lib_name) catch |err| {
3857                        fatal("failed to check zig installation for DLL import libs: {s}", .{
3858                            @errorName(err),
3859                        });
3860                    };
3861                    if (exists) {
3862                        try create_module.windows_libs.put(arena, lib_name, {});
3863                        continue;
3864                    }
3865                }
3866
3867                if (fs.path.isAbsolute(lib_name)) {
3868                    fatal("cannot use absolute path as a system library: {s}", .{lib_name});
3869                }
3870
3871                unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
3872                any_name_queries_remaining = true;
3873            },
3874            else => {
3875                unresolved_link_inputs.appendAssumeCapacity(cli_link_input);
3876            },
3877        }; // After this point, unresolved_link_inputs is used instead of cli_link_inputs.
3878
3879        if (any_name_queries_remaining) create_module.want_native_include_dirs = true;
3880
3881        // Resolve the library path arguments with respect to sysroot.
3882        try create_module.lib_directories.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len);
3883        if (create_module.sysroot) |root| {
3884            for (create_module.lib_dir_args.items) |lib_dir_arg| {
3885                if (fs.path.isAbsolute(lib_dir_arg)) {
3886                    const stripped_dir = lib_dir_arg[fs.path.parsePath(lib_dir_arg).root.len..];
3887                    const full_path = try fs.path.join(arena, &[_][]const u8{ root, stripped_dir });
3888                    addLibDirectoryWarn(&create_module.lib_directories, full_path);
3889                } else {
3890                    addLibDirectoryWarn(&create_module.lib_directories, lib_dir_arg);
3891                }
3892            }
3893        } else {
3894            for (create_module.lib_dir_args.items) |lib_dir_arg| {
3895                addLibDirectoryWarn(&create_module.lib_directories, lib_dir_arg);
3896            }
3897        }
3898        create_module.lib_dir_args = undefined; // From here we use lib_directories instead.
3899
3900        if (resolved_target.is_native_os and target.os.tag.isDarwin()) {
3901            // If we want to link against frameworks, we need system headers.
3902            if (create_module.frameworks.count() > 0)
3903                create_module.want_native_include_dirs = true;
3904        }
3905
3906        if (create_module.each_lib_rpath orelse resolved_target.is_native_os) {
3907            try create_module.rpath_list.ensureUnusedCapacity(arena, create_module.lib_directories.items.len);
3908            for (create_module.lib_directories.items) |lib_directory| {
3909                create_module.rpath_list.appendAssumeCapacity(lib_directory.path.?);
3910            }
3911        }
3912
3913        // Trigger native system library path detection if necessary.
3914        if (create_module.sysroot == null and
3915            resolved_target.is_native_os and resolved_target.is_native_abi and
3916            create_module.want_native_include_dirs)
3917        {
3918            var paths = std.zig.system.NativePaths.detect(arena, target) catch |err| {
3919                fatal("unable to detect native system paths: {s}", .{@errorName(err)});
3920            };
3921            for (paths.warnings.items) |warning| {
3922                warn("{s}", .{warning});
3923            }
3924
3925            create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena);
3926
3927            try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items);
3928            try create_module.rpath_list.appendSlice(arena, paths.rpaths.items);
3929
3930            try create_module.lib_directories.ensureUnusedCapacity(arena, paths.lib_dirs.items.len);
3931            for (paths.lib_dirs.items) |path| addLibDirectoryWarn2(&create_module.lib_directories, path, true);
3932        }
3933
3934        if (create_module.libc_paths_file) |paths_file| {
3935            create_module.libc_installation = LibCInstallation.parse(arena, paths_file, target) catch |err| {
3936                fatal("unable to parse libc paths file at path {s}: {s}", .{
3937                    paths_file, @errorName(err),
3938                });
3939            };
3940        }
3941
3942        if (target.os.tag == .windows and (target.abi == .msvc or target.abi == .itanium) and
3943            any_name_queries_remaining)
3944        {
3945            if (create_module.libc_installation == null) {
3946                create_module.libc_installation = LibCInstallation.findNative(.{
3947                    .allocator = arena,
3948                    .verbose = true,
3949                    .target = target,
3950                }) catch |err| {
3951                    fatal("unable to find native libc installation: {s}", .{@errorName(err)});
3952                };
3953            }
3954            try create_module.lib_directories.ensureUnusedCapacity(arena, 2);
3955            addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.msvc_lib_dir.?);
3956            addLibDirectoryWarn(&create_module.lib_directories, create_module.libc_installation.?.kernel32_lib_dir.?);
3957        }
3958
3959        // Destructively mutates but does not transfer ownership of `unresolved_link_inputs`.
3960        link.resolveInputs(
3961            gpa,
3962            arena,
3963            target,
3964            &unresolved_link_inputs,
3965            &create_module.link_inputs,
3966            create_module.lib_directories.items,
3967            color,
3968        ) catch |err| fatal("failed to resolve link inputs: {s}", .{@errorName(err)});
3969
3970        if (!create_module.opts.any_dyn_libs) for (create_module.link_inputs.items) |item| switch (item) {
3971            .dso, .dso_exact => {
3972                create_module.opts.any_dyn_libs = true;
3973                break;
3974            },
3975            else => {},
3976        };
3977
3978        create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) {
3979            error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}),
3980            error.SharedMemoryIsWasmOnly => fatal("only WebAssembly CPU targets support shared memory", .{}),
3981            error.ObjectFilesCannotShareMemory => fatal("object files cannot share memory", .{}),
3982            error.ObjectFilesCannotSpecifyDynamicLinker => fatal("object files cannot specify --dynamic-linker", .{}),
3983            error.SharedMemoryRequiresAtomicsAndBulkMemory => fatal("shared memory requires atomics and bulk_memory CPU features", .{}),
3984            error.ThreadsRequireSharedMemory => fatal("threads require shared memory", .{}),
3985            error.EmittingLlvmModuleRequiresLlvmBackend => fatal("emitting an LLVM module requires using the LLVM backend", .{}),
3986            error.LlvmLacksTargetSupport => fatal("LLVM lacks support for the specified target", .{}),
3987            error.ZigLacksTargetSupport => fatal("compiler backend unavailable for the specified target", .{}),
3988            error.EmittingBinaryRequiresLlvmLibrary => fatal("producing machine code via LLVM requires using the LLVM library", .{}),
3989            error.LldIncompatibleObjectFormat => fatal("using LLD to link {s} files is unsupported", .{@tagName(target.ofmt)}),
3990            error.LldCannotIncrementallyLink => fatal("self-hosted backends do not support linking with LLD", .{}),
3991            error.LldCannotSpecifyDynamicLinkerForSharedLibraries => fatal("LLD does not support --dynamic-linker on shared libraries", .{}),
3992            error.LtoRequiresLld => fatal("LTO requires using LLD", .{}),
3993            error.SanitizeThreadRequiresLibCpp => fatal("thread sanitization is (for now) implemented in C++, so it requires linking libc++", .{}),
3994            error.LibCRequiresLibUnwind => fatal("libc of the specified target requires linking libunwind", .{}),
3995            error.LibCppRequiresLibUnwind => fatal("libc++ requires linking libunwind", .{}),
3996            error.OsRequiresLibC => fatal("the target OS requires using libc as the stable syscall interface", .{}),
3997            error.LibCppRequiresLibC => fatal("libc++ requires linking libc", .{}),
3998            error.LibUnwindRequiresLibC => fatal("libunwind requires linking libc", .{}),
3999            error.TargetCannotDynamicLink => fatal("dynamic linking unavailable on the specified target", .{}),
4000            error.TargetCannotStaticLinkExecutables => fatal("static linking of executables unavailable on the specified target", .{}),
4001            error.LibCRequiresDynamicLinking => fatal("libc of the specified target requires dynamic linking", .{}),
4002            error.SharedLibrariesRequireDynamicLinking => fatal("using shared libraries requires dynamic linking", .{}),
4003            error.DynamicLinkingWithLldRequiresSharedLibraries => fatal("dynamic linking with lld requires at least one shared library", .{}),
4004            error.ExportMemoryAndDynamicIncompatible => fatal("exporting memory is incompatible with dynamic linking", .{}),
4005            error.DynamicLibraryPrecludesPie => fatal("dynamic libraries cannot be position independent executables", .{}),
4006            error.TargetRequiresPie => fatal("the specified target requires position independent executables", .{}),
4007            error.SanitizeThreadRequiresPie => fatal("thread sanitization requires position independent executables", .{}),
4008            error.BackendLacksErrorTracing => fatal("the selected backend has not yet implemented error return tracing", .{}),
4009            error.LlvmLibraryUnavailable => fatal("zig was compiled without LLVM libraries", .{}),
4010            error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}),
4011            error.ClangUnavailable => fatal("zig was compiled without Clang libraries", .{}),
4012            error.DllExportFnsRequiresWindows => fatal("only Windows OS targets support DLLs", .{}),
4013            error.NewLinkerIncompatibleObjectFormat => fatal("using the new linker to link {s} files is unsupported", .{@tagName(target.ofmt)}),
4014            error.NewLinkerIncompatibleWithLld => fatal("using the new linker is incompatible with using lld", .{}),
4015        };
4016    }
4017
4018    const root: Compilation.Path = try .fromUnresolved(arena, create_module.dirs, &.{cli_mod.root_path});
4019
4020    const mod = Package.Module.create(arena, .{
4021        .paths = .{
4022            .root = root,
4023            .root_src_path = cli_mod.root_src_path,
4024        },
4025        .fully_qualified_name = name,
4026
4027        .cc_argv = cli_mod.cc_argv,
4028        .inherited = cli_mod.inherited,
4029        .global = create_module.resolved_options,
4030        .parent = parent,
4031    }) catch |err| switch (err) {
4032        error.ValgrindUnsupportedOnTarget => fatal("unable to create module '{s}': valgrind does not support the selected target CPU architecture", .{name}),
4033        error.TargetRequiresSingleThreaded => fatal("unable to create module '{s}': the selected target does not support multithreading", .{name}),
4034        error.BackendRequiresSingleThreaded => fatal("unable to create module '{s}': the selected machine code backend is limited to single-threaded applications", .{name}),
4035        error.TargetRequiresPic => fatal("unable to create module '{s}': the selected target requires position independent code", .{name}),
4036        error.PieRequiresPic => fatal("unable to create module '{s}': making a Position Independent Executable requires enabling Position Independent Code", .{name}),
4037        error.DynamicLinkingRequiresPic => fatal("unable to create module '{s}': dynamic linking requires enabling Position Independent Code", .{name}),
4038        error.TargetHasNoRedZone => fatal("unable to create module '{s}': the selected target does not have a red zone", .{name}),
4039        error.StackCheckUnsupportedByTarget => fatal("unable to create module '{s}': the selected target does not support stack checking", .{name}),
4040        error.StackProtectorUnsupportedByTarget => fatal("unable to create module '{s}': the selected target does not support stack protection", .{name}),
4041        error.StackProtectorUnavailableWithoutLibC => fatal("unable to create module '{s}': enabling stack protection requires libc", .{name}),
4042        error.OutOfMemory => return error.OutOfMemory,
4043    };
4044    cli_mod.resolved = mod;
4045
4046    for (create_module.c_source_files.items[cli_mod.c_source_files_start..cli_mod.c_source_files_end]) |*item| item.owner = mod;
4047
4048    for (create_module.rc_source_files.items[cli_mod.rc_source_files_start..cli_mod.rc_source_files_end]) |*item| item.owner = mod;
4049
4050    for (cli_mod.deps) |dep| {
4051        const dep_index = create_module.modules.getIndex(dep.value) orelse
4052            fatal("module '{s}' depends on non-existent module '{s}'", .{ name, dep.key });
4053        const dep_mod = try createModule(gpa, arena, io, create_module, dep_index, mod, color);
4054        try mod.deps.put(arena, dep.key, dep_mod);
4055    }
4056
4057    return mod;
4058}
4059
4060fn saveState(comp: *Compilation, incremental: bool) void {
4061    if (incremental) {
4062        comp.saveState() catch |err| {
4063            warn("unable to save incremental compilation state: {s}", .{@errorName(err)});
4064        };
4065    }
4066}
4067
4068fn serve(
4069    comp: *Compilation,
4070    in: *Io.Reader,
4071    out: *Io.Writer,
4072    test_exec_args: []const ?[]const u8,
4073    self_exe_path: ?[]const u8,
4074    arg_mode: ArgMode,
4075    all_args: []const []const u8,
4076    runtime_args_start: ?usize,
4077) !void {
4078    const gpa = comp.gpa;
4079
4080    var server = try Server.init(.{
4081        .in = in,
4082        .out = out,
4083        .zig_version = build_options.version,
4084    });
4085
4086    var child_pid: ?std.process.Child.Id = null;
4087
4088    const main_progress_node = std.Progress.start(.{});
4089    const file_system_inputs = comp.file_system_inputs.?;
4090
4091    const IncrementalDebugServer = if (build_options.enable_debug_extensions and !builtin.single_threaded)
4092        @import("IncrementalDebugServer.zig")
4093    else
4094        void;
4095
4096    var ids: IncrementalDebugServer = if (comp.debugIncremental()) ids: {
4097        break :ids .init(comp.zcu orelse @panic("--debug-incremental requires a ZCU"));
4098    } else undefined;
4099    defer if (comp.debugIncremental()) ids.deinit();
4100
4101    if (comp.debugIncremental()) ids.spawn();
4102
4103    while (true) {
4104        const hdr = try server.receiveMessage();
4105
4106        // Lock the debug server while handling the message.
4107        if (comp.debugIncremental()) ids.mutex.lock();
4108        defer if (comp.debugIncremental()) ids.mutex.unlock();
4109
4110        switch (hdr.tag) {
4111            .exit => return cleanExit(),
4112            .update => {
4113                tracy.frameMark();
4114                file_system_inputs.clearRetainingCapacity();
4115
4116                if (arg_mode == .translate_c) {
4117                    var arena_instance = std.heap.ArenaAllocator.init(gpa);
4118                    defer arena_instance.deinit();
4119                    const arena = arena_instance.allocator();
4120                    var output: Compilation.CImportResult = undefined;
4121                    try cmdTranslateC(comp, arena, &output, file_system_inputs, main_progress_node);
4122                    defer output.deinit(gpa);
4123
4124                    if (file_system_inputs.items.len != 0) {
4125                        try server.serveStringMessage(.file_system_inputs, file_system_inputs.items);
4126                    }
4127
4128                    if (output.errors.errorMessageCount() != 0) {
4129                        try server.serveErrorBundle(output.errors);
4130                    } else {
4131                        try server.serveEmitDigest(&output.digest, .{
4132                            .flags = .{ .cache_hit = output.cache_hit },
4133                        });
4134                    }
4135
4136                    continue;
4137                }
4138
4139                if (comp.config.output_mode == .Exe) {
4140                    try comp.makeBinFileWritable();
4141                }
4142
4143                try comp.update(main_progress_node);
4144
4145                try comp.makeBinFileExecutable();
4146                try serveUpdateResults(&server, comp);
4147            },
4148            .run => {
4149                if (child_pid != null) {
4150                    @panic("TODO block until the child exits");
4151                }
4152                @panic("TODO call runOrTest");
4153                //try runOrTest(
4154                //    comp,
4155                //    gpa,
4156                //    arena,
4157                //    io,
4158                //    test_exec_args,
4159                //    self_exe_path.?,
4160                //    arg_mode,
4161                //    target,
4162                //    true,
4163                //    &comp_destroyed,
4164                //    all_args,
4165                //    runtime_args_start,
4166                //    link_libc,
4167                //);
4168            },
4169            .hot_update => {
4170                tracy.frameMark();
4171                file_system_inputs.clearRetainingCapacity();
4172                if (child_pid) |pid| {
4173                    try comp.hotCodeSwap(main_progress_node, pid);
4174                    try serveUpdateResults(&server, comp);
4175                } else {
4176                    if (comp.config.output_mode == .Exe) {
4177                        try comp.makeBinFileWritable();
4178                    }
4179                    try comp.update(main_progress_node);
4180                    try comp.makeBinFileExecutable();
4181                    try serveUpdateResults(&server, comp);
4182
4183                    child_pid = try runOrTestHotSwap(
4184                        comp,
4185                        gpa,
4186                        test_exec_args,
4187                        self_exe_path.?,
4188                        arg_mode,
4189                        all_args,
4190                        runtime_args_start,
4191                    );
4192                }
4193            },
4194            else => {
4195                fatal("unrecognized message from client: 0x{x}", .{@intFromEnum(hdr.tag)});
4196            },
4197        }
4198    }
4199}
4200
4201fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
4202    const gpa = comp.gpa;
4203
4204    var error_bundle = try comp.getAllErrorsAlloc();
4205    defer error_bundle.deinit(gpa);
4206
4207    if (comp.file_system_inputs) |file_system_inputs| {
4208        if (file_system_inputs.items.len == 0) {
4209            assert(error_bundle.errorMessageCount() > 0);
4210        } else {
4211            try s.serveStringMessage(.file_system_inputs, file_system_inputs.items);
4212        }
4213    }
4214
4215    if (comp.time_report) |*tr| {
4216        var decls_len: u32 = 0;
4217
4218        var file_name_bytes: std.ArrayList(u8) = .empty;
4219        defer file_name_bytes.deinit(gpa);
4220        var files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, void) = .empty;
4221        defer files.deinit(gpa);
4222        var decl_data: std.ArrayList(u8) = .empty;
4223        defer decl_data.deinit(gpa);
4224
4225        // Each decl needs at least 34 bytes:
4226        // * 2 for 1-byte name plus null terminator
4227        // * 4 for `file`
4228        // * 4 for `sema_count`
4229        // * 8 for `sema_ns`
4230        // * 8 for `codegen_ns`
4231        // * 8 for `link_ns`
4232        // Most, if not all, decls in `tr.decl_sema_ns` are valid, so we have a good size estimate.
4233        try decl_data.ensureUnusedCapacity(gpa, tr.decl_sema_info.count() * 34);
4234
4235        for (tr.decl_sema_info.keys(), tr.decl_sema_info.values()) |tracked_inst, sema_info| {
4236            const resolved = tracked_inst.resolveFull(&comp.zcu.?.intern_pool) orelse continue;
4237            const file = comp.zcu.?.fileByIndex(resolved.file);
4238            const zir = file.zir orelse continue;
4239            const decl_name = zir.nullTerminatedString(zir.getDeclaration(resolved.inst).name);
4240
4241            const gop = try files.getOrPut(gpa, resolved.file);
4242            if (!gop.found_existing) try file_name_bytes.print(gpa, "{f}\x00", .{file.path.fmt(comp)});
4243
4244            const codegen_ns = tr.decl_codegen_ns.get(tracked_inst) orelse 0;
4245            const link_ns = tr.decl_link_ns.get(tracked_inst) orelse 0;
4246
4247            decls_len += 1;
4248
4249            try decl_data.ensureUnusedCapacity(gpa, 33 + decl_name.len);
4250            decl_data.appendSliceAssumeCapacity(decl_name);
4251            decl_data.appendAssumeCapacity(0);
4252
4253            const out_file = decl_data.addManyAsArrayAssumeCapacity(4);
4254            const out_sema_count = decl_data.addManyAsArrayAssumeCapacity(4);
4255            const out_sema_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4256            const out_codegen_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4257            const out_link_ns = decl_data.addManyAsArrayAssumeCapacity(8);
4258            std.mem.writeInt(u32, out_file, @intCast(gop.index), .little);
4259            std.mem.writeInt(u32, out_sema_count, sema_info.count, .little);
4260            std.mem.writeInt(u64, out_sema_ns, sema_info.ns, .little);
4261            std.mem.writeInt(u64, out_codegen_ns, codegen_ns, .little);
4262            std.mem.writeInt(u64, out_link_ns, link_ns, .little);
4263        }
4264
4265        const header: std.zig.Server.Message.TimeReport = .{
4266            .stats = tr.stats,
4267            .llvm_pass_timings_len = @intCast(tr.llvm_pass_timings.len),
4268            .files_len = @intCast(files.count()),
4269            .decls_len = decls_len,
4270            .flags = .{
4271                .use_llvm = comp.zcu != null and comp.zcu.?.llvm_object != null,
4272            },
4273        };
4274
4275        var slices: [4][]const u8 = .{
4276            @ptrCast(&header),
4277            tr.llvm_pass_timings,
4278            file_name_bytes.items,
4279            decl_data.items,
4280        };
4281        try s.serveMessageHeader(.{
4282            .tag = .time_report,
4283            .bytes_len = len: {
4284                var len: u32 = 0;
4285                for (slices) |slice| len += @intCast(slice.len);
4286                break :len len;
4287            },
4288        });
4289        try s.out.writeVecAll(&slices);
4290        try s.out.flush();
4291    }
4292
4293    if (error_bundle.errorMessageCount() > 0) {
4294        try s.serveErrorBundle(error_bundle);
4295        return;
4296    }
4297
4298    if (comp.digest) |digest| {
4299        try s.serveEmitDigest(&digest, .{
4300            .flags = .{ .cache_hit = comp.last_update_was_cache_hit },
4301        });
4302    }
4303
4304    // Serve empty error bundle to indicate the update is done.
4305    try s.serveErrorBundle(std.zig.ErrorBundle.empty);
4306}
4307
4308fn runOrTest(
4309    comp: *Compilation,
4310    gpa: Allocator,
4311    arena: Allocator,
4312    io: Io,
4313    test_exec_args: []const ?[]const u8,
4314    self_exe_path: []const u8,
4315    arg_mode: ArgMode,
4316    target: *const std.Target,
4317    comp_destroyed: *bool,
4318    all_args: []const []const u8,
4319    runtime_args_start: ?usize,
4320    link_libc: bool,
4321) !void {
4322    const raw_emit_bin = comp.emit_bin orelse return;
4323    const exe_path = switch (comp.cache_use) {
4324        .none => p: {
4325            if (fs.path.isAbsolute(raw_emit_bin)) break :p raw_emit_bin;
4326            // Use `fs.path.join` to make a file in the cwd is still executed properly.
4327            break :p try fs.path.join(arena, &.{
4328                ".",
4329                raw_emit_bin,
4330            });
4331        },
4332        .whole, .incremental => try comp.dirs.local_cache.join(arena, &.{
4333            "o",
4334            &Cache.binToHex(comp.digest.?),
4335            raw_emit_bin,
4336        }),
4337    };
4338
4339    var argv = std.array_list.Managed([]const u8).init(gpa);
4340    defer argv.deinit();
4341
4342    if (test_exec_args.len == 0) {
4343        try argv.append(exe_path);
4344        if (arg_mode == .zig_test) {
4345            try argv.append(
4346                try std.fmt.allocPrint(arena, "--seed=0x{x}", .{std.crypto.random.int(u32)}),
4347            );
4348        }
4349    } else {
4350        for (test_exec_args) |arg| {
4351            try argv.append(arg orelse exe_path);
4352        }
4353    }
4354    if (runtime_args_start) |i| {
4355        try argv.appendSlice(all_args[i..]);
4356    }
4357    var env_map = try process.getEnvMap(arena);
4358    try env_map.put("ZIG_EXE", self_exe_path);
4359
4360    // We do not execve for tests because if the test fails we want to print
4361    // the error message and invocation below.
4362    if (process.can_execv and arg_mode == .run) {
4363        // execv releases the locks; no need to destroy the Compilation here.
4364        std.debug.lockStdErr();
4365        const err = process.execve(gpa, argv.items, &env_map);
4366        std.debug.unlockStdErr();
4367        try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
4368        const cmd = try std.mem.join(arena, " ", argv.items);
4369        fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
4370    } else if (process.can_spawn) {
4371        var child = std.process.Child.init(argv.items, gpa);
4372        child.env_map = &env_map;
4373        child.stdin_behavior = .Inherit;
4374        child.stdout_behavior = .Inherit;
4375        child.stderr_behavior = .Inherit;
4376
4377        // Here we release all the locks associated with the Compilation so
4378        // that whatever this child process wants to do won't deadlock.
4379        comp.destroy();
4380        comp_destroyed.* = true;
4381
4382        const term_result = t: {
4383            std.debug.lockStdErr();
4384            defer std.debug.unlockStdErr();
4385            break :t child.spawnAndWait();
4386        };
4387        const term = term_result catch |err| {
4388            try warnAboutForeignBinaries(io, arena, arg_mode, target, link_libc);
4389            const cmd = try std.mem.join(arena, " ", argv.items);
4390            fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
4391        };
4392        switch (arg_mode) {
4393            .run, .build => {
4394                switch (term) {
4395                    .Exited => |code| {
4396                        if (code == 0) {
4397                            return cleanExit();
4398                        } else {
4399                            process.exit(code);
4400                        }
4401                    },
4402                    else => {
4403                        process.exit(1);
4404                    },
4405                }
4406            },
4407            .zig_test => {
4408                switch (term) {
4409                    .Exited => |code| {
4410                        if (code == 0) {
4411                            return cleanExit();
4412                        } else {
4413                            const cmd = try std.mem.join(arena, " ", argv.items);
4414                            fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
4415                        }
4416                    },
4417                    else => {
4418                        const cmd = try std.mem.join(arena, " ", argv.items);
4419                        fatal("the following test command crashed:\n{s}", .{cmd});
4420                    },
4421                }
4422            },
4423            else => unreachable,
4424        }
4425    } else {
4426        const cmd = try std.mem.join(arena, " ", argv.items);
4427        fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd });
4428    }
4429}
4430
4431fn runOrTestHotSwap(
4432    comp: *Compilation,
4433    gpa: Allocator,
4434    test_exec_args: []const ?[]const u8,
4435    self_exe_path: []const u8,
4436    arg_mode: ArgMode,
4437    all_args: []const []const u8,
4438    runtime_args_start: ?usize,
4439) !std.process.Child.Id {
4440    const lf = comp.bin_file.?;
4441
4442    const exe_path = switch (builtin.target.os.tag) {
4443        // On Windows it seems impossible to perform an atomic rename of a file that is currently
4444        // running in a process. Therefore, we do the opposite. We create a copy of the file in
4445        // tmp zig-cache and use it to spawn the child process. This way we are free to update
4446        // the binary with each requested hot update.
4447        .windows => blk: {
4448            try lf.emit.root_dir.handle.copyFile(lf.emit.sub_path, comp.dirs.local_cache.handle, lf.emit.sub_path, .{});
4449            break :blk try fs.path.join(gpa, &.{ comp.dirs.local_cache.path orelse ".", lf.emit.sub_path });
4450        },
4451
4452        // A naive `directory.join` here will indeed get the correct path to the binary,
4453        // however, in the case of cwd, we actually want `./foo` so that the path can be executed.
4454        else => try fs.path.join(gpa, &.{
4455            lf.emit.root_dir.path orelse ".", lf.emit.sub_path,
4456        }),
4457    };
4458    defer gpa.free(exe_path);
4459
4460    var argv = std.array_list.Managed([]const u8).init(gpa);
4461    defer argv.deinit();
4462
4463    if (test_exec_args.len == 0) {
4464        // when testing pass the zig_exe_path to argv
4465        if (arg_mode == .zig_test)
4466            try argv.appendSlice(&[_][]const u8{
4467                exe_path, self_exe_path,
4468            })
4469            // when running just pass the current exe
4470        else
4471            try argv.appendSlice(&[_][]const u8{
4472                exe_path,
4473            });
4474    } else {
4475        for (test_exec_args) |arg| {
4476            if (arg) |a| {
4477                try argv.append(a);
4478            } else {
4479                try argv.appendSlice(&[_][]const u8{
4480                    exe_path, self_exe_path,
4481                });
4482            }
4483        }
4484    }
4485    if (runtime_args_start) |i| {
4486        try argv.appendSlice(all_args[i..]);
4487    }
4488
4489    switch (builtin.target.os.tag) {
4490        .macos => {
4491            const PosixSpawn = @import("DarwinPosixSpawn.zig");
4492
4493            var attr = try PosixSpawn.Attr.init();
4494            defer attr.deinit();
4495
4496            // ASLR is probably a good default for better debugging experience/programming
4497            // with hot-code updates in mind. However, we can also make it work with ASLR on.
4498            try attr.set(.{
4499                .SETSIGDEF = true,
4500                .SETSIGMASK = true,
4501                .DISABLE_ASLR = true,
4502            });
4503
4504            var arena_allocator = std.heap.ArenaAllocator.init(gpa);
4505            defer arena_allocator.deinit();
4506            const arena = arena_allocator.allocator();
4507
4508            const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.items.len, null);
4509            for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
4510
4511            const pid = try PosixSpawn.spawn(argv.items[0], null, attr, argv_buf, std.c.environ);
4512            return pid;
4513        },
4514        else => {
4515            var child = std.process.Child.init(argv.items, gpa);
4516
4517            child.stdin_behavior = .Inherit;
4518            child.stdout_behavior = .Inherit;
4519            child.stderr_behavior = .Inherit;
4520
4521            try child.spawn();
4522
4523            return child.id;
4524        },
4525    }
4526}
4527
4528const UpdateModuleError = Compilation.UpdateError || error{
4529    /// The update caused compile errors. The error bundle has already been
4530    /// reported to the user by being rendered to stderr.
4531    CompileErrorsReported,
4532};
4533fn updateModule(comp: *Compilation, color: Color, prog_node: std.Progress.Node) UpdateModuleError!void {
4534    try comp.update(prog_node);
4535
4536    var errors = try comp.getAllErrorsAlloc();
4537    defer errors.deinit(comp.gpa);
4538
4539    if (errors.errorMessageCount() > 0) {
4540        errors.renderToStdErr(.{}, color);
4541        return error.CompileErrorsReported;
4542    }
4543}
4544
4545fn cmdTranslateC(
4546    comp: *Compilation,
4547    arena: Allocator,
4548    fancy_output: ?*Compilation.CImportResult,
4549    file_system_inputs: ?*std.ArrayList(u8),
4550    prog_node: std.Progress.Node,
4551) !void {
4552    dev.check(.translate_c_command);
4553
4554    const io = comp.io;
4555
4556    assert(comp.c_source_files.len == 1);
4557    const c_source_file = comp.c_source_files[0];
4558
4559    const translated_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.root_name});
4560
4561    var man: Cache.Manifest = comp.obtainCObjectCacheManifest(comp.root_mod);
4562    man.want_shared_lock = false;
4563    defer man.deinit();
4564
4565    man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
4566    man.hash.add(comp.config.c_frontend);
4567    Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| {
4568        fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
4569    };
4570
4571    const result: Compilation.CImportResult = if (try man.hit()) .{
4572        .digest = man.finalBin(),
4573        .cache_hit = true,
4574        .errors = std.zig.ErrorBundle.empty,
4575    } else result: {
4576        const result = try comp.translateC(
4577            arena,
4578            &man,
4579            Compilation.classifyFileExt(c_source_file.src_path),
4580            .{ .path = c_source_file.src_path },
4581            translated_basename,
4582            comp.root_mod,
4583            prog_node,
4584        );
4585
4586        if (result.errors.errorMessageCount() != 0) {
4587            if (fancy_output) |p| {
4588                if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
4589                p.* = result;
4590                return;
4591            } else {
4592                const color: Color = .auto;
4593                result.errors.renderToStdErr(.{}, color);
4594                process.exit(1);
4595            }
4596        }
4597
4598        man.writeManifest() catch |err| warn("failed to write cache manifest: {t}", .{err});
4599        break :result result;
4600    };
4601
4602    if (file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
4603    if (fancy_output) |p| {
4604        p.* = result;
4605    } else {
4606        const hex_digest = Cache.binToHex(result.digest);
4607        const out_zig_path = try fs.path.join(arena, &.{ "o", &hex_digest, translated_basename });
4608        const zig_file = comp.dirs.local_cache.handle.openFile(out_zig_path, .{}) catch |err| {
4609            const path = comp.dirs.local_cache.path orelse ".";
4610            fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{
4611                path,
4612                fs.path.sep_str,
4613                out_zig_path,
4614                @errorName(err),
4615            });
4616        };
4617        defer zig_file.close();
4618        var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
4619        var file_reader = zig_file.reader(io, &.{});
4620        _ = try stdout_writer.interface.sendFileAll(&file_reader, .unlimited);
4621        try stdout_writer.interface.flush();
4622        return cleanExit();
4623    }
4624}
4625
4626pub fn translateC(
4627    gpa: Allocator,
4628    arena: Allocator,
4629    io: Io,
4630    argv: []const []const u8,
4631    prog_node: std.Progress.Node,
4632    capture: ?*[]u8,
4633) !void {
4634    try jitCmd(gpa, arena, io, argv, .{
4635        .cmd_name = "translate-c",
4636        .root_src_path = "translate-c/main.zig",
4637        .depend_on_aro = true,
4638        .progress_node = prog_node,
4639        .capture = capture,
4640    });
4641}
4642
4643const usage_init =
4644    \\Usage: zig init
4645    \\
4646    \\   Initializes a `zig build` project in the current working
4647    \\   directory.
4648    \\
4649    \\Options:
4650    \\  -m, --minimal          Use minimal init template
4651    \\  -h, --help             Print this help and exit
4652    \\
4653    \\
4654;
4655
4656fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
4657    dev.check(.init_command);
4658
4659    var template: enum { example, minimal } = .example;
4660    {
4661        var i: usize = 0;
4662        while (i < args.len) : (i += 1) {
4663            const arg = args[i];
4664            if (mem.startsWith(u8, arg, "-")) {
4665                if (mem.eql(u8, arg, "-m") or mem.eql(u8, arg, "--minimal")) {
4666                    template = .minimal;
4667                } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
4668                    try fs.File.stdout().writeAll(usage_init);
4669                    return cleanExit();
4670                } else {
4671                    fatal("unrecognized parameter: '{s}'", .{arg});
4672                }
4673            } else {
4674                fatal("unexpected extra parameter: '{s}'", .{arg});
4675            }
4676        }
4677    }
4678
4679    const cwd_path = try introspect.getResolvedCwd(arena);
4680    const cwd_basename = fs.path.basename(cwd_path);
4681    const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);
4682
4683    const fingerprint: Package.Fingerprint = .generate(sanitized_root_name);
4684
4685    switch (template) {
4686        .example => {
4687            var templates = findTemplates(gpa, arena);
4688            defer templates.deinit();
4689
4690            const s = fs.path.sep_str;
4691            const template_paths = [_][]const u8{
4692                Package.build_zig_basename,
4693                Package.Manifest.basename,
4694                "src" ++ s ++ "main.zig",
4695                "src" ++ s ++ "root.zig",
4696            };
4697            var ok_count: usize = 0;
4698
4699            for (template_paths) |template_path| {
4700                if (templates.write(arena, fs.cwd(), sanitized_root_name, template_path, fingerprint)) |_| {
4701                    std.log.info("created {s}", .{template_path});
4702                    ok_count += 1;
4703                } else |err| switch (err) {
4704                    error.PathAlreadyExists => std.log.info("preserving already existing file: {s}", .{
4705                        template_path,
4706                    }),
4707                    else => std.log.err("unable to write {s}: {s}\n", .{ template_path, @errorName(err) }),
4708                }
4709            }
4710
4711            if (ok_count == template_paths.len) {
4712                std.log.info("see `zig build --help` for a menu of options", .{});
4713            }
4714            return cleanExit();
4715        },
4716        .minimal => {
4717            writeSimpleTemplateFile(Package.Manifest.basename,
4718                \\.{{
4719                \\    .name = .{s},
4720                \\    .version = "0.0.1",
4721                \\    .minimum_zig_version = "{s}",
4722                \\    .paths = .{{""}},
4723                \\    .fingerprint = 0x{x},
4724                \\}}
4725                \\
4726            , .{
4727                sanitized_root_name,
4728                build_options.version,
4729                fingerprint.int(),
4730            }) catch |err| switch (err) {
4731                else => fatal("failed to create '{s}': {s}", .{ Package.Manifest.basename, @errorName(err) }),
4732                error.PathAlreadyExists => fatal("refusing to overwrite '{s}'", .{Package.Manifest.basename}),
4733            };
4734            writeSimpleTemplateFile(Package.build_zig_basename,
4735                \\const std = @import("std");
4736                \\
4737                \\pub fn build(b: *std.Build) void {{
4738                \\    _ = b; // stub
4739                \\}}
4740                \\
4741            , .{}) catch |err| switch (err) {
4742                else => fatal("failed to create '{s}': {s}", .{ Package.build_zig_basename, @errorName(err) }),
4743                // `build.zig` already existing is okay: the user has just used `zig init` to set up
4744                // their `build.zig.zon` *after* writing their `build.zig`. So this one isn't fatal.
4745                error.PathAlreadyExists => {
4746                    std.log.info("successfully populated '{s}', preserving existing '{s}'", .{ Package.Manifest.basename, Package.build_zig_basename });
4747                    return cleanExit();
4748                },
4749            };
4750            std.log.info("successfully populated '{s}' and '{s}'", .{ Package.Manifest.basename, Package.build_zig_basename });
4751            return cleanExit();
4752        },
4753    }
4754}
4755
4756fn sanitizeExampleName(arena: Allocator, bytes: []const u8) error{OutOfMemory}![]const u8 {
4757    var result: std.ArrayList(u8) = .empty;
4758    for (bytes, 0..) |byte, i| switch (byte) {
4759        '0'...'9' => {
4760            if (i == 0) try result.append(arena, '_');
4761            try result.append(arena, byte);
4762        },
4763        '_', 'a'...'z', 'A'...'Z' => try result.append(arena, byte),
4764        '-', '.', ' ' => try result.append(arena, '_'),
4765        else => continue,
4766    };
4767    if (!std.zig.isValidId(result.items)) return "foo";
4768    if (result.items.len > Package.Manifest.max_name_len)
4769        result.shrinkRetainingCapacity(Package.Manifest.max_name_len);
4770
4771    return result.toOwnedSlice(arena);
4772}
4773
4774test sanitizeExampleName {
4775    var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator);
4776    defer arena_instance.deinit();
4777    const arena = arena_instance.allocator();
4778
4779    try std.testing.expectEqualStrings("foo_bar", try sanitizeExampleName(arena, "foo bar+"));
4780    try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, ""));
4781    try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "!"));
4782    try std.testing.expectEqualStrings("a", try sanitizeExampleName(arena, "!a"));
4783    try std.testing.expectEqualStrings("a_b", try sanitizeExampleName(arena, "a.b!"));
4784    try std.testing.expectEqualStrings("_01234", try sanitizeExampleName(arena, "01234"));
4785    try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "error"));
4786    try std.testing.expectEqualStrings("foo", try sanitizeExampleName(arena, "test"));
4787    try std.testing.expectEqualStrings("tests", try sanitizeExampleName(arena, "tests"));
4788    try std.testing.expectEqualStrings("test_project", try sanitizeExampleName(arena, "test project"));
4789}
4790
4791fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !void {
4792    dev.check(.build_command);
4793
4794    var build_file: ?[]const u8 = null;
4795    var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
4796    var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
4797    var override_local_cache_dir: ?[]const u8 = try EnvVar.ZIG_LOCAL_CACHE_DIR.get(arena);
4798    var override_build_runner: ?[]const u8 = try EnvVar.ZIG_BUILD_RUNNER.get(arena);
4799    var child_argv = std.array_list.Managed([]const u8).init(arena);
4800    var reference_trace: ?u32 = null;
4801    var debug_compile_errors = false;
4802    var verbose_link = (native_os != .wasi or builtin.link_libc) and
4803        EnvVar.ZIG_VERBOSE_LINK.isSet();
4804    var verbose_cc = (native_os != .wasi or builtin.link_libc) and
4805        EnvVar.ZIG_VERBOSE_CC.isSet();
4806    var verbose_air = false;
4807    var verbose_intern_pool = false;
4808    var verbose_generic_instances = false;
4809    var verbose_llvm_ir: ?[]const u8 = null;
4810    var verbose_llvm_bc: ?[]const u8 = null;
4811    var verbose_cimport = false;
4812    var verbose_llvm_cpu_features = false;
4813    var fetch_only = false;
4814    var fetch_mode: Package.Fetch.JobQueue.Mode = .needed;
4815    var system_pkg_dir_path: ?[]const u8 = null;
4816    var debug_target: ?[]const u8 = null;
4817    var debug_libc_paths_file: ?[]const u8 = null;
4818
4819    const argv_index_exe = child_argv.items.len;
4820    _ = try child_argv.addOne();
4821
4822    const self_exe_path = try fs.selfExePathAlloc(arena);
4823    try child_argv.append(self_exe_path);
4824
4825    const argv_index_zig_lib_dir = child_argv.items.len;
4826    _ = try child_argv.addOne();
4827
4828    const argv_index_build_file = child_argv.items.len;
4829    _ = try child_argv.addOne();
4830
4831    const argv_index_cache_dir = child_argv.items.len;
4832    _ = try child_argv.addOne();
4833
4834    const argv_index_global_cache_dir = child_argv.items.len;
4835    _ = try child_argv.addOne();
4836
4837    try child_argv.appendSlice(&.{
4838        "--seed",
4839        try std.fmt.allocPrint(arena, "0x{x}", .{std.crypto.random.int(u32)}),
4840    });
4841    const argv_index_seed = child_argv.items.len - 1;
4842
4843    // This parent process needs a way to obtain results from the configuration
4844    // phase of the child process. In the future, the make phase will be
4845    // executed in a separate process than the configure phase, and we can then
4846    // use stdout from the configuration phase for this purpose.
4847    //
4848    // However, currently, both phases are in the same process, and Run Step
4849    // provides API for making the runned subprocesses inherit stdout and stderr
4850    // which means these streams are not available for passing metadata back
4851    // to the parent.
4852    //
4853    // Until make and configure phases are separated into different processes,
4854    // the strategy is to choose a temporary file name ahead of time, and then
4855    // read this file in the parent to obtain the results, in the case the child
4856    // exits with code 3.
4857    const results_tmp_file_nonce = std.fmt.hex(std.crypto.random.int(u64));
4858    try child_argv.append("-Z" ++ results_tmp_file_nonce);
4859
4860    var color: Color = .auto;
4861    var n_jobs: ?u32 = null;
4862
4863    {
4864        var i: usize = 0;
4865        while (i < args.len) : (i += 1) {
4866            const arg = args[i];
4867            if (mem.startsWith(u8, arg, "-")) {
4868                if (mem.eql(u8, arg, "--build-file")) {
4869                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4870                    i += 1;
4871                    build_file = args[i];
4872                    continue;
4873                } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
4874                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4875                    i += 1;
4876                    override_lib_dir = args[i];
4877                    continue;
4878                } else if (mem.eql(u8, arg, "--build-runner")) {
4879                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4880                    i += 1;
4881                    override_build_runner = args[i];
4882                    continue;
4883                } else if (mem.eql(u8, arg, "--cache-dir")) {
4884                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4885                    i += 1;
4886                    override_local_cache_dir = args[i];
4887                    continue;
4888                } else if (mem.eql(u8, arg, "--global-cache-dir")) {
4889                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4890                    i += 1;
4891                    override_global_cache_dir = args[i];
4892                    continue;
4893                } else if (mem.eql(u8, arg, "-freference-trace")) {
4894                    reference_trace = 256;
4895                } else if (mem.eql(u8, arg, "--fetch")) {
4896                    fetch_only = true;
4897                } else if (mem.cutPrefix(u8, arg, "--fetch=")) |sub_arg| {
4898                    fetch_only = true;
4899                    fetch_mode = std.meta.stringToEnum(Package.Fetch.JobQueue.Mode, sub_arg) orelse
4900                        fatal("expected [needed|all] after '--fetch=', found '{s}'", .{
4901                            sub_arg,
4902                        });
4903                } else if (mem.eql(u8, arg, "--system")) {
4904                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4905                    i += 1;
4906                    system_pkg_dir_path = args[i];
4907                    try child_argv.append("--system");
4908                    continue;
4909                } else if (mem.cutPrefix(u8, arg, "-freference-trace=")) |num| {
4910                    reference_trace = std.fmt.parseUnsigned(u32, num, 10) catch |err| {
4911                        fatal("unable to parse reference_trace count '{s}': {s}", .{ num, @errorName(err) });
4912                    };
4913                } else if (mem.eql(u8, arg, "-fno-reference-trace")) {
4914                    reference_trace = null;
4915                } else if (mem.eql(u8, arg, "--debug-log")) {
4916                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4917                    try child_argv.appendSlice(args[i .. i + 2]);
4918                    i += 1;
4919                    if (!build_options.enable_logging) {
4920                        warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
4921                    } else {
4922                        try log_scopes.append(arena, args[i]);
4923                    }
4924                    continue;
4925                } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
4926                    if (build_options.enable_debug_extensions) {
4927                        debug_compile_errors = true;
4928                    } else {
4929                        warn("Zig was compiled without debug extensions. --debug-compile-errors has no effect.", .{});
4930                    }
4931                } else if (mem.eql(u8, arg, "--debug-target")) {
4932                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4933                    i += 1;
4934                    if (build_options.enable_debug_extensions) {
4935                        debug_target = args[i];
4936                    } else {
4937                        warn("Zig was compiled without debug extensions. --debug-target has no effect.", .{});
4938                    }
4939                } else if (mem.eql(u8, arg, "--debug-libc")) {
4940                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4941                    i += 1;
4942                    if (build_options.enable_debug_extensions) {
4943                        debug_libc_paths_file = args[i];
4944                    } else {
4945                        warn("Zig was compiled without debug extensions. --debug-libc has no effect.", .{});
4946                    }
4947                } else if (mem.eql(u8, arg, "--verbose-link")) {
4948                    verbose_link = true;
4949                } else if (mem.eql(u8, arg, "--verbose-cc")) {
4950                    verbose_cc = true;
4951                } else if (mem.eql(u8, arg, "--verbose-air")) {
4952                    verbose_air = true;
4953                } else if (mem.eql(u8, arg, "--verbose-intern-pool")) {
4954                    verbose_intern_pool = true;
4955                } else if (mem.eql(u8, arg, "--verbose-generic-instances")) {
4956                    verbose_generic_instances = true;
4957                } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
4958                    verbose_llvm_ir = "-";
4959                } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-ir=")) |rest| {
4960                    verbose_llvm_ir = rest;
4961                } else if (mem.cutPrefix(u8, arg, "--verbose-llvm-bc=")) |rest| {
4962                    verbose_llvm_bc = rest;
4963                } else if (mem.eql(u8, arg, "--verbose-cimport")) {
4964                    verbose_cimport = true;
4965                } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
4966                    verbose_llvm_cpu_features = true;
4967                } else if (mem.eql(u8, arg, "--color")) {
4968                    if (i + 1 >= args.len) fatal("expected [auto|on|off] after {s}", .{arg});
4969                    i += 1;
4970                    color = std.meta.stringToEnum(Color, args[i]) orelse {
4971                        fatal("expected [auto|on|off] after {s}, found '{s}'", .{ arg, args[i] });
4972                    };
4973                    try child_argv.appendSlice(&.{ arg, args[i] });
4974                    continue;
4975                } else if (mem.cutPrefix(u8, arg, "-j")) |str| {
4976                    const num = std.fmt.parseUnsigned(u32, str, 10) catch |err| {
4977                        fatal("unable to parse jobs count '{s}': {s}", .{
4978                            str, @errorName(err),
4979                        });
4980                    };
4981                    if (num < 1) {
4982                        fatal("number of jobs must be at least 1\n", .{});
4983                    }
4984                    n_jobs = num;
4985                } else if (mem.eql(u8, arg, "--seed")) {
4986                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
4987                    i += 1;
4988                    child_argv.items[argv_index_seed] = args[i];
4989                    continue;
4990                } else if (mem.eql(u8, arg, "--")) {
4991                    // The rest of the args are supposed to get passed onto
4992                    // build runner's `build.args`
4993                    try child_argv.appendSlice(args[i..]);
4994                    break;
4995                }
4996            }
4997            try child_argv.append(arg);
4998        }
4999    }
5000
5001    const work_around_btrfs_bug = native_os == .linux and
5002        EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
5003    const root_prog_node = std.Progress.start(.{
5004        .disable_printing = (color == .off),
5005        .root_name = "Compile Build Script",
5006    });
5007    defer root_prog_node.end();
5008
5009    // Normally the build runner is compiled for the host target but here is
5010    // some code to help when debugging edits to the build runner so that you
5011    // can make sure it compiles successfully on other targets.
5012    const resolved_target: Package.Module.ResolvedTarget = t: {
5013        if (build_options.enable_debug_extensions) {
5014            if (debug_target) |triple| {
5015                const target_query = try std.Target.Query.parse(.{
5016                    .arch_os_abi = triple,
5017                });
5018                break :t .{
5019                    .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
5020                    .is_native_os = false,
5021                    .is_native_abi = false,
5022                    .is_explicit_dynamic_linker = false,
5023                };
5024            }
5025        }
5026        break :t .{
5027            .result = std.zig.resolveTargetQueryOrFatal(io, .{}),
5028            .is_native_os = true,
5029            .is_native_abi = true,
5030            .is_explicit_dynamic_linker = false,
5031        };
5032    };
5033    // Likewise, `--debug-libc` allows overriding the libc installation.
5034    const libc_installation: ?*const LibCInstallation = lci: {
5035        const paths_file = debug_libc_paths_file orelse break :lci null;
5036        if (!build_options.enable_debug_extensions) unreachable;
5037        const lci = try arena.create(LibCInstallation);
5038        lci.* = try .parse(arena, paths_file, &resolved_target.result);
5039        break :lci lci;
5040    };
5041
5042    process.raiseFileDescriptorLimit();
5043
5044    const cwd_path = try introspect.getResolvedCwd(arena);
5045    const build_root = try findBuildRoot(arena, .{
5046        .cwd_path = cwd_path,
5047        .build_file = build_file,
5048    });
5049
5050    // This `init` calls `fatal` on error.
5051    var dirs: Compilation.Directories = .init(
5052        arena,
5053        override_lib_dir,
5054        override_global_cache_dir,
5055        .{ .override = path: {
5056            if (override_local_cache_dir) |d| break :path d;
5057            break :path try build_root.directory.join(arena, &.{introspect.default_local_zig_cache_basename});
5058        } },
5059        {},
5060        self_exe_path,
5061    );
5062    defer dirs.deinit();
5063
5064    child_argv.items[argv_index_zig_lib_dir] = dirs.zig_lib.path orelse cwd_path;
5065    child_argv.items[argv_index_build_file] = build_root.directory.path orelse cwd_path;
5066    child_argv.items[argv_index_global_cache_dir] = dirs.global_cache.path orelse cwd_path;
5067    child_argv.items[argv_index_cache_dir] = dirs.local_cache.path orelse cwd_path;
5068
5069    var thread_pool: ThreadPool = undefined;
5070    try thread_pool.init(.{
5071        .allocator = gpa,
5072        .n_jobs = @min(@max(n_jobs orelse std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
5073        .track_ids = true,
5074        .stack_size = thread_stack_size,
5075    });
5076    defer thread_pool.deinit();
5077
5078    // Dummy http client that is not actually used when fetch_command is unsupported.
5079    // Prevents bootstrap from depending on a bunch of unnecessary stuff.
5080    var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct {
5081        allocator: Allocator,
5082        io: Io,
5083        fn deinit(_: @This()) void {}
5084    } = .{ .allocator = gpa, .io = io };
5085    defer http_client.deinit();
5086
5087    var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{};
5088
5089    // This loop is re-evaluated when the build script exits with an indication that it
5090    // could not continue due to missing lazy dependencies.
5091    while (true) {
5092        // We want to release all the locks before executing the child process, so we make a nice
5093        // big block here to ensure the cleanup gets run when we extract out our argv.
5094        {
5095            const main_mod_paths: Package.Module.CreateOptions.Paths = if (override_build_runner) |runner| .{
5096                .root = try .fromUnresolved(arena, dirs, &.{fs.path.dirname(runner) orelse "."}),
5097                .root_src_path = fs.path.basename(runner),
5098            } else .{
5099                .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"),
5100                .root_src_path = "build_runner.zig",
5101            };
5102
5103            const config = try Compilation.Config.resolve(.{
5104                .output_mode = .Exe,
5105                .resolved_target = resolved_target,
5106                .have_zcu = true,
5107                .emit_bin = true,
5108                .is_test = false,
5109            });
5110
5111            const root_mod = try Package.Module.create(arena, .{
5112                .paths = main_mod_paths,
5113                .fully_qualified_name = "root",
5114                .cc_argv = &.{},
5115                .inherited = .{
5116                    .resolved_target = resolved_target,
5117                },
5118                .global = config,
5119                .parent = null,
5120            });
5121
5122            const build_mod = try Package.Module.create(arena, .{
5123                .paths = .{
5124                    .root = try .fromUnresolved(arena, dirs, &.{build_root.directory.path orelse "."}),
5125                    .root_src_path = build_root.build_zig_basename,
5126                },
5127                .fully_qualified_name = "root.@build",
5128                .cc_argv = &.{},
5129                .inherited = .{},
5130                .global = config,
5131                .parent = root_mod,
5132            });
5133
5134            var cleanup_build_dir: ?fs.Dir = null;
5135            defer if (cleanup_build_dir) |*dir| dir.close();
5136
5137            if (dev.env.supports(.fetch_command)) {
5138                const fetch_prog_node = root_prog_node.start("Fetch Packages", 0);
5139                defer fetch_prog_node.end();
5140
5141                var job_queue: Package.Fetch.JobQueue = .{
5142                    .io = io,
5143                    .http_client = &http_client,
5144                    .global_cache = dirs.global_cache,
5145                    .read_only = false,
5146                    .recursive = true,
5147                    .debug_hash = false,
5148                    .work_around_btrfs_bug = work_around_btrfs_bug,
5149                    .unlazy_set = unlazy_set,
5150                    .mode = fetch_mode,
5151                };
5152                defer job_queue.deinit();
5153
5154                if (system_pkg_dir_path) |p| {
5155                    job_queue.global_cache = .{
5156                        .path = p,
5157                        .handle = fs.cwd().openDir(p, .{}) catch |err| {
5158                            fatal("unable to open system package directory '{s}': {s}", .{
5159                                p, @errorName(err),
5160                            });
5161                        },
5162                    };
5163                    job_queue.read_only = true;
5164                    cleanup_build_dir = job_queue.global_cache.handle;
5165                } else {
5166                    try http_client.initDefaultProxies(arena);
5167                }
5168
5169                try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
5170                try job_queue.table.ensureUnusedCapacity(gpa, 1);
5171
5172                const phantom_package_root: Cache.Path = .{ .root_dir = build_root.directory };
5173
5174                var fetch: Package.Fetch = .{
5175                    .arena = std.heap.ArenaAllocator.init(gpa),
5176                    .location = .{ .relative_path = phantom_package_root },
5177                    .location_tok = 0,
5178                    .hash_tok = .none,
5179                    .name_tok = 0,
5180                    .lazy_status = .eager,
5181                    .parent_package_root = phantom_package_root,
5182                    .parent_manifest_ast = null,
5183                    .prog_node = fetch_prog_node,
5184                    .job_queue = &job_queue,
5185                    .omit_missing_hash_error = true,
5186                    .allow_missing_paths_field = false,
5187                    .allow_missing_fingerprint = false,
5188                    .allow_name_string = false,
5189                    .use_latest_commit = false,
5190
5191                    .package_root = undefined,
5192                    .error_bundle = undefined,
5193                    .manifest = null,
5194                    .manifest_ast = undefined,
5195                    .computed_hash = undefined,
5196                    .has_build_zig = true,
5197                    .oom_flag = false,
5198                    .latest_commit = null,
5199
5200                    .module = build_mod,
5201                };
5202                job_queue.all_fetches.appendAssumeCapacity(&fetch);
5203
5204                job_queue.table.putAssumeCapacityNoClobber(
5205                    Package.Fetch.relativePathDigest(phantom_package_root, dirs.global_cache),
5206                    &fetch,
5207                );
5208
5209                job_queue.group.async(io, Package.Fetch.workerRun, .{ &fetch, "root" });
5210                job_queue.group.wait(io);
5211
5212                try job_queue.consolidateErrors();
5213
5214                if (fetch.error_bundle.root_list.items.len > 0) {
5215                    var errors = try fetch.error_bundle.toOwnedBundle("");
5216                    errors.renderToStdErr(.{}, color);
5217                    process.exit(1);
5218                }
5219
5220                if (fetch_only) return cleanExit();
5221
5222                var source_buf = std.array_list.Managed(u8).init(gpa);
5223                defer source_buf.deinit();
5224                try job_queue.createDependenciesSource(&source_buf);
5225                const deps_mod = try createDependenciesModule(
5226                    arena,
5227                    source_buf.items,
5228                    root_mod,
5229                    dirs,
5230                    config,
5231                );
5232
5233                {
5234                    // We need a Module for each package's build.zig.
5235                    const hashes = job_queue.table.keys();
5236                    const fetches = job_queue.table.values();
5237                    try deps_mod.deps.ensureUnusedCapacity(arena, @intCast(hashes.len));
5238                    for (hashes, fetches) |*hash, f| {
5239                        if (f == &fetch) {
5240                            // The first one is a dummy package for the current project.
5241                            continue;
5242                        }
5243                        if (!f.has_build_zig)
5244                            continue;
5245                        const hash_slice = hash.toSlice();
5246                        const mod_root_path = try f.package_root.toString(arena);
5247                        const m = try Package.Module.create(arena, .{
5248                            .paths = .{
5249                                .root = try .fromUnresolved(arena, dirs, &.{mod_root_path}),
5250                                .root_src_path = Package.build_zig_basename,
5251                            },
5252                            .fully_qualified_name = try std.fmt.allocPrint(
5253                                arena,
5254                                "root.@dependencies.{s}",
5255                                .{hash_slice},
5256                            ),
5257                            .cc_argv = &.{},
5258                            .inherited = .{},
5259                            .global = config,
5260                            .parent = root_mod,
5261                        });
5262                        const hash_cloned = try arena.dupe(u8, hash_slice);
5263                        deps_mod.deps.putAssumeCapacityNoClobber(hash_cloned, m);
5264                        f.module = m;
5265                    }
5266
5267                    // Each build.zig module needs access to each of its
5268                    // dependencies' build.zig modules by name.
5269                    for (fetches) |f| {
5270                        const mod = f.module orelse continue;
5271                        const man = f.manifest orelse continue;
5272                        const dep_names = man.dependencies.keys();
5273                        try mod.deps.ensureUnusedCapacity(arena, @intCast(dep_names.len));
5274                        for (dep_names, man.dependencies.values()) |name, dep| {
5275                            const dep_digest = Package.Fetch.depDigest(
5276                                f.package_root,
5277                                dirs.global_cache,
5278                                dep,
5279                            ) orelse continue;
5280                            const dep_mod = job_queue.table.get(dep_digest).?.module orelse continue;
5281                            const name_cloned = try arena.dupe(u8, name);
5282                            mod.deps.putAssumeCapacityNoClobber(name_cloned, dep_mod);
5283                        }
5284                    }
5285                }
5286            } else try createEmptyDependenciesModule(
5287                arena,
5288                root_mod,
5289                dirs,
5290                config,
5291            );
5292
5293            try root_mod.deps.put(arena, "@build", build_mod);
5294
5295            var create_diag: Compilation.CreateDiagnostic = undefined;
5296            const comp = Compilation.create(gpa, arena, io, &create_diag, .{
5297                .libc_installation = libc_installation,
5298                .dirs = dirs,
5299                .root_name = "build",
5300                .config = config,
5301                .root_mod = root_mod,
5302                .main_mod = build_mod,
5303                .emit_bin = .yes_cache,
5304                .self_exe_path = self_exe_path,
5305                .thread_pool = &thread_pool,
5306                .verbose_cc = verbose_cc,
5307                .verbose_link = verbose_link,
5308                .verbose_air = verbose_air,
5309                .verbose_intern_pool = verbose_intern_pool,
5310                .verbose_generic_instances = verbose_generic_instances,
5311                .verbose_llvm_ir = verbose_llvm_ir,
5312                .verbose_llvm_bc = verbose_llvm_bc,
5313                .verbose_cimport = verbose_cimport,
5314                .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
5315                .cache_mode = .whole,
5316                .reference_trace = reference_trace,
5317                .debug_compile_errors = debug_compile_errors,
5318            }) catch |err| switch (err) {
5319                error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
5320                else => fatal("failed to create compilation: {s}", .{@errorName(err)}),
5321            };
5322            defer comp.destroy();
5323
5324            updateModule(comp, color, root_prog_node) catch |err| switch (err) {
5325                error.CompileErrorsReported => process.exit(2),
5326                else => |e| return e,
5327            };
5328
5329            // Since incremental compilation isn't done yet, we use cache_mode = whole
5330            // above, and thus the output file is already closed.
5331            //try comp.makeBinFileExecutable();
5332            child_argv.items[argv_index_exe] = try dirs.local_cache.join(arena, &.{
5333                "o",
5334                &Cache.binToHex(comp.digest.?),
5335                comp.emit_bin.?,
5336            });
5337        }
5338
5339        if (process.can_spawn) {
5340            var child = std.process.Child.init(child_argv.items, gpa);
5341            child.stdin_behavior = .Inherit;
5342            child.stdout_behavior = .Inherit;
5343            child.stderr_behavior = .Inherit;
5344
5345            const term = t: {
5346                std.debug.lockStdErr();
5347                defer std.debug.unlockStdErr();
5348                break :t child.spawnAndWait() catch |err| {
5349                    fatal("failed to spawn build runner {s}: {s}", .{ child_argv.items[0], @errorName(err) });
5350                };
5351            };
5352
5353            switch (term) {
5354                .Exited => |code| {
5355                    if (code == 0) return cleanExit();
5356                    // Indicates that the build runner has reported compile errors
5357                    // and this parent process does not need to report any further
5358                    // diagnostics.
5359                    if (code == 2) process.exit(2);
5360
5361                    if (code == 3) {
5362                        if (!dev.env.supports(.fetch_command)) process.exit(3);
5363                        // Indicates the configure phase failed due to missing lazy
5364                        // dependencies and stdout contains the hashes of the ones
5365                        // that are missing.
5366                        const s = fs.path.sep_str;
5367                        const tmp_sub_path = "tmp" ++ s ++ results_tmp_file_nonce;
5368                        const stdout = dirs.local_cache.handle.readFileAlloc(tmp_sub_path, arena, .limited(50 * 1024 * 1024)) catch |err| {
5369                            fatal("unable to read results of configure phase from '{f}{s}': {s}", .{
5370                                dirs.local_cache, tmp_sub_path, @errorName(err),
5371                            });
5372                        };
5373                        dirs.local_cache.handle.deleteFile(tmp_sub_path) catch {};
5374
5375                        var it = mem.splitScalar(u8, stdout, '\n');
5376                        var any_errors = false;
5377                        while (it.next()) |hash| {
5378                            if (hash.len == 0) continue;
5379                            if (hash.len > Package.Hash.max_len) {
5380                                std.log.err("invalid digest (length {d} exceeds maximum): '{s}'", .{
5381                                    hash.len, hash,
5382                                });
5383                                any_errors = true;
5384                                continue;
5385                            }
5386                            try unlazy_set.put(arena, .fromSlice(hash), {});
5387                        }
5388                        if (any_errors) process.exit(3);
5389                        if (system_pkg_dir_path) |p| {
5390                            // In this mode, the system needs to provide these packages; they
5391                            // cannot be fetched by Zig.
5392                            for (unlazy_set.keys()) |*hash| {
5393                                std.log.err("lazy dependency package not found: {s}" ++ s ++ "{s}", .{
5394                                    p, hash.toSlice(),
5395                                });
5396                            }
5397                            std.log.info("remote package fetching disabled due to --system mode", .{});
5398                            std.log.info("dependencies might be avoidable depending on build configuration", .{});
5399                            process.exit(3);
5400                        }
5401                        continue;
5402                    }
5403
5404                    const cmd = try std.mem.join(arena, " ", child_argv.items);
5405                    fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
5406                },
5407                else => {
5408                    const cmd = try std.mem.join(arena, " ", child_argv.items);
5409                    fatal("the following build command crashed:\n{s}", .{cmd});
5410                },
5411            }
5412        } else {
5413            const cmd = try std.mem.join(arena, " ", child_argv.items);
5414            fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(native_os), cmd });
5415        }
5416    }
5417}
5418
5419const JitCmdOptions = struct {
5420    cmd_name: []const u8,
5421    root_src_path: []const u8,
5422    prepend_zig_lib_dir_path: bool = false,
5423    prepend_global_cache_path: bool = false,
5424    prepend_zig_exe_path: bool = false,
5425    depend_on_aro: bool = false,
5426    capture: ?*[]u8 = null,
5427    /// Send error bundles via std.zig.Server over stdout
5428    server: bool = false,
5429    progress_node: ?std.Progress.Node = null,
5430};
5431
5432fn jitCmd(
5433    gpa: Allocator,
5434    arena: Allocator,
5435    io: Io,
5436    args: []const []const u8,
5437    options: JitCmdOptions,
5438) !void {
5439    dev.check(.jit_command);
5440
5441    const color: Color = .auto;
5442    const root_prog_node = if (options.progress_node) |node| node else std.Progress.start(.{
5443        .disable_printing = (color == .off),
5444    });
5445
5446    const target_query: std.Target.Query = .{};
5447    const resolved_target: Package.Module.ResolvedTarget = .{
5448        .result = std.zig.resolveTargetQueryOrFatal(io, target_query),
5449        .is_native_os = true,
5450        .is_native_abi = true,
5451        .is_explicit_dynamic_linker = false,
5452    };
5453
5454    const self_exe_path = fs.selfExePathAlloc(arena) catch |err| {
5455        fatal("unable to find self exe path: {s}", .{@errorName(err)});
5456    };
5457
5458    const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet())
5459        .Debug
5460    else
5461        .ReleaseFast;
5462    const strip = optimize_mode != .Debug;
5463    const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
5464    const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
5465
5466    // This `init` calls `fatal` on error.
5467    var dirs: Compilation.Directories = .init(
5468        arena,
5469        override_lib_dir,
5470        override_global_cache_dir,
5471        .global,
5472        if (native_os == .wasi) wasi_preopens,
5473        self_exe_path,
5474    );
5475    defer dirs.deinit();
5476
5477    var thread_pool: ThreadPool = undefined;
5478    try thread_pool.init(.{
5479        .allocator = gpa,
5480        .n_jobs = @min(@max(std.Thread.getCpuCount() catch 1, 1), std.math.maxInt(Zcu.PerThread.IdBacking)),
5481        .track_ids = true,
5482        .stack_size = thread_stack_size,
5483    });
5484    defer thread_pool.deinit();
5485
5486    var child_argv: std.ArrayList([]const u8) = .empty;
5487    try child_argv.ensureUnusedCapacity(arena, args.len + 4);
5488
5489    // We want to release all the locks before executing the child process, so we make a nice
5490    // big block here to ensure the cleanup gets run when we extract out our argv.
5491    {
5492        const main_mod_paths: Package.Module.CreateOptions.Paths = .{
5493            .root = try .fromRoot(arena, dirs, .zig_lib, "compiler"),
5494            .root_src_path = options.root_src_path,
5495        };
5496
5497        const config = try Compilation.Config.resolve(.{
5498            .output_mode = .Exe,
5499            .root_strip = strip,
5500            .root_optimize_mode = optimize_mode,
5501            .resolved_target = resolved_target,
5502            .have_zcu = true,
5503            .emit_bin = true,
5504            .is_test = false,
5505        });
5506
5507        const root_mod = try Package.Module.create(arena, .{
5508            .paths = main_mod_paths,
5509            .fully_qualified_name = "root",
5510            .cc_argv = &.{},
5511            .inherited = .{
5512                .resolved_target = resolved_target,
5513                .optimize_mode = optimize_mode,
5514                .strip = strip,
5515            },
5516            .global = config,
5517            .parent = null,
5518        });
5519
5520        if (options.depend_on_aro) {
5521            const aro_mod = try Package.Module.create(arena, .{
5522                .paths = .{
5523                    .root = try .fromRoot(arena, dirs, .zig_lib, "compiler/aro"),
5524                    .root_src_path = "aro.zig",
5525                },
5526                .fully_qualified_name = "aro",
5527                .cc_argv = &.{},
5528                .inherited = .{
5529                    .resolved_target = resolved_target,
5530                    .optimize_mode = optimize_mode,
5531                    .strip = strip,
5532                },
5533                .global = config,
5534                .parent = null,
5535            });
5536            try root_mod.deps.put(arena, "aro", aro_mod);
5537        }
5538
5539        var create_diag: Compilation.CreateDiagnostic = undefined;
5540        const comp = Compilation.create(gpa, arena, io, &create_diag, .{
5541            .dirs = dirs,
5542            .root_name = options.cmd_name,
5543            .config = config,
5544            .root_mod = root_mod,
5545            .main_mod = root_mod,
5546            .emit_bin = .yes_cache,
5547            .self_exe_path = self_exe_path,
5548            .thread_pool = &thread_pool,
5549            .cache_mode = .whole,
5550        }) catch |err| switch (err) {
5551            error.CreateFail => fatal("failed to create compilation: {f}", .{create_diag}),
5552            else => fatal("failed to create compilation: {s}", .{@errorName(err)}),
5553        };
5554        defer comp.destroy();
5555
5556        if (options.server) {
5557            var stdout_writer = fs.File.stdout().writer(&stdout_buffer);
5558            var server: std.zig.Server = .{
5559                .out = &stdout_writer.interface,
5560                .in = undefined, // won't be receiving messages
5561            };
5562
5563            try comp.update(root_prog_node);
5564
5565            var error_bundle = try comp.getAllErrorsAlloc();
5566            defer error_bundle.deinit(comp.gpa);
5567            if (error_bundle.errorMessageCount() > 0) {
5568                try server.serveErrorBundle(error_bundle);
5569                process.exit(2);
5570            }
5571        } else {
5572            updateModule(comp, color, root_prog_node) catch |err| switch (err) {
5573                error.CompileErrorsReported => process.exit(2),
5574                else => |e| return e,
5575            };
5576        }
5577
5578        const exe_path = try dirs.global_cache.join(arena, &.{
5579            "o",
5580            &Cache.binToHex(comp.digest.?),
5581            comp.emit_bin.?,
5582        });
5583        child_argv.appendAssumeCapacity(exe_path);
5584    }
5585
5586    if (options.prepend_zig_lib_dir_path)
5587        child_argv.appendAssumeCapacity(dirs.zig_lib.path.?);
5588    if (options.prepend_zig_exe_path)
5589        child_argv.appendAssumeCapacity(self_exe_path);
5590    if (options.prepend_global_cache_path)
5591        child_argv.appendAssumeCapacity(dirs.global_cache.path.?);
5592
5593    child_argv.appendSliceAssumeCapacity(args);
5594
5595    if (process.can_execv and options.capture == null) {
5596        if (EnvVar.ZIG_DEBUG_CMD.isSet()) {
5597            const cmd = try std.mem.join(arena, " ", child_argv.items);
5598            std.debug.print("{s}\n", .{cmd});
5599        }
5600        const err = process.execv(gpa, child_argv.items);
5601        const cmd = try std.mem.join(arena, " ", child_argv.items);
5602        fatal("the following command failed to execve with '{t}':\n{s}", .{ err, cmd });
5603    }
5604
5605    if (!process.can_spawn) {
5606        const cmd = try std.mem.join(arena, " ", child_argv.items);
5607        fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
5608            @tagName(native_os), cmd,
5609        });
5610    }
5611
5612    var child = std.process.Child.init(child_argv.items, gpa);
5613    child.stdin_behavior = .Inherit;
5614    child.stdout_behavior = if (options.capture == null) .Inherit else .Pipe;
5615    child.stderr_behavior = .Inherit;
5616
5617    try child.spawn();
5618
5619    if (options.capture) |ptr| {
5620        var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
5621        ptr.* = try stdout_reader.interface.allocRemaining(arena, .limited(std.math.maxInt(u32)));
5622    }
5623
5624    const term = try child.wait();
5625    switch (term) {
5626        .Exited => |code| {
5627            if (code == 0) {
5628                if (options.capture != null) return;
5629                return cleanExit();
5630            }
5631            const cmd = try std.mem.join(arena, " ", child_argv.items);
5632            fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
5633        },
5634        else => {
5635            const cmd = try std.mem.join(arena, " ", child_argv.items);
5636            fatal("the following build command crashed:\n{s}", .{cmd});
5637        },
5638    }
5639}
5640
5641const info_zen =
5642    \\
5643    \\ * Communicate intent precisely.
5644    \\ * Edge cases matter.
5645    \\ * Favor reading code over writing code.
5646    \\ * Only one obvious way to do things.
5647    \\ * Runtime crashes are better than bugs.
5648    \\ * Compile errors are better than runtime crashes.
5649    \\ * Incremental improvements.
5650    \\ * Avoid local maximums.
5651    \\ * Reduce the amount one must remember.
5652    \\ * Focus on code rather than style.
5653    \\ * Resource allocation may fail; resource deallocation must succeed.
5654    \\ * Memory is a resource.
5655    \\ * Together we serve the users.
5656    \\
5657    \\
5658;
5659
5660extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
5661
5662extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
5663extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
5664
5665fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 {
5666    var argv = try alloc.allocSentinel(?[*:0]u8, args.len, null);
5667    for (args, 0..) |arg, i| {
5668        argv[i] = try alloc.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
5669    }
5670    return argv;
5671}
5672
5673pub fn clangMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 {
5674    if (!build_options.have_llvm)
5675        fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{});
5676
5677    var arena_instance = std.heap.ArenaAllocator.init(alloc);
5678    defer arena_instance.deinit();
5679    const arena = arena_instance.allocator();
5680
5681    // Convert the args to the null-terminated format Clang expects.
5682    const argv = try argsCopyZ(arena, args);
5683    const exit_code = ZigClang_main(@as(c_int, @intCast(argv.len)), argv.ptr);
5684    return @as(u8, @bitCast(@as(i8, @truncate(exit_code))));
5685}
5686
5687pub fn llvmArMain(alloc: Allocator, args: []const []const u8) error{OutOfMemory}!u8 {
5688    if (!build_options.have_llvm)
5689        fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{});
5690
5691    var arena_instance = std.heap.ArenaAllocator.init(alloc);
5692    defer arena_instance.deinit();
5693    const arena = arena_instance.allocator();
5694
5695    // Convert the args to the format llvm-ar expects.
5696    // We intentionally shave off the zig binary at args[0].
5697    const argv = try argsCopyZ(arena, args[1..]);
5698    const exit_code = ZigLlvmAr_main(@as(c_int, @intCast(argv.len)), argv.ptr);
5699    return @as(u8, @bitCast(@as(i8, @truncate(exit_code))));
5700}
5701
5702/// The first argument determines which backend is invoked. The options are:
5703/// * `ld.lld` - ELF
5704/// * `lld-link` - COFF
5705/// * `wasm-ld` - WebAssembly
5706pub fn lldMain(
5707    alloc: Allocator,
5708    args: []const []const u8,
5709    can_exit_early: bool,
5710) error{OutOfMemory}!u8 {
5711    if (!build_options.have_llvm)
5712        fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
5713
5714    // Print a warning if lld is called multiple times in the same process,
5715    // since it may misbehave
5716    // https://github.com/ziglang/zig/issues/3825
5717    const CallCounter = struct {
5718        var count: usize = 0;
5719    };
5720    if (CallCounter.count == 1) { // Issue the warning on the first repeat call
5721        warn("invoking LLD for the second time within the same process because the host OS ({s}) does not support spawning child processes. This sometimes activates LLD bugs", .{@tagName(native_os)});
5722    }
5723    CallCounter.count += 1;
5724
5725    var arena_instance = std.heap.ArenaAllocator.init(alloc);
5726    defer arena_instance.deinit();
5727    const arena = arena_instance.allocator();
5728
5729    // Convert the args to the format LLD expects.
5730    // We intentionally shave off the zig binary at args[0].
5731    const argv = try argsCopyZ(arena, args[1..]);
5732    // "If an error occurs, false will be returned."
5733    const ok = rc: {
5734        const llvm = @import("codegen/llvm/bindings.zig");
5735        const argc = @as(c_int, @intCast(argv.len));
5736        if (mem.eql(u8, args[1], "ld.lld")) {
5737            break :rc llvm.LinkELF(argc, argv.ptr, can_exit_early, false);
5738        } else if (mem.eql(u8, args[1], "lld-link")) {
5739            break :rc llvm.LinkCOFF(argc, argv.ptr, can_exit_early, false);
5740        } else if (mem.eql(u8, args[1], "wasm-ld")) {
5741            break :rc llvm.LinkWasm(argc, argv.ptr, can_exit_early, false);
5742        } else {
5743            unreachable;
5744        }
5745    };
5746    return @intFromBool(!ok);
5747}
5748
5749const ArgIteratorResponseFile = process.ArgIteratorGeneral(.{ .comments = true, .single_quotes = true });
5750
5751/// Initialize the arguments from a Response File. "*.rsp"
5752fn initArgIteratorResponseFile(allocator: Allocator, resp_file_path: []const u8) !ArgIteratorResponseFile {
5753    const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit
5754    const cmd_line = try fs.cwd().readFileAlloc(resp_file_path, allocator, .limited(max_bytes));
5755    errdefer allocator.free(cmd_line);
5756
5757    return ArgIteratorResponseFile.initTakeOwnership(allocator, cmd_line);
5758}
5759
5760const clang_args = @import("clang_options.zig").list;
5761
5762pub const ClangArgIterator = struct {
5763    has_next: bool,
5764    zig_equivalent: ZigEquivalent,
5765    only_arg: []const u8,
5766    second_arg: []const u8,
5767    other_args: []const []const u8,
5768    argv: []const []const u8,
5769    next_index: usize,
5770    root_args: ?*Args,
5771    arg_iterator_response_file: ArgIteratorResponseFile,
5772    arena: Allocator,
5773
5774    pub const ZigEquivalent = enum {
5775        target,
5776        o,
5777        c,
5778        r,
5779        m,
5780        x,
5781        other,
5782        positional,
5783        l,
5784        ignore,
5785        driver_punt,
5786        pic,
5787        no_pic,
5788        pie,
5789        no_pie,
5790        lto,
5791        no_lto,
5792        unwind_tables,
5793        no_unwind_tables,
5794        asynchronous_unwind_tables,
5795        no_asynchronous_unwind_tables,
5796        nostdlib,
5797        nostdlib_cpp,
5798        shared,
5799        rdynamic,
5800        wl,
5801        wp,
5802        preprocess_only,
5803        asm_only,
5804        optimize,
5805        debug,
5806        gdwarf32,
5807        gdwarf64,
5808        sanitize,
5809        no_sanitize,
5810        sanitize_trap,
5811        no_sanitize_trap,
5812        linker_script,
5813        dry_run,
5814        verbose,
5815        for_linker,
5816        linker_input_z,
5817        lib_dir,
5818        mcpu,
5819        dep_file,
5820        dep_file_to_stdout,
5821        framework_dir,
5822        framework,
5823        nostdlibinc,
5824        red_zone,
5825        no_red_zone,
5826        omit_frame_pointer,
5827        no_omit_frame_pointer,
5828        function_sections,
5829        no_function_sections,
5830        data_sections,
5831        no_data_sections,
5832        builtin,
5833        no_builtin,
5834        color_diagnostics,
5835        no_color_diagnostics,
5836        stack_check,
5837        no_stack_check,
5838        stack_protector,
5839        no_stack_protector,
5840        strip,
5841        exec_model,
5842        emit_llvm,
5843        sysroot,
5844        entry,
5845        force_undefined_symbol,
5846        weak_library,
5847        weak_framework,
5848        headerpad_max_install_names,
5849        compress_debug_sections,
5850        install_name,
5851        undefined,
5852        force_load_objc,
5853        mingw_unicode_entry_point,
5854        san_cov_trace_pc_guard,
5855        san_cov,
5856        no_san_cov,
5857        rtlib,
5858        static,
5859        dynamic,
5860    };
5861
5862    const Args = struct {
5863        next_index: usize,
5864        argv: []const []const u8,
5865    };
5866
5867    fn init(arena: Allocator, argv: []const []const u8) ClangArgIterator {
5868        return .{
5869            .next_index = 2, // `zig cc foo` this points to `foo`
5870            .has_next = argv.len > 2,
5871            .zig_equivalent = undefined,
5872            .only_arg = undefined,
5873            .second_arg = undefined,
5874            .other_args = undefined,
5875            .argv = argv,
5876            .root_args = null,
5877            .arg_iterator_response_file = undefined,
5878            .arena = arena,
5879        };
5880    }
5881
5882    fn next(self: *ClangArgIterator) !void {
5883        assert(self.has_next);
5884        assert(self.next_index < self.argv.len);
5885        // In this state we know that the parameter we are looking at is a root parameter
5886        // rather than an argument to a parameter.
5887        // We adjust the len below when necessary.
5888        self.other_args = (self.argv.ptr + self.next_index)[0..1];
5889        var arg = self.argv[self.next_index];
5890        self.incrementArgIndex();
5891
5892        if (mem.startsWith(u8, arg, "@")) {
5893            if (self.root_args != null) return error.NestedResponseFile;
5894
5895            // This is a "compiler response file". We must parse the file and treat its
5896            // contents as command line parameters.
5897            const arena = self.arena;
5898            const resp_file_path = arg[1..];
5899
5900            self.arg_iterator_response_file =
5901                initArgIteratorResponseFile(arena, resp_file_path) catch |err| {
5902                    fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) });
5903                };
5904            // NOTE: The ArgIteratorResponseFile returns tokens from next() that are slices of an
5905            // internal buffer. This internal buffer is arena allocated, so it is not cleaned up here.
5906
5907            var resp_arg_list = std.array_list.Managed([]const u8).init(arena);
5908            defer resp_arg_list.deinit();
5909            {
5910                while (self.arg_iterator_response_file.next()) |token| {
5911                    try resp_arg_list.append(token);
5912                }
5913
5914                const args = try arena.create(Args);
5915                errdefer arena.destroy(args);
5916                args.* = .{
5917                    .next_index = self.next_index,
5918                    .argv = self.argv,
5919                };
5920                self.root_args = args;
5921            }
5922            const resp_arg_slice = try resp_arg_list.toOwnedSlice();
5923            self.next_index = 0;
5924            self.argv = resp_arg_slice;
5925
5926            if (resp_arg_slice.len == 0) {
5927                self.resolveRespFileArgs();
5928                return;
5929            }
5930
5931            self.has_next = true;
5932            self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary.
5933            arg = self.argv[self.next_index];
5934            self.incrementArgIndex();
5935        }
5936
5937        if (mem.eql(u8, arg, "-") or !mem.startsWith(u8, arg, "-")) {
5938            self.zig_equivalent = .positional;
5939            self.only_arg = arg;
5940            return;
5941        }
5942
5943        find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
5944            .flag => {
5945                const prefix_len = clang_arg.matchEql(arg);
5946                if (prefix_len > 0) {
5947                    self.zig_equivalent = clang_arg.zig_equivalent;
5948                    self.only_arg = arg[prefix_len..];
5949
5950                    break :find_clang_arg;
5951                }
5952            },
5953            .joined, .comma_joined => {
5954                // joined example: --target=foo
5955                // comma_joined example: -Wl,-soname,libsoundio.so.2
5956                const prefix_len = clang_arg.matchStartsWith(arg);
5957                if (prefix_len != 0) {
5958                    self.zig_equivalent = clang_arg.zig_equivalent;
5959                    self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part.
5960
5961                    break :find_clang_arg;
5962                }
5963            },
5964            .joined_or_separate => {
5965                // Examples: `-lfoo`, `-l foo`
5966                const prefix_len = clang_arg.matchStartsWith(arg);
5967                if (prefix_len == arg.len) {
5968                    if (self.next_index >= self.argv.len) {
5969                        fatal("Expected parameter after '{s}'", .{arg});
5970                    }
5971                    self.only_arg = self.argv[self.next_index];
5972                    self.incrementArgIndex();
5973                    self.other_args.len += 1;
5974                    self.zig_equivalent = clang_arg.zig_equivalent;
5975
5976                    break :find_clang_arg;
5977                } else if (prefix_len != 0) {
5978                    self.zig_equivalent = clang_arg.zig_equivalent;
5979                    self.only_arg = arg[prefix_len..];
5980
5981                    break :find_clang_arg;
5982                }
5983            },
5984            .joined_and_separate => {
5985                // Example: `-Xopenmp-target=riscv64-linux-unknown foo`
5986                const prefix_len = clang_arg.matchStartsWith(arg);
5987                if (prefix_len != 0) {
5988                    self.only_arg = arg[prefix_len..];
5989                    if (self.next_index >= self.argv.len) {
5990                        fatal("Expected parameter after '{s}'", .{arg});
5991                    }
5992                    self.second_arg = self.argv[self.next_index];
5993                    self.incrementArgIndex();
5994                    self.other_args.len += 1;
5995                    self.zig_equivalent = clang_arg.zig_equivalent;
5996                    break :find_clang_arg;
5997                }
5998            },
5999            .separate => if (clang_arg.matchEql(arg) > 0) {
6000                if (self.next_index >= self.argv.len) {
6001                    fatal("Expected parameter after '{s}'", .{arg});
6002                }
6003                self.only_arg = self.argv[self.next_index];
6004                self.incrementArgIndex();
6005                self.other_args.len += 1;
6006                self.zig_equivalent = clang_arg.zig_equivalent;
6007                break :find_clang_arg;
6008            },
6009            .remaining_args_joined => {
6010                const prefix_len = clang_arg.matchStartsWith(arg);
6011                if (prefix_len != 0) {
6012                    @panic("TODO");
6013                }
6014            },
6015            .multi_arg => |num_args| if (clang_arg.matchEql(arg) > 0) {
6016                // Example `-sectcreate <arg1> <arg2> <arg3>`.
6017                var i: usize = 0;
6018                while (i < num_args) : (i += 1) {
6019                    self.incrementArgIndex();
6020                    self.other_args.len += 1;
6021                }
6022                self.zig_equivalent = clang_arg.zig_equivalent;
6023                break :find_clang_arg;
6024            },
6025        } else {
6026            fatal("Unknown Clang option: '{s}'", .{arg});
6027        }
6028    }
6029
6030    fn incrementArgIndex(self: *ClangArgIterator) void {
6031        self.next_index += 1;
6032        self.resolveRespFileArgs();
6033    }
6034
6035    fn resolveRespFileArgs(self: *ClangArgIterator) void {
6036        const arena = self.arena;
6037        if (self.next_index >= self.argv.len) {
6038            if (self.root_args) |root_args| {
6039                self.next_index = root_args.next_index;
6040                self.argv = root_args.argv;
6041
6042                arena.destroy(root_args);
6043                self.root_args = null;
6044            }
6045            if (self.next_index >= self.argv.len) {
6046                self.has_next = false;
6047            }
6048        }
6049    }
6050};
6051
6052fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
6053    return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse
6054        fatal("unsupported machine code model: '{s}'", .{arg});
6055}
6056
6057const usage_ast_check =
6058    \\Usage: zig ast-check [file]
6059    \\
6060    \\    Given a .zig source file or .zon file, reports any compile errors
6061    \\    that can be ascertained on the basis of the source code alone,
6062    \\    without target information or type checking.
6063    \\
6064    \\    If [file] is omitted, stdin is used.
6065    \\
6066    \\Options:
6067    \\  -h, --help            Print this help and exit
6068    \\  --color [auto|off|on] Enable or disable colored error messages
6069    \\  --zon                 Treat the input file as ZON, regardless of file extension
6070    \\  -t                    (debug option) Output ZIR in text form to stdout
6071    \\
6072    \\
6073;
6074
6075fn cmdAstCheck(arena: Allocator, io: Io, args: []const []const u8) !void {
6076    dev.check(.ast_check_command);
6077
6078    const Zir = std.zig.Zir;
6079
6080    var color: Color = .auto;
6081    var want_output_text = false;
6082    var force_zon = false;
6083    var zig_source_path: ?[]const u8 = null;
6084
6085    var i: usize = 0;
6086    while (i < args.len) : (i += 1) {
6087        const arg = args[i];
6088        if (mem.startsWith(u8, arg, "-")) {
6089            if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
6090                try fs.File.stdout().writeAll(usage_ast_check);
6091                return cleanExit();
6092            } else if (mem.eql(u8, arg, "-t")) {
6093                want_output_text = true;
6094            } else if (mem.eql(u8, arg, "--zon")) {
6095                force_zon = true;
6096            } else if (mem.eql(u8, arg, "--color")) {
6097                if (i + 1 >= args.len) {
6098                    fatal("expected [auto|on|off] after --color", .{});
6099                }
6100                i += 1;
6101                const next_arg = args[i];
6102                color = std.meta.stringToEnum(Color, next_arg) orelse {
6103                    fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
6104                };
6105            } else {
6106                fatal("unrecognized parameter: '{s}'", .{arg});
6107            }
6108        } else if (zig_source_path == null) {
6109            zig_source_path = arg;
6110        } else {
6111            fatal("extra positional parameter: '{s}'", .{arg});
6112        }
6113    }
6114
6115    const display_path = zig_source_path orelse "<stdin>";
6116    const source: [:0]const u8 = s: {
6117        var f = if (zig_source_path) |p| file: {
6118            break :file fs.cwd().openFile(p, .{}) catch |err| {
6119                fatal("unable to open file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });
6120            };
6121        } else fs.File.stdin();
6122        defer if (zig_source_path != null) f.close();
6123        var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer);
6124        break :s std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err| {
6125            fatal("unable to load file '{s}' for ast-check: {s}", .{ display_path, @errorName(err) });
6126        };
6127    };
6128
6129    const mode: Ast.Mode = mode: {
6130        if (force_zon) break :mode .zon;
6131        if (zig_source_path) |path| {
6132            if (mem.endsWith(u8, path, ".zon")) {
6133                break :mode .zon;
6134            }
6135        }
6136        break :mode .zig;
6137    };
6138
6139    const tree = try Ast.parse(arena, source, mode);
6140
6141    var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6142    const stdout_bw = &stdout_writer.interface;
6143    switch (mode) {
6144        .zig => {
6145            const zir = try AstGen.generate(arena, tree);
6146
6147            if (zir.hasCompileErrors()) {
6148                var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6149                try wip_errors.init(arena);
6150                try wip_errors.addZirErrorMessages(zir, tree, source, display_path);
6151                var error_bundle = try wip_errors.toOwnedBundle("");
6152                error_bundle.renderToStdErr(.{}, color);
6153                if (zir.loweringFailed()) {
6154                    process.exit(1);
6155                }
6156            }
6157
6158            if (!want_output_text) {
6159                if (zir.hasCompileErrors()) {
6160                    process.exit(1);
6161                } else {
6162                    return cleanExit();
6163                }
6164            }
6165            if (!build_options.enable_debug_extensions) {
6166                fatal("-t option only available in builds of zig with debug extensions", .{});
6167            }
6168
6169            {
6170                const token_bytes = @sizeOf(Ast.TokenList) +
6171                    tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
6172                const tree_bytes = @sizeOf(Ast) + tree.nodes.len *
6173                    (@sizeOf(Ast.Node.Tag) +
6174                        @sizeOf(Ast.TokenIndex) +
6175                        // Here we don't use @sizeOf(Ast.Node.Data) because it would include
6176                        // the debug safety tag but we want to measure release size.
6177                        8);
6178                const instruction_bytes = zir.instructions.len *
6179                    // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
6180                    // the debug safety tag but we want to measure release size.
6181                    (@sizeOf(Zir.Inst.Tag) + 8);
6182                const extra_bytes = zir.extra.len * @sizeOf(u32);
6183                const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6184                    zir.string_bytes.len * @sizeOf(u8);
6185                // zig fmt: off
6186                try stdout_bw.print(
6187                    \\# Source bytes:       {Bi}
6188                    \\# Tokens:             {} ({Bi})
6189                    \\# AST Nodes:          {} ({Bi})
6190                    \\# Total ZIR bytes:    {Bi}
6191                    \\# Instructions:       {d} ({Bi})
6192                    \\# String Table Bytes: {}
6193                    \\# Extra Data Items:   {d} ({Bi})
6194                    \\
6195                , .{
6196                    source.len,
6197                    tree.tokens.len, token_bytes,
6198                    tree.nodes.len, tree_bytes,
6199                    total_bytes,
6200                    zir.instructions.len, instruction_bytes,
6201                    zir.string_bytes.len,
6202                    zir.extra.len, extra_bytes,
6203                });
6204                // zig fmt: on
6205            }
6206
6207            try @import("print_zir.zig").renderAsText(arena, tree, zir, stdout_bw);
6208            try stdout_bw.flush();
6209
6210            if (zir.hasCompileErrors()) {
6211                process.exit(1);
6212            } else {
6213                return cleanExit();
6214            }
6215        },
6216        .zon => {
6217            const zoir = try ZonGen.generate(arena, tree, .{});
6218            if (zoir.hasCompileErrors()) {
6219                var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6220                try wip_errors.init(arena);
6221                try wip_errors.addZoirErrorMessages(zoir, tree, source, display_path);
6222                var error_bundle = try wip_errors.toOwnedBundle("");
6223                error_bundle.renderToStdErr(.{}, color);
6224                process.exit(1);
6225            }
6226
6227            if (!want_output_text) {
6228                return cleanExit();
6229            }
6230
6231            if (!build_options.enable_debug_extensions) {
6232                fatal("-t option only available in builds of zig with debug extensions", .{});
6233            }
6234
6235            try @import("print_zoir.zig").renderToWriter(zoir, arena, stdout_bw);
6236            try stdout_bw.flush();
6237            return cleanExit();
6238        },
6239    }
6240}
6241
6242fn cmdDetectCpu(io: Io, args: []const []const u8) !void {
6243    dev.check(.detect_cpu_command);
6244
6245    const detect_cpu_usage =
6246        \\Usage: zig detect-cpu [--llvm]
6247        \\
6248        \\    Print the host CPU name and feature set to stdout.
6249        \\
6250        \\Options:
6251        \\  -h, --help                    Print this help and exit
6252        \\  --llvm                        Detect using LLVM API
6253        \\
6254    ;
6255
6256    var use_llvm = false;
6257
6258    {
6259        var i: usize = 0;
6260        while (i < args.len) : (i += 1) {
6261            const arg = args[i];
6262            if (mem.startsWith(u8, arg, "-")) {
6263                if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
6264                    try fs.File.stdout().writeAll(detect_cpu_usage);
6265                    return cleanExit();
6266                } else if (mem.eql(u8, arg, "--llvm")) {
6267                    use_llvm = true;
6268                } else {
6269                    fatal("unrecognized parameter: '{s}'", .{arg});
6270                }
6271            } else {
6272                fatal("unexpected extra parameter: '{s}'", .{arg});
6273            }
6274        }
6275    }
6276
6277    if (use_llvm) {
6278        if (!build_options.have_llvm)
6279            fatal("compiler does not use LLVM; cannot compare CPU features with LLVM", .{});
6280
6281        const llvm = @import("codegen/llvm/bindings.zig");
6282        const name = llvm.GetHostCPUName() orelse fatal("LLVM could not figure out the host cpu name", .{});
6283        const features = llvm.GetHostCPUFeatures() orelse fatal("LLVM could not figure out the host cpu feature set", .{});
6284        const cpu = try detectNativeCpuWithLLVM(builtin.cpu.arch, name, features);
6285        try printCpu(cpu);
6286    } else {
6287        const host_target = std.zig.resolveTargetQueryOrFatal(io, .{});
6288        try printCpu(host_target.cpu);
6289    }
6290}
6291
6292fn detectNativeCpuWithLLVM(
6293    arch: std.Target.Cpu.Arch,
6294    llvm_cpu_name_z: ?[*:0]const u8,
6295    llvm_cpu_features_opt: ?[*:0]const u8,
6296) !std.Target.Cpu {
6297    var result = std.Target.Cpu.baseline(arch, builtin.os);
6298
6299    if (llvm_cpu_name_z) |cpu_name_z| {
6300        const llvm_cpu_name = mem.span(cpu_name_z);
6301
6302        for (arch.allCpuModels()) |model| {
6303            const this_llvm_name = model.llvm_name orelse continue;
6304            if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) {
6305                // Here we use the non-dependencies-populated set,
6306                // so that subtracting features later in this function
6307                // affect the prepopulated set.
6308                result = std.Target.Cpu{
6309                    .arch = arch,
6310                    .model = model,
6311                    .features = model.features,
6312                };
6313                break;
6314            }
6315        }
6316    }
6317
6318    const all_features = arch.allFeaturesList();
6319
6320    if (llvm_cpu_features_opt) |llvm_cpu_features| {
6321        var it = mem.tokenizeScalar(u8, mem.span(llvm_cpu_features), ',');
6322        while (it.next()) |decorated_llvm_feat| {
6323            var op: enum {
6324                add,
6325                sub,
6326            } = undefined;
6327            var llvm_feat: []const u8 = undefined;
6328            if (mem.startsWith(u8, decorated_llvm_feat, "+")) {
6329                op = .add;
6330                llvm_feat = decorated_llvm_feat[1..];
6331            } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) {
6332                op = .sub;
6333                llvm_feat = decorated_llvm_feat[1..];
6334            } else {
6335                return error.InvalidLlvmCpuFeaturesFormat;
6336            }
6337            for (all_features, 0..) |feature, index_usize| {
6338                const this_llvm_name = feature.llvm_name orelse continue;
6339                if (mem.eql(u8, llvm_feat, this_llvm_name)) {
6340                    const index: std.Target.Cpu.Feature.Set.Index = @intCast(index_usize);
6341                    switch (op) {
6342                        .add => result.features.addFeature(index),
6343                        .sub => result.features.removeFeature(index),
6344                    }
6345                    break;
6346                }
6347            }
6348        }
6349    }
6350
6351    result.features.populateDependencies(all_features);
6352    return result;
6353}
6354
6355fn printCpu(cpu: std.Target.Cpu) !void {
6356    var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6357    const stdout_bw = &stdout_writer.interface;
6358
6359    if (cpu.model.llvm_name) |llvm_name| {
6360        try stdout_bw.print("{s}\n", .{llvm_name});
6361    }
6362
6363    const all_features = cpu.arch.allFeaturesList();
6364    for (all_features, 0..) |feature, index_usize| {
6365        const llvm_name = feature.llvm_name orelse continue;
6366        const index: std.Target.Cpu.Feature.Set.Index = @intCast(index_usize);
6367        const is_enabled = cpu.features.isEnabled(index);
6368        const plus_or_minus = "-+"[@intFromBool(is_enabled)];
6369        try stdout_bw.print("{c}{s}\n", .{ plus_or_minus, llvm_name });
6370    }
6371
6372    try stdout_bw.flush();
6373}
6374
6375fn cmdDumpLlvmInts(
6376    gpa: Allocator,
6377    arena: Allocator,
6378    args: []const []const u8,
6379) !void {
6380    dev.check(.llvm_ints_command);
6381
6382    _ = gpa;
6383
6384    if (!build_options.have_llvm)
6385        fatal("compiler does not use LLVM; cannot dump LLVM integer sizes", .{});
6386
6387    const triple = try arena.dupeZ(u8, args[0]);
6388
6389    const llvm = @import("codegen/llvm/bindings.zig");
6390
6391    for ([_]std.Target.Cpu.Arch{ .aarch64, .x86 }) |arch| {
6392        @import("codegen/llvm.zig").initializeLLVMTarget(arch);
6393    }
6394
6395    const target: *llvm.Target = t: {
6396        var target: *llvm.Target = undefined;
6397        var error_message: [*:0]const u8 = undefined;
6398        if (llvm.Target.getFromTriple(triple, &target, &error_message) != .False) @panic("bad");
6399        break :t target;
6400    };
6401    const tm = llvm.TargetMachine.create(target, triple, null, null, .None, .Default, .Default, false, false, .Default, null, false);
6402    const dl = tm.createTargetDataLayout();
6403    const context = llvm.Context.create();
6404
6405    var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6406    const stdout_bw = &stdout_writer.interface;
6407    for ([_]u16{ 1, 8, 16, 32, 64, 128, 256 }) |bits| {
6408        const int_type = context.intType(bits);
6409        const alignment = dl.abiAlignmentOfType(int_type);
6410        try stdout_bw.print("LLVMABIAlignmentOfType(i{d}) == {d}\n", .{ bits, alignment });
6411    }
6412    try stdout_bw.flush();
6413
6414    return cleanExit();
6415}
6416
6417/// This is only enabled for debug builds.
6418fn cmdDumpZir(arena: Allocator, io: Io, args: []const []const u8) !void {
6419    dev.check(.dump_zir_command);
6420
6421    const Zir = std.zig.Zir;
6422
6423    const cache_file = args[0];
6424
6425    var f = fs.cwd().openFile(cache_file, .{}) catch |err| {
6426        fatal("unable to open zir cache file for dumping '{s}': {s}", .{ cache_file, @errorName(err) });
6427    };
6428    defer f.close();
6429
6430    const zir = try Zcu.loadZirCache(arena, io, f);
6431    var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6432    const stdout_bw = &stdout_writer.interface;
6433    {
6434        const instruction_bytes = zir.instructions.len *
6435            // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
6436            // the debug safety tag but we want to measure release size.
6437            (@sizeOf(Zir.Inst.Tag) + 8);
6438        const extra_bytes = zir.extra.len * @sizeOf(u32);
6439        const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
6440            zir.string_bytes.len * @sizeOf(u8);
6441        // zig fmt: off
6442        try stdout_bw.print(
6443            \\# Total ZIR bytes:    {Bi}
6444            \\# Instructions:       {d} ({Bi})
6445            \\# String Table Bytes: {Bi}
6446            \\# Extra Data Items:   {d} ({Bi})
6447            \\
6448        , .{
6449            total_bytes,
6450            zir.instructions.len, instruction_bytes,
6451            zir.string_bytes.len,
6452            zir.extra.len, extra_bytes,
6453        });
6454        // zig fmt: on
6455    }
6456
6457    try @import("print_zir.zig").renderAsText(arena, null, zir, stdout_bw);
6458    try stdout_bw.flush();
6459}
6460
6461/// This is only enabled for debug builds.
6462fn cmdChangelist(arena: Allocator, io: Io, args: []const []const u8) !void {
6463    dev.check(.changelist_command);
6464
6465    const color: Color = .auto;
6466    const Zir = std.zig.Zir;
6467
6468    const old_source_path = args[0];
6469    const new_source_path = args[1];
6470
6471    const old_source = source: {
6472        var f = fs.cwd().openFile(old_source_path, .{}) catch |err|
6473            fatal("unable to open old source file '{s}': {s}", .{ old_source_path, @errorName(err) });
6474        defer f.close();
6475        var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer);
6476        break :source std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err|
6477            fatal("unable to read old source file '{s}': {s}", .{ old_source_path, @errorName(err) });
6478    };
6479    const new_source = source: {
6480        var f = fs.cwd().openFile(new_source_path, .{}) catch |err|
6481            fatal("unable to open new source file '{s}': {s}", .{ new_source_path, @errorName(err) });
6482        defer f.close();
6483        var file_reader: fs.File.Reader = f.reader(io, &stdin_buffer);
6484        break :source std.zig.readSourceFileToEndAlloc(arena, &file_reader) catch |err|
6485            fatal("unable to read new source file '{s}': {s}", .{ new_source_path, @errorName(err) });
6486    };
6487
6488    const old_tree = try Ast.parse(arena, old_source, .zig);
6489    const old_zir = try AstGen.generate(arena, old_tree);
6490
6491    if (old_zir.loweringFailed()) {
6492        var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6493        try wip_errors.init(arena);
6494        try wip_errors.addZirErrorMessages(old_zir, old_tree, old_source, old_source_path);
6495        var error_bundle = try wip_errors.toOwnedBundle("");
6496        error_bundle.renderToStdErr(.{}, color);
6497        process.exit(1);
6498    }
6499
6500    const new_tree = try Ast.parse(arena, new_source, .zig);
6501    const new_zir = try AstGen.generate(arena, new_tree);
6502
6503    if (new_zir.loweringFailed()) {
6504        var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6505        try wip_errors.init(arena);
6506        try wip_errors.addZirErrorMessages(new_zir, new_tree, new_source, new_source_path);
6507        var error_bundle = try wip_errors.toOwnedBundle("");
6508        error_bundle.renderToStdErr(.{}, color);
6509        process.exit(1);
6510    }
6511
6512    var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty;
6513    try Zcu.mapOldZirToNew(arena, old_zir, new_zir, &inst_map);
6514
6515    var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
6516    const stdout_bw = &stdout_writer.interface;
6517    {
6518        try stdout_bw.print("Instruction mappings:\n", .{});
6519        var it = inst_map.iterator();
6520        while (it.next()) |entry| {
6521            try stdout_bw.print(" %{d} => %{d}\n", .{
6522                @intFromEnum(entry.key_ptr.*),
6523                @intFromEnum(entry.value_ptr.*),
6524            });
6525        }
6526    }
6527    try stdout_bw.flush();
6528}
6529
6530fn eatIntPrefix(arg: []const u8, base: u8) []const u8 {
6531    if (arg.len > 2 and arg[0] == '0') {
6532        switch (std.ascii.toLower(arg[1])) {
6533            'b' => if (base == 2) return arg[2..],
6534            'o' => if (base == 8) return arg[2..],
6535            'x' => if (base == 16) return arg[2..],
6536            else => {},
6537        }
6538    }
6539    return arg;
6540}
6541
6542fn prefixedIntArg(arg: []const u8, prefix: []const u8) ?u64 {
6543    const number = mem.cutPrefix(u8, arg, prefix) orelse return null;
6544    return std.fmt.parseUnsigned(u64, number, 0) catch |err| fatal("unable to parse '{s}': {t}", .{ arg, err });
6545}
6546
6547fn warnAboutForeignBinaries(
6548    io: Io,
6549    arena: Allocator,
6550    arg_mode: ArgMode,
6551    target: *const std.Target,
6552    link_libc: bool,
6553) !void {
6554    const host_query: std.Target.Query = .{};
6555    const host_target = std.zig.resolveTargetQueryOrFatal(io, host_query);
6556
6557    switch (std.zig.system.getExternalExecutor(&host_target, target, .{ .link_libc = link_libc })) {
6558        .native => return,
6559        .rosetta => {
6560            const host_name = try host_target.zigTriple(arena);
6561            const foreign_name = try target.zigTriple(arena);
6562            warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{
6563                host_name, foreign_name,
6564            });
6565        },
6566        .qemu => |qemu| {
6567            const host_name = try host_target.zigTriple(arena);
6568            const foreign_name = try target.zigTriple(arena);
6569            switch (arg_mode) {
6570                .zig_test => warn(
6571                    "the host system ({s}) does not appear to be capable of executing binaries " ++
6572                        "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
6573                        "to run the tests",
6574                    .{ host_name, foreign_name, qemu },
6575                ),
6576                else => warn(
6577                    "the host system ({s}) does not appear to be capable of executing binaries " ++
6578                        "from the target ({s}). Consider using '{s}' to run the binary",
6579                    .{ host_name, foreign_name, qemu },
6580                ),
6581            }
6582        },
6583        .wine => |wine| {
6584            const host_name = try host_target.zigTriple(arena);
6585            const foreign_name = try target.zigTriple(arena);
6586            switch (arg_mode) {
6587                .zig_test => warn(
6588                    "the host system ({s}) does not appear to be capable of executing binaries " ++
6589                        "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
6590                        "to run the tests",
6591                    .{ host_name, foreign_name, wine },
6592                ),
6593                else => warn(
6594                    "the host system ({s}) does not appear to be capable of executing binaries " ++
6595                        "from the target ({s}). Consider using '{s}' to run the binary",
6596                    .{ host_name, foreign_name, wine },
6597                ),
6598            }
6599        },
6600        .wasmtime => |wasmtime| {
6601            const host_name = try host_target.zigTriple(arena);
6602            const foreign_name = try target.zigTriple(arena);
6603            switch (arg_mode) {
6604                .zig_test => warn(
6605                    "the host system ({s}) does not appear to be capable of executing binaries " ++
6606                        "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
6607                        "to run the tests",
6608                    .{ host_name, foreign_name, wasmtime },
6609                ),
6610                else => warn(
6611                    "the host system ({s}) does not appear to be capable of executing binaries " ++
6612                        "from the target ({s}). Consider using '{s}' to run the binary",
6613                    .{ host_name, foreign_name, wasmtime },
6614                ),
6615            }
6616        },
6617        .darling => |darling| {
6618            const host_name = try host_target.zigTriple(arena);
6619            const foreign_name = try target.zigTriple(arena);
6620            switch (arg_mode) {
6621                .zig_test => warn(
6622                    "the host system ({s}) does not appear to be capable of executing binaries " ++
6623                        "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
6624                        "to run the tests",
6625                    .{ host_name, foreign_name, darling },
6626                ),
6627                else => warn(
6628                    "the host system ({s}) does not appear to be capable of executing binaries " ++
6629                        "from the target ({s}). Consider using '{s}' to run the binary",
6630                    .{ host_name, foreign_name, darling },
6631                ),
6632            }
6633        },
6634        .bad_dl => |foreign_dl| {
6635            const host_dl = host_target.dynamic_linker.get() orelse "(none)";
6636            const tip_suffix = switch (arg_mode) {
6637                .zig_test => ", '--test-no-exec', or '--test-cmd'",
6638                else => "",
6639            };
6640            warn("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider using '--dynamic-linker'{s}", .{
6641                host_dl, foreign_dl, tip_suffix,
6642            });
6643        },
6644        .bad_os_or_cpu => {
6645            const host_name = try host_target.zigTriple(arena);
6646            const foreign_name = try target.zigTriple(arena);
6647            const tip_suffix = switch (arg_mode) {
6648                .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'",
6649                else => "",
6650            };
6651            warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}){s}", .{
6652                host_name, foreign_name, tip_suffix,
6653            });
6654        },
6655    }
6656}
6657
6658fn parseSubsystem(arg: []const u8) !std.zig.Subsystem {
6659    return std.meta.stringToEnum(std.zig.Subsystem, arg) orelse
6660        fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{
6661            arg,
6662            \\  console
6663            \\  windows
6664            \\  posix
6665            \\  native
6666            \\  efi_application
6667            \\  efi_boot_service_driver
6668            \\  efi_rom
6669            \\  efi_runtime_driver
6670            \\
6671        });
6672}
6673
6674/// Model a header searchlist as a group.
6675/// Silently ignore superfluous search dirs.
6676/// Warn when a dir is added to multiple searchlists.
6677const ClangSearchSanitizer = struct {
6678    map: std.StringHashMapUnmanaged(Membership) = .empty,
6679
6680    fn reset(self: *@This()) void {
6681        self.map.clearRetainingCapacity();
6682    }
6683
6684    fn addIncludePath(
6685        self: *@This(),
6686        ally: Allocator,
6687        argv: *std.ArrayList([]const u8),
6688        group: Group,
6689        arg: []const u8,
6690        dir: []const u8,
6691        joined: bool,
6692    ) !void {
6693        const gopr = try self.map.getOrPut(ally, dir);
6694        const m = gopr.value_ptr;
6695        if (!gopr.found_existing) {
6696            // init empty membership
6697            m.* = .{};
6698        }
6699        const wtxt = "add '{s}' to header searchlist '-{s}' conflicts with '-{s}'";
6700        switch (group) {
6701            .I => {
6702                if (m.I) return;
6703                m.I = true;
6704                if (m.isystem) warn(wtxt, .{ dir, "I", "isystem" });
6705                if (m.idirafter) warn(wtxt, .{ dir, "I", "idirafter" });
6706                if (m.iframework) warn(wtxt, .{ dir, "I", "iframework" });
6707            },
6708            .isystem => {
6709                if (m.isystem) return;
6710                m.isystem = true;
6711                if (m.I) warn(wtxt, .{ dir, "isystem", "I" });
6712                if (m.idirafter) warn(wtxt, .{ dir, "isystem", "idirafter" });
6713                if (m.iframework) warn(wtxt, .{ dir, "isystem", "iframework" });
6714            },
6715            .iwithsysroot => {
6716                if (m.iwithsysroot) return;
6717                m.iwithsysroot = true;
6718                if (m.iframeworkwithsysroot) warn(wtxt, .{ dir, "iwithsysroot", "iframeworkwithsysroot" });
6719            },
6720            .idirafter => {
6721                if (m.idirafter) return;
6722                m.idirafter = true;
6723                if (m.I) warn(wtxt, .{ dir, "idirafter", "I" });
6724                if (m.isystem) warn(wtxt, .{ dir, "idirafter", "isystem" });
6725                if (m.iframework) warn(wtxt, .{ dir, "idirafter", "iframework" });
6726            },
6727            .iframework => {
6728                if (m.iframework) return;
6729                m.iframework = true;
6730                if (m.I) warn(wtxt, .{ dir, "iframework", "I" });
6731                if (m.isystem) warn(wtxt, .{ dir, "iframework", "isystem" });
6732                if (m.idirafter) warn(wtxt, .{ dir, "iframework", "idirafter" });
6733            },
6734            .iframeworkwithsysroot => {
6735                if (m.iframeworkwithsysroot) return;
6736                m.iframeworkwithsysroot = true;
6737                if (m.iwithsysroot) warn(wtxt, .{ dir, "iframeworkwithsysroot", "iwithsysroot" });
6738            },
6739            .embed_dir => {
6740                if (m.embed_dir) return;
6741                m.embed_dir = true;
6742            },
6743        }
6744        try argv.ensureUnusedCapacity(ally, 2);
6745        argv.appendAssumeCapacity(arg);
6746        if (!joined) argv.appendAssumeCapacity(dir);
6747    }
6748
6749    const Group = enum { I, isystem, iwithsysroot, idirafter, iframework, iframeworkwithsysroot, embed_dir };
6750
6751    const Membership = packed struct {
6752        I: bool = false,
6753        isystem: bool = false,
6754        iwithsysroot: bool = false,
6755        idirafter: bool = false,
6756        iframework: bool = false,
6757        iframeworkwithsysroot: bool = false,
6758        embed_dir: bool = false,
6759    };
6760};
6761
6762fn accessFrameworkPath(
6763    test_path: *std.array_list.Managed(u8),
6764    checked_paths: *std.array_list.Managed(u8),
6765    framework_dir_path: []const u8,
6766    framework_name: []const u8,
6767) !bool {
6768    const sep = fs.path.sep_str;
6769
6770    for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
6771        test_path.clearRetainingCapacity();
6772        try test_path.print("{s}" ++ sep ++ "{s}.framework" ++ sep ++ "{s}{s}", .{
6773            framework_dir_path, framework_name, framework_name, ext,
6774        });
6775        try checked_paths.print("\n {s}", .{test_path.items});
6776        fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
6777            error.FileNotFound => continue,
6778            else => |e| fatal("unable to search for {s} framework '{s}': {s}", .{
6779                ext, test_path.items, @errorName(e),
6780            }),
6781        };
6782        return true;
6783    }
6784
6785    return false;
6786}
6787
6788fn parseRcIncludes(arg: []const u8) std.zig.RcIncludes {
6789    return std.meta.stringToEnum(std.zig.RcIncludes, arg) orelse
6790        fatal("unsupported rc includes type: '{s}'", .{arg});
6791}
6792
6793const usage_fetch =
6794    \\Usage: zig fetch [options] <url>
6795    \\Usage: zig fetch [options] <path>
6796    \\
6797    \\    Copy a package into the global cache and print its hash.
6798    \\    <url> must point to one of the following:
6799    \\      - A git+http / git+https server for the package
6800    \\      - A tarball file (with or without compression) containing
6801    \\        package source
6802    \\      - A git bundle file containing package source
6803    \\
6804    \\Examples:
6805    \\
6806    \\  zig fetch --save git+https://example.com/andrewrk/fun-example-tool.git
6807    \\  zig fetch --save https://example.com/andrewrk/fun-example-tool/archive/refs/heads/master.tar.gz
6808    \\
6809    \\Options:
6810    \\  -h, --help                    Print this help and exit
6811    \\  --global-cache-dir [path]     Override path to global Zig cache directory
6812    \\  --debug-hash                  Print verbose hash information to stdout
6813    \\  --save                        Add the fetched package to build.zig.zon
6814    \\  --save=[name]                 Add the fetched package to build.zig.zon as name
6815    \\  --save-exact                  Add the fetched package to build.zig.zon, storing the URL verbatim
6816    \\  --save-exact=[name]           Add the fetched package to build.zig.zon as name, storing the URL verbatim
6817    \\
6818;
6819
6820fn cmdFetch(
6821    gpa: Allocator,
6822    arena: Allocator,
6823    io: Io,
6824    args: []const []const u8,
6825) !void {
6826    dev.check(.fetch_command);
6827
6828    const color: Color = .auto;
6829    const work_around_btrfs_bug = native_os == .linux and
6830        EnvVar.ZIG_BTRFS_WORKAROUND.isSet();
6831    var opt_path_or_url: ?[]const u8 = null;
6832    var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
6833    var debug_hash: bool = false;
6834    var save: union(enum) {
6835        no,
6836        yes: ?[]const u8,
6837        exact: ?[]const u8,
6838    } = .no;
6839
6840    {
6841        var i: usize = 0;
6842        while (i < args.len) : (i += 1) {
6843            const arg = args[i];
6844            if (mem.startsWith(u8, arg, "-")) {
6845                if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
6846                    try fs.File.stdout().writeAll(usage_fetch);
6847                    return cleanExit();
6848                } else if (mem.eql(u8, arg, "--global-cache-dir")) {
6849                    if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
6850                    i += 1;
6851                    override_global_cache_dir = args[i];
6852                } else if (mem.eql(u8, arg, "--debug-hash")) {
6853                    debug_hash = true;
6854                } else if (mem.eql(u8, arg, "--save")) {
6855                    save = .{ .yes = null };
6856                } else if (mem.cutPrefix(u8, arg, "--save=")) |rest| {
6857                    save = .{ .yes = rest };
6858                } else if (mem.eql(u8, arg, "--save-exact")) {
6859                    save = .{ .exact = null };
6860                } else if (mem.cutPrefix(u8, arg, "--save-exact=")) |rest| {
6861                    save = .{ .exact = rest };
6862                } else {
6863                    fatal("unrecognized parameter: '{s}'", .{arg});
6864                }
6865            } else if (opt_path_or_url != null) {
6866                fatal("unexpected extra parameter: '{s}'", .{arg});
6867            } else {
6868                opt_path_or_url = arg;
6869            }
6870        }
6871    }
6872
6873    const path_or_url = opt_path_or_url orelse fatal("missing url or path parameter", .{});
6874
6875    var thread_pool: ThreadPool = undefined;
6876    try thread_pool.init(.{ .allocator = gpa });
6877    defer thread_pool.deinit();
6878
6879    var http_client: std.http.Client = .{ .allocator = gpa, .io = io };
6880    defer http_client.deinit();
6881
6882    try http_client.initDefaultProxies(arena);
6883
6884    var root_prog_node = std.Progress.start(.{
6885        .root_name = "Fetch",
6886    });
6887    defer root_prog_node.end();
6888
6889    var global_cache_directory: Directory = l: {
6890        const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
6891        break :l .{
6892            .handle = try fs.cwd().makeOpenPath(p, .{}),
6893            .path = p,
6894        };
6895    };
6896    defer global_cache_directory.handle.close();
6897
6898    var job_queue: Package.Fetch.JobQueue = .{
6899        .io = io,
6900        .http_client = &http_client,
6901        .global_cache = global_cache_directory,
6902        .recursive = false,
6903        .read_only = false,
6904        .debug_hash = debug_hash,
6905        .work_around_btrfs_bug = work_around_btrfs_bug,
6906        .mode = .all,
6907    };
6908    defer job_queue.deinit();
6909
6910    var fetch: Package.Fetch = .{
6911        .arena = std.heap.ArenaAllocator.init(gpa),
6912        .location = .{ .path_or_url = path_or_url },
6913        .location_tok = 0,
6914        .hash_tok = .none,
6915        .name_tok = 0,
6916        .lazy_status = .eager,
6917        .parent_package_root = undefined,
6918        .parent_manifest_ast = null,
6919        .prog_node = root_prog_node,
6920        .job_queue = &job_queue,
6921        .omit_missing_hash_error = true,
6922        .allow_missing_paths_field = false,
6923        .allow_missing_fingerprint = true,
6924        .allow_name_string = true,
6925        .use_latest_commit = true,
6926
6927        .package_root = undefined,
6928        .error_bundle = undefined,
6929        .manifest = null,
6930        .manifest_ast = undefined,
6931        .computed_hash = undefined,
6932        .has_build_zig = false,
6933        .oom_flag = false,
6934        .latest_commit = null,
6935
6936        .module = null,
6937    };
6938    defer fetch.deinit();
6939
6940    fetch.run() catch |err| switch (err) {
6941        error.OutOfMemory, error.Canceled => |e| return e,
6942        error.FetchFailed => {}, // error bundle checked below
6943    };
6944
6945    if (fetch.error_bundle.root_list.items.len > 0) {
6946        var errors = try fetch.error_bundle.toOwnedBundle("");
6947        errors.renderToStdErr(.{}, color);
6948        process.exit(1);
6949    }
6950
6951    const package_hash = fetch.computedPackageHash();
6952    const package_hash_slice = package_hash.toSlice();
6953
6954    root_prog_node.end();
6955    root_prog_node = .{ .index = .none };
6956
6957    const name = switch (save) {
6958        .no => {
6959            var stdout = fs.File.stdout().writerStreaming(&stdout_buffer);
6960            try stdout.interface.print("{s}\n", .{package_hash_slice});
6961            try stdout.interface.flush();
6962            return cleanExit();
6963        },
6964        .yes, .exact => |name| name: {
6965            if (name) |n| break :name n;
6966            const fetched_manifest = fetch.manifest orelse
6967                fatal("unable to determine name; fetched package has no build.zig.zon file", .{});
6968            break :name fetched_manifest.name;
6969        },
6970    };
6971
6972    const cwd_path = try introspect.getResolvedCwd(arena);
6973
6974    var build_root = try findBuildRoot(arena, .{
6975        .cwd_path = cwd_path,
6976    });
6977    defer build_root.deinit();
6978
6979    // The name to use in case the manifest file needs to be created now.
6980    const init_root_name = fs.path.basename(build_root.directory.path orelse cwd_path);
6981    var manifest, var ast = try loadManifest(gpa, arena, .{
6982        .root_name = try sanitizeExampleName(arena, init_root_name),
6983        .dir = build_root.directory.handle,
6984        .color = color,
6985    });
6986    defer {
6987        manifest.deinit(gpa);
6988        ast.deinit(gpa);
6989    }
6990
6991    var fixups: Ast.Render.Fixups = .{};
6992    defer fixups.deinit(gpa);
6993
6994    var saved_path_or_url = path_or_url;
6995
6996    if (fetch.latest_commit) |latest_commit| resolved: {
6997        const latest_commit_hex = try std.fmt.allocPrint(arena, "{f}", .{latest_commit});
6998
6999        var uri = try std.Uri.parse(path_or_url);
7000
7001        if (uri.fragment) |fragment| {
7002            const target_ref = try fragment.toRawMaybeAlloc(arena);
7003
7004            // the refspec may already be fully resolved
7005            if (std.mem.eql(u8, target_ref, latest_commit_hex)) break :resolved;
7006
7007            std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex });
7008
7009            // include the original refspec in a query parameter, could be used to check for updates
7010            uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f}", .{
7011                std.fmt.alt(fragment, .formatEscaped),
7012            }) };
7013        } else {
7014            std.log.info("resolved to commit {s}", .{latest_commit_hex});
7015        }
7016
7017        // replace the refspec with the resolved commit SHA
7018        uri.fragment = .{ .raw = latest_commit_hex };
7019
7020        switch (save) {
7021            .yes => saved_path_or_url = try std.fmt.allocPrint(arena, "{f}", .{uri}),
7022            .no, .exact => {}, // keep the original URL
7023        }
7024    }
7025
7026    const new_node_init = try std.fmt.allocPrint(arena,
7027        \\.{{
7028        \\            .url = "{f}",
7029        \\            .hash = "{f}",
7030        \\        }}
7031    , .{
7032        std.zig.fmtString(saved_path_or_url),
7033        std.zig.fmtString(package_hash_slice),
7034    });
7035
7036    const new_node_text = try std.fmt.allocPrint(arena, ".{f} = {s},\n", .{
7037        std.zig.fmtIdPU(name), new_node_init,
7038    });
7039
7040    const dependencies_init = try std.fmt.allocPrint(arena, ".{{\n        {s}    }}", .{
7041        new_node_text,
7042    });
7043
7044    const dependencies_text = try std.fmt.allocPrint(arena, ".dependencies = {s},\n", .{
7045        dependencies_init,
7046    });
7047
7048    if (manifest.dependencies.get(name)) |dep| {
7049        if (dep.hash) |h| {
7050            switch (dep.location) {
7051                .url => |u| {
7052                    if (mem.eql(u8, h, package_hash_slice) and mem.eql(u8, u, saved_path_or_url)) {
7053                        std.log.info("existing dependency named '{s}' is up-to-date", .{name});
7054                        process.exit(0);
7055                    }
7056                },
7057                .path => {},
7058            }
7059        }
7060
7061        const location_replace = try std.fmt.allocPrint(
7062            arena,
7063            "\"{f}\"",
7064            .{std.zig.fmtString(saved_path_or_url)},
7065        );
7066        const hash_replace = try std.fmt.allocPrint(
7067            arena,
7068            "\"{f}\"",
7069            .{std.zig.fmtString(package_hash_slice)},
7070        );
7071
7072        warn("overwriting existing dependency named '{s}'", .{name});
7073        try fixups.replace_nodes_with_string.put(gpa, dep.location_node, location_replace);
7074        if (dep.hash_node.unwrap()) |hash_node| {
7075            try fixups.replace_nodes_with_string.put(gpa, hash_node, hash_replace);
7076        } else {
7077            // https://github.com/ziglang/zig/issues/21690
7078        }
7079    } else if (manifest.dependencies.count() > 0) {
7080        // Add fixup for adding another dependency.
7081        const deps = manifest.dependencies.values();
7082        const last_dep_node = deps[deps.len - 1].node;
7083        try fixups.append_string_after_node.put(gpa, last_dep_node, new_node_text);
7084    } else if (manifest.dependencies_node.unwrap()) |dependencies_node| {
7085        // Add fixup for replacing the entire dependencies struct.
7086        try fixups.replace_nodes_with_string.put(gpa, dependencies_node, dependencies_init);
7087    } else {
7088        // Add fixup for adding dependencies struct.
7089        try fixups.append_string_after_node.put(gpa, manifest.version_node, dependencies_text);
7090    }
7091
7092    var aw: Io.Writer.Allocating = .init(gpa);
7093    defer aw.deinit();
7094    try ast.render(gpa, &aw.writer, fixups);
7095    const rendered = aw.written();
7096
7097    build_root.directory.handle.writeFile(.{ .sub_path = Package.Manifest.basename, .data = rendered }) catch |err| {
7098        fatal("unable to write {s} file: {t}", .{ Package.Manifest.basename, err });
7099    };
7100
7101    return cleanExit();
7102}
7103
7104fn createEmptyDependenciesModule(
7105    arena: Allocator,
7106    main_mod: *Package.Module,
7107    dirs: Compilation.Directories,
7108    global_options: Compilation.Config,
7109) !void {
7110    var source = std.array_list.Managed(u8).init(arena);
7111    try Package.Fetch.JobQueue.createEmptyDependenciesSource(&source);
7112    _ = try createDependenciesModule(
7113        arena,
7114        source.items,
7115        main_mod,
7116        dirs,
7117        global_options,
7118    );
7119}
7120
7121/// Creates the dependencies.zig file and corresponding `Package.Module` for the
7122/// build runner to obtain via `@import("@dependencies")`.
7123fn createDependenciesModule(
7124    arena: Allocator,
7125    source: []const u8,
7126    main_mod: *Package.Module,
7127    dirs: Compilation.Directories,
7128    global_options: Compilation.Config,
7129) !*Package.Module {
7130    // Atomically create the file in a directory named after the hash of its contents.
7131    const basename = "dependencies.zig";
7132    const rand_int = std.crypto.random.int(u64);
7133    const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
7134    {
7135        var tmp_dir = try dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{});
7136        defer tmp_dir.close();
7137        try tmp_dir.writeFile(.{ .sub_path = basename, .data = source });
7138    }
7139
7140    var hh: Cache.HashHelper = .{};
7141    hh.addBytes(build_options.version);
7142    hh.addBytes(source);
7143    const hex_digest = hh.final();
7144
7145    const o_dir_sub_path = try arena.dupe(u8, "o" ++ fs.path.sep_str ++ hex_digest);
7146    try Package.Fetch.renameTmpIntoCache(
7147        dirs.local_cache.handle,
7148        tmp_dir_sub_path,
7149        o_dir_sub_path,
7150    );
7151
7152    const deps_mod = try Package.Module.create(arena, .{
7153        .paths = .{
7154            .root = try .fromRoot(arena, dirs, .local_cache, o_dir_sub_path),
7155            .root_src_path = basename,
7156        },
7157        .fully_qualified_name = "root.@dependencies",
7158        .parent = main_mod,
7159        .cc_argv = &.{},
7160        .inherited = .{},
7161        .global = global_options,
7162    });
7163    try main_mod.deps.put(arena, "@dependencies", deps_mod);
7164    return deps_mod;
7165}
7166
7167const BuildRoot = struct {
7168    directory: Cache.Directory,
7169    build_zig_basename: []const u8,
7170    cleanup_build_dir: ?fs.Dir,
7171
7172    fn deinit(br: *BuildRoot) void {
7173        if (br.cleanup_build_dir) |*dir| dir.close();
7174        br.* = undefined;
7175    }
7176};
7177
7178const FindBuildRootOptions = struct {
7179    build_file: ?[]const u8 = null,
7180    cwd_path: ?[]const u8 = null,
7181};
7182
7183fn findBuildRoot(arena: Allocator, options: FindBuildRootOptions) !BuildRoot {
7184    const cwd_path = options.cwd_path orelse try introspect.getResolvedCwd(arena);
7185    const build_zig_basename = if (options.build_file) |bf|
7186        fs.path.basename(bf)
7187    else
7188        Package.build_zig_basename;
7189
7190    if (options.build_file) |bf| {
7191        if (fs.path.dirname(bf)) |dirname| {
7192            const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
7193                fatal("unable to open directory to build file from argument 'build-file', '{s}': {s}", .{ dirname, @errorName(err) });
7194            };
7195            return .{
7196                .build_zig_basename = build_zig_basename,
7197                .directory = .{ .path = dirname, .handle = dir },
7198                .cleanup_build_dir = dir,
7199            };
7200        }
7201
7202        return .{
7203            .build_zig_basename = build_zig_basename,
7204            .directory = .{ .path = null, .handle = fs.cwd() },
7205            .cleanup_build_dir = null,
7206        };
7207    }
7208    // Search up parent directories until we find build.zig.
7209    var dirname: []const u8 = cwd_path;
7210    while (true) {
7211        const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename });
7212        if (fs.cwd().access(joined_path, .{})) |_| {
7213            const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
7214                fatal("unable to open directory while searching for build.zig file, '{s}': {s}", .{ dirname, @errorName(err) });
7215            };
7216            return .{
7217                .build_zig_basename = build_zig_basename,
7218                .directory = .{
7219                    .path = dirname,
7220                    .handle = dir,
7221                },
7222                .cleanup_build_dir = dir,
7223            };
7224        } else |err| switch (err) {
7225            error.FileNotFound => {
7226                dirname = fs.path.dirname(dirname) orelse {
7227                    std.log.info("initialize {s} template file with 'zig init'", .{
7228                        Package.build_zig_basename,
7229                    });
7230                    std.log.info("see 'zig --help' for more options", .{});
7231                    fatal("no build.zig file found, in the current directory or any parent directories", .{});
7232                };
7233                continue;
7234            },
7235            else => |e| return e,
7236        }
7237    }
7238}
7239
7240const LoadManifestOptions = struct {
7241    root_name: []const u8,
7242    dir: fs.Dir,
7243    color: Color,
7244};
7245
7246fn loadManifest(
7247    gpa: Allocator,
7248    arena: Allocator,
7249    options: LoadManifestOptions,
7250) !struct { Package.Manifest, Ast } {
7251    const manifest_bytes = while (true) {
7252        break options.dir.readFileAllocOptions(
7253            Package.Manifest.basename,
7254            arena,
7255            .limited(Package.Manifest.max_bytes),
7256            .@"1",
7257            0,
7258        ) catch |err| switch (err) {
7259            error.FileNotFound => {
7260                writeSimpleTemplateFile(Package.Manifest.basename,
7261                    \\.{{
7262                    \\    .name = .{s},
7263                    \\    .version = "{s}",
7264                    \\    .paths = .{{""}},
7265                    \\    .fingerprint = 0x{x},
7266                    \\}}
7267                    \\
7268                , .{
7269                    options.root_name,
7270                    build_options.version,
7271                    Package.Fingerprint.generate(options.root_name).int(),
7272                }) catch |e| {
7273                    fatal("unable to write {s}: {s}", .{ Package.Manifest.basename, @errorName(e) });
7274                };
7275                continue;
7276            },
7277            else => |e| fatal("unable to load {s}: {s}", .{
7278                Package.Manifest.basename, @errorName(e),
7279            }),
7280        };
7281    };
7282    var ast = try Ast.parse(gpa, manifest_bytes, .zon);
7283    errdefer ast.deinit(gpa);
7284
7285    if (ast.errors.len > 0) {
7286        try std.zig.printAstErrorsToStderr(gpa, ast, Package.Manifest.basename, options.color);
7287        process.exit(2);
7288    }
7289
7290    var manifest = try Package.Manifest.parse(gpa, ast, .{});
7291    errdefer manifest.deinit(gpa);
7292
7293    if (manifest.errors.len > 0) {
7294        var wip_errors: std.zig.ErrorBundle.Wip = undefined;
7295        try wip_errors.init(gpa);
7296        defer wip_errors.deinit();
7297
7298        const src_path = try wip_errors.addString(Package.Manifest.basename);
7299        try manifest.copyErrorsIntoBundle(ast, src_path, &wip_errors);
7300
7301        var error_bundle = try wip_errors.toOwnedBundle("");
7302        defer error_bundle.deinit(gpa);
7303        error_bundle.renderToStdErr(.{}, options.color);
7304
7305        process.exit(2);
7306    }
7307    return .{ manifest, ast };
7308}
7309
7310const Templates = struct {
7311    zig_lib_directory: Cache.Directory,
7312    dir: fs.Dir,
7313    buffer: std.array_list.Managed(u8),
7314
7315    fn deinit(templates: *Templates) void {
7316        templates.zig_lib_directory.handle.close();
7317        templates.dir.close();
7318        templates.buffer.deinit();
7319        templates.* = undefined;
7320    }
7321
7322    fn write(
7323        templates: *Templates,
7324        arena: Allocator,
7325        out_dir: fs.Dir,
7326        root_name: []const u8,
7327        template_path: []const u8,
7328        fingerprint: Package.Fingerprint,
7329    ) !void {
7330        if (fs.path.dirname(template_path)) |dirname| {
7331            out_dir.makePath(dirname) catch |err| {
7332                fatal("unable to make path '{s}': {s}", .{ dirname, @errorName(err) });
7333            };
7334        }
7335
7336        const max_bytes = 10 * 1024 * 1024;
7337        const contents = templates.dir.readFileAlloc(template_path, arena, .limited(max_bytes)) catch |err| {
7338            fatal("unable to read template file '{s}': {s}", .{ template_path, @errorName(err) });
7339        };
7340        templates.buffer.clearRetainingCapacity();
7341        try templates.buffer.ensureUnusedCapacity(contents.len);
7342        var i: usize = 0;
7343        while (i < contents.len) {
7344            if (contents[i] == '_' or contents[i] == '.') {
7345                // Both '_' and '.' are allowed because depending on the context
7346                // one prefix will be valid, while the other might not.
7347                if (std.mem.startsWith(u8, contents[i + 1 ..], "NAME")) {
7348                    try templates.buffer.appendSlice(root_name);
7349                    i += "_NAME".len;
7350                    continue;
7351                } else if (std.mem.startsWith(u8, contents[i + 1 ..], "FINGERPRINT")) {
7352                    try templates.buffer.print("0x{x}", .{fingerprint.int()});
7353                    i += "_FINGERPRINT".len;
7354                    continue;
7355                } else if (std.mem.startsWith(u8, contents[i + 1 ..], "ZIGVER")) {
7356                    try templates.buffer.appendSlice(build_options.version);
7357                    i += "_ZIGVER".len;
7358                    continue;
7359                }
7360            }
7361
7362            try templates.buffer.append(contents[i]);
7363            i += 1;
7364        }
7365
7366        return out_dir.writeFile(.{
7367            .sub_path = template_path,
7368            .data = templates.buffer.items,
7369            .flags = .{ .exclusive = true },
7370        });
7371    }
7372};
7373fn writeSimpleTemplateFile(file_name: []const u8, comptime fmt: []const u8, args: anytype) !void {
7374    const f = try fs.cwd().createFile(file_name, .{ .exclusive = true });
7375    defer f.close();
7376    var buf: [4096]u8 = undefined;
7377    var fw = f.writer(&buf);
7378    try fw.interface.print(fmt, args);
7379    try fw.interface.flush();
7380}
7381
7382fn findTemplates(gpa: Allocator, arena: Allocator) Templates {
7383    const cwd_path = introspect.getResolvedCwd(arena) catch |err| {
7384        fatal("unable to get cwd: {s}", .{@errorName(err)});
7385    };
7386    const self_exe_path = fs.selfExePathAlloc(arena) catch |err| {
7387        fatal("unable to find self exe path: {s}", .{@errorName(err)});
7388    };
7389    var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, cwd_path, self_exe_path) catch |err| {
7390        fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) });
7391    };
7392
7393    const s = fs.path.sep_str;
7394    const template_sub_path = "init";
7395    const template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| {
7396        const path = zig_lib_directory.path orelse ".";
7397        fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{
7398            path, s, template_sub_path, @errorName(err),
7399        });
7400    };
7401
7402    return .{
7403        .zig_lib_directory = zig_lib_directory,
7404        .dir = template_dir,
7405        .buffer = std.array_list.Managed(u8).init(gpa),
7406    };
7407}
7408
7409fn parseOptimizeMode(s: []const u8) std.builtin.OptimizeMode {
7410    return std.meta.stringToEnum(std.builtin.OptimizeMode, s) orelse
7411        fatal("unrecognized optimization mode: '{s}'", .{s});
7412}
7413
7414fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel {
7415    return std.meta.stringToEnum(std.builtin.WasiExecModel, s) orelse
7416        fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s});
7417}
7418
7419fn parseStackSize(s: []const u8) u64 {
7420    return std.fmt.parseUnsigned(u64, s, 0) catch |err|
7421        fatal("unable to parse stack size '{s}': {s}", .{ s, @errorName(err) });
7422}
7423
7424fn parseImageBase(s: []const u8) u64 {
7425    return std.fmt.parseUnsigned(u64, s, 0) catch |err|
7426        fatal("unable to parse image base '{s}': {s}", .{ s, @errorName(err) });
7427}
7428
7429fn handleModArg(
7430    arena: Allocator,
7431    mod_name: []const u8,
7432    opt_root_src_orig: ?[]const u8,
7433    create_module: *CreateModule,
7434    mod_opts: *Package.Module.CreateOptions.Inherited,
7435    cc_argv: *std.ArrayList([]const u8),
7436    target_arch_os_abi: *?[]const u8,
7437    target_mcpu: *?[]const u8,
7438    deps: *std.ArrayList(CliModule.Dep),
7439    c_source_files_owner_index: *usize,
7440    rc_source_files_owner_index: *usize,
7441    cssan: *ClangSearchSanitizer,
7442) !void {
7443    const gop = try create_module.modules.getOrPut(arena, mod_name);
7444
7445    if (gop.found_existing) {
7446        fatal("unable to add module '{s}': already exists as '{s}{c}{s}'", .{
7447            mod_name, gop.value_ptr.root_path, fs.path.sep, gop.value_ptr.root_src_path,
7448        });
7449    }
7450
7451    // See duplicate logic: ModCreationGlobalFlags
7452    if (mod_opts.single_threaded == false)
7453        create_module.opts.any_non_single_threaded = true;
7454    if (mod_opts.sanitize_thread == true)
7455        create_module.opts.any_sanitize_thread = true;
7456    if (mod_opts.sanitize_c) |sc| switch (sc) {
7457        .off => {},
7458        .trap => if (create_module.opts.any_sanitize_c == .off) {
7459            create_module.opts.any_sanitize_c = .trap;
7460        },
7461        .full => create_module.opts.any_sanitize_c = .full,
7462    };
7463    if (mod_opts.fuzz == true)
7464        create_module.opts.any_fuzz = true;
7465    if (mod_opts.unwind_tables) |uwt| switch (uwt) {
7466        .none => {},
7467        .sync, .async => create_module.opts.any_unwind_tables = true,
7468    };
7469    if (mod_opts.strip == false)
7470        create_module.opts.any_non_stripped = true;
7471    if (mod_opts.error_tracing == true)
7472        create_module.opts.any_error_tracing = true;
7473
7474    const root_path: []const u8, const root_src_path: []const u8 = if (opt_root_src_orig) |path| root: {
7475        create_module.opts.have_zcu = true;
7476        break :root .{ fs.path.dirname(path) orelse ".", fs.path.basename(path) };
7477    } else .{ ".", "" };
7478
7479    gop.value_ptr.* = .{
7480        .root_path = root_path,
7481        .root_src_path = root_src_path,
7482        .cc_argv = try cc_argv.toOwnedSlice(arena),
7483        .inherited = mod_opts.*,
7484        .target_arch_os_abi = target_arch_os_abi.*,
7485        .target_mcpu = target_mcpu.*,
7486        .deps = try deps.toOwnedSlice(arena),
7487        .resolved = null,
7488        .c_source_files_start = c_source_files_owner_index.*,
7489        .c_source_files_end = create_module.c_source_files.items.len,
7490        .rc_source_files_start = rc_source_files_owner_index.*,
7491        .rc_source_files_end = create_module.rc_source_files.items.len,
7492    };
7493    cssan.reset();
7494    mod_opts.* = .{};
7495    target_arch_os_abi.* = null;
7496    target_mcpu.* = null;
7497    c_source_files_owner_index.* = create_module.c_source_files.items.len;
7498    rc_source_files_owner_index.* = create_module.rc_source_files.items.len;
7499}
7500
7501fn anyObjectLinkInputs(link_inputs: []const link.UnresolvedInput) bool {
7502    for (link_inputs) |link_input| switch (link_input) {
7503        .path_query => |pq| switch (Compilation.classifyFileExt(pq.path.sub_path)) {
7504            .object, .static_library, .res => return true,
7505            else => continue,
7506        },
7507        else => continue,
7508    };
7509    return false;
7510}
7511
7512fn addLibDirectoryWarn(lib_directories: *std.ArrayList(Directory), path: []const u8) void {
7513    return addLibDirectoryWarn2(lib_directories, path, false);
7514}
7515
7516fn addLibDirectoryWarn2(
7517    lib_directories: *std.ArrayList(Directory),
7518    path: []const u8,
7519    ignore_not_found: bool,
7520) void {
7521    lib_directories.appendAssumeCapacity(.{
7522        .handle = fs.cwd().openDir(path, .{}) catch |err| {
7523            if (err == error.FileNotFound and ignore_not_found) return;
7524            warn("unable to open library directory '{s}': {s}", .{ path, @errorName(err) });
7525            return;
7526        },
7527        .path = path,
7528    });
7529}