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