From 877e4248fc1e8e28b1f43282411aa9868d6d1d9c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 21 Sep 2020 22:38:45 -0700 Subject: [PATCH] stage2: implement building & linking against libcxx and libcxxabi --- BRANCH_TODO | 1 - src/Compilation.zig | 35 ++++++- src/libcxx.zig | 239 +++++++++++++++++++++++++++++++++++++++++++- src/libunwind.zig | 6 +- src/link/Elf.zig | 4 +- 5 files changed, 273 insertions(+), 12 deletions(-) diff --git a/BRANCH_TODO b/BRANCH_TODO index f30f67fc69..026efaddce 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -1,4 +1,3 @@ - * build & link against libcxx and libcxxabi * `zig build` * repair @cImport * make sure zig cc works diff --git a/src/Compilation.zig b/src/Compilation.zig index f67e164ecc..70492b70be 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -16,6 +16,7 @@ const build_options = @import("build_options"); const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const glibc = @import("glibc.zig"); const libunwind = @import("libunwind.zig"); +const libcxx = @import("libcxx.zig"); const fatal = @import("main.zig").fatal; const Module = @import("Module.zig"); const Cache = @import("Cache.zig"); @@ -69,10 +70,10 @@ rand: *std.rand.Random, /// Populated when we build the libc++ static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libcxx_static_lib: ?[]const u8 = null, +libcxx_static_lib: ?CRTFile = null, /// Populated when we build the libc++abi static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). -libcxxabi_static_lib: ?[]const u8 = null, +libcxxabi_static_lib: ?CRTFile = null, /// Populated when we build the libunwind static library. A Job to build this is placed in the queue /// and resolved before calling linker.flush(). libunwind_static_lib: ?CRTFile = null, @@ -140,6 +141,8 @@ const Job = union(enum) { glibc_shared_objects, /// libunwind.a, usually needed when linking libc libunwind: void, + libcxx: void, + libcxxabi: void, /// needed when producing a dynamic library or executable libcompiler_rt: void, /// needed when not linking libc and using LLVM for code generation because it generates @@ -770,10 +773,18 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { if (comp.wantBuildLibUnwindFromSource()) { try comp.work_queue.writeItem(.{ .libunwind = {} }); } + if (build_options.have_llvm and comp.bin_file.options.output_mode != .Obj and + comp.bin_file.options.link_libcpp) + { + try comp.work_queue.writeItem(.libcxx); + try comp.work_queue.writeItem(.libcxxabi); + } if (build_options.is_stage1 and comp.bin_file.options.use_llvm) { try comp.work_queue.writeItem(.{ .stage1_module = {} }); } - if (is_exe_or_dyn_lib and comp.bin_file.options.use_llvm) { + if (is_exe_or_dyn_lib and !comp.bin_file.options.is_compiler_rt_or_libc and + build_options.is_stage1) + { try comp.work_queue.writeItem(.{ .libcompiler_rt = {} }); if (!comp.bin_file.options.link_libc) { try comp.work_queue.writeItem(.{ .zig_libc = {} }); @@ -811,6 +822,12 @@ pub fn destroy(self: *Compilation) void { if (self.libunwind_static_lib) |*crt_file| { crt_file.deinit(gpa); } + if (self.libcxx_static_lib) |*crt_file| { + crt_file.deinit(gpa); + } + if (self.libcxxabi_static_lib) |*crt_file| { + crt_file.deinit(gpa); + } if (self.compiler_rt_static_lib) |*crt_file| { crt_file.deinit(gpa); } @@ -1109,6 +1126,18 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { fatal("unable to build libunwind: {}", .{@errorName(err)}); }; }, + .libcxx => { + libcxx.buildLibCXX(self) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build libcxx: {}", .{@errorName(err)}); + }; + }, + .libcxxabi => { + libcxx.buildLibCXXABI(self) catch |err| { + // TODO Expose this as a normal compile error rather than crashing here. + fatal("unable to build libcxxabi: {}", .{@errorName(err)}); + }; + }, .libcompiler_rt => { self.buildStaticLibFromZig("compiler_rt.zig", &self.compiler_rt_static_lib) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. diff --git a/src/libcxx.zig b/src/libcxx.zig index c7dc24ae9f..e6ed35c560 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -1,6 +1,13 @@ -//! TODO build libcxx and libcxxabi from source +const std = @import("std"); +const path = std.fs.path; +const assert = std.debug.assert; -pub const libcxxabi_files = [_][]const u8{ +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const trace = @import("tracy.zig").trace; + +const libcxxabi_files = [_][]const u8{ "src/abort_message.cpp", "src/cxa_aux_runtime.cpp", "src/cxa_default_handlers.cpp", @@ -22,7 +29,7 @@ pub const libcxxabi_files = [_][]const u8{ "src/stdlib_typeinfo.cpp", }; -pub const libcxx_files = [_][]const u8{ +const libcxx_files = [_][]const u8{ "src/algorithm.cpp", "src/any.cpp", "src/bind.cpp", @@ -64,3 +71,229 @@ pub const libcxx_files = [_][]const u8{ "src/variant.cpp", "src/vector.cpp", }; + +pub fn buildLibCXX(comp: *Compilation) !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 root_name = "c++"; + const output_mode = .Lib; + const link_mode = .Static; + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(arena, root_name, target, output_mode, link_mode, null); + + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + + const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" }); + const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + try c_source_files.ensureCapacity(libcxx_files.len); + + for (libcxx_files) |cxx_src| { + var cflags = std.ArrayList([]const u8).init(arena); + + if (target.os.tag == .windows) { + // Filesystem stuff isn't supported on Windows. + if (std.mem.startsWith(u8, cxx_src, "src/filesystem/")) + continue; + } else { + if (std.mem.startsWith(u8, cxx_src, "src/support/win32/")) + continue; + } + + try cflags.append("-DNDEBUG"); + try cflags.append("-D_LIBCPP_BUILDING_LIBRARY"); + try cflags.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER"); + try cflags.append("-DLIBCXX_BUILDING_LIBCXXABI"); + try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + + if (target.abi.isMusl()) { + try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } + + try cflags.append("-I"); + try cflags.append(cxx_include_path); + + try cflags.append("-I"); + try cflags.append(cxxabi_include_path); + + try cflags.append("-O3"); + try cflags.append("-DNDEBUG"); + if (target_util.supports_fpic(target)) { + try cflags.append("-fPIC"); + } + try cflags.append("-nostdinc++"); + try cflags.append("-fvisibility-inlines-hidden"); + try cflags.append("-std=c++14"); + try cflags.append("-Wno-user-defined-literals"); + + c_source_files.appendAssumeCapacity(.{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }), + .extra_flags = cflags.items, + }); + } + + const sub_compilation = try Compilation.create(comp.gpa, .{ + // TODO use the global cache directory here + .zig_cache_directory = comp.zig_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .link_mode = link_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = c_source_files.items, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .link_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.libcxx_static_lib == null); + comp.libcxx_static_lib = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} + +pub fn buildLibCXXABI(comp: *Compilation) !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 root_name = "c++abi"; + const output_mode = .Lib; + const link_mode = .Static; + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(arena, root_name, target, output_mode, link_mode, null); + + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + + const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" }); + const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }); + + var c_source_files: [libcxxabi_files.len]Compilation.CSourceFile = undefined; + for (libcxxabi_files) |cxxabi_src, i| { + var cflags = std.ArrayList([]const u8).init(arena); + + try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); + try cflags.append("-D_LIBCPP_DISABLE_EXTERN_TEMPLATE"); + try cflags.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS"); + try cflags.append("-D_LIBCXXABI_BUILDING_LIBRARY"); + try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + + if (target.abi.isMusl()) { + try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } + + try cflags.append("-I"); + try cflags.append(cxxabi_include_path); + + try cflags.append("-I"); + try cflags.append(cxx_include_path); + + try cflags.append("-O3"); + try cflags.append("-DNDEBUG"); + if (target_util.supports_fpic(target)) { + try cflags.append("-fPIC"); + } + try cflags.append("-nostdinc++"); + try cflags.append("-fstrict-aliasing"); + try cflags.append("-funwind-tables"); + try cflags.append("-D_DEBUG"); + try cflags.append("-UNDEBUG"); + try cflags.append("-std=c++11"); + + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }), + .extra_flags = cflags.items, + }; + } + + const sub_compilation = try Compilation.create(comp.gpa, .{ + // TODO use the global cache directory here + .zig_cache_directory = comp.zig_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .link_mode = link_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = &c_source_files, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .link_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.libcxxabi_static_lib == null); + comp.libcxxabi_static_lib = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.directory.join(comp.gpa, &[_][]const u8{basename}), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} diff --git a/src/libunwind.zig b/src/libunwind.zig index 0bb44808ff..e7562670e4 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -8,13 +8,13 @@ const build_options = @import("build_options"); const trace = @import("tracy.zig").trace; pub fn buildStaticLib(comp: *Compilation) !void { - const tracy = trace(@src()); - defer tracy.end(); - 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; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 3d8afa8483..b275026e8e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1513,8 +1513,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (!is_obj) { // libc++ dep if (self.base.options.link_libcpp) { - try argv.append(comp.libcxxabi_static_lib.?); - try argv.append(comp.libcxx_static_lib.?); + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); } // libc dep