zig

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

build.zig (72270B) - 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 io = std.io;
      7 const fs = std.fs;
      8 const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
      9 const assert = std.debug.assert;
     10 const DevEnv = @import("src/dev.zig").Env;
     11 const ValueInterpretMode = enum { direct, by_name };
     12 const corpus = @import("stage0/corpus.zig");
     13 
     14 const zig0_headers = &[_][]const u8{
     15     "air.h",
     16     "ast.h",
     17     "astgen.h",
     18     "common.h",
     19     "compilation.h",
     20     "intern_pool.h",
     21     "parser.h",
     22     "sema.h",
     23     "type.h",
     24     "value.h",
     25     "verbose_air.h",
     26     "verbose_intern_pool.h",
     27     "wyhash.h",
     28     "zcu.h",
     29     "zcu_per_thread.h",
     30     "zir.h",
     31 };
     32 const zig0_c_lib_files = &[_][]const u8{
     33     "air.c",
     34     "ast.c",
     35     "astgen.c",
     36     "intern_pool.c",
     37     "parser.c",
     38     "sema.c",
     39     "tokenizer.c",
     40     "type.c",
     41     "value.c",
     42     "verbose_air.c",
     43     "verbose_intern_pool.c",
     44     "wyhash.c",
     45     "zig0.c",
     46     "zcu.c",
     47     "zcu_per_thread.c",
     48     "zir.c",
     49 };
     50 const zig0_all_c_files = zig0_c_lib_files ++ &[_][]const u8{"main.c"};
     51 const zig0_cflags = &[_][]const u8{
     52     "-std=c99",
     53     "-pedantic",
     54     "-Wall",
     55     "-Wvla",
     56     "-Wextra",
     57     "-Werror",
     58     "-Wshadow",
     59     "-Wswitch",
     60     "-Walloca",
     61     "-Wformat=2",
     62     "-fno-common",
     63     "-Wconversion",
     64     "-Wuninitialized",
     65     "-Wdouble-promotion",
     66     "-fstack-protector-all",
     67     "-Wimplicit-fallthrough",
     68     "-Wno-unused-function",
     69 };
     70 const zig0_compilers = &[_][]const u8{ "zig", "clang", "gcc", "tcc" };
     71 
     72 const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 15, .patch = 3 };
     73 const stack_size = 46 * 1024 * 1024;
     74 
     75 pub fn build(b: *std.Build) !void {
     76     const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
     77     const target = b.standardTargetOptions(.{
     78         .default_target = .{
     79             .ofmt = if (only_c) .c else null,
     80         },
     81     });
     82     const optimize = b.standardOptimizeOption(.{});
     83 
     84     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;
     85     const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode");
     86     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;
     87 
     88     const test_step = b.step("test", "Run all the tests");
     89     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;
     90     const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;
     91     const std_docs = b.option(bool, "std-docs", "include standard library autodocs") orelse false;
     92     const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;
     93     const enable_superhtml = b.option(bool, "enable-superhtml", "Check langref output HTML validity") orelse false;
     94 
     95     const langref_file = generateLangRef(b);
     96     const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html");
     97     const check_langref = superHtmlCheck(b, langref_file);
     98     if (enable_superhtml) install_langref.step.dependOn(check_langref);
     99 
    100     const check_autodocs = superHtmlCheck(b, b.path("lib/docs/index.html"));
    101     if (enable_superhtml) {
    102         test_step.dependOn(check_langref);
    103         test_step.dependOn(check_autodocs);
    104     }
    105     if (!skip_install_langref) {
    106         b.getInstallStep().dependOn(&install_langref.step);
    107     }
    108 
    109     const autodoc_test = b.addObject(.{
    110         .name = "std",
    111         .zig_lib_dir = b.path("lib"),
    112         .root_module = b.createModule(.{
    113             .root_source_file = b.path("lib/std/std.zig"),
    114             .target = target,
    115             .optimize = .Debug,
    116         }),
    117     });
    118     const install_std_docs = b.addInstallDirectory(.{
    119         .source_dir = autodoc_test.getEmittedDocs(),
    120         .install_dir = .prefix,
    121         .install_subdir = "doc/std",
    122     });
    123     //if (enable_tidy) install_std_docs.step.dependOn(check_autodocs);
    124     if (std_docs) {
    125         b.getInstallStep().dependOn(&install_std_docs.step);
    126     }
    127 
    128     if (flat) {
    129         b.installFile("LICENSE", "LICENSE");
    130         b.installFile("README.md", "README.md");
    131     }
    132 
    133     const langref_step = b.step("langref", "Build and install the language reference");
    134     langref_step.dependOn(&install_langref.step);
    135 
    136     const std_docs_step = b.step("std-docs", "Build and install the standard library documentation");
    137     std_docs_step.dependOn(&install_std_docs.step);
    138 
    139     const docs_step = b.step("docs", "Build and install documentation");
    140     docs_step.dependOn(langref_step);
    141     docs_step.dependOn(std_docs_step);
    142 
    143     const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
    144     const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
    145     const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
    146     const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
    147     const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release;
    148     const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;
    149     const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
    150     const skip_single_threaded = b.option(bool, "skip-single-threaded", "Main test suite skips tests that are single-threaded") orelse false;
    151     const skip_compile_errors = b.option(bool, "skip-compile-errors", "Main test suite skips compile error tests") orelse false;
    152     const skip_translate_c = b.option(bool, "skip-translate-c", "Main test suite skips translate-c tests") orelse false;
    153     const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false;
    154     const skip_freebsd = b.option(bool, "skip-freebsd", "Main test suite skips targets with freebsd OS") orelse false;
    155     const skip_netbsd = b.option(bool, "skip-netbsd", "Main test suite skips targets with netbsd OS") orelse false;
    156     const skip_windows = b.option(bool, "skip-windows", "Main test suite skips targets with windows OS") orelse false;
    157     const skip_macos = b.option(bool, "skip-macos", "Main test suite skips targets with macos OS") orelse false;
    158     const skip_linux = b.option(bool, "skip-linux", "Main test suite skips targets with linux OS") orelse false;
    159     const skip_llvm = b.option(bool, "skip-llvm", "Main test suite skips targets that use LLVM backend") orelse false;
    160 
    161     const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
    162 
    163     const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false;
    164     const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse static_llvm;
    165     const llvm_has_m68k = b.option(
    166         bool,
    167         "llvm-has-m68k",
    168         "Whether LLVM has the experimental target m68k enabled",
    169     ) orelse false;
    170     const llvm_has_csky = b.option(
    171         bool,
    172         "llvm-has-csky",
    173         "Whether LLVM has the experimental target csky enabled",
    174     ) orelse false;
    175     const llvm_has_arc = b.option(
    176         bool,
    177         "llvm-has-arc",
    178         "Whether LLVM has the experimental target arc enabled",
    179     ) orelse false;
    180     const llvm_has_xtensa = b.option(
    181         bool,
    182         "llvm-has-xtensa",
    183         "Whether LLVM has the experimental target xtensa enabled",
    184     ) orelse false;
    185     const enable_ios_sdk = b.option(bool, "enable-ios-sdk", "Run tests requiring presence of iOS SDK and frameworks") orelse false;
    186     const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse enable_ios_sdk;
    187     const enable_symlinks_windows = b.option(bool, "enable-symlinks-windows", "Run tests requiring presence of symlinks on Windows") orelse false;
    188     const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");
    189 
    190     if (!skip_install_lib_files) {
    191         b.installDirectory(.{
    192             .source_dir = b.path("lib"),
    193             .install_dir = if (flat) .prefix else .lib,
    194             .install_subdir = if (flat) "lib" else "zig",
    195             .exclude_extensions = &[_][]const u8{
    196                 // exclude files from lib/std/compress/testdata
    197                 ".gz",
    198                 ".z.0",
    199                 ".z.9",
    200                 ".zst.3",
    201                 ".zst.19",
    202                 "rfc1951.txt",
    203                 "rfc1952.txt",
    204                 "rfc8478.txt",
    205                 // exclude files from lib/std/compress/flate/testdata
    206                 ".expect",
    207                 ".expect-noinput",
    208                 ".golden",
    209                 ".input",
    210                 "compress-e.txt",
    211                 "compress-gettysburg.txt",
    212                 "compress-pi.txt",
    213                 "rfc1951.txt",
    214                 // exclude files from lib/std/compress/lzma/testdata
    215                 ".lzma",
    216                 // exclude files from lib/std/compress/xz/testdata
    217                 ".xz",
    218                 // exclude files from lib/std/tz/
    219                 ".tzif",
    220                 // exclude files from lib/std/tar/testdata
    221                 ".tar",
    222                 // others
    223                 "README.md",
    224             },
    225             .blank_extensions = &[_][]const u8{
    226                 "test.zig",
    227             },
    228         });
    229     }
    230 
    231     if (only_install_lib_files)
    232         return;
    233 
    234     const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS");
    235     const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
    236     const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
    237     const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
    238     const tracy_callstack_depth: u32 = b.option(u32, "tracy-callstack-depth", "Declare callstack depth for Tracy data. Does nothing if -Dtracy_callstack is not provided") orelse 10;
    239     const debug_gpa = b.option(bool, "debug-allocator", "Force the compiler to use DebugAllocator") orelse false;
    240     const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
    241     const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
    242     const strip = b.option(bool, "strip", "Omit debug information");
    243     const valgrind = b.option(bool, "valgrind", "Enable valgrind integration");
    244     const pie = b.option(bool, "pie", "Produce a Position Independent Executable");
    245     const value_interpret_mode = b.option(ValueInterpretMode, "value-interpret-mode", "How the compiler translates between 'std.builtin' types and its internal datastructures") orelse .direct;
    246     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;
    247 
    248     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: {
    249         if (strip == true) break :blk @as(u32, 0);
    250         if (optimize != .Debug) break :blk 0;
    251         break :blk 4;
    252     };
    253 
    254     const exe = addCompilerStep(b, .{
    255         .optimize = optimize,
    256         .target = target,
    257         .strip = strip,
    258         .valgrind = valgrind,
    259         .sanitize_thread = sanitize_thread,
    260         .single_threaded = single_threaded,
    261     });
    262     exe.pie = pie;
    263     exe.entitlements = entitlements;
    264 
    265     const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
    266     exe.use_llvm = use_llvm;
    267     exe.use_lld = use_llvm;
    268 
    269     if (no_bin) {
    270         b.getInstallStep().dependOn(&exe.step);
    271     } else {
    272         const install_exe = b.addInstallArtifact(exe, .{
    273             .dest_dir = if (flat) .{ .override = .prefix } else .default,
    274         });
    275         b.getInstallStep().dependOn(&install_exe.step);
    276     }
    277 
    278     test_step.dependOn(&exe.step);
    279 
    280     const exe_options = b.addOptions();
    281     exe.root_module.addOptions("build_options", exe_options);
    282 
    283     exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
    284     exe_options.addOption(bool, "skip_non_native", skip_non_native);
    285     exe_options.addOption(bool, "have_llvm", enable_llvm);
    286     exe_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k);
    287     exe_options.addOption(bool, "llvm_has_csky", llvm_has_csky);
    288     exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc);
    289     exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
    290     exe_options.addOption(bool, "debug_gpa", debug_gpa);
    291     const dev_mode: DevEnv = b.option(DevEnv, "dev", "Build a compiler with a reduced feature set for development of specific features") orelse if (only_c) .bootstrap else .full;
    292     exe_options.addOption(DevEnv, "dev", dev_mode);
    293     exe_options.addOption(ValueInterpretMode, "value_interpret_mode", value_interpret_mode);
    294 
    295     if (link_libc) {
    296         exe.root_module.link_libc = true;
    297     }
    298 
    299     const is_debug = optimize == .Debug;
    300     const enable_debug_extensions = b.option(bool, "debug-extensions", "Enable commands and options useful for debugging the compiler") orelse is_debug;
    301     const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
    302     const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
    303 
    304     const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
    305     const version_slice = if (opt_version_string) |version| version else v: {
    306         if (!std.process.can_spawn) {
    307             std.debug.print("error: version info cannot be retrieved from git. Zig version must be provided using -Dversion-string\n", .{});
    308             std.process.exit(1);
    309         }
    310         const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch });
    311 
    312         var code: u8 = undefined;
    313         const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{
    314             "git",
    315             "-C", b.build_root.path orelse ".", // affects the --git-dir argument
    316             "--git-dir", ".git", // affected by the -C argument
    317             "describe", "--match",    "*.*.*", //
    318             "--tags",   "--abbrev=9",
    319         }, &code, .Ignore) catch {
    320             break :v version_string;
    321         };
    322         const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r");
    323 
    324         switch (mem.count(u8, git_describe, "-")) {
    325             0 => {
    326                 // Tagged release version (e.g. 0.10.0).
    327                 if (!mem.eql(u8, git_describe, version_string)) {
    328                     std.debug.print("Zig version '{s}' does not match Git tag '{s}'\n", .{ version_string, git_describe });
    329                     std.process.exit(1);
    330                 }
    331                 break :v version_string;
    332             },
    333             2 => {
    334                 // Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
    335                 var it = mem.splitScalar(u8, git_describe, '-');
    336                 const tagged_ancestor = it.first();
    337                 const commit_height = it.next().?;
    338                 const commit_id = it.next().?;
    339 
    340                 const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
    341                 if (zig_version.order(ancestor_ver) != .gt) {
    342                     std.debug.print("Zig version '{f}' must be greater than tagged ancestor '{f}'\n", .{ zig_version, ancestor_ver });
    343                     std.process.exit(1);
    344                 }
    345 
    346                 // Check that the commit hash is prefixed with a 'g' (a Git convention).
    347                 if (commit_id.len < 1 or commit_id[0] != 'g') {
    348                     std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
    349                     break :v version_string;
    350                 }
    351 
    352                 // The version is reformatted in accordance with the https://semver.org specification.
    353                 break :v b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] });
    354             },
    355             else => {
    356                 std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
    357                 break :v version_string;
    358             },
    359         }
    360     };
    361     const version = try b.allocator.dupeZ(u8, version_slice);
    362     exe_options.addOption([:0]const u8, "version", version);
    363 
    364     if (enable_llvm) {
    365         const cmake_cfg = if (static_llvm) null else blk: {
    366             if (findConfigH(b, config_h_path_option)) |config_h_path| {
    367                 const file_contents = fs.cwd().readFileAlloc(b.allocator, config_h_path, max_config_h_bytes) catch unreachable;
    368                 break :blk parseConfigH(b, file_contents);
    369             } else {
    370                 std.log.warn("config.h could not be located automatically. Consider providing it explicitly via \"-Dconfig_h\"", .{});
    371                 break :blk null;
    372             }
    373         };
    374 
    375         if (cmake_cfg) |cfg| {
    376             // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD.
    377             // That means we also have to rely on stage1 compiled c++ files. We parse config.h to find
    378             // the information passed on to us from cmake.
    379             if (cfg.cmake_prefix_path.len > 0) {
    380                 var it = mem.tokenizeScalar(u8, cfg.cmake_prefix_path, ';');
    381                 while (it.next()) |path| {
    382                     b.addSearchPrefix(path);
    383                 }
    384             }
    385 
    386             try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);
    387         } else {
    388             // Here we are -Denable-llvm but no cmake integration.
    389             try addStaticLlvmOptionsToModule(exe.root_module, .{
    390                 .llvm_has_m68k = llvm_has_m68k,
    391                 .llvm_has_csky = llvm_has_csky,
    392                 .llvm_has_arc = llvm_has_arc,
    393                 .llvm_has_xtensa = llvm_has_xtensa,
    394             });
    395         }
    396         if (target.result.os.tag == .windows) {
    397             // LLVM depends on networking as of version 18.
    398             exe.root_module.linkSystemLibrary("ws2_32", .{});
    399 
    400             exe.root_module.linkSystemLibrary("version", .{});
    401             exe.root_module.linkSystemLibrary("uuid", .{});
    402             exe.root_module.linkSystemLibrary("ole32", .{});
    403         }
    404     }
    405 
    406     const semver = try std.SemanticVersion.parse(version);
    407     exe_options.addOption(std.SemanticVersion, "semver", semver);
    408 
    409     exe_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
    410     exe_options.addOption(bool, "enable_logging", enable_logging);
    411     exe_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
    412     exe_options.addOption(bool, "enable_tracy", tracy != null);
    413     exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
    414     exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
    415     exe_options.addOption(u32, "tracy_callstack_depth", tracy_callstack_depth);
    416     exe_options.addOption(bool, "value_tracing", value_tracing);
    417     if (tracy) |tracy_path| {
    418         const client_cpp = b.pathJoin(
    419             &[_][]const u8{ tracy_path, "public", "TracyClient.cpp" },
    420         );
    421 
    422         const tracy_c_flags: []const []const u8 = &.{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" };
    423 
    424         exe.root_module.addIncludePath(.{ .cwd_relative = tracy_path });
    425         exe.root_module.addCSourceFile(.{ .file = .{ .cwd_relative = client_cpp }, .flags = tracy_c_flags });
    426         if (!enable_llvm) {
    427             exe.root_module.linkSystemLibrary("c++", .{ .use_pkg_config = .no });
    428         }
    429         exe.root_module.link_libc = true;
    430 
    431         if (target.result.os.tag == .windows) {
    432             exe.root_module.linkSystemLibrary("dbghelp", .{});
    433             exe.root_module.linkSystemLibrary("ws2_32", .{});
    434         }
    435     }
    436 
    437     const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match any filter") orelse &[0][]const u8{};
    438     const test_target_filters = b.option([]const []const u8, "test-target-filter", "Skip tests whose target triple do not match any filter") orelse &[0][]const u8{};
    439     const test_extra_targets = b.option(bool, "test-extra-targets", "Enable running module tests for additional targets") orelse false;
    440 
    441     var chosen_opt_modes_buf: [4]builtin.OptimizeMode = undefined;
    442     var chosen_mode_index: usize = 0;
    443     if (!skip_debug) {
    444         chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.Debug;
    445         chosen_mode_index += 1;
    446     }
    447     if (!skip_release_safe) {
    448         chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSafe;
    449         chosen_mode_index += 1;
    450     }
    451     if (!skip_release_fast) {
    452         chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseFast;
    453         chosen_mode_index += 1;
    454     }
    455     if (!skip_release_small) {
    456         chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSmall;
    457         chosen_mode_index += 1;
    458     }
    459     const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
    460 
    461     const fmt_include_paths = &.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" };
    462     const fmt_exclude_paths = &.{ "test/cases", "test/behavior/zon" };
    463     const do_fmt = b.addFmt(.{
    464         .paths = fmt_include_paths,
    465         .exclude_paths = fmt_exclude_paths,
    466     });
    467     b.step("fmt", "Modify source files in place to have conforming formatting").dependOn(&do_fmt.step);
    468 
    469     const check_fmt = b.step("test-fmt", "Check source files having conforming formatting");
    470     check_fmt.dependOn(&b.addFmt(.{
    471         .paths = fmt_include_paths,
    472         .exclude_paths = fmt_exclude_paths,
    473         .check = true,
    474     }).step);
    475     test_step.dependOn(check_fmt);
    476 
    477     const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
    478     try tests.addCases(b, test_cases_step, target, .{
    479         .test_filters = test_filters,
    480         .test_target_filters = test_target_filters,
    481         .skip_compile_errors = skip_compile_errors,
    482         .skip_non_native = skip_non_native,
    483         .skip_freebsd = skip_freebsd,
    484         .skip_netbsd = skip_netbsd,
    485         .skip_windows = skip_windows,
    486         .skip_macos = skip_macos,
    487         .skip_linux = skip_linux,
    488         .skip_llvm = skip_llvm,
    489         .skip_libc = skip_libc,
    490     }, .{
    491         .skip_translate_c = skip_translate_c,
    492         .skip_run_translated_c = skip_run_translated_c,
    493     }, .{
    494         .enable_llvm = enable_llvm,
    495         .llvm_has_m68k = llvm_has_m68k,
    496         .llvm_has_csky = llvm_has_csky,
    497         .llvm_has_arc = llvm_has_arc,
    498         .llvm_has_xtensa = llvm_has_xtensa,
    499     });
    500     test_step.dependOn(test_cases_step);
    501 
    502     const test_modules_step = b.step("test-modules", "Run the per-target module tests");
    503     test_step.dependOn(test_modules_step);
    504 
    505     test_modules_step.dependOn(tests.addModuleTests(b, .{
    506         .test_filters = test_filters,
    507         .test_target_filters = test_target_filters,
    508         .test_extra_targets = test_extra_targets,
    509         .root_src = "test/behavior.zig",
    510         .name = "behavior",
    511         .desc = "Run the behavior tests",
    512         .optimize_modes = optimization_modes,
    513         .include_paths = &.{},
    514         .skip_single_threaded = skip_single_threaded,
    515         .skip_non_native = skip_non_native,
    516         .skip_freebsd = skip_freebsd,
    517         .skip_netbsd = skip_netbsd,
    518         .skip_windows = skip_windows,
    519         .skip_macos = skip_macos,
    520         .skip_linux = skip_linux,
    521         .skip_llvm = skip_llvm,
    522         .skip_libc = skip_libc,
    523         // 3888779264 was observed on an x86_64-linux-gnu host.
    524         .max_rss = 4000000000,
    525     }));
    526 
    527     test_modules_step.dependOn(tests.addModuleTests(b, .{
    528         .test_filters = test_filters,
    529         .test_target_filters = test_target_filters,
    530         .test_extra_targets = test_extra_targets,
    531         .root_src = "test/c_import.zig",
    532         .name = "c-import",
    533         .desc = "Run the @cImport tests",
    534         .optimize_modes = optimization_modes,
    535         .include_paths = &.{"test/c_import"},
    536         .skip_single_threaded = true,
    537         .skip_non_native = skip_non_native,
    538         .skip_freebsd = skip_freebsd,
    539         .skip_netbsd = skip_netbsd,
    540         .skip_windows = skip_windows,
    541         .skip_macos = skip_macos,
    542         .skip_linux = skip_linux,
    543         .skip_llvm = skip_llvm,
    544         .skip_libc = skip_libc,
    545     }));
    546 
    547     test_modules_step.dependOn(tests.addModuleTests(b, .{
    548         .test_filters = test_filters,
    549         .test_target_filters = test_target_filters,
    550         .test_extra_targets = test_extra_targets,
    551         .root_src = "lib/compiler_rt.zig",
    552         .name = "compiler-rt",
    553         .desc = "Run the compiler_rt tests",
    554         .optimize_modes = optimization_modes,
    555         .include_paths = &.{},
    556         .skip_single_threaded = true,
    557         .skip_non_native = skip_non_native,
    558         .skip_freebsd = skip_freebsd,
    559         .skip_netbsd = skip_netbsd,
    560         .skip_windows = skip_windows,
    561         .skip_macos = skip_macos,
    562         .skip_linux = skip_linux,
    563         .skip_llvm = skip_llvm,
    564         .skip_libc = true,
    565         .no_builtin = true,
    566     }));
    567 
    568     test_modules_step.dependOn(tests.addModuleTests(b, .{
    569         .test_filters = test_filters,
    570         .test_target_filters = test_target_filters,
    571         .test_extra_targets = test_extra_targets,
    572         .root_src = "lib/c.zig",
    573         .name = "zigc",
    574         .desc = "Run the zigc tests",
    575         .optimize_modes = optimization_modes,
    576         .include_paths = &.{},
    577         .skip_single_threaded = true,
    578         .skip_non_native = skip_non_native,
    579         .skip_freebsd = skip_freebsd,
    580         .skip_netbsd = skip_netbsd,
    581         .skip_windows = skip_windows,
    582         .skip_macos = skip_macos,
    583         .skip_linux = skip_linux,
    584         .skip_llvm = skip_llvm,
    585         .skip_libc = true,
    586         .no_builtin = true,
    587     }));
    588 
    589     test_modules_step.dependOn(tests.addModuleTests(b, .{
    590         .test_filters = test_filters,
    591         .test_target_filters = test_target_filters,
    592         .test_extra_targets = test_extra_targets,
    593         .root_src = "lib/std/std.zig",
    594         .name = "std",
    595         .desc = "Run the standard library tests",
    596         .optimize_modes = optimization_modes,
    597         .include_paths = &.{},
    598         .skip_single_threaded = skip_single_threaded,
    599         .skip_non_native = skip_non_native,
    600         .skip_freebsd = skip_freebsd,
    601         .skip_netbsd = skip_netbsd,
    602         .skip_windows = skip_windows,
    603         .skip_macos = skip_macos,
    604         .skip_linux = skip_linux,
    605         .skip_llvm = skip_llvm,
    606         .skip_libc = skip_libc,
    607         // I observed a value of 5605064704 on the M2 CI.
    608         .max_rss = 6165571174,
    609     }));
    610 
    611     const unit_tests_step = b.step("test-unit", "Run the compiler source unit tests");
    612     test_step.dependOn(unit_tests_step);
    613 
    614     const unit_tests = b.addTest(.{
    615         .root_module = addCompilerMod(b, .{
    616             .optimize = optimize,
    617             .target = target,
    618             .single_threaded = single_threaded,
    619         }),
    620         .filters = test_filters,
    621         .use_llvm = use_llvm,
    622         .use_lld = use_llvm,
    623         .zig_lib_dir = b.path("lib"),
    624     });
    625     if (link_libc) {
    626         unit_tests.root_module.link_libc = true;
    627     }
    628     unit_tests.root_module.addOptions("build_options", exe_options);
    629     unit_tests_step.dependOn(&b.addRunArtifact(unit_tests).step);
    630 
    631     test_step.dependOn(tests.addCompareOutputTests(b, test_filters, optimization_modes));
    632     test_step.dependOn(tests.addStandaloneTests(
    633         b,
    634         optimization_modes,
    635         enable_macos_sdk,
    636         enable_ios_sdk,
    637         enable_symlinks_windows,
    638     ));
    639     test_step.dependOn(tests.addCAbiTests(b, .{
    640         .test_target_filters = test_target_filters,
    641         .skip_non_native = skip_non_native,
    642         .skip_freebsd = skip_freebsd,
    643         .skip_netbsd = skip_netbsd,
    644         .skip_windows = skip_windows,
    645         .skip_macos = skip_macos,
    646         .skip_linux = skip_linux,
    647         .skip_llvm = skip_llvm,
    648         .skip_release = skip_release,
    649     }));
    650     test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows));
    651     test_step.dependOn(tests.addStackTraceTests(b, test_filters, optimization_modes));
    652     test_step.dependOn(tests.addCliTests(b));
    653     test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filters, optimization_modes));
    654     if (tests.addDebuggerTests(b, .{
    655         .test_filters = test_filters,
    656         .test_target_filters = test_target_filters,
    657         .gdb = b.option([]const u8, "gdb", "path to gdb binary"),
    658         .lldb = b.option([]const u8, "lldb", "path to lldb binary"),
    659         .optimize_modes = optimization_modes,
    660         .skip_single_threaded = skip_single_threaded,
    661         .skip_libc = skip_libc,
    662     })) |test_debugger_step| test_step.dependOn(test_debugger_step);
    663     if (tests.addLlvmIrTests(b, .{
    664         .enable_llvm = enable_llvm,
    665         .test_filters = test_filters,
    666         .test_target_filters = test_target_filters,
    667     })) |test_llvm_ir_step| test_step.dependOn(test_llvm_ir_step);
    668 
    669     try addWasiUpdateStep(b, version);
    670 
    671     const update_mingw_step = b.step("update-mingw", "Update zig's bundled mingw");
    672     const opt_mingw_src_path = b.option([]const u8, "mingw-src", "path to mingw-w64 source directory");
    673     if (opt_mingw_src_path) |mingw_src_path| {
    674         const update_mingw_exe = b.addExecutable(.{
    675             .name = "update_mingw",
    676             .root_module = b.createModule(.{
    677                 .target = b.graph.host,
    678                 .root_source_file = b.path("tools/update_mingw.zig"),
    679             }),
    680         });
    681         const update_mingw_run = b.addRunArtifact(update_mingw_exe);
    682         update_mingw_run.addDirectoryArg(b.path("lib"));
    683         update_mingw_run.addDirectoryArg(.{ .cwd_relative = mingw_src_path });
    684 
    685         update_mingw_step.dependOn(&update_mingw_run.step);
    686     } else {
    687         update_mingw_step.dependOn(&b.addFail("The -Dmingw-src=... option is required for this step").step);
    688     }
    689 
    690     const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases");
    691     try tests.addIncrementalTests(b, test_incremental_step);
    692     test_step.dependOn(test_incremental_step);
    693 
    694     // zig0 (C implementation) build steps
    695     const zig0_cc = b.option([]const u8, "zig0-cc", "C compiler for zig0 tests") orelse "zig";
    696     const zig0_no_exec = b.option(bool, "zig0-no-exec", "Compile zig0 test binary without running it") orelse false;
    697     const zig0_test_timeout = b.option([]const u8, "zig0-test-timeout", "Test execution timeout for zig0");
    698     const zig0_valgrind = valgrind orelse false;
    699 
    700     const zig0_target = blk: {
    701         var query = target.query;
    702         const arch = query.cpu_arch orelse @import("builtin").cpu.arch;
    703         if (arch == .x86_64) {
    704             // Valgrind doesn't support AVX-512 instructions (EVEX prefix).
    705             // Subtract all AVX-512 features so the zig CC won't emit them.
    706             const F = std.Target.x86.Feature;
    707             const avx512_features = [_]F{
    708                 .avx512f,            .avx512bw,
    709                 .avx512cd,           .avx512dq,
    710                 .avx512vl,           .avx512bf16,
    711                 .avx512bitalg,       .avx512er,
    712                 .avx512fp16,         .avx512ifma,
    713                 .avx512pf,           .avx512vbmi,
    714                 .avx512vbmi2,        .avx512vnni,
    715                 .avx512vp2intersect, .avx512vpopcntdq,
    716                 .evex512,
    717             };
    718             for (avx512_features) |f|
    719                 query.cpu_features_sub.addFeature(@intFromEnum(f));
    720         }
    721         break :blk b.resolveTargetQuery(query);
    722     };
    723 
    724     // Separate build_options for zig0 tests with a fixed version string, so
    725     // that the verbose_dumper cache is not invalidated on every git commit.
    726     const zig0_airgen_options = b.addOptions();
    727     zig0_airgen_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
    728     zig0_airgen_options.addOption(bool, "skip_non_native", skip_non_native);
    729     // air_gen only needs source->AIR (no codegen), so disable LLVM.
    730     zig0_airgen_options.addOption(bool, "have_llvm", false);
    731     zig0_airgen_options.addOption(bool, "llvm_has_m68k", false);
    732     zig0_airgen_options.addOption(bool, "llvm_has_csky", false);
    733     zig0_airgen_options.addOption(bool, "llvm_has_arc", false);
    734     zig0_airgen_options.addOption(bool, "llvm_has_xtensa", false);
    735     zig0_airgen_options.addOption(bool, "debug_gpa", debug_gpa);
    736     zig0_airgen_options.addOption(DevEnv, "dev", .full);
    737     zig0_airgen_options.addOption(ValueInterpretMode, "value_interpret_mode", value_interpret_mode);
    738     zig0_airgen_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, "0.15.2-zig0-dev"));
    739     zig0_airgen_options.addOption(std.SemanticVersion, "semver", try std.SemanticVersion.parse("0.15.2-zig0-dev"));
    740     zig0_airgen_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
    741     zig0_airgen_options.addOption(bool, "enable_logging", enable_logging);
    742     zig0_airgen_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
    743     zig0_airgen_options.addOption(bool, "enable_tracy", tracy != null);
    744     zig0_airgen_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
    745     zig0_airgen_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
    746     zig0_airgen_options.addOption(u32, "tracy_callstack_depth", tracy_callstack_depth);
    747     zig0_airgen_options.addOption(bool, "value_tracing", value_tracing);
    748 
    749     // ReleaseFast, because we trust compiler will emit the correct AIR
    750     // under all modes.
    751     const air_gen_result = addAirGen(b, zig0_airgen_options, .ReleaseFast);
    752 
    753     const prepare_zig0 = b.step("prepare-zig0", "Generate pre-computed AIR files for zig0 tests");
    754     prepare_zig0.dependOn(air_gen_result.step);
    755 
    756     const test_zig0_step = b.step("test-zig0", "Run zig0 C implementation tests");
    757     addZig0TestStep(b, test_zig0_step, zig0_target, optimize, zig0_cc, zig0_no_exec, zig0_valgrind, zig0_test_timeout, air_gen_result);
    758 
    759     // zig0 standalone executable
    760     const zig0_step = b.step("zig0", "Build zig0 standalone executable");
    761     const zig0_mod = b.createModule(.{
    762         .target = zig0_target,
    763         .optimize = optimize,
    764     });
    765     zig0_mod.addCSourceFiles(.{
    766         .root = b.path("stage0"),
    767         .files = zig0_all_c_files,
    768         .flags = zig0_cflags,
    769     });
    770     zig0_mod.linkSystemLibrary("c", .{});
    771     const zig0_exe = b.addExecutable(.{
    772         .name = "zig0",
    773         .root_module = zig0_mod,
    774     });
    775     const zig0_install = b.addInstallArtifact(zig0_exe, .{});
    776     zig0_step.dependOn(&zig0_install.step);
    777 
    778     const fmt_zig0 = b.step("fmt-zig0", "Format zig0 C code");
    779     const clang_format = b.addSystemCommand(&.{ "clang-format", "-i" });
    780     for (zig0_all_c_files ++ zig0_headers) |f| clang_format.addFileArg(b.path(b.fmt("stage0/{s}", .{f})));
    781     fmt_zig0.dependOn(&clang_format.step);
    782 
    783     const lint_zig0 = b.step("lint-zig0", "Run zig0 linters");
    784     for (zig0_all_c_files) |cfile| {
    785         const clang_analyze = b.addSystemCommand(&.{
    786             "clang",
    787             "--analyze",
    788             "--analyzer-output",
    789             "text",
    790             "-Wno-unused-command-line-argument",
    791             "-Werror",
    792             "-Xclang",
    793             "-analyzer-disable-checker",
    794             "-Xclang",
    795             "unix.Malloc",
    796         });
    797         clang_analyze.addFileArg(b.path(b.fmt("stage0/{s}", .{cfile})));
    798         clang_analyze.step.name = b.fmt("clang --analyze ({s})", .{cfile});
    799         clang_analyze.expectExitCode(0);
    800         lint_zig0.dependOn(&clang_analyze.step);
    801 
    802         // Uses >20GiB of memory, disabled for now.
    803         // const gcc_analyze = b.addSystemCommand(&.{
    804         //     "gcc",
    805         //     "-c",
    806         //     "--analyzer",
    807         //     "-Werror",
    808         //     "-o",
    809         //     "/dev/null",
    810         // });
    811         // gcc_analyze.addFileArg(b.path(b.fmt("stage0/{s}", .{cfile})));
    812         // gcc_analyze.expectExitCode(0);
    813         // lint_zig0.dependOn(&gcc_analyze.step);
    814 
    815         const cppcheck = b.addSystemCommand(&.{
    816             "cppcheck",
    817             "--quiet",
    818             "--error-exitcode=1",
    819             "--check-level=exhaustive",
    820             "--enable=warning,style,performance,portability",
    821             "--inline-suppr",
    822         });
    823         cppcheck.addFileArg(b.path(b.fmt("stage0/{s}", .{cfile})));
    824         cppcheck.step.name = b.fmt("cppcheck ({s})", .{cfile});
    825         cppcheck.expectExitCode(0);
    826         lint_zig0.dependOn(&cppcheck.step);
    827     }
    828 
    829     // TODO enable unusedFunction when it's settled.
    830     //const cppcheck_unused = b.addSystemCommand(&.{
    831     //    "cppcheck",
    832     //    "--quiet",
    833     //    "--error-exitcode=1",
    834     //    "--check-level=exhaustive",
    835     //    "--enable=unusedFunction",
    836     //    "--inline-suppr",
    837     //});
    838     //for (zig0_all_c_files) |cfile| {
    839     //    cppcheck_unused.addFileArg(b.path(b.fmt("stage0/{s}", .{cfile})));
    840     //}
    841     //cppcheck_unused.step.name = "cppcheck (unusedFunction)";
    842     //cppcheck_unused.expectExitCode(0);
    843     //lint_zig0.dependOn(&cppcheck_unused.step);
    844 
    845     const all_zig0 = b.step("all-zig0", "Run zig0 fmt check, lint, and tests with all compilers");
    846     // fmt check (dry-run)
    847     const zig0_fmt_check = b.addSystemCommand(&.{ "clang-format", "--dry-run", "-Werror" });
    848     for (zig0_all_c_files ++ zig0_headers) |f| zig0_fmt_check.addFileArg(b.path(b.fmt("stage0/{s}", .{f})));
    849     zig0_fmt_check.expectExitCode(0);
    850     all_zig0.dependOn(&zig0_fmt_check.step);
    851     all_zig0.dependOn(lint_zig0);
    852     for (zig0_compilers) |compiler| {
    853         addZig0TestStep(b, all_zig0, zig0_target, optimize, compiler, false, zig0_valgrind, zig0_test_timeout, air_gen_result);
    854     }
    855 }
    856 
    857 fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
    858     const semver = try std.SemanticVersion.parse(version);
    859 
    860     const exe = addCompilerStep(b, .{
    861         .optimize = .ReleaseSmall,
    862         .target = b.resolveTargetQuery(std.Target.Query.parse(.{
    863             .arch_os_abi = "wasm32-wasi",
    864             // * `extended_const` is not supported by the `wasm-opt` version in CI.
    865             // * `nontrapping_bulk_memory_len0` is supported by `wasm2c`.
    866             .cpu_features = "baseline-extended_const+nontrapping_bulk_memory_len0",
    867         }) catch unreachable),
    868     });
    869 
    870     const exe_options = b.addOptions();
    871     exe.root_module.addOptions("build_options", exe_options);
    872 
    873     exe_options.addOption(u32, "mem_leak_frames", 0);
    874     exe_options.addOption(bool, "have_llvm", false);
    875     exe_options.addOption(bool, "debug_gpa", false);
    876     exe_options.addOption([:0]const u8, "version", version);
    877     exe_options.addOption(std.SemanticVersion, "semver", semver);
    878     exe_options.addOption(bool, "enable_debug_extensions", false);
    879     exe_options.addOption(bool, "enable_logging", false);
    880     exe_options.addOption(bool, "enable_link_snapshots", false);
    881     exe_options.addOption(bool, "enable_tracy", false);
    882     exe_options.addOption(bool, "enable_tracy_callstack", false);
    883     exe_options.addOption(bool, "enable_tracy_allocation", false);
    884     exe_options.addOption(u32, "tracy_callstack_depth", 0);
    885     exe_options.addOption(bool, "value_tracing", false);
    886     exe_options.addOption(DevEnv, "dev", .bootstrap);
    887 
    888     // zig1 chooses to interpret values by name. The tradeoff is as follows:
    889     //
    890     // * We lose a small amount of performance. This is essentially irrelevant for zig1.
    891     //
    892     // * We lose the ability to perform trivial renames on certain `std.builtin` types without
    893     //   zig1.wasm updates. For instance, we cannot rename an enum from PascalCase fields to
    894     //   snake_case fields without an update.
    895     //
    896     // * We gain the ability to add and remove fields to and from `std.builtin` types without
    897     //   zig1.wasm updates. For instance, we can add a new tag to `CallingConvention` without
    898     //   an update.
    899     //
    900     // Because field renames only happen when we apply a breaking change to the language (which
    901     // is becoming progressively rarer), but tags may be added to or removed from target-dependent
    902     // types over time in response to new targets coming into use, we gain more than we lose here.
    903     exe_options.addOption(ValueInterpretMode, "value_interpret_mode", .by_name);
    904 
    905     const run_opt = b.addSystemCommand(&.{
    906         "wasm-opt",
    907         "-Oz",
    908         "--enable-bulk-memory",
    909         "--enable-mutable-globals",
    910         "--enable-nontrapping-float-to-int",
    911         "--enable-sign-ext",
    912     });
    913     run_opt.addArtifactArg(exe);
    914     run_opt.addArg("-o");
    915     run_opt.addFileArg(b.path("stage1/zig1.wasm"));
    916 
    917     const copy_zig_h = b.addUpdateSourceFiles();
    918     copy_zig_h.addCopyFileToSource(b.path("lib/zig.h"), "stage1/zig.h");
    919 
    920     const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm");
    921     update_zig1_step.dependOn(&run_opt.step);
    922     update_zig1_step.dependOn(&copy_zig_h.step);
    923 }
    924 
    925 const AddCompilerModOptions = struct {
    926     optimize: std.builtin.OptimizeMode,
    927     target: std.Build.ResolvedTarget,
    928     strip: ?bool = null,
    929     valgrind: ?bool = null,
    930     sanitize_thread: ?bool = null,
    931     single_threaded: ?bool = null,
    932 };
    933 
    934 fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Module {
    935     const compiler_mod = b.createModule(.{
    936         .root_source_file = b.path("src/main.zig"),
    937         .target = options.target,
    938         .optimize = options.optimize,
    939         .strip = options.strip,
    940         .sanitize_thread = options.sanitize_thread,
    941         .single_threaded = options.single_threaded,
    942         .valgrind = options.valgrind,
    943     });
    944 
    945     const aro_mod = b.createModule(.{
    946         .root_source_file = b.path("lib/compiler/aro/aro.zig"),
    947     });
    948 
    949     const aro_translate_c_mod = b.createModule(.{
    950         .root_source_file = b.path("lib/compiler/aro_translate_c.zig"),
    951     });
    952 
    953     aro_translate_c_mod.addImport("aro", aro_mod);
    954     compiler_mod.addImport("aro", aro_mod);
    955     compiler_mod.addImport("aro_translate_c", aro_translate_c_mod);
    956 
    957     return compiler_mod;
    958 }
    959 
    960 fn addCompilerStep(b: *std.Build, options: AddCompilerModOptions) *std.Build.Step.Compile {
    961     const exe = b.addExecutable(.{
    962         .name = "zig",
    963         .max_rss = 7_800_000_000,
    964         .root_module = addCompilerMod(b, options),
    965     });
    966     exe.stack_size = stack_size;
    967 
    968     return exe;
    969 }
    970 
    971 const exe_cflags = [_][]const u8{
    972     "-std=c++17",
    973     "-D__STDC_CONSTANT_MACROS",
    974     "-D__STDC_FORMAT_MACROS",
    975     "-D__STDC_LIMIT_MACROS",
    976     "-D_GNU_SOURCE",
    977     "-fno-exceptions",
    978     "-fno-rtti",
    979     "-fno-stack-protector",
    980     "-fvisibility-inlines-hidden",
    981     "-Wno-type-limits",
    982     "-Wno-missing-braces",
    983     "-Wno-comment",
    984     // `exe_cflags` is only used for static linking.
    985     "-DLLVM_BUILD_STATIC",
    986     "-DCLANG_BUILD_STATIC",
    987 };
    988 
    989 fn addCmakeCfgOptionsToExe(
    990     b: *std.Build,
    991     cfg: CMakeConfig,
    992     exe: *std.Build.Step.Compile,
    993     use_zig_libcxx: bool,
    994 ) !void {
    995     const mod = exe.root_module;
    996     const target = &mod.resolved_target.?.result;
    997 
    998     if (target.os.tag.isDarwin()) {
    999         // useful for package maintainers
   1000         exe.headerpad_max_install_names = true;
   1001     }
   1002 
   1003     mod.addObjectFile(.{ .cwd_relative = b.pathJoin(&.{
   1004         cfg.cmake_binary_dir,
   1005         "zigcpp",
   1006         b.fmt("{s}{s}{s}", .{
   1007             cfg.cmake_static_library_prefix,
   1008             "zigcpp",
   1009             cfg.cmake_static_library_suffix,
   1010         }),
   1011     }) });
   1012     assert(cfg.lld_include_dir.len != 0);
   1013     mod.addIncludePath(.{ .cwd_relative = cfg.lld_include_dir });
   1014     mod.addIncludePath(.{ .cwd_relative = cfg.llvm_include_dir });
   1015     mod.addLibraryPath(.{ .cwd_relative = cfg.llvm_lib_dir });
   1016     addCMakeLibraryList(mod, cfg.clang_libraries);
   1017     addCMakeLibraryList(mod, cfg.lld_libraries);
   1018     addCMakeLibraryList(mod, cfg.llvm_libraries);
   1019 
   1020     if (use_zig_libcxx) {
   1021         mod.link_libcpp = true;
   1022     } else {
   1023         // System -lc++ must be used because in this code path we are attempting to link
   1024         // against system-provided LLVM, Clang, LLD.
   1025         const need_cpp_includes = true;
   1026         const static = cfg.llvm_linkage == .static;
   1027         const lib_suffix = if (static) target.staticLibSuffix()[1..] else target.dynamicLibSuffix()[1..];
   1028         switch (target.os.tag) {
   1029             .linux => {
   1030                 // First we try to link against the detected libcxx name. If that doesn't work, we fall
   1031                 // back to -lc++ and cross our fingers.
   1032                 addCxxKnownPath(b, cfg, exe, b.fmt("lib{s}.{s}", .{ cfg.system_libcxx, lib_suffix }), "", need_cpp_includes) catch |err| switch (err) {
   1033                     error.RequiredLibraryNotFound => {
   1034                         mod.link_libcpp = true;
   1035                     },
   1036                     else => |e| return e,
   1037                 };
   1038                 mod.linkSystemLibrary("unwind", .{});
   1039             },
   1040             .ios, .macos, .watchos, .tvos, .visionos => {
   1041                 mod.link_libcpp = true;
   1042             },
   1043             .windows => {
   1044                 if (target.abi != .msvc) mod.link_libcpp = true;
   1045             },
   1046             .freebsd => {
   1047                 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
   1048                 if (static) try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
   1049             },
   1050             .openbsd => {
   1051                 // - llvm requires libexecinfo which has conflicting symbols with libc++abi
   1052                 // - only an issue with .a linking
   1053                 // - workaround is to link c++abi dynamically
   1054                 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{target.dynamicLibSuffix()[1..]}), null, need_cpp_includes);
   1055                 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{target.dynamicLibSuffix()[1..]}), null, need_cpp_includes);
   1056             },
   1057             .netbsd, .dragonfly => {
   1058                 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
   1059                 if (static) try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
   1060             },
   1061             .solaris, .illumos => {
   1062                 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
   1063                 try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
   1064             },
   1065             .haiku => {
   1066                 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
   1067             },
   1068             else => {},
   1069         }
   1070     }
   1071 
   1072     if (cfg.dia_guids_lib.len != 0) {
   1073         mod.addObjectFile(.{ .cwd_relative = cfg.dia_guids_lib });
   1074     }
   1075 }
   1076 
   1077 fn addStaticLlvmOptionsToModule(mod: *std.Build.Module, options: struct {
   1078     llvm_has_m68k: bool,
   1079     llvm_has_csky: bool,
   1080     llvm_has_arc: bool,
   1081     llvm_has_xtensa: bool,
   1082 }) !void {
   1083     // Adds the Zig C++ sources which both stage1 and stage2 need.
   1084     //
   1085     // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
   1086     // in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
   1087     // unavailable when LLVM is compiled in Release mode.
   1088     const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
   1089     mod.addCSourceFiles(.{
   1090         .files = &zig_cpp_sources,
   1091         .flags = &zig_cpp_cflags,
   1092     });
   1093 
   1094     for (clang_libs) |lib_name| {
   1095         mod.linkSystemLibrary(lib_name, .{});
   1096     }
   1097 
   1098     for (lld_libs) |lib_name| {
   1099         mod.linkSystemLibrary(lib_name, .{});
   1100     }
   1101 
   1102     for (llvm_libs) |lib_name| {
   1103         mod.linkSystemLibrary(lib_name, .{});
   1104     }
   1105 
   1106     if (options.llvm_has_m68k) for (llvm_libs_m68k) |lib_name| {
   1107         mod.linkSystemLibrary(lib_name, .{});
   1108     };
   1109 
   1110     if (options.llvm_has_csky) for (llvm_libs_csky) |lib_name| {
   1111         mod.linkSystemLibrary(lib_name, .{});
   1112     };
   1113 
   1114     if (options.llvm_has_arc) for (llvm_libs_arc) |lib_name| {
   1115         mod.linkSystemLibrary(lib_name, .{});
   1116     };
   1117 
   1118     if (options.llvm_has_xtensa) for (llvm_libs_xtensa) |lib_name| {
   1119         mod.linkSystemLibrary(lib_name, .{});
   1120     };
   1121 
   1122     mod.linkSystemLibrary("z", .{});
   1123     mod.linkSystemLibrary("zstd", .{});
   1124 
   1125     if (mod.resolved_target.?.result.os.tag != .windows or mod.resolved_target.?.result.abi != .msvc) {
   1126         // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
   1127         mod.linkSystemLibrary("c++", .{});
   1128     }
   1129 
   1130     if (mod.resolved_target.?.result.os.tag == .windows) {
   1131         mod.linkSystemLibrary("version", .{});
   1132         mod.linkSystemLibrary("uuid", .{});
   1133         mod.linkSystemLibrary("ole32", .{});
   1134     }
   1135 }
   1136 
   1137 fn addCxxKnownPath(
   1138     b: *std.Build,
   1139     ctx: CMakeConfig,
   1140     exe: *std.Build.Step.Compile,
   1141     objname: []const u8,
   1142     errtxt: ?[]const u8,
   1143     need_cpp_includes: bool,
   1144 ) !void {
   1145     if (!std.process.can_spawn)
   1146         return error.RequiredLibraryNotFound;
   1147 
   1148     const path_padded = run: {
   1149         var args = std.array_list.Managed([]const u8).init(b.allocator);
   1150         try args.append(ctx.cxx_compiler);
   1151         var it = std.mem.tokenizeAny(u8, ctx.cxx_compiler_arg1, &std.ascii.whitespace);
   1152         while (it.next()) |arg| try args.append(arg);
   1153         try args.append(b.fmt("-print-file-name={s}", .{objname}));
   1154         break :run b.run(args.items);
   1155     };
   1156     var tokenizer = mem.tokenizeAny(u8, path_padded, "\r\n");
   1157     const path_unpadded = tokenizer.next().?;
   1158     if (mem.eql(u8, path_unpadded, objname)) {
   1159         if (errtxt) |msg| {
   1160             std.debug.print("{s}", .{msg});
   1161         } else {
   1162             std.debug.print("Unable to determine path to {s}\n", .{objname});
   1163         }
   1164         return error.RequiredLibraryNotFound;
   1165     }
   1166     // By default, explicit library paths are not checked for being linker scripts,
   1167     // but libc++ may very well be one, so force all inputs to be checked when passing
   1168     // an explicit path to libc++.
   1169     exe.allow_so_scripts = true;
   1170     exe.root_module.addObjectFile(.{ .cwd_relative = path_unpadded });
   1171 
   1172     // TODO a way to integrate with system c++ include files here
   1173     // c++ -E -Wp,-v -xc++ /dev/null
   1174     if (need_cpp_includes) {
   1175         // I used these temporarily for testing something but we obviously need a
   1176         // more general purpose solution here.
   1177         //exe.root_module.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0");
   1178         //exe.root_module.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0/x86_64-unknown-linux-gnu");
   1179     }
   1180 }
   1181 
   1182 fn addCMakeLibraryList(mod: *std.Build.Module, list: []const u8) void {
   1183     var it = mem.tokenizeScalar(u8, list, ';');
   1184     while (it.next()) |lib| {
   1185         if (mem.startsWith(u8, lib, "-l")) {
   1186             mod.linkSystemLibrary(lib["-l".len..], .{});
   1187         } else if (mod.resolved_target.?.result.os.tag == .windows and
   1188             mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib))
   1189         {
   1190             mod.linkSystemLibrary(lib[0 .. lib.len - ".lib".len], .{});
   1191         } else {
   1192             mod.addObjectFile(.{ .cwd_relative = lib });
   1193         }
   1194     }
   1195 }
   1196 
   1197 const CMakeConfig = struct {
   1198     llvm_linkage: std.builtin.LinkMode,
   1199     cmake_binary_dir: []const u8,
   1200     cmake_prefix_path: []const u8,
   1201     cmake_static_library_prefix: []const u8,
   1202     cmake_static_library_suffix: []const u8,
   1203     cxx_compiler: []const u8,
   1204     cxx_compiler_arg1: []const u8,
   1205     lld_include_dir: []const u8,
   1206     lld_libraries: []const u8,
   1207     clang_libraries: []const u8,
   1208     llvm_lib_dir: []const u8,
   1209     llvm_include_dir: []const u8,
   1210     llvm_libraries: []const u8,
   1211     dia_guids_lib: []const u8,
   1212     system_libcxx: []const u8,
   1213 };
   1214 
   1215 const max_config_h_bytes = 1 * 1024 * 1024;
   1216 
   1217 fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
   1218     if (config_h_path_option) |path| {
   1219         var config_h_or_err = fs.cwd().openFile(path, .{});
   1220         if (config_h_or_err) |*file| {
   1221             file.close();
   1222             return path;
   1223         } else |_| {
   1224             std.log.err("Could not open provided config.h: \"{s}\"", .{path});
   1225             std.process.exit(1);
   1226         }
   1227     }
   1228 
   1229     var check_dir = fs.path.dirname(b.graph.zig_exe).?;
   1230     while (true) {
   1231         var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
   1232         defer dir.close();
   1233 
   1234         // Check if config.h is present in dir
   1235         var config_h_or_err = dir.openFile("config.h", .{});
   1236         if (config_h_or_err) |*file| {
   1237             file.close();
   1238             return fs.path.join(
   1239                 b.allocator,
   1240                 &[_][]const u8{ check_dir, "config.h" },
   1241             ) catch unreachable;
   1242         } else |e| switch (e) {
   1243             error.FileNotFound => {},
   1244             else => unreachable,
   1245         }
   1246 
   1247         // Check if we reached the source root by looking for .git, and bail if so
   1248         var git_dir_or_err = dir.openDir(".git", .{});
   1249         if (git_dir_or_err) |*git_dir| {
   1250             git_dir.close();
   1251             return null;
   1252         } else |_| {}
   1253 
   1254         // Otherwise, continue search in the parent directory
   1255         const new_check_dir = fs.path.dirname(check_dir);
   1256         if (new_check_dir == null or mem.eql(u8, new_check_dir.?, check_dir)) {
   1257             return null;
   1258         }
   1259         check_dir = new_check_dir.?;
   1260     }
   1261 }
   1262 
   1263 fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
   1264     var ctx: CMakeConfig = .{
   1265         .llvm_linkage = undefined,
   1266         .cmake_binary_dir = undefined,
   1267         .cmake_prefix_path = undefined,
   1268         .cmake_static_library_prefix = undefined,
   1269         .cmake_static_library_suffix = undefined,
   1270         .cxx_compiler = undefined,
   1271         .cxx_compiler_arg1 = "",
   1272         .lld_include_dir = undefined,
   1273         .lld_libraries = undefined,
   1274         .clang_libraries = undefined,
   1275         .llvm_lib_dir = undefined,
   1276         .llvm_include_dir = undefined,
   1277         .llvm_libraries = undefined,
   1278         .dia_guids_lib = undefined,
   1279         .system_libcxx = undefined,
   1280     };
   1281 
   1282     const mappings = [_]struct { prefix: []const u8, field: []const u8 }{
   1283         .{
   1284             .prefix = "#define ZIG_CMAKE_BINARY_DIR ",
   1285             .field = "cmake_binary_dir",
   1286         },
   1287         .{
   1288             .prefix = "#define ZIG_CMAKE_PREFIX_PATH ",
   1289             .field = "cmake_prefix_path",
   1290         },
   1291         .{
   1292             .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_PREFIX ",
   1293             .field = "cmake_static_library_prefix",
   1294         },
   1295         .{
   1296             .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_SUFFIX ",
   1297             .field = "cmake_static_library_suffix",
   1298         },
   1299         .{
   1300             .prefix = "#define ZIG_CXX_COMPILER ",
   1301             .field = "cxx_compiler",
   1302         },
   1303         .{
   1304             .prefix = "#define ZIG_CXX_COMPILER_ARG1 ",
   1305             .field = "cxx_compiler_arg1",
   1306         },
   1307         .{
   1308             .prefix = "#define ZIG_LLD_INCLUDE_PATH ",
   1309             .field = "lld_include_dir",
   1310         },
   1311         .{
   1312             .prefix = "#define ZIG_LLD_LIBRARIES ",
   1313             .field = "lld_libraries",
   1314         },
   1315         .{
   1316             .prefix = "#define ZIG_CLANG_LIBRARIES ",
   1317             .field = "clang_libraries",
   1318         },
   1319         .{
   1320             .prefix = "#define ZIG_LLVM_LIBRARIES ",
   1321             .field = "llvm_libraries",
   1322         },
   1323         .{
   1324             .prefix = "#define ZIG_DIA_GUIDS_LIB ",
   1325             .field = "dia_guids_lib",
   1326         },
   1327         .{
   1328             .prefix = "#define ZIG_LLVM_INCLUDE_PATH ",
   1329             .field = "llvm_include_dir",
   1330         },
   1331         .{
   1332             .prefix = "#define ZIG_LLVM_LIB_PATH ",
   1333             .field = "llvm_lib_dir",
   1334         },
   1335         .{
   1336             .prefix = "#define ZIG_SYSTEM_LIBCXX",
   1337             .field = "system_libcxx",
   1338         },
   1339         // .prefix = ZIG_LLVM_LINK_MODE parsed manually below
   1340     };
   1341 
   1342     var lines_it = mem.tokenizeAny(u8, config_h_text, "\r\n");
   1343     while (lines_it.next()) |line| {
   1344         inline for (mappings) |mapping| {
   1345             if (mem.startsWith(u8, line, mapping.prefix)) {
   1346                 var it = mem.splitScalar(u8, line, '"');
   1347                 _ = it.first(); // skip the stuff before the quote
   1348                 const quoted = it.next().?; // the stuff inside the quote
   1349                 const trimmed = mem.trim(u8, quoted, " ");
   1350                 @field(ctx, mapping.field) = toNativePathSep(b, trimmed);
   1351             }
   1352         }
   1353         if (mem.startsWith(u8, line, "#define ZIG_LLVM_LINK_MODE ")) {
   1354             var it = mem.splitScalar(u8, line, '"');
   1355             _ = it.next().?; // skip the stuff before the quote
   1356             const quoted = it.next().?; // the stuff inside the quote
   1357             ctx.llvm_linkage = if (mem.eql(u8, quoted, "shared")) .dynamic else .static;
   1358         }
   1359     }
   1360     return ctx;
   1361 }
   1362 
   1363 fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
   1364     const duplicated = b.allocator.dupe(u8, s) catch unreachable;
   1365     for (duplicated) |*byte| switch (byte.*) {
   1366         '/' => byte.* = fs.path.sep,
   1367         else => {},
   1368     };
   1369     return duplicated;
   1370 }
   1371 
   1372 const zig_cpp_sources = [_][]const u8{
   1373     // These are planned to stay even when we are self-hosted.
   1374     "src/zig_llvm.cpp",
   1375     "src/zig_clang.cpp",
   1376     "src/zig_llvm-ar.cpp",
   1377     "src/zig_clang_driver.cpp",
   1378     "src/zig_clang_cc1_main.cpp",
   1379     "src/zig_clang_cc1as_main.cpp",
   1380 };
   1381 
   1382 const clang_libs = [_][]const u8{
   1383     "clangFrontendTool",
   1384     "clangCodeGen",
   1385     "clangFrontend",
   1386     "clangDriver",
   1387     "clangSerialization",
   1388     "clangSema",
   1389     "clangStaticAnalyzerFrontend",
   1390     "clangStaticAnalyzerCheckers",
   1391     "clangStaticAnalyzerCore",
   1392     "clangAnalysis",
   1393     "clangASTMatchers",
   1394     "clangAST",
   1395     "clangParse",
   1396     "clangSema",
   1397     "clangAPINotes",
   1398     "clangBasic",
   1399     "clangEdit",
   1400     "clangLex",
   1401     "clangARCMigrate",
   1402     "clangRewriteFrontend",
   1403     "clangRewrite",
   1404     "clangCrossTU",
   1405     "clangIndex",
   1406     "clangToolingCore",
   1407     "clangExtractAPI",
   1408     "clangSupport",
   1409     "clangInstallAPI",
   1410     "clangAST",
   1411 };
   1412 const lld_libs = [_][]const u8{
   1413     "lldMinGW",
   1414     "lldELF",
   1415     "lldCOFF",
   1416     "lldWasm",
   1417     "lldMachO",
   1418     "lldCommon",
   1419 };
   1420 // This list can be re-generated with `llvm-config --libfiles` and then
   1421 // reformatting using your favorite text editor. Note we do not execute
   1422 // `llvm-config` here because we are cross compiling. Also omit LLVMTableGen
   1423 // from these libs.
   1424 const llvm_libs = [_][]const u8{
   1425     "LLVMWindowsManifest",
   1426     "LLVMXRay",
   1427     "LLVMLibDriver",
   1428     "LLVMDlltoolDriver",
   1429     "LLVMTelemetry",
   1430     "LLVMTextAPIBinaryReader",
   1431     "LLVMCoverage",
   1432     "LLVMLineEditor",
   1433     "LLVMXCoreDisassembler",
   1434     "LLVMXCoreCodeGen",
   1435     "LLVMXCoreDesc",
   1436     "LLVMXCoreInfo",
   1437     "LLVMX86TargetMCA",
   1438     "LLVMX86Disassembler",
   1439     "LLVMX86AsmParser",
   1440     "LLVMX86CodeGen",
   1441     "LLVMX86Desc",
   1442     "LLVMX86Info",
   1443     "LLVMWebAssemblyDisassembler",
   1444     "LLVMWebAssemblyAsmParser",
   1445     "LLVMWebAssemblyCodeGen",
   1446     "LLVMWebAssemblyUtils",
   1447     "LLVMWebAssemblyDesc",
   1448     "LLVMWebAssemblyInfo",
   1449     "LLVMVEDisassembler",
   1450     "LLVMVEAsmParser",
   1451     "LLVMVECodeGen",
   1452     "LLVMVEDesc",
   1453     "LLVMVEInfo",
   1454     "LLVMSystemZDisassembler",
   1455     "LLVMSystemZAsmParser",
   1456     "LLVMSystemZCodeGen",
   1457     "LLVMSystemZDesc",
   1458     "LLVMSystemZInfo",
   1459     "LLVMSPIRVCodeGen",
   1460     "LLVMSPIRVDesc",
   1461     "LLVMSPIRVInfo",
   1462     "LLVMSPIRVAnalysis",
   1463     "LLVMSparcDisassembler",
   1464     "LLVMSparcAsmParser",
   1465     "LLVMSparcCodeGen",
   1466     "LLVMSparcDesc",
   1467     "LLVMSparcInfo",
   1468     "LLVMRISCVTargetMCA",
   1469     "LLVMRISCVDisassembler",
   1470     "LLVMRISCVAsmParser",
   1471     "LLVMRISCVCodeGen",
   1472     "LLVMRISCVDesc",
   1473     "LLVMRISCVInfo",
   1474     "LLVMPowerPCDisassembler",
   1475     "LLVMPowerPCAsmParser",
   1476     "LLVMPowerPCCodeGen",
   1477     "LLVMPowerPCDesc",
   1478     "LLVMPowerPCInfo",
   1479     "LLVMNVPTXCodeGen",
   1480     "LLVMNVPTXDesc",
   1481     "LLVMNVPTXInfo",
   1482     "LLVMMSP430Disassembler",
   1483     "LLVMMSP430AsmParser",
   1484     "LLVMMSP430CodeGen",
   1485     "LLVMMSP430Desc",
   1486     "LLVMMSP430Info",
   1487     "LLVMMipsDisassembler",
   1488     "LLVMMipsAsmParser",
   1489     "LLVMMipsCodeGen",
   1490     "LLVMMipsDesc",
   1491     "LLVMMipsInfo",
   1492     "LLVMLoongArchDisassembler",
   1493     "LLVMLoongArchAsmParser",
   1494     "LLVMLoongArchCodeGen",
   1495     "LLVMLoongArchDesc",
   1496     "LLVMLoongArchInfo",
   1497     "LLVMLanaiDisassembler",
   1498     "LLVMLanaiCodeGen",
   1499     "LLVMLanaiAsmParser",
   1500     "LLVMLanaiDesc",
   1501     "LLVMLanaiInfo",
   1502     "LLVMHexagonDisassembler",
   1503     "LLVMHexagonCodeGen",
   1504     "LLVMHexagonAsmParser",
   1505     "LLVMHexagonDesc",
   1506     "LLVMHexagonInfo",
   1507     "LLVMBPFDisassembler",
   1508     "LLVMBPFAsmParser",
   1509     "LLVMBPFCodeGen",
   1510     "LLVMBPFDesc",
   1511     "LLVMBPFInfo",
   1512     "LLVMAVRDisassembler",
   1513     "LLVMAVRAsmParser",
   1514     "LLVMAVRCodeGen",
   1515     "LLVMAVRDesc",
   1516     "LLVMAVRInfo",
   1517     "LLVMARMDisassembler",
   1518     "LLVMARMAsmParser",
   1519     "LLVMARMCodeGen",
   1520     "LLVMARMDesc",
   1521     "LLVMARMUtils",
   1522     "LLVMARMInfo",
   1523     "LLVMAMDGPUTargetMCA",
   1524     "LLVMAMDGPUDisassembler",
   1525     "LLVMAMDGPUAsmParser",
   1526     "LLVMAMDGPUCodeGen",
   1527     "LLVMAMDGPUDesc",
   1528     "LLVMAMDGPUUtils",
   1529     "LLVMAMDGPUInfo",
   1530     "LLVMAArch64Disassembler",
   1531     "LLVMAArch64AsmParser",
   1532     "LLVMAArch64CodeGen",
   1533     "LLVMAArch64Desc",
   1534     "LLVMAArch64Utils",
   1535     "LLVMAArch64Info",
   1536     "LLVMOrcDebugging",
   1537     "LLVMOrcJIT",
   1538     "LLVMWindowsDriver",
   1539     "LLVMMCJIT",
   1540     "LLVMJITLink",
   1541     "LLVMInterpreter",
   1542     "LLVMExecutionEngine",
   1543     "LLVMRuntimeDyld",
   1544     "LLVMOrcTargetProcess",
   1545     "LLVMOrcShared",
   1546     "LLVMDWP",
   1547     "LLVMDebugInfoLogicalView",
   1548     "LLVMDebugInfoGSYM",
   1549     "LLVMOption",
   1550     "LLVMObjectYAML",
   1551     "LLVMObjCopy",
   1552     "LLVMMCA",
   1553     "LLVMMCDisassembler",
   1554     "LLVMLTO",
   1555     "LLVMPasses",
   1556     "LLVMHipStdPar",
   1557     "LLVMCFGuard",
   1558     "LLVMCoroutines",
   1559     "LLVMipo",
   1560     "LLVMVectorize",
   1561     "LLVMSandboxIR",
   1562     "LLVMLinker",
   1563     "LLVMInstrumentation",
   1564     "LLVMFrontendOpenMP",
   1565     "LLVMFrontendOffloading",
   1566     "LLVMFrontendOpenACC",
   1567     "LLVMFrontendHLSL",
   1568     "LLVMFrontendDriver",
   1569     "LLVMFrontendAtomic",
   1570     "LLVMExtensions",
   1571     "LLVMDWARFLinkerParallel",
   1572     "LLVMDWARFLinkerClassic",
   1573     "LLVMDWARFLinker",
   1574     "LLVMGlobalISel",
   1575     "LLVMMIRParser",
   1576     "LLVMAsmPrinter",
   1577     "LLVMSelectionDAG",
   1578     "LLVMCodeGen",
   1579     "LLVMTarget",
   1580     "LLVMObjCARCOpts",
   1581     "LLVMCodeGenTypes",
   1582     "LLVMCGData",
   1583     "LLVMIRPrinter",
   1584     "LLVMInterfaceStub",
   1585     "LLVMFileCheck",
   1586     "LLVMFuzzMutate",
   1587     "LLVMScalarOpts",
   1588     "LLVMInstCombine",
   1589     "LLVMAggressiveInstCombine",
   1590     "LLVMTransformUtils",
   1591     "LLVMBitWriter",
   1592     "LLVMAnalysis",
   1593     "LLVMProfileData",
   1594     "LLVMSymbolize",
   1595     "LLVMDebugInfoBTF",
   1596     "LLVMDebugInfoPDB",
   1597     "LLVMDebugInfoMSF",
   1598     "LLVMDebugInfoCodeView",
   1599     "LLVMDebugInfoDWARF",
   1600     "LLVMObject",
   1601     "LLVMTextAPI",
   1602     "LLVMMCParser",
   1603     "LLVMIRReader",
   1604     "LLVMAsmParser",
   1605     "LLVMMC",
   1606     "LLVMBitReader",
   1607     "LLVMFuzzerCLI",
   1608     "LLVMCore",
   1609     "LLVMRemarks",
   1610     "LLVMBitstreamReader",
   1611     "LLVMBinaryFormat",
   1612     "LLVMTargetParser",
   1613     "LLVMSupport",
   1614     "LLVMDemangle",
   1615 };
   1616 const llvm_libs_m68k = [_][]const u8{
   1617     "LLVMM68kDisassembler",
   1618     "LLVMM68kAsmParser",
   1619     "LLVMM68kCodeGen",
   1620     "LLVMM68kDesc",
   1621     "LLVMM68kInfo",
   1622 };
   1623 const llvm_libs_csky = [_][]const u8{
   1624     "LLVMCSKYDisassembler",
   1625     "LLVMCSKYAsmParser",
   1626     "LLVMCSKYCodeGen",
   1627     "LLVMCSKYDesc",
   1628     "LLVMCSKYInfo",
   1629 };
   1630 const llvm_libs_arc = [_][]const u8{
   1631     "LLVMARCDisassembler",
   1632     "LLVMARCCodeGen",
   1633     "LLVMARCDesc",
   1634     "LLVMARCInfo",
   1635 };
   1636 const llvm_libs_xtensa = [_][]const u8{
   1637     "LLVMXtensaDisassembler",
   1638     "LLVMXtensaAsmParser",
   1639     "LLVMXtensaCodeGen",
   1640     "LLVMXtensaDesc",
   1641     "LLVMXtensaInfo",
   1642 };
   1643 
   1644 fn generateLangRef(b: *std.Build) std.Build.LazyPath {
   1645     const doctest_exe = b.addExecutable(.{
   1646         .name = "doctest",
   1647         .root_module = b.createModule(.{
   1648             .root_source_file = b.path("tools/doctest.zig"),
   1649             .target = b.graph.host,
   1650             .optimize = .Debug,
   1651         }),
   1652     });
   1653 
   1654     var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
   1655         std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{
   1656             b.build_root, @errorName(err),
   1657         });
   1658     };
   1659     defer dir.close();
   1660 
   1661     var wf = b.addWriteFiles();
   1662 
   1663     var it = dir.iterateAssumeFirstIteration();
   1664     while (it.next() catch @panic("failed to read dir")) |entry| {
   1665         if (std.mem.startsWith(u8, entry.name, ".") or entry.kind != .file)
   1666             continue;
   1667 
   1668         const out_basename = b.fmt("{s}.out", .{std.fs.path.stem(entry.name)});
   1669         const cmd = b.addRunArtifact(doctest_exe);
   1670         cmd.addArgs(&.{
   1671             "--zig",        b.graph.zig_exe,
   1672             // TODO: enhance doctest to use "--listen=-" rather than operating
   1673             // in a temporary directory
   1674             "--cache-root", b.cache_root.path orelse ".",
   1675         });
   1676         cmd.addArgs(&.{ "--zig-lib-dir", b.fmt("{f}", .{b.graph.zig_lib_directory}) });
   1677         cmd.addArgs(&.{"-i"});
   1678         cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name})));
   1679 
   1680         cmd.addArgs(&.{"-o"});
   1681         _ = wf.addCopyFile(cmd.addOutputFileArg(out_basename), out_basename);
   1682     }
   1683 
   1684     const docgen_exe = b.addExecutable(.{
   1685         .name = "docgen",
   1686         .root_module = b.createModule(.{
   1687             .root_source_file = b.path("tools/docgen.zig"),
   1688             .target = b.graph.host,
   1689             .optimize = .Debug,
   1690         }),
   1691     });
   1692 
   1693     const docgen_cmd = b.addRunArtifact(docgen_exe);
   1694     docgen_cmd.addArgs(&.{"--code-dir"});
   1695     docgen_cmd.addDirectoryArg(wf.getDirectory());
   1696 
   1697     docgen_cmd.addFileArg(b.path("doc/langref.html.in"));
   1698     return docgen_cmd.addOutputFileArg("langref.html");
   1699 }
   1700 
   1701 fn superHtmlCheck(b: *std.Build, html_file: std.Build.LazyPath) *std.Build.Step {
   1702     const run_superhtml = b.addSystemCommand(&.{
   1703         "superhtml", "check",
   1704     });
   1705     run_superhtml.addFileArg(html_file);
   1706     run_superhtml.expectExitCode(0);
   1707     return &run_superhtml.step;
   1708 }
   1709 
   1710 const AirGenResult = struct {
   1711     step: *std.Build.Step,
   1712     air_dir: std.Build.LazyPath,
   1713 };
   1714 
   1715 fn addAirGen(
   1716     b: *std.Build,
   1717     exe_options: *std.Build.Step.Options,
   1718     optimize: std.builtin.OptimizeMode,
   1719 ) AirGenResult {
   1720     const zig_internals_mod = b.createModule(.{
   1721         .root_source_file = b.path("src/test_exports.zig"),
   1722     });
   1723     const aro_mod = b.createModule(.{
   1724         .root_source_file = b.path("lib/compiler/aro/aro.zig"),
   1725     });
   1726     const aro_translate_c_mod = b.createModule(.{
   1727         .root_source_file = b.path("lib/compiler/aro_translate_c.zig"),
   1728     });
   1729     aro_translate_c_mod.addImport("aro", aro_mod);
   1730     zig_internals_mod.addImport("aro", aro_mod);
   1731     zig_internals_mod.addImport("aro_translate_c", aro_translate_c_mod);
   1732     zig_internals_mod.addOptions("build_options", exe_options);
   1733 
   1734     const dumper_mod = b.createModule(.{
   1735         .root_source_file = b.path("src/verbose_air.zig"),
   1736         .target = b.graph.host,
   1737         .optimize = optimize,
   1738     });
   1739     dumper_mod.addImport("zig_internals", zig_internals_mod);
   1740     dumper_mod.linkSystemLibrary("c", .{});
   1741 
   1742     const gen_mod = b.createModule(.{
   1743         .root_source_file = b.path("src/verbose_air_gen.zig"),
   1744         .target = b.graph.host,
   1745         .optimize = optimize,
   1746     });
   1747     gen_mod.addImport("verbose_air", dumper_mod);
   1748     gen_mod.addImport("zig_internals", zig_internals_mod);
   1749     gen_mod.linkSystemLibrary("c", .{});
   1750 
   1751     const gen_exe = b.addExecutable(.{
   1752         .name = "air_gen",
   1753         .root_module = gen_mod,
   1754         .use_llvm = false,
   1755         .use_lld = false,
   1756     });
   1757 
   1758     // Run generator: air_gen <output_dir> [<name> <resolved_path>]...
   1759     const gen_run = b.addRunArtifact(gen_exe);
   1760     const air_dir = gen_run.addOutputDirectoryArg("air");
   1761     // Add non-lib/std/ corpus files as name/path pairs.
   1762     // lib/std/ files are compiled via std.zig root inside air_gen.
   1763     // stage0/sema_tests/ files are also included (standalone compilation).
   1764     for (corpus.files) |path| {
   1765         if (std.mem.startsWith(u8, path, "lib/std/")) continue;
   1766         gen_run.addArg(path);
   1767         gen_run.addFileArg(b.path(path));
   1768     }
   1769 
   1770     return .{ .step = &gen_run.step, .air_dir = air_dir };
   1771 }
   1772 
   1773 fn addZig0TestStep(
   1774     b: *std.Build,
   1775     step: *std.Build.Step,
   1776     target: std.Build.ResolvedTarget,
   1777     optimize: std.builtin.OptimizeMode,
   1778     cc: []const u8,
   1779     no_exec: bool,
   1780     valgrind: bool,
   1781     test_timeout: ?[]const u8,
   1782     air_gen: AirGenResult,
   1783 ) void {
   1784     // Step 1: Compile Zig test code to .o (cached independently of C objects).
   1785     // NOTE: test_mod does NOT import zig_internals — stage0 tests are fast.
   1786     const test_mod = b.createModule(.{
   1787         .root_source_file = b.path("stage0_test_root.zig"),
   1788         .optimize = optimize,
   1789         .target = target,
   1790         .valgrind = true,
   1791     });
   1792     test_mod.addIncludePath(b.path("stage0"));
   1793     test_mod.linkSystemLibrary("c", .{});
   1794     // Make pre-computed AIR data available to tests.
   1795     test_mod.addAnonymousImport("air_tag_names", .{
   1796         .root_source_file = air_gen.air_dir.path(b, "tag_names.zig"),
   1797     });
   1798     test_mod.addAnonymousImport("air_data", .{
   1799         .root_source_file = air_gen.air_dir.path(b, "air_data.zig"),
   1800     });
   1801 
   1802     const test_obj = b.addTest(.{
   1803         .root_module = test_mod,
   1804         .emit_object = true,
   1805         .use_llvm = false,
   1806         .use_lld = false,
   1807     });
   1808     test_obj.step.dependOn(air_gen.step);
   1809 
   1810     // Step 2: Link test_obj + C objects into final executable.
   1811     // No more dumper_obj — AIR is pre-computed at build time.
   1812     const link_mod = b.createModule(.{
   1813         .target = target,
   1814         .optimize = optimize,
   1815     });
   1816     link_mod.addObject(test_obj);
   1817     addZig0CSources(b, link_mod, cc, optimize);
   1818     link_mod.linkSystemLibrary("c", .{});
   1819 
   1820     const test_exe = b.addExecutable(.{
   1821         .name = "test",
   1822         .root_module = link_mod,
   1823     });
   1824 
   1825     const timeout: ?[]const u8 = test_timeout orelse if (valgrind) null else "300";
   1826 
   1827     if (no_exec) {
   1828         const install = b.addInstallArtifact(test_exe, .{});
   1829         step.dependOn(&install.step);
   1830     } else {
   1831         // Step 3: Run with test IPC protocol.
   1832         const run = std.Build.Step.Run.create(b, b.fmt("test ({s})", .{cc}));
   1833         if (valgrind) {
   1834             run.addArgs(&.{
   1835                 "valgrind",
   1836                 "--error-exitcode=2",
   1837                 "--leak-check=full",
   1838                 "--show-leak-kinds=all",
   1839                 "--errors-for-leak-kinds=all",
   1840                 "--track-fds=yes",
   1841                 "--quiet",
   1842             });
   1843         } else if (timeout) |t| {
   1844             run.addArgs(&.{ "timeout", t });
   1845         }
   1846         run.addArtifactArg(test_exe);
   1847         run.enableTestRunnerMode();
   1848         step.dependOn(&run.step);
   1849     }
   1850 }
   1851 
   1852 fn addZig0CSources(
   1853     b: *std.Build,
   1854     mod: *std.Build.Module,
   1855     cc: []const u8,
   1856     optimize: std.builtin.OptimizeMode,
   1857 ) void {
   1858     if (std.mem.eql(u8, cc, "zig")) {
   1859         mod.addCSourceFiles(.{
   1860             .root = b.path("stage0"),
   1861             .files = zig0_c_lib_files,
   1862             .flags = zig0_cflags,
   1863         });
   1864     } else for (zig0_c_lib_files) |cfile| {
   1865         const cc1 = b.addSystemCommand(&.{cc});
   1866         cc1.addArgs(zig0_cflags ++ .{"-g"});
   1867         cc1.addArg(switch (optimize) {
   1868             .Debug => "-O0",
   1869             .ReleaseFast, .ReleaseSafe => "-O3",
   1870             .ReleaseSmall => "-Os",
   1871         });
   1872         cc1.addArg("-c");
   1873         cc1.addFileArg(b.path(b.fmt("stage0/{s}", .{cfile})));
   1874         // Track headers as extra inputs so changes invalidate the cache.
   1875         for (zig0_headers) |h| cc1.addFileInput(b.path(b.fmt("stage0/{s}", .{h})));
   1876         cc1.addArg("-o");
   1877         mod.addObjectFile(cc1.addOutputFileArg(b.fmt("{s}.o", .{cfile[0 .. cfile.len - 2]})));
   1878     }
   1879 }