zig

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

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