zig

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

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