DoxigAlpha

getZigArgs

Function parameters

Parameters

#
compile:*Compile
fuzz:bool

Type definitions in this namespace

Types

#

Functions in this namespace

Functions

#
installHeader
Marks the specified header for installation alongside this artifact.
installHeadersDirectory
Marks headers from the specified directory for installation alongside this artifact.
installConfigHeader
Marks the specified config header for installation alongside this artifact.
installLibraryHeaders
Forwards all headers marked for installation from `lib` to this artifact.
dependsOnSystemLibrary
Returns whether the library, executable, or object depends on a particular system library.
linkLibC
Deprecated; use `compile.root_module.link_libc = true` instead.
linkLibCpp
Deprecated; use `compile.root_module.link_libcpp = true` instead.
linkSystemLibrary
Deprecated; use `compile.root_module.linkSystemLibrary(name, .{})` instead.
linkSystemLibrary2
Deprecated; use `compile.root_module.linkSystemLibrary(name, options)` instead.
linkFramework
Deprecated; use `c.root_module.linkFramework(name, .{})` instead.
addCSourceFiles
Deprecated; use `compile.root_module.addCSourceFiles(options)` instead.
addCSourceFile
Deprecated; use `compile.root_module.addCSourceFile(source)` instead.
addWin32ResourceFile
Deprecated; use `compile.root_module.addWin32ResourceFile(source)` instead.
getEmittedBinDirectory
Returns the path to the directory that contains the emitted binary file.
getEmittedBin
Returns the path to the generated executable, library or object file.
getEmittedImplib
Returns the path to the generated import library.
getEmittedH
Returns the path to the generated header file.
getEmittedPdb
Returns the generated PDB file.
getEmittedDocs
Returns the path to the generated documentation directory.
getEmittedAsm
Returns the path to the generated assembly code.
getEmittedLlvmIr
Returns the path to the generated LLVM IR.
getEmittedLlvmBc
Returns the path to the generated LLVM BC.
addAssemblyFile
Deprecated; use `compile.root_module.addAssemblyFile(source)` instead.
addObjectFile
Deprecated; use `compile.root_module.addObjectFile(source)` instead.
addObject
Deprecated; use `compile.root_module.addObject(object)` instead.
linkLibrary
Deprecated; use `compile.root_module.linkLibrary(library)` instead.
addAfterIncludePath
Deprecated; use `compile.root_module.addAfterIncludePath(lazy_path)` instead.
addSystemIncludePath
Deprecated; use `compile.root_module.addSystemIncludePath(lazy_path)` instead.
addIncludePath
Deprecated; use `compile.root_module.addIncludePath(lazy_path)` instead.
addConfigHeader
Deprecated; use `compile.root_module.addConfigHeader(config_header)` instead.
addEmbedPath
Deprecated; use `compile.root_module.addEmbedPath(lazy_path)` instead.
addLibraryPath
Deprecated; use `compile.root_module.addLibraryPath(directory_path)` instead.
addRPath
Deprecated; use `compile.root_module.addRPath(directory_path)` instead.
addSystemFrameworkPath
Deprecated; use `compile.root_module.addSystemFrameworkPath(directory_path)` instead.
addFrameworkPath
Deprecated; use `compile.root_module.addFrameworkPath(directory_path)` instead.
getCompileDependencies
Return the full set of `Step.Compile` which `start` depends on, recursively.

= .compile

Values

#
base_id
= .compile

Source

Implementation

#
fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
    const step = &compile.step;
    const b = step.owner;
    const arena = b.allocator;

    var zig_args = std.array_list.Managed([]const u8).init(arena);
    defer zig_args.deinit();

    try zig_args.append(b.graph.zig_exe);

    const cmd = switch (compile.kind) {
        .lib => "build-lib",
        .exe => "build-exe",
        .obj => "build-obj",
        .@"test" => "test",
        .test_obj => "test-obj",
    };
    try zig_args.append(cmd);

    if (b.reference_trace) |some| {
        try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some}));
    }
    try addFlag(&zig_args, "allow-so-scripts", compile.allow_so_scripts orelse b.graph.allow_so_scripts);

    try addFlag(&zig_args, "llvm", compile.use_llvm);
    try addFlag(&zig_args, "lld", compile.use_lld);

    if (compile.root_module.resolved_target.?.query.ofmt) |ofmt| {
        try zig_args.append(try std.fmt.allocPrint(arena, "-ofmt={s}", .{@tagName(ofmt)}));
    }

    switch (compile.entry) {
        .default => {},
        .disabled => try zig_args.append("-fno-entry"),
        .enabled => try zig_args.append("-fentry"),
        .symbol_name => |entry_name| {
            try zig_args.append(try std.fmt.allocPrint(arena, "-fentry={s}", .{entry_name}));
        },
    }

    {
        var symbol_it = compile.force_undefined_symbols.keyIterator();
        while (symbol_it.next()) |symbol_name| {
            try zig_args.append("--force_undefined");
            try zig_args.append(symbol_name.*);
        }
    }

    if (compile.stack_size) |stack_size| {
        try zig_args.append("--stack");
        try zig_args.append(try std.fmt.allocPrint(arena, "{}", .{stack_size}));
    }

    if (fuzz) {
        try zig_args.append("-ffuzz");
    }

    {
        // Stores system libraries that have already been seen for at least one
        // module, along with any arguments that need to be passed to the
        // compiler for each module individually.
        var seen_system_libs: std.StringHashMapUnmanaged([]const []const u8) = .empty;
        var frameworks: std.StringArrayHashMapUnmanaged(Module.LinkFrameworkOptions) = .empty;

        var prev_has_cflags = false;
        var prev_has_rcflags = false;
        var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first;
        var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic;
        // Track the number of positional arguments so that a nice error can be
        // emitted if there is nothing to link.
        var total_linker_objects: usize = @intFromBool(compile.root_module.root_source_file != null);

        // Fully recursive iteration including dynamic libraries to detect
        // libc and libc++ linkage.
        for (compile.getCompileDependencies(true)) |some_compile| {
            for (some_compile.root_module.getGraph().modules) |mod| {
                if (mod.link_libc == true) compile.is_linking_libc = true;
                if (mod.link_libcpp == true) compile.is_linking_libcpp = true;
            }
        }

        var cli_named_modules = try CliNamedModules.init(arena, compile.root_module);

        // For this loop, don't chase dynamic libraries because their link
        // objects are already linked.
        for (compile.getCompileDependencies(false)) |dep_compile| {
            for (dep_compile.root_module.getGraph().modules) |mod| {
                // While walking transitive dependencies, if a given link object is
                // already included in a library, it should not redundantly be
                // placed on the linker line of the dependee.
                const my_responsibility = dep_compile == compile;
                const already_linked = !my_responsibility and dep_compile.isDynamicLibrary();

                // Inherit dependencies on darwin frameworks.
                if (!already_linked) {
                    for (mod.frameworks.keys(), mod.frameworks.values()) |name, info| {
                        try frameworks.put(arena, name, info);
                    }
                }

                // Inherit dependencies on system libraries and static libraries.
                for (mod.link_objects.items) |link_object| {
                    switch (link_object) {
                        .static_path => |static_path| {
                            if (my_responsibility) {
                                try zig_args.append(static_path.getPath2(mod.owner, step));
                                total_linker_objects += 1;
                            }
                        },
                        .system_lib => |system_lib| {
                            const system_lib_gop = try seen_system_libs.getOrPut(arena, system_lib.name);
                            if (system_lib_gop.found_existing) {
                                try zig_args.appendSlice(system_lib_gop.value_ptr.*);
                                continue;
                            } else {
                                system_lib_gop.value_ptr.* = &.{};
                            }

                            if (already_linked)
                                continue;

                            if ((system_lib.search_strategy != prev_search_strategy or
                                system_lib.preferred_link_mode != prev_preferred_link_mode) and
                                compile.linkage != .static)
                            {
                                switch (system_lib.search_strategy) {
                                    .no_fallback => switch (system_lib.preferred_link_mode) {
                                        .dynamic => try zig_args.append("-search_dylibs_only"),
                                        .static => try zig_args.append("-search_static_only"),
                                    },
                                    .paths_first => switch (system_lib.preferred_link_mode) {
                                        .dynamic => try zig_args.append("-search_paths_first"),
                                        .static => try zig_args.append("-search_paths_first_static"),
                                    },
                                    .mode_first => switch (system_lib.preferred_link_mode) {
                                        .dynamic => try zig_args.append("-search_dylibs_first"),
                                        .static => try zig_args.append("-search_static_first"),
                                    },
                                }
                                prev_search_strategy = system_lib.search_strategy;
                                prev_preferred_link_mode = system_lib.preferred_link_mode;
                            }

                            const prefix: []const u8 = prefix: {
                                if (system_lib.needed) break :prefix "-needed-l";
                                if (system_lib.weak) break :prefix "-weak-l";
                                break :prefix "-l";
                            };
                            switch (system_lib.use_pkg_config) {
                                .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })),
                                .yes, .force => {
                                    if (compile.runPkgConfig(system_lib.name)) |result| {
                                        try zig_args.appendSlice(result.cflags);
                                        try zig_args.appendSlice(result.libs);
                                        try seen_system_libs.put(arena, system_lib.name, result.cflags);
                                    } else |err| switch (err) {
                                        error.PkgConfigInvalidOutput,
                                        error.PkgConfigCrashed,
                                        error.PkgConfigFailed,
                                        error.PkgConfigNotInstalled,
                                        error.PackageNotFound,
                                        => switch (system_lib.use_pkg_config) {
                                            .yes => {
                                                // pkg-config failed, so fall back to linking the library
                                                // by name directly.
                                                try zig_args.append(b.fmt("{s}{s}", .{
                                                    prefix,
                                                    system_lib.name,
                                                }));
                                            },
                                            .force => {
                                                panic("pkg-config failed for library {s}", .{system_lib.name});
                                            },
                                            .no => unreachable,
                                        },

                                        else => |e| return e,
                                    }
                                },
                            }
                        },
                        .other_step => |other| {
                            switch (other.kind) {
                                .exe => return step.fail("cannot link with an executable build artifact", .{}),
                                .@"test" => return step.fail("cannot link with a test", .{}),
                                .obj, .test_obj => {
                                    const included_in_lib_or_obj = !my_responsibility and
                                        (dep_compile.kind == .lib or dep_compile.kind == .obj or dep_compile.kind == .test_obj);
                                    if (!already_linked and !included_in_lib_or_obj) {
                                        try zig_args.append(other.getEmittedBin().getPath2(b, step));
                                        total_linker_objects += 1;
                                    }
                                },
                                .lib => l: {
                                    const other_produces_implib = other.producesImplib();
                                    const other_is_static = other_produces_implib or other.isStaticLibrary();

                                    if (compile.isStaticLibrary() and other_is_static) {
                                        // Avoid putting a static library inside a static library.
                                        break :l;
                                    }

                                    // For DLLs, we must link against the implib.
                                    // For everything else, we directly link
                                    // against the library file.
                                    const full_path_lib = if (other_produces_implib)
                                        other.getGeneratedFilePath("generated_implib", &compile.step)
                                    else
                                        other.getGeneratedFilePath("generated_bin", &compile.step);

                                    try zig_args.append(full_path_lib);
                                    total_linker_objects += 1;

                                    if (other.linkage == .dynamic and
                                        compile.rootModuleTarget().os.tag != .windows)
                                    {
                                        if (fs.path.dirname(full_path_lib)) |dirname| {
                                            try zig_args.append("-rpath");
                                            try zig_args.append(dirname);
                                        }
                                    }
                                },
                            }
                        },
                        .assembly_file => |asm_file| l: {
                            if (!my_responsibility) break :l;

                            if (prev_has_cflags) {
                                try zig_args.append("-cflags");
                                try zig_args.append("--");
                                prev_has_cflags = false;
                            }
                            try zig_args.append(asm_file.getPath2(mod.owner, step));
                            total_linker_objects += 1;
                        },

                        .c_source_file => |c_source_file| l: {
                            if (!my_responsibility) break :l;

                            if (prev_has_cflags or c_source_file.flags.len != 0) {
                                try zig_args.append("-cflags");
                                for (c_source_file.flags) |arg| {
                                    try zig_args.append(arg);
                                }
                                try zig_args.append("--");
                            }
                            prev_has_cflags = (c_source_file.flags.len != 0);

                            if (c_source_file.language) |lang| {
                                try zig_args.append("-x");
                                try zig_args.append(lang.internalIdentifier());
                            }

                            try zig_args.append(c_source_file.file.getPath2(mod.owner, step));

                            if (c_source_file.language != null) {
                                try zig_args.append("-x");
                                try zig_args.append("none");
                            }
                            total_linker_objects += 1;
                        },

                        .c_source_files => |c_source_files| l: {
                            if (!my_responsibility) break :l;

                            if (prev_has_cflags or c_source_files.flags.len != 0) {
                                try zig_args.append("-cflags");
                                for (c_source_files.flags) |arg| {
                                    try zig_args.append(arg);
                                }
                                try zig_args.append("--");
                            }
                            prev_has_cflags = (c_source_files.flags.len != 0);

                            if (c_source_files.language) |lang| {
                                try zig_args.append("-x");
                                try zig_args.append(lang.internalIdentifier());
                            }

                            const root_path = c_source_files.root.getPath2(mod.owner, step);
                            for (c_source_files.files) |file| {
                                try zig_args.append(b.pathJoin(&.{ root_path, file }));
                            }

                            if (c_source_files.language != null) {
                                try zig_args.append("-x");
                                try zig_args.append("none");
                            }

                            total_linker_objects += c_source_files.files.len;
                        },

                        .win32_resource_file => |rc_source_file| l: {
                            if (!my_responsibility) break :l;

                            if (rc_source_file.flags.len == 0 and rc_source_file.include_paths.len == 0) {
                                if (prev_has_rcflags) {
                                    try zig_args.append("-rcflags");
                                    try zig_args.append("--");
                                    prev_has_rcflags = false;
                                }
                            } else {
                                try zig_args.append("-rcflags");
                                for (rc_source_file.flags) |arg| {
                                    try zig_args.append(arg);
                                }
                                for (rc_source_file.include_paths) |include_path| {
                                    try zig_args.append("/I");
                                    try zig_args.append(include_path.getPath2(mod.owner, step));
                                }
                                try zig_args.append("--");
                                prev_has_rcflags = true;
                            }
                            try zig_args.append(rc_source_file.file.getPath2(mod.owner, step));
                            total_linker_objects += 1;
                        },
                    }
                }

                // We need to emit the --mod argument here so that the above link objects
                // have the correct parent module, but only if the module is part of
                // this compilation.
                if (!my_responsibility) continue;
                if (cli_named_modules.modules.getIndex(mod)) |module_cli_index| {
                    const module_cli_name = cli_named_modules.names.keys()[module_cli_index];
                    try mod.appendZigProcessFlags(&zig_args, step);

                    // --dep arguments
                    try zig_args.ensureUnusedCapacity(mod.import_table.count() * 2);
                    for (mod.import_table.keys(), mod.import_table.values()) |name, import| {
                        const import_index = cli_named_modules.modules.getIndex(import).?;
                        const import_cli_name = cli_named_modules.names.keys()[import_index];
                        zig_args.appendAssumeCapacity("--dep");
                        if (std.mem.eql(u8, import_cli_name, name)) {
                            zig_args.appendAssumeCapacity(import_cli_name);
                        } else {
                            zig_args.appendAssumeCapacity(b.fmt("{s}={s}", .{ name, import_cli_name }));
                        }
                    }

                    // When the CLI sees a -M argument, it determines whether it
                    // implies the existence of a Zig compilation unit based on
                    // whether there is a root source file. If there is no root
                    // source file, then this is not a zig compilation unit - it is
                    // perhaps a set of linker objects, or C source files instead.
                    // Linker objects are added to the CLI globally, while C source
                    // files must have a module parent.
                    if (mod.root_source_file) |lp| {
                        const src = lp.getPath2(mod.owner, step);
                        try zig_args.append(b.fmt("-M{s}={s}", .{ module_cli_name, src }));
                    } else if (moduleNeedsCliArg(mod)) {
                        try zig_args.append(b.fmt("-M{s}", .{module_cli_name}));
                    }
                }
            }
        }

        if (total_linker_objects == 0) {
            return step.fail("the linker needs one or more objects to link", .{});
        }

        for (frameworks.keys(), frameworks.values()) |name, info| {
            if (info.needed) {
                try zig_args.append("-needed_framework");
            } else if (info.weak) {
                try zig_args.append("-weak_framework");
            } else {
                try zig_args.append("-framework");
            }
            try zig_args.append(name);
        }

        if (compile.is_linking_libcpp) {
            try zig_args.append("-lc++");
        }

        if (compile.is_linking_libc) {
            try zig_args.append("-lc");
        }
    }

    if (compile.win32_manifest) |manifest_file| {
        try zig_args.append(manifest_file.getPath2(b, step));
    }

    if (compile.image_base) |image_base| {
        try zig_args.append("--image-base");
        try zig_args.append(b.fmt("0x{x}", .{image_base}));
    }

    for (compile.filters) |filter| {
        try zig_args.append("--test-filter");
        try zig_args.append(filter);
    }

    if (compile.test_runner) |test_runner| {
        try zig_args.append("--test-runner");
        try zig_args.append(test_runner.path.getPath2(b, step));
    }

    for (b.debug_log_scopes) |log_scope| {
        try zig_args.append("--debug-log");
        try zig_args.append(log_scope);
    }

    if (b.debug_compile_errors) {
        try zig_args.append("--debug-compile-errors");
    }

    if (b.debug_incremental) {
        try zig_args.append("--debug-incremental");
    }

    if (b.verbose_cimport) try zig_args.append("--verbose-cimport");
    if (b.verbose_air) try zig_args.append("--verbose-air");
    if (b.verbose_llvm_ir) |path| try zig_args.append(b.fmt("--verbose-llvm-ir={s}", .{path}));
    if (b.verbose_llvm_bc) |path| try zig_args.append(b.fmt("--verbose-llvm-bc={s}", .{path}));
    if (b.verbose_link or compile.verbose_link) try zig_args.append("--verbose-link");
    if (b.verbose_cc or compile.verbose_cc) try zig_args.append("--verbose-cc");
    if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
    if (b.graph.time_report) try zig_args.append("--time-report");

    if (compile.generated_asm != null) try zig_args.append("-femit-asm");
    if (compile.generated_bin == null) try zig_args.append("-fno-emit-bin");
    if (compile.generated_docs != null) try zig_args.append("-femit-docs");
    if (compile.generated_implib != null) try zig_args.append("-femit-implib");
    if (compile.generated_llvm_bc != null) try zig_args.append("-femit-llvm-bc");
    if (compile.generated_llvm_ir != null) try zig_args.append("-femit-llvm-ir");
    if (compile.generated_h != null) try zig_args.append("-femit-h");

    try addFlag(&zig_args, "formatted-panics", compile.formatted_panics);

    switch (compile.compress_debug_sections) {
        .none => {},
        .zlib => try zig_args.append("--compress-debug-sections=zlib"),
        .zstd => try zig_args.append("--compress-debug-sections=zstd"),
    }

    if (compile.link_eh_frame_hdr) {
        try zig_args.append("--eh-frame-hdr");
    }
    if (compile.link_emit_relocs) {
        try zig_args.append("--emit-relocs");
    }
    if (compile.link_function_sections) {
        try zig_args.append("-ffunction-sections");
    }
    if (compile.link_data_sections) {
        try zig_args.append("-fdata-sections");
    }
    if (compile.link_gc_sections) |x| {
        try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
    }
    if (!compile.linker_dynamicbase) {
        try zig_args.append("--no-dynamicbase");
    }
    if (compile.linker_allow_shlib_undefined) |x| {
        try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
    }
    if (compile.link_z_notext) {
        try zig_args.append("-z");
        try zig_args.append("notext");
    }
    if (!compile.link_z_relro) {
        try zig_args.append("-z");
        try zig_args.append("norelro");
    }
    if (compile.link_z_lazy) {
        try zig_args.append("-z");
        try zig_args.append("lazy");
    }
    if (compile.link_z_common_page_size) |size| {
        try zig_args.append("-z");
        try zig_args.append(b.fmt("common-page-size={d}", .{size}));
    }
    if (compile.link_z_max_page_size) |size| {
        try zig_args.append("-z");
        try zig_args.append(b.fmt("max-page-size={d}", .{size}));
    }

    if (compile.libc_file) |libc_file| {
        try zig_args.append("--libc");
        try zig_args.append(libc_file.getPath2(b, step));
    } else if (b.libc_file) |libc_file| {
        try zig_args.append("--libc");
        try zig_args.append(libc_file);
    }

    try zig_args.append("--cache-dir");
    try zig_args.append(b.cache_root.path orelse ".");

    try zig_args.append("--global-cache-dir");
    try zig_args.append(b.graph.global_cache_root.path orelse ".");

    if (b.graph.debug_compiler_runtime_libs) try zig_args.append("--debug-rt");

    try zig_args.append("--name");
    try zig_args.append(compile.name);

    if (compile.linkage) |some| switch (some) {
        .dynamic => try zig_args.append("-dynamic"),
        .static => try zig_args.append("-static"),
    };
    if (compile.kind == .lib and compile.linkage != null and compile.linkage.? == .dynamic) {
        if (compile.version) |version| {
            try zig_args.append("--version");
            try zig_args.append(b.fmt("{f}", .{version}));
        }

        if (compile.rootModuleTarget().os.tag.isDarwin()) {
            const install_name = compile.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{
                compile.rootModuleTarget().libPrefix(),
                compile.name,
                compile.rootModuleTarget().dynamicLibSuffix(),
            });
            try zig_args.append("-install_name");
            try zig_args.append(install_name);
        }
    }

    if (compile.entitlements) |entitlements| {
        try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
    }
    if (compile.pagezero_size) |pagezero_size| {
        const size = try std.fmt.allocPrint(arena, "{x}", .{pagezero_size});
        try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
    }
    if (compile.headerpad_size) |headerpad_size| {
        const size = try std.fmt.allocPrint(arena, "{x}", .{headerpad_size});
        try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
    }
    if (compile.headerpad_max_install_names) {
        try zig_args.append("-headerpad_max_install_names");
    }
    if (compile.dead_strip_dylibs) {
        try zig_args.append("-dead_strip_dylibs");
    }
    if (compile.force_load_objc) {
        try zig_args.append("-ObjC");
    }
    if (compile.discard_local_symbols) {
        try zig_args.append("--discard-all");
    }

    try addFlag(&zig_args, "compiler-rt", compile.bundle_compiler_rt);
    try addFlag(&zig_args, "ubsan-rt", compile.bundle_ubsan_rt);
    try addFlag(&zig_args, "dll-export-fns", compile.dll_export_fns);
    if (compile.rdynamic) {
        try zig_args.append("-rdynamic");
    }
    if (compile.import_memory) {
        try zig_args.append("--import-memory");
    }
    if (compile.export_memory) {
        try zig_args.append("--export-memory");
    }
    if (compile.import_symbols) {
        try zig_args.append("--import-symbols");
    }
    if (compile.import_table) {
        try zig_args.append("--import-table");
    }
    if (compile.export_table) {
        try zig_args.append("--export-table");
    }
    if (compile.initial_memory) |initial_memory| {
        try zig_args.append(b.fmt("--initial-memory={d}", .{initial_memory}));
    }
    if (compile.max_memory) |max_memory| {
        try zig_args.append(b.fmt("--max-memory={d}", .{max_memory}));
    }
    if (compile.shared_memory) {
        try zig_args.append("--shared-memory");
    }
    if (compile.global_base) |global_base| {
        try zig_args.append(b.fmt("--global-base={d}", .{global_base}));
    }

    if (compile.wasi_exec_model) |model| {
        try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)}));
    }
    if (compile.linker_script) |linker_script| {
        try zig_args.append("--script");
        try zig_args.append(linker_script.getPath2(b, step));
    }

    if (compile.version_script) |version_script| {
        try zig_args.append("--version-script");
        try zig_args.append(version_script.getPath2(b, step));
    }
    if (compile.linker_allow_undefined_version) |x| {
        try zig_args.append(if (x) "--undefined-version" else "--no-undefined-version");
    }

    if (compile.linker_enable_new_dtags) |enabled| {
        try zig_args.append(if (enabled) "--enable-new-dtags" else "--disable-new-dtags");
    }

    if (compile.kind == .@"test") {
        if (compile.exec_cmd_args) |exec_cmd_args| {
            for (exec_cmd_args) |cmd_arg| {
                if (cmd_arg) |arg| {
                    try zig_args.append("--test-cmd");
                    try zig_args.append(arg);
                } else {
                    try zig_args.append("--test-cmd-bin");
                }
            }
        }
    }

    if (b.sysroot) |sysroot| {
        try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
    }

    // -I and -L arguments that appear after the last --mod argument apply to all modules.
    for (b.search_prefixes.items) |search_prefix| {
        var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| {
            return step.fail("unable to open prefix directory '{s}': {s}", .{
                search_prefix, @errorName(err),
            });
        };
        defer prefix_dir.close();

        // Avoid passing -L and -I flags for nonexistent directories.
        // This prevents a warning, that should probably be upgraded to an error in Zig's
        // CLI parsing code, when the linker sees an -L directory that does not exist.

        if (prefix_dir.accessZ("lib", .{})) |_| {
            try zig_args.appendSlice(&.{
                "-L", b.pathJoin(&.{ search_prefix, "lib" }),
            });
        } else |err| switch (err) {
            error.FileNotFound => {},
            else => |e| return step.fail("unable to access '{s}/lib' directory: {s}", .{
                search_prefix, @errorName(e),
            }),
        }

        if (prefix_dir.accessZ("include", .{})) |_| {
            try zig_args.appendSlice(&.{
                "-I", b.pathJoin(&.{ search_prefix, "include" }),
            });
        } else |err| switch (err) {
            error.FileNotFound => {},
            else => |e| return step.fail("unable to access '{s}/include' directory: {s}", .{
                search_prefix, @errorName(e),
            }),
        }
    }

    if (compile.rc_includes != .any) {
        try zig_args.append("-rcincludes");
        try zig_args.append(@tagName(compile.rc_includes));
    }

    try addFlag(&zig_args, "each-lib-rpath", compile.each_lib_rpath);

    if (compile.build_id orelse b.build_id) |build_id| {
        try zig_args.append(switch (build_id) {
            .hexstring => |hs| b.fmt("--build-id=0x{x}", .{hs.toSlice()}),
            .none, .fast, .uuid, .sha1, .md5 => b.fmt("--build-id={s}", .{@tagName(build_id)}),
        });
    }

    const opt_zig_lib_dir = if (compile.zig_lib_dir) |dir|
        dir.getPath2(b, step)
    else if (b.graph.zig_lib_directory.path) |_|
        b.fmt("{f}", .{b.graph.zig_lib_directory})
    else
        null;

    if (opt_zig_lib_dir) |zig_lib_dir| {
        try zig_args.append("--zig-lib-dir");
        try zig_args.append(zig_lib_dir);
    }

    try addFlag(&zig_args, "PIE", compile.pie);

    if (compile.lto) |lto| {
        try zig_args.append(switch (lto) {
            .full => "-flto=full",
            .thin => "-flto=thin",
            .none => "-fno-lto",
        });
    } else try addFlag(&zig_args, "lto", compile.want_lto);

    try addFlag(&zig_args, "sanitize-coverage-trace-pc-guard", compile.sanitize_coverage_trace_pc_guard);

    if (compile.subsystem) |subsystem| {
        try zig_args.append("--subsystem");
        try zig_args.append(switch (subsystem) {
            .Console => "console",
            .Windows => "windows",
            .Posix => "posix",
            .Native => "native",
            .EfiApplication => "efi_application",
            .EfiBootServiceDriver => "efi_boot_service_driver",
            .EfiRom => "efi_rom",
            .EfiRuntimeDriver => "efi_runtime_driver",
        });
    }

    if (compile.mingw_unicode_entry_point) {
        try zig_args.append("-municode");
    }

    if (compile.error_limit) |err_limit| try zig_args.appendSlice(&.{
        "--error-limit", b.fmt("{d}", .{err_limit}),
    });

    try addFlag(&zig_args, "incremental", b.graph.incremental);

    try zig_args.append("--listen=-");

    // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
    // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
    // pass that to zig, e.g. via 'zig build-lib @args.rsp'
    // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
    var args_length: usize = 0;
    for (zig_args.items) |arg| {
        args_length += arg.len + 1; // +1 to account for null terminator
    }
    if (args_length >= 30 * 1024) {
        try b.cache_root.handle.makePath("args");

        const args_to_escape = zig_args.items[2..];
        var escaped_args = try std.array_list.Managed([]const u8).initCapacity(arena, args_to_escape.len);
        arg_blk: for (args_to_escape) |arg| {
            for (arg, 0..) |c, arg_idx| {
                if (c == '\\' or c == '"') {
                    // Slow path for arguments that need to be escaped. We'll need to allocate and copy
                    var escaped: std.ArrayListUnmanaged(u8) = .empty;
                    try escaped.ensureTotalCapacityPrecise(arena, arg.len + 1);
                    try escaped.appendSlice(arena, arg[0..arg_idx]);
                    for (arg[arg_idx..]) |to_escape| {
                        if (to_escape == '\\' or to_escape == '"') try escaped.append(arena, '\\');
                        try escaped.append(arena, to_escape);
                    }
                    escaped_args.appendAssumeCapacity(escaped.items);
                    continue :arg_blk;
                }
            }
            escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
        }

        // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
        // other zig build commands running in parallel.
        const partially_quoted = try std.mem.join(arena, "\" \"", escaped_args.items);
        const args = try std.mem.concat(arena, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });

        var args_hash: [Sha256.digest_length]u8 = undefined;
        Sha256.hash(args, &args_hash, .{});
        var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
        _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});

        const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
        if (b.cache_root.handle.access(args_file, .{})) |_| {
            // The args file is already present from a previous run.
        } else |err| switch (err) {
            error.FileNotFound => {
                try b.cache_root.handle.makePath("tmp");
                const rand_int = std.crypto.random.int(u64);
                const tmp_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
                try b.cache_root.handle.writeFile(.{ .sub_path = tmp_path, .data = args });
                defer b.cache_root.handle.deleteFile(tmp_path) catch {
                    // It's fine if the temporary file can't be cleaned up.
                };
                b.cache_root.handle.rename(tmp_path, args_file) catch |rename_err| switch (rename_err) {
                    error.PathAlreadyExists => {
                        // The args file was created by another concurrent build process.
                    },
                    else => |other_err| return other_err,
                };
            },
            else => |other_err| return other_err,
        }

        const resolved_args_file = try mem.concat(arena, u8, &.{
            "@",
            try b.cache_root.join(arena, &.{args_file}),
        });

        zig_args.shrinkRetainingCapacity(2);
        try zig_args.append(resolved_args_file);
    }

    return try zig_args.toOwnedSlice();
}