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}