zig

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

blob 7daf10c6 (83508B) - Raw


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