master
   1const builtin = @import("builtin");
   2const std = @import("std");
   3const mem = std.mem;
   4const fs = std.fs;
   5const assert = std.debug.assert;
   6const panic = std.debug.panic;
   7const StringHashMap = std.StringHashMap;
   8const Sha256 = std.crypto.hash.sha2.Sha256;
   9const Allocator = mem.Allocator;
  10const Step = std.Build.Step;
  11const LazyPath = std.Build.LazyPath;
  12const PkgConfigPkg = std.Build.PkgConfigPkg;
  13const PkgConfigError = std.Build.PkgConfigError;
  14const RunError = std.Build.RunError;
  15const Module = std.Build.Module;
  16const InstallDir = std.Build.InstallDir;
  17const GeneratedFile = std.Build.GeneratedFile;
  18const Compile = @This();
  19const Path = std.Build.Cache.Path;
  20
  21pub const base_id: Step.Id = .compile;
  22
  23step: Step,
  24root_module: *Module,
  25
  26name: []const u8,
  27linker_script: ?LazyPath = null,
  28version_script: ?LazyPath = null,
  29out_filename: []const u8,
  30out_lib_filename: []const u8,
  31linkage: ?std.builtin.LinkMode = null,
  32version: ?std.SemanticVersion,
  33kind: Kind,
  34major_only_filename: ?[]const u8,
  35name_only_filename: ?[]const u8,
  36formatted_panics: ?bool = null,
  37compress_debug_sections: std.zig.CompressDebugSections = .none,
  38verbose_link: bool,
  39verbose_cc: bool,
  40bundle_compiler_rt: ?bool = null,
  41bundle_ubsan_rt: ?bool = null,
  42rdynamic: bool,
  43import_memory: bool = false,
  44export_memory: bool = false,
  45/// For WebAssembly targets, this will allow for undefined symbols to
  46/// be imported from the host environment.
  47import_symbols: bool = false,
  48import_table: bool = false,
  49export_table: bool = false,
  50initial_memory: ?u64 = null,
  51max_memory: ?u64 = null,
  52shared_memory: bool = false,
  53global_base: ?u64 = null,
  54/// Set via options; intended to be read-only after that.
  55zig_lib_dir: ?LazyPath,
  56exec_cmd_args: ?[]const ?[]const u8,
  57filters: []const []const u8,
  58test_runner: ?TestRunner,
  59wasi_exec_model: ?std.builtin.WasiExecModel = null,
  60
  61installed_headers: std.array_list.Managed(HeaderInstallation),
  62
  63/// This step is used to create an include tree that dependent modules can add to their include
  64/// search paths. Installed headers are copied to this step.
  65/// This step is created the first time a module links with this artifact and is not
  66/// created otherwise.
  67installed_headers_include_tree: ?*Step.WriteFile = null,
  68
  69/// Behavior of automatic detection of include directories when compiling .rc files.
  70///  any: Use MSVC if available, fall back to MinGW.
  71///  msvc: Use MSVC include paths (must be present on the system).
  72///  gnu: Use MinGW include paths (distributed with Zig).
  73///  none: Do not use any autodetected include paths.
  74rc_includes: std.zig.RcIncludes = .any,
  75
  76/// (Windows) .manifest file to embed in the compilation
  77/// Set via options; intended to be read-only after that.
  78win32_manifest: ?LazyPath = null,
  79
  80installed_path: ?[]const u8,
  81
  82/// Base address for an executable image.
  83image_base: ?u64 = null,
  84
  85libc_file: ?LazyPath = null,
  86
  87each_lib_rpath: ?bool = null,
  88/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
  89/// which can be used to coordinate a stripped binary with its debug symbols.
  90/// As an example, the bloaty project refuses to work unless its inputs have
  91/// build ids, in order to prevent accidental mismatches.
  92/// The default is to not include this section because it slows down linking.
  93build_id: ?std.zig.BuildId = null,
  94
  95/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
  96/// file.
  97link_eh_frame_hdr: bool = false,
  98link_emit_relocs: bool = false,
  99
 100/// Place every function in its own section so that unused ones may be
 101/// safely garbage-collected during the linking phase.
 102link_function_sections: bool = false,
 103
 104/// Place every data in its own section so that unused ones may be
 105/// safely garbage-collected during the linking phase.
 106link_data_sections: bool = false,
 107
 108/// Remove functions and data that are unreachable by the entry point or
 109/// exported symbols.
 110link_gc_sections: ?bool = null,
 111
 112/// (Windows) Whether or not to enable ASLR. Maps to the /DYNAMICBASE[:NO] linker argument.
 113linker_dynamicbase: bool = true,
 114
 115linker_allow_shlib_undefined: ?bool = null,
 116
 117/// Allow version scripts to refer to undefined symbols.
 118linker_allow_undefined_version: ?bool = null,
 119
 120// Enable (or disable) the new DT_RUNPATH tag in the dynamic section.
 121linker_enable_new_dtags: ?bool = null,
 122
 123/// Permit read-only relocations in read-only segments. Disallowed by default.
 124link_z_notext: bool = false,
 125
 126/// Force all relocations to be read-only after processing.
 127link_z_relro: bool = true,
 128
 129/// Allow relocations to be lazily processed after load.
 130link_z_lazy: bool = false,
 131
 132/// Common page size
 133link_z_common_page_size: ?u64 = null,
 134
 135/// Maximum page size
 136link_z_max_page_size: ?u64 = null,
 137
 138/// Force a fatal error if any undefined symbols remain.
 139link_z_defs: bool = false,
 140
 141/// (Darwin) Install name for the dylib
 142install_name: ?[]const u8 = null,
 143
 144/// (Darwin) Path to entitlements file
 145entitlements: ?[]const u8 = null,
 146
 147/// (Darwin) Size of the pagezero segment.
 148pagezero_size: ?u64 = null,
 149
 150/// (Darwin) Set size of the padding between the end of load commands
 151/// and start of `__TEXT,__text` section.
 152headerpad_size: ?u32 = null,
 153
 154/// (Darwin) Automatically Set size of the padding between the end of load commands
 155/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
 156headerpad_max_install_names: bool = false,
 157
 158/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
 159dead_strip_dylibs: bool = false,
 160
 161/// (Darwin) Force load all members of static archives that implement an Objective-C class or category
 162force_load_objc: bool = false,
 163
 164/// Whether local symbols should be discarded from the symbol table.
 165discard_local_symbols: bool = false,
 166
 167/// Position Independent Executable
 168pie: ?bool = null,
 169
 170/// Link Time Optimization mode
 171lto: ?std.zig.LtoMode = null,
 172
 173dll_export_fns: ?bool = null,
 174
 175subsystem: ?std.zig.Subsystem = null,
 176
 177/// (Windows) When targeting the MinGW ABI, use the unicode entry point (wmain/wWinMain)
 178mingw_unicode_entry_point: bool = false,
 179
 180/// How the linker must handle the entry point of the executable.
 181entry: Entry = .default,
 182
 183/// List of symbols forced as undefined in the symbol table
 184/// thus forcing their resolution by the linker.
 185/// Corresponds to `-u <symbol>` for ELF/MachO and `/include:<symbol>` for COFF/PE.
 186force_undefined_symbols: std.StringHashMap(void),
 187
 188/// Overrides the default stack size
 189stack_size: ?u64 = null,
 190
 191/// Deprecated; prefer using `lto`.
 192want_lto: ?bool = null,
 193
 194use_llvm: ?bool,
 195use_lld: ?bool,
 196use_new_linker: ?bool,
 197
 198/// Corresponds to the `-fallow-so-scripts` / `-fno-allow-so-scripts` CLI
 199/// flags, overriding the global user setting provided to the `zig build`
 200/// command.
 201///
 202/// The compiler defaults this value to off so that users whose system shared
 203/// libraries are all ELF files don't have to pay the cost of checking every
 204/// file to find out if it is a text file instead.
 205allow_so_scripts: ?bool = null,
 206
 207/// This is an advanced setting that can change the intent of this Compile step.
 208/// If this value is non-null, it means that this Compile step exists to
 209/// check for compile errors and return *success* if they match, and failure
 210/// otherwise.
 211expect_errors: ?ExpectedCompileErrors = null,
 212
 213emit_directory: ?*GeneratedFile,
 214
 215generated_docs: ?*GeneratedFile,
 216generated_asm: ?*GeneratedFile,
 217generated_bin: ?*GeneratedFile,
 218generated_pdb: ?*GeneratedFile,
 219generated_implib: ?*GeneratedFile,
 220generated_llvm_bc: ?*GeneratedFile,
 221generated_llvm_ir: ?*GeneratedFile,
 222generated_h: ?*GeneratedFile,
 223
 224/// The maximum number of distinct errors within a compilation step
 225/// Defaults to `std.math.maxInt(u16)`
 226error_limit: ?u32 = null,
 227
 228/// Computed during make().
 229is_linking_libc: bool = false,
 230/// Computed during make().
 231is_linking_libcpp: bool = false,
 232
 233/// Populated during the make phase when there is a long-lived compiler process.
 234/// Managed by the build runner, not user build script.
 235zig_process: ?*Step.ZigProcess,
 236
 237/// Enables coverage instrumentation that is only useful if you are using third
 238/// party fuzzers that depend on it. Otherwise, slows down the instrumented
 239/// binary with unnecessary function calls.
 240///
 241/// This kind of coverage instrumentation is used by AFLplusplus v4.21c,
 242/// however, modern fuzzers - including Zig - have switched to using "inline
 243/// 8-bit counters" or "inline bool flag" which incurs only a single
 244/// instruction for coverage, along with "trace cmp" which instruments
 245/// comparisons and reports the operands.
 246///
 247/// To instead enable fuzz testing instrumentation on a compilation using Zig's
 248/// builtin fuzzer, see the `fuzz` flag in `Module`.
 249sanitize_coverage_trace_pc_guard: ?bool = null,
 250
 251pub const ExpectedCompileErrors = union(enum) {
 252    contains: []const u8,
 253    exact: []const []const u8,
 254    starts_with: []const u8,
 255    stderr_contains: []const u8,
 256};
 257
 258pub const Entry = union(enum) {
 259    /// Let the compiler decide whether to make an entry point and what to name
 260    /// it.
 261    default,
 262    /// The executable will have no entry point.
 263    disabled,
 264    /// The executable will have an entry point with the default symbol name.
 265    enabled,
 266    /// The executable will have an entry point with the specified symbol name.
 267    symbol_name: []const u8,
 268};
 269
 270pub const Options = struct {
 271    name: []const u8,
 272    root_module: *Module,
 273    kind: Kind,
 274    linkage: ?std.builtin.LinkMode = null,
 275    version: ?std.SemanticVersion = null,
 276    max_rss: usize = 0,
 277    filters: []const []const u8 = &.{},
 278    test_runner: ?TestRunner = null,
 279    use_llvm: ?bool = null,
 280    use_lld: ?bool = null,
 281    zig_lib_dir: ?LazyPath = null,
 282    /// Embed a `.manifest` file in the compilation if the object format supports it.
 283    /// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference
 284    /// Manifest files must have the extension `.manifest`.
 285    /// Can be set regardless of target. The `.manifest` file will be ignored
 286    /// if the target object format does not support embedded manifests.
 287    win32_manifest: ?LazyPath = null,
 288};
 289
 290pub const Kind = enum {
 291    exe,
 292    lib,
 293    obj,
 294    @"test",
 295    test_obj,
 296
 297    pub fn isTest(kind: Kind) bool {
 298        return switch (kind) {
 299            .exe, .lib, .obj => false,
 300            .@"test", .test_obj => true,
 301        };
 302    }
 303};
 304
 305pub const HeaderInstallation = union(enum) {
 306    file: File,
 307    directory: Directory,
 308
 309    pub const File = struct {
 310        source: LazyPath,
 311        dest_rel_path: []const u8,
 312
 313        pub fn dupe(file: File, b: *std.Build) File {
 314            return .{
 315                .source = file.source.dupe(b),
 316                .dest_rel_path = b.dupePath(file.dest_rel_path),
 317            };
 318        }
 319    };
 320
 321    pub const Directory = struct {
 322        source: LazyPath,
 323        dest_rel_path: []const u8,
 324        options: Directory.Options,
 325
 326        pub const Options = struct {
 327            /// File paths that end in any of these suffixes will be excluded from installation.
 328            exclude_extensions: []const []const u8 = &.{},
 329            /// Only file paths that end in any of these suffixes will be included in installation.
 330            /// `null` means that all suffixes will be included.
 331            /// `exclude_extensions` takes precedence over `include_extensions`.
 332            include_extensions: ?[]const []const u8 = &.{".h"},
 333
 334            pub fn dupe(opts: Directory.Options, b: *std.Build) Directory.Options {
 335                return .{
 336                    .exclude_extensions = b.dupeStrings(opts.exclude_extensions),
 337                    .include_extensions = if (opts.include_extensions) |incs| b.dupeStrings(incs) else null,
 338                };
 339            }
 340        };
 341
 342        pub fn dupe(dir: Directory, b: *std.Build) Directory {
 343            return .{
 344                .source = dir.source.dupe(b),
 345                .dest_rel_path = b.dupePath(dir.dest_rel_path),
 346                .options = dir.options.dupe(b),
 347            };
 348        }
 349    };
 350
 351    pub fn getSource(installation: HeaderInstallation) LazyPath {
 352        return switch (installation) {
 353            inline .file, .directory => |x| x.source,
 354        };
 355    }
 356
 357    pub fn dupe(installation: HeaderInstallation, b: *std.Build) HeaderInstallation {
 358        return switch (installation) {
 359            .file => |f| .{ .file = f.dupe(b) },
 360            .directory => |d| .{ .directory = d.dupe(b) },
 361        };
 362    }
 363};
 364
 365pub const TestRunner = struct {
 366    path: LazyPath,
 367    /// Test runners can either be "simple", running tests when spawned and terminating when the
 368    /// tests are complete, or they can use `std.zig.Server` over stdio to interact more closely
 369    /// with the build system.
 370    mode: enum { simple, server },
 371};
 372
 373pub fn create(owner: *std.Build, options: Options) *Compile {
 374    const name = owner.dupe(options.name);
 375    if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
 376        panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
 377    }
 378
 379    const resolved_target = options.root_module.resolved_target orelse
 380        @panic("the root Module of a Compile step must be created with a known 'target' field");
 381    const target = &resolved_target.result;
 382
 383    const step_name = owner.fmt("compile {s} {s} {s}", .{
 384        // Avoid the common case of the step name looking like "compile test test".
 385        if (options.kind.isTest() and mem.eql(u8, name, "test"))
 386            @tagName(options.kind)
 387        else
 388            owner.fmt("{s} {s}", .{ @tagName(options.kind), name }),
 389        @tagName(options.root_module.optimize orelse .Debug),
 390        resolved_target.query.zigTriple(owner.allocator) catch @panic("OOM"),
 391    });
 392
 393    const out_filename = std.zig.binNameAlloc(owner.allocator, .{
 394        .root_name = name,
 395        .target = target,
 396        .output_mode = switch (options.kind) {
 397            .lib => .Lib,
 398            .obj, .test_obj => .Obj,
 399            .exe, .@"test" => .Exe,
 400        },
 401        .link_mode = options.linkage,
 402        .version = options.version,
 403    }) catch @panic("OOM");
 404
 405    const compile = owner.allocator.create(Compile) catch @panic("OOM");
 406    compile.* = .{
 407        .root_module = options.root_module,
 408        .verbose_link = false,
 409        .verbose_cc = false,
 410        .linkage = options.linkage,
 411        .kind = options.kind,
 412        .name = name,
 413        .step = .init(.{
 414            .id = base_id,
 415            .name = step_name,
 416            .owner = owner,
 417            .makeFn = make,
 418            .max_rss = options.max_rss,
 419        }),
 420        .version = options.version,
 421        .out_filename = out_filename,
 422        .out_lib_filename = undefined,
 423        .major_only_filename = null,
 424        .name_only_filename = null,
 425        .installed_headers = std.array_list.Managed(HeaderInstallation).init(owner.allocator),
 426        .zig_lib_dir = null,
 427        .exec_cmd_args = null,
 428        .filters = options.filters,
 429        .test_runner = null, // set below
 430        .rdynamic = false,
 431        .installed_path = null,
 432        .force_undefined_symbols = StringHashMap(void).init(owner.allocator),
 433
 434        .emit_directory = null,
 435        .generated_docs = null,
 436        .generated_asm = null,
 437        .generated_bin = null,
 438        .generated_pdb = null,
 439        .generated_implib = null,
 440        .generated_llvm_bc = null,
 441        .generated_llvm_ir = null,
 442        .generated_h = null,
 443
 444        .use_llvm = options.use_llvm,
 445        .use_lld = options.use_lld,
 446        .use_new_linker = null,
 447
 448        .zig_process = null,
 449    };
 450
 451    if (options.zig_lib_dir) |lp| {
 452        compile.zig_lib_dir = lp.dupe(compile.step.owner);
 453        lp.addStepDependencies(&compile.step);
 454    }
 455
 456    if (options.test_runner) |runner| {
 457        compile.test_runner = .{
 458            .path = runner.path.dupe(compile.step.owner),
 459            .mode = runner.mode,
 460        };
 461        runner.path.addStepDependencies(&compile.step);
 462    }
 463
 464    // Only the PE/COFF format has a Resource Table which is where the manifest
 465    // gets embedded, so for any other target the manifest file is just ignored.
 466    if (target.ofmt == .coff) {
 467        if (options.win32_manifest) |lp| {
 468            compile.win32_manifest = lp.dupe(compile.step.owner);
 469            lp.addStepDependencies(&compile.step);
 470        }
 471    }
 472
 473    if (compile.kind == .lib) {
 474        if (compile.linkage != null and compile.linkage.? == .static) {
 475            compile.out_lib_filename = compile.out_filename;
 476        } else if (compile.version) |version| {
 477            if (target.os.tag.isDarwin()) {
 478                compile.major_only_filename = owner.fmt("lib{s}.{d}.dylib", .{
 479                    compile.name,
 480                    version.major,
 481                });
 482                compile.name_only_filename = owner.fmt("lib{s}.dylib", .{compile.name});
 483                compile.out_lib_filename = compile.out_filename;
 484            } else if (target.os.tag == .windows) {
 485                compile.out_lib_filename = owner.fmt("{s}.lib", .{compile.name});
 486            } else {
 487                compile.major_only_filename = owner.fmt("lib{s}.so.{d}", .{ compile.name, version.major });
 488                compile.name_only_filename = owner.fmt("lib{s}.so", .{compile.name});
 489                compile.out_lib_filename = compile.out_filename;
 490            }
 491        } else {
 492            if (target.os.tag.isDarwin()) {
 493                compile.out_lib_filename = compile.out_filename;
 494            } else if (target.os.tag == .windows) {
 495                compile.out_lib_filename = owner.fmt("{s}.lib", .{compile.name});
 496            } else {
 497                compile.out_lib_filename = compile.out_filename;
 498            }
 499        }
 500    }
 501
 502    return compile;
 503}
 504
 505/// Marks the specified header for installation alongside this artifact.
 506/// When a module links with this artifact, all headers marked for installation are added to that
 507/// module's include search path.
 508pub fn installHeader(cs: *Compile, source: LazyPath, dest_rel_path: []const u8) void {
 509    const b = cs.step.owner;
 510    const installation: HeaderInstallation = .{ .file = .{
 511        .source = source.dupe(b),
 512        .dest_rel_path = b.dupePath(dest_rel_path),
 513    } };
 514    cs.installed_headers.append(installation) catch @panic("OOM");
 515    cs.addHeaderInstallationToIncludeTree(installation);
 516    installation.getSource().addStepDependencies(&cs.step);
 517}
 518
 519/// Marks headers from the specified directory for installation alongside this artifact.
 520/// When a module links with this artifact, all headers marked for installation are added to that
 521/// module's include search path.
 522pub fn installHeadersDirectory(
 523    cs: *Compile,
 524    source: LazyPath,
 525    dest_rel_path: []const u8,
 526    options: HeaderInstallation.Directory.Options,
 527) void {
 528    const b = cs.step.owner;
 529    const installation: HeaderInstallation = .{ .directory = .{
 530        .source = source.dupe(b),
 531        .dest_rel_path = b.dupePath(dest_rel_path),
 532        .options = options.dupe(b),
 533    } };
 534    cs.installed_headers.append(installation) catch @panic("OOM");
 535    cs.addHeaderInstallationToIncludeTree(installation);
 536    installation.getSource().addStepDependencies(&cs.step);
 537}
 538
 539/// Marks the specified config header for installation alongside this artifact.
 540/// When a module links with this artifact, all headers marked for installation are added to that
 541/// module's include search path.
 542pub fn installConfigHeader(cs: *Compile, config_header: *Step.ConfigHeader) void {
 543    cs.installHeader(config_header.getOutput(), config_header.include_path);
 544}
 545
 546/// Forwards all headers marked for installation from `lib` to this artifact.
 547/// When a module links with this artifact, all headers marked for installation are added to that
 548/// module's include search path.
 549pub fn installLibraryHeaders(cs: *Compile, lib: *Compile) void {
 550    assert(lib.kind == .lib);
 551    for (lib.installed_headers.items) |installation| {
 552        const installation_copy = installation.dupe(lib.step.owner);
 553        cs.installed_headers.append(installation_copy) catch @panic("OOM");
 554        cs.addHeaderInstallationToIncludeTree(installation_copy);
 555        installation_copy.getSource().addStepDependencies(&cs.step);
 556    }
 557}
 558
 559fn addHeaderInstallationToIncludeTree(cs: *Compile, installation: HeaderInstallation) void {
 560    if (cs.installed_headers_include_tree) |wf| switch (installation) {
 561        .file => |file| {
 562            _ = wf.addCopyFile(file.source, file.dest_rel_path);
 563        },
 564        .directory => |dir| {
 565            _ = wf.addCopyDirectory(dir.source, dir.dest_rel_path, .{
 566                .exclude_extensions = dir.options.exclude_extensions,
 567                .include_extensions = dir.options.include_extensions,
 568            });
 569        },
 570    };
 571}
 572
 573pub fn getEmittedIncludeTree(cs: *Compile) LazyPath {
 574    if (cs.installed_headers_include_tree) |wf| return wf.getDirectory();
 575    const b = cs.step.owner;
 576    const wf = b.addWriteFiles();
 577    cs.installed_headers_include_tree = wf;
 578    for (cs.installed_headers.items) |installation| {
 579        cs.addHeaderInstallationToIncludeTree(installation);
 580    }
 581    // The compile step itself does not need to depend on the write files step,
 582    // only dependent modules do.
 583    return wf.getDirectory();
 584}
 585
 586pub fn addObjCopy(cs: *Compile, options: Step.ObjCopy.Options) *Step.ObjCopy {
 587    const b = cs.step.owner;
 588    var copy = options;
 589    if (copy.basename == null) {
 590        if (options.format) |f| {
 591            copy.basename = b.fmt("{s}.{s}", .{ cs.name, @tagName(f) });
 592        } else {
 593            copy.basename = cs.name;
 594        }
 595    }
 596    return b.addObjCopy(cs.getEmittedBin(), copy);
 597}
 598
 599pub fn checkObject(compile: *Compile) *Step.CheckObject {
 600    return Step.CheckObject.create(compile.step.owner, compile.getEmittedBin(), compile.rootModuleTarget().ofmt);
 601}
 602
 603pub fn setLinkerScript(compile: *Compile, source: LazyPath) void {
 604    const b = compile.step.owner;
 605    compile.linker_script = source.dupe(b);
 606    source.addStepDependencies(&compile.step);
 607}
 608
 609pub fn setVersionScript(compile: *Compile, source: LazyPath) void {
 610    const b = compile.step.owner;
 611    compile.version_script = source.dupe(b);
 612    source.addStepDependencies(&compile.step);
 613}
 614
 615pub fn forceUndefinedSymbol(compile: *Compile, symbol_name: []const u8) void {
 616    const b = compile.step.owner;
 617    compile.force_undefined_symbols.put(b.dupe(symbol_name), {}) catch @panic("OOM");
 618}
 619
 620/// Returns whether the library, executable, or object depends on a particular system library.
 621/// Includes transitive dependencies.
 622pub fn dependsOnSystemLibrary(compile: *Compile, name: []const u8) bool {
 623    var is_linking_libc = false;
 624    var is_linking_libcpp = false;
 625
 626    for (compile.getCompileDependencies(true)) |some_compile| {
 627        for (some_compile.root_module.getGraph().modules) |mod| {
 628            for (mod.link_objects.items) |lo| {
 629                switch (lo) {
 630                    .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
 631                    else => {},
 632                }
 633            }
 634            if (mod.link_libc orelse false) is_linking_libc = true;
 635            if (mod.link_libcpp orelse false) is_linking_libcpp = true;
 636        }
 637    }
 638
 639    const target = compile.rootModuleTarget();
 640
 641    if (std.zig.target.isLibCLibName(target, name)) {
 642        return is_linking_libc;
 643    }
 644
 645    if (std.zig.target.isLibCxxLibName(target, name)) {
 646        return is_linking_libcpp;
 647    }
 648
 649    return false;
 650}
 651
 652pub fn isDynamicLibrary(compile: *const Compile) bool {
 653    return compile.kind == .lib and compile.linkage == .dynamic;
 654}
 655
 656pub fn isStaticLibrary(compile: *const Compile) bool {
 657    return compile.kind == .lib and compile.linkage != .dynamic;
 658}
 659
 660pub fn isDll(compile: *Compile) bool {
 661    return compile.isDynamicLibrary() and compile.rootModuleTarget().os.tag == .windows;
 662}
 663
 664pub fn producesPdbFile(compile: *Compile) bool {
 665    const target = compile.rootModuleTarget();
 666    // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
 667    // TODO: just share this logic with the compiler, silly!
 668    switch (target.os.tag) {
 669        .windows, .uefi => {},
 670        else => return false,
 671    }
 672    if (target.ofmt == .c) return false;
 673    if (compile.use_llvm == false) return false;
 674    if (compile.root_module.strip == true or
 675        (compile.root_module.strip == null and compile.root_module.optimize == .ReleaseSmall))
 676    {
 677        return false;
 678    }
 679    return compile.isDynamicLibrary() or compile.kind == .exe or compile.kind == .@"test";
 680}
 681
 682pub fn producesImplib(compile: *Compile) bool {
 683    return compile.isDll();
 684}
 685
 686/// Deprecated; use `compile.root_module.link_libc = true` instead.
 687/// To be removed after 0.15.0 is tagged.
 688pub fn linkLibC(compile: *Compile) void {
 689    compile.root_module.link_libc = true;
 690}
 691
 692/// Deprecated; use `compile.root_module.link_libcpp = true` instead.
 693/// To be removed after 0.15.0 is tagged.
 694pub fn linkLibCpp(compile: *Compile) void {
 695    compile.root_module.link_libcpp = true;
 696}
 697
 698const PkgConfigResult = struct {
 699    cflags: []const []const u8,
 700    libs: []const []const u8,
 701};
 702
 703/// Run pkg-config for the given library name and parse the output, returning the arguments
 704/// that should be passed to zig to link the given library.
 705fn runPkgConfig(compile: *Compile, lib_name: []const u8) !PkgConfigResult {
 706    const wl_rpath_prefix = "-Wl,-rpath,";
 707
 708    const b = compile.step.owner;
 709    const pkg_name = match: {
 710        // First we have to map the library name to pkg config name. Unfortunately,
 711        // there are several examples where this is not straightforward:
 712        // -lSDL2 -> pkg-config sdl2
 713        // -lgdk-3 -> pkg-config gdk-3.0
 714        // -latk-1.0 -> pkg-config atk
 715        // -lpulse -> pkg-config libpulse
 716        const pkgs = try getPkgConfigList(b);
 717
 718        // Exact match means instant winner.
 719        for (pkgs) |pkg| {
 720            if (mem.eql(u8, pkg.name, lib_name)) {
 721                break :match pkg.name;
 722            }
 723        }
 724
 725        // Next we'll try ignoring case.
 726        for (pkgs) |pkg| {
 727            if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
 728                break :match pkg.name;
 729            }
 730        }
 731
 732        // Prefixed "lib" or suffixed ".0".
 733        for (pkgs) |pkg| {
 734            if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
 735                const prefix = pkg.name[0..pos];
 736                const suffix = pkg.name[pos + lib_name.len ..];
 737                if (prefix.len > 0 and !mem.eql(u8, prefix, "lib")) continue;
 738                if (suffix.len > 0 and !mem.eql(u8, suffix, ".0")) continue;
 739                break :match pkg.name;
 740            }
 741        }
 742
 743        // Trimming "-1.0".
 744        if (mem.endsWith(u8, lib_name, "-1.0")) {
 745            const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
 746            for (pkgs) |pkg| {
 747                if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
 748                    break :match pkg.name;
 749                }
 750            }
 751        }
 752
 753        return error.PackageNotFound;
 754    };
 755
 756    var code: u8 = undefined;
 757    const pkg_config_exe = b.graph.env_map.get("PKG_CONFIG") orelse "pkg-config";
 758    const stdout = if (b.runAllowFail(&[_][]const u8{
 759        pkg_config_exe,
 760        pkg_name,
 761        "--cflags",
 762        "--libs",
 763    }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
 764        error.ProcessTerminated => return error.PkgConfigCrashed,
 765        error.ExecNotSupported => return error.PkgConfigFailed,
 766        error.ExitCodeFailure => return error.PkgConfigFailed,
 767        error.FileNotFound => return error.PkgConfigNotInstalled,
 768        else => return err,
 769    };
 770
 771    var zig_cflags = std.array_list.Managed([]const u8).init(b.allocator);
 772    defer zig_cflags.deinit();
 773    var zig_libs = std.array_list.Managed([]const u8).init(b.allocator);
 774    defer zig_libs.deinit();
 775
 776    var arg_it = mem.tokenizeAny(u8, stdout, " \r\n\t");
 777    while (arg_it.next()) |arg| {
 778        if (mem.eql(u8, arg, "-I")) {
 779            const dir = arg_it.next() orelse return error.PkgConfigInvalidOutput;
 780            try zig_cflags.appendSlice(&[_][]const u8{ "-I", dir });
 781        } else if (mem.startsWith(u8, arg, "-I")) {
 782            try zig_cflags.append(arg);
 783        } else if (mem.eql(u8, arg, "-L")) {
 784            const dir = arg_it.next() orelse return error.PkgConfigInvalidOutput;
 785            try zig_libs.appendSlice(&[_][]const u8{ "-L", dir });
 786        } else if (mem.startsWith(u8, arg, "-L")) {
 787            try zig_libs.append(arg);
 788        } else if (mem.eql(u8, arg, "-l")) {
 789            const lib = arg_it.next() orelse return error.PkgConfigInvalidOutput;
 790            try zig_libs.appendSlice(&[_][]const u8{ "-l", lib });
 791        } else if (mem.startsWith(u8, arg, "-l")) {
 792            try zig_libs.append(arg);
 793        } else if (mem.eql(u8, arg, "-D")) {
 794            const macro = arg_it.next() orelse return error.PkgConfigInvalidOutput;
 795            try zig_cflags.appendSlice(&[_][]const u8{ "-D", macro });
 796        } else if (mem.startsWith(u8, arg, "-D")) {
 797            try zig_cflags.append(arg);
 798        } else if (mem.startsWith(u8, arg, wl_rpath_prefix)) {
 799            try zig_cflags.appendSlice(&[_][]const u8{ "-rpath", arg[wl_rpath_prefix.len..] });
 800        } else if (b.debug_pkg_config) {
 801            return compile.step.fail("unknown pkg-config flag '{s}'", .{arg});
 802        }
 803    }
 804
 805    return .{
 806        .cflags = try zig_cflags.toOwnedSlice(),
 807        .libs = try zig_libs.toOwnedSlice(),
 808    };
 809}
 810
 811/// Deprecated; use `compile.root_module.linkSystemLibrary(name, .{})` instead.
 812/// To be removed after 0.15.0 is tagged.
 813pub fn linkSystemLibrary(compile: *Compile, name: []const u8) void {
 814    return compile.root_module.linkSystemLibrary(name, .{});
 815}
 816
 817/// Deprecated; use `compile.root_module.linkSystemLibrary(name, options)` instead.
 818/// To be removed after 0.15.0 is tagged.
 819pub fn linkSystemLibrary2(
 820    compile: *Compile,
 821    name: []const u8,
 822    options: Module.LinkSystemLibraryOptions,
 823) void {
 824    return compile.root_module.linkSystemLibrary(name, options);
 825}
 826
 827/// Deprecated; use `c.root_module.linkFramework(name, .{})` instead.
 828/// To be removed after 0.15.0 is tagged.
 829pub fn linkFramework(c: *Compile, name: []const u8) void {
 830    c.root_module.linkFramework(name, .{});
 831}
 832
 833/// Deprecated; use `compile.root_module.addCSourceFiles(options)` instead.
 834/// To be removed after 0.15.0 is tagged.
 835pub fn addCSourceFiles(compile: *Compile, options: Module.AddCSourceFilesOptions) void {
 836    compile.root_module.addCSourceFiles(options);
 837}
 838
 839/// Deprecated; use `compile.root_module.addCSourceFile(source)` instead.
 840/// To be removed after 0.15.0 is tagged.
 841pub fn addCSourceFile(compile: *Compile, source: Module.CSourceFile) void {
 842    compile.root_module.addCSourceFile(source);
 843}
 844
 845/// Deprecated; use `compile.root_module.addWin32ResourceFile(source)` instead.
 846/// To be removed after 0.15.0 is tagged.
 847pub fn addWin32ResourceFile(compile: *Compile, source: Module.RcSourceFile) void {
 848    compile.root_module.addWin32ResourceFile(source);
 849}
 850
 851pub fn setVerboseLink(compile: *Compile, value: bool) void {
 852    compile.verbose_link = value;
 853}
 854
 855pub fn setVerboseCC(compile: *Compile, value: bool) void {
 856    compile.verbose_cc = value;
 857}
 858
 859pub fn setLibCFile(compile: *Compile, libc_file: ?LazyPath) void {
 860    const b = compile.step.owner;
 861    if (libc_file) |f| {
 862        compile.libc_file = f.dupe(b);
 863        f.addStepDependencies(&compile.step);
 864    } else {
 865        compile.libc_file = null;
 866    }
 867}
 868
 869fn getEmittedFileGeneric(compile: *Compile, output_file: *?*GeneratedFile) LazyPath {
 870    if (output_file.*) |file| return .{ .generated = .{ .file = file } };
 871    const arena = compile.step.owner.allocator;
 872    const generated_file = arena.create(GeneratedFile) catch @panic("OOM");
 873    generated_file.* = .{ .step = &compile.step };
 874    output_file.* = generated_file;
 875    return .{ .generated = .{ .file = generated_file } };
 876}
 877
 878/// Returns the path to the directory that contains the emitted binary file.
 879pub fn getEmittedBinDirectory(compile: *Compile) LazyPath {
 880    _ = compile.getEmittedBin();
 881    return compile.getEmittedFileGeneric(&compile.emit_directory);
 882}
 883
 884/// Returns the path to the generated executable, library or object file.
 885/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
 886pub fn getEmittedBin(compile: *Compile) LazyPath {
 887    return compile.getEmittedFileGeneric(&compile.generated_bin);
 888}
 889
 890/// Returns the path to the generated import library.
 891/// This function can only be called for libraries.
 892pub fn getEmittedImplib(compile: *Compile) LazyPath {
 893    assert(compile.kind == .lib);
 894    return compile.getEmittedFileGeneric(&compile.generated_implib);
 895}
 896
 897/// Returns the path to the generated header file.
 898/// This function can only be called for libraries or objects.
 899pub fn getEmittedH(compile: *Compile) LazyPath {
 900    assert(compile.kind != .exe and compile.kind != .@"test");
 901    return compile.getEmittedFileGeneric(&compile.generated_h);
 902}
 903
 904/// Returns the generated PDB file.
 905/// If the compilation does not produce a PDB file, this causes a FileNotFound error
 906/// at build time.
 907pub fn getEmittedPdb(compile: *Compile) LazyPath {
 908    _ = compile.getEmittedBin();
 909    return compile.getEmittedFileGeneric(&compile.generated_pdb);
 910}
 911
 912/// Returns the path to the generated documentation directory.
 913pub fn getEmittedDocs(compile: *Compile) LazyPath {
 914    return compile.getEmittedFileGeneric(&compile.generated_docs);
 915}
 916
 917/// Returns the path to the generated assembly code.
 918pub fn getEmittedAsm(compile: *Compile) LazyPath {
 919    return compile.getEmittedFileGeneric(&compile.generated_asm);
 920}
 921
 922/// Returns the path to the generated LLVM IR.
 923pub fn getEmittedLlvmIr(compile: *Compile) LazyPath {
 924    return compile.getEmittedFileGeneric(&compile.generated_llvm_ir);
 925}
 926
 927/// Returns the path to the generated LLVM BC.
 928pub fn getEmittedLlvmBc(compile: *Compile) LazyPath {
 929    return compile.getEmittedFileGeneric(&compile.generated_llvm_bc);
 930}
 931
 932/// Deprecated; use `compile.root_module.addAssemblyFile(source)` instead.
 933/// To be removed after 0.15.0 is tagged.
 934pub fn addAssemblyFile(compile: *Compile, source: LazyPath) void {
 935    compile.root_module.addAssemblyFile(source);
 936}
 937
 938/// Deprecated; use `compile.root_module.addObjectFile(source)` instead.
 939/// To be removed after 0.15.0 is tagged.
 940pub fn addObjectFile(compile: *Compile, source: LazyPath) void {
 941    compile.root_module.addObjectFile(source);
 942}
 943
 944/// Deprecated; use `compile.root_module.addObject(object)` instead.
 945/// To be removed after 0.15.0 is tagged.
 946pub fn addObject(compile: *Compile, object: *Compile) void {
 947    compile.root_module.addObject(object);
 948}
 949
 950/// Deprecated; use `compile.root_module.linkLibrary(library)` instead.
 951/// To be removed after 0.15.0 is tagged.
 952pub fn linkLibrary(compile: *Compile, library: *Compile) void {
 953    compile.root_module.linkLibrary(library);
 954}
 955
 956/// Deprecated; use `compile.root_module.addAfterIncludePath(lazy_path)` instead.
 957/// To be removed after 0.15.0 is tagged.
 958pub fn addAfterIncludePath(compile: *Compile, lazy_path: LazyPath) void {
 959    compile.root_module.addAfterIncludePath(lazy_path);
 960}
 961
 962/// Deprecated; use `compile.root_module.addSystemIncludePath(lazy_path)` instead.
 963/// To be removed after 0.15.0 is tagged.
 964pub fn addSystemIncludePath(compile: *Compile, lazy_path: LazyPath) void {
 965    compile.root_module.addSystemIncludePath(lazy_path);
 966}
 967
 968/// Deprecated; use `compile.root_module.addIncludePath(lazy_path)` instead.
 969/// To be removed after 0.15.0 is tagged.
 970pub fn addIncludePath(compile: *Compile, lazy_path: LazyPath) void {
 971    compile.root_module.addIncludePath(lazy_path);
 972}
 973
 974/// Deprecated; use `compile.root_module.addConfigHeader(config_header)` instead.
 975/// To be removed after 0.15.0 is tagged.
 976pub fn addConfigHeader(compile: *Compile, config_header: *Step.ConfigHeader) void {
 977    compile.root_module.addConfigHeader(config_header);
 978}
 979
 980/// Deprecated; use `compile.root_module.addEmbedPath(lazy_path)` instead.
 981/// To be removed after 0.15.0 is tagged.
 982pub fn addEmbedPath(compile: *Compile, lazy_path: LazyPath) void {
 983    compile.root_module.addEmbedPath(lazy_path);
 984}
 985
 986/// Deprecated; use `compile.root_module.addLibraryPath(directory_path)` instead.
 987/// To be removed after 0.15.0 is tagged.
 988pub fn addLibraryPath(compile: *Compile, directory_path: LazyPath) void {
 989    compile.root_module.addLibraryPath(directory_path);
 990}
 991
 992/// Deprecated; use `compile.root_module.addRPath(directory_path)` instead.
 993/// To be removed after 0.15.0 is tagged.
 994pub fn addRPath(compile: *Compile, directory_path: LazyPath) void {
 995    compile.root_module.addRPath(directory_path);
 996}
 997
 998/// Deprecated; use `compile.root_module.addSystemFrameworkPath(directory_path)` instead.
 999/// To be removed after 0.15.0 is tagged.
1000pub fn addSystemFrameworkPath(compile: *Compile, directory_path: LazyPath) void {
1001    compile.root_module.addSystemFrameworkPath(directory_path);
1002}
1003
1004/// Deprecated; use `compile.root_module.addFrameworkPath(directory_path)` instead.
1005/// To be removed after 0.15.0 is tagged.
1006pub fn addFrameworkPath(compile: *Compile, directory_path: LazyPath) void {
1007    compile.root_module.addFrameworkPath(directory_path);
1008}
1009
1010pub fn setExecCmd(compile: *Compile, args: []const ?[]const u8) void {
1011    const b = compile.step.owner;
1012    assert(compile.kind == .@"test");
1013    const duped_args = b.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
1014    for (args, 0..) |arg, i| {
1015        duped_args[i] = if (arg) |a| b.dupe(a) else null;
1016    }
1017    compile.exec_cmd_args = duped_args;
1018}
1019
1020const CliNamedModules = struct {
1021    modules: std.AutoArrayHashMapUnmanaged(*Module, void),
1022    names: std.StringArrayHashMapUnmanaged(void),
1023
1024    /// Traverse the whole dependency graph and give every module a unique
1025    /// name, ideally one named after what it's called somewhere in the graph.
1026    /// It will help here to have both a mapping from module to name and a set
1027    /// of all the currently-used names.
1028    fn init(arena: Allocator, root_module: *Module) Allocator.Error!CliNamedModules {
1029        var compile: CliNamedModules = .{
1030            .modules = .{},
1031            .names = .{},
1032        };
1033        const graph = root_module.getGraph();
1034        {
1035            assert(graph.modules[0] == root_module);
1036            try compile.modules.put(arena, root_module, {});
1037            try compile.names.put(arena, "root", {});
1038        }
1039        for (graph.modules[1..], graph.names[1..]) |mod, orig_name| {
1040            var name = orig_name;
1041            var n: usize = 0;
1042            while (true) {
1043                const gop = try compile.names.getOrPut(arena, name);
1044                if (!gop.found_existing) {
1045                    try compile.modules.putNoClobber(arena, mod, {});
1046                    break;
1047                }
1048                name = try std.fmt.allocPrint(arena, "{s}{d}", .{ orig_name, n });
1049                n += 1;
1050            }
1051        }
1052        return compile;
1053    }
1054};
1055
1056fn getGeneratedFilePath(compile: *Compile, comptime tag_name: []const u8, asking_step: ?*Step) []const u8 {
1057    const maybe_path: ?*GeneratedFile = @field(compile, tag_name);
1058
1059    const generated_file = maybe_path orelse {
1060        const w, const ttyconf = std.debug.lockStderrWriter(&.{});
1061        std.Build.dumpBadGetPathHelp(&compile.step, w, ttyconf, compile.step.owner, asking_step) catch {};
1062        std.debug.unlockStderrWriter();
1063        @panic("missing emit option for " ++ tag_name);
1064    };
1065
1066    const path = generated_file.path orelse {
1067        const w, const ttyconf = std.debug.lockStderrWriter(&.{});
1068        std.Build.dumpBadGetPathHelp(&compile.step, w, ttyconf, compile.step.owner, asking_step) catch {};
1069        std.debug.unlockStderrWriter();
1070        @panic(tag_name ++ " is null. Is there a missing step dependency?");
1071    };
1072
1073    return path;
1074}
1075
1076fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
1077    const step = &compile.step;
1078    const b = step.owner;
1079    const arena = b.allocator;
1080
1081    var zig_args = std.array_list.Managed([]const u8).init(arena);
1082    defer zig_args.deinit();
1083
1084    try zig_args.append(b.graph.zig_exe);
1085
1086    const cmd = switch (compile.kind) {
1087        .lib => "build-lib",
1088        .exe => "build-exe",
1089        .obj => "build-obj",
1090        .@"test" => "test",
1091        .test_obj => "test-obj",
1092    };
1093    try zig_args.append(cmd);
1094
1095    if (b.reference_trace) |some| {
1096        try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some}));
1097    }
1098    try addFlag(&zig_args, "allow-so-scripts", compile.allow_so_scripts orelse b.graph.allow_so_scripts);
1099
1100    try addFlag(&zig_args, "llvm", compile.use_llvm);
1101    try addFlag(&zig_args, "lld", compile.use_lld);
1102    try addFlag(&zig_args, "new-linker", compile.use_new_linker);
1103
1104    if (compile.root_module.resolved_target.?.query.ofmt) |ofmt| {
1105        try zig_args.append(try std.fmt.allocPrint(arena, "-ofmt={s}", .{@tagName(ofmt)}));
1106    }
1107
1108    switch (compile.entry) {
1109        .default => {},
1110        .disabled => try zig_args.append("-fno-entry"),
1111        .enabled => try zig_args.append("-fentry"),
1112        .symbol_name => |entry_name| {
1113            try zig_args.append(try std.fmt.allocPrint(arena, "-fentry={s}", .{entry_name}));
1114        },
1115    }
1116
1117    {
1118        var symbol_it = compile.force_undefined_symbols.keyIterator();
1119        while (symbol_it.next()) |symbol_name| {
1120            try zig_args.append("--force_undefined");
1121            try zig_args.append(symbol_name.*);
1122        }
1123    }
1124
1125    if (compile.stack_size) |stack_size| {
1126        try zig_args.append("--stack");
1127        try zig_args.append(try std.fmt.allocPrint(arena, "{}", .{stack_size}));
1128    }
1129
1130    if (fuzz) {
1131        try zig_args.append("-ffuzz");
1132    }
1133
1134    {
1135        // Stores system libraries that have already been seen for at least one
1136        // module, along with any arguments that need to be passed to the
1137        // compiler for each module individually.
1138        var seen_system_libs: std.StringHashMapUnmanaged([]const []const u8) = .empty;
1139        var frameworks: std.StringArrayHashMapUnmanaged(Module.LinkFrameworkOptions) = .empty;
1140
1141        var prev_has_cflags = false;
1142        var prev_has_rcflags = false;
1143        var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first;
1144        var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic;
1145        // Track the number of positional arguments so that a nice error can be
1146        // emitted if there is nothing to link.
1147        var total_linker_objects: usize = @intFromBool(compile.root_module.root_source_file != null);
1148
1149        // Fully recursive iteration including dynamic libraries to detect
1150        // libc and libc++ linkage.
1151        for (compile.getCompileDependencies(true)) |some_compile| {
1152            for (some_compile.root_module.getGraph().modules) |mod| {
1153                if (mod.link_libc == true) compile.is_linking_libc = true;
1154                if (mod.link_libcpp == true) compile.is_linking_libcpp = true;
1155            }
1156        }
1157
1158        var cli_named_modules = try CliNamedModules.init(arena, compile.root_module);
1159
1160        // For this loop, don't chase dynamic libraries because their link
1161        // objects are already linked.
1162        for (compile.getCompileDependencies(false)) |dep_compile| {
1163            for (dep_compile.root_module.getGraph().modules) |mod| {
1164                // While walking transitive dependencies, if a given link object is
1165                // already included in a library, it should not redundantly be
1166                // placed on the linker line of the dependee.
1167                const my_responsibility = dep_compile == compile;
1168                const already_linked = !my_responsibility and dep_compile.isDynamicLibrary();
1169
1170                // Inherit dependencies on darwin frameworks.
1171                if (!already_linked) {
1172                    for (mod.frameworks.keys(), mod.frameworks.values()) |name, info| {
1173                        try frameworks.put(arena, name, info);
1174                    }
1175                }
1176
1177                // Inherit dependencies on system libraries and static libraries.
1178                for (mod.link_objects.items) |link_object| {
1179                    switch (link_object) {
1180                        .static_path => |static_path| {
1181                            if (my_responsibility) {
1182                                try zig_args.append(static_path.getPath2(mod.owner, step));
1183                                total_linker_objects += 1;
1184                            }
1185                        },
1186                        .system_lib => |system_lib| {
1187                            const system_lib_gop = try seen_system_libs.getOrPut(arena, system_lib.name);
1188                            if (system_lib_gop.found_existing) {
1189                                try zig_args.appendSlice(system_lib_gop.value_ptr.*);
1190                                continue;
1191                            } else {
1192                                system_lib_gop.value_ptr.* = &.{};
1193                            }
1194
1195                            if (already_linked)
1196                                continue;
1197
1198                            if ((system_lib.search_strategy != prev_search_strategy or
1199                                system_lib.preferred_link_mode != prev_preferred_link_mode) and
1200                                compile.linkage != .static)
1201                            {
1202                                switch (system_lib.search_strategy) {
1203                                    .no_fallback => switch (system_lib.preferred_link_mode) {
1204                                        .dynamic => try zig_args.append("-search_dylibs_only"),
1205                                        .static => try zig_args.append("-search_static_only"),
1206                                    },
1207                                    .paths_first => switch (system_lib.preferred_link_mode) {
1208                                        .dynamic => try zig_args.append("-search_paths_first"),
1209                                        .static => try zig_args.append("-search_paths_first_static"),
1210                                    },
1211                                    .mode_first => switch (system_lib.preferred_link_mode) {
1212                                        .dynamic => try zig_args.append("-search_dylibs_first"),
1213                                        .static => try zig_args.append("-search_static_first"),
1214                                    },
1215                                }
1216                                prev_search_strategy = system_lib.search_strategy;
1217                                prev_preferred_link_mode = system_lib.preferred_link_mode;
1218                            }
1219
1220                            const prefix: []const u8 = prefix: {
1221                                if (system_lib.needed) break :prefix "-needed-l";
1222                                if (system_lib.weak) break :prefix "-weak-l";
1223                                break :prefix "-l";
1224                            };
1225                            switch (system_lib.use_pkg_config) {
1226                                .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
1227                                .yes, .force => {
1228                                    if (compile.runPkgConfig(system_lib.name)) |result| {
1229                                        try zig_args.appendSlice(result.cflags);
1230                                        try zig_args.appendSlice(result.libs);
1231                                        try seen_system_libs.put(arena, system_lib.name, result.cflags);
1232                                    } else |err| switch (err) {
1233                                        error.PkgConfigInvalidOutput,
1234                                        error.PkgConfigCrashed,
1235                                        error.PkgConfigFailed,
1236                                        error.PkgConfigNotInstalled,
1237                                        error.PackageNotFound,
1238                                        => switch (system_lib.use_pkg_config) {
1239                                            .yes => {
1240                                                // pkg-config failed, so fall back to linking the library
1241                                                // by name directly.
1242                                                try zig_args.append(b.fmt("{s}{s}", .{
1243                                                    prefix,
1244                                                    system_lib.name,
1245                                                }));
1246                                            },
1247                                            .force => {
1248                                                panic("pkg-config failed for library {s}", .{system_lib.name});
1249                                            },
1250                                            .no => unreachable,
1251                                        },
1252
1253                                        else => |e| return e,
1254                                    }
1255                                },
1256                            }
1257                        },
1258                        .other_step => |other| {
1259                            switch (other.kind) {
1260                                .exe => return step.fail("cannot link with an executable build artifact", .{}),
1261                                .@"test" => return step.fail("cannot link with a test", .{}),
1262                                .obj, .test_obj => {
1263                                    const included_in_lib_or_obj = !my_responsibility and
1264                                        (dep_compile.kind == .lib or dep_compile.kind == .obj or dep_compile.kind == .test_obj);
1265                                    if (!already_linked and !included_in_lib_or_obj) {
1266                                        try zig_args.append(other.getEmittedBin().getPath2(b, step));
1267                                        total_linker_objects += 1;
1268                                    }
1269                                },
1270                                .lib => l: {
1271                                    const other_produces_implib = other.producesImplib();
1272                                    const other_is_static = other_produces_implib or other.isStaticLibrary();
1273
1274                                    if (compile.isStaticLibrary() and other_is_static) {
1275                                        // Avoid putting a static library inside a static library.
1276                                        break :l;
1277                                    }
1278
1279                                    // For DLLs, we must link against the implib.
1280                                    // For everything else, we directly link
1281                                    // against the library file.
1282                                    const full_path_lib = if (other_produces_implib)
1283                                        other.getGeneratedFilePath("generated_implib", &compile.step)
1284                                    else
1285                                        other.getGeneratedFilePath("generated_bin", &compile.step);
1286
1287                                    try zig_args.append(full_path_lib);
1288                                    total_linker_objects += 1;
1289
1290                                    if (other.linkage == .dynamic and
1291                                        compile.rootModuleTarget().os.tag != .windows)
1292                                    {
1293                                        if (fs.path.dirname(full_path_lib)) |dirname| {
1294                                            try zig_args.append("-rpath");
1295                                            try zig_args.append(dirname);
1296                                        }
1297                                    }
1298                                },
1299                            }
1300                        },
1301                        .assembly_file => |asm_file| l: {
1302                            if (!my_responsibility) break :l;
1303
1304                            if (prev_has_cflags) {
1305                                try zig_args.append("-cflags");
1306                                try zig_args.append("--");
1307                                prev_has_cflags = false;
1308                            }
1309                            try zig_args.append(asm_file.getPath2(mod.owner, step));
1310                            total_linker_objects += 1;
1311                        },
1312
1313                        .c_source_file => |c_source_file| l: {
1314                            if (!my_responsibility) break :l;
1315
1316                            if (prev_has_cflags or c_source_file.flags.len != 0) {
1317                                try zig_args.append("-cflags");
1318                                for (c_source_file.flags) |arg| {
1319                                    try zig_args.append(arg);
1320                                }
1321                                try zig_args.append("--");
1322                            }
1323                            prev_has_cflags = (c_source_file.flags.len != 0);
1324
1325                            if (c_source_file.language) |lang| {
1326                                try zig_args.append("-x");
1327                                try zig_args.append(lang.internalIdentifier());
1328                            }
1329
1330                            try zig_args.append(c_source_file.file.getPath2(mod.owner, step));
1331
1332                            if (c_source_file.language != null) {
1333                                try zig_args.append("-x");
1334                                try zig_args.append("none");
1335                            }
1336                            total_linker_objects += 1;
1337                        },
1338
1339                        .c_source_files => |c_source_files| l: {
1340                            if (!my_responsibility) break :l;
1341
1342                            if (prev_has_cflags or c_source_files.flags.len != 0) {
1343                                try zig_args.append("-cflags");
1344                                for (c_source_files.flags) |arg| {
1345                                    try zig_args.append(arg);
1346                                }
1347                                try zig_args.append("--");
1348                            }
1349                            prev_has_cflags = (c_source_files.flags.len != 0);
1350
1351                            if (c_source_files.language) |lang| {
1352                                try zig_args.append("-x");
1353                                try zig_args.append(lang.internalIdentifier());
1354                            }
1355
1356                            const root_path = c_source_files.root.getPath2(mod.owner, step);
1357                            for (c_source_files.files) |file| {
1358                                try zig_args.append(b.pathJoin(&.{ root_path, file }));
1359                            }
1360
1361                            if (c_source_files.language != null) {
1362                                try zig_args.append("-x");
1363                                try zig_args.append("none");
1364                            }
1365
1366                            total_linker_objects += c_source_files.files.len;
1367                        },
1368
1369                        .win32_resource_file => |rc_source_file| l: {
1370                            if (!my_responsibility) break :l;
1371
1372                            if (rc_source_file.flags.len == 0 and rc_source_file.include_paths.len == 0) {
1373                                if (prev_has_rcflags) {
1374                                    try zig_args.append("-rcflags");
1375                                    try zig_args.append("--");
1376                                    prev_has_rcflags = false;
1377                                }
1378                            } else {
1379                                try zig_args.append("-rcflags");
1380                                for (rc_source_file.flags) |arg| {
1381                                    try zig_args.append(arg);
1382                                }
1383                                for (rc_source_file.include_paths) |include_path| {
1384                                    try zig_args.append("/I");
1385                                    try zig_args.append(include_path.getPath2(mod.owner, step));
1386                                }
1387                                try zig_args.append("--");
1388                                prev_has_rcflags = true;
1389                            }
1390                            try zig_args.append(rc_source_file.file.getPath2(mod.owner, step));
1391                            total_linker_objects += 1;
1392                        },
1393                    }
1394                }
1395
1396                // We need to emit the --mod argument here so that the above link objects
1397                // have the correct parent module, but only if the module is part of
1398                // this compilation.
1399                if (!my_responsibility) continue;
1400                if (cli_named_modules.modules.getIndex(mod)) |module_cli_index| {
1401                    const module_cli_name = cli_named_modules.names.keys()[module_cli_index];
1402                    try mod.appendZigProcessFlags(&zig_args, step);
1403
1404                    // --dep arguments
1405                    try zig_args.ensureUnusedCapacity(mod.import_table.count() * 2);
1406                    for (mod.import_table.keys(), mod.import_table.values()) |name, import| {
1407                        const import_index = cli_named_modules.modules.getIndex(import).?;
1408                        const import_cli_name = cli_named_modules.names.keys()[import_index];
1409                        zig_args.appendAssumeCapacity("--dep");
1410                        if (std.mem.eql(u8, import_cli_name, name)) {
1411                            zig_args.appendAssumeCapacity(import_cli_name);
1412                        } else {
1413                            zig_args.appendAssumeCapacity(b.fmt("{s}={s}", .{ name, import_cli_name }));
1414                        }
1415                    }
1416
1417                    // When the CLI sees a -M argument, it determines whether it
1418                    // implies the existence of a Zig compilation unit based on
1419                    // whether there is a root source file. If there is no root
1420                    // source file, then this is not a zig compilation unit - it is
1421                    // perhaps a set of linker objects, or C source files instead.
1422                    // Linker objects are added to the CLI globally, while C source
1423                    // files must have a module parent.
1424                    if (mod.root_source_file) |lp| {
1425                        const src = lp.getPath2(mod.owner, step);
1426                        try zig_args.append(b.fmt("-M{s}={s}", .{ module_cli_name, src }));
1427                    } else if (moduleNeedsCliArg(mod)) {
1428                        try zig_args.append(b.fmt("-M{s}", .{module_cli_name}));
1429                    }
1430                }
1431            }
1432        }
1433
1434        if (total_linker_objects == 0) {
1435            return step.fail("the linker needs one or more objects to link", .{});
1436        }
1437
1438        for (frameworks.keys(), frameworks.values()) |name, info| {
1439            if (info.needed) {
1440                try zig_args.append("-needed_framework");
1441            } else if (info.weak) {
1442                try zig_args.append("-weak_framework");
1443            } else {
1444                try zig_args.append("-framework");
1445            }
1446            try zig_args.append(name);
1447        }
1448
1449        if (compile.is_linking_libcpp) {
1450            try zig_args.append("-lc++");
1451        }
1452
1453        if (compile.is_linking_libc) {
1454            try zig_args.append("-lc");
1455        }
1456    }
1457
1458    if (compile.win32_manifest) |manifest_file| {
1459        try zig_args.append(manifest_file.getPath2(b, step));
1460    }
1461
1462    if (compile.image_base) |image_base| {
1463        try zig_args.append("--image-base");
1464        try zig_args.append(b.fmt("0x{x}", .{image_base}));
1465    }
1466
1467    for (compile.filters) |filter| {
1468        try zig_args.append("--test-filter");
1469        try zig_args.append(filter);
1470    }
1471
1472    if (compile.test_runner) |test_runner| {
1473        try zig_args.append("--test-runner");
1474        try zig_args.append(test_runner.path.getPath2(b, step));
1475    }
1476
1477    for (b.debug_log_scopes) |log_scope| {
1478        try zig_args.append("--debug-log");
1479        try zig_args.append(log_scope);
1480    }
1481
1482    if (b.debug_compile_errors) {
1483        try zig_args.append("--debug-compile-errors");
1484    }
1485
1486    if (b.debug_incremental) {
1487        try zig_args.append("--debug-incremental");
1488    }
1489
1490    if (b.verbose_cimport) try zig_args.append("--verbose-cimport");
1491    if (b.verbose_air) try zig_args.append("--verbose-air");
1492    if (b.verbose_llvm_ir) |path| try zig_args.append(b.fmt("--verbose-llvm-ir={s}", .{path}));
1493    if (b.verbose_llvm_bc) |path| try zig_args.append(b.fmt("--verbose-llvm-bc={s}", .{path}));
1494    if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link");
1495    if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc");
1496    if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
1497    if (b.graph.time_report) try zig_args.append("--time-report");
1498
1499    if (compile.generated_asm != null) try zig_args.append("-femit-asm");
1500    if (compile.generated_bin == null) try zig_args.append("-fno-emit-bin");
1501    if (compile.generated_docs != null) try zig_args.append("-femit-docs");
1502    if (compile.generated_implib != null) try zig_args.append("-femit-implib");
1503    if (compile.generated_llvm_bc != null) try zig_args.append("-femit-llvm-bc");
1504    if (compile.generated_llvm_ir != null) try zig_args.append("-femit-llvm-ir");
1505    if (compile.generated_h != null) try zig_args.append("-femit-h");
1506
1507    try addFlag(&zig_args, "formatted-panics", compile.formatted_panics);
1508
1509    switch (compile.compress_debug_sections) {
1510        .none => {},
1511        .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1512        .zstd => try zig_args.append("--compress-debug-sections=zstd"),
1513    }
1514
1515    if (compile.link_eh_frame_hdr) {
1516        try zig_args.append("--eh-frame-hdr");
1517    }
1518    if (compile.link_emit_relocs) {
1519        try zig_args.append("--emit-relocs");
1520    }
1521    if (compile.link_function_sections) {
1522        try zig_args.append("-ffunction-sections");
1523    }
1524    if (compile.link_data_sections) {
1525        try zig_args.append("-fdata-sections");
1526    }
1527    if (compile.link_gc_sections) |x| {
1528        try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1529    }
1530    if (!compile.linker_dynamicbase) {
1531        try zig_args.append("--no-dynamicbase");
1532    }
1533    if (compile.linker_allow_shlib_undefined) |x| {
1534        try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1535    }
1536    if (compile.link_z_notext) {
1537        try zig_args.append("-z");
1538        try zig_args.append("notext");
1539    }
1540    if (!compile.link_z_relro) {
1541        try zig_args.append("-z");
1542        try zig_args.append("norelro");
1543    }
1544    if (compile.link_z_lazy) {
1545        try zig_args.append("-z");
1546        try zig_args.append("lazy");
1547    }
1548    if (compile.link_z_common_page_size) |size| {
1549        try zig_args.append("-z");
1550        try zig_args.append(b.fmt("common-page-size={d}", .{size}));
1551    }
1552    if (compile.link_z_max_page_size) |size| {
1553        try zig_args.append("-z");
1554        try zig_args.append(b.fmt("max-page-size={d}", .{size}));
1555    }
1556    if (compile.link_z_defs) {
1557        try zig_args.append("-z");
1558        try zig_args.append("defs");
1559    }
1560
1561    if (compile.libc_file) |libc_file| {
1562        try zig_args.append("--libc");
1563        try zig_args.append(libc_file.getPath2(b, step));
1564    } else if (b.libc_file) |libc_file| {
1565        try zig_args.append("--libc");
1566        try zig_args.append(libc_file);
1567    }
1568
1569    try zig_args.append("--cache-dir");
1570    try zig_args.append(b.cache_root.path orelse ".");
1571
1572    try zig_args.append("--global-cache-dir");
1573    try zig_args.append(b.graph.global_cache_root.path orelse ".");
1574
1575    if (b.graph.debug_compiler_runtime_libs) try zig_args.append("--debug-rt");
1576
1577    try zig_args.append("--name");
1578    try zig_args.append(compile.name);
1579
1580    if (compile.linkage) |some| switch (some) {
1581        .dynamic => try zig_args.append("-dynamic"),
1582        .static => try zig_args.append("-static"),
1583    };
1584    if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic) {
1585        if (compile.version) |version| {
1586            try zig_args.append("--version");
1587            try zig_args.append(b.fmt("{f}", .{version}));
1588        }
1589
1590        if (compile.rootModuleTarget().os.tag.isDarwin()) {
1591            const install_name = compile.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{
1592                compile.rootModuleTarget().libPrefix(),
1593                compile.name,
1594                compile.rootModuleTarget().dynamicLibSuffix(),
1595            });
1596            try zig_args.append("-install_name");
1597            try zig_args.append(install_name);
1598        }
1599    }
1600
1601    if (compile.entitlements) |entitlements| {
1602        try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1603    }
1604    if (compile.pagezero_size) |pagezero_size| {
1605        const size = try std.fmt.allocPrint(arena, "{x}", .{pagezero_size});
1606        try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1607    }
1608    if (compile.headerpad_size) |headerpad_size| {
1609        const size = try std.fmt.allocPrint(arena, "{x}", .{headerpad_size});
1610        try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1611    }
1612    if (compile.headerpad_max_install_names) {
1613        try zig_args.append("-headerpad_max_install_names");
1614    }
1615    if (compile.dead_strip_dylibs) {
1616        try zig_args.append("-dead_strip_dylibs");
1617    }
1618    if (compile.force_load_objc) {
1619        try zig_args.append("-ObjC");
1620    }
1621    if (compile.discard_local_symbols) {
1622        try zig_args.append("--discard-all");
1623    }
1624
1625    try addFlag(&zig_args, "compiler-rt", compile.bundle_compiler_rt);
1626    try addFlag(&zig_args, "ubsan-rt", compile.bundle_ubsan_rt);
1627    try addFlag(&zig_args, "dll-export-fns", compile.dll_export_fns);
1628    if (compile.rdynamic) {
1629        try zig_args.append("-rdynamic");
1630    }
1631    if (compile.import_memory) {
1632        try zig_args.append("--import-memory");
1633    }
1634    if (compile.export_memory) {
1635        try zig_args.append("--export-memory");
1636    }
1637    if (compile.import_symbols) {
1638        try zig_args.append("--import-symbols");
1639    }
1640    if (compile.import_table) {
1641        try zig_args.append("--import-table");
1642    }
1643    if (compile.export_table) {
1644        try zig_args.append("--export-table");
1645    }
1646    if (compile.initial_memory) |initial_memory| {
1647        try zig_args.append(b.fmt("--initial-memory={d}", .{initial_memory}));
1648    }
1649    if (compile.max_memory) |max_memory| {
1650        try zig_args.append(b.fmt("--max-memory={d}", .{max_memory}));
1651    }
1652    if (compile.shared_memory) {
1653        try zig_args.append("--shared-memory");
1654    }
1655    if (compile.global_base) |global_base| {
1656        try zig_args.append(b.fmt("--global-base={d}", .{global_base}));
1657    }
1658
1659    if (compile.wasi_exec_model) |model| {
1660        try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)}));
1661    }
1662    if (compile.linker_script) |linker_script| {
1663        try zig_args.append("--script");
1664        try zig_args.append(linker_script.getPath2(b, step));
1665    }
1666
1667    if (compile.version_script) |version_script| {
1668        try zig_args.append("--version-script");
1669        try zig_args.append(version_script.getPath2(b, step));
1670    }
1671    if (compile.linker_allow_undefined_version) |x| {
1672        try zig_args.append(if (x) "--undefined-version" else "--no-undefined-version");
1673    }
1674
1675    if (compile.linker_enable_new_dtags) |enabled| {
1676        try zig_args.append(if (enabled) "--enable-new-dtags" else "--disable-new-dtags");
1677    }
1678
1679    if (compile.kind == .@"test") {
1680        if (compile.exec_cmd_args) |exec_cmd_args| {
1681            for (exec_cmd_args) |cmd_arg| {
1682                if (cmd_arg) |arg| {
1683                    try zig_args.append("--test-cmd");
1684                    try zig_args.append(arg);
1685                } else {
1686                    try zig_args.append("--test-cmd-bin");
1687                }
1688            }
1689        }
1690    }
1691
1692    if (b.sysroot) |sysroot| {
1693        try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1694    }
1695
1696    // -I and -L arguments that appear after the last --mod argument apply to all modules.
1697    for (b.search_prefixes.items) |search_prefix| {
1698        var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| {
1699            return step.fail("unable to open prefix directory '{s}': {s}", .{
1700                search_prefix, @errorName(err),
1701            });
1702        };
1703        defer prefix_dir.close();
1704
1705        // Avoid passing -L and -I flags for nonexistent directories.
1706        // This prevents a warning, that should probably be upgraded to an error in Zig's
1707        // CLI parsing code, when the linker sees an -L directory that does not exist.
1708
1709        if (prefix_dir.access("lib", .{})) |_| {
1710            try zig_args.appendSlice(&.{
1711                "-L", b.pathJoin(&.{ search_prefix, "lib" }),
1712            });
1713        } else |err| switch (err) {
1714            error.FileNotFound => {},
1715            else => |e| return step.fail("unable to access '{s}/lib' directory: {s}", .{
1716                search_prefix, @errorName(e),
1717            }),
1718        }
1719
1720        if (prefix_dir.access("include", .{})) |_| {
1721            try zig_args.appendSlice(&.{
1722                "-I", b.pathJoin(&.{ search_prefix, "include" }),
1723            });
1724        } else |err| switch (err) {
1725            error.FileNotFound => {},
1726            else => |e| return step.fail("unable to access '{s}/include' directory: {s}", .{
1727                search_prefix, @errorName(e),
1728            }),
1729        }
1730    }
1731
1732    if (compile.rc_includes != .any) {
1733        try zig_args.append("-rcincludes");
1734        try zig_args.append(@tagName(compile.rc_includes));
1735    }
1736
1737    try addFlag(&zig_args, "each-lib-rpath", compile.each_lib_rpath);
1738
1739    if (compile.build_id orelse b.build_id) |build_id| {
1740        try zig_args.append(switch (build_id) {
1741            .hexstring => |hs| b.fmt("--build-id=0x{x}", .{hs.toSlice()}),
1742            .none, .fast, .uuid, .sha1, .md5 => b.fmt("--build-id={s}", .{@tagName(build_id)}),
1743        });
1744    }
1745
1746    const opt_zig_lib_dir = if (compile.zig_lib_dir) |dir|
1747        dir.getPath2(b, step)
1748    else if (b.graph.zig_lib_directory.path) |_|
1749        b.fmt("{f}", .{b.graph.zig_lib_directory})
1750    else
1751        null;
1752
1753    if (opt_zig_lib_dir) |zig_lib_dir| {
1754        try zig_args.append("--zig-lib-dir");
1755        try zig_args.append(zig_lib_dir);
1756    }
1757
1758    try addFlag(&zig_args, "PIE", compile.pie);
1759
1760    if (compile.lto) |lto| {
1761        try zig_args.append(switch (lto) {
1762            .full => "-flto=full",
1763            .thin => "-flto=thin",
1764            .none => "-fno-lto",
1765        });
1766    } else try addFlag(&zig_args, "lto", compile.want_lto);
1767
1768    try addFlag(&zig_args, "sanitize-coverage-trace-pc-guard", compile.sanitize_coverage_trace_pc_guard);
1769
1770    if (compile.subsystem) |subsystem| {
1771        try zig_args.append("--subsystem");
1772        try zig_args.append(@tagName(subsystem));
1773    }
1774
1775    if (compile.mingw_unicode_entry_point) {
1776        try zig_args.append("-municode");
1777    }
1778
1779    if (compile.error_limit) |err_limit| try zig_args.appendSlice(&.{
1780        "--error-limit", b.fmt("{d}", .{err_limit}),
1781    });
1782
1783    try addFlag(&zig_args, "incremental", b.graph.incremental);
1784
1785    try zig_args.append("--listen=-");
1786
1787    // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1788    // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1789    // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1790    // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1791    var args_length: usize = 0;
1792    for (zig_args.items) |arg| {
1793        args_length += arg.len + 1; // +1 to account for null terminator
1794    }
1795    if (args_length >= 30 * 1024) {
1796        try b.cache_root.handle.makePath("args");
1797
1798        const args_to_escape = zig_args.items[2..];
1799        var escaped_args = try std.array_list.Managed([]const u8).initCapacity(arena, args_to_escape.len);
1800        arg_blk: for (args_to_escape) |arg| {
1801            for (arg, 0..) |c, arg_idx| {
1802                if (c == '\\' or c == '"') {
1803                    // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1804                    var escaped: std.ArrayList(u8) = .empty;
1805                    try escaped.ensureTotalCapacityPrecise(arena, arg.len + 1);
1806                    try escaped.appendSlice(arena, arg[0..arg_idx]);
1807                    for (arg[arg_idx..]) |to_escape| {
1808                        if (to_escape == '\\' or to_escape == '"') try escaped.append(arena, '\\');
1809                        try escaped.append(arena, to_escape);
1810                    }
1811                    escaped_args.appendAssumeCapacity(escaped.items);
1812                    continue :arg_blk;
1813                }
1814            }
1815            escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1816        }
1817
1818        // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1819        // other zig build commands running in parallel.
1820        const partially_quoted = try std.mem.join(arena, "\" \"", escaped_args.items);
1821        const args = try std.mem.concat(arena, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1822
1823        var args_hash: [Sha256.digest_length]u8 = undefined;
1824        Sha256.hash(args, &args_hash, .{});
1825        var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1826        _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});
1827
1828        const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
1829        if (b.cache_root.handle.access(args_file, .{})) |_| {
1830            // The args file is already present from a previous run.
1831        } else |err| switch (err) {
1832            error.FileNotFound => {
1833                try b.cache_root.handle.makePath("tmp");
1834                const rand_int = std.crypto.random.int(u64);
1835                const tmp_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
1836                try b.cache_root.handle.writeFile(.{ .sub_path = tmp_path, .data = args });
1837                defer b.cache_root.handle.deleteFile(tmp_path) catch {
1838                    // It's fine if the temporary file can't be cleaned up.
1839                };
1840                b.cache_root.handle.rename(tmp_path, args_file) catch |rename_err| switch (rename_err) {
1841                    error.PathAlreadyExists => {
1842                        // The args file was created by another concurrent build process.
1843                    },
1844                    else => |other_err| return other_err,
1845                };
1846            },
1847            else => |other_err| return other_err,
1848        }
1849
1850        const resolved_args_file = try mem.concat(arena, u8, &.{
1851            "@",
1852            try b.cache_root.join(arena, &.{args_file}),
1853        });
1854
1855        zig_args.shrinkRetainingCapacity(2);
1856        try zig_args.append(resolved_args_file);
1857    }
1858
1859    return try zig_args.toOwnedSlice();
1860}
1861
1862fn make(step: *Step, options: Step.MakeOptions) !void {
1863    const b = step.owner;
1864    const compile: *Compile = @fieldParentPtr("step", step);
1865
1866    const zig_args = try getZigArgs(compile, false);
1867
1868    const maybe_output_dir = step.evalZigProcess(
1869        zig_args,
1870        options.progress_node,
1871        (b.graph.incremental == true) and (options.watch or options.web_server != null),
1872        options.web_server,
1873        options.gpa,
1874    ) catch |err| switch (err) {
1875        error.NeedCompileErrorCheck => {
1876            assert(compile.expect_errors != null);
1877            try checkCompileErrors(compile);
1878            return;
1879        },
1880        else => |e| return e,
1881    };
1882
1883    // Update generated files
1884    if (maybe_output_dir) |output_dir| {
1885        if (compile.emit_directory) |lp| {
1886            lp.path = b.fmt("{f}", .{output_dir});
1887        }
1888
1889        // zig fmt: off
1890        if (compile.generated_bin)     |lp| lp.path = compile.outputPath(output_dir, .bin);
1891        if (compile.generated_pdb)     |lp| lp.path = compile.outputPath(output_dir, .pdb);
1892        if (compile.generated_implib)  |lp| lp.path = compile.outputPath(output_dir, .implib);
1893        if (compile.generated_h)       |lp| lp.path = compile.outputPath(output_dir, .h);
1894        if (compile.generated_docs)    |lp| lp.path = compile.outputPath(output_dir, .docs);
1895        if (compile.generated_asm)     |lp| lp.path = compile.outputPath(output_dir, .@"asm");
1896        if (compile.generated_llvm_ir) |lp| lp.path = compile.outputPath(output_dir, .llvm_ir);
1897        if (compile.generated_llvm_bc) |lp| lp.path = compile.outputPath(output_dir, .llvm_bc);
1898        // zig fmt: on
1899    }
1900
1901    if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic and
1902        compile.version != null and std.Build.wantSharedLibSymLinks(compile.rootModuleTarget()))
1903    {
1904        try doAtomicSymLinks(
1905            step,
1906            compile.getEmittedBin().getPath2(b, step),
1907            compile.major_only_filename.?,
1908            compile.name_only_filename.?,
1909        );
1910    }
1911}
1912fn outputPath(c: *Compile, out_dir: std.Build.Cache.Path, ea: std.zig.EmitArtifact) []const u8 {
1913    const arena = c.step.owner.graph.arena;
1914    const name = ea.cacheName(arena, .{
1915        .root_name = c.name,
1916        .target = &c.root_module.resolved_target.?.result,
1917        .output_mode = switch (c.kind) {
1918            .lib => .Lib,
1919            .obj, .test_obj => .Obj,
1920            .exe, .@"test" => .Exe,
1921        },
1922        .link_mode = c.linkage,
1923        .version = c.version,
1924    }) catch @panic("OOM");
1925    return out_dir.joinString(arena, name) catch @panic("OOM");
1926}
1927
1928pub fn rebuildInFuzzMode(c: *Compile, gpa: Allocator, progress_node: std.Progress.Node) !Path {
1929    c.step.result_error_msgs.clearRetainingCapacity();
1930    c.step.result_stderr = "";
1931
1932    c.step.result_error_bundle.deinit(gpa);
1933    c.step.result_error_bundle = std.zig.ErrorBundle.empty;
1934
1935    if (c.step.result_failed_command) |cmd| {
1936        gpa.free(cmd);
1937        c.step.result_failed_command = null;
1938    }
1939
1940    const zig_args = try getZigArgs(c, true);
1941    const maybe_output_bin_path = try c.step.evalZigProcess(zig_args, progress_node, false, null, gpa);
1942    return maybe_output_bin_path.?;
1943}
1944
1945pub fn doAtomicSymLinks(
1946    step: *Step,
1947    output_path: []const u8,
1948    filename_major_only: []const u8,
1949    filename_name_only: []const u8,
1950) !void {
1951    const b = step.owner;
1952    const out_dir = fs.path.dirname(output_path) orelse ".";
1953    const out_basename = fs.path.basename(output_path);
1954    // sym link for libfoo.so.1 to libfoo.so.1.2.3
1955    const major_only_path = b.pathJoin(&.{ out_dir, filename_major_only });
1956    fs.cwd().atomicSymLink(out_basename, major_only_path, .{}) catch |err| {
1957        return step.fail("unable to symlink {s} -> {s}: {s}", .{
1958            major_only_path, out_basename, @errorName(err),
1959        });
1960    };
1961    // sym link for libfoo.so to libfoo.so.1
1962    const name_only_path = b.pathJoin(&.{ out_dir, filename_name_only });
1963    fs.cwd().atomicSymLink(filename_major_only, name_only_path, .{}) catch |err| {
1964        return step.fail("Unable to symlink {s} -> {s}: {s}", .{
1965            name_only_path, filename_major_only, @errorName(err),
1966        });
1967    };
1968}
1969
1970fn execPkgConfigList(b: *std.Build, out_code: *u8) (PkgConfigError || RunError)![]const PkgConfigPkg {
1971    const pkg_config_exe = b.graph.env_map.get("PKG_CONFIG") orelse "pkg-config";
1972    const stdout = try b.runAllowFail(&[_][]const u8{ pkg_config_exe, "--list-all" }, out_code, .Ignore);
1973    var list = std.array_list.Managed(PkgConfigPkg).init(b.allocator);
1974    errdefer list.deinit();
1975    var line_it = mem.tokenizeAny(u8, stdout, "\r\n");
1976    while (line_it.next()) |line| {
1977        if (mem.trim(u8, line, " \t").len == 0) continue;
1978        var tok_it = mem.tokenizeAny(u8, line, " \t");
1979        try list.append(PkgConfigPkg{
1980            .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1981            .desc = tok_it.rest(),
1982        });
1983    }
1984    return list.toOwnedSlice();
1985}
1986
1987fn getPkgConfigList(b: *std.Build) ![]const PkgConfigPkg {
1988    if (b.pkg_config_pkg_list) |res| {
1989        return res;
1990    }
1991    var code: u8 = undefined;
1992    if (execPkgConfigList(b, &code)) |list| {
1993        b.pkg_config_pkg_list = list;
1994        return list;
1995    } else |err| {
1996        const result = switch (err) {
1997            error.ProcessTerminated => error.PkgConfigCrashed,
1998            error.ExecNotSupported => error.PkgConfigFailed,
1999            error.ExitCodeFailure => error.PkgConfigFailed,
2000            error.FileNotFound => error.PkgConfigNotInstalled,
2001            error.InvalidName => error.PkgConfigNotInstalled,
2002            error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
2003            else => return err,
2004        };
2005        b.pkg_config_pkg_list = result;
2006        return result;
2007    }
2008}
2009
2010fn addFlag(args: *std.array_list.Managed([]const u8), comptime name: []const u8, opt: ?bool) !void {
2011    const cond = opt orelse return;
2012    try args.ensureUnusedCapacity(1);
2013    if (cond) {
2014        args.appendAssumeCapacity("-f" ++ name);
2015    } else {
2016        args.appendAssumeCapacity("-fno-" ++ name);
2017    }
2018}
2019
2020fn checkCompileErrors(compile: *Compile) !void {
2021    // Clear this field so that it does not get printed by the build runner.
2022    const actual_eb = compile.step.result_error_bundle;
2023    compile.step.result_error_bundle = .empty;
2024
2025    const arena = compile.step.owner.allocator;
2026
2027    const actual_errors = ae: {
2028        var aw: std.Io.Writer.Allocating = .init(arena);
2029        defer aw.deinit();
2030        try actual_eb.renderToWriter(.{
2031            .include_reference_trace = false,
2032            .include_source_line = false,
2033        }, &aw.writer, .no_color);
2034        break :ae try aw.toOwnedSlice();
2035    };
2036
2037    // Render the expected lines into a string that we can compare verbatim.
2038    var expected_generated: std.ArrayList(u8) = .empty;
2039    const expect_errors = compile.expect_errors.?;
2040
2041    var actual_line_it = mem.splitScalar(u8, actual_errors, '\n');
2042
2043    // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile
2044    switch (expect_errors) {
2045        .starts_with => |expect_starts_with| {
2046            if (std.mem.startsWith(u8, actual_errors, expect_starts_with)) return;
2047            return compile.step.fail(
2048                \\
2049                \\========= should start with: ============
2050                \\{s}
2051                \\========= but not found: ================
2052                \\{s}
2053                \\=========================================
2054            , .{ expect_starts_with, actual_errors });
2055        },
2056        .contains => |expect_line| {
2057            while (actual_line_it.next()) |actual_line| {
2058                if (!matchCompileError(actual_line, expect_line)) continue;
2059                return;
2060            }
2061
2062            return compile.step.fail(
2063                \\
2064                \\========= should contain: ===============
2065                \\{s}
2066                \\========= but not found: ================
2067                \\{s}
2068                \\=========================================
2069            , .{ expect_line, actual_errors });
2070        },
2071        .stderr_contains => |expect_line| {
2072            const actual_stderr: []const u8 = if (compile.step.result_error_msgs.items.len > 0)
2073                compile.step.result_error_msgs.items[0]
2074            else
2075                &.{};
2076            compile.step.result_error_msgs.clearRetainingCapacity();
2077
2078            var stderr_line_it = mem.splitScalar(u8, actual_stderr, '\n');
2079
2080            while (stderr_line_it.next()) |actual_line| {
2081                if (!matchCompileError(actual_line, expect_line)) continue;
2082                return;
2083            }
2084
2085            return compile.step.fail(
2086                \\
2087                \\========= should contain: ===============
2088                \\{s}
2089                \\========= but not found: ================
2090                \\{s}
2091                \\=========================================
2092            , .{ expect_line, actual_stderr });
2093        },
2094        .exact => |expect_lines| {
2095            for (expect_lines) |expect_line| {
2096                const actual_line = actual_line_it.next() orelse {
2097                    try expected_generated.appendSlice(arena, expect_line);
2098                    try expected_generated.append(arena, '\n');
2099                    continue;
2100                };
2101                if (matchCompileError(actual_line, expect_line)) {
2102                    try expected_generated.appendSlice(arena, actual_line);
2103                    try expected_generated.append(arena, '\n');
2104                    continue;
2105                }
2106                try expected_generated.appendSlice(arena, expect_line);
2107                try expected_generated.append(arena, '\n');
2108            }
2109
2110            if (mem.eql(u8, expected_generated.items, actual_errors)) return;
2111
2112            return compile.step.fail(
2113                \\
2114                \\========= expected: =====================
2115                \\{s}
2116                \\========= but found: ====================
2117                \\{s}
2118                \\=========================================
2119            , .{ expected_generated.items, actual_errors });
2120        },
2121    }
2122}
2123
2124fn matchCompileError(actual: []const u8, expected: []const u8) bool {
2125    if (mem.endsWith(u8, actual, expected)) return true;
2126    if (mem.startsWith(u8, expected, ":?:?: ")) {
2127        if (mem.endsWith(u8, actual, expected[":?:?: ".len..])) return true;
2128    }
2129    // We scan for /?/ in expected line and if there is a match, we match everything
2130    // up to and after /?/.
2131    const expected_trim = mem.trim(u8, expected, " ");
2132    if (mem.indexOf(u8, expected_trim, "/?/")) |index| {
2133        const actual_trim = mem.trim(u8, actual, " ");
2134        const lhs = expected_trim[0..index];
2135        const rhs = expected_trim[index + "/?/".len ..];
2136        if (mem.startsWith(u8, actual_trim, lhs) and mem.endsWith(u8, actual_trim, rhs)) return true;
2137    }
2138    return false;
2139}
2140
2141pub fn rootModuleTarget(c: *Compile) std.Target {
2142    // The root module is always given a target, so we know this to be non-null.
2143    return c.root_module.resolved_target.?.result;
2144}
2145
2146fn moduleNeedsCliArg(mod: *const Module) bool {
2147    return for (mod.link_objects.items) |o| switch (o) {
2148        .c_source_file, .c_source_files, .assembly_file, .win32_resource_file => break true,
2149        else => continue,
2150    } else false;
2151}
2152
2153/// Return the full set of `Step.Compile` which `start` depends on, recursively. `start` itself is
2154/// always returned as the first element. If `chase_dynamic` is `false`, then dynamic libraries are
2155/// not included, and their dependencies are not considered; if `chase_dynamic` is `true`, dynamic
2156/// libraries are treated the same as other linked `Compile`s.
2157pub fn getCompileDependencies(start: *Compile, chase_dynamic: bool) []const *Compile {
2158    const arena = start.step.owner.graph.arena;
2159
2160    var compiles: std.AutoArrayHashMapUnmanaged(*Compile, void) = .empty;
2161    var next_idx: usize = 0;
2162
2163    compiles.putNoClobber(arena, start, {}) catch @panic("OOM");
2164
2165    while (next_idx < compiles.count()) {
2166        const compile = compiles.keys()[next_idx];
2167        next_idx += 1;
2168
2169        for (compile.root_module.getGraph().modules) |mod| {
2170            for (mod.link_objects.items) |lo| {
2171                switch (lo) {
2172                    .other_step => |other_compile| {
2173                        if (!chase_dynamic and other_compile.isDynamicLibrary()) continue;
2174                        compiles.put(arena, other_compile, {}) catch @panic("OOM");
2175                    },
2176                    else => {},
2177                }
2178            }
2179        }
2180    }
2181
2182    return compiles.keys();
2183}