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("../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} || std.Io.Cancelable;
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 .pic = if (target_util.supports_fpic(target)) true else null,
176 .code_model = comp.root_mod.code_model,
177 },
178 .global = config,
179 .cc_argv = &.{},
180 .parent = null,
181 }) catch |err| {
182 comp.lockAndSetMiscFailure(
183 .libcxx,
184 "unable to build libc++: creating module failed: {s}",
185 .{@errorName(err)},
186 );
187 return error.AlreadyReported;
188 };
189
190 const libcxx_files = if (comp.config.any_non_single_threaded)
191 &(libcxx_base_files ++ libcxx_thread_files)
192 else
193 &libcxx_base_files;
194
195 var c_source_files = try std.array_list.Managed(Compilation.CSourceFile).initCapacity(arena, libcxx_files.len);
196
197 for (libcxx_files) |cxx_src| {
198 // These don't compile on WASI due to e.g. `fchmod` usage.
199 if (std.mem.startsWith(u8, cxx_src, "src/filesystem/") and target.os.tag == .wasi)
200 continue;
201 if (std.mem.startsWith(u8, cxx_src, "src/support/win32/") and target.os.tag != .windows)
202 continue;
203
204 var cflags = std.array_list.Managed([]const u8).init(arena);
205
206 try addCxxArgs(comp, arena, &cflags);
207
208 try cflags.append("-w"); // Disable all warnings.
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
227 // These depend on only the zig lib directory file path, which is
228 // purposefully either in the cache or not in the cache. The decision
229 // should not be overridden here.
230 var cache_exempt_flags = std.array_list.Managed([]const u8).init(arena);
231
232 try cache_exempt_flags.append("-I");
233 try cache_exempt_flags.append(cxx_include_path);
234
235 try cache_exempt_flags.append("-I");
236 try cache_exempt_flags.append(cxxabi_include_path);
237
238 try cache_exempt_flags.append("-I");
239 try cache_exempt_flags.append(cxx_src_include_path);
240
241 try cache_exempt_flags.append("-I");
242 try cache_exempt_flags.append(cxx_libc_include_path);
243
244 c_source_files.appendAssumeCapacity(.{
245 .src_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", cxx_src }),
246 .extra_flags = cflags.items,
247 .cache_exempt_flags = cache_exempt_flags.items,
248 .owner = root_mod,
249 });
250 }
251
252 const misc_task: Compilation.MiscTask = .libcxx;
253
254 var sub_create_diag: Compilation.CreateDiagnostic = undefined;
255 const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
256 .thread_limit = comp.thread_limit,
257 .dirs = comp.dirs.withoutLocalCache(),
258 .self_exe_path = comp.self_exe_path,
259 .cache_mode = .whole,
260 .config = config,
261 .root_mod = root_mod,
262 .root_name = root_name,
263 .libc_installation = comp.libc_installation,
264 .emit_bin = .yes_cache,
265 .function_sections = true,
266 .data_sections = true,
267 .c_source_files = c_source_files.items,
268 .verbose_cc = comp.verbose_cc,
269 .verbose_link = comp.verbose_link,
270 .verbose_air = comp.verbose_air,
271 .verbose_llvm_ir = comp.verbose_llvm_ir,
272 .verbose_llvm_bc = comp.verbose_llvm_bc,
273 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
274 .clang_passthrough_mode = comp.clang_passthrough_mode,
275 .skip_linker_dependencies = true,
276 .environ_map = comp.environ_map,
277 }) catch |err| {
278 switch (err) {
279 else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {t}", .{err}),
280 error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {f}", .{sub_create_diag}),
281 }
282 return error.AlreadyReported;
283 };
284 defer sub_compilation.destroy();
285
286 comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) {
287 error.AlreadyReported => |e| return e,
288 else => |e| {
289 comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: compilation failed: {t}", .{e});
290 return error.AlreadyReported;
291 },
292 };
293
294 assert(comp.libcxx_static_lib == null);
295 const crt_file = try sub_compilation.toCrtFile();
296 comp.libcxx_static_lib = crt_file;
297 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
298}
299
300pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void {
301 if (!build_options.have_llvm) {
302 return error.ZigCompilerNotBuiltWithLLVMExtensions;
303 }
304
305 const tracy = trace(@src());
306 defer tracy.end();
307
308 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
309 defer arena_allocator.deinit();
310 const arena = arena_allocator.allocator();
311
312 const io = comp.io;
313 const root_name = "c++abi";
314 const output_mode = .Lib;
315 const link_mode = .static;
316 const target = &comp.root_mod.resolved_target.result;
317
318 const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" });
319 const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" });
320 const cxx_src_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "src" });
321
322 const optimize_mode = comp.compilerRtOptMode();
323 const strip = comp.compilerRtStrip();
324 // See the `-fno-exceptions` logic for WASI.
325 // The old 32-bit x86 variant of SEH doesn't use tables.
326 const unwind_tables: std.lang.UnwindTables =
327 if (target.cpu.arch == .x86 and target.os.tag == .windows) .none else .async;
328
329 const config = Compilation.Config.resolve(.{
330 .output_mode = output_mode,
331 .link_mode = link_mode,
332 .resolved_target = comp.root_mod.resolved_target,
333 .is_test = false,
334 .have_zcu = false,
335 .emit_bin = true,
336 .root_optimize_mode = optimize_mode,
337 .root_strip = strip,
338 .link_libc = true,
339 .any_unwind_tables = unwind_tables != .none,
340 .lto = comp.config.lto,
341 .any_sanitize_thread = comp.config.any_sanitize_thread,
342 }) catch |err| {
343 comp.lockAndSetMiscFailure(
344 .libcxxabi,
345 "unable to build libc++abi: resolving configuration failed: {s}",
346 .{@errorName(err)},
347 );
348 return error.AlreadyReported;
349 };
350
351 const root_mod = Module.create(arena, .{
352 .paths = .{
353 .root = .zig_lib_root,
354 .root_src_path = "",
355 },
356 .fully_qualified_name = "root",
357 .inherited = .{
358 .resolved_target = comp.root_mod.resolved_target,
359 .strip = strip,
360 .stack_check = false,
361 .stack_protector = 0,
362 .sanitize_c = .off,
363 .sanitize_thread = comp.config.any_sanitize_thread,
364 .red_zone = comp.root_mod.red_zone,
365 .omit_frame_pointer = comp.root_mod.omit_frame_pointer,
366 .valgrind = false,
367 .optimize_mode = optimize_mode,
368 .unwind_tables = unwind_tables,
369 .pic = if (target_util.supports_fpic(target)) true else null,
370 .code_model = comp.root_mod.code_model,
371 },
372 .global = config,
373 .cc_argv = &.{},
374 .parent = null,
375 }) catch |err| {
376 comp.lockAndSetMiscFailure(
377 .libcxxabi,
378 "unable to build libc++abi: creating module failed: {s}",
379 .{@errorName(err)},
380 );
381 return error.AlreadyReported;
382 };
383
384 var c_source_files = try std.array_list.Managed(Compilation.CSourceFile).initCapacity(arena, libcxxabi_files.len);
385
386 for (libcxxabi_files) |cxxabi_src| {
387 if (!comp.config.any_non_single_threaded and std.mem.eql(u8, cxxabi_src, "src/cxa_thread_atexit.cpp"))
388 continue;
389 if (target.os.tag == .wasi and
390 (std.mem.eql(u8, cxxabi_src, "src/cxa_exception.cpp") or std.mem.eql(u8, cxxabi_src, "src/cxa_personality.cpp")))
391 continue;
392 if (target.os.tag != .wasi and std.mem.eql(u8, cxxabi_src, "src/cxa_noexception.cpp"))
393 continue;
394
395 var cflags = std.array_list.Managed([]const u8).init(arena);
396
397 try addCxxArgs(comp, arena, &cflags);
398
399 try cflags.append("-w"); // Disable all warnings.
400 try cflags.append("-DNDEBUG");
401 try cflags.append("-D_LIBCPP_BUILDING_LIBRARY");
402 try cflags.append("-D_LIBCXXABI_BUILDING_LIBRARY");
403 if (!comp.config.any_non_single_threaded) {
404 try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS");
405 }
406 if (target.abi.isGnu()) {
407 if (target.os.tag != .linux or !(target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 18, .patch = 0 }) == .lt))
408 try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL");
409 }
410
411 if (target.os.tag == .wasi) {
412 try cflags.append("-fno-exceptions");
413 }
414
415 try cflags.append("-fvisibility=hidden");
416 try cflags.append("-fvisibility-inlines-hidden");
417
418 try cflags.append("-nostdinc++");
419 try cflags.append("-fstrict-aliasing");
420 try cflags.append("-std=c++23");
421
422 // These depend on only the zig lib directory file path, which is
423 // purposefully either in the cache or not in the cache. The decision
424 // should not be overridden here.
425 var cache_exempt_flags = std.array_list.Managed([]const u8).init(arena);
426
427 try cache_exempt_flags.append("-I");
428 try cache_exempt_flags.append(cxxabi_include_path);
429
430 try cache_exempt_flags.append("-I");
431 try cache_exempt_flags.append(cxx_include_path);
432
433 try cache_exempt_flags.append("-I");
434 try cache_exempt_flags.append(cxx_src_include_path);
435
436 c_source_files.appendAssumeCapacity(.{
437 .src_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", cxxabi_src }),
438 .extra_flags = cflags.items,
439 .cache_exempt_flags = cache_exempt_flags.items,
440 .owner = root_mod,
441 });
442 }
443
444 const misc_task: Compilation.MiscTask = .libcxxabi;
445
446 var sub_create_diag: Compilation.CreateDiagnostic = undefined;
447 const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{
448 .thread_limit = comp.thread_limit,
449 .dirs = comp.dirs.withoutLocalCache(),
450 .self_exe_path = comp.self_exe_path,
451 .cache_mode = .whole,
452 .config = config,
453 .root_mod = root_mod,
454 .root_name = root_name,
455 .libc_installation = comp.libc_installation,
456 .emit_bin = .yes_cache,
457 .function_sections = true,
458 .data_sections = true,
459 .c_source_files = c_source_files.items,
460 .verbose_cc = comp.verbose_cc,
461 .verbose_link = comp.verbose_link,
462 .verbose_air = comp.verbose_air,
463 .verbose_llvm_ir = comp.verbose_llvm_ir,
464 .verbose_llvm_bc = comp.verbose_llvm_bc,
465 .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features,
466 .clang_passthrough_mode = comp.clang_passthrough_mode,
467 .skip_linker_dependencies = true,
468 .environ_map = comp.environ_map,
469 }) catch |err| {
470 switch (err) {
471 else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {t}", .{err}),
472 error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {f}", .{sub_create_diag}),
473 }
474 return error.AlreadyReported;
475 };
476 defer sub_compilation.destroy();
477
478 comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) {
479 error.AlreadyReported => |e| return e,
480 else => |e| {
481 comp.lockAndSetMiscFailure(
482 .libcxxabi,
483 "unable to build libc++abi: compilation failed: {s}",
484 .{@errorName(e)},
485 );
486 return error.AlreadyReported;
487 },
488 };
489
490 assert(comp.libcxxabi_static_lib == null);
491 const crt_file = try sub_compilation.toCrtFile();
492 comp.libcxxabi_static_lib = crt_file;
493 try comp.queuePrelinkTaskMode(crt_file.full_object_path, false, &config);
494}
495
496pub fn addCxxArgs(
497 comp: *const Compilation,
498 arena: std.mem.Allocator,
499 cflags: *std.array_list.Managed([]const u8),
500) error{OutOfMemory}!void {
501 const target = comp.getTarget();
502 const optimize_mode = comp.compilerRtOptMode();
503
504 const abi_version: u2 = if (target.os.tag == .emscripten) 2 else 1;
505 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_VERSION={d}", .{
506 abi_version,
507 }));
508 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{
509 abi_version,
510 }));
511 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_THREADS={d}", .{
512 @as(u1, if (comp.config.any_non_single_threaded) 1 else 0),
513 }));
514 try cflags.append("-D_LIBCPP_HAS_MONOTONIC_CLOCK");
515 try cflags.append("-D_LIBCPP_HAS_TERMINAL");
516 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_MUSL_LIBC={d}", .{
517 @as(u1, if (target.abi.isMusl()) 1 else 0),
518 }));
519 try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS");
520 try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS");
521 try cflags.append("-D_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS=0");
522 try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_FILESYSTEM={d}", .{
523 @as(u1, if (target.os.tag == .wasi) 0 else 1),
524 }));
525 try cflags.append("-D_LIBCPP_HAS_RANDOM_DEVICE");
526 try cflags.append("-D_LIBCPP_HAS_LOCALIZATION");
527 try cflags.append("-D_LIBCPP_HAS_UNICODE");
528 try cflags.append("-D_LIBCPP_HAS_WIDE_CHARACTERS");
529 try cflags.append("-D_LIBCPP_HAS_NO_STD_MODULES");
530 if (target.os.tag == .linux) {
531 try cflags.append("-D_LIBCPP_HAS_TIME_ZONE_DATABASE");
532 }
533 // See libcxx/include/__algorithm/pstl_backends/cpu_backends/backend.h
534 // for potentially enabling some fancy features here, which would
535 // require corresponding changes in libcxx.zig, as well as
536 // Compilation.addCCArgs. This option makes it use serial backend which
537 // is simple and works everywhere.
538 try cflags.append("-D_LIBCPP_PSTL_BACKEND_SERIAL");
539 switch (optimize_mode) {
540 .debug => {
541 try cflags.append("-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG");
542 try cflags.append("-D_LIBCPP_ASSERTION_SEMANTIC_DEFAULT=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE");
543 },
544 .fast, .small => {
545 try cflags.append("-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE");
546 try cflags.append("-D_LIBCPP_ASSERTION_SEMANTIC_DEFAULT=_LIBCPP_ASSERTION_SEMANTIC_IGNORE");
547 },
548 .safe => {
549 try cflags.append("-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST");
550 try cflags.append("-D_LIBCPP_ASSERTION_SEMANTIC_DEFAULT=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE");
551 },
552 }
553 if (target.isGnuLibC()) {
554 // glibc 2.16 introduced aligned_alloc
555 if (target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) {
556 try cflags.append("-D_LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION=0");
557 }
558 }
559}