master
   1const std = @import("std");
   2const builtin = std.builtin;
   3const tests = @import("test/tests.zig");
   4const BufMap = std.BufMap;
   5const mem = std.mem;
   6const io = std.io;
   7const fs = std.fs;
   8const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
   9const assert = std.debug.assert;
  10const DevEnv = @import("src/dev.zig").Env;
  11const ValueInterpretMode = enum { direct, by_name };
  12
  13const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 16, .patch = 0 };
  14const stack_size = 46 * 1024 * 1024;
  15
  16pub fn build(b: *std.Build) !void {
  17    const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
  18    const target = b.standardTargetOptions(.{
  19        .default_target = .{
  20            .ofmt = if (only_c) .c else null,
  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_superhtml = b.option(bool, "enable-superhtml", "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 = superHtmlCheck(b, langref_file);
  39    if (enable_superhtml) install_langref.step.dependOn(check_langref);
  40
  41    const check_autodocs = superHtmlCheck(b, b.path("lib/docs/index.html"));
  42    if (enable_superhtml) {
  43        test_step.dependOn(check_langref);
  44        test_step.dependOn(check_autodocs);
  45    }
  46    if (!skip_install_langref) {
  47        b.getInstallStep().dependOn(&install_langref.step);
  48    }
  49
  50    const autodoc_test = b.addObject(.{
  51        .name = "std",
  52        .zig_lib_dir = b.path("lib"),
  53        .root_module = b.createModule(.{
  54            .root_source_file = b.path("lib/std/std.zig"),
  55            .target = target,
  56            .optimize = .Debug,
  57        }),
  58    });
  59    const install_std_docs = b.addInstallDirectory(.{
  60        .source_dir = autodoc_test.getEmittedDocs(),
  61        .install_dir = .prefix,
  62        .install_subdir = "doc/std",
  63    });
  64    //if (enable_tidy) install_std_docs.step.dependOn(check_autodocs);
  65    if (std_docs) {
  66        b.getInstallStep().dependOn(&install_std_docs.step);
  67    }
  68
  69    if (flat) {
  70        b.installFile("LICENSE", "LICENSE");
  71        b.installFile("README.md", "README.md");
  72    }
  73
  74    const langref_step = b.step("langref", "Build and install the language reference");
  75    langref_step.dependOn(&install_langref.step);
  76
  77    const std_docs_step = b.step("std-docs", "Build and install the standard library documentation");
  78    std_docs_step.dependOn(&install_std_docs.step);
  79
  80    const docs_step = b.step("docs", "Build and install documentation");
  81    docs_step.dependOn(langref_step);
  82    docs_step.dependOn(std_docs_step);
  83
  84    const no_matrix = b.option(bool, "no-matrix", "Limit test matrix to exactly one target configuration") orelse false;
  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 no_matrix;
  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 no_matrix;
  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_compile_errors = b.option(bool, "skip-compile-errors", "Main test suite skips compile error tests") orelse false;
  94    const skip_freebsd = b.option(bool, "skip-freebsd", "Main test suite skips targets with freebsd OS") orelse false;
  95    const skip_netbsd = b.option(bool, "skip-netbsd", "Main test suite skips targets with netbsd OS") orelse false;
  96    const skip_windows = b.option(bool, "skip-windows", "Main test suite skips targets with windows OS") orelse false;
  97    const skip_darwin = b.option(bool, "skip-darwin", "Main test suite skips targets with darwin OSs") orelse false;
  98    const skip_linux = b.option(bool, "skip-linux", "Main test suite skips targets with linux OS") orelse false;
  99    const skip_llvm = b.option(bool, "skip-llvm", "Main test suite skips targets that use LLVM backend") orelse false;
 100    const skip_test_incremental = b.option(bool, "skip-test-incremental", "Main test step omits dependency on test-incremental step") orelse false;
 101
 102    const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
 103
 104    const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false;
 105    const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse static_llvm;
 106    const llvm_has_m68k = b.option(
 107        bool,
 108        "llvm-has-m68k",
 109        "Whether LLVM has the experimental target m68k enabled",
 110    ) orelse false;
 111    const llvm_has_csky = b.option(
 112        bool,
 113        "llvm-has-csky",
 114        "Whether LLVM has the experimental target csky enabled",
 115    ) orelse false;
 116    const llvm_has_arc = b.option(
 117        bool,
 118        "llvm-has-arc",
 119        "Whether LLVM has the experimental target arc enabled",
 120    ) orelse false;
 121    const llvm_has_xtensa = b.option(
 122        bool,
 123        "llvm-has-xtensa",
 124        "Whether LLVM has the experimental target xtensa enabled",
 125    ) orelse false;
 126    const enable_ios_sdk = b.option(bool, "enable-ios-sdk", "Run tests requiring presence of iOS SDK and frameworks") orelse false;
 127    const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse enable_ios_sdk;
 128    const enable_symlinks_windows = b.option(bool, "enable-symlinks-windows", "Run tests requiring presence of symlinks on Windows") orelse false;
 129    const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");
 130
 131    if (!skip_install_lib_files) {
 132        b.installDirectory(.{
 133            .source_dir = b.path("lib"),
 134            .install_dir = if (flat) .prefix else .lib,
 135            .install_subdir = if (flat) "lib" else "zig",
 136            .exclude_extensions = &[_][]const u8{
 137                // exclude files from lib/std/compress/testdata
 138                ".gz",
 139                ".z.0",
 140                ".z.9",
 141                ".zst.3",
 142                ".zst.19",
 143                "rfc1951.txt",
 144                "rfc1952.txt",
 145                "rfc8478.txt",
 146                // exclude files from lib/std/compress/flate/testdata
 147                ".expect",
 148                ".expect-noinput",
 149                ".golden",
 150                ".input",
 151                "compress-e.txt",
 152                "compress-gettysburg.txt",
 153                "compress-pi.txt",
 154                "rfc1951.txt",
 155                // exclude files from lib/std/compress/lzma/testdata
 156                ".lzma",
 157                // exclude files from lib/std/compress/xz/testdata
 158                ".xz",
 159                // exclude files from lib/std/tz/
 160                ".tzif",
 161                // exclude files from lib/std/tar/testdata
 162                ".tar",
 163                // others
 164                "README.md",
 165            },
 166            .blank_extensions = &[_][]const u8{
 167                "test.zig",
 168            },
 169        });
 170    }
 171
 172    if (only_install_lib_files)
 173        return;
 174
 175    const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS");
 176    const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
 177    const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
 178    const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
 179    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;
 180    const debug_gpa = b.option(bool, "debug-allocator", "Force the compiler to use DebugAllocator") orelse false;
 181    const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
 182    const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
 183    const strip = b.option(bool, "strip", "Omit debug information");
 184    const valgrind = b.option(bool, "valgrind", "Enable valgrind integration");
 185    const pie = b.option(bool, "pie", "Produce a Position Independent Executable");
 186    const value_interpret_mode = b.option(ValueInterpretMode, "value-interpret-mode", "How the compiler translates between 'std.builtin' types and its internal datastructures") orelse .direct;
 187    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;
 188
 189    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: {
 190        if (strip == true) break :blk @as(u32, 0);
 191        if (optimize != .Debug) break :blk 0;
 192        break :blk 4;
 193    };
 194
 195    const exe = addCompilerStep(b, .{
 196        .optimize = optimize,
 197        .target = target,
 198        .strip = strip,
 199        .valgrind = valgrind,
 200        .sanitize_thread = sanitize_thread,
 201        .single_threaded = single_threaded,
 202    });
 203    exe.pie = pie;
 204    exe.entitlements = entitlements;
 205    exe.use_new_linker = b.option(bool, "new-linker", "Use the new linker");
 206
 207    const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
 208    exe.use_llvm = use_llvm;
 209    exe.use_lld = use_llvm;
 210
 211    if (no_bin) {
 212        b.getInstallStep().dependOn(&exe.step);
 213    } else {
 214        const install_exe = b.addInstallArtifact(exe, .{
 215            .dest_dir = if (flat) .{ .override = .prefix } else .default,
 216        });
 217        b.getInstallStep().dependOn(&install_exe.step);
 218    }
 219
 220    test_step.dependOn(&exe.step);
 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, "debug_gpa", debug_gpa);
 233    exe_options.addOption(DevEnv, "dev", b.option(DevEnv, "dev", "Build a compiler with a reduced feature set for development of specific features") orelse if (only_c) .bootstrap else .full);
 234    exe_options.addOption(ValueInterpretMode, "value_interpret_mode", value_interpret_mode);
 235
 236    if (link_libc) {
 237        exe.root_module.link_libc = true;
 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", b.build_root.path orelse ".", // affects the --git-dir argument
 257            "--git-dir", ".git", // affected by the -C argument
 258            "describe", "--match",    "*.*.*", //
 259            "--tags",   "--abbrev=9",
 260        }, &code, .Ignore) catch {
 261            break :v version_string;
 262        };
 263        const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r");
 264
 265        switch (mem.countScalar(u8, git_describe, '-')) {
 266            0 => {
 267                // Tagged release version (e.g. 0.10.0).
 268                if (!mem.eql(u8, git_describe, version_string)) {
 269                    std.debug.print("Zig version '{s}' does not match Git tag '{s}'\n", .{ version_string, git_describe });
 270                    std.process.exit(1);
 271                }
 272                break :v version_string;
 273            },
 274            2 => {
 275                // Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
 276                var it = mem.splitScalar(u8, git_describe, '-');
 277                const tagged_ancestor = it.first();
 278                const commit_height = it.next().?;
 279                const commit_id = it.next().?;
 280
 281                const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
 282                if (zig_version.order(ancestor_ver) != .gt) {
 283                    std.debug.print("Zig version '{f}' must be greater than tagged ancestor '{f}'\n", .{ zig_version, ancestor_ver });
 284                    std.process.exit(1);
 285                }
 286
 287                // Check that the commit hash is prefixed with a 'g' (a Git convention).
 288                if (commit_id.len < 1 or commit_id[0] != 'g') {
 289                    std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
 290                    break :v version_string;
 291                }
 292
 293                // The version is reformatted in accordance with the https://semver.org specification.
 294                break :v b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] });
 295            },
 296            else => {
 297                std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
 298                break :v version_string;
 299            },
 300        }
 301    };
 302    const version = try b.allocator.dupeZ(u8, version_slice);
 303    exe_options.addOption([:0]const u8, "version", version);
 304
 305    if (enable_llvm) {
 306        const cmake_cfg = if (static_llvm) null else blk: {
 307            if (findConfigH(b, config_h_path_option)) |config_h_path| {
 308                const file_contents = fs.cwd().readFileAlloc(config_h_path, b.allocator, .limited(max_config_h_bytes)) catch unreachable;
 309                break :blk parseConfigH(b, file_contents);
 310            } else {
 311                std.log.warn("config.h could not be located automatically. Consider providing it explicitly via \"-Dconfig_h\"", .{});
 312                break :blk null;
 313            }
 314        };
 315
 316        if (cmake_cfg) |cfg| {
 317            // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD.
 318            // That means we also have to rely on stage1 compiled c++ files. We parse config.h to find
 319            // the information passed on to us from cmake.
 320            if (cfg.cmake_prefix_path.len > 0) {
 321                var it = mem.tokenizeScalar(u8, cfg.cmake_prefix_path, ';');
 322                while (it.next()) |path| {
 323                    b.addSearchPrefix(path);
 324                }
 325            }
 326
 327            try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);
 328        } else {
 329            // Here we are -Denable-llvm but no cmake integration.
 330            try addStaticLlvmOptionsToModule(exe.root_module, .{
 331                .llvm_has_m68k = llvm_has_m68k,
 332                .llvm_has_csky = llvm_has_csky,
 333                .llvm_has_arc = llvm_has_arc,
 334                .llvm_has_xtensa = llvm_has_xtensa,
 335            });
 336        }
 337        if (target.result.os.tag == .windows) {
 338            // LLVM depends on networking as of version 18.
 339            exe.root_module.linkSystemLibrary("ws2_32", .{});
 340
 341            exe.root_module.linkSystemLibrary("version", .{});
 342            exe.root_module.linkSystemLibrary("uuid", .{});
 343            exe.root_module.linkSystemLibrary("ole32", .{});
 344        }
 345    }
 346
 347    const semver = try std.SemanticVersion.parse(version);
 348    exe_options.addOption(std.SemanticVersion, "semver", semver);
 349
 350    exe_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
 351    exe_options.addOption(bool, "enable_logging", enable_logging);
 352    exe_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
 353    exe_options.addOption(bool, "enable_tracy", tracy != null);
 354    exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
 355    exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
 356    exe_options.addOption(u32, "tracy_callstack_depth", tracy_callstack_depth);
 357    exe_options.addOption(bool, "value_tracing", value_tracing);
 358    if (tracy) |tracy_path| {
 359        const client_cpp = b.pathJoin(
 360            &[_][]const u8{ tracy_path, "public", "TracyClient.cpp" },
 361        );
 362
 363        const tracy_c_flags: []const []const u8 = &.{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" };
 364
 365        exe.root_module.addIncludePath(.{ .cwd_relative = tracy_path });
 366        exe.root_module.addCSourceFile(.{ .file = .{ .cwd_relative = client_cpp }, .flags = tracy_c_flags });
 367        if (!enable_llvm) {
 368            exe.root_module.linkSystemLibrary("c++", .{ .use_pkg_config = .no });
 369        }
 370        exe.root_module.link_libc = true;
 371
 372        if (target.result.os.tag == .windows) {
 373            exe.root_module.linkSystemLibrary("dbghelp", .{});
 374            exe.root_module.linkSystemLibrary("ws2_32", .{});
 375        }
 376    }
 377
 378    const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match any filter") orelse &[0][]const u8{};
 379    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{};
 380    const test_extra_targets = b.option(bool, "test-extra-targets", "Enable running module tests for additional targets") orelse false;
 381
 382    var chosen_opt_modes_buf: [4]builtin.OptimizeMode = undefined;
 383    var chosen_mode_index: usize = 0;
 384    if (!skip_debug) {
 385        chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.Debug;
 386        chosen_mode_index += 1;
 387    }
 388    if (!skip_release_safe) {
 389        chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSafe;
 390        chosen_mode_index += 1;
 391    }
 392    if (!skip_release_fast) {
 393        chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseFast;
 394        chosen_mode_index += 1;
 395    }
 396    if (!skip_release_small) {
 397        chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSmall;
 398        chosen_mode_index += 1;
 399    }
 400    const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
 401
 402    const fmt_include_paths = &.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" };
 403    const fmt_exclude_paths = &.{ "test/cases", "test/behavior/zon" };
 404    const do_fmt = b.addFmt(.{
 405        .paths = fmt_include_paths,
 406        .exclude_paths = fmt_exclude_paths,
 407    });
 408    b.step("fmt", "Modify source files in place to have conforming formatting").dependOn(&do_fmt.step);
 409
 410    const check_fmt = b.step("test-fmt", "Check source files having conforming formatting");
 411    check_fmt.dependOn(&b.addFmt(.{
 412        .paths = fmt_include_paths,
 413        .exclude_paths = fmt_exclude_paths,
 414        .check = true,
 415    }).step);
 416    test_step.dependOn(check_fmt);
 417
 418    const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
 419    try tests.addCases(b, test_cases_step, .{
 420        .test_filters = test_filters,
 421        .test_target_filters = test_target_filters,
 422        .skip_compile_errors = skip_compile_errors,
 423        .skip_non_native = skip_non_native,
 424        .skip_freebsd = skip_freebsd,
 425        .skip_netbsd = skip_netbsd,
 426        .skip_windows = skip_windows,
 427        .skip_darwin = skip_darwin,
 428        .skip_linux = skip_linux,
 429        .skip_llvm = skip_llvm,
 430        .skip_libc = skip_libc,
 431    }, .{
 432        .enable_llvm = enable_llvm,
 433        .llvm_has_m68k = llvm_has_m68k,
 434        .llvm_has_csky = llvm_has_csky,
 435        .llvm_has_arc = llvm_has_arc,
 436        .llvm_has_xtensa = llvm_has_xtensa,
 437    });
 438    test_step.dependOn(test_cases_step);
 439
 440    const test_modules_step = b.step("test-modules", "Run the per-target module tests");
 441    test_step.dependOn(test_modules_step);
 442
 443    test_modules_step.dependOn(tests.addModuleTests(b, .{
 444        .test_filters = test_filters,
 445        .test_target_filters = test_target_filters,
 446        .test_extra_targets = test_extra_targets,
 447        .root_src = "test/behavior.zig",
 448        .name = "behavior",
 449        .desc = "Run the behavior tests",
 450        .optimize_modes = optimization_modes,
 451        .include_paths = &.{},
 452        .skip_single_threaded = skip_single_threaded,
 453        .skip_non_native = skip_non_native,
 454        .test_default_only = no_matrix,
 455        .skip_freebsd = skip_freebsd,
 456        .skip_netbsd = skip_netbsd,
 457        .skip_windows = skip_windows,
 458        .skip_darwin = skip_darwin,
 459        .skip_linux = skip_linux,
 460        .skip_llvm = skip_llvm,
 461        .skip_libc = skip_libc,
 462        // 3888779264 was observed on an x86_64-linux-gnu host.
 463        .max_rss = 4000000000,
 464    }));
 465
 466    test_modules_step.dependOn(tests.addModuleTests(b, .{
 467        .test_filters = test_filters,
 468        .test_target_filters = test_target_filters,
 469        .test_extra_targets = test_extra_targets,
 470        .root_src = "lib/compiler_rt.zig",
 471        .name = "compiler-rt",
 472        .desc = "Run the compiler_rt tests",
 473        .optimize_modes = optimization_modes,
 474        .include_paths = &.{},
 475        .skip_single_threaded = true,
 476        .skip_non_native = skip_non_native,
 477        .test_default_only = no_matrix,
 478        .skip_freebsd = skip_freebsd,
 479        .skip_netbsd = skip_netbsd,
 480        .skip_windows = skip_windows,
 481        .skip_darwin = skip_darwin,
 482        .skip_linux = skip_linux,
 483        .skip_llvm = skip_llvm,
 484        .skip_libc = true,
 485        .no_builtin = true,
 486    }));
 487
 488    test_modules_step.dependOn(tests.addModuleTests(b, .{
 489        .test_filters = test_filters,
 490        .test_target_filters = test_target_filters,
 491        .test_extra_targets = test_extra_targets,
 492        .root_src = "lib/c.zig",
 493        .name = "zigc",
 494        .desc = "Run the zigc tests",
 495        .optimize_modes = optimization_modes,
 496        .include_paths = &.{},
 497        .skip_single_threaded = true,
 498        .skip_non_native = skip_non_native,
 499        .test_default_only = no_matrix,
 500        .skip_freebsd = skip_freebsd,
 501        .skip_netbsd = skip_netbsd,
 502        .skip_windows = skip_windows,
 503        .skip_darwin = skip_darwin,
 504        .skip_linux = skip_linux,
 505        .skip_llvm = skip_llvm,
 506        .skip_libc = true,
 507        .no_builtin = true,
 508    }));
 509
 510    test_modules_step.dependOn(tests.addModuleTests(b, .{
 511        .test_filters = test_filters,
 512        .test_target_filters = test_target_filters,
 513        .test_extra_targets = test_extra_targets,
 514        .root_src = "lib/std/std.zig",
 515        .name = "std",
 516        .desc = "Run the standard library tests",
 517        .optimize_modes = optimization_modes,
 518        .include_paths = &.{},
 519        .skip_single_threaded = skip_single_threaded,
 520        .skip_non_native = skip_non_native,
 521        .test_default_only = no_matrix,
 522        .skip_freebsd = skip_freebsd,
 523        .skip_netbsd = skip_netbsd,
 524        .skip_windows = skip_windows,
 525        .skip_darwin = skip_darwin,
 526        .skip_linux = skip_linux,
 527        .skip_llvm = skip_llvm,
 528        .skip_libc = skip_libc,
 529        // I observed a value of 5605064704 on the M2 CI.
 530        .max_rss = 6165571174,
 531    }));
 532
 533    const unit_tests_step = b.step("test-unit", "Run the compiler source unit tests");
 534    test_step.dependOn(unit_tests_step);
 535
 536    const unit_tests = b.addTest(.{
 537        .root_module = addCompilerMod(b, .{
 538            .optimize = optimize,
 539            .target = target,
 540            .single_threaded = single_threaded,
 541        }),
 542        .filters = test_filters,
 543        .use_llvm = use_llvm,
 544        .use_lld = use_llvm,
 545        .zig_lib_dir = b.path("lib"),
 546    });
 547    if (link_libc) {
 548        unit_tests.root_module.link_libc = true;
 549    }
 550    unit_tests.root_module.addOptions("build_options", exe_options);
 551    unit_tests_step.dependOn(&b.addRunArtifact(unit_tests).step);
 552
 553    test_step.dependOn(tests.addStandaloneTests(
 554        b,
 555        optimization_modes,
 556        enable_macos_sdk,
 557        enable_ios_sdk,
 558        enable_symlinks_windows,
 559    ));
 560    test_step.dependOn(tests.addCAbiTests(b, .{
 561        .test_target_filters = test_target_filters,
 562        .skip_non_native = skip_non_native,
 563        .skip_freebsd = skip_freebsd,
 564        .skip_netbsd = skip_netbsd,
 565        .skip_windows = skip_windows,
 566        .skip_darwin = skip_darwin,
 567        .skip_linux = skip_linux,
 568        .skip_llvm = skip_llvm,
 569        .skip_release = skip_release,
 570    }));
 571    test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows));
 572    test_step.dependOn(tests.addStackTraceTests(b, test_filters, skip_non_native));
 573    test_step.dependOn(tests.addErrorTraceTests(b, test_filters, optimization_modes, skip_non_native));
 574    test_step.dependOn(tests.addCliTests(b));
 575    if (tests.addDebuggerTests(b, .{
 576        .test_filters = test_filters,
 577        .test_target_filters = test_target_filters,
 578        .gdb = b.option([]const u8, "gdb", "path to gdb binary"),
 579        .lldb = b.option([]const u8, "lldb", "path to lldb binary"),
 580        .optimize_modes = optimization_modes,
 581        .skip_single_threaded = skip_single_threaded,
 582        .skip_libc = skip_libc,
 583    })) |test_debugger_step| test_step.dependOn(test_debugger_step);
 584    if (tests.addLlvmIrTests(b, .{
 585        .enable_llvm = enable_llvm,
 586        .test_filters = test_filters,
 587        .test_target_filters = test_target_filters,
 588    })) |test_llvm_ir_step| test_step.dependOn(test_llvm_ir_step);
 589
 590    try addWasiUpdateStep(b, version);
 591
 592    const update_mingw_step = b.step("update-mingw", "Update zig's bundled mingw");
 593    const opt_mingw_src_path = b.option([]const u8, "mingw-src", "path to mingw-w64 source directory");
 594    if (opt_mingw_src_path) |mingw_src_path| {
 595        const update_mingw_exe = b.addExecutable(.{
 596            .name = "update_mingw",
 597            .root_module = b.createModule(.{
 598                .target = b.graph.host,
 599                .root_source_file = b.path("tools/update_mingw.zig"),
 600            }),
 601        });
 602        const update_mingw_run = b.addRunArtifact(update_mingw_exe);
 603        update_mingw_run.addDirectoryArg(b.path("lib"));
 604        update_mingw_run.addDirectoryArg(.{ .cwd_relative = mingw_src_path });
 605
 606        update_mingw_step.dependOn(&update_mingw_run.step);
 607    } else {
 608        update_mingw_step.dependOn(&b.addFail("The -Dmingw-src=... option is required for this step").step);
 609    }
 610
 611    const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases");
 612    try tests.addIncrementalTests(b, test_incremental_step);
 613    if (!skip_test_incremental) test_step.dependOn(test_incremental_step);
 614
 615    if (tests.addLibcTests(b, .{
 616        .optimize_modes = optimization_modes,
 617        .test_filters = test_filters,
 618        .test_target_filters = test_target_filters,
 619        // Highest RSS observed in any test case was exactly 1802878976 on x86_64-linux.
 620        .max_rss = 2253598720,
 621    })) |test_libc_step| test_step.dependOn(test_libc_step);
 622}
 623
 624fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
 625    const semver = try std.SemanticVersion.parse(version);
 626
 627    const exe = addCompilerStep(b, .{
 628        .optimize = .ReleaseSmall,
 629        .target = b.resolveTargetQuery(std.Target.Query.parse(.{
 630            .arch_os_abi = "wasm32-wasi",
 631            // * `extended_const` is not supported by the `wasm-opt` version in CI.
 632            // * `nontrapping_bulk_memory_len0` is supported by `wasm2c`.
 633            .cpu_features = "baseline-extended_const+nontrapping_bulk_memory_len0",
 634        }) catch unreachable),
 635    });
 636
 637    const exe_options = b.addOptions();
 638    exe.root_module.addOptions("build_options", exe_options);
 639
 640    exe_options.addOption(u32, "mem_leak_frames", 0);
 641    exe_options.addOption(bool, "have_llvm", false);
 642    exe_options.addOption(bool, "debug_gpa", false);
 643    exe_options.addOption([:0]const u8, "version", version);
 644    exe_options.addOption(std.SemanticVersion, "semver", semver);
 645    exe_options.addOption(bool, "enable_debug_extensions", false);
 646    exe_options.addOption(bool, "enable_logging", false);
 647    exe_options.addOption(bool, "enable_link_snapshots", false);
 648    exe_options.addOption(bool, "enable_tracy", false);
 649    exe_options.addOption(bool, "enable_tracy_callstack", false);
 650    exe_options.addOption(bool, "enable_tracy_allocation", false);
 651    exe_options.addOption(u32, "tracy_callstack_depth", 0);
 652    exe_options.addOption(bool, "value_tracing", false);
 653    exe_options.addOption(DevEnv, "dev", .bootstrap);
 654
 655    // zig1 chooses to interpret values by name. The tradeoff is as follows:
 656    //
 657    // * We lose a small amount of performance. This is essentially irrelevant for zig1.
 658    //
 659    // * We lose the ability to perform trivial renames on certain `std.builtin` types without
 660    //   zig1.wasm updates. For instance, we cannot rename an enum from PascalCase fields to
 661    //   snake_case fields without an update.
 662    //
 663    // * We gain the ability to add and remove fields to and from `std.builtin` types without
 664    //   zig1.wasm updates. For instance, we can add a new tag to `CallingConvention` without
 665    //   an update.
 666    //
 667    // Because field renames only happen when we apply a breaking change to the language (which
 668    // is becoming progressively rarer), but tags may be added to or removed from target-dependent
 669    // types over time in response to new targets coming into use, we gain more than we lose here.
 670    exe_options.addOption(ValueInterpretMode, "value_interpret_mode", .by_name);
 671
 672    const run_opt = b.addSystemCommand(&.{
 673        "wasm-opt",
 674        "-Oz",
 675        "--enable-bulk-memory",
 676        "--enable-mutable-globals",
 677        "--enable-nontrapping-float-to-int",
 678        "--enable-sign-ext",
 679    });
 680    run_opt.addArtifactArg(exe);
 681    run_opt.addArg("-o");
 682    const optimized_wasm = run_opt.addOutputFileArg("zig1.wasm");
 683
 684    const update_zig1 = b.addUpdateSourceFiles();
 685    update_zig1.addCopyFileToSource(optimized_wasm, "stage1/zig1.wasm");
 686    update_zig1.addCopyFileToSource(b.path("lib/zig.h"), "stage1/zig.h");
 687
 688    const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm");
 689    update_zig1_step.dependOn(&update_zig1.step);
 690}
 691
 692const AddCompilerModOptions = struct {
 693    optimize: std.builtin.OptimizeMode,
 694    target: std.Build.ResolvedTarget,
 695    strip: ?bool = null,
 696    valgrind: ?bool = null,
 697    sanitize_thread: ?bool = null,
 698    single_threaded: ?bool = null,
 699};
 700
 701fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Module {
 702    const compiler_mod = b.createModule(.{
 703        .root_source_file = b.path("src/main.zig"),
 704        .target = options.target,
 705        .optimize = options.optimize,
 706        .strip = options.strip,
 707        .sanitize_thread = options.sanitize_thread,
 708        .single_threaded = options.single_threaded,
 709        .valgrind = options.valgrind,
 710    });
 711
 712    const aro_mod = b.createModule(.{
 713        .root_source_file = b.path("lib/compiler/aro/aro.zig"),
 714    });
 715
 716    compiler_mod.addImport("aro", aro_mod);
 717
 718    return compiler_mod;
 719}
 720
 721fn addCompilerStep(b: *std.Build, options: AddCompilerModOptions) *std.Build.Step.Compile {
 722    const exe = b.addExecutable(.{
 723        .name = "zig",
 724        .max_rss = 7_800_000_000,
 725        .root_module = addCompilerMod(b, options),
 726    });
 727    exe.stack_size = stack_size;
 728
 729    return exe;
 730}
 731
 732const exe_cflags = [_][]const u8{
 733    "-std=c++17",
 734    "-D__STDC_CONSTANT_MACROS",
 735    "-D__STDC_FORMAT_MACROS",
 736    "-D__STDC_LIMIT_MACROS",
 737    "-D_GNU_SOURCE",
 738    "-fno-exceptions",
 739    "-fno-rtti",
 740    "-fno-stack-protector",
 741    "-fvisibility-inlines-hidden",
 742    "-Wno-type-limits",
 743    "-Wno-missing-braces",
 744    "-Wno-comment",
 745    // `exe_cflags` is only used for static linking.
 746    "-DLLVM_BUILD_STATIC",
 747    "-DCLANG_BUILD_STATIC",
 748};
 749
 750fn addCmakeCfgOptionsToExe(
 751    b: *std.Build,
 752    cfg: CMakeConfig,
 753    exe: *std.Build.Step.Compile,
 754    use_zig_libcxx: bool,
 755) !void {
 756    const mod = exe.root_module;
 757    const target = &mod.resolved_target.?.result;
 758
 759    if (target.os.tag.isDarwin()) {
 760        // useful for package maintainers
 761        exe.headerpad_max_install_names = true;
 762    }
 763
 764    mod.addObjectFile(.{ .cwd_relative = b.pathJoin(&.{
 765        cfg.cmake_binary_dir,
 766        "zigcpp",
 767        b.fmt("{s}{s}{s}", .{
 768            cfg.cmake_static_library_prefix,
 769            "zigcpp",
 770            cfg.cmake_static_library_suffix,
 771        }),
 772    }) });
 773    assert(cfg.lld_include_dir.len != 0);
 774    mod.addIncludePath(.{ .cwd_relative = cfg.lld_include_dir });
 775    mod.addIncludePath(.{ .cwd_relative = cfg.llvm_include_dir });
 776    mod.addLibraryPath(.{ .cwd_relative = cfg.llvm_lib_dir });
 777    addCMakeLibraryList(mod, cfg.clang_libraries);
 778    addCMakeLibraryList(mod, cfg.lld_libraries);
 779    addCMakeLibraryList(mod, cfg.llvm_libraries);
 780
 781    if (use_zig_libcxx) {
 782        mod.link_libcpp = true;
 783    } else {
 784        // System -lc++ must be used because in this code path we are attempting to link
 785        // against system-provided LLVM, Clang, LLD.
 786        const need_cpp_includes = true;
 787        const static = cfg.llvm_linkage == .static;
 788        const lib_suffix = if (static) target.staticLibSuffix()[1..] else target.dynamicLibSuffix()[1..];
 789        switch (target.os.tag) {
 790            .linux => {
 791                // First we try to link against the detected libcxx name. If that doesn't work, we fall
 792                // back to -lc++ and cross our fingers.
 793                addCxxKnownPath(b, cfg, exe, b.fmt("lib{s}.{s}", .{ cfg.system_libcxx, lib_suffix }), "", need_cpp_includes) catch |err| switch (err) {
 794                    error.RequiredLibraryNotFound => {
 795                        mod.link_libcpp = true;
 796                    },
 797                    else => |e| return e,
 798                };
 799                mod.linkSystemLibrary("unwind", .{});
 800            },
 801            .ios, .macos, .watchos, .tvos, .visionos => {
 802                mod.link_libcpp = true;
 803            },
 804            .windows => {
 805                if (target.abi != .msvc) mod.link_libcpp = true;
 806            },
 807            .freebsd => {
 808                try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
 809                if (static) try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
 810            },
 811            .openbsd => {
 812                // - llvm requires libexecinfo which has conflicting symbols with libc++abi
 813                // - only an issue with .a linking
 814                // - workaround is to link c++abi dynamically
 815                try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{target.dynamicLibSuffix()[1..]}), null, need_cpp_includes);
 816                try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{target.dynamicLibSuffix()[1..]}), null, need_cpp_includes);
 817            },
 818            .netbsd, .dragonfly => {
 819                try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
 820                if (static) try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
 821            },
 822            .illumos => {
 823                try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
 824                try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
 825            },
 826            .haiku => {
 827                try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
 828            },
 829            else => {},
 830        }
 831    }
 832
 833    if (cfg.dia_guids_lib.len != 0) {
 834        mod.addObjectFile(.{ .cwd_relative = cfg.dia_guids_lib });
 835    }
 836}
 837
 838fn addStaticLlvmOptionsToModule(mod: *std.Build.Module, options: struct {
 839    llvm_has_m68k: bool,
 840    llvm_has_csky: bool,
 841    llvm_has_arc: bool,
 842    llvm_has_xtensa: bool,
 843}) !void {
 844    // Adds the Zig C++ sources which both stage1 and stage2 need.
 845    //
 846    // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
 847    // in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
 848    // unavailable when LLVM is compiled in Release mode.
 849    const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
 850    mod.addCSourceFiles(.{
 851        .files = &zig_cpp_sources,
 852        .flags = &zig_cpp_cflags,
 853    });
 854
 855    for (clang_libs) |lib_name| {
 856        mod.linkSystemLibrary(lib_name, .{});
 857    }
 858
 859    for (lld_libs) |lib_name| {
 860        mod.linkSystemLibrary(lib_name, .{});
 861    }
 862
 863    for (llvm_libs) |lib_name| {
 864        mod.linkSystemLibrary(lib_name, .{});
 865    }
 866
 867    if (options.llvm_has_m68k) for (llvm_libs_m68k) |lib_name| {
 868        mod.linkSystemLibrary(lib_name, .{});
 869    };
 870
 871    if (options.llvm_has_csky) for (llvm_libs_csky) |lib_name| {
 872        mod.linkSystemLibrary(lib_name, .{});
 873    };
 874
 875    if (options.llvm_has_arc) for (llvm_libs_arc) |lib_name| {
 876        mod.linkSystemLibrary(lib_name, .{});
 877    };
 878
 879    if (options.llvm_has_xtensa) for (llvm_libs_xtensa) |lib_name| {
 880        mod.linkSystemLibrary(lib_name, .{});
 881    };
 882
 883    mod.linkSystemLibrary("z", .{});
 884    mod.linkSystemLibrary("zstd", .{});
 885
 886    if (mod.resolved_target.?.result.os.tag != .windows or mod.resolved_target.?.result.abi != .msvc) {
 887        // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
 888        mod.linkSystemLibrary("c++", .{});
 889    }
 890
 891    if (mod.resolved_target.?.result.os.tag == .windows) {
 892        mod.linkSystemLibrary("version", .{});
 893        mod.linkSystemLibrary("uuid", .{});
 894        mod.linkSystemLibrary("ole32", .{});
 895    }
 896}
 897
 898fn addCxxKnownPath(
 899    b: *std.Build,
 900    ctx: CMakeConfig,
 901    exe: *std.Build.Step.Compile,
 902    objname: []const u8,
 903    errtxt: ?[]const u8,
 904    need_cpp_includes: bool,
 905) !void {
 906    if (!std.process.can_spawn)
 907        return error.RequiredLibraryNotFound;
 908
 909    const path_padded = run: {
 910        var args = std.array_list.Managed([]const u8).init(b.allocator);
 911        try args.append(ctx.cxx_compiler);
 912        var it = std.mem.tokenizeAny(u8, ctx.cxx_compiler_arg1, &std.ascii.whitespace);
 913        while (it.next()) |arg| try args.append(arg);
 914        try args.append(b.fmt("-print-file-name={s}", .{objname}));
 915        break :run b.run(args.items);
 916    };
 917    var tokenizer = mem.tokenizeAny(u8, path_padded, "\r\n");
 918    const path_unpadded = tokenizer.next().?;
 919    if (mem.eql(u8, path_unpadded, objname)) {
 920        if (errtxt) |msg| {
 921            std.debug.print("{s}", .{msg});
 922        } else {
 923            std.debug.print("Unable to determine path to {s}\n", .{objname});
 924        }
 925        return error.RequiredLibraryNotFound;
 926    }
 927    // By default, explicit library paths are not checked for being linker scripts,
 928    // but libc++ may very well be one, so force all inputs to be checked when passing
 929    // an explicit path to libc++.
 930    exe.allow_so_scripts = true;
 931    exe.root_module.addObjectFile(.{ .cwd_relative = path_unpadded });
 932
 933    // TODO a way to integrate with system c++ include files here
 934    // c++ -E -Wp,-v -xc++ /dev/null
 935    if (need_cpp_includes) {
 936        // I used these temporarily for testing something but we obviously need a
 937        // more general purpose solution here.
 938        //exe.root_module.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0");
 939        //exe.root_module.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0/x86_64-unknown-linux-gnu");
 940    }
 941}
 942
 943fn addCMakeLibraryList(mod: *std.Build.Module, list: []const u8) void {
 944    var it = mem.tokenizeScalar(u8, list, ';');
 945    while (it.next()) |lib| {
 946        if (mem.startsWith(u8, lib, "-l")) {
 947            mod.linkSystemLibrary(lib["-l".len..], .{});
 948        } else if (mod.resolved_target.?.result.os.tag == .windows and
 949            mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib))
 950        {
 951            mod.linkSystemLibrary(lib[0 .. lib.len - ".lib".len], .{});
 952        } else {
 953            mod.addObjectFile(.{ .cwd_relative = lib });
 954        }
 955    }
 956}
 957
 958const CMakeConfig = struct {
 959    llvm_linkage: std.builtin.LinkMode,
 960    cmake_binary_dir: []const u8,
 961    cmake_prefix_path: []const u8,
 962    cmake_static_library_prefix: []const u8,
 963    cmake_static_library_suffix: []const u8,
 964    cxx_compiler: []const u8,
 965    cxx_compiler_arg1: []const u8,
 966    lld_include_dir: []const u8,
 967    lld_libraries: []const u8,
 968    clang_libraries: []const u8,
 969    llvm_lib_dir: []const u8,
 970    llvm_include_dir: []const u8,
 971    llvm_libraries: []const u8,
 972    dia_guids_lib: []const u8,
 973    system_libcxx: []const u8,
 974};
 975
 976const max_config_h_bytes = 1 * 1024 * 1024;
 977
 978fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
 979    if (config_h_path_option) |path| {
 980        var config_h_or_err = fs.cwd().openFile(path, .{});
 981        if (config_h_or_err) |*file| {
 982            file.close();
 983            return path;
 984        } else |_| {
 985            std.log.err("Could not open provided config.h: \"{s}\"", .{path});
 986            std.process.exit(1);
 987        }
 988    }
 989
 990    var check_dir = fs.path.dirname(b.graph.zig_exe).?;
 991    while (true) {
 992        var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
 993        defer dir.close();
 994
 995        // Check if config.h is present in dir
 996        var config_h_or_err = dir.openFile("config.h", .{});
 997        if (config_h_or_err) |*file| {
 998            file.close();
 999            return fs.path.join(
1000                b.allocator,
1001                &[_][]const u8{ check_dir, "config.h" },
1002            ) catch unreachable;
1003        } else |e| switch (e) {
1004            error.FileNotFound => {},
1005            else => unreachable,
1006        }
1007
1008        // Check if we reached the source root by looking for .git, and bail if so
1009        var git_dir_or_err = dir.openDir(".git", .{});
1010        if (git_dir_or_err) |*git_dir| {
1011            git_dir.close();
1012            return null;
1013        } else |_| {}
1014
1015        // Otherwise, continue search in the parent directory
1016        const new_check_dir = fs.path.dirname(check_dir);
1017        if (new_check_dir == null or mem.eql(u8, new_check_dir.?, check_dir)) {
1018            return null;
1019        }
1020        check_dir = new_check_dir.?;
1021    }
1022}
1023
1024fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
1025    var ctx: CMakeConfig = .{
1026        .llvm_linkage = undefined,
1027        .cmake_binary_dir = undefined,
1028        .cmake_prefix_path = undefined,
1029        .cmake_static_library_prefix = undefined,
1030        .cmake_static_library_suffix = undefined,
1031        .cxx_compiler = undefined,
1032        .cxx_compiler_arg1 = "",
1033        .lld_include_dir = undefined,
1034        .lld_libraries = undefined,
1035        .clang_libraries = undefined,
1036        .llvm_lib_dir = undefined,
1037        .llvm_include_dir = undefined,
1038        .llvm_libraries = undefined,
1039        .dia_guids_lib = undefined,
1040        .system_libcxx = undefined,
1041    };
1042
1043    const mappings = [_]struct { prefix: []const u8, field: []const u8 }{
1044        .{
1045            .prefix = "#define ZIG_CMAKE_BINARY_DIR ",
1046            .field = "cmake_binary_dir",
1047        },
1048        .{
1049            .prefix = "#define ZIG_CMAKE_PREFIX_PATH ",
1050            .field = "cmake_prefix_path",
1051        },
1052        .{
1053            .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_PREFIX ",
1054            .field = "cmake_static_library_prefix",
1055        },
1056        .{
1057            .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_SUFFIX ",
1058            .field = "cmake_static_library_suffix",
1059        },
1060        .{
1061            .prefix = "#define ZIG_CXX_COMPILER ",
1062            .field = "cxx_compiler",
1063        },
1064        .{
1065            .prefix = "#define ZIG_CXX_COMPILER_ARG1 ",
1066            .field = "cxx_compiler_arg1",
1067        },
1068        .{
1069            .prefix = "#define ZIG_LLD_INCLUDE_PATH ",
1070            .field = "lld_include_dir",
1071        },
1072        .{
1073            .prefix = "#define ZIG_LLD_LIBRARIES ",
1074            .field = "lld_libraries",
1075        },
1076        .{
1077            .prefix = "#define ZIG_CLANG_LIBRARIES ",
1078            .field = "clang_libraries",
1079        },
1080        .{
1081            .prefix = "#define ZIG_LLVM_LIBRARIES ",
1082            .field = "llvm_libraries",
1083        },
1084        .{
1085            .prefix = "#define ZIG_DIA_GUIDS_LIB ",
1086            .field = "dia_guids_lib",
1087        },
1088        .{
1089            .prefix = "#define ZIG_LLVM_INCLUDE_PATH ",
1090            .field = "llvm_include_dir",
1091        },
1092        .{
1093            .prefix = "#define ZIG_LLVM_LIB_PATH ",
1094            .field = "llvm_lib_dir",
1095        },
1096        .{
1097            .prefix = "#define ZIG_SYSTEM_LIBCXX",
1098            .field = "system_libcxx",
1099        },
1100        // .prefix = ZIG_LLVM_LINK_MODE parsed manually below
1101    };
1102
1103    var lines_it = mem.tokenizeAny(u8, config_h_text, "\r\n");
1104    while (lines_it.next()) |line| {
1105        inline for (mappings) |mapping| {
1106            if (mem.startsWith(u8, line, mapping.prefix)) {
1107                var it = mem.splitScalar(u8, line, '"');
1108                _ = it.first(); // skip the stuff before the quote
1109                const quoted = it.next().?; // the stuff inside the quote
1110                const trimmed = mem.trim(u8, quoted, " ");
1111                @field(ctx, mapping.field) = toNativePathSep(b, trimmed);
1112            }
1113        }
1114        if (mem.startsWith(u8, line, "#define ZIG_LLVM_LINK_MODE ")) {
1115            var it = mem.splitScalar(u8, line, '"');
1116            _ = it.next().?; // skip the stuff before the quote
1117            const quoted = it.next().?; // the stuff inside the quote
1118            ctx.llvm_linkage = if (mem.eql(u8, quoted, "shared")) .dynamic else .static;
1119        }
1120    }
1121    return ctx;
1122}
1123
1124fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
1125    const duplicated = b.allocator.dupe(u8, s) catch unreachable;
1126    for (duplicated) |*byte| switch (byte.*) {
1127        '/' => byte.* = fs.path.sep,
1128        else => {},
1129    };
1130    return duplicated;
1131}
1132
1133const zig_cpp_sources = [_][]const u8{
1134    // These are planned to stay even when we are self-hosted.
1135    "src/zig_llvm.cpp",
1136    "src/zig_llvm-ar.cpp",
1137    "src/zig_clang_driver.cpp",
1138    "src/zig_clang_cc1_main.cpp",
1139    "src/zig_clang_cc1as_main.cpp",
1140};
1141
1142const clang_libs = [_][]const u8{
1143    "clangFrontendTool",
1144    "clangCodeGen",
1145    "clangFrontend",
1146    "clangDriver",
1147    "clangSerialization",
1148    "clangSema",
1149    "clangStaticAnalyzerFrontend",
1150    "clangStaticAnalyzerCheckers",
1151    "clangStaticAnalyzerCore",
1152    "clangAnalysis",
1153    "clangASTMatchers",
1154    "clangAST",
1155    "clangParse",
1156    "clangSema",
1157    "clangAPINotes",
1158    "clangBasic",
1159    "clangEdit",
1160    "clangLex",
1161    "clangRewriteFrontend",
1162    "clangRewrite",
1163    "clangCrossTU",
1164    "clangIndex",
1165    "clangToolingCore",
1166    "clangExtractAPI",
1167    "clangSupport",
1168    "clangInstallAPI",
1169    "clangAST",
1170};
1171const lld_libs = [_][]const u8{
1172    "lldMinGW",
1173    "lldELF",
1174    "lldCOFF",
1175    "lldWasm",
1176    "lldMachO",
1177    "lldCommon",
1178};
1179// This list can be re-generated with `llvm-config --libfiles` and then
1180// reformatting using your favorite text editor. Note we do not execute
1181// `llvm-config` here because we are cross compiling. Also omit LLVMTableGen
1182// from these libs.
1183const llvm_libs = [_][]const u8{
1184    "LLVMWindowsManifest",
1185    "LLVMXRay",
1186    "LLVMLibDriver",
1187    "LLVMDlltoolDriver",
1188    "LLVMTelemetry",
1189    "LLVMTextAPIBinaryReader",
1190    "LLVMCoverage",
1191    "LLVMLineEditor",
1192    "LLVMXCoreDisassembler",
1193    "LLVMXCoreCodeGen",
1194    "LLVMXCoreDesc",
1195    "LLVMXCoreInfo",
1196    "LLVMX86TargetMCA",
1197    "LLVMX86Disassembler",
1198    "LLVMX86AsmParser",
1199    "LLVMX86CodeGen",
1200    "LLVMX86Desc",
1201    "LLVMX86Info",
1202    "LLVMWebAssemblyDisassembler",
1203    "LLVMWebAssemblyAsmParser",
1204    "LLVMWebAssemblyCodeGen",
1205    "LLVMWebAssemblyUtils",
1206    "LLVMWebAssemblyDesc",
1207    "LLVMWebAssemblyInfo",
1208    "LLVMVEDisassembler",
1209    "LLVMVEAsmParser",
1210    "LLVMVECodeGen",
1211    "LLVMVEDesc",
1212    "LLVMVEInfo",
1213    "LLVMSystemZDisassembler",
1214    "LLVMSystemZAsmParser",
1215    "LLVMSystemZCodeGen",
1216    "LLVMSystemZDesc",
1217    "LLVMSystemZInfo",
1218    "LLVMSPIRVCodeGen",
1219    "LLVMSPIRVDesc",
1220    "LLVMSPIRVInfo",
1221    "LLVMSPIRVAnalysis",
1222    "LLVMSparcDisassembler",
1223    "LLVMSparcAsmParser",
1224    "LLVMSparcCodeGen",
1225    "LLVMSparcDesc",
1226    "LLVMSparcInfo",
1227    "LLVMRISCVTargetMCA",
1228    "LLVMRISCVDisassembler",
1229    "LLVMRISCVAsmParser",
1230    "LLVMRISCVCodeGen",
1231    "LLVMRISCVDesc",
1232    "LLVMRISCVInfo",
1233    "LLVMPowerPCDisassembler",
1234    "LLVMPowerPCAsmParser",
1235    "LLVMPowerPCCodeGen",
1236    "LLVMPowerPCDesc",
1237    "LLVMPowerPCInfo",
1238    "LLVMNVPTXCodeGen",
1239    "LLVMNVPTXDesc",
1240    "LLVMNVPTXInfo",
1241    "LLVMMSP430Disassembler",
1242    "LLVMMSP430AsmParser",
1243    "LLVMMSP430CodeGen",
1244    "LLVMMSP430Desc",
1245    "LLVMMSP430Info",
1246    "LLVMMipsDisassembler",
1247    "LLVMMipsAsmParser",
1248    "LLVMMipsCodeGen",
1249    "LLVMMipsDesc",
1250    "LLVMMipsInfo",
1251    "LLVMLoongArchDisassembler",
1252    "LLVMLoongArchAsmParser",
1253    "LLVMLoongArchCodeGen",
1254    "LLVMLoongArchDesc",
1255    "LLVMLoongArchInfo",
1256    "LLVMLanaiDisassembler",
1257    "LLVMLanaiCodeGen",
1258    "LLVMLanaiAsmParser",
1259    "LLVMLanaiDesc",
1260    "LLVMLanaiInfo",
1261    "LLVMHexagonDisassembler",
1262    "LLVMHexagonCodeGen",
1263    "LLVMHexagonAsmParser",
1264    "LLVMHexagonDesc",
1265    "LLVMHexagonInfo",
1266    "LLVMBPFDisassembler",
1267    "LLVMBPFAsmParser",
1268    "LLVMBPFCodeGen",
1269    "LLVMBPFDesc",
1270    "LLVMBPFInfo",
1271    "LLVMAVRDisassembler",
1272    "LLVMAVRAsmParser",
1273    "LLVMAVRCodeGen",
1274    "LLVMAVRDesc",
1275    "LLVMAVRInfo",
1276    "LLVMARMDisassembler",
1277    "LLVMARMAsmParser",
1278    "LLVMARMCodeGen",
1279    "LLVMARMDesc",
1280    "LLVMARMUtils",
1281    "LLVMARMInfo",
1282    "LLVMAMDGPUTargetMCA",
1283    "LLVMAMDGPUDisassembler",
1284    "LLVMAMDGPUAsmParser",
1285    "LLVMAMDGPUCodeGen",
1286    "LLVMAMDGPUDesc",
1287    "LLVMAMDGPUUtils",
1288    "LLVMAMDGPUInfo",
1289    "LLVMAArch64Disassembler",
1290    "LLVMAArch64AsmParser",
1291    "LLVMAArch64CodeGen",
1292    "LLVMAArch64Desc",
1293    "LLVMAArch64Utils",
1294    "LLVMAArch64Info",
1295    "LLVMOrcDebugging",
1296    "LLVMOrcJIT",
1297    "LLVMWindowsDriver",
1298    "LLVMMCJIT",
1299    "LLVMJITLink",
1300    "LLVMInterpreter",
1301    "LLVMExecutionEngine",
1302    "LLVMRuntimeDyld",
1303    "LLVMOrcTargetProcess",
1304    "LLVMOrcShared",
1305    "LLVMDWP",
1306    "LLVMDWARFCFIChecker",
1307    "LLVMDebugInfoLogicalView",
1308    "LLVMOption",
1309    "LLVMObjCopy",
1310    "LLVMMCA",
1311    "LLVMMCDisassembler",
1312    "LLVMLTO",
1313    "LLVMFrontendOpenACC",
1314    "LLVMFrontendHLSL",
1315    "LLVMFrontendDriver",
1316    "LLVMExtensions",
1317    "LLVMPasses",
1318    "LLVMHipStdPar",
1319    "LLVMCoroutines",
1320    "LLVMCFGuard",
1321    "LLVMipo",
1322    "LLVMInstrumentation",
1323    "LLVMVectorize",
1324    "LLVMSandboxIR",
1325    "LLVMLinker",
1326    "LLVMFrontendOpenMP",
1327    "LLVMFrontendDirective",
1328    "LLVMFrontendAtomic",
1329    "LLVMFrontendOffloading",
1330    "LLVMObjectYAML",
1331    "LLVMDWARFLinkerParallel",
1332    "LLVMDWARFLinkerClassic",
1333    "LLVMDWARFLinker",
1334    "LLVMGlobalISel",
1335    "LLVMMIRParser",
1336    "LLVMAsmPrinter",
1337    "LLVMSelectionDAG",
1338    "LLVMCodeGen",
1339    "LLVMTarget",
1340    "LLVMObjCARCOpts",
1341    "LLVMCodeGenTypes",
1342    "LLVMCGData",
1343    "LLVMIRPrinter",
1344    "LLVMInterfaceStub",
1345    "LLVMFileCheck",
1346    "LLVMFuzzMutate",
1347    "LLVMScalarOpts",
1348    "LLVMInstCombine",
1349    "LLVMAggressiveInstCombine",
1350    "LLVMTransformUtils",
1351    "LLVMBitWriter",
1352    "LLVMAnalysis",
1353    "LLVMProfileData",
1354    "LLVMSymbolize",
1355    "LLVMDebugInfoBTF",
1356    "LLVMDebugInfoPDB",
1357    "LLVMDebugInfoMSF",
1358    "LLVMDebugInfoCodeView",
1359    "LLVMDebugInfoGSYM",
1360    "LLVMDebugInfoDWARF",
1361    "LLVMDebugInfoDWARFLowLevel",
1362    "LLVMObject",
1363    "LLVMTextAPI",
1364    "LLVMMCParser",
1365    "LLVMIRReader",
1366    "LLVMAsmParser",
1367    "LLVMMC",
1368    "LLVMBitReader",
1369    "LLVMFuzzerCLI",
1370    "LLVMCore",
1371    "LLVMRemarks",
1372    "LLVMBitstreamReader",
1373    "LLVMBinaryFormat",
1374    "LLVMTargetParser",
1375    "LLVMSupport",
1376    "LLVMDemangle",
1377};
1378const llvm_libs_m68k = [_][]const u8{
1379    "LLVMM68kDisassembler",
1380    "LLVMM68kAsmParser",
1381    "LLVMM68kCodeGen",
1382    "LLVMM68kDesc",
1383    "LLVMM68kInfo",
1384};
1385const llvm_libs_csky = [_][]const u8{
1386    "LLVMCSKYDisassembler",
1387    "LLVMCSKYAsmParser",
1388    "LLVMCSKYCodeGen",
1389    "LLVMCSKYDesc",
1390    "LLVMCSKYInfo",
1391};
1392const llvm_libs_arc = [_][]const u8{
1393    "LLVMARCDisassembler",
1394    "LLVMARCCodeGen",
1395    "LLVMARCDesc",
1396    "LLVMARCInfo",
1397};
1398const llvm_libs_xtensa = [_][]const u8{
1399    "LLVMXtensaDisassembler",
1400    "LLVMXtensaAsmParser",
1401    "LLVMXtensaCodeGen",
1402    "LLVMXtensaDesc",
1403    "LLVMXtensaInfo",
1404};
1405
1406fn generateLangRef(b: *std.Build) std.Build.LazyPath {
1407    const doctest_exe = b.addExecutable(.{
1408        .name = "doctest",
1409        .root_module = b.createModule(.{
1410            .root_source_file = b.path("tools/doctest.zig"),
1411            .target = b.graph.host,
1412            .optimize = .Debug,
1413        }),
1414    });
1415
1416    var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
1417        std.debug.panic("unable to open '{f}doc/langref' directory: {s}", .{
1418            b.build_root, @errorName(err),
1419        });
1420    };
1421    defer dir.close();
1422
1423    var wf = b.addWriteFiles();
1424
1425    var it = dir.iterateAssumeFirstIteration();
1426    while (it.next() catch @panic("failed to read dir")) |entry| {
1427        if (std.mem.startsWith(u8, entry.name, ".") or entry.kind != .file)
1428            continue;
1429
1430        const out_basename = b.fmt("{s}.out", .{std.fs.path.stem(entry.name)});
1431        const cmd = b.addRunArtifact(doctest_exe);
1432        cmd.addArgs(&.{
1433            "--zig",        b.graph.zig_exe,
1434            // TODO: enhance doctest to use "--listen=-" rather than operating
1435            // in a temporary directory
1436            "--cache-root", b.cache_root.path orelse ".",
1437        });
1438        cmd.addArgs(&.{ "--zig-lib-dir", b.fmt("{f}", .{b.graph.zig_lib_directory}) });
1439        cmd.addArgs(&.{"-i"});
1440        cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name})));
1441
1442        cmd.addArgs(&.{"-o"});
1443        _ = wf.addCopyFile(cmd.addOutputFileArg(out_basename), out_basename);
1444    }
1445
1446    const docgen_exe = b.addExecutable(.{
1447        .name = "docgen",
1448        .root_module = b.createModule(.{
1449            .root_source_file = b.path("tools/docgen.zig"),
1450            .target = b.graph.host,
1451            .optimize = .Debug,
1452        }),
1453    });
1454
1455    const docgen_cmd = b.addRunArtifact(docgen_exe);
1456    docgen_cmd.addArgs(&.{"--code-dir"});
1457    docgen_cmd.addDirectoryArg(wf.getDirectory());
1458
1459    docgen_cmd.addFileArg(b.path("doc/langref.html.in"));
1460    return docgen_cmd.addOutputFileArg("langref.html");
1461}
1462
1463fn superHtmlCheck(b: *std.Build, html_file: std.Build.LazyPath) *std.Build.Step {
1464    const run_superhtml = b.addSystemCommand(&.{
1465        "superhtml", "check",
1466    });
1467    run_superhtml.addFileArg(html_file);
1468    run_superhtml.expectExitCode(0);
1469    return &run_superhtml.step;
1470}