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