1 const std = @import("std");
2 const builtin = std.builtin;
3 const tests = @import("test/tests.zig");
4 const BufMap = std.BufMap;
6 const ArrayList = std.ArrayList;
9 const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
10 const assert = std.debug.assert;
11 const DevEnv = @import("src/dev.zig").Env;
12 const ValueInterpretMode = enum { direct, by_name };
14 const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 14, .patch = 0 };
15 const stack_size = 46 * 1024 * 1024;
17 pub fn build(b: *std.Build) !void {
18 const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
19 const target = b.standardTargetOptions(.{
21 .ofmt = if (only_c) .c else null,
24 const optimize = b.standardOptimizeOption(.{});
26 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;
27 const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode");
28 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;
30 const test_step = b.step("test", "Run all the tests");
31 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;
32 const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;
33 const std_docs = b.option(bool, "std-docs", "include standard library autodocs") orelse false;
34 const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;
35 const enable_superhtml = b.option(bool, "enable-superhtml", "Check langref output HTML validity") orelse false;
37 const langref_file = generateLangRef(b);
38 const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html");
39 const check_langref = superHtmlCheck(b, langref_file);
40 if (enable_superhtml) install_langref.step.dependOn(check_langref);
42 const check_autodocs = superHtmlCheck(b, b.path("lib/docs/index.html"));
43 if (enable_superhtml) {
44 test_step.dependOn(check_langref);
45 test_step.dependOn(check_autodocs);
47 if (!skip_install_langref) {
48 b.getInstallStep().dependOn(&install_langref.step);
51 const autodoc_test = b.addObject(.{
53 .zig_lib_dir = b.path("lib"),
54 .root_module = b.createModule(.{
55 .root_source_file = b.path("lib/std/std.zig"),
60 const install_std_docs = b.addInstallDirectory(.{
61 .source_dir = autodoc_test.getEmittedDocs(),
62 .install_dir = .prefix,
63 .install_subdir = "doc/std",
65 //if (enable_tidy) install_std_docs.step.dependOn(check_autodocs);
67 b.getInstallStep().dependOn(&install_std_docs.step);
71 b.installFile("LICENSE", "LICENSE");
72 b.installFile("README.md", "README.md");
75 const langref_step = b.step("langref", "Build and install the language reference");
76 langref_step.dependOn(&install_langref.step);
78 const std_docs_step = b.step("std-docs", "Build and install the standard library documentation");
79 std_docs_step.dependOn(&install_std_docs.step);
81 const docs_step = b.step("docs", "Build and install documentation");
82 docs_step.dependOn(langref_step);
83 docs_step.dependOn(std_docs_step);
85 const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
86 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
87 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
88 const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
89 const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release;
90 const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;
91 const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
92 const skip_single_threaded = b.option(bool, "skip-single-threaded", "Main test suite skips tests that are single-threaded") orelse false;
93 const skip_translate_c = b.option(bool, "skip-translate-c", "Main test suite skips translate-c tests") orelse false;
94 const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false;
96 const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
98 const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false;
99 const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse static_llvm;
100 const llvm_has_m68k = b.option(
103 "Whether LLVM has the experimental target m68k enabled",
105 const llvm_has_csky = b.option(
108 "Whether LLVM has the experimental target csky enabled",
110 const llvm_has_arc = b.option(
113 "Whether LLVM has the experimental target arc enabled",
115 const llvm_has_xtensa = b.option(
118 "Whether LLVM has the experimental target xtensa enabled",
120 const enable_ios_sdk = b.option(bool, "enable-ios-sdk", "Run tests requiring presence of iOS SDK and frameworks") orelse false;
121 const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse enable_ios_sdk;
122 const enable_symlinks_windows = b.option(bool, "enable-symlinks-windows", "Run tests requiring presence of symlinks on Windows") orelse false;
123 const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");
125 if (!skip_install_lib_files) {
126 b.installDirectory(.{
127 .source_dir = b.path("lib"),
128 .install_dir = if (flat) .prefix else .lib,
129 .install_subdir = if (flat) "lib" else "zig",
130 .exclude_extensions = &[_][]const u8{
131 // exclude files from lib/std/compress/testdata
140 // exclude files from lib/std/compress/flate/testdata
146 "compress-gettysburg.txt",
149 // exclude files from lib/std/compress/lzma/testdata
151 // exclude files from lib/std/compress/xz/testdata
153 // exclude files from lib/std/tz/
155 // exclude files from lib/std/tar/testdata
160 .blank_extensions = &[_][]const u8{
166 if (only_install_lib_files)
169 const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS");
170 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
171 const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
172 const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
173 const 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;
174 const force_gpa = b.option(bool, "force-gpa", "Force the compiler to use GeneralPurposeAllocator") orelse false;
175 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
176 const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
177 const strip = b.option(bool, "strip", "Omit debug information");
178 const valgrind = b.option(bool, "valgrind", "Enable valgrind integration");
179 const pie = b.option(bool, "pie", "Produce a Position Independent Executable");
180 const value_interpret_mode = b.option(ValueInterpretMode, "value-interpret-mode", "How the compiler translates between 'std.builtin' types and its internal datastructures") orelse .direct;
181 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;
183 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: {
184 if (strip == true) break :blk @as(u32, 0);
185 if (optimize != .Debug) break :blk 0;
189 const exe = addCompilerStep(b, .{
190 .optimize = optimize,
193 .valgrind = valgrind,
194 .sanitize_thread = sanitize_thread,
195 .single_threaded = single_threaded,
198 exe.entitlements = entitlements;
200 exe.build_id = b.option(
203 "Request creation of '.note.gnu.build-id' section",
207 b.getInstallStep().dependOn(&exe.step);
209 const install_exe = b.addInstallArtifact(exe, .{
210 .dest_dir = if (flat) .{ .override = .prefix } else .default,
212 b.getInstallStep().dependOn(&install_exe.step);
215 test_step.dependOn(&exe.step);
217 if (target.result.os.tag == .windows and target.result.abi == .gnu) {
218 // LTO is currently broken on mingw, this can be removed when it's fixed.
219 exe.want_lto = false;
222 const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
223 exe.use_llvm = use_llvm;
224 exe.use_lld = use_llvm;
226 const exe_options = b.addOptions();
227 exe.root_module.addOptions("build_options", exe_options);
229 exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
230 exe_options.addOption(bool, "skip_non_native", skip_non_native);
231 exe_options.addOption(bool, "have_llvm", enable_llvm);
232 exe_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k);
233 exe_options.addOption(bool, "llvm_has_csky", llvm_has_csky);
234 exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc);
235 exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
236 exe_options.addOption(bool, "force_gpa", force_gpa);
237 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);
238 exe_options.addOption(ValueInterpretMode, "value_interpret_mode", value_interpret_mode);
241 exe.root_module.link_libc = true;
244 const is_debug = optimize == .Debug;
245 const enable_debug_extensions = b.option(bool, "debug-extensions", "Enable commands and options useful for debugging the compiler") orelse is_debug;
246 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
247 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
249 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
250 const version_slice = if (opt_version_string) |version| version else v: {
251 if (!std.process.can_spawn) {
252 std.debug.print("error: version info cannot be retrieved from git. Zig version must be provided using -Dversion-string\n", .{});
255 const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch });
257 var code: u8 = undefined;
258 const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{
261 b.build_root.path orelse ".",
267 }, &code, .Ignore) catch {
268 break :v version_string;
270 const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r");
272 switch (mem.count(u8, git_describe, "-")) {
274 // Tagged release version (e.g. 0.10.0).
275 if (!mem.eql(u8, git_describe, version_string)) {
276 std.debug.print("Zig version '{s}' does not match Git tag '{s}'\n", .{ version_string, git_describe });
279 break :v version_string;
282 // Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
283 var it = mem.splitScalar(u8, git_describe, '-');
284 const tagged_ancestor = it.first();
285 const commit_height = it.next().?;
286 const commit_id = it.next().?;
288 const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
289 if (zig_version.order(ancestor_ver) != .gt) {
290 std.debug.print("Zig version '{}' must be greater than tagged ancestor '{}'\n", .{ zig_version, ancestor_ver });
294 // Check that the commit hash is prefixed with a 'g' (a Git convention).
295 if (commit_id.len < 1 or commit_id[0] != 'g') {
296 std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
297 break :v version_string;
300 // The version is reformatted in accordance with the https://semver.org specification.
301 break :v b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] });
304 std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
305 break :v version_string;
309 const version = try b.allocator.dupeZ(u8, version_slice);
310 exe_options.addOption([:0]const u8, "version", version);
313 const cmake_cfg = if (static_llvm) null else blk: {
314 if (findConfigH(b, config_h_path_option)) |config_h_path| {
315 const file_contents = fs.cwd().readFileAlloc(b.allocator, config_h_path, max_config_h_bytes) catch unreachable;
316 break :blk parseConfigH(b, file_contents);
318 std.log.warn("config.h could not be located automatically. Consider providing it explicitly via \"-Dconfig_h\"", .{});
323 if (cmake_cfg) |cfg| {
324 // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD.
325 // That means we also have to rely on stage1 compiled c++ files. We parse config.h to find
326 // the information passed on to us from cmake.
327 if (cfg.cmake_prefix_path.len > 0) {
328 var it = mem.tokenizeScalar(u8, cfg.cmake_prefix_path, ';');
329 while (it.next()) |path| {
330 b.addSearchPrefix(path);
334 try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);
336 // Here we are -Denable-llvm but no cmake integration.
337 try addStaticLlvmOptionsToModule(exe.root_module);
339 if (target.result.os.tag == .windows) {
340 // LLVM depends on networking as of version 18.
341 exe.root_module.linkSystemLibrary("ws2_32", .{});
343 exe.root_module.linkSystemLibrary("version", .{});
344 exe.root_module.linkSystemLibrary("uuid", .{});
345 exe.root_module.linkSystemLibrary("ole32", .{});
349 const semver = try std.SemanticVersion.parse(version);
350 exe_options.addOption(std.SemanticVersion, "semver", semver);
352 exe_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
353 exe_options.addOption(bool, "enable_logging", enable_logging);
354 exe_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
355 exe_options.addOption(bool, "enable_tracy", tracy != null);
356 exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
357 exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
358 exe_options.addOption(u32, "tracy_callstack_depth", tracy_callstack_depth);
359 exe_options.addOption(bool, "value_tracing", value_tracing);
360 if (tracy) |tracy_path| {
361 const client_cpp = b.pathJoin(
362 &[_][]const u8{ tracy_path, "public", "TracyClient.cpp" },
365 // On mingw, we need to opt into windows 7+ to get some features required by tracy.
366 const tracy_c_flags: []const []const u8 = if (target.result.os.tag == .windows and target.result.abi == .gnu)
367 &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined", "-D_WIN32_WINNT=0x601" }
369 &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" };
371 exe.root_module.addIncludePath(.{ .cwd_relative = tracy_path });
372 exe.root_module.addCSourceFile(.{ .file = .{ .cwd_relative = client_cpp }, .flags = tracy_c_flags });
374 exe.root_module.linkSystemLibrary("c++", .{ .use_pkg_config = .no });
376 exe.root_module.link_libc = true;
378 if (target.result.os.tag == .windows) {
379 exe.root_module.linkSystemLibrary("dbghelp", .{});
380 exe.root_module.linkSystemLibrary("ws2_32", .{});
384 const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match any filter") orelse &[0][]const u8{};
385 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{};
386 const test_slow_targets = b.option(bool, "test-slow-targets", "Enable running module tests for targets that have a slow compiler backend") orelse false;
388 var chosen_opt_modes_buf: [4]builtin.OptimizeMode = undefined;
389 var chosen_mode_index: usize = 0;
391 chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.Debug;
392 chosen_mode_index += 1;
394 if (!skip_release_safe) {
395 chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSafe;
396 chosen_mode_index += 1;
398 if (!skip_release_fast) {
399 chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseFast;
400 chosen_mode_index += 1;
402 if (!skip_release_small) {
403 chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSmall;
404 chosen_mode_index += 1;
406 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
408 const fmt_include_paths = &.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" };
409 const fmt_exclude_paths = &.{"test/cases"};
410 const do_fmt = b.addFmt(.{
411 .paths = fmt_include_paths,
412 .exclude_paths = fmt_exclude_paths,
414 b.step("fmt", "Modify source files in place to have conforming formatting").dependOn(&do_fmt.step);
416 const check_fmt = b.step("test-fmt", "Check source files having conforming formatting");
417 check_fmt.dependOn(&b.addFmt(.{
418 .paths = fmt_include_paths,
419 .exclude_paths = fmt_exclude_paths,
422 test_step.dependOn(check_fmt);
424 const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
425 try tests.addCases(b, test_cases_step, test_filters, test_target_filters, target, .{
426 .skip_translate_c = skip_translate_c,
427 .skip_run_translated_c = skip_run_translated_c,
429 .enable_llvm = enable_llvm,
430 .llvm_has_m68k = llvm_has_m68k,
431 .llvm_has_csky = llvm_has_csky,
432 .llvm_has_arc = llvm_has_arc,
433 .llvm_has_xtensa = llvm_has_xtensa,
435 test_step.dependOn(test_cases_step);
437 const test_modules_step = b.step("test-modules", "Run the per-target module tests");
438 test_step.dependOn(test_modules_step);
440 test_modules_step.dependOn(tests.addModuleTests(b, .{
441 .test_filters = test_filters,
442 .test_target_filters = test_target_filters,
443 .test_slow_targets = test_slow_targets,
444 .root_src = "test/behavior.zig",
446 .desc = "Run the behavior tests",
447 .optimize_modes = optimization_modes,
448 .include_paths = &.{},
449 .skip_single_threaded = skip_single_threaded,
450 .skip_non_native = skip_non_native,
451 .skip_libc = skip_libc,
452 .use_llvm = use_llvm,
453 .max_rss = 1 * 1024 * 1024 * 1024,
456 test_modules_step.dependOn(tests.addModuleTests(b, .{
457 .test_filters = test_filters,
458 .test_target_filters = test_target_filters,
459 .test_slow_targets = test_slow_targets,
460 .root_src = "test/c_import.zig",
462 .desc = "Run the @cImport tests",
463 .optimize_modes = optimization_modes,
464 .include_paths = &.{"test/c_import"},
465 .skip_single_threaded = true,
466 .skip_non_native = skip_non_native,
467 .skip_libc = skip_libc,
468 .use_llvm = use_llvm,
471 test_modules_step.dependOn(tests.addModuleTests(b, .{
472 .test_filters = test_filters,
473 .test_target_filters = test_target_filters,
474 .test_slow_targets = test_slow_targets,
475 .root_src = "lib/compiler_rt.zig",
476 .name = "compiler-rt",
477 .desc = "Run the compiler_rt tests",
478 .optimize_modes = optimization_modes,
479 .include_paths = &.{},
480 .skip_single_threaded = true,
481 .skip_non_native = skip_non_native,
483 .use_llvm = use_llvm,
487 test_modules_step.dependOn(tests.addModuleTests(b, .{
488 .test_filters = test_filters,
489 .test_target_filters = test_target_filters,
490 .test_slow_targets = test_slow_targets,
491 .root_src = "lib/c.zig",
492 .name = "universal-libc",
493 .desc = "Run the universal libc tests",
494 .optimize_modes = optimization_modes,
495 .include_paths = &.{},
496 .skip_single_threaded = true,
497 .skip_non_native = skip_non_native,
499 .use_llvm = use_llvm,
503 test_modules_step.dependOn(tests.addModuleTests(b, .{
504 .test_filters = test_filters,
505 .test_target_filters = test_target_filters,
506 .test_slow_targets = test_slow_targets,
507 .root_src = "lib/std/std.zig",
509 .desc = "Run the standard library tests",
510 .optimize_modes = optimization_modes,
511 .include_paths = &.{},
512 .skip_single_threaded = skip_single_threaded,
513 .skip_non_native = skip_non_native,
514 .skip_libc = skip_libc,
515 .use_llvm = use_llvm,
516 // I observed a value of 4572626944 on the M2 CI.
517 .max_rss = 5029889638,
520 const unit_tests_step = b.step("test-unit", "Run the compiler source unit tests");
521 test_step.dependOn(unit_tests_step);
523 const unit_tests = b.addTest(.{
524 .root_module = b.createModule(.{
525 .root_source_file = b.path("src/main.zig"),
526 .optimize = optimize,
528 .link_libc = link_libc,
529 .single_threaded = single_threaded,
531 .filters = test_filters,
532 .use_llvm = use_llvm,
534 .zig_lib_dir = b.path("lib"),
536 unit_tests.root_module.addOptions("build_options", exe_options);
537 unit_tests_step.dependOn(&b.addRunArtifact(unit_tests).step);
539 test_step.dependOn(tests.addCompareOutputTests(b, test_filters, optimization_modes));
540 test_step.dependOn(tests.addStandaloneTests(
545 enable_symlinks_windows,
547 test_step.dependOn(tests.addCAbiTests(b, .{
548 .test_target_filters = test_target_filters,
549 .skip_non_native = skip_non_native,
550 .skip_release = skip_release,
552 test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows));
553 test_step.dependOn(tests.addStackTraceTests(b, test_filters, optimization_modes));
554 test_step.dependOn(tests.addCliTests(b));
555 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filters, optimization_modes));
556 if (tests.addDebuggerTests(b, .{
557 .test_filters = test_filters,
558 .test_target_filters = test_target_filters,
559 .gdb = b.option([]const u8, "gdb", "path to gdb binary"),
560 .lldb = b.option([]const u8, "lldb", "path to lldb binary"),
561 .optimize_modes = optimization_modes,
562 .skip_single_threaded = skip_single_threaded,
563 .skip_non_native = skip_non_native,
564 .skip_libc = skip_libc,
565 })) |test_debugger_step| test_step.dependOn(test_debugger_step);
567 try addWasiUpdateStep(b, version);
569 const update_mingw_step = b.step("update-mingw", "Update zig's bundled mingw");
570 const opt_mingw_src_path = b.option([]const u8, "mingw-src", "path to mingw-w64 source directory");
571 if (opt_mingw_src_path) |mingw_src_path| {
572 const update_mingw_exe = b.addExecutable(.{
573 .name = "update_mingw",
574 .root_module = b.createModule(.{
575 .target = b.graph.host,
576 .root_source_file = b.path("tools/update_mingw.zig"),
579 const update_mingw_run = b.addRunArtifact(update_mingw_exe);
580 update_mingw_run.addDirectoryArg(b.path("lib"));
581 update_mingw_run.addDirectoryArg(.{ .cwd_relative = mingw_src_path });
583 update_mingw_step.dependOn(&update_mingw_run.step);
585 update_mingw_step.dependOn(&b.addFail("The -Dmingw-src=... option is required for this step").step);
588 const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases");
589 try tests.addIncrementalTests(b, test_incremental_step);
590 test_step.dependOn(test_incremental_step);
593 fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
594 const semver = try std.SemanticVersion.parse(version);
596 const exe = addCompilerStep(b, .{
597 .optimize = .ReleaseSmall,
598 .target = b.resolveTargetQuery(std.Target.Query.parse(.{
599 .arch_os_abi = "wasm32-wasi",
600 // * `extended_const` is not supported by the `wasm-opt` version in CI.
601 // * `nontrapping_bulk_memory_len0` is supported by `wasm2c`.
602 .cpu_features = "baseline-extended_const+nontrapping_bulk_memory_len0",
603 }) catch unreachable),
606 const exe_options = b.addOptions();
607 exe.root_module.addOptions("build_options", exe_options);
609 exe_options.addOption(u32, "mem_leak_frames", 0);
610 exe_options.addOption(bool, "have_llvm", false);
611 exe_options.addOption(bool, "force_gpa", false);
612 exe_options.addOption([:0]const u8, "version", version);
613 exe_options.addOption(std.SemanticVersion, "semver", semver);
614 exe_options.addOption(bool, "enable_debug_extensions", false);
615 exe_options.addOption(bool, "enable_logging", false);
616 exe_options.addOption(bool, "enable_link_snapshots", false);
617 exe_options.addOption(bool, "enable_tracy", false);
618 exe_options.addOption(bool, "enable_tracy_callstack", false);
619 exe_options.addOption(bool, "enable_tracy_allocation", false);
620 exe_options.addOption(u32, "tracy_callstack_depth", 0);
621 exe_options.addOption(bool, "value_tracing", false);
622 exe_options.addOption(DevEnv, "dev", .bootstrap);
624 // zig1 chooses to interpret values by name. The tradeoff is as follows:
626 // * We lose a small amount of performance. This is essentially irrelevant for zig1.
628 // * We lose the ability to perform trivial renames on certain `std.builtin` types without
629 // zig1.wasm updates. For instance, we cannot rename an enum from PascalCase fields to
630 // snake_case fields without an update.
632 // * We gain the ability to add and remove fields to and from `std.builtin` types without
633 // zig1.wasm updates. For instance, we can add a new tag to `CallingConvention` without
636 // Because field renames only happen when we apply a breaking change to the language (which
637 // is becoming progressively rarer), but tags may be added to or removed from target-dependent
638 // types over time in response to new targets coming into use, we gain more than we lose here.
639 exe_options.addOption(ValueInterpretMode, "value_interpret_mode", .by_name);
641 const run_opt = b.addSystemCommand(&.{
644 "--enable-bulk-memory",
645 "--enable-mutable-globals",
646 "--enable-nontrapping-float-to-int",
649 run_opt.addArtifactArg(exe);
650 run_opt.addArg("-o");
651 run_opt.addFileArg(b.path("stage1/zig1.wasm"));
653 const copy_zig_h = b.addUpdateSourceFiles();
654 copy_zig_h.addCopyFileToSource(b.path("lib/zig.h"), "stage1/zig.h");
656 const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm");
657 update_zig1_step.dependOn(&run_opt.step);
658 update_zig1_step.dependOn(©_zig_h.step);
661 const AddCompilerStepOptions = struct {
662 optimize: std.builtin.OptimizeMode,
663 target: std.Build.ResolvedTarget,
665 valgrind: ?bool = null,
666 sanitize_thread: ?bool = null,
667 single_threaded: ?bool = null,
670 fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.Step.Compile {
671 const compiler_mod = b.createModule(.{
672 .root_source_file = b.path("src/main.zig"),
673 .target = options.target,
674 .optimize = options.optimize,
675 .strip = options.strip,
676 .sanitize_thread = options.sanitize_thread,
677 .single_threaded = options.single_threaded,
678 .code_model = switch (options.target.result.cpu.arch) {
680 // For loongarch, LLVM supports only small, medium and large
681 // code model. If we don't explicitly specify the code model,
682 // the default value `small' will be used.
684 // Since zig binary itself is relatively large, using a `small'
685 // code model will cause
687 // relocation R_LARCH_B26 out of range
689 // error when linking a loongarch32/loongarch64 zig binary.
691 // Here we explicitly set code model to `medium' to avoid this
693 .loongarch32, .loongarch64 => .medium,
696 .valgrind = options.valgrind,
699 const aro_mod = b.createModule(.{
700 .root_source_file = b.path("lib/compiler/aro/aro.zig"),
703 const aro_translate_c_mod = b.createModule(.{
704 .root_source_file = b.path("lib/compiler/aro_translate_c.zig"),
707 aro_translate_c_mod.addImport("aro", aro_mod);
708 compiler_mod.addImport("aro", aro_mod);
709 compiler_mod.addImport("aro_translate_c", aro_translate_c_mod);
711 const exe = b.addExecutable(.{
713 .max_rss = 7_800_000_000,
714 .root_module = compiler_mod,
716 exe.stack_size = stack_size;
721 const exe_cflags = [_][]const u8{
723 "-D__STDC_CONSTANT_MACROS",
724 "-D__STDC_FORMAT_MACROS",
725 "-D__STDC_LIMIT_MACROS",
729 "-fno-stack-protector",
730 "-fvisibility-inlines-hidden",
732 "-Wno-missing-braces",
736 fn addCmakeCfgOptionsToExe(
739 exe: *std.Build.Step.Compile,
740 use_zig_libcxx: bool,
742 const mod = exe.root_module;
743 const target = mod.resolved_target.?.result;
745 if (target.isDarwin()) {
746 // useful for package maintainers
747 exe.headerpad_max_install_names = true;
750 mod.addObjectFile(.{ .cwd_relative = b.pathJoin(&.{
751 cfg.cmake_binary_dir,
753 b.fmt("{s}{s}{s}", .{
754 cfg.cmake_static_library_prefix,
756 cfg.cmake_static_library_suffix,
759 assert(cfg.lld_include_dir.len != 0);
760 mod.addIncludePath(.{ .cwd_relative = cfg.lld_include_dir });
761 mod.addIncludePath(.{ .cwd_relative = cfg.llvm_include_dir });
762 mod.addLibraryPath(.{ .cwd_relative = cfg.llvm_lib_dir });
763 addCMakeLibraryList(mod, cfg.clang_libraries);
764 addCMakeLibraryList(mod, cfg.lld_libraries);
765 addCMakeLibraryList(mod, cfg.llvm_libraries);
767 if (use_zig_libcxx) {
768 mod.link_libcpp = true;
770 // System -lc++ must be used because in this code path we are attempting to link
771 // against system-provided LLVM, Clang, LLD.
772 const need_cpp_includes = true;
773 const static = cfg.llvm_linkage == .static;
774 const lib_suffix = if (static) target.staticLibSuffix()[1..] else target.dynamicLibSuffix()[1..];
775 switch (target.os.tag) {
777 // First we try to link against the detected libcxx name. If that doesn't work, we fall
778 // back to -lc++ and cross our fingers.
779 addCxxKnownPath(b, cfg, exe, b.fmt("lib{s}.{s}", .{ cfg.system_libcxx, lib_suffix }), "", need_cpp_includes) catch |err| switch (err) {
780 error.RequiredLibraryNotFound => {
781 mod.link_libcpp = true;
783 else => |e| return e,
785 mod.linkSystemLibrary("unwind", .{});
787 .ios, .macos, .watchos, .tvos, .visionos => {
788 mod.link_libcpp = true;
791 if (target.abi != .msvc) mod.link_libcpp = true;
795 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
796 try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
798 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
802 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
803 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{lib_suffix}), null, need_cpp_includes);
805 .netbsd, .dragonfly => {
807 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
808 try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
810 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
813 .solaris, .illumos => {
814 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
815 try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
818 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
824 if (cfg.dia_guids_lib.len != 0) {
825 mod.addObjectFile(.{ .cwd_relative = cfg.dia_guids_lib });
829 fn addStaticLlvmOptionsToModule(mod: *std.Build.Module) !void {
830 // Adds the Zig C++ sources which both stage1 and stage2 need.
832 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
833 // in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
834 // unavailable when LLVM is compiled in Release mode.
835 const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
836 mod.addCSourceFiles(.{
837 .files = &zig_cpp_sources,
838 .flags = &zig_cpp_cflags,
841 for (clang_libs) |lib_name| {
842 mod.linkSystemLibrary(lib_name, .{});
845 for (lld_libs) |lib_name| {
846 mod.linkSystemLibrary(lib_name, .{});
849 for (llvm_libs) |lib_name| {
850 mod.linkSystemLibrary(lib_name, .{});
853 mod.linkSystemLibrary("z", .{});
854 mod.linkSystemLibrary("zstd", .{});
856 if (mod.resolved_target.?.result.os.tag != .windows or mod.resolved_target.?.result.abi != .msvc) {
857 // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
858 mod.linkSystemLibrary("c++", .{});
861 if (mod.resolved_target.?.result.os.tag == .windows) {
862 mod.linkSystemLibrary("version", .{});
863 mod.linkSystemLibrary("uuid", .{});
864 mod.linkSystemLibrary("ole32", .{});
871 exe: *std.Build.Step.Compile,
874 need_cpp_includes: bool,
876 if (!std.process.can_spawn)
877 return error.RequiredLibraryNotFound;
879 const path_padded = run: {
880 var args = std.ArrayList([]const u8).init(b.allocator);
881 try args.append(ctx.cxx_compiler);
882 var it = std.mem.tokenizeAny(u8, ctx.cxx_compiler_arg1, &std.ascii.whitespace);
883 while (it.next()) |arg| try args.append(arg);
884 try args.append(b.fmt("-print-file-name={s}", .{objname}));
885 break :run b.run(args.items);
887 var tokenizer = mem.tokenizeAny(u8, path_padded, "\r\n");
888 const path_unpadded = tokenizer.next().?;
889 if (mem.eql(u8, path_unpadded, objname)) {
891 std.debug.print("{s}", .{msg});
893 std.debug.print("Unable to determine path to {s}\n", .{objname});
895 return error.RequiredLibraryNotFound;
897 // By default, explicit library paths are not checked for being linker scripts,
898 // but libc++ may very well be one, so force all inputs to be checked when passing
899 // an explicit path to libc++.
900 exe.allow_so_scripts = true;
901 exe.root_module.addObjectFile(.{ .cwd_relative = path_unpadded });
903 // TODO a way to integrate with system c++ include files here
904 // c++ -E -Wp,-v -xc++ /dev/null
905 if (need_cpp_includes) {
906 // I used these temporarily for testing something but we obviously need a
907 // more general purpose solution here.
908 //exe.root_module.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0");
909 //exe.root_module.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0/x86_64-unknown-linux-gnu");
913 fn addCMakeLibraryList(mod: *std.Build.Module, list: []const u8) void {
914 var it = mem.tokenizeScalar(u8, list, ';');
915 while (it.next()) |lib| {
916 if (mem.startsWith(u8, lib, "-l")) {
917 mod.linkSystemLibrary(lib["-l".len..], .{});
918 } else if (mod.resolved_target.?.result.os.tag == .windows and
919 mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib))
921 mod.linkSystemLibrary(lib[0 .. lib.len - ".lib".len], .{});
923 mod.addObjectFile(.{ .cwd_relative = lib });
928 const CMakeConfig = struct {
929 llvm_linkage: std.builtin.LinkMode,
930 cmake_binary_dir: []const u8,
931 cmake_prefix_path: []const u8,
932 cmake_static_library_prefix: []const u8,
933 cmake_static_library_suffix: []const u8,
934 cxx_compiler: []const u8,
935 cxx_compiler_arg1: []const u8,
936 lld_include_dir: []const u8,
937 lld_libraries: []const u8,
938 clang_libraries: []const u8,
939 llvm_lib_dir: []const u8,
940 llvm_include_dir: []const u8,
941 llvm_libraries: []const u8,
942 dia_guids_lib: []const u8,
943 system_libcxx: []const u8,
946 const max_config_h_bytes = 1 * 1024 * 1024;
948 fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
949 if (config_h_path_option) |path| {
950 var config_h_or_err = fs.cwd().openFile(path, .{});
951 if (config_h_or_err) |*file| {
955 std.log.err("Could not open provided config.h: \"{s}\"", .{path});
960 var check_dir = fs.path.dirname(b.graph.zig_exe).?;
962 var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
965 // Check if config.h is present in dir
966 var config_h_or_err = dir.openFile("config.h", .{});
967 if (config_h_or_err) |*file| {
971 &[_][]const u8{ check_dir, "config.h" },
973 } else |e| switch (e) {
974 error.FileNotFound => {},
978 // Check if we reached the source root by looking for .git, and bail if so
979 var git_dir_or_err = dir.openDir(".git", .{});
980 if (git_dir_or_err) |*git_dir| {
985 // Otherwise, continue search in the parent directory
986 const new_check_dir = fs.path.dirname(check_dir);
987 if (new_check_dir == null or mem.eql(u8, new_check_dir.?, check_dir)) {
990 check_dir = new_check_dir.?;
994 fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
995 var ctx: CMakeConfig = .{
996 .llvm_linkage = undefined,
997 .cmake_binary_dir = undefined,
998 .cmake_prefix_path = undefined,
999 .cmake_static_library_prefix = undefined,
1000 .cmake_static_library_suffix = undefined,
1001 .cxx_compiler = undefined,
1002 .cxx_compiler_arg1 = "",
1003 .lld_include_dir = undefined,
1004 .lld_libraries = undefined,
1005 .clang_libraries = undefined,
1006 .llvm_lib_dir = undefined,
1007 .llvm_include_dir = undefined,
1008 .llvm_libraries = undefined,
1009 .dia_guids_lib = undefined,
1010 .system_libcxx = undefined,
1013 const mappings = [_]struct { prefix: []const u8, field: []const u8 }{
1015 .prefix = "#define ZIG_CMAKE_BINARY_DIR ",
1016 .field = "cmake_binary_dir",
1019 .prefix = "#define ZIG_CMAKE_PREFIX_PATH ",
1020 .field = "cmake_prefix_path",
1023 .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_PREFIX ",
1024 .field = "cmake_static_library_prefix",
1027 .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_SUFFIX ",
1028 .field = "cmake_static_library_suffix",
1031 .prefix = "#define ZIG_CXX_COMPILER ",
1032 .field = "cxx_compiler",
1035 .prefix = "#define ZIG_CXX_COMPILER_ARG1 ",
1036 .field = "cxx_compiler_arg1",
1039 .prefix = "#define ZIG_LLD_INCLUDE_PATH ",
1040 .field = "lld_include_dir",
1043 .prefix = "#define ZIG_LLD_LIBRARIES ",
1044 .field = "lld_libraries",
1047 .prefix = "#define ZIG_CLANG_LIBRARIES ",
1048 .field = "clang_libraries",
1051 .prefix = "#define ZIG_LLVM_LIBRARIES ",
1052 .field = "llvm_libraries",
1055 .prefix = "#define ZIG_DIA_GUIDS_LIB ",
1056 .field = "dia_guids_lib",
1059 .prefix = "#define ZIG_LLVM_INCLUDE_PATH ",
1060 .field = "llvm_include_dir",
1063 .prefix = "#define ZIG_LLVM_LIB_PATH ",
1064 .field = "llvm_lib_dir",
1067 .prefix = "#define ZIG_SYSTEM_LIBCXX",
1068 .field = "system_libcxx",
1070 // .prefix = ZIG_LLVM_LINK_MODE parsed manually below
1073 var lines_it = mem.tokenizeAny(u8, config_h_text, "\r\n");
1074 while (lines_it.next()) |line| {
1075 inline for (mappings) |mapping| {
1076 if (mem.startsWith(u8, line, mapping.prefix)) {
1077 var it = mem.splitScalar(u8, line, '"');
1078 _ = it.first(); // skip the stuff before the quote
1079 const quoted = it.next().?; // the stuff inside the quote
1080 const trimmed = mem.trim(u8, quoted, " ");
1081 @field(ctx, mapping.field) = toNativePathSep(b, trimmed);
1084 if (mem.startsWith(u8, line, "#define ZIG_LLVM_LINK_MODE ")) {
1085 var it = mem.splitScalar(u8, line, '"');
1086 _ = it.next().?; // skip the stuff before the quote
1087 const quoted = it.next().?; // the stuff inside the quote
1088 ctx.llvm_linkage = if (mem.eql(u8, quoted, "shared")) .dynamic else .static;
1094 fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
1095 const duplicated = b.allocator.dupe(u8, s) catch unreachable;
1096 for (duplicated) |*byte| switch (byte.*) {
1097 '/' => byte.* = fs.path.sep,
1103 const zig_cpp_sources = [_][]const u8{
1104 // These are planned to stay even when we are self-hosted.
1106 "src/zig_clang.cpp",
1107 "src/zig_llvm-ar.cpp",
1108 "src/zig_clang_driver.cpp",
1109 "src/zig_clang_cc1_main.cpp",
1110 "src/zig_clang_cc1as_main.cpp",
1113 const clang_libs = [_][]const u8{
1114 "clangFrontendTool",
1118 "clangSerialization",
1120 "clangStaticAnalyzerFrontend",
1121 "clangStaticAnalyzerCheckers",
1122 "clangStaticAnalyzerCore",
1133 "clangRewriteFrontend",
1143 const lld_libs = [_][]const u8{
1151 // This list can be re-generated with `llvm-config --libfiles` and then
1152 // reformatting using your favorite text editor. Note we do not execute
1153 // `llvm-config` here because we are cross compiling. Also omit LLVMTableGen
1155 const llvm_libs = [_][]const u8{
1156 "LLVMWindowsManifest",
1159 "LLVMDlltoolDriver",
1160 "LLVMTextAPIBinaryReader",
1164 "LLVMXCoreDisassembler",
1169 "LLVMX86Disassembler",
1174 "LLVMWebAssemblyDisassembler",
1175 "LLVMWebAssemblyAsmParser",
1176 "LLVMWebAssemblyCodeGen",
1177 "LLVMWebAssemblyUtils",
1178 "LLVMWebAssemblyDesc",
1179 "LLVMWebAssemblyInfo",
1180 "LLVMVEDisassembler",
1185 "LLVMSystemZDisassembler",
1186 "LLVMSystemZAsmParser",
1187 "LLVMSystemZCodeGen",
1190 "LLVMSparcDisassembler",
1191 "LLVMSparcAsmParser",
1195 "LLVMRISCVTargetMCA",
1196 "LLVMRISCVDisassembler",
1197 "LLVMRISCVAsmParser",
1201 "LLVMPowerPCDisassembler",
1202 "LLVMPowerPCAsmParser",
1203 "LLVMPowerPCCodeGen",
1209 "LLVMMSP430Disassembler",
1210 "LLVMMSP430AsmParser",
1211 "LLVMMSP430CodeGen",
1214 "LLVMMipsDisassembler",
1215 "LLVMMipsAsmParser",
1219 "LLVMLoongArchDisassembler",
1220 "LLVMLoongArchAsmParser",
1221 "LLVMLoongArchCodeGen",
1222 "LLVMLoongArchDesc",
1223 "LLVMLoongArchInfo",
1224 "LLVMLanaiDisassembler",
1226 "LLVMLanaiAsmParser",
1229 "LLVMHexagonDisassembler",
1230 "LLVMHexagonCodeGen",
1231 "LLVMHexagonAsmParser",
1234 "LLVMBPFDisassembler",
1239 "LLVMAVRDisassembler",
1244 "LLVMARMDisassembler",
1250 "LLVMAMDGPUTargetMCA",
1251 "LLVMAMDGPUDisassembler",
1252 "LLVMAMDGPUAsmParser",
1253 "LLVMAMDGPUCodeGen",
1257 "LLVMAArch64Disassembler",
1258 "LLVMAArch64AsmParser",
1259 "LLVMAArch64CodeGen",
1265 "LLVMWindowsDriver",
1269 "LLVMExecutionEngine",
1271 "LLVMOrcTargetProcess",
1274 "LLVMDebugInfoLogicalView",
1275 "LLVMDebugInfoGSYM",
1280 "LLVMMCDisassembler",
1289 "LLVMInstrumentation",
1290 "LLVMFrontendOpenMP",
1291 "LLVMFrontendOffloading",
1292 "LLVMFrontendOpenACC",
1294 "LLVMFrontendDriver",
1296 "LLVMDWARFLinkerParallel",
1297 "LLVMDWARFLinkerClassic",
1309 "LLVMInterfaceStub",
1314 "LLVMAggressiveInstCombine",
1315 "LLVMTransformUtils",
1323 "LLVMDebugInfoDWARF",
1330 "LLVMDebugInfoCodeView",
1335 "LLVMBitstreamReader",
1342 fn generateLangRef(b: *std.Build) std.Build.LazyPath {
1343 const doctest_exe = b.addExecutable(.{
1345 .root_module = b.createModule(.{
1346 .root_source_file = b.path("tools/doctest.zig"),
1347 .target = b.graph.host,
1352 var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
1353 std.debug.panic("unable to open '{}doc/langref' directory: {s}", .{
1354 b.build_root, @errorName(err),
1359 var wf = b.addWriteFiles();
1361 var it = dir.iterateAssumeFirstIteration();
1362 while (it.next() catch @panic("failed to read dir")) |entry| {
1363 if (std.mem.startsWith(u8, entry.name, ".") or entry.kind != .file)
1366 const out_basename = b.fmt("{s}.out", .{std.fs.path.stem(entry.name)});
1367 const cmd = b.addRunArtifact(doctest_exe);
1369 "--zig", b.graph.zig_exe,
1370 // TODO: enhance doctest to use "--listen=-" rather than operating
1371 // in a temporary directory
1372 "--cache-root", b.cache_root.path orelse ".",
1374 cmd.addArgs(&.{ "--zig-lib-dir", b.fmt("{}", .{b.graph.zig_lib_directory}) });
1375 cmd.addArgs(&.{"-i"});
1376 cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name})));
1378 cmd.addArgs(&.{"-o"});
1379 _ = wf.addCopyFile(cmd.addOutputFileArg(out_basename), out_basename);
1382 const docgen_exe = b.addExecutable(.{
1384 .root_module = b.createModule(.{
1385 .root_source_file = b.path("tools/docgen.zig"),
1386 .target = b.graph.host,
1391 const docgen_cmd = b.addRunArtifact(docgen_exe);
1392 docgen_cmd.addArgs(&.{"--code-dir"});
1393 docgen_cmd.addDirectoryArg(wf.getDirectory());
1395 docgen_cmd.addFileArg(b.path("doc/langref.html.in"));
1396 return docgen_cmd.addOutputFileArg("langref.html");
1399 fn superHtmlCheck(b: *std.Build, html_file: std.Build.LazyPath) *std.Build.Step {
1400 const run_superhtml = b.addSystemCommand(&.{
1401 "superhtml", "check",
1403 run_superhtml.addFileArg(html_file);
1404 run_superhtml.expectExitCode(0);
1405 return &run_superhtml.step;