zig

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

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