zig

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

blob a364982c (50691B) - Raw


      1 const std = @import("std");
      2 const builtin = std.builtin;
      3 const tests = @import("test/tests.zig");
      4 const BufMap = std.BufMap;
      5 const mem = std.mem;
      6 const ArrayList = std.ArrayList;
      7 const io = std.io;
      8 const fs = std.fs;
      9 const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
     10 const assert = std.debug.assert;
     11 
     12 const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 14, .patch = 0 };
     13 const stack_size = 32 * 1024 * 1024;
     14 
     15 pub fn build(b: *std.Build) !void {
     16     const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
     17     const target = t: {
     18         var default_target: std.Target.Query = .{};
     19         default_target.ofmt = b.option(std.Target.ObjectFormat, "ofmt", "Object format to target") orelse if (only_c) .c else null;
     20         break :t b.standardTargetOptions(.{ .default_target = default_target });
     21     };
     22 
     23     const optimize = b.standardOptimizeOption(.{});
     24 
     25     const flat = b.option(bool, "flat", "Put files into the installation prefix in a manner suited for upstream distribution rather than a posix file system hierarchy standard") orelse false;
     26     const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode");
     27     const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false;
     28 
     29     const test_step = b.step("test", "Run all the tests");
     30     const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files and langref to installation prefix. Useful for development") orelse false;
     31     const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;
     32     const std_docs = b.option(bool, "std-docs", "include standard library autodocs") orelse false;
     33     const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;
     34     const enable_tidy = b.option(bool, "enable-tidy", "Check langref output HTML validity") orelse false;
     35 
     36     const langref_file = generateLangRef(b);
     37     const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html");
     38     const check_langref = tidyCheck(b, langref_file);
     39     if (enable_tidy) install_langref.step.dependOn(check_langref);
     40     // Checking autodocs is disabled because tidy gives a false positive:
     41     // line 304 column 9 - Warning: moved <style> tag to <head>! fix-style-tags: no to avoid.
     42     // I noticed that `--show-warnings no` still incorrectly causes exit code 1.
     43     // I was unable to find an alternative to tidy.
     44     //const check_autodocs = tidyCheck(b, b.path("lib/docs/index.html"));
     45     if (enable_tidy) {
     46         test_step.dependOn(check_langref);
     47         //test_step.dependOn(check_autodocs);
     48     }
     49     if (!skip_install_langref) {
     50         b.getInstallStep().dependOn(&install_langref.step);
     51     }
     52 
     53     const autodoc_test = b.addObject(.{
     54         .name = "std",
     55         .root_source_file = b.path("lib/std/std.zig"),
     56         .target = target,
     57         .zig_lib_dir = b.path("lib"),
     58         .optimize = .Debug,
     59     });
     60     const install_std_docs = b.addInstallDirectory(.{
     61         .source_dir = autodoc_test.getEmittedDocs(),
     62         .install_dir = .prefix,
     63         .install_subdir = "doc/std",
     64     });
     65     //if (enable_tidy) install_std_docs.step.dependOn(check_autodocs);
     66     if (std_docs) {
     67         b.getInstallStep().dependOn(&install_std_docs.step);
     68     }
     69 
     70     if (flat) {
     71         b.installFile("LICENSE", "LICENSE");
     72         b.installFile("README.md", "README.md");
     73     }
     74 
     75     const langref_step = b.step("langref", "Build and install the language reference");
     76     langref_step.dependOn(&install_langref.step);
     77 
     78     const std_docs_step = b.step("std-docs", "Build and install the standard library documentation");
     79     std_docs_step.dependOn(&install_std_docs.step);
     80 
     81     const docs_step = b.step("docs", "Build and install documentation");
     82     docs_step.dependOn(langref_step);
     83     docs_step.dependOn(std_docs_step);
     84 
     85     const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
     86     const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
     87     const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
     88     const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
     89     const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release;
     90     const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;
     91     const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
     92     const skip_single_threaded = b.option(bool, "skip-single-threaded", "Main test suite skips tests that are single-threaded") orelse false;
     93     const skip_translate_c = b.option(bool, "skip-translate-c", "Main test suite skips translate-c tests") orelse false;
     94     const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false;
     95 
     96     const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
     97 
     98     const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false;
     99     const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse static_llvm;
    100     const llvm_has_m68k = b.option(
    101         bool,
    102         "llvm-has-m68k",
    103         "Whether LLVM has the experimental target m68k enabled",
    104     ) orelse false;
    105     const llvm_has_csky = b.option(
    106         bool,
    107         "llvm-has-csky",
    108         "Whether LLVM has the experimental target csky enabled",
    109     ) orelse false;
    110     const llvm_has_arc = b.option(
    111         bool,
    112         "llvm-has-arc",
    113         "Whether LLVM has the experimental target arc enabled",
    114     ) orelse false;
    115     const llvm_has_xtensa = b.option(
    116         bool,
    117         "llvm-has-xtensa",
    118         "Whether LLVM has the experimental target xtensa enabled",
    119     ) orelse false;
    120     const enable_ios_sdk = b.option(bool, "enable-ios-sdk", "Run tests requiring presence of iOS SDK and frameworks") orelse false;
    121     const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse enable_ios_sdk;
    122     const enable_symlinks_windows = b.option(bool, "enable-symlinks-windows", "Run tests requiring presence of symlinks on Windows") orelse false;
    123     const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");
    124 
    125     if (!skip_install_lib_files) {
    126         b.installDirectory(.{
    127             .source_dir = b.path("lib"),
    128             .install_dir = if (flat) .prefix else .lib,
    129             .install_subdir = if (flat) "lib" else "zig",
    130             .exclude_extensions = &[_][]const u8{
    131                 // exclude files from lib/std/compress/testdata
    132                 ".gz",
    133                 ".z.0",
    134                 ".z.9",
    135                 ".zst.3",
    136                 ".zst.19",
    137                 "rfc1951.txt",
    138                 "rfc1952.txt",
    139                 "rfc8478.txt",
    140                 // exclude files from lib/std/compress/flate/testdata
    141                 ".expect",
    142                 ".expect-noinput",
    143                 ".golden",
    144                 ".input",
    145                 "compress-e.txt",
    146                 "compress-gettysburg.txt",
    147                 "compress-pi.txt",
    148                 "rfc1951.txt",
    149                 // exclude files from lib/std/compress/lzma/testdata
    150                 ".lzma",
    151                 // exclude files from lib/std/compress/xz/testdata
    152                 ".xz",
    153                 // exclude files from lib/std/tz/
    154                 ".tzif",
    155                 // exclude files from lib/std/tar/testdata
    156                 ".tar",
    157                 // others
    158                 "README.md",
    159             },
    160             .blank_extensions = &[_][]const u8{
    161                 "test.zig",
    162             },
    163         });
    164     }
    165 
    166     if (only_install_lib_files)
    167         return;
    168 
    169     const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS");
    170     const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
    171     const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
    172     const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
    173     const force_gpa = b.option(bool, "force-gpa", "Force the compiler to use GeneralPurposeAllocator") orelse false;
    174     const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
    175     const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
    176     const strip = b.option(bool, "strip", "Omit debug information");
    177     const pie = b.option(bool, "pie", "Produce a Position Independent Executable");
    178     const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false;
    179 
    180     const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: {
    181         if (strip == true) break :blk @as(u32, 0);
    182         if (optimize != .Debug) break :blk 0;
    183         break :blk 4;
    184     };
    185 
    186     const exe = addCompilerStep(b, .{
    187         .optimize = optimize,
    188         .target = target,
    189         .strip = strip,
    190         .sanitize_thread = sanitize_thread,
    191         .single_threaded = single_threaded,
    192     });
    193     exe.pie = pie;
    194     exe.entitlements = entitlements;
    195 
    196     exe.build_id = b.option(
    197         std.zig.BuildId,
    198         "build-id",
    199         "Request creation of '.note.gnu.build-id' section",
    200     );
    201 
    202     if (no_bin) {
    203         b.getInstallStep().dependOn(&exe.step);
    204     } else {
    205         const install_exe = b.addInstallArtifact(exe, .{
    206             .dest_dir = if (flat) .{ .override = .prefix } else .default,
    207         });
    208         b.getInstallStep().dependOn(&install_exe.step);
    209     }
    210 
    211     test_step.dependOn(&exe.step);
    212 
    213     if (target.result.os.tag == .windows and target.result.abi == .gnu) {
    214         // LTO is currently broken on mingw, this can be removed when it's fixed.
    215         exe.want_lto = false;
    216     }
    217 
    218     const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
    219     exe.use_llvm = use_llvm;
    220     exe.use_lld = use_llvm;
    221 
    222     const exe_options = b.addOptions();
    223     exe.root_module.addOptions("build_options", exe_options);
    224 
    225     exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
    226     exe_options.addOption(bool, "skip_non_native", skip_non_native);
    227     exe_options.addOption(bool, "have_llvm", enable_llvm);
    228     exe_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k);
    229     exe_options.addOption(bool, "llvm_has_csky", llvm_has_csky);
    230     exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc);
    231     exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
    232     exe_options.addOption(bool, "force_gpa", force_gpa);
    233     exe_options.addOption(bool, "only_c", only_c);
    234     exe_options.addOption(bool, "only_core_functionality", only_c);
    235 
    236     if (link_libc) {
    237         exe.linkLibC();
    238     }
    239 
    240     const is_debug = optimize == .Debug;
    241     const enable_debug_extensions = b.option(bool, "debug-extensions", "Enable commands and options useful for debugging the compiler") orelse is_debug;
    242     const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
    243     const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
    244 
    245     const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
    246     const version_slice = if (opt_version_string) |version| version else v: {
    247         if (!std.process.can_spawn) {
    248             std.debug.print("error: version info cannot be retrieved from git. Zig version must be provided using -Dversion-string\n", .{});
    249             std.process.exit(1);
    250         }
    251         const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch });
    252 
    253         var code: u8 = undefined;
    254         const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{
    255             "git",
    256             "-C",
    257             b.build_root.path orelse ".",
    258             "describe",
    259             "--match",
    260             "*.*.*",
    261             "--tags",
    262             "--abbrev=9",
    263         }, &code, .Ignore) catch {
    264             break :v version_string;
    265         };
    266         const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r");
    267 
    268         switch (mem.count(u8, git_describe, "-")) {
    269             0 => {
    270                 // Tagged release version (e.g. 0.10.0).
    271                 if (!mem.eql(u8, git_describe, version_string)) {
    272                     std.debug.print("Zig version '{s}' does not match Git tag '{s}'\n", .{ version_string, git_describe });
    273                     std.process.exit(1);
    274                 }
    275                 break :v version_string;
    276             },
    277             2 => {
    278                 // Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
    279                 var it = mem.splitScalar(u8, git_describe, '-');
    280                 const tagged_ancestor = it.first();
    281                 const commit_height = it.next().?;
    282                 const commit_id = it.next().?;
    283 
    284                 const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
    285                 if (zig_version.order(ancestor_ver) != .gt) {
    286                     std.debug.print("Zig version '{}' must be greater than tagged ancestor '{}'\n", .{ zig_version, ancestor_ver });
    287                     std.process.exit(1);
    288                 }
    289 
    290                 // Check that the commit hash is prefixed with a 'g' (a Git convention).
    291                 if (commit_id.len < 1 or commit_id[0] != 'g') {
    292                     std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
    293                     break :v version_string;
    294                 }
    295 
    296                 // The version is reformatted in accordance with the https://semver.org specification.
    297                 break :v b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] });
    298             },
    299             else => {
    300                 std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
    301                 break :v version_string;
    302             },
    303         }
    304     };
    305     const version = try b.allocator.dupeZ(u8, version_slice);
    306     exe_options.addOption([:0]const u8, "version", version);
    307 
    308     if (enable_llvm) {
    309         const cmake_cfg = if (static_llvm) null else blk: {
    310             if (findConfigH(b, config_h_path_option)) |config_h_path| {
    311                 const file_contents = fs.cwd().readFileAlloc(b.allocator, config_h_path, max_config_h_bytes) catch unreachable;
    312                 break :blk parseConfigH(b, file_contents);
    313             } else {
    314                 std.log.warn("config.h could not be located automatically. Consider providing it explicitly via \"-Dconfig_h\"", .{});
    315                 break :blk null;
    316             }
    317         };
    318 
    319         if (cmake_cfg) |cfg| {
    320             // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD.
    321             // That means we also have to rely on stage1 compiled c++ files. We parse config.h to find
    322             // the information passed on to us from cmake.
    323             if (cfg.cmake_prefix_path.len > 0) {
    324                 var it = mem.tokenizeScalar(u8, cfg.cmake_prefix_path, ';');
    325                 while (it.next()) |path| {
    326                     b.addSearchPrefix(path);
    327                 }
    328             }
    329 
    330             try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);
    331         } else {
    332             // Here we are -Denable-llvm but no cmake integration.
    333             try addStaticLlvmOptionsToExe(exe);
    334         }
    335         if (target.result.os.tag == .windows) {
    336             // LLVM depends on networking as of version 18.
    337             exe.linkSystemLibrary("ws2_32");
    338 
    339             exe.linkSystemLibrary("version");
    340             exe.linkSystemLibrary("uuid");
    341             exe.linkSystemLibrary("ole32");
    342         }
    343     }
    344 
    345     const semver = try std.SemanticVersion.parse(version);
    346     exe_options.addOption(std.SemanticVersion, "semver", semver);
    347 
    348     exe_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
    349     exe_options.addOption(bool, "enable_logging", enable_logging);
    350     exe_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
    351     exe_options.addOption(bool, "enable_tracy", tracy != null);
    352     exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
    353     exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
    354     exe_options.addOption(bool, "value_tracing", value_tracing);
    355     if (tracy) |tracy_path| {
    356         const client_cpp = b.pathJoin(
    357             &[_][]const u8{ tracy_path, "public", "TracyClient.cpp" },
    358         );
    359 
    360         // On mingw, we need to opt into windows 7+ to get some features required by tracy.
    361         const tracy_c_flags: []const []const u8 = if (target.result.os.tag == .windows and target.result.abi == .gnu)
    362             &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined", "-D_WIN32_WINNT=0x601" }
    363         else
    364             &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" };
    365 
    366         exe.addIncludePath(.{ .cwd_relative = tracy_path });
    367         exe.addCSourceFile(.{ .file = .{ .cwd_relative = client_cpp }, .flags = tracy_c_flags });
    368         if (!enable_llvm) {
    369             exe.root_module.linkSystemLibrary("c++", .{ .use_pkg_config = .no });
    370         }
    371         exe.linkLibC();
    372 
    373         if (target.result.os.tag == .windows) {
    374             exe.linkSystemLibrary("dbghelp");
    375             exe.linkSystemLibrary("ws2_32");
    376         }
    377     }
    378 
    379     const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match any filter") orelse &[0][]const u8{};
    380 
    381     const test_cases_options = b.addOptions();
    382 
    383     test_cases_options.addOption(bool, "enable_tracy", false);
    384     test_cases_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
    385     test_cases_options.addOption(bool, "enable_logging", enable_logging);
    386     test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
    387     test_cases_options.addOption(bool, "skip_non_native", skip_non_native);
    388     test_cases_options.addOption(bool, "have_llvm", enable_llvm);
    389     test_cases_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k);
    390     test_cases_options.addOption(bool, "llvm_has_csky", llvm_has_csky);
    391     test_cases_options.addOption(bool, "llvm_has_arc", llvm_has_arc);
    392     test_cases_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
    393     test_cases_options.addOption(bool, "force_gpa", force_gpa);
    394     test_cases_options.addOption(bool, "only_c", only_c);
    395     test_cases_options.addOption(bool, "only_core_functionality", true);
    396     test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu);
    397     test_cases_options.addOption(bool, "enable_wine", b.enable_wine);
    398     test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime);
    399     test_cases_options.addOption(bool, "enable_rosetta", b.enable_rosetta);
    400     test_cases_options.addOption(bool, "enable_darling", b.enable_darling);
    401     test_cases_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2);
    402     test_cases_options.addOption(bool, "value_tracing", value_tracing);
    403     test_cases_options.addOption(?[]const u8, "glibc_runtimes_dir", b.glibc_runtimes_dir);
    404     test_cases_options.addOption([:0]const u8, "version", version);
    405     test_cases_options.addOption(std.SemanticVersion, "semver", semver);
    406     test_cases_options.addOption([]const []const u8, "test_filters", test_filters);
    407 
    408     var chosen_opt_modes_buf: [4]builtin.OptimizeMode = undefined;
    409     var chosen_mode_index: usize = 0;
    410     if (!skip_debug) {
    411         chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.Debug;
    412         chosen_mode_index += 1;
    413     }
    414     if (!skip_release_safe) {
    415         chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSafe;
    416         chosen_mode_index += 1;
    417     }
    418     if (!skip_release_fast) {
    419         chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseFast;
    420         chosen_mode_index += 1;
    421     }
    422     if (!skip_release_small) {
    423         chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSmall;
    424         chosen_mode_index += 1;
    425     }
    426     const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
    427 
    428     const fmt_include_paths = &.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" };
    429     const fmt_exclude_paths = &.{"test/cases"};
    430     const do_fmt = b.addFmt(.{
    431         .paths = fmt_include_paths,
    432         .exclude_paths = fmt_exclude_paths,
    433     });
    434     b.step("fmt", "Modify source files in place to have conforming formatting").dependOn(&do_fmt.step);
    435 
    436     const check_fmt = b.step("test-fmt", "Check source files having conforming formatting");
    437     check_fmt.dependOn(&b.addFmt(.{
    438         .paths = fmt_include_paths,
    439         .exclude_paths = fmt_exclude_paths,
    440         .check = true,
    441     }).step);
    442     test_step.dependOn(check_fmt);
    443 
    444     const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
    445     try tests.addCases(b, test_cases_step, test_filters, target, .{
    446         .skip_translate_c = skip_translate_c,
    447         .skip_run_translated_c = skip_run_translated_c,
    448     }, .{
    449         .enable_llvm = enable_llvm,
    450         .llvm_has_m68k = llvm_has_m68k,
    451         .llvm_has_csky = llvm_has_csky,
    452         .llvm_has_arc = llvm_has_arc,
    453         .llvm_has_xtensa = llvm_has_xtensa,
    454     });
    455     test_step.dependOn(test_cases_step);
    456 
    457     test_step.dependOn(tests.addModuleTests(b, .{
    458         .test_filters = test_filters,
    459         .root_src = "test/behavior.zig",
    460         .name = "behavior",
    461         .desc = "Run the behavior tests",
    462         .optimize_modes = optimization_modes,
    463         .include_paths = &.{},
    464         .skip_single_threaded = skip_single_threaded,
    465         .skip_non_native = skip_non_native,
    466         .skip_libc = skip_libc,
    467         .max_rss = 1 * 1024 * 1024 * 1024,
    468     }));
    469 
    470     test_step.dependOn(tests.addModuleTests(b, .{
    471         .test_filters = test_filters,
    472         .root_src = "test/c_import.zig",
    473         .name = "c-import",
    474         .desc = "Run the @cImport tests",
    475         .optimize_modes = optimization_modes,
    476         .include_paths = &.{"test/c_import"},
    477         .skip_single_threaded = true,
    478         .skip_non_native = skip_non_native,
    479         .skip_libc = skip_libc,
    480     }));
    481 
    482     test_step.dependOn(tests.addModuleTests(b, .{
    483         .test_filters = test_filters,
    484         .root_src = "lib/compiler_rt.zig",
    485         .name = "compiler-rt",
    486         .desc = "Run the compiler_rt tests",
    487         .optimize_modes = optimization_modes,
    488         .include_paths = &.{},
    489         .skip_single_threaded = true,
    490         .skip_non_native = skip_non_native,
    491         .skip_libc = true,
    492         .no_builtin = true,
    493     }));
    494 
    495     test_step.dependOn(tests.addModuleTests(b, .{
    496         .test_filters = test_filters,
    497         .root_src = "lib/c.zig",
    498         .name = "universal-libc",
    499         .desc = "Run the universal libc tests",
    500         .optimize_modes = optimization_modes,
    501         .include_paths = &.{},
    502         .skip_single_threaded = true,
    503         .skip_non_native = skip_non_native,
    504         .skip_libc = true,
    505         .no_builtin = true,
    506     }));
    507 
    508     test_step.dependOn(tests.addCompareOutputTests(b, test_filters, optimization_modes));
    509     test_step.dependOn(tests.addStandaloneTests(
    510         b,
    511         optimization_modes,
    512         enable_macos_sdk,
    513         enable_ios_sdk,
    514         enable_symlinks_windows,
    515     ));
    516     test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
    517     test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows));
    518     test_step.dependOn(tests.addStackTraceTests(b, test_filters, optimization_modes));
    519     test_step.dependOn(tests.addCliTests(b));
    520     test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filters, optimization_modes));
    521     test_step.dependOn(tests.addModuleTests(b, .{
    522         .test_filters = test_filters,
    523         .root_src = "lib/std/std.zig",
    524         .name = "std",
    525         .desc = "Run the standard library tests",
    526         .optimize_modes = optimization_modes,
    527         .include_paths = &.{},
    528         .skip_single_threaded = skip_single_threaded,
    529         .skip_non_native = skip_non_native,
    530         .skip_libc = skip_libc,
    531         // I observed a value of 4572626944 on the M2 CI.
    532         .max_rss = 5029889638,
    533     }));
    534 
    535     try addWasiUpdateStep(b, version);
    536 
    537     const update_mingw_step = b.step("update-mingw", "Update zig's bundled mingw");
    538     const opt_mingw_src_path = b.option([]const u8, "mingw-src", "path to mingw-w64 source directory");
    539     const update_mingw_exe = b.addExecutable(.{
    540         .name = "update_mingw",
    541         .target = b.graph.host,
    542         .root_source_file = b.path("tools/update_mingw.zig"),
    543     });
    544     const update_mingw_run = b.addRunArtifact(update_mingw_exe);
    545     update_mingw_run.addDirectoryArg(b.path("lib"));
    546     if (opt_mingw_src_path) |mingw_src_path| {
    547         update_mingw_run.addDirectoryArg(.{ .cwd_relative = mingw_src_path });
    548     } else {
    549         // Intentionally cause an error if this build step is requested.
    550         update_mingw_run.addArg("--missing-mingw-source-directory");
    551     }
    552 
    553     update_mingw_step.dependOn(&update_mingw_run.step);
    554 }
    555 
    556 fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
    557     const semver = try std.SemanticVersion.parse(version);
    558 
    559     var target_query: std.Target.Query = .{
    560         .cpu_arch = .wasm32,
    561         .os_tag = .wasi,
    562     };
    563     target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory));
    564 
    565     const exe = addCompilerStep(b, .{
    566         .optimize = .ReleaseSmall,
    567         .target = b.resolveTargetQuery(target_query),
    568     });
    569 
    570     const exe_options = b.addOptions();
    571     exe.root_module.addOptions("build_options", exe_options);
    572 
    573     exe_options.addOption(u32, "mem_leak_frames", 0);
    574     exe_options.addOption(bool, "have_llvm", false);
    575     exe_options.addOption(bool, "force_gpa", false);
    576     exe_options.addOption(bool, "only_c", true);
    577     exe_options.addOption([:0]const u8, "version", version);
    578     exe_options.addOption(std.SemanticVersion, "semver", semver);
    579     exe_options.addOption(bool, "enable_debug_extensions", false);
    580     exe_options.addOption(bool, "enable_logging", false);
    581     exe_options.addOption(bool, "enable_link_snapshots", false);
    582     exe_options.addOption(bool, "enable_tracy", false);
    583     exe_options.addOption(bool, "enable_tracy_callstack", false);
    584     exe_options.addOption(bool, "enable_tracy_allocation", false);
    585     exe_options.addOption(bool, "value_tracing", false);
    586     exe_options.addOption(bool, "only_core_functionality", true);
    587 
    588     const run_opt = b.addSystemCommand(&.{
    589         "wasm-opt",
    590         "-Oz",
    591         "--enable-bulk-memory",
    592         "--enable-sign-ext",
    593     });
    594     run_opt.addArtifactArg(exe);
    595     run_opt.addArg("-o");
    596     run_opt.addFileArg(b.path("stage1/zig1.wasm"));
    597 
    598     const copy_zig_h = b.addUpdateSourceFiles();
    599     copy_zig_h.addCopyFileToSource(b.path("lib/zig.h"), "stage1/zig.h");
    600 
    601     const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm");
    602     update_zig1_step.dependOn(&run_opt.step);
    603     update_zig1_step.dependOn(&copy_zig_h.step);
    604 }
    605 
    606 const AddCompilerStepOptions = struct {
    607     optimize: std.builtin.OptimizeMode,
    608     target: std.Build.ResolvedTarget,
    609     strip: ?bool = null,
    610     sanitize_thread: ?bool = null,
    611     single_threaded: ?bool = null,
    612 };
    613 
    614 fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.Step.Compile {
    615     const exe = b.addExecutable(.{
    616         .name = "zig",
    617         .root_source_file = b.path("src/main.zig"),
    618         .target = options.target,
    619         .optimize = options.optimize,
    620         .max_rss = 7_000_000_000,
    621         .strip = options.strip,
    622         .sanitize_thread = options.sanitize_thread,
    623         .single_threaded = options.single_threaded,
    624     });
    625     exe.stack_size = stack_size;
    626 
    627     const aro_module = b.createModule(.{
    628         .root_source_file = b.path("lib/compiler/aro/aro.zig"),
    629     });
    630 
    631     const aro_translate_c_module = b.createModule(.{
    632         .root_source_file = b.path("lib/compiler/aro_translate_c.zig"),
    633         .imports = &.{
    634             .{
    635                 .name = "aro",
    636                 .module = aro_module,
    637             },
    638         },
    639     });
    640 
    641     exe.root_module.addImport("aro", aro_module);
    642     exe.root_module.addImport("aro_translate_c", aro_translate_c_module);
    643     return exe;
    644 }
    645 
    646 const exe_cflags = [_][]const u8{
    647     "-std=c++17",
    648     "-D__STDC_CONSTANT_MACROS",
    649     "-D__STDC_FORMAT_MACROS",
    650     "-D__STDC_LIMIT_MACROS",
    651     "-D_GNU_SOURCE",
    652     "-fno-exceptions",
    653     "-fno-rtti",
    654     "-fno-stack-protector",
    655     "-fvisibility-inlines-hidden",
    656     "-Wno-type-limits",
    657     "-Wno-missing-braces",
    658     "-Wno-comment",
    659 };
    660 
    661 fn addCmakeCfgOptionsToExe(
    662     b: *std.Build,
    663     cfg: CMakeConfig,
    664     exe: *std.Build.Step.Compile,
    665     use_zig_libcxx: bool,
    666 ) !void {
    667     if (exe.rootModuleTarget().isDarwin()) {
    668         // useful for package maintainers
    669         exe.headerpad_max_install_names = true;
    670     }
    671     exe.addObjectFile(.{ .cwd_relative = b.pathJoin(&[_][]const u8{
    672         cfg.cmake_binary_dir,
    673         "zigcpp",
    674         b.fmt("{s}{s}{s}", .{
    675             cfg.cmake_static_library_prefix,
    676             "zigcpp",
    677             cfg.cmake_static_library_suffix,
    678         }),
    679     }) });
    680     assert(cfg.lld_include_dir.len != 0);
    681     exe.addIncludePath(.{ .cwd_relative = cfg.lld_include_dir });
    682     exe.addIncludePath(.{ .cwd_relative = cfg.llvm_include_dir });
    683     exe.addLibraryPath(.{ .cwd_relative = cfg.llvm_lib_dir });
    684     addCMakeLibraryList(exe, cfg.clang_libraries);
    685     addCMakeLibraryList(exe, cfg.lld_libraries);
    686     addCMakeLibraryList(exe, cfg.llvm_libraries);
    687 
    688     if (use_zig_libcxx) {
    689         exe.linkLibCpp();
    690     } else {
    691         // System -lc++ must be used because in this code path we are attempting to link
    692         // against system-provided LLVM, Clang, LLD.
    693         const need_cpp_includes = true;
    694         const static = cfg.llvm_linkage == .static;
    695         const lib_suffix = if (static) exe.rootModuleTarget().staticLibSuffix()[1..] else exe.rootModuleTarget().dynamicLibSuffix()[1..];
    696         switch (exe.rootModuleTarget().os.tag) {
    697             .linux => {
    698                 // First we try to link against the detected libcxx name. If that doesn't work, we fall
    699                 // back to -lc++ and cross our fingers.
    700                 addCxxKnownPath(b, cfg, exe, b.fmt("lib{s}.{s}", .{ cfg.system_libcxx, lib_suffix }), "", need_cpp_includes) catch |err| switch (err) {
    701                     error.RequiredLibraryNotFound => {
    702                         exe.linkLibCpp();
    703                     },
    704                     else => |e| return e,
    705                 };
    706                 exe.linkSystemLibrary("unwind");
    707             },
    708             .ios, .macos, .watchos, .tvos, .visionos => {
    709                 exe.linkLibCpp();
    710             },
    711             .windows => {
    712                 if (exe.rootModuleTarget().abi != .msvc) exe.linkLibCpp();
    713             },
    714             .freebsd => {
    715                 if (static) {
    716                     try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
    717                     try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
    718                 } else {
    719                     try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
    720                 }
    721             },
    722             .openbsd => {
    723                 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
    724                 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{lib_suffix}), null, need_cpp_includes);
    725             },
    726             .netbsd, .dragonfly => {
    727                 if (static) {
    728                     try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
    729                     try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
    730                 } else {
    731                     try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
    732                 }
    733             },
    734             .solaris, .illumos => {
    735                 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
    736                 try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
    737             },
    738             .haiku => {
    739                 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
    740             },
    741             else => {},
    742         }
    743     }
    744 
    745     if (cfg.dia_guids_lib.len != 0) {
    746         exe.addObjectFile(.{ .cwd_relative = cfg.dia_guids_lib });
    747     }
    748 }
    749 
    750 fn addStaticLlvmOptionsToExe(exe: *std.Build.Step.Compile) !void {
    751     // Adds the Zig C++ sources which both stage1 and stage2 need.
    752     //
    753     // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
    754     // in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
    755     // unavailable when LLVM is compiled in Release mode.
    756     const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
    757     exe.addCSourceFiles(.{
    758         .files = &zig_cpp_sources,
    759         .flags = &zig_cpp_cflags,
    760     });
    761 
    762     for (clang_libs) |lib_name| {
    763         exe.linkSystemLibrary(lib_name);
    764     }
    765 
    766     for (lld_libs) |lib_name| {
    767         exe.linkSystemLibrary(lib_name);
    768     }
    769 
    770     for (llvm_libs) |lib_name| {
    771         exe.linkSystemLibrary(lib_name);
    772     }
    773 
    774     exe.linkSystemLibrary("z");
    775     exe.linkSystemLibrary("zstd");
    776 
    777     if (exe.rootModuleTarget().os.tag != .windows or exe.rootModuleTarget().abi != .msvc) {
    778         // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
    779         exe.linkSystemLibrary("c++");
    780     }
    781 
    782     if (exe.rootModuleTarget().os.tag == .windows) {
    783         exe.linkSystemLibrary("version");
    784         exe.linkSystemLibrary("uuid");
    785         exe.linkSystemLibrary("ole32");
    786     }
    787 }
    788 
    789 fn addCxxKnownPath(
    790     b: *std.Build,
    791     ctx: CMakeConfig,
    792     exe: *std.Build.Step.Compile,
    793     objname: []const u8,
    794     errtxt: ?[]const u8,
    795     need_cpp_includes: bool,
    796 ) !void {
    797     if (!std.process.can_spawn)
    798         return error.RequiredLibraryNotFound;
    799 
    800     const path_padded = run: {
    801         var args = std.ArrayList([]const u8).init(b.allocator);
    802         try args.append(ctx.cxx_compiler);
    803         var it = std.mem.tokenizeAny(u8, ctx.cxx_compiler_arg1, &std.ascii.whitespace);
    804         while (it.next()) |arg| try args.append(arg);
    805         try args.append(b.fmt("-print-file-name={s}", .{objname}));
    806         break :run b.run(args.items);
    807     };
    808     var tokenizer = mem.tokenizeAny(u8, path_padded, "\r\n");
    809     const path_unpadded = tokenizer.next().?;
    810     if (mem.eql(u8, path_unpadded, objname)) {
    811         if (errtxt) |msg| {
    812             std.debug.print("{s}", .{msg});
    813         } else {
    814             std.debug.print("Unable to determine path to {s}\n", .{objname});
    815         }
    816         return error.RequiredLibraryNotFound;
    817     }
    818     exe.addObjectFile(.{ .cwd_relative = path_unpadded });
    819 
    820     // TODO a way to integrate with system c++ include files here
    821     // c++ -E -Wp,-v -xc++ /dev/null
    822     if (need_cpp_includes) {
    823         // I used these temporarily for testing something but we obviously need a
    824         // more general purpose solution here.
    825         //exe.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0");
    826         //exe.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0/x86_64-unknown-linux-gnu");
    827     }
    828 }
    829 
    830 fn addCMakeLibraryList(exe: *std.Build.Step.Compile, list: []const u8) void {
    831     var it = mem.tokenizeScalar(u8, list, ';');
    832     while (it.next()) |lib| {
    833         if (mem.startsWith(u8, lib, "-l")) {
    834             exe.linkSystemLibrary(lib["-l".len..]);
    835         } else if (exe.rootModuleTarget().os.tag == .windows and
    836             mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib))
    837         {
    838             exe.linkSystemLibrary(lib[0 .. lib.len - ".lib".len]);
    839         } else {
    840             exe.addObjectFile(.{ .cwd_relative = lib });
    841         }
    842     }
    843 }
    844 
    845 const CMakeConfig = struct {
    846     llvm_linkage: std.builtin.LinkMode,
    847     cmake_binary_dir: []const u8,
    848     cmake_prefix_path: []const u8,
    849     cmake_static_library_prefix: []const u8,
    850     cmake_static_library_suffix: []const u8,
    851     cxx_compiler: []const u8,
    852     cxx_compiler_arg1: []const u8,
    853     lld_include_dir: []const u8,
    854     lld_libraries: []const u8,
    855     clang_libraries: []const u8,
    856     llvm_lib_dir: []const u8,
    857     llvm_include_dir: []const u8,
    858     llvm_libraries: []const u8,
    859     dia_guids_lib: []const u8,
    860     system_libcxx: []const u8,
    861 };
    862 
    863 const max_config_h_bytes = 1 * 1024 * 1024;
    864 
    865 fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
    866     if (config_h_path_option) |path| {
    867         var config_h_or_err = fs.cwd().openFile(path, .{});
    868         if (config_h_or_err) |*file| {
    869             file.close();
    870             return path;
    871         } else |_| {
    872             std.log.err("Could not open provided config.h: \"{s}\"", .{path});
    873             std.process.exit(1);
    874         }
    875     }
    876 
    877     var check_dir = fs.path.dirname(b.graph.zig_exe).?;
    878     while (true) {
    879         var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
    880         defer dir.close();
    881 
    882         // Check if config.h is present in dir
    883         var config_h_or_err = dir.openFile("config.h", .{});
    884         if (config_h_or_err) |*file| {
    885             file.close();
    886             return fs.path.join(
    887                 b.allocator,
    888                 &[_][]const u8{ check_dir, "config.h" },
    889             ) catch unreachable;
    890         } else |e| switch (e) {
    891             error.FileNotFound => {},
    892             else => unreachable,
    893         }
    894 
    895         // Check if we reached the source root by looking for .git, and bail if so
    896         var git_dir_or_err = dir.openDir(".git", .{});
    897         if (git_dir_or_err) |*git_dir| {
    898             git_dir.close();
    899             return null;
    900         } else |_| {}
    901 
    902         // Otherwise, continue search in the parent directory
    903         const new_check_dir = fs.path.dirname(check_dir);
    904         if (new_check_dir == null or mem.eql(u8, new_check_dir.?, check_dir)) {
    905             return null;
    906         }
    907         check_dir = new_check_dir.?;
    908     }
    909 }
    910 
    911 fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
    912     var ctx: CMakeConfig = .{
    913         .llvm_linkage = undefined,
    914         .cmake_binary_dir = undefined,
    915         .cmake_prefix_path = undefined,
    916         .cmake_static_library_prefix = undefined,
    917         .cmake_static_library_suffix = undefined,
    918         .cxx_compiler = undefined,
    919         .cxx_compiler_arg1 = "",
    920         .lld_include_dir = undefined,
    921         .lld_libraries = undefined,
    922         .clang_libraries = undefined,
    923         .llvm_lib_dir = undefined,
    924         .llvm_include_dir = undefined,
    925         .llvm_libraries = undefined,
    926         .dia_guids_lib = undefined,
    927         .system_libcxx = undefined,
    928     };
    929 
    930     const mappings = [_]struct { prefix: []const u8, field: []const u8 }{
    931         .{
    932             .prefix = "#define ZIG_CMAKE_BINARY_DIR ",
    933             .field = "cmake_binary_dir",
    934         },
    935         .{
    936             .prefix = "#define ZIG_CMAKE_PREFIX_PATH ",
    937             .field = "cmake_prefix_path",
    938         },
    939         .{
    940             .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_PREFIX ",
    941             .field = "cmake_static_library_prefix",
    942         },
    943         .{
    944             .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_SUFFIX ",
    945             .field = "cmake_static_library_suffix",
    946         },
    947         .{
    948             .prefix = "#define ZIG_CXX_COMPILER ",
    949             .field = "cxx_compiler",
    950         },
    951         .{
    952             .prefix = "#define ZIG_CXX_COMPILER_ARG1 ",
    953             .field = "cxx_compiler_arg1",
    954         },
    955         .{
    956             .prefix = "#define ZIG_LLD_INCLUDE_PATH ",
    957             .field = "lld_include_dir",
    958         },
    959         .{
    960             .prefix = "#define ZIG_LLD_LIBRARIES ",
    961             .field = "lld_libraries",
    962         },
    963         .{
    964             .prefix = "#define ZIG_CLANG_LIBRARIES ",
    965             .field = "clang_libraries",
    966         },
    967         .{
    968             .prefix = "#define ZIG_LLVM_LIBRARIES ",
    969             .field = "llvm_libraries",
    970         },
    971         .{
    972             .prefix = "#define ZIG_DIA_GUIDS_LIB ",
    973             .field = "dia_guids_lib",
    974         },
    975         .{
    976             .prefix = "#define ZIG_LLVM_INCLUDE_PATH ",
    977             .field = "llvm_include_dir",
    978         },
    979         .{
    980             .prefix = "#define ZIG_LLVM_LIB_PATH ",
    981             .field = "llvm_lib_dir",
    982         },
    983         .{
    984             .prefix = "#define ZIG_SYSTEM_LIBCXX",
    985             .field = "system_libcxx",
    986         },
    987         // .prefix = ZIG_LLVM_LINK_MODE parsed manually below
    988     };
    989 
    990     var lines_it = mem.tokenizeAny(u8, config_h_text, "\r\n");
    991     while (lines_it.next()) |line| {
    992         inline for (mappings) |mapping| {
    993             if (mem.startsWith(u8, line, mapping.prefix)) {
    994                 var it = mem.splitScalar(u8, line, '"');
    995                 _ = it.first(); // skip the stuff before the quote
    996                 const quoted = it.next().?; // the stuff inside the quote
    997                 const trimmed = mem.trim(u8, quoted, " ");
    998                 @field(ctx, mapping.field) = toNativePathSep(b, trimmed);
    999             }
   1000         }
   1001         if (mem.startsWith(u8, line, "#define ZIG_LLVM_LINK_MODE ")) {
   1002             var it = mem.splitScalar(u8, line, '"');
   1003             _ = it.next().?; // skip the stuff before the quote
   1004             const quoted = it.next().?; // the stuff inside the quote
   1005             ctx.llvm_linkage = if (mem.eql(u8, quoted, "shared")) .dynamic else .static;
   1006         }
   1007     }
   1008     return ctx;
   1009 }
   1010 
   1011 fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
   1012     const duplicated = b.allocator.dupe(u8, s) catch unreachable;
   1013     for (duplicated) |*byte| switch (byte.*) {
   1014         '/' => byte.* = fs.path.sep,
   1015         else => {},
   1016     };
   1017     return duplicated;
   1018 }
   1019 
   1020 const zig_cpp_sources = [_][]const u8{
   1021     // These are planned to stay even when we are self-hosted.
   1022     "src/zig_llvm.cpp",
   1023     "src/zig_clang.cpp",
   1024     "src/zig_llvm-ar.cpp",
   1025     "src/zig_clang_driver.cpp",
   1026     "src/zig_clang_cc1_main.cpp",
   1027     "src/zig_clang_cc1as_main.cpp",
   1028 };
   1029 
   1030 const clang_libs = [_][]const u8{
   1031     "clangFrontendTool",
   1032     "clangCodeGen",
   1033     "clangFrontend",
   1034     "clangDriver",
   1035     "clangSerialization",
   1036     "clangSema",
   1037     "clangStaticAnalyzerFrontend",
   1038     "clangStaticAnalyzerCheckers",
   1039     "clangStaticAnalyzerCore",
   1040     "clangAnalysis",
   1041     "clangASTMatchers",
   1042     "clangAST",
   1043     "clangParse",
   1044     "clangSema",
   1045     "clangAPINotes",
   1046     "clangBasic",
   1047     "clangEdit",
   1048     "clangLex",
   1049     "clangARCMigrate",
   1050     "clangRewriteFrontend",
   1051     "clangRewrite",
   1052     "clangCrossTU",
   1053     "clangIndex",
   1054     "clangToolingCore",
   1055     "clangExtractAPI",
   1056     "clangSupport",
   1057 };
   1058 const lld_libs = [_][]const u8{
   1059     "lldMinGW",
   1060     "lldELF",
   1061     "lldCOFF",
   1062     "lldWasm",
   1063     "lldMachO",
   1064     "lldCommon",
   1065 };
   1066 // This list can be re-generated with `llvm-config --libfiles` and then
   1067 // reformatting using your favorite text editor. Note we do not execute
   1068 // `llvm-config` here because we are cross compiling. Also omit LLVMTableGen
   1069 // from these libs.
   1070 const llvm_libs = [_][]const u8{
   1071     "LLVMWindowsManifest",
   1072     "LLVMXRay",
   1073     "LLVMLibDriver",
   1074     "LLVMDlltoolDriver",
   1075     "LLVMTextAPIBinaryReader",
   1076     "LLVMCoverage",
   1077     "LLVMLineEditor",
   1078     "LLVMXCoreDisassembler",
   1079     "LLVMXCoreCodeGen",
   1080     "LLVMXCoreDesc",
   1081     "LLVMXCoreInfo",
   1082     "LLVMX86TargetMCA",
   1083     "LLVMX86Disassembler",
   1084     "LLVMX86AsmParser",
   1085     "LLVMX86CodeGen",
   1086     "LLVMX86Desc",
   1087     "LLVMX86Info",
   1088     "LLVMWebAssemblyDisassembler",
   1089     "LLVMWebAssemblyAsmParser",
   1090     "LLVMWebAssemblyCodeGen",
   1091     "LLVMWebAssemblyUtils",
   1092     "LLVMWebAssemblyDesc",
   1093     "LLVMWebAssemblyInfo",
   1094     "LLVMVEDisassembler",
   1095     "LLVMVEAsmParser",
   1096     "LLVMVECodeGen",
   1097     "LLVMVEDesc",
   1098     "LLVMVEInfo",
   1099     "LLVMSystemZDisassembler",
   1100     "LLVMSystemZAsmParser",
   1101     "LLVMSystemZCodeGen",
   1102     "LLVMSystemZDesc",
   1103     "LLVMSystemZInfo",
   1104     "LLVMSparcDisassembler",
   1105     "LLVMSparcAsmParser",
   1106     "LLVMSparcCodeGen",
   1107     "LLVMSparcDesc",
   1108     "LLVMSparcInfo",
   1109     "LLVMRISCVTargetMCA",
   1110     "LLVMRISCVDisassembler",
   1111     "LLVMRISCVAsmParser",
   1112     "LLVMRISCVCodeGen",
   1113     "LLVMRISCVDesc",
   1114     "LLVMRISCVInfo",
   1115     "LLVMPowerPCDisassembler",
   1116     "LLVMPowerPCAsmParser",
   1117     "LLVMPowerPCCodeGen",
   1118     "LLVMPowerPCDesc",
   1119     "LLVMPowerPCInfo",
   1120     "LLVMNVPTXCodeGen",
   1121     "LLVMNVPTXDesc",
   1122     "LLVMNVPTXInfo",
   1123     "LLVMMSP430Disassembler",
   1124     "LLVMMSP430AsmParser",
   1125     "LLVMMSP430CodeGen",
   1126     "LLVMMSP430Desc",
   1127     "LLVMMSP430Info",
   1128     "LLVMMipsDisassembler",
   1129     "LLVMMipsAsmParser",
   1130     "LLVMMipsCodeGen",
   1131     "LLVMMipsDesc",
   1132     "LLVMMipsInfo",
   1133     "LLVMLoongArchDisassembler",
   1134     "LLVMLoongArchAsmParser",
   1135     "LLVMLoongArchCodeGen",
   1136     "LLVMLoongArchDesc",
   1137     "LLVMLoongArchInfo",
   1138     "LLVMLanaiDisassembler",
   1139     "LLVMLanaiCodeGen",
   1140     "LLVMLanaiAsmParser",
   1141     "LLVMLanaiDesc",
   1142     "LLVMLanaiInfo",
   1143     "LLVMHexagonDisassembler",
   1144     "LLVMHexagonCodeGen",
   1145     "LLVMHexagonAsmParser",
   1146     "LLVMHexagonDesc",
   1147     "LLVMHexagonInfo",
   1148     "LLVMBPFDisassembler",
   1149     "LLVMBPFAsmParser",
   1150     "LLVMBPFCodeGen",
   1151     "LLVMBPFDesc",
   1152     "LLVMBPFInfo",
   1153     "LLVMAVRDisassembler",
   1154     "LLVMAVRAsmParser",
   1155     "LLVMAVRCodeGen",
   1156     "LLVMAVRDesc",
   1157     "LLVMAVRInfo",
   1158     "LLVMARMDisassembler",
   1159     "LLVMARMAsmParser",
   1160     "LLVMARMCodeGen",
   1161     "LLVMARMDesc",
   1162     "LLVMARMUtils",
   1163     "LLVMARMInfo",
   1164     "LLVMAMDGPUTargetMCA",
   1165     "LLVMAMDGPUDisassembler",
   1166     "LLVMAMDGPUAsmParser",
   1167     "LLVMAMDGPUCodeGen",
   1168     "LLVMAMDGPUDesc",
   1169     "LLVMAMDGPUUtils",
   1170     "LLVMAMDGPUInfo",
   1171     "LLVMAArch64Disassembler",
   1172     "LLVMAArch64AsmParser",
   1173     "LLVMAArch64CodeGen",
   1174     "LLVMAArch64Desc",
   1175     "LLVMAArch64Utils",
   1176     "LLVMAArch64Info",
   1177     "LLVMOrcDebugging",
   1178     "LLVMOrcJIT",
   1179     "LLVMWindowsDriver",
   1180     "LLVMMCJIT",
   1181     "LLVMJITLink",
   1182     "LLVMInterpreter",
   1183     "LLVMExecutionEngine",
   1184     "LLVMRuntimeDyld",
   1185     "LLVMOrcTargetProcess",
   1186     "LLVMOrcShared",
   1187     "LLVMDWP",
   1188     "LLVMDebugInfoLogicalView",
   1189     "LLVMDebugInfoGSYM",
   1190     "LLVMOption",
   1191     "LLVMObjectYAML",
   1192     "LLVMObjCopy",
   1193     "LLVMMCA",
   1194     "LLVMMCDisassembler",
   1195     "LLVMLTO",
   1196     "LLVMPasses",
   1197     "LLVMHipStdPar",
   1198     "LLVMCFGuard",
   1199     "LLVMCoroutines",
   1200     "LLVMipo",
   1201     "LLVMVectorize",
   1202     "LLVMLinker",
   1203     "LLVMInstrumentation",
   1204     "LLVMFrontendOpenMP",
   1205     "LLVMFrontendOffloading",
   1206     "LLVMFrontendOpenACC",
   1207     "LLVMFrontendHLSL",
   1208     "LLVMFrontendDriver",
   1209     "LLVMExtensions",
   1210     "LLVMDWARFLinkerParallel",
   1211     "LLVMDWARFLinkerClassic",
   1212     "LLVMDWARFLinker",
   1213     "LLVMGlobalISel",
   1214     "LLVMMIRParser",
   1215     "LLVMAsmPrinter",
   1216     "LLVMSelectionDAG",
   1217     "LLVMCodeGen",
   1218     "LLVMTarget",
   1219     "LLVMObjCARCOpts",
   1220     "LLVMCodeGenTypes",
   1221     "LLVMIRPrinter",
   1222     "LLVMInterfaceStub",
   1223     "LLVMFileCheck",
   1224     "LLVMFuzzMutate",
   1225     "LLVMScalarOpts",
   1226     "LLVMInstCombine",
   1227     "LLVMAggressiveInstCombine",
   1228     "LLVMTransformUtils",
   1229     "LLVMBitWriter",
   1230     "LLVMAnalysis",
   1231     "LLVMProfileData",
   1232     "LLVMSymbolize",
   1233     "LLVMDebugInfoBTF",
   1234     "LLVMDebugInfoPDB",
   1235     "LLVMDebugInfoMSF",
   1236     "LLVMDebugInfoDWARF",
   1237     "LLVMObject",
   1238     "LLVMTextAPI",
   1239     "LLVMMCParser",
   1240     "LLVMIRReader",
   1241     "LLVMAsmParser",
   1242     "LLVMMC",
   1243     "LLVMDebugInfoCodeView",
   1244     "LLVMBitReader",
   1245     "LLVMFuzzerCLI",
   1246     "LLVMCore",
   1247     "LLVMRemarks",
   1248     "LLVMBitstreamReader",
   1249     "LLVMBinaryFormat",
   1250     "LLVMTargetParser",
   1251     "LLVMSupport",
   1252     "LLVMDemangle",
   1253 };
   1254 
   1255 fn generateLangRef(b: *std.Build) std.Build.LazyPath {
   1256     const doctest_exe = b.addExecutable(.{
   1257         .name = "doctest",
   1258         .root_source_file = b.path("tools/doctest.zig"),
   1259         .target = b.graph.host,
   1260         .optimize = .Debug,
   1261     });
   1262 
   1263     var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
   1264         std.debug.panic("unable to open 'doc/langref' directory: {s}", .{@errorName(err)});
   1265     };
   1266     defer dir.close();
   1267 
   1268     var wf = b.addWriteFiles();
   1269 
   1270     var it = dir.iterateAssumeFirstIteration();
   1271     while (it.next() catch @panic("failed to read dir")) |entry| {
   1272         if (std.mem.startsWith(u8, entry.name, ".") or entry.kind != .file)
   1273             continue;
   1274 
   1275         const out_basename = b.fmt("{s}.out", .{std.fs.path.stem(entry.name)});
   1276         const cmd = b.addRunArtifact(doctest_exe);
   1277         cmd.addArgs(&.{
   1278             "--zig",        b.graph.zig_exe,
   1279             // TODO: enhance doctest to use "--listen=-" rather than operating
   1280             // in a temporary directory
   1281             "--cache-root", b.cache_root.path orelse ".",
   1282         });
   1283         if (b.zig_lib_dir) |p| {
   1284             cmd.addArg("--zig-lib-dir");
   1285             cmd.addDirectoryArg(p);
   1286         }
   1287         cmd.addArgs(&.{"-i"});
   1288         cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name})));
   1289 
   1290         cmd.addArgs(&.{"-o"});
   1291         _ = wf.addCopyFile(cmd.addOutputFileArg(out_basename), out_basename);
   1292     }
   1293 
   1294     const docgen_exe = b.addExecutable(.{
   1295         .name = "docgen",
   1296         .root_source_file = b.path("tools/docgen.zig"),
   1297         .target = b.graph.host,
   1298         .optimize = .Debug,
   1299     });
   1300 
   1301     const docgen_cmd = b.addRunArtifact(docgen_exe);
   1302     docgen_cmd.addArgs(&.{"--code-dir"});
   1303     docgen_cmd.addDirectoryArg(wf.getDirectory());
   1304 
   1305     docgen_cmd.addFileArg(b.path("doc/langref.html.in"));
   1306     return docgen_cmd.addOutputFileArg("langref.html");
   1307 }
   1308 
   1309 fn tidyCheck(b: *std.Build, html_file: std.Build.LazyPath) *std.Build.Step {
   1310     const run_tidy = b.addSystemCommand(&.{
   1311         "tidy", "--drop-empty-elements", "no", "-qe",
   1312     });
   1313     run_tidy.addFileArg(html_file);
   1314     run_tidy.expectExitCode(0);
   1315     return &run_tidy.step;
   1316 }