zig

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

blob b47f962a (148555B) - Raw


      1 const std = @import("std.zig");
      2 const builtin = @import("builtin");
      3 const io = std.io;
      4 const fs = std.fs;
      5 const mem = std.mem;
      6 const debug = std.debug;
      7 const panic = std.debug.panic;
      8 const assert = debug.assert;
      9 const log = std.log;
     10 const ArrayList = std.ArrayList;
     11 const StringHashMap = std.StringHashMap;
     12 const Allocator = mem.Allocator;
     13 const process = std.process;
     14 const EnvMap = std.process.EnvMap;
     15 const fmt_lib = std.fmt;
     16 const File = std.fs.File;
     17 const CrossTarget = std.zig.CrossTarget;
     18 const NativeTargetInfo = std.zig.system.NativeTargetInfo;
     19 const Sha256 = std.crypto.hash.sha2.Sha256;
     20 
     21 pub const FmtStep = @import("build/FmtStep.zig");
     22 pub const TranslateCStep = @import("build/TranslateCStep.zig");
     23 pub const WriteFileStep = @import("build/WriteFileStep.zig");
     24 pub const RunStep = @import("build/RunStep.zig");
     25 pub const CheckFileStep = @import("build/CheckFileStep.zig");
     26 pub const CheckObjectStep = @import("build/CheckObjectStep.zig");
     27 pub const InstallRawStep = @import("build/InstallRawStep.zig");
     28 pub const OptionsStep = @import("build/OptionsStep.zig");
     29 pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig");
     30 
     31 pub const Builder = struct {
     32     install_tls: TopLevelStep,
     33     uninstall_tls: TopLevelStep,
     34     allocator: Allocator,
     35     user_input_options: UserInputOptionsMap,
     36     available_options_map: AvailableOptionsMap,
     37     available_options_list: ArrayList(AvailableOption),
     38     verbose: bool,
     39     verbose_link: bool,
     40     verbose_cc: bool,
     41     verbose_air: bool,
     42     verbose_llvm_ir: bool,
     43     verbose_cimport: bool,
     44     verbose_llvm_cpu_features: bool,
     45     /// The purpose of executing the command is for a human to read compile errors from the terminal
     46     prominent_compile_errors: bool,
     47     color: enum { auto, on, off } = .auto,
     48     use_stage1: ?bool = null,
     49     invalid_user_input: bool,
     50     zig_exe: []const u8,
     51     default_step: *Step,
     52     env_map: *EnvMap,
     53     top_level_steps: ArrayList(*TopLevelStep),
     54     install_prefix: []const u8,
     55     dest_dir: ?[]const u8,
     56     lib_dir: []const u8,
     57     exe_dir: []const u8,
     58     h_dir: []const u8,
     59     install_path: []const u8,
     60     sysroot: ?[]const u8 = null,
     61     search_prefixes: ArrayList([]const u8),
     62     libc_file: ?[]const u8 = null,
     63     installed_files: ArrayList(InstalledFile),
     64     build_root: []const u8,
     65     cache_root: []const u8,
     66     global_cache_root: []const u8,
     67     release_mode: ?std.builtin.Mode,
     68     is_release: bool,
     69     override_lib_dir: ?[]const u8,
     70     vcpkg_root: VcpkgRoot,
     71     pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
     72     args: ?[][]const u8 = null,
     73     debug_log_scopes: []const []const u8 = &.{},
     74 
     75     /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
     76     enable_darling: bool = false,
     77     /// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
     78     enable_qemu: bool = false,
     79     /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
     80     enable_rosetta: bool = false,
     81     /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
     82     enable_wasmtime: bool = false,
     83     /// Use system Wine installation to run cross compiled Windows build artifacts.
     84     enable_wine: bool = false,
     85     /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
     86     /// this will be the directory $glibc-build-dir/install/glibcs
     87     /// Given the example of the aarch64 target, this is the directory
     88     /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
     89     glibc_runtimes_dir: ?[]const u8 = null,
     90 
     91     /// Information about the native target. Computed before build() is invoked.
     92     host: NativeTargetInfo,
     93 
     94     pub const ExecError = error{
     95         ReadFailure,
     96         ExitCodeFailure,
     97         ProcessTerminated,
     98         ExecNotSupported,
     99     } || std.ChildProcess.SpawnError;
    100 
    101     pub const PkgConfigError = error{
    102         PkgConfigCrashed,
    103         PkgConfigFailed,
    104         PkgConfigNotInstalled,
    105         PkgConfigInvalidOutput,
    106     };
    107 
    108     pub const PkgConfigPkg = struct {
    109         name: []const u8,
    110         desc: []const u8,
    111     };
    112 
    113     pub const CStd = enum {
    114         C89,
    115         C99,
    116         C11,
    117     };
    118 
    119     const UserInputOptionsMap = StringHashMap(UserInputOption);
    120     const AvailableOptionsMap = StringHashMap(AvailableOption);
    121 
    122     const AvailableOption = struct {
    123         name: []const u8,
    124         type_id: TypeId,
    125         description: []const u8,
    126         /// If the `type_id` is `enum` this provides the list of enum options
    127         enum_options: ?[]const []const u8,
    128     };
    129 
    130     const UserInputOption = struct {
    131         name: []const u8,
    132         value: UserValue,
    133         used: bool,
    134     };
    135 
    136     const UserValue = union(enum) {
    137         flag: void,
    138         scalar: []const u8,
    139         list: ArrayList([]const u8),
    140     };
    141 
    142     const TypeId = enum {
    143         bool,
    144         int,
    145         float,
    146         @"enum",
    147         string,
    148         list,
    149     };
    150 
    151     const TopLevelStep = struct {
    152         pub const base_id = .top_level;
    153 
    154         step: Step,
    155         description: []const u8,
    156     };
    157 
    158     pub const DirList = struct {
    159         lib_dir: ?[]const u8 = null,
    160         exe_dir: ?[]const u8 = null,
    161         include_dir: ?[]const u8 = null,
    162     };
    163 
    164     pub fn create(
    165         allocator: Allocator,
    166         zig_exe: []const u8,
    167         build_root: []const u8,
    168         cache_root: []const u8,
    169         global_cache_root: []const u8,
    170     ) !*Builder {
    171         const env_map = try allocator.create(EnvMap);
    172         env_map.* = try process.getEnvMap(allocator);
    173 
    174         const host = try NativeTargetInfo.detect(allocator, .{});
    175 
    176         const self = try allocator.create(Builder);
    177         self.* = Builder{
    178             .zig_exe = zig_exe,
    179             .build_root = build_root,
    180             .cache_root = try fs.path.relative(allocator, build_root, cache_root),
    181             .global_cache_root = global_cache_root,
    182             .verbose = false,
    183             .verbose_link = false,
    184             .verbose_cc = false,
    185             .verbose_air = false,
    186             .verbose_llvm_ir = false,
    187             .verbose_cimport = false,
    188             .verbose_llvm_cpu_features = false,
    189             .prominent_compile_errors = false,
    190             .invalid_user_input = false,
    191             .allocator = allocator,
    192             .user_input_options = UserInputOptionsMap.init(allocator),
    193             .available_options_map = AvailableOptionsMap.init(allocator),
    194             .available_options_list = ArrayList(AvailableOption).init(allocator),
    195             .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
    196             .default_step = undefined,
    197             .env_map = env_map,
    198             .search_prefixes = ArrayList([]const u8).init(allocator),
    199             .install_prefix = undefined,
    200             .lib_dir = undefined,
    201             .exe_dir = undefined,
    202             .h_dir = undefined,
    203             .dest_dir = env_map.get("DESTDIR"),
    204             .installed_files = ArrayList(InstalledFile).init(allocator),
    205             .install_tls = TopLevelStep{
    206                 .step = Step.initNoOp(.top_level, "install", allocator),
    207                 .description = "Copy build artifacts to prefix path",
    208             },
    209             .uninstall_tls = TopLevelStep{
    210                 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
    211                 .description = "Remove build artifacts from prefix path",
    212             },
    213             .release_mode = null,
    214             .is_release = false,
    215             .override_lib_dir = null,
    216             .install_path = undefined,
    217             .vcpkg_root = VcpkgRoot{ .unattempted = {} },
    218             .args = null,
    219             .host = host,
    220         };
    221         try self.top_level_steps.append(&self.install_tls);
    222         try self.top_level_steps.append(&self.uninstall_tls);
    223         self.default_step = &self.install_tls.step;
    224         return self;
    225     }
    226 
    227     pub fn destroy(self: *Builder) void {
    228         self.env_map.deinit();
    229         self.top_level_steps.deinit();
    230         self.allocator.destroy(self);
    231     }
    232 
    233     /// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
    234     pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void {
    235         if (self.dest_dir) |dest_dir| {
    236             self.install_prefix = install_prefix orelse "/usr";
    237             self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
    238         } else {
    239             self.install_prefix = install_prefix orelse
    240                 (self.pathJoin(&.{ self.build_root, "zig-out" }));
    241             self.install_path = self.install_prefix;
    242         }
    243 
    244         var lib_list = [_][]const u8{ self.install_path, "lib" };
    245         var exe_list = [_][]const u8{ self.install_path, "bin" };
    246         var h_list = [_][]const u8{ self.install_path, "include" };
    247 
    248         if (dir_list.lib_dir) |dir| {
    249             if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
    250             lib_list[1] = dir;
    251         }
    252 
    253         if (dir_list.exe_dir) |dir| {
    254             if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
    255             exe_list[1] = dir;
    256         }
    257 
    258         if (dir_list.include_dir) |dir| {
    259             if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
    260             h_list[1] = dir;
    261         }
    262 
    263         self.lib_dir = self.pathJoin(&lib_list);
    264         self.exe_dir = self.pathJoin(&exe_list);
    265         self.h_dir = self.pathJoin(&h_list);
    266     }
    267 
    268     fn convertOptionalPathToFileSource(path: ?[]const u8) ?FileSource {
    269         return if (path) |p|
    270             FileSource{ .path = p }
    271         else
    272             null;
    273     }
    274 
    275     pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
    276         return addExecutableSource(self, name, convertOptionalPathToFileSource(root_src));
    277     }
    278 
    279     pub fn addExecutableSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
    280         return LibExeObjStep.createExecutable(builder, name, root_src);
    281     }
    282 
    283     pub fn addOptions(self: *Builder) *OptionsStep {
    284         return OptionsStep.create(self);
    285     }
    286 
    287     pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
    288         return addObjectSource(self, name, convertOptionalPathToFileSource(root_src));
    289     }
    290 
    291     pub fn addObjectSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
    292         return LibExeObjStep.createObject(builder, name, root_src);
    293     }
    294 
    295     pub fn addSharedLibrary(
    296         self: *Builder,
    297         name: []const u8,
    298         root_src: ?[]const u8,
    299         kind: LibExeObjStep.SharedLibKind,
    300     ) *LibExeObjStep {
    301         return addSharedLibrarySource(self, name, convertOptionalPathToFileSource(root_src), kind);
    302     }
    303 
    304     pub fn addSharedLibrarySource(
    305         self: *Builder,
    306         name: []const u8,
    307         root_src: ?FileSource,
    308         kind: LibExeObjStep.SharedLibKind,
    309     ) *LibExeObjStep {
    310         return LibExeObjStep.createSharedLibrary(self, name, root_src, kind);
    311     }
    312 
    313     pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
    314         return addStaticLibrarySource(self, name, convertOptionalPathToFileSource(root_src));
    315     }
    316 
    317     pub fn addStaticLibrarySource(self: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
    318         return LibExeObjStep.createStaticLibrary(self, name, root_src);
    319     }
    320 
    321     pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep {
    322         return LibExeObjStep.createTest(self, "test", .{ .path = root_src });
    323     }
    324 
    325     pub fn addTestSource(self: *Builder, root_src: FileSource) *LibExeObjStep {
    326         return LibExeObjStep.createTest(self, "test", root_src.dupe(self));
    327     }
    328 
    329     pub fn addTestExe(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep {
    330         return LibExeObjStep.createTestExe(self, name, .{ .path = root_src });
    331     }
    332 
    333     pub fn addTestExeSource(self: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
    334         return LibExeObjStep.createTestExe(self, name, root_src.dupe(self));
    335     }
    336 
    337     pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {
    338         return addAssembleSource(self, name, .{ .path = src });
    339     }
    340 
    341     pub fn addAssembleSource(self: *Builder, name: []const u8, src: FileSource) *LibExeObjStep {
    342         const obj_step = LibExeObjStep.createObject(self, name, null);
    343         obj_step.addAssemblyFileSource(src.dupe(self));
    344         return obj_step;
    345     }
    346 
    347     /// Initializes a RunStep with argv, which must at least have the path to the
    348     /// executable. More command line arguments can be added with `addArg`,
    349     /// `addArgs`, and `addArtifactArg`.
    350     /// Be careful using this function, as it introduces a system dependency.
    351     /// To run an executable built with zig build, see `LibExeObjStep.run`.
    352     pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep {
    353         assert(argv.len >= 1);
    354         const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
    355         run_step.addArgs(argv);
    356         return run_step;
    357     }
    358 
    359     /// Allocator.dupe without the need to handle out of memory.
    360     pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
    361         return self.allocator.dupe(u8, bytes) catch unreachable;
    362     }
    363 
    364     /// Duplicates an array of strings without the need to handle out of memory.
    365     pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 {
    366         const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
    367         for (strings) |s, i| {
    368             array[i] = self.dupe(s);
    369         }
    370         return array;
    371     }
    372 
    373     /// Duplicates a path and converts all slashes to the OS's canonical path separator.
    374     pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
    375         const the_copy = self.dupe(bytes);
    376         for (the_copy) |*byte| {
    377             switch (byte.*) {
    378                 '/', '\\' => byte.* = fs.path.sep,
    379                 else => {},
    380             }
    381         }
    382         return the_copy;
    383     }
    384 
    385     /// Duplicates a package recursively.
    386     pub fn dupePkg(self: *Builder, package: Pkg) Pkg {
    387         var the_copy = Pkg{
    388             .name = self.dupe(package.name),
    389             .source = package.source.dupe(self),
    390         };
    391 
    392         if (package.dependencies) |dependencies| {
    393             const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
    394             the_copy.dependencies = new_dependencies;
    395 
    396             for (dependencies) |dep_package, i| {
    397                 new_dependencies[i] = self.dupePkg(dep_package);
    398             }
    399         }
    400         return the_copy;
    401     }
    402 
    403     pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
    404         const write_file_step = self.addWriteFiles();
    405         write_file_step.add(file_path, data);
    406         return write_file_step;
    407     }
    408 
    409     pub fn addWriteFiles(self: *Builder) *WriteFileStep {
    410         const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
    411         write_file_step.* = WriteFileStep.init(self);
    412         return write_file_step;
    413     }
    414 
    415     pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
    416         const data = self.fmt(format, args);
    417         const log_step = self.allocator.create(LogStep) catch unreachable;
    418         log_step.* = LogStep.init(self, data);
    419         return log_step;
    420     }
    421 
    422     pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep {
    423         const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
    424         remove_dir_step.* = RemoveDirStep.init(self, dir_path);
    425         return remove_dir_step;
    426     }
    427 
    428     pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep {
    429         return FmtStep.create(self, paths);
    430     }
    431 
    432     pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep {
    433         return TranslateCStep.create(self, source.dupe(self));
    434     }
    435 
    436     pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind {
    437         _ = self;
    438         return .{
    439             .versioned = .{
    440                 .major = major,
    441                 .minor = minor,
    442                 .patch = patch,
    443             },
    444         };
    445     }
    446 
    447     pub fn make(self: *Builder, step_names: []const []const u8) !void {
    448         try self.makePath(self.cache_root);
    449 
    450         var wanted_steps = ArrayList(*Step).init(self.allocator);
    451         defer wanted_steps.deinit();
    452 
    453         if (step_names.len == 0) {
    454             try wanted_steps.append(self.default_step);
    455         } else {
    456             for (step_names) |step_name| {
    457                 const s = try self.getTopLevelStepByName(step_name);
    458                 try wanted_steps.append(s);
    459             }
    460         }
    461 
    462         for (wanted_steps.items) |s| {
    463             try self.makeOneStep(s);
    464         }
    465     }
    466 
    467     pub fn getInstallStep(self: *Builder) *Step {
    468         return &self.install_tls.step;
    469     }
    470 
    471     pub fn getUninstallStep(self: *Builder) *Step {
    472         return &self.uninstall_tls.step;
    473     }
    474 
    475     fn makeUninstall(uninstall_step: *Step) anyerror!void {
    476         const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
    477         const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
    478 
    479         for (self.installed_files.items) |installed_file| {
    480             const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
    481             if (self.verbose) {
    482                 log.info("rm {s}", .{full_path});
    483             }
    484             fs.cwd().deleteTree(full_path) catch {};
    485         }
    486 
    487         // TODO remove empty directories
    488     }
    489 
    490     fn makeOneStep(self: *Builder, s: *Step) anyerror!void {
    491         if (s.loop_flag) {
    492             log.err("Dependency loop detected:\n  {s}", .{s.name});
    493             return error.DependencyLoopDetected;
    494         }
    495         s.loop_flag = true;
    496 
    497         for (s.dependencies.items) |dep| {
    498             self.makeOneStep(dep) catch |err| {
    499                 if (err == error.DependencyLoopDetected) {
    500                     log.err("  {s}", .{s.name});
    501                 }
    502                 return err;
    503             };
    504         }
    505 
    506         s.loop_flag = false;
    507 
    508         try s.make();
    509     }
    510 
    511     fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step {
    512         for (self.top_level_steps.items) |top_level_step| {
    513             if (mem.eql(u8, top_level_step.step.name, name)) {
    514                 return &top_level_step.step;
    515             }
    516         }
    517         log.err("Cannot run step '{s}' because it does not exist", .{name});
    518         return error.InvalidStepName;
    519     }
    520 
    521     pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
    522         const name = self.dupe(name_raw);
    523         const description = self.dupe(description_raw);
    524         const type_id = comptime typeToEnum(T);
    525         const enum_options = if (type_id == .@"enum") blk: {
    526             const fields = comptime std.meta.fields(T);
    527             var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable;
    528 
    529             inline for (fields) |field| {
    530                 options.appendAssumeCapacity(field.name);
    531             }
    532 
    533             break :blk options.toOwnedSlice();
    534         } else null;
    535         const available_option = AvailableOption{
    536             .name = name,
    537             .type_id = type_id,
    538             .description = description,
    539             .enum_options = enum_options,
    540         };
    541         if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
    542             panic("Option '{s}' declared twice", .{name});
    543         }
    544         self.available_options_list.append(available_option) catch unreachable;
    545 
    546         const option_ptr = self.user_input_options.getPtr(name) orelse return null;
    547         option_ptr.used = true;
    548         switch (type_id) {
    549             .bool => switch (option_ptr.value) {
    550                 .flag => return true,
    551                 .scalar => |s| {
    552                     if (mem.eql(u8, s, "true")) {
    553                         return true;
    554                     } else if (mem.eql(u8, s, "false")) {
    555                         return false;
    556                     } else {
    557                         log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
    558                         self.markInvalidUserInput();
    559                         return null;
    560                     }
    561                 },
    562                 .list => {
    563                     log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name});
    564                     self.markInvalidUserInput();
    565                     return null;
    566                 },
    567             },
    568             .int => switch (option_ptr.value) {
    569                 .flag => {
    570                     log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name});
    571                     self.markInvalidUserInput();
    572                     return null;
    573                 },
    574                 .scalar => |s| {
    575                     const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
    576                         error.Overflow => {
    577                             log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
    578                             self.markInvalidUserInput();
    579                             return null;
    580                         },
    581                         else => {
    582                             log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
    583                             self.markInvalidUserInput();
    584                             return null;
    585                         },
    586                     };
    587                     return n;
    588                 },
    589                 .list => {
    590                     log.err("Expected -D{s} to be an integer, but received a list.\n", .{name});
    591                     self.markInvalidUserInput();
    592                     return null;
    593                 },
    594             },
    595             .float => switch (option_ptr.value) {
    596                 .flag => {
    597                     log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name});
    598                     self.markInvalidUserInput();
    599                     return null;
    600                 },
    601                 .scalar => |s| {
    602                     const n = std.fmt.parseFloat(T, s) catch {
    603                         log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
    604                         self.markInvalidUserInput();
    605                         return null;
    606                     };
    607                     return n;
    608                 },
    609                 .list => {
    610                     log.err("Expected -D{s} to be a float, but received a list.\n", .{name});
    611                     self.markInvalidUserInput();
    612                     return null;
    613                 },
    614             },
    615             .@"enum" => switch (option_ptr.value) {
    616                 .flag => {
    617                     log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
    618                     self.markInvalidUserInput();
    619                     return null;
    620                 },
    621                 .scalar => |s| {
    622                     if (std.meta.stringToEnum(T, s)) |enum_lit| {
    623                         return enum_lit;
    624                     } else {
    625                         log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
    626                         self.markInvalidUserInput();
    627                         return null;
    628                     }
    629                 },
    630                 .list => {
    631                     log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
    632                     self.markInvalidUserInput();
    633                     return null;
    634                 },
    635             },
    636             .string => switch (option_ptr.value) {
    637                 .flag => {
    638                     log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
    639                     self.markInvalidUserInput();
    640                     return null;
    641                 },
    642                 .list => {
    643                     log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
    644                     self.markInvalidUserInput();
    645                     return null;
    646                 },
    647                 .scalar => |s| return s,
    648             },
    649             .list => switch (option_ptr.value) {
    650                 .flag => {
    651                     log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name});
    652                     self.markInvalidUserInput();
    653                     return null;
    654                 },
    655                 .scalar => |s| {
    656                     return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
    657                 },
    658                 .list => |lst| return lst.items,
    659             },
    660         }
    661     }
    662 
    663     pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step {
    664         const step_info = self.allocator.create(TopLevelStep) catch unreachable;
    665         step_info.* = TopLevelStep{
    666             .step = Step.initNoOp(.top_level, name, self.allocator),
    667             .description = self.dupe(description),
    668         };
    669         self.top_level_steps.append(step_info) catch unreachable;
    670         return &step_info.step;
    671     }
    672 
    673     /// This provides the -Drelease option to the build user and does not give them the choice.
    674     pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void {
    675         if (self.release_mode != null) {
    676             @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
    677         }
    678         const description = self.fmt("Create a release build ({s})", .{@tagName(mode)});
    679         self.is_release = self.option(bool, "release", description) orelse false;
    680         self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug;
    681     }
    682 
    683     /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user
    684     /// the choice of what kind of release.
    685     pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode {
    686         if (self.release_mode) |mode| return mode;
    687 
    688         const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false;
    689         const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false;
    690         const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false;
    691 
    692         const mode = if (release_safe and !release_fast and !release_small)
    693             std.builtin.Mode.ReleaseSafe
    694         else if (release_fast and !release_safe and !release_small)
    695             std.builtin.Mode.ReleaseFast
    696         else if (release_small and !release_fast and !release_safe)
    697             std.builtin.Mode.ReleaseSmall
    698         else if (!release_fast and !release_safe and !release_small)
    699             std.builtin.Mode.Debug
    700         else x: {
    701             log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{});
    702             self.markInvalidUserInput();
    703             break :x std.builtin.Mode.Debug;
    704         };
    705         self.is_release = mode != .Debug;
    706         self.release_mode = mode;
    707         return mode;
    708     }
    709 
    710     pub const StandardTargetOptionsArgs = struct {
    711         whitelist: ?[]const CrossTarget = null,
    712 
    713         default_target: CrossTarget = CrossTarget{},
    714     };
    715 
    716     /// Exposes standard `zig build` options for choosing a target.
    717     pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget {
    718         const maybe_triple = self.option(
    719             []const u8,
    720             "target",
    721             "The CPU architecture, OS, and ABI to build for",
    722         );
    723         const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
    724 
    725         if (maybe_triple == null and mcpu == null) {
    726             return args.default_target;
    727         }
    728 
    729         const triple = maybe_triple orelse "native";
    730 
    731         var diags: CrossTarget.ParseOptions.Diagnostics = .{};
    732         const selected_target = CrossTarget.parse(.{
    733             .arch_os_abi = triple,
    734             .cpu_features = mcpu,
    735             .diagnostics = &diags,
    736         }) catch |err| switch (err) {
    737             error.UnknownCpuModel => {
    738                 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
    739                     diags.cpu_name.?,
    740                     @tagName(diags.arch.?),
    741                 });
    742                 for (diags.arch.?.allCpuModels()) |cpu| {
    743                     log.err(" {s}", .{cpu.name});
    744                 }
    745                 self.markInvalidUserInput();
    746                 return args.default_target;
    747             },
    748             error.UnknownCpuFeature => {
    749                 log.err(
    750                     \\Unknown CPU feature: '{s}'
    751                     \\Available CPU features for architecture '{s}':
    752                     \\
    753                 , .{
    754                     diags.unknown_feature_name.?,
    755                     @tagName(diags.arch.?),
    756                 });
    757                 for (diags.arch.?.allFeaturesList()) |feature| {
    758                     log.err(" {s}: {s}", .{ feature.name, feature.description });
    759                 }
    760                 self.markInvalidUserInput();
    761                 return args.default_target;
    762             },
    763             error.UnknownOperatingSystem => {
    764                 log.err(
    765                     \\Unknown OS: '{s}'
    766                     \\Available operating systems:
    767                     \\
    768                 , .{diags.os_name.?});
    769                 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
    770                     log.err(" {s}", .{field.name});
    771                 }
    772                 self.markInvalidUserInput();
    773                 return args.default_target;
    774             },
    775             else => |e| {
    776                 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
    777                 self.markInvalidUserInput();
    778                 return args.default_target;
    779             },
    780         };
    781 
    782         const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
    783 
    784         if (args.whitelist) |list| whitelist_check: {
    785             // Make sure it's a match of one of the list.
    786             var mismatch_triple = true;
    787             var mismatch_cpu_features = true;
    788             var whitelist_item = CrossTarget{};
    789             for (list) |t| {
    790                 mismatch_cpu_features = true;
    791                 mismatch_triple = true;
    792 
    793                 const t_triple = t.zigTriple(self.allocator) catch unreachable;
    794                 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
    795                     mismatch_triple = false;
    796                     whitelist_item = t;
    797                     if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
    798                         mismatch_cpu_features = false;
    799                         break :whitelist_check;
    800                     } else {
    801                         break;
    802                     }
    803                 }
    804             }
    805             if (mismatch_triple) {
    806                 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
    807                     selected_canonicalized_triple,
    808                 });
    809                 for (list) |t| {
    810                     const t_triple = t.zigTriple(self.allocator) catch unreachable;
    811                     log.err(" {s}", .{t_triple});
    812                 }
    813             } else {
    814                 assert(mismatch_cpu_features);
    815                 const whitelist_cpu = whitelist_item.getCpu();
    816                 const selected_cpu = selected_target.getCpu();
    817                 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
    818                     selected_cpu.model.name,
    819                 });
    820                 log.err("  Supported feature Set: ", .{});
    821                 const all_features = whitelist_cpu.arch.allFeaturesList();
    822                 var populated_cpu_features = whitelist_cpu.model.features;
    823                 populated_cpu_features.populateDependencies(all_features);
    824                 for (all_features) |feature, i_usize| {
    825                     const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
    826                     const in_cpu_set = populated_cpu_features.isEnabled(i);
    827                     if (in_cpu_set) {
    828                         log.err("{s} ", .{feature.name});
    829                     }
    830                 }
    831                 log.err("  Remove: ", .{});
    832                 for (all_features) |feature, i_usize| {
    833                     const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
    834                     const in_cpu_set = populated_cpu_features.isEnabled(i);
    835                     const in_actual_set = selected_cpu.features.isEnabled(i);
    836                     if (in_actual_set and !in_cpu_set) {
    837                         log.err("{s} ", .{feature.name});
    838                     }
    839                 }
    840             }
    841             self.markInvalidUserInput();
    842             return args.default_target;
    843         }
    844 
    845         return selected_target;
    846     }
    847 
    848     pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool {
    849         const name = self.dupe(name_raw);
    850         const value = self.dupe(value_raw);
    851         const gop = try self.user_input_options.getOrPut(name);
    852         if (!gop.found_existing) {
    853             gop.value_ptr.* = UserInputOption{
    854                 .name = name,
    855                 .value = .{ .scalar = value },
    856                 .used = false,
    857             };
    858             return false;
    859         }
    860 
    861         // option already exists
    862         switch (gop.value_ptr.value) {
    863             .scalar => |s| {
    864                 // turn it into a list
    865                 var list = ArrayList([]const u8).init(self.allocator);
    866                 list.append(s) catch unreachable;
    867                 list.append(value) catch unreachable;
    868                 self.user_input_options.put(name, .{
    869                     .name = name,
    870                     .value = .{ .list = list },
    871                     .used = false,
    872                 }) catch unreachable;
    873             },
    874             .list => |*list| {
    875                 // append to the list
    876                 list.append(value) catch unreachable;
    877                 self.user_input_options.put(name, .{
    878                     .name = name,
    879                     .value = .{ .list = list.* },
    880                     .used = false,
    881                 }) catch unreachable;
    882             },
    883             .flag => {
    884                 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
    885                 return true;
    886             },
    887         }
    888         return false;
    889     }
    890 
    891     pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool {
    892         const name = self.dupe(name_raw);
    893         const gop = try self.user_input_options.getOrPut(name);
    894         if (!gop.found_existing) {
    895             gop.value_ptr.* = .{
    896                 .name = name,
    897                 .value = .{ .flag = {} },
    898                 .used = false,
    899             };
    900             return false;
    901         }
    902 
    903         // option already exists
    904         switch (gop.value_ptr.value) {
    905             .scalar => |s| {
    906                 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
    907                 return true;
    908             },
    909             .list => {
    910                 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
    911                 return true;
    912             },
    913             .flag => {},
    914         }
    915         return false;
    916     }
    917 
    918     fn typeToEnum(comptime T: type) TypeId {
    919         return switch (@typeInfo(T)) {
    920             .Int => .int,
    921             .Float => .float,
    922             .Bool => .bool,
    923             .Enum => .@"enum",
    924             else => switch (T) {
    925                 []const u8 => .string,
    926                 []const []const u8 => .list,
    927                 else => @compileError("Unsupported type: " ++ @typeName(T)),
    928             },
    929         };
    930     }
    931 
    932     fn markInvalidUserInput(self: *Builder) void {
    933         self.invalid_user_input = true;
    934     }
    935 
    936     pub fn validateUserInputDidItFail(self: *Builder) bool {
    937         // make sure all args are used
    938         var it = self.user_input_options.iterator();
    939         while (it.next()) |entry| {
    940             if (!entry.value_ptr.used) {
    941                 log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*});
    942                 self.markInvalidUserInput();
    943             }
    944         }
    945 
    946         return self.invalid_user_input;
    947     }
    948 
    949     pub fn spawnChild(self: *Builder, argv: []const []const u8) !void {
    950         return self.spawnChildEnvMap(null, self.env_map, argv);
    951     }
    952 
    953     fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
    954         if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
    955         for (argv) |arg| {
    956             std.debug.print("{s} ", .{arg});
    957         }
    958         std.debug.print("\n", .{});
    959     }
    960 
    961     pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
    962         if (self.verbose) {
    963             printCmd(cwd, argv);
    964         }
    965 
    966         if (!std.process.can_spawn)
    967             return error.ExecNotSupported;
    968 
    969         var child = std.ChildProcess.init(argv, self.allocator);
    970         child.cwd = cwd;
    971         child.env_map = env_map;
    972 
    973         const term = child.spawnAndWait() catch |err| {
    974             log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
    975             return err;
    976         };
    977 
    978         switch (term) {
    979             .Exited => |code| {
    980                 if (code != 0) {
    981                     log.err("The following command exited with error code {}:", .{code});
    982                     printCmd(cwd, argv);
    983                     return error.UncleanExit;
    984                 }
    985             },
    986             else => {
    987                 log.err("The following command terminated unexpectedly:", .{});
    988                 printCmd(cwd, argv);
    989 
    990                 return error.UncleanExit;
    991             },
    992         }
    993     }
    994 
    995     pub fn makePath(self: *Builder, path: []const u8) !void {
    996         fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
    997             log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
    998             return err;
    999         };
   1000     }
   1001 
   1002     pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void {
   1003         self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
   1004     }
   1005 
   1006     pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep {
   1007         return InstallArtifactStep.create(self, artifact);
   1008     }
   1009 
   1010     ///`dest_rel_path` is relative to prefix path
   1011     pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
   1012         self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
   1013     }
   1014 
   1015     pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void {
   1016         self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
   1017     }
   1018 
   1019     ///`dest_rel_path` is relative to bin path
   1020     pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
   1021         self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
   1022     }
   1023 
   1024     ///`dest_rel_path` is relative to lib path
   1025     pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
   1026         self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
   1027     }
   1028 
   1029     /// Output format (BIN vs Intel HEX) determined by filename
   1030     pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
   1031         const raw = self.addInstallRaw(artifact, dest_filename, options);
   1032         self.getInstallStep().dependOn(&raw.step);
   1033         return raw;
   1034     }
   1035 
   1036     ///`dest_rel_path` is relative to install prefix path
   1037     pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
   1038         return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
   1039     }
   1040 
   1041     ///`dest_rel_path` is relative to bin path
   1042     pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
   1043         return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
   1044     }
   1045 
   1046     ///`dest_rel_path` is relative to lib path
   1047     pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
   1048         return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
   1049     }
   1050 
   1051     pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
   1052         return InstallRawStep.create(self, artifact, dest_filename, options);
   1053     }
   1054 
   1055     pub fn addInstallFileWithDir(
   1056         self: *Builder,
   1057         source: FileSource,
   1058         install_dir: InstallDir,
   1059         dest_rel_path: []const u8,
   1060     ) *InstallFileStep {
   1061         if (dest_rel_path.len == 0) {
   1062             panic("dest_rel_path must be non-empty", .{});
   1063         }
   1064         const install_step = self.allocator.create(InstallFileStep) catch unreachable;
   1065         install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
   1066         return install_step;
   1067     }
   1068 
   1069     pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep {
   1070         const install_step = self.allocator.create(InstallDirStep) catch unreachable;
   1071         install_step.* = InstallDirStep.init(self, options);
   1072         return install_step;
   1073     }
   1074 
   1075     pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
   1076         const file = InstalledFile{
   1077             .dir = dir,
   1078             .path = dest_rel_path,
   1079         };
   1080         self.installed_files.append(file.dupe(self)) catch unreachable;
   1081     }
   1082 
   1083     pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
   1084         if (self.verbose) {
   1085             log.info("cp {s} {s} ", .{ source_path, dest_path });
   1086         }
   1087         const cwd = fs.cwd();
   1088         const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
   1089         if (self.verbose) switch (prev_status) {
   1090             .stale => log.info("# installed", .{}),
   1091             .fresh => log.info("# up-to-date", .{}),
   1092         };
   1093     }
   1094 
   1095     pub fn truncateFile(self: *Builder, dest_path: []const u8) !void {
   1096         if (self.verbose) {
   1097             log.info("truncate {s}", .{dest_path});
   1098         }
   1099         const cwd = fs.cwd();
   1100         var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
   1101             error.FileNotFound => blk: {
   1102                 if (fs.path.dirname(dest_path)) |dirname| {
   1103                     try cwd.makePath(dirname);
   1104                 }
   1105                 break :blk try cwd.createFile(dest_path, .{});
   1106             },
   1107             else => |e| return e,
   1108         };
   1109         src_file.close();
   1110     }
   1111 
   1112     pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
   1113         return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
   1114     }
   1115 
   1116     /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable`
   1117     pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 {
   1118         return fs.path.join(self.allocator, paths) catch unreachable;
   1119     }
   1120 
   1121     pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
   1122         return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
   1123     }
   1124 
   1125     pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
   1126         // TODO report error for ambiguous situations
   1127         const exe_extension = @as(CrossTarget, .{}).exeFileExt();
   1128         for (self.search_prefixes.items) |search_prefix| {
   1129             for (names) |name| {
   1130                 if (fs.path.isAbsolute(name)) {
   1131                     return name;
   1132                 }
   1133                 const full_path = self.pathJoin(&.{
   1134                     search_prefix,
   1135                     "bin",
   1136                     self.fmt("{s}{s}", .{ name, exe_extension }),
   1137                 });
   1138                 return fs.realpathAlloc(self.allocator, full_path) catch continue;
   1139             }
   1140         }
   1141         if (self.env_map.get("PATH")) |PATH| {
   1142             for (names) |name| {
   1143                 if (fs.path.isAbsolute(name)) {
   1144                     return name;
   1145                 }
   1146                 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
   1147                 while (it.next()) |path| {
   1148                     const full_path = self.pathJoin(&.{
   1149                         path,
   1150                         self.fmt("{s}{s}", .{ name, exe_extension }),
   1151                     });
   1152                     return fs.realpathAlloc(self.allocator, full_path) catch continue;
   1153                 }
   1154             }
   1155         }
   1156         for (names) |name| {
   1157             if (fs.path.isAbsolute(name)) {
   1158                 return name;
   1159             }
   1160             for (paths) |path| {
   1161                 const full_path = self.pathJoin(&.{
   1162                     path,
   1163                     self.fmt("{s}{s}", .{ name, exe_extension }),
   1164                 });
   1165                 return fs.realpathAlloc(self.allocator, full_path) catch continue;
   1166             }
   1167         }
   1168         return error.FileNotFound;
   1169     }
   1170 
   1171     pub fn execAllowFail(
   1172         self: *Builder,
   1173         argv: []const []const u8,
   1174         out_code: *u8,
   1175         stderr_behavior: std.ChildProcess.StdIo,
   1176     ) ExecError![]u8 {
   1177         assert(argv.len != 0);
   1178 
   1179         if (!std.process.can_spawn)
   1180             return error.ExecNotSupported;
   1181 
   1182         const max_output_size = 400 * 1024;
   1183         var child = std.ChildProcess.init(argv, self.allocator);
   1184         child.stdin_behavior = .Ignore;
   1185         child.stdout_behavior = .Pipe;
   1186         child.stderr_behavior = stderr_behavior;
   1187         child.env_map = self.env_map;
   1188 
   1189         try child.spawn();
   1190 
   1191         const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
   1192             return error.ReadFailure;
   1193         };
   1194         errdefer self.allocator.free(stdout);
   1195 
   1196         const term = try child.wait();
   1197         switch (term) {
   1198             .Exited => |code| {
   1199                 if (code != 0) {
   1200                     out_code.* = @truncate(u8, code);
   1201                     return error.ExitCodeFailure;
   1202                 }
   1203                 return stdout;
   1204             },
   1205             .Signal, .Stopped, .Unknown => |code| {
   1206                 out_code.* = @truncate(u8, code);
   1207                 return error.ProcessTerminated;
   1208             },
   1209         }
   1210     }
   1211 
   1212     pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 {
   1213         assert(argv.len != 0);
   1214 
   1215         if (self.verbose) {
   1216             printCmd(null, argv);
   1217         }
   1218 
   1219         if (!std.process.can_spawn) {
   1220             if (src_step) |s| log.err("{s}...", .{s.name});
   1221             log.err("Unable to spawn the following command: cannot spawn child process", .{});
   1222             printCmd(null, argv);
   1223             std.os.abort();
   1224         }
   1225 
   1226         var code: u8 = undefined;
   1227         return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
   1228             error.ExecNotSupported => {
   1229                 if (src_step) |s| log.err("{s}...", .{s.name});
   1230                 log.err("Unable to spawn the following command: cannot spawn child process", .{});
   1231                 printCmd(null, argv);
   1232                 std.os.abort();
   1233             },
   1234             error.FileNotFound => {
   1235                 if (src_step) |s| log.err("{s}...", .{s.name});
   1236                 log.err("Unable to spawn the following command: file not found", .{});
   1237                 printCmd(null, argv);
   1238                 std.os.exit(@truncate(u8, code));
   1239             },
   1240             error.ExitCodeFailure => {
   1241                 if (src_step) |s| log.err("{s}...", .{s.name});
   1242                 if (self.prominent_compile_errors) {
   1243                     log.err("The step exited with error code {d}", .{code});
   1244                 } else {
   1245                     log.err("The following command exited with error code {d}:", .{code});
   1246                     printCmd(null, argv);
   1247                 }
   1248 
   1249                 std.os.exit(@truncate(u8, code));
   1250             },
   1251             error.ProcessTerminated => {
   1252                 if (src_step) |s| log.err("{s}...", .{s.name});
   1253                 log.err("The following command terminated unexpectedly:", .{});
   1254                 printCmd(null, argv);
   1255                 std.os.exit(@truncate(u8, code));
   1256             },
   1257             else => |e| return e,
   1258         };
   1259     }
   1260 
   1261     pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {
   1262         return self.execFromStep(argv, null);
   1263     }
   1264 
   1265     pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
   1266         self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
   1267     }
   1268 
   1269     pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
   1270         assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
   1271         const base_dir = switch (dir) {
   1272             .prefix => self.install_path,
   1273             .bin => self.exe_dir,
   1274             .lib => self.lib_dir,
   1275             .header => self.h_dir,
   1276             .custom => |path| self.pathJoin(&.{ self.install_path, path }),
   1277         };
   1278         return fs.path.resolve(
   1279             self.allocator,
   1280             &[_][]const u8{ base_dir, dest_rel_path },
   1281         ) catch unreachable;
   1282     }
   1283 
   1284     fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
   1285         const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
   1286         var list = ArrayList(PkgConfigPkg).init(self.allocator);
   1287         errdefer list.deinit();
   1288         var line_it = mem.tokenize(u8, stdout, "\r\n");
   1289         while (line_it.next()) |line| {
   1290             if (mem.trim(u8, line, " \t").len == 0) continue;
   1291             var tok_it = mem.tokenize(u8, line, " \t");
   1292             try list.append(PkgConfigPkg{
   1293                 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
   1294                 .desc = tok_it.rest(),
   1295             });
   1296         }
   1297         return list.toOwnedSlice();
   1298     }
   1299 
   1300     fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
   1301         if (self.pkg_config_pkg_list) |res| {
   1302             return res;
   1303         }
   1304         var code: u8 = undefined;
   1305         if (self.execPkgConfigList(&code)) |list| {
   1306             self.pkg_config_pkg_list = list;
   1307             return list;
   1308         } else |err| {
   1309             const result = switch (err) {
   1310                 error.ProcessTerminated => error.PkgConfigCrashed,
   1311                 error.ExecNotSupported => error.PkgConfigFailed,
   1312                 error.ExitCodeFailure => error.PkgConfigFailed,
   1313                 error.FileNotFound => error.PkgConfigNotInstalled,
   1314                 error.InvalidName => error.PkgConfigNotInstalled,
   1315                 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
   1316                 error.ChildExecFailed => error.PkgConfigFailed,
   1317                 else => return err,
   1318             };
   1319             self.pkg_config_pkg_list = result;
   1320             return result;
   1321         }
   1322     }
   1323 };
   1324 
   1325 test "builder.findProgram compiles" {
   1326     if (builtin.os.tag == .wasi) return error.SkipZigTest;
   1327 
   1328     var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
   1329     defer arena.deinit();
   1330 
   1331     const builder = try Builder.create(
   1332         arena.allocator(),
   1333         "zig",
   1334         "zig-cache",
   1335         "zig-cache",
   1336         "zig-cache",
   1337     );
   1338     defer builder.destroy();
   1339     _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
   1340 }
   1341 
   1342 /// TODO: propose some kind of `@deprecate` builtin so that we can deprecate
   1343 /// this while still having somewhat non-lazy decls. In this file we wanted to do
   1344 /// refAllDecls for example which makes it trigger `@compileError` if you try
   1345 /// to use that strategy.
   1346 pub const Version = @compileError("deprecated; Use `std.builtin.Version`");
   1347 pub const Target = @compileError("deprecated; Use `std.zig.CrossTarget`");
   1348 
   1349 pub const Pkg = struct {
   1350     name: []const u8,
   1351     source: FileSource,
   1352     dependencies: ?[]const Pkg = null,
   1353 };
   1354 
   1355 pub const CSourceFile = struct {
   1356     source: FileSource,
   1357     args: []const []const u8,
   1358 
   1359     fn dupe(self: CSourceFile, b: *Builder) CSourceFile {
   1360         return .{
   1361             .source = self.source.dupe(b),
   1362             .args = b.dupeStrings(self.args),
   1363         };
   1364     }
   1365 };
   1366 
   1367 const CSourceFiles = struct {
   1368     files: []const []const u8,
   1369     flags: []const []const u8,
   1370 };
   1371 
   1372 fn isLibCLibrary(name: []const u8) bool {
   1373     const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
   1374     for (libc_libraries) |libc_lib_name| {
   1375         if (mem.eql(u8, name, libc_lib_name))
   1376             return true;
   1377     }
   1378     return false;
   1379 }
   1380 
   1381 fn isLibCppLibrary(name: []const u8) bool {
   1382     const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
   1383     for (libcpp_libraries) |libcpp_lib_name| {
   1384         if (mem.eql(u8, name, libcpp_lib_name))
   1385             return true;
   1386     }
   1387     return false;
   1388 }
   1389 
   1390 /// A file that is generated by a build step.
   1391 /// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
   1392 pub const GeneratedFile = struct {
   1393     /// The step that generates the file
   1394     step: *Step,
   1395 
   1396     /// The path to the generated file. Must be either absolute or relative to the build root.
   1397     /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
   1398     path: ?[]const u8 = null,
   1399 
   1400     pub fn getPath(self: GeneratedFile) []const u8 {
   1401         return self.path orelse std.debug.panic(
   1402             "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
   1403             .{self.step.name},
   1404         );
   1405     }
   1406 };
   1407 
   1408 /// A file source is a reference to an existing or future file.
   1409 ///
   1410 pub const FileSource = union(enum) {
   1411     /// A plain file path, relative to build root or absolute.
   1412     path: []const u8,
   1413 
   1414     /// A file that is generated by an interface. Those files usually are
   1415     /// not available until built by a build step.
   1416     generated: *const GeneratedFile,
   1417 
   1418     /// Returns a new file source that will have a relative path to the build root guaranteed.
   1419     /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
   1420     pub fn relative(path: []const u8) FileSource {
   1421         std.debug.assert(!std.fs.path.isAbsolute(path));
   1422         return FileSource{ .path = path };
   1423     }
   1424 
   1425     /// Returns a string that can be shown to represent the file source.
   1426     /// Either returns the path or `"generated"`.
   1427     pub fn getDisplayName(self: FileSource) []const u8 {
   1428         return switch (self) {
   1429             .path => self.path,
   1430             .generated => "generated",
   1431         };
   1432     }
   1433 
   1434     /// Adds dependencies this file source implies to the given step.
   1435     pub fn addStepDependencies(self: FileSource, step: *Step) void {
   1436         switch (self) {
   1437             .path => {},
   1438             .generated => |gen| step.dependOn(gen.step),
   1439         }
   1440     }
   1441 
   1442     /// Should only be called during make(), returns a path relative to the build root or absolute.
   1443     pub fn getPath(self: FileSource, builder: *Builder) []const u8 {
   1444         const path = switch (self) {
   1445             .path => |p| builder.pathFromRoot(p),
   1446             .generated => |gen| gen.getPath(),
   1447         };
   1448         return path;
   1449     }
   1450 
   1451     /// Duplicates the file source for a given builder.
   1452     pub fn dupe(self: FileSource, b: *Builder) FileSource {
   1453         return switch (self) {
   1454             .path => |p| .{ .path = b.dupePath(p) },
   1455             .generated => |gen| .{ .generated = gen },
   1456         };
   1457     }
   1458 };
   1459 
   1460 pub const LibExeObjStep = struct {
   1461     pub const base_id = .lib_exe_obj;
   1462 
   1463     step: Step,
   1464     builder: *Builder,
   1465     name: []const u8,
   1466     target: CrossTarget = CrossTarget{},
   1467     target_info: NativeTargetInfo,
   1468     linker_script: ?FileSource = null,
   1469     version_script: ?[]const u8 = null,
   1470     out_filename: []const u8,
   1471     linkage: ?Linkage = null,
   1472     version: ?std.builtin.Version,
   1473     build_mode: std.builtin.Mode,
   1474     kind: Kind,
   1475     major_only_filename: ?[]const u8,
   1476     name_only_filename: ?[]const u8,
   1477     strip: bool,
   1478     // keep in sync with src/link.zig:CompressDebugSections
   1479     compress_debug_sections: enum { none, zlib } = .none,
   1480     lib_paths: ArrayList([]const u8),
   1481     rpaths: ArrayList([]const u8),
   1482     framework_dirs: ArrayList([]const u8),
   1483     frameworks: StringHashMap(FrameworkLinkInfo),
   1484     verbose_link: bool,
   1485     verbose_cc: bool,
   1486     emit_analysis: EmitOption = .default,
   1487     emit_asm: EmitOption = .default,
   1488     emit_bin: EmitOption = .default,
   1489     emit_docs: EmitOption = .default,
   1490     emit_implib: EmitOption = .default,
   1491     emit_llvm_bc: EmitOption = .default,
   1492     emit_llvm_ir: EmitOption = .default,
   1493     // Lots of things depend on emit_h having a consistent path,
   1494     // so it is not an EmitOption for now.
   1495     emit_h: bool = false,
   1496     bundle_compiler_rt: ?bool = null,
   1497     single_threaded: ?bool = null,
   1498     disable_stack_probing: bool,
   1499     disable_sanitize_c: bool,
   1500     sanitize_thread: bool,
   1501     rdynamic: bool,
   1502     import_memory: bool = false,
   1503     import_table: bool = false,
   1504     export_table: bool = false,
   1505     initial_memory: ?u64 = null,
   1506     max_memory: ?u64 = null,
   1507     shared_memory: bool = false,
   1508     global_base: ?u64 = null,
   1509     c_std: Builder.CStd,
   1510     override_lib_dir: ?[]const u8,
   1511     main_pkg_path: ?[]const u8,
   1512     exec_cmd_args: ?[]const ?[]const u8,
   1513     name_prefix: []const u8,
   1514     filter: ?[]const u8,
   1515     test_evented_io: bool = false,
   1516     code_model: std.builtin.CodeModel = .default,
   1517     wasi_exec_model: ?std.builtin.WasiExecModel = null,
   1518     /// Symbols to be exported when compiling to wasm
   1519     export_symbol_names: []const []const u8 = &.{},
   1520 
   1521     root_src: ?FileSource,
   1522     out_h_filename: []const u8,
   1523     out_lib_filename: []const u8,
   1524     out_pdb_filename: []const u8,
   1525     packages: ArrayList(Pkg),
   1526 
   1527     object_src: []const u8,
   1528 
   1529     link_objects: ArrayList(LinkObject),
   1530     include_dirs: ArrayList(IncludeDir),
   1531     c_macros: ArrayList([]const u8),
   1532     output_dir: ?[]const u8,
   1533     is_linking_libc: bool = false,
   1534     is_linking_libcpp: bool = false,
   1535     vcpkg_bin_path: ?[]const u8 = null,
   1536 
   1537     /// This may be set in order to override the default install directory
   1538     override_dest_dir: ?InstallDir,
   1539     installed_path: ?[]const u8,
   1540     install_step: ?*InstallArtifactStep,
   1541 
   1542     /// Base address for an executable image.
   1543     image_base: ?u64 = null,
   1544 
   1545     libc_file: ?FileSource = null,
   1546 
   1547     valgrind_support: ?bool = null,
   1548     each_lib_rpath: ?bool = null,
   1549     /// On ELF targets, this will emit a link section called ".note.gnu.build-id"
   1550     /// which can be used to coordinate a stripped binary with its debug symbols.
   1551     /// As an example, the bloaty project refuses to work unless its inputs have
   1552     /// build ids, in order to prevent accidental mismatches.
   1553     /// The default is to not include this section because it slows down linking.
   1554     build_id: ?bool = null,
   1555 
   1556     /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
   1557     /// file.
   1558     link_eh_frame_hdr: bool = false,
   1559     link_emit_relocs: bool = false,
   1560 
   1561     /// Place every function in its own section so that unused ones may be
   1562     /// safely garbage-collected during the linking phase.
   1563     link_function_sections: bool = false,
   1564 
   1565     /// Remove functions and data that are unreachable by the entry point or
   1566     /// exported symbols.
   1567     link_gc_sections: ?bool = null,
   1568 
   1569     linker_allow_shlib_undefined: ?bool = null,
   1570 
   1571     /// Permit read-only relocations in read-only segments. Disallowed by default.
   1572     link_z_notext: bool = false,
   1573 
   1574     /// Force all relocations to be read-only after processing.
   1575     link_z_relro: bool = true,
   1576 
   1577     /// Allow relocations to be lazily processed after load.
   1578     link_z_lazy: bool = false,
   1579 
   1580     /// (Darwin) Install name for the dylib
   1581     install_name: ?[]const u8 = null,
   1582 
   1583     /// (Darwin) Path to entitlements file
   1584     entitlements: ?[]const u8 = null,
   1585 
   1586     /// (Darwin) Size of the pagezero segment.
   1587     pagezero_size: ?u64 = null,
   1588 
   1589     /// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
   1590     /// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
   1591     /// option.
   1592     /// By default, if no option is specified, the linker assumes `paths_first` as the default
   1593     /// search strategy.
   1594     search_strategy: ?enum { paths_first, dylibs_first } = null,
   1595 
   1596     /// (Darwin) Set size of the padding between the end of load commands
   1597     /// and start of `__TEXT,__text` section.
   1598     headerpad_size: ?u32 = null,
   1599 
   1600     /// (Darwin) Automatically Set size of the padding between the end of load commands
   1601     /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
   1602     headerpad_max_install_names: bool = false,
   1603 
   1604     /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
   1605     dead_strip_dylibs: bool = false,
   1606 
   1607     /// Position Independent Code
   1608     force_pic: ?bool = null,
   1609 
   1610     /// Position Independent Executable
   1611     pie: ?bool = null,
   1612 
   1613     red_zone: ?bool = null,
   1614 
   1615     omit_frame_pointer: ?bool = null,
   1616     dll_export_fns: ?bool = null,
   1617 
   1618     subsystem: ?std.Target.SubSystem = null,
   1619 
   1620     entry_symbol_name: ?[]const u8 = null,
   1621 
   1622     /// Overrides the default stack size
   1623     stack_size: ?u64 = null,
   1624 
   1625     want_lto: ?bool = null,
   1626     use_stage1: ?bool = null,
   1627     use_llvm: ?bool = null,
   1628     use_lld: ?bool = null,
   1629     ofmt: ?std.Target.ObjectFormat = null,
   1630 
   1631     output_path_source: GeneratedFile,
   1632     output_lib_path_source: GeneratedFile,
   1633     output_h_path_source: GeneratedFile,
   1634     output_pdb_path_source: GeneratedFile,
   1635 
   1636     pub const LinkObject = union(enum) {
   1637         static_path: FileSource,
   1638         other_step: *LibExeObjStep,
   1639         system_lib: SystemLib,
   1640         assembly_file: FileSource,
   1641         c_source_file: *CSourceFile,
   1642         c_source_files: *CSourceFiles,
   1643     };
   1644 
   1645     pub const SystemLib = struct {
   1646         name: []const u8,
   1647         needed: bool,
   1648         weak: bool,
   1649         use_pkg_config: enum {
   1650             /// Don't use pkg-config, just pass -lfoo where foo is name.
   1651             no,
   1652             /// Try to get information on how to link the library from pkg-config.
   1653             /// If that fails, fall back to passing -lfoo where foo is name.
   1654             yes,
   1655             /// Try to get information on how to link the library from pkg-config.
   1656             /// If that fails, error out.
   1657             force,
   1658         },
   1659     };
   1660 
   1661     const FrameworkLinkInfo = struct {
   1662         needed: bool = false,
   1663         weak: bool = false,
   1664     };
   1665 
   1666     pub const IncludeDir = union(enum) {
   1667         raw_path: []const u8,
   1668         raw_path_system: []const u8,
   1669         other_step: *LibExeObjStep,
   1670     };
   1671 
   1672     pub const Kind = enum {
   1673         exe,
   1674         lib,
   1675         obj,
   1676         @"test",
   1677         test_exe,
   1678     };
   1679 
   1680     pub const SharedLibKind = union(enum) {
   1681         versioned: std.builtin.Version,
   1682         unversioned: void,
   1683     };
   1684 
   1685     pub const Linkage = enum { dynamic, static };
   1686 
   1687     pub const EmitOption = union(enum) {
   1688         default: void,
   1689         no_emit: void,
   1690         emit: void,
   1691         emit_to: []const u8,
   1692 
   1693         fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 {
   1694             return switch (self) {
   1695                 .no_emit => b.fmt("-fno-{s}", .{arg_name}),
   1696                 .default => null,
   1697                 .emit => b.fmt("-f{s}", .{arg_name}),
   1698                 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
   1699             };
   1700         }
   1701     };
   1702 
   1703     pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep {
   1704         return initExtraArgs(builder, name, root_src, .lib, .dynamic, switch (kind) {
   1705             .versioned => |ver| ver,
   1706             .unversioned => null,
   1707         });
   1708     }
   1709 
   1710     pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
   1711         return initExtraArgs(builder, name, root_src, .lib, .static, null);
   1712     }
   1713 
   1714     pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
   1715         return initExtraArgs(builder, name, root_src, .obj, null, null);
   1716     }
   1717 
   1718     pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
   1719         return initExtraArgs(builder, name, root_src, .exe, null, null);
   1720     }
   1721 
   1722     pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
   1723         return initExtraArgs(builder, name, root_src, .@"test", null, null);
   1724     }
   1725 
   1726     pub fn createTestExe(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
   1727         return initExtraArgs(builder, name, root_src, .test_exe, null, null);
   1728     }
   1729 
   1730     fn initExtraArgs(
   1731         builder: *Builder,
   1732         name_raw: []const u8,
   1733         root_src_raw: ?FileSource,
   1734         kind: Kind,
   1735         linkage: ?Linkage,
   1736         ver: ?std.builtin.Version,
   1737     ) *LibExeObjStep {
   1738         const name = builder.dupe(name_raw);
   1739         const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null;
   1740         if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
   1741             panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
   1742         }
   1743 
   1744         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
   1745         self.* = LibExeObjStep{
   1746             .strip = false,
   1747             .builder = builder,
   1748             .verbose_link = false,
   1749             .verbose_cc = false,
   1750             .build_mode = std.builtin.Mode.Debug,
   1751             .linkage = linkage,
   1752             .kind = kind,
   1753             .root_src = root_src,
   1754             .name = name,
   1755             .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
   1756             .step = Step.init(base_id, name, builder.allocator, make),
   1757             .version = ver,
   1758             .out_filename = undefined,
   1759             .out_h_filename = builder.fmt("{s}.h", .{name}),
   1760             .out_lib_filename = undefined,
   1761             .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
   1762             .major_only_filename = null,
   1763             .name_only_filename = null,
   1764             .packages = ArrayList(Pkg).init(builder.allocator),
   1765             .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
   1766             .link_objects = ArrayList(LinkObject).init(builder.allocator),
   1767             .c_macros = ArrayList([]const u8).init(builder.allocator),
   1768             .lib_paths = ArrayList([]const u8).init(builder.allocator),
   1769             .rpaths = ArrayList([]const u8).init(builder.allocator),
   1770             .framework_dirs = ArrayList([]const u8).init(builder.allocator),
   1771             .object_src = undefined,
   1772             .c_std = Builder.CStd.C99,
   1773             .override_lib_dir = null,
   1774             .main_pkg_path = null,
   1775             .exec_cmd_args = null,
   1776             .name_prefix = "",
   1777             .filter = null,
   1778             .disable_stack_probing = false,
   1779             .disable_sanitize_c = false,
   1780             .sanitize_thread = false,
   1781             .rdynamic = false,
   1782             .output_dir = null,
   1783             .override_dest_dir = null,
   1784             .installed_path = null,
   1785             .install_step = null,
   1786 
   1787             .output_path_source = GeneratedFile{ .step = &self.step },
   1788             .output_lib_path_source = GeneratedFile{ .step = &self.step },
   1789             .output_h_path_source = GeneratedFile{ .step = &self.step },
   1790             .output_pdb_path_source = GeneratedFile{ .step = &self.step },
   1791 
   1792             .target_info = undefined, // populated in computeOutFileNames
   1793         };
   1794         self.computeOutFileNames();
   1795         if (root_src) |rs| rs.addStepDependencies(&self.step);
   1796         return self;
   1797     }
   1798 
   1799     fn computeOutFileNames(self: *LibExeObjStep) void {
   1800         self.target_info = NativeTargetInfo.detect(self.builder.allocator, self.target) catch
   1801             unreachable;
   1802 
   1803         const target = self.target_info.target;
   1804 
   1805         self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
   1806             .root_name = self.name,
   1807             .target = target,
   1808             .output_mode = switch (self.kind) {
   1809                 .lib => .Lib,
   1810                 .obj => .Obj,
   1811                 .exe, .@"test", .test_exe => .Exe,
   1812             },
   1813             .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
   1814                 .dynamic => .Dynamic,
   1815                 .static => .Static,
   1816             }) else null,
   1817             .version = self.version,
   1818         }) catch unreachable;
   1819 
   1820         if (self.kind == .lib) {
   1821             if (self.linkage != null and self.linkage.? == .static) {
   1822                 self.out_lib_filename = self.out_filename;
   1823             } else if (self.version) |version| {
   1824                 if (target.isDarwin()) {
   1825                     self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
   1826                         self.name,
   1827                         version.major,
   1828                     });
   1829                     self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
   1830                     self.out_lib_filename = self.out_filename;
   1831                 } else if (target.os.tag == .windows) {
   1832                     self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
   1833                 } else {
   1834                     self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
   1835                     self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
   1836                     self.out_lib_filename = self.out_filename;
   1837                 }
   1838             } else {
   1839                 if (target.isDarwin()) {
   1840                     self.out_lib_filename = self.out_filename;
   1841                 } else if (target.os.tag == .windows) {
   1842                     self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
   1843                 } else {
   1844                     self.out_lib_filename = self.out_filename;
   1845                 }
   1846             }
   1847             if (self.output_dir != null) {
   1848                 self.output_lib_path_source.path = self.builder.pathJoin(
   1849                     &.{ self.output_dir.?, self.out_lib_filename },
   1850                 );
   1851             }
   1852         }
   1853     }
   1854 
   1855     pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void {
   1856         self.target = target;
   1857         self.computeOutFileNames();
   1858     }
   1859 
   1860     pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
   1861         self.output_dir = self.builder.dupePath(dir);
   1862     }
   1863 
   1864     pub fn install(self: *LibExeObjStep) void {
   1865         self.builder.installArtifact(self);
   1866     }
   1867 
   1868     pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
   1869         return self.builder.installRaw(self, dest_filename, options);
   1870     }
   1871 
   1872     /// Creates a `RunStep` with an executable built with `addExecutable`.
   1873     /// Add command line arguments with `addArg`.
   1874     pub fn run(exe: *LibExeObjStep) *RunStep {
   1875         assert(exe.kind == .exe or exe.kind == .test_exe);
   1876 
   1877         // It doesn't have to be native. We catch that if you actually try to run it.
   1878         // Consider that this is declarative; the run step may not be run unless a user
   1879         // option is supplied.
   1880         const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
   1881         run_step.addArtifactArg(exe);
   1882 
   1883         if (exe.kind == .test_exe) {
   1884             run_step.addArg(exe.builder.zig_exe);
   1885         }
   1886 
   1887         if (exe.vcpkg_bin_path) |path| {
   1888             run_step.addPathDir(path);
   1889         }
   1890 
   1891         return run_step;
   1892     }
   1893 
   1894     /// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
   1895     /// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
   1896     /// When a binary cannot be ran through emulation or the option is disabled, a warning
   1897     /// will be printed and the binary will *NOT* be ran.
   1898     pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
   1899         assert(exe.kind == .exe or exe.kind == .test_exe);
   1900 
   1901         const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
   1902         if (exe.vcpkg_bin_path) |path| {
   1903             RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
   1904         }
   1905         return run_step;
   1906     }
   1907 
   1908     pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
   1909         return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
   1910     }
   1911 
   1912     pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
   1913         self.linker_script = source.dupe(self.builder);
   1914         source.addStepDependencies(&self.step);
   1915     }
   1916 
   1917     pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
   1918         self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
   1919     }
   1920 
   1921     pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
   1922         self.frameworks.put(self.builder.dupe(framework_name), .{
   1923             .needed = true,
   1924         }) catch unreachable;
   1925     }
   1926 
   1927     pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
   1928         self.frameworks.put(self.builder.dupe(framework_name), .{
   1929             .weak = true,
   1930         }) catch unreachable;
   1931     }
   1932 
   1933     /// Returns whether the library, executable, or object depends on a particular system library.
   1934     pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
   1935         if (isLibCLibrary(name)) {
   1936             return self.is_linking_libc;
   1937         }
   1938         if (isLibCppLibrary(name)) {
   1939             return self.is_linking_libcpp;
   1940         }
   1941         for (self.link_objects.items) |link_object| {
   1942             switch (link_object) {
   1943                 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
   1944                 else => continue,
   1945             }
   1946         }
   1947         return false;
   1948     }
   1949 
   1950     pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
   1951         assert(lib.kind == .lib);
   1952         self.linkLibraryOrObject(lib);
   1953     }
   1954 
   1955     pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
   1956         return self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic;
   1957     }
   1958 
   1959     pub fn producesPdbFile(self: *LibExeObjStep) bool {
   1960         if (!self.target.isWindows() and !self.target.isUefi()) return false;
   1961         if (self.strip) return false;
   1962         return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
   1963     }
   1964 
   1965     pub fn linkLibC(self: *LibExeObjStep) void {
   1966         if (!self.is_linking_libc) {
   1967             self.is_linking_libc = true;
   1968             self.link_objects.append(.{
   1969                 .system_lib = .{
   1970                     .name = "c",
   1971                     .needed = false,
   1972                     .weak = false,
   1973                     .use_pkg_config = .no,
   1974                 },
   1975             }) catch unreachable;
   1976         }
   1977     }
   1978 
   1979     pub fn linkLibCpp(self: *LibExeObjStep) void {
   1980         if (!self.is_linking_libcpp) {
   1981             self.is_linking_libcpp = true;
   1982             self.link_objects.append(.{
   1983                 .system_lib = .{
   1984                     .name = "c++",
   1985                     .needed = false,
   1986                     .weak = false,
   1987                     .use_pkg_config = .no,
   1988                 },
   1989             }) catch unreachable;
   1990         }
   1991     }
   1992 
   1993     /// If the value is omitted, it is set to 1.
   1994     /// `name` and `value` need not live longer than the function call.
   1995     pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
   1996         const macro = constructCMacro(self.builder.allocator, name, value);
   1997         self.c_macros.append(macro) catch unreachable;
   1998     }
   1999 
   2000     /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
   2001     pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
   2002         self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
   2003     }
   2004 
   2005     /// This one has no integration with anything, it just puts -lname on the command line.
   2006     /// Prefer to use `linkSystemLibrary` instead.
   2007     pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
   2008         self.link_objects.append(.{
   2009             .system_lib = .{
   2010                 .name = self.builder.dupe(name),
   2011                 .needed = false,
   2012                 .weak = false,
   2013                 .use_pkg_config = .no,
   2014             },
   2015         }) catch unreachable;
   2016     }
   2017 
   2018     /// This one has no integration with anything, it just puts -needed-lname on the command line.
   2019     /// Prefer to use `linkSystemLibraryNeeded` instead.
   2020     pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
   2021         self.link_objects.append(.{
   2022             .system_lib = .{
   2023                 .name = self.builder.dupe(name),
   2024                 .needed = true,
   2025                 .weak = false,
   2026                 .use_pkg_config = .no,
   2027             },
   2028         }) catch unreachable;
   2029     }
   2030 
   2031     /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
   2032     /// command line. Prefer to use `linkSystemLibraryWeak` instead.
   2033     pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
   2034         self.link_objects.append(.{
   2035             .system_lib = .{
   2036                 .name = self.builder.dupe(name),
   2037                 .needed = false,
   2038                 .weak = true,
   2039                 .use_pkg_config = .no,
   2040             },
   2041         }) catch unreachable;
   2042     }
   2043 
   2044     /// This links against a system library, exclusively using pkg-config to find the library.
   2045     /// Prefer to use `linkSystemLibrary` instead.
   2046     pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
   2047         self.link_objects.append(.{
   2048             .system_lib = .{
   2049                 .name = self.builder.dupe(lib_name),
   2050                 .needed = false,
   2051                 .weak = false,
   2052                 .use_pkg_config = .force,
   2053             },
   2054         }) catch unreachable;
   2055     }
   2056 
   2057     /// This links against a system library, exclusively using pkg-config to find the library.
   2058     /// Prefer to use `linkSystemLibraryNeeded` instead.
   2059     pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
   2060         self.link_objects.append(.{
   2061             .system_lib = .{
   2062                 .name = self.builder.dupe(lib_name),
   2063                 .needed = true,
   2064                 .weak = false,
   2065                 .use_pkg_config = .force,
   2066             },
   2067         }) catch unreachable;
   2068     }
   2069 
   2070     /// Run pkg-config for the given library name and parse the output, returning the arguments
   2071     /// that should be passed to zig to link the given library.
   2072     fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
   2073         const pkg_name = match: {
   2074             // First we have to map the library name to pkg config name. Unfortunately,
   2075             // there are several examples where this is not straightforward:
   2076             // -lSDL2 -> pkg-config sdl2
   2077             // -lgdk-3 -> pkg-config gdk-3.0
   2078             // -latk-1.0 -> pkg-config atk
   2079             const pkgs = try self.builder.getPkgConfigList();
   2080 
   2081             // Exact match means instant winner.
   2082             for (pkgs) |pkg| {
   2083                 if (mem.eql(u8, pkg.name, lib_name)) {
   2084                     break :match pkg.name;
   2085                 }
   2086             }
   2087 
   2088             // Next we'll try ignoring case.
   2089             for (pkgs) |pkg| {
   2090                 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
   2091                     break :match pkg.name;
   2092                 }
   2093             }
   2094 
   2095             // Now try appending ".0".
   2096             for (pkgs) |pkg| {
   2097                 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
   2098                     if (pos != 0) continue;
   2099                     if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
   2100                         break :match pkg.name;
   2101                     }
   2102                 }
   2103             }
   2104 
   2105             // Trimming "-1.0".
   2106             if (mem.endsWith(u8, lib_name, "-1.0")) {
   2107                 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
   2108                 for (pkgs) |pkg| {
   2109                     if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
   2110                         break :match pkg.name;
   2111                     }
   2112                 }
   2113             }
   2114 
   2115             return error.PackageNotFound;
   2116         };
   2117 
   2118         var code: u8 = undefined;
   2119         const stdout = if (self.builder.execAllowFail(&[_][]const u8{
   2120             "pkg-config",
   2121             pkg_name,
   2122             "--cflags",
   2123             "--libs",
   2124         }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
   2125             error.ProcessTerminated => return error.PkgConfigCrashed,
   2126             error.ExecNotSupported => return error.PkgConfigFailed,
   2127             error.ExitCodeFailure => return error.PkgConfigFailed,
   2128             error.FileNotFound => return error.PkgConfigNotInstalled,
   2129             error.ChildExecFailed => return error.PkgConfigFailed,
   2130             else => return err,
   2131         };
   2132 
   2133         var zig_args = std.ArrayList([]const u8).init(self.builder.allocator);
   2134         defer zig_args.deinit();
   2135 
   2136         var it = mem.tokenize(u8, stdout, " \r\n\t");
   2137         while (it.next()) |tok| {
   2138             if (mem.eql(u8, tok, "-I")) {
   2139                 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
   2140                 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
   2141             } else if (mem.startsWith(u8, tok, "-I")) {
   2142                 try zig_args.append(tok);
   2143             } else if (mem.eql(u8, tok, "-L")) {
   2144                 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
   2145                 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
   2146             } else if (mem.startsWith(u8, tok, "-L")) {
   2147                 try zig_args.append(tok);
   2148             } else if (mem.eql(u8, tok, "-l")) {
   2149                 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
   2150                 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
   2151             } else if (mem.startsWith(u8, tok, "-l")) {
   2152                 try zig_args.append(tok);
   2153             } else if (mem.eql(u8, tok, "-D")) {
   2154                 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
   2155                 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
   2156             } else if (mem.startsWith(u8, tok, "-D")) {
   2157                 try zig_args.append(tok);
   2158             } else if (self.builder.verbose) {
   2159                 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
   2160             }
   2161         }
   2162 
   2163         return zig_args.toOwnedSlice();
   2164     }
   2165 
   2166     pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
   2167         self.linkSystemLibraryInner(name, .{});
   2168     }
   2169 
   2170     pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
   2171         self.linkSystemLibraryInner(name, .{ .needed = true });
   2172     }
   2173 
   2174     pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
   2175         self.linkSystemLibraryInner(name, .{ .weak = true });
   2176     }
   2177 
   2178     fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
   2179         needed: bool = false,
   2180         weak: bool = false,
   2181     }) void {
   2182         if (isLibCLibrary(name)) {
   2183             self.linkLibC();
   2184             return;
   2185         }
   2186         if (isLibCppLibrary(name)) {
   2187             self.linkLibCpp();
   2188             return;
   2189         }
   2190 
   2191         self.link_objects.append(.{
   2192             .system_lib = .{
   2193                 .name = self.builder.dupe(name),
   2194                 .needed = opts.needed,
   2195                 .weak = opts.weak,
   2196                 .use_pkg_config = .yes,
   2197             },
   2198         }) catch unreachable;
   2199     }
   2200 
   2201     pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
   2202         assert(self.kind == .@"test" or self.kind == .test_exe);
   2203         self.name_prefix = self.builder.dupe(text);
   2204     }
   2205 
   2206     pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
   2207         assert(self.kind == .@"test" or self.kind == .test_exe);
   2208         self.filter = if (text) |t| self.builder.dupe(t) else null;
   2209     }
   2210 
   2211     /// Handy when you have many C/C++ source files and want them all to have the same flags.
   2212     pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
   2213         const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
   2214 
   2215         const files_copy = self.builder.dupeStrings(files);
   2216         const flags_copy = self.builder.dupeStrings(flags);
   2217 
   2218         c_source_files.* = .{
   2219             .files = files_copy,
   2220             .flags = flags_copy,
   2221         };
   2222         self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
   2223     }
   2224 
   2225     pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
   2226         self.addCSourceFileSource(.{
   2227             .args = flags,
   2228             .source = .{ .path = file },
   2229         });
   2230     }
   2231 
   2232     pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
   2233         const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
   2234         c_source_file.* = source.dupe(self.builder);
   2235         self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
   2236         source.source.addStepDependencies(&self.step);
   2237     }
   2238 
   2239     pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
   2240         self.verbose_link = value;
   2241     }
   2242 
   2243     pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
   2244         self.verbose_cc = value;
   2245     }
   2246 
   2247     pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void {
   2248         self.build_mode = mode;
   2249     }
   2250 
   2251     pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
   2252         self.override_lib_dir = self.builder.dupePath(dir_path);
   2253     }
   2254 
   2255     pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
   2256         self.main_pkg_path = self.builder.dupePath(dir_path);
   2257     }
   2258 
   2259     pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
   2260         self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
   2261     }
   2262 
   2263     /// Returns the generated executable, library or object file.
   2264     /// To run an executable built with zig build, use `run`, or create an install step and invoke it.
   2265     pub fn getOutputSource(self: *LibExeObjStep) FileSource {
   2266         return FileSource{ .generated = &self.output_path_source };
   2267     }
   2268 
   2269     /// Returns the generated import library. This function can only be called for libraries.
   2270     pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
   2271         assert(self.kind == .lib);
   2272         return FileSource{ .generated = &self.output_lib_path_source };
   2273     }
   2274 
   2275     /// Returns the generated header file.
   2276     /// This function can only be called for libraries or object files which have `emit_h` set.
   2277     pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
   2278         assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
   2279         assert(self.emit_h);
   2280         return FileSource{ .generated = &self.output_h_path_source };
   2281     }
   2282 
   2283     /// Returns the generated PDB file. This function can only be called for Windows and UEFI.
   2284     pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
   2285         // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
   2286         assert(self.target.isWindows() or self.target.isUefi());
   2287         return FileSource{ .generated = &self.output_pdb_path_source };
   2288     }
   2289 
   2290     pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
   2291         self.link_objects.append(.{
   2292             .assembly_file = .{ .path = self.builder.dupe(path) },
   2293         }) catch unreachable;
   2294     }
   2295 
   2296     pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
   2297         const source_duped = source.dupe(self.builder);
   2298         self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
   2299         source_duped.addStepDependencies(&self.step);
   2300     }
   2301 
   2302     pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
   2303         self.addObjectFileSource(.{ .path = source_file });
   2304     }
   2305 
   2306     pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
   2307         self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
   2308         source.addStepDependencies(&self.step);
   2309     }
   2310 
   2311     pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
   2312         assert(obj.kind == .obj);
   2313         self.linkLibraryOrObject(obj);
   2314     }
   2315 
   2316     /// TODO deprecated, use `addSystemIncludePath`.
   2317     pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
   2318         self.addSystemIncludePath(path);
   2319     }
   2320 
   2321     pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
   2322         self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
   2323     }
   2324 
   2325     /// TODO deprecated, use `addIncludePath`.
   2326     pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void {
   2327         self.addIncludePath(path);
   2328     }
   2329 
   2330     pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
   2331         self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
   2332     }
   2333 
   2334     /// TODO deprecated, use `addLibraryPath`.
   2335     pub fn addLibPath(self: *LibExeObjStep, path: []const u8) void {
   2336         self.addLibraryPath(path);
   2337     }
   2338 
   2339     pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
   2340         self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
   2341     }
   2342 
   2343     pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
   2344         self.rpaths.append(self.builder.dupe(path)) catch unreachable;
   2345     }
   2346 
   2347     /// TODO deprecated, use `addFrameworkPath`.
   2348     pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void {
   2349         self.addFrameworkPath(dir_path);
   2350     }
   2351 
   2352     pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
   2353         self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
   2354     }
   2355 
   2356     pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
   2357         self.packages.append(self.builder.dupePkg(package)) catch unreachable;
   2358         self.addRecursiveBuildDeps(package);
   2359     }
   2360 
   2361     pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
   2362         self.addPackage(options.getPackage(package_name));
   2363     }
   2364 
   2365     fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
   2366         package.source.addStepDependencies(&self.step);
   2367         if (package.dependencies) |deps| {
   2368             for (deps) |dep| {
   2369                 self.addRecursiveBuildDeps(dep);
   2370             }
   2371         }
   2372     }
   2373 
   2374     pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
   2375         self.addPackage(Pkg{
   2376             .name = self.builder.dupe(name),
   2377             .source = .{ .path = self.builder.dupe(pkg_index_path) },
   2378         });
   2379     }
   2380 
   2381     /// If Vcpkg was found on the system, it will be added to include and lib
   2382     /// paths for the specified target.
   2383     pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
   2384         // Ideally in the Unattempted case we would call the function recursively
   2385         // after findVcpkgRoot and have only one switch statement, but the compiler
   2386         // cannot resolve the error set.
   2387         switch (self.builder.vcpkg_root) {
   2388             .unattempted => {
   2389                 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
   2390                     VcpkgRoot{ .found = root }
   2391                 else
   2392                     .not_found;
   2393             },
   2394             .not_found => return error.VcpkgNotFound,
   2395             .found => {},
   2396         }
   2397 
   2398         switch (self.builder.vcpkg_root) {
   2399             .unattempted => unreachable,
   2400             .not_found => return error.VcpkgNotFound,
   2401             .found => |root| {
   2402                 const allocator = self.builder.allocator;
   2403                 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
   2404                 defer self.builder.allocator.free(triplet);
   2405 
   2406                 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
   2407                 errdefer allocator.free(include_path);
   2408                 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
   2409 
   2410                 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
   2411                 try self.lib_paths.append(lib_path);
   2412 
   2413                 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
   2414             },
   2415         }
   2416     }
   2417 
   2418     pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
   2419         assert(self.kind == .@"test");
   2420         const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
   2421         for (args) |arg, i| {
   2422             duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
   2423         }
   2424         self.exec_cmd_args = duped_args;
   2425     }
   2426 
   2427     fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
   2428         self.step.dependOn(&other.step);
   2429         self.link_objects.append(.{ .other_step = other }) catch unreachable;
   2430         self.include_dirs.append(.{ .other_step = other }) catch unreachable;
   2431     }
   2432 
   2433     fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
   2434         const builder = self.builder;
   2435 
   2436         try zig_args.append("--pkg-begin");
   2437         try zig_args.append(pkg.name);
   2438         try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
   2439 
   2440         if (pkg.dependencies) |dependencies| {
   2441             for (dependencies) |sub_pkg| {
   2442                 try self.makePackageCmd(sub_pkg, zig_args);
   2443             }
   2444         }
   2445 
   2446         try zig_args.append("--pkg-end");
   2447     }
   2448 
   2449     fn make(step: *Step) !void {
   2450         const self = @fieldParentPtr(LibExeObjStep, "step", step);
   2451         const builder = self.builder;
   2452 
   2453         if (self.root_src == null and self.link_objects.items.len == 0) {
   2454             log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
   2455             return error.NeedAnObject;
   2456         }
   2457 
   2458         var zig_args = ArrayList([]const u8).init(builder.allocator);
   2459         defer zig_args.deinit();
   2460 
   2461         zig_args.append(builder.zig_exe) catch unreachable;
   2462 
   2463         const cmd = switch (self.kind) {
   2464             .lib => "build-lib",
   2465             .exe => "build-exe",
   2466             .obj => "build-obj",
   2467             .@"test" => "test",
   2468             .test_exe => "test",
   2469         };
   2470         zig_args.append(cmd) catch unreachable;
   2471 
   2472         if (builder.color != .auto) {
   2473             try zig_args.append("--color");
   2474             try zig_args.append(@tagName(builder.color));
   2475         }
   2476 
   2477         if (self.use_stage1) |stage1| {
   2478             if (stage1) {
   2479                 try zig_args.append("-fstage1");
   2480             } else {
   2481                 try zig_args.append("-fno-stage1");
   2482             }
   2483         } else if (builder.use_stage1) |stage1| {
   2484             if (stage1) {
   2485                 try zig_args.append("-fstage1");
   2486             } else {
   2487                 try zig_args.append("-fno-stage1");
   2488             }
   2489         }
   2490 
   2491         if (self.use_llvm) |use_llvm| {
   2492             if (use_llvm) {
   2493                 try zig_args.append("-fLLVM");
   2494             } else {
   2495                 try zig_args.append("-fno-LLVM");
   2496             }
   2497         }
   2498 
   2499         if (self.use_lld) |use_lld| {
   2500             if (use_lld) {
   2501                 try zig_args.append("-fLLD");
   2502             } else {
   2503                 try zig_args.append("-fno-LLD");
   2504             }
   2505         }
   2506 
   2507         if (self.ofmt) |ofmt| {
   2508             try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
   2509         }
   2510 
   2511         if (self.entry_symbol_name) |entry| {
   2512             try zig_args.append("--entry");
   2513             try zig_args.append(entry);
   2514         }
   2515 
   2516         if (self.stack_size) |stack_size| {
   2517             try zig_args.append("--stack");
   2518             try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
   2519         }
   2520 
   2521         if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
   2522 
   2523         var prev_has_extra_flags = false;
   2524 
   2525         // Resolve transitive dependencies
   2526         {
   2527             var transitive_dependencies = std.ArrayList(LinkObject).init(builder.allocator);
   2528             defer transitive_dependencies.deinit();
   2529 
   2530             for (self.link_objects.items) |link_object| {
   2531                 switch (link_object) {
   2532                     .other_step => |other| {
   2533                         // Inherit dependency on system libraries
   2534                         for (other.link_objects.items) |other_link_object| {
   2535                             switch (other_link_object) {
   2536                                 .system_lib => try transitive_dependencies.append(other_link_object),
   2537                                 else => continue,
   2538                             }
   2539                         }
   2540 
   2541                         // Inherit dependencies on darwin frameworks
   2542                         if (!other.isDynamicLibrary()) {
   2543                             var it = other.frameworks.iterator();
   2544                             while (it.next()) |framework| {
   2545                                 self.frameworks.put(framework.key_ptr.*, framework.value_ptr.*) catch unreachable;
   2546                             }
   2547                         }
   2548                     },
   2549                     else => continue,
   2550                 }
   2551             }
   2552 
   2553             try self.link_objects.appendSlice(transitive_dependencies.items);
   2554         }
   2555 
   2556         for (self.link_objects.items) |link_object| {
   2557             switch (link_object) {
   2558                 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
   2559 
   2560                 .other_step => |other| switch (other.kind) {
   2561                     .exe => @panic("Cannot link with an executable build artifact"),
   2562                     .test_exe => @panic("Cannot link with an executable build artifact"),
   2563                     .@"test" => @panic("Cannot link with a test"),
   2564                     .obj => {
   2565                         try zig_args.append(other.getOutputSource().getPath(builder));
   2566                     },
   2567                     .lib => {
   2568                         const full_path_lib = other.getOutputLibSource().getPath(builder);
   2569                         try zig_args.append(full_path_lib);
   2570 
   2571                         if (other.linkage != null and other.linkage.? == .dynamic and !self.target.isWindows()) {
   2572                             if (fs.path.dirname(full_path_lib)) |dirname| {
   2573                                 try zig_args.append("-rpath");
   2574                                 try zig_args.append(dirname);
   2575                             }
   2576                         }
   2577                     },
   2578                 },
   2579 
   2580                 .system_lib => |system_lib| {
   2581                     const prefix: []const u8 = prefix: {
   2582                         if (system_lib.needed) break :prefix "-needed-l";
   2583                         if (system_lib.weak) {
   2584                             if (self.target.isDarwin()) break :prefix "-weak-l";
   2585                             log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
   2586                         }
   2587                         break :prefix "-l";
   2588                     };
   2589                     switch (system_lib.use_pkg_config) {
   2590                         .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
   2591                         .yes, .force => {
   2592                             if (self.runPkgConfig(system_lib.name)) |args| {
   2593                                 try zig_args.appendSlice(args);
   2594                             } else |err| switch (err) {
   2595                                 error.PkgConfigInvalidOutput,
   2596                                 error.PkgConfigCrashed,
   2597                                 error.PkgConfigFailed,
   2598                                 error.PkgConfigNotInstalled,
   2599                                 error.PackageNotFound,
   2600                                 => switch (system_lib.use_pkg_config) {
   2601                                     .yes => {
   2602                                         // pkg-config failed, so fall back to linking the library
   2603                                         // by name directly.
   2604                                         try zig_args.append(builder.fmt("{s}{s}", .{
   2605                                             prefix,
   2606                                             system_lib.name,
   2607                                         }));
   2608                                     },
   2609                                     .force => {
   2610                                         panic("pkg-config failed for library {s}", .{system_lib.name});
   2611                                     },
   2612                                     .no => unreachable,
   2613                                 },
   2614 
   2615                                 else => |e| return e,
   2616                             }
   2617                         },
   2618                     }
   2619                 },
   2620 
   2621                 .assembly_file => |asm_file| {
   2622                     if (prev_has_extra_flags) {
   2623                         try zig_args.append("-extra-cflags");
   2624                         try zig_args.append("--");
   2625                         prev_has_extra_flags = false;
   2626                     }
   2627                     try zig_args.append(asm_file.getPath(builder));
   2628                 },
   2629 
   2630                 .c_source_file => |c_source_file| {
   2631                     if (c_source_file.args.len == 0) {
   2632                         if (prev_has_extra_flags) {
   2633                             try zig_args.append("-cflags");
   2634                             try zig_args.append("--");
   2635                             prev_has_extra_flags = false;
   2636                         }
   2637                     } else {
   2638                         try zig_args.append("-cflags");
   2639                         for (c_source_file.args) |arg| {
   2640                             try zig_args.append(arg);
   2641                         }
   2642                         try zig_args.append("--");
   2643                     }
   2644                     try zig_args.append(c_source_file.source.getPath(builder));
   2645                 },
   2646 
   2647                 .c_source_files => |c_source_files| {
   2648                     if (c_source_files.flags.len == 0) {
   2649                         if (prev_has_extra_flags) {
   2650                             try zig_args.append("-cflags");
   2651                             try zig_args.append("--");
   2652                             prev_has_extra_flags = false;
   2653                         }
   2654                     } else {
   2655                         try zig_args.append("-cflags");
   2656                         for (c_source_files.flags) |flag| {
   2657                             try zig_args.append(flag);
   2658                         }
   2659                         try zig_args.append("--");
   2660                     }
   2661                     for (c_source_files.files) |file| {
   2662                         try zig_args.append(builder.pathFromRoot(file));
   2663                     }
   2664                 },
   2665             }
   2666         }
   2667 
   2668         if (self.image_base) |image_base| {
   2669             try zig_args.append("--image-base");
   2670             try zig_args.append(builder.fmt("0x{x}", .{image_base}));
   2671         }
   2672 
   2673         if (self.filter) |filter| {
   2674             try zig_args.append("--test-filter");
   2675             try zig_args.append(filter);
   2676         }
   2677 
   2678         if (self.test_evented_io) {
   2679             try zig_args.append("--test-evented-io");
   2680         }
   2681 
   2682         if (self.name_prefix.len != 0) {
   2683             try zig_args.append("--test-name-prefix");
   2684             try zig_args.append(self.name_prefix);
   2685         }
   2686 
   2687         for (builder.debug_log_scopes) |log_scope| {
   2688             try zig_args.append("--debug-log");
   2689             try zig_args.append(log_scope);
   2690         }
   2691 
   2692         if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
   2693         if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
   2694         if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
   2695         if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
   2696         if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
   2697         if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
   2698 
   2699         if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
   2700         if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
   2701         if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
   2702         if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
   2703         if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
   2704         if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
   2705         if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
   2706 
   2707         if (self.emit_h) try zig_args.append("-femit-h");
   2708 
   2709         if (self.strip) {
   2710             try zig_args.append("--strip");
   2711         }
   2712 
   2713         switch (self.compress_debug_sections) {
   2714             .none => {},
   2715             .zlib => try zig_args.append("--compress-debug-sections=zlib"),
   2716         }
   2717 
   2718         if (self.link_eh_frame_hdr) {
   2719             try zig_args.append("--eh-frame-hdr");
   2720         }
   2721         if (self.link_emit_relocs) {
   2722             try zig_args.append("--emit-relocs");
   2723         }
   2724         if (self.link_function_sections) {
   2725             try zig_args.append("-ffunction-sections");
   2726         }
   2727         if (self.link_gc_sections) |x| {
   2728             try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
   2729         }
   2730         if (self.linker_allow_shlib_undefined) |x| {
   2731             try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
   2732         }
   2733         if (self.link_z_notext) {
   2734             try zig_args.append("-z");
   2735             try zig_args.append("notext");
   2736         }
   2737         if (!self.link_z_relro) {
   2738             try zig_args.append("-z");
   2739             try zig_args.append("norelro");
   2740         }
   2741         if (self.link_z_lazy) {
   2742             try zig_args.append("-z");
   2743             try zig_args.append("lazy");
   2744         }
   2745 
   2746         if (self.libc_file) |libc_file| {
   2747             try zig_args.append("--libc");
   2748             try zig_args.append(libc_file.getPath(self.builder));
   2749         } else if (builder.libc_file) |libc_file| {
   2750             try zig_args.append("--libc");
   2751             try zig_args.append(libc_file);
   2752         }
   2753 
   2754         switch (self.build_mode) {
   2755             .Debug => {}, // Skip since it's the default.
   2756             else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable,
   2757         }
   2758 
   2759         try zig_args.append("--cache-dir");
   2760         try zig_args.append(builder.pathFromRoot(builder.cache_root));
   2761 
   2762         try zig_args.append("--global-cache-dir");
   2763         try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
   2764 
   2765         zig_args.append("--name") catch unreachable;
   2766         zig_args.append(self.name) catch unreachable;
   2767 
   2768         if (self.linkage) |some| switch (some) {
   2769             .dynamic => try zig_args.append("-dynamic"),
   2770             .static => try zig_args.append("-static"),
   2771         };
   2772         if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
   2773             if (self.version) |version| {
   2774                 zig_args.append("--version") catch unreachable;
   2775                 zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
   2776             }
   2777 
   2778             if (self.target.isDarwin()) {
   2779                 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
   2780                     self.target.libPrefix(),
   2781                     self.name,
   2782                     self.target.dynamicLibSuffix(),
   2783                 });
   2784                 try zig_args.append("-install_name");
   2785                 try zig_args.append(install_name);
   2786             }
   2787         }
   2788 
   2789         if (self.entitlements) |entitlements| {
   2790             try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
   2791         }
   2792         if (self.pagezero_size) |pagezero_size| {
   2793             const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
   2794             try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
   2795         }
   2796         if (self.search_strategy) |strat| switch (strat) {
   2797             .paths_first => try zig_args.append("-search_paths_first"),
   2798             .dylibs_first => try zig_args.append("-search_dylibs_first"),
   2799         };
   2800         if (self.headerpad_size) |headerpad_size| {
   2801             const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
   2802             try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
   2803         }
   2804         if (self.headerpad_max_install_names) {
   2805             try zig_args.append("-headerpad_max_install_names");
   2806         }
   2807         if (self.dead_strip_dylibs) {
   2808             try zig_args.append("-dead_strip_dylibs");
   2809         }
   2810 
   2811         if (self.bundle_compiler_rt) |x| {
   2812             if (x) {
   2813                 try zig_args.append("-fcompiler-rt");
   2814             } else {
   2815                 try zig_args.append("-fno-compiler-rt");
   2816             }
   2817         }
   2818         if (self.single_threaded) |single_threaded| {
   2819             if (single_threaded) {
   2820                 try zig_args.append("-fsingle-threaded");
   2821             } else {
   2822                 try zig_args.append("-fno-single-threaded");
   2823             }
   2824         }
   2825         if (self.disable_stack_probing) {
   2826             try zig_args.append("-fno-stack-check");
   2827         }
   2828         if (self.red_zone) |red_zone| {
   2829             if (red_zone) {
   2830                 try zig_args.append("-mred-zone");
   2831             } else {
   2832                 try zig_args.append("-mno-red-zone");
   2833             }
   2834         }
   2835         if (self.omit_frame_pointer) |omit_frame_pointer| {
   2836             if (omit_frame_pointer) {
   2837                 try zig_args.append("-fomit-frame-pointer");
   2838             } else {
   2839                 try zig_args.append("-fno-omit-frame-pointer");
   2840             }
   2841         }
   2842         if (self.dll_export_fns) |dll_export_fns| {
   2843             if (dll_export_fns) {
   2844                 try zig_args.append("-fdll-export-fns");
   2845             } else {
   2846                 try zig_args.append("-fno-dll-export-fns");
   2847             }
   2848         }
   2849         if (self.disable_sanitize_c) {
   2850             try zig_args.append("-fno-sanitize-c");
   2851         }
   2852         if (self.sanitize_thread) {
   2853             try zig_args.append("-fsanitize-thread");
   2854         }
   2855         if (self.rdynamic) {
   2856             try zig_args.append("-rdynamic");
   2857         }
   2858         if (self.import_memory) {
   2859             try zig_args.append("--import-memory");
   2860         }
   2861         if (self.import_table) {
   2862             try zig_args.append("--import-table");
   2863         }
   2864         if (self.export_table) {
   2865             try zig_args.append("--export-table");
   2866         }
   2867         if (self.initial_memory) |initial_memory| {
   2868             try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
   2869         }
   2870         if (self.max_memory) |max_memory| {
   2871             try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
   2872         }
   2873         if (self.shared_memory) {
   2874             try zig_args.append("--shared-memory");
   2875         }
   2876         if (self.global_base) |global_base| {
   2877             try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
   2878         }
   2879 
   2880         if (self.code_model != .default) {
   2881             try zig_args.append("-mcmodel");
   2882             try zig_args.append(@tagName(self.code_model));
   2883         }
   2884         if (self.wasi_exec_model) |model| {
   2885             try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
   2886         }
   2887         for (self.export_symbol_names) |symbol_name| {
   2888             try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
   2889         }
   2890 
   2891         if (!self.target.isNative()) {
   2892             try zig_args.append("-target");
   2893             try zig_args.append(try self.target.zigTriple(builder.allocator));
   2894 
   2895             // TODO this logic can disappear if cpu model + features becomes part of the target triple
   2896             const cross = self.target.toTarget();
   2897             const all_features = cross.cpu.arch.allFeaturesList();
   2898             var populated_cpu_features = cross.cpu.model.features;
   2899             populated_cpu_features.populateDependencies(all_features);
   2900 
   2901             if (populated_cpu_features.eql(cross.cpu.features)) {
   2902                 // The CPU name alone is sufficient.
   2903                 try zig_args.append("-mcpu");
   2904                 try zig_args.append(cross.cpu.model.name);
   2905             } else {
   2906                 var mcpu_buffer = std.ArrayList(u8).init(builder.allocator);
   2907 
   2908                 try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name});
   2909 
   2910                 for (all_features) |feature, i_usize| {
   2911                     const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
   2912                     const in_cpu_set = populated_cpu_features.isEnabled(i);
   2913                     const in_actual_set = cross.cpu.features.isEnabled(i);
   2914                     if (in_cpu_set and !in_actual_set) {
   2915                         try mcpu_buffer.writer().print("-{s}", .{feature.name});
   2916                     } else if (!in_cpu_set and in_actual_set) {
   2917                         try mcpu_buffer.writer().print("+{s}", .{feature.name});
   2918                     }
   2919                 }
   2920 
   2921                 try zig_args.append(mcpu_buffer.toOwnedSlice());
   2922             }
   2923 
   2924             if (self.target.dynamic_linker.get()) |dynamic_linker| {
   2925                 try zig_args.append("--dynamic-linker");
   2926                 try zig_args.append(dynamic_linker);
   2927             }
   2928         }
   2929 
   2930         if (self.linker_script) |linker_script| {
   2931             try zig_args.append("--script");
   2932             try zig_args.append(linker_script.getPath(builder));
   2933         }
   2934 
   2935         if (self.version_script) |version_script| {
   2936             try zig_args.append("--version-script");
   2937             try zig_args.append(builder.pathFromRoot(version_script));
   2938         }
   2939 
   2940         if (self.kind == .@"test") {
   2941             if (self.exec_cmd_args) |exec_cmd_args| {
   2942                 for (exec_cmd_args) |cmd_arg| {
   2943                     if (cmd_arg) |arg| {
   2944                         try zig_args.append("--test-cmd");
   2945                         try zig_args.append(arg);
   2946                     } else {
   2947                         try zig_args.append("--test-cmd-bin");
   2948                     }
   2949                 }
   2950             } else {
   2951                 const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc;
   2952 
   2953                 switch (self.builder.host.getExternalExecutor(self.target_info, .{
   2954                     .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
   2955                     .link_libc = self.is_linking_libc,
   2956                 })) {
   2957                     .native => {},
   2958                     .bad_dl, .bad_os_or_cpu => {
   2959                         try zig_args.append("--test-no-exec");
   2960                     },
   2961                     .rosetta => if (builder.enable_rosetta) {
   2962                         try zig_args.append("--test-cmd-bin");
   2963                     } else {
   2964                         try zig_args.append("--test-no-exec");
   2965                     },
   2966                     .qemu => |bin_name| ok: {
   2967                         if (builder.enable_qemu) qemu: {
   2968                             const glibc_dir_arg = if (need_cross_glibc)
   2969                                 builder.glibc_runtimes_dir orelse break :qemu
   2970                             else
   2971                                 null;
   2972                             try zig_args.append("--test-cmd");
   2973                             try zig_args.append(bin_name);
   2974                             if (glibc_dir_arg) |dir| {
   2975                                 // TODO look into making this a call to `linuxTriple`. This
   2976                                 // needs the directory to be called "i686" rather than
   2977                                 // "i386" which is why we do it manually here.
   2978                                 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
   2979                                 const cpu_arch = self.target.getCpuArch();
   2980                                 const os_tag = self.target.getOsTag();
   2981                                 const abi = self.target.getAbi();
   2982                                 const cpu_arch_name: []const u8 = if (cpu_arch == .i386)
   2983                                     "i686"
   2984                                 else
   2985                                     @tagName(cpu_arch);
   2986                                 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
   2987                                     dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
   2988                                 });
   2989 
   2990                                 try zig_args.append("--test-cmd");
   2991                                 try zig_args.append("-L");
   2992                                 try zig_args.append("--test-cmd");
   2993                                 try zig_args.append(full_dir);
   2994                             }
   2995                             try zig_args.append("--test-cmd-bin");
   2996                             break :ok;
   2997                         }
   2998                         try zig_args.append("--test-no-exec");
   2999                     },
   3000                     .wine => |bin_name| if (builder.enable_wine) {
   3001                         try zig_args.append("--test-cmd");
   3002                         try zig_args.append(bin_name);
   3003                         try zig_args.append("--test-cmd-bin");
   3004                     } else {
   3005                         try zig_args.append("--test-no-exec");
   3006                     },
   3007                     .wasmtime => |bin_name| if (builder.enable_wasmtime) {
   3008                         try zig_args.append("--test-cmd");
   3009                         try zig_args.append(bin_name);
   3010                         try zig_args.append("--test-cmd");
   3011                         try zig_args.append("--dir=.");
   3012                         try zig_args.append("--test-cmd");
   3013                         try zig_args.append("--allow-unknown-exports"); // TODO: Remove when stage2 is default compiler
   3014                         try zig_args.append("--test-cmd-bin");
   3015                     } else {
   3016                         try zig_args.append("--test-no-exec");
   3017                     },
   3018                     .darling => |bin_name| if (builder.enable_darling) {
   3019                         try zig_args.append("--test-cmd");
   3020                         try zig_args.append(bin_name);
   3021                         try zig_args.append("--test-cmd-bin");
   3022                     } else {
   3023                         try zig_args.append("--test-no-exec");
   3024                     },
   3025                 }
   3026             }
   3027         } else if (self.kind == .test_exe) {
   3028             try zig_args.append("--test-no-exec");
   3029         }
   3030 
   3031         for (self.packages.items) |pkg| {
   3032             try self.makePackageCmd(pkg, &zig_args);
   3033         }
   3034 
   3035         for (self.include_dirs.items) |include_dir| {
   3036             switch (include_dir) {
   3037                 .raw_path => |include_path| {
   3038                     try zig_args.append("-I");
   3039                     try zig_args.append(self.builder.pathFromRoot(include_path));
   3040                 },
   3041                 .raw_path_system => |include_path| {
   3042                     if (builder.sysroot != null) {
   3043                         try zig_args.append("-iwithsysroot");
   3044                     } else {
   3045                         try zig_args.append("-isystem");
   3046                     }
   3047 
   3048                     const resolved_include_path = self.builder.pathFromRoot(include_path);
   3049 
   3050                     const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
   3051                         // We need to check for disk designator and strip it out from dir path so
   3052                         // that zig/clang can concat resolved_include_path with sysroot.
   3053                         const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
   3054 
   3055                         if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
   3056                             break :blk resolved_include_path[where + disk_designator.len ..];
   3057                         }
   3058 
   3059                         break :blk resolved_include_path;
   3060                     } else resolved_include_path;
   3061 
   3062                     try zig_args.append(common_include_path);
   3063                 },
   3064                 .other_step => |other| if (other.emit_h) {
   3065                     const h_path = other.getOutputHSource().getPath(self.builder);
   3066                     try zig_args.append("-isystem");
   3067                     try zig_args.append(fs.path.dirname(h_path).?);
   3068                 },
   3069             }
   3070         }
   3071 
   3072         for (self.lib_paths.items) |lib_path| {
   3073             try zig_args.append("-L");
   3074             try zig_args.append(lib_path);
   3075         }
   3076 
   3077         for (self.rpaths.items) |rpath| {
   3078             try zig_args.append("-rpath");
   3079             try zig_args.append(rpath);
   3080         }
   3081 
   3082         for (self.c_macros.items) |c_macro| {
   3083             try zig_args.append("-D");
   3084             try zig_args.append(c_macro);
   3085         }
   3086 
   3087         if (self.target.isDarwin()) {
   3088             for (self.framework_dirs.items) |dir| {
   3089                 if (builder.sysroot != null) {
   3090                     try zig_args.append("-iframeworkwithsysroot");
   3091                 } else {
   3092                     try zig_args.append("-iframework");
   3093                 }
   3094                 try zig_args.append(dir);
   3095                 try zig_args.append("-F");
   3096                 try zig_args.append(dir);
   3097             }
   3098 
   3099             var it = self.frameworks.iterator();
   3100             while (it.next()) |entry| {
   3101                 const name = entry.key_ptr.*;
   3102                 const info = entry.value_ptr.*;
   3103                 if (info.needed) {
   3104                     zig_args.append("-needed_framework") catch unreachable;
   3105                 } else if (info.weak) {
   3106                     zig_args.append("-weak_framework") catch unreachable;
   3107                 } else {
   3108                     zig_args.append("-framework") catch unreachable;
   3109                 }
   3110                 zig_args.append(name) catch unreachable;
   3111             }
   3112         } else {
   3113             if (self.framework_dirs.items.len > 0) {
   3114                 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
   3115             }
   3116 
   3117             if (self.frameworks.count() > 0) {
   3118                 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
   3119             }
   3120         }
   3121 
   3122         if (builder.sysroot) |sysroot| {
   3123             try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
   3124         }
   3125 
   3126         for (builder.search_prefixes.items) |search_prefix| {
   3127             try zig_args.append("-L");
   3128             try zig_args.append(builder.pathJoin(&.{
   3129                 search_prefix, "lib",
   3130             }));
   3131             try zig_args.append("-isystem");
   3132             try zig_args.append(builder.pathJoin(&.{
   3133                 search_prefix, "include",
   3134             }));
   3135         }
   3136 
   3137         if (self.valgrind_support) |valgrind_support| {
   3138             if (valgrind_support) {
   3139                 try zig_args.append("-fvalgrind");
   3140             } else {
   3141                 try zig_args.append("-fno-valgrind");
   3142             }
   3143         }
   3144 
   3145         if (self.each_lib_rpath) |each_lib_rpath| {
   3146             if (each_lib_rpath) {
   3147                 try zig_args.append("-feach-lib-rpath");
   3148             } else {
   3149                 try zig_args.append("-fno-each-lib-rpath");
   3150             }
   3151         }
   3152 
   3153         if (self.build_id) |build_id| {
   3154             if (build_id) {
   3155                 try zig_args.append("-fbuild-id");
   3156             } else {
   3157                 try zig_args.append("-fno-build-id");
   3158             }
   3159         }
   3160 
   3161         if (self.override_lib_dir) |dir| {
   3162             try zig_args.append("--zig-lib-dir");
   3163             try zig_args.append(builder.pathFromRoot(dir));
   3164         } else if (self.builder.override_lib_dir) |dir| {
   3165             try zig_args.append("--zig-lib-dir");
   3166             try zig_args.append(builder.pathFromRoot(dir));
   3167         }
   3168 
   3169         if (self.main_pkg_path) |dir| {
   3170             try zig_args.append("--main-pkg-path");
   3171             try zig_args.append(builder.pathFromRoot(dir));
   3172         }
   3173 
   3174         if (self.force_pic) |pic| {
   3175             if (pic) {
   3176                 try zig_args.append("-fPIC");
   3177             } else {
   3178                 try zig_args.append("-fno-PIC");
   3179             }
   3180         }
   3181 
   3182         if (self.pie) |pie| {
   3183             if (pie) {
   3184                 try zig_args.append("-fPIE");
   3185             } else {
   3186                 try zig_args.append("-fno-PIE");
   3187             }
   3188         }
   3189 
   3190         if (self.want_lto) |lto| {
   3191             if (lto) {
   3192                 try zig_args.append("-flto");
   3193             } else {
   3194                 try zig_args.append("-fno-lto");
   3195             }
   3196         }
   3197 
   3198         if (self.subsystem) |subsystem| {
   3199             try zig_args.append("--subsystem");
   3200             try zig_args.append(switch (subsystem) {
   3201                 .Console => "console",
   3202                 .Windows => "windows",
   3203                 .Posix => "posix",
   3204                 .Native => "native",
   3205                 .EfiApplication => "efi_application",
   3206                 .EfiBootServiceDriver => "efi_boot_service_driver",
   3207                 .EfiRom => "efi_rom",
   3208                 .EfiRuntimeDriver => "efi_runtime_driver",
   3209             });
   3210         }
   3211 
   3212         try zig_args.append("--enable-cache");
   3213 
   3214         // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
   3215         // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
   3216         // pass that to zig, e.g. via 'zig build-lib @args.rsp'
   3217         // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
   3218         var args_length: usize = 0;
   3219         for (zig_args.items) |arg| {
   3220             args_length += arg.len + 1; // +1 to account for null terminator
   3221         }
   3222         if (args_length >= 30 * 1024) {
   3223             const args_dir = try fs.path.join(
   3224                 builder.allocator,
   3225                 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
   3226             );
   3227             try std.fs.cwd().makePath(args_dir);
   3228 
   3229             var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
   3230             defer args_arena.deinit();
   3231 
   3232             const args_to_escape = zig_args.items[2..];
   3233             var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
   3234 
   3235             arg_blk: for (args_to_escape) |arg| {
   3236                 for (arg) |c, arg_idx| {
   3237                     if (c == '\\' or c == '"') {
   3238                         // Slow path for arguments that need to be escaped. We'll need to allocate and copy
   3239                         var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
   3240                         const writer = escaped.writer();
   3241                         writer.writeAll(arg[0..arg_idx]) catch unreachable;
   3242                         for (arg[arg_idx..]) |to_escape| {
   3243                             if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
   3244                             try writer.writeByte(to_escape);
   3245                         }
   3246                         escaped_args.appendAssumeCapacity(escaped.items);
   3247                         continue :arg_blk;
   3248                     }
   3249                 }
   3250                 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
   3251             }
   3252 
   3253             // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
   3254             // other zig build commands running in parallel.
   3255             const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
   3256             const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
   3257 
   3258             var args_hash: [Sha256.digest_length]u8 = undefined;
   3259             Sha256.hash(args, &args_hash, .{});
   3260             var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
   3261             _ = try std.fmt.bufPrint(
   3262                 &args_hex_hash,
   3263                 "{s}",
   3264                 .{std.fmt.fmtSliceHexLower(&args_hash)},
   3265             );
   3266 
   3267             const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
   3268             try std.fs.cwd().writeFile(args_file, args);
   3269 
   3270             zig_args.shrinkRetainingCapacity(2);
   3271             try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
   3272         }
   3273 
   3274         const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
   3275         const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
   3276 
   3277         if (self.output_dir) |output_dir| {
   3278             var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
   3279             defer src_dir.close();
   3280 
   3281             // Create the output directory if it doesn't exist.
   3282             try std.fs.cwd().makePath(output_dir);
   3283 
   3284             var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
   3285             defer dest_dir.close();
   3286 
   3287             var it = src_dir.iterate();
   3288             while (try it.next()) |entry| {
   3289                 // The compiler can put these files into the same directory, but we don't
   3290                 // want to copy them over.
   3291                 if (mem.eql(u8, entry.name, "stage1.id") or
   3292                     mem.eql(u8, entry.name, "llvm-ar.id") or
   3293                     mem.eql(u8, entry.name, "libs.txt") or
   3294                     mem.eql(u8, entry.name, "builtin.zig") or
   3295                     mem.eql(u8, entry.name, "zld.id") or
   3296                     mem.eql(u8, entry.name, "lld.id")) continue;
   3297 
   3298                 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
   3299             }
   3300         } else {
   3301             self.output_dir = build_output_dir;
   3302         }
   3303 
   3304         // This will ensure all output filenames will now have the output_dir available!
   3305         self.computeOutFileNames();
   3306 
   3307         // Update generated files
   3308         if (self.output_dir != null) {
   3309             self.output_path_source.path = builder.pathJoin(
   3310                 &.{ self.output_dir.?, self.out_filename },
   3311             );
   3312 
   3313             if (self.emit_h) {
   3314                 self.output_h_path_source.path = builder.pathJoin(
   3315                     &.{ self.output_dir.?, self.out_h_filename },
   3316                 );
   3317             }
   3318 
   3319             if (self.target.isWindows() or self.target.isUefi()) {
   3320                 self.output_pdb_path_source.path = builder.pathJoin(
   3321                     &.{ self.output_dir.?, self.out_pdb_filename },
   3322                 );
   3323             }
   3324         }
   3325 
   3326         if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
   3327             try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
   3328         }
   3329     }
   3330 };
   3331 
   3332 /// Allocates a new string for assigning a value to a named macro.
   3333 /// If the value is omitted, it is set to 1.
   3334 /// `name` and `value` need not live longer than the function call.
   3335 pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
   3336     var macro = allocator.alloc(
   3337         u8,
   3338         name.len + if (value) |value_slice| value_slice.len + 1 else 0,
   3339     ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
   3340     mem.copy(u8, macro, name);
   3341     if (value) |value_slice| {
   3342         macro[name.len] = '=';
   3343         mem.copy(u8, macro[name.len + 1 ..], value_slice);
   3344     }
   3345     return macro;
   3346 }
   3347 
   3348 pub const InstallArtifactStep = struct {
   3349     pub const base_id = .install_artifact;
   3350 
   3351     step: Step,
   3352     builder: *Builder,
   3353     artifact: *LibExeObjStep,
   3354     dest_dir: InstallDir,
   3355     pdb_dir: ?InstallDir,
   3356     h_dir: ?InstallDir,
   3357 
   3358     const Self = @This();
   3359 
   3360     pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self {
   3361         if (artifact.install_step) |s| return s;
   3362 
   3363         const self = builder.allocator.create(Self) catch unreachable;
   3364         self.* = Self{
   3365             .builder = builder,
   3366             .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
   3367             .artifact = artifact,
   3368             .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
   3369                 .obj => @panic("Cannot install a .obj build artifact."),
   3370                 .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."),
   3371                 .exe, .test_exe => InstallDir{ .bin = {} },
   3372                 .lib => InstallDir{ .lib = {} },
   3373             },
   3374             .pdb_dir = if (artifact.producesPdbFile()) blk: {
   3375                 if (artifact.kind == .exe or artifact.kind == .test_exe) {
   3376                     break :blk InstallDir{ .bin = {} };
   3377                 } else {
   3378                     break :blk InstallDir{ .lib = {} };
   3379                 }
   3380             } else null,
   3381             .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
   3382         };
   3383         self.step.dependOn(&artifact.step);
   3384         artifact.install_step = self;
   3385 
   3386         builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
   3387         if (self.artifact.isDynamicLibrary()) {
   3388             if (artifact.major_only_filename) |name| {
   3389                 builder.pushInstalledFile(.lib, name);
   3390             }
   3391             if (artifact.name_only_filename) |name| {
   3392                 builder.pushInstalledFile(.lib, name);
   3393             }
   3394             if (self.artifact.target.isWindows()) {
   3395                 builder.pushInstalledFile(.lib, artifact.out_lib_filename);
   3396             }
   3397         }
   3398         if (self.pdb_dir) |pdb_dir| {
   3399             builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
   3400         }
   3401         if (self.h_dir) |h_dir| {
   3402             builder.pushInstalledFile(h_dir, artifact.out_h_filename);
   3403         }
   3404         return self;
   3405     }
   3406 
   3407     fn make(step: *Step) !void {
   3408         const self = @fieldParentPtr(Self, "step", step);
   3409         const builder = self.builder;
   3410 
   3411         const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
   3412         try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
   3413         if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
   3414             try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
   3415         }
   3416         if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
   3417             const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
   3418             try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
   3419         }
   3420         if (self.pdb_dir) |pdb_dir| {
   3421             const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
   3422             try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
   3423         }
   3424         if (self.h_dir) |h_dir| {
   3425             const full_pdb_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
   3426             try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_pdb_path);
   3427         }
   3428         self.artifact.installed_path = full_dest_path;
   3429     }
   3430 };
   3431 
   3432 pub const InstallFileStep = struct {
   3433     pub const base_id = .install_file;
   3434 
   3435     step: Step,
   3436     builder: *Builder,
   3437     source: FileSource,
   3438     dir: InstallDir,
   3439     dest_rel_path: []const u8,
   3440 
   3441     pub fn init(
   3442         builder: *Builder,
   3443         source: FileSource,
   3444         dir: InstallDir,
   3445         dest_rel_path: []const u8,
   3446     ) InstallFileStep {
   3447         builder.pushInstalledFile(dir, dest_rel_path);
   3448         return InstallFileStep{
   3449             .builder = builder,
   3450             .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
   3451             .source = source.dupe(builder),
   3452             .dir = dir.dupe(builder),
   3453             .dest_rel_path = builder.dupePath(dest_rel_path),
   3454         };
   3455     }
   3456 
   3457     fn make(step: *Step) !void {
   3458         const self = @fieldParentPtr(InstallFileStep, "step", step);
   3459         const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
   3460         const full_src_path = self.source.getPath(self.builder);
   3461         try self.builder.updateFile(full_src_path, full_dest_path);
   3462     }
   3463 };
   3464 
   3465 pub const InstallDirectoryOptions = struct {
   3466     source_dir: []const u8,
   3467     install_dir: InstallDir,
   3468     install_subdir: []const u8,
   3469     /// File paths which end in any of these suffixes will be excluded
   3470     /// from being installed.
   3471     exclude_extensions: []const []const u8 = &.{},
   3472     /// File paths which end in any of these suffixes will result in
   3473     /// empty files being installed. This is mainly intended for large
   3474     /// test.zig files in order to prevent needless installation bloat.
   3475     /// However if the files were not present at all, then
   3476     /// `@import("test.zig")` would be a compile error.
   3477     blank_extensions: []const []const u8 = &.{},
   3478 
   3479     fn dupe(self: InstallDirectoryOptions, b: *Builder) InstallDirectoryOptions {
   3480         return .{
   3481             .source_dir = b.dupe(self.source_dir),
   3482             .install_dir = self.install_dir.dupe(b),
   3483             .install_subdir = b.dupe(self.install_subdir),
   3484             .exclude_extensions = b.dupeStrings(self.exclude_extensions),
   3485             .blank_extensions = b.dupeStrings(self.blank_extensions),
   3486         };
   3487     }
   3488 };
   3489 
   3490 pub const InstallDirStep = struct {
   3491     pub const base_id = .install_dir;
   3492 
   3493     step: Step,
   3494     builder: *Builder,
   3495     options: InstallDirectoryOptions,
   3496 
   3497     pub fn init(
   3498         builder: *Builder,
   3499         options: InstallDirectoryOptions,
   3500     ) InstallDirStep {
   3501         builder.pushInstalledFile(options.install_dir, options.install_subdir);
   3502         return InstallDirStep{
   3503             .builder = builder,
   3504             .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
   3505             .options = options.dupe(builder),
   3506         };
   3507     }
   3508 
   3509     fn make(step: *Step) !void {
   3510         const self = @fieldParentPtr(InstallDirStep, "step", step);
   3511         const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
   3512         const full_src_dir = self.builder.pathFromRoot(self.options.source_dir);
   3513         var src_dir = try std.fs.cwd().openIterableDir(full_src_dir, .{});
   3514         defer src_dir.close();
   3515         var it = try src_dir.walk(self.builder.allocator);
   3516         next_entry: while (try it.next()) |entry| {
   3517             for (self.options.exclude_extensions) |ext| {
   3518                 if (mem.endsWith(u8, entry.path, ext)) {
   3519                     continue :next_entry;
   3520                 }
   3521             }
   3522 
   3523             const full_path = self.builder.pathJoin(&.{
   3524                 full_src_dir, entry.path,
   3525             });
   3526 
   3527             const dest_path = self.builder.pathJoin(&.{
   3528                 dest_prefix, entry.path,
   3529             });
   3530 
   3531             switch (entry.kind) {
   3532                 .Directory => try fs.cwd().makePath(dest_path),
   3533                 .File => {
   3534                     for (self.options.blank_extensions) |ext| {
   3535                         if (mem.endsWith(u8, entry.path, ext)) {
   3536                             try self.builder.truncateFile(dest_path);
   3537                             continue :next_entry;
   3538                         }
   3539                     }
   3540 
   3541                     try self.builder.updateFile(full_path, dest_path);
   3542                 },
   3543                 else => continue,
   3544             }
   3545         }
   3546     }
   3547 };
   3548 
   3549 pub const LogStep = struct {
   3550     pub const base_id = .log;
   3551 
   3552     step: Step,
   3553     builder: *Builder,
   3554     data: []const u8,
   3555 
   3556     pub fn init(builder: *Builder, data: []const u8) LogStep {
   3557         return LogStep{
   3558             .builder = builder,
   3559             .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
   3560             .data = builder.dupe(data),
   3561         };
   3562     }
   3563 
   3564     fn make(step: *Step) anyerror!void {
   3565         const self = @fieldParentPtr(LogStep, "step", step);
   3566         log.info("{s}", .{self.data});
   3567     }
   3568 };
   3569 
   3570 pub const RemoveDirStep = struct {
   3571     pub const base_id = .remove_dir;
   3572 
   3573     step: Step,
   3574     builder: *Builder,
   3575     dir_path: []const u8,
   3576 
   3577     pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep {
   3578         return RemoveDirStep{
   3579             .builder = builder,
   3580             .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
   3581             .dir_path = builder.dupePath(dir_path),
   3582         };
   3583     }
   3584 
   3585     fn make(step: *Step) !void {
   3586         const self = @fieldParentPtr(RemoveDirStep, "step", step);
   3587 
   3588         const full_path = self.builder.pathFromRoot(self.dir_path);
   3589         fs.cwd().deleteTree(full_path) catch |err| {
   3590             log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
   3591             return err;
   3592         };
   3593     }
   3594 };
   3595 
   3596 const ThisModule = @This();
   3597 pub const Step = struct {
   3598     id: Id,
   3599     name: []const u8,
   3600     makeFn: MakeFn,
   3601     dependencies: ArrayList(*Step),
   3602     loop_flag: bool,
   3603     done_flag: bool,
   3604 
   3605     const MakeFn = switch (builtin.zig_backend) {
   3606         .stage1 => fn (self: *Step) anyerror!void,
   3607         else => *const fn (self: *Step) anyerror!void,
   3608     };
   3609 
   3610     pub const Id = enum {
   3611         top_level,
   3612         lib_exe_obj,
   3613         install_artifact,
   3614         install_file,
   3615         install_dir,
   3616         log,
   3617         remove_dir,
   3618         fmt,
   3619         translate_c,
   3620         write_file,
   3621         run,
   3622         emulatable_run,
   3623         check_file,
   3624         check_object,
   3625         install_raw,
   3626         options,
   3627         custom,
   3628     };
   3629 
   3630     pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step {
   3631         return Step{
   3632             .id = id,
   3633             .name = allocator.dupe(u8, name) catch unreachable,
   3634             .makeFn = makeFn,
   3635             .dependencies = ArrayList(*Step).init(allocator),
   3636             .loop_flag = false,
   3637             .done_flag = false,
   3638         };
   3639     }
   3640     pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
   3641         return init(id, name, allocator, makeNoOp);
   3642     }
   3643 
   3644     pub fn make(self: *Step) !void {
   3645         if (self.done_flag) return;
   3646 
   3647         try self.makeFn(self);
   3648         self.done_flag = true;
   3649     }
   3650 
   3651     pub fn dependOn(self: *Step, other: *Step) void {
   3652         self.dependencies.append(other) catch unreachable;
   3653     }
   3654 
   3655     fn makeNoOp(self: *Step) anyerror!void {
   3656         _ = self;
   3657     }
   3658 
   3659     pub fn cast(step: *Step, comptime T: type) ?*T {
   3660         if (step.id == T.base_id) {
   3661             return @fieldParentPtr(T, "step", step);
   3662         }
   3663         return null;
   3664     }
   3665 };
   3666 
   3667 fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
   3668     const out_dir = fs.path.dirname(output_path) orelse ".";
   3669     const out_basename = fs.path.basename(output_path);
   3670     // sym link for libfoo.so.1 to libfoo.so.1.2.3
   3671     const major_only_path = fs.path.join(
   3672         allocator,
   3673         &[_][]const u8{ out_dir, filename_major_only },
   3674     ) catch unreachable;
   3675     fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
   3676         log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
   3677         return err;
   3678     };
   3679     // sym link for libfoo.so to libfoo.so.1
   3680     const name_only_path = fs.path.join(
   3681         allocator,
   3682         &[_][]const u8{ out_dir, filename_name_only },
   3683     ) catch unreachable;
   3684     fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
   3685         log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
   3686         return err;
   3687     };
   3688 }
   3689 
   3690 /// Returned slice must be freed by the caller.
   3691 fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
   3692     const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
   3693     defer allocator.free(appdata_path);
   3694 
   3695     const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
   3696     defer allocator.free(path_file);
   3697 
   3698     const file = fs.cwd().openFile(path_file, .{}) catch return null;
   3699     defer file.close();
   3700 
   3701     const size = @intCast(usize, try file.getEndPos());
   3702     const vcpkg_path = try allocator.alloc(u8, size);
   3703     const size_read = try file.read(vcpkg_path);
   3704     std.debug.assert(size == size_read);
   3705 
   3706     return vcpkg_path;
   3707 }
   3708 
   3709 const VcpkgRoot = union(VcpkgRootStatus) {
   3710     unattempted: void,
   3711     not_found: void,
   3712     found: []const u8,
   3713 };
   3714 
   3715 const VcpkgRootStatus = enum {
   3716     unattempted,
   3717     not_found,
   3718     found,
   3719 };
   3720 
   3721 pub const InstallDir = union(enum) {
   3722     prefix: void,
   3723     lib: void,
   3724     bin: void,
   3725     header: void,
   3726     /// A path relative to the prefix
   3727     custom: []const u8,
   3728 
   3729     /// Duplicates the install directory including the path if set to custom.
   3730     pub fn dupe(self: InstallDir, builder: *Builder) InstallDir {
   3731         if (self == .custom) {
   3732             // Written with this temporary to avoid RLS problems
   3733             const duped_path = builder.dupe(self.custom);
   3734             return .{ .custom = duped_path };
   3735         } else {
   3736             return self;
   3737         }
   3738     }
   3739 };
   3740 
   3741 pub const InstalledFile = struct {
   3742     dir: InstallDir,
   3743     path: []const u8,
   3744 
   3745     /// Duplicates the installed file path and directory.
   3746     pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile {
   3747         return .{
   3748             .dir = self.dir.dupe(builder),
   3749             .path = builder.dupe(self.path),
   3750         };
   3751     }
   3752 };
   3753 
   3754 test "Builder.dupePkg()" {
   3755     if (builtin.os.tag == .wasi) return error.SkipZigTest;
   3756 
   3757     var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
   3758     defer arena.deinit();
   3759     var builder = try Builder.create(
   3760         arena.allocator(),
   3761         "test",
   3762         "test",
   3763         "test",
   3764         "test",
   3765     );
   3766     defer builder.destroy();
   3767 
   3768     var pkg_dep = Pkg{
   3769         .name = "pkg_dep",
   3770         .source = .{ .path = "/not/a/pkg_dep.zig" },
   3771     };
   3772     var pkg_top = Pkg{
   3773         .name = "pkg_top",
   3774         .source = .{ .path = "/not/a/pkg_top.zig" },
   3775         .dependencies = &[_]Pkg{pkg_dep},
   3776     };
   3777     const dupe = builder.dupePkg(pkg_top);
   3778 
   3779     const original_deps = pkg_top.dependencies.?;
   3780     const dupe_deps = dupe.dependencies.?;
   3781 
   3782     // probably the same top level package details
   3783     try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
   3784 
   3785     // probably the same dependencies
   3786     try std.testing.expectEqual(original_deps.len, dupe_deps.len);
   3787     try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
   3788 
   3789     // could segfault otherwise if pointers in duplicated package's fields are
   3790     // the same as those in stack allocated package's fields
   3791     try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
   3792     try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr);
   3793     try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr);
   3794     try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
   3795     try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
   3796 }
   3797 
   3798 test "LibExeObjStep.addPackage" {
   3799     if (builtin.os.tag == .wasi) return error.SkipZigTest;
   3800 
   3801     var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
   3802     defer arena.deinit();
   3803 
   3804     var builder = try Builder.create(
   3805         arena.allocator(),
   3806         "test",
   3807         "test",
   3808         "test",
   3809         "test",
   3810     );
   3811     defer builder.destroy();
   3812 
   3813     const pkg_dep = Pkg{
   3814         .name = "pkg_dep",
   3815         .source = .{ .path = "/not/a/pkg_dep.zig" },
   3816     };
   3817     const pkg_top = Pkg{
   3818         .name = "pkg_dep",
   3819         .source = .{ .path = "/not/a/pkg_top.zig" },
   3820         .dependencies = &[_]Pkg{pkg_dep},
   3821     };
   3822 
   3823     var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
   3824     exe.addPackage(pkg_top);
   3825 
   3826     try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
   3827 
   3828     const dupe = exe.packages.items[0];
   3829     try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
   3830 }