zig

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

blob 4475241c (105818B) - Raw


      1 // SPDX-License-Identifier: MIT
      2 // Copyright (c) 2015-2020 Zig Contributors
      3 // This file is part of [zig](https://ziglang.org/), which is MIT licensed.
      4 // The MIT license requires this copyright notice to be included in all copies
      5 // and substantial portions of the software.
      6 const std = @import("std.zig");
      7 const builtin = std.builtin;
      8 const io = std.io;
      9 const fs = std.fs;
     10 const mem = std.mem;
     11 const debug = std.debug;
     12 const panic = std.debug.panic;
     13 const assert = debug.assert;
     14 const warn = std.debug.warn;
     15 const ArrayList = std.ArrayList;
     16 const StringHashMap = std.StringHashMap;
     17 const Allocator = mem.Allocator;
     18 const process = std.process;
     19 const BufSet = std.BufSet;
     20 const BufMap = std.BufMap;
     21 const fmt_lib = std.fmt;
     22 const File = std.fs.File;
     23 const CrossTarget = std.zig.CrossTarget;
     24 
     25 pub const FmtStep = @import("build/fmt.zig").FmtStep;
     26 pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep;
     27 pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep;
     28 pub const RunStep = @import("build/run.zig").RunStep;
     29 pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep;
     30 pub const InstallRawStep = @import("build/emit_raw.zig").InstallRawStep;
     31 
     32 pub const Builder = struct {
     33     install_tls: TopLevelStep,
     34     uninstall_tls: TopLevelStep,
     35     allocator: *Allocator,
     36     user_input_options: UserInputOptionsMap,
     37     available_options_map: AvailableOptionsMap,
     38     available_options_list: ArrayList(AvailableOption),
     39     verbose: bool,
     40     verbose_tokenize: bool,
     41     verbose_ast: bool,
     42     verbose_link: bool,
     43     verbose_cc: bool,
     44     verbose_ir: bool,
     45     verbose_llvm_ir: bool,
     46     verbose_cimport: bool,
     47     verbose_llvm_cpu_features: bool,
     48     color: enum { auto, on, off } = .auto,
     49     invalid_user_input: bool,
     50     zig_exe: []const u8,
     51     default_step: *Step,
     52     env_map: *BufMap,
     53     top_level_steps: ArrayList(*TopLevelStep),
     54     install_prefix: ?[]const u8,
     55     dest_dir: ?[]const u8,
     56     lib_dir: []const u8,
     57     exe_dir: []const u8,
     58     h_dir: []const u8,
     59     install_path: []const u8,
     60     search_prefixes: ArrayList([]const u8),
     61     installed_files: ArrayList(InstalledFile),
     62     build_root: []const u8,
     63     cache_root: []const u8,
     64     release_mode: ?builtin.Mode,
     65     is_release: bool,
     66     override_lib_dir: ?[]const u8,
     67     vcpkg_root: VcpkgRoot,
     68     pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
     69     args: ?[][]const u8 = null,
     70 
     71     const PkgConfigError = error{
     72         PkgConfigCrashed,
     73         PkgConfigFailed,
     74         PkgConfigNotInstalled,
     75         PkgConfigInvalidOutput,
     76     };
     77 
     78     pub const PkgConfigPkg = struct {
     79         name: []const u8,
     80         desc: []const u8,
     81     };
     82 
     83     pub const CStd = enum {
     84         C89,
     85         C99,
     86         C11,
     87     };
     88 
     89     const UserInputOptionsMap = StringHashMap(UserInputOption);
     90     const AvailableOptionsMap = StringHashMap(AvailableOption);
     91 
     92     const AvailableOption = struct {
     93         name: []const u8,
     94         type_id: TypeId,
     95         description: []const u8,
     96     };
     97 
     98     const UserInputOption = struct {
     99         name: []const u8,
    100         value: UserValue,
    101         used: bool,
    102     };
    103 
    104     const UserValue = union(enum) {
    105         Flag: void,
    106         Scalar: []const u8,
    107         List: ArrayList([]const u8),
    108     };
    109 
    110     const TypeId = enum {
    111         Bool,
    112         Int,
    113         Float,
    114         Enum,
    115         String,
    116         List,
    117     };
    118 
    119     const TopLevelStep = struct {
    120         step: Step,
    121         description: []const u8,
    122     };
    123 
    124     pub fn create(
    125         allocator: *Allocator,
    126         zig_exe: []const u8,
    127         build_root: []const u8,
    128         cache_root: []const u8,
    129     ) !*Builder {
    130         const env_map = try allocator.create(BufMap);
    131         env_map.* = try process.getEnvMap(allocator);
    132 
    133         const self = try allocator.create(Builder);
    134         self.* = Builder{
    135             .zig_exe = zig_exe,
    136             .build_root = build_root,
    137             .cache_root = try fs.path.relative(allocator, build_root, cache_root),
    138             .verbose = false,
    139             .verbose_tokenize = false,
    140             .verbose_ast = false,
    141             .verbose_link = false,
    142             .verbose_cc = false,
    143             .verbose_ir = false,
    144             .verbose_llvm_ir = false,
    145             .verbose_cimport = false,
    146             .verbose_llvm_cpu_features = false,
    147             .invalid_user_input = false,
    148             .allocator = allocator,
    149             .user_input_options = UserInputOptionsMap.init(allocator),
    150             .available_options_map = AvailableOptionsMap.init(allocator),
    151             .available_options_list = ArrayList(AvailableOption).init(allocator),
    152             .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
    153             .default_step = undefined,
    154             .env_map = env_map,
    155             .search_prefixes = ArrayList([]const u8).init(allocator),
    156             .install_prefix = null,
    157             .lib_dir = undefined,
    158             .exe_dir = undefined,
    159             .h_dir = undefined,
    160             .dest_dir = env_map.get("DESTDIR"),
    161             .installed_files = ArrayList(InstalledFile).init(allocator),
    162             .install_tls = TopLevelStep{
    163                 .step = Step.initNoOp(.TopLevel, "install", allocator),
    164                 .description = "Copy build artifacts to prefix path",
    165             },
    166             .uninstall_tls = TopLevelStep{
    167                 .step = Step.init(.TopLevel, "uninstall", allocator, makeUninstall),
    168                 .description = "Remove build artifacts from prefix path",
    169             },
    170             .release_mode = null,
    171             .is_release = false,
    172             .override_lib_dir = null,
    173             .install_path = undefined,
    174             .vcpkg_root = VcpkgRoot{ .Unattempted = {} },
    175             .args = null,
    176         };
    177         try self.top_level_steps.append(&self.install_tls);
    178         try self.top_level_steps.append(&self.uninstall_tls);
    179         self.default_step = &self.install_tls.step;
    180         return self;
    181     }
    182 
    183     pub fn destroy(self: *Builder) void {
    184         self.env_map.deinit();
    185         self.top_level_steps.deinit();
    186         self.allocator.destroy(self);
    187     }
    188 
    189     /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
    190     pub fn setInstallPrefix(self: *Builder, optional_prefix: ?[]const u8) void {
    191         self.install_prefix = optional_prefix;
    192     }
    193 
    194     /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
    195     pub fn resolveInstallPrefix(self: *Builder) void {
    196         if (self.dest_dir) |dest_dir| {
    197             const install_prefix = self.install_prefix orelse "/usr";
    198             self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable;
    199         } else {
    200             const install_prefix = self.install_prefix orelse blk: {
    201                 const p = self.cache_root;
    202                 self.install_prefix = p;
    203                 break :blk p;
    204             };
    205             self.install_path = install_prefix;
    206         }
    207         self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable;
    208         self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable;
    209         self.h_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "include" }) catch unreachable;
    210     }
    211 
    212     pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
    213         return LibExeObjStep.createExecutable(
    214             self,
    215             name,
    216             if (root_src) |p| FileSource{ .path = p } else null,
    217             false,
    218         );
    219     }
    220 
    221     pub fn addExecutableFromWriteFileStep(
    222         self: *Builder,
    223         name: []const u8,
    224         wfs: *WriteFileStep,
    225         basename: []const u8,
    226     ) *LibExeObjStep {
    227         return LibExeObjStep.createExecutable(self, name, @as(FileSource, .{
    228             .write_file = .{
    229                 .step = wfs,
    230                 .basename = basename,
    231             },
    232         }), false);
    233     }
    234 
    235     pub fn addExecutableSource(
    236         self: *Builder,
    237         name: []const u8,
    238         root_src: ?FileSource,
    239     ) *LibExeObjStep {
    240         return LibExeObjStep.createExecutable(self, name, root_src, false);
    241     }
    242 
    243     pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
    244         const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null;
    245         return LibExeObjStep.createObject(self, name, root_src_param);
    246     }
    247 
    248     pub fn addObjectFromWriteFileStep(
    249         self: *Builder,
    250         name: []const u8,
    251         wfs: *WriteFileStep,
    252         basename: []const u8,
    253     ) *LibExeObjStep {
    254         return LibExeObjStep.createObject(self, name, @as(FileSource, .{
    255             .write_file = .{
    256                 .step = wfs,
    257                 .basename = basename,
    258             },
    259         }));
    260     }
    261 
    262     pub fn addSharedLibrary(
    263         self: *Builder,
    264         name: []const u8,
    265         root_src: ?[]const u8,
    266         kind: LibExeObjStep.SharedLibKind,
    267     ) *LibExeObjStep {
    268         const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null;
    269         return LibExeObjStep.createSharedLibrary(self, name, root_src_param, kind);
    270     }
    271 
    272     pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
    273         const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null;
    274         return LibExeObjStep.createStaticLibrary(self, name, root_src_param);
    275     }
    276 
    277     pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep {
    278         return LibExeObjStep.createTest(self, "test", .{ .path = root_src });
    279     }
    280 
    281     pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {
    282         const obj_step = LibExeObjStep.createObject(self, name, null);
    283         obj_step.addAssemblyFile(src);
    284         return obj_step;
    285     }
    286 
    287     /// Initializes a RunStep with argv, which must at least have the path to the
    288     /// executable. More command line arguments can be added with `addArg`,
    289     /// `addArgs`, and `addArtifactArg`.
    290     /// Be careful using this function, as it introduces a system dependency.
    291     /// To run an executable built with zig build, see `LibExeObjStep.run`.
    292     pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep {
    293         assert(argv.len >= 1);
    294         const run_step = RunStep.create(self, self.fmt("run {}", .{argv[0]}));
    295         run_step.addArgs(argv);
    296         return run_step;
    297     }
    298 
    299     pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
    300         return self.allocator.dupe(u8, bytes) catch unreachable;
    301     }
    302 
    303     pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
    304         const the_copy = self.dupe(bytes);
    305         for (the_copy) |*byte| {
    306             switch (byte.*) {
    307                 '/', '\\' => byte.* = fs.path.sep,
    308                 else => {},
    309             }
    310         }
    311         return the_copy;
    312     }
    313 
    314     pub fn dupePkg(self: *Builder, package: Pkg) Pkg {
    315         var the_copy = Pkg{
    316             .name = self.dupe(package.name),
    317             .path = self.dupePath(package.path),
    318         };
    319 
    320         if (package.dependencies) |dependencies| {
    321             const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
    322             the_copy.dependencies = new_dependencies;
    323 
    324             for (dependencies) |dep_package, i| {
    325                 new_dependencies[i] = self.dupePkg(dep_package);
    326             }
    327         }
    328         return the_copy;
    329     }
    330 
    331     pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
    332         const write_file_step = self.addWriteFiles();
    333         write_file_step.add(file_path, data);
    334         return write_file_step;
    335     }
    336 
    337     pub fn addWriteFiles(self: *Builder) *WriteFileStep {
    338         const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
    339         write_file_step.* = WriteFileStep.init(self);
    340         return write_file_step;
    341     }
    342 
    343     pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
    344         const data = self.fmt(format, args);
    345         const log_step = self.allocator.create(LogStep) catch unreachable;
    346         log_step.* = LogStep.init(self, data);
    347         return log_step;
    348     }
    349 
    350     pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep {
    351         const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
    352         remove_dir_step.* = RemoveDirStep.init(self, dir_path);
    353         return remove_dir_step;
    354     }
    355 
    356     pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep {
    357         return FmtStep.create(self, paths);
    358     }
    359 
    360     pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep {
    361         return TranslateCStep.create(self, source);
    362     }
    363 
    364     pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind {
    365         return .{
    366             .versioned = .{
    367                 .major = major,
    368                 .minor = minor,
    369                 .patch = patch,
    370             },
    371         };
    372     }
    373 
    374     pub fn make(self: *Builder, step_names: []const []const u8) !void {
    375         try self.makePath(self.cache_root);
    376 
    377         var wanted_steps = ArrayList(*Step).init(self.allocator);
    378         defer wanted_steps.deinit();
    379 
    380         if (step_names.len == 0) {
    381             try wanted_steps.append(self.default_step);
    382         } else {
    383             for (step_names) |step_name| {
    384                 const s = try self.getTopLevelStepByName(step_name);
    385                 try wanted_steps.append(s);
    386             }
    387         }
    388 
    389         for (wanted_steps.items) |s| {
    390             try self.makeOneStep(s);
    391         }
    392     }
    393 
    394     pub fn getInstallStep(self: *Builder) *Step {
    395         return &self.install_tls.step;
    396     }
    397 
    398     pub fn getUninstallStep(self: *Builder) *Step {
    399         return &self.uninstall_tls.step;
    400     }
    401 
    402     fn makeUninstall(uninstall_step: *Step) anyerror!void {
    403         const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
    404         const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
    405 
    406         for (self.installed_files.items) |installed_file| {
    407             const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
    408             if (self.verbose) {
    409                 warn("rm {}\n", .{full_path});
    410             }
    411             fs.cwd().deleteTree(full_path) catch {};
    412         }
    413 
    414         // TODO remove empty directories
    415     }
    416 
    417     fn makeOneStep(self: *Builder, s: *Step) anyerror!void {
    418         if (s.loop_flag) {
    419             warn("Dependency loop detected:\n  {}\n", .{s.name});
    420             return error.DependencyLoopDetected;
    421         }
    422         s.loop_flag = true;
    423 
    424         for (s.dependencies.items) |dep| {
    425             self.makeOneStep(dep) catch |err| {
    426                 if (err == error.DependencyLoopDetected) {
    427                     warn("  {}\n", .{s.name});
    428                 }
    429                 return err;
    430             };
    431         }
    432 
    433         s.loop_flag = false;
    434 
    435         try s.make();
    436     }
    437 
    438     fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step {
    439         for (self.top_level_steps.items) |top_level_step| {
    440             if (mem.eql(u8, top_level_step.step.name, name)) {
    441                 return &top_level_step.step;
    442             }
    443         }
    444         warn("Cannot run step '{}' because it does not exist\n", .{name});
    445         return error.InvalidStepName;
    446     }
    447 
    448     pub fn option(self: *Builder, comptime T: type, name: []const u8, description: []const u8) ?T {
    449         const type_id = comptime typeToEnum(T);
    450         const available_option = AvailableOption{
    451             .name = name,
    452             .type_id = type_id,
    453             .description = description,
    454         };
    455         if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
    456             panic("Option '{}' declared twice", .{name});
    457         }
    458         self.available_options_list.append(available_option) catch unreachable;
    459 
    460         const entry = self.user_input_options.getEntry(name) orelse return null;
    461         entry.value.used = true;
    462         switch (type_id) {
    463             .Bool => switch (entry.value.value) {
    464                 .Flag => return true,
    465                 .Scalar => |s| {
    466                     if (mem.eql(u8, s, "true")) {
    467                         return true;
    468                     } else if (mem.eql(u8, s, "false")) {
    469                         return false;
    470                     } else {
    471                         warn("Expected -D{} to be a boolean, but received '{}'\n", .{ name, s });
    472                         self.markInvalidUserInput();
    473                         return null;
    474                     }
    475                 },
    476                 .List => {
    477                     warn("Expected -D{} to be a boolean, but received a list.\n", .{name});
    478                     self.markInvalidUserInput();
    479                     return null;
    480                 },
    481             },
    482             .Int => switch (entry.value.value) {
    483                 .Flag => {
    484                     warn("Expected -D{} to be an integer, but received a boolean.\n", .{name});
    485                     self.markInvalidUserInput();
    486                     return null;
    487                 },
    488                 .Scalar => |s| {
    489                     const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
    490                         error.Overflow => {
    491                             warn("-D{} value {} cannot fit into type {}.\n", .{ name, s, @typeName(T) });
    492                             self.markInvalidUserInput();
    493                             return null;
    494                         },
    495                         else => {
    496                             warn("Expected -D{} to be an integer of type {}.\n", .{ name, @typeName(T) });
    497                             self.markInvalidUserInput();
    498                             return null;
    499                         },
    500                     };
    501                     return n;
    502                 },
    503                 .List => {
    504                     warn("Expected -D{} to be an integer, but received a list.\n", .{name});
    505                     self.markInvalidUserInput();
    506                     return null;
    507                 },
    508             },
    509             .Float => panic("TODO float options to build script", .{}),
    510             .Enum => switch (entry.value.value) {
    511                 .Flag => {
    512                     warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
    513                     self.markInvalidUserInput();
    514                     return null;
    515                 },
    516                 .Scalar => |s| {
    517                     if (std.meta.stringToEnum(T, s)) |enum_lit| {
    518                         return enum_lit;
    519                     } else {
    520                         warn("Expected -D{} to be of type {}.\n", .{ name, @typeName(T) });
    521                         self.markInvalidUserInput();
    522                         return null;
    523                     }
    524                 },
    525                 .List => {
    526                     warn("Expected -D{} to be a string, but received a list.\n", .{name});
    527                     self.markInvalidUserInput();
    528                     return null;
    529                 },
    530             },
    531             .String => switch (entry.value.value) {
    532                 .Flag => {
    533                     warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
    534                     self.markInvalidUserInput();
    535                     return null;
    536                 },
    537                 .List => {
    538                     warn("Expected -D{} to be a string, but received a list.\n", .{name});
    539                     self.markInvalidUserInput();
    540                     return null;
    541                 },
    542                 .Scalar => |s| return s,
    543             },
    544             .List => switch (entry.value.value) {
    545                 .Flag => {
    546                     warn("Expected -D{} to be a list, but received a boolean.\n", .{name});
    547                     self.markInvalidUserInput();
    548                     return null;
    549                 },
    550                 .Scalar => |s| {
    551                     return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
    552                 },
    553                 .List => |lst| return lst.items,
    554             },
    555         }
    556     }
    557 
    558     pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step {
    559         const step_info = self.allocator.create(TopLevelStep) catch unreachable;
    560         step_info.* = TopLevelStep{
    561             .step = Step.initNoOp(.TopLevel, name, self.allocator),
    562             .description = description,
    563         };
    564         self.top_level_steps.append(step_info) catch unreachable;
    565         return &step_info.step;
    566     }
    567 
    568     /// This provides the -Drelease option to the build user and does not give them the choice.
    569     pub fn setPreferredReleaseMode(self: *Builder, mode: builtin.Mode) void {
    570         if (self.release_mode != null) {
    571             @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
    572         }
    573         const description = self.fmt("Create a release build ({})", .{@tagName(mode)});
    574         self.is_release = self.option(bool, "release", description) orelse false;
    575         self.release_mode = if (self.is_release) mode else builtin.Mode.Debug;
    576     }
    577 
    578     /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user
    579     /// the choice of what kind of release.
    580     pub fn standardReleaseOptions(self: *Builder) builtin.Mode {
    581         if (self.release_mode) |mode| return mode;
    582 
    583         const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false;
    584         const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false;
    585         const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false;
    586 
    587         const mode = if (release_safe and !release_fast and !release_small)
    588             builtin.Mode.ReleaseSafe
    589         else if (release_fast and !release_safe and !release_small)
    590             builtin.Mode.ReleaseFast
    591         else if (release_small and !release_fast and !release_safe)
    592             builtin.Mode.ReleaseSmall
    593         else if (!release_fast and !release_safe and !release_small)
    594             builtin.Mode.Debug
    595         else x: {
    596             warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)", .{});
    597             self.markInvalidUserInput();
    598             break :x builtin.Mode.Debug;
    599         };
    600         self.is_release = mode != .Debug;
    601         self.release_mode = mode;
    602         return mode;
    603     }
    604 
    605     pub const StandardTargetOptionsArgs = struct {
    606         whitelist: ?[]const CrossTarget = null,
    607 
    608         default_target: CrossTarget = CrossTarget{},
    609     };
    610 
    611     /// Exposes standard `zig build` options for choosing a target.
    612     pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget {
    613         const triple = self.option(
    614             []const u8,
    615             "target",
    616             "The CPU architecture, OS, and ABI to build for",
    617         ) orelse return args.default_target;
    618 
    619         // TODO add cpu and features as part of the target triple
    620 
    621         var diags: CrossTarget.ParseOptions.Diagnostics = .{};
    622         const selected_target = CrossTarget.parse(.{
    623             .arch_os_abi = triple,
    624             .diagnostics = &diags,
    625         }) catch |err| switch (err) {
    626             error.UnknownCpuModel => {
    627                 std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
    628                     diags.cpu_name.?,
    629                     @tagName(diags.arch.?),
    630                 });
    631                 for (diags.arch.?.allCpuModels()) |cpu| {
    632                     std.debug.warn(" {}\n", .{cpu.name});
    633                 }
    634                 process.exit(1);
    635             },
    636             error.UnknownCpuFeature => {
    637                 std.debug.warn(
    638                     \\Unknown CPU feature: '{}'
    639                     \\Available CPU features for architecture '{}':
    640                     \\
    641                 , .{
    642                     diags.unknown_feature_name,
    643                     @tagName(diags.arch.?),
    644                 });
    645                 for (diags.arch.?.allFeaturesList()) |feature| {
    646                     std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
    647                 }
    648                 process.exit(1);
    649             },
    650             error.UnknownOperatingSystem => {
    651                 std.debug.warn(
    652                     \\Unknown OS: '{}'
    653                     \\Available operating systems:
    654                     \\
    655                 , .{diags.os_name});
    656                 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
    657                     std.debug.warn(" {}\n", .{field.name});
    658                 }
    659                 process.exit(1);
    660             },
    661             else => |e| {
    662                 std.debug.warn("Unable to parse target '{}': {}\n", .{ triple, @errorName(e) });
    663                 process.exit(1);
    664             },
    665         };
    666 
    667         const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
    668 
    669         if (args.whitelist) |list| whitelist_check: {
    670             // Make sure it's a match of one of the list.
    671             for (list) |t| {
    672                 const t_triple = t.zigTriple(self.allocator) catch unreachable;
    673                 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
    674                     break :whitelist_check;
    675                 }
    676             }
    677             std.debug.warn("Chosen target '{}' does not match one of the supported targets:\n", .{
    678                 selected_canonicalized_triple,
    679             });
    680             for (list) |t| {
    681                 const t_triple = t.zigTriple(self.allocator) catch unreachable;
    682                 std.debug.warn(" {}\n", .{t_triple});
    683             }
    684             // TODO instead of process exit, return error and have a zig build flag implemented by
    685             // the build runner that turns process exits into error return traces
    686             process.exit(1);
    687         }
    688 
    689         return selected_target;
    690     }
    691 
    692     pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
    693         const gop = try self.user_input_options.getOrPut(name);
    694         if (!gop.found_existing) {
    695             gop.entry.value = UserInputOption{
    696                 .name = name,
    697                 .value = UserValue{ .Scalar = value },
    698                 .used = false,
    699             };
    700             return false;
    701         }
    702 
    703         // option already exists
    704         switch (gop.entry.value.value) {
    705             UserValue.Scalar => |s| {
    706                 // turn it into a list
    707                 var list = ArrayList([]const u8).init(self.allocator);
    708                 list.append(s) catch unreachable;
    709                 list.append(value) catch unreachable;
    710                 _ = self.user_input_options.put(name, UserInputOption{
    711                     .name = name,
    712                     .value = UserValue{ .List = list },
    713                     .used = false,
    714                 }) catch unreachable;
    715             },
    716             UserValue.List => |*list| {
    717                 // append to the list
    718                 list.append(value) catch unreachable;
    719                 _ = self.user_input_options.put(name, UserInputOption{
    720                     .name = name,
    721                     .value = UserValue{ .List = list.* },
    722                     .used = false,
    723                 }) catch unreachable;
    724             },
    725             UserValue.Flag => {
    726                 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", .{ name, value, name });
    727                 return true;
    728             },
    729         }
    730         return false;
    731     }
    732 
    733     pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
    734         const gop = try self.user_input_options.getOrPut(name);
    735         if (!gop.found_existing) {
    736             gop.entry.value = UserInputOption{
    737                 .name = name,
    738                 .value = UserValue{ .Flag = {} },
    739                 .used = false,
    740             };
    741             return false;
    742         }
    743 
    744         // option already exists
    745         switch (gop.entry.value.value) {
    746             UserValue.Scalar => |s| {
    747                 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s });
    748                 return true;
    749             },
    750             UserValue.List => {
    751                 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", .{name});
    752                 return true;
    753             },
    754             UserValue.Flag => {},
    755         }
    756         return false;
    757     }
    758 
    759     fn typeToEnum(comptime T: type) TypeId {
    760         return switch (@typeInfo(T)) {
    761             .Int => .Int,
    762             .Float => .Float,
    763             .Bool => .Bool,
    764             .Enum => .Enum,
    765             else => switch (T) {
    766                 []const u8 => .String,
    767                 []const []const u8 => .List,
    768                 else => @compileError("Unsupported type: " ++ @typeName(T)),
    769             },
    770         };
    771     }
    772 
    773     fn markInvalidUserInput(self: *Builder) void {
    774         self.invalid_user_input = true;
    775     }
    776 
    777     pub fn typeIdName(id: TypeId) []const u8 {
    778         return switch (id) {
    779             .Bool => "bool",
    780             .Int => "int",
    781             .Float => "float",
    782             .Enum => "enum",
    783             .String => "string",
    784             .List => "list",
    785         };
    786     }
    787 
    788     pub fn validateUserInputDidItFail(self: *Builder) bool {
    789         // make sure all args are used
    790         var it = self.user_input_options.iterator();
    791         while (true) {
    792             const entry = it.next() orelse break;
    793             if (!entry.value.used) {
    794                 warn("Invalid option: -D{}\n\n", .{entry.key});
    795                 self.markInvalidUserInput();
    796             }
    797         }
    798 
    799         return self.invalid_user_input;
    800     }
    801 
    802     pub fn spawnChild(self: *Builder, argv: []const []const u8) !void {
    803         return self.spawnChildEnvMap(null, self.env_map, argv);
    804     }
    805 
    806     fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
    807         if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd});
    808         for (argv) |arg| {
    809             warn("{} ", .{arg});
    810         }
    811         warn("\n", .{});
    812     }
    813 
    814     fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const BufMap, argv: []const []const u8) !void {
    815         if (self.verbose) {
    816             printCmd(cwd, argv);
    817         }
    818 
    819         const child = std.ChildProcess.init(argv, self.allocator) catch unreachable;
    820         defer child.deinit();
    821 
    822         child.cwd = cwd;
    823         child.env_map = env_map;
    824 
    825         const term = child.spawnAndWait() catch |err| {
    826             warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) });
    827             return err;
    828         };
    829 
    830         switch (term) {
    831             .Exited => |code| {
    832                 if (code != 0) {
    833                     warn("The following command exited with error code {}:\n", .{code});
    834                     printCmd(cwd, argv);
    835                     return error.UncleanExit;
    836                 }
    837             },
    838             else => {
    839                 warn("The following command terminated unexpectedly:\n", .{});
    840                 printCmd(cwd, argv);
    841 
    842                 return error.UncleanExit;
    843             },
    844         }
    845     }
    846 
    847     pub fn makePath(self: *Builder, path: []const u8) !void {
    848         fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
    849             warn("Unable to create path {}: {}\n", .{ path, @errorName(err) });
    850             return err;
    851         };
    852     }
    853 
    854     pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void {
    855         self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
    856     }
    857 
    858     pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep {
    859         return InstallArtifactStep.create(self, artifact);
    860     }
    861 
    862     ///`dest_rel_path` is relative to prefix path
    863     pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
    864         self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path).step);
    865     }
    866 
    867     pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void {
    868         self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
    869     }
    870 
    871     ///`dest_rel_path` is relative to bin path
    872     pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
    873         self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Bin, dest_rel_path).step);
    874     }
    875 
    876     ///`dest_rel_path` is relative to lib path
    877     pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
    878         self.getInstallStep().dependOn(&self.addInstallFileWithDir(src_path, .Lib, dest_rel_path).step);
    879     }
    880 
    881     pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) void {
    882         self.getInstallStep().dependOn(&self.addInstallRaw(artifact, dest_filename).step);
    883     }
    884 
    885     ///`dest_rel_path` is relative to install prefix path
    886     pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
    887         return self.addInstallFileWithDir(src_path, .Prefix, dest_rel_path);
    888     }
    889 
    890     ///`dest_rel_path` is relative to bin path
    891     pub fn addInstallBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
    892         return self.addInstallFileWithDir(src_path, .Bin, dest_rel_path);
    893     }
    894 
    895     ///`dest_rel_path` is relative to lib path
    896     pub fn addInstallLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
    897         return self.addInstallFileWithDir(src_path, .Lib, dest_rel_path);
    898     }
    899 
    900     pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *InstallRawStep {
    901         return InstallRawStep.create(self, artifact, dest_filename);
    902     }
    903 
    904     pub fn addInstallFileWithDir(
    905         self: *Builder,
    906         src_path: []const u8,
    907         install_dir: InstallDir,
    908         dest_rel_path: []const u8,
    909     ) *InstallFileStep {
    910         if (dest_rel_path.len == 0) {
    911             panic("dest_rel_path must be non-empty", .{});
    912         }
    913         const install_step = self.allocator.create(InstallFileStep) catch unreachable;
    914         install_step.* = InstallFileStep.init(self, src_path, install_dir, dest_rel_path);
    915         return install_step;
    916     }
    917 
    918     pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep {
    919         const install_step = self.allocator.create(InstallDirStep) catch unreachable;
    920         install_step.* = InstallDirStep.init(self, options);
    921         return install_step;
    922     }
    923 
    924     pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
    925         self.installed_files.append(InstalledFile{
    926             .dir = dir,
    927             .path = dest_rel_path,
    928         }) catch unreachable;
    929     }
    930 
    931     pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
    932         if (self.verbose) {
    933             warn("cp {} {} ", .{ source_path, dest_path });
    934         }
    935         const cwd = fs.cwd();
    936         const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
    937         if (self.verbose) switch (prev_status) {
    938             .stale => warn("# installed\n", .{}),
    939             .fresh => warn("# up-to-date\n", .{}),
    940         };
    941     }
    942 
    943     pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
    944         return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
    945     }
    946 
    947     pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
    948         return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
    949     }
    950 
    951     pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
    952         // TODO report error for ambiguous situations
    953         const exe_extension = @as(CrossTarget, .{}).exeFileExt();
    954         for (self.search_prefixes.items) |search_prefix| {
    955             for (names) |name| {
    956                 if (fs.path.isAbsolute(name)) {
    957                     return name;
    958                 }
    959                 const full_path = try fs.path.join(self.allocator, &[_][]const u8{
    960                     search_prefix,
    961                     "bin",
    962                     self.fmt("{}{}", .{ name, exe_extension }),
    963                 });
    964                 return fs.realpathAlloc(self.allocator, full_path) catch continue;
    965             }
    966         }
    967         if (self.env_map.get("PATH")) |PATH| {
    968             for (names) |name| {
    969                 if (fs.path.isAbsolute(name)) {
    970                     return name;
    971                 }
    972                 var it = mem.tokenize(PATH, &[_]u8{fs.path.delimiter});
    973                 while (it.next()) |path| {
    974                     const full_path = try fs.path.join(self.allocator, &[_][]const u8{
    975                         path,
    976                         self.fmt("{}{}", .{ name, exe_extension }),
    977                     });
    978                     return fs.realpathAlloc(self.allocator, full_path) catch continue;
    979                 }
    980             }
    981         }
    982         for (names) |name| {
    983             if (fs.path.isAbsolute(name)) {
    984                 return name;
    985             }
    986             for (paths) |path| {
    987                 const full_path = try fs.path.join(self.allocator, &[_][]const u8{
    988                     path,
    989                     self.fmt("{}{}", .{ name, exe_extension }),
    990                 });
    991                 return fs.realpathAlloc(self.allocator, full_path) catch continue;
    992             }
    993         }
    994         return error.FileNotFound;
    995     }
    996 
    997     pub fn execAllowFail(
    998         self: *Builder,
    999         argv: []const []const u8,
   1000         out_code: *u8,
   1001         stderr_behavior: std.ChildProcess.StdIo,
   1002     ) ![]u8 {
   1003         assert(argv.len != 0);
   1004 
   1005         const max_output_size = 400 * 1024;
   1006         const child = try std.ChildProcess.init(argv, self.allocator);
   1007         defer child.deinit();
   1008 
   1009         child.stdin_behavior = .Ignore;
   1010         child.stdout_behavior = .Pipe;
   1011         child.stderr_behavior = stderr_behavior;
   1012 
   1013         try child.spawn();
   1014 
   1015         const stdout = try child.stdout.?.inStream().readAllAlloc(self.allocator, max_output_size);
   1016         errdefer self.allocator.free(stdout);
   1017 
   1018         const term = try child.wait();
   1019         switch (term) {
   1020             .Exited => |code| {
   1021                 if (code != 0) {
   1022                     out_code.* = @truncate(u8, code);
   1023                     return error.ExitCodeFailure;
   1024                 }
   1025                 return stdout;
   1026             },
   1027             .Signal, .Stopped, .Unknown => |code| {
   1028                 out_code.* = @truncate(u8, code);
   1029                 return error.ProcessTerminated;
   1030             },
   1031         }
   1032     }
   1033 
   1034     pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 {
   1035         assert(argv.len != 0);
   1036 
   1037         if (self.verbose) {
   1038             printCmd(null, argv);
   1039         }
   1040 
   1041         var code: u8 = undefined;
   1042         return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
   1043             error.FileNotFound => {
   1044                 if (src_step) |s| warn("{}...", .{s.name});
   1045                 warn("Unable to spawn the following command: file not found\n", .{});
   1046                 printCmd(null, argv);
   1047                 std.os.exit(@truncate(u8, code));
   1048             },
   1049             error.ExitCodeFailure => {
   1050                 if (src_step) |s| warn("{}...", .{s.name});
   1051                 warn("The following command exited with error code {}:\n", .{code});
   1052                 printCmd(null, argv);
   1053                 std.os.exit(@truncate(u8, code));
   1054             },
   1055             error.ProcessTerminated => {
   1056                 if (src_step) |s| warn("{}...", .{s.name});
   1057                 warn("The following command terminated unexpectedly:\n", .{});
   1058                 printCmd(null, argv);
   1059                 std.os.exit(@truncate(u8, code));
   1060             },
   1061             else => |e| return e,
   1062         };
   1063     }
   1064 
   1065     pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {
   1066         return self.execFromStep(argv, null);
   1067     }
   1068 
   1069     pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
   1070         self.search_prefixes.append(search_prefix) catch unreachable;
   1071     }
   1072 
   1073     pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
   1074         const base_dir = switch (dir) {
   1075             .Prefix => self.install_path,
   1076             .Bin => self.exe_dir,
   1077             .Lib => self.lib_dir,
   1078             .Header => self.h_dir,
   1079             .Custom => |path| fs.path.join(self.allocator, &[_][]const u8{ self.install_path, path }) catch unreachable,
   1080         };
   1081         return fs.path.resolve(
   1082             self.allocator,
   1083             &[_][]const u8{ base_dir, dest_rel_path },
   1084         ) catch unreachable;
   1085     }
   1086 
   1087     fn execPkgConfigList(self: *Builder, out_code: *u8) ![]const PkgConfigPkg {
   1088         const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
   1089         var list = ArrayList(PkgConfigPkg).init(self.allocator);
   1090         var line_it = mem.tokenize(stdout, "\r\n");
   1091         while (line_it.next()) |line| {
   1092             if (mem.trim(u8, line, " \t").len == 0) continue;
   1093             var tok_it = mem.tokenize(line, " \t");
   1094             try list.append(PkgConfigPkg{
   1095                 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
   1096                 .desc = tok_it.rest(),
   1097             });
   1098         }
   1099         return list.items;
   1100     }
   1101 
   1102     fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
   1103         if (self.pkg_config_pkg_list) |res| {
   1104             return res;
   1105         }
   1106         var code: u8 = undefined;
   1107         if (self.execPkgConfigList(&code)) |list| {
   1108             self.pkg_config_pkg_list = list;
   1109             return list;
   1110         } else |err| {
   1111             const result = switch (err) {
   1112                 error.ProcessTerminated => error.PkgConfigCrashed,
   1113                 error.ExitCodeFailure => error.PkgConfigFailed,
   1114                 error.FileNotFound => error.PkgConfigNotInstalled,
   1115                 error.InvalidName => error.PkgConfigNotInstalled,
   1116                 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
   1117                 else => return err,
   1118             };
   1119             self.pkg_config_pkg_list = result;
   1120             return result;
   1121         }
   1122     }
   1123 };
   1124 
   1125 test "builder.findProgram compiles" {
   1126     if (builtin.os.tag == .wasi) return error.SkipZigTest;
   1127 
   1128     var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
   1129     defer arena.deinit();
   1130 
   1131     const builder = try Builder.create(&arena.allocator, "zig", "zig-cache", "zig-cache");
   1132     defer builder.destroy();
   1133     _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
   1134 }
   1135 
   1136 /// Deprecated. Use `std.builtin.Version`.
   1137 pub const Version = builtin.Version;
   1138 
   1139 /// Deprecated. Use `std.zig.CrossTarget`.
   1140 pub const Target = std.zig.CrossTarget;
   1141 
   1142 pub const Pkg = struct {
   1143     name: []const u8,
   1144     path: []const u8,
   1145     dependencies: ?[]const Pkg = null,
   1146 };
   1147 
   1148 const CSourceFile = struct {
   1149     source: FileSource,
   1150     args: []const []const u8,
   1151 };
   1152 
   1153 fn isLibCLibrary(name: []const u8) bool {
   1154     const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
   1155     for (libc_libraries) |libc_lib_name| {
   1156         if (mem.eql(u8, name, libc_lib_name))
   1157             return true;
   1158     }
   1159     return false;
   1160 }
   1161 
   1162 pub const FileSource = union(enum) {
   1163     /// Relative to build root
   1164     path: []const u8,
   1165     write_file: struct {
   1166         step: *WriteFileStep,
   1167         basename: []const u8,
   1168     },
   1169     translate_c: *TranslateCStep,
   1170 
   1171     pub fn addStepDependencies(self: FileSource, step: *Step) void {
   1172         switch (self) {
   1173             .path => {},
   1174             .write_file => |wf| step.dependOn(&wf.step.step),
   1175             .translate_c => |tc| step.dependOn(&tc.step),
   1176         }
   1177     }
   1178 
   1179     /// Should only be called during make()
   1180     pub fn getPath(self: FileSource, builder: *Builder) []const u8 {
   1181         return switch (self) {
   1182             .path => |p| builder.pathFromRoot(p),
   1183             .write_file => |wf| wf.step.getOutputPath(wf.basename),
   1184             .translate_c => |tc| tc.getOutputPath(),
   1185         };
   1186     }
   1187 };
   1188 
   1189 const BuildOptionArtifactArg = struct {
   1190     name: []const u8,
   1191     artifact: *LibExeObjStep,
   1192 };
   1193 
   1194 pub const LibExeObjStep = struct {
   1195     step: Step,
   1196     builder: *Builder,
   1197     name: []const u8,
   1198     target: CrossTarget = CrossTarget{},
   1199     linker_script: ?[]const u8 = null,
   1200     version_script: ?[]const u8 = null,
   1201     out_filename: []const u8,
   1202     is_dynamic: bool,
   1203     version: ?Version,
   1204     build_mode: builtin.Mode,
   1205     kind: Kind,
   1206     major_only_filename: []const u8,
   1207     name_only_filename: []const u8,
   1208     strip: bool,
   1209     lib_paths: ArrayList([]const u8),
   1210     framework_dirs: ArrayList([]const u8),
   1211     frameworks: BufSet,
   1212     verbose_link: bool,
   1213     verbose_cc: bool,
   1214     emit_llvm_ir: bool = false,
   1215     emit_asm: bool = false,
   1216     emit_bin: bool = true,
   1217     emit_docs: bool = false,
   1218     emit_h: bool = false,
   1219     bundle_compiler_rt: ?bool = null,
   1220     disable_stack_probing: bool,
   1221     disable_sanitize_c: bool,
   1222     rdynamic: bool,
   1223     c_std: Builder.CStd,
   1224     override_lib_dir: ?[]const u8,
   1225     main_pkg_path: ?[]const u8,
   1226     exec_cmd_args: ?[]const ?[]const u8,
   1227     name_prefix: []const u8,
   1228     filter: ?[]const u8,
   1229     single_threaded: bool,
   1230     test_evented_io: bool = false,
   1231     code_model: builtin.CodeModel = .default,
   1232 
   1233     root_src: ?FileSource,
   1234     out_h_filename: []const u8,
   1235     out_lib_filename: []const u8,
   1236     out_pdb_filename: []const u8,
   1237     packages: ArrayList(Pkg),
   1238     build_options_contents: std.ArrayList(u8),
   1239     build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg),
   1240 
   1241     object_src: []const u8,
   1242 
   1243     link_objects: ArrayList(LinkObject),
   1244     include_dirs: ArrayList(IncludeDir),
   1245     c_macros: ArrayList([]const u8),
   1246     output_dir: ?[]const u8,
   1247     is_linking_libc: bool = false,
   1248     vcpkg_bin_path: ?[]const u8 = null,
   1249 
   1250     /// This may be set in order to override the default install directory
   1251     override_dest_dir: ?InstallDir,
   1252     installed_path: ?[]const u8,
   1253     install_step: ?*InstallArtifactStep,
   1254 
   1255     /// Base address for an executable image.
   1256     image_base: ?u64 = null,
   1257 
   1258     libc_file: ?[]const u8 = null,
   1259 
   1260     valgrind_support: ?bool = null,
   1261 
   1262     /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
   1263     /// file.
   1264     link_eh_frame_hdr: bool = false,
   1265     link_emit_relocs: bool = false,
   1266 
   1267     /// Place every function in its own section so that unused ones may be
   1268     /// safely garbage-collected during the linking phase.
   1269     link_function_sections: bool = false,
   1270 
   1271     /// Uses system Wine installation to run cross compiled Windows build artifacts.
   1272     enable_wine: bool = false,
   1273 
   1274     /// Uses system QEMU installation to run cross compiled foreign architecture build artifacts.
   1275     enable_qemu: bool = false,
   1276 
   1277     /// Uses system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
   1278     enable_wasmtime: bool = false,
   1279 
   1280     /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
   1281     /// this will be the directory $glibc-build-dir/install/glibcs
   1282     /// Given the example of the aarch64 target, this is the directory
   1283     /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
   1284     glibc_multi_install_dir: ?[]const u8 = null,
   1285 
   1286     /// Position Independent Code
   1287     force_pic: ?bool = null,
   1288 
   1289     /// Position Independent Executable
   1290     pie: ?bool = null,
   1291 
   1292     subsystem: ?builtin.SubSystem = null,
   1293 
   1294     const LinkObject = union(enum) {
   1295         StaticPath: []const u8,
   1296         OtherStep: *LibExeObjStep,
   1297         SystemLib: []const u8,
   1298         AssemblyFile: FileSource,
   1299         CSourceFile: *CSourceFile,
   1300     };
   1301 
   1302     const IncludeDir = union(enum) {
   1303         RawPath: []const u8,
   1304         RawPathSystem: []const u8,
   1305         OtherStep: *LibExeObjStep,
   1306     };
   1307 
   1308     const Kind = enum {
   1309         Exe,
   1310         Lib,
   1311         Obj,
   1312         Test,
   1313     };
   1314 
   1315     const SharedLibKind = union(enum) {
   1316         versioned: Version,
   1317         unversioned: void,
   1318     };
   1319 
   1320     pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep {
   1321         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
   1322         self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, switch (kind) {
   1323             .versioned => |ver| ver,
   1324             .unversioned => null,
   1325         });
   1326         return self;
   1327     }
   1328 
   1329     pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
   1330         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
   1331         self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, null);
   1332         return self;
   1333     }
   1334 
   1335     pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
   1336         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
   1337         self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, null);
   1338         return self;
   1339     }
   1340 
   1341     pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep {
   1342         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
   1343         self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, null);
   1344         return self;
   1345     }
   1346 
   1347     pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
   1348         const self = builder.allocator.create(LibExeObjStep) catch unreachable;
   1349         self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, null);
   1350         return self;
   1351     }
   1352 
   1353     fn initExtraArgs(
   1354         builder: *Builder,
   1355         name: []const u8,
   1356         root_src: ?FileSource,
   1357         kind: Kind,
   1358         is_dynamic: bool,
   1359         ver: ?Version,
   1360     ) LibExeObjStep {
   1361         if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
   1362             panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
   1363         }
   1364         var self = LibExeObjStep{
   1365             .strip = false,
   1366             .builder = builder,
   1367             .verbose_link = false,
   1368             .verbose_cc = false,
   1369             .build_mode = builtin.Mode.Debug,
   1370             .is_dynamic = is_dynamic,
   1371             .kind = kind,
   1372             .root_src = root_src,
   1373             .name = name,
   1374             .frameworks = BufSet.init(builder.allocator),
   1375             .step = Step.init(.LibExeObj, name, builder.allocator, make),
   1376             .version = ver,
   1377             .out_filename = undefined,
   1378             .out_h_filename = builder.fmt("{}.h", .{name}),
   1379             .out_lib_filename = undefined,
   1380             .out_pdb_filename = builder.fmt("{}.pdb", .{name}),
   1381             .major_only_filename = undefined,
   1382             .name_only_filename = undefined,
   1383             .packages = ArrayList(Pkg).init(builder.allocator),
   1384             .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
   1385             .link_objects = ArrayList(LinkObject).init(builder.allocator),
   1386             .c_macros = ArrayList([]const u8).init(builder.allocator),
   1387             .lib_paths = ArrayList([]const u8).init(builder.allocator),
   1388             .framework_dirs = ArrayList([]const u8).init(builder.allocator),
   1389             .object_src = undefined,
   1390             .build_options_contents = std.ArrayList(u8).init(builder.allocator),
   1391             .build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator),
   1392             .c_std = Builder.CStd.C99,
   1393             .override_lib_dir = null,
   1394             .main_pkg_path = null,
   1395             .exec_cmd_args = null,
   1396             .name_prefix = "",
   1397             .filter = null,
   1398             .disable_stack_probing = false,
   1399             .disable_sanitize_c = false,
   1400             .rdynamic = false,
   1401             .output_dir = null,
   1402             .single_threaded = false,
   1403             .override_dest_dir = null,
   1404             .installed_path = null,
   1405             .install_step = null,
   1406         };
   1407         self.computeOutFileNames();
   1408         if (root_src) |rs| rs.addStepDependencies(&self.step);
   1409         return self;
   1410     }
   1411 
   1412     fn computeOutFileNames(self: *LibExeObjStep) void {
   1413         const target_info = std.zig.system.NativeTargetInfo.detect(
   1414             self.builder.allocator,
   1415             self.target,
   1416         ) catch unreachable;
   1417         const target = target_info.target;
   1418         self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
   1419             .root_name = self.name,
   1420             .target = target,
   1421             .output_mode = switch (self.kind) {
   1422                 .Lib => .Lib,
   1423                 .Obj => .Obj,
   1424                 .Exe, .Test => .Exe,
   1425             },
   1426             .link_mode = if (self.is_dynamic) .Dynamic else .Static,
   1427             .version = self.version,
   1428         }) catch unreachable;
   1429 
   1430         if (self.kind == .Lib) {
   1431             if (!self.is_dynamic) {
   1432                 self.out_lib_filename = self.out_filename;
   1433             } else if (self.version) |version| {
   1434                 if (target.isDarwin()) {
   1435                     self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
   1436                         self.name,
   1437                         version.major,
   1438                     });
   1439                     self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
   1440                     self.out_lib_filename = self.out_filename;
   1441                 } else if (target.os.tag == .windows) {
   1442                     self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
   1443                 } else {
   1444                     self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
   1445                     self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
   1446                     self.out_lib_filename = self.out_filename;
   1447                 }
   1448             } else {
   1449                 if (target.isDarwin()) {
   1450                     self.out_lib_filename = self.out_filename;
   1451                 } else if (target.os.tag == .windows) {
   1452                     self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
   1453                 } else {
   1454                     self.out_lib_filename = self.out_filename;
   1455                 }
   1456             }
   1457         }
   1458     }
   1459 
   1460     pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void {
   1461         self.target = target;
   1462         self.computeOutFileNames();
   1463     }
   1464 
   1465     pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
   1466         self.output_dir = self.builder.dupePath(dir);
   1467     }
   1468 
   1469     pub fn install(self: *LibExeObjStep) void {
   1470         self.builder.installArtifact(self);
   1471     }
   1472 
   1473     pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8) void {
   1474         self.builder.installRaw(self, dest_filename);
   1475     }
   1476 
   1477     /// Creates a `RunStep` with an executable built with `addExecutable`.
   1478     /// Add command line arguments with `addArg`.
   1479     pub fn run(exe: *LibExeObjStep) *RunStep {
   1480         assert(exe.kind == Kind.Exe);
   1481 
   1482         // It doesn't have to be native. We catch that if you actually try to run it.
   1483         // Consider that this is declarative; the run step may not be run unless a user
   1484         // option is supplied.
   1485         const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {}", .{exe.step.name}));
   1486         run_step.addArtifactArg(exe);
   1487 
   1488         if (exe.vcpkg_bin_path) |path| {
   1489             run_step.addPathDir(path);
   1490         }
   1491 
   1492         return run_step;
   1493     }
   1494 
   1495     pub fn setLinkerScriptPath(self: *LibExeObjStep, path: []const u8) void {
   1496         self.linker_script = path;
   1497     }
   1498 
   1499     pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
   1500         assert(self.target.isDarwin());
   1501         self.frameworks.put(framework_name) catch unreachable;
   1502     }
   1503 
   1504     /// Returns whether the library, executable, or object depends on a particular system library.
   1505     pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
   1506         if (isLibCLibrary(name)) {
   1507             return self.is_linking_libc;
   1508         }
   1509         for (self.link_objects.items) |link_object| {
   1510             switch (link_object) {
   1511                 LinkObject.SystemLib => |n| if (mem.eql(u8, n, name)) return true,
   1512                 else => continue,
   1513             }
   1514         }
   1515         return false;
   1516     }
   1517 
   1518     pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
   1519         assert(lib.kind == Kind.Lib);
   1520         self.linkLibraryOrObject(lib);
   1521     }
   1522 
   1523     pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
   1524         return self.kind == Kind.Lib and self.is_dynamic;
   1525     }
   1526 
   1527     pub fn producesPdbFile(self: *LibExeObjStep) bool {
   1528         if (!self.target.isWindows() and !self.target.isUefi()) return false;
   1529         if (self.strip) return false;
   1530         return self.isDynamicLibrary() or self.kind == .Exe;
   1531     }
   1532 
   1533     pub fn linkLibC(self: *LibExeObjStep) void {
   1534         if (!self.is_linking_libc) {
   1535             self.is_linking_libc = true;
   1536             self.link_objects.append(LinkObject{ .SystemLib = "c" }) catch unreachable;
   1537         }
   1538     }
   1539 
   1540     /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
   1541     pub fn defineCMacro(self: *LibExeObjStep, name_and_value: []const u8) void {
   1542         self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
   1543     }
   1544 
   1545     /// This one has no integration with anything, it just puts -lname on the command line.
   1546     /// Prefer to use `linkSystemLibrary` instead.
   1547     pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
   1548         self.link_objects.append(LinkObject{ .SystemLib = self.builder.dupe(name) }) catch unreachable;
   1549     }
   1550 
   1551     /// This links against a system library, exclusively using pkg-config to find the library.
   1552     /// Prefer to use `linkSystemLibrary` instead.
   1553     pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) !void {
   1554         const pkg_name = match: {
   1555             // First we have to map the library name to pkg config name. Unfortunately,
   1556             // there are several examples where this is not straightforward:
   1557             // -lSDL2 -> pkg-config sdl2
   1558             // -lgdk-3 -> pkg-config gdk-3.0
   1559             // -latk-1.0 -> pkg-config atk
   1560             const pkgs = try self.builder.getPkgConfigList();
   1561 
   1562             // Exact match means instant winner.
   1563             for (pkgs) |pkg| {
   1564                 if (mem.eql(u8, pkg.name, lib_name)) {
   1565                     break :match pkg.name;
   1566                 }
   1567             }
   1568 
   1569             // Next we'll try ignoring case.
   1570             for (pkgs) |pkg| {
   1571                 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
   1572                     break :match pkg.name;
   1573                 }
   1574             }
   1575 
   1576             // Now try appending ".0".
   1577             for (pkgs) |pkg| {
   1578                 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
   1579                     if (pos != 0) continue;
   1580                     if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
   1581                         break :match pkg.name;
   1582                     }
   1583                 }
   1584             }
   1585 
   1586             // Trimming "-1.0".
   1587             if (mem.endsWith(u8, lib_name, "-1.0")) {
   1588                 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
   1589                 for (pkgs) |pkg| {
   1590                     if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
   1591                         break :match pkg.name;
   1592                     }
   1593                 }
   1594             }
   1595 
   1596             return error.PackageNotFound;
   1597         };
   1598 
   1599         var code: u8 = undefined;
   1600         const stdout = if (self.builder.execAllowFail(&[_][]const u8{
   1601             "pkg-config",
   1602             pkg_name,
   1603             "--cflags",
   1604             "--libs",
   1605         }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
   1606             error.ProcessTerminated => return error.PkgConfigCrashed,
   1607             error.ExitCodeFailure => return error.PkgConfigFailed,
   1608             error.FileNotFound => return error.PkgConfigNotInstalled,
   1609             else => return err,
   1610         };
   1611         var it = mem.tokenize(stdout, " \r\n\t");
   1612         while (it.next()) |tok| {
   1613             if (mem.eql(u8, tok, "-I")) {
   1614                 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
   1615                 self.addIncludeDir(dir);
   1616             } else if (mem.startsWith(u8, tok, "-I")) {
   1617                 self.addIncludeDir(tok["-I".len..]);
   1618             } else if (mem.eql(u8, tok, "-L")) {
   1619                 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
   1620                 self.addLibPath(dir);
   1621             } else if (mem.startsWith(u8, tok, "-L")) {
   1622                 self.addLibPath(tok["-L".len..]);
   1623             } else if (mem.eql(u8, tok, "-l")) {
   1624                 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
   1625                 self.linkSystemLibraryName(lib);
   1626             } else if (mem.startsWith(u8, tok, "-l")) {
   1627                 self.linkSystemLibraryName(tok["-l".len..]);
   1628             } else if (mem.eql(u8, tok, "-D")) {
   1629                 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
   1630                 self.defineCMacro(macro);
   1631             } else if (mem.startsWith(u8, tok, "-D")) {
   1632                 self.defineCMacro(tok["-D".len..]);
   1633             } else if (mem.eql(u8, tok, "-pthread")) {
   1634                 self.linkLibC();
   1635             } else if (self.builder.verbose) {
   1636                 warn("Ignoring pkg-config flag '{}'\n", .{tok});
   1637             }
   1638         }
   1639     }
   1640 
   1641     pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
   1642         if (isLibCLibrary(name)) {
   1643             self.linkLibC();
   1644             return;
   1645         }
   1646         if (self.linkSystemLibraryPkgConfigOnly(name)) |_| {
   1647             // pkg-config worked, so nothing further needed to do.
   1648             return;
   1649         } else |err| switch (err) {
   1650             error.PkgConfigInvalidOutput,
   1651             error.PkgConfigCrashed,
   1652             error.PkgConfigFailed,
   1653             error.PkgConfigNotInstalled,
   1654             error.PackageNotFound,
   1655             => {},
   1656 
   1657             else => unreachable,
   1658         }
   1659 
   1660         self.linkSystemLibraryName(name);
   1661     }
   1662 
   1663     pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
   1664         assert(self.kind == Kind.Test);
   1665         self.name_prefix = text;
   1666     }
   1667 
   1668     pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
   1669         assert(self.kind == Kind.Test);
   1670         self.filter = text;
   1671     }
   1672 
   1673     pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, args: []const []const u8) void {
   1674         self.addCSourceFileSource(.{
   1675             .args = args,
   1676             .source = .{ .path = file },
   1677         });
   1678     }
   1679 
   1680     pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
   1681         const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
   1682 
   1683         const args_copy = self.builder.allocator.alloc([]u8, source.args.len) catch unreachable;
   1684         for (source.args) |arg, i| {
   1685             args_copy[i] = self.builder.dupe(arg);
   1686         }
   1687 
   1688         c_source_file.* = source;
   1689         c_source_file.args = args_copy;
   1690         self.link_objects.append(LinkObject{ .CSourceFile = c_source_file }) catch unreachable;
   1691     }
   1692 
   1693     pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
   1694         self.verbose_link = value;
   1695     }
   1696 
   1697     pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
   1698         self.verbose_cc = value;
   1699     }
   1700 
   1701     pub fn setBuildMode(self: *LibExeObjStep, mode: builtin.Mode) void {
   1702         self.build_mode = mode;
   1703     }
   1704 
   1705     pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
   1706         self.override_lib_dir = self.builder.dupe(dir_path);
   1707     }
   1708 
   1709     pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
   1710         self.main_pkg_path = dir_path;
   1711     }
   1712 
   1713     pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?[]const u8) void {
   1714         self.libc_file = libc_file;
   1715     }
   1716 
   1717     /// Unless setOutputDir was called, this function must be called only in
   1718     /// the make step, from a step that has declared a dependency on this one.
   1719     /// To run an executable built with zig build, use `run`, or create an install step and invoke it.
   1720     pub fn getOutputPath(self: *LibExeObjStep) []const u8 {
   1721         return fs.path.join(
   1722             self.builder.allocator,
   1723             &[_][]const u8{ self.output_dir.?, self.out_filename },
   1724         ) catch unreachable;
   1725     }
   1726 
   1727     /// Unless setOutputDir was called, this function must be called only in
   1728     /// the make step, from a step that has declared a dependency on this one.
   1729     pub fn getOutputLibPath(self: *LibExeObjStep) []const u8 {
   1730         assert(self.kind == Kind.Lib);
   1731         return fs.path.join(
   1732             self.builder.allocator,
   1733             &[_][]const u8{ self.output_dir.?, self.out_lib_filename },
   1734         ) catch unreachable;
   1735     }
   1736 
   1737     /// Unless setOutputDir was called, this function must be called only in
   1738     /// the make step, from a step that has declared a dependency on this one.
   1739     pub fn getOutputHPath(self: *LibExeObjStep) []const u8 {
   1740         assert(self.kind != Kind.Exe);
   1741         assert(self.emit_h);
   1742         return fs.path.join(
   1743             self.builder.allocator,
   1744             &[_][]const u8{ self.output_dir.?, self.out_h_filename },
   1745         ) catch unreachable;
   1746     }
   1747 
   1748     /// Unless setOutputDir was called, this function must be called only in
   1749     /// the make step, from a step that has declared a dependency on this one.
   1750     pub fn getOutputPdbPath(self: *LibExeObjStep) []const u8 {
   1751         assert(self.target.isWindows() or self.target.isUefi());
   1752         return fs.path.join(
   1753             self.builder.allocator,
   1754             &[_][]const u8{ self.output_dir.?, self.out_pdb_filename },
   1755         ) catch unreachable;
   1756     }
   1757 
   1758     pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
   1759         self.link_objects.append(LinkObject{
   1760             .AssemblyFile = .{ .path = self.builder.dupe(path) },
   1761         }) catch unreachable;
   1762     }
   1763 
   1764     pub fn addAssemblyFileFromWriteFileStep(self: *LibExeObjStep, wfs: *WriteFileStep, basename: []const u8) void {
   1765         self.addAssemblyFileSource(.{
   1766             .write_file = .{
   1767                 .step = wfs,
   1768                 .basename = self.builder.dupe(basename),
   1769             },
   1770         });
   1771     }
   1772 
   1773     pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
   1774         self.link_objects.append(LinkObject{ .AssemblyFile = source }) catch unreachable;
   1775         source.addStepDependencies(&self.step);
   1776     }
   1777 
   1778     pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void {
   1779         self.link_objects.append(LinkObject{ .StaticPath = self.builder.dupe(path) }) catch unreachable;
   1780     }
   1781 
   1782     pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
   1783         assert(obj.kind == Kind.Obj);
   1784         self.linkLibraryOrObject(obj);
   1785     }
   1786 
   1787     pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
   1788         const out = self.build_options_contents.outStream();
   1789         switch (T) {
   1790             []const []const u8 => {
   1791                 out.print("pub const {z}: []const []const u8 = &[_][]const u8{{\n", .{name}) catch unreachable;
   1792                 for (value) |slice| {
   1793                     out.print("    \"{Z}\",\n", .{slice}) catch unreachable;
   1794                 }
   1795                 out.writeAll("};\n") catch unreachable;
   1796                 return;
   1797             },
   1798             []const u8 => {
   1799                 out.print("pub const {z}: []const u8 = \"{Z}\";\n", .{ name, value }) catch unreachable;
   1800                 return;
   1801             },
   1802             ?[]const u8 => {
   1803                 out.print("pub const {z}: ?[]const u8 = ", .{name}) catch unreachable;
   1804                 if (value) |payload| {
   1805                     out.print("\"{Z}\";\n", .{payload}) catch unreachable;
   1806                 } else {
   1807                     out.writeAll("null;\n") catch unreachable;
   1808                 }
   1809                 return;
   1810             },
   1811             else => {},
   1812         }
   1813         switch (@typeInfo(T)) {
   1814             .Enum => |enum_info| {
   1815                 out.print("pub const {z} = enum {{\n", .{@typeName(T)}) catch unreachable;
   1816                 inline for (enum_info.fields) |field| {
   1817                     out.print("    {z},\n", .{field.name}) catch unreachable;
   1818                 }
   1819                 out.writeAll("};\n") catch unreachable;
   1820             },
   1821             else => {},
   1822         }
   1823         out.print("pub const {z}: {} = {};\n", .{ name, @typeName(T), value }) catch unreachable;
   1824     }
   1825 
   1826     /// The value is the path in the cache dir.
   1827     /// Adds a dependency automatically.
   1828     pub fn addBuildOptionArtifact(self: *LibExeObjStep, name: []const u8, artifact: *LibExeObjStep) void {
   1829         self.build_options_artifact_args.append(.{ .name = name, .artifact = artifact }) catch unreachable;
   1830         self.step.dependOn(&artifact.step);
   1831     }
   1832 
   1833     pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void {
   1834         self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable;
   1835     }
   1836 
   1837     pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void {
   1838         self.include_dirs.append(IncludeDir{ .RawPath = self.builder.dupe(path) }) catch unreachable;
   1839     }
   1840 
   1841     pub fn addLibPath(self: *LibExeObjStep, path: []const u8) void {
   1842         self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
   1843     }
   1844 
   1845     pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void {
   1846         self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
   1847     }
   1848 
   1849     pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
   1850         self.packages.append(self.builder.dupePkg(package)) catch unreachable;
   1851     }
   1852 
   1853     pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
   1854         self.packages.append(Pkg{
   1855             .name = self.builder.dupe(name),
   1856             .path = self.builder.dupe(pkg_index_path),
   1857         }) catch unreachable;
   1858     }
   1859 
   1860     /// If Vcpkg was found on the system, it will be added to include and lib
   1861     /// paths for the specified target.
   1862     pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: VcpkgLinkage) !void {
   1863         // Ideally in the Unattempted case we would call the function recursively
   1864         // after findVcpkgRoot and have only one switch statement, but the compiler
   1865         // cannot resolve the error set.
   1866         switch (self.builder.vcpkg_root) {
   1867             .Unattempted => {
   1868                 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
   1869                     VcpkgRoot{ .Found = root }
   1870                 else
   1871                     .NotFound;
   1872             },
   1873             .NotFound => return error.VcpkgNotFound,
   1874             .Found => {},
   1875         }
   1876 
   1877         switch (self.builder.vcpkg_root) {
   1878             .Unattempted => unreachable,
   1879             .NotFound => return error.VcpkgNotFound,
   1880             .Found => |root| {
   1881                 const allocator = self.builder.allocator;
   1882                 const triplet = try self.target.vcpkgTriplet(allocator, linkage);
   1883                 defer self.builder.allocator.free(triplet);
   1884 
   1885                 const include_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "include" });
   1886                 errdefer allocator.free(include_path);
   1887                 try self.include_dirs.append(IncludeDir{ .RawPath = include_path });
   1888 
   1889                 const lib_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "lib" });
   1890                 try self.lib_paths.append(lib_path);
   1891 
   1892                 self.vcpkg_bin_path = try fs.path.join(allocator, &[_][]const u8{ root, "installed", triplet, "bin" });
   1893             },
   1894         }
   1895     }
   1896 
   1897     pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
   1898         assert(self.kind == Kind.Test);
   1899         self.exec_cmd_args = args;
   1900     }
   1901 
   1902     fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
   1903         self.step.dependOn(&other.step);
   1904         self.link_objects.append(LinkObject{ .OtherStep = other }) catch unreachable;
   1905         self.include_dirs.append(IncludeDir{ .OtherStep = other }) catch unreachable;
   1906 
   1907         // Inherit dependency on system libraries
   1908         for (other.link_objects.items) |link_object| {
   1909             switch (link_object) {
   1910                 .SystemLib => |name| self.linkSystemLibrary(name),
   1911                 else => continue,
   1912             }
   1913         }
   1914 
   1915         // Inherit dependencies on darwin frameworks
   1916         if (self.target.isDarwin() and !other.isDynamicLibrary()) {
   1917             var it = other.frameworks.iterator();
   1918             while (it.next()) |entry| {
   1919                 self.frameworks.put(entry.key) catch unreachable;
   1920             }
   1921         }
   1922     }
   1923 
   1924     fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
   1925         const builder = self.builder;
   1926 
   1927         try zig_args.append("--pkg-begin");
   1928         try zig_args.append(pkg.name);
   1929         try zig_args.append(builder.pathFromRoot(pkg.path));
   1930 
   1931         if (pkg.dependencies) |dependencies| {
   1932             for (dependencies) |sub_pkg| {
   1933                 try self.makePackageCmd(sub_pkg, zig_args);
   1934             }
   1935         }
   1936 
   1937         try zig_args.append("--pkg-end");
   1938     }
   1939 
   1940     fn make(step: *Step) !void {
   1941         const self = @fieldParentPtr(LibExeObjStep, "step", step);
   1942         const builder = self.builder;
   1943 
   1944         if (self.root_src == null and self.link_objects.items.len == 0) {
   1945             warn("{}: linker needs 1 or more objects to link\n", .{self.step.name});
   1946             return error.NeedAnObject;
   1947         }
   1948 
   1949         var zig_args = ArrayList([]const u8).init(builder.allocator);
   1950         defer zig_args.deinit();
   1951 
   1952         zig_args.append(builder.zig_exe) catch unreachable;
   1953 
   1954         const cmd = switch (self.kind) {
   1955             .Lib => "build-lib",
   1956             .Exe => "build-exe",
   1957             .Obj => "build-obj",
   1958             .Test => "test",
   1959         };
   1960         zig_args.append(cmd) catch unreachable;
   1961 
   1962         if (builder.color != .auto) {
   1963             try zig_args.append("--color");
   1964             try zig_args.append(@tagName(builder.color));
   1965         }
   1966 
   1967         if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
   1968 
   1969         var prev_has_extra_flags = false;
   1970         for (self.link_objects.items) |link_object| {
   1971             switch (link_object) {
   1972                 .StaticPath => |static_path| {
   1973                     try zig_args.append(builder.pathFromRoot(static_path));
   1974                 },
   1975 
   1976                 .OtherStep => |other| switch (other.kind) {
   1977                     .Exe => unreachable,
   1978                     .Test => unreachable,
   1979                     .Obj => {
   1980                         try zig_args.append(other.getOutputPath());
   1981                     },
   1982                     .Lib => {
   1983                         const full_path_lib = other.getOutputLibPath();
   1984                         try zig_args.append(full_path_lib);
   1985 
   1986                         if (other.is_dynamic and !self.target.isWindows()) {
   1987                             if (fs.path.dirname(full_path_lib)) |dirname| {
   1988                                 try zig_args.append("-rpath");
   1989                                 try zig_args.append(dirname);
   1990                             }
   1991                         }
   1992                     },
   1993                 },
   1994                 .SystemLib => |name| {
   1995                     try zig_args.append("--library");
   1996                     try zig_args.append(name);
   1997                 },
   1998                 .AssemblyFile => |asm_file| {
   1999                     if (prev_has_extra_flags) {
   2000                         try zig_args.append("-extra-cflags");
   2001                         try zig_args.append("--");
   2002                         prev_has_extra_flags = false;
   2003                     }
   2004                     try zig_args.append(asm_file.getPath(builder));
   2005                 },
   2006                 .CSourceFile => |c_source_file| {
   2007                     if (c_source_file.args.len == 0) {
   2008                         if (prev_has_extra_flags) {
   2009                             try zig_args.append("-cflags");
   2010                             try zig_args.append("--");
   2011                             prev_has_extra_flags = false;
   2012                         }
   2013                     } else {
   2014                         try zig_args.append("-cflags");
   2015                         for (c_source_file.args) |arg| {
   2016                             try zig_args.append(arg);
   2017                         }
   2018                         try zig_args.append("--");
   2019                     }
   2020                     try zig_args.append(c_source_file.source.getPath(builder));
   2021                 },
   2022             }
   2023         }
   2024 
   2025         if (self.build_options_contents.items.len > 0 or self.build_options_artifact_args.items.len > 0) {
   2026             // Render build artifact options at the last minute, now that the path is known.
   2027             for (self.build_options_artifact_args.items) |item| {
   2028                 const out = self.build_options_contents.writer();
   2029                 out.print("pub const {}: []const u8 = \"{Z}\";\n", .{ item.name, item.artifact.getOutputPath() }) catch unreachable;
   2030             }
   2031 
   2032             const build_options_file = try fs.path.join(
   2033                 builder.allocator,
   2034                 &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) },
   2035             );
   2036             const path_from_root = builder.pathFromRoot(build_options_file);
   2037             try fs.cwd().writeFile(path_from_root, self.build_options_contents.items);
   2038             try zig_args.append("--pkg-begin");
   2039             try zig_args.append("build_options");
   2040             try zig_args.append(path_from_root);
   2041             try zig_args.append("--pkg-end");
   2042         }
   2043 
   2044         if (self.image_base) |image_base| {
   2045             try zig_args.append("--image-base");
   2046             try zig_args.append(builder.fmt("0x{x}", .{image_base}));
   2047         }
   2048 
   2049         if (self.filter) |filter| {
   2050             try zig_args.append("--test-filter");
   2051             try zig_args.append(filter);
   2052         }
   2053 
   2054         if (self.test_evented_io) {
   2055             try zig_args.append("--test-evented-io");
   2056         }
   2057 
   2058         if (self.name_prefix.len != 0) {
   2059             try zig_args.append("--test-name-prefix");
   2060             try zig_args.append(self.name_prefix);
   2061         }
   2062 
   2063         if (builder.verbose_tokenize) zig_args.append("--verbose-tokenize") catch unreachable;
   2064         if (builder.verbose_ast) zig_args.append("--verbose-ast") catch unreachable;
   2065         if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
   2066         if (builder.verbose_ir) zig_args.append("--verbose-ir") catch unreachable;
   2067         if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
   2068         if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
   2069         if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
   2070         if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
   2071 
   2072         if (self.emit_llvm_ir) try zig_args.append("-femit-llvm-ir");
   2073         if (self.emit_asm) try zig_args.append("-femit-asm");
   2074         if (!self.emit_bin) try zig_args.append("-fno-emit-bin");
   2075         if (self.emit_docs) try zig_args.append("-femit-docs");
   2076         if (self.emit_h) try zig_args.append("-femit-h");
   2077 
   2078         if (self.strip) {
   2079             try zig_args.append("--strip");
   2080         }
   2081         if (self.link_eh_frame_hdr) {
   2082             try zig_args.append("--eh-frame-hdr");
   2083         }
   2084         if (self.link_emit_relocs) {
   2085             try zig_args.append("--emit-relocs");
   2086         }
   2087         if (self.link_function_sections) {
   2088             try zig_args.append("-ffunction-sections");
   2089         }
   2090         if (self.single_threaded) {
   2091             try zig_args.append("--single-threaded");
   2092         }
   2093 
   2094         if (self.libc_file) |libc_file| {
   2095             try zig_args.append("--libc");
   2096             try zig_args.append(builder.pathFromRoot(libc_file));
   2097         }
   2098 
   2099         switch (self.build_mode) {
   2100             .Debug => {}, // Skip since it's the default.
   2101             else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable,
   2102         }
   2103 
   2104         try zig_args.append("--cache-dir");
   2105         try zig_args.append(builder.pathFromRoot(builder.cache_root));
   2106 
   2107         zig_args.append("--name") catch unreachable;
   2108         zig_args.append(self.name) catch unreachable;
   2109 
   2110         if (self.kind == Kind.Lib and self.is_dynamic) {
   2111             if (self.version) |version| {
   2112                 zig_args.append("--version") catch unreachable;
   2113                 zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
   2114             }
   2115         }
   2116         if (self.is_dynamic) {
   2117             try zig_args.append("-dynamic");
   2118         }
   2119         if (self.bundle_compiler_rt) |x| {
   2120             if (x) {
   2121                 try zig_args.append("-fcompiler-rt");
   2122             } else {
   2123                 try zig_args.append("-fno-compiler-rt");
   2124             }
   2125         }
   2126         if (self.disable_stack_probing) {
   2127             try zig_args.append("-fno-stack-check");
   2128         }
   2129         if (self.disable_sanitize_c) {
   2130             try zig_args.append("-fno-sanitize-c");
   2131         }
   2132         if (self.rdynamic) {
   2133             try zig_args.append("-rdynamic");
   2134         }
   2135 
   2136         if (self.code_model != .default) {
   2137             try zig_args.append("-mcmodel");
   2138             try zig_args.append(@tagName(self.code_model));
   2139         }
   2140 
   2141         if (!self.target.isNative()) {
   2142             try zig_args.append("-target");
   2143             try zig_args.append(try self.target.zigTriple(builder.allocator));
   2144 
   2145             // TODO this logic can disappear if cpu model + features becomes part of the target triple
   2146             const cross = self.target.toTarget();
   2147             const all_features = cross.cpu.arch.allFeaturesList();
   2148             var populated_cpu_features = cross.cpu.model.features;
   2149             populated_cpu_features.populateDependencies(all_features);
   2150 
   2151             if (populated_cpu_features.eql(cross.cpu.features)) {
   2152                 // The CPU name alone is sufficient.
   2153                 // If it is the baseline CPU, no command line args are required.
   2154                 if (cross.cpu.model != std.Target.Cpu.baseline(cross.cpu.arch).model) {
   2155                     try zig_args.append("-mcpu");
   2156                     try zig_args.append(cross.cpu.model.name);
   2157                 }
   2158             } else {
   2159                 var mcpu_buffer = std.ArrayList(u8).init(builder.allocator);
   2160 
   2161                 try mcpu_buffer.outStream().print("-mcpu={}", .{cross.cpu.model.name});
   2162 
   2163                 for (all_features) |feature, i_usize| {
   2164                     const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
   2165                     const in_cpu_set = populated_cpu_features.isEnabled(i);
   2166                     const in_actual_set = cross.cpu.features.isEnabled(i);
   2167                     if (in_cpu_set and !in_actual_set) {
   2168                         try mcpu_buffer.outStream().print("-{}", .{feature.name});
   2169                     } else if (!in_cpu_set and in_actual_set) {
   2170                         try mcpu_buffer.outStream().print("+{}", .{feature.name});
   2171                     }
   2172                 }
   2173 
   2174                 try zig_args.append(mcpu_buffer.toOwnedSlice());
   2175             }
   2176 
   2177             if (self.target.dynamic_linker.get()) |dynamic_linker| {
   2178                 try zig_args.append("--dynamic-linker");
   2179                 try zig_args.append(dynamic_linker);
   2180             }
   2181         }
   2182 
   2183         if (self.linker_script) |linker_script| {
   2184             try zig_args.append("--script");
   2185             try zig_args.append(builder.pathFromRoot(linker_script));
   2186         }
   2187 
   2188         if (self.version_script) |version_script| {
   2189             try zig_args.append("--version-script");
   2190             try zig_args.append(builder.pathFromRoot(version_script));
   2191         }
   2192 
   2193         if (self.exec_cmd_args) |exec_cmd_args| {
   2194             for (exec_cmd_args) |cmd_arg| {
   2195                 if (cmd_arg) |arg| {
   2196                     try zig_args.append("--test-cmd");
   2197                     try zig_args.append(arg);
   2198                 } else {
   2199                     try zig_args.append("--test-cmd-bin");
   2200                 }
   2201             }
   2202         } else switch (self.target.getExternalExecutor()) {
   2203             .native, .unavailable => {},
   2204             .qemu => |bin_name| if (self.enable_qemu) qemu: {
   2205                 const need_cross_glibc = self.target.isGnuLibC() and self.is_linking_libc;
   2206                 const glibc_dir_arg = if (need_cross_glibc)
   2207                     self.glibc_multi_install_dir orelse break :qemu
   2208                 else
   2209                     null;
   2210                 try zig_args.append("--test-cmd");
   2211                 try zig_args.append(bin_name);
   2212                 if (glibc_dir_arg) |dir| {
   2213                     const full_dir = try fs.path.join(builder.allocator, &[_][]const u8{
   2214                         dir,
   2215                         try self.target.linuxTriple(builder.allocator),
   2216                     });
   2217 
   2218                     try zig_args.append("--test-cmd");
   2219                     try zig_args.append("-L");
   2220                     try zig_args.append("--test-cmd");
   2221                     try zig_args.append(full_dir);
   2222                 }
   2223                 try zig_args.append("--test-cmd-bin");
   2224             },
   2225             .wine => |bin_name| if (self.enable_wine) {
   2226                 try zig_args.append("--test-cmd");
   2227                 try zig_args.append(bin_name);
   2228                 try zig_args.append("--test-cmd-bin");
   2229             },
   2230             .wasmtime => |bin_name| if (self.enable_wasmtime) {
   2231                 try zig_args.append("--test-cmd");
   2232                 try zig_args.append(bin_name);
   2233                 try zig_args.append("--test-cmd");
   2234                 try zig_args.append("--dir=.");
   2235                 try zig_args.append("--test-cmd-bin");
   2236             },
   2237         }
   2238 
   2239         for (self.packages.items) |pkg| {
   2240             try self.makePackageCmd(pkg, &zig_args);
   2241         }
   2242 
   2243         for (self.include_dirs.items) |include_dir| {
   2244             switch (include_dir) {
   2245                 .RawPath => |include_path| {
   2246                     try zig_args.append("-I");
   2247                     try zig_args.append(self.builder.pathFromRoot(include_path));
   2248                 },
   2249                 .RawPathSystem => |include_path| {
   2250                     try zig_args.append("-isystem");
   2251                     try zig_args.append(self.builder.pathFromRoot(include_path));
   2252                 },
   2253                 .OtherStep => |other| if (other.emit_h) {
   2254                     const h_path = other.getOutputHPath();
   2255                     try zig_args.append("-isystem");
   2256                     try zig_args.append(fs.path.dirname(h_path).?);
   2257                 },
   2258             }
   2259         }
   2260 
   2261         for (self.lib_paths.items) |lib_path| {
   2262             try zig_args.append("-L");
   2263             try zig_args.append(lib_path);
   2264         }
   2265 
   2266         for (self.c_macros.items) |c_macro| {
   2267             try zig_args.append("-D");
   2268             try zig_args.append(c_macro);
   2269         }
   2270 
   2271         if (self.target.isDarwin()) {
   2272             for (self.framework_dirs.items) |dir| {
   2273                 try zig_args.append("-F");
   2274                 try zig_args.append(dir);
   2275             }
   2276 
   2277             var it = self.frameworks.iterator();
   2278             while (it.next()) |entry| {
   2279                 zig_args.append("-framework") catch unreachable;
   2280                 zig_args.append(entry.key) catch unreachable;
   2281             }
   2282         }
   2283 
   2284         if (self.valgrind_support) |valgrind_support| {
   2285             if (valgrind_support) {
   2286                 try zig_args.append("-fvalgrind");
   2287             } else {
   2288                 try zig_args.append("-fno-valgrind");
   2289             }
   2290         }
   2291 
   2292         if (self.override_lib_dir) |dir| {
   2293             try zig_args.append("--override-lib-dir");
   2294             try zig_args.append(builder.pathFromRoot(dir));
   2295         } else if (self.builder.override_lib_dir) |dir| {
   2296             try zig_args.append("--override-lib-dir");
   2297             try zig_args.append(builder.pathFromRoot(dir));
   2298         }
   2299 
   2300         if (self.main_pkg_path) |dir| {
   2301             try zig_args.append("--main-pkg-path");
   2302             try zig_args.append(builder.pathFromRoot(dir));
   2303         }
   2304 
   2305         if (self.force_pic) |pic| {
   2306             if (pic) {
   2307                 try zig_args.append("-fPIC");
   2308             } else {
   2309                 try zig_args.append("-fno-PIC");
   2310             }
   2311         }
   2312 
   2313         if (self.pie) |pie| {
   2314             if (pie) {
   2315                 try zig_args.append("-fPIE");
   2316             } else {
   2317                 try zig_args.append("-fno-PIE");
   2318             }
   2319         }
   2320 
   2321         if (self.subsystem) |subsystem| {
   2322             try zig_args.append("--subsystem");
   2323             try zig_args.append(switch (subsystem) {
   2324                 .Console => "console",
   2325                 .Windows => "windows",
   2326                 .Posix => "posix",
   2327                 .Native => "native",
   2328                 .EfiApplication => "efi_application",
   2329                 .EfiBootServiceDriver => "efi_boot_service_driver",
   2330                 .EfiRom => "efi_rom",
   2331                 .EfiRuntimeDriver => "efi_runtime_driver",
   2332             });
   2333         }
   2334 
   2335         if (self.kind == Kind.Test) {
   2336             try builder.spawnChild(zig_args.items);
   2337         } else {
   2338             try zig_args.append("--enable-cache");
   2339 
   2340             const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
   2341             const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
   2342 
   2343             if (self.output_dir) |output_dir| {
   2344                 var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true });
   2345                 defer src_dir.close();
   2346 
   2347                 // Create the output directory if it doesn't exist.
   2348                 try std.fs.cwd().makePath(output_dir);
   2349 
   2350                 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
   2351                 defer dest_dir.close();
   2352 
   2353                 var it = src_dir.iterate();
   2354                 while (try it.next()) |entry| {
   2355                     // The compiler can put these files into the same directory, but we don't
   2356                     // want to copy them over.
   2357                     if (mem.eql(u8, entry.name, "stage1.id") or
   2358                         mem.eql(u8, entry.name, "llvm-ar.id") or
   2359                         mem.eql(u8, entry.name, "libs.txt") or
   2360                         mem.eql(u8, entry.name, "builtin.zig") or
   2361                         mem.eql(u8, entry.name, "lld.id")) continue;
   2362 
   2363                     _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{});
   2364                 }
   2365             } else {
   2366                 self.output_dir = build_output_dir;
   2367             }
   2368         }
   2369 
   2370         if (self.kind == Kind.Lib and self.is_dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
   2371             try doAtomicSymLinks(builder.allocator, self.getOutputPath(), self.major_only_filename, self.name_only_filename);
   2372         }
   2373     }
   2374 };
   2375 
   2376 pub const InstallArtifactStep = struct {
   2377     step: Step,
   2378     builder: *Builder,
   2379     artifact: *LibExeObjStep,
   2380     dest_dir: InstallDir,
   2381     pdb_dir: ?InstallDir,
   2382     h_dir: ?InstallDir,
   2383 
   2384     const Self = @This();
   2385 
   2386     pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self {
   2387         if (artifact.install_step) |s| return s;
   2388 
   2389         const self = builder.allocator.create(Self) catch unreachable;
   2390         self.* = Self{
   2391             .builder = builder,
   2392             .step = Step.init(.InstallArtifact, builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make),
   2393             .artifact = artifact,
   2394             .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
   2395                 .Obj => unreachable,
   2396                 .Test => unreachable,
   2397                 .Exe => InstallDir{ .Bin = {} },
   2398                 .Lib => InstallDir{ .Lib = {} },
   2399             },
   2400             .pdb_dir = if (artifact.producesPdbFile()) blk: {
   2401                 if (artifact.kind == .Exe) {
   2402                     break :blk InstallDir{ .Bin = {} };
   2403                 } else {
   2404                     break :blk InstallDir{ .Lib = {} };
   2405                 }
   2406             } else null,
   2407             .h_dir = if (artifact.kind == .Lib and artifact.emit_h) .Header else null,
   2408         };
   2409         self.step.dependOn(&artifact.step);
   2410         artifact.install_step = self;
   2411 
   2412         builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
   2413         if (self.artifact.isDynamicLibrary()) {
   2414             if (self.artifact.version != null) {
   2415                 builder.pushInstalledFile(.Lib, artifact.major_only_filename);
   2416                 builder.pushInstalledFile(.Lib, artifact.name_only_filename);
   2417             }
   2418             if (self.artifact.target.isWindows()) {
   2419                 builder.pushInstalledFile(.Lib, artifact.out_lib_filename);
   2420             }
   2421         }
   2422         if (self.pdb_dir) |pdb_dir| {
   2423             builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
   2424         }
   2425         if (self.h_dir) |h_dir| {
   2426             builder.pushInstalledFile(h_dir, artifact.out_h_filename);
   2427         }
   2428         return self;
   2429     }
   2430 
   2431     fn make(step: *Step) !void {
   2432         const self = @fieldParentPtr(Self, "step", step);
   2433         const builder = self.builder;
   2434 
   2435         const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
   2436         try builder.updateFile(self.artifact.getOutputPath(), full_dest_path);
   2437         if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
   2438             try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename);
   2439         }
   2440         if (self.pdb_dir) |pdb_dir| {
   2441             const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
   2442             try builder.updateFile(self.artifact.getOutputPdbPath(), full_pdb_path);
   2443         }
   2444         if (self.h_dir) |h_dir| {
   2445             const full_pdb_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
   2446             try builder.updateFile(self.artifact.getOutputHPath(), full_pdb_path);
   2447         }
   2448         self.artifact.installed_path = full_dest_path;
   2449     }
   2450 };
   2451 
   2452 pub const InstallFileStep = struct {
   2453     step: Step,
   2454     builder: *Builder,
   2455     src_path: []const u8,
   2456     dir: InstallDir,
   2457     dest_rel_path: []const u8,
   2458 
   2459     pub fn init(
   2460         builder: *Builder,
   2461         src_path: []const u8,
   2462         dir: InstallDir,
   2463         dest_rel_path: []const u8,
   2464     ) InstallFileStep {
   2465         builder.pushInstalledFile(dir, dest_rel_path);
   2466         return InstallFileStep{
   2467             .builder = builder,
   2468             .step = Step.init(.InstallFile, builder.fmt("install {}", .{src_path}), builder.allocator, make),
   2469             .src_path = src_path,
   2470             .dir = dir,
   2471             .dest_rel_path = dest_rel_path,
   2472         };
   2473     }
   2474 
   2475     fn make(step: *Step) !void {
   2476         const self = @fieldParentPtr(InstallFileStep, "step", step);
   2477         const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
   2478         const full_src_path = self.builder.pathFromRoot(self.src_path);
   2479         try self.builder.updateFile(full_src_path, full_dest_path);
   2480     }
   2481 };
   2482 
   2483 pub const InstallDirectoryOptions = struct {
   2484     source_dir: []const u8,
   2485     install_dir: InstallDir,
   2486     install_subdir: []const u8,
   2487     exclude_extensions: ?[]const []const u8 = null,
   2488 };
   2489 
   2490 pub const InstallDirStep = struct {
   2491     step: Step,
   2492     builder: *Builder,
   2493     options: InstallDirectoryOptions,
   2494 
   2495     pub fn init(
   2496         builder: *Builder,
   2497         options: InstallDirectoryOptions,
   2498     ) InstallDirStep {
   2499         builder.pushInstalledFile(options.install_dir, options.install_subdir);
   2500         return InstallDirStep{
   2501             .builder = builder,
   2502             .step = Step.init(.InstallDir, builder.fmt("install {}/", .{options.source_dir}), builder.allocator, make),
   2503             .options = options,
   2504         };
   2505     }
   2506 
   2507     fn make(step: *Step) !void {
   2508         const self = @fieldParentPtr(InstallDirStep, "step", step);
   2509         const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
   2510         const full_src_dir = self.builder.pathFromRoot(self.options.source_dir);
   2511         var it = try fs.walkPath(self.builder.allocator, full_src_dir);
   2512         next_entry: while (try it.next()) |entry| {
   2513             if (self.options.exclude_extensions) |ext_list| for (ext_list) |ext| {
   2514                 if (mem.endsWith(u8, entry.path, ext)) {
   2515                     continue :next_entry;
   2516                 }
   2517             };
   2518 
   2519             const rel_path = entry.path[full_src_dir.len + 1 ..];
   2520             const dest_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ dest_prefix, rel_path });
   2521             switch (entry.kind) {
   2522                 .Directory => try fs.cwd().makePath(dest_path),
   2523                 .File => try self.builder.updateFile(entry.path, dest_path),
   2524                 else => continue,
   2525             }
   2526         }
   2527     }
   2528 };
   2529 
   2530 pub const LogStep = struct {
   2531     step: Step,
   2532     builder: *Builder,
   2533     data: []const u8,
   2534 
   2535     pub fn init(builder: *Builder, data: []const u8) LogStep {
   2536         return LogStep{
   2537             .builder = builder,
   2538             .step = Step.init(.Log, builder.fmt("log {}", .{data}), builder.allocator, make),
   2539             .data = data,
   2540         };
   2541     }
   2542 
   2543     fn make(step: *Step) anyerror!void {
   2544         const self = @fieldParentPtr(LogStep, "step", step);
   2545         warn("{}", .{self.data});
   2546     }
   2547 };
   2548 
   2549 pub const RemoveDirStep = struct {
   2550     step: Step,
   2551     builder: *Builder,
   2552     dir_path: []const u8,
   2553 
   2554     pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep {
   2555         return RemoveDirStep{
   2556             .builder = builder,
   2557             .step = Step.init(.RemoveDir, builder.fmt("RemoveDir {}", .{dir_path}), builder.allocator, make),
   2558             .dir_path = dir_path,
   2559         };
   2560     }
   2561 
   2562     fn make(step: *Step) !void {
   2563         const self = @fieldParentPtr(RemoveDirStep, "step", step);
   2564 
   2565         const full_path = self.builder.pathFromRoot(self.dir_path);
   2566         fs.cwd().deleteTree(full_path) catch |err| {
   2567             warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) });
   2568             return err;
   2569         };
   2570     }
   2571 };
   2572 
   2573 const ThisModule = @This();
   2574 pub const Step = struct {
   2575     id: Id,
   2576     name: []const u8,
   2577     makeFn: fn (self: *Step) anyerror!void,
   2578     dependencies: ArrayList(*Step),
   2579     loop_flag: bool,
   2580     done_flag: bool,
   2581 
   2582     pub const Id = enum {
   2583         TopLevel,
   2584         LibExeObj,
   2585         InstallArtifact,
   2586         InstallFile,
   2587         InstallDir,
   2588         Log,
   2589         RemoveDir,
   2590         Fmt,
   2591         TranslateC,
   2592         WriteFile,
   2593         Run,
   2594         CheckFile,
   2595         InstallRaw,
   2596         Custom,
   2597     };
   2598 
   2599     pub fn init(id: Id, name: []const u8, allocator: *Allocator, makeFn: fn (*Step) anyerror!void) Step {
   2600         return Step{
   2601             .id = id,
   2602             .name = name,
   2603             .makeFn = makeFn,
   2604             .dependencies = ArrayList(*Step).init(allocator),
   2605             .loop_flag = false,
   2606             .done_flag = false,
   2607         };
   2608     }
   2609     pub fn initNoOp(id: Id, name: []const u8, allocator: *Allocator) Step {
   2610         return init(id, name, allocator, makeNoOp);
   2611     }
   2612 
   2613     pub fn make(self: *Step) !void {
   2614         if (self.done_flag) return;
   2615 
   2616         try self.makeFn(self);
   2617         self.done_flag = true;
   2618     }
   2619 
   2620     pub fn dependOn(self: *Step, other: *Step) void {
   2621         self.dependencies.append(other) catch unreachable;
   2622     }
   2623 
   2624     fn makeNoOp(self: *Step) anyerror!void {}
   2625 
   2626     pub fn cast(step: *Step, comptime T: type) ?*T {
   2627         if (step.id == comptime typeToId(T)) {
   2628             return @fieldParentPtr(T, "step", step);
   2629         }
   2630         return null;
   2631     }
   2632 
   2633     fn typeToId(comptime T: type) Id {
   2634         inline for (@typeInfo(Id).Enum.fields) |f| {
   2635             if (std.mem.eql(u8, f.name, "TopLevel") or
   2636                 std.mem.eql(u8, f.name, "Custom")) continue;
   2637 
   2638             if (T == @field(ThisModule, f.name ++ "Step")) {
   2639                 return @field(Id, f.name);
   2640             }
   2641         }
   2642         unreachable;
   2643     }
   2644 };
   2645 
   2646 fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
   2647     const out_dir = fs.path.dirname(output_path) orelse ".";
   2648     const out_basename = fs.path.basename(output_path);
   2649     // sym link for libfoo.so.1 to libfoo.so.1.2.3
   2650     const major_only_path = fs.path.join(
   2651         allocator,
   2652         &[_][]const u8{ out_dir, filename_major_only },
   2653     ) catch unreachable;
   2654     fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
   2655         warn("Unable to symlink {} -> {}\n", .{ major_only_path, out_basename });
   2656         return err;
   2657     };
   2658     // sym link for libfoo.so to libfoo.so.1
   2659     const name_only_path = fs.path.join(
   2660         allocator,
   2661         &[_][]const u8{ out_dir, filename_name_only },
   2662     ) catch unreachable;
   2663     fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
   2664         warn("Unable to symlink {} -> {}\n", .{ name_only_path, filename_major_only });
   2665         return err;
   2666     };
   2667 }
   2668 
   2669 /// Returned slice must be freed by the caller.
   2670 fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 {
   2671     const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
   2672     defer allocator.free(appdata_path);
   2673 
   2674     const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
   2675     defer allocator.free(path_file);
   2676 
   2677     const file = fs.cwd().openFile(path_file, .{}) catch return null;
   2678     defer file.close();
   2679 
   2680     const size = @intCast(usize, try file.getEndPos());
   2681     const vcpkg_path = try allocator.alloc(u8, size);
   2682     const size_read = try file.read(vcpkg_path);
   2683     std.debug.assert(size == size_read);
   2684 
   2685     return vcpkg_path;
   2686 }
   2687 
   2688 const VcpkgRoot = union(VcpkgRootStatus) {
   2689     Unattempted: void,
   2690     NotFound: void,
   2691     Found: []const u8,
   2692 };
   2693 
   2694 const VcpkgRootStatus = enum {
   2695     Unattempted,
   2696     NotFound,
   2697     Found,
   2698 };
   2699 
   2700 pub const VcpkgLinkage = std.builtin.LinkMode;
   2701 
   2702 pub const InstallDir = union(enum) {
   2703     Prefix: void,
   2704     Lib: void,
   2705     Bin: void,
   2706     Header: void,
   2707     /// A path relative to the prefix
   2708     Custom: []const u8,
   2709 };
   2710 
   2711 pub const InstalledFile = struct {
   2712     dir: InstallDir,
   2713     path: []const u8,
   2714 };
   2715 
   2716 test "Builder.dupePkg()" {
   2717     if (builtin.os.tag == .wasi) return error.SkipZigTest;
   2718 
   2719     var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
   2720     defer arena.deinit();
   2721     var builder = try Builder.create(
   2722         &arena.allocator,
   2723         "test",
   2724         "test",
   2725         "test",
   2726     );
   2727     defer builder.destroy();
   2728 
   2729     var pkg_dep = Pkg{
   2730         .name = "pkg_dep",
   2731         .path = "/not/a/pkg_dep.zig",
   2732     };
   2733     var pkg_top = Pkg{
   2734         .name = "pkg_top",
   2735         .path = "/not/a/pkg_top.zig",
   2736         .dependencies = &[_]Pkg{pkg_dep},
   2737     };
   2738     const dupe = builder.dupePkg(pkg_top);
   2739 
   2740     const original_deps = pkg_top.dependencies.?;
   2741     const dupe_deps = dupe.dependencies.?;
   2742 
   2743     // probably the same top level package details
   2744     std.testing.expectEqualStrings(pkg_top.name, dupe.name);
   2745 
   2746     // probably the same dependencies
   2747     std.testing.expectEqual(original_deps.len, dupe_deps.len);
   2748     std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
   2749 
   2750     // could segfault otherwise if pointers in duplicated package's fields are
   2751     // the same as those in stack allocated package's fields
   2752     std.testing.expect(dupe_deps.ptr != original_deps.ptr);
   2753     std.testing.expect(dupe.name.ptr != pkg_top.name.ptr);
   2754     std.testing.expect(dupe.path.ptr != pkg_top.path.ptr);
   2755     std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
   2756     std.testing.expect(dupe_deps[0].path.ptr != pkg_dep.path.ptr);
   2757 }
   2758 
   2759 test "LibExeObjStep.addBuildOption" {
   2760     if (builtin.os.tag == .wasi) return error.SkipZigTest;
   2761 
   2762     var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
   2763     defer arena.deinit();
   2764     var builder = try Builder.create(
   2765         &arena.allocator,
   2766         "test",
   2767         "test",
   2768         "test",
   2769     );
   2770     defer builder.destroy();
   2771 
   2772     var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
   2773     exe.addBuildOption(usize, "option1", 1);
   2774     exe.addBuildOption(?usize, "option2", null);
   2775     exe.addBuildOption([]const u8, "string", "zigisthebest");
   2776     exe.addBuildOption(?[]const u8, "optional_string", null);
   2777 
   2778     std.testing.expectEqualStrings(
   2779         \\pub const option1: usize = 1;
   2780         \\pub const option2: ?usize = null;
   2781         \\pub const string: []const u8 = "zigisthebest";
   2782         \\pub const optional_string: ?[]const u8 = null;
   2783         \\
   2784     , exe.build_options_contents.items);
   2785 }
   2786 
   2787 test "LibExeObjStep.addPackage" {
   2788     if (builtin.os.tag == .wasi) return error.SkipZigTest;
   2789 
   2790     var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
   2791     defer arena.deinit();
   2792 
   2793     var builder = try Builder.create(
   2794         &arena.allocator,
   2795         "test",
   2796         "test",
   2797         "test",
   2798     );
   2799     defer builder.destroy();
   2800 
   2801     const pkg_dep = Pkg{
   2802         .name = "pkg_dep",
   2803         .path = "/not/a/pkg_dep.zig",
   2804     };
   2805     const pkg_top = Pkg{
   2806         .name = "pkg_dep",
   2807         .path = "/not/a/pkg_top.zig",
   2808         .dependencies = &[_]Pkg{pkg_dep},
   2809     };
   2810 
   2811     var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
   2812     exe.addPackage(pkg_top);
   2813 
   2814     std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
   2815 
   2816     const dupe = exe.packages.items[0];
   2817     std.testing.expectEqualStrings(pkg_top.name, dupe.name);
   2818 }
   2819 
   2820 test "" {
   2821     // The only purpose of this test is to get all these untested functions
   2822     // to be referenced to avoid regression so it is okay to skip some targets.
   2823     if (comptime std.Target.current.cpu.arch.ptrBitWidth() == 64) {
   2824         std.testing.refAllDecls(@This());
   2825         std.testing.refAllDecls(Builder);
   2826 
   2827         inline for (std.meta.declarations(@This())) |decl|
   2828             if (comptime mem.endsWith(u8, decl.name, "Step"))
   2829                 std.testing.refAllDecls(decl.data.Type);
   2830     }
   2831 }