From db17c0d88c44183b3060993d85657e7637b43d68 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 Mar 2020 22:48:37 -0400 Subject: [PATCH] ability to compile c++ hello world with `zig c++` closes #4786 --- src-self-hosted/clang_options_data.zig | 9 ++- src-self-hosted/stage2.zig | 1 + src/all_types.hpp | 1 + src/analyze.cpp | 6 ++ src/codegen.cpp | 10 +++ src/install_files.h | 65 +++++++++++++++ src/link.cpp | 108 +++++++++++++++++++++++++ src/main.cpp | 18 ++++- src/stage2.h | 1 + tools/update_clang_options.zig | 4 + 10 files changed, 221 insertions(+), 2 deletions(-) diff --git a/src-self-hosted/clang_options_data.zig b/src-self-hosted/clang_options_data.zig index 89cf29024f..2179b47253 100644 --- a/src-self-hosted/clang_options_data.zig +++ b/src-self-hosted/clang_options_data.zig @@ -3752,7 +3752,14 @@ flagpd1("nostdinc++"), .psl = false, }, flagpd1("nostdlibinc"), -flagpd1("nostdlib++"), +.{ + .name = "nostdlib++", + .syntax = .flag, + .zig_equivalent = .nostdlib_cpp, + .pd1 = true, + .pd2 = false, + .psl = false, +}, flagpd1("nostdsysteminc"), flagpd1("objcmt-atomic-property"), flagpd1("objcmt-migrate-all"), diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index cd0956cae3..aa4d0ad492 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1273,6 +1273,7 @@ pub const ClangArgIterator = extern struct { pic, no_pic, nostdlib, + nostdlib_cpp, shared, rdynamic, wl, diff --git a/src/all_types.hpp b/src/all_types.hpp index b2abd17f99..f51a9c0572 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2019,6 +2019,7 @@ struct CodeGen { ZigLLVMDICompileUnit *compile_unit; ZigLLVMDIFile *compile_unit_file; LinkLib *libc_link_lib; + LinkLib *libcpp_link_lib; LLVMTargetDataRef target_data_ref; LLVMTargetMachineRef target_machine; ZigLLVMDIFile *dummy_di_file; diff --git a/src/analyze.cpp b/src/analyze.cpp index 77d3f33331..df7bcdf9de 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -7756,10 +7756,14 @@ LinkLib *create_link_lib(Buf *name) { LinkLib *add_link_lib(CodeGen *g, Buf *name) { bool is_libc = buf_eql_str(name, "c"); + bool is_libcpp = buf_eql_str(name, "c++") || buf_eql_str(name, "c++abi"); if (is_libc && g->libc_link_lib != nullptr) return g->libc_link_lib; + if (is_libcpp && g->libcpp_link_lib != nullptr) + return g->libcpp_link_lib; + for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *existing_lib = g->link_libs_list.at(i); if (buf_eql_buf(existing_lib->name, name)) { @@ -7772,6 +7776,8 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { if (is_libc) g->libc_link_lib = link_lib; + if (is_libcpp) + g->libcpp_link_lib = link_lib; return link_lib; } diff --git a/src/codegen.cpp b/src/codegen.cpp index cb1ab6b6b8..d9e011c4f3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8700,6 +8700,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); + buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->libcpp_link_lib != nullptr)); buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); buf_appendf(contents, "pub const valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g))); buf_appendf(contents, "pub const position_independent_code = %s;\n", bool_to_str(g->have_pic)); @@ -8798,6 +8799,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { } cache_bool(&cache_hash, g->have_err_ret_tracing); cache_bool(&cache_hash, g->libc_link_lib != nullptr); + cache_bool(&cache_hash, g->libcpp_link_lib != nullptr); cache_bool(&cache_hash, g->valgrind_support); cache_bool(&cache_hash, g->link_eh_frame_hdr); cache_int(&cache_hash, detect_subsystem(g)); @@ -9205,6 +9207,14 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa args.append(g->framework_dirs.at(i)); } + if (g->libcpp_link_lib != nullptr) { + const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", + buf_ptr(g->zig_lib_dir))); + + args.append("-isystem"); + args.append(libcxx_include_path); + } + // According to Rich Felker libc headers are supposed to go before C language headers. // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics // and other compiler specific items. diff --git a/src/install_files.h b/src/install_files.h index 8c81a3fc80..e3a5f95763 100644 --- a/src/install_files.h +++ b/src/install_files.h @@ -1841,4 +1841,69 @@ static const char *ZIG_MUSL_COMPAT_TIME32_FILES[] = { "musl/compat/time32/wait3_time32.c", "musl/compat/time32/wait4_time32.c", }; +static const char *ZIG_LIBCXXABI_FILES[] = { +"src/abort_message.cpp", +"src/cxa_aux_runtime.cpp", +"src/cxa_default_handlers.cpp", +"src/cxa_demangle.cpp", +"src/cxa_exception.cpp", +"src/cxa_exception_storage.cpp", +"src/cxa_guard.cpp", +"src/cxa_handlers.cpp", +"src/cxa_noexception.cpp", +"src/cxa_personality.cpp", +"src/cxa_thread_atexit.cpp", +"src/cxa_unexpected.cpp", +"src/cxa_vector.cpp", +"src/cxa_virtual.cpp", +"src/fallback_malloc.cpp", +"src/private_typeinfo.cpp", +"src/stdlib_exception.cpp", +"src/stdlib_new_delete.cpp", +"src/stdlib_stdexcept.cpp", +"src/stdlib_typeinfo.cpp", +}; +static const char *ZIG_LIBCXX_FILES[] = { +"src/algorithm.cpp", +"src/any.cpp", +"src/bind.cpp", +"src/charconv.cpp", +"src/chrono.cpp", +"src/condition_variable.cpp", +"src/condition_variable_destructor.cpp", +"src/debug.cpp", +"src/exception.cpp", +"src/experimental/memory_resource.cpp", +"src/filesystem/directory_iterator.cpp", +"src/filesystem/int128_builtins.cpp", +"src/filesystem/operations.cpp", +"src/functional.cpp", +"src/future.cpp", +"src/hash.cpp", +"src/ios.cpp", +"src/iostream.cpp", +"src/locale.cpp", +"src/memory.cpp", +"src/mutex.cpp", +"src/mutex_destructor.cpp", +"src/new.cpp", +"src/optional.cpp", +"src/random.cpp", +"src/regex.cpp", +"src/shared_mutex.cpp", +"src/stdexcept.cpp", +"src/string.cpp", +"src/strstream.cpp", +"src/support/solaris/xlocale.cpp", +"src/support/win32/locale_win32.cpp", +"src/support/win32/support.cpp", +"src/support/win32/thread_win32.cpp", +"src/system_error.cpp", +"src/thread.cpp", +"src/typeinfo.cpp", +"src/utility.cpp", +"src/valarray.cpp", +"src/variant.cpp", +"src/vector.cpp", +}; #endif diff --git a/src/link.cpp b/src/link.cpp index 25b913be21..9dfc01af8f 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -1107,6 +1107,105 @@ static const char *build_musl(CodeGen *parent, Stage2ProgressNode *progress_node return buf_ptr(&child_gen->bin_file_output_path); } +static const char *build_libcxxabi(CodeGen *parent, Stage2ProgressNode *progress_node) { + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c++abi", progress_node); + codegen_add_link_lib(child_gen, buf_create_from_str("c")); + + ZigList c_source_files = {0}; + + const char *cxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", + buf_ptr(parent->zig_lib_dir))); + const char *cxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", + buf_ptr(parent->zig_lib_dir))); + + for (size_t i = 0; i < array_length(ZIG_LIBCXXABI_FILES); i += 1) { + const char *rel_src_path = ZIG_LIBCXXABI_FILES[i]; + + CFile *c_file = heap::c_allocator.create(); + c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "%s", + buf_ptr(parent->zig_lib_dir), rel_src_path)); + + c_file->args.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); + c_file->args.append("-D_LIBCPP_DISABLE_EXTERN_TEMPLATE"); + c_file->args.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS"); + c_file->args.append("-D_LIBCXXABI_BUILDING_LIBRARY"); + + c_file->args.append("-I"); + c_file->args.append(cxxabi_include_path); + + c_file->args.append("-I"); + c_file->args.append(cxx_include_path); + + c_file->args.append("-O3"); + c_file->args.append("-DNDEBUG"); + c_file->args.append("-fPIC"); + c_file->args.append("-nostdinc++"); + c_file->args.append("-fstrict-aliasing"); + c_file->args.append("-funwind-tables"); + c_file->args.append("-D_DEBUG"); + c_file->args.append("-UNDEBUG"); + c_file->args.append("-std=c++11"); + + c_source_files.append(c_file); + } + + + child_gen->c_source_files = c_source_files; + codegen_build_and_link(child_gen); + return buf_ptr(&child_gen->bin_file_output_path); +} + +static const char *build_libcxx(CodeGen *parent, Stage2ProgressNode *progress_node) { + CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c++", progress_node); + codegen_add_link_lib(child_gen, buf_create_from_str("c")); + + ZigList c_source_files = {0}; + + const char *cxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", + buf_ptr(parent->zig_lib_dir))); + const char *cxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", + buf_ptr(parent->zig_lib_dir))); + + for (size_t i = 0; i < array_length(ZIG_LIBCXX_FILES); i += 1) { + const char *rel_src_path = ZIG_LIBCXX_FILES[i]; + + Buf *src_path_buf = buf_create_from_str(rel_src_path); + if (buf_starts_with_str(src_path_buf, "src/support/win32") && parent->zig_target->os != OsWindows) { + continue; + } + + CFile *c_file = heap::c_allocator.create(); + c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "%s", + buf_ptr(parent->zig_lib_dir), rel_src_path)); + + c_file->args.append("-DNDEBUG"); + c_file->args.append("-D_LIBCPP_BUILDING_LIBRARY"); + c_file->args.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER"); + c_file->args.append("-DLIBCXX_BUILDING_LIBCXXABI"); + + c_file->args.append("-I"); + c_file->args.append(cxx_include_path); + + c_file->args.append("-I"); + c_file->args.append(cxxabi_include_path); + + c_file->args.append("-O3"); + c_file->args.append("-DNDEBUG"); + c_file->args.append("-fPIC"); + c_file->args.append("-nostdinc++"); + c_file->args.append("-fvisibility-inlines-hidden"); + c_file->args.append("-std=c++14"); + c_file->args.append("-Wno-user-defined-literals"); + + c_source_files.append(c_file); + } + + + child_gen->c_source_files = c_source_files; + codegen_build_and_link(child_gen); + return buf_ptr(&child_gen->bin_file_output_path); +} + static void add_msvcrt_os_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) { CFile *c_file = heap::c_allocator.create(); c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s", @@ -1770,6 +1869,10 @@ static void construct_linker_job_elf(LinkJob *lj) { // libc is linked specially continue; } + if (buf_eql_str(link_lib->name, "c++") || buf_eql_str(link_lib->name, "c++abi")) { + // libc++ is linked specially + continue; + } if (g->libc == nullptr && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { // these libraries are always linked below when targeting glibc continue; @@ -1785,6 +1888,11 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(buf_ptr(arg)); } + // libc++ dep + if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { + lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); + lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); + } // libc dep if (g->libc_link_lib != nullptr && g->out_type != OutTypeObj) { diff --git a/src/main.cpp b/src/main.cpp index a543b43579..ae44185d04 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -412,6 +412,7 @@ static int main0(int argc, char **argv) { ZigList framework_dirs = {0}; ZigList frameworks = {0}; bool have_libc = false; + bool have_libcpp = false; const char *target_string = nullptr; bool rdynamic = false; const char *linker_script = nullptr; @@ -456,6 +457,7 @@ static int main0(int argc, char **argv) { const char *override_soname = nullptr; bool only_preprocess = false; bool ensure_libc_on_non_freestanding = false; + bool ensure_libcpp_on_non_freestanding = false; ZigList llvm_argv = {0}; llvm_argv.append("zig (LLVM option parsing)"); @@ -580,10 +582,11 @@ static int main0(int argc, char **argv) { return (term.how == TerminationIdClean) ? term.code : -1; } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { return stage2_fmt(argc, argv); - } else if (argc >= 2 && strcmp(argv[1], "cc") == 0) { + } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) { emit_h = false; strip = true; ensure_libc_on_non_freestanding = true; + ensure_libcpp_on_non_freestanding = (strcmp(argv[1], "c++") == 0); bool c_arg = false; Stage2ClangArgIterator it; @@ -632,6 +635,8 @@ static int main0(int argc, char **argv) { case Stage2ClangArgL: // -l if (strcmp(it.only_arg, "c") == 0) have_libc = true; + if (strcmp(it.only_arg, "c++") == 0) + have_libcpp = true; link_libs.append(it.only_arg); break; case Stage2ClangArgIgnore: @@ -648,6 +653,9 @@ static int main0(int argc, char **argv) { case Stage2ClangArgNoStdLib: ensure_libc_on_non_freestanding = false; break; + case Stage2ClangArgNoStdLibCpp: + ensure_libcpp_on_non_freestanding = false; + break; case Stage2ClangArgShared: is_dynamic = true; is_shared_lib = true; @@ -916,6 +924,8 @@ static int main0(int argc, char **argv) { const char *l = &arg[2]; if (strcmp(l, "c") == 0) have_libc = true; + if (strcmp(l, "c++") == 0) + have_libcpp = true; link_libs.append(l); } else if (arg[1] == 'I' && arg[2] != 0) { clang_argv.append("-I"); @@ -1057,6 +1067,8 @@ static int main0(int argc, char **argv) { } else if (strcmp(arg, "--library") == 0 || strcmp(arg, "-l") == 0) { if (strcmp(argv[i], "c") == 0) have_libc = true; + if (strcmp(argv[i], "c++") == 0) + have_libcpp = true; link_libs.append(argv[i]); } else if (strcmp(arg, "--forbid-library") == 0) { forbidden_link_libs.append(argv[i]); @@ -1221,6 +1233,10 @@ static int main0(int argc, char **argv) { have_libc = true; link_libs.append("c"); } + if (!have_libcpp && ensure_libcpp_on_non_freestanding && target.os != OsFreestanding) { + have_libcpp = true; + link_libs.append("c++"); + } Buf zig_triple_buf = BUF_INIT; target_triple_zig(&zig_triple_buf, &target); diff --git a/src/stage2.h b/src/stage2.h index 4b86fdc059..42ef1ba612 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -334,6 +334,7 @@ enum Stage2ClangArg { Stage2ClangArgPIC, Stage2ClangArgNoPIC, Stage2ClangArgNoStdLib, + Stage2ClangArgNoStdLibCpp, Stage2ClangArgShared, Stage2ClangArgRDynamic, Stage2ClangArgWL, diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 97be4978d2..46f2275b69 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -62,6 +62,10 @@ const known_options = [_]KnownOpt{ .name = "no-standard-libraries", .ident = "nostdlib", }, + .{ + .name = "nostdlib++", + .ident = "nostdlib_cpp", + }, .{ .name = "shared", .ident = "shared",