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;
12 const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 13, .patch = 0 };
13 const stack_size = 32 * 1024 * 1024;
15 pub fn build(b: *std.Build) !void {
16 const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
18 var default_target: std.zig.CrossTarget = .{};
19 default_target.ofmt = b.option(std.Target.ObjectFormat, "ofmt", "Object format to target") orelse if (only_c) .c else null;
20 break :t b.standardTargetOptions(.{ .default_target = default_target });
23 const optimize = b.standardOptimizeOption(.{});
25 const flat = b.option(bool, "flat", "Put files into the installation prefix in a manner suited for upstream distribution rather than a posix file system hierarchy standard") orelse false;
26 const single_threaded = b.option(bool, "single-threaded", "Build artifacts that run in single threaded mode");
27 const use_zig_libcxx = b.option(bool, "use-zig-libcxx", "If libc++ is needed, use zig's bundled version, don't try to integrate with the system") orelse false;
29 const test_step = b.step("test", "Run all the tests");
30 const skip_install_lib_files = b.option(bool, "no-lib", "skip copying of lib/ files and langref to installation prefix. Useful for development") orelse false;
31 const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;
32 const std_docs = b.option(bool, "std-docs", "include standard library autodocs") orelse false;
33 const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;
35 const langref_file = generateLangRef(b);
36 const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html");
37 if (!skip_install_langref) {
38 b.getInstallStep().dependOn(&install_langref.step);
41 const autodoc_test = b.addObject(.{
43 .root_source_file = b.path("lib/std/std.zig"),
45 .zig_lib_dir = b.path("lib"),
48 const install_std_docs = b.addInstallDirectory(.{
49 .source_dir = autodoc_test.getEmittedDocs(),
50 .install_dir = .prefix,
51 .install_subdir = "doc/std",
54 b.getInstallStep().dependOn(&install_std_docs.step);
58 b.installFile("LICENSE", "LICENSE");
59 b.installFile("README.md", "README.md");
62 const langref_step = b.step("langref", "Build and install the language reference");
63 langref_step.dependOn(&install_langref.step);
65 const std_docs_step = b.step("std-docs", "Build and install the standard library documentation");
66 std_docs_step.dependOn(&install_std_docs.step);
68 const docs_step = b.step("docs", "Build and install documentation");
69 docs_step.dependOn(langref_step);
70 docs_step.dependOn(std_docs_step);
72 const check_case_exe = b.addExecutable(.{
74 .root_source_file = b.path("test/src/Cases.zig"),
77 .single_threaded = single_threaded,
79 check_case_exe.stack_size = stack_size;
81 const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
82 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
83 const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release;
84 const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release;
85 const skip_release_safe = b.option(bool, "skip-release-safe", "Main test suite skips release-safe builds") orelse skip_release;
86 const skip_non_native = b.option(bool, "skip-non-native", "Main test suite skips non-native builds") orelse false;
87 const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
88 const skip_single_threaded = b.option(bool, "skip-single-threaded", "Main test suite skips tests that are single-threaded") orelse false;
89 const skip_translate_c = b.option(bool, "skip-translate-c", "Main test suite skips translate-c tests") orelse false;
90 const skip_run_translated_c = b.option(bool, "skip-run-translated-c", "Main test suite skips run-translated-c tests") orelse false;
92 const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
94 const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false;
95 const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse static_llvm;
96 const llvm_has_m68k = b.option(
99 "Whether LLVM has the experimental target m68k enabled",
101 const llvm_has_csky = b.option(
104 "Whether LLVM has the experimental target csky enabled",
106 const llvm_has_arc = b.option(
109 "Whether LLVM has the experimental target arc enabled",
111 const llvm_has_xtensa = b.option(
114 "Whether LLVM has the experimental target xtensa enabled",
116 const enable_ios_sdk = b.option(bool, "enable-ios-sdk", "Run tests requiring presence of iOS SDK and frameworks") orelse false;
117 const enable_macos_sdk = b.option(bool, "enable-macos-sdk", "Run tests requiring presence of macOS SDK and frameworks") orelse enable_ios_sdk;
118 const enable_symlinks_windows = b.option(bool, "enable-symlinks-windows", "Run tests requiring presence of symlinks on Windows") orelse false;
119 const config_h_path_option = b.option([]const u8, "config_h", "Path to the generated config.h");
121 if (!skip_install_lib_files) {
122 b.installDirectory(.{
123 .source_dir = b.path("lib"),
124 .install_dir = if (flat) .prefix else .lib,
125 .install_subdir = if (flat) "lib" else "zig",
126 .exclude_extensions = &[_][]const u8{
127 // exclude files from lib/std/compress/testdata
136 // exclude files from lib/std/compress/flate/testdata
142 "compress-gettysburg.txt",
145 // exclude files from lib/std/compress/lzma/testdata
147 // exclude files from lib/std/compress/xz/testdata
149 // exclude files from lib/std/tz/
151 // exclude files from lib/std/tar/testdata
156 .blank_extensions = &[_][]const u8{
162 if (only_install_lib_files)
165 const entitlements = b.option([]const u8, "entitlements", "Path to entitlements file for hot-code swapping without sudo on macOS");
166 const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
167 const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
168 const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse (tracy != null);
169 const force_gpa = b.option(bool, "force-gpa", "Force the compiler to use GeneralPurposeAllocator") orelse false;
170 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse (enable_llvm or only_c);
171 const sanitize_thread = b.option(bool, "sanitize-thread", "Enable thread-sanitization") orelse false;
172 const strip = b.option(bool, "strip", "Omit debug information");
173 const pie = b.option(bool, "pie", "Produce a Position Independent Executable");
174 const value_tracing = b.option(bool, "value-tracing", "Enable extra state tracking to help troubleshoot bugs in the compiler (using the std.debug.Trace API)") orelse false;
176 const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: {
177 if (strip == true) break :blk @as(u32, 0);
178 if (optimize != .Debug) break :blk 0;
182 const exe = addCompilerStep(b, .{
183 .optimize = optimize,
186 .sanitize_thread = sanitize_thread,
187 .single_threaded = single_threaded,
190 exe.entitlements = entitlements;
192 exe.build_id = b.option(
195 "Request creation of '.note.gnu.build-id' section",
199 b.getInstallStep().dependOn(&exe.step);
201 const install_exe = b.addInstallArtifact(exe, .{
202 .dest_dir = if (flat) .{ .override = .prefix } else .default,
204 b.getInstallStep().dependOn(&install_exe.step);
207 test_step.dependOn(&exe.step);
209 if (target.result.os.tag == .windows and target.result.abi == .gnu) {
210 // LTO is currently broken on mingw, this can be removed when it's fixed.
211 exe.want_lto = false;
212 check_case_exe.want_lto = false;
215 const use_llvm = b.option(bool, "use-llvm", "Use the llvm backend");
216 exe.use_llvm = use_llvm;
217 exe.use_lld = use_llvm;
219 const exe_options = b.addOptions();
220 exe.root_module.addOptions("build_options", exe_options);
222 exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames);
223 exe_options.addOption(bool, "skip_non_native", skip_non_native);
224 exe_options.addOption(bool, "have_llvm", enable_llvm);
225 exe_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k);
226 exe_options.addOption(bool, "llvm_has_csky", llvm_has_csky);
227 exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc);
228 exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
229 exe_options.addOption(bool, "force_gpa", force_gpa);
230 exe_options.addOption(bool, "only_c", only_c);
231 exe_options.addOption(bool, "only_core_functionality", only_c);
235 check_case_exe.linkLibC();
238 const is_debug = optimize == .Debug;
239 const enable_debug_extensions = b.option(bool, "debug-extensions", "Enable commands and options useful for debugging the compiler") orelse is_debug;
240 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
241 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
243 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
244 const version_slice = if (opt_version_string) |version| version else v: {
245 if (!std.process.can_spawn) {
246 std.debug.print("error: version info cannot be retrieved from git. Zig version must be provided using -Dversion-string\n", .{});
249 const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch });
251 var code: u8 = undefined;
252 const git_describe_untrimmed = b.runAllowFail(&[_][]const u8{
255 b.build_root.path orelse ".",
261 }, &code, .Ignore) catch {
262 break :v version_string;
264 const git_describe = mem.trim(u8, git_describe_untrimmed, " \n\r");
266 switch (mem.count(u8, git_describe, "-")) {
268 // Tagged release version (e.g. 0.10.0).
269 if (!mem.eql(u8, git_describe, version_string)) {
270 std.debug.print("Zig version '{s}' does not match Git tag '{s}'\n", .{ version_string, git_describe });
273 break :v version_string;
276 // Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
277 var it = mem.splitScalar(u8, git_describe, '-');
278 const tagged_ancestor = it.first();
279 const commit_height = it.next().?;
280 const commit_id = it.next().?;
282 const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
283 if (zig_version.order(ancestor_ver) != .gt) {
284 std.debug.print("Zig version '{}' must be greater than tagged ancestor '{}'\n", .{ zig_version, ancestor_ver });
288 // Check that the commit hash is prefixed with a 'g' (a Git convention).
289 if (commit_id.len < 1 or commit_id[0] != 'g') {
290 std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
291 break :v version_string;
294 // The version is reformatted in accordance with the https://semver.org specification.
295 break :v b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] });
298 std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
299 break :v version_string;
303 const version = try b.allocator.dupeZ(u8, version_slice);
304 exe_options.addOption([:0]const u8, "version", version);
307 const cmake_cfg = if (static_llvm) null else blk: {
308 if (findConfigH(b, config_h_path_option)) |config_h_path| {
309 const file_contents = fs.cwd().readFileAlloc(b.allocator, config_h_path, max_config_h_bytes) catch unreachable;
310 break :blk parseConfigH(b, file_contents);
312 std.log.warn("config.h could not be located automatically. Consider providing it explicitly via \"-Dconfig_h\"", .{});
317 if (cmake_cfg) |cfg| {
318 // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD.
319 // That means we also have to rely on stage1 compiled c++ files. We parse config.h to find
320 // the information passed on to us from cmake.
321 if (cfg.cmake_prefix_path.len > 0) {
322 var it = mem.tokenizeScalar(u8, cfg.cmake_prefix_path, ';');
323 while (it.next()) |path| {
324 b.addSearchPrefix(path);
328 try addCmakeCfgOptionsToExe(b, cfg, exe, use_zig_libcxx);
329 try addCmakeCfgOptionsToExe(b, cfg, check_case_exe, use_zig_libcxx);
331 // Here we are -Denable-llvm but no cmake integration.
332 try addStaticLlvmOptionsToExe(exe);
333 try addStaticLlvmOptionsToExe(check_case_exe);
335 if (target.result.os.tag == .windows) {
336 inline for (.{ exe, check_case_exe }) |artifact| {
337 // LLVM depends on networking as of version 18.
338 artifact.linkSystemLibrary("ws2_32");
340 artifact.linkSystemLibrary("version");
341 artifact.linkSystemLibrary("uuid");
342 artifact.linkSystemLibrary("ole32");
347 const semver = try std.SemanticVersion.parse(version);
348 exe_options.addOption(std.SemanticVersion, "semver", semver);
350 exe_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
351 exe_options.addOption(bool, "enable_logging", enable_logging);
352 exe_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
353 exe_options.addOption(bool, "enable_tracy", tracy != null);
354 exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
355 exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
356 exe_options.addOption(bool, "value_tracing", value_tracing);
357 if (tracy) |tracy_path| {
358 const client_cpp = b.pathJoin(
359 &[_][]const u8{ tracy_path, "public", "TracyClient.cpp" },
362 // On mingw, we need to opt into windows 7+ to get some features required by tracy.
363 const tracy_c_flags: []const []const u8 = if (target.result.os.tag == .windows and target.result.abi == .gnu)
364 &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined", "-D_WIN32_WINNT=0x601" }
366 &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" };
368 exe.addIncludePath(.{ .cwd_relative = tracy_path });
369 exe.addCSourceFile(.{ .file = .{ .cwd_relative = client_cpp }, .flags = tracy_c_flags });
371 exe.root_module.linkSystemLibrary("c++", .{ .use_pkg_config = .no });
375 if (target.result.os.tag == .windows) {
376 exe.linkSystemLibrary("dbghelp");
377 exe.linkSystemLibrary("ws2_32");
381 const test_filters = b.option([]const []const u8, "test-filter", "Skip tests that do not match any filter") orelse &[0][]const u8{};
383 const test_cases_options = b.addOptions();
384 check_case_exe.root_module.addOptions("build_options", test_cases_options);
386 test_cases_options.addOption(bool, "enable_tracy", false);
387 test_cases_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
388 test_cases_options.addOption(bool, "enable_logging", enable_logging);
389 test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
390 test_cases_options.addOption(bool, "skip_non_native", skip_non_native);
391 test_cases_options.addOption(bool, "have_llvm", enable_llvm);
392 test_cases_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k);
393 test_cases_options.addOption(bool, "llvm_has_csky", llvm_has_csky);
394 test_cases_options.addOption(bool, "llvm_has_arc", llvm_has_arc);
395 test_cases_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa);
396 test_cases_options.addOption(bool, "force_gpa", force_gpa);
397 test_cases_options.addOption(bool, "only_c", only_c);
398 test_cases_options.addOption(bool, "only_core_functionality", true);
399 test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu);
400 test_cases_options.addOption(bool, "enable_wine", b.enable_wine);
401 test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime);
402 test_cases_options.addOption(bool, "enable_rosetta", b.enable_rosetta);
403 test_cases_options.addOption(bool, "enable_darling", b.enable_darling);
404 test_cases_options.addOption(u32, "mem_leak_frames", mem_leak_frames * 2);
405 test_cases_options.addOption(bool, "value_tracing", value_tracing);
406 test_cases_options.addOption(?[]const u8, "glibc_runtimes_dir", b.glibc_runtimes_dir);
407 test_cases_options.addOption([:0]const u8, "version", version);
408 test_cases_options.addOption(std.SemanticVersion, "semver", semver);
409 test_cases_options.addOption([]const []const u8, "test_filters", test_filters);
411 var chosen_opt_modes_buf: [4]builtin.OptimizeMode = undefined;
412 var chosen_mode_index: usize = 0;
414 chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.Debug;
415 chosen_mode_index += 1;
417 if (!skip_release_safe) {
418 chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSafe;
419 chosen_mode_index += 1;
421 if (!skip_release_fast) {
422 chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseFast;
423 chosen_mode_index += 1;
425 if (!skip_release_small) {
426 chosen_opt_modes_buf[chosen_mode_index] = builtin.OptimizeMode.ReleaseSmall;
427 chosen_mode_index += 1;
429 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
431 const fmt_include_paths = &.{ "doc", "lib", "src", "test", "tools", "build.zig" };
432 const fmt_exclude_paths = &.{"test/cases"};
433 const do_fmt = b.addFmt(.{
434 .paths = fmt_include_paths,
435 .exclude_paths = fmt_exclude_paths,
438 b.step("test-fmt", "Check source files having conforming formatting").dependOn(&b.addFmt(.{
439 .paths = fmt_include_paths,
440 .exclude_paths = fmt_exclude_paths,
444 const test_cases_step = b.step("test-cases", "Run the main compiler test cases");
445 try tests.addCases(b, test_cases_step, test_filters, check_case_exe, target, .{
446 .skip_translate_c = skip_translate_c,
447 .skip_run_translated_c = skip_run_translated_c,
449 .enable_llvm = enable_llvm,
450 .llvm_has_m68k = llvm_has_m68k,
451 .llvm_has_csky = llvm_has_csky,
452 .llvm_has_arc = llvm_has_arc,
453 .llvm_has_xtensa = llvm_has_xtensa,
455 test_step.dependOn(test_cases_step);
457 test_step.dependOn(tests.addModuleTests(b, .{
458 .test_filters = test_filters,
459 .root_src = "test/behavior.zig",
461 .desc = "Run the behavior tests",
462 .optimize_modes = optimization_modes,
463 .include_paths = &.{},
464 .skip_single_threaded = skip_single_threaded,
465 .skip_non_native = skip_non_native,
466 .skip_libc = skip_libc,
467 .max_rss = 1 * 1024 * 1024 * 1024,
470 test_step.dependOn(tests.addModuleTests(b, .{
471 .test_filters = test_filters,
472 .root_src = "test/c_import.zig",
474 .desc = "Run the @cImport tests",
475 .optimize_modes = optimization_modes,
476 .include_paths = &.{"test/c_import"},
477 .skip_single_threaded = true,
478 .skip_non_native = skip_non_native,
479 .skip_libc = skip_libc,
482 test_step.dependOn(tests.addModuleTests(b, .{
483 .test_filters = test_filters,
484 .root_src = "lib/compiler_rt.zig",
485 .name = "compiler-rt",
486 .desc = "Run the compiler_rt tests",
487 .optimize_modes = optimization_modes,
488 .include_paths = &.{},
489 .skip_single_threaded = true,
490 .skip_non_native = skip_non_native,
494 test_step.dependOn(tests.addModuleTests(b, .{
495 .test_filters = test_filters,
496 .root_src = "lib/c.zig",
497 .name = "universal-libc",
498 .desc = "Run the universal libc tests",
499 .optimize_modes = optimization_modes,
500 .include_paths = &.{},
501 .skip_single_threaded = true,
502 .skip_non_native = skip_non_native,
506 test_step.dependOn(tests.addCompareOutputTests(b, test_filters, optimization_modes));
507 test_step.dependOn(tests.addStandaloneTests(
512 enable_symlinks_windows,
514 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
515 test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows));
516 test_step.dependOn(tests.addStackTraceTests(b, test_filters, optimization_modes));
517 test_step.dependOn(tests.addCliTests(b));
518 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filters, optimization_modes));
519 test_step.dependOn(tests.addModuleTests(b, .{
520 .test_filters = test_filters,
521 .root_src = "lib/std/std.zig",
523 .desc = "Run the standard library tests",
524 .optimize_modes = optimization_modes,
525 .include_paths = &.{},
526 .skip_single_threaded = skip_single_threaded,
527 .skip_non_native = skip_non_native,
528 .skip_libc = skip_libc,
529 // I observed a value of 4572626944 on the M2 CI.
530 .max_rss = 5029889638,
533 try addWasiUpdateStep(b, version);
535 b.step("fmt", "Modify source files in place to have conforming formatting")
536 .dependOn(&do_fmt.step);
538 const update_mingw_step = b.step("update-mingw", "Update zig's bundled mingw");
539 const opt_mingw_src_path = b.option([]const u8, "mingw-src", "path to mingw-w64 source directory");
540 const update_mingw_exe = b.addExecutable(.{
541 .name = "update_mingw",
543 .root_source_file = b.path("tools/update_mingw.zig"),
545 const update_mingw_run = b.addRunArtifact(update_mingw_exe);
546 update_mingw_run.addDirectoryArg(b.path("lib"));
547 if (opt_mingw_src_path) |mingw_src_path| {
548 update_mingw_run.addDirectoryArg(.{ .cwd_relative = mingw_src_path });
550 // Intentionally cause an error if this build step is requested.
551 update_mingw_run.addArg("--missing-mingw-source-directory");
554 update_mingw_step.dependOn(&update_mingw_run.step);
557 fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
558 const semver = try std.SemanticVersion.parse(version);
560 var target_query: std.zig.CrossTarget = .{
564 target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory));
566 const exe = addCompilerStep(b, .{
567 .optimize = .ReleaseSmall,
568 .target = b.resolveTargetQuery(target_query),
571 const exe_options = b.addOptions();
572 exe.root_module.addOptions("build_options", exe_options);
574 exe_options.addOption(u32, "mem_leak_frames", 0);
575 exe_options.addOption(bool, "have_llvm", false);
576 exe_options.addOption(bool, "force_gpa", false);
577 exe_options.addOption(bool, "only_c", true);
578 exe_options.addOption([:0]const u8, "version", version);
579 exe_options.addOption(std.SemanticVersion, "semver", semver);
580 exe_options.addOption(bool, "enable_debug_extensions", false);
581 exe_options.addOption(bool, "enable_logging", false);
582 exe_options.addOption(bool, "enable_link_snapshots", false);
583 exe_options.addOption(bool, "enable_tracy", false);
584 exe_options.addOption(bool, "enable_tracy_callstack", false);
585 exe_options.addOption(bool, "enable_tracy_allocation", false);
586 exe_options.addOption(bool, "value_tracing", false);
587 exe_options.addOption(bool, "only_core_functionality", true);
589 const run_opt = b.addSystemCommand(&.{
592 "--enable-bulk-memory",
595 run_opt.addArtifactArg(exe);
596 run_opt.addArg("-o");
597 run_opt.addFileArg(b.path("stage1/zig1.wasm"));
599 const copy_zig_h = b.addWriteFiles();
600 copy_zig_h.addCopyFileToSource(b.path("lib/zig.h"), "stage1/zig.h");
602 const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm");
603 update_zig1_step.dependOn(&run_opt.step);
604 update_zig1_step.dependOn(©_zig_h.step);
607 const AddCompilerStepOptions = struct {
608 optimize: std.builtin.OptimizeMode,
609 target: std.Build.ResolvedTarget,
611 sanitize_thread: ?bool = null,
612 single_threaded: ?bool = null,
615 fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.Step.Compile {
616 const exe = b.addExecutable(.{
618 .root_source_file = b.path("src/main.zig"),
619 .target = options.target,
620 .optimize = options.optimize,
621 .max_rss = 7_000_000_000,
622 .strip = options.strip,
623 .sanitize_thread = options.sanitize_thread,
624 .single_threaded = options.single_threaded,
626 exe.stack_size = stack_size;
628 const aro_module = b.createModule(.{
629 .root_source_file = b.path("lib/compiler/aro/aro.zig"),
632 const aro_translate_c_module = b.createModule(.{
633 .root_source_file = b.path("lib/compiler/aro_translate_c.zig"),
637 .module = aro_module,
642 exe.root_module.addImport("aro", aro_module);
643 exe.root_module.addImport("aro_translate_c", aro_translate_c_module);
647 const exe_cflags = [_][]const u8{
649 "-D__STDC_CONSTANT_MACROS",
650 "-D__STDC_FORMAT_MACROS",
651 "-D__STDC_LIMIT_MACROS",
655 "-fno-stack-protector",
656 "-fvisibility-inlines-hidden",
658 "-Wno-missing-braces",
662 fn addCmakeCfgOptionsToExe(
665 exe: *std.Build.Step.Compile,
666 use_zig_libcxx: bool,
668 if (exe.rootModuleTarget().isDarwin()) {
669 // useful for package maintainers
670 exe.headerpad_max_install_names = true;
672 exe.addObjectFile(.{ .cwd_relative = b.pathJoin(&[_][]const u8{
673 cfg.cmake_binary_dir,
675 b.fmt("{s}{s}{s}", .{
676 cfg.cmake_static_library_prefix,
678 cfg.cmake_static_library_suffix,
681 assert(cfg.lld_include_dir.len != 0);
682 exe.addIncludePath(.{ .cwd_relative = cfg.lld_include_dir });
683 exe.addIncludePath(.{ .cwd_relative = cfg.llvm_include_dir });
684 exe.addLibraryPath(.{ .cwd_relative = cfg.llvm_lib_dir });
685 addCMakeLibraryList(exe, cfg.clang_libraries);
686 addCMakeLibraryList(exe, cfg.lld_libraries);
687 addCMakeLibraryList(exe, cfg.llvm_libraries);
689 if (use_zig_libcxx) {
692 // System -lc++ must be used because in this code path we are attempting to link
693 // against system-provided LLVM, Clang, LLD.
694 const need_cpp_includes = true;
695 const static = cfg.llvm_linkage == .static;
696 const lib_suffix = if (static) exe.rootModuleTarget().staticLibSuffix()[1..] else exe.rootModuleTarget().dynamicLibSuffix()[1..];
697 switch (exe.rootModuleTarget().os.tag) {
699 // First we try to link against the detected libcxx name. If that doesn't work, we fall
700 // back to -lc++ and cross our fingers.
701 addCxxKnownPath(b, cfg, exe, b.fmt("lib{s}.{s}", .{ cfg.system_libcxx, lib_suffix }), "", need_cpp_includes) catch |err| switch (err) {
702 error.RequiredLibraryNotFound => {
705 else => |e| return e,
707 exe.linkSystemLibrary("unwind");
709 .ios, .macos, .watchos, .tvos, .visionos => {
713 if (exe.rootModuleTarget().abi != .msvc) exe.linkLibCpp();
717 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
718 try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
720 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
724 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
725 try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{lib_suffix}), null, need_cpp_includes);
727 .netbsd, .dragonfly => {
729 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
730 try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
732 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
735 .solaris, .illumos => {
736 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
737 try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes);
740 try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes);
746 if (cfg.dia_guids_lib.len != 0) {
747 exe.addObjectFile(.{ .cwd_relative = cfg.dia_guids_lib });
751 fn addStaticLlvmOptionsToExe(exe: *std.Build.Step.Compile) !void {
752 // Adds the Zig C++ sources which both stage1 and stage2 need.
754 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
755 // in a dependency on llvm::cfg::Update<llvm::BasicBlock*>::dump() which is
756 // unavailable when LLVM is compiled in Release mode.
757 const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"};
758 exe.addCSourceFiles(.{
759 .files = &zig_cpp_sources,
760 .flags = &zig_cpp_cflags,
763 for (clang_libs) |lib_name| {
764 exe.linkSystemLibrary(lib_name);
767 for (lld_libs) |lib_name| {
768 exe.linkSystemLibrary(lib_name);
771 for (llvm_libs) |lib_name| {
772 exe.linkSystemLibrary(lib_name);
775 exe.linkSystemLibrary("z");
776 exe.linkSystemLibrary("zstd");
778 if (exe.rootModuleTarget().os.tag != .windows or exe.rootModuleTarget().abi != .msvc) {
779 // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries.
780 exe.linkSystemLibrary("c++");
783 if (exe.rootModuleTarget().os.tag == .windows) {
784 exe.linkSystemLibrary("version");
785 exe.linkSystemLibrary("uuid");
786 exe.linkSystemLibrary("ole32");
793 exe: *std.Build.Step.Compile,
796 need_cpp_includes: bool,
798 if (!std.process.can_spawn)
799 return error.RequiredLibraryNotFound;
801 const path_padded = run: {
802 var args = std.ArrayList([]const u8).init(b.allocator);
803 try args.append(ctx.cxx_compiler);
804 var it = std.mem.tokenizeAny(u8, ctx.cxx_compiler_arg1, &std.ascii.whitespace);
805 while (it.next()) |arg| try args.append(arg);
806 try args.append(b.fmt("-print-file-name={s}", .{objname}));
807 break :run b.run(args.items);
809 var tokenizer = mem.tokenizeAny(u8, path_padded, "\r\n");
810 const path_unpadded = tokenizer.next().?;
811 if (mem.eql(u8, path_unpadded, objname)) {
813 std.debug.print("{s}", .{msg});
815 std.debug.print("Unable to determine path to {s}\n", .{objname});
817 return error.RequiredLibraryNotFound;
819 exe.addObjectFile(.{ .cwd_relative = path_unpadded });
821 // TODO a way to integrate with system c++ include files here
822 // c++ -E -Wp,-v -xc++ /dev/null
823 if (need_cpp_includes) {
824 // I used these temporarily for testing something but we obviously need a
825 // more general purpose solution here.
826 //exe.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0");
827 //exe.addIncludePath("/nix/store/2lr0fc0ak8rwj0k8n3shcyz1hz63wzma-gcc-11.3.0/include/c++/11.3.0/x86_64-unknown-linux-gnu");
831 fn addCMakeLibraryList(exe: *std.Build.Step.Compile, list: []const u8) void {
832 var it = mem.tokenizeScalar(u8, list, ';');
833 while (it.next()) |lib| {
834 if (mem.startsWith(u8, lib, "-l")) {
835 exe.linkSystemLibrary(lib["-l".len..]);
836 } else if (exe.rootModuleTarget().os.tag == .windows and
837 mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib))
839 exe.linkSystemLibrary(lib[0 .. lib.len - ".lib".len]);
841 exe.addObjectFile(.{ .cwd_relative = lib });
846 const CMakeConfig = struct {
847 llvm_linkage: std.builtin.LinkMode,
848 cmake_binary_dir: []const u8,
849 cmake_prefix_path: []const u8,
850 cmake_static_library_prefix: []const u8,
851 cmake_static_library_suffix: []const u8,
852 cxx_compiler: []const u8,
853 cxx_compiler_arg1: []const u8,
854 lld_include_dir: []const u8,
855 lld_libraries: []const u8,
856 clang_libraries: []const u8,
857 llvm_lib_dir: []const u8,
858 llvm_include_dir: []const u8,
859 llvm_libraries: []const u8,
860 dia_guids_lib: []const u8,
861 system_libcxx: []const u8,
864 const max_config_h_bytes = 1 * 1024 * 1024;
866 fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
867 if (config_h_path_option) |path| {
868 var config_h_or_err = fs.cwd().openFile(path, .{});
869 if (config_h_or_err) |*file| {
873 std.log.err("Could not open provided config.h: \"{s}\"", .{path});
878 var check_dir = fs.path.dirname(b.graph.zig_exe).?;
880 var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
883 // Check if config.h is present in dir
884 var config_h_or_err = dir.openFile("config.h", .{});
885 if (config_h_or_err) |*file| {
889 &[_][]const u8{ check_dir, "config.h" },
891 } else |e| switch (e) {
892 error.FileNotFound => {},
896 // Check if we reached the source root by looking for .git, and bail if so
897 var git_dir_or_err = dir.openDir(".git", .{});
898 if (git_dir_or_err) |*git_dir| {
903 // Otherwise, continue search in the parent directory
904 const new_check_dir = fs.path.dirname(check_dir);
905 if (new_check_dir == null or mem.eql(u8, new_check_dir.?, check_dir)) {
908 check_dir = new_check_dir.?;
912 fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
913 var ctx: CMakeConfig = .{
914 .llvm_linkage = undefined,
915 .cmake_binary_dir = undefined,
916 .cmake_prefix_path = undefined,
917 .cmake_static_library_prefix = undefined,
918 .cmake_static_library_suffix = undefined,
919 .cxx_compiler = undefined,
920 .cxx_compiler_arg1 = "",
921 .lld_include_dir = undefined,
922 .lld_libraries = undefined,
923 .clang_libraries = undefined,
924 .llvm_lib_dir = undefined,
925 .llvm_include_dir = undefined,
926 .llvm_libraries = undefined,
927 .dia_guids_lib = undefined,
928 .system_libcxx = undefined,
931 const mappings = [_]struct { prefix: []const u8, field: []const u8 }{
933 .prefix = "#define ZIG_CMAKE_BINARY_DIR ",
934 .field = "cmake_binary_dir",
937 .prefix = "#define ZIG_CMAKE_PREFIX_PATH ",
938 .field = "cmake_prefix_path",
941 .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_PREFIX ",
942 .field = "cmake_static_library_prefix",
945 .prefix = "#define ZIG_CMAKE_STATIC_LIBRARY_SUFFIX ",
946 .field = "cmake_static_library_suffix",
949 .prefix = "#define ZIG_CXX_COMPILER ",
950 .field = "cxx_compiler",
953 .prefix = "#define ZIG_CXX_COMPILER_ARG1 ",
954 .field = "cxx_compiler_arg1",
957 .prefix = "#define ZIG_LLD_INCLUDE_PATH ",
958 .field = "lld_include_dir",
961 .prefix = "#define ZIG_LLD_LIBRARIES ",
962 .field = "lld_libraries",
965 .prefix = "#define ZIG_CLANG_LIBRARIES ",
966 .field = "clang_libraries",
969 .prefix = "#define ZIG_LLVM_LIBRARIES ",
970 .field = "llvm_libraries",
973 .prefix = "#define ZIG_DIA_GUIDS_LIB ",
974 .field = "dia_guids_lib",
977 .prefix = "#define ZIG_LLVM_INCLUDE_PATH ",
978 .field = "llvm_include_dir",
981 .prefix = "#define ZIG_LLVM_LIB_PATH ",
982 .field = "llvm_lib_dir",
985 .prefix = "#define ZIG_SYSTEM_LIBCXX",
986 .field = "system_libcxx",
988 // .prefix = ZIG_LLVM_LINK_MODE parsed manually below
991 var lines_it = mem.tokenizeAny(u8, config_h_text, "\r\n");
992 while (lines_it.next()) |line| {
993 inline for (mappings) |mapping| {
994 if (mem.startsWith(u8, line, mapping.prefix)) {
995 var it = mem.splitScalar(u8, line, '"');
996 _ = it.first(); // skip the stuff before the quote
997 const quoted = it.next().?; // the stuff inside the quote
998 const trimmed = mem.trim(u8, quoted, " ");
999 @field(ctx, mapping.field) = toNativePathSep(b, trimmed);
1002 if (mem.startsWith(u8, line, "#define ZIG_LLVM_LINK_MODE ")) {
1003 var it = mem.splitScalar(u8, line, '"');
1004 _ = it.next().?; // skip the stuff before the quote
1005 const quoted = it.next().?; // the stuff inside the quote
1006 ctx.llvm_linkage = if (mem.eql(u8, quoted, "shared")) .dynamic else .static;
1012 fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
1013 const duplicated = b.allocator.dupe(u8, s) catch unreachable;
1014 for (duplicated) |*byte| switch (byte.*) {
1015 '/' => byte.* = fs.path.sep,
1021 const zig_cpp_sources = [_][]const u8{
1022 // These are planned to stay even when we are self-hosted.
1024 "src/zig_clang.cpp",
1025 "src/zig_llvm-ar.cpp",
1026 "src/zig_clang_driver.cpp",
1027 "src/zig_clang_cc1_main.cpp",
1028 "src/zig_clang_cc1as_main.cpp",
1031 const clang_libs = [_][]const u8{
1032 "clangFrontendTool",
1036 "clangSerialization",
1038 "clangStaticAnalyzerFrontend",
1039 "clangStaticAnalyzerCheckers",
1040 "clangStaticAnalyzerCore",
1051 "clangRewriteFrontend",
1059 const lld_libs = [_][]const u8{
1067 // This list can be re-generated with `llvm-config --libfiles` and then
1068 // reformatting using your favorite text editor. Note we do not execute
1069 // `llvm-config` here because we are cross compiling. Also omit LLVMTableGen
1071 const llvm_libs = [_][]const u8{
1072 "LLVMWindowsManifest",
1075 "LLVMDlltoolDriver",
1076 "LLVMTextAPIBinaryReader",
1079 "LLVMXCoreDisassembler",
1084 "LLVMX86Disassembler",
1089 "LLVMWebAssemblyDisassembler",
1090 "LLVMWebAssemblyAsmParser",
1091 "LLVMWebAssemblyCodeGen",
1092 "LLVMWebAssemblyUtils",
1093 "LLVMWebAssemblyDesc",
1094 "LLVMWebAssemblyInfo",
1095 "LLVMVEDisassembler",
1100 "LLVMSystemZDisassembler",
1101 "LLVMSystemZAsmParser",
1102 "LLVMSystemZCodeGen",
1105 "LLVMSparcDisassembler",
1106 "LLVMSparcAsmParser",
1110 "LLVMRISCVTargetMCA",
1111 "LLVMRISCVDisassembler",
1112 "LLVMRISCVAsmParser",
1116 "LLVMPowerPCDisassembler",
1117 "LLVMPowerPCAsmParser",
1118 "LLVMPowerPCCodeGen",
1124 "LLVMMSP430Disassembler",
1125 "LLVMMSP430AsmParser",
1126 "LLVMMSP430CodeGen",
1129 "LLVMMipsDisassembler",
1130 "LLVMMipsAsmParser",
1134 "LLVMLoongArchDisassembler",
1135 "LLVMLoongArchAsmParser",
1136 "LLVMLoongArchCodeGen",
1137 "LLVMLoongArchDesc",
1138 "LLVMLoongArchInfo",
1139 "LLVMLanaiDisassembler",
1141 "LLVMLanaiAsmParser",
1144 "LLVMHexagonDisassembler",
1145 "LLVMHexagonCodeGen",
1146 "LLVMHexagonAsmParser",
1149 "LLVMBPFDisassembler",
1154 "LLVMAVRDisassembler",
1159 "LLVMARMDisassembler",
1165 "LLVMAMDGPUTargetMCA",
1166 "LLVMAMDGPUDisassembler",
1167 "LLVMAMDGPUAsmParser",
1168 "LLVMAMDGPUCodeGen",
1172 "LLVMAArch64Disassembler",
1173 "LLVMAArch64AsmParser",
1174 "LLVMAArch64CodeGen",
1180 "LLVMWindowsDriver",
1184 "LLVMExecutionEngine",
1186 "LLVMOrcTargetProcess",
1189 "LLVMDebugInfoLogicalView",
1190 "LLVMDebugInfoGSYM",
1195 "LLVMMCDisassembler",
1204 "LLVMInstrumentation",
1205 "LLVMFrontendOpenMP",
1206 "LLVMFrontendOffloading",
1207 "LLVMFrontendOpenACC",
1209 "LLVMFrontendDriver",
1211 "LLVMDWARFLinkerParallel",
1212 "LLVMDWARFLinkerClassic",
1223 "LLVMInterfaceStub",
1228 "LLVMAggressiveInstCombine",
1229 "LLVMTransformUtils",
1237 "LLVMDebugInfoDWARF",
1244 "LLVMDebugInfoCodeView",
1249 "LLVMBitstreamReader",
1256 fn generateLangRef(b: *std.Build) std.Build.LazyPath {
1257 const doctest_exe = b.addExecutable(.{
1259 .root_source_file = b.path("tools/doctest.zig"),
1264 var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
1265 std.debug.panic("unable to open 'doc/langref' directory: {s}", .{@errorName(err)});
1269 var wf = b.addWriteFiles();
1271 var it = dir.iterateAssumeFirstIteration();
1272 while (it.next() catch @panic("failed to read dir")) |entry| {
1273 if (std.mem.startsWith(u8, entry.name, ".") or entry.kind != .file)
1276 const out_basename = b.fmt("{s}.out", .{std.fs.path.stem(entry.name)});
1277 const cmd = b.addRunArtifact(doctest_exe);
1279 "--zig", b.graph.zig_exe,
1280 // TODO: enhance doctest to use "--listen=-" rather than operating
1281 // in a temporary directory
1282 "--cache-root", b.cache_root.path orelse ".",
1284 if (b.zig_lib_dir) |p| {
1285 cmd.addArg("--zig-lib-dir");
1286 cmd.addDirectoryArg(p);
1288 cmd.addArgs(&.{"-i"});
1289 cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name})));
1291 cmd.addArgs(&.{"-o"});
1292 _ = wf.addCopyFile(cmd.addOutputFileArg(out_basename), out_basename);
1295 const docgen_exe = b.addExecutable(.{
1297 .root_source_file = b.path("tools/docgen.zig"),
1302 const docgen_cmd = b.addRunArtifact(docgen_exe);
1303 docgen_cmd.addArgs(&.{"--code-dir"});
1304 docgen_cmd.addDirectoryArg(wf.getDirectory());
1306 docgen_cmd.addFileArg(b.path("doc/langref.html.in"));
1307 return docgen_cmd.addOutputFileArg("langref.html");