const std = @import("std"); const path = std.fs.path; const assert = std.debug.assert; const target_util = @import("../target.zig"); const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const trace = @import("../tracy.zig").trace; const Module = @import("../Package/Module.zig"); const libcxxabi_files = [_][]const u8{ "src/cxa_aux_runtime.cpp", "src/cxa_default_handlers.cpp", "src/cxa_demangle.cpp", "src/cxa_exception_storage.cpp", "src/cxa_guard.cpp", "src/cxa_handlers.cpp", "src/cxa_vector.cpp", "src/cxa_virtual.cpp", "src/stdlib_exception.cpp", "src/stdlib_stdexcept.cpp", "src/stdlib_typeinfo.cpp", "src/stdlib_new_delete.cpp", "src/abort_message.cpp", "src/fallback_malloc.cpp", "src/private_typeinfo.cpp", "src/cxa_exception.cpp", "src/cxa_personality.cpp", "src/cxa_noexception.cpp", "src/cxa_thread_atexit.cpp", }; const libcxx_base_files = [_][]const u8{ "src/algorithm.cpp", "src/any.cpp", "src/bind.cpp", "src/call_once.cpp", "src/charconv.cpp", "src/chrono.cpp", "src/error_category.cpp", "src/exception.cpp", "src/expected.cpp", "src/filesystem/directory_entry.cpp", "src/filesystem/directory_iterator.cpp", "src/filesystem/filesystem_clock.cpp", "src/filesystem/filesystem_error.cpp", // omit int128_builtins.cpp because it provides __muloti4 which is already provided // by compiler_rt and crashes on Windows x86_64: https://github.com/ziglang/zig/issues/10719 //"src/filesystem/int128_builtins.cpp", "src/filesystem/operations.cpp", "src/filesystem/path.cpp", "src/fstream.cpp", "src/functional.cpp", "src/hash.cpp", "src/ios.cpp", "src/ios.instantiations.cpp", "src/iostream.cpp", "src/locale.cpp", "src/memory.cpp", "src/memory_resource.cpp", "src/new.cpp", "src/new_handler.cpp", "src/new_helpers.cpp", "src/optional.cpp", "src/ostream.cpp", "src/print.cpp", //"src/pstl/libdispatch.cpp", "src/random.cpp", "src/random_shuffle.cpp", "src/regex.cpp", "src/ryu/d2fixed.cpp", "src/ryu/d2s.cpp", "src/ryu/f2s.cpp", "src/stdexcept.cpp", "src/string.cpp", "src/strstream.cpp", "src/support/win32/locale_win32.cpp", "src/support/win32/support.cpp", "src/system_error.cpp", "src/typeinfo.cpp", "src/valarray.cpp", "src/variant.cpp", "src/vector.cpp", "src/verbose_abort.cpp", }; const libcxx_thread_files = [_][]const u8{ "src/atomic.cpp", "src/barrier.cpp", "src/condition_variable.cpp", "src/condition_variable_destructor.cpp", "src/future.cpp", "src/mutex.cpp", "src/mutex_destructor.cpp", "src/shared_mutex.cpp", "src/support/win32/thread_win32.cpp", "src/thread.cpp", }; pub const BuildError = error{ OutOfMemory, AlreadyReported, ZigCompilerNotBuiltWithLLVMExtensions, }; pub fn buildLibCxx(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } const tracy = trace(@src()); defer tracy.end(); var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); const io = comp.io; const root_name = "c++"; const output_mode = .Lib; const link_mode = .static; const target = &comp.root_mod.resolved_target.result; const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" }); const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" }); const cxx_src_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "src" }); const cxx_libc_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "libc" }); const optimize_mode = comp.compilerRtOptMode(); const strip = comp.compilerRtStrip(); const config = Compilation.Config.resolve(.{ .output_mode = output_mode, .link_mode = link_mode, .resolved_target = comp.root_mod.resolved_target, .is_test = false, .have_zcu = false, .emit_bin = true, .root_optimize_mode = optimize_mode, .root_strip = strip, .link_libc = true, .lto = comp.config.lto, .any_sanitize_thread = comp.config.any_sanitize_thread, }) catch |err| { comp.lockAndSetMiscFailure( .libcxx, "unable to build libc++: resolving configuration failed: {s}", .{@errorName(err)}, ); return error.AlreadyReported; }; const root_mod = Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "", }, .fully_qualified_name = "root", .inherited = .{ .resolved_target = comp.root_mod.resolved_target, .strip = strip, .stack_check = false, .stack_protector = 0, .sanitize_c = .off, .sanitize_thread = comp.config.any_sanitize_thread, .red_zone = comp.root_mod.red_zone, .omit_frame_pointer = comp.root_mod.omit_frame_pointer, .valgrind = false, .optimize_mode = optimize_mode, .structured_cfg = comp.root_mod.structured_cfg, .pic = if (target_util.supports_fpic(target)) true else null, .code_model = comp.root_mod.code_model, }, .global = config, .cc_argv = &.{}, .parent = null, }) catch |err| { comp.lockAndSetMiscFailure( .libcxx, "unable to build libc++: creating module failed: {s}", .{@errorName(err)}, ); return error.AlreadyReported; }; const libcxx_files = if (comp.config.any_non_single_threaded) &(libcxx_base_files ++ libcxx_thread_files) else &libcxx_base_files; var c_source_files = try std.array_list.Managed(Compilation.CSourceFile).initCapacity(arena, libcxx_files.len); for (libcxx_files) |cxx_src| { // These don't compile on WASI due to e.g. `fchmod` usage. if (std.mem.startsWith(u8, cxx_src, "src/filesystem/") and target.os.tag == .wasi) continue; if (std.mem.startsWith(u8, cxx_src, "src/support/win32/") and target.os.tag != .windows) continue; var cflags = std.array_list.Managed([]const u8).init(arena); try addCxxArgs(comp, arena, &cflags); try cflags.append("-DNDEBUG"); try cflags.append("-DLIBC_NAMESPACE=__llvm_libc_common_utils"); try cflags.append("-D_LIBCPP_BUILDING_LIBRARY"); try cflags.append("-DLIBCXX_BUILDING_LIBCXXABI"); try cflags.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER"); if (target.os.tag == .wasi) { try cflags.append("-fno-exceptions"); } try cflags.append("-fvisibility=hidden"); try cflags.append("-fvisibility-inlines-hidden"); try cflags.append("-faligned-allocation"); try cflags.append("-nostdinc++"); try cflags.append("-std=c++23"); try cflags.append("-Wno-user-defined-literals"); try cflags.append("-Wno-covered-switch-default"); try cflags.append("-Wno-suggest-override"); // These depend on only the zig lib directory file path, which is // purposefully either in the cache or not in the cache. The decision // should not be overridden here. var cache_exempt_flags = std.array_list.Managed([]const u8).init(arena); try cache_exempt_flags.append("-I"); try cache_exempt_flags.append(cxx_include_path); try cache_exempt_flags.append("-I"); try cache_exempt_flags.append(cxxabi_include_path); try cache_exempt_flags.append("-I"); try cache_exempt_flags.append(cxx_src_include_path); try cache_exempt_flags.append("-I"); try cache_exempt_flags.append(cxx_libc_include_path); c_source_files.appendAssumeCapacity(.{ .src_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", cxx_src }), .extra_flags = cflags.items, .cache_exempt_flags = cache_exempt_flags.items, .owner = root_mod, }); } const misc_task: Compilation.MiscTask = .libcxx; var sub_create_diag: Compilation.CreateDiagnostic = undefined; const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .self_exe_path = comp.self_exe_path, .cache_mode = .whole, .config = config, .root_mod = root_mod, .root_name = root_name, .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, .emit_bin = .yes_cache, .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, }) catch |err| { switch (err) { else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {t}", .{err}), error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: create compilation failed: {f}", .{sub_create_diag}), } return error.AlreadyReported; }; defer sub_compilation.destroy(); comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) { error.AlreadyReported => return error.AlreadyReported, else => |e| { comp.lockAndSetMiscFailure(misc_task, "unable to build libc++: compilation failed: {t}", .{e}); return error.AlreadyReported; }, }; assert(comp.libcxx_static_lib == null); const crt_file = try sub_compilation.toCrtFile(); comp.libcxx_static_lib = crt_file; comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); } pub fn buildLibCxxAbi(comp: *Compilation, prog_node: std.Progress.Node) BuildError!void { if (!build_options.have_llvm) { return error.ZigCompilerNotBuiltWithLLVMExtensions; } const tracy = trace(@src()); defer tracy.end(); var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); const io = comp.io; const root_name = "c++abi"; const output_mode = .Lib; const link_mode = .static; const target = &comp.root_mod.resolved_target.result; const cxxabi_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", "include" }); const cxx_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "include" }); const cxx_src_include_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxx", "src" }); const optimize_mode = comp.compilerRtOptMode(); const strip = comp.compilerRtStrip(); // See the `-fno-exceptions` logic for WASI. // The old 32-bit x86 variant of SEH doesn't use tables. const unwind_tables: std.builtin.UnwindTables = if (target.os.tag == .wasi or (target.cpu.arch == .x86 and target.os.tag == .windows)) .none else .async; const config = Compilation.Config.resolve(.{ .output_mode = output_mode, .link_mode = link_mode, .resolved_target = comp.root_mod.resolved_target, .is_test = false, .have_zcu = false, .emit_bin = true, .root_optimize_mode = optimize_mode, .root_strip = strip, .link_libc = true, .any_unwind_tables = unwind_tables != .none, .lto = comp.config.lto, .any_sanitize_thread = comp.config.any_sanitize_thread, }) catch |err| { comp.lockAndSetMiscFailure( .libcxxabi, "unable to build libc++abi: resolving configuration failed: {s}", .{@errorName(err)}, ); return error.AlreadyReported; }; const root_mod = Module.create(arena, .{ .paths = .{ .root = .zig_lib_root, .root_src_path = "", }, .fully_qualified_name = "root", .inherited = .{ .resolved_target = comp.root_mod.resolved_target, .strip = strip, .stack_check = false, .stack_protector = 0, .sanitize_c = .off, .sanitize_thread = comp.config.any_sanitize_thread, .red_zone = comp.root_mod.red_zone, .omit_frame_pointer = comp.root_mod.omit_frame_pointer, .valgrind = false, .optimize_mode = optimize_mode, .structured_cfg = comp.root_mod.structured_cfg, .unwind_tables = unwind_tables, .pic = if (target_util.supports_fpic(target)) true else null, .code_model = comp.root_mod.code_model, }, .global = config, .cc_argv = &.{}, .parent = null, }) catch |err| { comp.lockAndSetMiscFailure( .libcxxabi, "unable to build libc++abi: creating module failed: {s}", .{@errorName(err)}, ); return error.AlreadyReported; }; var c_source_files = try std.array_list.Managed(Compilation.CSourceFile).initCapacity(arena, libcxxabi_files.len); for (libcxxabi_files) |cxxabi_src| { if (!comp.config.any_non_single_threaded and std.mem.eql(u8, cxxabi_src, "src/cxa_thread_atexit.cpp")) continue; if (target.os.tag == .wasi and (std.mem.eql(u8, cxxabi_src, "src/cxa_exception.cpp") or std.mem.eql(u8, cxxabi_src, "src/cxa_personality.cpp"))) continue; if (target.os.tag != .wasi and std.mem.eql(u8, cxxabi_src, "src/cxa_noexception.cpp")) continue; var cflags = std.array_list.Managed([]const u8).init(arena); try addCxxArgs(comp, arena, &cflags); try cflags.append("-DNDEBUG"); try cflags.append("-D_LIBCPP_BUILDING_LIBRARY"); try cflags.append("-D_LIBCXXABI_BUILDING_LIBRARY"); if (!comp.config.any_non_single_threaded) { try cflags.append("-D_LIBCXXABI_HAS_NO_THREADS"); } if (target.abi.isGnu()) { if (target.os.tag != .linux or !(target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 18, .patch = 0 }) == .lt)) try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); } if (target.os.tag == .wasi) { try cflags.append("-fno-exceptions"); } try cflags.append("-fvisibility=hidden"); try cflags.append("-fvisibility-inlines-hidden"); try cflags.append("-nostdinc++"); try cflags.append("-fstrict-aliasing"); try cflags.append("-std=c++23"); try cflags.append("-Wno-user-defined-literals"); try cflags.append("-Wno-covered-switch-default"); try cflags.append("-Wno-suggest-override"); // These depend on only the zig lib directory file path, which is // purposefully either in the cache or not in the cache. The decision // should not be overridden here. var cache_exempt_flags = std.array_list.Managed([]const u8).init(arena); try cache_exempt_flags.append("-I"); try cache_exempt_flags.append(cxxabi_include_path); try cache_exempt_flags.append("-I"); try cache_exempt_flags.append(cxx_include_path); try cache_exempt_flags.append("-I"); try cache_exempt_flags.append(cxx_src_include_path); c_source_files.appendAssumeCapacity(.{ .src_path = try comp.dirs.zig_lib.join(arena, &.{ "libcxxabi", cxxabi_src }), .extra_flags = cflags.items, .cache_exempt_flags = cache_exempt_flags.items, .owner = root_mod, }); } const misc_task: Compilation.MiscTask = .libcxxabi; var sub_create_diag: Compilation.CreateDiagnostic = undefined; const sub_compilation = Compilation.create(comp.gpa, arena, io, &sub_create_diag, .{ .dirs = comp.dirs.withoutLocalCache(), .self_exe_path = comp.self_exe_path, .cache_mode = .whole, .config = config, .root_mod = root_mod, .root_name = root_name, .thread_pool = comp.thread_pool, .libc_installation = comp.libc_installation, .emit_bin = .yes_cache, .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, }) catch |err| { switch (err) { else => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {t}", .{err}), error.CreateFail => comp.lockAndSetMiscFailure(misc_task, "unable to build libc++abi: create compilation failed: {f}", .{sub_create_diag}), } return error.AlreadyReported; }; defer sub_compilation.destroy(); comp.updateSubCompilation(sub_compilation, misc_task, prog_node) catch |err| switch (err) { error.AlreadyReported => return error.AlreadyReported, else => |e| { comp.lockAndSetMiscFailure( .libcxxabi, "unable to build libc++abi: compilation failed: {s}", .{@errorName(e)}, ); return error.AlreadyReported; }, }; assert(comp.libcxxabi_static_lib == null); const crt_file = try sub_compilation.toCrtFile(); comp.libcxxabi_static_lib = crt_file; comp.queuePrelinkTaskMode(crt_file.full_object_path, &config); } pub fn addCxxArgs( comp: *const Compilation, arena: std.mem.Allocator, cflags: *std.array_list.Managed([]const u8), ) error{OutOfMemory}!void { const target = comp.getTarget(); const optimize_mode = comp.compilerRtOptMode(); const abi_version: u2 = if (target.os.tag == .emscripten) 2 else 1; try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_VERSION={d}", .{ abi_version, })); try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{ abi_version, })); try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_THREADS={d}", .{ @as(u1, if (comp.config.any_non_single_threaded) 1 else 0), })); try cflags.append("-D_LIBCPP_HAS_MONOTONIC_CLOCK"); try cflags.append("-D_LIBCPP_HAS_TERMINAL"); try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_MUSL_LIBC={d}", .{ @as(u1, if (target.abi.isMusl()) 1 else 0), })); try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); try cflags.append("-D_LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS=0"); try cflags.append(try std.fmt.allocPrint(arena, "-D_LIBCPP_HAS_FILESYSTEM={d}", .{ @as(u1, if (target.os.tag == .wasi) 0 else 1), })); try cflags.append("-D_LIBCPP_HAS_RANDOM_DEVICE"); try cflags.append("-D_LIBCPP_HAS_LOCALIZATION"); try cflags.append("-D_LIBCPP_HAS_UNICODE"); try cflags.append("-D_LIBCPP_HAS_WIDE_CHARACTERS"); try cflags.append("-D_LIBCPP_HAS_NO_STD_MODULES"); if (target.os.tag == .linux) { try cflags.append("-D_LIBCPP_HAS_TIME_ZONE_DATABASE"); } // See libcxx/include/__algorithm/pstl_backends/cpu_backends/backend.h // for potentially enabling some fancy features here, which would // require corresponding changes in libcxx.zig, as well as // Compilation.addCCArgs. This option makes it use serial backend which // is simple and works everywhere. try cflags.append("-D_LIBCPP_PSTL_BACKEND_SERIAL"); try cflags.append(switch (optimize_mode) { .Debug => "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG", .ReleaseFast, .ReleaseSmall => "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE", .ReleaseSafe => "-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST", }); if (target.isGnuLibC()) { // glibc 2.16 introduced aligned_alloc if (target.os.versionRange().gnuLibCVersion().?.order(.{ .major = 2, .minor = 16, .patch = 0 }) == .lt) { try cflags.append("-D_LIBCPP_HAS_LIBRARY_ALIGNED_ALLOCATION=0"); } } }