zig

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

blob bfde2f52 (93196B) - Raw


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