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