zig

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

blob ad184b2b (316946B) - Raw


      1 const Compilation = @This();
      2 
      3 const std = @import("std");
      4 const builtin = @import("builtin");
      5 const fs = std.fs;
      6 const mem = std.mem;
      7 const Allocator = std.mem.Allocator;
      8 const assert = std.debug.assert;
      9 const log = std.log.scoped(.compilation);
     10 const Target = std.Target;
     11 const ThreadPool = std.Thread.Pool;
     12 const WaitGroup = std.Thread.WaitGroup;
     13 const ErrorBundle = std.zig.ErrorBundle;
     14 const fatal = std.process.fatal;
     15 
     16 const Value = @import("Value.zig");
     17 const Type = @import("Type.zig");
     18 const target_util = @import("target.zig");
     19 const Package = @import("Package.zig");
     20 const introspect = @import("introspect.zig");
     21 const link = @import("link.zig");
     22 const tracy = @import("tracy.zig");
     23 const trace = tracy.trace;
     24 const build_options = @import("build_options");
     25 const LibCInstallation = std.zig.LibCInstallation;
     26 const glibc = @import("libs/glibc.zig");
     27 const musl = @import("libs/musl.zig");
     28 const freebsd = @import("libs/freebsd.zig");
     29 const netbsd = @import("libs/netbsd.zig");
     30 const mingw = @import("libs/mingw.zig");
     31 const libunwind = @import("libs/libunwind.zig");
     32 const libcxx = @import("libs/libcxx.zig");
     33 const wasi_libc = @import("libs/wasi_libc.zig");
     34 const clangMain = @import("main.zig").clangMain;
     35 const Zcu = @import("Zcu.zig");
     36 const Sema = @import("Sema.zig");
     37 const InternPool = @import("InternPool.zig");
     38 const Cache = std.Build.Cache;
     39 const c_codegen = @import("codegen/c.zig");
     40 const libtsan = @import("libs/libtsan.zig");
     41 const Zir = std.zig.Zir;
     42 const Air = @import("Air.zig");
     43 const Builtin = @import("Builtin.zig");
     44 const LlvmObject = @import("codegen/llvm.zig").Object;
     45 const dev = @import("dev.zig");
     46 
     47 pub const Config = @import("Compilation/Config.zig");
     48 
     49 /// General-purpose allocator. Used for both temporary and long-term storage.
     50 gpa: Allocator,
     51 /// Arena-allocated memory, mostly used during initialization. However, it can
     52 /// be used for other things requiring the same lifetime as the `Compilation`.
     53 /// Not thread-safe - lock `mutex` if potentially accessing from multiple
     54 /// threads at once.
     55 arena: Allocator,
     56 /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
     57 zcu: ?*Zcu,
     58 /// Contains different state depending on the `CacheMode` used by this `Compilation`.
     59 cache_use: CacheUse,
     60 /// All compilations have a root module because this is where some important
     61 /// settings are stored, such as target and optimization mode. This module
     62 /// might not have any .zig code associated with it, however.
     63 root_mod: *Package.Module,
     64 
     65 /// User-specified settings that have all the defaults resolved into concrete values.
     66 config: Config,
     67 
     68 /// The main output file.
     69 /// In `CacheMode.whole`, this is null except for during the body of `update`.
     70 /// In `CacheMode.none` and `CacheMode.incremental`, this is long-lived.
     71 /// Regardless of cache mode, this is `null` when `-fno-emit-bin` is used.
     72 bin_file: ?*link.File,
     73 
     74 /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
     75 sysroot: ?[]const u8,
     76 root_name: [:0]const u8,
     77 compiler_rt_strat: RtStrat,
     78 ubsan_rt_strat: RtStrat,
     79 /// Resolved into known paths, any GNU ld scripts already resolved.
     80 link_inputs: []const link.Input,
     81 /// Needed only for passing -F args to clang.
     82 framework_dirs: []const []const u8,
     83 /// These are only for DLLs dependencies fulfilled by the `.def` files shipped
     84 /// with Zig. Static libraries are provided as `link.Input` values.
     85 windows_libs: std.StringArrayHashMapUnmanaged(void),
     86 version: ?std.SemanticVersion,
     87 libc_installation: ?*const LibCInstallation,
     88 skip_linker_dependencies: bool,
     89 function_sections: bool,
     90 data_sections: bool,
     91 link_eh_frame_hdr: bool,
     92 native_system_include_paths: []const []const u8,
     93 /// List of symbols forced as undefined in the symbol table
     94 /// thus forcing their resolution by the linker.
     95 /// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
     96 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
     97 
     98 c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .empty,
     99 win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMapUnmanaged(*Win32Resource, void) else struct {
    100     pub fn keys(_: @This()) [0]void {
    101         return .{};
    102     }
    103     pub fn count(_: @This()) u0 {
    104         return 0;
    105     }
    106     pub fn deinit(_: @This(), _: Allocator) void {}
    107 } = .{},
    108 
    109 link_diags: link.Diags,
    110 link_task_queue: link.Queue = .empty,
    111 
    112 /// Set of work that can be represented by only flags to determine whether the
    113 /// work is queued or not.
    114 queued_jobs: QueuedJobs,
    115 
    116 work_queues: [
    117     len: {
    118         var len: usize = 0;
    119         for (std.enums.values(Job.Tag)) |tag| {
    120             len = @max(Job.stage(tag) + 1, len);
    121         }
    122         break :len len;
    123     }
    124 ]std.fifo.LinearFifo(Job, .Dynamic),
    125 
    126 /// These jobs are to invoke the Clang compiler to create an object file, which
    127 /// gets linked with the Compilation.
    128 c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),
    129 
    130 /// These jobs are to invoke the RC compiler to create a compiled resource file (.res), which
    131 /// gets linked with the Compilation.
    132 win32_resource_work_queue: if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic) else struct {
    133     pub fn ensureUnusedCapacity(_: @This(), _: u0) error{}!void {}
    134     pub fn readItem(_: @This()) ?noreturn {
    135         return null;
    136     }
    137     pub fn deinit(_: @This()) void {}
    138 },
    139 
    140 /// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
    141 /// This data is accessed by multiple threads and is protected by `mutex`.
    142 failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.Diag.Bundle) = .empty,
    143 
    144 /// The ErrorBundle memory is owned by the `Win32Resource`, using Compilation's general purpose allocator.
    145 /// This data is accessed by multiple threads and is protected by `mutex`.
    146 failed_win32_resources: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMapUnmanaged(*Win32Resource, ErrorBundle) else struct {
    147     pub fn values(_: @This()) [0]void {
    148         return .{};
    149     }
    150     pub fn deinit(_: @This(), _: Allocator) void {}
    151 } = .{},
    152 
    153 /// Miscellaneous things that can fail.
    154 misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .empty,
    155 
    156 /// When this is `true` it means invoking clang as a sub-process is expected to inherit
    157 /// stdin, stdout, stderr, and if it returns non success, to forward the exit code.
    158 /// Otherwise we attempt to parse the error messages and expose them via the Compilation API.
    159 /// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`.
    160 clang_passthrough_mode: bool,
    161 clang_preprocessor_mode: ClangPreprocessorMode,
    162 /// Whether to print clang argvs to stdout.
    163 verbose_cc: bool,
    164 verbose_air: bool,
    165 verbose_intern_pool: bool,
    166 verbose_generic_instances: bool,
    167 verbose_llvm_ir: ?[]const u8,
    168 verbose_llvm_bc: ?[]const u8,
    169 verbose_cimport: bool,
    170 verbose_llvm_cpu_features: bool,
    171 verbose_link: bool,
    172 disable_c_depfile: bool,
    173 time_report: bool,
    174 stack_report: bool,
    175 debug_compiler_runtime_libs: bool,
    176 debug_compile_errors: bool,
    177 /// Do not check this field directly. Instead, use the `debugIncremental` wrapper function.
    178 debug_incremental: bool,
    179 incremental: bool,
    180 alloc_failure_occurred: bool = false,
    181 last_update_was_cache_hit: bool = false,
    182 
    183 c_source_files: []const CSourceFile,
    184 rc_source_files: []const RcSourceFile,
    185 global_cc_argv: []const []const u8,
    186 cache_parent: *Cache,
    187 /// Populated when a sub-Compilation is created during the `update` of its parent.
    188 /// In this case the child must additionally add file system inputs to this object.
    189 parent_whole_cache: ?ParentWholeCache,
    190 /// Path to own executable for invoking `zig clang`.
    191 self_exe_path: ?[]const u8,
    192 /// Owned by the caller of `Compilation.create`.
    193 dirs: Directories,
    194 libc_include_dir_list: []const []const u8,
    195 libc_framework_dir_list: []const []const u8,
    196 rc_includes: RcIncludes,
    197 mingw_unicode_entry_point: bool,
    198 thread_pool: *ThreadPool,
    199 
    200 /// Populated when we build the libc++ static library. A Job to build this is placed in the queue
    201 /// and resolved before calling linker.flush().
    202 libcxx_static_lib: ?CrtFile = null,
    203 /// Populated when we build the libc++abi static library. A Job to build this is placed in the queue
    204 /// and resolved before calling linker.flush().
    205 libcxxabi_static_lib: ?CrtFile = null,
    206 /// Populated when we build the libunwind static library. A Job to build this is placed in the queue
    207 /// and resolved before calling linker.flush().
    208 libunwind_static_lib: ?CrtFile = null,
    209 /// Populated when we build the TSAN library. A Job to build this is placed in the queue
    210 /// and resolved before calling linker.flush().
    211 tsan_lib: ?CrtFile = null,
    212 /// Populated when we build the UBSAN library. A Job to build this is placed in the queue
    213 /// and resolved before calling linker.flush().
    214 ubsan_rt_lib: ?CrtFile = null,
    215 /// Populated when we build the UBSAN object. A Job to build this is placed in the queue
    216 /// and resolved before calling linker.flush().
    217 ubsan_rt_obj: ?CrtFile = null,
    218 /// Populated when we build the libc static library. A Job to build this is placed in the queue
    219 /// and resolved before calling linker.flush().
    220 zigc_static_lib: ?CrtFile = null,
    221 /// Populated when we build the libcompiler_rt static library. A Job to build this is indicated
    222 /// by setting `queued_jobs.compiler_rt_lib` and resolved before calling linker.flush().
    223 compiler_rt_lib: ?CrtFile = null,
    224 /// Populated when we build the compiler_rt_obj object. A Job to build this is indicated
    225 /// by setting `queued_jobs.compiler_rt_obj` and resolved before calling linker.flush().
    226 compiler_rt_obj: ?CrtFile = null,
    227 /// Populated when we build the libfuzzer static library. A Job to build this
    228 /// is indicated by setting `queued_jobs.fuzzer_lib` and resolved before
    229 /// calling linker.flush().
    230 fuzzer_lib: ?CrtFile = null,
    231 
    232 glibc_so_files: ?glibc.BuiltSharedObjects = null,
    233 freebsd_so_files: ?freebsd.BuiltSharedObjects = null,
    234 netbsd_so_files: ?netbsd.BuiltSharedObjects = null,
    235 wasi_emulated_libs: []const wasi_libc.CrtFile,
    236 
    237 /// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source,
    238 /// The set of needed CRT (C runtime) files differs depending on the target and compilation settings.
    239 /// The key is the basename, and the value is the absolute path to the completed build artifact.
    240 crt_files: std.StringHashMapUnmanaged(CrtFile) = .empty,
    241 
    242 /// How many lines of reference trace should be included per compile error.
    243 /// Null means only show snippet on first error.
    244 reference_trace: ?u32 = null,
    245 
    246 /// This mutex guards all `Compilation` mutable state.
    247 /// Disabled in single-threaded mode because the thread pool spawns in the same thread.
    248 mutex: if (builtin.single_threaded) struct {
    249     pub inline fn tryLock(_: @This()) void {}
    250     pub inline fn lock(_: @This()) void {}
    251     pub inline fn unlock(_: @This()) void {}
    252 } else std.Thread.Mutex = .{},
    253 
    254 test_filters: []const []const u8,
    255 test_name_prefix: ?[]const u8,
    256 
    257 link_task_wait_group: WaitGroup = .{},
    258 link_prog_node: std.Progress.Node = std.Progress.Node.none,
    259 
    260 llvm_opt_bisect_limit: c_int,
    261 
    262 file_system_inputs: ?*std.ArrayListUnmanaged(u8),
    263 
    264 /// This is the digest of the cache for the current compilation.
    265 /// This digest will be known after update() is called.
    266 digest: ?[Cache.bin_digest_len]u8 = null,
    267 
    268 /// Non-`null` iff we are emitting a binary.
    269 /// Does not change for the lifetime of this `Compilation`.
    270 /// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
    271 emit_bin: ?[]const u8,
    272 /// Non-`null` iff we are emitting assembly.
    273 /// Does not change for the lifetime of this `Compilation`.
    274 /// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
    275 emit_asm: ?[]const u8,
    276 /// Non-`null` iff we are emitting an implib.
    277 /// Does not change for the lifetime of this `Compilation`.
    278 /// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
    279 emit_implib: ?[]const u8,
    280 /// Non-`null` iff we are emitting LLVM IR.
    281 /// Does not change for the lifetime of this `Compilation`.
    282 /// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
    283 emit_llvm_ir: ?[]const u8,
    284 /// Non-`null` iff we are emitting LLVM bitcode.
    285 /// Does not change for the lifetime of this `Compilation`.
    286 /// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
    287 emit_llvm_bc: ?[]const u8,
    288 /// Non-`null` iff we are emitting documentation.
    289 /// Does not change for the lifetime of this `Compilation`.
    290 /// Cwd-relative if `cache_use == .none`. Otherwise, relative to our subdirectory in the cache.
    291 emit_docs: ?[]const u8,
    292 
    293 const QueuedJobs = struct {
    294     compiler_rt_lib: bool = false,
    295     compiler_rt_obj: bool = false,
    296     ubsan_rt_lib: bool = false,
    297     ubsan_rt_obj: bool = false,
    298     fuzzer_lib: bool = false,
    299     musl_crt_file: [@typeInfo(musl.CrtFile).@"enum".fields.len]bool = @splat(false),
    300     glibc_crt_file: [@typeInfo(glibc.CrtFile).@"enum".fields.len]bool = @splat(false),
    301     freebsd_crt_file: [@typeInfo(freebsd.CrtFile).@"enum".fields.len]bool = @splat(false),
    302     netbsd_crt_file: [@typeInfo(netbsd.CrtFile).@"enum".fields.len]bool = @splat(false),
    303     /// one of WASI libc static objects
    304     wasi_libc_crt_file: [@typeInfo(wasi_libc.CrtFile).@"enum".fields.len]bool = @splat(false),
    305     /// one of the mingw-w64 static objects
    306     mingw_crt_file: [@typeInfo(mingw.CrtFile).@"enum".fields.len]bool = @splat(false),
    307     /// all of the glibc shared objects
    308     glibc_shared_objects: bool = false,
    309     freebsd_shared_objects: bool = false,
    310     netbsd_shared_objects: bool = false,
    311     /// libunwind.a, usually needed when linking libc
    312     libunwind: bool = false,
    313     libcxx: bool = false,
    314     libcxxabi: bool = false,
    315     libtsan: bool = false,
    316     zigc_lib: bool = false,
    317 };
    318 
    319 /// A filesystem path, represented relative to one of a few specific directories where possible.
    320 /// Every path (considering symlinks as distinct paths) has a canonical representation in this form.
    321 /// This abstraction allows us to:
    322 /// * always open files relative to a consistent root on the filesystem
    323 /// * detect when two paths correspond to the same file, e.g. for deduplicating `@import`s
    324 pub const Path = struct {
    325     root: Root,
    326     /// This path is always in a normalized form, where:
    327     /// * All components are separated by `fs.path.sep`
    328     /// * There are no repeated separators (like "foo//bar")
    329     /// * There are no "." or ".." components
    330     /// * There is no trailing path separator
    331     ///
    332     /// There is a leading separator iff `root` is `.none` *and* `builtin.target.os.tag != .wasi`.
    333     ///
    334     /// If this `Path` exactly represents a `Root`, the sub path is "", not ".".
    335     sub_path: []u8,
    336 
    337     const Root = enum {
    338         /// `sub_path` is relative to the Zig lib directory on `Compilation`.
    339         zig_lib,
    340         /// `sub_path` is relative to the global cache directory on `Compilation`.
    341         global_cache,
    342         /// `sub_path` is relative to the local cache directory on `Compilation`.
    343         local_cache,
    344         /// `sub_path` is not relative to any of the roots listed above.
    345         /// It is resolved starting with `Directories.cwd`; so it is an absolute path on most
    346         /// targets, but cwd-relative on WASI. We do not make it cwd-relative on other targets
    347         /// so that `Path.digest` gives hashes which can be stored in the Zig cache (as they
    348         /// don't depend on a specific compiler instance).
    349         none,
    350     };
    351 
    352     /// In general, we can only construct canonical `Path`s at runtime, because weird nesting might
    353     /// mean that e.g. a sub path inside zig/lib/ is actually in the global cache. However, because
    354     /// `Directories` guarantees that `zig_lib` is a distinct path from both cache directories, it's
    355     /// okay for us to construct this path, and only this path, as a comptime constant.
    356     pub const zig_lib_root: Path = .{ .root = .zig_lib, .sub_path = "" };
    357 
    358     pub fn deinit(p: Path, gpa: Allocator) void {
    359         gpa.free(p.sub_path);
    360     }
    361 
    362     /// The added data is relocatable across any compiler process using the same lib and cache
    363     /// directories; it does not depend on cwd.
    364     pub fn addToHasher(p: Path, h: *Cache.Hasher) void {
    365         h.update(&.{@intFromEnum(p.root)});
    366         h.update(p.sub_path);
    367     }
    368 
    369     /// Small convenience wrapper around `addToHasher`.
    370     pub fn digest(p: Path) Cache.BinDigest {
    371         var h = Cache.hasher_init;
    372         p.addToHasher(&h);
    373         return h.finalResult();
    374     }
    375 
    376     /// Given a `Path`, returns the directory handle and sub path to be used to open the path.
    377     pub fn openInfo(p: Path, dirs: Directories) struct { fs.Dir, []const u8 } {
    378         const dir = switch (p.root) {
    379             .none => {
    380                 const cwd_sub_path = absToCwdRelative(p.sub_path, dirs.cwd);
    381                 return .{ fs.cwd(), cwd_sub_path };
    382             },
    383             .zig_lib => dirs.zig_lib.handle,
    384             .global_cache => dirs.global_cache.handle,
    385             .local_cache => dirs.local_cache.handle,
    386         };
    387         if (p.sub_path.len == 0) return .{ dir, "." };
    388         assert(!fs.path.isAbsolute(p.sub_path));
    389         return .{ dir, p.sub_path };
    390     }
    391 
    392     pub const format = unreachable; // do not format direcetly
    393     pub fn fmt(p: Path, comp: *Compilation) Formatter {
    394         return .{ .p = p, .comp = comp };
    395     }
    396     const Formatter = struct {
    397         p: Path,
    398         comp: *Compilation,
    399         pub fn format(f: Formatter, comptime unused_fmt: []const u8, options: std.fmt.FormatOptions, w: anytype) !void {
    400             comptime assert(unused_fmt.len == 0);
    401             _ = options;
    402             const root_path: []const u8 = switch (f.p.root) {
    403                 .zig_lib => f.comp.dirs.zig_lib.path orelse ".",
    404                 .global_cache => f.comp.dirs.global_cache.path orelse ".",
    405                 .local_cache => f.comp.dirs.local_cache.path orelse ".",
    406                 .none => {
    407                     const cwd_sub_path = absToCwdRelative(f.p.sub_path, f.comp.dirs.cwd);
    408                     try w.writeAll(cwd_sub_path);
    409                     return;
    410                 },
    411             };
    412             assert(root_path.len != 0);
    413             try w.writeAll(root_path);
    414             if (f.p.sub_path.len > 0) {
    415                 try w.writeByte(fs.path.sep);
    416                 try w.writeAll(f.p.sub_path);
    417             }
    418         }
    419     };
    420 
    421     /// Given the `sub_path` of a `Path` with `Path.root == .none`, attempts to convert
    422     /// the (absolute) path to a cwd-relative path. Otherwise, returns the absolute path
    423     /// unmodified. The returned string is never empty: "" is converted to ".".
    424     fn absToCwdRelative(sub_path: []const u8, cwd_path: []const u8) []const u8 {
    425         if (builtin.target.os.tag == .wasi) {
    426             if (sub_path.len == 0) return ".";
    427             assert(!fs.path.isAbsolute(sub_path));
    428             return sub_path;
    429         }
    430         assert(fs.path.isAbsolute(sub_path));
    431         if (!std.mem.startsWith(u8, sub_path, cwd_path)) return sub_path;
    432         if (sub_path.len == cwd_path.len) return "."; // the strings are equal
    433         if (sub_path[cwd_path.len] != fs.path.sep) return sub_path; // last component before cwd differs
    434         return sub_path[cwd_path.len + 1 ..]; // remove '/path/to/cwd/' prefix
    435     }
    436 
    437     /// From an unresolved path (which can be made of multiple not-yet-joined strings), construct a
    438     /// canonical `Path`.
    439     pub fn fromUnresolved(gpa: Allocator, dirs: Compilation.Directories, unresolved_parts: []const []const u8) Allocator.Error!Path {
    440         const resolved = try introspect.resolvePath(gpa, dirs.cwd, unresolved_parts);
    441         errdefer gpa.free(resolved);
    442 
    443         // If, for instance, `dirs.local_cache.path` is within the lib dir, it must take priority,
    444         // so that we prefer `.root = .local_cache` over `.root = .zig_lib`. The easiest way to do
    445         // this is simply to prioritize the longest root path.
    446         const PathAndRoot = struct { ?[]const u8, Root };
    447         var roots: [3]PathAndRoot = .{
    448             .{ dirs.zig_lib.path, .zig_lib },
    449             .{ dirs.global_cache.path, .global_cache },
    450             .{ dirs.local_cache.path, .local_cache },
    451         };
    452         // This must be a stable sort, because the global and local cache directories may be the same, in
    453         // which case we need to make a consistent choice.
    454         std.mem.sort(PathAndRoot, &roots, {}, struct {
    455             fn lessThan(_: void, lhs: PathAndRoot, rhs: PathAndRoot) bool {
    456                 const lhs_path_len = if (lhs[0]) |p| p.len else 0;
    457                 const rhs_path_len = if (rhs[0]) |p| p.len else 0;
    458                 return lhs_path_len > rhs_path_len; // '>' instead of '<' to sort descending
    459             }
    460         }.lessThan);
    461 
    462         for (roots) |path_and_root| {
    463             const opt_root_path, const root = path_and_root;
    464             const root_path = opt_root_path orelse {
    465                 // This root is the cwd.
    466                 if (!fs.path.isAbsolute(resolved)) {
    467                     return .{
    468                         .root = root,
    469                         .sub_path = resolved,
    470                     };
    471                 }
    472                 continue;
    473             };
    474             if (!mem.startsWith(u8, resolved, root_path)) continue;
    475             const sub: []const u8 = if (resolved.len != root_path.len) sub: {
    476                 // Check the trailing slash, so that we don't match e.g. `/foo/bar` with `/foo/barren`
    477                 if (resolved[root_path.len] != fs.path.sep) continue;
    478                 break :sub resolved[root_path.len + 1 ..];
    479             } else "";
    480             const duped = try gpa.dupe(u8, sub);
    481             gpa.free(resolved);
    482             return .{ .root = root, .sub_path = duped };
    483         }
    484 
    485         // We're not relative to any root, so we will use an absolute path (on targets where they are available).
    486 
    487         if (builtin.target.os.tag == .wasi or fs.path.isAbsolute(resolved)) {
    488             // `resolved` is already absolute (or we're on WASI, where absolute paths don't really exist).
    489             return .{ .root = .none, .sub_path = resolved };
    490         }
    491 
    492         if (resolved.len == 0) {
    493             // We just need the cwd path, no trailing separator. Note that `gpa.free(resolved)` would be a nop.
    494             return .{ .root = .none, .sub_path = try gpa.dupe(u8, dirs.cwd) };
    495         }
    496 
    497         // We need to make an absolute path. Because `resolved` came from `introspect.resolvePath`, we can just
    498         // join the paths with a simple format string.
    499         const abs_path = try std.fmt.allocPrint(gpa, "{s}{c}{s}", .{ dirs.cwd, fs.path.sep, resolved });
    500         gpa.free(resolved);
    501         return .{ .root = .none, .sub_path = abs_path };
    502     }
    503 
    504     /// Constructs a canonical `Path` representing `sub_path` relative to `root`.
    505     ///
    506     /// If `sub_path` is resolved, this is almost like directly constructing a `Path`, but this
    507     /// function also canonicalizes the result, which matters because `sub_path` may move us into
    508     /// a different root.
    509     ///
    510     /// For instance, if the Zig lib directory is inside the global cache, passing `root` as
    511     /// `.global_cache` could still end up returning a `Path` with `Path.root == .zig_lib`.
    512     pub fn fromRoot(
    513         gpa: Allocator,
    514         dirs: Compilation.Directories,
    515         root: Path.Root,
    516         sub_path: []const u8,
    517     ) Allocator.Error!Path {
    518         // Currently, this just wraps `fromUnresolved` for simplicity. A more efficient impl is
    519         // probably possible if this function ever ends up impacting performance somehow.
    520         return .fromUnresolved(gpa, dirs, &.{
    521             switch (root) {
    522                 .zig_lib => dirs.zig_lib.path orelse "",
    523                 .global_cache => dirs.global_cache.path orelse "",
    524                 .local_cache => dirs.local_cache.path orelse "",
    525                 .none => "",
    526             },
    527             sub_path,
    528         });
    529     }
    530 
    531     /// Given a `Path` and an (unresolved) sub path relative to it, construct a `Path` representing
    532     /// the joined path `p/sub_path`. Note that, like with `fromRoot`, the `sub_path` might cause us
    533     /// to move into a different `Path.Root`.
    534     pub fn join(
    535         p: Path,
    536         gpa: Allocator,
    537         dirs: Compilation.Directories,
    538         sub_path: []const u8,
    539     ) Allocator.Error!Path {
    540         // Currently, this just wraps `fromUnresolved` for simplicity. A more efficient impl is
    541         // probably possible if this function ever ends up impacting performance somehow.
    542         return .fromUnresolved(gpa, dirs, &.{
    543             switch (p.root) {
    544                 .zig_lib => dirs.zig_lib.path orelse "",
    545                 .global_cache => dirs.global_cache.path orelse "",
    546                 .local_cache => dirs.local_cache.path orelse "",
    547                 .none => "",
    548             },
    549             p.sub_path,
    550             sub_path,
    551         });
    552     }
    553 
    554     /// Like `join`, but `sub_path` is relative to the dirname of `p` instead of `p` itself.
    555     pub fn upJoin(
    556         p: Path,
    557         gpa: Allocator,
    558         dirs: Compilation.Directories,
    559         sub_path: []const u8,
    560     ) Allocator.Error!Path {
    561         return .fromUnresolved(gpa, dirs, &.{
    562             switch (p.root) {
    563                 .zig_lib => dirs.zig_lib.path orelse "",
    564                 .global_cache => dirs.global_cache.path orelse "",
    565                 .local_cache => dirs.local_cache.path orelse "",
    566                 .none => "",
    567             },
    568             p.sub_path,
    569             "..",
    570             sub_path,
    571         });
    572     }
    573 
    574     pub fn toCachePath(p: Path, dirs: Directories) Cache.Path {
    575         const root_dir: Cache.Directory = switch (p.root) {
    576             .zig_lib => dirs.zig_lib,
    577             .global_cache => dirs.global_cache,
    578             .local_cache => dirs.local_cache,
    579             else => {
    580                 const cwd_sub_path = absToCwdRelative(p.sub_path, dirs.cwd);
    581                 return .{
    582                     .root_dir = .cwd(),
    583                     .sub_path = cwd_sub_path,
    584                 };
    585             },
    586         };
    587         assert(!fs.path.isAbsolute(p.sub_path));
    588         return .{
    589             .root_dir = root_dir,
    590             .sub_path = p.sub_path,
    591         };
    592     }
    593 
    594     /// This should not be used for most of the compiler pipeline, but is useful when emitting
    595     /// paths from the compilation (e.g. in debug info), because they will not depend on the cwd.
    596     /// The returned path is owned by the caller and allocated into `gpa`.
    597     pub fn toAbsolute(p: Path, dirs: Directories, gpa: Allocator) Allocator.Error![]u8 {
    598         const root_path: []const u8 = switch (p.root) {
    599             .zig_lib => dirs.zig_lib.path orelse "",
    600             .global_cache => dirs.global_cache.path orelse "",
    601             .local_cache => dirs.local_cache.path orelse "",
    602             .none => "",
    603         };
    604         return fs.path.resolve(gpa, &.{
    605             dirs.cwd,
    606             root_path,
    607             p.sub_path,
    608         });
    609     }
    610 
    611     pub fn isNested(inner: Path, outer: Path) union(enum) {
    612         /// Value is the sub path, which is a sub-slice of `inner.sub_path`.
    613         yes: []const u8,
    614         no,
    615         different_roots,
    616     } {
    617         if (inner.root != outer.root) return .different_roots;
    618         if (!mem.startsWith(u8, inner.sub_path, outer.sub_path)) return .no;
    619         if (inner.sub_path.len == outer.sub_path.len) return .no;
    620         if (outer.sub_path.len == 0) return .{ .yes = inner.sub_path };
    621         if (inner.sub_path[outer.sub_path.len] != fs.path.sep) return .no;
    622         return .{ .yes = inner.sub_path[outer.sub_path.len + 1 ..] };
    623     }
    624 
    625     /// Returns whether this `Path` is illegal to have as a user-imported `Zcu.File` (including
    626     /// as the root of a module). Such paths exist in directories which the Zig compiler treats
    627     /// specially, like 'global_cache/b/', which stores 'builtin.zig' files.
    628     pub fn isIllegalZigImport(p: Path, gpa: Allocator, dirs: Directories) Allocator.Error!bool {
    629         const zig_builtin_dir: Path = try .fromRoot(gpa, dirs, .global_cache, "b");
    630         defer zig_builtin_dir.deinit(gpa);
    631         return switch (p.isNested(zig_builtin_dir)) {
    632             .yes => true,
    633             .no, .different_roots => false,
    634         };
    635     }
    636 };
    637 
    638 pub const Directories = struct {
    639     /// The string returned by `introspect.getResolvedCwd`. This is typically an absolute path,
    640     /// but on WASI is the empty string "" instead, because WASI does not have absolute paths.
    641     cwd: []const u8,
    642     /// The Zig 'lib' directory.
    643     /// `zig_lib.path` is resolved (`introspect.resolvePath`) or `null` for cwd.
    644     /// Guaranteed to be a different path from `global_cache` and `local_cache`.
    645     zig_lib: Cache.Directory,
    646     /// The global Zig cache directory.
    647     /// `global_cache.path` is resolved (`introspect.resolvePath`) or `null` for cwd.
    648     global_cache: Cache.Directory,
    649     /// The local Zig cache directory.
    650     /// `local_cache.path` is resolved (`introspect.resolvePath`) or `null` for cwd.
    651     /// This may be the same as `global_cache`.
    652     local_cache: Cache.Directory,
    653 
    654     pub fn deinit(dirs: *Directories) void {
    655         // The local and global caches could be the same.
    656         const close_local = dirs.local_cache.handle.fd != dirs.global_cache.handle.fd;
    657 
    658         dirs.global_cache.handle.close();
    659         if (close_local) dirs.local_cache.handle.close();
    660         dirs.zig_lib.handle.close();
    661     }
    662 
    663     /// Returns a `Directories` where `local_cache` is replaced with `global_cache`, intended for
    664     /// use by sub-compilations (e.g. compiler_rt). Do not `deinit` the returned `Directories`; it
    665     /// shares handles with `dirs`.
    666     pub fn withoutLocalCache(dirs: Directories) Directories {
    667         return .{
    668             .cwd = dirs.cwd,
    669             .zig_lib = dirs.zig_lib,
    670             .global_cache = dirs.global_cache,
    671             .local_cache = dirs.global_cache,
    672         };
    673     }
    674 
    675     /// Uses `std.process.fatal` on error conditions.
    676     pub fn init(
    677         arena: Allocator,
    678         override_zig_lib: ?[]const u8,
    679         override_global_cache: ?[]const u8,
    680         local_cache_strat: union(enum) {
    681             override: []const u8,
    682             search,
    683             global,
    684         },
    685         wasi_preopens: switch (builtin.target.os.tag) {
    686             .wasi => std.fs.wasi.Preopens,
    687             else => void,
    688         },
    689         self_exe_path: switch (builtin.target.os.tag) {
    690             .wasi => void,
    691             else => []const u8,
    692         },
    693     ) Directories {
    694         const wasi = builtin.target.os.tag == .wasi;
    695 
    696         const cwd = introspect.getResolvedCwd(arena) catch |err| {
    697             fatal("unable to get cwd: {s}", .{@errorName(err)});
    698         };
    699 
    700         const zig_lib: Cache.Directory = d: {
    701             if (override_zig_lib) |path| break :d openUnresolved(arena, cwd, path, .@"zig lib");
    702             if (wasi) break :d openWasiPreopen(wasi_preopens, "/lib");
    703             break :d introspect.findZigLibDirFromSelfExe(arena, cwd, self_exe_path) catch |err| {
    704                 fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) });
    705             };
    706         };
    707 
    708         const global_cache: Cache.Directory = d: {
    709             if (override_global_cache) |path| break :d openUnresolved(arena, cwd, path, .@"global cache");
    710             if (wasi) break :d openWasiPreopen(wasi_preopens, "/cache");
    711             const path = introspect.resolveGlobalCacheDir(arena) catch |err| {
    712                 fatal("unable to resolve zig cache directory: {s}", .{@errorName(err)});
    713             };
    714             break :d openUnresolved(arena, cwd, path, .@"global cache");
    715         };
    716 
    717         const local_cache: Cache.Directory = switch (local_cache_strat) {
    718             .override => |path| openUnresolved(arena, cwd, path, .@"local cache"),
    719             .search => d: {
    720                 const maybe_path = introspect.resolveSuitableLocalCacheDir(arena, cwd) catch |err| {
    721                     fatal("unable to resolve zig cache directory: {s}", .{@errorName(err)});
    722                 };
    723                 const path = maybe_path orelse break :d global_cache;
    724                 break :d openUnresolved(arena, cwd, path, .@"local cache");
    725             },
    726             .global => global_cache,
    727         };
    728 
    729         if (std.mem.eql(u8, zig_lib.path orelse "", global_cache.path orelse "")) {
    730             fatal("zig lib directory '{}' cannot be equal to global cache directory '{}'", .{ zig_lib, global_cache });
    731         }
    732         if (std.mem.eql(u8, zig_lib.path orelse "", local_cache.path orelse "")) {
    733             fatal("zig lib directory '{}' cannot be equal to local cache directory '{}'", .{ zig_lib, local_cache });
    734         }
    735 
    736         return .{
    737             .cwd = cwd,
    738             .zig_lib = zig_lib,
    739             .global_cache = global_cache,
    740             .local_cache = local_cache,
    741         };
    742     }
    743     fn openWasiPreopen(preopens: std.fs.wasi.Preopens, name: []const u8) Cache.Directory {
    744         return .{
    745             .path = if (std.mem.eql(u8, name, ".")) null else name,
    746             .handle = .{
    747                 .fd = preopens.find(name) orelse fatal("WASI preopen not found: '{s}'", .{name}),
    748             },
    749         };
    750     }
    751     fn openUnresolved(arena: Allocator, cwd: []const u8, unresolved_path: []const u8, thing: enum { @"zig lib", @"global cache", @"local cache" }) Cache.Directory {
    752         const path = introspect.resolvePath(arena, cwd, &.{unresolved_path}) catch |err| {
    753             fatal("unable to resolve {s} directory: {s}", .{ @tagName(thing), @errorName(err) });
    754         };
    755         const nonempty_path = if (path.len == 0) "." else path;
    756         const handle_or_err = switch (thing) {
    757             .@"zig lib" => std.fs.cwd().openDir(nonempty_path, .{}),
    758             .@"global cache", .@"local cache" => std.fs.cwd().makeOpenPath(nonempty_path, .{}),
    759         };
    760         return .{
    761             .path = if (path.len == 0) null else path,
    762             .handle = handle_or_err catch |err| {
    763                 const extra_str: []const u8 = e: {
    764                     if (thing == .@"global cache") switch (err) {
    765                         error.AccessDenied, error.ReadOnlyFileSystem => break :e "\n" ++
    766                             "If this location is not writable then consider specifying an alternative with " ++
    767                             "the ZIG_GLOBAL_CACHE_DIR environment variable or the --global-cache-dir option.",
    768                         else => {},
    769                     };
    770                     break :e "";
    771                 };
    772                 fatal("unable to open {s} directory '{s}': {s}{s}", .{ @tagName(thing), nonempty_path, @errorName(err), extra_str });
    773             },
    774         };
    775     }
    776 };
    777 
    778 /// This small wrapper function just checks whether debug extensions are enabled before checking
    779 /// `comp.debug_incremental`. It is inline so that comptime-known `false` propagates to the caller,
    780 /// preventing debugging features from making it into release builds of the compiler.
    781 pub inline fn debugIncremental(comp: *const Compilation) bool {
    782     if (!build_options.enable_debug_extensions or builtin.single_threaded) return false;
    783     return comp.debug_incremental;
    784 }
    785 
    786 pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size;
    787 pub const SemaError = Zcu.SemaError;
    788 
    789 pub const CrtFile = struct {
    790     lock: Cache.Lock,
    791     full_object_path: Cache.Path,
    792 
    793     pub fn deinit(self: *CrtFile, gpa: Allocator) void {
    794         self.lock.release();
    795         gpa.free(self.full_object_path.sub_path);
    796         self.* = undefined;
    797     }
    798 };
    799 
    800 /// Supported languages for "zig clang -x <lang>".
    801 /// Loosely based on llvm-project/clang/include/clang/Driver/Types.def
    802 pub const LangToExt = std.StaticStringMap(FileExt).initComptime(.{
    803     .{ "c", .c },
    804     .{ "c-header", .h },
    805     .{ "c++", .cpp },
    806     .{ "c++-header", .hpp },
    807     .{ "objective-c", .m },
    808     .{ "objective-c-header", .hm },
    809     .{ "objective-c++", .mm },
    810     .{ "objective-c++-header", .hmm },
    811     .{ "assembler", .assembly },
    812     .{ "assembler-with-cpp", .assembly_with_cpp },
    813 });
    814 
    815 /// For passing to a C compiler.
    816 pub const CSourceFile = struct {
    817     /// Many C compiler flags are determined by settings contained in the owning Module.
    818     owner: *Package.Module,
    819     src_path: []const u8,
    820     extra_flags: []const []const u8 = &.{},
    821     /// Same as extra_flags except they are not added to the Cache hash.
    822     cache_exempt_flags: []const []const u8 = &.{},
    823     /// This field is non-null if and only if the language was explicitly set
    824     /// with "-x lang".
    825     ext: ?FileExt = null,
    826 };
    827 
    828 /// For passing to resinator.
    829 pub const RcSourceFile = struct {
    830     owner: *Package.Module,
    831     src_path: []const u8,
    832     extra_flags: []const []const u8 = &.{},
    833 };
    834 
    835 pub const RcIncludes = enum {
    836     /// Use MSVC if available, fall back to MinGW.
    837     any,
    838     /// Use MSVC include paths (MSVC install + Windows SDK, must be present on the system).
    839     msvc,
    840     /// Use MinGW include paths (distributed with Zig).
    841     gnu,
    842     /// Do not use any autodetected include paths.
    843     none,
    844 };
    845 
    846 const Job = union(enum) {
    847     /// Given the generated AIR for a function, put it onto the code generation queue.
    848     /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
    849     /// all types are resolved before the linker task is queued.
    850     /// If the backend does not support `Zcu.Feature.separate_thread`, codegen and linking happen immediately.
    851     codegen_func: struct {
    852         func: InternPool.Index,
    853         /// The AIR emitted from analyzing `func`; owned by this `Job` in `gpa`.
    854         air: Air,
    855     },
    856     /// Queue a `link.ZcuTask` to emit this non-function `Nav` into the output binary.
    857     /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
    858     /// all types are resolved before the linker task is queued.
    859     /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately.
    860     link_nav: InternPool.Nav.Index,
    861     /// Queue a `link.ZcuTask` to emit debug information for this container type.
    862     /// This `Job` exists (instead of the `link.ZcuTask` being directly queued) to ensure that
    863     /// all types are resolved before the linker task is queued.
    864     /// If the backend does not support `Zcu.Feature.separate_thread`, the task is run immediately.
    865     link_type: InternPool.Index,
    866     update_line_number: InternPool.TrackedInst.Index,
    867     /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed.
    868     /// This may be its first time being analyzed, or it may be outdated.
    869     /// If the unit is a test function, an `analyze_func` job will then be queued.
    870     analyze_comptime_unit: InternPool.AnalUnit,
    871     /// This function must be semantically analyzed.
    872     /// This may be its first time being analyzed, or it may be outdated.
    873     /// After analysis, a `codegen_func` job will be queued.
    874     /// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.
    875     /// This job is separate from `analyze_comptime_unit` because it has a different priority.
    876     analyze_func: InternPool.Index,
    877     /// The main source file for the module needs to be analyzed.
    878     analyze_mod: *Package.Module,
    879     /// Fully resolve the given `struct` or `union` type.
    880     resolve_type_fully: InternPool.Index,
    881 
    882     /// The value is the index into `windows_libs`.
    883     windows_import_lib: usize,
    884 
    885     const Tag = @typeInfo(Job).@"union".tag_type.?;
    886     fn stage(tag: Tag) usize {
    887         return switch (tag) {
    888             // Prioritize functions so that codegen can get to work on them on a
    889             // separate thread, while Sema goes back to its own work.
    890             .resolve_type_fully, .analyze_func, .codegen_func => 0,
    891             else => 1,
    892         };
    893     }
    894     comptime {
    895         // Job dependencies
    896         assert(stage(.resolve_type_fully) <= stage(.codegen_func));
    897     }
    898 };
    899 
    900 pub const CObject = struct {
    901     /// Relative to cwd. Owned by arena.
    902     src: CSourceFile,
    903     status: union(enum) {
    904         new,
    905         success: struct {
    906             /// The outputted result. `sub_path` owned by gpa.
    907             object_path: Cache.Path,
    908             /// This is a file system lock on the cache hash manifest representing this
    909             /// object. It prevents other invocations of the Zig compiler from interfering
    910             /// with this object until released.
    911             lock: Cache.Lock,
    912         },
    913         /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects.
    914         failure,
    915         /// A transient failure happened when trying to compile the C Object; it may
    916         /// succeed if we try again. There may be a corresponding ErrorMsg in
    917         /// Compilation.failed_c_objects. If there is not, the failure is out of memory.
    918         failure_retryable,
    919     },
    920 
    921     pub const Diag = struct {
    922         level: u32 = 0,
    923         category: u32 = 0,
    924         msg: []const u8 = &.{},
    925         src_loc: SrcLoc = .{},
    926         src_ranges: []const SrcRange = &.{},
    927         sub_diags: []const Diag = &.{},
    928 
    929         pub const SrcLoc = struct {
    930             file: u32 = 0,
    931             line: u32 = 0,
    932             column: u32 = 0,
    933             offset: u32 = 0,
    934         };
    935 
    936         pub const SrcRange = struct {
    937             start: SrcLoc = .{},
    938             end: SrcLoc = .{},
    939         };
    940 
    941         pub fn deinit(diag: *Diag, gpa: Allocator) void {
    942             gpa.free(diag.msg);
    943             gpa.free(diag.src_ranges);
    944             for (diag.sub_diags) |sub_diag| {
    945                 var sub_diag_mut = sub_diag;
    946                 sub_diag_mut.deinit(gpa);
    947             }
    948             gpa.free(diag.sub_diags);
    949             diag.* = undefined;
    950         }
    951 
    952         pub fn count(diag: Diag) u32 {
    953             var total: u32 = 1;
    954             for (diag.sub_diags) |sub_diag| total += sub_diag.count();
    955             return total;
    956         }
    957 
    958         pub fn addToErrorBundle(diag: Diag, eb: *ErrorBundle.Wip, bundle: Bundle, note: *u32) !void {
    959             const err_msg = try eb.addErrorMessage(try diag.toErrorMessage(eb, bundle, 0));
    960             eb.extra.items[note.*] = @intFromEnum(err_msg);
    961             note.* += 1;
    962             for (diag.sub_diags) |sub_diag| try sub_diag.addToErrorBundle(eb, bundle, note);
    963         }
    964 
    965         pub fn toErrorMessage(
    966             diag: Diag,
    967             eb: *ErrorBundle.Wip,
    968             bundle: Bundle,
    969             notes_len: u32,
    970         ) !ErrorBundle.ErrorMessage {
    971             var start = diag.src_loc.offset;
    972             var end = diag.src_loc.offset;
    973             for (diag.src_ranges) |src_range| {
    974                 if (src_range.start.file == diag.src_loc.file and
    975                     src_range.start.line == diag.src_loc.line)
    976                 {
    977                     start = @min(src_range.start.offset, start);
    978                 }
    979                 if (src_range.end.file == diag.src_loc.file and
    980                     src_range.end.line == diag.src_loc.line)
    981                 {
    982                     end = @max(src_range.end.offset, end);
    983                 }
    984             }
    985 
    986             const file_name = bundle.file_names.get(diag.src_loc.file) orelse "";
    987             const source_line = source_line: {
    988                 if (diag.src_loc.offset == 0 or diag.src_loc.column == 0) break :source_line 0;
    989 
    990                 const file = std.fs.cwd().openFile(file_name, .{}) catch break :source_line 0;
    991                 defer file.close();
    992                 file.seekTo(diag.src_loc.offset + 1 - diag.src_loc.column) catch break :source_line 0;
    993 
    994                 var line = std.ArrayList(u8).init(eb.gpa);
    995                 defer line.deinit();
    996                 file.reader().readUntilDelimiterArrayList(&line, '\n', 1 << 10) catch break :source_line 0;
    997 
    998                 break :source_line try eb.addString(line.items);
    999             };
   1000 
   1001             return .{
   1002                 .msg = try eb.addString(diag.msg),
   1003                 .src_loc = try eb.addSourceLocation(.{
   1004                     .src_path = try eb.addString(file_name),
   1005                     .line = diag.src_loc.line -| 1,
   1006                     .column = diag.src_loc.column -| 1,
   1007                     .span_start = start,
   1008                     .span_main = diag.src_loc.offset,
   1009                     .span_end = end + 1,
   1010                     .source_line = source_line,
   1011                 }),
   1012                 .notes_len = notes_len,
   1013             };
   1014         }
   1015 
   1016         pub const Bundle = struct {
   1017             file_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty,
   1018             category_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty,
   1019             diags: []Diag = &.{},
   1020 
   1021             pub fn destroy(bundle: *Bundle, gpa: Allocator) void {
   1022                 for (bundle.file_names.values()) |file_name| gpa.free(file_name);
   1023                 for (bundle.category_names.values()) |category_name| gpa.free(category_name);
   1024                 for (bundle.diags) |*diag| diag.deinit(gpa);
   1025                 gpa.free(bundle.diags);
   1026                 gpa.destroy(bundle);
   1027             }
   1028 
   1029             pub fn parse(gpa: Allocator, path: []const u8) !*Bundle {
   1030                 const BlockId = enum(u32) {
   1031                     Meta = 8,
   1032                     Diag,
   1033                     _,
   1034                 };
   1035                 const RecordId = enum(u32) {
   1036                     Version = 1,
   1037                     DiagInfo,
   1038                     SrcRange,
   1039                     DiagFlag,
   1040                     CatName,
   1041                     FileName,
   1042                     FixIt,
   1043                     _,
   1044                 };
   1045                 const WipDiag = struct {
   1046                     level: u32 = 0,
   1047                     category: u32 = 0,
   1048                     msg: []const u8 = &.{},
   1049                     src_loc: SrcLoc = .{},
   1050                     src_ranges: std.ArrayListUnmanaged(SrcRange) = .empty,
   1051                     sub_diags: std.ArrayListUnmanaged(Diag) = .empty,
   1052 
   1053                     fn deinit(wip_diag: *@This(), allocator: Allocator) void {
   1054                         allocator.free(wip_diag.msg);
   1055                         wip_diag.src_ranges.deinit(allocator);
   1056                         for (wip_diag.sub_diags.items) |*sub_diag| sub_diag.deinit(allocator);
   1057                         wip_diag.sub_diags.deinit(allocator);
   1058                         wip_diag.* = undefined;
   1059                     }
   1060                 };
   1061 
   1062                 const file = try std.fs.cwd().openFile(path, .{});
   1063                 defer file.close();
   1064                 var br = std.io.bufferedReader(file.reader());
   1065                 const reader = br.reader();
   1066                 var bc = std.zig.llvm.BitcodeReader.init(gpa, .{ .reader = reader.any() });
   1067                 defer bc.deinit();
   1068 
   1069                 var file_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty;
   1070                 errdefer {
   1071                     for (file_names.values()) |file_name| gpa.free(file_name);
   1072                     file_names.deinit(gpa);
   1073                 }
   1074 
   1075                 var category_names: std.AutoArrayHashMapUnmanaged(u32, []const u8) = .empty;
   1076                 errdefer {
   1077                     for (category_names.values()) |category_name| gpa.free(category_name);
   1078                     category_names.deinit(gpa);
   1079                 }
   1080 
   1081                 var stack: std.ArrayListUnmanaged(WipDiag) = .empty;
   1082                 defer {
   1083                     for (stack.items) |*wip_diag| wip_diag.deinit(gpa);
   1084                     stack.deinit(gpa);
   1085                 }
   1086                 try stack.append(gpa, .{});
   1087 
   1088                 try bc.checkMagic("DIAG");
   1089                 while (try bc.next()) |item| switch (item) {
   1090                     .start_block => |block| switch (@as(BlockId, @enumFromInt(block.id))) {
   1091                         .Meta => if (stack.items.len > 0) try bc.skipBlock(block),
   1092                         .Diag => try stack.append(gpa, .{}),
   1093                         _ => try bc.skipBlock(block),
   1094                     },
   1095                     .record => |record| switch (@as(RecordId, @enumFromInt(record.id))) {
   1096                         .Version => if (record.operands[0] != 2) return error.InvalidVersion,
   1097                         .DiagInfo => {
   1098                             const top = &stack.items[stack.items.len - 1];
   1099                             top.level = @intCast(record.operands[0]);
   1100                             top.src_loc = .{
   1101                                 .file = @intCast(record.operands[1]),
   1102                                 .line = @intCast(record.operands[2]),
   1103                                 .column = @intCast(record.operands[3]),
   1104                                 .offset = @intCast(record.operands[4]),
   1105                             };
   1106                             top.category = @intCast(record.operands[5]);
   1107                             top.msg = try gpa.dupe(u8, record.blob);
   1108                         },
   1109                         .SrcRange => try stack.items[stack.items.len - 1].src_ranges.append(gpa, .{
   1110                             .start = .{
   1111                                 .file = @intCast(record.operands[0]),
   1112                                 .line = @intCast(record.operands[1]),
   1113                                 .column = @intCast(record.operands[2]),
   1114                                 .offset = @intCast(record.operands[3]),
   1115                             },
   1116                             .end = .{
   1117                                 .file = @intCast(record.operands[4]),
   1118                                 .line = @intCast(record.operands[5]),
   1119                                 .column = @intCast(record.operands[6]),
   1120                                 .offset = @intCast(record.operands[7]),
   1121                             },
   1122                         }),
   1123                         .DiagFlag => {},
   1124                         .CatName => {
   1125                             try category_names.ensureUnusedCapacity(gpa, 1);
   1126                             category_names.putAssumeCapacity(
   1127                                 @intCast(record.operands[0]),
   1128                                 try gpa.dupe(u8, record.blob),
   1129                             );
   1130                         },
   1131                         .FileName => {
   1132                             try file_names.ensureUnusedCapacity(gpa, 1);
   1133                             file_names.putAssumeCapacity(
   1134                                 @intCast(record.operands[0]),
   1135                                 try gpa.dupe(u8, record.blob),
   1136                             );
   1137                         },
   1138                         .FixIt => {},
   1139                         _ => {},
   1140                     },
   1141                     .end_block => |block| switch (@as(BlockId, @enumFromInt(block.id))) {
   1142                         .Meta => {},
   1143                         .Diag => {
   1144                             var wip_diag = stack.pop().?;
   1145                             errdefer wip_diag.deinit(gpa);
   1146 
   1147                             const src_ranges = try wip_diag.src_ranges.toOwnedSlice(gpa);
   1148                             errdefer gpa.free(src_ranges);
   1149 
   1150                             const sub_diags = try wip_diag.sub_diags.toOwnedSlice(gpa);
   1151                             errdefer {
   1152                                 for (sub_diags) |*sub_diag| sub_diag.deinit(gpa);
   1153                                 gpa.free(sub_diags);
   1154                             }
   1155 
   1156                             try stack.items[stack.items.len - 1].sub_diags.append(gpa, .{
   1157                                 .level = wip_diag.level,
   1158                                 .category = wip_diag.category,
   1159                                 .msg = wip_diag.msg,
   1160                                 .src_loc = wip_diag.src_loc,
   1161                                 .src_ranges = src_ranges,
   1162                                 .sub_diags = sub_diags,
   1163                             });
   1164                         },
   1165                         _ => {},
   1166                     },
   1167                 };
   1168 
   1169                 const bundle = try gpa.create(Bundle);
   1170                 assert(stack.items.len == 1);
   1171                 bundle.* = .{
   1172                     .file_names = file_names,
   1173                     .category_names = category_names,
   1174                     .diags = try stack.items[0].sub_diags.toOwnedSlice(gpa),
   1175                 };
   1176                 return bundle;
   1177             }
   1178 
   1179             pub fn addToErrorBundle(bundle: Bundle, eb: *ErrorBundle.Wip) !void {
   1180                 for (bundle.diags) |diag| {
   1181                     const notes_len = diag.count() - 1;
   1182                     try eb.addRootErrorMessage(try diag.toErrorMessage(eb, bundle, notes_len));
   1183                     if (notes_len > 0) {
   1184                         var note = try eb.reserveNotes(notes_len);
   1185                         for (diag.sub_diags) |sub_diag|
   1186                             try sub_diag.addToErrorBundle(eb, bundle, &note);
   1187                     }
   1188                 }
   1189             }
   1190         };
   1191     };
   1192 
   1193     /// Returns if there was failure.
   1194     pub fn clearStatus(self: *CObject, gpa: Allocator) bool {
   1195         switch (self.status) {
   1196             .new => return false,
   1197             .failure, .failure_retryable => {
   1198                 self.status = .new;
   1199                 return true;
   1200             },
   1201             .success => |*success| {
   1202                 gpa.free(success.object_path.sub_path);
   1203                 success.lock.release();
   1204                 self.status = .new;
   1205                 return false;
   1206             },
   1207         }
   1208     }
   1209 
   1210     pub fn destroy(self: *CObject, gpa: Allocator) void {
   1211         _ = self.clearStatus(gpa);
   1212         gpa.destroy(self);
   1213     }
   1214 };
   1215 
   1216 pub const Win32Resource = struct {
   1217     /// Relative to cwd. Owned by arena.
   1218     src: union(enum) {
   1219         rc: RcSourceFile,
   1220         manifest: []const u8,
   1221     },
   1222     status: union(enum) {
   1223         new,
   1224         success: struct {
   1225             /// The outputted result. Owned by gpa.
   1226             res_path: []u8,
   1227             /// This is a file system lock on the cache hash manifest representing this
   1228             /// object. It prevents other invocations of the Zig compiler from interfering
   1229             /// with this object until released.
   1230             lock: Cache.Lock,
   1231         },
   1232         /// There will be a corresponding ErrorMsg in Compilation.failed_win32_resources.
   1233         failure,
   1234         /// A transient failure happened when trying to compile the resource file; it may
   1235         /// succeed if we try again. There may be a corresponding ErrorMsg in
   1236         /// Compilation.failed_win32_resources. If there is not, the failure is out of memory.
   1237         failure_retryable,
   1238     },
   1239 
   1240     /// Returns true if there was failure.
   1241     pub fn clearStatus(self: *Win32Resource, gpa: Allocator) bool {
   1242         switch (self.status) {
   1243             .new => return false,
   1244             .failure, .failure_retryable => {
   1245                 self.status = .new;
   1246                 return true;
   1247             },
   1248             .success => |*success| {
   1249                 gpa.free(success.res_path);
   1250                 success.lock.release();
   1251                 self.status = .new;
   1252                 return false;
   1253             },
   1254         }
   1255     }
   1256 
   1257     pub fn destroy(self: *Win32Resource, gpa: Allocator) void {
   1258         _ = self.clearStatus(gpa);
   1259         gpa.destroy(self);
   1260     }
   1261 };
   1262 
   1263 pub const MiscTask = enum {
   1264     write_builtin_zig,
   1265     rename_results,
   1266     check_whole_cache,
   1267     glibc_crt_file,
   1268     glibc_shared_objects,
   1269     musl_crt_file,
   1270     freebsd_crt_file,
   1271     freebsd_shared_objects,
   1272     netbsd_crt_file,
   1273     netbsd_shared_objects,
   1274     mingw_crt_file,
   1275     windows_import_lib,
   1276     libunwind,
   1277     libcxx,
   1278     libcxxabi,
   1279     libtsan,
   1280     libubsan,
   1281     libfuzzer,
   1282     wasi_libc_crt_file,
   1283     compiler_rt,
   1284     libzigc,
   1285     analyze_mod,
   1286     docs_copy,
   1287     docs_wasm,
   1288 
   1289     @"musl crt1.o",
   1290     @"musl rcrt1.o",
   1291     @"musl Scrt1.o",
   1292     @"musl libc.a",
   1293     @"musl libc.so",
   1294 
   1295     @"wasi crt1-reactor.o",
   1296     @"wasi crt1-command.o",
   1297     @"wasi libc.a",
   1298     @"wasi libdl.a",
   1299     @"libwasi-emulated-process-clocks.a",
   1300     @"libwasi-emulated-getpid.a",
   1301     @"libwasi-emulated-mman.a",
   1302     @"libwasi-emulated-signal.a",
   1303 
   1304     @"glibc Scrt1.o",
   1305     @"glibc libc_nonshared.a",
   1306     @"glibc shared object",
   1307 
   1308     @"freebsd libc Scrt1.o",
   1309     @"freebsd libc shared object",
   1310 
   1311     @"netbsd libc Scrt0.o",
   1312     @"netbsd libc shared object",
   1313 
   1314     @"mingw-w64 crt2.o",
   1315     @"mingw-w64 dllcrt2.o",
   1316     @"mingw-w64 libmingw32.lib",
   1317 };
   1318 
   1319 pub const MiscError = struct {
   1320     /// Allocated with gpa.
   1321     msg: []u8,
   1322     children: ?ErrorBundle = null,
   1323 
   1324     pub fn deinit(misc_err: *MiscError, gpa: Allocator) void {
   1325         gpa.free(misc_err.msg);
   1326         if (misc_err.children) |*children| {
   1327             children.deinit(gpa);
   1328         }
   1329         misc_err.* = undefined;
   1330     }
   1331 };
   1332 
   1333 pub const cache_helpers = struct {
   1334     pub fn addModule(hh: *Cache.HashHelper, mod: *const Package.Module) void {
   1335         addResolvedTarget(hh, mod.resolved_target);
   1336         hh.add(mod.optimize_mode);
   1337         hh.add(mod.code_model);
   1338         hh.add(mod.single_threaded);
   1339         hh.add(mod.error_tracing);
   1340         hh.add(mod.valgrind);
   1341         hh.add(mod.pic);
   1342         hh.add(mod.strip);
   1343         hh.add(mod.omit_frame_pointer);
   1344         hh.add(mod.stack_check);
   1345         hh.add(mod.red_zone);
   1346         hh.add(mod.sanitize_c);
   1347         hh.add(mod.sanitize_thread);
   1348         hh.add(mod.fuzz);
   1349         hh.add(mod.unwind_tables);
   1350         hh.add(mod.structured_cfg);
   1351         hh.add(mod.no_builtin);
   1352         hh.addListOfBytes(mod.cc_argv);
   1353     }
   1354 
   1355     pub fn addResolvedTarget(
   1356         hh: *Cache.HashHelper,
   1357         resolved_target: Package.Module.ResolvedTarget,
   1358     ) void {
   1359         const target = resolved_target.result;
   1360         hh.add(target.cpu.arch);
   1361         hh.addBytes(target.cpu.model.name);
   1362         hh.add(target.cpu.features.ints);
   1363         hh.add(target.os.tag);
   1364         hh.add(target.os.versionRange());
   1365         hh.add(target.abi);
   1366         hh.add(target.ofmt);
   1367         hh.add(resolved_target.is_native_os);
   1368         hh.add(resolved_target.is_native_abi);
   1369         hh.add(resolved_target.is_explicit_dynamic_linker);
   1370     }
   1371 
   1372     pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void {
   1373         hh.add(x != null);
   1374         addDebugFormat(hh, x orelse return);
   1375     }
   1376 
   1377     pub fn addDebugFormat(hh: *Cache.HashHelper, x: Config.DebugFormat) void {
   1378         const tag: @typeInfo(Config.DebugFormat).@"union".tag_type.? = x;
   1379         hh.add(tag);
   1380         switch (x) {
   1381             .strip, .code_view => {},
   1382             .dwarf => |f| hh.add(f),
   1383         }
   1384     }
   1385 
   1386     pub fn hashCSource(self: *Cache.Manifest, c_source: CSourceFile) !void {
   1387         _ = try self.addFile(c_source.src_path, null);
   1388         // Hash the extra flags, with special care to call addFile for file parameters.
   1389         // TODO this logic can likely be improved by utilizing clang_options_data.zig.
   1390         const file_args = [_][]const u8{"-include"};
   1391         var arg_i: usize = 0;
   1392         while (arg_i < c_source.extra_flags.len) : (arg_i += 1) {
   1393             const arg = c_source.extra_flags[arg_i];
   1394             self.hash.addBytes(arg);
   1395             for (file_args) |file_arg| {
   1396                 if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_source.extra_flags.len) {
   1397                     arg_i += 1;
   1398                     _ = try self.addFile(c_source.extra_flags[arg_i], null);
   1399                 }
   1400             }
   1401         }
   1402     }
   1403 };
   1404 
   1405 pub const ClangPreprocessorMode = enum {
   1406     no,
   1407     /// This means we are doing `zig cc -E -o <path>`.
   1408     yes,
   1409     /// This means we are doing `zig cc -E`.
   1410     stdout,
   1411     /// precompiled C header
   1412     pch,
   1413 };
   1414 
   1415 pub const Framework = link.File.MachO.Framework;
   1416 pub const SystemLib = link.SystemLib;
   1417 
   1418 pub const CacheMode = enum {
   1419     /// The results of this compilation are not cached. The compilation is always performed, and the
   1420     /// results are emitted directly to their output locations. Temporary files will be placed in a
   1421     /// temporary directory in the cache, but deleted after the compilation is done.
   1422     ///
   1423     /// This mode is typically used for direct CLI invocations like `zig build-exe`, because such
   1424     /// processes are typically low-level usages which would not make efficient use of the cache.
   1425     none,
   1426     /// The compilation is cached based only on the options given when creating the `Compilation`.
   1427     /// In particular, Zig source file contents are not included in the cache manifest. This mode
   1428     /// allows incremental compilation, because the old cached compilation state can be restored
   1429     /// and the old binary patched up with the changes. All files, including temporary files, are
   1430     /// stored in the cache directory like '<cache>/o/<hash>/'. Temporary files are not deleted.
   1431     ///
   1432     /// At the time of writing, incremental compilation is only supported with the `-fincremental`
   1433     /// command line flag, so this mode is rarely used. However, it is required in order to use
   1434     /// incremental compilation.
   1435     incremental,
   1436     /// The compilation is cached based on the `Compilation` options and every input, including Zig
   1437     /// source files, linker inputs, and `@embedFile` targets. If any of them change, we will see a
   1438     /// cache miss, and the entire compilation will be re-run. On a cache miss, we initially write
   1439     /// all output files to a directory under '<cache>/tmp/', because we don't know the final
   1440     /// manifest digest until the update is almost done. Once we can compute the final digest, this
   1441     /// directory is moved to '<cache>/o/<hash>/'. Temporary files are not deleted.
   1442     ///
   1443     /// At the time of writing, this is the most commonly used cache mode: it is used by the build
   1444     /// system (and any other parent using `--listen`) unless incremental compilation is enabled.
   1445     /// Once incremental compilation is more mature, it will be replaced by `incremental` in many
   1446     /// cases, but still has use cases, such as for release binaries, particularly globally cached
   1447     /// artifacts like compiler_rt.
   1448     whole,
   1449 };
   1450 
   1451 pub const ParentWholeCache = struct {
   1452     manifest: *Cache.Manifest,
   1453     mutex: *std.Thread.Mutex,
   1454     prefix_map: [4]u8,
   1455 };
   1456 
   1457 const CacheUse = union(CacheMode) {
   1458     none: *None,
   1459     incremental: *Incremental,
   1460     whole: *Whole,
   1461 
   1462     const None = struct {
   1463         /// User-requested artifacts are written directly to their output path in this cache mode.
   1464         /// However, if we need to emit any temporary files, they are placed in this directory.
   1465         /// We will recursively delete this directory at the end of this update. This field is
   1466         /// non-`null` only inside `update`.
   1467         tmp_artifact_directory: ?Cache.Directory,
   1468     };
   1469 
   1470     const Incremental = struct {
   1471         /// All output files, including artifacts and incremental compilation metadata, are placed
   1472         /// in this directory, which is some 'o/<hash>' in a cache directory.
   1473         artifact_directory: Cache.Directory,
   1474     };
   1475 
   1476     const Whole = struct {
   1477         /// Since we don't open the output file until `update`, we must save these options for then.
   1478         lf_open_opts: link.File.OpenOptions,
   1479         /// This is a pointer to a local variable inside `update`.
   1480         cache_manifest: ?*Cache.Manifest,
   1481         cache_manifest_mutex: std.Thread.Mutex,
   1482         /// This is non-`null` for most of the body of `update`. It is the temporary directory which
   1483         /// we initially emit our artifacts to. After the main part of the update is done, it will
   1484         /// be closed and moved to its final location, and this field set to `null`.
   1485         tmp_artifact_directory: ?Cache.Directory,
   1486         /// Prevents other processes from clobbering files in the output directory.
   1487         lock: ?Cache.Lock,
   1488 
   1489         fn releaseLock(whole: *Whole) void {
   1490             if (whole.lock) |*lock| {
   1491                 lock.release();
   1492                 whole.lock = null;
   1493             }
   1494         }
   1495 
   1496         fn moveLock(whole: *Whole) Cache.Lock {
   1497             const result = whole.lock.?;
   1498             whole.lock = null;
   1499             return result;
   1500         }
   1501     };
   1502 
   1503     fn deinit(cu: CacheUse) void {
   1504         switch (cu) {
   1505             .none => |none| {
   1506                 assert(none.tmp_artifact_directory == null);
   1507             },
   1508             .incremental => |incremental| {
   1509                 incremental.artifact_directory.handle.close();
   1510             },
   1511             .whole => |whole| {
   1512                 assert(whole.tmp_artifact_directory == null);
   1513                 whole.releaseLock();
   1514             },
   1515         }
   1516     }
   1517 };
   1518 
   1519 pub const CreateOptions = struct {
   1520     dirs: Directories,
   1521     thread_pool: *ThreadPool,
   1522     self_exe_path: ?[]const u8 = null,
   1523 
   1524     /// Options that have been resolved by calling `resolveDefaults`.
   1525     config: Compilation.Config,
   1526 
   1527     root_mod: *Package.Module,
   1528     /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig
   1529     /// test`, in which `root_mod` is the test runner, and `main_mod` is the
   1530     /// user's source file which has the tests.
   1531     main_mod: ?*Package.Module = null,
   1532     /// This is provided so that the API user has a chance to tweak the
   1533     /// per-module settings of the standard library.
   1534     /// When this is null, a default configuration of the std lib is created
   1535     /// based on the settings of root_mod.
   1536     std_mod: ?*Package.Module = null,
   1537     root_name: []const u8,
   1538     sysroot: ?[]const u8 = null,
   1539     cache_mode: CacheMode,
   1540     emit_h: Emit = .no,
   1541     emit_bin: Emit,
   1542     emit_asm: Emit = .no,
   1543     emit_implib: Emit = .no,
   1544     emit_llvm_ir: Emit = .no,
   1545     emit_llvm_bc: Emit = .no,
   1546     emit_docs: Emit = .no,
   1547     /// This field is intended to be removed.
   1548     /// The ELF implementation no longer uses this data, however the MachO and COFF
   1549     /// implementations still do.
   1550     lib_directories: []const Cache.Directory = &.{},
   1551     rpath_list: []const []const u8 = &[0][]const u8{},
   1552     symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .empty,
   1553     c_source_files: []const CSourceFile = &.{},
   1554     rc_source_files: []const RcSourceFile = &.{},
   1555     manifest_file: ?[]const u8 = null,
   1556     rc_includes: RcIncludes = .any,
   1557     link_inputs: []const link.Input = &.{},
   1558     framework_dirs: []const []const u8 = &[0][]const u8{},
   1559     frameworks: []const Framework = &.{},
   1560     windows_lib_names: []const []const u8 = &.{},
   1561     /// These correspond to the WASI libc emulated subcomponents including:
   1562     /// * process clocks
   1563     /// * getpid
   1564     /// * mman
   1565     /// * signal
   1566     wasi_emulated_libs: []const wasi_libc.CrtFile = &.{},
   1567     /// This means that if the output mode is an executable it will be a
   1568     /// Position Independent Executable. If the output mode is not an
   1569     /// executable this field is ignored.
   1570     want_compiler_rt: ?bool = null,
   1571     want_ubsan_rt: ?bool = null,
   1572     function_sections: bool = false,
   1573     data_sections: bool = false,
   1574     time_report: bool = false,
   1575     stack_report: bool = false,
   1576     link_eh_frame_hdr: bool = false,
   1577     link_emit_relocs: bool = false,
   1578     linker_script: ?[]const u8 = null,
   1579     version_script: ?[]const u8 = null,
   1580     linker_allow_undefined_version: bool = false,
   1581     linker_enable_new_dtags: ?bool = null,
   1582     soname: ?[]const u8 = null,
   1583     linker_gc_sections: ?bool = null,
   1584     linker_repro: ?bool = null,
   1585     linker_allow_shlib_undefined: ?bool = null,
   1586     linker_bind_global_refs_locally: ?bool = null,
   1587     linker_import_symbols: bool = false,
   1588     linker_import_table: bool = false,
   1589     linker_export_table: bool = false,
   1590     linker_initial_memory: ?u64 = null,
   1591     linker_max_memory: ?u64 = null,
   1592     linker_global_base: ?u64 = null,
   1593     linker_export_symbol_names: []const []const u8 = &.{},
   1594     linker_print_gc_sections: bool = false,
   1595     linker_print_icf_sections: bool = false,
   1596     linker_print_map: bool = false,
   1597     llvm_opt_bisect_limit: i32 = -1,
   1598     build_id: ?std.zig.BuildId = null,
   1599     disable_c_depfile: bool = false,
   1600     linker_z_nodelete: bool = false,
   1601     linker_z_notext: bool = false,
   1602     linker_z_defs: bool = false,
   1603     linker_z_origin: bool = false,
   1604     linker_z_now: bool = true,
   1605     linker_z_relro: bool = true,
   1606     linker_z_nocopyreloc: bool = false,
   1607     linker_z_common_page_size: ?u64 = null,
   1608     linker_z_max_page_size: ?u64 = null,
   1609     linker_tsaware: bool = false,
   1610     linker_nxcompat: bool = false,
   1611     linker_dynamicbase: bool = true,
   1612     linker_compress_debug_sections: ?link.File.Lld.Elf.CompressDebugSections = null,
   1613     linker_module_definition_file: ?[]const u8 = null,
   1614     linker_sort_section: ?link.File.Lld.Elf.SortSection = null,
   1615     major_subsystem_version: ?u16 = null,
   1616     minor_subsystem_version: ?u16 = null,
   1617     clang_passthrough_mode: bool = false,
   1618     verbose_cc: bool = false,
   1619     verbose_link: bool = false,
   1620     verbose_air: bool = false,
   1621     verbose_intern_pool: bool = false,
   1622     verbose_generic_instances: bool = false,
   1623     verbose_llvm_ir: ?[]const u8 = null,
   1624     verbose_llvm_bc: ?[]const u8 = null,
   1625     verbose_cimport: bool = false,
   1626     verbose_llvm_cpu_features: bool = false,
   1627     debug_compiler_runtime_libs: bool = false,
   1628     debug_compile_errors: bool = false,
   1629     debug_incremental: bool = false,
   1630     incremental: bool = false,
   1631     /// Normally when you create a `Compilation`, Zig will automatically build
   1632     /// and link in required dependencies, such as compiler-rt and libc. When
   1633     /// building such dependencies themselves, this flag must be set to avoid
   1634     /// infinite recursion.
   1635     skip_linker_dependencies: bool = false,
   1636     hash_style: link.File.Lld.Elf.HashStyle = .both,
   1637     entry: Entry = .default,
   1638     force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .empty,
   1639     stack_size: ?u64 = null,
   1640     image_base: ?u64 = null,
   1641     version: ?std.SemanticVersion = null,
   1642     compatibility_version: ?std.SemanticVersion = null,
   1643     libc_installation: ?*const LibCInstallation = null,
   1644     native_system_include_paths: []const []const u8 = &.{},
   1645     clang_preprocessor_mode: ClangPreprocessorMode = .no,
   1646     reference_trace: ?u32 = null,
   1647     test_filters: []const []const u8 = &.{},
   1648     test_name_prefix: ?[]const u8 = null,
   1649     test_runner_path: ?[]const u8 = null,
   1650     subsystem: ?std.Target.SubSystem = null,
   1651     mingw_unicode_entry_point: bool = false,
   1652     /// (Zig compiler development) Enable dumping linker's state as JSON.
   1653     enable_link_snapshots: bool = false,
   1654     /// (Darwin) Install name of the dylib
   1655     install_name: ?[]const u8 = null,
   1656     /// (Darwin) Path to entitlements file
   1657     entitlements: ?[]const u8 = null,
   1658     /// (Darwin) size of the __PAGEZERO segment
   1659     pagezero_size: ?u64 = null,
   1660     /// (Darwin) set minimum space for future expansion of the load commands
   1661     headerpad_size: ?u32 = null,
   1662     /// (Darwin) set enough space as if all paths were MATPATHLEN
   1663     headerpad_max_install_names: bool = false,
   1664     /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
   1665     dead_strip_dylibs: bool = false,
   1666     /// (Darwin) Force load all members of static archives that implement an Objective-C class or category
   1667     force_load_objc: bool = false,
   1668     /// Whether local symbols should be discarded from the symbol table.
   1669     discard_local_symbols: bool = false,
   1670     /// (Windows) PDB source path prefix to instruct the linker how to resolve relative
   1671     /// paths when consolidating CodeView streams into a single PDB file.
   1672     pdb_source_path: ?[]const u8 = null,
   1673     /// (Windows) PDB output path
   1674     pdb_out_path: ?[]const u8 = null,
   1675     error_limit: ?Zcu.ErrorInt = null,
   1676     global_cc_argv: []const []const u8 = &.{},
   1677 
   1678     /// Tracks all files that can cause the Compilation to be invalidated and need a rebuild.
   1679     file_system_inputs: ?*std.ArrayListUnmanaged(u8) = null,
   1680 
   1681     parent_whole_cache: ?ParentWholeCache = null,
   1682 
   1683     pub const Entry = link.File.OpenOptions.Entry;
   1684 
   1685     /// Which fields are valid depends on the `cache_mode` given.
   1686     pub const Emit = union(enum) {
   1687         /// Do not emit this file. Always valid.
   1688         no,
   1689         /// Emit this file into its default name in the cache directory.
   1690         /// Requires `cache_mode` to not be `.none`.
   1691         yes_cache,
   1692         /// Emit this file to the given path (absolute or cwd-relative).
   1693         /// Requires `cache_mode` to be `.none`.
   1694         yes_path: []const u8,
   1695 
   1696         fn resolve(emit: Emit, arena: Allocator, opts: *const CreateOptions, ea: std.zig.EmitArtifact) Allocator.Error!?[]const u8 {
   1697             switch (emit) {
   1698                 .no => return null,
   1699                 .yes_cache => {
   1700                     assert(opts.cache_mode != .none);
   1701                     return try ea.cacheName(arena, .{
   1702                         .root_name = opts.root_name,
   1703                         .target = opts.root_mod.resolved_target.result,
   1704                         .output_mode = opts.config.output_mode,
   1705                         .link_mode = opts.config.link_mode,
   1706                         .version = opts.version,
   1707                     });
   1708                 },
   1709                 .yes_path => |path| {
   1710                     assert(opts.cache_mode == .none);
   1711                     return try arena.dupe(u8, path);
   1712                 },
   1713             }
   1714         }
   1715     };
   1716 };
   1717 
   1718 fn addModuleTableToCacheHash(
   1719     zcu: *Zcu,
   1720     arena: Allocator,
   1721     hash: *Cache.HashHelper,
   1722     hash_type: union(enum) { path_bytes, files: *Cache.Manifest },
   1723 ) error{
   1724     OutOfMemory,
   1725     Unexpected,
   1726     CurrentWorkingDirectoryUnlinked,
   1727 }!void {
   1728     assert(zcu.module_roots.count() != 0); // module_roots is populated
   1729 
   1730     for (zcu.module_roots.keys(), zcu.module_roots.values()) |mod, opt_mod_root_file| {
   1731         if (mod == zcu.std_mod) continue; // redundant
   1732         if (opt_mod_root_file.unwrap()) |mod_root_file| {
   1733             if (zcu.fileByIndex(mod_root_file).is_builtin) continue; // redundant
   1734         }
   1735         cache_helpers.addModule(hash, mod);
   1736         switch (hash_type) {
   1737             .path_bytes => {
   1738                 hash.add(mod.root.root);
   1739                 hash.addBytes(mod.root.sub_path);
   1740                 hash.addBytes(mod.root_src_path);
   1741             },
   1742             .files => |man| if (mod.root_src_path.len != 0) {
   1743                 const root_src_path = try mod.root.toCachePath(zcu.comp.dirs).join(arena, mod.root_src_path);
   1744                 _ = try man.addFilePath(root_src_path, null);
   1745             },
   1746         }
   1747         hash.addListOfBytes(mod.deps.keys());
   1748     }
   1749 }
   1750 
   1751 const RtStrat = enum { none, lib, obj, zcu };
   1752 
   1753 pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compilation {
   1754     const output_mode = options.config.output_mode;
   1755     const is_dyn_lib = switch (output_mode) {
   1756         .Obj, .Exe => false,
   1757         .Lib => options.config.link_mode == .dynamic,
   1758     };
   1759     const is_exe_or_dyn_lib = switch (output_mode) {
   1760         .Obj => false,
   1761         .Lib => is_dyn_lib,
   1762         .Exe => true,
   1763     };
   1764 
   1765     if (options.linker_export_table and options.linker_import_table) {
   1766         return error.ExportTableAndImportTableConflict;
   1767     }
   1768 
   1769     const have_zcu = options.config.have_zcu;
   1770 
   1771     const comp: *Compilation = comp: {
   1772         // We put the `Compilation` itself in the arena. Freeing the arena will free the module.
   1773         // It's initialized later after we prepare the initialization options.
   1774         const root_name = try arena.dupeZ(u8, options.root_name);
   1775 
   1776         const use_llvm = options.config.use_llvm;
   1777 
   1778         // The "any" values provided by resolved config only account for
   1779         // explicitly-provided settings. We now make them additionally account
   1780         // for default setting resolution.
   1781         const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables != .none;
   1782         const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded;
   1783         const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread;
   1784         const any_sanitize_c: std.zig.SanitizeC = switch (options.config.any_sanitize_c) {
   1785             .off => options.root_mod.sanitize_c,
   1786             .trap => if (options.root_mod.sanitize_c == .full)
   1787                 .full
   1788             else
   1789                 .trap,
   1790             .full => .full,
   1791         };
   1792         const any_fuzz = options.config.any_fuzz or options.root_mod.fuzz;
   1793 
   1794         const link_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables;
   1795         const build_id = options.build_id orelse .none;
   1796 
   1797         const link_libc = options.config.link_libc;
   1798 
   1799         const libc_dirs = try std.zig.LibCDirs.detect(
   1800             arena,
   1801             options.dirs.zig_lib.path.?,
   1802             options.root_mod.resolved_target.result,
   1803             options.root_mod.resolved_target.is_native_abi,
   1804             link_libc,
   1805             options.libc_installation,
   1806         );
   1807 
   1808         const sysroot = options.sysroot orelse libc_dirs.sysroot;
   1809 
   1810         const compiler_rt_strat: RtStrat = s: {
   1811             if (options.skip_linker_dependencies) break :s .none;
   1812             const want = options.want_compiler_rt orelse is_exe_or_dyn_lib;
   1813             if (!want) break :s .none;
   1814             if (have_zcu and output_mode == .Obj) break :s .zcu;
   1815             if (is_exe_or_dyn_lib) break :s .lib;
   1816             break :s .obj;
   1817         };
   1818 
   1819         if (compiler_rt_strat == .zcu) {
   1820             // For objects, this mechanism relies on essentially `_ = @import("compiler-rt");`
   1821             // injected into the object.
   1822             const compiler_rt_mod = try Package.Module.create(arena, .{
   1823                 .paths = .{
   1824                     .root = .zig_lib_root,
   1825                     .root_src_path = "compiler_rt.zig",
   1826                 },
   1827                 .fully_qualified_name = "compiler_rt",
   1828                 .cc_argv = &.{},
   1829                 .inherited = .{
   1830                     .stack_check = false,
   1831                     .stack_protector = 0,
   1832                     .no_builtin = true,
   1833                 },
   1834                 .global = options.config,
   1835                 .parent = options.root_mod,
   1836             });
   1837             try options.root_mod.deps.putNoClobber(arena, "compiler_rt", compiler_rt_mod);
   1838         }
   1839 
   1840         // unlike compiler_rt, we always want to go through the `_ = @import("ubsan-rt")`
   1841         // approach, since the ubsan runtime uses quite a lot of the standard library
   1842         // and this reduces unnecessary bloat.
   1843         const ubsan_rt_strat: RtStrat = s: {
   1844             const can_build_ubsan_rt = target_util.canBuildLibUbsanRt(options.root_mod.resolved_target.result);
   1845             const want_ubsan_rt = options.want_ubsan_rt orelse (can_build_ubsan_rt and any_sanitize_c == .full and is_exe_or_dyn_lib);
   1846             if (!want_ubsan_rt) break :s .none;
   1847             if (options.skip_linker_dependencies) break :s .none;
   1848             if (have_zcu) break :s .zcu;
   1849             if (is_exe_or_dyn_lib) break :s .lib;
   1850             break :s .obj;
   1851         };
   1852 
   1853         if (ubsan_rt_strat == .zcu) {
   1854             const ubsan_rt_mod = try Package.Module.create(arena, .{
   1855                 .paths = .{
   1856                     .root = .zig_lib_root,
   1857                     .root_src_path = "ubsan_rt.zig",
   1858                 },
   1859                 .fully_qualified_name = "ubsan_rt",
   1860                 .cc_argv = &.{},
   1861                 .inherited = .{},
   1862                 .global = options.config,
   1863                 .parent = options.root_mod,
   1864             });
   1865             try options.root_mod.deps.putNoClobber(arena, "ubsan_rt", ubsan_rt_mod);
   1866         }
   1867 
   1868         if (options.verbose_llvm_cpu_features) {
   1869             if (options.root_mod.resolved_target.llvm_cpu_features) |cf| print: {
   1870                 const target = options.root_mod.resolved_target.result;
   1871                 std.debug.lockStdErr();
   1872                 defer std.debug.unlockStdErr();
   1873                 const stderr = std.io.getStdErr().writer();
   1874                 nosuspend {
   1875                     stderr.print("compilation: {s}\n", .{options.root_name}) catch break :print;
   1876                     stderr.print("  target: {s}\n", .{try target.zigTriple(arena)}) catch break :print;
   1877                     stderr.print("  cpu: {s}\n", .{target.cpu.model.name}) catch break :print;
   1878                     stderr.print("  features: {s}\n", .{cf}) catch {};
   1879                 }
   1880             }
   1881         }
   1882 
   1883         const error_limit = options.error_limit orelse (std.math.maxInt(u16) - 1);
   1884 
   1885         // We put everything into the cache hash that *cannot be modified
   1886         // during an incremental update*. For example, one cannot change the
   1887         // target between updates, but one can change source files, so the
   1888         // target goes into the cache hash, but source files do not. This is so
   1889         // that we can find the same binary and incrementally update it even if
   1890         // there are modified source files. We do this even if outputting to
   1891         // the current directory because we need somewhere to store incremental
   1892         // compilation metadata.
   1893         const cache = try arena.create(Cache);
   1894         cache.* = .{
   1895             .gpa = gpa,
   1896             .manifest_dir = try options.dirs.local_cache.handle.makeOpenPath("h", .{}),
   1897         };
   1898         // These correspond to std.zig.Server.Message.PathPrefix.
   1899         cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
   1900         cache.addPrefix(options.dirs.zig_lib);
   1901         cache.addPrefix(options.dirs.local_cache);
   1902         cache.addPrefix(options.dirs.global_cache);
   1903         errdefer cache.manifest_dir.close();
   1904 
   1905         // This is shared hasher state common to zig source and all C source files.
   1906         cache.hash.addBytes(build_options.version);
   1907         cache.hash.add(builtin.zig_backend);
   1908         cache.hash.add(options.config.pie);
   1909         cache.hash.add(options.config.lto);
   1910         cache.hash.add(options.config.link_mode);
   1911         cache.hash.add(options.config.any_unwind_tables);
   1912         cache.hash.add(options.config.any_non_single_threaded);
   1913         cache.hash.add(options.config.any_sanitize_thread);
   1914         cache.hash.add(options.config.any_sanitize_c);
   1915         cache.hash.add(options.config.any_fuzz);
   1916         cache.hash.add(options.function_sections);
   1917         cache.hash.add(options.data_sections);
   1918         cache.hash.add(link_libc);
   1919         cache.hash.add(options.config.link_libcpp);
   1920         cache.hash.add(options.config.link_libunwind);
   1921         cache.hash.add(output_mode);
   1922         cache_helpers.addDebugFormat(&cache.hash, options.config.debug_format);
   1923         cache.hash.addBytes(options.root_name);
   1924         cache.hash.add(options.config.wasi_exec_model);
   1925         cache.hash.add(options.config.san_cov_trace_pc_guard);
   1926         cache.hash.add(options.debug_compiler_runtime_libs);
   1927         // The actual emit paths don't matter. They're only user-specified if we aren't using the
   1928         // cache! However, it does matter whether the files are emitted at all.
   1929         cache.hash.add(options.emit_bin != .no);
   1930         cache.hash.add(options.emit_asm != .no);
   1931         cache.hash.add(options.emit_implib != .no);
   1932         cache.hash.add(options.emit_llvm_ir != .no);
   1933         cache.hash.add(options.emit_llvm_bc != .no);
   1934         cache.hash.add(options.emit_docs != .no);
   1935         // TODO audit this and make sure everything is in it
   1936 
   1937         const main_mod = options.main_mod orelse options.root_mod;
   1938         const comp = try arena.create(Compilation);
   1939         const opt_zcu: ?*Zcu = if (have_zcu) blk: {
   1940             // Pre-open the directory handles for cached ZIR code so that it does not need
   1941             // to redundantly happen for each AstGen operation.
   1942             const zir_sub_dir = "z";
   1943 
   1944             var local_zir_dir = try options.dirs.local_cache.handle.makeOpenPath(zir_sub_dir, .{});
   1945             errdefer local_zir_dir.close();
   1946             const local_zir_cache: Cache.Directory = .{
   1947                 .handle = local_zir_dir,
   1948                 .path = try options.dirs.local_cache.join(arena, &.{zir_sub_dir}),
   1949             };
   1950             var global_zir_dir = try options.dirs.global_cache.handle.makeOpenPath(zir_sub_dir, .{});
   1951             errdefer global_zir_dir.close();
   1952             const global_zir_cache: Cache.Directory = .{
   1953                 .handle = global_zir_dir,
   1954                 .path = try options.dirs.global_cache.join(arena, &.{zir_sub_dir}),
   1955             };
   1956 
   1957             const std_mod = options.std_mod orelse try Package.Module.create(arena, .{
   1958                 .paths = .{
   1959                     .root = try .fromRoot(arena, options.dirs, .zig_lib, "std"),
   1960                     .root_src_path = "std.zig",
   1961                 },
   1962                 .fully_qualified_name = "std",
   1963                 .cc_argv = &.{},
   1964                 .inherited = .{},
   1965                 .global = options.config,
   1966                 .parent = options.root_mod,
   1967             });
   1968 
   1969             const zcu = try arena.create(Zcu);
   1970             zcu.* = .{
   1971                 .gpa = gpa,
   1972                 .comp = comp,
   1973                 .main_mod = main_mod,
   1974                 .root_mod = options.root_mod,
   1975                 .std_mod = std_mod,
   1976                 .global_zir_cache = global_zir_cache,
   1977                 .local_zir_cache = local_zir_cache,
   1978                 .error_limit = error_limit,
   1979                 .llvm_object = null,
   1980             };
   1981             try zcu.init(options.thread_pool.getIdCount());
   1982             break :blk zcu;
   1983         } else blk: {
   1984             if (options.emit_h != .no) return error.NoZigModuleForCHeader;
   1985             break :blk null;
   1986         };
   1987         errdefer if (opt_zcu) |zcu| zcu.deinit();
   1988 
   1989         var windows_libs = try std.StringArrayHashMapUnmanaged(void).init(gpa, options.windows_lib_names, &.{});
   1990         errdefer windows_libs.deinit(gpa);
   1991 
   1992         comp.* = .{
   1993             .gpa = gpa,
   1994             .arena = arena,
   1995             .zcu = opt_zcu,
   1996             .cache_use = undefined, // populated below
   1997             .bin_file = null, // populated below if necessary
   1998             .root_mod = options.root_mod,
   1999             .config = options.config,
   2000             .dirs = options.dirs,
   2001             .work_queues = @splat(.init(gpa)),
   2002             .c_object_work_queue = .init(gpa),
   2003             .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) .init(gpa) else .{},
   2004             .c_source_files = options.c_source_files,
   2005             .rc_source_files = options.rc_source_files,
   2006             .cache_parent = cache,
   2007             .self_exe_path = options.self_exe_path,
   2008             .libc_include_dir_list = libc_dirs.libc_include_dir_list,
   2009             .libc_framework_dir_list = libc_dirs.libc_framework_dir_list,
   2010             .rc_includes = options.rc_includes,
   2011             .mingw_unicode_entry_point = options.mingw_unicode_entry_point,
   2012             .thread_pool = options.thread_pool,
   2013             .clang_passthrough_mode = options.clang_passthrough_mode,
   2014             .clang_preprocessor_mode = options.clang_preprocessor_mode,
   2015             .verbose_cc = options.verbose_cc,
   2016             .verbose_air = options.verbose_air,
   2017             .verbose_intern_pool = options.verbose_intern_pool,
   2018             .verbose_generic_instances = options.verbose_generic_instances,
   2019             .verbose_llvm_ir = options.verbose_llvm_ir,
   2020             .verbose_llvm_bc = options.verbose_llvm_bc,
   2021             .verbose_cimport = options.verbose_cimport,
   2022             .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features,
   2023             .verbose_link = options.verbose_link,
   2024             .disable_c_depfile = options.disable_c_depfile,
   2025             .reference_trace = options.reference_trace,
   2026             .time_report = options.time_report,
   2027             .stack_report = options.stack_report,
   2028             .test_filters = options.test_filters,
   2029             .test_name_prefix = options.test_name_prefix,
   2030             .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs,
   2031             .debug_compile_errors = options.debug_compile_errors,
   2032             .debug_incremental = options.debug_incremental,
   2033             .incremental = options.incremental,
   2034             .root_name = root_name,
   2035             .sysroot = sysroot,
   2036             .windows_libs = windows_libs,
   2037             .version = options.version,
   2038             .libc_installation = libc_dirs.libc_installation,
   2039             .compiler_rt_strat = compiler_rt_strat,
   2040             .ubsan_rt_strat = ubsan_rt_strat,
   2041             .link_inputs = options.link_inputs,
   2042             .framework_dirs = options.framework_dirs,
   2043             .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
   2044             .skip_linker_dependencies = options.skip_linker_dependencies,
   2045             .queued_jobs = .{},
   2046             .function_sections = options.function_sections,
   2047             .data_sections = options.data_sections,
   2048             .native_system_include_paths = options.native_system_include_paths,
   2049             .wasi_emulated_libs = options.wasi_emulated_libs,
   2050             .force_undefined_symbols = options.force_undefined_symbols,
   2051             .link_eh_frame_hdr = link_eh_frame_hdr,
   2052             .global_cc_argv = options.global_cc_argv,
   2053             .file_system_inputs = options.file_system_inputs,
   2054             .parent_whole_cache = options.parent_whole_cache,
   2055             .link_diags = .init(gpa),
   2056             .emit_bin = try options.emit_bin.resolve(arena, &options, .bin),
   2057             .emit_asm = try options.emit_asm.resolve(arena, &options, .@"asm"),
   2058             .emit_implib = try options.emit_implib.resolve(arena, &options, .implib),
   2059             .emit_llvm_ir = try options.emit_llvm_ir.resolve(arena, &options, .llvm_ir),
   2060             .emit_llvm_bc = try options.emit_llvm_bc.resolve(arena, &options, .llvm_bc),
   2061             .emit_docs = try options.emit_docs.resolve(arena, &options, .docs),
   2062         };
   2063 
   2064         // Prevent some footguns by making the "any" fields of config reflect
   2065         // the default Module settings.
   2066         comp.config.any_unwind_tables = any_unwind_tables;
   2067         comp.config.any_non_single_threaded = any_non_single_threaded;
   2068         comp.config.any_sanitize_thread = any_sanitize_thread;
   2069         comp.config.any_sanitize_c = any_sanitize_c;
   2070         comp.config.any_fuzz = any_fuzz;
   2071 
   2072         if (opt_zcu) |zcu| {
   2073             // Populate `zcu.module_roots`.
   2074             const pt: Zcu.PerThread = .activate(zcu, .main);
   2075             defer pt.deactivate();
   2076             try pt.populateModuleRootTable();
   2077         }
   2078 
   2079         const lf_open_opts: link.File.OpenOptions = .{
   2080             .linker_script = options.linker_script,
   2081             .z_nodelete = options.linker_z_nodelete,
   2082             .z_notext = options.linker_z_notext,
   2083             .z_defs = options.linker_z_defs,
   2084             .z_origin = options.linker_z_origin,
   2085             .z_nocopyreloc = options.linker_z_nocopyreloc,
   2086             .z_now = options.linker_z_now,
   2087             .z_relro = options.linker_z_relro,
   2088             .z_common_page_size = options.linker_z_common_page_size,
   2089             .z_max_page_size = options.linker_z_max_page_size,
   2090             .darwin_sdk_layout = libc_dirs.darwin_sdk_layout,
   2091             .frameworks = options.frameworks,
   2092             .lib_directories = options.lib_directories,
   2093             .framework_dirs = options.framework_dirs,
   2094             .rpath_list = options.rpath_list,
   2095             .symbol_wrap_set = options.symbol_wrap_set,
   2096             .repro = options.linker_repro orelse (options.root_mod.optimize_mode != .Debug),
   2097             .allow_shlib_undefined = options.linker_allow_shlib_undefined,
   2098             .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
   2099             .compress_debug_sections = options.linker_compress_debug_sections orelse .none,
   2100             .module_definition_file = options.linker_module_definition_file,
   2101             .sort_section = options.linker_sort_section,
   2102             .import_symbols = options.linker_import_symbols,
   2103             .import_table = options.linker_import_table,
   2104             .export_table = options.linker_export_table,
   2105             .initial_memory = options.linker_initial_memory,
   2106             .max_memory = options.linker_max_memory,
   2107             .global_base = options.linker_global_base,
   2108             .export_symbol_names = options.linker_export_symbol_names,
   2109             .print_gc_sections = options.linker_print_gc_sections,
   2110             .print_icf_sections = options.linker_print_icf_sections,
   2111             .print_map = options.linker_print_map,
   2112             .tsaware = options.linker_tsaware,
   2113             .nxcompat = options.linker_nxcompat,
   2114             .dynamicbase = options.linker_dynamicbase,
   2115             .major_subsystem_version = options.major_subsystem_version,
   2116             .minor_subsystem_version = options.minor_subsystem_version,
   2117             .entry = options.entry,
   2118             .stack_size = options.stack_size,
   2119             .image_base = options.image_base,
   2120             .version_script = options.version_script,
   2121             .allow_undefined_version = options.linker_allow_undefined_version,
   2122             .enable_new_dtags = options.linker_enable_new_dtags,
   2123             .gc_sections = options.linker_gc_sections,
   2124             .emit_relocs = options.link_emit_relocs,
   2125             .soname = options.soname,
   2126             .compatibility_version = options.compatibility_version,
   2127             .build_id = build_id,
   2128             .subsystem = options.subsystem,
   2129             .hash_style = options.hash_style,
   2130             .enable_link_snapshots = options.enable_link_snapshots,
   2131             .install_name = options.install_name,
   2132             .entitlements = options.entitlements,
   2133             .pagezero_size = options.pagezero_size,
   2134             .headerpad_size = options.headerpad_size,
   2135             .headerpad_max_install_names = options.headerpad_max_install_names,
   2136             .dead_strip_dylibs = options.dead_strip_dylibs,
   2137             .force_load_objc = options.force_load_objc,
   2138             .discard_local_symbols = options.discard_local_symbols,
   2139             .pdb_source_path = options.pdb_source_path,
   2140             .pdb_out_path = options.pdb_out_path,
   2141             .entry_addr = null, // CLI does not expose this option (yet?)
   2142             .object_host_name = "env",
   2143         };
   2144 
   2145         switch (options.cache_mode) {
   2146             .none => {
   2147                 const none = try arena.create(CacheUse.None);
   2148                 none.* = .{ .tmp_artifact_directory = null };
   2149                 comp.cache_use = .{ .none = none };
   2150                 if (comp.emit_bin) |path| {
   2151                     comp.bin_file = try link.File.open(arena, comp, .{
   2152                         .root_dir = .cwd(),
   2153                         .sub_path = path,
   2154                     }, lf_open_opts);
   2155                 }
   2156             },
   2157             .incremental => {
   2158                 // Options that are specific to zig source files, that cannot be
   2159                 // modified between incremental updates.
   2160                 var hash = cache.hash;
   2161 
   2162                 // Synchronize with other matching comments: ZigOnlyHashStuff
   2163                 hash.add(use_llvm);
   2164                 hash.add(options.config.use_lib_llvm);
   2165                 hash.add(options.config.dll_export_fns);
   2166                 hash.add(options.config.is_test);
   2167                 hash.addListOfBytes(options.test_filters);
   2168                 hash.addOptionalBytes(options.test_name_prefix);
   2169                 hash.add(options.skip_linker_dependencies);
   2170                 hash.add(options.emit_h != .no);
   2171                 hash.add(error_limit);
   2172 
   2173                 // Here we put the root source file path name, but *not* with addFile.
   2174                 // We want the hash to be the same regardless of the contents of the
   2175                 // source file, because incremental compilation will handle it, but we
   2176                 // do want to namespace different source file names because they are
   2177                 // likely different compilations and therefore this would be likely to
   2178                 // cause cache hits.
   2179                 if (comp.zcu) |zcu| {
   2180                     try addModuleTableToCacheHash(zcu, arena, &hash, .path_bytes);
   2181                 } else {
   2182                     cache_helpers.addModule(&hash, options.root_mod);
   2183                 }
   2184 
   2185                 // In the case of incremental cache mode, this `artifact_directory`
   2186                 // is computed based on a hash of non-linker inputs, and it is where all
   2187                 // build artifacts are stored (even while in-progress).
   2188                 comp.digest = hash.peekBin();
   2189                 const digest = hash.final();
   2190 
   2191                 const artifact_sub_dir = "o" ++ std.fs.path.sep_str ++ digest;
   2192                 var artifact_dir = try options.dirs.local_cache.handle.makeOpenPath(artifact_sub_dir, .{});
   2193                 errdefer artifact_dir.close();
   2194                 const artifact_directory: Cache.Directory = .{
   2195                     .handle = artifact_dir,
   2196                     .path = try options.dirs.local_cache.join(arena, &.{artifact_sub_dir}),
   2197                 };
   2198 
   2199                 const incremental = try arena.create(CacheUse.Incremental);
   2200                 incremental.* = .{
   2201                     .artifact_directory = artifact_directory,
   2202                 };
   2203                 comp.cache_use = .{ .incremental = incremental };
   2204 
   2205                 if (comp.emit_bin) |cache_rel_path| {
   2206                     const emit: Cache.Path = .{
   2207                         .root_dir = artifact_directory,
   2208                         .sub_path = cache_rel_path,
   2209                     };
   2210                     comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts);
   2211                 }
   2212             },
   2213             .whole => {
   2214                 // For whole cache mode, we don't know where to put outputs from the linker until
   2215                 // the final cache hash, which is available after the compilation is complete.
   2216                 //
   2217                 // Therefore, `comp.bin_file` is left `null` (already done) until `update`, where
   2218                 // it may find a cache hit, or else will use a temporary directory to hold output
   2219                 // artifacts.
   2220                 const whole = try arena.create(CacheUse.Whole);
   2221                 whole.* = .{
   2222                     .lf_open_opts = lf_open_opts,
   2223                     .cache_manifest = null,
   2224                     .cache_manifest_mutex = .{},
   2225                     .tmp_artifact_directory = null,
   2226                     .lock = null,
   2227                 };
   2228                 comp.cache_use = .{ .whole = whole };
   2229             },
   2230         }
   2231 
   2232         if (use_llvm) {
   2233             if (opt_zcu) |zcu| {
   2234                 zcu.llvm_object = try LlvmObject.create(arena, comp);
   2235             }
   2236         }
   2237 
   2238         break :comp comp;
   2239     };
   2240     errdefer comp.destroy();
   2241 
   2242     const target = comp.root_mod.resolved_target.result;
   2243     const can_build_compiler_rt = target_util.canBuildLibCompilerRt(target, comp.config.use_llvm, build_options.have_llvm);
   2244 
   2245     // Add a `CObject` for each `c_source_files`.
   2246     try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len);
   2247     for (options.c_source_files) |c_source_file| {
   2248         const c_object = try gpa.create(CObject);
   2249         errdefer gpa.destroy(c_object);
   2250 
   2251         c_object.* = .{
   2252             .status = .{ .new = {} },
   2253             .src = c_source_file,
   2254         };
   2255         comp.c_object_table.putAssumeCapacityNoClobber(c_object, {});
   2256     }
   2257     comp.link_task_queue.pending_prelink_tasks += @intCast(comp.c_object_table.count());
   2258 
   2259     // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`.
   2260     const win32_resource_count =
   2261         options.rc_source_files.len + @intFromBool(options.manifest_file != null);
   2262     if (win32_resource_count > 0) {
   2263         dev.check(.win32_resource);
   2264         try comp.win32_resource_table.ensureTotalCapacity(gpa, win32_resource_count);
   2265         // Add this after adding logic to updateWin32Resource to pass the
   2266         // result into link.loadInput. loadInput integration is not implemented
   2267         // for Windows linking logic yet.
   2268         //comp.link_task_queue.pending_prelink_tasks += @intCast(win32_resource_count);
   2269         for (options.rc_source_files) |rc_source_file| {
   2270             const win32_resource = try gpa.create(Win32Resource);
   2271             errdefer gpa.destroy(win32_resource);
   2272 
   2273             win32_resource.* = .{
   2274                 .status = .{ .new = {} },
   2275                 .src = .{ .rc = rc_source_file },
   2276             };
   2277             comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});
   2278         }
   2279 
   2280         if (options.manifest_file) |manifest_path| {
   2281             const win32_resource = try gpa.create(Win32Resource);
   2282             errdefer gpa.destroy(win32_resource);
   2283 
   2284             win32_resource.* = .{
   2285                 .status = .{ .new = {} },
   2286                 .src = .{ .manifest = manifest_path },
   2287             };
   2288             comp.win32_resource_table.putAssumeCapacityNoClobber(win32_resource, {});
   2289         }
   2290     }
   2291 
   2292     if (comp.emit_bin != null and target.ofmt != .c) {
   2293         if (!comp.skip_linker_dependencies) {
   2294             // If we need to build libc for the target, add work items for it.
   2295             // We go through the work queue so that building can be done in parallel.
   2296             // If linking against host libc installation, instead queue up jobs
   2297             // for loading those files in the linker.
   2298             if (comp.config.link_libc and is_exe_or_dyn_lib) {
   2299                 // If the "is darwin" check is moved below the libc_installation check below,
   2300                 // error.LibCInstallationMissingCrtDir is returned from lci.resolveCrtPaths().
   2301                 if (target.isDarwinLibC()) {
   2302                     // TODO delete logic from MachO flush() and queue up tasks here instead.
   2303                 } else if (comp.libc_installation) |lci| {
   2304                     const basenames = LibCInstallation.CrtBasenames.get(.{
   2305                         .target = target,
   2306                         .link_libc = comp.config.link_libc,
   2307                         .output_mode = comp.config.output_mode,
   2308                         .link_mode = comp.config.link_mode,
   2309                         .pie = comp.config.pie,
   2310                     });
   2311                     const paths = try lci.resolveCrtPaths(arena, basenames, target);
   2312 
   2313                     const fields = @typeInfo(@TypeOf(paths)).@"struct".fields;
   2314                     try comp.link_task_queue.queued_prelink.ensureUnusedCapacity(gpa, fields.len + 1);
   2315                     inline for (fields) |field| {
   2316                         if (@field(paths, field.name)) |path| {
   2317                             comp.link_task_queue.queued_prelink.appendAssumeCapacity(.{ .load_object = path });
   2318                         }
   2319                     }
   2320                     // Loads the libraries provided by `target_util.libcFullLinkFlags(target)`.
   2321                     comp.link_task_queue.queued_prelink.appendAssumeCapacity(.load_host_libc);
   2322                 } else if (target.isMuslLibC()) {
   2323                     if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
   2324 
   2325                     if (musl.needsCrt0(comp.config.output_mode, comp.config.link_mode, comp.config.pie)) |f| {
   2326                         comp.queued_jobs.musl_crt_file[@intFromEnum(f)] = true;
   2327                         comp.link_task_queue.pending_prelink_tasks += 1;
   2328                     }
   2329                     switch (comp.config.link_mode) {
   2330                         .static => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_a)] = true,
   2331                         .dynamic => comp.queued_jobs.musl_crt_file[@intFromEnum(musl.CrtFile.libc_so)] = true,
   2332                     }
   2333                     comp.link_task_queue.pending_prelink_tasks += 1;
   2334                 } else if (target.isGnuLibC()) {
   2335                     if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
   2336 
   2337                     if (glibc.needsCrt0(comp.config.output_mode)) |f| {
   2338                         comp.queued_jobs.glibc_crt_file[@intFromEnum(f)] = true;
   2339                         comp.link_task_queue.pending_prelink_tasks += 1;
   2340                     }
   2341                     comp.queued_jobs.glibc_shared_objects = true;
   2342                     comp.link_task_queue.pending_prelink_tasks += glibc.sharedObjectsCount(&target);
   2343 
   2344                     comp.queued_jobs.glibc_crt_file[@intFromEnum(glibc.CrtFile.libc_nonshared_a)] = true;
   2345                     comp.link_task_queue.pending_prelink_tasks += 1;
   2346                 } else if (target.isFreeBSDLibC()) {
   2347                     if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
   2348 
   2349                     if (freebsd.needsCrt0(comp.config.output_mode)) |f| {
   2350                         comp.queued_jobs.freebsd_crt_file[@intFromEnum(f)] = true;
   2351                         comp.link_task_queue.pending_prelink_tasks += 1;
   2352                     }
   2353 
   2354                     comp.queued_jobs.freebsd_shared_objects = true;
   2355                     comp.link_task_queue.pending_prelink_tasks += freebsd.sharedObjectsCount();
   2356                 } else if (target.isNetBSDLibC()) {
   2357                     if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
   2358 
   2359                     if (netbsd.needsCrt0(comp.config.output_mode)) |f| {
   2360                         comp.queued_jobs.netbsd_crt_file[@intFromEnum(f)] = true;
   2361                         comp.link_task_queue.pending_prelink_tasks += 1;
   2362                     }
   2363 
   2364                     comp.queued_jobs.netbsd_shared_objects = true;
   2365                     comp.link_task_queue.pending_prelink_tasks += netbsd.sharedObjectsCount();
   2366                 } else if (target.isWasiLibC()) {
   2367                     if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
   2368 
   2369                     for (comp.wasi_emulated_libs) |crt_file| {
   2370                         comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = true;
   2371                     }
   2372                     comp.link_task_queue.pending_prelink_tasks += @intCast(comp.wasi_emulated_libs.len);
   2373 
   2374                     comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.execModelCrtFile(comp.config.wasi_exec_model))] = true;
   2375                     comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(wasi_libc.CrtFile.libc_a)] = true;
   2376                     comp.link_task_queue.pending_prelink_tasks += 2;
   2377                 } else if (target.isMinGW()) {
   2378                     if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
   2379 
   2380                     const main_crt_file: mingw.CrtFile = if (is_dyn_lib) .dllcrt2_o else .crt2_o;
   2381                     comp.queued_jobs.mingw_crt_file[@intFromEnum(main_crt_file)] = true;
   2382                     comp.queued_jobs.mingw_crt_file[@intFromEnum(mingw.CrtFile.libmingw32_lib)] = true;
   2383                     comp.link_task_queue.pending_prelink_tasks += 2;
   2384 
   2385                     // When linking mingw-w64 there are some import libs we always need.
   2386                     try comp.windows_libs.ensureUnusedCapacity(gpa, mingw.always_link_libs.len);
   2387                     for (mingw.always_link_libs) |name| comp.windows_libs.putAssumeCapacity(name, {});
   2388                 } else {
   2389                     return error.LibCUnavailable;
   2390                 }
   2391 
   2392                 if ((target.isMuslLibC() and comp.config.link_mode == .static) or
   2393                     target.isWasiLibC() or
   2394                     target.isMinGW())
   2395                 {
   2396                     comp.queued_jobs.zigc_lib = true;
   2397                     comp.link_task_queue.pending_prelink_tasks += 1;
   2398                 }
   2399             }
   2400 
   2401             // Generate Windows import libs.
   2402             if (target.os.tag == .windows) {
   2403                 const count = comp.windows_libs.count();
   2404                 for (0..count) |i| {
   2405                     try comp.queueJob(.{ .windows_import_lib = i });
   2406                 }
   2407                 // when integrating coff linker with prelink, the above
   2408                 // queueJob will need to change into something else since those
   2409                 // jobs are dispatched *after* the link_task_wait_group.wait()
   2410                 // that happens when separateCodegenThreadOk() is false.
   2411             }
   2412             if (comp.wantBuildLibUnwindFromSource()) {
   2413                 comp.queued_jobs.libunwind = true;
   2414                 comp.link_task_queue.pending_prelink_tasks += 1;
   2415             }
   2416             if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) {
   2417                 comp.queued_jobs.libcxx = true;
   2418                 comp.queued_jobs.libcxxabi = true;
   2419                 comp.link_task_queue.pending_prelink_tasks += 2;
   2420             }
   2421             if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.any_sanitize_thread) {
   2422                 comp.queued_jobs.libtsan = true;
   2423                 comp.link_task_queue.pending_prelink_tasks += 1;
   2424             }
   2425 
   2426             if (can_build_compiler_rt) {
   2427                 if (comp.compiler_rt_strat == .lib) {
   2428                     log.debug("queuing a job to build compiler_rt_lib", .{});
   2429                     comp.queued_jobs.compiler_rt_lib = true;
   2430                     comp.link_task_queue.pending_prelink_tasks += 1;
   2431                 } else if (comp.compiler_rt_strat == .obj) {
   2432                     log.debug("queuing a job to build compiler_rt_obj", .{});
   2433                     // In this case we are making a static library, so we ask
   2434                     // for a compiler-rt object to put in it.
   2435                     comp.queued_jobs.compiler_rt_obj = true;
   2436                     comp.link_task_queue.pending_prelink_tasks += 1;
   2437                 }
   2438 
   2439                 if (comp.ubsan_rt_strat == .lib) {
   2440                     log.debug("queuing a job to build ubsan_rt_lib", .{});
   2441                     comp.queued_jobs.ubsan_rt_lib = true;
   2442                     comp.link_task_queue.pending_prelink_tasks += 1;
   2443                 } else if (comp.ubsan_rt_strat == .obj) {
   2444                     log.debug("queuing a job to build ubsan_rt_obj", .{});
   2445                     comp.queued_jobs.ubsan_rt_obj = true;
   2446                     comp.link_task_queue.pending_prelink_tasks += 1;
   2447                 }
   2448 
   2449                 if (is_exe_or_dyn_lib and comp.config.any_fuzz) {
   2450                     log.debug("queuing a job to build libfuzzer", .{});
   2451                     comp.queued_jobs.fuzzer_lib = true;
   2452                     comp.link_task_queue.pending_prelink_tasks += 1;
   2453                 }
   2454             }
   2455         }
   2456 
   2457         try comp.link_task_queue.queued_prelink.append(gpa, .load_explicitly_provided);
   2458     }
   2459     log.debug("queued prelink tasks: {d}", .{comp.link_task_queue.queued_prelink.items.len});
   2460     log.debug("pending prelink tasks: {d}", .{comp.link_task_queue.pending_prelink_tasks});
   2461 
   2462     return comp;
   2463 }
   2464 
   2465 pub fn destroy(comp: *Compilation) void {
   2466     const gpa = comp.gpa;
   2467 
   2468     // This needs to be destroyed first, because it might contain MIR which we only know
   2469     // how to interpret (which kind of MIR it is) from `comp.bin_file`.
   2470     comp.link_task_queue.deinit(comp);
   2471 
   2472     if (comp.bin_file) |lf| lf.destroy();
   2473     if (comp.zcu) |zcu| zcu.deinit();
   2474     comp.cache_use.deinit();
   2475 
   2476     for (comp.work_queues) |work_queue| work_queue.deinit();
   2477     comp.c_object_work_queue.deinit();
   2478     comp.win32_resource_work_queue.deinit();
   2479 
   2480     comp.windows_libs.deinit(gpa);
   2481 
   2482     {
   2483         var it = comp.crt_files.iterator();
   2484         while (it.next()) |entry| {
   2485             gpa.free(entry.key_ptr.*);
   2486             entry.value_ptr.deinit(gpa);
   2487         }
   2488         comp.crt_files.deinit(gpa);
   2489     }
   2490 
   2491     if (comp.libunwind_static_lib) |*crt_file| {
   2492         crt_file.deinit(gpa);
   2493     }
   2494     if (comp.libcxx_static_lib) |*crt_file| {
   2495         crt_file.deinit(gpa);
   2496     }
   2497     if (comp.libcxxabi_static_lib) |*crt_file| {
   2498         crt_file.deinit(gpa);
   2499     }
   2500     if (comp.compiler_rt_lib) |*crt_file| {
   2501         crt_file.deinit(gpa);
   2502     }
   2503     if (comp.compiler_rt_obj) |*crt_file| {
   2504         crt_file.deinit(gpa);
   2505     }
   2506     if (comp.ubsan_rt_lib) |*crt_file| {
   2507         crt_file.deinit(gpa);
   2508     }
   2509     if (comp.ubsan_rt_obj) |*crt_file| {
   2510         crt_file.deinit(gpa);
   2511     }
   2512     if (comp.fuzzer_lib) |*crt_file| {
   2513         crt_file.deinit(gpa);
   2514     }
   2515 
   2516     if (comp.zigc_static_lib) |*crt_file| {
   2517         crt_file.deinit(gpa);
   2518     }
   2519 
   2520     if (comp.glibc_so_files) |*glibc_file| {
   2521         glibc_file.deinit(gpa);
   2522     }
   2523 
   2524     if (comp.freebsd_so_files) |*freebsd_file| {
   2525         freebsd_file.deinit(gpa);
   2526     }
   2527 
   2528     if (comp.netbsd_so_files) |*netbsd_file| {
   2529         netbsd_file.deinit(gpa);
   2530     }
   2531 
   2532     for (comp.c_object_table.keys()) |key| {
   2533         key.destroy(gpa);
   2534     }
   2535     comp.c_object_table.deinit(gpa);
   2536 
   2537     for (comp.failed_c_objects.values()) |bundle| {
   2538         bundle.destroy(gpa);
   2539     }
   2540     comp.failed_c_objects.deinit(gpa);
   2541 
   2542     for (comp.win32_resource_table.keys()) |key| {
   2543         key.destroy(gpa);
   2544     }
   2545     comp.win32_resource_table.deinit(gpa);
   2546 
   2547     for (comp.failed_win32_resources.values()) |*value| {
   2548         value.deinit(gpa);
   2549     }
   2550     comp.failed_win32_resources.deinit(gpa);
   2551 
   2552     comp.link_diags.deinit();
   2553 
   2554     comp.clearMiscFailures();
   2555 
   2556     comp.cache_parent.manifest_dir.close();
   2557 }
   2558 
   2559 pub fn clearMiscFailures(comp: *Compilation) void {
   2560     comp.alloc_failure_occurred = false;
   2561     comp.link_diags.flags = .{};
   2562     for (comp.misc_failures.values()) |*value| {
   2563         value.deinit(comp.gpa);
   2564     }
   2565     comp.misc_failures.deinit(comp.gpa);
   2566     comp.misc_failures = .{};
   2567 }
   2568 
   2569 pub fn getTarget(self: Compilation) Target {
   2570     return self.root_mod.resolved_target.result;
   2571 }
   2572 
   2573 /// Only legal to call when cache mode is incremental and a link file is present.
   2574 pub fn hotCodeSwap(
   2575     comp: *Compilation,
   2576     prog_node: std.Progress.Node,
   2577     pid: std.process.Child.Id,
   2578 ) !void {
   2579     const lf = comp.bin_file.?;
   2580     lf.child_pid = pid;
   2581     try lf.makeWritable();
   2582     try comp.update(prog_node);
   2583     try lf.makeExecutable();
   2584 }
   2585 
   2586 fn cleanupAfterUpdate(comp: *Compilation, tmp_dir_rand_int: u64) void {
   2587     switch (comp.cache_use) {
   2588         .none => |none| {
   2589             if (none.tmp_artifact_directory) |*tmp_dir| {
   2590                 tmp_dir.handle.close();
   2591                 none.tmp_artifact_directory = null;
   2592                 if (dev.env == .bootstrap) {
   2593                     // zig1 uses `CacheMode.none`, but it doesn't need to know how to delete
   2594                     // temporary directories; it doesn't have a real cache directory anyway.
   2595                     return;
   2596                 }
   2597                 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
   2598                 comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| {
   2599                     log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{
   2600                         comp.dirs.local_cache.path orelse ".",
   2601                         std.fs.path.sep,
   2602                         tmp_dir_sub_path,
   2603                         @errorName(err),
   2604                     });
   2605                 };
   2606             }
   2607         },
   2608         .incremental => return,
   2609         .whole => |whole| {
   2610             if (whole.cache_manifest) |man| {
   2611                 man.deinit();
   2612                 whole.cache_manifest = null;
   2613             }
   2614             if (comp.bin_file) |lf| {
   2615                 lf.destroy();
   2616                 comp.bin_file = null;
   2617             }
   2618             if (whole.tmp_artifact_directory) |*tmp_dir| {
   2619                 tmp_dir.handle.close();
   2620                 whole.tmp_artifact_directory = null;
   2621                 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
   2622                 comp.dirs.local_cache.handle.deleteTree(tmp_dir_sub_path) catch |err| {
   2623                     log.warn("failed to delete temporary directory '{s}{c}{s}': {s}", .{
   2624                         comp.dirs.local_cache.path orelse ".",
   2625                         std.fs.path.sep,
   2626                         tmp_dir_sub_path,
   2627                         @errorName(err),
   2628                     });
   2629                 };
   2630             }
   2631         },
   2632     }
   2633 }
   2634 
   2635 /// Detect changes to source files, perform semantic analysis, and update the output files.
   2636 pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
   2637     const tracy_trace = trace(@src());
   2638     defer tracy_trace.end();
   2639 
   2640     // This arena is scoped to this one update.
   2641     const gpa = comp.gpa;
   2642     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
   2643     defer arena_allocator.deinit();
   2644     const arena = arena_allocator.allocator();
   2645 
   2646     comp.clearMiscFailures();
   2647     comp.last_update_was_cache_hit = false;
   2648 
   2649     var tmp_dir_rand_int: u64 = undefined;
   2650     var man: Cache.Manifest = undefined;
   2651     defer cleanupAfterUpdate(comp, tmp_dir_rand_int);
   2652 
   2653     // If using the whole caching strategy, we check for *everything* up front, including
   2654     // C source files.
   2655     log.debug("Compilation.update for {s}, CacheMode.{s}", .{ comp.root_name, @tagName(comp.cache_use) });
   2656     switch (comp.cache_use) {
   2657         .none => |none| {
   2658             assert(none.tmp_artifact_directory == null);
   2659             none.tmp_artifact_directory = d: {
   2660                 tmp_dir_rand_int = std.crypto.random.int(u64);
   2661                 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
   2662                 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
   2663                 break :d .{
   2664                     .path = path,
   2665                     .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}),
   2666                 };
   2667             };
   2668         },
   2669         .incremental => {},
   2670         .whole => |whole| {
   2671             assert(comp.bin_file == null);
   2672             // We are about to obtain this lock, so here we give other processes a chance first.
   2673             whole.releaseLock();
   2674 
   2675             man = comp.cache_parent.obtain();
   2676             whole.cache_manifest = &man;
   2677             try addNonIncrementalStuffToCacheManifest(comp, arena, &man);
   2678 
   2679             const is_hit = man.hit() catch |err| switch (err) {
   2680                 error.CacheCheckFailed => switch (man.diagnostic) {
   2681                     .none => unreachable,
   2682                     .manifest_create, .manifest_read, .manifest_lock, .manifest_seek => |e| return comp.setMiscFailure(
   2683                         .check_whole_cache,
   2684                         "failed to check cache: {s} {s}",
   2685                         .{ @tagName(man.diagnostic), @errorName(e) },
   2686                     ),
   2687                     .file_open, .file_stat, .file_read, .file_hash => |op| {
   2688                         const pp = man.files.keys()[op.file_index].prefixed_path;
   2689                         const prefix = man.cache.prefixes()[pp.prefix];
   2690                         return comp.setMiscFailure(
   2691                             .check_whole_cache,
   2692                             "failed to check cache: '{}{s}' {s} {s}",
   2693                             .{ prefix, pp.sub_path, @tagName(man.diagnostic), @errorName(op.err) },
   2694                         );
   2695                     },
   2696                 },
   2697                 error.OutOfMemory => return error.OutOfMemory,
   2698                 error.InvalidFormat => return comp.setMiscFailure(
   2699                     .check_whole_cache,
   2700                     "failed to check cache: invalid manifest file format",
   2701                     .{},
   2702                 ),
   2703             };
   2704             if (is_hit) {
   2705                 // In this case the cache hit contains the full set of file system inputs. Nice!
   2706                 if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
   2707                 if (comp.parent_whole_cache) |pwc| {
   2708                     pwc.mutex.lock();
   2709                     defer pwc.mutex.unlock();
   2710                     try man.populateOtherManifest(pwc.manifest, pwc.prefix_map);
   2711                 }
   2712 
   2713                 comp.last_update_was_cache_hit = true;
   2714                 log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name});
   2715                 const bin_digest = man.finalBin();
   2716 
   2717                 comp.digest = bin_digest;
   2718 
   2719                 assert(whole.lock == null);
   2720                 whole.lock = man.toOwnedLock();
   2721                 return;
   2722             }
   2723             log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name});
   2724 
   2725             // Compile the artifacts to a temporary directory.
   2726             whole.tmp_artifact_directory = d: {
   2727                 tmp_dir_rand_int = std.crypto.random.int(u64);
   2728                 const tmp_dir_sub_path = "tmp" ++ std.fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
   2729                 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
   2730                 break :d .{
   2731                     .path = path,
   2732                     .handle = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{}),
   2733                 };
   2734             };
   2735             if (comp.emit_bin) |sub_path| {
   2736                 const emit: Cache.Path = .{
   2737                     .root_dir = whole.tmp_artifact_directory.?,
   2738                     .sub_path = sub_path,
   2739                 };
   2740                 comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts);
   2741             }
   2742         },
   2743     }
   2744 
   2745     // From this point we add a preliminary set of file system inputs that
   2746     // affects both incremental and whole cache mode. For incremental cache
   2747     // mode, the long-lived compiler state will track additional file system
   2748     // inputs discovered after this point. For whole cache mode, we rely on
   2749     // these inputs to make it past AstGen, and once there, we can rely on
   2750     // learning file system inputs from the Cache object.
   2751 
   2752     // For compiling C objects, we rely on the cache hash system to avoid duplicating work.
   2753     // Add a Job for each C object.
   2754     try comp.c_object_work_queue.ensureUnusedCapacity(comp.c_object_table.count());
   2755     for (comp.c_object_table.keys()) |c_object| {
   2756         comp.c_object_work_queue.writeItemAssumeCapacity(c_object);
   2757         try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{c_object.src.src_path}));
   2758     }
   2759 
   2760     // For compiling Win32 resources, we rely on the cache hash system to avoid duplicating work.
   2761     // Add a Job for each Win32 resource file.
   2762     try comp.win32_resource_work_queue.ensureUnusedCapacity(comp.win32_resource_table.count());
   2763     for (comp.win32_resource_table.keys()) |win32_resource| {
   2764         comp.win32_resource_work_queue.writeItemAssumeCapacity(win32_resource);
   2765         switch (win32_resource.src) {
   2766             .rc => |f| {
   2767                 try comp.appendFileSystemInput(try .fromUnresolved(arena, comp.dirs, &.{f.src_path}));
   2768             },
   2769             .manifest => {},
   2770         }
   2771     }
   2772 
   2773     if (comp.zcu) |zcu| {
   2774         const pt: Zcu.PerThread = .activate(zcu, .main);
   2775         defer pt.deactivate();
   2776 
   2777         zcu.skip_analysis_this_update = false;
   2778 
   2779         // TODO: doing this in `resolveReferences` later could avoid adding inputs for dead embedfiles. Investigate!
   2780         for (zcu.embed_table.keys()) |embed_file| {
   2781             try comp.appendFileSystemInput(embed_file.path);
   2782         }
   2783 
   2784         zcu.analysis_roots.clear();
   2785 
   2786         zcu.analysis_roots.appendAssumeCapacity(zcu.std_mod);
   2787 
   2788         // Normally we rely on importing std to in turn import the root source file in the start code.
   2789         // However, the main module is distinct from the root module in tests, so that won't happen there.
   2790         if (comp.config.is_test and zcu.main_mod != zcu.std_mod) {
   2791             zcu.analysis_roots.appendAssumeCapacity(zcu.main_mod);
   2792         }
   2793 
   2794         if (zcu.root_mod.deps.get("compiler_rt")) |compiler_rt_mod| {
   2795             zcu.analysis_roots.appendAssumeCapacity(compiler_rt_mod);
   2796         }
   2797 
   2798         if (zcu.root_mod.deps.get("ubsan_rt")) |ubsan_rt_mod| {
   2799             zcu.analysis_roots.appendAssumeCapacity(ubsan_rt_mod);
   2800         }
   2801     }
   2802 
   2803     // The linker progress node is set up here instead of in `performAllTheWork`, because
   2804     // we also want it around during `flush`.
   2805     const have_link_node = comp.bin_file != null;
   2806     if (have_link_node) {
   2807         comp.link_prog_node = main_progress_node.start("Linking", 0);
   2808     }
   2809     defer if (have_link_node) {
   2810         comp.link_prog_node.end();
   2811         comp.link_prog_node = .none;
   2812     };
   2813 
   2814     try comp.performAllTheWork(main_progress_node);
   2815 
   2816     if (comp.zcu) |zcu| {
   2817         const pt: Zcu.PerThread = .activate(zcu, .main);
   2818         defer pt.deactivate();
   2819 
   2820         if (!zcu.skip_analysis_this_update) {
   2821             if (comp.config.is_test) {
   2822                 // The `test_functions` decl has been intentionally postponed until now,
   2823                 // at which point we must populate it with the list of test functions that
   2824                 // have been discovered and not filtered out.
   2825                 try pt.populateTestFunctions();
   2826             }
   2827 
   2828             try pt.processExports();
   2829         }
   2830 
   2831         if (build_options.enable_debug_extensions and comp.verbose_intern_pool) {
   2832             std.debug.print("intern pool stats for '{s}':\n", .{
   2833                 comp.root_name,
   2834             });
   2835             zcu.intern_pool.dump();
   2836         }
   2837 
   2838         if (build_options.enable_debug_extensions and comp.verbose_generic_instances) {
   2839             std.debug.print("generic instances for '{s}:0x{x}':\n", .{
   2840                 comp.root_name,
   2841                 @intFromPtr(zcu),
   2842             });
   2843             zcu.intern_pool.dumpGenericInstances(gpa);
   2844         }
   2845     }
   2846 
   2847     if (anyErrors(comp)) {
   2848         // Skip flushing and keep source files loaded for error reporting.
   2849         return;
   2850     }
   2851 
   2852     if (comp.zcu == null and comp.config.output_mode == .Obj and comp.c_object_table.count() == 1) {
   2853         // This is `zig build-obj foo.c`. We can emit asm and LLVM IR/bitcode.
   2854         const c_obj_path = comp.c_object_table.keys()[0].status.success.object_path;
   2855         if (comp.emit_asm) |path| try comp.emitFromCObject(arena, c_obj_path, ".s", path);
   2856         if (comp.emit_llvm_ir) |path| try comp.emitFromCObject(arena, c_obj_path, ".ll", path);
   2857         if (comp.emit_llvm_bc) |path| try comp.emitFromCObject(arena, c_obj_path, ".bc", path);
   2858     }
   2859 
   2860     switch (comp.cache_use) {
   2861         .none, .incremental => {
   2862             try flush(comp, arena, .main);
   2863         },
   2864         .whole => |whole| {
   2865             if (comp.file_system_inputs) |buf| try man.populateFileSystemInputs(buf);
   2866             if (comp.parent_whole_cache) |pwc| {
   2867                 pwc.mutex.lock();
   2868                 defer pwc.mutex.unlock();
   2869                 try man.populateOtherManifest(pwc.manifest, pwc.prefix_map);
   2870             }
   2871 
   2872             const bin_digest = man.finalBin();
   2873             const hex_digest = Cache.binToHex(bin_digest);
   2874 
   2875             // Work around windows `AccessDenied` if any files within this
   2876             // directory are open by closing and reopening the file handles.
   2877             const need_writable_dance: enum { no, lf_only, lf_and_debug } = w: {
   2878                 if (builtin.os.tag == .windows) {
   2879                     if (comp.bin_file) |lf| {
   2880                         // We cannot just call `makeExecutable` as it makes a false
   2881                         // assumption that we have a file handle open only when linking
   2882                         // an executable file. This used to be true when our linkers
   2883                         // were incapable of emitting relocatables and static archive.
   2884                         // Now that they are capable, we need to unconditionally close
   2885                         // the file handle and re-open it in the follow up call to
   2886                         // `makeWritable`.
   2887                         if (lf.file) |f| {
   2888                             f.close();
   2889                             lf.file = null;
   2890 
   2891                             if (lf.closeDebugInfo()) break :w .lf_and_debug;
   2892                             break :w .lf_only;
   2893                         }
   2894                     }
   2895                 }
   2896                 break :w .no;
   2897             };
   2898 
   2899             // Rename the temporary directory into place.
   2900             // Close tmp dir and link.File to avoid open handle during rename.
   2901             whole.tmp_artifact_directory.?.handle.close();
   2902             whole.tmp_artifact_directory = null;
   2903             const s = std.fs.path.sep_str;
   2904             const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int);
   2905             const o_sub_path = "o" ++ s ++ hex_digest;
   2906             renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| {
   2907                 return comp.setMiscFailure(
   2908                     .rename_results,
   2909                     "failed to rename compilation results ('{}{s}') into local cache ('{}{s}'): {s}",
   2910                     .{
   2911                         comp.dirs.local_cache, tmp_dir_sub_path,
   2912                         comp.dirs.local_cache, o_sub_path,
   2913                         @errorName(err),
   2914                     },
   2915                 );
   2916             };
   2917             comp.digest = bin_digest;
   2918 
   2919             // The linker flush functions need to know the final output path
   2920             // for debug info purposes because executable debug info contains
   2921             // references object file paths.
   2922             if (comp.bin_file) |lf| {
   2923                 lf.emit = .{
   2924                     .root_dir = comp.dirs.local_cache,
   2925                     .sub_path = try std.fs.path.join(arena, &.{ o_sub_path, comp.emit_bin.? }),
   2926                 };
   2927 
   2928                 switch (need_writable_dance) {
   2929                     .no => {},
   2930                     .lf_only => try lf.makeWritable(),
   2931                     .lf_and_debug => {
   2932                         try lf.makeWritable();
   2933                         try lf.reopenDebugInfo();
   2934                     },
   2935                 }
   2936             }
   2937 
   2938             try flush(comp, arena, .main);
   2939 
   2940             // Calling `flush` may have produced errors, in which case the
   2941             // cache manifest must not be written.
   2942             if (anyErrors(comp)) return;
   2943 
   2944             // Failure here only means an unnecessary cache miss.
   2945             man.writeManifest() catch |err| {
   2946                 log.warn("failed to write cache manifest: {s}", .{@errorName(err)});
   2947             };
   2948 
   2949             if (comp.bin_file) |lf| {
   2950                 lf.destroy();
   2951                 comp.bin_file = null;
   2952             }
   2953 
   2954             assert(whole.lock == null);
   2955             whole.lock = man.toOwnedLock();
   2956         },
   2957     }
   2958 }
   2959 
   2960 pub fn appendFileSystemInput(comp: *Compilation, path: Compilation.Path) Allocator.Error!void {
   2961     const gpa = comp.gpa;
   2962     const fsi = comp.file_system_inputs orelse return;
   2963     const prefixes = comp.cache_parent.prefixes();
   2964 
   2965     const want_prefix_dir: Cache.Directory = switch (path.root) {
   2966         .zig_lib => comp.dirs.zig_lib,
   2967         .global_cache => comp.dirs.global_cache,
   2968         .local_cache => comp.dirs.local_cache,
   2969         .none => .cwd(),
   2970     };
   2971     const prefix: u8 = for (prefixes, 1..) |prefix_dir, i| {
   2972         if (prefix_dir.eql(want_prefix_dir)) {
   2973             break @intCast(i);
   2974         }
   2975     } else std.debug.panic(
   2976         "missing prefix directory '{s}' ('{}') for '{s}'",
   2977         .{ @tagName(path.root), want_prefix_dir, path.sub_path },
   2978     );
   2979 
   2980     try fsi.ensureUnusedCapacity(gpa, path.sub_path.len + 3);
   2981     if (fsi.items.len > 0) fsi.appendAssumeCapacity(0);
   2982     fsi.appendAssumeCapacity(prefix);
   2983     fsi.appendSliceAssumeCapacity(path.sub_path);
   2984 }
   2985 
   2986 fn resolveEmitPath(comp: *Compilation, path: []const u8) Cache.Path {
   2987     return .{
   2988         .root_dir = switch (comp.cache_use) {
   2989             .none => .cwd(),
   2990             .incremental => |i| i.artifact_directory,
   2991             .whole => |w| w.tmp_artifact_directory.?,
   2992         },
   2993         .sub_path = path,
   2994     };
   2995 }
   2996 /// Like `resolveEmitPath`, but for calling during `flush`. The returned `Cache.Path` may reference
   2997 /// memory from `arena`, and may reference `path` itself.
   2998 /// If `kind == .temp`, then the returned path will be in a temporary or cache directory. This is
   2999 /// useful for intermediate files, such as the ZCU object file emitted by the LLVM backend.
   3000 pub fn resolveEmitPathFlush(
   3001     comp: *Compilation,
   3002     arena: Allocator,
   3003     kind: enum { temp, artifact },
   3004     path: []const u8,
   3005 ) Allocator.Error!Cache.Path {
   3006     switch (comp.cache_use) {
   3007         .none => |none| return .{
   3008             .root_dir = switch (kind) {
   3009                 .temp => none.tmp_artifact_directory.?,
   3010                 .artifact => .cwd(),
   3011             },
   3012             .sub_path = path,
   3013         },
   3014         .incremental, .whole => return .{
   3015             .root_dir = comp.dirs.local_cache,
   3016             .sub_path = try fs.path.join(arena, &.{
   3017                 "o",
   3018                 &Cache.binToHex(comp.digest.?),
   3019                 path,
   3020             }),
   3021         },
   3022     }
   3023 }
   3024 fn flush(
   3025     comp: *Compilation,
   3026     arena: Allocator,
   3027     tid: Zcu.PerThread.Id,
   3028 ) !void {
   3029     if (comp.zcu) |zcu| {
   3030         if (zcu.llvm_object) |llvm_object| {
   3031             // Emit the ZCU object from LLVM now; it's required to flush the output file.
   3032             // If there's an output file, it wants to decide where the LLVM object goes!
   3033             const sub_prog_node = comp.link_prog_node.start("LLVM Emit Object", 0);
   3034             defer sub_prog_node.end();
   3035             try llvm_object.emit(.{
   3036                 .pre_ir_path = comp.verbose_llvm_ir,
   3037                 .pre_bc_path = comp.verbose_llvm_bc,
   3038 
   3039                 .bin_path = p: {
   3040                     const lf = comp.bin_file orelse break :p null;
   3041                     const p = try comp.resolveEmitPathFlush(arena, .temp, lf.zcu_object_basename.?);
   3042                     break :p try p.toStringZ(arena);
   3043                 },
   3044                 .asm_path = p: {
   3045                     const raw = comp.emit_asm orelse break :p null;
   3046                     const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
   3047                     break :p try p.toStringZ(arena);
   3048                 },
   3049                 .post_ir_path = p: {
   3050                     const raw = comp.emit_llvm_ir orelse break :p null;
   3051                     const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
   3052                     break :p try p.toStringZ(arena);
   3053                 },
   3054                 .post_bc_path = p: {
   3055                     const raw = comp.emit_llvm_bc orelse break :p null;
   3056                     const p = try comp.resolveEmitPathFlush(arena, .artifact, raw);
   3057                     break :p try p.toStringZ(arena);
   3058                 },
   3059 
   3060                 .is_debug = comp.root_mod.optimize_mode == .Debug,
   3061                 .is_small = comp.root_mod.optimize_mode == .ReleaseSmall,
   3062                 .time_report = comp.time_report,
   3063                 .sanitize_thread = comp.config.any_sanitize_thread,
   3064                 .fuzz = comp.config.any_fuzz,
   3065                 .lto = comp.config.lto,
   3066             });
   3067         }
   3068     }
   3069     if (comp.bin_file) |lf| {
   3070         // This is needed before reading the error flags.
   3071         lf.flush(arena, tid, comp.link_prog_node) catch |err| switch (err) {
   3072             error.LinkFailure => {}, // Already reported.
   3073             error.OutOfMemory => return error.OutOfMemory,
   3074         };
   3075     }
   3076     if (comp.zcu) |zcu| {
   3077         try link.File.C.flushEmitH(zcu);
   3078     }
   3079 }
   3080 
   3081 /// This function is called by the frontend before flush(). It communicates that
   3082 /// `options.bin_file.emit` directory needs to be renamed from
   3083 /// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`.
   3084 /// The frontend would like to simply perform a file system rename, however,
   3085 /// some linker backends care about the file paths of the objects they are linking.
   3086 /// So this function call tells linker backends to rename the paths of object files
   3087 /// to observe the new directory path.
   3088 /// Linker backends which do not have this requirement can fall back to the simple
   3089 /// implementation at the bottom of this function.
   3090 /// This function is only called when CacheMode is `whole`.
   3091 fn renameTmpIntoCache(
   3092     cache_directory: Cache.Directory,
   3093     tmp_dir_sub_path: []const u8,
   3094     o_sub_path: []const u8,
   3095 ) !void {
   3096     var seen_eaccess = false;
   3097     while (true) {
   3098         std.fs.rename(
   3099             cache_directory.handle,
   3100             tmp_dir_sub_path,
   3101             cache_directory.handle,
   3102             o_sub_path,
   3103         ) catch |err| switch (err) {
   3104             // On Windows, rename fails with `AccessDenied` rather than `PathAlreadyExists`.
   3105             // See https://github.com/ziglang/zig/issues/8362
   3106             error.AccessDenied => switch (builtin.os.tag) {
   3107                 .windows => {
   3108                     if (seen_eaccess) return error.AccessDenied;
   3109                     seen_eaccess = true;
   3110                     try cache_directory.handle.deleteTree(o_sub_path);
   3111                     continue;
   3112                 },
   3113                 else => return error.AccessDenied,
   3114             },
   3115             error.PathAlreadyExists => {
   3116                 try cache_directory.handle.deleteTree(o_sub_path);
   3117                 continue;
   3118             },
   3119             error.FileNotFound => {
   3120                 try cache_directory.handle.makePath("o");
   3121                 continue;
   3122             },
   3123             else => |e| return e,
   3124         };
   3125         break;
   3126     }
   3127 }
   3128 
   3129 /// This is only observed at compile-time and used to emit a compile error
   3130 /// to remind the programmer to update multiple related pieces of code that
   3131 /// are in different locations. Bump this number when adding or deleting
   3132 /// anything from the link cache manifest.
   3133 pub const link_hash_implementation_version = 14;
   3134 
   3135 fn addNonIncrementalStuffToCacheManifest(
   3136     comp: *Compilation,
   3137     arena: Allocator,
   3138     man: *Cache.Manifest,
   3139 ) !void {
   3140     comptime assert(link_hash_implementation_version == 14);
   3141 
   3142     if (comp.zcu) |zcu| {
   3143         try addModuleTableToCacheHash(zcu, arena, &man.hash, .{ .files = man });
   3144 
   3145         // Synchronize with other matching comments: ZigOnlyHashStuff
   3146         man.hash.addListOfBytes(comp.test_filters);
   3147         man.hash.addOptionalBytes(comp.test_name_prefix);
   3148         man.hash.add(comp.skip_linker_dependencies);
   3149         //man.hash.add(zcu.emit_h != .no);
   3150         man.hash.add(zcu.error_limit);
   3151     } else {
   3152         cache_helpers.addModule(&man.hash, comp.root_mod);
   3153     }
   3154 
   3155     try link.hashInputs(man, comp.link_inputs);
   3156 
   3157     for (comp.c_object_table.keys()) |key| {
   3158         _ = try man.addFile(key.src.src_path, null);
   3159         man.hash.addOptional(key.src.ext);
   3160         man.hash.addListOfBytes(key.src.extra_flags);
   3161     }
   3162 
   3163     for (comp.win32_resource_table.keys()) |key| {
   3164         switch (key.src) {
   3165             .rc => |rc_src| {
   3166                 _ = try man.addFile(rc_src.src_path, null);
   3167                 man.hash.addListOfBytes(rc_src.extra_flags);
   3168             },
   3169             .manifest => |manifest_path| {
   3170                 _ = try man.addFile(manifest_path, null);
   3171             },
   3172         }
   3173     }
   3174 
   3175     man.hash.add(comp.config.use_llvm);
   3176     man.hash.add(comp.config.use_lib_llvm);
   3177     man.hash.add(comp.config.is_test);
   3178     man.hash.add(comp.config.import_memory);
   3179     man.hash.add(comp.config.export_memory);
   3180     man.hash.add(comp.config.shared_memory);
   3181     man.hash.add(comp.config.dll_export_fns);
   3182     man.hash.add(comp.config.rdynamic);
   3183 
   3184     man.hash.addOptionalBytes(comp.sysroot);
   3185     man.hash.addOptional(comp.version);
   3186     man.hash.add(comp.link_eh_frame_hdr);
   3187     man.hash.add(comp.skip_linker_dependencies);
   3188     man.hash.add(comp.compiler_rt_strat);
   3189     man.hash.add(comp.ubsan_rt_strat);
   3190     man.hash.add(comp.rc_includes);
   3191     man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
   3192     man.hash.addListOfBytes(comp.framework_dirs);
   3193     man.hash.addListOfBytes(comp.windows_libs.keys());
   3194 
   3195     man.hash.addListOfBytes(comp.global_cc_argv);
   3196 
   3197     const opts = comp.cache_use.whole.lf_open_opts;
   3198 
   3199     try man.addOptionalFile(opts.linker_script);
   3200     try man.addOptionalFile(opts.version_script);
   3201     man.hash.add(opts.allow_undefined_version);
   3202     man.hash.addOptional(opts.enable_new_dtags);
   3203 
   3204     man.hash.addOptional(opts.stack_size);
   3205     man.hash.addOptional(opts.image_base);
   3206     man.hash.addOptional(opts.gc_sections);
   3207     man.hash.add(opts.emit_relocs);
   3208     const target = comp.root_mod.resolved_target.result;
   3209     if (target.ofmt == .macho or target.ofmt == .coff) {
   3210         // TODO remove this, libraries need to be resolved by the frontend. this is already
   3211         // done by ELF.
   3212         for (opts.lib_directories) |lib_directory| man.hash.addOptionalBytes(lib_directory.path);
   3213     }
   3214     man.hash.addListOfBytes(opts.rpath_list);
   3215     man.hash.addListOfBytes(opts.symbol_wrap_set.keys());
   3216     if (comp.config.link_libc) {
   3217         man.hash.add(comp.libc_installation != null);
   3218         if (comp.libc_installation) |libc_installation| {
   3219             man.hash.addOptionalBytes(libc_installation.crt_dir);
   3220             if (target.abi == .msvc or target.abi == .itanium) {
   3221                 man.hash.addOptionalBytes(libc_installation.msvc_lib_dir);
   3222                 man.hash.addOptionalBytes(libc_installation.kernel32_lib_dir);
   3223             }
   3224         }
   3225         man.hash.addOptionalBytes(target.dynamic_linker.get());
   3226     }
   3227     man.hash.add(opts.repro);
   3228     man.hash.addOptional(opts.allow_shlib_undefined);
   3229     man.hash.add(opts.bind_global_refs_locally);
   3230 
   3231     // ELF specific stuff
   3232     man.hash.add(opts.z_nodelete);
   3233     man.hash.add(opts.z_notext);
   3234     man.hash.add(opts.z_defs);
   3235     man.hash.add(opts.z_origin);
   3236     man.hash.add(opts.z_nocopyreloc);
   3237     man.hash.add(opts.z_now);
   3238     man.hash.add(opts.z_relro);
   3239     man.hash.add(opts.z_common_page_size orelse 0);
   3240     man.hash.add(opts.z_max_page_size orelse 0);
   3241     man.hash.add(opts.hash_style);
   3242     man.hash.add(opts.compress_debug_sections);
   3243     man.hash.addOptional(opts.sort_section);
   3244     man.hash.addOptionalBytes(opts.soname);
   3245     man.hash.add(opts.build_id);
   3246 
   3247     // WASM specific stuff
   3248     man.hash.addOptional(opts.initial_memory);
   3249     man.hash.addOptional(opts.max_memory);
   3250     man.hash.addOptional(opts.global_base);
   3251     man.hash.addListOfBytes(opts.export_symbol_names);
   3252 
   3253     // Mach-O specific stuff
   3254     try link.File.MachO.hashAddFrameworks(man, opts.frameworks);
   3255     try man.addOptionalFile(opts.entitlements);
   3256     man.hash.addOptional(opts.pagezero_size);
   3257     man.hash.addOptional(opts.headerpad_size);
   3258     man.hash.add(opts.headerpad_max_install_names);
   3259     man.hash.add(opts.dead_strip_dylibs);
   3260     man.hash.add(opts.force_load_objc);
   3261     man.hash.add(opts.discard_local_symbols);
   3262 
   3263     // COFF specific stuff
   3264     man.hash.addOptional(opts.subsystem);
   3265     man.hash.add(opts.tsaware);
   3266     man.hash.add(opts.nxcompat);
   3267     man.hash.add(opts.dynamicbase);
   3268     man.hash.addOptional(opts.major_subsystem_version);
   3269     man.hash.addOptional(opts.minor_subsystem_version);
   3270 }
   3271 
   3272 fn emitFromCObject(
   3273     comp: *Compilation,
   3274     arena: Allocator,
   3275     c_obj_path: Cache.Path,
   3276     new_ext: []const u8,
   3277     unresolved_emit_path: []const u8,
   3278 ) Allocator.Error!void {
   3279     // The dirname and stem (i.e. everything but the extension), of the sub path of the C object.
   3280     // We'll append `new_ext` to it to get the path to the right thing (asm, LLVM IR, etc).
   3281     const c_obj_dir_and_stem: []const u8 = p: {
   3282         const p = c_obj_path.sub_path;
   3283         const ext_len = fs.path.extension(p).len;
   3284         break :p p[0 .. p.len - ext_len];
   3285     };
   3286     const src_path: Cache.Path = .{
   3287         .root_dir = c_obj_path.root_dir,
   3288         .sub_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
   3289             c_obj_dir_and_stem,
   3290             new_ext,
   3291         }),
   3292     };
   3293     const emit_path = comp.resolveEmitPath(unresolved_emit_path);
   3294 
   3295     src_path.root_dir.handle.copyFile(
   3296         src_path.sub_path,
   3297         emit_path.root_dir.handle,
   3298         emit_path.sub_path,
   3299         .{},
   3300     ) catch |err| log.err("unable to copy '{}' to '{}': {s}", .{
   3301         src_path,
   3302         emit_path,
   3303         @errorName(err),
   3304     });
   3305 }
   3306 
   3307 /// Having the file open for writing is problematic as far as executing the
   3308 /// binary is concerned. This will remove the write flag, or close the file,
   3309 /// or whatever is needed so that it can be executed.
   3310 /// After this, one must call` makeFileWritable` before calling `update`.
   3311 pub fn makeBinFileExecutable(comp: *Compilation) !void {
   3312     if (!dev.env.supports(.make_executable)) return;
   3313     const lf = comp.bin_file orelse return;
   3314     return lf.makeExecutable();
   3315 }
   3316 
   3317 pub fn makeBinFileWritable(comp: *Compilation) !void {
   3318     const lf = comp.bin_file orelse return;
   3319     return lf.makeWritable();
   3320 }
   3321 
   3322 const Header = extern struct {
   3323     intern_pool: extern struct {
   3324         thread_count: u32,
   3325         src_hash_deps_len: u32,
   3326         nav_val_deps_len: u32,
   3327         nav_ty_deps_len: u32,
   3328         interned_deps_len: u32,
   3329         zon_file_deps_len: u32,
   3330         embed_file_deps_len: u32,
   3331         namespace_deps_len: u32,
   3332         namespace_name_deps_len: u32,
   3333         first_dependency_len: u32,
   3334         dep_entries_len: u32,
   3335         free_dep_entries_len: u32,
   3336     },
   3337 
   3338     const PerThread = extern struct {
   3339         intern_pool: extern struct {
   3340             items_len: u32,
   3341             extra_len: u32,
   3342             limbs_len: u32,
   3343             string_bytes_len: u32,
   3344             tracked_insts_len: u32,
   3345             files_len: u32,
   3346         },
   3347     };
   3348 };
   3349 
   3350 /// Note that all state that is included in the cache hash namespace is *not*
   3351 /// saved, such as the target and most CLI flags. A cache hit will only occur
   3352 /// when subsequent compiler invocations use the same set of flags.
   3353 pub fn saveState(comp: *Compilation) !void {
   3354     dev.check(.incremental);
   3355 
   3356     const lf = comp.bin_file orelse return;
   3357 
   3358     const gpa = comp.gpa;
   3359 
   3360     var bufs = std.ArrayList(std.posix.iovec_const).init(gpa);
   3361     defer bufs.deinit();
   3362 
   3363     var pt_headers = std.ArrayList(Header.PerThread).init(gpa);
   3364     defer pt_headers.deinit();
   3365 
   3366     if (comp.zcu) |zcu| {
   3367         const ip = &zcu.intern_pool;
   3368         const header: Header = .{
   3369             .intern_pool = .{
   3370                 .thread_count = @intCast(ip.locals.len),
   3371                 .src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
   3372                 .nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
   3373                 .nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
   3374                 .interned_deps_len = @intCast(ip.interned_deps.count()),
   3375                 .zon_file_deps_len = @intCast(ip.zon_file_deps.count()),
   3376                 .embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
   3377                 .namespace_deps_len = @intCast(ip.namespace_deps.count()),
   3378                 .namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
   3379                 .first_dependency_len = @intCast(ip.first_dependency.count()),
   3380                 .dep_entries_len = @intCast(ip.dep_entries.items.len),
   3381                 .free_dep_entries_len = @intCast(ip.free_dep_entries.items.len),
   3382             },
   3383         };
   3384 
   3385         try pt_headers.ensureTotalCapacityPrecise(header.intern_pool.thread_count);
   3386         for (ip.locals) |*local| pt_headers.appendAssumeCapacity(.{
   3387             .intern_pool = .{
   3388                 .items_len = @intCast(local.mutate.items.len),
   3389                 .extra_len = @intCast(local.mutate.extra.len),
   3390                 .limbs_len = @intCast(local.mutate.limbs.len),
   3391                 .string_bytes_len = @intCast(local.mutate.strings.len),
   3392                 .tracked_insts_len = @intCast(local.mutate.tracked_insts.len),
   3393                 .files_len = @intCast(local.mutate.files.len),
   3394             },
   3395         });
   3396 
   3397         try bufs.ensureTotalCapacityPrecise(14 + 8 * pt_headers.items.len);
   3398         addBuf(&bufs, mem.asBytes(&header));
   3399         addBuf(&bufs, mem.sliceAsBytes(pt_headers.items));
   3400 
   3401         addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.keys()));
   3402         addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
   3403         addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
   3404         addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));
   3405         addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));
   3406         addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));
   3407         addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.keys()));
   3408         addBuf(&bufs, mem.sliceAsBytes(ip.interned_deps.values()));
   3409         addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.keys()));
   3410         addBuf(&bufs, mem.sliceAsBytes(ip.zon_file_deps.values()));
   3411         addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.keys()));
   3412         addBuf(&bufs, mem.sliceAsBytes(ip.embed_file_deps.values()));
   3413         addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));
   3414         addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));
   3415         addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
   3416         addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.values()));
   3417 
   3418         addBuf(&bufs, mem.sliceAsBytes(ip.first_dependency.keys()));
   3419         addBuf(&bufs, mem.sliceAsBytes(ip.first_dependency.values()));
   3420         addBuf(&bufs, mem.sliceAsBytes(ip.dep_entries.items));
   3421         addBuf(&bufs, mem.sliceAsBytes(ip.free_dep_entries.items));
   3422 
   3423         for (ip.locals, pt_headers.items) |*local, pt_header| {
   3424             if (pt_header.intern_pool.limbs_len > 0) {
   3425                 addBuf(&bufs, mem.sliceAsBytes(local.shared.limbs.view().items(.@"0")[0..pt_header.intern_pool.limbs_len]));
   3426             }
   3427             if (pt_header.intern_pool.extra_len > 0) {
   3428                 addBuf(&bufs, mem.sliceAsBytes(local.shared.extra.view().items(.@"0")[0..pt_header.intern_pool.extra_len]));
   3429             }
   3430             if (pt_header.intern_pool.items_len > 0) {
   3431                 addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.data)[0..pt_header.intern_pool.items_len]));
   3432                 addBuf(&bufs, mem.sliceAsBytes(local.shared.items.view().items(.tag)[0..pt_header.intern_pool.items_len]));
   3433             }
   3434             if (pt_header.intern_pool.string_bytes_len > 0) {
   3435                 addBuf(&bufs, local.shared.strings.view().items(.@"0")[0..pt_header.intern_pool.string_bytes_len]);
   3436             }
   3437             if (pt_header.intern_pool.tracked_insts_len > 0) {
   3438                 addBuf(&bufs, mem.sliceAsBytes(local.shared.tracked_insts.view().items(.@"0")[0..pt_header.intern_pool.tracked_insts_len]));
   3439             }
   3440             if (pt_header.intern_pool.files_len > 0) {
   3441                 addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.bin_digest)[0..pt_header.intern_pool.files_len]));
   3442                 addBuf(&bufs, mem.sliceAsBytes(local.shared.files.view().items(.root_type)[0..pt_header.intern_pool.files_len]));
   3443             }
   3444         }
   3445 
   3446         //// TODO: compilation errors
   3447         //// TODO: namespaces
   3448         //// TODO: decls
   3449     }
   3450 
   3451     // linker state
   3452     switch (lf.tag) {
   3453         .wasm => {
   3454             dev.check(link.File.Tag.wasm.devFeature());
   3455             const wasm = lf.cast(.wasm).?;
   3456             const is_obj = comp.config.output_mode == .Obj;
   3457             try bufs.ensureUnusedCapacity(85);
   3458             addBuf(&bufs, wasm.string_bytes.items);
   3459             // TODO make it well-defined memory layout
   3460             //addBuf(&bufs, mem.sliceAsBytes(wasm.objects.items));
   3461             addBuf(&bufs, mem.sliceAsBytes(wasm.func_types.keys()));
   3462             addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.keys()));
   3463             addBuf(&bufs, mem.sliceAsBytes(wasm.object_function_imports.values()));
   3464             addBuf(&bufs, mem.sliceAsBytes(wasm.object_functions.items));
   3465             addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.keys()));
   3466             addBuf(&bufs, mem.sliceAsBytes(wasm.object_global_imports.values()));
   3467             addBuf(&bufs, mem.sliceAsBytes(wasm.object_globals.items));
   3468             addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.keys()));
   3469             addBuf(&bufs, mem.sliceAsBytes(wasm.object_table_imports.values()));
   3470             addBuf(&bufs, mem.sliceAsBytes(wasm.object_tables.items));
   3471             addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.keys()));
   3472             addBuf(&bufs, mem.sliceAsBytes(wasm.object_memory_imports.values()));
   3473             addBuf(&bufs, mem.sliceAsBytes(wasm.object_memories.items));
   3474             addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.tag)));
   3475             addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.offset)));
   3476             // TODO handle the union safety field
   3477             //addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.pointee)));
   3478             addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations.items(.addend)));
   3479             addBuf(&bufs, mem.sliceAsBytes(wasm.object_init_funcs.items));
   3480             addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_segments.items));
   3481             addBuf(&bufs, mem.sliceAsBytes(wasm.object_datas.items));
   3482             addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.keys()));
   3483             addBuf(&bufs, mem.sliceAsBytes(wasm.object_data_imports.values()));
   3484             addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.keys()));
   3485             addBuf(&bufs, mem.sliceAsBytes(wasm.object_custom_segments.values()));
   3486             // TODO make it well-defined memory layout
   3487             // addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdats.items));
   3488             addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.keys()));
   3489             addBuf(&bufs, mem.sliceAsBytes(wasm.object_relocations_table.values()));
   3490             addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.kind)));
   3491             addBuf(&bufs, mem.sliceAsBytes(wasm.object_comdat_symbols.items(.index)));
   3492             addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.tag)));
   3493             addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.offset)));
   3494             // TODO handle the union safety field
   3495             //addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.pointee)));
   3496             addBuf(&bufs, mem.sliceAsBytes(wasm.out_relocs.items(.addend)));
   3497             addBuf(&bufs, mem.sliceAsBytes(wasm.uav_fixups.items));
   3498             addBuf(&bufs, mem.sliceAsBytes(wasm.nav_fixups.items));
   3499             addBuf(&bufs, mem.sliceAsBytes(wasm.func_table_fixups.items));
   3500             if (is_obj) {
   3501                 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.keys()));
   3502                 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_obj.values()));
   3503                 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.keys()));
   3504                 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_obj.values()));
   3505             } else {
   3506                 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.keys()));
   3507                 addBuf(&bufs, mem.sliceAsBytes(wasm.navs_exe.values()));
   3508                 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.keys()));
   3509                 addBuf(&bufs, mem.sliceAsBytes(wasm.uavs_exe.values()));
   3510             }
   3511             addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.keys()));
   3512             addBuf(&bufs, mem.sliceAsBytes(wasm.overaligned_uavs.values()));
   3513             addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.keys()));
   3514             // TODO handle the union safety field
   3515             // addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_funcs.values()));
   3516             addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.keys()));
   3517             addBuf(&bufs, mem.sliceAsBytes(wasm.nav_exports.values()));
   3518             addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.keys()));
   3519             addBuf(&bufs, mem.sliceAsBytes(wasm.uav_exports.values()));
   3520             addBuf(&bufs, mem.sliceAsBytes(wasm.imports.keys()));
   3521             addBuf(&bufs, mem.sliceAsBytes(wasm.missing_exports.keys()));
   3522             addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.keys()));
   3523             addBuf(&bufs, mem.sliceAsBytes(wasm.function_exports.values()));
   3524             addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.keys()));
   3525             addBuf(&bufs, mem.sliceAsBytes(wasm.hidden_function_exports.values()));
   3526             addBuf(&bufs, mem.sliceAsBytes(wasm.global_exports.items));
   3527             addBuf(&bufs, mem.sliceAsBytes(wasm.functions.keys()));
   3528             addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.keys()));
   3529             addBuf(&bufs, mem.sliceAsBytes(wasm.function_imports.values()));
   3530             addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.keys()));
   3531             addBuf(&bufs, mem.sliceAsBytes(wasm.data_imports.values()));
   3532             addBuf(&bufs, mem.sliceAsBytes(wasm.data_segments.keys()));
   3533             addBuf(&bufs, mem.sliceAsBytes(wasm.globals.keys()));
   3534             addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.keys()));
   3535             addBuf(&bufs, mem.sliceAsBytes(wasm.global_imports.values()));
   3536             addBuf(&bufs, mem.sliceAsBytes(wasm.tables.keys()));
   3537             addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.keys()));
   3538             addBuf(&bufs, mem.sliceAsBytes(wasm.table_imports.values()));
   3539             addBuf(&bufs, mem.sliceAsBytes(wasm.zcu_indirect_function_set.keys()));
   3540             addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_import_set.keys()));
   3541             addBuf(&bufs, mem.sliceAsBytes(wasm.object_indirect_function_set.keys()));
   3542             addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.tag)));
   3543             // TODO handle the union safety field
   3544             //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data)));
   3545             addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items));
   3546             addBuf(&bufs, mem.sliceAsBytes(wasm.mir_locals.items));
   3547             addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items));
   3548             addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items));
   3549 
   3550             // TODO add as header fields
   3551             // entry_resolution: FunctionImport.Resolution
   3552             // function_exports_len: u32
   3553             // global_exports_len: u32
   3554             // functions_end_prelink: u32
   3555             // globals_end_prelink: u32
   3556             // error_name_table_ref_count: u32
   3557             // tag_name_table_ref_count: u32
   3558             // any_tls_relocs: bool
   3559             // any_passive_inits: bool
   3560         },
   3561         else => log.err("TODO implement saving linker state for {s}", .{@tagName(lf.tag)}),
   3562     }
   3563 
   3564     var basename_buf: [255]u8 = undefined;
   3565     const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{
   3566         comp.root_name,
   3567     }) catch o: {
   3568         basename_buf[basename_buf.len - 4 ..].* = ".zcs".*;
   3569         break :o &basename_buf;
   3570     };
   3571 
   3572     // Using an atomic file prevents a crash or power failure from corrupting
   3573     // the previous incremental compilation state.
   3574     var af = try lf.emit.root_dir.handle.atomicFile(basename, .{});
   3575     defer af.deinit();
   3576     try af.file.pwritevAll(bufs.items, 0);
   3577     try af.finish();
   3578 }
   3579 
   3580 fn addBuf(list: *std.ArrayList(std.posix.iovec_const), buf: []const u8) void {
   3581     // Even when len=0, the undefined pointer might cause EFAULT.
   3582     if (buf.len == 0) return;
   3583     list.appendAssumeCapacity(.{ .base = buf.ptr, .len = buf.len });
   3584 }
   3585 
   3586 /// This function is temporally single-threaded.
   3587 pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
   3588     const gpa = comp.gpa;
   3589 
   3590     var bundle: ErrorBundle.Wip = undefined;
   3591     try bundle.init(gpa);
   3592     defer bundle.deinit();
   3593 
   3594     for (comp.failed_c_objects.values()) |diag_bundle| {
   3595         try diag_bundle.addToErrorBundle(&bundle);
   3596     }
   3597 
   3598     for (comp.failed_win32_resources.values()) |error_bundle| {
   3599         try bundle.addBundleAsRoots(error_bundle);
   3600     }
   3601 
   3602     for (comp.link_diags.lld.items) |lld_error| {
   3603         const notes_len = @as(u32, @intCast(lld_error.context_lines.len));
   3604 
   3605         try bundle.addRootErrorMessage(.{
   3606             .msg = try bundle.addString(lld_error.msg),
   3607             .notes_len = notes_len,
   3608         });
   3609         const notes_start = try bundle.reserveNotes(notes_len);
   3610         for (notes_start.., lld_error.context_lines) |note, context_line| {
   3611             bundle.extra.items[note] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{
   3612                 .msg = try bundle.addString(context_line),
   3613             }));
   3614         }
   3615     }
   3616     for (comp.misc_failures.values()) |*value| {
   3617         try bundle.addRootErrorMessage(.{
   3618             .msg = try bundle.addString(value.msg),
   3619             .notes_len = if (value.children) |b| b.errorMessageCount() else 0,
   3620         });
   3621         if (value.children) |b| try bundle.addBundleAsNotes(b);
   3622     }
   3623     if (comp.alloc_failure_occurred or comp.link_diags.flags.alloc_failure_occurred) {
   3624         try bundle.addRootErrorMessage(.{
   3625             .msg = try bundle.addString("memory allocation failure"),
   3626         });
   3627     }
   3628 
   3629     if (comp.zcu) |zcu| zcu_errors: {
   3630         if (zcu.multi_module_err != null) {
   3631             try zcu.addFileInMultipleModulesError(&bundle);
   3632             break :zcu_errors;
   3633         }
   3634         for (zcu.failed_imports.items) |failed| {
   3635             assert(zcu.alive_files.contains(failed.file_index)); // otherwise it wouldn't have been added
   3636             const file = zcu.fileByIndex(failed.file_index);
   3637             const source = try file.getSource(zcu);
   3638             const tree = try file.getTree(zcu);
   3639             const start = tree.tokenStart(failed.import_token);
   3640             const end = start + tree.tokenSlice(failed.import_token).len;
   3641             const loc = std.zig.findLineColumn(source.bytes, start);
   3642             try bundle.addRootErrorMessage(.{
   3643                 .msg = switch (failed.kind) {
   3644                     .file_outside_module_root => try bundle.addString("import of file outside module path"),
   3645                     .illegal_zig_import => try bundle.addString("this compiler implementation does not allow importing files from this directory"),
   3646                 },
   3647                 .src_loc = try bundle.addSourceLocation(.{
   3648                     .src_path = try bundle.printString("{}", .{file.path.fmt(comp)}),
   3649                     .span_start = start,
   3650                     .span_main = start,
   3651                     .span_end = @intCast(end),
   3652                     .line = @intCast(loc.line),
   3653                     .column = @intCast(loc.column),
   3654                     .source_line = try bundle.addString(loc.source_line),
   3655                 }),
   3656                 .notes_len = 0,
   3657             });
   3658         }
   3659 
   3660         // Before iterating `failed_files`, we need to sort it into a consistent order so that error
   3661         // messages appear consistently despite different ordering from the AstGen worker pool. File
   3662         // paths are a great key for this sort! We are using sorting the `ArrayHashMap` itself to
   3663         // make sure it reindexes; that's important because these entries need to be retained for
   3664         // future updates.
   3665         const FileSortCtx = struct {
   3666             zcu: *Zcu,
   3667             failed_files_keys: []const Zcu.File.Index,
   3668             pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
   3669                 const lhs_path = ctx.zcu.fileByIndex(ctx.failed_files_keys[lhs_index]).path;
   3670                 const rhs_path = ctx.zcu.fileByIndex(ctx.failed_files_keys[rhs_index]).path;
   3671                 if (lhs_path.root != rhs_path.root) return @intFromEnum(lhs_path.root) < @intFromEnum(rhs_path.root);
   3672                 return std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).compare(.lt);
   3673             }
   3674         };
   3675         zcu.failed_files.sort(@as(FileSortCtx, .{
   3676             .zcu = zcu,
   3677             .failed_files_keys = zcu.failed_files.keys(),
   3678         }));
   3679 
   3680         for (zcu.failed_files.keys(), zcu.failed_files.values()) |file_index, error_msg| {
   3681             if (!zcu.alive_files.contains(file_index)) continue;
   3682             const file = zcu.fileByIndex(file_index);
   3683             const is_retryable = switch (file.status) {
   3684                 .retryable_failure => true,
   3685                 .success, .astgen_failure => false,
   3686                 .never_loaded => unreachable,
   3687             };
   3688             if (error_msg) |msg| {
   3689                 assert(is_retryable);
   3690                 try addWholeFileError(zcu, &bundle, file_index, msg);
   3691             } else {
   3692                 assert(!is_retryable);
   3693                 // AstGen/ZoirGen succeeded with errors. Note that this may include AST errors.
   3694                 _ = try file.getTree(zcu); // Tree must be loaded.
   3695                 const path = try std.fmt.allocPrint(gpa, "{}", .{file.path.fmt(comp)});
   3696                 defer gpa.free(path);
   3697                 if (file.zir != null) {
   3698                     try bundle.addZirErrorMessages(file.zir.?, file.tree.?, file.source.?, path);
   3699                 } else if (file.zoir != null) {
   3700                     try bundle.addZoirErrorMessages(file.zoir.?, file.tree.?, file.source.?, path);
   3701                 } else {
   3702                     // Either Zir or Zoir must have been loaded.
   3703                     unreachable;
   3704                 }
   3705             }
   3706         }
   3707         if (zcu.skip_analysis_this_update) break :zcu_errors;
   3708         var sorted_failed_analysis: std.AutoArrayHashMapUnmanaged(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: {
   3709             const SortOrder = struct {
   3710                 zcu: *Zcu,
   3711                 errors: []const *Zcu.ErrorMsg,
   3712                 err: *?Error,
   3713 
   3714                 const Error = @typeInfo(
   3715                     @typeInfo(@TypeOf(Zcu.LazySrcLoc.lessThan)).@"fn".return_type.?,
   3716                 ).error_union.error_set;
   3717 
   3718                 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
   3719                     if (ctx.err.* != null) return lhs_index < rhs_index;
   3720                     return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu) catch |e| {
   3721                         ctx.err.* = e;
   3722                         return lhs_index < rhs_index;
   3723                     };
   3724                 }
   3725             };
   3726 
   3727             // We can't directly sort `zcu.failed_analysis.entries`, because that would leave the map
   3728             // in an invalid state, and we need it intact for future incremental updates. The amount
   3729             // of data here is only as large as the number of analysis errors, so just dupe it all.
   3730             var entries = try zcu.failed_analysis.entries.clone(gpa);
   3731             errdefer entries.deinit(gpa);
   3732 
   3733             var err: ?SortOrder.Error = null;
   3734             entries.sort(SortOrder{
   3735                 .zcu = zcu,
   3736                 .errors = entries.items(.value),
   3737                 .err = &err,
   3738             });
   3739             if (err) |e| return e;
   3740             break :s entries.slice();
   3741         };
   3742         defer sorted_failed_analysis.deinit(gpa);
   3743         var added_any_analysis_error = false;
   3744         for (sorted_failed_analysis.items(.key), sorted_failed_analysis.items(.value)) |anal_unit, error_msg| {
   3745             if (comp.incremental) {
   3746                 const refs = try zcu.resolveReferences();
   3747                 if (!refs.contains(anal_unit)) continue;
   3748             }
   3749 
   3750             std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{
   3751                 error_msg.msg,
   3752                 zcu.fmtAnalUnit(anal_unit),
   3753             });
   3754 
   3755             try addModuleErrorMsg(zcu, &bundle, error_msg.*, added_any_analysis_error);
   3756             added_any_analysis_error = true;
   3757 
   3758             if (zcu.cimport_errors.get(anal_unit)) |errors| {
   3759                 for (errors.getMessages()) |err_msg_index| {
   3760                     const err_msg = errors.getErrorMessage(err_msg_index);
   3761                     try bundle.addRootErrorMessage(.{
   3762                         .msg = try bundle.addString(errors.nullTerminatedString(err_msg.msg)),
   3763                         .src_loc = if (err_msg.src_loc != .none) blk: {
   3764                             const src_loc = errors.getSourceLocation(err_msg.src_loc);
   3765                             break :blk try bundle.addSourceLocation(.{
   3766                                 .src_path = try bundle.addString(errors.nullTerminatedString(src_loc.src_path)),
   3767                                 .span_start = src_loc.span_start,
   3768                                 .span_main = src_loc.span_main,
   3769                                 .span_end = src_loc.span_end,
   3770                                 .line = src_loc.line,
   3771                                 .column = src_loc.column,
   3772                                 .source_line = if (src_loc.source_line != 0) try bundle.addString(errors.nullTerminatedString(src_loc.source_line)) else 0,
   3773                             });
   3774                         } else .none,
   3775                     });
   3776                 }
   3777             }
   3778         }
   3779         for (zcu.failed_codegen.values()) |error_msg| {
   3780             try addModuleErrorMsg(zcu, &bundle, error_msg.*, false);
   3781         }
   3782         for (zcu.failed_types.values()) |error_msg| {
   3783             try addModuleErrorMsg(zcu, &bundle, error_msg.*, false);
   3784         }
   3785         for (zcu.failed_exports.values()) |value| {
   3786             try addModuleErrorMsg(zcu, &bundle, value.*, false);
   3787         }
   3788 
   3789         const actual_error_count = zcu.intern_pool.global_error_set.getNamesFromMainThread().len;
   3790         if (actual_error_count > zcu.error_limit) {
   3791             try bundle.addRootErrorMessage(.{
   3792                 .msg = try bundle.printString("ZCU used more errors than possible: used {d}, max {d}", .{
   3793                     actual_error_count, zcu.error_limit,
   3794                 }),
   3795                 .notes_len = 1,
   3796             });
   3797             const notes_start = try bundle.reserveNotes(1);
   3798             bundle.extra.items[notes_start] = @intFromEnum(try bundle.addErrorMessage(.{
   3799                 .msg = try bundle.printString("use '--error-limit {d}' to increase limit", .{
   3800                     actual_error_count,
   3801                 }),
   3802             }));
   3803         }
   3804     }
   3805 
   3806     if (bundle.root_list.items.len == 0) {
   3807         if (comp.link_diags.flags.no_entry_point_found) {
   3808             try bundle.addRootErrorMessage(.{
   3809                 .msg = try bundle.addString("no entry point found"),
   3810             });
   3811         }
   3812     }
   3813 
   3814     if (comp.link_diags.flags.missing_libc) {
   3815         try bundle.addRootErrorMessage(.{
   3816             .msg = try bundle.addString("libc not available"),
   3817             .notes_len = 2,
   3818         });
   3819         const notes_start = try bundle.reserveNotes(2);
   3820         bundle.extra.items[notes_start + 0] = @intFromEnum(try bundle.addErrorMessage(.{
   3821             .msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"),
   3822         }));
   3823         bundle.extra.items[notes_start + 1] = @intFromEnum(try bundle.addErrorMessage(.{
   3824             .msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"),
   3825         }));
   3826     }
   3827 
   3828     try comp.link_diags.addMessagesToBundle(&bundle, comp.bin_file);
   3829 
   3830     const compile_log_text: []const u8 = compile_log_text: {
   3831         const zcu = comp.zcu orelse break :compile_log_text "";
   3832         if (zcu.skip_analysis_this_update) break :compile_log_text "";
   3833         if (zcu.compile_logs.count() == 0) break :compile_log_text "";
   3834 
   3835         // If there are no other errors, we include a "found compile log statement" error.
   3836         // Otherwise, we just show the compile log output, with no error.
   3837         const include_compile_log_sources = bundle.root_list.items.len == 0;
   3838 
   3839         const refs = try zcu.resolveReferences();
   3840 
   3841         var messages: std.ArrayListUnmanaged(Zcu.ErrorMsg) = .empty;
   3842         defer messages.deinit(gpa);
   3843         for (zcu.compile_logs.keys(), zcu.compile_logs.values()) |logging_unit, compile_log| {
   3844             if (!refs.contains(logging_unit)) continue;
   3845             try messages.append(gpa, .{
   3846                 .src_loc = compile_log.src(),
   3847                 .msg = undefined, // populated later
   3848                 .notes = &.{},
   3849                 // We actually clear this later for most of these, but we populate
   3850                 // this field for now to avoid having to allocate more data to track
   3851                 // which compile log text this corresponds to.
   3852                 .reference_trace_root = logging_unit.toOptional(),
   3853             });
   3854         }
   3855 
   3856         if (messages.items.len == 0) break :compile_log_text "";
   3857 
   3858         // Okay, there *are* referenced compile logs. Sort them into a consistent order.
   3859 
   3860         const SortContext = struct {
   3861             err: *?Error,
   3862             zcu: *Zcu,
   3863             const Error = @typeInfo(
   3864                 @typeInfo(@TypeOf(Zcu.LazySrcLoc.lessThan)).@"fn".return_type.?,
   3865             ).error_union.error_set;
   3866             fn lessThan(ctx: @This(), lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool {
   3867                 if (ctx.err.* != null) return false;
   3868                 return lhs.src_loc.lessThan(rhs.src_loc, ctx.zcu) catch |e| {
   3869                     ctx.err.* = e;
   3870                     return false;
   3871                 };
   3872             }
   3873         };
   3874         var sort_err: ?SortContext.Error = null;
   3875         std.mem.sort(Zcu.ErrorMsg, messages.items, @as(SortContext, .{ .err = &sort_err, .zcu = zcu }), SortContext.lessThan);
   3876         if (sort_err) |e| return e;
   3877 
   3878         var log_text: std.ArrayListUnmanaged(u8) = .empty;
   3879         defer log_text.deinit(gpa);
   3880 
   3881         // Index 0 will be the root message; the rest will be notes.
   3882         // Only the actual message, i.e. index 0, will retain its reference trace.
   3883         try appendCompileLogLines(&log_text, zcu, messages.items[0].reference_trace_root.unwrap().?);
   3884         messages.items[0].notes = messages.items[1..];
   3885         messages.items[0].msg = "found compile log statement";
   3886         for (messages.items[1..]) |*note| {
   3887             try appendCompileLogLines(&log_text, zcu, note.reference_trace_root.unwrap().?);
   3888             note.reference_trace_root = .none; // notes don't have reference traces
   3889             note.msg = "also here";
   3890         }
   3891 
   3892         // We don't actually include the error here if `!include_compile_log_sources`.
   3893         // The sorting above was still necessary, though, to get `log_text` in the right order.
   3894         if (include_compile_log_sources) {
   3895             try addModuleErrorMsg(zcu, &bundle, messages.items[0], false);
   3896         }
   3897 
   3898         break :compile_log_text try log_text.toOwnedSlice(gpa);
   3899     };
   3900 
   3901     // TODO: eventually, this should be behind `std.debug.runtime_safety`. But right now, this is a
   3902     // very common way for incremental compilation bugs to manifest, so let's always check it.
   3903     if (comp.zcu) |zcu| if (comp.incremental and bundle.root_list.items.len == 0) {
   3904         for (zcu.transitive_failed_analysis.keys()) |failed_unit| {
   3905             const refs = try zcu.resolveReferences();
   3906             var ref = refs.get(failed_unit) orelse continue;
   3907             // This AU is referenced and has a transitive compile error, meaning it referenced something with a compile error.
   3908             // However, we haven't reported any such error.
   3909             // This is a compiler bug.
   3910             const stderr = std.io.getStdErr().writer();
   3911             try stderr.writeAll("referenced transitive analysis errors, but none actually emitted\n");
   3912             try stderr.print("{} [transitive failure]\n", .{zcu.fmtAnalUnit(failed_unit)});
   3913             while (ref) |r| {
   3914                 try stderr.print("referenced by: {}{s}\n", .{
   3915                     zcu.fmtAnalUnit(r.referencer),
   3916                     if (zcu.transitive_failed_analysis.contains(r.referencer)) " [transitive failure]" else "",
   3917                 });
   3918                 ref = refs.get(r.referencer).?;
   3919             }
   3920 
   3921             @panic("referenced transitive analysis errors, but none actually emitted");
   3922         }
   3923     };
   3924 
   3925     return bundle.toOwnedBundle(compile_log_text);
   3926 }
   3927 
   3928 /// Writes all compile log lines belonging to `logging_unit` into `log_text` using `zcu.gpa`.
   3929 fn appendCompileLogLines(log_text: *std.ArrayListUnmanaged(u8), zcu: *Zcu, logging_unit: InternPool.AnalUnit) Allocator.Error!void {
   3930     const gpa = zcu.gpa;
   3931     const ip = &zcu.intern_pool;
   3932     var opt_line_idx = zcu.compile_logs.get(logging_unit).?.first_line.toOptional();
   3933     while (opt_line_idx.unwrap()) |line_idx| {
   3934         const line = line_idx.get(zcu).*;
   3935         opt_line_idx = line.next;
   3936         const line_slice = line.data.toSlice(ip);
   3937         try log_text.ensureUnusedCapacity(gpa, line_slice.len + 1);
   3938         log_text.appendSliceAssumeCapacity(line_slice);
   3939         log_text.appendAssumeCapacity('\n');
   3940     }
   3941 }
   3942 
   3943 fn anyErrors(comp: *Compilation) bool {
   3944     return (totalErrorCount(comp) catch return true) != 0;
   3945 }
   3946 
   3947 fn totalErrorCount(comp: *Compilation) !u32 {
   3948     var errors = try comp.getAllErrorsAlloc();
   3949     defer errors.deinit(comp.gpa);
   3950     return errors.errorMessageCount();
   3951 }
   3952 
   3953 pub const ErrorNoteHashContext = struct {
   3954     eb: *const ErrorBundle.Wip,
   3955 
   3956     pub fn hash(ctx: ErrorNoteHashContext, key: ErrorBundle.ErrorMessage) u32 {
   3957         var hasher = std.hash.Wyhash.init(0);
   3958         const eb = ctx.eb.tmpBundle();
   3959 
   3960         hasher.update(eb.nullTerminatedString(key.msg));
   3961         if (key.src_loc != .none) {
   3962             const src = eb.getSourceLocation(key.src_loc);
   3963             hasher.update(eb.nullTerminatedString(src.src_path));
   3964             std.hash.autoHash(&hasher, src.line);
   3965             std.hash.autoHash(&hasher, src.column);
   3966             std.hash.autoHash(&hasher, src.span_main);
   3967         }
   3968 
   3969         return @as(u32, @truncate(hasher.final()));
   3970     }
   3971 
   3972     pub fn eql(
   3973         ctx: ErrorNoteHashContext,
   3974         a: ErrorBundle.ErrorMessage,
   3975         b: ErrorBundle.ErrorMessage,
   3976         b_index: usize,
   3977     ) bool {
   3978         _ = b_index;
   3979         const eb = ctx.eb.tmpBundle();
   3980         const msg_a = eb.nullTerminatedString(a.msg);
   3981         const msg_b = eb.nullTerminatedString(b.msg);
   3982         if (!mem.eql(u8, msg_a, msg_b)) return false;
   3983 
   3984         if (a.src_loc == .none and b.src_loc == .none) return true;
   3985         if (a.src_loc == .none or b.src_loc == .none) return false;
   3986         const src_a = eb.getSourceLocation(a.src_loc);
   3987         const src_b = eb.getSourceLocation(b.src_loc);
   3988 
   3989         const src_path_a = eb.nullTerminatedString(src_a.src_path);
   3990         const src_path_b = eb.nullTerminatedString(src_b.src_path);
   3991 
   3992         return mem.eql(u8, src_path_a, src_path_b) and
   3993             src_a.line == src_b.line and
   3994             src_a.column == src_b.column and
   3995             src_a.span_main == src_b.span_main;
   3996     }
   3997 };
   3998 
   3999 const default_reference_trace_len = 2;
   4000 pub fn addModuleErrorMsg(
   4001     zcu: *Zcu,
   4002     eb: *ErrorBundle.Wip,
   4003     module_err_msg: Zcu.ErrorMsg,
   4004     /// If `-freference-trace` is not specified, we only want to show the one reference trace.
   4005     /// So, this is whether we have already emitted an error with a reference trace.
   4006     already_added_error: bool,
   4007 ) !void {
   4008     const gpa = eb.gpa;
   4009     const ip = &zcu.intern_pool;
   4010     const err_src_loc = module_err_msg.src_loc.upgrade(zcu);
   4011     const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| {
   4012         try eb.addRootErrorMessage(.{
   4013             .msg = try eb.printString("unable to load '{}': {s}", .{
   4014                 err_src_loc.file_scope.path.fmt(zcu.comp), @errorName(err),
   4015             }),
   4016         });
   4017         return;
   4018     };
   4019     const err_span = try err_src_loc.span(zcu);
   4020     const err_loc = std.zig.findLineColumn(err_source.bytes, err_span.main);
   4021 
   4022     var ref_traces: std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace) = .empty;
   4023     defer ref_traces.deinit(gpa);
   4024 
   4025     rt: {
   4026         const rt_root = module_err_msg.reference_trace_root.unwrap() orelse break :rt;
   4027         const max_references = zcu.comp.reference_trace orelse refs: {
   4028             if (already_added_error) break :rt;
   4029             break :refs default_reference_trace_len;
   4030         };
   4031 
   4032         const all_references = try zcu.resolveReferences();
   4033 
   4034         var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty;
   4035         defer seen.deinit(gpa);
   4036 
   4037         var referenced_by = rt_root;
   4038         while (all_references.get(referenced_by)) |maybe_ref| {
   4039             const ref = maybe_ref orelse break;
   4040             const gop = try seen.getOrPut(gpa, ref.referencer);
   4041             if (gop.found_existing) break;
   4042             if (ref_traces.items.len < max_references) {
   4043                 var last_call_src = ref.src;
   4044                 var opt_inline_frame = ref.inline_frame;
   4045                 while (opt_inline_frame.unwrap()) |inline_frame| {
   4046                     const f = inline_frame.ptr(zcu).*;
   4047                     const func_nav = ip.indexToKey(f.callee).func.owner_nav;
   4048                     const func_name = ip.getNav(func_nav).name.toSlice(ip);
   4049                     try addReferenceTraceFrame(zcu, eb, &ref_traces, func_name, last_call_src, true);
   4050                     last_call_src = f.call_src;
   4051                     opt_inline_frame = f.parent;
   4052                 }
   4053                 const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) {
   4054                     .@"comptime" => "comptime",
   4055                     .nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
   4056                     .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
   4057                     .func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
   4058                     .memoized_state => null,
   4059                 };
   4060                 if (root_name) |n| {
   4061                     try addReferenceTraceFrame(zcu, eb, &ref_traces, n, last_call_src, false);
   4062                 }
   4063             }
   4064             referenced_by = ref.referencer;
   4065         }
   4066 
   4067         if (seen.count() > ref_traces.items.len) {
   4068             try ref_traces.append(gpa, .{
   4069                 .decl_name = @intCast(seen.count() - ref_traces.items.len),
   4070                 .src_loc = .none,
   4071             });
   4072         }
   4073     }
   4074 
   4075     const src_loc = try eb.addSourceLocation(.{
   4076         .src_path = try eb.printString("{}", .{err_src_loc.file_scope.path.fmt(zcu.comp)}),
   4077         .span_start = err_span.start,
   4078         .span_main = err_span.main,
   4079         .span_end = err_span.end,
   4080         .line = @intCast(err_loc.line),
   4081         .column = @intCast(err_loc.column),
   4082         .source_line = try eb.addString(err_loc.source_line),
   4083         .reference_trace_len = @intCast(ref_traces.items.len),
   4084     });
   4085 
   4086     for (ref_traces.items) |rt| {
   4087         try eb.addReferenceTrace(rt);
   4088     }
   4089 
   4090     // De-duplicate error notes. The main use case in mind for this is
   4091     // too many "note: called from here" notes when eval branch quota is reached.
   4092     var notes: std.ArrayHashMapUnmanaged(ErrorBundle.ErrorMessage, void, ErrorNoteHashContext, true) = .empty;
   4093     defer notes.deinit(gpa);
   4094 
   4095     var last_note_loc: ?std.zig.Loc = null;
   4096     for (module_err_msg.notes) |module_note| {
   4097         const note_src_loc = module_note.src_loc.upgrade(zcu);
   4098         const source = try note_src_loc.file_scope.getSource(zcu);
   4099         const span = try note_src_loc.span(zcu);
   4100         const loc = std.zig.findLineColumn(source.bytes, span.main);
   4101 
   4102         const omit_source_line = loc.eql(err_loc) or (last_note_loc != null and loc.eql(last_note_loc.?));
   4103         last_note_loc = loc;
   4104 
   4105         const gop = try notes.getOrPutContext(gpa, .{
   4106             .msg = try eb.addString(module_note.msg),
   4107             .src_loc = try eb.addSourceLocation(.{
   4108                 .src_path = try eb.printString("{}", .{note_src_loc.file_scope.path.fmt(zcu.comp)}),
   4109                 .span_start = span.start,
   4110                 .span_main = span.main,
   4111                 .span_end = span.end,
   4112                 .line = @intCast(loc.line),
   4113                 .column = @intCast(loc.column),
   4114                 .source_line = if (omit_source_line) 0 else try eb.addString(loc.source_line),
   4115             }),
   4116         }, .{ .eb = eb });
   4117         if (gop.found_existing) {
   4118             gop.key_ptr.count += 1;
   4119         }
   4120     }
   4121 
   4122     const notes_len: u32 = @intCast(notes.entries.len);
   4123 
   4124     try eb.addRootErrorMessage(.{
   4125         .msg = try eb.addString(module_err_msg.msg),
   4126         .src_loc = src_loc,
   4127         .notes_len = notes_len,
   4128     });
   4129 
   4130     const notes_start = try eb.reserveNotes(notes_len);
   4131 
   4132     for (notes_start.., notes.keys()) |i, note| {
   4133         eb.extra.items[i] = @intFromEnum(try eb.addErrorMessage(note));
   4134     }
   4135 }
   4136 
   4137 fn addReferenceTraceFrame(
   4138     zcu: *Zcu,
   4139     eb: *ErrorBundle.Wip,
   4140     ref_traces: *std.ArrayListUnmanaged(ErrorBundle.ReferenceTrace),
   4141     name: []const u8,
   4142     lazy_src: Zcu.LazySrcLoc,
   4143     inlined: bool,
   4144 ) !void {
   4145     const gpa = zcu.gpa;
   4146     const src = lazy_src.upgrade(zcu);
   4147     const source = try src.file_scope.getSource(zcu);
   4148     const span = try src.span(zcu);
   4149     const loc = std.zig.findLineColumn(source.bytes, span.main);
   4150     try ref_traces.append(gpa, .{
   4151         .decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }),
   4152         .src_loc = try eb.addSourceLocation(.{
   4153             .src_path = try eb.printString("{}", .{src.file_scope.path.fmt(zcu.comp)}),
   4154             .span_start = span.start,
   4155             .span_main = span.main,
   4156             .span_end = span.end,
   4157             .line = @intCast(loc.line),
   4158             .column = @intCast(loc.column),
   4159             .source_line = 0,
   4160         }),
   4161     });
   4162 }
   4163 
   4164 pub fn addWholeFileError(
   4165     zcu: *Zcu,
   4166     eb: *ErrorBundle.Wip,
   4167     file_index: Zcu.File.Index,
   4168     msg: []const u8,
   4169 ) !void {
   4170     // note: "file imported here" on the import reference token
   4171     const imported_note: ?ErrorBundle.MessageIndex = switch (zcu.alive_files.get(file_index).?) {
   4172         .analysis_root => null,
   4173         .import => |import| try eb.addErrorMessage(.{
   4174             .msg = try eb.addString("file imported here"),
   4175             .src_loc = try zcu.fileByIndex(import.importer).errorBundleTokenSrc(import.tok, zcu, eb),
   4176         }),
   4177     };
   4178 
   4179     try eb.addRootErrorMessage(.{
   4180         .msg = try eb.addString(msg),
   4181         .src_loc = try zcu.fileByIndex(file_index).errorBundleWholeFileSrc(zcu, eb),
   4182         .notes_len = if (imported_note != null) 1 else 0,
   4183     });
   4184     if (imported_note) |n| {
   4185         const note_idx = try eb.reserveNotes(1);
   4186         eb.extra.items[note_idx] = @intFromEnum(n);
   4187     }
   4188 }
   4189 
   4190 fn performAllTheWork(
   4191     comp: *Compilation,
   4192     main_progress_node: std.Progress.Node,
   4193 ) JobError!void {
   4194     // Regardless of errors, `comp.zcu` needs to update its generation number.
   4195     defer if (comp.zcu) |zcu| {
   4196         zcu.generation += 1;
   4197     };
   4198 
   4199     // Here we queue up all the AstGen tasks first, followed by C object compilation.
   4200     // We wait until the AstGen tasks are all completed before proceeding to the
   4201     // (at least for now) single-threaded main work queue. However, C object compilation
   4202     // only needs to be finished by the end of this function.
   4203 
   4204     var work_queue_wait_group: WaitGroup = .{};
   4205     defer work_queue_wait_group.wait();
   4206 
   4207     comp.link_task_wait_group.reset();
   4208     defer comp.link_task_wait_group.wait();
   4209 
   4210     comp.link_prog_node.increaseEstimatedTotalItems(
   4211         comp.link_task_queue.queued_prelink.items.len + // already queued prelink tasks
   4212             comp.link_task_queue.pending_prelink_tasks, // prelink tasks which will be queued
   4213     );
   4214     comp.link_task_queue.start(comp);
   4215 
   4216     if (comp.emit_docs != null) {
   4217         dev.check(.docs_emit);
   4218         comp.thread_pool.spawnWg(&work_queue_wait_group, workerDocsCopy, .{comp});
   4219         work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node });
   4220     }
   4221 
   4222     // In case it failed last time, try again. `clearMiscFailures` was already
   4223     // called at the start of `update`.
   4224     if (comp.queued_jobs.compiler_rt_lib and comp.compiler_rt_lib == null) {
   4225         // LLVM disables LTO for its compiler-rt and we've had various issues with LTO of our
   4226         // compiler-rt due to LLD bugs as well, e.g.:
   4227         //
   4228         // https://github.com/llvm/llvm-project/issues/43698#issuecomment-2542660611
   4229         comp.link_task_wait_group.spawnManager(buildRt, .{
   4230             comp,
   4231             "compiler_rt.zig",
   4232             "compiler_rt",
   4233             .Lib,
   4234             .compiler_rt,
   4235             main_progress_node,
   4236             RtOptions{
   4237                 .checks_valgrind = true,
   4238                 .allow_lto = false,
   4239             },
   4240             &comp.compiler_rt_lib,
   4241         });
   4242     }
   4243 
   4244     if (comp.queued_jobs.compiler_rt_obj and comp.compiler_rt_obj == null) {
   4245         comp.link_task_wait_group.spawnManager(buildRt, .{
   4246             comp,
   4247             "compiler_rt.zig",
   4248             "compiler_rt",
   4249             .Obj,
   4250             .compiler_rt,
   4251             main_progress_node,
   4252             RtOptions{
   4253                 .checks_valgrind = true,
   4254                 .allow_lto = false,
   4255             },
   4256             &comp.compiler_rt_obj,
   4257         });
   4258     }
   4259 
   4260     if (comp.queued_jobs.fuzzer_lib and comp.fuzzer_lib == null) {
   4261         comp.link_task_wait_group.spawnManager(buildRt, .{
   4262             comp,
   4263             "fuzzer.zig",
   4264             "fuzzer",
   4265             .Lib,
   4266             .libfuzzer,
   4267             main_progress_node,
   4268             RtOptions{},
   4269             &comp.fuzzer_lib,
   4270         });
   4271     }
   4272 
   4273     if (comp.queued_jobs.ubsan_rt_lib and comp.ubsan_rt_lib == null) {
   4274         comp.link_task_wait_group.spawnManager(buildRt, .{
   4275             comp,
   4276             "ubsan_rt.zig",
   4277             "ubsan_rt",
   4278             .Lib,
   4279             .libubsan,
   4280             main_progress_node,
   4281             RtOptions{
   4282                 .allow_lto = false,
   4283             },
   4284             &comp.ubsan_rt_lib,
   4285         });
   4286     }
   4287 
   4288     if (comp.queued_jobs.ubsan_rt_obj and comp.ubsan_rt_obj == null) {
   4289         comp.link_task_wait_group.spawnManager(buildRt, .{
   4290             comp,
   4291             "ubsan_rt.zig",
   4292             "ubsan_rt",
   4293             .Obj,
   4294             .libubsan,
   4295             main_progress_node,
   4296             RtOptions{
   4297                 .allow_lto = false,
   4298             },
   4299             &comp.ubsan_rt_obj,
   4300         });
   4301     }
   4302 
   4303     if (comp.queued_jobs.glibc_shared_objects) {
   4304         comp.link_task_wait_group.spawnManager(buildGlibcSharedObjects, .{ comp, main_progress_node });
   4305     }
   4306 
   4307     if (comp.queued_jobs.freebsd_shared_objects) {
   4308         comp.link_task_wait_group.spawnManager(buildFreeBSDSharedObjects, .{ comp, main_progress_node });
   4309     }
   4310 
   4311     if (comp.queued_jobs.netbsd_shared_objects) {
   4312         comp.link_task_wait_group.spawnManager(buildNetBSDSharedObjects, .{ comp, main_progress_node });
   4313     }
   4314 
   4315     if (comp.queued_jobs.libunwind) {
   4316         comp.link_task_wait_group.spawnManager(buildLibUnwind, .{ comp, main_progress_node });
   4317     }
   4318 
   4319     if (comp.queued_jobs.libcxx) {
   4320         comp.link_task_wait_group.spawnManager(buildLibCxx, .{ comp, main_progress_node });
   4321     }
   4322 
   4323     if (comp.queued_jobs.libcxxabi) {
   4324         comp.link_task_wait_group.spawnManager(buildLibCxxAbi, .{ comp, main_progress_node });
   4325     }
   4326 
   4327     if (comp.queued_jobs.libtsan) {
   4328         comp.link_task_wait_group.spawnManager(buildLibTsan, .{ comp, main_progress_node });
   4329     }
   4330 
   4331     if (comp.queued_jobs.zigc_lib and comp.zigc_static_lib == null) {
   4332         comp.link_task_wait_group.spawnManager(buildLibZigC, .{ comp, main_progress_node });
   4333     }
   4334 
   4335     for (0..@typeInfo(musl.CrtFile).@"enum".fields.len) |i| {
   4336         if (comp.queued_jobs.musl_crt_file[i]) {
   4337             const tag: musl.CrtFile = @enumFromInt(i);
   4338             comp.link_task_wait_group.spawnManager(buildMuslCrtFile, .{ comp, tag, main_progress_node });
   4339         }
   4340     }
   4341 
   4342     for (0..@typeInfo(glibc.CrtFile).@"enum".fields.len) |i| {
   4343         if (comp.queued_jobs.glibc_crt_file[i]) {
   4344             const tag: glibc.CrtFile = @enumFromInt(i);
   4345             comp.link_task_wait_group.spawnManager(buildGlibcCrtFile, .{ comp, tag, main_progress_node });
   4346         }
   4347     }
   4348 
   4349     for (0..@typeInfo(freebsd.CrtFile).@"enum".fields.len) |i| {
   4350         if (comp.queued_jobs.freebsd_crt_file[i]) {
   4351             const tag: freebsd.CrtFile = @enumFromInt(i);
   4352             comp.link_task_wait_group.spawnManager(buildFreeBSDCrtFile, .{ comp, tag, main_progress_node });
   4353         }
   4354     }
   4355 
   4356     for (0..@typeInfo(netbsd.CrtFile).@"enum".fields.len) |i| {
   4357         if (comp.queued_jobs.netbsd_crt_file[i]) {
   4358             const tag: netbsd.CrtFile = @enumFromInt(i);
   4359             comp.link_task_wait_group.spawnManager(buildNetBSDCrtFile, .{ comp, tag, main_progress_node });
   4360         }
   4361     }
   4362 
   4363     for (0..@typeInfo(wasi_libc.CrtFile).@"enum".fields.len) |i| {
   4364         if (comp.queued_jobs.wasi_libc_crt_file[i]) {
   4365             const tag: wasi_libc.CrtFile = @enumFromInt(i);
   4366             comp.link_task_wait_group.spawnManager(buildWasiLibcCrtFile, .{ comp, tag, main_progress_node });
   4367         }
   4368     }
   4369 
   4370     for (0..@typeInfo(mingw.CrtFile).@"enum".fields.len) |i| {
   4371         if (comp.queued_jobs.mingw_crt_file[i]) {
   4372             const tag: mingw.CrtFile = @enumFromInt(i);
   4373             comp.link_task_wait_group.spawnManager(buildMingwCrtFile, .{ comp, tag, main_progress_node });
   4374         }
   4375     }
   4376 
   4377     {
   4378         const astgen_frame = tracy.namedFrame("astgen");
   4379         defer astgen_frame.end();
   4380 
   4381         const zir_prog_node = main_progress_node.start("AST Lowering", 0);
   4382         defer zir_prog_node.end();
   4383 
   4384         var astgen_wait_group: WaitGroup = .{};
   4385         defer astgen_wait_group.wait();
   4386 
   4387         if (comp.zcu) |zcu| {
   4388             const gpa = zcu.gpa;
   4389 
   4390             // We cannot reference `zcu.import_table` after we spawn any `workerUpdateFile` jobs,
   4391             // because on single-threaded targets the worker will be run eagerly, meaning the
   4392             // `import_table` could be mutated, and not even holding `comp.mutex` will save us. So,
   4393             // build up a list of the files to update *before* we spawn any jobs.
   4394             var astgen_work_items: std.MultiArrayList(struct {
   4395                 file_index: Zcu.File.Index,
   4396                 file: *Zcu.File,
   4397             }) = .empty;
   4398             defer astgen_work_items.deinit(gpa);
   4399             // Not every item in `import_table` will need updating, because some are builtin.zig
   4400             // files. However, most will, so let's just reserve sufficient capacity upfront.
   4401             try astgen_work_items.ensureTotalCapacity(gpa, zcu.import_table.count());
   4402             for (zcu.import_table.keys()) |file_index| {
   4403                 const file = zcu.fileByIndex(file_index);
   4404                 if (file.is_builtin) {
   4405                     // This is a `builtin.zig`, so updating is redundant. However, we want to make
   4406                     // sure the file contents are still correct on disk, since it can improve the
   4407                     // debugging experience better. That job only needs `file`, so we can kick it
   4408                     // off right now.
   4409                     comp.thread_pool.spawnWg(&astgen_wait_group, workerUpdateBuiltinFile, .{ comp, file });
   4410                     continue;
   4411                 }
   4412                 astgen_work_items.appendAssumeCapacity(.{
   4413                     .file_index = file_index,
   4414                     .file = file,
   4415                 });
   4416             }
   4417 
   4418             // Now that we're not going to touch `zcu.import_table` again, we can spawn `workerUpdateFile` jobs.
   4419             for (astgen_work_items.items(.file_index), astgen_work_items.items(.file)) |file_index, file| {
   4420                 comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateFile, .{
   4421                     comp, file, file_index, zir_prog_node, &astgen_wait_group,
   4422                 });
   4423             }
   4424 
   4425             // On the other hand, it's fine to directly iterate `zcu.embed_table.keys()` here
   4426             // because `workerUpdateEmbedFile` can't invalidate it. The different here is that one
   4427             // `@embedFile` can't trigger analysis of a new `@embedFile`!
   4428             for (0.., zcu.embed_table.keys()) |ef_index_usize, ef| {
   4429                 const ef_index: Zcu.EmbedFile.Index = @enumFromInt(ef_index_usize);
   4430                 comp.thread_pool.spawnWgId(&astgen_wait_group, workerUpdateEmbedFile, .{
   4431                     comp, ef_index, ef,
   4432                 });
   4433             }
   4434         }
   4435 
   4436         while (comp.c_object_work_queue.readItem()) |c_object| {
   4437             comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateCObject, .{
   4438                 comp, c_object, main_progress_node,
   4439             });
   4440         }
   4441 
   4442         while (comp.win32_resource_work_queue.readItem()) |win32_resource| {
   4443             comp.thread_pool.spawnWg(&comp.link_task_wait_group, workerUpdateWin32Resource, .{
   4444                 comp, win32_resource, main_progress_node,
   4445             });
   4446         }
   4447     }
   4448 
   4449     if (comp.zcu) |zcu| {
   4450         const pt: Zcu.PerThread = .activate(zcu, .main);
   4451         defer pt.deactivate();
   4452 
   4453         const gpa = zcu.gpa;
   4454 
   4455         // On an incremental update, a source file might become "dead", in that all imports of
   4456         // the file were removed. This could even change what module the file belongs to! As such,
   4457         // we do a traversal over the files, to figure out which ones are alive and the modules
   4458         // they belong to.
   4459         const any_fatal_files = try pt.computeAliveFiles();
   4460 
   4461         // If the cache mode is `whole`, add every alive source file to the manifest.
   4462         switch (comp.cache_use) {
   4463             .whole => |whole| if (whole.cache_manifest) |man| {
   4464                 for (zcu.alive_files.keys()) |file_index| {
   4465                     const file = zcu.fileByIndex(file_index);
   4466 
   4467                     switch (file.status) {
   4468                         .never_loaded => unreachable, // AstGen tried to load it
   4469                         .retryable_failure => continue, // the file cannot be read; this is a guaranteed error
   4470                         .astgen_failure, .success => {}, // the file was read successfully
   4471                     }
   4472 
   4473                     const path = try file.path.toAbsolute(comp.dirs, gpa);
   4474                     defer gpa.free(path);
   4475 
   4476                     const result = res: {
   4477                         whole.cache_manifest_mutex.lock();
   4478                         defer whole.cache_manifest_mutex.unlock();
   4479                         if (file.source) |source| {
   4480                             break :res man.addFilePostContents(path, source, file.stat);
   4481                         } else {
   4482                             break :res man.addFilePost(path);
   4483                         }
   4484                     };
   4485                     result catch |err| switch (err) {
   4486                         error.OutOfMemory => |e| return e,
   4487                         else => {
   4488                             try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)});
   4489                             continue;
   4490                         },
   4491                     };
   4492                 }
   4493             },
   4494             .none, .incremental => {},
   4495         }
   4496 
   4497         if (any_fatal_files or
   4498             zcu.multi_module_err != null or
   4499             zcu.failed_imports.items.len > 0 or
   4500             comp.alloc_failure_occurred)
   4501         {
   4502             // We give up right now! No updating of ZIR refs, no nothing. The idea is that this prevents
   4503             // us from invalidating lots of incremental dependencies due to files with e.g. parse errors.
   4504             // However, this means our analysis data is invalid, so we want to omit all analysis errors.
   4505             zcu.skip_analysis_this_update = true;
   4506             return;
   4507         }
   4508 
   4509         if (comp.incremental) {
   4510             const update_zir_refs_node = main_progress_node.start("Update ZIR References", 0);
   4511             defer update_zir_refs_node.end();
   4512             try pt.updateZirRefs();
   4513         }
   4514         try zcu.flushRetryableFailures();
   4515 
   4516         // It's analysis time! Queue up our initial analysis.
   4517         for (zcu.analysis_roots.slice()) |mod| {
   4518             try comp.queueJob(.{ .analyze_mod = mod });
   4519         }
   4520 
   4521         zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
   4522         if (comp.bin_file != null) {
   4523             zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
   4524         }
   4525         // We increment `pending_codegen_jobs` so that it doesn't reach 0 until after analysis finishes.
   4526         // That prevents the "Code Generation" node from constantly disappearing and reappearing when
   4527         // we're probably going to analyze more functions at some point.
   4528         assert(zcu.pending_codegen_jobs.swap(1, .monotonic) == 0); // don't let this become 0 until analysis finishes
   4529     }
   4530     // When analysis ends, delete the progress nodes for "Semantic Analysis" and possibly "Code Generation".
   4531     defer if (comp.zcu) |zcu| {
   4532         zcu.sema_prog_node.end();
   4533         zcu.sema_prog_node = .none;
   4534         if (zcu.pending_codegen_jobs.rmw(.Sub, 1, .monotonic) == 1) {
   4535             // Decremented to 0, so all done.
   4536             zcu.codegen_prog_node.end();
   4537             zcu.codegen_prog_node = .none;
   4538         }
   4539     };
   4540 
   4541     if (!comp.separateCodegenThreadOk()) {
   4542         // Waits until all input files have been parsed.
   4543         comp.link_task_wait_group.wait();
   4544         comp.link_task_wait_group.reset();
   4545         std.log.scoped(.link).debug("finished waiting for link_task_wait_group", .{});
   4546         if (comp.link_task_queue.pending_prelink_tasks > 0) {
   4547             // Indicates an error occurred preventing prelink phase from completing.
   4548             return;
   4549         }
   4550     }
   4551 
   4552     work: while (true) {
   4553         for (&comp.work_queues) |*work_queue| if (work_queue.readItem()) |job| {
   4554             try processOneJob(@intFromEnum(Zcu.PerThread.Id.main), comp, job);
   4555             continue :work;
   4556         };
   4557         if (comp.zcu) |zcu| {
   4558             // If there's no work queued, check if there's anything outdated
   4559             // which we need to work on, and queue it if so.
   4560             if (try zcu.findOutdatedToAnalyze()) |outdated| {
   4561                 try comp.queueJob(switch (outdated.unwrap()) {
   4562                     .func => |f| .{ .analyze_func = f },
   4563                     .memoized_state,
   4564                     .@"comptime",
   4565                     .nav_ty,
   4566                     .nav_val,
   4567                     .type,
   4568                     => .{ .analyze_comptime_unit = outdated },
   4569                 });
   4570                 continue;
   4571             }
   4572             zcu.sema_prog_node.end();
   4573             zcu.sema_prog_node = .none;
   4574         }
   4575         break;
   4576     }
   4577 }
   4578 
   4579 const JobError = Allocator.Error;
   4580 
   4581 pub fn queueJob(comp: *Compilation, job: Job) !void {
   4582     try comp.work_queues[Job.stage(job)].writeItem(job);
   4583 }
   4584 
   4585 pub fn queueJobs(comp: *Compilation, jobs: []const Job) !void {
   4586     for (jobs) |job| try comp.queueJob(job);
   4587 }
   4588 
   4589 fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
   4590     switch (job) {
   4591         .codegen_func => |func| {
   4592             const zcu = comp.zcu.?;
   4593             const gpa = zcu.gpa;
   4594             var air = func.air;
   4595             errdefer air.deinit(gpa);
   4596             if (!air.typesFullyResolved(zcu)) {
   4597                 // Type resolution failed in a way which affects this function. This is a transitive
   4598                 // failure, but it doesn't need recording, because this function semantically depends
   4599                 // on the failed type, so when it is changed the function is updated.
   4600                 air.deinit(gpa);
   4601                 return;
   4602             }
   4603             const shared_mir = try gpa.create(link.ZcuTask.LinkFunc.SharedMir);
   4604             shared_mir.* = .{
   4605                 .status = .init(.pending),
   4606                 .value = undefined,
   4607             };
   4608             assert(zcu.pending_codegen_jobs.rmw(.Add, 1, .monotonic) > 0); // the "Code Generation" node hasn't been ended
   4609             zcu.codegen_prog_node.increaseEstimatedTotalItems(1);
   4610             // This value is used as a heuristic to avoid queueing too much AIR/MIR at once (hence
   4611             // using a lot of memory). If this would cause too many AIR bytes to be in-flight, we
   4612             // will block on the `dispatchZcuLinkTask` call below.
   4613             const air_bytes: u32 = @intCast(air.instructions.len * 5 + air.extra.items.len * 4);
   4614             if (comp.separateCodegenThreadOk()) {
   4615                 // `workerZcuCodegen` takes ownership of `air`.
   4616                 comp.thread_pool.spawnWgId(&comp.link_task_wait_group, workerZcuCodegen, .{ comp, func.func, air, shared_mir });
   4617                 comp.dispatchZcuLinkTask(tid, .{ .link_func = .{
   4618                     .func = func.func,
   4619                     .mir = shared_mir,
   4620                     .air_bytes = air_bytes,
   4621                 } });
   4622             } else {
   4623                 {
   4624                     const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
   4625                     defer pt.deactivate();
   4626                     pt.runCodegen(func.func, &air, shared_mir);
   4627                 }
   4628                 assert(shared_mir.status.load(.monotonic) != .pending);
   4629                 comp.dispatchZcuLinkTask(tid, .{ .link_func = .{
   4630                     .func = func.func,
   4631                     .mir = shared_mir,
   4632                     .air_bytes = air_bytes,
   4633                 } });
   4634                 air.deinit(gpa);
   4635             }
   4636         },
   4637         .link_nav => |nav_index| {
   4638             const zcu = comp.zcu.?;
   4639             const nav = zcu.intern_pool.getNav(nav_index);
   4640             if (nav.analysis != null) {
   4641                 const unit: InternPool.AnalUnit = .wrap(.{ .nav_val = nav_index });
   4642                 if (zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit)) {
   4643                     return;
   4644                 }
   4645             }
   4646             assert(nav.status == .fully_resolved);
   4647             if (!Air.valFullyResolved(zcu.navValue(nav_index), zcu)) {
   4648                 // Type resolution failed in a way which affects this `Nav`. This is a transitive
   4649                 // failure, but it doesn't need recording, because this `Nav` semantically depends
   4650                 // on the failed type, so when it is changed the `Nav` will be updated.
   4651                 return;
   4652             }
   4653             comp.dispatchZcuLinkTask(tid, .{ .link_nav = nav_index });
   4654         },
   4655         .link_type => |ty| {
   4656             const zcu = comp.zcu.?;
   4657             if (zcu.failed_types.fetchSwapRemove(ty)) |*entry| entry.value.deinit(zcu.gpa);
   4658             if (!Air.typeFullyResolved(.fromInterned(ty), zcu)) {
   4659                 // Type resolution failed in a way which affects this type. This is a transitive
   4660                 // failure, but it doesn't need recording, because this type semantically depends
   4661                 // on the failed type, so when that is changed, this type will be updated.
   4662                 return;
   4663             }
   4664             comp.dispatchZcuLinkTask(tid, .{ .link_type = ty });
   4665         },
   4666         .update_line_number => |ti| {
   4667             comp.dispatchZcuLinkTask(tid, .{ .update_line_number = ti });
   4668         },
   4669         .analyze_func => |func| {
   4670             const named_frame = tracy.namedFrame("analyze_func");
   4671             defer named_frame.end();
   4672 
   4673             const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
   4674             defer pt.deactivate();
   4675 
   4676             pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) {
   4677                 error.OutOfMemory => |e| return e,
   4678                 error.AnalysisFail => return,
   4679             };
   4680         },
   4681         .analyze_comptime_unit => |unit| {
   4682             const named_frame = tracy.namedFrame("analyze_comptime_unit");
   4683             defer named_frame.end();
   4684 
   4685             const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
   4686             defer pt.deactivate();
   4687 
   4688             const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) {
   4689                 .@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu),
   4690                 .nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
   4691                 .nav_val => |nav| pt.ensureNavValUpToDate(nav),
   4692                 .type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
   4693                 .memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage),
   4694                 .func => unreachable,
   4695             };
   4696             maybe_err catch |err| switch (err) {
   4697                 error.OutOfMemory => |e| return e,
   4698                 error.AnalysisFail => return,
   4699             };
   4700 
   4701             queue_test_analysis: {
   4702                 if (!comp.config.is_test) break :queue_test_analysis;
   4703                 const nav = switch (unit.unwrap()) {
   4704                     .nav_val => |nav| nav,
   4705                     else => break :queue_test_analysis,
   4706                 };
   4707 
   4708                 // Check if this is a test function.
   4709                 const ip = &pt.zcu.intern_pool;
   4710                 if (!pt.zcu.test_functions.contains(nav)) {
   4711                     break :queue_test_analysis;
   4712                 }
   4713 
   4714                 // Tests are always emitted in test binaries. The decl_refs are created by
   4715                 // Zcu.populateTestFunctions, but this will not queue body analysis, so do
   4716                 // that now.
   4717                 try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.fully_resolved.val);
   4718             }
   4719         },
   4720         .resolve_type_fully => |ty| {
   4721             const named_frame = tracy.namedFrame("resolve_type_fully");
   4722             defer named_frame.end();
   4723 
   4724             const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
   4725             defer pt.deactivate();
   4726             Type.fromInterned(ty).resolveFully(pt) catch |err| switch (err) {
   4727                 error.OutOfMemory => return error.OutOfMemory,
   4728                 error.AnalysisFail => return,
   4729             };
   4730         },
   4731         .analyze_mod => |mod| {
   4732             const named_frame = tracy.namedFrame("analyze_mod");
   4733             defer named_frame.end();
   4734 
   4735             const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
   4736             defer pt.deactivate();
   4737             pt.semaMod(mod) catch |err| switch (err) {
   4738                 error.OutOfMemory => return error.OutOfMemory,
   4739                 error.AnalysisFail => return,
   4740             };
   4741         },
   4742         .windows_import_lib => |index| {
   4743             const named_frame = tracy.namedFrame("windows_import_lib");
   4744             defer named_frame.end();
   4745 
   4746             const link_lib = comp.windows_libs.keys()[index];
   4747             mingw.buildImportLib(comp, link_lib) catch |err| {
   4748                 // TODO Surface more error details.
   4749                 comp.lockAndSetMiscFailure(
   4750                     .windows_import_lib,
   4751                     "unable to generate DLL import .lib file for {s}: {s}",
   4752                     .{ link_lib, @errorName(err) },
   4753                 );
   4754             };
   4755         },
   4756     }
   4757 }
   4758 
   4759 pub fn separateCodegenThreadOk(comp: *const Compilation) bool {
   4760     if (InternPool.single_threaded) return false;
   4761     const zcu = comp.zcu orelse return true;
   4762     return zcu.backendSupportsFeature(.separate_thread);
   4763 }
   4764 
   4765 fn workerDocsCopy(comp: *Compilation) void {
   4766     docsCopyFallible(comp) catch |err| {
   4767         return comp.lockAndSetMiscFailure(
   4768             .docs_copy,
   4769             "unable to copy autodocs artifacts: {s}",
   4770             .{@errorName(err)},
   4771         );
   4772     };
   4773 }
   4774 
   4775 fn docsCopyFallible(comp: *Compilation) anyerror!void {
   4776     const zcu = comp.zcu orelse
   4777         return comp.lockAndSetMiscFailure(.docs_copy, "no Zig code to document", .{});
   4778 
   4779     const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
   4780     var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
   4781         return comp.lockAndSetMiscFailure(
   4782             .docs_copy,
   4783             "unable to create output directory '{}': {s}",
   4784             .{ docs_path, @errorName(err) },
   4785         );
   4786     };
   4787     defer out_dir.close();
   4788 
   4789     for (&[_][]const u8{ "docs/main.js", "docs/index.html" }) |sub_path| {
   4790         const basename = std.fs.path.basename(sub_path);
   4791         comp.dirs.zig_lib.handle.copyFile(sub_path, out_dir, basename, .{}) catch |err| {
   4792             comp.lockAndSetMiscFailure(.docs_copy, "unable to copy {s}: {s}", .{
   4793                 sub_path,
   4794                 @errorName(err),
   4795             });
   4796             return;
   4797         };
   4798     }
   4799 
   4800     var tar_file = out_dir.createFile("sources.tar", .{}) catch |err| {
   4801         return comp.lockAndSetMiscFailure(
   4802             .docs_copy,
   4803             "unable to create '{}/sources.tar': {s}",
   4804             .{ docs_path, @errorName(err) },
   4805         );
   4806     };
   4807     defer tar_file.close();
   4808 
   4809     var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, []const u8) = .empty;
   4810     defer seen_table.deinit(comp.gpa);
   4811 
   4812     try seen_table.put(comp.gpa, zcu.main_mod, comp.root_name);
   4813     try seen_table.put(comp.gpa, zcu.std_mod, zcu.std_mod.fully_qualified_name);
   4814 
   4815     var i: usize = 0;
   4816     while (i < seen_table.count()) : (i += 1) {
   4817         const mod = seen_table.keys()[i];
   4818         try comp.docsCopyModule(mod, seen_table.values()[i], tar_file);
   4819 
   4820         const deps = mod.deps.values();
   4821         try seen_table.ensureUnusedCapacity(comp.gpa, deps.len);
   4822         for (deps) |dep| seen_table.putAssumeCapacity(dep, dep.fully_qualified_name);
   4823     }
   4824 }
   4825 
   4826 fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8, tar_file: std.fs.File) !void {
   4827     const root = module.root;
   4828     var mod_dir = d: {
   4829         const root_dir, const sub_path = root.openInfo(comp.dirs);
   4830         break :d root_dir.openDir(sub_path, .{ .iterate = true });
   4831     } catch |err| {
   4832         return comp.lockAndSetMiscFailure(.docs_copy, "unable to open directory '{}': {s}", .{
   4833             root.fmt(comp), @errorName(err),
   4834         });
   4835     };
   4836     defer mod_dir.close();
   4837 
   4838     var walker = try mod_dir.walk(comp.gpa);
   4839     defer walker.deinit();
   4840 
   4841     var archiver = std.tar.writer(tar_file.writer().any());
   4842     archiver.prefix = name;
   4843 
   4844     while (try walker.next()) |entry| {
   4845         switch (entry.kind) {
   4846             .file => {
   4847                 if (!std.mem.endsWith(u8, entry.basename, ".zig")) continue;
   4848                 if (std.mem.eql(u8, entry.basename, "test.zig")) continue;
   4849                 if (std.mem.endsWith(u8, entry.basename, "_test.zig")) continue;
   4850             },
   4851             else => continue,
   4852         }
   4853         var file = mod_dir.openFile(entry.path, .{}) catch |err| {
   4854             return comp.lockAndSetMiscFailure(.docs_copy, "unable to open '{}{s}': {s}", .{
   4855                 root.fmt(comp), entry.path, @errorName(err),
   4856             });
   4857         };
   4858         defer file.close();
   4859         archiver.writeFile(entry.path, file) catch |err| {
   4860             return comp.lockAndSetMiscFailure(.docs_copy, "unable to archive '{}{s}': {s}", .{
   4861                 root.fmt(comp), entry.path, @errorName(err),
   4862             });
   4863         };
   4864     }
   4865 }
   4866 
   4867 fn workerDocsWasm(comp: *Compilation, parent_prog_node: std.Progress.Node) void {
   4868     const prog_node = parent_prog_node.start("Compile Autodocs", 0);
   4869     defer prog_node.end();
   4870 
   4871     workerDocsWasmFallible(comp, prog_node) catch |err| switch (err) {
   4872         error.SubCompilationFailed => return, // error reported already
   4873         else => comp.lockAndSetMiscFailure(.docs_wasm, "unable to build autodocs: {s}", .{
   4874             @errorName(err),
   4875         }),
   4876     };
   4877 }
   4878 
   4879 fn workerDocsWasmFallible(comp: *Compilation, prog_node: std.Progress.Node) anyerror!void {
   4880     const gpa = comp.gpa;
   4881 
   4882     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
   4883     defer arena_allocator.deinit();
   4884     const arena = arena_allocator.allocator();
   4885 
   4886     const optimize_mode = std.builtin.OptimizeMode.ReleaseSmall;
   4887     const output_mode = std.builtin.OutputMode.Exe;
   4888     const resolved_target: Package.Module.ResolvedTarget = .{
   4889         .result = std.zig.system.resolveTargetQuery(.{
   4890             .cpu_arch = .wasm32,
   4891             .os_tag = .freestanding,
   4892             .cpu_features_add = std.Target.wasm.featureSet(&.{
   4893                 .atomics,
   4894                 // .extended_const, not supported by Safari
   4895                 .reference_types,
   4896                 //.relaxed_simd, not supported by Firefox or Safari
   4897                 // observed to cause Error occured during wast conversion :
   4898                 // Unknown operator: 0xfd058 in Firefox 117
   4899                 //.simd128,
   4900                 // .tail_call, not supported by Safari
   4901             }),
   4902         }) catch unreachable,
   4903 
   4904         .is_native_os = false,
   4905         .is_native_abi = false,
   4906         .is_explicit_dynamic_linker = false,
   4907     };
   4908 
   4909     const config = try Config.resolve(.{
   4910         .output_mode = output_mode,
   4911         .resolved_target = resolved_target,
   4912         .is_test = false,
   4913         .have_zcu = true,
   4914         .emit_bin = true,
   4915         .root_optimize_mode = optimize_mode,
   4916         .link_libc = false,
   4917         .rdynamic = true,
   4918     });
   4919 
   4920     const src_basename = "main.zig";
   4921     const root_name = std.fs.path.stem(src_basename);
   4922 
   4923     const dirs = comp.dirs.withoutLocalCache();
   4924 
   4925     const root_mod = try Package.Module.create(arena, .{
   4926         .paths = .{
   4927             .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"),
   4928             .root_src_path = src_basename,
   4929         },
   4930         .fully_qualified_name = root_name,
   4931         .inherited = .{
   4932             .resolved_target = resolved_target,
   4933             .optimize_mode = optimize_mode,
   4934         },
   4935         .global = config,
   4936         .cc_argv = &.{},
   4937         .parent = null,
   4938     });
   4939     const walk_mod = try Package.Module.create(arena, .{
   4940         .paths = .{
   4941             .root = try .fromRoot(arena, dirs, .zig_lib, "docs/wasm"),
   4942             .root_src_path = "Walk.zig",
   4943         },
   4944         .fully_qualified_name = "Walk",
   4945         .inherited = .{
   4946             .resolved_target = resolved_target,
   4947             .optimize_mode = optimize_mode,
   4948         },
   4949         .global = config,
   4950         .cc_argv = &.{},
   4951         .parent = root_mod,
   4952     });
   4953     try root_mod.deps.put(arena, "Walk", walk_mod);
   4954 
   4955     const sub_compilation = try Compilation.create(gpa, arena, .{
   4956         .dirs = dirs,
   4957         .self_exe_path = comp.self_exe_path,
   4958         .config = config,
   4959         .root_mod = root_mod,
   4960         .entry = .disabled,
   4961         .cache_mode = .whole,
   4962         .root_name = root_name,
   4963         .thread_pool = comp.thread_pool,
   4964         .libc_installation = comp.libc_installation,
   4965         .emit_bin = .yes_cache,
   4966         .verbose_cc = comp.verbose_cc,
   4967         .verbose_link = comp.verbose_link,
   4968         .verbose_air = comp.verbose_air,
   4969         .verbose_intern_pool = comp.verbose_intern_pool,
   4970         .verbose_generic_instances = comp.verbose_intern_pool,
   4971         .verbose_llvm_ir = comp.verbose_llvm_ir,
   4972         .verbose_llvm_bc = comp.verbose_llvm_bc,
   4973         .verbose_cimport = comp.verbose_cimport,
   4974         .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
   4975     });
   4976     defer sub_compilation.destroy();
   4977 
   4978     try comp.updateSubCompilation(sub_compilation, .docs_wasm, prog_node);
   4979 
   4980     var crt_file = try sub_compilation.toCrtFile();
   4981     defer crt_file.deinit(gpa);
   4982 
   4983     const docs_bin_file = crt_file.full_object_path;
   4984     assert(docs_bin_file.sub_path.len > 0); // emitted binary is not a directory
   4985 
   4986     const docs_path = comp.resolveEmitPath(comp.emit_docs.?);
   4987     var out_dir = docs_path.root_dir.handle.makeOpenPath(docs_path.sub_path, .{}) catch |err| {
   4988         return comp.lockAndSetMiscFailure(
   4989             .docs_copy,
   4990             "unable to create output directory '{}': {s}",
   4991             .{ docs_path, @errorName(err) },
   4992         );
   4993     };
   4994     defer out_dir.close();
   4995 
   4996     crt_file.full_object_path.root_dir.handle.copyFile(
   4997         crt_file.full_object_path.sub_path,
   4998         out_dir,
   4999         "main.wasm",
   5000         .{},
   5001     ) catch |err| {
   5002         return comp.lockAndSetMiscFailure(.docs_copy, "unable to copy '{}' to '{}': {s}", .{
   5003             crt_file.full_object_path,
   5004             docs_path,
   5005             @errorName(err),
   5006         });
   5007     };
   5008 }
   5009 
   5010 fn workerUpdateFile(
   5011     tid: usize,
   5012     comp: *Compilation,
   5013     file: *Zcu.File,
   5014     file_index: Zcu.File.Index,
   5015     prog_node: std.Progress.Node,
   5016     wg: *WaitGroup,
   5017 ) void {
   5018     const child_prog_node = prog_node.start(std.fs.path.basename(file.path.sub_path), 0);
   5019     defer child_prog_node.end();
   5020 
   5021     const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
   5022     defer pt.deactivate();
   5023     pt.updateFile(file_index, file) catch |err| {
   5024         pt.reportRetryableFileError(file_index, "unable to load '{s}': {s}", .{ std.fs.path.basename(file.path.sub_path), @errorName(err) }) catch |oom| switch (oom) {
   5025             error.OutOfMemory => {
   5026                 comp.mutex.lock();
   5027                 defer comp.mutex.unlock();
   5028                 comp.setAllocFailure();
   5029             },
   5030         };
   5031         return;
   5032     };
   5033 
   5034     switch (file.getMode()) {
   5035         .zig => {}, // continue to logic below
   5036         .zon => return, // ZON can't import anything so we're done
   5037     }
   5038 
   5039     // Discover all imports in the file. Imports of modules we ignore for now since we don't
   5040     // know which module we're in, but imports of file paths might need us to queue up other
   5041     // AstGen jobs.
   5042     const imports_index = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)];
   5043     if (imports_index != 0) {
   5044         const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_index);
   5045         var import_i: u32 = 0;
   5046         var extra_index = extra.end;
   5047 
   5048         while (import_i < extra.data.imports_len) : (import_i += 1) {
   5049             const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra_index);
   5050             extra_index = item.end;
   5051 
   5052             const import_path = file.zir.?.nullTerminatedString(item.data.name);
   5053 
   5054             if (pt.discoverImport(file.path, import_path)) |res| switch (res) {
   5055                 .module, .existing_file => {},
   5056                 .new_file => |new| {
   5057                     comp.thread_pool.spawnWgId(wg, workerUpdateFile, .{
   5058                         comp, new.file, new.index, prog_node, wg,
   5059                     });
   5060                 },
   5061             } else |err| switch (err) {
   5062                 error.OutOfMemory => {
   5063                     comp.mutex.lock();
   5064                     defer comp.mutex.unlock();
   5065                     comp.setAllocFailure();
   5066                 },
   5067             }
   5068         }
   5069     }
   5070 }
   5071 
   5072 fn workerUpdateBuiltinFile(comp: *Compilation, file: *Zcu.File) void {
   5073     Builtin.updateFileOnDisk(file, comp) catch |err| {
   5074         comp.mutex.lock();
   5075         defer comp.mutex.unlock();
   5076         comp.setMiscFailure(
   5077             .write_builtin_zig,
   5078             "unable to write '{}': {s}",
   5079             .{ file.path.fmt(comp), @errorName(err) },
   5080         );
   5081     };
   5082 }
   5083 
   5084 fn workerUpdateEmbedFile(tid: usize, comp: *Compilation, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) void {
   5085     comp.detectEmbedFileUpdate(@enumFromInt(tid), ef_index, ef) catch |err| switch (err) {
   5086         error.OutOfMemory => {
   5087             comp.mutex.lock();
   5088             defer comp.mutex.unlock();
   5089             comp.setAllocFailure();
   5090         },
   5091     };
   5092 }
   5093 
   5094 fn detectEmbedFileUpdate(comp: *Compilation, tid: Zcu.PerThread.Id, ef_index: Zcu.EmbedFile.Index, ef: *Zcu.EmbedFile) !void {
   5095     const zcu = comp.zcu.?;
   5096     const pt: Zcu.PerThread = .activate(zcu, tid);
   5097     defer pt.deactivate();
   5098 
   5099     const old_val = ef.val;
   5100     const old_err = ef.err;
   5101 
   5102     try pt.updateEmbedFile(ef, null);
   5103 
   5104     if (ef.val != .none and ef.val == old_val) return; // success, value unchanged
   5105     if (ef.val == .none and old_val == .none and ef.err == old_err) return; // failure, error unchanged
   5106 
   5107     comp.mutex.lock();
   5108     defer comp.mutex.unlock();
   5109 
   5110     try zcu.markDependeeOutdated(.not_marked_po, .{ .embed_file = ef_index });
   5111 }
   5112 
   5113 pub fn obtainCObjectCacheManifest(
   5114     comp: *const Compilation,
   5115     owner_mod: *Package.Module,
   5116 ) Cache.Manifest {
   5117     var man = comp.cache_parent.obtain();
   5118 
   5119     // Only things that need to be added on top of the base hash, and only things
   5120     // that apply both to @cImport and compiling C objects. No linking stuff here!
   5121     // Also nothing that applies only to compiling .zig code.
   5122     cache_helpers.addModule(&man.hash, owner_mod);
   5123     man.hash.addListOfBytes(comp.global_cc_argv);
   5124     man.hash.add(comp.config.link_libcpp);
   5125 
   5126     // When libc_installation is null it means that Zig generated this dir list
   5127     // based on the zig library directory alone. The zig lib directory file
   5128     // path is purposefully either in the cache or not in the cache. The
   5129     // decision should not be overridden here.
   5130     if (comp.libc_installation != null) {
   5131         man.hash.addListOfBytes(comp.libc_include_dir_list);
   5132     }
   5133 
   5134     return man;
   5135 }
   5136 
   5137 pub fn obtainWin32ResourceCacheManifest(comp: *const Compilation) Cache.Manifest {
   5138     var man = comp.cache_parent.obtain();
   5139 
   5140     man.hash.add(comp.rc_includes);
   5141 
   5142     return man;
   5143 }
   5144 
   5145 pub const CImportResult = struct {
   5146     digest: [Cache.bin_digest_len]u8,
   5147     cache_hit: bool,
   5148     errors: std.zig.ErrorBundle,
   5149 
   5150     pub fn deinit(result: *CImportResult, gpa: mem.Allocator) void {
   5151         result.errors.deinit(gpa);
   5152     }
   5153 };
   5154 
   5155 /// Caller owns returned memory.
   5156 pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult {
   5157     dev.check(.translate_c_command);
   5158 
   5159     const tracy_trace = trace(@src());
   5160     defer tracy_trace.end();
   5161 
   5162     const cimport_zig_basename = "cimport.zig";
   5163 
   5164     var man = comp.obtainCObjectCacheManifest(owner_mod);
   5165     defer man.deinit();
   5166 
   5167     man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
   5168     man.hash.addBytes(c_src);
   5169     man.hash.add(comp.config.c_frontend);
   5170 
   5171     // If the previous invocation resulted in clang errors, we will see a hit
   5172     // here with 0 files in the manifest, in which case it is actually a miss.
   5173     // We need to "unhit" in this case, to keep the digests matching.
   5174     const prev_hash_state = man.hash.peekBin();
   5175     const actual_hit = hit: {
   5176         _ = try man.hit();
   5177         if (man.files.entries.len == 0) {
   5178             man.unhit(prev_hash_state, 0);
   5179             break :hit false;
   5180         }
   5181         break :hit true;
   5182     };
   5183     const digest = if (!actual_hit) digest: {
   5184         var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
   5185         defer arena_allocator.deinit();
   5186         const arena = arena_allocator.allocator();
   5187 
   5188         const tmp_digest = man.hash.peek();
   5189         const tmp_dir_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &tmp_digest });
   5190         var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath(tmp_dir_sub_path, .{});
   5191         defer zig_cache_tmp_dir.close();
   5192         const cimport_basename = "cimport.h";
   5193         const out_h_path = try comp.dirs.local_cache.join(arena, &[_][]const u8{
   5194             tmp_dir_sub_path, cimport_basename,
   5195         });
   5196         const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_h_path});
   5197 
   5198         try zig_cache_tmp_dir.writeFile(.{ .sub_path = cimport_basename, .data = c_src });
   5199         if (comp.verbose_cimport) {
   5200             log.info("C import source: {s}", .{out_h_path});
   5201         }
   5202 
   5203         var argv = std.ArrayList([]const u8).init(comp.gpa);
   5204         defer argv.deinit();
   5205 
   5206         try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1]
   5207         try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path, owner_mod);
   5208 
   5209         try argv.append(out_h_path);
   5210 
   5211         if (comp.verbose_cc) {
   5212             dump_argv(argv.items);
   5213         }
   5214         var tree = switch (comp.config.c_frontend) {
   5215             .aro => tree: {
   5216                 if (true) @panic("TODO");
   5217                 break :tree undefined;
   5218             },
   5219             .clang => tree: {
   5220                 if (!build_options.have_llvm) unreachable;
   5221                 const translate_c = @import("translate_c.zig");
   5222 
   5223                 // Convert to null terminated args.
   5224                 const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
   5225                 new_argv_with_sentinel[argv.items.len] = null;
   5226                 const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
   5227                 for (argv.items, 0..) |arg, i| {
   5228                     new_argv[i] = try arena.dupeZ(u8, arg);
   5229                 }
   5230 
   5231                 const c_headers_dir_path_z = try comp.dirs.zig_lib.joinZ(arena, &.{"include"});
   5232                 var errors = std.zig.ErrorBundle.empty;
   5233                 errdefer errors.deinit(comp.gpa);
   5234                 break :tree translate_c.translate(
   5235                     comp.gpa,
   5236                     new_argv.ptr,
   5237                     new_argv.ptr + new_argv.len,
   5238                     &errors,
   5239                     c_headers_dir_path_z,
   5240                 ) catch |err| switch (err) {
   5241                     error.OutOfMemory => return error.OutOfMemory,
   5242                     error.SemanticAnalyzeFail => {
   5243                         return CImportResult{
   5244                             .digest = undefined,
   5245                             .cache_hit = actual_hit,
   5246                             .errors = errors,
   5247                         };
   5248                     },
   5249                 };
   5250             },
   5251         };
   5252         defer tree.deinit(comp.gpa);
   5253 
   5254         if (comp.verbose_cimport) {
   5255             log.info("C import .d file: {s}", .{out_dep_path});
   5256         }
   5257 
   5258         const dep_basename = std.fs.path.basename(out_dep_path);
   5259         try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
   5260         switch (comp.cache_use) {
   5261             .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
   5262                 whole.cache_manifest_mutex.lock();
   5263                 defer whole.cache_manifest_mutex.unlock();
   5264                 try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
   5265             },
   5266             .incremental, .none => {},
   5267         }
   5268 
   5269         const bin_digest = man.finalBin();
   5270         const hex_digest = Cache.binToHex(bin_digest);
   5271         const o_sub_path = "o" ++ std.fs.path.sep_str ++ hex_digest;
   5272         var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{});
   5273         defer o_dir.close();
   5274 
   5275         var out_zig_file = try o_dir.createFile(cimport_zig_basename, .{});
   5276         defer out_zig_file.close();
   5277 
   5278         const formatted = try tree.render(comp.gpa);
   5279         defer comp.gpa.free(formatted);
   5280 
   5281         try out_zig_file.writeAll(formatted);
   5282 
   5283         break :digest bin_digest;
   5284     } else man.finalBin();
   5285 
   5286     if (man.have_exclusive_lock) {
   5287         // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
   5288         // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
   5289         // the contents were the same, we hit the cache but the manifest is dirty and we need to update
   5290         // it to prevent doing a full file content comparison the next time around.
   5291         man.writeManifest() catch |err| {
   5292             log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});
   5293         };
   5294     }
   5295 
   5296     return CImportResult{
   5297         .digest = digest,
   5298         .cache_hit = actual_hit,
   5299         .errors = std.zig.ErrorBundle.empty,
   5300     };
   5301 }
   5302 
   5303 fn workerUpdateCObject(
   5304     comp: *Compilation,
   5305     c_object: *CObject,
   5306     progress_node: std.Progress.Node,
   5307 ) void {
   5308     comp.updateCObject(c_object, progress_node) catch |err| switch (err) {
   5309         error.AnalysisFail => return,
   5310         else => {
   5311             comp.reportRetryableCObjectError(c_object, err) catch |oom| switch (oom) {
   5312                 // Swallowing this error is OK because it's implied to be OOM when
   5313                 // there is a missing failed_c_objects error message.
   5314                 error.OutOfMemory => {},
   5315             };
   5316         },
   5317     };
   5318 }
   5319 
   5320 fn workerUpdateWin32Resource(
   5321     comp: *Compilation,
   5322     win32_resource: *Win32Resource,
   5323     progress_node: std.Progress.Node,
   5324 ) void {
   5325     comp.updateWin32Resource(win32_resource, progress_node) catch |err| switch (err) {
   5326         error.AnalysisFail => return,
   5327         else => {
   5328             comp.reportRetryableWin32ResourceError(win32_resource, err) catch |oom| switch (oom) {
   5329                 // Swallowing this error is OK because it's implied to be OOM when
   5330                 // there is a missing failed_win32_resources error message.
   5331                 error.OutOfMemory => {},
   5332             };
   5333         },
   5334     };
   5335 }
   5336 
   5337 pub const RtOptions = struct {
   5338     checks_valgrind: bool = false,
   5339     allow_lto: bool = true,
   5340 };
   5341 
   5342 fn workerZcuCodegen(
   5343     tid: usize,
   5344     comp: *Compilation,
   5345     func_index: InternPool.Index,
   5346     orig_air: Air,
   5347     out: *link.ZcuTask.LinkFunc.SharedMir,
   5348 ) void {
   5349     var air = orig_air;
   5350     // We own `air` now, so we are responsbile for freeing it.
   5351     defer air.deinit(comp.gpa);
   5352     const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
   5353     defer pt.deactivate();
   5354     pt.runCodegen(func_index, &air, out);
   5355 }
   5356 
   5357 fn buildRt(
   5358     comp: *Compilation,
   5359     root_source_name: []const u8,
   5360     root_name: []const u8,
   5361     output_mode: std.builtin.OutputMode,
   5362     misc_task: MiscTask,
   5363     prog_node: std.Progress.Node,
   5364     options: RtOptions,
   5365     out: *?CrtFile,
   5366 ) void {
   5367     comp.buildOutputFromZig(
   5368         root_source_name,
   5369         root_name,
   5370         output_mode,
   5371         misc_task,
   5372         prog_node,
   5373         options,
   5374         out,
   5375     ) catch |err| switch (err) {
   5376         error.SubCompilationFailed => return, // error reported already
   5377         else => comp.lockAndSetMiscFailure(misc_task, "unable to build {s}: {s}", .{
   5378             @tagName(misc_task), @errorName(err),
   5379         }),
   5380     };
   5381 }
   5382 
   5383 fn buildMuslCrtFile(comp: *Compilation, crt_file: musl.CrtFile, prog_node: std.Progress.Node) void {
   5384     if (musl.buildCrtFile(comp, crt_file, prog_node)) |_| {
   5385         comp.queued_jobs.musl_crt_file[@intFromEnum(crt_file)] = false;
   5386     } else |err| switch (err) {
   5387         error.SubCompilationFailed => return, // error reported already
   5388         else => comp.lockAndSetMiscFailure(.musl_crt_file, "unable to build musl {s}: {s}", .{
   5389             @tagName(crt_file), @errorName(err),
   5390         }),
   5391     }
   5392 }
   5393 
   5394 fn buildGlibcCrtFile(comp: *Compilation, crt_file: glibc.CrtFile, prog_node: std.Progress.Node) void {
   5395     if (glibc.buildCrtFile(comp, crt_file, prog_node)) |_| {
   5396         comp.queued_jobs.glibc_crt_file[@intFromEnum(crt_file)] = false;
   5397     } else |err| switch (err) {
   5398         error.SubCompilationFailed => return, // error reported already
   5399         else => comp.lockAndSetMiscFailure(.glibc_crt_file, "unable to build glibc {s}: {s}", .{
   5400             @tagName(crt_file), @errorName(err),
   5401         }),
   5402     }
   5403 }
   5404 
   5405 fn buildGlibcSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
   5406     if (glibc.buildSharedObjects(comp, prog_node)) |_| {
   5407         // The job should no longer be queued up since it succeeded.
   5408         comp.queued_jobs.glibc_shared_objects = false;
   5409     } else |err| switch (err) {
   5410         error.SubCompilationFailed => return, // error reported already
   5411         else => comp.lockAndSetMiscFailure(.glibc_shared_objects, "unable to build glibc shared objects: {s}", .{
   5412             @errorName(err),
   5413         }),
   5414     }
   5415 }
   5416 
   5417 fn buildFreeBSDCrtFile(comp: *Compilation, crt_file: freebsd.CrtFile, prog_node: std.Progress.Node) void {
   5418     if (freebsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
   5419         comp.queued_jobs.freebsd_crt_file[@intFromEnum(crt_file)] = false;
   5420     } else |err| switch (err) {
   5421         error.SubCompilationFailed => return, // error reported already
   5422         else => comp.lockAndSetMiscFailure(.freebsd_crt_file, "unable to build FreeBSD {s}: {s}", .{
   5423             @tagName(crt_file), @errorName(err),
   5424         }),
   5425     }
   5426 }
   5427 
   5428 fn buildFreeBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
   5429     if (freebsd.buildSharedObjects(comp, prog_node)) |_| {
   5430         // The job should no longer be queued up since it succeeded.
   5431         comp.queued_jobs.freebsd_shared_objects = false;
   5432     } else |err| switch (err) {
   5433         error.SubCompilationFailed => return, // error reported already
   5434         else => comp.lockAndSetMiscFailure(.freebsd_shared_objects, "unable to build FreeBSD libc shared objects: {s}", .{
   5435             @errorName(err),
   5436         }),
   5437     }
   5438 }
   5439 
   5440 fn buildNetBSDCrtFile(comp: *Compilation, crt_file: netbsd.CrtFile, prog_node: std.Progress.Node) void {
   5441     if (netbsd.buildCrtFile(comp, crt_file, prog_node)) |_| {
   5442         comp.queued_jobs.netbsd_crt_file[@intFromEnum(crt_file)] = false;
   5443     } else |err| switch (err) {
   5444         error.SubCompilationFailed => return, // error reported already
   5445         else => comp.lockAndSetMiscFailure(.netbsd_crt_file, "unable to build NetBSD {s}: {s}", .{
   5446             @tagName(crt_file), @errorName(err),
   5447         }),
   5448     }
   5449 }
   5450 
   5451 fn buildNetBSDSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) void {
   5452     if (netbsd.buildSharedObjects(comp, prog_node)) |_| {
   5453         // The job should no longer be queued up since it succeeded.
   5454         comp.queued_jobs.netbsd_shared_objects = false;
   5455     } else |err| switch (err) {
   5456         error.SubCompilationFailed => return, // error reported already
   5457         else => comp.lockAndSetMiscFailure(.netbsd_shared_objects, "unable to build NetBSD libc shared objects: {s}", .{
   5458             @errorName(err),
   5459         }),
   5460     }
   5461 }
   5462 
   5463 fn buildMingwCrtFile(comp: *Compilation, crt_file: mingw.CrtFile, prog_node: std.Progress.Node) void {
   5464     if (mingw.buildCrtFile(comp, crt_file, prog_node)) |_| {
   5465         comp.queued_jobs.mingw_crt_file[@intFromEnum(crt_file)] = false;
   5466     } else |err| switch (err) {
   5467         error.SubCompilationFailed => return, // error reported already
   5468         else => comp.lockAndSetMiscFailure(.mingw_crt_file, "unable to build mingw-w64 {s}: {s}", .{
   5469             @tagName(crt_file), @errorName(err),
   5470         }),
   5471     }
   5472 }
   5473 
   5474 fn buildWasiLibcCrtFile(comp: *Compilation, crt_file: wasi_libc.CrtFile, prog_node: std.Progress.Node) void {
   5475     if (wasi_libc.buildCrtFile(comp, crt_file, prog_node)) |_| {
   5476         comp.queued_jobs.wasi_libc_crt_file[@intFromEnum(crt_file)] = false;
   5477     } else |err| switch (err) {
   5478         error.SubCompilationFailed => return, // error reported already
   5479         else => comp.lockAndSetMiscFailure(.wasi_libc_crt_file, "unable to build WASI libc {s}: {s}", .{
   5480             @tagName(crt_file), @errorName(err),
   5481         }),
   5482     }
   5483 }
   5484 
   5485 fn buildLibUnwind(comp: *Compilation, prog_node: std.Progress.Node) void {
   5486     if (libunwind.buildStaticLib(comp, prog_node)) |_| {
   5487         comp.queued_jobs.libunwind = false;
   5488     } else |err| switch (err) {
   5489         error.SubCompilationFailed => return, // error reported already
   5490         else => comp.lockAndSetMiscFailure(.libunwind, "unable to build libunwind: {s}", .{@errorName(err)}),
   5491     }
   5492 }
   5493 
   5494 fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) void {
   5495     if (libcxx.buildLibCxx(comp, prog_node)) |_| {
   5496         comp.queued_jobs.libcxx = false;
   5497     } else |err| switch (err) {
   5498         error.SubCompilationFailed => return, // error reported already
   5499         else => comp.lockAndSetMiscFailure(.libcxx, "unable to build libcxx: {s}", .{@errorName(err)}),
   5500     }
   5501 }
   5502 
   5503 fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) void {
   5504     if (libcxx.buildLibCxxAbi(comp, prog_node)) |_| {
   5505         comp.queued_jobs.libcxxabi = false;
   5506     } else |err| switch (err) {
   5507         error.SubCompilationFailed => return, // error reported already
   5508         else => comp.lockAndSetMiscFailure(.libcxxabi, "unable to build libcxxabi: {s}", .{@errorName(err)}),
   5509     }
   5510 }
   5511 
   5512 fn buildLibTsan(comp: *Compilation, prog_node: std.Progress.Node) void {
   5513     if (libtsan.buildTsan(comp, prog_node)) |_| {
   5514         comp.queued_jobs.libtsan = false;
   5515     } else |err| switch (err) {
   5516         error.SubCompilationFailed => return, // error reported already
   5517         else => comp.lockAndSetMiscFailure(.libtsan, "unable to build TSAN library: {s}", .{@errorName(err)}),
   5518     }
   5519 }
   5520 
   5521 fn buildLibZigC(comp: *Compilation, prog_node: std.Progress.Node) void {
   5522     comp.buildOutputFromZig(
   5523         "c.zig",
   5524         "zigc",
   5525         .Lib,
   5526         .libzigc,
   5527         prog_node,
   5528         .{},
   5529         &comp.zigc_static_lib,
   5530     ) catch |err| switch (err) {
   5531         error.SubCompilationFailed => return, // error reported already
   5532         else => comp.lockAndSetMiscFailure(.libzigc, "unable to build libzigc: {s}", .{@errorName(err)}),
   5533     };
   5534 }
   5535 
   5536 fn reportRetryableCObjectError(
   5537     comp: *Compilation,
   5538     c_object: *CObject,
   5539     err: anyerror,
   5540 ) error{OutOfMemory}!void {
   5541     c_object.status = .failure_retryable;
   5542 
   5543     switch (comp.failCObj(c_object, "{s}", .{@errorName(err)})) {
   5544         error.AnalysisFail => return,
   5545         else => |e| return e,
   5546     }
   5547 }
   5548 
   5549 fn reportRetryableWin32ResourceError(
   5550     comp: *Compilation,
   5551     win32_resource: *Win32Resource,
   5552     err: anyerror,
   5553 ) error{OutOfMemory}!void {
   5554     win32_resource.status = .failure_retryable;
   5555 
   5556     var bundle: ErrorBundle.Wip = undefined;
   5557     try bundle.init(comp.gpa);
   5558     errdefer bundle.deinit();
   5559     try bundle.addRootErrorMessage(.{
   5560         .msg = try bundle.printString("{s}", .{@errorName(err)}),
   5561         .src_loc = try bundle.addSourceLocation(.{
   5562             .src_path = try bundle.addString(switch (win32_resource.src) {
   5563                 .rc => |rc_src| rc_src.src_path,
   5564                 .manifest => |manifest_src| manifest_src,
   5565             }),
   5566             .line = 0,
   5567             .column = 0,
   5568             .span_start = 0,
   5569             .span_main = 0,
   5570             .span_end = 0,
   5571         }),
   5572     });
   5573     const finished_bundle = try bundle.toOwnedBundle("");
   5574     {
   5575         comp.mutex.lock();
   5576         defer comp.mutex.unlock();
   5577         try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, finished_bundle);
   5578     }
   5579 }
   5580 
   5581 fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: std.Progress.Node) !void {
   5582     if (comp.config.c_frontend == .aro) {
   5583         return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{});
   5584     }
   5585     if (!build_options.have_llvm) {
   5586         return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
   5587     }
   5588     const self_exe_path = comp.self_exe_path orelse
   5589         return comp.failCObj(c_object, "clang compilation disabled", .{});
   5590 
   5591     const tracy_trace = trace(@src());
   5592     defer tracy_trace.end();
   5593 
   5594     log.debug("updating C object: {s}", .{c_object.src.src_path});
   5595 
   5596     const gpa = comp.gpa;
   5597 
   5598     if (c_object.clearStatus(gpa)) {
   5599         // There was previous failure.
   5600         comp.mutex.lock();
   5601         defer comp.mutex.unlock();
   5602         // If the failure was OOM, there will not be an entry here, so we do
   5603         // not assert discard.
   5604         _ = comp.failed_c_objects.swapRemove(c_object);
   5605     }
   5606 
   5607     var man = comp.obtainCObjectCacheManifest(c_object.src.owner);
   5608     defer man.deinit();
   5609 
   5610     man.hash.add(comp.clang_preprocessor_mode);
   5611     man.hash.addOptionalBytes(comp.emit_asm);
   5612     man.hash.addOptionalBytes(comp.emit_llvm_ir);
   5613     man.hash.addOptionalBytes(comp.emit_llvm_bc);
   5614 
   5615     try cache_helpers.hashCSource(&man, c_object.src);
   5616 
   5617     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
   5618     defer arena_allocator.deinit();
   5619     const arena = arena_allocator.allocator();
   5620 
   5621     const c_source_basename = std.fs.path.basename(c_object.src.src_path);
   5622 
   5623     const child_progress_node = c_obj_prog_node.start(c_source_basename, 0);
   5624     defer child_progress_node.end();
   5625 
   5626     // Special case when doing build-obj for just one C file. When there are more than one object
   5627     // file and building an object we need to link them together, but with just one it should go
   5628     // directly to the output file.
   5629     const direct_o = comp.c_source_files.len == 1 and comp.zcu == null and
   5630         comp.config.output_mode == .Obj and !link.anyObjectInputs(comp.link_inputs);
   5631     const o_basename_noext = if (direct_o)
   5632         comp.root_name
   5633     else
   5634         c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len];
   5635 
   5636     const target = comp.getTarget();
   5637     const o_ext = target.ofmt.fileExt(target.cpu.arch);
   5638     const digest = if (!comp.disable_c_depfile and try man.hit()) man.final() else blk: {
   5639         var argv = std.ArrayList([]const u8).init(gpa);
   5640         defer argv.deinit();
   5641 
   5642         // In case we are doing passthrough mode, we need to detect -S and -emit-llvm.
   5643         const out_ext = e: {
   5644             if (!comp.clang_passthrough_mode)
   5645                 break :e o_ext;
   5646             if (comp.emit_asm != null)
   5647                 break :e ".s";
   5648             if (comp.emit_llvm_ir != null)
   5649                 break :e ".ll";
   5650             if (comp.emit_llvm_bc != null)
   5651                 break :e ".bc";
   5652 
   5653             break :e o_ext;
   5654         };
   5655         const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, out_ext });
   5656         const ext = c_object.src.ext orelse classifyFileExt(c_object.src.src_path);
   5657 
   5658         try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang" });
   5659         // if "ext" is explicit, add "-x <lang>". Otherwise let clang do its thing.
   5660         if (c_object.src.ext != null or ext.clangNeedsLanguageOverride()) {
   5661             try argv.appendSlice(&[_][]const u8{ "-x", switch (ext) {
   5662                 .assembly => "assembler",
   5663                 .assembly_with_cpp => "assembler-with-cpp",
   5664                 .c => "c",
   5665                 .h => "c-header",
   5666                 .cpp => "c++",
   5667                 .hpp => "c++-header",
   5668                 .m => "objective-c",
   5669                 .hm => "objective-c-header",
   5670                 .mm => "objective-c++",
   5671                 .hmm => "objective-c++-header",
   5672                 else => fatal("language '{s}' is unsupported in this context", .{@tagName(ext)}),
   5673             } });
   5674         }
   5675         try argv.append(c_object.src.src_path);
   5676 
   5677         // When all these flags are true, it means that the entire purpose of
   5678         // this compilation is to perform a single zig cc operation. This means
   5679         // that we could "tail call" clang by doing an execve, and any use of
   5680         // the caching system would actually be problematic since the user is
   5681         // presumably doing their own caching by using dep file flags.
   5682         if (std.process.can_execv and direct_o and
   5683             comp.disable_c_depfile and comp.clang_passthrough_mode)
   5684         {
   5685             try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner);
   5686             try argv.appendSlice(c_object.src.extra_flags);
   5687             try argv.appendSlice(c_object.src.cache_exempt_flags);
   5688 
   5689             const out_obj_path = if (comp.bin_file) |lf|
   5690                 try lf.emit.root_dir.join(arena, &.{lf.emit.sub_path})
   5691             else
   5692                 "/dev/null";
   5693 
   5694             try argv.ensureUnusedCapacity(6);
   5695             switch (comp.clang_preprocessor_mode) {
   5696                 .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
   5697                 .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
   5698                 .pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
   5699                 .stdout => argv.appendAssumeCapacity("-E"),
   5700             }
   5701 
   5702             if (comp.emit_asm != null) {
   5703                 argv.appendAssumeCapacity("-S");
   5704             } else if (comp.emit_llvm_ir != null) {
   5705                 argv.appendSliceAssumeCapacity(&[_][]const u8{ "-emit-llvm", "-S" });
   5706             } else if (comp.emit_llvm_bc != null) {
   5707                 argv.appendAssumeCapacity("-emit-llvm");
   5708             }
   5709 
   5710             if (comp.verbose_cc) {
   5711                 dump_argv(argv.items);
   5712             }
   5713 
   5714             const err = std.process.execv(arena, argv.items);
   5715             fatal("unable to execv clang: {s}", .{@errorName(err)});
   5716         }
   5717 
   5718         // We can't know the digest until we do the C compiler invocation,
   5719         // so we need a temporary filename.
   5720         const out_obj_path = try comp.tmpFilePath(arena, o_basename);
   5721         var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath("tmp", .{});
   5722         defer zig_cache_tmp_dir.close();
   5723 
   5724         const out_diag_path = if (comp.clang_passthrough_mode or !ext.clangSupportsDiagnostics())
   5725             null
   5726         else
   5727             try std.fmt.allocPrint(arena, "{s}.diag", .{out_obj_path});
   5728         const out_dep_path = if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
   5729             null
   5730         else
   5731             try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
   5732 
   5733         try comp.addCCArgs(arena, &argv, ext, out_dep_path, c_object.src.owner);
   5734         try argv.appendSlice(c_object.src.extra_flags);
   5735         try argv.appendSlice(c_object.src.cache_exempt_flags);
   5736 
   5737         try argv.ensureUnusedCapacity(6);
   5738         switch (comp.clang_preprocessor_mode) {
   5739             .no => argv.appendSliceAssumeCapacity(&.{ "-c", "-o", out_obj_path }),
   5740             .yes => argv.appendSliceAssumeCapacity(&.{ "-E", "-o", out_obj_path }),
   5741             .pch => argv.appendSliceAssumeCapacity(&.{ "-Xclang", "-emit-pch", "-o", out_obj_path }),
   5742             .stdout => argv.appendAssumeCapacity("-E"),
   5743         }
   5744         if (out_diag_path) |diag_file_path| {
   5745             argv.appendSliceAssumeCapacity(&.{ "--serialize-diagnostics", diag_file_path });
   5746         } else if (comp.clang_passthrough_mode) {
   5747             if (comp.emit_asm != null) {
   5748                 argv.appendAssumeCapacity("-S");
   5749             } else if (comp.emit_llvm_ir != null) {
   5750                 argv.appendSliceAssumeCapacity(&.{ "-emit-llvm", "-S" });
   5751             } else if (comp.emit_llvm_bc != null) {
   5752                 argv.appendAssumeCapacity("-emit-llvm");
   5753             }
   5754         }
   5755 
   5756         if (comp.verbose_cc) {
   5757             dump_argv(argv.items);
   5758         }
   5759 
   5760         // Just to save disk space, we delete the files that are never needed again.
   5761         defer if (out_diag_path) |diag_file_path| zig_cache_tmp_dir.deleteFile(std.fs.path.basename(diag_file_path)) catch |err| switch (err) {
   5762             error.FileNotFound => {}, // the file wasn't created due to an error we reported
   5763             else => log.warn("failed to delete '{s}': {s}", .{ diag_file_path, @errorName(err) }),
   5764         };
   5765         defer if (out_dep_path) |dep_file_path| zig_cache_tmp_dir.deleteFile(std.fs.path.basename(dep_file_path)) catch |err| switch (err) {
   5766             error.FileNotFound => {}, // the file wasn't created due to an error we reported
   5767             else => log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }),
   5768         };
   5769         if (std.process.can_spawn) {
   5770             var child = std.process.Child.init(argv.items, arena);
   5771             if (comp.clang_passthrough_mode) {
   5772                 child.stdin_behavior = .Inherit;
   5773                 child.stdout_behavior = .Inherit;
   5774                 child.stderr_behavior = .Inherit;
   5775 
   5776                 const term = child.spawnAndWait() catch |err| {
   5777                     return comp.failCObj(c_object, "failed to spawn zig clang (passthrough mode) {s}: {s}", .{ argv.items[0], @errorName(err) });
   5778                 };
   5779                 switch (term) {
   5780                     .Exited => |code| {
   5781                         if (code != 0) {
   5782                             std.process.exit(code);
   5783                         }
   5784                         if (comp.clang_preprocessor_mode == .stdout)
   5785                             std.process.exit(0);
   5786                     },
   5787                     else => std.process.abort(),
   5788                 }
   5789             } else {
   5790                 child.stdin_behavior = .Ignore;
   5791                 child.stdout_behavior = .Ignore;
   5792                 child.stderr_behavior = .Pipe;
   5793 
   5794                 try child.spawn();
   5795 
   5796                 const stderr = try child.stderr.?.reader().readAllAlloc(arena, std.math.maxInt(usize));
   5797 
   5798                 const term = child.wait() catch |err| {
   5799                     return comp.failCObj(c_object, "failed to spawn zig clang {s}: {s}", .{ argv.items[0], @errorName(err) });
   5800                 };
   5801 
   5802                 switch (term) {
   5803                     .Exited => |code| if (code != 0) if (out_diag_path) |diag_file_path| {
   5804                         const bundle = CObject.Diag.Bundle.parse(gpa, diag_file_path) catch |err| {
   5805                             log.err("{}: failed to parse clang diagnostics: {s}", .{ err, stderr });
   5806                             return comp.failCObj(c_object, "clang exited with code {d}", .{code});
   5807                         };
   5808                         return comp.failCObjWithOwnedDiagBundle(c_object, bundle);
   5809                     } else {
   5810                         log.err("clang failed with stderr: {s}", .{stderr});
   5811                         return comp.failCObj(c_object, "clang exited with code {d}", .{code});
   5812                     },
   5813                     else => {
   5814                         log.err("clang terminated with stderr: {s}", .{stderr});
   5815                         return comp.failCObj(c_object, "clang terminated unexpectedly", .{});
   5816                     },
   5817                 }
   5818             }
   5819         } else {
   5820             const exit_code = try clangMain(arena, argv.items);
   5821             if (exit_code != 0) {
   5822                 if (comp.clang_passthrough_mode) {
   5823                     std.process.exit(exit_code);
   5824                 } else {
   5825                     return comp.failCObj(c_object, "clang exited with code {d}", .{exit_code});
   5826                 }
   5827             }
   5828             if (comp.clang_passthrough_mode and
   5829                 comp.clang_preprocessor_mode == .stdout)
   5830             {
   5831                 std.process.exit(0);
   5832             }
   5833         }
   5834 
   5835         if (out_dep_path) |dep_file_path| {
   5836             const dep_basename = std.fs.path.basename(dep_file_path);
   5837             // Add the files depended on to the cache system.
   5838             try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
   5839             switch (comp.cache_use) {
   5840                 .whole => |whole| {
   5841                     if (whole.cache_manifest) |whole_cache_manifest| {
   5842                         whole.cache_manifest_mutex.lock();
   5843                         defer whole.cache_manifest_mutex.unlock();
   5844                         try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename);
   5845                     }
   5846                 },
   5847                 .incremental, .none => {},
   5848             }
   5849         }
   5850 
   5851         // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
   5852         if (comp.disable_c_depfile) _ = try man.hit();
   5853 
   5854         // Rename into place.
   5855         const digest = man.final();
   5856         const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
   5857         var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{});
   5858         defer o_dir.close();
   5859         const tmp_basename = std.fs.path.basename(out_obj_path);
   5860         try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename);
   5861         break :blk digest;
   5862     };
   5863 
   5864     if (man.have_exclusive_lock) {
   5865         // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
   5866         // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
   5867         // the contents were the same, we hit the cache but the manifest is dirty and we need to update
   5868         // it to prevent doing a full file content comparison the next time around.
   5869         man.writeManifest() catch |err| {
   5870             log.warn("failed to write cache manifest when compiling '{s}': {s}", .{
   5871                 c_object.src.src_path, @errorName(err),
   5872             });
   5873         };
   5874     }
   5875 
   5876     const o_basename = try std.fmt.allocPrint(arena, "{s}{s}", .{ o_basename_noext, o_ext });
   5877 
   5878     c_object.status = .{
   5879         .success = .{
   5880             .object_path = .{
   5881                 .root_dir = comp.dirs.local_cache,
   5882                 .sub_path = try std.fs.path.join(gpa, &.{ "o", &digest, o_basename }),
   5883             },
   5884             .lock = man.toOwnedLock(),
   5885         },
   5886     };
   5887 
   5888     comp.queuePrelinkTasks(&.{.{ .load_object = c_object.status.success.object_path }});
   5889 }
   5890 
   5891 fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32_resource_prog_node: std.Progress.Node) !void {
   5892     if (!std.process.can_spawn) {
   5893         return comp.failWin32Resource(win32_resource, "{s} does not support spawning a child process", .{@tagName(builtin.os.tag)});
   5894     }
   5895 
   5896     const self_exe_path = comp.self_exe_path orelse
   5897         return comp.failWin32Resource(win32_resource, "unable to find self exe path", .{});
   5898 
   5899     const tracy_trace = trace(@src());
   5900     defer tracy_trace.end();
   5901 
   5902     const src_path = switch (win32_resource.src) {
   5903         .rc => |rc_src| rc_src.src_path,
   5904         .manifest => |src_path| src_path,
   5905     };
   5906     const src_basename = std.fs.path.basename(src_path);
   5907 
   5908     log.debug("updating win32 resource: {s}", .{src_path});
   5909 
   5910     var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
   5911     defer arena_allocator.deinit();
   5912     const arena = arena_allocator.allocator();
   5913 
   5914     if (win32_resource.clearStatus(comp.gpa)) {
   5915         // There was previous failure.
   5916         comp.mutex.lock();
   5917         defer comp.mutex.unlock();
   5918         // If the failure was OOM, there will not be an entry here, so we do
   5919         // not assert discard.
   5920         _ = comp.failed_win32_resources.swapRemove(win32_resource);
   5921     }
   5922 
   5923     const child_progress_node = win32_resource_prog_node.start(src_basename, 0);
   5924     defer child_progress_node.end();
   5925 
   5926     var man = comp.obtainWin32ResourceCacheManifest();
   5927     defer man.deinit();
   5928 
   5929     // For .manifest files, we ultimately just want to generate a .res with
   5930     // the XML data as a RT_MANIFEST resource. This means we can skip preprocessing,
   5931     // include paths, CLI options, etc.
   5932     if (win32_resource.src == .manifest) {
   5933         _ = try man.addFile(src_path, null);
   5934 
   5935         const rc_basename = try std.fmt.allocPrint(arena, "{s}.rc", .{src_basename});
   5936         const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{src_basename});
   5937 
   5938         const digest = if (try man.hit()) man.final() else blk: {
   5939             // The digest only depends on the .manifest file, so we can
   5940             // get the digest now and write the .res directly to the cache
   5941             const digest = man.final();
   5942 
   5943             const o_sub_path = try std.fs.path.join(arena, &.{ "o", &digest });
   5944             var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{});
   5945             defer o_dir.close();
   5946 
   5947             const in_rc_path = try comp.dirs.local_cache.join(comp.gpa, &.{
   5948                 o_sub_path, rc_basename,
   5949             });
   5950             const out_res_path = try comp.dirs.local_cache.join(comp.gpa, &.{
   5951                 o_sub_path, res_basename,
   5952             });
   5953 
   5954             // In .rc files, a " within a quoted string is escaped as ""
   5955             const fmtRcEscape = struct {
   5956                 fn formatRcEscape(bytes: []const u8, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
   5957                     _ = fmt;
   5958                     _ = options;
   5959                     for (bytes) |byte| switch (byte) {
   5960                         '"' => try writer.writeAll("\"\""),
   5961                         '\\' => try writer.writeAll("\\\\"),
   5962                         else => try writer.writeByte(byte),
   5963                     };
   5964                 }
   5965 
   5966                 pub fn fmtRcEscape(bytes: []const u8) std.fmt.Formatter(formatRcEscape) {
   5967                     return .{ .data = bytes };
   5968                 }
   5969             }.fmtRcEscape;
   5970 
   5971             // https://learn.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies-as-a-resource
   5972             // WinUser.h defines:
   5973             // CREATEPROCESS_MANIFEST_RESOURCE_ID to 1, which is the default
   5974             // ISOLATIONAWARE_MANIFEST_RESOURCE_ID to 2, which must be used for .dlls
   5975             const resource_id: u32 = if (comp.config.output_mode == .Lib and comp.config.link_mode == .dynamic) 2 else 1;
   5976 
   5977             // 24 is RT_MANIFEST
   5978             const resource_type = 24;
   5979 
   5980             const input = try std.fmt.allocPrint(arena, "{} {} \"{s}\"", .{ resource_id, resource_type, fmtRcEscape(src_path) });
   5981 
   5982             try o_dir.writeFile(.{ .sub_path = rc_basename, .data = input });
   5983 
   5984             var argv = std.ArrayList([]const u8).init(comp.gpa);
   5985             defer argv.deinit();
   5986 
   5987             try argv.appendSlice(&.{
   5988                 self_exe_path,
   5989                 "rc",
   5990                 "--zig-integration",
   5991                 "/:no-preprocess",
   5992                 "/x", // ignore INCLUDE environment variable
   5993                 "/c65001", // UTF-8 codepage
   5994                 "/:auto-includes",
   5995                 "none",
   5996             });
   5997             try argv.appendSlice(&.{ "--", in_rc_path, out_res_path });
   5998 
   5999             try spawnZigRc(comp, win32_resource, arena, argv.items, child_progress_node);
   6000 
   6001             break :blk digest;
   6002         };
   6003 
   6004         if (man.have_exclusive_lock) {
   6005             man.writeManifest() catch |err| {
   6006                 log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ src_path, @errorName(err) });
   6007             };
   6008         }
   6009 
   6010         win32_resource.status = .{
   6011             .success = .{
   6012                 .res_path = try comp.dirs.local_cache.join(comp.gpa, &[_][]const u8{
   6013                     "o", &digest, res_basename,
   6014                 }),
   6015                 .lock = man.toOwnedLock(),
   6016             },
   6017         };
   6018         return;
   6019     }
   6020 
   6021     // We now know that we're compiling an .rc file
   6022     const rc_src = win32_resource.src.rc;
   6023 
   6024     _ = try man.addFile(rc_src.src_path, null);
   6025     man.hash.addListOfBytes(rc_src.extra_flags);
   6026 
   6027     const rc_basename_noext = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len];
   6028 
   6029     const digest = if (try man.hit()) man.final() else blk: {
   6030         var zig_cache_tmp_dir = try comp.dirs.local_cache.handle.makeOpenPath("tmp", .{});
   6031         defer zig_cache_tmp_dir.close();
   6032 
   6033         const res_filename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext});
   6034 
   6035         // We can't know the digest until we do the compilation,
   6036         // so we need a temporary filename.
   6037         const out_res_path = try comp.tmpFilePath(arena, res_filename);
   6038 
   6039         var argv = std.ArrayList([]const u8).init(comp.gpa);
   6040         defer argv.deinit();
   6041 
   6042         const depfile_filename = try std.fmt.allocPrint(arena, "{s}.d.json", .{rc_basename_noext});
   6043         const out_dep_path = try comp.tmpFilePath(arena, depfile_filename);
   6044         try argv.appendSlice(&.{
   6045             self_exe_path,
   6046             "rc",
   6047             "--zig-integration",
   6048             "/:depfile",
   6049             out_dep_path,
   6050             "/:depfile-fmt",
   6051             "json",
   6052             "/x", // ignore INCLUDE environment variable
   6053             "/:auto-includes",
   6054             @tagName(comp.rc_includes),
   6055         });
   6056         // While these defines are not normally present when calling rc.exe directly,
   6057         // them being defined matches the behavior of how MSVC calls rc.exe which is the more
   6058         // relevant behavior in this case.
   6059         switch (rc_src.owner.optimize_mode) {
   6060             .Debug, .ReleaseSafe => {},
   6061             .ReleaseFast, .ReleaseSmall => try argv.append("-DNDEBUG"),
   6062         }
   6063         try argv.appendSlice(rc_src.extra_flags);
   6064         try argv.appendSlice(&.{ "--", rc_src.src_path, out_res_path });
   6065 
   6066         try spawnZigRc(comp, win32_resource, arena, argv.items, child_progress_node);
   6067 
   6068         // Read depfile and update cache manifest
   6069         {
   6070             const dep_basename = std.fs.path.basename(out_dep_path);
   6071             const dep_file_contents = try zig_cache_tmp_dir.readFileAlloc(arena, dep_basename, 50 * 1024 * 1024);
   6072             defer arena.free(dep_file_contents);
   6073 
   6074             const value = try std.json.parseFromSliceLeaky(std.json.Value, arena, dep_file_contents, .{});
   6075             if (value != .array) {
   6076                 return comp.failWin32Resource(win32_resource, "depfile from zig rc has unexpected format", .{});
   6077             }
   6078 
   6079             for (value.array.items) |element| {
   6080                 if (element != .string) {
   6081                     return comp.failWin32Resource(win32_resource, "depfile from zig rc has unexpected format", .{});
   6082                 }
   6083                 const dep_file_path = element.string;
   6084                 try man.addFilePost(dep_file_path);
   6085                 switch (comp.cache_use) {
   6086                     .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
   6087                         whole.cache_manifest_mutex.lock();
   6088                         defer whole.cache_manifest_mutex.unlock();
   6089                         try whole_cache_manifest.addFilePost(dep_file_path);
   6090                     },
   6091                     .incremental, .none => {},
   6092                 }
   6093             }
   6094         }
   6095 
   6096         // Rename into place.
   6097         const digest = man.final();
   6098         const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest });
   6099         var o_dir = try comp.dirs.local_cache.handle.makeOpenPath(o_sub_path, .{});
   6100         defer o_dir.close();
   6101         const tmp_basename = std.fs.path.basename(out_res_path);
   6102         try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, res_filename);
   6103         break :blk digest;
   6104     };
   6105 
   6106     if (man.have_exclusive_lock) {
   6107         // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
   6108         // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
   6109         // the contents were the same, we hit the cache but the manifest is dirty and we need to update
   6110         // it to prevent doing a full file content comparison the next time around.
   6111         man.writeManifest() catch |err| {
   6112             log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ rc_src.src_path, @errorName(err) });
   6113         };
   6114     }
   6115 
   6116     const res_basename = try std.fmt.allocPrint(arena, "{s}.res", .{rc_basename_noext});
   6117 
   6118     win32_resource.status = .{
   6119         .success = .{
   6120             .res_path = try comp.dirs.local_cache.join(comp.gpa, &[_][]const u8{
   6121                 "o", &digest, res_basename,
   6122             }),
   6123             .lock = man.toOwnedLock(),
   6124         },
   6125     };
   6126 }
   6127 
   6128 fn spawnZigRc(
   6129     comp: *Compilation,
   6130     win32_resource: *Win32Resource,
   6131     arena: Allocator,
   6132     argv: []const []const u8,
   6133     child_progress_node: std.Progress.Node,
   6134 ) !void {
   6135     var node_name: std.ArrayListUnmanaged(u8) = .empty;
   6136     defer node_name.deinit(arena);
   6137 
   6138     var child = std.process.Child.init(argv, arena);
   6139     child.stdin_behavior = .Ignore;
   6140     child.stdout_behavior = .Pipe;
   6141     child.stderr_behavior = .Pipe;
   6142     child.progress_node = child_progress_node;
   6143 
   6144     child.spawn() catch |err| {
   6145         return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) });
   6146     };
   6147 
   6148     var poller = std.io.poll(comp.gpa, enum { stdout }, .{
   6149         .stdout = child.stdout.?,
   6150     });
   6151     defer poller.deinit();
   6152 
   6153     const stdout = poller.fifo(.stdout);
   6154 
   6155     poll: while (true) {
   6156         while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) {
   6157             if (!(try poller.poll())) break :poll;
   6158         }
   6159         const header = stdout.reader().readStruct(std.zig.Server.Message.Header) catch unreachable;
   6160         while (stdout.readableLength() < header.bytes_len) {
   6161             if (!(try poller.poll())) break :poll;
   6162         }
   6163         const body = stdout.readableSliceOfLen(header.bytes_len);
   6164 
   6165         switch (header.tag) {
   6166             // We expect exactly one ErrorBundle, and if any error_bundle header is
   6167             // sent then it's a fatal error.
   6168             .error_bundle => {
   6169                 const EbHdr = std.zig.Server.Message.ErrorBundle;
   6170                 const eb_hdr = @as(*align(1) const EbHdr, @ptrCast(body));
   6171                 const extra_bytes =
   6172                     body[@sizeOf(EbHdr)..][0 .. @sizeOf(u32) * eb_hdr.extra_len];
   6173                 const string_bytes =
   6174                     body[@sizeOf(EbHdr) + extra_bytes.len ..][0..eb_hdr.string_bytes_len];
   6175                 const unaligned_extra = std.mem.bytesAsSlice(u32, extra_bytes);
   6176                 const extra_array = try comp.gpa.alloc(u32, unaligned_extra.len);
   6177                 @memcpy(extra_array, unaligned_extra);
   6178                 const error_bundle = std.zig.ErrorBundle{
   6179                     .string_bytes = try comp.gpa.dupe(u8, string_bytes),
   6180                     .extra = extra_array,
   6181                 };
   6182                 return comp.failWin32ResourceWithOwnedBundle(win32_resource, error_bundle);
   6183             },
   6184             else => {}, // ignore other messages
   6185         }
   6186 
   6187         stdout.discard(body.len);
   6188     }
   6189 
   6190     // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
   6191     const stderr_reader = child.stderr.?.reader();
   6192     const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
   6193 
   6194     const term = child.wait() catch |err| {
   6195         return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) });
   6196     };
   6197 
   6198     switch (term) {
   6199         .Exited => |code| {
   6200             if (code != 0) {
   6201                 log.err("zig rc failed with stderr:\n{s}", .{stderr});
   6202                 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
   6203             }
   6204         },
   6205         else => {
   6206             log.err("zig rc terminated with stderr:\n{s}", .{stderr});
   6207             return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
   6208         },
   6209     }
   6210 }
   6211 
   6212 pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
   6213     const s = std.fs.path.sep_str;
   6214     const rand_int = std.crypto.random.int(u64);
   6215     if (comp.dirs.local_cache.path) |p| {
   6216         return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
   6217     } else {
   6218         return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
   6219     }
   6220 }
   6221 
   6222 pub fn addTranslateCCArgs(
   6223     comp: *Compilation,
   6224     arena: Allocator,
   6225     argv: *std.ArrayList([]const u8),
   6226     ext: FileExt,
   6227     out_dep_path: ?[]const u8,
   6228     owner_mod: *Package.Module,
   6229 ) !void {
   6230     try argv.appendSlice(&.{ "-x", "c" });
   6231     try comp.addCCArgs(arena, argv, ext, out_dep_path, owner_mod);
   6232     // This gives us access to preprocessing entities, presumably at the cost of performance.
   6233     try argv.appendSlice(&.{ "-Xclang", "-detailed-preprocessing-record" });
   6234 }
   6235 
   6236 /// Add common C compiler args between translate-c and C object compilation.
   6237 pub fn addCCArgs(
   6238     comp: *const Compilation,
   6239     arena: Allocator,
   6240     argv: *std.ArrayList([]const u8),
   6241     ext: FileExt,
   6242     out_dep_path: ?[]const u8,
   6243     mod: *Package.Module,
   6244 ) !void {
   6245     const target = mod.resolved_target.result;
   6246 
   6247     // As of Clang 16.x, it will by default read extra flags from /etc/clang.
   6248     // I'm sure the person who implemented this means well, but they have a lot
   6249     // to learn about abstractions and where the appropriate boundaries between
   6250     // them are. The road to hell is paved with good intentions. Fortunately it
   6251     // can be disabled.
   6252     try argv.append("--no-default-config");
   6253 
   6254     // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode
   6255     // we want Clang to infer it, and in normal mode we always want it off, which will be true since
   6256     // clang will detect stderr as a pipe rather than a terminal.
   6257     if (!comp.clang_passthrough_mode and ext.clangSupportsDiagnostics()) {
   6258         // Make stderr more easily parseable.
   6259         try argv.append("-fno-caret-diagnostics");
   6260     }
   6261 
   6262     // We never want clang to invoke the system assembler for anything. So we would want
   6263     // this option always enabled. However, it only matters for some targets. To avoid
   6264     // "unused parameter" warnings, and to keep CLI spam to a minimum, we only put this
   6265     // flag on the command line if it is necessary.
   6266     if (target_util.clangMightShellOutForAssembly(target)) {
   6267         try argv.append("-integrated-as");
   6268     }
   6269 
   6270     const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target);
   6271     try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple });
   6272 
   6273     switch (target.os.tag) {
   6274         .ios, .macos, .tvos, .watchos => |os| {
   6275             try argv.ensureUnusedCapacity(2);
   6276             // Pass the proper -m<os>-version-min argument for darwin.
   6277             const ver = target.os.version_range.semver.min;
   6278             argv.appendAssumeCapacity(try std.fmt.allocPrint(arena, "-m{s}{s}-version-min={d}.{d}.{d}", .{
   6279                 @tagName(os),
   6280                 switch (target.abi) {
   6281                     .simulator => "-simulator",
   6282                     else => "",
   6283                 },
   6284                 ver.major,
   6285                 ver.minor,
   6286                 ver.patch,
   6287             }));
   6288             // This avoids a warning that sometimes occurs when
   6289             // providing both a -target argument that contains a
   6290             // version as well as the -mmacosx-version-min argument.
   6291             // Zig provides the correct value in both places, so it
   6292             // doesn't matter which one gets overridden.
   6293             argv.appendAssumeCapacity("-Wno-overriding-option");
   6294         },
   6295         else => {},
   6296     }
   6297 
   6298     if (target.cpu.arch.isArm()) {
   6299         try argv.append(if (target.cpu.arch.isThumb()) "-mthumb" else "-mno-thumb");
   6300     }
   6301 
   6302     if (target_util.llvmMachineAbi(target)) |mabi| {
   6303         // Clang's integrated Arm assembler doesn't support `-mabi` yet...
   6304         // Clang's FreeBSD driver doesn't support `-mabi` on PPC64 (ELFv2 is used anyway).
   6305         if (!(target.cpu.arch.isArm() and (ext == .assembly or ext == .assembly_with_cpp)) and
   6306             !(target.cpu.arch.isPowerPC64() and target.os.tag == .freebsd))
   6307         {
   6308             try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi}));
   6309         }
   6310     }
   6311 
   6312     // We might want to support -mfloat-abi=softfp for Arm and CSKY here in the future.
   6313     if (target_util.clangSupportsFloatAbiArg(target)) {
   6314         const fabi = @tagName(target.abi.float());
   6315 
   6316         try argv.append(switch (target.cpu.arch) {
   6317             // For whatever reason, Clang doesn't support `-mfloat-abi` for s390x.
   6318             .s390x => try std.fmt.allocPrint(arena, "-m{s}-float", .{fabi}),
   6319             else => try std.fmt.allocPrint(arena, "-mfloat-abi={s}", .{fabi}),
   6320         });
   6321     }
   6322 
   6323     if (target_util.supports_fpic(target)) {
   6324         // PIE needs to go before PIC because Clang interprets `-fno-PIE` to imply `-fno-PIC`, which
   6325         // we don't necessarily want.
   6326         try argv.append(if (comp.config.pie) "-fPIE" else "-fno-PIE");
   6327         try argv.append(if (mod.pic) "-fPIC" else "-fno-PIC");
   6328     }
   6329 
   6330     if (comp.mingw_unicode_entry_point) {
   6331         try argv.append("-municode");
   6332     }
   6333 
   6334     if (mod.code_model != .default) {
   6335         try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mod.code_model)}));
   6336     }
   6337 
   6338     try argv.ensureUnusedCapacity(2);
   6339     switch (comp.config.debug_format) {
   6340         .strip => {},
   6341         .code_view => {
   6342             // -g is required here because -gcodeview doesn't trigger debug info
   6343             // generation, it only changes the type of information generated.
   6344             argv.appendSliceAssumeCapacity(&.{ "-g", "-gcodeview" });
   6345         },
   6346         .dwarf => |f| {
   6347             argv.appendAssumeCapacity("-gdwarf-4");
   6348             switch (f) {
   6349                 .@"32" => argv.appendAssumeCapacity("-gdwarf32"),
   6350                 .@"64" => argv.appendAssumeCapacity("-gdwarf64"),
   6351             }
   6352         },
   6353     }
   6354 
   6355     switch (comp.config.lto) {
   6356         .none => try argv.append("-fno-lto"),
   6357         .full => try argv.append("-flto=full"),
   6358         .thin => try argv.append("-flto=thin"),
   6359     }
   6360 
   6361     // This only works for preprocessed files. Guarded by `FileExt.clangSupportsDepFile`.
   6362     if (out_dep_path) |p| {
   6363         try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p });
   6364     }
   6365 
   6366     // Non-preprocessed assembly files don't support these flags.
   6367     if (ext != .assembly) {
   6368         try argv.append(if (target.os.tag == .freestanding) "-ffreestanding" else "-fhosted");
   6369 
   6370         if (target_util.clangSupportsNoImplicitFloatArg(target) and target.abi.float() == .soft) {
   6371             try argv.append("-mno-implicit-float");
   6372         }
   6373 
   6374         if (target_util.hasRedZone(target)) {
   6375             try argv.append(if (mod.red_zone) "-mred-zone" else "-mno-red-zone");
   6376         }
   6377 
   6378         try argv.append(if (mod.omit_frame_pointer) "-fomit-frame-pointer" else "-fno-omit-frame-pointer");
   6379 
   6380         const ssp_buf_size = mod.stack_protector;
   6381         if (ssp_buf_size != 0) {
   6382             try argv.appendSlice(&[_][]const u8{
   6383                 "-fstack-protector-strong",
   6384                 "--param",
   6385                 try std.fmt.allocPrint(arena, "ssp-buffer-size={d}", .{ssp_buf_size}),
   6386             });
   6387         } else {
   6388             try argv.append("-fno-stack-protector");
   6389         }
   6390 
   6391         try argv.append(if (mod.no_builtin) "-fno-builtin" else "-fbuiltin");
   6392 
   6393         try argv.append(if (comp.function_sections) "-ffunction-sections" else "-fno-function-sections");
   6394         try argv.append(if (comp.data_sections) "-fdata-sections" else "-fno-data-sections");
   6395 
   6396         switch (mod.unwind_tables) {
   6397             .none => {
   6398                 try argv.append("-fno-unwind-tables");
   6399                 try argv.append("-fno-asynchronous-unwind-tables");
   6400             },
   6401             .sync => {
   6402                 // Need to override Clang's convoluted default logic.
   6403                 try argv.append("-fno-asynchronous-unwind-tables");
   6404                 try argv.append("-funwind-tables");
   6405             },
   6406             .@"async" => try argv.append("-fasynchronous-unwind-tables"),
   6407         }
   6408 
   6409         try argv.append("-nostdinc");
   6410 
   6411         if (ext == .cpp or ext == .hpp) {
   6412             try argv.append("-nostdinc++");
   6413         }
   6414 
   6415         // LLVM IR files don't support these flags.
   6416         if (ext != .ll and ext != .bc) {
   6417             switch (mod.optimize_mode) {
   6418                 .Debug => {},
   6419                 .ReleaseSafe => {
   6420                     try argv.append("-D_FORTIFY_SOURCE=2");
   6421                 },
   6422                 .ReleaseFast, .ReleaseSmall => {
   6423                     try argv.append("-DNDEBUG");
   6424                 },
   6425             }
   6426 
   6427             if (comp.config.link_libc) {
   6428                 if (target.isGnuLibC()) {
   6429                     const target_version = target.os.versionRange().gnuLibCVersion().?;
   6430                     const glibc_minor_define = try std.fmt.allocPrint(arena, "-D__GLIBC_MINOR__={d}", .{
   6431                         target_version.minor,
   6432                     });
   6433                     try argv.append(glibc_minor_define);
   6434                 } else if (target.isMinGW()) {
   6435                     try argv.append("-D__MSVCRT_VERSION__=0xE00"); // use ucrt
   6436 
   6437                     const minver: u16 = @truncate(@intFromEnum(target.os.versionRange().windows.min) >> 16);
   6438                     try argv.append(
   6439                         try std.fmt.allocPrint(arena, "-D_WIN32_WINNT=0x{x:0>4}", .{minver}),
   6440                     );
   6441                 } else if (target.isFreeBSDLibC()) {
   6442                     // https://docs.freebsd.org/en/books/porters-handbook/versions
   6443                     const min_ver = target.os.version_range.semver.min;
   6444                     try argv.append(try std.fmt.allocPrint(arena, "-D__FreeBSD_version={d}", .{
   6445                         // We don't currently respect the minor and patch components. This wouldn't be particularly
   6446                         // helpful because our abilists file only tracks major FreeBSD releases, so the link-time stub
   6447                         // symbols would be inconsistent with header declarations.
   6448                         min_ver.major * 100_000,
   6449                     }));
   6450                 } else if (target.isNetBSDLibC()) {
   6451                     const min_ver = target.os.version_range.semver.min;
   6452                     try argv.append(try std.fmt.allocPrint(arena, "-D__NetBSD_Version__={d}", .{
   6453                         // We don't currently respect the patch component. This wouldn't be particularly helpful because
   6454                         // our abilists file only tracks major and minor NetBSD releases, so the link-time stub symbols
   6455                         // would be inconsistent with header declarations.
   6456                         (min_ver.major * 100_000_000) + (min_ver.minor * 1_000_000),
   6457                     }));
   6458                 }
   6459             }
   6460 
   6461             if (comp.config.link_libcpp) {
   6462                 try argv.append("-isystem");
   6463                 try argv.append(try std.fs.path.join(arena, &[_][]const u8{
   6464                     comp.dirs.zig_lib.path.?, "libcxx", "include",
   6465                 }));
   6466 
   6467                 try argv.append("-isystem");
   6468                 try argv.append(try std.fs.path.join(arena, &[_][]const u8{
   6469                     comp.dirs.zig_lib.path.?, "libcxxabi", "include",
   6470                 }));
   6471 
   6472                 try libcxx.addCxxArgs(comp, arena, argv);
   6473             }
   6474 
   6475             // According to Rich Felker libc headers are supposed to go before C language headers.
   6476             // However as noted by @dimenus, appending libc headers before compiler headers breaks
   6477             // intrinsics and other compiler specific items.
   6478             try argv.append("-isystem");
   6479             try argv.append(try std.fs.path.join(arena, &.{ comp.dirs.zig_lib.path.?, "include" }));
   6480 
   6481             try argv.ensureUnusedCapacity(comp.libc_include_dir_list.len * 2);
   6482             for (comp.libc_include_dir_list) |include_dir| {
   6483                 try argv.append("-isystem");
   6484                 try argv.append(include_dir);
   6485             }
   6486 
   6487             if (mod.resolved_target.is_native_os and mod.resolved_target.is_native_abi) {
   6488                 try argv.ensureUnusedCapacity(comp.native_system_include_paths.len * 2);
   6489                 for (comp.native_system_include_paths) |include_path| {
   6490                     argv.appendAssumeCapacity("-isystem");
   6491                     argv.appendAssumeCapacity(include_path);
   6492                 }
   6493             }
   6494 
   6495             if (comp.config.link_libunwind) {
   6496                 try argv.append("-isystem");
   6497                 try argv.append(try std.fs.path.join(arena, &[_][]const u8{
   6498                     comp.dirs.zig_lib.path.?, "libunwind", "include",
   6499                 }));
   6500             }
   6501 
   6502             try argv.ensureUnusedCapacity(comp.libc_framework_dir_list.len * 2);
   6503             for (comp.libc_framework_dir_list) |framework_dir| {
   6504                 try argv.appendSlice(&.{ "-iframework", framework_dir });
   6505             }
   6506 
   6507             try argv.ensureUnusedCapacity(comp.framework_dirs.len * 2);
   6508             for (comp.framework_dirs) |framework_dir| {
   6509                 try argv.appendSlice(&.{ "-F", framework_dir });
   6510             }
   6511         }
   6512     }
   6513 
   6514     // Only C-family files support these flags.
   6515     switch (ext) {
   6516         .c,
   6517         .h,
   6518         .cpp,
   6519         .hpp,
   6520         .m,
   6521         .hm,
   6522         .mm,
   6523         .hmm,
   6524         => {
   6525             try argv.append("-fno-spell-checking");
   6526 
   6527             if (target.os.tag == .windows and target.abi.isGnu()) {
   6528                 // windows.h has files such as pshpack1.h which do #pragma packing,
   6529                 // triggering a clang warning. So for this target, we disable this warning.
   6530                 try argv.append("-Wno-pragma-pack");
   6531             }
   6532 
   6533             if (mod.optimize_mode != .Debug) {
   6534                 try argv.append("-Werror=date-time");
   6535             }
   6536         },
   6537         else => {},
   6538     }
   6539 
   6540     // Only assembly files support these flags.
   6541     switch (ext) {
   6542         .assembly,
   6543         .assembly_with_cpp,
   6544         => {
   6545             // The Clang assembler does not accept the list of CPU features like the
   6546             // compiler frontend does. Therefore we must hard-code the -m flags for
   6547             // all CPU features here.
   6548             switch (target.cpu.arch) {
   6549                 .riscv32, .riscv64 => {
   6550                     const RvArchFeat = struct { char: u8, feat: std.Target.riscv.Feature };
   6551                     const letters = [_]RvArchFeat{
   6552                         .{ .char = 'm', .feat = .m },
   6553                         .{ .char = 'a', .feat = .a },
   6554                         .{ .char = 'f', .feat = .f },
   6555                         .{ .char = 'd', .feat = .d },
   6556                         .{ .char = 'c', .feat = .c },
   6557                     };
   6558                     const prefix: []const u8 = if (target.cpu.arch == .riscv64) "rv64" else "rv32";
   6559                     const prefix_len = 4;
   6560                     assert(prefix.len == prefix_len);
   6561                     var march_buf: [prefix_len + letters.len + 1]u8 = undefined;
   6562                     var march_index: usize = prefix_len;
   6563                     @memcpy(march_buf[0..prefix.len], prefix);
   6564 
   6565                     if (target.cpu.has(.riscv, .e)) {
   6566                         march_buf[march_index] = 'e';
   6567                     } else {
   6568                         march_buf[march_index] = 'i';
   6569                     }
   6570                     march_index += 1;
   6571 
   6572                     for (letters) |letter| {
   6573                         if (target.cpu.has(.riscv, letter.feat)) {
   6574                             march_buf[march_index] = letter.char;
   6575                             march_index += 1;
   6576                         }
   6577                     }
   6578 
   6579                     const march_arg = try std.fmt.allocPrint(arena, "-march={s}", .{
   6580                         march_buf[0..march_index],
   6581                     });
   6582                     try argv.append(march_arg);
   6583 
   6584                     if (target.cpu.has(.riscv, .relax)) {
   6585                         try argv.append("-mrelax");
   6586                     } else {
   6587                         try argv.append("-mno-relax");
   6588                     }
   6589                     if (target.cpu.has(.riscv, .save_restore)) {
   6590                         try argv.append("-msave-restore");
   6591                     } else {
   6592                         try argv.append("-mno-save-restore");
   6593                     }
   6594                 },
   6595                 .mips, .mipsel, .mips64, .mips64el => {
   6596                     if (target.cpu.model.llvm_name) |llvm_name| {
   6597                         try argv.append(try std.fmt.allocPrint(arena, "-march={s}", .{llvm_name}));
   6598                     }
   6599                 },
   6600                 else => {
   6601                     // TODO
   6602                 },
   6603             }
   6604 
   6605             if (target_util.clangAssemblerSupportsMcpuArg(target)) {
   6606                 if (target.cpu.model.llvm_name) |llvm_name| {
   6607                     try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{llvm_name}));
   6608                 }
   6609             }
   6610         },
   6611         else => {},
   6612     }
   6613 
   6614     // Only compiled files support these flags.
   6615     switch (ext) {
   6616         .c,
   6617         .h,
   6618         .cpp,
   6619         .hpp,
   6620         .m,
   6621         .hm,
   6622         .mm,
   6623         .hmm,
   6624         .ll,
   6625         .bc,
   6626         => {
   6627             if (target_util.clangSupportsTargetCpuArg(target)) {
   6628                 if (target.cpu.model.llvm_name) |llvm_name| {
   6629                     try argv.appendSlice(&[_][]const u8{
   6630                         "-Xclang", "-target-cpu", "-Xclang", llvm_name,
   6631                     });
   6632                 }
   6633             }
   6634 
   6635             // It would be really nice if there was a more compact way to communicate this info to Clang.
   6636             const all_features_list = target.cpu.arch.allFeaturesList();
   6637             try argv.ensureUnusedCapacity(all_features_list.len * 4);
   6638             for (all_features_list, 0..) |feature, index_usize| {
   6639                 const index = @as(std.Target.Cpu.Feature.Set.Index, @intCast(index_usize));
   6640                 const is_enabled = target.cpu.features.isEnabled(index);
   6641 
   6642                 if (feature.llvm_name) |llvm_name| {
   6643                     // We communicate float ABI to Clang through the dedicated options.
   6644                     if (std.mem.startsWith(u8, llvm_name, "soft-float") or
   6645                         std.mem.startsWith(u8, llvm_name, "hard-float"))
   6646                         continue;
   6647 
   6648                     // Ignore these until we figure out how to handle the concept of omitting features.
   6649                     // See https://github.com/ziglang/zig/issues/23539
   6650                     if (target_util.isDynamicAMDGCNFeature(target, feature)) continue;
   6651 
   6652                     argv.appendSliceAssumeCapacity(&[_][]const u8{ "-Xclang", "-target-feature", "-Xclang" });
   6653                     const plus_or_minus = "-+"[@intFromBool(is_enabled)];
   6654                     const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name });
   6655                     argv.appendAssumeCapacity(arg);
   6656                 }
   6657             }
   6658 
   6659             {
   6660                 var san_arg: std.ArrayListUnmanaged(u8) = .empty;
   6661                 const prefix = "-fsanitize=";
   6662                 if (mod.sanitize_c != .off) {
   6663                     if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
   6664                     try san_arg.appendSlice(arena, "undefined,");
   6665                 }
   6666                 if (mod.sanitize_thread) {
   6667                     if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
   6668                     try san_arg.appendSlice(arena, "thread,");
   6669                 }
   6670                 if (mod.fuzz) {
   6671                     if (san_arg.items.len == 0) try san_arg.appendSlice(arena, prefix);
   6672                     try san_arg.appendSlice(arena, "fuzzer-no-link,");
   6673                 }
   6674                 // Chop off the trailing comma and append to argv.
   6675                 if (san_arg.pop()) |_| {
   6676                     try argv.append(san_arg.items);
   6677 
   6678                     switch (mod.sanitize_c) {
   6679                         .off => {},
   6680                         .trap => {
   6681                             try argv.append("-fsanitize-trap=undefined");
   6682                         },
   6683                         .full => {
   6684                             // This check requires implementing the Itanium C++ ABI.
   6685                             // We would make it `-fsanitize-trap=vptr`, however this check requires
   6686                             // a full runtime due to the type hashing involved.
   6687                             try argv.append("-fno-sanitize=vptr");
   6688 
   6689                             // It is very common, and well-defined, for a pointer on one side of a C ABI
   6690                             // to have a different but compatible element type. Examples include:
   6691                             // `char*` vs `uint8_t*` on a system with 8-bit bytes
   6692                             // `const char*` vs `char*`
   6693                             // `char*` vs `unsigned char*`
   6694                             // Without this flag, Clang would invoke UBSAN when such an extern
   6695                             // function was called.
   6696                             try argv.append("-fno-sanitize=function");
   6697 
   6698                             // This is necessary because, by default, Clang instructs LLVM to embed
   6699                             // a COFF link dependency on `libclang_rt.ubsan_standalone.a` when the
   6700                             // UBSan runtime is used.
   6701                             if (target.os.tag == .windows) {
   6702                                 try argv.append("-fno-rtlib-defaultlib");
   6703                             }
   6704                         },
   6705                     }
   6706                 }
   6707 
   6708                 if (comp.config.san_cov_trace_pc_guard) {
   6709                     try argv.append("-fsanitize-coverage=trace-pc-guard");
   6710                 }
   6711             }
   6712 
   6713             switch (mod.optimize_mode) {
   6714                 .Debug => {
   6715                     // Clang has -Og for compatibility with GCC, but currently it is just equivalent
   6716                     // to -O1. Besides potentially impairing debugging, -O1/-Og significantly
   6717                     // increases compile times.
   6718                     try argv.append("-O0");
   6719                 },
   6720                 .ReleaseSafe => {
   6721                     // See the comment in the BuildModeFastRelease case for why we pass -O2 rather
   6722                     // than -O3 here.
   6723                     try argv.append("-O2");
   6724                 },
   6725                 .ReleaseFast => {
   6726                     // Here we pass -O2 rather than -O3 because, although we do the equivalent of
   6727                     // -O3 in Zig code, the justification for the difference here is that Zig
   6728                     // has better detection and prevention of undefined behavior, so -O3 is safer for
   6729                     // Zig code than it is for C code. Also, C programmers are used to their code
   6730                     // running in -O2 and thus the -O3 path has been tested less.
   6731                     try argv.append("-O2");
   6732                 },
   6733                 .ReleaseSmall => {
   6734                     try argv.append("-Os");
   6735                 },
   6736             }
   6737         },
   6738         else => {},
   6739     }
   6740 
   6741     try argv.appendSlice(comp.global_cc_argv);
   6742     try argv.appendSlice(mod.cc_argv);
   6743 }
   6744 
   6745 fn failCObj(
   6746     comp: *Compilation,
   6747     c_object: *CObject,
   6748     comptime format: []const u8,
   6749     args: anytype,
   6750 ) SemaError {
   6751     @branchHint(.cold);
   6752     const diag_bundle = blk: {
   6753         const diag_bundle = try comp.gpa.create(CObject.Diag.Bundle);
   6754         diag_bundle.* = .{};
   6755         errdefer diag_bundle.destroy(comp.gpa);
   6756 
   6757         try diag_bundle.file_names.ensureTotalCapacity(comp.gpa, 1);
   6758         diag_bundle.file_names.putAssumeCapacity(1, try comp.gpa.dupe(u8, c_object.src.src_path));
   6759 
   6760         diag_bundle.diags = try comp.gpa.alloc(CObject.Diag, 1);
   6761         diag_bundle.diags[0] = .{};
   6762         diag_bundle.diags[0].level = 3;
   6763         diag_bundle.diags[0].msg = try std.fmt.allocPrint(comp.gpa, format, args);
   6764         diag_bundle.diags[0].src_loc.file = 1;
   6765         break :blk diag_bundle;
   6766     };
   6767     return comp.failCObjWithOwnedDiagBundle(c_object, diag_bundle);
   6768 }
   6769 
   6770 fn failCObjWithOwnedDiagBundle(
   6771     comp: *Compilation,
   6772     c_object: *CObject,
   6773     diag_bundle: *CObject.Diag.Bundle,
   6774 ) SemaError {
   6775     @branchHint(.cold);
   6776     assert(diag_bundle.diags.len > 0);
   6777     {
   6778         comp.mutex.lock();
   6779         defer comp.mutex.unlock();
   6780         {
   6781             errdefer diag_bundle.destroy(comp.gpa);
   6782             try comp.failed_c_objects.ensureUnusedCapacity(comp.gpa, 1);
   6783         }
   6784         comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, diag_bundle);
   6785     }
   6786     c_object.status = .failure;
   6787     return error.AnalysisFail;
   6788 }
   6789 
   6790 fn failWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, comptime format: []const u8, args: anytype) SemaError {
   6791     @branchHint(.cold);
   6792     var bundle: ErrorBundle.Wip = undefined;
   6793     try bundle.init(comp.gpa);
   6794     errdefer bundle.deinit();
   6795     try bundle.addRootErrorMessage(.{
   6796         .msg = try bundle.printString(format, args),
   6797         .src_loc = try bundle.addSourceLocation(.{
   6798             .src_path = try bundle.addString(switch (win32_resource.src) {
   6799                 .rc => |rc_src| rc_src.src_path,
   6800                 .manifest => |manifest_src| manifest_src,
   6801             }),
   6802             .line = 0,
   6803             .column = 0,
   6804             .span_start = 0,
   6805             .span_main = 0,
   6806             .span_end = 0,
   6807         }),
   6808     });
   6809     const finished_bundle = try bundle.toOwnedBundle("");
   6810     return comp.failWin32ResourceWithOwnedBundle(win32_resource, finished_bundle);
   6811 }
   6812 
   6813 fn failWin32ResourceWithOwnedBundle(
   6814     comp: *Compilation,
   6815     win32_resource: *Win32Resource,
   6816     err_bundle: ErrorBundle,
   6817 ) SemaError {
   6818     @branchHint(.cold);
   6819     {
   6820         comp.mutex.lock();
   6821         defer comp.mutex.unlock();
   6822         try comp.failed_win32_resources.putNoClobber(comp.gpa, win32_resource, err_bundle);
   6823     }
   6824     win32_resource.status = .failure;
   6825     return error.AnalysisFail;
   6826 }
   6827 
   6828 pub const FileExt = enum {
   6829     c,
   6830     cpp,
   6831     h,
   6832     hpp,
   6833     hm,
   6834     hmm,
   6835     m,
   6836     mm,
   6837     ll,
   6838     bc,
   6839     assembly,
   6840     assembly_with_cpp,
   6841     shared_library,
   6842     object,
   6843     static_library,
   6844     zig,
   6845     def,
   6846     rc,
   6847     res,
   6848     manifest,
   6849     unknown,
   6850 
   6851     pub fn clangNeedsLanguageOverride(ext: FileExt) bool {
   6852         return switch (ext) {
   6853             .h,
   6854             .hpp,
   6855             .hm,
   6856             .hmm,
   6857             => true,
   6858 
   6859             .c,
   6860             .cpp,
   6861             .m,
   6862             .mm,
   6863             .ll,
   6864             .bc,
   6865             .assembly,
   6866             .assembly_with_cpp,
   6867             .shared_library,
   6868             .object,
   6869             .static_library,
   6870             .zig,
   6871             .def,
   6872             .rc,
   6873             .res,
   6874             .manifest,
   6875             .unknown,
   6876             => false,
   6877         };
   6878     }
   6879 
   6880     pub fn clangSupportsDiagnostics(ext: FileExt) bool {
   6881         return switch (ext) {
   6882             .c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm, .ll, .bc => true,
   6883 
   6884             .assembly,
   6885             .assembly_with_cpp,
   6886             .shared_library,
   6887             .object,
   6888             .static_library,
   6889             .zig,
   6890             .def,
   6891             .rc,
   6892             .res,
   6893             .manifest,
   6894             .unknown,
   6895             => false,
   6896         };
   6897     }
   6898 
   6899     pub fn clangSupportsDepFile(ext: FileExt) bool {
   6900         return switch (ext) {
   6901             .assembly_with_cpp, .c, .cpp, .h, .hpp, .hm, .hmm, .m, .mm => true,
   6902 
   6903             .ll,
   6904             .bc,
   6905             .assembly,
   6906             .shared_library,
   6907             .object,
   6908             .static_library,
   6909             .zig,
   6910             .def,
   6911             .rc,
   6912             .res,
   6913             .manifest,
   6914             .unknown,
   6915             => false,
   6916         };
   6917     }
   6918 
   6919     pub fn canonicalName(ext: FileExt, target: Target) [:0]const u8 {
   6920         return switch (ext) {
   6921             .c => ".c",
   6922             .cpp => ".cpp",
   6923             .h => ".h",
   6924             .hpp => ".hpp",
   6925             .hm => ".hm",
   6926             .hmm => ".hmm",
   6927             .m => ".m",
   6928             .mm => ".mm",
   6929             .ll => ".ll",
   6930             .bc => ".bc",
   6931             .assembly => ".s",
   6932             .assembly_with_cpp => ".S",
   6933             .shared_library => target.dynamicLibSuffix(),
   6934             .object => target.ofmt.fileExt(target.cpu.arch),
   6935             .static_library => target.staticLibSuffix(),
   6936             .zig => ".zig",
   6937             .def => ".def",
   6938             .rc => ".rc",
   6939             .res => ".res",
   6940             .manifest => ".manifest",
   6941             .unknown => "",
   6942         };
   6943     }
   6944 };
   6945 
   6946 pub fn hasObjectExt(filename: []const u8) bool {
   6947     return mem.endsWith(u8, filename, ".o") or
   6948         mem.endsWith(u8, filename, ".lo") or
   6949         mem.endsWith(u8, filename, ".obj") or
   6950         mem.endsWith(u8, filename, ".rmeta");
   6951 }
   6952 
   6953 pub fn hasStaticLibraryExt(filename: []const u8) bool {
   6954     return mem.endsWith(u8, filename, ".a") or
   6955         mem.endsWith(u8, filename, ".lib") or
   6956         mem.endsWith(u8, filename, ".rlib");
   6957 }
   6958 
   6959 pub fn hasCExt(filename: []const u8) bool {
   6960     return mem.endsWith(u8, filename, ".c");
   6961 }
   6962 
   6963 pub fn hasCHExt(filename: []const u8) bool {
   6964     return mem.endsWith(u8, filename, ".h");
   6965 }
   6966 
   6967 pub fn hasCppExt(filename: []const u8) bool {
   6968     return mem.endsWith(u8, filename, ".C") or
   6969         mem.endsWith(u8, filename, ".cc") or
   6970         mem.endsWith(u8, filename, ".cp") or
   6971         mem.endsWith(u8, filename, ".CPP") or
   6972         mem.endsWith(u8, filename, ".cpp") or
   6973         mem.endsWith(u8, filename, ".cxx") or
   6974         mem.endsWith(u8, filename, ".c++");
   6975 }
   6976 
   6977 pub fn hasCppHExt(filename: []const u8) bool {
   6978     return mem.endsWith(u8, filename, ".hh") or
   6979         mem.endsWith(u8, filename, ".hpp") or
   6980         mem.endsWith(u8, filename, ".hxx");
   6981 }
   6982 
   6983 pub fn hasObjCExt(filename: []const u8) bool {
   6984     return mem.endsWith(u8, filename, ".m");
   6985 }
   6986 
   6987 pub fn hasObjCHExt(filename: []const u8) bool {
   6988     return mem.endsWith(u8, filename, ".hm");
   6989 }
   6990 
   6991 pub fn hasObjCppExt(filename: []const u8) bool {
   6992     return mem.endsWith(u8, filename, ".M") or
   6993         mem.endsWith(u8, filename, ".mm");
   6994 }
   6995 
   6996 pub fn hasObjCppHExt(filename: []const u8) bool {
   6997     return mem.endsWith(u8, filename, ".hmm");
   6998 }
   6999 
   7000 pub fn hasSharedLibraryExt(filename: []const u8) bool {
   7001     if (mem.endsWith(u8, filename, ".so") or
   7002         mem.endsWith(u8, filename, ".dll") or
   7003         mem.endsWith(u8, filename, ".dylib") or
   7004         mem.endsWith(u8, filename, ".tbd"))
   7005     {
   7006         return true;
   7007     }
   7008     // Look for .so.X, .so.X.Y, .so.X.Y.Z
   7009     var it = mem.splitScalar(u8, filename, '.');
   7010     _ = it.first();
   7011     var so_txt = it.next() orelse return false;
   7012     while (!mem.eql(u8, so_txt, "so")) {
   7013         so_txt = it.next() orelse return false;
   7014     }
   7015     const n1 = it.next() orelse return false;
   7016     const n2 = it.next();
   7017     const n3 = it.next();
   7018 
   7019     _ = std.fmt.parseInt(u32, n1, 10) catch return false;
   7020     if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
   7021     if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return false;
   7022     if (it.next() != null) return false;
   7023 
   7024     return true;
   7025 }
   7026 
   7027 pub fn classifyFileExt(filename: []const u8) FileExt {
   7028     if (hasCExt(filename)) {
   7029         return .c;
   7030     } else if (hasCHExt(filename)) {
   7031         return .h;
   7032     } else if (hasCppExt(filename)) {
   7033         return .cpp;
   7034     } else if (hasCppHExt(filename)) {
   7035         return .hpp;
   7036     } else if (hasObjCExt(filename)) {
   7037         return .m;
   7038     } else if (hasObjCHExt(filename)) {
   7039         return .hm;
   7040     } else if (hasObjCppExt(filename)) {
   7041         return .mm;
   7042     } else if (hasObjCppHExt(filename)) {
   7043         return .hmm;
   7044     } else if (mem.endsWith(u8, filename, ".ll")) {
   7045         return .ll;
   7046     } else if (mem.endsWith(u8, filename, ".bc")) {
   7047         return .bc;
   7048     } else if (mem.endsWith(u8, filename, ".s")) {
   7049         return .assembly;
   7050     } else if (mem.endsWith(u8, filename, ".S")) {
   7051         return .assembly_with_cpp;
   7052     } else if (mem.endsWith(u8, filename, ".zig")) {
   7053         return .zig;
   7054     } else if (hasSharedLibraryExt(filename)) {
   7055         return .shared_library;
   7056     } else if (hasStaticLibraryExt(filename)) {
   7057         return .static_library;
   7058     } else if (hasObjectExt(filename)) {
   7059         return .object;
   7060     } else if (mem.endsWith(u8, filename, ".def")) {
   7061         return .def;
   7062     } else if (std.ascii.endsWithIgnoreCase(filename, ".rc")) {
   7063         return .rc;
   7064     } else if (std.ascii.endsWithIgnoreCase(filename, ".res")) {
   7065         return .res;
   7066     } else if (std.ascii.endsWithIgnoreCase(filename, ".manifest")) {
   7067         return .manifest;
   7068     } else {
   7069         return .unknown;
   7070     }
   7071 }
   7072 
   7073 test "classifyFileExt" {
   7074     try std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc"));
   7075     try std.testing.expectEqual(FileExt.m, classifyFileExt("foo.m"));
   7076     try std.testing.expectEqual(FileExt.mm, classifyFileExt("foo.mm"));
   7077     try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim"));
   7078     try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so"));
   7079     try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1"));
   7080     try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2"));
   7081     try std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3"));
   7082     try std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~"));
   7083     try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
   7084 }
   7085 
   7086 fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) !Cache.Path {
   7087     return (try crtFilePath(&comp.crt_files, basename)) orelse {
   7088         const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable;
   7089         const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCrtDir;
   7090         const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename });
   7091         return Cache.Path.initCwd(full_path);
   7092     };
   7093 }
   7094 
   7095 pub fn crtFileAsString(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
   7096     const path = try get_libc_crt_file(comp, arena, basename);
   7097     return path.toString(arena);
   7098 }
   7099 
   7100 fn crtFilePath(crt_files: *std.StringHashMapUnmanaged(CrtFile), basename: []const u8) Allocator.Error!?Cache.Path {
   7101     const crt_file = crt_files.get(basename) orelse return null;
   7102     return crt_file.full_object_path;
   7103 }
   7104 
   7105 fn wantBuildLibUnwindFromSource(comp: *Compilation) bool {
   7106     const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
   7107         .Obj => false,
   7108         .Lib => comp.config.link_mode == .dynamic,
   7109         .Exe => true,
   7110     };
   7111     const ofmt = comp.root_mod.resolved_target.result.ofmt;
   7112     return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c;
   7113 }
   7114 
   7115 pub fn setAllocFailure(comp: *Compilation) void {
   7116     @branchHint(.cold);
   7117     log.debug("memory allocation failure", .{});
   7118     comp.alloc_failure_occurred = true;
   7119 }
   7120 
   7121 /// Assumes that Compilation mutex is locked.
   7122 /// See also `lockAndSetMiscFailure`.
   7123 pub fn setMiscFailure(
   7124     comp: *Compilation,
   7125     tag: MiscTask,
   7126     comptime format: []const u8,
   7127     args: anytype,
   7128 ) void {
   7129     comp.misc_failures.ensureUnusedCapacity(comp.gpa, 1) catch return comp.setAllocFailure();
   7130     const msg = std.fmt.allocPrint(comp.gpa, format, args) catch return comp.setAllocFailure();
   7131     const gop = comp.misc_failures.getOrPutAssumeCapacity(tag);
   7132     if (gop.found_existing) {
   7133         gop.value_ptr.deinit(comp.gpa);
   7134     }
   7135     gop.value_ptr.* = .{ .msg = msg };
   7136 }
   7137 
   7138 /// See also `setMiscFailure`.
   7139 pub fn lockAndSetMiscFailure(
   7140     comp: *Compilation,
   7141     tag: MiscTask,
   7142     comptime format: []const u8,
   7143     args: anytype,
   7144 ) void {
   7145     comp.mutex.lock();
   7146     defer comp.mutex.unlock();
   7147 
   7148     return setMiscFailure(comp, tag, format, args);
   7149 }
   7150 
   7151 pub fn dump_argv(argv: []const []const u8) void {
   7152     std.debug.lockStdErr();
   7153     defer std.debug.unlockStdErr();
   7154     const stderr = std.io.getStdErr().writer();
   7155     for (argv[0 .. argv.len - 1]) |arg| {
   7156         nosuspend stderr.print("{s} ", .{arg}) catch return;
   7157     }
   7158     nosuspend stderr.print("{s}\n", .{argv[argv.len - 1]}) catch {};
   7159 }
   7160 
   7161 pub fn getZigBackend(comp: Compilation) std.builtin.CompilerBackend {
   7162     const target = comp.root_mod.resolved_target.result;
   7163     return target_util.zigBackend(target, comp.config.use_llvm);
   7164 }
   7165 
   7166 pub fn updateSubCompilation(
   7167     parent_comp: *Compilation,
   7168     sub_comp: *Compilation,
   7169     misc_task: MiscTask,
   7170     prog_node: std.Progress.Node,
   7171 ) !void {
   7172     {
   7173         const sub_node = prog_node.start(@tagName(misc_task), 0);
   7174         defer sub_node.end();
   7175 
   7176         try sub_comp.update(sub_node);
   7177     }
   7178 
   7179     // Look for compilation errors in this sub compilation
   7180     const gpa = parent_comp.gpa;
   7181     var keep_errors = false;
   7182     var errors = try sub_comp.getAllErrorsAlloc();
   7183     defer if (!keep_errors) errors.deinit(gpa);
   7184 
   7185     if (errors.errorMessageCount() > 0) {
   7186         try parent_comp.misc_failures.ensureUnusedCapacity(gpa, 1);
   7187         parent_comp.misc_failures.putAssumeCapacityNoClobber(misc_task, .{
   7188             .msg = try std.fmt.allocPrint(gpa, "sub-compilation of {s} failed", .{
   7189                 @tagName(misc_task),
   7190             }),
   7191             .children = errors,
   7192         });
   7193         keep_errors = true;
   7194         return error.SubCompilationFailed;
   7195     }
   7196 }
   7197 
   7198 fn buildOutputFromZig(
   7199     comp: *Compilation,
   7200     src_basename: []const u8,
   7201     root_name: []const u8,
   7202     output_mode: std.builtin.OutputMode,
   7203     misc_task_tag: MiscTask,
   7204     prog_node: std.Progress.Node,
   7205     options: RtOptions,
   7206     out: *?CrtFile,
   7207 ) !void {
   7208     const tracy_trace = trace(@src());
   7209     defer tracy_trace.end();
   7210 
   7211     const gpa = comp.gpa;
   7212     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
   7213     defer arena_allocator.deinit();
   7214     const arena = arena_allocator.allocator();
   7215 
   7216     assert(output_mode != .Exe);
   7217 
   7218     const strip = comp.compilerRtStrip();
   7219     const optimize_mode = comp.compilerRtOptMode();
   7220 
   7221     const config = try Config.resolve(.{
   7222         .output_mode = output_mode,
   7223         .link_mode = .static,
   7224         .resolved_target = comp.root_mod.resolved_target,
   7225         .is_test = false,
   7226         .have_zcu = true,
   7227         .emit_bin = true,
   7228         .root_optimize_mode = optimize_mode,
   7229         .root_strip = strip,
   7230         .link_libc = comp.config.link_libc,
   7231         .any_unwind_tables = comp.root_mod.unwind_tables != .none,
   7232         .any_error_tracing = false,
   7233         .root_error_tracing = false,
   7234         .lto = if (options.allow_lto) comp.config.lto else .none,
   7235     });
   7236 
   7237     const root_mod = try Package.Module.create(arena, .{
   7238         .paths = .{
   7239             .root = .zig_lib_root,
   7240             .root_src_path = src_basename,
   7241         },
   7242         .fully_qualified_name = "root",
   7243         .inherited = .{
   7244             .resolved_target = comp.root_mod.resolved_target,
   7245             .strip = strip,
   7246             .stack_check = false,
   7247             .stack_protector = 0,
   7248             .red_zone = comp.root_mod.red_zone,
   7249             .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
   7250             .unwind_tables = comp.root_mod.unwind_tables,
   7251             .pic = comp.root_mod.pic,
   7252             .optimize_mode = optimize_mode,
   7253             .structured_cfg = comp.root_mod.structured_cfg,
   7254             .no_builtin = true,
   7255             .code_model = comp.root_mod.code_model,
   7256             .error_tracing = false,
   7257             .valgrind = if (options.checks_valgrind) comp.root_mod.valgrind else null,
   7258         },
   7259         .global = config,
   7260         .cc_argv = &.{},
   7261         .parent = null,
   7262     });
   7263 
   7264     const parent_whole_cache: ?ParentWholeCache = switch (comp.cache_use) {
   7265         .whole => |whole| .{
   7266             .manifest = whole.cache_manifest.?,
   7267             .mutex = &whole.cache_manifest_mutex,
   7268             .prefix_map = .{
   7269                 0, // cwd is the same
   7270                 1, // zig lib dir is the same
   7271                 3, // local cache is mapped to global cache
   7272                 3, // global cache is the same
   7273             },
   7274         },
   7275         .incremental, .none => null,
   7276     };
   7277 
   7278     const sub_compilation = try Compilation.create(gpa, arena, .{
   7279         .dirs = comp.dirs.withoutLocalCache(),
   7280         .cache_mode = .whole,
   7281         .parent_whole_cache = parent_whole_cache,
   7282         .self_exe_path = comp.self_exe_path,
   7283         .config = config,
   7284         .root_mod = root_mod,
   7285         .root_name = root_name,
   7286         .thread_pool = comp.thread_pool,
   7287         .libc_installation = comp.libc_installation,
   7288         .emit_bin = .yes_cache,
   7289         .function_sections = true,
   7290         .data_sections = true,
   7291         .verbose_cc = comp.verbose_cc,
   7292         .verbose_link = comp.verbose_link,
   7293         .verbose_air = comp.verbose_air,
   7294         .verbose_intern_pool = comp.verbose_intern_pool,
   7295         .verbose_generic_instances = comp.verbose_intern_pool,
   7296         .verbose_llvm_ir = comp.verbose_llvm_ir,
   7297         .verbose_llvm_bc = comp.verbose_llvm_bc,
   7298         .verbose_cimport = comp.verbose_cimport,
   7299         .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
   7300         .clang_passthrough_mode = comp.clang_passthrough_mode,
   7301         .skip_linker_dependencies = true,
   7302     });
   7303     defer sub_compilation.destroy();
   7304 
   7305     try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
   7306 
   7307     const crt_file = try sub_compilation.toCrtFile();
   7308     assert(out.* == null);
   7309     out.* = crt_file;
   7310 
   7311     comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
   7312 }
   7313 
   7314 pub const CrtFileOptions = struct {
   7315     function_sections: ?bool = null,
   7316     data_sections: ?bool = null,
   7317     omit_frame_pointer: ?bool = null,
   7318     unwind_tables: ?std.builtin.UnwindTables = null,
   7319     pic: ?bool = null,
   7320     no_builtin: ?bool = null,
   7321 
   7322     allow_lto: bool = true,
   7323 };
   7324 
   7325 pub fn build_crt_file(
   7326     comp: *Compilation,
   7327     root_name: []const u8,
   7328     output_mode: std.builtin.OutputMode,
   7329     misc_task_tag: MiscTask,
   7330     prog_node: std.Progress.Node,
   7331     /// These elements have to get mutated to add the owner module after it is
   7332     /// created within this function.
   7333     c_source_files: []CSourceFile,
   7334     options: CrtFileOptions,
   7335 ) !void {
   7336     const tracy_trace = trace(@src());
   7337     defer tracy_trace.end();
   7338 
   7339     const gpa = comp.gpa;
   7340     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
   7341     defer arena_allocator.deinit();
   7342     const arena = arena_allocator.allocator();
   7343 
   7344     const basename = try std.zig.binNameAlloc(gpa, .{
   7345         .root_name = root_name,
   7346         .target = comp.root_mod.resolved_target.result,
   7347         .output_mode = output_mode,
   7348     });
   7349 
   7350     const config = try Config.resolve(.{
   7351         .output_mode = output_mode,
   7352         .resolved_target = comp.root_mod.resolved_target,
   7353         .is_test = false,
   7354         .have_zcu = false,
   7355         .emit_bin = true,
   7356         .root_optimize_mode = comp.compilerRtOptMode(),
   7357         .root_strip = comp.compilerRtStrip(),
   7358         .link_libc = false,
   7359         .any_unwind_tables = options.unwind_tables != .none,
   7360         .lto = switch (output_mode) {
   7361             .Lib => if (options.allow_lto) comp.config.lto else .none,
   7362             .Obj, .Exe => .none,
   7363         },
   7364     });
   7365     const root_mod = try Package.Module.create(arena, .{
   7366         .paths = .{
   7367             .root = .zig_lib_root,
   7368             .root_src_path = "",
   7369         },
   7370         .fully_qualified_name = "root",
   7371         .inherited = .{
   7372             .resolved_target = comp.root_mod.resolved_target,
   7373             .strip = comp.compilerRtStrip(),
   7374             .stack_check = false,
   7375             .stack_protector = 0,
   7376             .sanitize_c = .off,
   7377             .sanitize_thread = false,
   7378             .red_zone = comp.root_mod.red_zone,
   7379             // Some libcs (e.g. musl) are opinionated about -fomit-frame-pointer.
   7380             .omit_frame_pointer = options.omit_frame_pointer orelse comp.root_mod.omit_frame_pointer,
   7381             .valgrind = false,
   7382             // Some libcs (e.g. MinGW) are opinionated about -funwind-tables.
   7383             .unwind_tables = options.unwind_tables orelse .none,
   7384             // Some CRT objects (e.g. musl's rcrt1.o and Scrt1.o) are opinionated about PIC.
   7385             .pic = options.pic orelse comp.root_mod.pic,
   7386             .optimize_mode = comp.compilerRtOptMode(),
   7387             .structured_cfg = comp.root_mod.structured_cfg,
   7388             // Some libcs (e.g. musl) are opinionated about -fno-builtin.
   7389             .no_builtin = options.no_builtin orelse comp.root_mod.no_builtin,
   7390             .code_model = comp.root_mod.code_model,
   7391         },
   7392         .global = config,
   7393         .cc_argv = &.{},
   7394         .parent = null,
   7395     });
   7396 
   7397     for (c_source_files) |*item| {
   7398         item.owner = root_mod;
   7399     }
   7400 
   7401     const sub_compilation = try Compilation.create(gpa, arena, .{
   7402         .dirs = comp.dirs.withoutLocalCache(),
   7403         .self_exe_path = comp.self_exe_path,
   7404         .cache_mode = .whole,
   7405         .config = config,
   7406         .root_mod = root_mod,
   7407         .root_name = root_name,
   7408         .thread_pool = comp.thread_pool,
   7409         .libc_installation = comp.libc_installation,
   7410         .emit_bin = .yes_cache,
   7411         .function_sections = options.function_sections orelse false,
   7412         .data_sections = options.data_sections orelse false,
   7413         .c_source_files = c_source_files,
   7414         .verbose_cc = comp.verbose_cc,
   7415         .verbose_link = comp.verbose_link,
   7416         .verbose_air = comp.verbose_air,
   7417         .verbose_intern_pool = comp.verbose_intern_pool,
   7418         .verbose_generic_instances = comp.verbose_generic_instances,
   7419         .verbose_llvm_ir = comp.verbose_llvm_ir,
   7420         .verbose_llvm_bc = comp.verbose_llvm_bc,
   7421         .verbose_cimport = comp.verbose_cimport,
   7422         .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
   7423         .clang_passthrough_mode = comp.clang_passthrough_mode,
   7424         .skip_linker_dependencies = true,
   7425     });
   7426     defer sub_compilation.destroy();
   7427 
   7428     try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node);
   7429 
   7430     const crt_file = try sub_compilation.toCrtFile();
   7431     comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
   7432 
   7433     {
   7434         comp.mutex.lock();
   7435         defer comp.mutex.unlock();
   7436         try comp.crt_files.ensureUnusedCapacity(gpa, 1);
   7437         comp.crt_files.putAssumeCapacityNoClobber(basename, crt_file);
   7438     }
   7439 }
   7440 
   7441 pub fn queuePrelinkTaskMode(comp: *Compilation, path: Cache.Path, config: *const Compilation.Config) void {
   7442     comp.queuePrelinkTasks(switch (config.output_mode) {
   7443         .Exe => unreachable,
   7444         .Obj => &.{.{ .load_object = path }},
   7445         .Lib => &.{switch (config.link_mode) {
   7446             .static => .{ .load_archive = path },
   7447             .dynamic => .{ .load_dso = path },
   7448         }},
   7449     });
   7450 }
   7451 
   7452 /// Only valid to call during `update`. Automatically handles queuing up a
   7453 /// linker worker task if there is not already one.
   7454 pub fn queuePrelinkTasks(comp: *Compilation, tasks: []const link.PrelinkTask) void {
   7455     comp.link_task_queue.enqueuePrelink(comp, tasks) catch |err| switch (err) {
   7456         error.OutOfMemory => return comp.setAllocFailure(),
   7457     };
   7458 }
   7459 
   7460 /// The reason for the double-queue here is that the first queue ensures any
   7461 /// resolve_type_fully tasks are complete before this dispatch function is called.
   7462 fn dispatchZcuLinkTask(comp: *Compilation, tid: usize, task: link.ZcuTask) void {
   7463     comp.link_prog_node.increaseEstimatedTotalItems(1);
   7464     if (!comp.separateCodegenThreadOk()) {
   7465         assert(tid == 0);
   7466         if (task == .link_func) {
   7467             assert(task.link_func.mir.status.load(.monotonic) != .pending);
   7468         }
   7469         link.doZcuTask(comp, tid, task);
   7470         task.deinit(comp.zcu.?);
   7471         return;
   7472     }
   7473     comp.link_task_queue.enqueueZcu(comp, task) catch |err| switch (err) {
   7474         error.OutOfMemory => {
   7475             task.deinit(comp.zcu.?);
   7476             comp.setAllocFailure();
   7477         },
   7478     };
   7479 }
   7480 
   7481 pub fn toCrtFile(comp: *Compilation) Allocator.Error!CrtFile {
   7482     return .{
   7483         .full_object_path = .{
   7484             .root_dir = comp.dirs.local_cache,
   7485             .sub_path = try std.fs.path.join(comp.gpa, &.{
   7486                 "o",
   7487                 &Cache.binToHex(comp.digest.?),
   7488                 comp.emit_bin.?,
   7489             }),
   7490         },
   7491         .lock = comp.cache_use.whole.moveLock(),
   7492     };
   7493 }
   7494 
   7495 pub fn getCrtPaths(
   7496     comp: *Compilation,
   7497     arena: Allocator,
   7498 ) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
   7499     const target = comp.root_mod.resolved_target.result;
   7500     return getCrtPathsInner(arena, target, comp.config, comp.libc_installation, &comp.crt_files);
   7501 }
   7502 
   7503 fn getCrtPathsInner(
   7504     arena: Allocator,
   7505     target: std.Target,
   7506     config: Config,
   7507     libc_installation: ?*const LibCInstallation,
   7508     crt_files: *std.StringHashMapUnmanaged(CrtFile),
   7509 ) error{ OutOfMemory, LibCInstallationMissingCrtDir }!LibCInstallation.CrtPaths {
   7510     const basenames = LibCInstallation.CrtBasenames.get(.{
   7511         .target = target,
   7512         .link_libc = config.link_libc,
   7513         .output_mode = config.output_mode,
   7514         .link_mode = config.link_mode,
   7515         .pie = config.pie,
   7516     });
   7517     if (libc_installation) |lci| return lci.resolveCrtPaths(arena, basenames, target);
   7518 
   7519     return .{
   7520         .crt0 = if (basenames.crt0) |basename| try crtFilePath(crt_files, basename) else null,
   7521         .crti = if (basenames.crti) |basename| try crtFilePath(crt_files, basename) else null,
   7522         .crtbegin = if (basenames.crtbegin) |basename| try crtFilePath(crt_files, basename) else null,
   7523         .crtend = if (basenames.crtend) |basename| try crtFilePath(crt_files, basename) else null,
   7524         .crtn = if (basenames.crtn) |basename| try crtFilePath(crt_files, basename) else null,
   7525     };
   7526 }
   7527 
   7528 pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void {
   7529     // Avoid deadlocking on building import libs such as kernel32.lib
   7530     // This can happen when the user uses `build-exe foo.obj -lkernel32` and
   7531     // then when we create a sub-Compilation for zig libc, it also tries to
   7532     // build kernel32.lib.
   7533     if (comp.skip_linker_dependencies) return;
   7534     const target = comp.root_mod.resolved_target.result;
   7535     if (target.os.tag != .windows or target.ofmt == .c) return;
   7536 
   7537     // This happens when an `extern "foo"` function is referenced.
   7538     // If we haven't seen this library yet and we're targeting Windows, we need
   7539     // to queue up a work item to produce the DLL import library for this.
   7540     const gop = try comp.windows_libs.getOrPut(comp.gpa, lib_name);
   7541     if (!gop.found_existing) try comp.queueJob(.{ .windows_import_lib = comp.windows_libs.count() - 1 });
   7542 }
   7543 
   7544 /// This decides the optimization mode for all zig-provided libraries, including
   7545 /// compiler-rt, libcxx, libc, libunwind, etc.
   7546 pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode {
   7547     if (comp.debug_compiler_runtime_libs) {
   7548         return comp.root_mod.optimize_mode;
   7549     }
   7550     const target = comp.root_mod.resolved_target.result;
   7551     switch (comp.root_mod.optimize_mode) {
   7552         .Debug, .ReleaseSafe => return target_util.defaultCompilerRtOptimizeMode(target),
   7553         .ReleaseFast => return .ReleaseFast,
   7554         .ReleaseSmall => return .ReleaseSmall,
   7555     }
   7556 }
   7557 
   7558 /// This decides whether to strip debug info for all zig-provided libraries, including
   7559 /// compiler-rt, libcxx, libc, libunwind, etc.
   7560 pub fn compilerRtStrip(comp: Compilation) bool {
   7561     return comp.root_mod.strip;
   7562 }