master
1const std = @import("std");
2const path = std.fs.path;
3const assert = std.debug.assert;
4
5const target_util = @import("../target.zig");
6const Compilation = @import("../Compilation.zig");
7const build_options = @import("build_options");
8const trace = @import("../tracy.zig").trace;
9const Module = @import("../Package/Module.zig");
10
11const libcxxabi_files = [_][]const u8{
12 "src/cxa_aux_runtime.cpp",
13 "src/cxa_default_handlers.cpp",
14 "src/cxa_demangle.cpp",
15 "src/cxa_exception_storage.cpp",
16 "src/cxa_guard.cpp",
17 "src/cxa_handlers.cpp",
18 "src/cxa_vector.cpp",
19 "src/cxa_virtual.cpp",
20
21 "src/stdlib_exception.cpp",
22 "src/stdlib_stdexcept.cpp",
23 "src/stdlib_typeinfo.cpp",
24 "src/stdlib_new_delete.cpp",
25
26 "src/abort_message.cpp",
27 "src/fallback_malloc.cpp",
28 "src/private_typeinfo.cpp",
29
30 "src/cxa_exception.cpp",
31 "src/cxa_personality.cpp",
32
33 "src/cxa_noexception.cpp",
34
35 "src/cxa_thread_atexit.cpp",
36};
37
38const libcxx_base_files = [_][]const u8{
39 "src/algorithm.cpp",
40 "src/any.cpp",
41 "src/bind.cpp",
42 "src/call_once.cpp",
43 "src/charconv.cpp",
44 "src/chrono.cpp",
45 "src/error_category.cpp",
46 "src/exception.cpp",
47 "src/expected.cpp",
48 "src/filesystem/directory_entry.cpp",
49 "src/filesystem/directory_iterator.cpp",
50 "src/filesystem/filesystem_clock.cpp",
51 "src/filesystem/filesystem_error.cpp",
52 // omit int128_builtins.cpp because it provides __muloti4 which is already provided
53 // by compiler_rt and crashes on Windows x86_64: https://github.com/ziglang/zig/issues/10719
54 //"src/filesystem/int128_builtins.cpp",
55 "src/filesystem/operations.cpp",
56 "src/filesystem/path.cpp",
57 "src/fstream.cpp",
58 "src/functional.cpp",
59 "src/hash.cpp",
60 "src/ios.cpp",
61 "src/ios.instantiations.cpp",
62 "src/iostream.cpp",
63 "src/locale.cpp",
64 "src/memory.cpp",
65 "src/memory_resource.cpp",
66 "src/new.cpp",
67 "src/new_handler.cpp",
68 "src/new_helpers.cpp",
69 "src/optional.cpp",
70 "src/ostream.cpp",
71 "src/print.cpp",
72 //"src/pstl/libdispatch.cpp",
73 "src/random.cpp",
74 "src/random_shuffle.cpp",
75 "src/regex.cpp",
76 "src/ryu/d2fixed.cpp",
77 "src/ryu/d2s.cpp",
78 "src/ryu/f2s.cpp",
79 "src/stdexcept.cpp",
80 "src/string.cpp",
81 "src/strstream.cpp",
82 "src/support/win32/locale_win32.cpp",
83 "src/support/win32/support.cpp",
84 "src/system_error.cpp",
85 "src/typeinfo.cpp",
86 "src/valarray.cpp",
87 "src/variant.cpp",
88 "src/vector.cpp",
89 "src/verbose_abort.cpp",
90};
91
92const libcxx_thread_files = [_][]const u8{
93 "src/atomic.cpp",
94 "src/barrier.cpp",
95 "src/condition_variable.cpp",
96 "src/condition_variable_destructor.cpp",
97 "src/future.cpp",
98 "src/mutex.cpp",
99 "src/mutex_destructor.cpp",
100 "src/shared_mutex.cpp",
101 "src/support/win32/thread_win32.cpp",
102 "src/thread.cpp",
103};
104
105pub const BuildError = error{
106 OutOfMemory,
107 AlreadyReported,
108 ZigCompilerNotBuiltWithLLVMExtensions,
109};
110
111pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
112 if (!build_options.have_llvm) {
113 return error.ZigCompilerNotBuiltWithLLVMExtensions;
114 }
115
116 const tracy = trace(@src());
117 defer tracy.end();
118
119 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
120 defer arena_allocator.deinit();
121 const arena = arena_allocator.allocator();
122
123 const io = comp.io;
124 const root_name = "c++";
125 const output_mode = .Lib;
126 const link_mode = .static;
127 const target = &comp.root_mod.resolved_target.result;
128
129 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });
130 const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" });
131 const cxx_src_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "src" });
132 const cxx_libc_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "libc" });
133
134 const optimize_mode = comp.compilerRtOptMode();
135 const strip = comp.compilerRtStrip();
136
137 const config = Compilation.Config.resolve(.{
138 .output_mode = output_mode,
139 .link_mode = link_mode,
140 .resolved_target = comp.root_mod.resolved_target,
141 .is_test = false,
142 .have_zcu = false,
143 .emit_bin = true,
144 .root_optimize_mode = optimize_mode,
145 .root_strip = strip,
146 .link_libc = true,
147 .lto = comp.config.lto,
148 .any_sanitize_thread = comp.config.any_sanitize_thread,
149 }) catch |err| {
150 comp.lockAndSetMiscFailure(
151 .libcxx,
152 "unable to build libc++: resolving configuration failed: {s}",
153 .{@errorName(err)},
154 );
155 return error.AlreadyReported;
156 };
157
158 const root_mod = Module.create(arena, .{
159 .paths = .{
160 .root = .zig_lib_root,
161 .root_src_path = "",
162 },
163 .fully_qualified_name = "root",
164 .inherited = .{
165 .resolved_target = comp.root_mod.resolved_target,
166 .strip = strip,
167 .stack_check = false,
168 .stack_protector = 0,
169 .sanitize_c = .off,
170 .sanitize_thread = comp.config.any_sanitize_thread,
171 .red_zone = comp.root_mod.red_zone,
172 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
173 .valgrind = false,
174 .optimize_mode = optimize_mode,
175 .structured_cfg = comp.root_mod.structured_cfg,
176 .pic = if (target_util.supports_fpic(target)) true else null,
177 .code_model = comp.root_mod.code_model,
178 },
179 .global = config,
180 .cc_argv = &.{},
181 .parent = null,
182 }) catch |err| {
183 comp.lockAndSetMiscFailure(
184 .libcxx,
185 "unable to build libc++: creating module failed: {s}",
186 .{@errorName(err)},
187 );
188 return error.AlreadyReported;
189 };
190
191 const libcxx_files = if (comp.config.any_non_single_threaded)
192 &(libcxx_base_files ++ libcxx_thread_files)
193 else
194 &libcxx_base_files;
195
196 var c_source_files = try std.array_list.Managed(Compilation.CSourceFile).initCapacity(arena, libcxx_files.len);
197
198 for (libcxx_files) |cxx_src| {
199 // These don't compile on WASI due to e.g. `fchmod` usage.
200 if (std.mem.startsWith(u8, cxx_src, "src/filesystem/") and target.os.tag == .wasi)
201 continue;
202 if (std.mem.startsWith(u8, cxx_src, "src/support/win32/") and target.os.tag != .windows)
203 continue;
204
205 var cflags = std.array_list.Managed([]const u8).init(arena);
206
207 try addCxxArgs(comp, arena, &cflags);
208
209 try cflags.append("-DNDEBUG");
210 try cflags.append("-DLIBC_NAMESPACE=__llvm_libc_common_utils");
211 try cflags.append("-D_LIBCPP_BUILDING_LIBRARY");
212 try cflags.append("-DLIBCXX_BUILDING_LIBCXXABI");
213 try cflags.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER");
214
215 if (target.os.tag == .wasi) {
216 try cflags.append("-fno-exceptions");
217 }
218
219 try cflags.append("-fvisibility=hidden");
220 try cflags.append("-fvisibility-inlines-hidden");
221
222 try cflags.append("-faligned-allocation");
223
224 try cflags.append("-nostdinc++");
225 try cflags.append("-std=c++23");
226 try cflags.append("-Wno-user-defined-literals");
227 try cflags.append("-Wno-covered-switch-default");
228 try cflags.append("-Wno-suggest-override");
229
230 // These depend on only the zig lib directory file path, which is
231 // purposefully either in the cache or not in the cache. The decision
232 // should not be overridden here.
233 var cache_exempt_flags = std.array_list.Managed([]const u8).init(arena);
234
235 try cache_exempt_flags.append("-I");
236 try cache_exempt_flags.append(cxx_include_path);
237
238 try cache_exempt_flags.append("-I");
239 try cache_exempt_flags.append(cxxabi_include_path);
240
241 try cache_exempt_flags.append("-I");
242 try cache_exempt_flags.append(cxx_src_include_path);
243
244 try cache_exempt_flags.append("-I");
245 try cache_exempt_flags.append(cxx_libc_include_path);
246
247 c_source_files.appendAssumeCapacity(.{
248 .src_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", cxx_src }),
249 .extra_flags = cflags.items,
250 .cache_exempt_flags = cache_exempt_flags.items,
251 .owner = root_mod,
252 });
253 }
254
255 const misc_task: Compilation.MiscTask = .libcxx;
256
257 var sub_create_diag: Compilation.CreateDiagnostic = undefined;
258 const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
259 .dirs = comp.dirs.withoutLocalCache(),
260 .self_exe_path = comp.self_exe_path,
261 .cache_mode = .whole,
262 .config = config,
263 .root_mod = root_mod,
264 .root_name = root_name,
265 .thread_pool = comp.thread_pool,
266 .libc_installation = comp.libc_installation,
267 .emit_bin = .yes_cache,
268 .c_source_files = c_source_files.items,
269 .verbose_cc = comp.verbose_cc,
270 .verbose_link = comp.verbose_link,
271 .verbose_air = comp.verbose_air,
272 .verbose_llvm_ir = comp.verbose_llvm_ir,
273 .verbose_llvm_bc = comp.verbose_llvm_bc,
274 .verbose_cimport = comp.verbose_cimport,
275 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
276 .clang_passthrough_mode = comp.clang_passthrough_mode,
277 .skip_linker_dependencies = true,
278 }) catch |err| {
279 switch (err) {
280 else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {t}", .{err}),
281 error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {f}", .{sub_create_diag}),
282 }
283 return error.AlreadyReported;
284 };
285 defer sub_compilation.destroy();
286
287 comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) {
288 error.AlreadyReported => return error.AlreadyReported,
289 else => |e| {
290 comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: compilation failed: {t}", .{e});
291 return error.AlreadyReported;
292 },
293 };
294
295 assert(comp.libcxx_static_lib == null);
296 const crt_file = try sub_compilation.toCrtFile();
297 comp.libcxx_static_lib = crt_file;
298 comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
299}
300
301pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
302 if (!build_options.have_llvm) {
303 return error.ZigCompilerNotBuiltWithLLVMExtensions;
304 }
305
306 const tracy = trace(@src());
307 defer tracy.end();
308
309 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
310 defer arena_allocator.deinit();
311 const arena = arena_allocator.allocator();
312
313 const io = comp.io;
314 const root_name = "c++abi";
315 const output_mode = .Lib;
316 const link_mode = .static;
317 const target = &comp.root_mod.resolved_target.result;
318
319 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });
320 const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" });
321 const cxx_src_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "src" });
322
323 const optimize_mode = comp.compilerRtOptMode();
324 const strip = comp.compilerRtStrip();
325 // See the `-fno-exceptions` logic for WASI.
326 // The old 32-bit x86 variant of SEH doesn't use tables.
327 const unwind_tables: std.builtin.UnwindTables =
328 if (target.os.tag == .wasi or (target.cpu.arch == .x86 and target.os.tag == .windows)) .none else .async;
329
330 const config = Compilation.Config.resolve(.{
331 .output_mode = output_mode,
332 .link_mode = link_mode,
333 .resolved_target = comp.root_mod.resolved_target,
334 .is_test = false,
335 .have_zcu = false,
336 .emit_bin = true,
337 .root_optimize_mode = optimize_mode,
338 .root_strip = strip,
339 .link_libc = true,
340 .any_unwind_tables = unwind_tables != .none,
341 .lto = comp.config.lto,
342 .any_sanitize_thread = comp.config.any_sanitize_thread,
343 }) catch |err| {
344 comp.lockAndSetMiscFailure(
345 .libcxxabi,
346 "unable to build libc++abi: resolving configuration failed: {s}",
347 .{@errorName(err)},
348 );
349 return error.AlreadyReported;
350 };
351
352 const root_mod = Module.create(arena, .{
353 .paths = .{
354 .root = .zig_lib_root,
355 .root_src_path = "",
356 },
357 .fully_qualified_name = "root",
358 .inherited = .{
359 .resolved_target = comp.root_mod.resolved_target,
360 .strip = strip,
361 .stack_check = false,
362 .stack_protector = 0,
363 .sanitize_c = .off,
364 .sanitize_thread = comp.config.any_sanitize_thread,
365 .red_zone = comp.root_mod.red_zone,
366 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
367 .valgrind = false,
368 .optimize_mode = optimize_mode,
369 .structured_cfg = comp.root_mod.structured_cfg,
370 .unwind_tables = unwind_tables,
371 .pic = if (target_util.supports_fpic(target)) true else null,
372 .code_model = comp.root_mod.code_model,
373 },
374 .global = config,
375 .cc_argv = &.{},
376 .parent = null,
377 }) catch |err| {
378 comp.lockAndSetMiscFailure(
379 .libcxxabi,
380 "unable to build libc++abi: creating module failed: {s}",
381 .{@errorName(err)},
382 );
383 return error.AlreadyReported;
384 };
385
386 var c_source_files = try std.array_list.Managed(Compilation.CSourceFile).initCapacity(arena, libcxxabi_files.len);
387
388 for (libcxxabi_files) |cxxabi_src| {
389 if (!comp.config.any_non_single_threaded and std.mem.eql(u8, cxxabi_src, "src/cxa_thread_atexit.cpp"))
390 continue;
391 if (target.os.tag == .wasi and
392 (std.mem.eql(u8, cxxabi_src, "src/cxa_exception.cpp") or std.mem.eql(u8, cxxabi_src, "src/cxa_personality.cpp")))
393 continue;
394 if (target.os.tag != .wasi and std.mem.eql(u8, cxxabi_src, "src/cxa_noexception.cpp"))
395 continue;
396
397 var cflags = std.array_list.Managed([]const u8).init(arena);
398
399 try addCxxArgs(comp, arena, &cflags);
400
401 try cflags.append("-DNDEBUG");
402 try cflags.append("-D_LIBCPP_BUILDING_LIBRARY");
403 try cflags.append("-D_LIBCXXABI_BUILDING_LIBRARY");
404 if (!comp.config.any_non_single_threaded) {
405 try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS");
406 }
407 if (target.abi.isGnu()) {
408 if (target.os.tag != .linux or !(target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 18, .patch = 0 }) == .lt))
409 try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL");
410 }
411
412 if (target.os.tag == .wasi) {
413 try cflags.append("-fno-exceptions");
414 }
415
416 try cflags.append("-fvisibility=hidden");
417 try cflags.append("-fvisibility-inlines-hidden");
418
419 try cflags.append("-nostdinc++");
420 try cflags.append("-fstrict-aliasing");
421 try cflags.append("-std=c++23");
422 try cflags.append("-Wno-user-defined-literals");
423 try cflags.append("-Wno-covered-switch-default");
424 try cflags.append("-Wno-suggest-override");
425
426 // These depend on only the zig lib directory file path, which is
427 // purposefully either in the cache or not in the cache. The decision
428 // should not be overridden here.
429 var cache_exempt_flags = std.array_list.Managed([]const u8).init(arena);
430
431 try cache_exempt_flags.append("-I");
432 try cache_exempt_flags.append(cxxabi_include_path);
433
434 try cache_exempt_flags.append("-I");
435 try cache_exempt_flags.append(cxx_include_path);
436
437 try cache_exempt_flags.append("-I");
438 try cache_exempt_flags.append(cxx_src_include_path);
439
440 c_source_files.appendAssumeCapacity(.{
441 .src_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", cxxabi_src }),
442 .extra_flags = cflags.items,
443 .cache_exempt_flags = cache_exempt_flags.items,
444 .owner = root_mod,
445 });
446 }
447
448 const misc_task: Compilation.MiscTask = .libcxxabi;
449
450 var sub_create_diag: Compilation.CreateDiagnostic = undefined;
451 const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
452 .dirs = comp.dirs.withoutLocalCache(),
453 .self_exe_path = comp.self_exe_path,
454 .cache_mode = .whole,
455 .config = config,
456 .root_mod = root_mod,
457 .root_name = root_name,
458 .thread_pool = comp.thread_pool,
459 .libc_installation = comp.libc_installation,
460 .emit_bin = .yes_cache,
461 .c_source_files = c_source_files.items,
462 .verbose_cc = comp.verbose_cc,
463 .verbose_link = comp.verbose_link,
464 .verbose_air = comp.verbose_air,
465 .verbose_llvm_ir = comp.verbose_llvm_ir,
466 .verbose_llvm_bc = comp.verbose_llvm_bc,
467 .verbose_cimport = comp.verbose_cimport,
468 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
469 .clang_passthrough_mode = comp.clang_passthrough_mode,
470 .skip_linker_dependencies = true,
471 }) catch |err| {
472 switch (err) {
473 else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {t}", .{err}),
474 error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {f}", .{sub_create_diag}),
475 }
476 return error.AlreadyReported;
477 };
478 defer sub_compilation.destroy();
479
480 comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) {
481 error.AlreadyReported => return error.AlreadyReported,
482 else => |e| {
483 comp.lockAndSetMiscFailure(
484 .libcxxabi,
485 "unable to build libc++abi: compilation failed: {s}",
486 .{@errorName(e)},
487 );
488 return error.AlreadyReported;
489 },
490 };
491
492 assert(comp.libcxxabi_static_lib == null);
493 const crt_file = try sub_compilation.toCrtFile();
494 comp.libcxxabi_static_lib = crt_file;
495 comp.queuePrelinkTaskMode(crt_file.full_object_path, &config);
496}
497
498pub fn addCxxArgs(
499 comp: *const Compilation,
500 arena: std.mem.Allocator,
501 cflags: *std.array_list.Managed([]const u8),
502) error{OutOfMemory}!void {
503 const target = comp.getTarget();
504 const optimize_mode = comp.compilerRtOptMode();
505
506 const abi_version: u2 = if (target.os.tag == .emscripten) 2 else 1;
507 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_VERSION={d}", .{
508 abi_version,
509 }));
510 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{
511 abi_version,
512 }));
513 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_THREADS={d}", .{
514 @as(u1, if (comp.config.any_non_single_threaded) 1 else 0),
515 }));
516 try cflags.append("-D_LIBCPP_HAS_MONOTONIC_CLOCK");
517 try cflags.append("-D_LIBCPP_HAS_TERMINAL");
518 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_MUSL_LIBC={d}", .{
519 @as(u1, if (target.abi.isMusl()) 1 else 0),
520 }));
521 try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
522 try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
523 try cflags.append("-D_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS=0");
524 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_FILESYSTEM={d}", .{
525 @as(u1, if (target.os.tag == .wasi) 0 else 1),
526 }));
527 try cflags.append("-D_LIBCPP_HAS_RANDOM_DEVICE");
528 try cflags.append("-D_LIBCPP_HAS_LOCALIZATION");
529 try cflags.append("-D_LIBCPP_HAS_UNICODE");
530 try cflags.append("-D_LIBCPP_HAS_WIDE_CHARACTERS");
531 try cflags.append("-D_LIBCPP_HAS_NO_STD_MODULES");
532 if (target.os.tag == .linux) {
533 try cflags.append("-D_LIBCPP_HAS_TIME_ZONE_DATABASE");
534 }
535 // See libcxx/include/__algorithm/pstl_backends/cpu_backends/backend.h
536 // for potentially enabling some fancy features here, which would
537 // require corresponding changes in libcxx.zig, as well as
538 // Compilation.addCCArgs. This option makes it use serial backend which
539 // is simple and works everywhere.
540 try cflags.append("-D_LIBCPP_PSTL_BACKEND_SERIAL");
541 try cflags.append(switch (optimize_mode) {
542 .Debug => "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG",
543 .ReleaseFast, .ReleaseSmall => "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE",
544 .ReleaseSafe => "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST",
545 });
546 if (target.isGnuLibC()) {
547 // glibc 2.16 introduced aligned_alloc
548 if (target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) {
549 try cflags.append("-D_LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION=0");
550 }
551 }
552}