zig

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

blob 3f2dbd0c (209570B) - Raw


      1 const std = @import("std");
      2 const builtin = @import("builtin");
      3 const assert = std.debug.assert;
      4 const io = std.io;
      5 const fs = std.fs;
      6 const mem = std.mem;
      7 const process = std.process;
      8 const Allocator = mem.Allocator;
      9 const ArrayList = std.ArrayList;
     10 const Ast = std.zig.Ast;
     11 const warn = std.log.warn;
     12 
     13 const tracy = @import("tracy.zig");
     14 const Compilation = @import("Compilation.zig");
     15 const link = @import("link.zig");
     16 const Package = @import("Package.zig");
     17 const build_options = @import("build_options");
     18 const introspect = @import("introspect.zig");
     19 const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
     20 const wasi_libc = @import("wasi_libc.zig");
     21 const translate_c = @import("translate_c.zig");
     22 const Cache = @import("Cache.zig");
     23 const target_util = @import("target.zig");
     24 const ThreadPool = @import("ThreadPool.zig");
     25 const crash_report = @import("crash_report.zig");
     26 
     27 // Crash report needs to override the panic handler and other root decls
     28 pub usingnamespace crash_report.root_decls;
     29 
     30 pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
     31     std.log.err(format, args);
     32     process.exit(1);
     33 }
     34 
     35 /// There are many assumptions in the entire codebase that Zig source files can
     36 /// be byte-indexed with a u32 integer.
     37 pub const max_src_size = std.math.maxInt(u32);
     38 
     39 pub const debug_extensions_enabled = builtin.mode == .Debug;
     40 
     41 pub const Color = enum {
     42     auto,
     43     off,
     44     on,
     45 };
     46 
     47 const normal_usage =
     48     \\Usage: zig [command] [options]
     49     \\
     50     \\Commands:
     51     \\
     52     \\  build            Build project from build.zig
     53     \\  init-exe         Initialize a `zig build` application in the cwd
     54     \\  init-lib         Initialize a `zig build` library in the cwd
     55     \\
     56     \\  ast-check        Look for simple compile errors in any set of files
     57     \\  build-exe        Create executable from source or object files
     58     \\  build-lib        Create library from source or object files
     59     \\  build-obj        Create object from source or object files
     60     \\  fmt              Reformat Zig source into canonical form
     61     \\  run              Create executable and run immediately
     62     \\  test             Create and run a test build
     63     \\  translate-c      Convert C code to Zig code
     64     \\
     65     \\  ar               Use Zig as a drop-in archiver
     66     \\  cc               Use Zig as a drop-in C compiler
     67     \\  c++              Use Zig as a drop-in C++ compiler
     68     \\  dlltool          Use Zig as a drop-in dlltool.exe
     69     \\  lib              Use Zig as a drop-in lib.exe
     70     \\  ranlib           Use Zig as a drop-in ranlib
     71     \\
     72     \\  env              Print lib path, std path, cache directory, and version
     73     \\  help             Print this help and exit
     74     \\  libc             Display native libc paths file or validate one
     75     \\  targets          List available compilation targets
     76     \\  version          Print version number and exit
     77     \\  zen              Print Zen of Zig and exit
     78     \\
     79     \\General Options:
     80     \\
     81     \\  -h, --help       Print command-specific usage
     82     \\
     83 ;
     84 
     85 const debug_usage = normal_usage ++
     86     \\
     87     \\Debug Commands:
     88     \\
     89     \\  changelist       Compute mappings from old ZIR to new ZIR
     90     \\
     91 ;
     92 
     93 const usage = if (debug_extensions_enabled) debug_usage else normal_usage;
     94 
     95 pub const log_level: std.log.Level = switch (builtin.mode) {
     96     .Debug => .debug,
     97     .ReleaseSafe, .ReleaseFast => .info,
     98     .ReleaseSmall => .err,
     99 };
    100 
    101 var log_scopes: std.ArrayListUnmanaged([]const u8) = .{};
    102 
    103 pub fn log(
    104     comptime level: std.log.Level,
    105     comptime scope: @TypeOf(.EnumLiteral),
    106     comptime format: []const u8,
    107     args: anytype,
    108 ) void {
    109     // Hide debug messages unless:
    110     // * logging enabled with `-Dlog`.
    111     // * the --debug-log arg for the scope has been provided
    112     if (@enumToInt(level) > @enumToInt(std.log.level) or
    113         @enumToInt(level) > @enumToInt(std.log.Level.info))
    114     {
    115         if (!build_options.enable_logging) return;
    116 
    117         const scope_name = @tagName(scope);
    118         for (log_scopes.items) |log_scope| {
    119             if (mem.eql(u8, log_scope, scope_name))
    120                 break;
    121         } else return;
    122     }
    123 
    124     const prefix1 = comptime level.asText();
    125     const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
    126 
    127     // Print the message to stderr, silently ignoring any errors
    128     std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args);
    129 }
    130 
    131 var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{
    132     .stack_trace_frames = build_options.mem_leak_frames,
    133 }){};
    134 
    135 pub fn main() anyerror!void {
    136     crash_report.initialize();
    137 
    138     var gpa_need_deinit = false;
    139     const gpa = gpa: {
    140         if (!builtin.link_libc) {
    141             gpa_need_deinit = true;
    142             break :gpa general_purpose_allocator.allocator();
    143         }
    144         // We would prefer to use raw libc allocator here, but cannot
    145         // use it if it won't support the alignment we need.
    146         if (@alignOf(std.c.max_align_t) < @alignOf(i128)) {
    147             break :gpa std.heap.c_allocator;
    148         }
    149         break :gpa std.heap.raw_c_allocator;
    150     };
    151     defer if (gpa_need_deinit) {
    152         _ = general_purpose_allocator.deinit();
    153     };
    154     var arena_instance = std.heap.ArenaAllocator.init(gpa);
    155     defer arena_instance.deinit();
    156     const arena = arena_instance.allocator();
    157 
    158     const args = try process.argsAlloc(arena);
    159 
    160     if (tracy.enable_allocation) {
    161         var gpa_tracy = tracy.tracyAllocator(gpa);
    162         return mainArgs(gpa_tracy.allocator(), arena, args);
    163     }
    164 
    165     return mainArgs(gpa, arena, args);
    166 }
    167 
    168 pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
    169     if (args.len <= 1) {
    170         std.log.info("{s}", .{usage});
    171         fatal("expected command argument", .{});
    172     }
    173 
    174     if (std.process.can_execv and std.os.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) {
    175         // In this case we have accidentally invoked ourselves as "the system C compiler"
    176         // to figure out where libc is installed. This is essentially infinite recursion
    177         // via child process execution due to the CC environment variable pointing to Zig.
    178         // Here we ignore the CC environment variable and exec `cc` as a child process.
    179         // However it's possible Zig is installed as *that* C compiler as well, which is
    180         // why we have this additional environment variable here to check.
    181         var env_map = try std.process.getEnvMap(arena);
    182 
    183         const inf_loop_env_key = "ZIG_IS_TRYING_TO_NOT_CALL_ITSELF";
    184         if (env_map.get(inf_loop_env_key) != null) {
    185             fatal("The compilation links against libc, but Zig is unable to provide a libc " ++
    186                 "for this operating system, and no --libc " ++
    187                 "parameter was provided, so Zig attempted to invoke the system C compiler " ++
    188                 "in order to determine where libc is installed. However the system C " ++
    189                 "compiler is `zig cc`, so no libc installation was found.", .{});
    190         }
    191         try env_map.put(inf_loop_env_key, "1");
    192 
    193         // Some programs such as CMake will strip the `cc` and subsequent args from the
    194         // CC environment variable. We detect and support this scenario here because of
    195         // the ZIG_IS_DETECTING_LIBC_PATHS environment variable.
    196         if (mem.eql(u8, args[1], "cc")) {
    197             return std.process.execve(arena, args[1..], &env_map);
    198         } else {
    199             const modified_args = try arena.dupe([]const u8, args);
    200             modified_args[0] = "cc";
    201             return std.process.execve(arena, modified_args, &env_map);
    202         }
    203     }
    204 
    205     defer log_scopes.deinit(gpa);
    206 
    207     const cmd = args[1];
    208     const cmd_args = args[2..];
    209     if (mem.eql(u8, cmd, "build-exe")) {
    210         return buildOutputType(gpa, arena, args, .{ .build = .Exe });
    211     } else if (mem.eql(u8, cmd, "build-lib")) {
    212         return buildOutputType(gpa, arena, args, .{ .build = .Lib });
    213     } else if (mem.eql(u8, cmd, "build-obj")) {
    214         return buildOutputType(gpa, arena, args, .{ .build = .Obj });
    215     } else if (mem.eql(u8, cmd, "test")) {
    216         return buildOutputType(gpa, arena, args, .zig_test);
    217     } else if (mem.eql(u8, cmd, "run")) {
    218         return buildOutputType(gpa, arena, args, .run);
    219     } else if (mem.eql(u8, cmd, "dlltool") or
    220         mem.eql(u8, cmd, "ranlib") or
    221         mem.eql(u8, cmd, "lib") or
    222         mem.eql(u8, cmd, "ar"))
    223     {
    224         return punt_to_llvm_ar(arena, args);
    225     } else if (mem.eql(u8, cmd, "cc")) {
    226         return buildOutputType(gpa, arena, args, .cc);
    227     } else if (mem.eql(u8, cmd, "c++")) {
    228         return buildOutputType(gpa, arena, args, .cpp);
    229     } else if (mem.eql(u8, cmd, "translate-c")) {
    230         return buildOutputType(gpa, arena, args, .translate_c);
    231     } else if (mem.eql(u8, cmd, "clang") or
    232         mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as"))
    233     {
    234         return punt_to_clang(arena, args);
    235     } else if (mem.eql(u8, cmd, "ld.lld") or
    236         mem.eql(u8, cmd, "lld-link") or
    237         mem.eql(u8, cmd, "wasm-ld"))
    238     {
    239         return punt_to_lld(arena, args);
    240     } else if (mem.eql(u8, cmd, "build")) {
    241         return cmdBuild(gpa, arena, cmd_args);
    242     } else if (mem.eql(u8, cmd, "fmt")) {
    243         return cmdFmt(gpa, arena, cmd_args);
    244     } else if (mem.eql(u8, cmd, "libc")) {
    245         return cmdLibC(gpa, cmd_args);
    246     } else if (mem.eql(u8, cmd, "init-exe")) {
    247         return cmdInit(gpa, arena, cmd_args, .Exe);
    248     } else if (mem.eql(u8, cmd, "init-lib")) {
    249         return cmdInit(gpa, arena, cmd_args, .Lib);
    250     } else if (mem.eql(u8, cmd, "targets")) {
    251         const info = try detectNativeTargetInfo(arena, .{});
    252         const stdout = io.getStdOut().writer();
    253         return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
    254     } else if (mem.eql(u8, cmd, "version")) {
    255         return std.io.getStdOut().writeAll(build_options.version ++ "\n");
    256     } else if (mem.eql(u8, cmd, "env")) {
    257         return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
    258     } else if (mem.eql(u8, cmd, "zen")) {
    259         return io.getStdOut().writeAll(info_zen);
    260     } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
    261         return io.getStdOut().writeAll(usage);
    262     } else if (mem.eql(u8, cmd, "ast-check")) {
    263         return cmdAstCheck(gpa, arena, cmd_args);
    264     } else if (debug_extensions_enabled and mem.eql(u8, cmd, "changelist")) {
    265         return cmdChangelist(gpa, arena, cmd_args);
    266     } else {
    267         std.log.info("{s}", .{usage});
    268         fatal("unknown command: {s}", .{args[1]});
    269     }
    270 }
    271 
    272 const usage_build_generic =
    273     \\Usage: zig build-exe   [options] [files]
    274     \\       zig build-lib   [options] [files]
    275     \\       zig build-obj   [options] [files]
    276     \\       zig test        [options] [files]
    277     \\       zig run         [options] [files] [-- [args]]
    278     \\       zig translate-c [options] [file]
    279     \\
    280     \\Supported file types:
    281     \\                    .zig    Zig source code
    282     \\                      .o    ELF object file
    283     \\                      .o    Mach-O (macOS) object file
    284     \\                      .o    WebAssembly object file
    285     \\                    .obj    COFF (Windows) object file
    286     \\                    .lib    COFF (Windows) static library
    287     \\                      .a    ELF static library
    288     \\                      .a    Mach-O (macOS) static library
    289     \\                      .a    WebAssembly static library
    290     \\                     .so    ELF shared object (dynamic link)
    291     \\                    .dll    Windows Dynamic Link Library
    292     \\                  .dylib    Mach-O (macOS) dynamic library
    293     \\                    .tbd    (macOS) text-based dylib definition
    294     \\                      .s    Target-specific assembly source code
    295     \\                      .S    Assembly with C preprocessor (requires LLVM extensions)
    296     \\                      .c    C source code (requires LLVM extensions)
    297     \\        .cxx .cc .C .cpp    C++ source code (requires LLVM extensions)
    298     \\                      .m    Objective-C source code (requires LLVM extensions)
    299     \\                     .mm    Objective-C++ source code (requires LLVM extensions)
    300     \\                     .bc    LLVM IR Module (requires LLVM extensions)
    301     \\
    302     \\General Options:
    303     \\  -h, --help                Print this help and exit
    304     \\  --watch                   Enable compiler REPL
    305     \\  --color [auto|off|on]     Enable or disable colored error messages
    306     \\  -femit-bin[=path]         (default) Output machine code
    307     \\  -fno-emit-bin             Do not output machine code
    308     \\  -femit-asm[=path]         Output .s (assembly code)
    309     \\  -fno-emit-asm             (default) Do not output .s (assembly code)
    310     \\  -femit-llvm-ir[=path]     Produce a .ll file with LLVM IR (requires LLVM extensions)
    311     \\  -fno-emit-llvm-ir         (default) Do not produce a .ll file with LLVM IR
    312     \\  -femit-llvm-bc[=path]     Produce a LLVM module as a .bc file (requires LLVM extensions)
    313     \\  -fno-emit-llvm-bc         (default) Do not produce a LLVM module as a .bc file
    314     \\  -femit-h[=path]           Generate a C header file (.h)
    315     \\  -fno-emit-h               (default) Do not generate a C header file (.h)
    316     \\  -femit-docs[=path]        Create a docs/ dir with html documentation
    317     \\  -fno-emit-docs            (default) Do not produce docs/ dir with html documentation
    318     \\  -femit-analysis[=path]    Write analysis JSON file with type information
    319     \\  -fno-emit-analysis        (default) Do not write analysis JSON file with type information
    320     \\  -femit-implib[=path]      (default) Produce an import .lib when building a Windows DLL
    321     \\  -fno-emit-implib          Do not produce an import .lib when building a Windows DLL
    322     \\  --show-builtin            Output the source of @import("builtin") then exit
    323     \\  --cache-dir [path]        Override the local cache directory
    324     \\  --global-cache-dir [path] Override the global cache directory
    325     \\  --zig-lib-dir [path]      Override path to Zig installation lib directory
    326     \\  --enable-cache            Output to cache directory; print path to stdout
    327     \\
    328     \\Compile Options:
    329     \\  -target [name]            <arch><sub>-<os>-<abi> see the targets command
    330     \\  -mcpu [cpu]               Specify target CPU and feature set
    331     \\  -mcmodel=[default|tiny|   Limit range of code and data virtual addresses
    332     \\            small|kernel|
    333     \\            medium|large]
    334     \\  -mred-zone                Force-enable the "red-zone"
    335     \\  -mno-red-zone             Force-disable the "red-zone"
    336     \\  -fomit-frame-pointer      Omit the stack frame pointer
    337     \\  -fno-omit-frame-pointer   Store the stack frame pointer
    338     \\  -mexec-model=[value]      (WASI) Execution model
    339     \\  --name [name]             Override root name (not a file path)
    340     \\  -O [mode]                 Choose what to optimize for
    341     \\    Debug                   (default) Optimizations off, safety on
    342     \\    ReleaseFast             Optimizations on, safety off
    343     \\    ReleaseSafe             Optimizations on, safety on
    344     \\    ReleaseSmall            Optimize for small binary, safety off
    345     \\  --pkg-begin [name] [path] Make pkg available to import and push current pkg
    346     \\  --pkg-end                 Pop current pkg
    347     \\  --main-pkg-path           Set the directory of the root package
    348     \\  -fPIC                     Force-enable Position Independent Code
    349     \\  -fno-PIC                  Force-disable Position Independent Code
    350     \\  -fPIE                     Force-enable Position Independent Executable
    351     \\  -fno-PIE                  Force-disable Position Independent Executable
    352     \\  -flto                     Force-enable Link Time Optimization (requires LLVM extensions)
    353     \\  -fno-lto                  Force-disable Link Time Optimization
    354     \\  -fstack-check             Enable stack probing in unsafe builds
    355     \\  -fno-stack-check          Disable stack probing in safe builds
    356     \\  -fsanitize-c              Enable C undefined behavior detection in unsafe builds
    357     \\  -fno-sanitize-c           Disable C undefined behavior detection in safe builds
    358     \\  -fvalgrind                Include valgrind client requests in release builds
    359     \\  -fno-valgrind             Omit valgrind client requests in debug builds
    360     \\  -fsanitize-thread         Enable Thread Sanitizer
    361     \\  -fno-sanitize-thread      Disable Thread Sanitizer
    362     \\  -fdll-export-fns          Mark exported functions as DLL exports (Windows)
    363     \\  -fno-dll-export-fns       Force-disable marking exported functions as DLL exports
    364     \\  -funwind-tables           Always produce unwind table entries for all functions
    365     \\  -fno-unwind-tables        Never produce unwind table entries
    366     \\  -fLLVM                    Force using LLVM as the codegen backend
    367     \\  -fno-LLVM                 Prevent using LLVM as the codegen backend
    368     \\  -fClang                   Force using Clang as the C/C++ compilation backend
    369     \\  -fno-Clang                Prevent using Clang as the C/C++ compilation backend
    370     \\  -fstage1                  Force using bootstrap compiler as the codegen backend
    371     \\  -fno-stage1               Prevent using bootstrap compiler as the codegen backend
    372     \\  -fsingle-threaded         Code assumes there is only one thread
    373     \\  -fno-single-threaded      Code may not assume there is only one thread
    374     \\  --strip                   Omit debug symbols
    375     \\  -ofmt=[mode]              Override target object format
    376     \\    elf                     Executable and Linking Format
    377     \\    c                       C source code
    378     \\    wasm                    WebAssembly
    379     \\    coff                    Common Object File Format (Windows)
    380     \\    macho                   macOS relocatables
    381     \\    spirv                   Standard, Portable Intermediate Representation V (SPIR-V)
    382     \\    plan9                   Plan 9 from Bell Labs object format
    383     \\    hex  (planned feature)  Intel IHEX
    384     \\    raw  (planned feature)  Dump machine code directly
    385     \\  -dirafter [dir]           Add directory to AFTER include search path
    386     \\  -isystem  [dir]           Add directory to SYSTEM include search path
    387     \\  -I[dir]                   Add directory to include search path
    388     \\  -D[macro]=[value]         Define C [macro] to [value] (1 if [value] omitted)
    389     \\  --libc [file]             Provide a file which specifies libc paths
    390     \\  -cflags [flags] --        Set extra flags for the next positional C source files
    391     \\  -ffunction-sections       Places each function in a separate section
    392     \\
    393     \\Link Options:
    394     \\  -l[lib], --library [lib]       Link against system library (only if actually used)
    395     \\  -needed-l[lib],                Link against system library (even if unused)
    396     \\    --needed-library [lib]
    397     \\  -L[d], --library-directory [d] Add a directory to the library search path
    398     \\  -T[script], --script [script]  Use a custom linker script
    399     \\  --version-script [path]        Provide a version .map file
    400     \\  --dynamic-linker [path]        Set the dynamic interpreter path (usually ld.so)
    401     \\  --sysroot [path]               Set the system root directory (usually /)
    402     \\  --version [ver]                Dynamic library semver
    403     \\  -fsoname[=name]                Override the default SONAME value
    404     \\  -fno-soname                    Disable emitting a SONAME
    405     \\  -fLLD                          Force using LLD as the linker
    406     \\  -fno-LLD                       Prevent using LLD as the linker
    407     \\  -fcompiler-rt                  Always include compiler-rt symbols in output
    408     \\  -fno-compiler-rt               Prevent including compiler-rt symbols in output
    409     \\  -rdynamic                      Add all symbols to the dynamic symbol table
    410     \\  -rpath [path]                  Add directory to the runtime library search path
    411     \\  -feach-lib-rpath               Ensure adding rpath for each used dynamic library
    412     \\  -fno-each-lib-rpath            Prevent adding rpath for each used dynamic library
    413     \\  -fallow-shlib-undefined        Allows undefined symbols in shared libraries
    414     \\  -fno-allow-shlib-undefined     Disallows undefined symbols in shared libraries
    415     \\  --eh-frame-hdr                 Enable C++ exception handling by passing --eh-frame-hdr to linker
    416     \\  --emit-relocs                  Enable output of relocation sections for post build tools
    417     \\  -z [arg]                       Set linker extension flags
    418     \\    nodelete                     Indicate that the object cannot be deleted from a process
    419     \\    notext                       Permit read-only relocations in read-only segments
    420     \\    defs                         Force a fatal error if any undefined symbols remain
    421     \\    origin                       Indicate that the object must have its origin processed
    422     \\    noexecstack                  Indicate that the object requires an executable stack
    423     \\    now                          Force all relocations to be processed on load
    424     \\    relro                        Force all relocations to be resolved and be read-only on load
    425     \\  -dynamic                       Force output to be dynamically linked
    426     \\  -static                        Force output to be statically linked
    427     \\  -Bsymbolic                     Bind global references locally
    428     \\  --subsystem [subsystem]        (Windows) /SUBSYSTEM:<subsystem> to the linker
    429     \\  --stack [size]                 Override default stack size
    430     \\  --image-base [addr]            Set base address for executable image
    431     \\  -framework [name]              (Darwin) link against framework
    432     \\  -F[dir]                        (Darwin) add search path for frameworks
    433     \\  -install_name=[value]          (Darwin) add dylib's install name
    434     \\  --import-memory                (WebAssembly) import memory from the environment
    435     \\  --import-table                 (WebAssembly) import function table from the host environment
    436     \\  --export-table                 (WebAssembly) export function table to the host environment
    437     \\  --initial-memory=[bytes]       (WebAssembly) initial size of the linear memory
    438     \\  --max-memory=[bytes]           (WebAssembly) maximum size of the linear memory
    439     \\  --global-base=[addr]           (WebAssembly) where to start to place global data
    440     \\  --export=[value]               (WebAssembly) Force a symbol to be exported
    441     \\
    442     \\Test Options:
    443     \\  --test-filter [text]           Skip tests that do not match filter
    444     \\  --test-name-prefix [text]      Add prefix to all tests
    445     \\  --test-cmd [arg]               Specify test execution command one arg at a time
    446     \\  --test-cmd-bin                 Appends test binary path to test cmd args
    447     \\  --test-evented-io              Runs the test in evented I/O mode
    448     \\  --test-no-exec                 Compiles test binary without running it
    449     \\
    450     \\Debug Options (Zig Compiler Development):
    451     \\  -ftime-report                Print timing diagnostics
    452     \\  -fstack-report               Print stack size diagnostics
    453     \\  --verbose-link               Display linker invocations
    454     \\  --verbose-cc                 Display C compiler invocations
    455     \\  --verbose-air                Enable compiler debug output for Zig AIR
    456     \\  --verbose-mir                Enable compiler debug output for Zig MIR
    457     \\  --verbose-llvm-ir            Enable compiler debug output for LLVM IR
    458     \\  --verbose-cimport            Enable compiler debug output for C imports
    459     \\  --verbose-llvm-cpu-features  Enable compiler debug output for LLVM CPU features
    460     \\  --debug-log [scope]          Enable printing debug/info log messages for scope
    461     \\  --debug-compile-errors       Crash with helpful diagnostics at the first compile error
    462     \\  --debug-link-snapshot        Enable dumping of the linker's state in JSON format
    463     \\
    464 ;
    465 
    466 const repl_help =
    467     \\Commands:
    468     \\         update  Detect changes to source files and update output files.
    469     \\            run  Execute the output file, if it is an executable or test.
    470     \\ update-and-run  Perform an `update` followed by `run`.
    471     \\           help  Print this text
    472     \\           exit  Quit this repl
    473     \\
    474 ;
    475 
    476 const SOName = union(enum) {
    477     no,
    478     yes_default_value,
    479     yes: []const u8,
    480 };
    481 
    482 const EmitBin = union(enum) {
    483     no,
    484     yes_default_path,
    485     yes: []const u8,
    486     yes_a_out,
    487 };
    488 
    489 const Emit = union(enum) {
    490     no,
    491     yes_default_path,
    492     yes: []const u8,
    493 
    494     const Resolved = struct {
    495         data: ?Compilation.EmitLoc,
    496         dir: ?fs.Dir,
    497 
    498         fn deinit(self: *Resolved) void {
    499             if (self.dir) |*dir| {
    500                 dir.close();
    501             }
    502         }
    503     };
    504 
    505     fn resolve(emit: Emit, default_basename: []const u8) !Resolved {
    506         var resolved: Resolved = .{ .data = null, .dir = null };
    507         errdefer resolved.deinit();
    508 
    509         switch (emit) {
    510             .no => {},
    511             .yes_default_path => {
    512                 resolved.data = Compilation.EmitLoc{
    513                     .directory = .{ .path = null, .handle = fs.cwd() },
    514                     .basename = default_basename,
    515                 };
    516             },
    517             .yes => |full_path| {
    518                 const basename = fs.path.basename(full_path);
    519                 if (fs.path.dirname(full_path)) |dirname| {
    520                     const handle = try fs.cwd().openDir(dirname, .{});
    521                     resolved = .{
    522                         .dir = handle,
    523                         .data = Compilation.EmitLoc{
    524                             .basename = basename,
    525                             .directory = .{
    526                                 .path = dirname,
    527                                 .handle = handle,
    528                             },
    529                         },
    530                     };
    531                 } else {
    532                     resolved.data = Compilation.EmitLoc{
    533                         .basename = basename,
    534                         .directory = .{ .path = null, .handle = fs.cwd() },
    535                     };
    536                 }
    537             },
    538         }
    539         return resolved;
    540     }
    541 };
    542 
    543 fn optionalStringEnvVar(arena: Allocator, name: []const u8) !?[]const u8 {
    544     if (std.process.getEnvVarOwned(arena, name)) |value| {
    545         return value;
    546     } else |err| switch (err) {
    547         error.EnvironmentVariableNotFound => return null,
    548         else => |e| return e,
    549     }
    550 }
    551 
    552 const ArgMode = union(enum) {
    553     build: std.builtin.OutputMode,
    554     cc,
    555     cpp,
    556     translate_c,
    557     zig_test,
    558     run,
    559 };
    560 
    561 fn buildOutputType(
    562     gpa: Allocator,
    563     arena: Allocator,
    564     all_args: []const []const u8,
    565     arg_mode: ArgMode,
    566 ) !void {
    567     var color: Color = .auto;
    568     var optimize_mode: std.builtin.Mode = .Debug;
    569     var provided_name: ?[]const u8 = null;
    570     var link_mode: ?std.builtin.LinkMode = null;
    571     var dll_export_fns: ?bool = null;
    572     var single_threaded: ?bool = null;
    573     var root_src_file: ?[]const u8 = null;
    574     var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
    575     var have_version = false;
    576     var compatibility_version: ?std.builtin.Version = null;
    577     var strip = false;
    578     var function_sections = false;
    579     var watch = false;
    580     var debug_compile_errors = false;
    581     var verbose_link = std.process.hasEnvVarConstant("ZIG_VERBOSE_LINK");
    582     var verbose_cc = std.process.hasEnvVarConstant("ZIG_VERBOSE_CC");
    583     var verbose_air = false;
    584     var verbose_mir = false;
    585     var verbose_llvm_ir = false;
    586     var verbose_cimport = false;
    587     var verbose_llvm_cpu_features = false;
    588     var time_report = false;
    589     var stack_report = false;
    590     var show_builtin = false;
    591     var emit_bin: EmitBin = .yes_default_path;
    592     var emit_asm: Emit = .no;
    593     var emit_llvm_ir: Emit = .no;
    594     var emit_llvm_bc: Emit = .no;
    595     var emit_docs: Emit = .no;
    596     var emit_analysis: Emit = .no;
    597     var emit_implib: Emit = .yes_default_path;
    598     var emit_implib_arg_provided = false;
    599     var target_arch_os_abi: []const u8 = "native";
    600     var target_mcpu: ?[]const u8 = null;
    601     var target_dynamic_linker: ?[]const u8 = null;
    602     var target_ofmt: ?[]const u8 = null;
    603     var output_mode: std.builtin.OutputMode = undefined;
    604     var emit_h: Emit = .no;
    605     var soname: SOName = undefined;
    606     var ensure_libc_on_non_freestanding = false;
    607     var ensure_libcpp_on_non_freestanding = false;
    608     var link_libc = false;
    609     var link_libcpp = false;
    610     var link_libunwind = false;
    611     var want_native_include_dirs = false;
    612     var enable_cache: ?bool = null;
    613     var want_pic: ?bool = null;
    614     var want_pie: ?bool = null;
    615     var want_lto: ?bool = null;
    616     var want_unwind_tables: ?bool = null;
    617     var want_sanitize_c: ?bool = null;
    618     var want_stack_check: ?bool = null;
    619     var want_red_zone: ?bool = null;
    620     var omit_frame_pointer: ?bool = null;
    621     var want_valgrind: ?bool = null;
    622     var want_tsan: ?bool = null;
    623     var want_compiler_rt: ?bool = null;
    624     var rdynamic: bool = false;
    625     var linker_script: ?[]const u8 = null;
    626     var version_script: ?[]const u8 = null;
    627     var disable_c_depfile = false;
    628     var linker_gc_sections: ?bool = null;
    629     var linker_allow_shlib_undefined: ?bool = null;
    630     var linker_bind_global_refs_locally: ?bool = null;
    631     var linker_import_memory: ?bool = null;
    632     var linker_import_table: bool = false;
    633     var linker_export_table: bool = false;
    634     var linker_initial_memory: ?u64 = null;
    635     var linker_max_memory: ?u64 = null;
    636     var linker_global_base: ?u64 = null;
    637     var linker_z_nodelete = false;
    638     var linker_z_notext = false;
    639     var linker_z_defs = false;
    640     var linker_z_origin = false;
    641     var linker_z_noexecstack = false;
    642     var linker_z_now = false;
    643     var linker_z_relro = false;
    644     var linker_tsaware = false;
    645     var linker_nxcompat = false;
    646     var linker_dynamicbase = false;
    647     var linker_optimization: ?u8 = null;
    648     var test_evented_io = false;
    649     var test_no_exec = false;
    650     var stack_size_override: ?u64 = null;
    651     var image_base_override: ?u64 = null;
    652     var use_llvm: ?bool = null;
    653     var use_lld: ?bool = null;
    654     var use_clang: ?bool = null;
    655     var use_stage1: ?bool = null;
    656     var link_eh_frame_hdr = false;
    657     var link_emit_relocs = false;
    658     var each_lib_rpath: ?bool = null;
    659     var sysroot: ?[]const u8 = null;
    660     var libc_paths_file: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIBC");
    661     var machine_code_model: std.builtin.CodeModel = .default;
    662     var runtime_args_start: ?usize = null;
    663     var test_filter: ?[]const u8 = null;
    664     var test_name_prefix: ?[]const u8 = null;
    665     var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR");
    666     var override_global_cache_dir: ?[]const u8 = null;
    667     var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");
    668     var main_pkg_path: ?[]const u8 = null;
    669     var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
    670     var subsystem: ?std.Target.SubSystem = null;
    671     var major_subsystem_version: ?u32 = null;
    672     var minor_subsystem_version: ?u32 = null;
    673     var wasi_exec_model: ?std.builtin.WasiExecModel = null;
    674     var enable_link_snapshots: bool = false;
    675     var native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null;
    676     var install_name: ?[]const u8 = null;
    677 
    678     // e.g. -m3dnow or -mno-outline-atomics. They correspond to std.Target llvm cpu feature names.
    679     // This array is populated by zig cc frontend and then has to be converted to zig-style
    680     // CPU features.
    681     var llvm_m_args = std.ArrayList([]const u8).init(gpa);
    682     defer llvm_m_args.deinit();
    683 
    684     var system_libs = std.StringArrayHashMap(Compilation.SystemLib).init(gpa);
    685     defer system_libs.deinit();
    686 
    687     var static_libs = std.ArrayList([]const u8).init(gpa);
    688     defer static_libs.deinit();
    689 
    690     var wasi_emulated_libs = std.ArrayList(wasi_libc.CRTFile).init(gpa);
    691     defer wasi_emulated_libs.deinit();
    692 
    693     var clang_argv = std.ArrayList([]const u8).init(gpa);
    694     defer clang_argv.deinit();
    695 
    696     var extra_cflags = std.ArrayList([]const u8).init(gpa);
    697     defer extra_cflags.deinit();
    698 
    699     var lib_dirs = std.ArrayList([]const u8).init(gpa);
    700     defer lib_dirs.deinit();
    701 
    702     var rpath_list = std.ArrayList([]const u8).init(gpa);
    703     defer rpath_list.deinit();
    704 
    705     var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa);
    706     defer c_source_files.deinit();
    707 
    708     var link_objects = std.ArrayList([]const u8).init(gpa);
    709     defer link_objects.deinit();
    710 
    711     var framework_dirs = std.ArrayList([]const u8).init(gpa);
    712     defer framework_dirs.deinit();
    713 
    714     var frameworks = std.ArrayList([]const u8).init(gpa);
    715     defer frameworks.deinit();
    716 
    717     // null means replace with the test executable binary
    718     var test_exec_args = std.ArrayList(?[]const u8).init(gpa);
    719     defer test_exec_args.deinit();
    720 
    721     var linker_export_symbol_names = std.ArrayList([]const u8).init(gpa);
    722     defer linker_export_symbol_names.deinit();
    723 
    724     // This package only exists to clean up the code parsing --pkg-begin and
    725     // --pkg-end flags. Use dummy values that are safe for the destroy call.
    726     var pkg_tree_root: Package = .{
    727         .root_src_directory = .{ .path = null, .handle = fs.cwd() },
    728         .root_src_path = &[0]u8{},
    729     };
    730     defer freePkgTree(gpa, &pkg_tree_root, false);
    731     var cur_pkg: *Package = &pkg_tree_root;
    732 
    733     // before arg parsing, check for the NO_COLOR environment variable
    734     // if it exists, default the color setting to .off
    735     // explicit --color arguments will still override this setting.
    736     color = if (std.process.hasEnvVarConstant("NO_COLOR")) .off else .auto;
    737 
    738     switch (arg_mode) {
    739         .build, .translate_c, .zig_test, .run => {
    740             var optimize_mode_string: ?[]const u8 = null;
    741             switch (arg_mode) {
    742                 .build => |m| {
    743                     output_mode = m;
    744                 },
    745                 .translate_c => {
    746                     emit_bin = .no;
    747                     output_mode = .Obj;
    748                 },
    749                 .zig_test, .run => {
    750                     output_mode = .Exe;
    751                 },
    752                 else => unreachable,
    753             }
    754 
    755             soname = .yes_default_value;
    756             const args = all_args[2..];
    757             var i: usize = 0;
    758             args_loop: while (i < args.len) : (i += 1) {
    759                 const arg = args[i];
    760                 if (mem.startsWith(u8, arg, "-")) {
    761                     if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
    762                         try io.getStdOut().writeAll(usage_build_generic);
    763                         return cleanExit();
    764                     } else if (mem.eql(u8, arg, "--")) {
    765                         if (arg_mode == .run) {
    766                             // The index refers to all_args so skip `zig` `run`
    767                             // and `--`
    768                             runtime_args_start = i + 3;
    769                             break :args_loop;
    770                         } else {
    771                             fatal("unexpected end-of-parameter mark: --", .{});
    772                         }
    773                     } else if (mem.eql(u8, arg, "--pkg-begin")) {
    774                         if (i + 2 >= args.len) fatal("Expected 2 arguments after {s}", .{arg});
    775                         i += 1;
    776                         const pkg_name = args[i];
    777                         i += 1;
    778                         const pkg_path = args[i];
    779 
    780                         const new_cur_pkg = Package.create(
    781                             gpa,
    782                             fs.path.dirname(pkg_path),
    783                             fs.path.basename(pkg_path),
    784                         ) catch |err| {
    785                             fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) });
    786                         };
    787                         try cur_pkg.addAndAdopt(gpa, pkg_name, new_cur_pkg);
    788                         cur_pkg = new_cur_pkg;
    789                     } else if (mem.eql(u8, arg, "--pkg-end")) {
    790                         cur_pkg = cur_pkg.parent orelse
    791                             fatal("encountered --pkg-end with no matching --pkg-begin", .{});
    792                     } else if (mem.eql(u8, arg, "--main-pkg-path")) {
    793                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    794                         i += 1;
    795                         main_pkg_path = args[i];
    796                     } else if (mem.eql(u8, arg, "-cflags")) {
    797                         extra_cflags.shrinkRetainingCapacity(0);
    798                         while (true) {
    799                             i += 1;
    800                             if (i >= args.len) fatal("expected -- after -cflags", .{});
    801                             if (mem.eql(u8, args[i], "--")) break;
    802                             try extra_cflags.append(args[i]);
    803                         }
    804                     } else if (mem.eql(u8, arg, "--color")) {
    805                         if (i + 1 >= args.len) {
    806                             fatal("expected [auto|on|off] after --color", .{});
    807                         }
    808                         i += 1;
    809                         const next_arg = args[i];
    810                         color = std.meta.stringToEnum(Color, next_arg) orelse {
    811                             fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
    812                         };
    813                     } else if (mem.eql(u8, arg, "--subsystem")) {
    814                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    815                         i += 1;
    816                         if (mem.eql(u8, args[i], "console")) {
    817                             subsystem = .Console;
    818                         } else if (mem.eql(u8, args[i], "windows")) {
    819                             subsystem = .Windows;
    820                         } else if (mem.eql(u8, args[i], "posix")) {
    821                             subsystem = .Posix;
    822                         } else if (mem.eql(u8, args[i], "native")) {
    823                             subsystem = .Native;
    824                         } else if (mem.eql(u8, args[i], "efi_application")) {
    825                             subsystem = .EfiApplication;
    826                         } else if (mem.eql(u8, args[i], "efi_boot_service_driver")) {
    827                             subsystem = .EfiBootServiceDriver;
    828                         } else if (mem.eql(u8, args[i], "efi_rom")) {
    829                             subsystem = .EfiRom;
    830                         } else if (mem.eql(u8, args[i], "efi_runtime_driver")) {
    831                             subsystem = .EfiRuntimeDriver;
    832                         } else {
    833                             fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{
    834                                 args[i],
    835                                 \\  console
    836                                 \\  windows
    837                                 \\  posix
    838                                 \\  native
    839                                 \\  efi_application
    840                                 \\  efi_boot_service_driver
    841                                 \\  efi_rom
    842                                 \\  efi_runtime_driver
    843                                 \\
    844                             });
    845                         }
    846                     } else if (mem.eql(u8, arg, "-O")) {
    847                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    848                         i += 1;
    849                         optimize_mode_string = args[i];
    850                     } else if (mem.eql(u8, arg, "--stack")) {
    851                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    852                         i += 1;
    853                         stack_size_override = std.fmt.parseUnsigned(u64, args[i], 0) catch |err| {
    854                             fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
    855                         };
    856                     } else if (mem.eql(u8, arg, "--image-base")) {
    857                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    858                         i += 1;
    859                         image_base_override = std.fmt.parseUnsigned(u64, args[i], 0) catch |err| {
    860                             fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
    861                         };
    862                     } else if (mem.eql(u8, arg, "--name")) {
    863                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    864                         i += 1;
    865                         provided_name = args[i];
    866                     } else if (mem.eql(u8, arg, "-rpath")) {
    867                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    868                         i += 1;
    869                         try rpath_list.append(args[i]);
    870                     } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) {
    871                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    872                         i += 1;
    873                         try lib_dirs.append(args[i]);
    874                     } else if (mem.eql(u8, arg, "-F")) {
    875                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    876                         i += 1;
    877                         try framework_dirs.append(args[i]);
    878                     } else if (mem.eql(u8, arg, "-framework")) {
    879                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    880                         i += 1;
    881                         try frameworks.append(args[i]);
    882                     } else if (mem.eql(u8, arg, "-install_name")) {
    883                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    884                         i += 1;
    885                         install_name = args[i];
    886                     } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
    887                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    888                         i += 1;
    889                         linker_script = args[i];
    890                     } else if (mem.eql(u8, arg, "--version-script")) {
    891                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    892                         i += 1;
    893                         version_script = args[i];
    894                     } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) {
    895                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    896                         // We don't know whether this library is part of libc or libc++ until
    897                         // we resolve the target, so we simply append to the list for now.
    898                         i += 1;
    899                         try system_libs.put(args[i], .{ .needed = false });
    900                     } else if (mem.eql(u8, arg, "--needed-library") or mem.eql(u8, arg, "-needed-l")) {
    901                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    902                         i += 1;
    903                         try system_libs.put(args[i], .{ .needed = true });
    904                     } else if (mem.eql(u8, arg, "-D") or
    905                         mem.eql(u8, arg, "-isystem") or
    906                         mem.eql(u8, arg, "-I") or
    907                         mem.eql(u8, arg, "-dirafter") or
    908                         mem.eql(u8, arg, "-iwithsysroot") or
    909                         mem.eql(u8, arg, "-iframework") or
    910                         mem.eql(u8, arg, "-iframeworkwithsysroot"))
    911                     {
    912                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    913                         i += 1;
    914                         try clang_argv.append(arg);
    915                         try clang_argv.append(args[i]);
    916                     } else if (mem.eql(u8, arg, "--version")) {
    917                         if (i + 1 >= args.len) {
    918                             fatal("expected parameter after --version", .{});
    919                         }
    920                         i += 1;
    921                         version = std.builtin.Version.parse(args[i]) catch |err| {
    922                             fatal("unable to parse --version '{s}': {s}", .{ args[i], @errorName(err) });
    923                         };
    924                         have_version = true;
    925                     } else if (mem.eql(u8, arg, "-target")) {
    926                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    927                         i += 1;
    928                         target_arch_os_abi = args[i];
    929                     } else if (mem.eql(u8, arg, "-mcpu")) {
    930                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    931                         i += 1;
    932                         target_mcpu = args[i];
    933                     } else if (mem.eql(u8, arg, "-mcmodel")) {
    934                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    935                         i += 1;
    936                         machine_code_model = parseCodeModel(args[i]);
    937                     } else if (mem.startsWith(u8, arg, "-ofmt=")) {
    938                         target_ofmt = arg["-ofmt=".len..];
    939                     } else if (mem.startsWith(u8, arg, "-mcpu=")) {
    940                         target_mcpu = arg["-mcpu=".len..];
    941                     } else if (mem.startsWith(u8, arg, "-mcmodel=")) {
    942                         machine_code_model = parseCodeModel(arg["-mcmodel=".len..]);
    943                     } else if (mem.startsWith(u8, arg, "-O")) {
    944                         optimize_mode_string = arg["-O".len..];
    945                     } else if (mem.eql(u8, arg, "--dynamic-linker")) {
    946                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    947                         i += 1;
    948                         target_dynamic_linker = args[i];
    949                     } else if (mem.eql(u8, arg, "--sysroot")) {
    950                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    951                         i += 1;
    952                         sysroot = args[i];
    953                         try clang_argv.append("-isysroot");
    954                         try clang_argv.append(args[i]);
    955                     } else if (mem.eql(u8, arg, "--libc")) {
    956                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    957                         i += 1;
    958                         libc_paths_file = args[i];
    959                     } else if (mem.eql(u8, arg, "--test-filter")) {
    960                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    961                         i += 1;
    962                         test_filter = args[i];
    963                     } else if (mem.eql(u8, arg, "--test-name-prefix")) {
    964                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    965                         i += 1;
    966                         test_name_prefix = args[i];
    967                     } else if (mem.eql(u8, arg, "--test-cmd")) {
    968                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    969                         i += 1;
    970                         try test_exec_args.append(args[i]);
    971                     } else if (mem.eql(u8, arg, "--cache-dir")) {
    972                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    973                         i += 1;
    974                         override_local_cache_dir = args[i];
    975                     } else if (mem.eql(u8, arg, "--global-cache-dir")) {
    976                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    977                         i += 1;
    978                         override_global_cache_dir = args[i];
    979                     } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
    980                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    981                         i += 1;
    982                         override_lib_dir = args[i];
    983                     } else if (mem.eql(u8, arg, "--debug-log")) {
    984                         if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
    985                         i += 1;
    986                         if (!build_options.enable_logging) {
    987                             std.log.warn("Zig was compiled without logging enabled (-Dlog). --debug-log has no effect.", .{});
    988                         } else {
    989                             try log_scopes.append(gpa, args[i]);
    990                         }
    991                     } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
    992                         if (!build_options.enable_link_snapshots) {
    993                             std.log.warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});
    994                         } else {
    995                             enable_link_snapshots = true;
    996                         }
    997                     } else if (mem.eql(u8, arg, "-fcompiler-rt")) {
    998                         want_compiler_rt = true;
    999                     } else if (mem.eql(u8, arg, "-fno-compiler-rt")) {
   1000                         want_compiler_rt = false;
   1001                     } else if (mem.eql(u8, arg, "-feach-lib-rpath")) {
   1002                         each_lib_rpath = true;
   1003                     } else if (mem.eql(u8, arg, "-fno-each-lib-rpath")) {
   1004                         each_lib_rpath = false;
   1005                     } else if (mem.eql(u8, arg, "--enable-cache")) {
   1006                         enable_cache = true;
   1007                     } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
   1008                         try test_exec_args.append(null);
   1009                     } else if (mem.eql(u8, arg, "--test-evented-io")) {
   1010                         test_evented_io = true;
   1011                     } else if (mem.eql(u8, arg, "--test-no-exec")) {
   1012                         test_no_exec = true;
   1013                     } else if (mem.eql(u8, arg, "--watch")) {
   1014                         watch = true;
   1015                     } else if (mem.eql(u8, arg, "-ftime-report")) {
   1016                         time_report = true;
   1017                     } else if (mem.eql(u8, arg, "-fstack-report")) {
   1018                         stack_report = true;
   1019                     } else if (mem.eql(u8, arg, "-fPIC")) {
   1020                         want_pic = true;
   1021                     } else if (mem.eql(u8, arg, "-fno-PIC")) {
   1022                         want_pic = false;
   1023                     } else if (mem.eql(u8, arg, "-fPIE")) {
   1024                         want_pie = true;
   1025                     } else if (mem.eql(u8, arg, "-fno-PIE")) {
   1026                         want_pie = false;
   1027                     } else if (mem.eql(u8, arg, "-flto")) {
   1028                         want_lto = true;
   1029                     } else if (mem.eql(u8, arg, "-fno-lto")) {
   1030                         want_lto = false;
   1031                     } else if (mem.eql(u8, arg, "-funwind-tables")) {
   1032                         want_unwind_tables = true;
   1033                     } else if (mem.eql(u8, arg, "-fno-unwind-tables")) {
   1034                         want_unwind_tables = false;
   1035                     } else if (mem.eql(u8, arg, "-fstack-check")) {
   1036                         want_stack_check = true;
   1037                     } else if (mem.eql(u8, arg, "-fno-stack-check")) {
   1038                         want_stack_check = false;
   1039                     } else if (mem.eql(u8, arg, "-mred-zone")) {
   1040                         want_red_zone = true;
   1041                     } else if (mem.eql(u8, arg, "-mno-red-zone")) {
   1042                         want_red_zone = false;
   1043                     } else if (mem.eql(u8, arg, "-fomit-frame-pointer")) {
   1044                         omit_frame_pointer = true;
   1045                     } else if (mem.eql(u8, arg, "-fno-omit-frame-pointer")) {
   1046                         omit_frame_pointer = false;
   1047                     } else if (mem.eql(u8, arg, "-fsanitize-c")) {
   1048                         want_sanitize_c = true;
   1049                     } else if (mem.eql(u8, arg, "-fno-sanitize-c")) {
   1050                         want_sanitize_c = false;
   1051                     } else if (mem.eql(u8, arg, "-fvalgrind")) {
   1052                         want_valgrind = true;
   1053                     } else if (mem.eql(u8, arg, "-fno-valgrind")) {
   1054                         want_valgrind = false;
   1055                     } else if (mem.eql(u8, arg, "-fsanitize-thread")) {
   1056                         want_tsan = true;
   1057                     } else if (mem.eql(u8, arg, "-fno-sanitize-thread")) {
   1058                         want_tsan = false;
   1059                     } else if (mem.eql(u8, arg, "-fLLVM")) {
   1060                         use_llvm = true;
   1061                     } else if (mem.eql(u8, arg, "-fno-LLVM")) {
   1062                         use_llvm = false;
   1063                     } else if (mem.eql(u8, arg, "-fLLD")) {
   1064                         use_lld = true;
   1065                     } else if (mem.eql(u8, arg, "-fno-LLD")) {
   1066                         use_lld = false;
   1067                     } else if (mem.eql(u8, arg, "-fClang")) {
   1068                         use_clang = true;
   1069                     } else if (mem.eql(u8, arg, "-fno-Clang")) {
   1070                         use_clang = false;
   1071                     } else if (mem.eql(u8, arg, "-fstage1")) {
   1072                         use_stage1 = true;
   1073                     } else if (mem.eql(u8, arg, "-fno-stage1")) {
   1074                         use_stage1 = false;
   1075                     } else if (mem.eql(u8, arg, "-rdynamic")) {
   1076                         rdynamic = true;
   1077                     } else if (mem.eql(u8, arg, "-fsoname")) {
   1078                         soname = .yes_default_value;
   1079                     } else if (mem.startsWith(u8, arg, "-fsoname=")) {
   1080                         soname = .{ .yes = arg["-fsoname=".len..] };
   1081                     } else if (mem.eql(u8, arg, "-fno-soname")) {
   1082                         soname = .no;
   1083                     } else if (mem.eql(u8, arg, "-femit-bin")) {
   1084                         emit_bin = .yes_default_path;
   1085                     } else if (mem.startsWith(u8, arg, "-femit-bin=")) {
   1086                         emit_bin = .{ .yes = arg["-femit-bin=".len..] };
   1087                     } else if (mem.eql(u8, arg, "-fno-emit-bin")) {
   1088                         emit_bin = .no;
   1089                     } else if (mem.eql(u8, arg, "-femit-h")) {
   1090                         emit_h = .yes_default_path;
   1091                     } else if (mem.startsWith(u8, arg, "-femit-h=")) {
   1092                         emit_h = .{ .yes = arg["-femit-h=".len..] };
   1093                     } else if (mem.eql(u8, arg, "-fno-emit-h")) {
   1094                         emit_h = .no;
   1095                     } else if (mem.eql(u8, arg, "-femit-asm")) {
   1096                         emit_asm = .yes_default_path;
   1097                     } else if (mem.startsWith(u8, arg, "-femit-asm=")) {
   1098                         emit_asm = .{ .yes = arg["-femit-asm=".len..] };
   1099                     } else if (mem.eql(u8, arg, "-fno-emit-asm")) {
   1100                         emit_asm = .no;
   1101                     } else if (mem.eql(u8, arg, "-femit-llvm-ir")) {
   1102                         emit_llvm_ir = .yes_default_path;
   1103                     } else if (mem.startsWith(u8, arg, "-femit-llvm-ir=")) {
   1104                         emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] };
   1105                     } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) {
   1106                         emit_llvm_ir = .no;
   1107                     } else if (mem.eql(u8, arg, "-femit-llvm-bc")) {
   1108                         emit_llvm_bc = .yes_default_path;
   1109                     } else if (mem.startsWith(u8, arg, "-femit-llvm-bc=")) {
   1110                         emit_llvm_bc = .{ .yes = arg["-femit-llvm-bc=".len..] };
   1111                     } else if (mem.eql(u8, arg, "-fno-emit-llvm-bc")) {
   1112                         emit_llvm_bc = .no;
   1113                     } else if (mem.eql(u8, arg, "-femit-docs")) {
   1114                         emit_docs = .yes_default_path;
   1115                     } else if (mem.startsWith(u8, arg, "-femit-docs=")) {
   1116                         emit_docs = .{ .yes = arg["-femit-docs=".len..] };
   1117                     } else if (mem.eql(u8, arg, "-fno-emit-docs")) {
   1118                         emit_docs = .no;
   1119                     } else if (mem.eql(u8, arg, "-femit-analysis")) {
   1120                         emit_analysis = .yes_default_path;
   1121                     } else if (mem.startsWith(u8, arg, "-femit-analysis=")) {
   1122                         emit_analysis = .{ .yes = arg["-femit-analysis=".len..] };
   1123                     } else if (mem.eql(u8, arg, "-fno-emit-analysis")) {
   1124                         emit_analysis = .no;
   1125                     } else if (mem.eql(u8, arg, "-femit-implib")) {
   1126                         emit_implib = .yes_default_path;
   1127                         emit_implib_arg_provided = true;
   1128                     } else if (mem.startsWith(u8, arg, "-femit-implib=")) {
   1129                         emit_implib = .{ .yes = arg["-femit-implib=".len..] };
   1130                         emit_implib_arg_provided = true;
   1131                     } else if (mem.eql(u8, arg, "-fno-emit-implib")) {
   1132                         emit_implib = .no;
   1133                         emit_implib_arg_provided = true;
   1134                     } else if (mem.eql(u8, arg, "-dynamic")) {
   1135                         link_mode = .Dynamic;
   1136                     } else if (mem.eql(u8, arg, "-static")) {
   1137                         link_mode = .Static;
   1138                     } else if (mem.eql(u8, arg, "-fdll-export-fns")) {
   1139                         dll_export_fns = true;
   1140                     } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) {
   1141                         dll_export_fns = false;
   1142                     } else if (mem.eql(u8, arg, "--show-builtin")) {
   1143                         show_builtin = true;
   1144                         emit_bin = .no;
   1145                     } else if (mem.eql(u8, arg, "--strip")) {
   1146                         strip = true;
   1147                     } else if (mem.eql(u8, arg, "-fsingle-threaded")) {
   1148                         single_threaded = true;
   1149                     } else if (mem.eql(u8, arg, "-fno-single-threaded")) {
   1150                         single_threaded = false;
   1151                     } else if (mem.eql(u8, arg, "-ffunction-sections")) {
   1152                         function_sections = true;
   1153                     } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
   1154                         link_eh_frame_hdr = true;
   1155                     } else if (mem.eql(u8, arg, "--emit-relocs")) {
   1156                         link_emit_relocs = true;
   1157                     } else if (mem.eql(u8, arg, "-fallow-shlib-undefined")) {
   1158                         linker_allow_shlib_undefined = true;
   1159                     } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) {
   1160                         linker_allow_shlib_undefined = false;
   1161                     } else if (mem.eql(u8, arg, "-z")) {
   1162                         i += 1;
   1163                         if (i >= args.len) {
   1164                             fatal("expected linker extension flag after '{s}'", .{arg});
   1165                         }
   1166                         const z_arg = args[i];
   1167                         if (mem.eql(u8, z_arg, "nodelete")) {
   1168                             linker_z_nodelete = true;
   1169                         } else if (mem.eql(u8, z_arg, "notext")) {
   1170                             linker_z_notext = true;
   1171                         } else if (mem.eql(u8, z_arg, "defs")) {
   1172                             linker_z_defs = true;
   1173                         } else if (mem.eql(u8, z_arg, "origin")) {
   1174                             linker_z_origin = true;
   1175                         } else if (mem.eql(u8, z_arg, "noexecstack")) {
   1176                             linker_z_noexecstack = true;
   1177                         } else if (mem.eql(u8, z_arg, "now")) {
   1178                             linker_z_now = true;
   1179                         } else if (mem.eql(u8, z_arg, "relro")) {
   1180                             linker_z_relro = true;
   1181                         } else {
   1182                             warn("unsupported linker extension flag: -z {s}", .{z_arg});
   1183                         }
   1184                     } else if (mem.eql(u8, arg, "--import-memory")) {
   1185                         linker_import_memory = true;
   1186                     } else if (mem.eql(u8, arg, "--import-table")) {
   1187                         linker_import_table = true;
   1188                     } else if (mem.eql(u8, arg, "--export-table")) {
   1189                         linker_export_table = true;
   1190                     } else if (mem.startsWith(u8, arg, "--initial-memory=")) {
   1191                         linker_initial_memory = parseIntSuffix(arg, "--initial-memory=".len);
   1192                     } else if (mem.startsWith(u8, arg, "--max-memory=")) {
   1193                         linker_max_memory = parseIntSuffix(arg, "--max-memory=".len);
   1194                     } else if (mem.startsWith(u8, arg, "--global-base=")) {
   1195                         linker_global_base = parseIntSuffix(arg, "--global-base=".len);
   1196                     } else if (mem.startsWith(u8, arg, "--export=")) {
   1197                         try linker_export_symbol_names.append(arg["--export=".len..]);
   1198                     } else if (mem.eql(u8, arg, "-Bsymbolic")) {
   1199                         linker_bind_global_refs_locally = true;
   1200                     } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
   1201                         debug_compile_errors = true;
   1202                     } else if (mem.eql(u8, arg, "--verbose-link")) {
   1203                         verbose_link = true;
   1204                     } else if (mem.eql(u8, arg, "--verbose-cc")) {
   1205                         verbose_cc = true;
   1206                     } else if (mem.eql(u8, arg, "--verbose-air")) {
   1207                         verbose_air = true;
   1208                     } else if (mem.eql(u8, arg, "--verbose-mir")) {
   1209                         verbose_mir = true;
   1210                     } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
   1211                         verbose_llvm_ir = true;
   1212                     } else if (mem.eql(u8, arg, "--verbose-cimport")) {
   1213                         verbose_cimport = true;
   1214                     } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
   1215                         verbose_llvm_cpu_features = true;
   1216                     } else if (mem.startsWith(u8, arg, "-T")) {
   1217                         linker_script = arg[2..];
   1218                     } else if (mem.startsWith(u8, arg, "-L")) {
   1219                         try lib_dirs.append(arg[2..]);
   1220                     } else if (mem.startsWith(u8, arg, "-F")) {
   1221                         try framework_dirs.append(arg[2..]);
   1222                     } else if (mem.startsWith(u8, arg, "-l")) {
   1223                         // We don't know whether this library is part of libc or libc++ until
   1224                         // we resolve the target, so we simply append to the list for now.
   1225                         try system_libs.put(arg["-l".len..], .{ .needed = false });
   1226                     } else if (mem.startsWith(u8, arg, "-needed-l")) {
   1227                         try system_libs.put(arg["-needed-l".len..], .{ .needed = true });
   1228                     } else if (mem.startsWith(u8, arg, "-D") or
   1229                         mem.startsWith(u8, arg, "-I"))
   1230                     {
   1231                         try clang_argv.append(arg);
   1232                     } else if (mem.startsWith(u8, arg, "-mexec-model=")) {
   1233                         wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, arg["-mexec-model=".len..]) orelse {
   1234                             fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{arg["-mexec-model=".len..]});
   1235                         };
   1236                     } else {
   1237                         fatal("unrecognized parameter: '{s}'", .{arg});
   1238                     }
   1239                 } else switch (Compilation.classifyFileExt(arg)) {
   1240                     .object, .static_library, .shared_library => {
   1241                         try link_objects.append(arg);
   1242                     },
   1243                     .assembly, .c, .cpp, .h, .ll, .bc, .m, .mm => {
   1244                         try c_source_files.append(.{
   1245                             .src_path = arg,
   1246                             .extra_flags = try arena.dupe([]const u8, extra_cflags.items),
   1247                         });
   1248                     },
   1249                     .zig => {
   1250                         if (root_src_file) |other| {
   1251                             fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other });
   1252                         } else {
   1253                             root_src_file = arg;
   1254                         }
   1255                     },
   1256                     .unknown => {
   1257                         fatal("unrecognized file extension of parameter '{s}'", .{arg});
   1258                     },
   1259                 }
   1260             }
   1261             if (optimize_mode_string) |s| {
   1262                 optimize_mode = std.meta.stringToEnum(std.builtin.Mode, s) orelse
   1263                     fatal("unrecognized optimization mode: '{s}'", .{s});
   1264             }
   1265         },
   1266         .cc, .cpp => {
   1267             emit_h = .no;
   1268             soname = .no;
   1269             strip = true;
   1270             ensure_libc_on_non_freestanding = true;
   1271             ensure_libcpp_on_non_freestanding = arg_mode == .cpp;
   1272             want_native_include_dirs = true;
   1273             // Clang's driver enables this switch unconditionally.
   1274             // Disabling the emission of .eh_frame_hdr can unexpectedly break
   1275             // some functionality that depend on it, such as C++ exceptions and
   1276             // DWARF-based stack traces.
   1277             link_eh_frame_hdr = true;
   1278 
   1279             const COutMode = enum {
   1280                 link,
   1281                 object,
   1282                 assembly,
   1283                 preprocessor,
   1284             };
   1285             var c_out_mode: COutMode = .link;
   1286             var out_path: ?[]const u8 = null;
   1287             var is_shared_lib = false;
   1288             var linker_args = std.ArrayList([]const u8).init(arena);
   1289             var it = ClangArgIterator.init(arena, all_args);
   1290             var emit_llvm = false;
   1291             var needed = false;
   1292             var force_static_libs = false;
   1293             while (it.has_next) {
   1294                 it.next() catch |err| {
   1295                     fatal("unable to parse command line parameters: {s}", .{@errorName(err)});
   1296                 };
   1297                 switch (it.zig_equivalent) {
   1298                     .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown
   1299                     .o => out_path = it.only_arg, // -o
   1300                     .c => c_out_mode = .object, // -c
   1301                     .asm_only => c_out_mode = .assembly, // -S
   1302                     .preprocess_only => c_out_mode = .preprocessor, // -E
   1303                     .emit_llvm => emit_llvm = true,
   1304                     .other => {
   1305                         try clang_argv.appendSlice(it.other_args);
   1306                     },
   1307                     .positional => {
   1308                         const file_ext = Compilation.classifyFileExt(mem.sliceTo(it.only_arg, 0));
   1309                         switch (file_ext) {
   1310                             .assembly, .c, .cpp, .ll, .bc, .h, .m, .mm => try c_source_files.append(.{ .src_path = it.only_arg }),
   1311                             .unknown, .shared_library, .object, .static_library => {
   1312                                 try link_objects.append(it.only_arg);
   1313                             },
   1314                             .zig => {
   1315                                 if (root_src_file) |other| {
   1316                                     fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other });
   1317                                 } else {
   1318                                     root_src_file = it.only_arg;
   1319                                 }
   1320                             },
   1321                         }
   1322                     },
   1323                     .l => {
   1324                         // -l
   1325                         // We don't know whether this library is part of libc or libc++ until
   1326                         // we resolve the target, so we simply append to the list for now.
   1327                         if (force_static_libs) {
   1328                             try static_libs.append(it.only_arg);
   1329                         } else {
   1330                             try system_libs.put(it.only_arg, .{ .needed = needed });
   1331                         }
   1332                     },
   1333                     .ignore => {},
   1334                     .driver_punt => {
   1335                         // Never mind what we're doing, just pass the args directly. For example --help.
   1336                         return punt_to_clang(arena, all_args);
   1337                     },
   1338                     .pic => want_pic = true,
   1339                     .no_pic => want_pic = false,
   1340                     .pie => want_pie = true,
   1341                     .no_pie => want_pie = false,
   1342                     .lto => want_lto = true,
   1343                     .no_lto => want_lto = false,
   1344                     .red_zone => want_red_zone = true,
   1345                     .no_red_zone => want_red_zone = false,
   1346                     .omit_frame_pointer => omit_frame_pointer = true,
   1347                     .no_omit_frame_pointer => omit_frame_pointer = false,
   1348                     .function_sections => function_sections = true,
   1349                     .no_function_sections => function_sections = false,
   1350                     .color_diagnostics => color = .on,
   1351                     .no_color_diagnostics => color = .off,
   1352                     .unwind_tables => want_unwind_tables = true,
   1353                     .no_unwind_tables => want_unwind_tables = false,
   1354                     .nostdlib => ensure_libc_on_non_freestanding = false,
   1355                     .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false,
   1356                     .shared => {
   1357                         link_mode = .Dynamic;
   1358                         is_shared_lib = true;
   1359                     },
   1360                     .rdynamic => rdynamic = true,
   1361                     .wl => {
   1362                         var split_it = mem.split(u8, it.only_arg, ",");
   1363                         while (split_it.next()) |linker_arg| {
   1364                             // Handle nested-joined args like `-Wl,-rpath=foo`.
   1365                             // Must be prefixed with 1 or 2 dashes.
   1366                             if (linker_arg.len >= 3 and linker_arg[0] == '-' and linker_arg[2] != '-') {
   1367                                 if (mem.indexOfScalar(u8, linker_arg, '=')) |equals_pos| {
   1368                                     try linker_args.append(linker_arg[0..equals_pos]);
   1369                                     try linker_args.append(linker_arg[equals_pos + 1 ..]);
   1370                                     continue;
   1371                                 }
   1372                             }
   1373                             if (mem.eql(u8, linker_arg, "--as-needed")) {
   1374                                 needed = false;
   1375                             } else if (mem.eql(u8, linker_arg, "--no-as-needed")) {
   1376                                 needed = true;
   1377                             } else if (mem.eql(u8, linker_arg, "-Bdynamic") or
   1378                                 mem.eql(u8, linker_arg, "-dy") or
   1379                                 mem.eql(u8, linker_arg, "-call_shared"))
   1380                             {
   1381                                 force_static_libs = false;
   1382                             } else if (mem.eql(u8, linker_arg, "-Bstatic") or
   1383                                 mem.eql(u8, linker_arg, "-dn") or
   1384                                 mem.eql(u8, linker_arg, "-non_shared") or
   1385                                 mem.eql(u8, linker_arg, "-static"))
   1386                             {
   1387                                 force_static_libs = true;
   1388                             } else {
   1389                                 try linker_args.append(linker_arg);
   1390                             }
   1391                         }
   1392                     },
   1393                     .optimize => {
   1394                         // Alright, what release mode do they want?
   1395                         const level = if (it.only_arg.len >= 1 and it.only_arg[0] == 'O') it.only_arg[1..] else it.only_arg;
   1396                         if (mem.eql(u8, level, "s") or
   1397                             mem.eql(u8, level, "z"))
   1398                         {
   1399                             optimize_mode = .ReleaseSmall;
   1400                         } else if (mem.eql(u8, level, "1") or
   1401                             mem.eql(u8, level, "2") or
   1402                             mem.eql(u8, level, "3") or
   1403                             mem.eql(u8, level, "4") or
   1404                             mem.eql(u8, level, "fast"))
   1405                         {
   1406                             optimize_mode = .ReleaseFast;
   1407                         } else if (mem.eql(u8, level, "g") or
   1408                             mem.eql(u8, level, "0"))
   1409                         {
   1410                             optimize_mode = .Debug;
   1411                         } else {
   1412                             try clang_argv.appendSlice(it.other_args);
   1413                         }
   1414                     },
   1415                     .debug => {
   1416                         strip = false;
   1417                         if (mem.eql(u8, it.only_arg, "g")) {
   1418                             // We handled with strip = false above.
   1419                         } else if (mem.eql(u8, it.only_arg, "g1") or
   1420                             mem.eql(u8, it.only_arg, "gline-tables-only"))
   1421                         {
   1422                             // We handled with strip = false above. but we also want reduced debug info.
   1423                             try clang_argv.append("-gline-tables-only");
   1424                         } else {
   1425                             try clang_argv.appendSlice(it.other_args);
   1426                         }
   1427                     },
   1428                     .sanitize => {
   1429                         if (mem.eql(u8, it.only_arg, "undefined")) {
   1430                             want_sanitize_c = true;
   1431                         } else if (mem.eql(u8, it.only_arg, "thread")) {
   1432                             want_tsan = true;
   1433                         } else {
   1434                             try clang_argv.appendSlice(it.other_args);
   1435                         }
   1436                     },
   1437                     .linker_script => linker_script = it.only_arg,
   1438                     .verbose => {
   1439                         verbose_link = true;
   1440                         // Have Clang print more infos, some tools such as CMake
   1441                         // parse this to discover any implicit include and
   1442                         // library dir to look-up into.
   1443                         try clang_argv.append("-v");
   1444                     },
   1445                     .dry_run => {
   1446                         verbose_link = true;
   1447                         try clang_argv.append("-###");
   1448                         // This flag is supposed to mean "dry run" but currently this
   1449                         // will actually still execute. The tracking issue for this is
   1450                         // https://github.com/ziglang/zig/issues/7170
   1451                     },
   1452                     .for_linker => try linker_args.append(it.only_arg),
   1453                     .linker_input_z => {
   1454                         try linker_args.append("-z");
   1455                         try linker_args.append(it.only_arg);
   1456                     },
   1457                     .lib_dir => try lib_dirs.append(it.only_arg),
   1458                     .mcpu => target_mcpu = it.only_arg,
   1459                     .m => try llvm_m_args.append(it.only_arg),
   1460                     .dep_file => {
   1461                         disable_c_depfile = true;
   1462                         try clang_argv.appendSlice(it.other_args);
   1463                     },
   1464                     .dep_file_mm => { // -MM
   1465                         // "Like -MMD, but also implies -E and writes to stdout by default"
   1466                         c_out_mode = .preprocessor;
   1467                         disable_c_depfile = true;
   1468                         try clang_argv.appendSlice(it.other_args);
   1469                     },
   1470                     .framework_dir => try framework_dirs.append(it.only_arg),
   1471                     .framework => try frameworks.append(it.only_arg),
   1472                     .nostdlibinc => want_native_include_dirs = false,
   1473                     .strip => strip = true,
   1474                     .exec_model => {
   1475                         wasi_exec_model = std.meta.stringToEnum(std.builtin.WasiExecModel, it.only_arg) orelse {
   1476                             fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{it.only_arg});
   1477                         };
   1478                     },
   1479                 }
   1480             }
   1481             // Parse linker args.
   1482             var i: usize = 0;
   1483             while (i < linker_args.items.len) : (i += 1) {
   1484                 const arg = linker_args.items[i];
   1485                 if (mem.eql(u8, arg, "-soname")) {
   1486                     i += 1;
   1487                     if (i >= linker_args.items.len) {
   1488                         fatal("expected linker arg after '{s}'", .{arg});
   1489                     }
   1490                     const name = linker_args.items[i];
   1491                     soname = .{ .yes = name };
   1492                     // Use it as --name.
   1493                     // Example: libsoundio.so.2
   1494                     var prefix: usize = 0;
   1495                     if (mem.startsWith(u8, name, "lib")) {
   1496                         prefix = 3;
   1497                     }
   1498                     var end: usize = name.len;
   1499                     if (mem.endsWith(u8, name, ".so")) {
   1500                         end -= 3;
   1501                     } else {
   1502                         var found_digit = false;
   1503                         while (end > 0 and std.ascii.isDigit(name[end - 1])) {
   1504                             found_digit = true;
   1505                             end -= 1;
   1506                         }
   1507                         if (found_digit and end > 0 and name[end - 1] == '.') {
   1508                             end -= 1;
   1509                         } else {
   1510                             end = name.len;
   1511                         }
   1512                         if (mem.endsWith(u8, name[prefix..end], ".so")) {
   1513                             end -= 3;
   1514                         }
   1515                     }
   1516                     provided_name = name[prefix..end];
   1517                 } else if (mem.eql(u8, arg, "-rpath")) {
   1518                     i += 1;
   1519                     if (i >= linker_args.items.len) {
   1520                         fatal("expected linker arg after '{s}'", .{arg});
   1521                     }
   1522                     try rpath_list.append(linker_args.items[i]);
   1523                 } else if (mem.eql(u8, arg, "-I") or
   1524                     mem.eql(u8, arg, "--dynamic-linker") or
   1525                     mem.eql(u8, arg, "-dynamic-linker"))
   1526                 {
   1527                     i += 1;
   1528                     if (i >= linker_args.items.len) {
   1529                         fatal("expected linker arg after '{s}'", .{arg});
   1530                     }
   1531                     target_dynamic_linker = linker_args.items[i];
   1532                 } else if (mem.eql(u8, arg, "-E") or
   1533                     mem.eql(u8, arg, "--export-dynamic") or
   1534                     mem.eql(u8, arg, "-export-dynamic"))
   1535                 {
   1536                     rdynamic = true;
   1537                 } else if (mem.eql(u8, arg, "--version-script")) {
   1538                     i += 1;
   1539                     if (i >= linker_args.items.len) {
   1540                         fatal("expected linker arg after '{s}'", .{arg});
   1541                     }
   1542                     version_script = linker_args.items[i];
   1543                 } else if (mem.eql(u8, arg, "-O")) {
   1544                     i += 1;
   1545                     if (i >= linker_args.items.len) {
   1546                         fatal("expected linker arg after '{s}'", .{arg});
   1547                     }
   1548                     linker_optimization = std.fmt.parseUnsigned(u8, linker_args.items[i], 10) catch |err| {
   1549                         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   1550                     };
   1551                 } else if (mem.startsWith(u8, arg, "-O")) {
   1552                     linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| {
   1553                         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   1554                     };
   1555                 } else if (mem.eql(u8, arg, "--gc-sections")) {
   1556                     linker_gc_sections = true;
   1557                 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
   1558                     linker_gc_sections = false;
   1559                 } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or
   1560                     mem.eql(u8, arg, "-allow-shlib-undefined"))
   1561                 {
   1562                     linker_allow_shlib_undefined = true;
   1563                 } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or
   1564                     mem.eql(u8, arg, "-no-allow-shlib-undefined"))
   1565                 {
   1566                     linker_allow_shlib_undefined = false;
   1567                 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
   1568                     linker_bind_global_refs_locally = true;
   1569                 } else if (mem.eql(u8, arg, "--import-memory")) {
   1570                     linker_import_memory = true;
   1571                 } else if (mem.eql(u8, arg, "--import-table")) {
   1572                     linker_import_table = true;
   1573                 } else if (mem.eql(u8, arg, "--export-table")) {
   1574                     linker_export_table = true;
   1575                 } else if (mem.startsWith(u8, arg, "--initial-memory=")) {
   1576                     linker_initial_memory = parseIntSuffix(arg, "--initial-memory=".len);
   1577                 } else if (mem.startsWith(u8, arg, "--max-memory=")) {
   1578                     linker_max_memory = parseIntSuffix(arg, "--max-memory=".len);
   1579                 } else if (mem.startsWith(u8, arg, "--global-base=")) {
   1580                     linker_global_base = parseIntSuffix(arg, "--global-base=".len);
   1581                 } else if (mem.startsWith(u8, arg, "--export=")) {
   1582                     try linker_export_symbol_names.append(arg["--export=".len..]);
   1583                 } else if (mem.eql(u8, arg, "-z")) {
   1584                     i += 1;
   1585                     if (i >= linker_args.items.len) {
   1586                         fatal("expected linker extension flag after '{s}'", .{arg});
   1587                     }
   1588                     const z_arg = linker_args.items[i];
   1589                     if (mem.eql(u8, z_arg, "nodelete")) {
   1590                         linker_z_nodelete = true;
   1591                     } else if (mem.eql(u8, z_arg, "notext")) {
   1592                         linker_z_notext = true;
   1593                     } else if (mem.eql(u8, z_arg, "defs")) {
   1594                         linker_z_defs = true;
   1595                     } else if (mem.eql(u8, z_arg, "origin")) {
   1596                         linker_z_origin = true;
   1597                     } else if (mem.eql(u8, z_arg, "noexecstack")) {
   1598                         linker_z_noexecstack = true;
   1599                     } else if (mem.eql(u8, z_arg, "now")) {
   1600                         linker_z_now = true;
   1601                     } else if (mem.eql(u8, z_arg, "relro")) {
   1602                         linker_z_relro = true;
   1603                     } else {
   1604                         warn("unsupported linker extension flag: -z {s}", .{z_arg});
   1605                     }
   1606                 } else if (mem.eql(u8, arg, "--major-image-version")) {
   1607                     i += 1;
   1608                     if (i >= linker_args.items.len) {
   1609                         fatal("expected linker arg after '{s}'", .{arg});
   1610                     }
   1611                     version.major = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| {
   1612                         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   1613                     };
   1614                     have_version = true;
   1615                 } else if (mem.eql(u8, arg, "--minor-image-version")) {
   1616                     i += 1;
   1617                     if (i >= linker_args.items.len) {
   1618                         fatal("expected linker arg after '{s}'", .{arg});
   1619                     }
   1620                     version.minor = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| {
   1621                         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   1622                     };
   1623                     have_version = true;
   1624                 } else if (mem.eql(u8, arg, "--stack")) {
   1625                     i += 1;
   1626                     if (i >= linker_args.items.len) {
   1627                         fatal("expected linker arg after '{s}'", .{arg});
   1628                     }
   1629                     stack_size_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| {
   1630                         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   1631                     };
   1632                 } else if (mem.eql(u8, arg, "--image-base")) {
   1633                     i += 1;
   1634                     if (i >= linker_args.items.len) {
   1635                         fatal("expected linker arg after '{s}'", .{arg});
   1636                     }
   1637                     image_base_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| {
   1638                         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   1639                     };
   1640                 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
   1641                     i += 1;
   1642                     if (i >= linker_args.items.len) {
   1643                         fatal("expected linker arg after '{s}'", .{arg});
   1644                     }
   1645                     linker_script = linker_args.items[i];
   1646                 } else if (mem.eql(u8, arg, "--eh-frame-hdr")) {
   1647                     link_eh_frame_hdr = true;
   1648                 } else if (mem.eql(u8, arg, "--no-eh-frame-hdr")) {
   1649                     link_eh_frame_hdr = false;
   1650                 } else if (mem.eql(u8, arg, "--tsaware")) {
   1651                     linker_tsaware = true;
   1652                 } else if (mem.eql(u8, arg, "--nxcompat")) {
   1653                     linker_nxcompat = true;
   1654                 } else if (mem.eql(u8, arg, "--dynamicbase")) {
   1655                     linker_dynamicbase = true;
   1656                 } else if (mem.eql(u8, arg, "--high-entropy-va")) {
   1657                     // This option does not do anything.
   1658                 } else if (mem.eql(u8, arg, "--export-all-symbols")) {
   1659                     rdynamic = true;
   1660                 } else if (mem.eql(u8, arg, "--start-group") or
   1661                     mem.eql(u8, arg, "--end-group"))
   1662                 {
   1663                     // We don't need to care about these because these args are
   1664                     // for resolving circular dependencies but our linker takes
   1665                     // care of this without explicit args.
   1666                 } else if (mem.eql(u8, arg, "--major-os-version") or
   1667                     mem.eql(u8, arg, "--minor-os-version"))
   1668                 {
   1669                     i += 1;
   1670                     if (i >= linker_args.items.len) {
   1671                         fatal("expected linker arg after '{s}'", .{arg});
   1672                     }
   1673                     // This option does not do anything.
   1674                 } else if (mem.eql(u8, arg, "--major-subsystem-version")) {
   1675                     i += 1;
   1676                     if (i >= linker_args.items.len) {
   1677                         fatal("expected linker arg after '{s}'", .{arg});
   1678                     }
   1679 
   1680                     major_subsystem_version = std.fmt.parseUnsigned(
   1681                         u32,
   1682                         linker_args.items[i],
   1683                         10,
   1684                     ) catch |err| {
   1685                         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   1686                     };
   1687                 } else if (mem.eql(u8, arg, "--minor-subsystem-version")) {
   1688                     i += 1;
   1689                     if (i >= linker_args.items.len) {
   1690                         fatal("expected linker arg after '{s}'", .{arg});
   1691                     }
   1692 
   1693                     minor_subsystem_version = std.fmt.parseUnsigned(
   1694                         u32,
   1695                         linker_args.items[i],
   1696                         10,
   1697                     ) catch |err| {
   1698                         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   1699                     };
   1700                 } else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) {
   1701                     i += 1;
   1702                     if (i >= linker_args.items.len) {
   1703                         fatal("expected linker arg after '{s}'", .{arg});
   1704                     }
   1705                     try frameworks.append(linker_args.items[i]);
   1706                 } else if (mem.eql(u8, arg, "-compatibility_version")) {
   1707                     i += 1;
   1708                     if (i >= linker_args.items.len) {
   1709                         fatal("expected linker arg after '{s}'", .{arg});
   1710                     }
   1711                     compatibility_version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
   1712                         fatal("unable to parse -compatibility_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
   1713                     };
   1714                 } else if (mem.eql(u8, arg, "-current_version")) {
   1715                     i += 1;
   1716                     if (i >= linker_args.items.len) {
   1717                         fatal("expected linker arg after '{s}'", .{arg});
   1718                     }
   1719                     version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
   1720                         fatal("unable to parse -current_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
   1721                     };
   1722                     have_version = true;
   1723                 } else if (mem.eql(u8, arg, "--out-implib") or
   1724                     mem.eql(u8, arg, "-implib"))
   1725                 {
   1726                     i += 1;
   1727                     if (i >= linker_args.items.len) {
   1728                         fatal("expected linker arg after '{s}'", .{arg});
   1729                     }
   1730                     emit_implib = .{ .yes = linker_args.items[i] };
   1731                     emit_implib_arg_provided = true;
   1732                 } else if (mem.eql(u8, arg, "-undefined")) {
   1733                     i += 1;
   1734                     if (i >= linker_args.items.len) {
   1735                         fatal("expected linker arg after '{s}'", .{arg});
   1736                     }
   1737                     if (mem.eql(u8, "dynamic_lookup", linker_args.items[i])) {
   1738                         linker_allow_shlib_undefined = true;
   1739                     } else {
   1740                         fatal("unsupported -undefined option '{s}'", .{linker_args.items[i]});
   1741                     }
   1742                 } else if (mem.eql(u8, arg, "-install_name")) {
   1743                     i += 1;
   1744                     if (i >= linker_args.items.len) {
   1745                         fatal("expected linker arg after '{s}'", .{arg});
   1746                     }
   1747                     install_name = linker_args.items[i];
   1748                 } else {
   1749                     warn("unsupported linker arg: {s}", .{arg});
   1750                 }
   1751             }
   1752 
   1753             if (want_sanitize_c) |wsc| {
   1754                 if (wsc and optimize_mode == .ReleaseFast) {
   1755                     optimize_mode = .ReleaseSafe;
   1756                 }
   1757             }
   1758 
   1759             switch (c_out_mode) {
   1760                 .link => {
   1761                     output_mode = if (is_shared_lib) .Lib else .Exe;
   1762                     emit_bin = if (out_path) |p| .{ .yes = p } else EmitBin.yes_a_out;
   1763                     enable_cache = true;
   1764                     if (emit_llvm) {
   1765                         fatal("-emit-llvm cannot be used when linking", .{});
   1766                     }
   1767                 },
   1768                 .object => {
   1769                     output_mode = .Obj;
   1770                     if (emit_llvm) {
   1771                         emit_bin = .no;
   1772                         if (out_path) |p| {
   1773                             emit_llvm_bc = .{ .yes = p };
   1774                         } else {
   1775                             emit_llvm_bc = .yes_default_path;
   1776                         }
   1777                     } else {
   1778                         if (out_path) |p| {
   1779                             emit_bin = .{ .yes = p };
   1780                         } else {
   1781                             emit_bin = .yes_default_path;
   1782                         }
   1783                     }
   1784                 },
   1785                 .assembly => {
   1786                     output_mode = .Obj;
   1787                     emit_bin = .no;
   1788                     if (emit_llvm) {
   1789                         if (out_path) |p| {
   1790                             emit_llvm_ir = .{ .yes = p };
   1791                         } else {
   1792                             emit_llvm_ir = .yes_default_path;
   1793                         }
   1794                     } else {
   1795                         if (out_path) |p| {
   1796                             emit_asm = .{ .yes = p };
   1797                         } else {
   1798                             emit_asm = .yes_default_path;
   1799                         }
   1800                     }
   1801                 },
   1802                 .preprocessor => {
   1803                     output_mode = .Obj;
   1804                     // An error message is generated when there is more than 1 C source file.
   1805                     if (c_source_files.items.len != 1) {
   1806                         // For example `zig cc` and no args should print the "no input files" message.
   1807                         return punt_to_clang(arena, all_args);
   1808                     }
   1809                     if (out_path) |p| {
   1810                         emit_bin = .{ .yes = p };
   1811                         clang_preprocessor_mode = .yes;
   1812                     } else {
   1813                         clang_preprocessor_mode = .stdout;
   1814                     }
   1815                 },
   1816             }
   1817             if (c_source_files.items.len == 0 and link_objects.items.len == 0) {
   1818                 // For example `zig cc` and no args should print the "no input files" message.
   1819                 return punt_to_clang(arena, all_args);
   1820             }
   1821         },
   1822     }
   1823 
   1824     if (arg_mode == .translate_c and c_source_files.items.len != 1) {
   1825         fatal("translate-c expects exactly 1 source file (found {d})", .{c_source_files.items.len});
   1826     }
   1827 
   1828     if (root_src_file == null and arg_mode == .zig_test) {
   1829         fatal("`zig test` expects a zig source file argument", .{});
   1830     }
   1831 
   1832     const root_name = if (provided_name) |n| n else blk: {
   1833         if (arg_mode == .zig_test) {
   1834             break :blk "test";
   1835         } else if (root_src_file) |file| {
   1836             const basename = fs.path.basename(file);
   1837             break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
   1838         } else if (c_source_files.items.len >= 1) {
   1839             const basename = fs.path.basename(c_source_files.items[0].src_path);
   1840             break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
   1841         } else if (link_objects.items.len >= 1) {
   1842             const basename = fs.path.basename(link_objects.items[0]);
   1843             break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
   1844         } else if (emit_bin == .yes) {
   1845             const basename = fs.path.basename(emit_bin.yes);
   1846             break :blk basename[0 .. basename.len - fs.path.extension(basename).len];
   1847         } else if (show_builtin) {
   1848             break :blk "builtin";
   1849         } else if (arg_mode == .run) {
   1850             fatal("`zig run` expects at least one positional argument", .{});
   1851             // TODO once the attempt to unwrap error: LinkingWithoutZigSourceUnimplemented
   1852             // is solved, remove the above fatal() and uncomment the `break` below.
   1853             //break :blk "run";
   1854         } else {
   1855             fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{});
   1856         }
   1857     };
   1858 
   1859     var target_parse_options: std.zig.CrossTarget.ParseOptions = .{
   1860         .arch_os_abi = target_arch_os_abi,
   1861         .cpu_features = target_mcpu,
   1862         .dynamic_linker = target_dynamic_linker,
   1863     };
   1864 
   1865     // Before passing the mcpu string in for parsing, we convert any -m flags that were
   1866     // passed in via zig cc to zig-style.
   1867     if (llvm_m_args.items.len != 0) {
   1868         // If this returns null, we let it fall through to the case below which will
   1869         // run the full parse function and do proper error handling.
   1870         if (std.zig.CrossTarget.parseCpuArch(target_parse_options)) |cpu_arch| {
   1871             var llvm_to_zig_name = std.StringHashMap([]const u8).init(gpa);
   1872             defer llvm_to_zig_name.deinit();
   1873 
   1874             for (cpu_arch.allFeaturesList()) |feature| {
   1875                 const llvm_name = feature.llvm_name orelse continue;
   1876                 try llvm_to_zig_name.put(llvm_name, feature.name);
   1877             }
   1878 
   1879             var mcpu_buffer = std.ArrayList(u8).init(gpa);
   1880             defer mcpu_buffer.deinit();
   1881 
   1882             try mcpu_buffer.appendSlice(target_mcpu orelse "baseline");
   1883 
   1884             for (llvm_m_args.items) |llvm_m_arg| {
   1885                 if (mem.startsWith(u8, llvm_m_arg, "mno-")) {
   1886                     const llvm_name = llvm_m_arg["mno-".len..];
   1887                     const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
   1888                         fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
   1889                             @tagName(cpu_arch), llvm_name,
   1890                         });
   1891                     };
   1892                     try mcpu_buffer.append('-');
   1893                     try mcpu_buffer.appendSlice(zig_name);
   1894                 } else if (mem.startsWith(u8, llvm_m_arg, "m")) {
   1895                     const llvm_name = llvm_m_arg["m".len..];
   1896                     const zig_name = llvm_to_zig_name.get(llvm_name) orelse {
   1897                         fatal("target architecture {s} has no LLVM CPU feature named '{s}'", .{
   1898                             @tagName(cpu_arch), llvm_name,
   1899                         });
   1900                     };
   1901                     try mcpu_buffer.append('+');
   1902                     try mcpu_buffer.appendSlice(zig_name);
   1903                 } else {
   1904                     unreachable;
   1905                 }
   1906             }
   1907 
   1908             const adjusted_target_mcpu = try arena.dupe(u8, mcpu_buffer.items);
   1909             std.log.debug("adjusted target_mcpu: {s}", .{adjusted_target_mcpu});
   1910             target_parse_options.cpu_features = adjusted_target_mcpu;
   1911         }
   1912     }
   1913 
   1914     const cross_target = try parseCrossTargetOrReportFatalError(arena, target_parse_options);
   1915     const target_info = try detectNativeTargetInfo(gpa, cross_target);
   1916 
   1917     if (target_info.target.os.tag != .freestanding) {
   1918         if (ensure_libc_on_non_freestanding)
   1919             link_libc = true;
   1920         if (ensure_libcpp_on_non_freestanding)
   1921             link_libcpp = true;
   1922     }
   1923 
   1924     // Now that we have target info, we can find out if any of the system libraries
   1925     // are part of libc or libc++. We remove them from the list and communicate their
   1926     // existence via flags instead.
   1927     {
   1928         var i: usize = 0;
   1929         while (i < system_libs.count()) {
   1930             const lib_name = system_libs.keys()[i];
   1931 
   1932             if (target_util.is_libc_lib_name(target_info.target, lib_name)) {
   1933                 link_libc = true;
   1934                 _ = system_libs.orderedRemove(lib_name);
   1935                 continue;
   1936             }
   1937             if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) {
   1938                 link_libcpp = true;
   1939                 _ = system_libs.orderedRemove(lib_name);
   1940                 continue;
   1941             }
   1942             if (mem.eql(u8, lib_name, "unwind")) {
   1943                 link_libunwind = true;
   1944                 _ = system_libs.orderedRemove(lib_name);
   1945                 continue;
   1946             }
   1947             if (std.fs.path.isAbsolute(lib_name)) {
   1948                 fatal("cannot use absolute path as a system library: {s}", .{lib_name});
   1949             }
   1950             if (target_info.target.os.tag == .wasi) {
   1951                 if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| {
   1952                     try wasi_emulated_libs.append(crt_file);
   1953                     _ = system_libs.orderedRemove(lib_name);
   1954                     continue;
   1955                 }
   1956             }
   1957             i += 1;
   1958         }
   1959     }
   1960 
   1961     if (use_lld) |opt| {
   1962         if (opt and cross_target.isDarwin()) {
   1963             fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{});
   1964         }
   1965     }
   1966 
   1967     if (want_lto) |opt| {
   1968         if (opt and cross_target.isDarwin()) {
   1969             fatal("LTO is not yet supported with the Mach-O object format. More details: https://github.com/ziglang/zig/issues/8680", .{});
   1970         }
   1971     }
   1972 
   1973     if (comptime builtin.target.isDarwin()) {
   1974         // If we want to link against frameworks, we need system headers.
   1975         if (framework_dirs.items.len > 0 or frameworks.items.len > 0)
   1976             want_native_include_dirs = true;
   1977     }
   1978 
   1979     if (sysroot == null and cross_target.isNativeOs() and (system_libs.count() != 0 or want_native_include_dirs)) {
   1980         const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| {
   1981             fatal("unable to detect native system paths: {s}", .{@errorName(err)});
   1982         };
   1983         for (paths.warnings.items) |warning| {
   1984             warn("{s}", .{warning});
   1985         }
   1986 
   1987         const has_sysroot = if (comptime builtin.target.isDarwin()) outer: {
   1988             if (std.zig.system.darwin.isDarwinSDKInstalled(arena)) {
   1989                 const sdk = std.zig.system.darwin.getDarwinSDK(arena, target_info.target) orelse
   1990                     break :outer false;
   1991                 native_darwin_sdk = sdk;
   1992                 try clang_argv.ensureUnusedCapacity(2);
   1993                 clang_argv.appendAssumeCapacity("-isysroot");
   1994                 clang_argv.appendAssumeCapacity(sdk.path);
   1995                 break :outer true;
   1996             } else break :outer false;
   1997         } else false;
   1998 
   1999         try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2);
   2000         const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem";
   2001         for (paths.include_dirs.items) |include_dir| {
   2002             clang_argv.appendAssumeCapacity(isystem_flag);
   2003             clang_argv.appendAssumeCapacity(include_dir);
   2004         }
   2005 
   2006         try clang_argv.ensureUnusedCapacity(paths.framework_dirs.items.len * 2);
   2007         try framework_dirs.ensureUnusedCapacity(paths.framework_dirs.items.len);
   2008         const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework";
   2009         for (paths.framework_dirs.items) |framework_dir| {
   2010             clang_argv.appendAssumeCapacity(iframework_flag);
   2011             clang_argv.appendAssumeCapacity(framework_dir);
   2012             framework_dirs.appendAssumeCapacity(framework_dir);
   2013         }
   2014 
   2015         for (paths.lib_dirs.items) |lib_dir| {
   2016             try lib_dirs.append(lib_dir);
   2017         }
   2018         for (paths.rpaths.items) |rpath| {
   2019             try rpath_list.append(rpath);
   2020         }
   2021     }
   2022 
   2023     {
   2024         // Resolve static libraries into full paths.
   2025         const sep = fs.path.sep_str;
   2026 
   2027         var test_path = std.ArrayList(u8).init(gpa);
   2028         defer test_path.deinit();
   2029 
   2030         for (static_libs.items) |static_lib| {
   2031             for (lib_dirs.items) |lib_dir_path| {
   2032                 test_path.clearRetainingCapacity();
   2033                 try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
   2034                     lib_dir_path,
   2035                     target_info.target.libPrefix(),
   2036                     static_lib,
   2037                     target_info.target.staticLibSuffix(),
   2038                 });
   2039                 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
   2040                     error.FileNotFound => continue,
   2041                     else => |e| fatal("unable to search for static library '{s}': {s}", .{
   2042                         test_path.items, @errorName(e),
   2043                     }),
   2044                 };
   2045                 try link_objects.append(try arena.dupe(u8, test_path.items));
   2046                 break;
   2047             } else {
   2048                 var search_paths = std.ArrayList(u8).init(arena);
   2049                 for (lib_dirs.items) |lib_dir_path| {
   2050                     try search_paths.writer().print("\n {s}" ++ sep ++ "{s}{s}{s}", .{
   2051                         lib_dir_path,
   2052                         target_info.target.libPrefix(),
   2053                         static_lib,
   2054                         target_info.target.staticLibSuffix(),
   2055                     });
   2056                 }
   2057                 try search_paths.appendSlice("\n suggestion: use full paths to static libraries on the command line rather than using -l and -L arguments");
   2058                 fatal("static library '{s}' not found. search paths: {s}", .{
   2059                     static_lib, search_paths.items,
   2060                 });
   2061             }
   2062         }
   2063     }
   2064 
   2065     const object_format: std.Target.ObjectFormat = blk: {
   2066         const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat();
   2067         if (mem.eql(u8, ofmt, "elf")) {
   2068             break :blk .elf;
   2069         } else if (mem.eql(u8, ofmt, "c")) {
   2070             break :blk .c;
   2071         } else if (mem.eql(u8, ofmt, "coff")) {
   2072             break :blk .coff;
   2073         } else if (mem.eql(u8, ofmt, "macho")) {
   2074             break :blk .macho;
   2075         } else if (mem.eql(u8, ofmt, "wasm")) {
   2076             break :blk .wasm;
   2077         } else if (mem.eql(u8, ofmt, "hex")) {
   2078             break :blk .hex;
   2079         } else if (mem.eql(u8, ofmt, "raw")) {
   2080             break :blk .raw;
   2081         } else if (mem.eql(u8, ofmt, "spirv")) {
   2082             break :blk .spirv;
   2083         } else {
   2084             fatal("unsupported object format: {s}", .{ofmt});
   2085         }
   2086     };
   2087 
   2088     if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) {
   2089         const total_obj_count = c_source_files.items.len +
   2090             @boolToInt(root_src_file != null) +
   2091             link_objects.items.len;
   2092         if (total_obj_count > 1) {
   2093             fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)});
   2094         }
   2095     }
   2096 
   2097     var cleanup_emit_bin_dir: ?fs.Dir = null;
   2098     defer if (cleanup_emit_bin_dir) |*dir| dir.close();
   2099 
   2100     const have_enable_cache = enable_cache orelse false;
   2101     const optional_version = if (have_version) version else null;
   2102 
   2103     const resolved_soname: ?[]const u8 = switch (soname) {
   2104         .yes => |explicit| explicit,
   2105         .no => null,
   2106         .yes_default_value => switch (object_format) {
   2107             .elf => if (have_version)
   2108                 try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major })
   2109             else
   2110                 try std.fmt.allocPrint(arena, "lib{s}.so", .{root_name}),
   2111             else => null,
   2112         },
   2113     };
   2114 
   2115     const a_out_basename = switch (object_format) {
   2116         .coff => "a.exe",
   2117         else => "a.out",
   2118     };
   2119 
   2120     const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {
   2121         .no => null,
   2122         .yes_default_path => Compilation.EmitLoc{
   2123             .directory = blk: {
   2124                 switch (arg_mode) {
   2125                     .run, .zig_test => break :blk null,
   2126                     else => {
   2127                         if (have_enable_cache) {
   2128                             break :blk null;
   2129                         } else {
   2130                             break :blk .{ .path = null, .handle = fs.cwd() };
   2131                         }
   2132                     },
   2133                 }
   2134             },
   2135             .basename = try std.zig.binNameAlloc(arena, .{
   2136                 .root_name = root_name,
   2137                 .target = target_info.target,
   2138                 .output_mode = output_mode,
   2139                 .link_mode = link_mode,
   2140                 .object_format = object_format,
   2141                 .version = optional_version,
   2142             }),
   2143         },
   2144         .yes => |full_path| b: {
   2145             const basename = fs.path.basename(full_path);
   2146             if (have_enable_cache) {
   2147                 break :b Compilation.EmitLoc{
   2148                     .basename = basename,
   2149                     .directory = null,
   2150                 };
   2151             }
   2152             if (fs.path.dirname(full_path)) |dirname| {
   2153                 const handle = fs.cwd().openDir(dirname, .{}) catch |err| {
   2154                     fatal("unable to open output directory '{s}': {s}", .{ dirname, @errorName(err) });
   2155                 };
   2156                 cleanup_emit_bin_dir = handle;
   2157                 break :b Compilation.EmitLoc{
   2158                     .basename = basename,
   2159                     .directory = .{
   2160                         .path = dirname,
   2161                         .handle = handle,
   2162                     },
   2163                 };
   2164             } else {
   2165                 break :b Compilation.EmitLoc{
   2166                     .basename = basename,
   2167                     .directory = .{ .path = null, .handle = fs.cwd() },
   2168                 };
   2169             }
   2170         },
   2171         .yes_a_out => Compilation.EmitLoc{
   2172             .directory = null,
   2173             .basename = a_out_basename,
   2174         },
   2175     };
   2176 
   2177     const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name});
   2178     var emit_h_resolved = emit_h.resolve(default_h_basename) catch |err| {
   2179         switch (emit_h) {
   2180             .yes => |p| {
   2181                 fatal("unable to open directory from argument '-femit-h', '{s}': {s}", .{
   2182                     p, @errorName(err),
   2183                 });
   2184             },
   2185             .yes_default_path => {
   2186                 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
   2187                     default_h_basename, @errorName(err),
   2188                 });
   2189             },
   2190             .no => unreachable,
   2191         }
   2192     };
   2193     defer emit_h_resolved.deinit();
   2194 
   2195     const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name});
   2196     var emit_asm_resolved = emit_asm.resolve(default_asm_basename) catch |err| {
   2197         switch (emit_asm) {
   2198             .yes => |p| {
   2199                 fatal("unable to open directory from argument '-femit-asm', '{s}': {s}", .{
   2200                     p, @errorName(err),
   2201                 });
   2202             },
   2203             .yes_default_path => {
   2204                 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
   2205                     default_asm_basename, @errorName(err),
   2206                 });
   2207             },
   2208             .no => unreachable,
   2209         }
   2210     };
   2211     defer emit_asm_resolved.deinit();
   2212 
   2213     const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name});
   2214     var emit_llvm_ir_resolved = emit_llvm_ir.resolve(default_llvm_ir_basename) catch |err| {
   2215         switch (emit_llvm_ir) {
   2216             .yes => |p| {
   2217                 fatal("unable to open directory from argument '-femit-llvm-ir', '{s}': {s}", .{
   2218                     p, @errorName(err),
   2219                 });
   2220             },
   2221             .yes_default_path => {
   2222                 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
   2223                     default_llvm_ir_basename, @errorName(err),
   2224                 });
   2225             },
   2226             .no => unreachable,
   2227         }
   2228     };
   2229     defer emit_llvm_ir_resolved.deinit();
   2230 
   2231     const default_llvm_bc_basename = try std.fmt.allocPrint(arena, "{s}.bc", .{root_name});
   2232     var emit_llvm_bc_resolved = emit_llvm_bc.resolve(default_llvm_bc_basename) catch |err| {
   2233         switch (emit_llvm_bc) {
   2234             .yes => |p| {
   2235                 fatal("unable to open directory from argument '-femit-llvm-bc', '{s}': {s}", .{
   2236                     p, @errorName(err),
   2237                 });
   2238             },
   2239             .yes_default_path => {
   2240                 fatal("unable to open directory from arguments '--name' or '-fsoname', '{s}': {s}", .{
   2241                     default_llvm_bc_basename, @errorName(err),
   2242                 });
   2243             },
   2244             .no => unreachable,
   2245         }
   2246     };
   2247     defer emit_llvm_bc_resolved.deinit();
   2248 
   2249     const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name});
   2250     var emit_analysis_resolved = emit_analysis.resolve(default_analysis_basename) catch |err| {
   2251         switch (emit_analysis) {
   2252             .yes => |p| {
   2253                 fatal("unable to open directory from argument '-femit-analysis',  '{s}': {s}", .{
   2254                     p, @errorName(err),
   2255                 });
   2256             },
   2257             .yes_default_path => {
   2258                 fatal("unable to open directory from arguments 'name' or 'soname', '{s}': {s}", .{
   2259                     default_analysis_basename, @errorName(err),
   2260                 });
   2261             },
   2262             .no => unreachable,
   2263         }
   2264     };
   2265     defer emit_analysis_resolved.deinit();
   2266 
   2267     var emit_docs_resolved = emit_docs.resolve("docs") catch |err| {
   2268         switch (emit_docs) {
   2269             .yes => |p| {
   2270                 fatal("unable to open directory from argument '-femit-docs', '{s}': {s}", .{
   2271                     p, @errorName(err),
   2272                 });
   2273             },
   2274             .yes_default_path => {
   2275                 fatal("unable to open directory 'docs': {s}", .{@errorName(err)});
   2276             },
   2277             .no => unreachable,
   2278         }
   2279     };
   2280     defer emit_docs_resolved.deinit();
   2281 
   2282     const is_exe_or_dyn_lib = switch (output_mode) {
   2283         .Obj => false,
   2284         .Lib => (link_mode orelse .Static) == .Dynamic,
   2285         .Exe => true,
   2286     };
   2287     // Note that cmake when targeting Windows will try to execute
   2288     // zig cc to make an executable and output an implib too.
   2289     const implib_eligible = is_exe_or_dyn_lib and
   2290         emit_bin_loc != null and target_info.target.os.tag == .windows;
   2291     if (!implib_eligible) {
   2292         if (!emit_implib_arg_provided) {
   2293             emit_implib = .no;
   2294         } else if (emit_implib != .no) {
   2295             fatal("the argument -femit-implib is allowed only when building a Windows DLL", .{});
   2296         }
   2297     }
   2298     const default_implib_basename = try std.fmt.allocPrint(arena, "{s}.lib", .{root_name});
   2299     var emit_implib_resolved = switch (emit_implib) {
   2300         .no => Emit.Resolved{ .data = null, .dir = null },
   2301         .yes => |p| emit_implib.resolve(default_implib_basename) catch |err| {
   2302             fatal("unable to open directory from argument '-femit-implib', '{s}': {s}", .{
   2303                 p, @errorName(err),
   2304             });
   2305         },
   2306         .yes_default_path => Emit.Resolved{
   2307             .data = Compilation.EmitLoc{
   2308                 .directory = emit_bin_loc.?.directory,
   2309                 .basename = default_implib_basename,
   2310             },
   2311             .dir = null,
   2312         },
   2313     };
   2314     defer emit_implib_resolved.deinit();
   2315 
   2316     const main_pkg: ?*Package = if (root_src_file) |src_path| blk: {
   2317         if (main_pkg_path) |p| {
   2318             const rel_src_path = try fs.path.relative(gpa, p, src_path);
   2319             defer gpa.free(rel_src_path);
   2320             break :blk try Package.create(gpa, p, rel_src_path);
   2321         } else {
   2322             break :blk try Package.create(gpa, fs.path.dirname(src_path), fs.path.basename(src_path));
   2323         }
   2324     } else null;
   2325     defer if (main_pkg) |p| p.destroy(gpa);
   2326 
   2327     // Transfer packages added with --pkg-begin/--pkg-end to the root package
   2328     if (main_pkg) |pkg| {
   2329         pkg.table = pkg_tree_root.table;
   2330         pkg_tree_root.table = .{};
   2331     }
   2332 
   2333     const self_exe_path = try fs.selfExePathAlloc(arena);
   2334     var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
   2335         .path = lib_dir,
   2336         .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
   2337             fatal("unable to open zig lib directory from 'zig-lib-dir' argument or env, '{s}': {s}", .{ lib_dir, @errorName(err) });
   2338         },
   2339     } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
   2340         fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
   2341     };
   2342     defer zig_lib_directory.handle.close();
   2343 
   2344     var thread_pool: ThreadPool = undefined;
   2345     try thread_pool.init(gpa);
   2346     defer thread_pool.deinit();
   2347 
   2348     var libc_installation: ?LibCInstallation = null;
   2349     defer if (libc_installation) |*l| l.deinit(gpa);
   2350 
   2351     if (libc_paths_file) |paths_file| {
   2352         libc_installation = LibCInstallation.parse(gpa, paths_file, cross_target) catch |err| {
   2353             fatal("unable to parse libc paths file at path {s}: {s}", .{ paths_file, @errorName(err) });
   2354         };
   2355     }
   2356 
   2357     var global_cache_directory: Compilation.Directory = l: {
   2358         const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
   2359         break :l .{
   2360             .handle = try fs.cwd().makeOpenPath(p, .{}),
   2361             .path = p,
   2362         };
   2363     };
   2364     defer global_cache_directory.handle.close();
   2365 
   2366     var cleanup_local_cache_dir: ?fs.Dir = null;
   2367     defer if (cleanup_local_cache_dir) |*dir| dir.close();
   2368 
   2369     var local_cache_directory: Compilation.Directory = l: {
   2370         if (override_local_cache_dir) |local_cache_dir_path| {
   2371             const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{});
   2372             cleanup_local_cache_dir = dir;
   2373             break :l .{
   2374                 .handle = dir,
   2375                 .path = local_cache_dir_path,
   2376             };
   2377         }
   2378         if (arg_mode == .run) {
   2379             break :l global_cache_directory;
   2380         }
   2381         if (main_pkg) |pkg| {
   2382             const cache_dir_path = try pkg.root_src_directory.join(arena, &[_][]const u8{"zig-cache"});
   2383             const dir = try pkg.root_src_directory.handle.makeOpenPath("zig-cache", .{});
   2384             cleanup_local_cache_dir = dir;
   2385             break :l .{
   2386                 .handle = dir,
   2387                 .path = cache_dir_path,
   2388             };
   2389         }
   2390         // Otherwise we really don't have a reasonable place to put the local cache directory,
   2391         // so we utilize the global one.
   2392         break :l global_cache_directory;
   2393     };
   2394 
   2395     if (build_options.have_llvm and emit_asm != .no) {
   2396         // LLVM has no way to set this non-globally.
   2397         const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" };
   2398         @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv);
   2399     }
   2400 
   2401     const clang_passthrough_mode = switch (arg_mode) {
   2402         .cc, .cpp, .translate_c => true,
   2403         else => false,
   2404     };
   2405 
   2406     gimmeMoreOfThoseSweetSweetFileDescriptors();
   2407 
   2408     const comp = Compilation.create(gpa, .{
   2409         .zig_lib_directory = zig_lib_directory,
   2410         .local_cache_directory = local_cache_directory,
   2411         .global_cache_directory = global_cache_directory,
   2412         .root_name = root_name,
   2413         .target = target_info.target,
   2414         .is_native_os = cross_target.isNativeOs(),
   2415         .is_native_abi = cross_target.isNativeAbi(),
   2416         .dynamic_linker = target_info.dynamic_linker.get(),
   2417         .sysroot = sysroot,
   2418         .output_mode = output_mode,
   2419         .main_pkg = main_pkg,
   2420         .emit_bin = emit_bin_loc,
   2421         .emit_h = emit_h_resolved.data,
   2422         .emit_asm = emit_asm_resolved.data,
   2423         .emit_llvm_ir = emit_llvm_ir_resolved.data,
   2424         .emit_llvm_bc = emit_llvm_bc_resolved.data,
   2425         .emit_docs = emit_docs_resolved.data,
   2426         .emit_analysis = emit_analysis_resolved.data,
   2427         .emit_implib = emit_implib_resolved.data,
   2428         .link_mode = link_mode,
   2429         .dll_export_fns = dll_export_fns,
   2430         .object_format = object_format,
   2431         .optimize_mode = optimize_mode,
   2432         .keep_source_files_loaded = false,
   2433         .clang_argv = clang_argv.items,
   2434         .lib_dirs = lib_dirs.items,
   2435         .rpath_list = rpath_list.items,
   2436         .c_source_files = c_source_files.items,
   2437         .link_objects = link_objects.items,
   2438         .framework_dirs = framework_dirs.items,
   2439         .frameworks = frameworks.items,
   2440         .system_lib_names = system_libs.keys(),
   2441         .system_lib_infos = system_libs.values(),
   2442         .wasi_emulated_libs = wasi_emulated_libs.items,
   2443         .link_libc = link_libc,
   2444         .link_libcpp = link_libcpp,
   2445         .link_libunwind = link_libunwind,
   2446         .want_pic = want_pic,
   2447         .want_pie = want_pie,
   2448         .want_lto = want_lto,
   2449         .want_unwind_tables = want_unwind_tables,
   2450         .want_sanitize_c = want_sanitize_c,
   2451         .want_stack_check = want_stack_check,
   2452         .want_red_zone = want_red_zone,
   2453         .omit_frame_pointer = omit_frame_pointer,
   2454         .want_valgrind = want_valgrind,
   2455         .want_tsan = want_tsan,
   2456         .want_compiler_rt = want_compiler_rt,
   2457         .use_llvm = use_llvm,
   2458         .use_lld = use_lld,
   2459         .use_clang = use_clang,
   2460         .use_stage1 = use_stage1,
   2461         .rdynamic = rdynamic,
   2462         .linker_script = linker_script,
   2463         .version_script = version_script,
   2464         .disable_c_depfile = disable_c_depfile,
   2465         .soname = resolved_soname,
   2466         .linker_gc_sections = linker_gc_sections,
   2467         .linker_allow_shlib_undefined = linker_allow_shlib_undefined,
   2468         .linker_bind_global_refs_locally = linker_bind_global_refs_locally,
   2469         .linker_import_memory = linker_import_memory,
   2470         .linker_import_table = linker_import_table,
   2471         .linker_export_table = linker_export_table,
   2472         .linker_initial_memory = linker_initial_memory,
   2473         .linker_max_memory = linker_max_memory,
   2474         .linker_global_base = linker_global_base,
   2475         .linker_export_symbol_names = linker_export_symbol_names.items,
   2476         .linker_z_nodelete = linker_z_nodelete,
   2477         .linker_z_notext = linker_z_notext,
   2478         .linker_z_defs = linker_z_defs,
   2479         .linker_z_origin = linker_z_origin,
   2480         .linker_z_noexecstack = linker_z_noexecstack,
   2481         .linker_z_now = linker_z_now,
   2482         .linker_z_relro = linker_z_relro,
   2483         .linker_tsaware = linker_tsaware,
   2484         .linker_nxcompat = linker_nxcompat,
   2485         .linker_dynamicbase = linker_dynamicbase,
   2486         .linker_optimization = linker_optimization,
   2487         .major_subsystem_version = major_subsystem_version,
   2488         .minor_subsystem_version = minor_subsystem_version,
   2489         .link_eh_frame_hdr = link_eh_frame_hdr,
   2490         .link_emit_relocs = link_emit_relocs,
   2491         .stack_size_override = stack_size_override,
   2492         .image_base_override = image_base_override,
   2493         .strip = strip,
   2494         .single_threaded = single_threaded,
   2495         .function_sections = function_sections,
   2496         .self_exe_path = self_exe_path,
   2497         .thread_pool = &thread_pool,
   2498         .clang_passthrough_mode = clang_passthrough_mode,
   2499         .clang_preprocessor_mode = clang_preprocessor_mode,
   2500         .version = optional_version,
   2501         .libc_installation = if (libc_installation) |*lci| lci else null,
   2502         .verbose_cc = verbose_cc,
   2503         .verbose_link = verbose_link,
   2504         .verbose_air = verbose_air,
   2505         .verbose_mir = verbose_mir,
   2506         .verbose_llvm_ir = verbose_llvm_ir,
   2507         .verbose_cimport = verbose_cimport,
   2508         .verbose_llvm_cpu_features = verbose_llvm_cpu_features,
   2509         .machine_code_model = machine_code_model,
   2510         .color = color,
   2511         .time_report = time_report,
   2512         .stack_report = stack_report,
   2513         .is_test = arg_mode == .zig_test,
   2514         .each_lib_rpath = each_lib_rpath,
   2515         .test_evented_io = test_evented_io,
   2516         .test_filter = test_filter,
   2517         .test_name_prefix = test_name_prefix,
   2518         .disable_lld_caching = !have_enable_cache,
   2519         .subsystem = subsystem,
   2520         .wasi_exec_model = wasi_exec_model,
   2521         .debug_compile_errors = debug_compile_errors,
   2522         .enable_link_snapshots = enable_link_snapshots,
   2523         .native_darwin_sdk = native_darwin_sdk,
   2524         .install_name = install_name,
   2525     }) catch |err| switch (err) {
   2526         error.LibCUnavailable => {
   2527             const target = target_info.target;
   2528             const triple_name = try target.zigTriple(arena);
   2529             std.log.err("unable to find or provide libc for target '{s}'", .{triple_name});
   2530 
   2531             for (target_util.available_libcs) |t| {
   2532                 if (t.arch == target.cpu.arch and t.os == target.os.tag) {
   2533                     if (t.os_ver) |os_ver| {
   2534                         std.log.info("zig can provide libc for related target {s}-{s}.{d}-{s}", .{
   2535                             @tagName(t.arch), @tagName(t.os), os_ver.major, @tagName(t.abi),
   2536                         });
   2537                     } else {
   2538                         std.log.info("zig can provide libc for related target {s}-{s}-{s}", .{
   2539                             @tagName(t.arch), @tagName(t.os), @tagName(t.abi),
   2540                         });
   2541                     }
   2542                 }
   2543             }
   2544             process.exit(1);
   2545         },
   2546         error.ExportTableAndImportTableConflict => {
   2547             fatal("--import-table and --export-table may not be used together", .{});
   2548         },
   2549         else => fatal("unable to create compilation: {s}", .{@errorName(err)}),
   2550     };
   2551     var comp_destroyed = false;
   2552     defer if (!comp_destroyed) comp.destroy();
   2553 
   2554     if (show_builtin) {
   2555         return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
   2556     }
   2557     if (arg_mode == .translate_c) {
   2558         return cmdTranslateC(comp, arena, have_enable_cache);
   2559     }
   2560 
   2561     const hook: AfterUpdateHook = blk: {
   2562         if (!have_enable_cache)
   2563             break :blk .none;
   2564 
   2565         switch (emit_bin) {
   2566             .no => break :blk .none,
   2567             .yes_default_path => break :blk .{
   2568                 .print = comp.bin_file.options.emit.?.directory.path orelse ".",
   2569             },
   2570             .yes => |full_path| break :blk .{ .update = full_path },
   2571             .yes_a_out => break :blk .{ .update = a_out_basename },
   2572         }
   2573     };
   2574 
   2575     updateModule(gpa, comp, hook) catch |err| switch (err) {
   2576         error.SemanticAnalyzeFail => if (!watch) process.exit(1),
   2577         else => |e| return e,
   2578     };
   2579     try comp.makeBinFileExecutable();
   2580 
   2581     if (build_options.is_stage1 and comp.stage1_lock != null and watch) {
   2582         warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
   2583     }
   2584 
   2585     if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: {
   2586         // Default to using `zig run` to execute the produced .c code from `zig test`.
   2587         const c_code_loc = emit_bin_loc orelse break :default_exec_args;
   2588         const c_code_directory = c_code_loc.directory orelse comp.bin_file.options.emit.?.directory;
   2589         const c_code_path = try fs.path.join(arena, &[_][]const u8{
   2590             c_code_directory.path orelse ".", c_code_loc.basename,
   2591         });
   2592         try test_exec_args.appendSlice(&.{ self_exe_path, "run", "-lc", c_code_path });
   2593     }
   2594 
   2595     const run_or_test = switch (arg_mode) {
   2596         .run => true,
   2597         .zig_test => !test_no_exec,
   2598         else => false,
   2599     };
   2600     if (run_or_test) {
   2601         try runOrTest(
   2602             comp,
   2603             gpa,
   2604             arena,
   2605             emit_bin_loc,
   2606             test_exec_args.items,
   2607             self_exe_path,
   2608             arg_mode,
   2609             target_info,
   2610             watch,
   2611             &comp_destroyed,
   2612             all_args,
   2613             runtime_args_start,
   2614             link_libc,
   2615         );
   2616     }
   2617 
   2618     const stdin = std.io.getStdIn().reader();
   2619     const stderr = std.io.getStdErr().writer();
   2620     var repl_buf: [1024]u8 = undefined;
   2621 
   2622     const ReplCmd = enum {
   2623         update,
   2624         help,
   2625         run,
   2626         update_and_run,
   2627     };
   2628 
   2629     var last_cmd: ReplCmd = .help;
   2630 
   2631     while (watch) {
   2632         try stderr.print("(zig) ", .{});
   2633         try comp.makeBinFileExecutable();
   2634         if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
   2635             try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)});
   2636             continue;
   2637         }) |line| {
   2638             const actual_line = mem.trimRight(u8, line, "\r\n ");
   2639             const cmd: ReplCmd = blk: {
   2640                 if (mem.eql(u8, actual_line, "update")) {
   2641                     break :blk .update;
   2642                 } else if (mem.eql(u8, actual_line, "exit")) {
   2643                     break;
   2644                 } else if (mem.eql(u8, actual_line, "help")) {
   2645                     break :blk .help;
   2646                 } else if (mem.eql(u8, actual_line, "run")) {
   2647                     break :blk .run;
   2648                 } else if (mem.eql(u8, actual_line, "update-and-run")) {
   2649                     break :blk .update_and_run;
   2650                 } else if (actual_line.len == 0) {
   2651                     break :blk last_cmd;
   2652                 } else {
   2653                     try stderr.print("unknown command: {s}\n", .{actual_line});
   2654                     continue;
   2655                 }
   2656             };
   2657             last_cmd = cmd;
   2658             switch (cmd) {
   2659                 .update => {
   2660                     tracy.frameMark();
   2661                     if (output_mode == .Exe) {
   2662                         try comp.makeBinFileWritable();
   2663                     }
   2664                     updateModule(gpa, comp, hook) catch |err| switch (err) {
   2665                         error.SemanticAnalyzeFail => continue,
   2666                         else => |e| return e,
   2667                     };
   2668                 },
   2669                 .help => {
   2670                     try stderr.writeAll(repl_help);
   2671                 },
   2672                 .run => {
   2673                     tracy.frameMark();
   2674                     try runOrTest(
   2675                         comp,
   2676                         gpa,
   2677                         arena,
   2678                         emit_bin_loc,
   2679                         test_exec_args.items,
   2680                         self_exe_path,
   2681                         arg_mode,
   2682                         target_info,
   2683                         watch,
   2684                         &comp_destroyed,
   2685                         all_args,
   2686                         runtime_args_start,
   2687                         link_libc,
   2688                     );
   2689                 },
   2690                 .update_and_run => {
   2691                     tracy.frameMark();
   2692                     if (output_mode == .Exe) {
   2693                         try comp.makeBinFileWritable();
   2694                     }
   2695                     updateModule(gpa, comp, hook) catch |err| switch (err) {
   2696                         error.SemanticAnalyzeFail => continue,
   2697                         else => |e| return e,
   2698                     };
   2699                     try comp.makeBinFileExecutable();
   2700                     try runOrTest(
   2701                         comp,
   2702                         gpa,
   2703                         arena,
   2704                         emit_bin_loc,
   2705                         test_exec_args.items,
   2706                         self_exe_path,
   2707                         arg_mode,
   2708                         target_info,
   2709                         watch,
   2710                         &comp_destroyed,
   2711                         all_args,
   2712                         runtime_args_start,
   2713                         link_libc,
   2714                     );
   2715                 },
   2716             }
   2717         } else {
   2718             break;
   2719         }
   2720     }
   2721     // Skip resource deallocation in release builds; let the OS do it.
   2722     return cleanExit();
   2723 }
   2724 
   2725 fn parseCrossTargetOrReportFatalError(
   2726     allocator: Allocator,
   2727     opts: std.zig.CrossTarget.ParseOptions,
   2728 ) !std.zig.CrossTarget {
   2729     var opts_with_diags = opts;
   2730     var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{};
   2731     if (opts_with_diags.diagnostics == null) {
   2732         opts_with_diags.diagnostics = &diags;
   2733     }
   2734     return std.zig.CrossTarget.parse(opts_with_diags) catch |err| switch (err) {
   2735         error.UnknownCpuModel => {
   2736             help: {
   2737                 var help_text = std.ArrayList(u8).init(allocator);
   2738                 defer help_text.deinit();
   2739                 for (diags.arch.?.allCpuModels()) |cpu| {
   2740                     help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
   2741                 }
   2742                 std.log.info("Available CPUs for architecture '{s}':\n{s}", .{
   2743                     @tagName(diags.arch.?), help_text.items,
   2744                 });
   2745             }
   2746             fatal("Unknown CPU: '{s}'", .{diags.cpu_name.?});
   2747         },
   2748         error.UnknownCpuFeature => {
   2749             help: {
   2750                 var help_text = std.ArrayList(u8).init(allocator);
   2751                 defer help_text.deinit();
   2752                 for (diags.arch.?.allFeaturesList()) |feature| {
   2753                     help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
   2754                 }
   2755                 std.log.info("Available CPU features for architecture '{s}':\n{s}", .{
   2756                     @tagName(diags.arch.?), help_text.items,
   2757                 });
   2758             }
   2759             fatal("Unknown CPU feature: '{s}'", .{diags.unknown_feature_name});
   2760         },
   2761         else => |e| return e,
   2762     };
   2763 }
   2764 
   2765 fn runOrTest(
   2766     comp: *Compilation,
   2767     gpa: Allocator,
   2768     arena: Allocator,
   2769     emit_bin_loc: ?Compilation.EmitLoc,
   2770     test_exec_args: []const ?[]const u8,
   2771     self_exe_path: []const u8,
   2772     arg_mode: ArgMode,
   2773     target_info: std.zig.system.NativeTargetInfo,
   2774     watch: bool,
   2775     comp_destroyed: *bool,
   2776     all_args: []const []const u8,
   2777     runtime_args_start: ?usize,
   2778     link_libc: bool,
   2779 ) !void {
   2780     const exe_loc = emit_bin_loc orelse return;
   2781     const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory;
   2782     const exe_path = try fs.path.join(arena, &[_][]const u8{
   2783         exe_directory.path orelse ".", exe_loc.basename,
   2784     });
   2785 
   2786     var argv = std.ArrayList([]const u8).init(gpa);
   2787     defer argv.deinit();
   2788 
   2789     if (test_exec_args.len == 0) {
   2790         // when testing pass the zig_exe_path to argv
   2791         if (arg_mode == .zig_test)
   2792             try argv.appendSlice(&[_][]const u8{
   2793                 exe_path, self_exe_path,
   2794             })
   2795             // when running just pass the current exe
   2796         else
   2797             try argv.appendSlice(&[_][]const u8{
   2798                 exe_path,
   2799             });
   2800     } else {
   2801         for (test_exec_args) |arg| {
   2802             if (arg) |a| {
   2803                 try argv.append(a);
   2804             } else {
   2805                 try argv.appendSlice(&[_][]const u8{
   2806                     exe_path, self_exe_path,
   2807                 });
   2808             }
   2809         }
   2810     }
   2811     if (runtime_args_start) |i| {
   2812         try argv.appendSlice(all_args[i..]);
   2813     }
   2814     // We do not execve for tests because if the test fails we want to print
   2815     // the error message and invocation below.
   2816     if (std.process.can_execv and arg_mode == .run and !watch) {
   2817         // execv releases the locks; no need to destroy the Compilation here.
   2818         const err = std.process.execv(gpa, argv.items);
   2819         try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc);
   2820         const cmd = try argvCmd(arena, argv.items);
   2821         fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd });
   2822     } else {
   2823         const child = try std.ChildProcess.init(argv.items, gpa);
   2824         defer child.deinit();
   2825 
   2826         child.stdin_behavior = .Inherit;
   2827         child.stdout_behavior = .Inherit;
   2828         child.stderr_behavior = .Inherit;
   2829 
   2830         if (!watch) {
   2831             // Here we release all the locks associated with the Compilation so
   2832             // that whatever this child process wants to do won't deadlock.
   2833             comp.destroy();
   2834             comp_destroyed.* = true;
   2835         }
   2836 
   2837         const term = child.spawnAndWait() catch |err| {
   2838             try warnAboutForeignBinaries(gpa, arena, arg_mode, target_info, link_libc);
   2839             const cmd = try argvCmd(arena, argv.items);
   2840             fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd });
   2841         };
   2842         switch (arg_mode) {
   2843             .run, .build => {
   2844                 switch (term) {
   2845                     .Exited => |code| {
   2846                         if (code == 0) {
   2847                             if (!watch) return cleanExit();
   2848                         } else if (watch) {
   2849                             warn("process exited with code {d}", .{code});
   2850                         } else {
   2851                             // TODO https://github.com/ziglang/zig/issues/6342
   2852                             process.exit(1);
   2853                         }
   2854                     },
   2855                     else => {
   2856                         if (watch) {
   2857                             warn("process aborted abnormally", .{});
   2858                         } else {
   2859                             process.exit(1);
   2860                         }
   2861                     },
   2862                 }
   2863             },
   2864             .zig_test => {
   2865                 switch (term) {
   2866                     .Exited => |code| {
   2867                         if (code == 0) {
   2868                             if (!watch) return cleanExit();
   2869                         } else {
   2870                             const cmd = try argvCmd(arena, argv.items);
   2871                             fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd });
   2872                         }
   2873                     },
   2874                     else => {
   2875                         const cmd = try argvCmd(arena, argv.items);
   2876                         fatal("the following test command crashed:\n{s}", .{cmd});
   2877                     },
   2878                 }
   2879             },
   2880             else => unreachable,
   2881         }
   2882     }
   2883 }
   2884 
   2885 const AfterUpdateHook = union(enum) {
   2886     none,
   2887     print: []const u8,
   2888     update: []const u8,
   2889 };
   2890 
   2891 fn updateModule(gpa: Allocator, comp: *Compilation, hook: AfterUpdateHook) !void {
   2892     try comp.update();
   2893 
   2894     var errors = try comp.getAllErrorsAlloc();
   2895     defer errors.deinit(comp.gpa);
   2896 
   2897     if (errors.list.len != 0) {
   2898         const ttyconf: std.debug.TTY.Config = switch (comp.color) {
   2899             .auto => std.debug.detectTTYConfig(),
   2900             .on => .escape_codes,
   2901             .off => .no_color,
   2902         };
   2903         for (errors.list) |full_err_msg| {
   2904             full_err_msg.renderToStdErr(ttyconf);
   2905         }
   2906         const log_text = comp.getCompileLogOutput();
   2907         if (log_text.len != 0) {
   2908             std.debug.print("\nCompile Log Output:\n{s}", .{log_text});
   2909         }
   2910         return error.SemanticAnalyzeFail;
   2911     } else switch (hook) {
   2912         .none => {},
   2913         .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}),
   2914         .update => |full_path| {
   2915             const bin_sub_path = comp.bin_file.options.emit.?.sub_path;
   2916             const cwd = fs.cwd();
   2917             const cache_dir = comp.bin_file.options.emit.?.directory.handle;
   2918             _ = try cache_dir.updateFile(bin_sub_path, cwd, full_path, .{});
   2919 
   2920             // If a .pdb file is part of the expected output, we must also copy
   2921             // it into place here.
   2922             const is_coff = comp.bin_file.options.object_format == .coff;
   2923             const have_pdb = is_coff and !comp.bin_file.options.strip;
   2924             if (have_pdb) {
   2925                 // Replace `.out` or `.exe` with `.pdb` on both the source and destination
   2926                 const src_bin_ext = fs.path.extension(bin_sub_path);
   2927                 const dst_bin_ext = fs.path.extension(full_path);
   2928 
   2929                 const src_pdb_path = try std.fmt.allocPrint(gpa, "{s}.pdb", .{
   2930                     bin_sub_path[0 .. bin_sub_path.len - src_bin_ext.len],
   2931                 });
   2932                 defer gpa.free(src_pdb_path);
   2933 
   2934                 const dst_pdb_path = try std.fmt.allocPrint(gpa, "{s}.pdb", .{
   2935                     full_path[0 .. full_path.len - dst_bin_ext.len],
   2936                 });
   2937                 defer gpa.free(dst_pdb_path);
   2938 
   2939                 _ = try cache_dir.updateFile(src_pdb_path, cwd, dst_pdb_path, .{});
   2940             }
   2941         },
   2942     }
   2943 }
   2944 
   2945 fn freePkgTree(gpa: Allocator, pkg: *Package, free_parent: bool) void {
   2946     {
   2947         var it = pkg.table.valueIterator();
   2948         while (it.next()) |value| {
   2949             freePkgTree(gpa, value.*, true);
   2950         }
   2951     }
   2952     if (free_parent) {
   2953         pkg.destroy(gpa);
   2954     }
   2955 }
   2956 
   2957 fn cmdTranslateC(comp: *Compilation, arena: Allocator, enable_cache: bool) !void {
   2958     if (!build_options.have_llvm)
   2959         fatal("cannot translate-c: compiler built without LLVM extensions", .{});
   2960 
   2961     assert(comp.c_source_files.len == 1);
   2962     const c_source_file = comp.c_source_files[0];
   2963 
   2964     const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name});
   2965 
   2966     var man: Cache.Manifest = comp.obtainCObjectCacheManifest();
   2967     defer if (enable_cache) man.deinit();
   2968 
   2969     man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
   2970     man.hashCSource(c_source_file) catch |err| {
   2971         fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
   2972     };
   2973 
   2974     const digest = if (try man.hit()) man.final() else digest: {
   2975         var argv = std.ArrayList([]const u8).init(arena);
   2976         try argv.append(""); // argv[0] is program name, actual args start at [1]
   2977 
   2978         var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
   2979         defer zig_cache_tmp_dir.close();
   2980 
   2981         const ext = Compilation.classifyFileExt(c_source_file.src_path);
   2982         const out_dep_path: ?[]const u8 = blk: {
   2983             if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
   2984                 break :blk null;
   2985 
   2986             const c_src_basename = fs.path.basename(c_source_file.src_path);
   2987             const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename});
   2988             const out_dep_path = try comp.tmpFilePath(arena, dep_basename);
   2989             break :blk out_dep_path;
   2990         };
   2991 
   2992         try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path);
   2993         try argv.append(c_source_file.src_path);
   2994 
   2995         if (comp.verbose_cc) {
   2996             std.debug.print("clang ", .{});
   2997             Compilation.dump_argv(argv.items);
   2998         }
   2999 
   3000         // Convert to null terminated args.
   3001         const clang_args_len = argv.items.len + c_source_file.extra_flags.len;
   3002         const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, clang_args_len + 1);
   3003         new_argv_with_sentinel[clang_args_len] = null;
   3004         const new_argv = new_argv_with_sentinel[0..clang_args_len :null];
   3005         for (argv.items) |arg, i| {
   3006             new_argv[i] = try arena.dupeZ(u8, arg);
   3007         }
   3008         for (c_source_file.extra_flags) |arg, i| {
   3009             new_argv[argv.items.len + i] = try arena.dupeZ(u8, arg);
   3010         }
   3011 
   3012         const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
   3013         const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
   3014         var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
   3015         var tree = translate_c.translate(
   3016             comp.gpa,
   3017             new_argv.ptr,
   3018             new_argv.ptr + new_argv.len,
   3019             &clang_errors,
   3020             c_headers_dir_path_z,
   3021         ) catch |err| switch (err) {
   3022             error.OutOfMemory => return error.OutOfMemory,
   3023             error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}),
   3024             error.SemanticAnalyzeFail => {
   3025                 for (clang_errors) |clang_err| {
   3026                     std.debug.print("{s}:{d}:{d}: {s}\n", .{
   3027                         if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
   3028                         clang_err.line + 1,
   3029                         clang_err.column + 1,
   3030                         clang_err.msg_ptr[0..clang_err.msg_len],
   3031                     });
   3032                 }
   3033                 process.exit(1);
   3034             },
   3035         };
   3036         defer tree.deinit(comp.gpa);
   3037 
   3038         if (out_dep_path) |dep_file_path| {
   3039             const dep_basename = std.fs.path.basename(dep_file_path);
   3040             // Add the files depended on to the cache system.
   3041             try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
   3042             // Just to save disk space, we delete the file because it is never needed again.
   3043             zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| {
   3044                 warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) });
   3045             };
   3046         }
   3047 
   3048         const digest = man.final();
   3049         const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
   3050 
   3051         var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
   3052         defer o_dir.close();
   3053 
   3054         var zig_file = try o_dir.createFile(translated_zig_basename, .{});
   3055         defer zig_file.close();
   3056 
   3057         const formatted = try tree.render(comp.gpa);
   3058         defer comp.gpa.free(formatted);
   3059 
   3060         try zig_file.writeAll(formatted);
   3061 
   3062         man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{
   3063             @errorName(err),
   3064         });
   3065 
   3066         break :digest digest;
   3067     };
   3068 
   3069     if (enable_cache) {
   3070         const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{
   3071             "o", &digest, translated_zig_basename,
   3072         });
   3073         try io.getStdOut().writer().print("{s}\n", .{full_zig_path});
   3074         return cleanExit();
   3075     } else {
   3076         const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename });
   3077         const zig_file = comp.local_cache_directory.handle.openFile(out_zig_path, .{}) catch |err| {
   3078             fatal("unable to open cached translated zig file '{s}{s}{s}': {s}", .{ comp.local_cache_directory.path, fs.path.sep_str, out_zig_path, @errorName(err) });
   3079         };
   3080         defer zig_file.close();
   3081         try io.getStdOut().writeFileAll(zig_file, .{});
   3082         return cleanExit();
   3083     }
   3084 }
   3085 
   3086 pub const usage_libc =
   3087     \\Usage: zig libc
   3088     \\
   3089     \\    Detect the native libc installation and print the resulting
   3090     \\    paths to stdout. You can save this into a file and then edit
   3091     \\    the paths to create a cross compilation libc kit. Then you
   3092     \\    can pass `--libc [file]` for Zig to use it.
   3093     \\
   3094     \\Usage: zig libc [paths_file]
   3095     \\
   3096     \\    Parse a libc installation text file and validate it.
   3097     \\
   3098     \\Options:
   3099     \\    -h, --help             Print this help and exit
   3100     \\    -target [name]         <arch><sub>-<os>-<abi> see the targets command
   3101     \\
   3102 ;
   3103 
   3104 pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
   3105     var input_file: ?[]const u8 = null;
   3106     var target_arch_os_abi: []const u8 = "native";
   3107     {
   3108         var i: usize = 0;
   3109         while (i < args.len) : (i += 1) {
   3110             const arg = args[i];
   3111             if (mem.startsWith(u8, arg, "-")) {
   3112                 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
   3113                     const stdout = io.getStdOut().writer();
   3114                     try stdout.writeAll(usage_libc);
   3115                     return cleanExit();
   3116                 } else if (mem.eql(u8, arg, "-target")) {
   3117                     if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
   3118                     i += 1;
   3119                     target_arch_os_abi = args[i];
   3120                 } else {
   3121                     fatal("unrecognized parameter: '{s}'", .{arg});
   3122                 }
   3123             } else if (input_file != null) {
   3124                 fatal("unexpected extra parameter: '{s}'", .{arg});
   3125             } else {
   3126                 input_file = arg;
   3127             }
   3128         }
   3129     }
   3130 
   3131     const cross_target = try parseCrossTargetOrReportFatalError(gpa, .{
   3132         .arch_os_abi = target_arch_os_abi,
   3133     });
   3134 
   3135     if (input_file) |libc_file| {
   3136         var libc = LibCInstallation.parse(gpa, libc_file, cross_target) catch |err| {
   3137             fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
   3138         };
   3139         defer libc.deinit(gpa);
   3140     } else {
   3141         if (!cross_target.isNative()) {
   3142             fatal("unable to detect libc for non-native target", .{});
   3143         }
   3144 
   3145         var libc = LibCInstallation.findNative(.{
   3146             .allocator = gpa,
   3147             .verbose = true,
   3148         }) catch |err| {
   3149             fatal("unable to detect native libc: {s}", .{@errorName(err)});
   3150         };
   3151         defer libc.deinit(gpa);
   3152 
   3153         var bw = io.bufferedWriter(io.getStdOut().writer());
   3154         try libc.render(bw.writer());
   3155         try bw.flush();
   3156     }
   3157 }
   3158 
   3159 pub const usage_init =
   3160     \\Usage: zig init-exe
   3161     \\       zig init-lib
   3162     \\
   3163     \\   Initializes a `zig build` project in the current working
   3164     \\   directory.
   3165     \\
   3166     \\Options:
   3167     \\   -h, --help             Print this help and exit
   3168     \\
   3169     \\
   3170 ;
   3171 
   3172 pub fn cmdInit(
   3173     gpa: Allocator,
   3174     arena: Allocator,
   3175     args: []const []const u8,
   3176     output_mode: std.builtin.OutputMode,
   3177 ) !void {
   3178     _ = gpa;
   3179     {
   3180         var i: usize = 0;
   3181         while (i < args.len) : (i += 1) {
   3182             const arg = args[i];
   3183             if (mem.startsWith(u8, arg, "-")) {
   3184                 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
   3185                     try io.getStdOut().writeAll(usage_init);
   3186                     return cleanExit();
   3187                 } else {
   3188                     fatal("unrecognized parameter: '{s}'", .{arg});
   3189                 }
   3190             } else {
   3191                 fatal("unexpected extra parameter: '{s}'", .{arg});
   3192             }
   3193         }
   3194     }
   3195     const self_exe_path = try fs.selfExePathAlloc(arena);
   3196     var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
   3197         fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
   3198     };
   3199     defer zig_lib_directory.handle.close();
   3200 
   3201     const s = fs.path.sep_str;
   3202     const template_sub_path = switch (output_mode) {
   3203         .Obj => unreachable,
   3204         .Lib => "std" ++ s ++ "special" ++ s ++ "init-lib",
   3205         .Exe => "std" ++ s ++ "special" ++ s ++ "init-exe",
   3206     };
   3207     var template_dir = zig_lib_directory.handle.openDir(template_sub_path, .{}) catch |err| {
   3208         fatal("unable to open zig project template directory '{s}{s}{s}': {s}", .{ zig_lib_directory.path, s, template_sub_path, @errorName(err) });
   3209     };
   3210     defer template_dir.close();
   3211 
   3212     const cwd_path = try process.getCwdAlloc(arena);
   3213     const cwd_basename = fs.path.basename(cwd_path);
   3214 
   3215     const max_bytes = 10 * 1024 * 1024;
   3216     const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| {
   3217         fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)});
   3218     };
   3219     var modified_build_zig_contents = try std.ArrayList(u8).initCapacity(arena, build_zig_contents.len);
   3220     for (build_zig_contents) |c| {
   3221         if (c == '$') {
   3222             try modified_build_zig_contents.appendSlice(cwd_basename);
   3223         } else {
   3224             try modified_build_zig_contents.append(c);
   3225         }
   3226     }
   3227     const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| {
   3228         fatal("unable to read template file 'main.zig': {s}", .{@errorName(err)});
   3229     };
   3230     if (fs.cwd().access("build.zig", .{})) |_| {
   3231         fatal("existing build.zig file would be overwritten", .{});
   3232     } else |err| switch (err) {
   3233         error.FileNotFound => {},
   3234         else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}),
   3235     }
   3236     if (fs.cwd().access("src" ++ s ++ "main.zig", .{})) |_| {
   3237         fatal("existing src" ++ s ++ "main.zig file would be overwritten", .{});
   3238     } else |err| switch (err) {
   3239         error.FileNotFound => {},
   3240         else => fatal("unable to test existence of src" ++ s ++ "main.zig: {s}\n", .{@errorName(err)}),
   3241     }
   3242     var src_dir = try fs.cwd().makeOpenPath("src", .{});
   3243     defer src_dir.close();
   3244 
   3245     try src_dir.writeFile("main.zig", main_zig_contents);
   3246     try fs.cwd().writeFile("build.zig", modified_build_zig_contents.items);
   3247 
   3248     std.log.info("Created build.zig", .{});
   3249     std.log.info("Created src" ++ s ++ "main.zig", .{});
   3250 
   3251     switch (output_mode) {
   3252         .Lib => std.log.info("Next, try `zig build --help` or `zig build test`", .{}),
   3253         .Exe => std.log.info("Next, try `zig build --help` or `zig build run`", .{}),
   3254         .Obj => unreachable,
   3255     }
   3256 }
   3257 
   3258 pub const usage_build =
   3259     \\Usage: zig build [steps] [options]
   3260     \\
   3261     \\   Build a project from build.zig.
   3262     \\
   3263     \\Options:
   3264     \\   -h, --help             Print this help and exit
   3265     \\
   3266     \\
   3267 ;
   3268 
   3269 pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
   3270     var prominent_compile_errors: bool = false;
   3271 
   3272     // We want to release all the locks before executing the child process, so we make a nice
   3273     // big block here to ensure the cleanup gets run when we extract out our argv.
   3274     const child_argv = argv: {
   3275         const self_exe_path = try fs.selfExePathAlloc(arena);
   3276 
   3277         var build_file: ?[]const u8 = null;
   3278         var override_lib_dir: ?[]const u8 = null;
   3279         var override_global_cache_dir: ?[]const u8 = null;
   3280         var override_local_cache_dir: ?[]const u8 = null;
   3281         var child_argv = std.ArrayList([]const u8).init(arena);
   3282 
   3283         const argv_index_exe = child_argv.items.len;
   3284         _ = try child_argv.addOne();
   3285 
   3286         try child_argv.append(self_exe_path);
   3287 
   3288         const argv_index_build_file = child_argv.items.len;
   3289         _ = try child_argv.addOne();
   3290 
   3291         const argv_index_cache_dir = child_argv.items.len;
   3292         _ = try child_argv.addOne();
   3293 
   3294         const argv_index_global_cache_dir = child_argv.items.len;
   3295         _ = try child_argv.addOne();
   3296 
   3297         {
   3298             var i: usize = 0;
   3299             while (i < args.len) : (i += 1) {
   3300                 const arg = args[i];
   3301                 if (mem.startsWith(u8, arg, "-")) {
   3302                     if (mem.eql(u8, arg, "--build-file")) {
   3303                         if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
   3304                         i += 1;
   3305                         build_file = args[i];
   3306                         continue;
   3307                     } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
   3308                         if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
   3309                         i += 1;
   3310                         override_lib_dir = args[i];
   3311                         try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
   3312                         continue;
   3313                     } else if (mem.eql(u8, arg, "--cache-dir")) {
   3314                         if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
   3315                         i += 1;
   3316                         override_local_cache_dir = args[i];
   3317                         continue;
   3318                     } else if (mem.eql(u8, arg, "--global-cache-dir")) {
   3319                         if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
   3320                         i += 1;
   3321                         override_global_cache_dir = args[i];
   3322                         continue;
   3323                     } else if (mem.eql(u8, arg, "--prominent-compile-errors")) {
   3324                         prominent_compile_errors = true;
   3325                     }
   3326                 }
   3327                 try child_argv.append(arg);
   3328             }
   3329         }
   3330 
   3331         var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
   3332             .path = lib_dir,
   3333             .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
   3334                 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
   3335             },
   3336         } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
   3337             fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) });
   3338         };
   3339         defer zig_lib_directory.handle.close();
   3340 
   3341         const std_special = "std" ++ fs.path.sep_str ++ "special";
   3342         const special_dir_path = try zig_lib_directory.join(arena, &[_][]const u8{std_special});
   3343 
   3344         var main_pkg: Package = .{
   3345             .root_src_directory = .{
   3346                 .path = special_dir_path,
   3347                 .handle = zig_lib_directory.handle.openDir(std_special, .{}) catch |err| {
   3348                     fatal("unable to open directory '{s}{s}{s}': {s}", .{ override_lib_dir, fs.path.sep_str, std_special, @errorName(err) });
   3349                 },
   3350             },
   3351             .root_src_path = "build_runner.zig",
   3352         };
   3353         defer main_pkg.root_src_directory.handle.close();
   3354 
   3355         var cleanup_build_dir: ?fs.Dir = null;
   3356         defer if (cleanup_build_dir) |*dir| dir.close();
   3357 
   3358         const cwd_path = try process.getCwdAlloc(arena);
   3359         const build_zig_basename = if (build_file) |bf| fs.path.basename(bf) else "build.zig";
   3360         const build_directory: Compilation.Directory = blk: {
   3361             if (build_file) |bf| {
   3362                 if (fs.path.dirname(bf)) |dirname| {
   3363                     const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
   3364                         fatal("unable to open directory to build file from argument 'build-file', '{s}': {s}", .{ dirname, @errorName(err) });
   3365                     };
   3366                     cleanup_build_dir = dir;
   3367                     break :blk .{ .path = dirname, .handle = dir };
   3368                 }
   3369 
   3370                 break :blk .{ .path = null, .handle = fs.cwd() };
   3371             }
   3372             // Search up parent directories until we find build.zig.
   3373             var dirname: []const u8 = cwd_path;
   3374             while (true) {
   3375                 const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename });
   3376                 if (fs.cwd().access(joined_path, .{})) |_| {
   3377                     const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
   3378                         fatal("unable to open directory while searching for build.zig file, '{s}': {s}", .{ dirname, @errorName(err) });
   3379                     };
   3380                     break :blk .{ .path = dirname, .handle = dir };
   3381                 } else |err| switch (err) {
   3382                     error.FileNotFound => {
   3383                         dirname = fs.path.dirname(dirname) orelse {
   3384                             std.log.info("{s}", .{
   3385                                 \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,
   3386                                 \\or see `zig --help` for more options.
   3387                             });
   3388                             fatal("No 'build.zig' file found, in the current directory or any parent directories.", .{});
   3389                         };
   3390                         continue;
   3391                     },
   3392                     else => |e| return e,
   3393                 }
   3394             }
   3395         };
   3396         child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path;
   3397 
   3398         var build_pkg: Package = .{
   3399             .root_src_directory = build_directory,
   3400             .root_src_path = build_zig_basename,
   3401         };
   3402         try main_pkg.addAndAdopt(arena, "@build", &build_pkg);
   3403 
   3404         var global_cache_directory: Compilation.Directory = l: {
   3405             const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
   3406             break :l .{
   3407                 .handle = try fs.cwd().makeOpenPath(p, .{}),
   3408                 .path = p,
   3409             };
   3410         };
   3411         defer global_cache_directory.handle.close();
   3412 
   3413         child_argv.items[argv_index_global_cache_dir] = global_cache_directory.path orelse cwd_path;
   3414 
   3415         var local_cache_directory: Compilation.Directory = l: {
   3416             if (override_local_cache_dir) |local_cache_dir_path| {
   3417                 break :l .{
   3418                     .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}),
   3419                     .path = local_cache_dir_path,
   3420                 };
   3421             }
   3422             const cache_dir_path = try build_directory.join(arena, &[_][]const u8{"zig-cache"});
   3423             break :l .{
   3424                 .handle = try build_directory.handle.makeOpenPath("zig-cache", .{}),
   3425                 .path = cache_dir_path,
   3426             };
   3427         };
   3428         defer local_cache_directory.handle.close();
   3429 
   3430         child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path;
   3431 
   3432         gimmeMoreOfThoseSweetSweetFileDescriptors();
   3433 
   3434         const cross_target: std.zig.CrossTarget = .{};
   3435         const target_info = try detectNativeTargetInfo(gpa, cross_target);
   3436 
   3437         const exe_basename = try std.zig.binNameAlloc(arena, .{
   3438             .root_name = "build",
   3439             .target = target_info.target,
   3440             .output_mode = .Exe,
   3441         });
   3442         const emit_bin: Compilation.EmitLoc = .{
   3443             .directory = null, // Use the local zig-cache.
   3444             .basename = exe_basename,
   3445         };
   3446         var thread_pool: ThreadPool = undefined;
   3447         try thread_pool.init(gpa);
   3448         defer thread_pool.deinit();
   3449         const comp = Compilation.create(gpa, .{
   3450             .zig_lib_directory = zig_lib_directory,
   3451             .local_cache_directory = local_cache_directory,
   3452             .global_cache_directory = global_cache_directory,
   3453             .root_name = "build",
   3454             .target = target_info.target,
   3455             .is_native_os = cross_target.isNativeOs(),
   3456             .is_native_abi = cross_target.isNativeAbi(),
   3457             .dynamic_linker = target_info.dynamic_linker.get(),
   3458             .output_mode = .Exe,
   3459             .main_pkg = &main_pkg,
   3460             .emit_bin = emit_bin,
   3461             .emit_h = null,
   3462             .optimize_mode = .Debug,
   3463             .self_exe_path = self_exe_path,
   3464             .thread_pool = &thread_pool,
   3465         }) catch |err| {
   3466             fatal("unable to create compilation: {s}", .{@errorName(err)});
   3467         };
   3468         defer comp.destroy();
   3469 
   3470         updateModule(gpa, comp, .none) catch |err| switch (err) {
   3471             error.SemanticAnalyzeFail => process.exit(1),
   3472             else => |e| return e,
   3473         };
   3474         try comp.makeBinFileExecutable();
   3475 
   3476         child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join(
   3477             arena,
   3478             &[_][]const u8{exe_basename},
   3479         );
   3480 
   3481         break :argv child_argv.items;
   3482     };
   3483     const child = try std.ChildProcess.init(child_argv, gpa);
   3484     defer child.deinit();
   3485 
   3486     child.stdin_behavior = .Inherit;
   3487     child.stdout_behavior = .Inherit;
   3488     child.stderr_behavior = .Inherit;
   3489 
   3490     const term = try child.spawnAndWait();
   3491     switch (term) {
   3492         .Exited => |code| {
   3493             if (code == 0) return cleanExit();
   3494 
   3495             if (prominent_compile_errors) {
   3496                 fatal("the build command failed with exit code {d}", .{code});
   3497             } else {
   3498                 const cmd = try argvCmd(arena, child_argv);
   3499                 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
   3500             }
   3501         },
   3502         else => {
   3503             const cmd = try argvCmd(arena, child_argv);
   3504             fatal("the following build command crashed:\n{s}", .{cmd});
   3505         },
   3506     }
   3507 }
   3508 
   3509 fn argvCmd(allocator: Allocator, argv: []const []const u8) ![]u8 {
   3510     var cmd = std.ArrayList(u8).init(allocator);
   3511     defer cmd.deinit();
   3512     for (argv[0 .. argv.len - 1]) |arg| {
   3513         try cmd.appendSlice(arg);
   3514         try cmd.append(' ');
   3515     }
   3516     try cmd.appendSlice(argv[argv.len - 1]);
   3517     return cmd.toOwnedSlice();
   3518 }
   3519 
   3520 fn readSourceFileToEndAlloc(
   3521     allocator: mem.Allocator,
   3522     input: *const fs.File,
   3523     size_hint: ?usize,
   3524 ) ![:0]u8 {
   3525     const source_code = input.readToEndAllocOptions(
   3526         allocator,
   3527         max_src_size,
   3528         size_hint,
   3529         @alignOf(u16),
   3530         0,
   3531     ) catch |err| switch (err) {
   3532         error.ConnectionResetByPeer => unreachable,
   3533         error.ConnectionTimedOut => unreachable,
   3534         error.NotOpenForReading => unreachable,
   3535         else => |e| return e,
   3536     };
   3537     errdefer allocator.free(source_code);
   3538 
   3539     // Detect unsupported file types with their Byte Order Mark
   3540     const unsupported_boms = [_][]const u8{
   3541         "\xff\xfe\x00\x00", // UTF-32 little endian
   3542         "\xfe\xff\x00\x00", // UTF-32 big endian
   3543         "\xfe\xff", // UTF-16 big endian
   3544     };
   3545     for (unsupported_boms) |bom| {
   3546         if (mem.startsWith(u8, source_code, bom)) {
   3547             return error.UnsupportedEncoding;
   3548         }
   3549     }
   3550 
   3551     // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
   3552     if (mem.startsWith(u8, source_code, "\xff\xfe")) {
   3553         const source_code_utf16_le = mem.bytesAsSlice(u16, source_code);
   3554         const source_code_utf8 = std.unicode.utf16leToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) {
   3555             error.DanglingSurrogateHalf => error.UnsupportedEncoding,
   3556             error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
   3557             error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
   3558             else => |e| return e,
   3559         };
   3560 
   3561         allocator.free(source_code);
   3562         return source_code_utf8;
   3563     }
   3564 
   3565     return source_code;
   3566 }
   3567 
   3568 pub const usage_fmt =
   3569     \\Usage: zig fmt [file]...
   3570     \\
   3571     \\   Formats the input files and modifies them in-place.
   3572     \\   Arguments can be files or directories, which are searched
   3573     \\   recursively.
   3574     \\
   3575     \\Options:
   3576     \\   -h, --help             Print this help and exit
   3577     \\   --color [auto|off|on]  Enable or disable colored error messages
   3578     \\   --stdin                Format code from stdin; output to stdout
   3579     \\   --check                List non-conforming files and exit with an error
   3580     \\                          if the list is non-empty
   3581     \\   --ast-check            Run zig ast-check on every file
   3582     \\
   3583     \\
   3584 ;
   3585 
   3586 const Fmt = struct {
   3587     seen: SeenMap,
   3588     any_error: bool,
   3589     check_ast: bool,
   3590     color: Color,
   3591     gpa: Allocator,
   3592     arena: Allocator,
   3593     out_buffer: std.ArrayList(u8),
   3594 
   3595     const SeenMap = std.AutoHashMap(fs.File.INode, void);
   3596 };
   3597 
   3598 pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
   3599     var color: Color = .auto;
   3600     var stdin_flag: bool = false;
   3601     var check_flag: bool = false;
   3602     var check_ast_flag: bool = false;
   3603     var input_files = ArrayList([]const u8).init(gpa);
   3604     defer input_files.deinit();
   3605 
   3606     {
   3607         var i: usize = 0;
   3608         while (i < args.len) : (i += 1) {
   3609             const arg = args[i];
   3610             if (mem.startsWith(u8, arg, "-")) {
   3611                 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
   3612                     const stdout = io.getStdOut().writer();
   3613                     try stdout.writeAll(usage_fmt);
   3614                     return cleanExit();
   3615                 } else if (mem.eql(u8, arg, "--color")) {
   3616                     if (i + 1 >= args.len) {
   3617                         fatal("expected [auto|on|off] after --color", .{});
   3618                     }
   3619                     i += 1;
   3620                     const next_arg = args[i];
   3621                     color = std.meta.stringToEnum(Color, next_arg) orelse {
   3622                         fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
   3623                     };
   3624                 } else if (mem.eql(u8, arg, "--stdin")) {
   3625                     stdin_flag = true;
   3626                 } else if (mem.eql(u8, arg, "--check")) {
   3627                     check_flag = true;
   3628                 } else if (mem.eql(u8, arg, "--ast-check")) {
   3629                     check_ast_flag = true;
   3630                 } else {
   3631                     fatal("unrecognized parameter: '{s}'", .{arg});
   3632                 }
   3633             } else {
   3634                 try input_files.append(arg);
   3635             }
   3636         }
   3637     }
   3638 
   3639     if (stdin_flag) {
   3640         if (input_files.items.len != 0) {
   3641             fatal("cannot use --stdin with positional arguments", .{});
   3642         }
   3643 
   3644         const stdin = io.getStdIn();
   3645         const source_code = readSourceFileToEndAlloc(gpa, &stdin, null) catch |err| {
   3646             fatal("unable to read stdin: {s}", .{err});
   3647         };
   3648         defer gpa.free(source_code);
   3649 
   3650         var tree = std.zig.parse(gpa, source_code) catch |err| {
   3651             fatal("error parsing stdin: {s}", .{err});
   3652         };
   3653         defer tree.deinit(gpa);
   3654 
   3655         for (tree.errors) |parse_error| {
   3656             try printErrMsgToStdErr(gpa, arena, parse_error, tree, "<stdin>", color);
   3657         }
   3658         var has_ast_error = false;
   3659         if (check_ast_flag) {
   3660             const Module = @import("Module.zig");
   3661             const AstGen = @import("AstGen.zig");
   3662 
   3663             var file: Module.File = .{
   3664                 .status = .never_loaded,
   3665                 .source_loaded = true,
   3666                 .zir_loaded = false,
   3667                 .sub_file_path = "<stdin>",
   3668                 .source = source_code,
   3669                 .stat_size = undefined,
   3670                 .stat_inode = undefined,
   3671                 .stat_mtime = undefined,
   3672                 .tree = tree,
   3673                 .tree_loaded = true,
   3674                 .zir = undefined,
   3675                 .pkg = undefined,
   3676                 .root_decl = null,
   3677             };
   3678 
   3679             file.pkg = try Package.create(gpa, null, file.sub_file_path);
   3680             defer file.pkg.destroy(gpa);
   3681 
   3682             file.zir = try AstGen.generate(gpa, file.tree);
   3683             file.zir_loaded = true;
   3684             defer file.zir.deinit(gpa);
   3685 
   3686             if (file.zir.hasCompileErrors()) {
   3687                 var arena_instance = std.heap.ArenaAllocator.init(gpa);
   3688                 defer arena_instance.deinit();
   3689                 var errors = std.ArrayList(Compilation.AllErrors.Message).init(gpa);
   3690                 defer errors.deinit();
   3691 
   3692                 try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);
   3693                 const ttyconf: std.debug.TTY.Config = switch (color) {
   3694                     .auto => std.debug.detectTTYConfig(),
   3695                     .on => .escape_codes,
   3696                     .off => .no_color,
   3697                 };
   3698                 for (errors.items) |full_err_msg| {
   3699                     full_err_msg.renderToStdErr(ttyconf);
   3700                 }
   3701                 has_ast_error = true;
   3702             }
   3703         }
   3704         if (tree.errors.len != 0 or has_ast_error) {
   3705             process.exit(1);
   3706         }
   3707         const formatted = try tree.render(gpa);
   3708         defer gpa.free(formatted);
   3709 
   3710         if (check_flag) {
   3711             const code: u8 = @boolToInt(mem.eql(u8, formatted, source_code));
   3712             process.exit(code);
   3713         }
   3714 
   3715         return io.getStdOut().writeAll(formatted);
   3716     }
   3717 
   3718     if (input_files.items.len == 0) {
   3719         fatal("expected at least one source file argument", .{});
   3720     }
   3721 
   3722     var fmt = Fmt{
   3723         .gpa = gpa,
   3724         .arena = arena,
   3725         .seen = Fmt.SeenMap.init(gpa),
   3726         .any_error = false,
   3727         .check_ast = check_ast_flag,
   3728         .color = color,
   3729         .out_buffer = std.ArrayList(u8).init(gpa),
   3730     };
   3731     defer fmt.seen.deinit();
   3732     defer fmt.out_buffer.deinit();
   3733 
   3734     for (input_files.items) |file_path| {
   3735         try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path);
   3736     }
   3737     if (fmt.any_error) {
   3738         process.exit(1);
   3739     }
   3740 }
   3741 
   3742 const FmtError = error{
   3743     SystemResources,
   3744     OperationAborted,
   3745     IoPending,
   3746     BrokenPipe,
   3747     Unexpected,
   3748     WouldBlock,
   3749     FileClosed,
   3750     DestinationAddressRequired,
   3751     DiskQuota,
   3752     FileTooBig,
   3753     InputOutput,
   3754     NoSpaceLeft,
   3755     AccessDenied,
   3756     OutOfMemory,
   3757     RenameAcrossMountPoints,
   3758     ReadOnlyFileSystem,
   3759     LinkQuotaExceeded,
   3760     FileBusy,
   3761     EndOfStream,
   3762     Unseekable,
   3763     NotOpenForWriting,
   3764     UnsupportedEncoding,
   3765     ConnectionResetByPeer,
   3766 } || fs.File.OpenError;
   3767 
   3768 fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
   3769     fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
   3770         error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
   3771         else => {
   3772             warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) });
   3773             fmt.any_error = true;
   3774             return;
   3775         },
   3776     };
   3777 }
   3778 
   3779 fn fmtPathDir(
   3780     fmt: *Fmt,
   3781     file_path: []const u8,
   3782     check_mode: bool,
   3783     parent_dir: fs.Dir,
   3784     parent_sub_path: []const u8,
   3785 ) FmtError!void {
   3786     var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
   3787     defer dir.close();
   3788 
   3789     const stat = try dir.stat();
   3790     if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
   3791 
   3792     var dir_it = dir.iterate();
   3793     while (try dir_it.next()) |entry| {
   3794         const is_dir = entry.kind == .Directory;
   3795 
   3796         if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;
   3797 
   3798         if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
   3799             const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
   3800             defer fmt.gpa.free(full_path);
   3801 
   3802             if (is_dir) {
   3803                 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
   3804             } else {
   3805                 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
   3806                     warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
   3807                     fmt.any_error = true;
   3808                     return;
   3809                 };
   3810             }
   3811         }
   3812     }
   3813 }
   3814 
   3815 fn fmtPathFile(
   3816     fmt: *Fmt,
   3817     file_path: []const u8,
   3818     check_mode: bool,
   3819     dir: fs.Dir,
   3820     sub_path: []const u8,
   3821 ) FmtError!void {
   3822     const source_file = try dir.openFile(sub_path, .{});
   3823     var file_closed = false;
   3824     errdefer if (!file_closed) source_file.close();
   3825 
   3826     const stat = try source_file.stat();
   3827 
   3828     if (stat.kind == .Directory)
   3829         return error.IsDir;
   3830 
   3831     const source_code = try readSourceFileToEndAlloc(
   3832         fmt.gpa,
   3833         &source_file,
   3834         std.math.cast(usize, stat.size) catch return error.FileTooBig,
   3835     );
   3836     defer fmt.gpa.free(source_code);
   3837 
   3838     source_file.close();
   3839     file_closed = true;
   3840 
   3841     // Add to set after no longer possible to get error.IsDir.
   3842     if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
   3843 
   3844     var tree = try std.zig.parse(fmt.gpa, source_code);
   3845     defer tree.deinit(fmt.gpa);
   3846 
   3847     for (tree.errors) |parse_error| {
   3848         try printErrMsgToStdErr(fmt.gpa, fmt.arena, parse_error, tree, file_path, fmt.color);
   3849     }
   3850     if (tree.errors.len != 0) {
   3851         fmt.any_error = true;
   3852         return;
   3853     }
   3854 
   3855     if (fmt.check_ast) {
   3856         const Module = @import("Module.zig");
   3857         const AstGen = @import("AstGen.zig");
   3858 
   3859         var file: Module.File = .{
   3860             .status = .never_loaded,
   3861             .source_loaded = true,
   3862             .zir_loaded = false,
   3863             .sub_file_path = file_path,
   3864             .source = source_code,
   3865             .stat_size = stat.size,
   3866             .stat_inode = stat.inode,
   3867             .stat_mtime = stat.mtime,
   3868             .tree = tree,
   3869             .tree_loaded = true,
   3870             .zir = undefined,
   3871             .pkg = undefined,
   3872             .root_decl = null,
   3873         };
   3874 
   3875         file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path);
   3876         defer file.pkg.destroy(fmt.gpa);
   3877 
   3878         if (stat.size > max_src_size)
   3879             return error.FileTooBig;
   3880 
   3881         file.zir = try AstGen.generate(fmt.gpa, file.tree);
   3882         file.zir_loaded = true;
   3883         defer file.zir.deinit(fmt.gpa);
   3884 
   3885         if (file.zir.hasCompileErrors()) {
   3886             var arena_instance = std.heap.ArenaAllocator.init(fmt.gpa);
   3887             defer arena_instance.deinit();
   3888             var errors = std.ArrayList(Compilation.AllErrors.Message).init(fmt.gpa);
   3889             defer errors.deinit();
   3890 
   3891             try Compilation.AllErrors.addZir(arena_instance.allocator(), &errors, &file);
   3892             const ttyconf: std.debug.TTY.Config = switch (fmt.color) {
   3893                 .auto => std.debug.detectTTYConfig(),
   3894                 .on => .escape_codes,
   3895                 .off => .no_color,
   3896             };
   3897             for (errors.items) |full_err_msg| {
   3898                 full_err_msg.renderToStdErr(ttyconf);
   3899             }
   3900             fmt.any_error = true;
   3901         }
   3902     }
   3903 
   3904     // As a heuristic, we make enough capacity for the same as the input source.
   3905     fmt.out_buffer.shrinkRetainingCapacity(0);
   3906     try fmt.out_buffer.ensureTotalCapacity(source_code.len);
   3907 
   3908     try tree.renderToArrayList(&fmt.out_buffer);
   3909     if (mem.eql(u8, fmt.out_buffer.items, source_code))
   3910         return;
   3911 
   3912     if (check_mode) {
   3913         const stdout = io.getStdOut().writer();
   3914         try stdout.print("{s}\n", .{file_path});
   3915         fmt.any_error = true;
   3916     } else {
   3917         var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
   3918         defer af.deinit();
   3919 
   3920         try af.file.writeAll(fmt.out_buffer.items);
   3921         try af.finish();
   3922         const stdout = io.getStdOut().writer();
   3923         try stdout.print("{s}\n", .{file_path});
   3924     }
   3925 }
   3926 
   3927 fn printErrMsgToStdErr(
   3928     gpa: mem.Allocator,
   3929     arena: mem.Allocator,
   3930     parse_error: Ast.Error,
   3931     tree: Ast,
   3932     path: []const u8,
   3933     color: Color,
   3934 ) !void {
   3935     const lok_token = parse_error.token;
   3936     const token_tags = tree.tokens.items(.tag);
   3937     const start_loc = tree.tokenLocation(0, lok_token);
   3938     const source_line = tree.source[start_loc.line_start..start_loc.line_end];
   3939 
   3940     var text_buf = std.ArrayList(u8).init(gpa);
   3941     defer text_buf.deinit();
   3942     const writer = text_buf.writer();
   3943     try tree.renderError(parse_error, writer);
   3944     const text = text_buf.items;
   3945 
   3946     var notes_buffer: [1]Compilation.AllErrors.Message = undefined;
   3947     var notes_len: usize = 0;
   3948 
   3949     if (token_tags[parse_error.token] == .invalid) {
   3950         const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token).len);
   3951         const byte_offset = @intCast(u32, start_loc.line_start) + bad_off;
   3952         notes_buffer[notes_len] = .{
   3953             .src = .{
   3954                 .src_path = path,
   3955                 .msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{
   3956                     std.zig.fmtEscapes(tree.source[byte_offset..][0..1]),
   3957                 }),
   3958                 .byte_offset = byte_offset,
   3959                 .line = @intCast(u32, start_loc.line),
   3960                 .column = @intCast(u32, start_loc.column) + bad_off,
   3961                 .source_line = source_line,
   3962             },
   3963         };
   3964         notes_len += 1;
   3965     }
   3966 
   3967     const message: Compilation.AllErrors.Message = .{
   3968         .src = .{
   3969             .src_path = path,
   3970             .msg = text,
   3971             .byte_offset = @intCast(u32, start_loc.line_start),
   3972             .line = @intCast(u32, start_loc.line),
   3973             .column = @intCast(u32, start_loc.column),
   3974             .source_line = source_line,
   3975             .notes = notes_buffer[0..notes_len],
   3976         },
   3977     };
   3978 
   3979     const ttyconf: std.debug.TTY.Config = switch (color) {
   3980         .auto => std.debug.detectTTYConfig(),
   3981         .on => .escape_codes,
   3982         .off => .no_color,
   3983     };
   3984 
   3985     message.renderToStdErr(ttyconf);
   3986 }
   3987 
   3988 pub const info_zen =
   3989     \\
   3990     \\ * Communicate intent precisely.
   3991     \\ * Edge cases matter.
   3992     \\ * Favor reading code over writing code.
   3993     \\ * Only one obvious way to do things.
   3994     \\ * Runtime crashes are better than bugs.
   3995     \\ * Compile errors are better than runtime crashes.
   3996     \\ * Incremental improvements.
   3997     \\ * Avoid local maximums.
   3998     \\ * Reduce the amount one must remember.
   3999     \\ * Focus on code rather than style.
   4000     \\ * Resource allocation may fail; resource deallocation must succeed.
   4001     \\ * Memory is a resource.
   4002     \\ * Together we serve the users.
   4003     \\
   4004     \\
   4005 ;
   4006 
   4007 extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
   4008 extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
   4009 
   4010 /// TODO https://github.com/ziglang/zig/issues/3257
   4011 fn punt_to_clang(arena: Allocator, args: []const []const u8) error{OutOfMemory} {
   4012     if (!build_options.have_llvm)
   4013         fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{});
   4014     // Convert the args to the format Clang expects.
   4015     const argv = try arena.alloc(?[*:0]u8, args.len + 1);
   4016     for (args) |arg, i| {
   4017         argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
   4018     }
   4019     argv[args.len] = null;
   4020     const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr);
   4021     process.exit(@bitCast(u8, @truncate(i8, exit_code)));
   4022 }
   4023 
   4024 /// TODO https://github.com/ziglang/zig/issues/3257
   4025 fn punt_to_llvm_ar(arena: Allocator, args: []const []const u8) error{OutOfMemory} {
   4026     if (!build_options.have_llvm)
   4027         fatal("`zig ar`, `zig dlltool`, `zig ranlib', and `zig lib` unavailable: compiler built without LLVM extensions", .{});
   4028 
   4029     // Convert the args to the format llvm-ar expects.
   4030     // We subtract 1 to shave off the zig binary from args[0].
   4031     const argv = try arena.allocSentinel(?[*:0]u8, args.len - 1, null);
   4032     for (args[1..]) |arg, i| {
   4033         // TODO If there was an argsAllocZ we could avoid this allocation.
   4034         argv[i] = try arena.dupeZ(u8, arg);
   4035     }
   4036     const argc = @intCast(c_int, argv.len);
   4037     const exit_code = ZigLlvmAr_main(argc, argv.ptr);
   4038     process.exit(@bitCast(u8, @truncate(i8, exit_code)));
   4039 }
   4040 
   4041 /// The first argument determines which backend is invoked. The options are:
   4042 /// * `ld.lld` - ELF
   4043 /// * `lld-link` - COFF
   4044 /// * `wasm-ld` - WebAssembly
   4045 /// TODO https://github.com/ziglang/zig/issues/3257
   4046 pub fn punt_to_lld(arena: Allocator, args: []const []const u8) error{OutOfMemory} {
   4047     if (!build_options.have_llvm)
   4048         fatal("`zig {s}` unavailable: compiler built without LLVM extensions", .{args[0]});
   4049     // Convert the args to the format LLD expects.
   4050     // We subtract 1 to shave off the zig binary from args[0].
   4051     const argv = try arena.allocSentinel(?[*:0]const u8, args.len - 1, null);
   4052     for (args[1..]) |arg, i| {
   4053         argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation.
   4054     }
   4055     const exit_code = rc: {
   4056         const llvm = @import("codegen/llvm/bindings.zig");
   4057         const argc = @intCast(c_int, argv.len);
   4058         if (mem.eql(u8, args[1], "ld.lld")) {
   4059             break :rc llvm.LinkELF(argc, argv.ptr, true);
   4060         } else if (mem.eql(u8, args[1], "lld-link")) {
   4061             break :rc llvm.LinkCOFF(argc, argv.ptr, true);
   4062         } else if (mem.eql(u8, args[1], "wasm-ld")) {
   4063             break :rc llvm.LinkWasm(argc, argv.ptr, true);
   4064         } else {
   4065             unreachable;
   4066         }
   4067     };
   4068     process.exit(@bitCast(u8, @truncate(i8, exit_code)));
   4069 }
   4070 
   4071 const clang_args = @import("clang_options.zig").list;
   4072 
   4073 pub const ClangArgIterator = struct {
   4074     has_next: bool,
   4075     zig_equivalent: ZigEquivalent,
   4076     only_arg: []const u8,
   4077     second_arg: []const u8,
   4078     other_args: []const []const u8,
   4079     argv: []const []const u8,
   4080     next_index: usize,
   4081     root_args: ?*Args,
   4082     allocator: Allocator,
   4083 
   4084     pub const ZigEquivalent = enum {
   4085         target,
   4086         o,
   4087         c,
   4088         m,
   4089         other,
   4090         positional,
   4091         l,
   4092         ignore,
   4093         driver_punt,
   4094         pic,
   4095         no_pic,
   4096         pie,
   4097         no_pie,
   4098         lto,
   4099         no_lto,
   4100         unwind_tables,
   4101         no_unwind_tables,
   4102         nostdlib,
   4103         nostdlib_cpp,
   4104         shared,
   4105         rdynamic,
   4106         wl,
   4107         preprocess_only,
   4108         asm_only,
   4109         optimize,
   4110         debug,
   4111         sanitize,
   4112         linker_script,
   4113         dry_run,
   4114         verbose,
   4115         for_linker,
   4116         linker_input_z,
   4117         lib_dir,
   4118         mcpu,
   4119         dep_file,
   4120         dep_file_mm,
   4121         framework_dir,
   4122         framework,
   4123         nostdlibinc,
   4124         red_zone,
   4125         no_red_zone,
   4126         omit_frame_pointer,
   4127         no_omit_frame_pointer,
   4128         function_sections,
   4129         no_function_sections,
   4130         color_diagnostics,
   4131         no_color_diagnostics,
   4132         strip,
   4133         exec_model,
   4134         emit_llvm,
   4135     };
   4136 
   4137     const Args = struct {
   4138         next_index: usize,
   4139         argv: []const []const u8,
   4140     };
   4141 
   4142     fn init(allocator: Allocator, argv: []const []const u8) ClangArgIterator {
   4143         return .{
   4144             .next_index = 2, // `zig cc foo` this points to `foo`
   4145             .has_next = argv.len > 2,
   4146             .zig_equivalent = undefined,
   4147             .only_arg = undefined,
   4148             .second_arg = undefined,
   4149             .other_args = undefined,
   4150             .argv = argv,
   4151             .root_args = null,
   4152             .allocator = allocator,
   4153         };
   4154     }
   4155 
   4156     fn next(self: *ClangArgIterator) !void {
   4157         assert(self.has_next);
   4158         assert(self.next_index < self.argv.len);
   4159         // In this state we know that the parameter we are looking at is a root parameter
   4160         // rather than an argument to a parameter.
   4161         // We adjust the len below when necessary.
   4162         self.other_args = (self.argv.ptr + self.next_index)[0..1];
   4163         var arg = mem.span(self.argv[self.next_index]);
   4164         self.incrementArgIndex();
   4165 
   4166         if (mem.startsWith(u8, arg, "@")) {
   4167             if (self.root_args != null) return error.NestedResponseFile;
   4168 
   4169             // This is a "compiler response file". We must parse the file and treat its
   4170             // contents as command line parameters.
   4171             const allocator = self.allocator;
   4172             const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit
   4173             const resp_file_path = arg[1..];
   4174             const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| {
   4175                 fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) });
   4176             };
   4177             defer allocator.free(resp_contents);
   4178             // TODO is there a specification for this file format? Let's find it and make this parsing more robust
   4179             // at the very least I'm guessing this needs to handle quotes and `#` comments.
   4180             var it = mem.tokenize(u8, resp_contents, " \t\r\n");
   4181             var resp_arg_list = std.ArrayList([]const u8).init(allocator);
   4182             defer resp_arg_list.deinit();
   4183             {
   4184                 errdefer {
   4185                     for (resp_arg_list.items) |item| {
   4186                         allocator.free(mem.span(item));
   4187                     }
   4188                 }
   4189                 while (it.next()) |token| {
   4190                     const dupe_token = try allocator.dupeZ(u8, token);
   4191                     errdefer allocator.free(dupe_token);
   4192                     try resp_arg_list.append(dupe_token);
   4193                 }
   4194                 const args = try allocator.create(Args);
   4195                 errdefer allocator.destroy(args);
   4196                 args.* = .{
   4197                     .next_index = self.next_index,
   4198                     .argv = self.argv,
   4199                 };
   4200                 self.root_args = args;
   4201             }
   4202             const resp_arg_slice = resp_arg_list.toOwnedSlice();
   4203             self.next_index = 0;
   4204             self.argv = resp_arg_slice;
   4205 
   4206             if (resp_arg_slice.len == 0) {
   4207                 self.resolveRespFileArgs();
   4208                 return;
   4209             }
   4210 
   4211             self.has_next = true;
   4212             self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary.
   4213             arg = mem.span(self.argv[self.next_index]);
   4214             self.incrementArgIndex();
   4215         }
   4216         if (mem.eql(u8, arg, "-") or !mem.startsWith(u8, arg, "-")) {
   4217             self.zig_equivalent = .positional;
   4218             self.only_arg = arg;
   4219             return;
   4220         }
   4221 
   4222         find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
   4223             .flag => {
   4224                 const prefix_len = clang_arg.matchEql(arg);
   4225                 if (prefix_len > 0) {
   4226                     self.zig_equivalent = clang_arg.zig_equivalent;
   4227                     self.only_arg = arg[prefix_len..];
   4228 
   4229                     break :find_clang_arg;
   4230                 }
   4231             },
   4232             .joined, .comma_joined => {
   4233                 // joined example: --target=foo
   4234                 // comma_joined example: -Wl,-soname,libsoundio.so.2
   4235                 const prefix_len = clang_arg.matchStartsWith(arg);
   4236                 if (prefix_len != 0) {
   4237                     self.zig_equivalent = clang_arg.zig_equivalent;
   4238                     self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part.
   4239 
   4240                     break :find_clang_arg;
   4241                 }
   4242             },
   4243             .joined_or_separate => {
   4244                 // Examples: `-lfoo`, `-l foo`
   4245                 const prefix_len = clang_arg.matchStartsWith(arg);
   4246                 if (prefix_len == arg.len) {
   4247                     if (self.next_index >= self.argv.len) {
   4248                         fatal("Expected parameter after '{s}'", .{arg});
   4249                     }
   4250                     self.only_arg = self.argv[self.next_index];
   4251                     self.incrementArgIndex();
   4252                     self.other_args.len += 1;
   4253                     self.zig_equivalent = clang_arg.zig_equivalent;
   4254 
   4255                     break :find_clang_arg;
   4256                 } else if (prefix_len != 0) {
   4257                     self.zig_equivalent = clang_arg.zig_equivalent;
   4258                     self.only_arg = arg[prefix_len..];
   4259 
   4260                     break :find_clang_arg;
   4261                 }
   4262             },
   4263             .joined_and_separate => {
   4264                 // Example: `-Xopenmp-target=riscv64-linux-unknown foo`
   4265                 const prefix_len = clang_arg.matchStartsWith(arg);
   4266                 if (prefix_len != 0) {
   4267                     self.only_arg = arg[prefix_len..];
   4268                     if (self.next_index >= self.argv.len) {
   4269                         fatal("Expected parameter after '{s}'", .{arg});
   4270                     }
   4271                     self.second_arg = self.argv[self.next_index];
   4272                     self.incrementArgIndex();
   4273                     self.other_args.len += 1;
   4274                     self.zig_equivalent = clang_arg.zig_equivalent;
   4275                     break :find_clang_arg;
   4276                 }
   4277             },
   4278             .separate => if (clang_arg.matchEql(arg) > 0) {
   4279                 if (self.next_index >= self.argv.len) {
   4280                     fatal("Expected parameter after '{s}'", .{arg});
   4281                 }
   4282                 self.only_arg = self.argv[self.next_index];
   4283                 self.incrementArgIndex();
   4284                 self.other_args.len += 1;
   4285                 self.zig_equivalent = clang_arg.zig_equivalent;
   4286                 break :find_clang_arg;
   4287             },
   4288             .remaining_args_joined => {
   4289                 const prefix_len = clang_arg.matchStartsWith(arg);
   4290                 if (prefix_len != 0) {
   4291                     @panic("TODO");
   4292                 }
   4293             },
   4294             .multi_arg => |num_args| if (clang_arg.matchEql(arg) > 0) {
   4295                 // Example `-sectcreate <arg1> <arg2> <arg3>`.
   4296                 var i: usize = 0;
   4297                 while (i < num_args) : (i += 1) {
   4298                     self.incrementArgIndex();
   4299                     self.other_args.len += 1;
   4300                 }
   4301                 self.zig_equivalent = clang_arg.zig_equivalent;
   4302                 break :find_clang_arg;
   4303             },
   4304         } else {
   4305             fatal("Unknown Clang option: '{s}'", .{arg});
   4306         }
   4307     }
   4308 
   4309     fn incrementArgIndex(self: *ClangArgIterator) void {
   4310         self.next_index += 1;
   4311         self.resolveRespFileArgs();
   4312     }
   4313 
   4314     fn resolveRespFileArgs(self: *ClangArgIterator) void {
   4315         const allocator = self.allocator;
   4316         if (self.next_index >= self.argv.len) {
   4317             if (self.root_args) |root_args| {
   4318                 self.next_index = root_args.next_index;
   4319                 self.argv = root_args.argv;
   4320 
   4321                 allocator.destroy(root_args);
   4322                 self.root_args = null;
   4323             }
   4324             if (self.next_index >= self.argv.len) {
   4325                 self.has_next = false;
   4326             }
   4327         }
   4328     }
   4329 };
   4330 
   4331 fn parseCodeModel(arg: []const u8) std.builtin.CodeModel {
   4332     return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse
   4333         fatal("unsupported machine code model: '{s}'", .{arg});
   4334 }
   4335 
   4336 /// Raise the open file descriptor limit. Ask and ye shall receive.
   4337 /// For one example of why this is handy, consider the case of building musl libc.
   4338 /// We keep a lock open for each of the object files in the form of a file descriptor
   4339 /// until they are finally put into an archive file. This is to allow a zig-cache
   4340 /// garbage collector to run concurrently to zig processes, and to allow multiple
   4341 /// zig processes to run concurrently with each other, without clobbering each other.
   4342 fn gimmeMoreOfThoseSweetSweetFileDescriptors() void {
   4343     if (!@hasDecl(std.os.system, "rlimit")) return;
   4344     const posix = std.os;
   4345 
   4346     var lim = posix.getrlimit(.NOFILE) catch return; // Oh well; we tried.
   4347     if (comptime builtin.target.isDarwin()) {
   4348         // On Darwin, `NOFILE` is bounded by a hardcoded value `OPEN_MAX`.
   4349         // According to the man pages for setrlimit():
   4350         //   setrlimit() now returns with errno set to EINVAL in places that historically succeeded.
   4351         //   It no longer accepts "rlim_cur = RLIM.INFINITY" for RLIM.NOFILE.
   4352         //   Use "rlim_cur = min(OPEN_MAX, rlim_max)".
   4353         lim.max = std.math.min(std.os.darwin.OPEN_MAX, lim.max);
   4354     }
   4355     if (lim.cur == lim.max) return;
   4356 
   4357     // Do a binary search for the limit.
   4358     var min: posix.rlim_t = lim.cur;
   4359     var max: posix.rlim_t = 1 << 20;
   4360     // But if there's a defined upper bound, don't search, just set it.
   4361     if (lim.max != posix.RLIM.INFINITY) {
   4362         min = lim.max;
   4363         max = lim.max;
   4364     }
   4365 
   4366     while (true) {
   4367         lim.cur = min + @divTrunc(max - min, 2); // on freebsd rlim_t is signed
   4368         if (posix.setrlimit(.NOFILE, lim)) |_| {
   4369             min = lim.cur;
   4370         } else |_| {
   4371             max = lim.cur;
   4372         }
   4373         if (min + 1 >= max) break;
   4374     }
   4375 }
   4376 
   4377 test "fds" {
   4378     gimmeMoreOfThoseSweetSweetFileDescriptors();
   4379 }
   4380 
   4381 fn detectNativeTargetInfo(gpa: Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo {
   4382     return std.zig.system.NativeTargetInfo.detect(gpa, cross_target);
   4383 }
   4384 
   4385 /// Indicate that we are now terminating with a successful exit code.
   4386 /// In debug builds, this is a no-op, so that the calling code's
   4387 /// cleanup mechanisms are tested and so that external tools that
   4388 /// check for resource leaks can be accurate. In release builds, this
   4389 /// calls exit(0), and does not return.
   4390 pub fn cleanExit() void {
   4391     if (builtin.mode == .Debug) {
   4392         return;
   4393     } else {
   4394         process.exit(0);
   4395     }
   4396 }
   4397 
   4398 const usage_ast_check =
   4399     \\Usage: zig ast-check [file]
   4400     \\
   4401     \\    Given a .zig source file, reports any compile errors that can be
   4402     \\    ascertained on the basis of the source code alone, without target
   4403     \\    information or type checking.
   4404     \\
   4405     \\    If [file] is omitted, stdin is used.
   4406     \\
   4407     \\Options:
   4408     \\  -h, --help            Print this help and exit
   4409     \\  --color [auto|off|on] Enable or disable colored error messages
   4410     \\  -t                    (debug option) Output ZIR in text form to stdout
   4411     \\
   4412     \\
   4413 ;
   4414 
   4415 pub fn cmdAstCheck(
   4416     gpa: Allocator,
   4417     arena: Allocator,
   4418     args: []const []const u8,
   4419 ) !void {
   4420     const Module = @import("Module.zig");
   4421     const AstGen = @import("AstGen.zig");
   4422     const Zir = @import("Zir.zig");
   4423 
   4424     var color: Color = .auto;
   4425     var want_output_text = false;
   4426     var zig_source_file: ?[]const u8 = null;
   4427 
   4428     var i: usize = 0;
   4429     while (i < args.len) : (i += 1) {
   4430         const arg = args[i];
   4431         if (mem.startsWith(u8, arg, "-")) {
   4432             if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
   4433                 try io.getStdOut().writeAll(usage_ast_check);
   4434                 return cleanExit();
   4435             } else if (mem.eql(u8, arg, "-t")) {
   4436                 want_output_text = true;
   4437             } else if (mem.eql(u8, arg, "--color")) {
   4438                 if (i + 1 >= args.len) {
   4439                     fatal("expected [auto|on|off] after --color", .{});
   4440                 }
   4441                 i += 1;
   4442                 const next_arg = args[i];
   4443                 color = std.meta.stringToEnum(Color, next_arg) orelse {
   4444                     fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
   4445                 };
   4446             } else {
   4447                 fatal("unrecognized parameter: '{s}'", .{arg});
   4448             }
   4449         } else if (zig_source_file == null) {
   4450             zig_source_file = arg;
   4451         } else {
   4452             fatal("extra positional parameter: '{s}'", .{arg});
   4453         }
   4454     }
   4455 
   4456     var file: Module.File = .{
   4457         .status = .never_loaded,
   4458         .source_loaded = false,
   4459         .tree_loaded = false,
   4460         .zir_loaded = false,
   4461         .sub_file_path = undefined,
   4462         .source = undefined,
   4463         .stat_size = undefined,
   4464         .stat_inode = undefined,
   4465         .stat_mtime = undefined,
   4466         .tree = undefined,
   4467         .zir = undefined,
   4468         .pkg = undefined,
   4469         .root_decl = null,
   4470     };
   4471     if (zig_source_file) |file_name| {
   4472         var f = fs.cwd().openFile(file_name, .{}) catch |err| {
   4473             fatal("unable to open file for ast-check '{s}': {s}", .{ file_name, @errorName(err) });
   4474         };
   4475         defer f.close();
   4476 
   4477         const stat = try f.stat();
   4478 
   4479         if (stat.size > max_src_size)
   4480             return error.FileTooBig;
   4481 
   4482         const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0);
   4483         const amt = try f.readAll(source);
   4484         if (amt != stat.size)
   4485             return error.UnexpectedEndOfFile;
   4486 
   4487         file.sub_file_path = file_name;
   4488         file.source = source;
   4489         file.source_loaded = true;
   4490         file.stat_size = stat.size;
   4491         file.stat_inode = stat.inode;
   4492         file.stat_mtime = stat.mtime;
   4493     } else {
   4494         const stdin = io.getStdIn();
   4495         const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| {
   4496             fatal("unable to read stdin: {s}", .{err});
   4497         };
   4498         file.sub_file_path = "<stdin>";
   4499         file.source = source;
   4500         file.source_loaded = true;
   4501         file.stat_size = source.len;
   4502     }
   4503 
   4504     file.pkg = try Package.create(gpa, null, file.sub_file_path);
   4505     defer file.pkg.destroy(gpa);
   4506 
   4507     file.tree = try std.zig.parse(gpa, file.source);
   4508     file.tree_loaded = true;
   4509     defer file.tree.deinit(gpa);
   4510 
   4511     for (file.tree.errors) |parse_error| {
   4512         try printErrMsgToStdErr(gpa, arena, parse_error, file.tree, file.sub_file_path, color);
   4513     }
   4514     if (file.tree.errors.len != 0) {
   4515         process.exit(1);
   4516     }
   4517 
   4518     file.zir = try AstGen.generate(gpa, file.tree);
   4519     file.zir_loaded = true;
   4520     defer file.zir.deinit(gpa);
   4521 
   4522     if (file.zir.hasCompileErrors()) {
   4523         var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
   4524         try Compilation.AllErrors.addZir(arena, &errors, &file);
   4525         const ttyconf: std.debug.TTY.Config = switch (color) {
   4526             .auto => std.debug.detectTTYConfig(),
   4527             .on => .escape_codes,
   4528             .off => .no_color,
   4529         };
   4530         for (errors.items) |full_err_msg| {
   4531             full_err_msg.renderToStdErr(ttyconf);
   4532         }
   4533         process.exit(1);
   4534     }
   4535 
   4536     if (!want_output_text) {
   4537         return cleanExit();
   4538     }
   4539     if (!debug_extensions_enabled) {
   4540         fatal("-t option only available in debug builds of zig", .{});
   4541     }
   4542 
   4543     {
   4544         const token_bytes = @sizeOf(Ast.TokenList) +
   4545             file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(Ast.ByteOffset));
   4546         const tree_bytes = @sizeOf(Ast) + file.tree.nodes.len *
   4547             (@sizeOf(Ast.Node.Tag) +
   4548             @sizeOf(Ast.Node.Data) +
   4549             @sizeOf(Ast.TokenIndex));
   4550         const instruction_bytes = file.zir.instructions.len *
   4551             // Here we don't use @sizeOf(Zir.Inst.Data) because it would include
   4552             // the debug safety tag but we want to measure release size.
   4553             (@sizeOf(Zir.Inst.Tag) + 8);
   4554         const extra_bytes = file.zir.extra.len * @sizeOf(u32);
   4555         const total_bytes = @sizeOf(Zir) + instruction_bytes + extra_bytes +
   4556             file.zir.string_bytes.len * @sizeOf(u8);
   4557         const stdout = io.getStdOut();
   4558         const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
   4559         // zig fmt: off
   4560         try stdout.writer().print(
   4561             \\# Source bytes:       {}
   4562             \\# Tokens:             {} ({})
   4563             \\# AST Nodes:          {} ({})
   4564             \\# Total ZIR bytes:    {}
   4565             \\# Instructions:       {d} ({})
   4566             \\# String Table Bytes: {}
   4567             \\# Extra Data Items:   {d} ({})
   4568             \\
   4569         , .{
   4570             fmtIntSizeBin(file.source.len),
   4571             file.tree.tokens.len, fmtIntSizeBin(token_bytes),
   4572             file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
   4573             fmtIntSizeBin(total_bytes),
   4574             file.zir.instructions.len, fmtIntSizeBin(instruction_bytes),
   4575             fmtIntSizeBin(file.zir.string_bytes.len),
   4576             file.zir.extra.len, fmtIntSizeBin(extra_bytes),
   4577         });
   4578         // zig fmt: on
   4579     }
   4580 
   4581     return @import("print_zir.zig").renderAsTextToFile(gpa, &file, io.getStdOut());
   4582 }
   4583 
   4584 /// This is only enabled for debug builds.
   4585 pub fn cmdChangelist(
   4586     gpa: Allocator,
   4587     arena: Allocator,
   4588     args: []const []const u8,
   4589 ) !void {
   4590     const Module = @import("Module.zig");
   4591     const AstGen = @import("AstGen.zig");
   4592     const Zir = @import("Zir.zig");
   4593 
   4594     const old_source_file = args[0];
   4595     const new_source_file = args[1];
   4596 
   4597     var f = fs.cwd().openFile(old_source_file, .{}) catch |err| {
   4598         fatal("unable to open old source file for comparison '{s}': {s}", .{ old_source_file, @errorName(err) });
   4599     };
   4600     defer f.close();
   4601 
   4602     const stat = try f.stat();
   4603 
   4604     if (stat.size > max_src_size)
   4605         return error.FileTooBig;
   4606 
   4607     var file: Module.File = .{
   4608         .status = .never_loaded,
   4609         .source_loaded = false,
   4610         .tree_loaded = false,
   4611         .zir_loaded = false,
   4612         .sub_file_path = old_source_file,
   4613         .source = undefined,
   4614         .stat_size = stat.size,
   4615         .stat_inode = stat.inode,
   4616         .stat_mtime = stat.mtime,
   4617         .tree = undefined,
   4618         .zir = undefined,
   4619         .pkg = undefined,
   4620         .root_decl = null,
   4621     };
   4622 
   4623     file.pkg = try Package.create(gpa, null, file.sub_file_path);
   4624     defer file.pkg.destroy(gpa);
   4625 
   4626     const source = try arena.allocSentinel(u8, @intCast(usize, stat.size), 0);
   4627     const amt = try f.readAll(source);
   4628     if (amt != stat.size)
   4629         return error.UnexpectedEndOfFile;
   4630     file.source = source;
   4631     file.source_loaded = true;
   4632 
   4633     file.tree = try std.zig.parse(gpa, file.source);
   4634     file.tree_loaded = true;
   4635     defer file.tree.deinit(gpa);
   4636 
   4637     for (file.tree.errors) |parse_error| {
   4638         try printErrMsgToStdErr(gpa, arena, parse_error, file.tree, old_source_file, .auto);
   4639     }
   4640     if (file.tree.errors.len != 0) {
   4641         process.exit(1);
   4642     }
   4643 
   4644     file.zir = try AstGen.generate(gpa, file.tree);
   4645     file.zir_loaded = true;
   4646     defer file.zir.deinit(gpa);
   4647 
   4648     if (file.zir.hasCompileErrors()) {
   4649         var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
   4650         try Compilation.AllErrors.addZir(arena, &errors, &file);
   4651         const ttyconf = std.debug.detectTTYConfig();
   4652         for (errors.items) |full_err_msg| {
   4653             full_err_msg.renderToStdErr(ttyconf);
   4654         }
   4655         process.exit(1);
   4656     }
   4657 
   4658     var new_f = fs.cwd().openFile(new_source_file, .{}) catch |err| {
   4659         fatal("unable to open new source file for comparison '{s}': {s}", .{ new_source_file, @errorName(err) });
   4660     };
   4661     defer new_f.close();
   4662 
   4663     const new_stat = try new_f.stat();
   4664 
   4665     if (new_stat.size > max_src_size)
   4666         return error.FileTooBig;
   4667 
   4668     const new_source = try arena.allocSentinel(u8, @intCast(usize, new_stat.size), 0);
   4669     const new_amt = try new_f.readAll(new_source);
   4670     if (new_amt != new_stat.size)
   4671         return error.UnexpectedEndOfFile;
   4672 
   4673     var new_tree = try std.zig.parse(gpa, new_source);
   4674     defer new_tree.deinit(gpa);
   4675 
   4676     for (new_tree.errors) |parse_error| {
   4677         try printErrMsgToStdErr(gpa, arena, parse_error, new_tree, new_source_file, .auto);
   4678     }
   4679     if (new_tree.errors.len != 0) {
   4680         process.exit(1);
   4681     }
   4682 
   4683     var old_zir = file.zir;
   4684     defer old_zir.deinit(gpa);
   4685     file.zir_loaded = false;
   4686     file.zir = try AstGen.generate(gpa, new_tree);
   4687     file.zir_loaded = true;
   4688 
   4689     if (file.zir.hasCompileErrors()) {
   4690         var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
   4691         try Compilation.AllErrors.addZir(arena, &errors, &file);
   4692         const ttyconf = std.debug.detectTTYConfig();
   4693         for (errors.items) |full_err_msg| {
   4694             full_err_msg.renderToStdErr(ttyconf);
   4695         }
   4696         process.exit(1);
   4697     }
   4698 
   4699     var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
   4700     defer inst_map.deinit(gpa);
   4701 
   4702     var extra_map: std.AutoHashMapUnmanaged(u32, u32) = .{};
   4703     defer extra_map.deinit(gpa);
   4704 
   4705     try Module.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map, &extra_map);
   4706 
   4707     var bw = io.bufferedWriter(io.getStdOut().writer());
   4708     const stdout = bw.writer();
   4709     {
   4710         try stdout.print("Instruction mappings:\n", .{});
   4711         var it = inst_map.iterator();
   4712         while (it.next()) |entry| {
   4713             try stdout.print(" %{d} => %{d}\n", .{
   4714                 entry.key_ptr.*, entry.value_ptr.*,
   4715             });
   4716         }
   4717     }
   4718     {
   4719         try stdout.print("Extra mappings:\n", .{});
   4720         var it = extra_map.iterator();
   4721         while (it.next()) |entry| {
   4722             try stdout.print(" {d} => {d}\n", .{
   4723                 entry.key_ptr.*, entry.value_ptr.*,
   4724             });
   4725         }
   4726     }
   4727     try bw.flush();
   4728 }
   4729 
   4730 fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 {
   4731     return std.fmt.parseUnsigned(u64, arg[prefix_len..], 0) catch |err| {
   4732         fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
   4733     };
   4734 }
   4735 
   4736 fn warnAboutForeignBinaries(
   4737     gpa: Allocator,
   4738     arena: Allocator,
   4739     arg_mode: ArgMode,
   4740     target_info: std.zig.system.NativeTargetInfo,
   4741     link_libc: bool,
   4742 ) !void {
   4743     const host_cross_target: std.zig.CrossTarget = .{};
   4744     const host_target_info = try detectNativeTargetInfo(gpa, host_cross_target);
   4745 
   4746     switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) {
   4747         .native => return,
   4748         .rosetta => {
   4749             const host_name = try host_target_info.target.zigTriple(arena);
   4750             const foreign_name = try target_info.target.zigTriple(arena);
   4751             warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{
   4752                 host_name, foreign_name,
   4753             });
   4754         },
   4755         .qemu => |qemu| {
   4756             const host_name = try host_target_info.target.zigTriple(arena);
   4757             const foreign_name = try target_info.target.zigTriple(arena);
   4758             switch (arg_mode) {
   4759                 .zig_test => warn(
   4760                     "the host system ({s}) does not appear to be capable of executing binaries " ++
   4761                         "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
   4762                         "to run the tests",
   4763                     .{ host_name, foreign_name, qemu },
   4764                 ),
   4765                 else => warn(
   4766                     "the host system ({s}) does not appear to be capable of executing binaries " ++
   4767                         "from the target ({s}). Consider using '{s}' to run the binary",
   4768                     .{ host_name, foreign_name, qemu },
   4769                 ),
   4770             }
   4771         },
   4772         .wine => |wine| {
   4773             const host_name = try host_target_info.target.zigTriple(arena);
   4774             const foreign_name = try target_info.target.zigTriple(arena);
   4775             switch (arg_mode) {
   4776                 .zig_test => warn(
   4777                     "the host system ({s}) does not appear to be capable of executing binaries " ++
   4778                         "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
   4779                         "to run the tests",
   4780                     .{ host_name, foreign_name, wine },
   4781                 ),
   4782                 else => warn(
   4783                     "the host system ({s}) does not appear to be capable of executing binaries " ++
   4784                         "from the target ({s}). Consider using '{s}' to run the binary",
   4785                     .{ host_name, foreign_name, wine },
   4786                 ),
   4787             }
   4788         },
   4789         .wasmtime => |wasmtime| {
   4790             const host_name = try host_target_info.target.zigTriple(arena);
   4791             const foreign_name = try target_info.target.zigTriple(arena);
   4792             switch (arg_mode) {
   4793                 .zig_test => warn(
   4794                     "the host system ({s}) does not appear to be capable of executing binaries " ++
   4795                         "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
   4796                         "to run the tests",
   4797                     .{ host_name, foreign_name, wasmtime },
   4798                 ),
   4799                 else => warn(
   4800                     "the host system ({s}) does not appear to be capable of executing binaries " ++
   4801                         "from the target ({s}). Consider using '{s}' to run the binary",
   4802                     .{ host_name, foreign_name, wasmtime },
   4803                 ),
   4804             }
   4805         },
   4806         .darling => |darling| {
   4807             const host_name = try host_target_info.target.zigTriple(arena);
   4808             const foreign_name = try target_info.target.zigTriple(arena);
   4809             switch (arg_mode) {
   4810                 .zig_test => warn(
   4811                     "the host system ({s}) does not appear to be capable of executing binaries " ++
   4812                         "from the target ({s}). Consider using '--test-cmd {s} --test-cmd-bin' " ++
   4813                         "to run the tests",
   4814                     .{ host_name, foreign_name, darling },
   4815                 ),
   4816                 else => warn(
   4817                     "the host system ({s}) does not appear to be capable of executing binaries " ++
   4818                         "from the target ({s}). Consider using '{s}' to run the binary",
   4819                     .{ host_name, foreign_name, darling },
   4820                 ),
   4821             }
   4822         },
   4823         .bad_dl => |foreign_dl| {
   4824             const host_dl = host_target_info.dynamic_linker.get() orelse "(none)";
   4825             const tip_suffix = switch (arg_mode) {
   4826                 .zig_test => ", '--test-no-exec', or '--test-cmd'",
   4827                 else => "",
   4828             };
   4829             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}", .{
   4830                 host_dl, foreign_dl, tip_suffix,
   4831             });
   4832         },
   4833         .bad_os_or_cpu => {
   4834             const host_name = try host_target_info.target.zigTriple(arena);
   4835             const foreign_name = try target_info.target.zigTriple(arena);
   4836             const tip_suffix = switch (arg_mode) {
   4837                 .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'",
   4838                 else => "",
   4839             };
   4840             warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}){s}", .{
   4841                 host_name, foreign_name, tip_suffix,
   4842             });
   4843         },
   4844     }
   4845 }