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