zig

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

blob 1bdd97b2 (118131B) - Raw


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