From db17c0d88c44183b3060993d85657e7637b43d68 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 26 Mar 2020 22:48:37 -0400 Subject: ability to compile c++ hello world with `zig c++` closes #4786 --- 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 + 7 files changed, 208 insertions(+), 1 deletion(-) (limited to 'src') 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, -- cgit v1.2.3 From f407109070be2a187c71cc2350ac6d9b7d64a3d0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Mar 2020 12:38:52 -0400 Subject: zig c++: get it working with musl and mingw-w64 --- lib/libc/mingw/misc/_create_locale.c | 33 ++++ lib/libc/mingw/misc/_free_locale.c | 31 +++ lib/libcxx/src/filesystem/int128_builtins.cpp | 54 ------ lib/libcxxabi/src/stdlib_new_delete.cpp | 262 -------------------------- src-self-hosted/stage2.zig | 4 + src/all_types.hpp | 2 + src/analyze.hpp | 1 + src/codegen.cpp | 27 ++- src/install_files.h | 2 - src/link.cpp | 64 ++++++- src/main.cpp | 18 ++ src/stage2.h | 4 + src/target.cpp | 7 + src/target.hpp | 1 + tools/update_clang_options.zig | 16 ++ 15 files changed, 199 insertions(+), 327 deletions(-) create mode 100644 lib/libc/mingw/misc/_create_locale.c create mode 100644 lib/libc/mingw/misc/_free_locale.c delete mode 100644 lib/libcxx/src/filesystem/int128_builtins.cpp delete mode 100644 lib/libcxxabi/src/stdlib_new_delete.cpp (limited to 'src') diff --git a/lib/libc/mingw/misc/_create_locale.c b/lib/libc/mingw/misc/_create_locale.c new file mode 100644 index 0000000000..00947b3ac1 --- /dev/null +++ b/lib/libc/mingw/misc/_create_locale.c @@ -0,0 +1,33 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include +#include +#include + +static _locale_t __cdecl init_func(int category, const char *locale); +_locale_t (__cdecl *__MINGW_IMP_SYMBOL(_create_locale))(int, const char *) = init_func; + +static _locale_t __cdecl null_func(int category, const char *locale) +{ + (void)category; + (void)locale; + return NULL; +} + +static _locale_t __cdecl init_func(int category, const char *locale) +{ + HMODULE msvcrt = __mingw_get_msvcrt_handle(); + _locale_t (__cdecl *func)(int, const char *) = NULL; + + if (msvcrt) + func = (void*)GetProcAddress(msvcrt, "_create_locale"); + + if (!func) + func = null_func; + + return (__MINGW_IMP_SYMBOL(_create_locale) = func)(category, locale); +} diff --git a/lib/libc/mingw/misc/_free_locale.c b/lib/libc/mingw/misc/_free_locale.c new file mode 100644 index 0000000000..d94e2274fa --- /dev/null +++ b/lib/libc/mingw/misc/_free_locale.c @@ -0,0 +1,31 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#include +#include +#include + +static void __cdecl init_func(_locale_t locale); +void (__cdecl *__MINGW_IMP_SYMBOL(_free_locale))(_locale_t) = init_func; + +static void __cdecl stub_func(_locale_t locale) +{ + (void)locale; +} + +static void __cdecl init_func(_locale_t locale) +{ + HMODULE msvcrt = __mingw_get_msvcrt_handle(); + void (__cdecl *func)(_locale_t) = NULL; + + if (msvcrt) + func = (void*)GetProcAddress(msvcrt, "_free_locale"); + + if (!func) + func = stub_func; + + (__MINGW_IMP_SYMBOL(_free_locale) = func)(locale); +} diff --git a/lib/libcxx/src/filesystem/int128_builtins.cpp b/lib/libcxx/src/filesystem/int128_builtins.cpp deleted file mode 100644 index ed531ee145..0000000000 --- a/lib/libcxx/src/filesystem/int128_builtins.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/*===-- int128_builtins.cpp - Implement __muloti4 --------------------------=== - * - * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. - * See https://llvm.org/LICENSE.txt for license information. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - * - * ===----------------------------------------------------------------------=== - * - * This file implements __muloti4, and is stolen from the compiler_rt library. - * - * FIXME: we steal and re-compile it into filesystem, which uses __int128_t, - * and requires this builtin when sanitized. See llvm.org/PR30643 - * - * ===----------------------------------------------------------------------=== - */ -#include "__config" -#include "climits" - -#if !defined(_LIBCPP_HAS_NO_INT128) - -extern "C" __attribute__((no_sanitize("undefined"))) _LIBCPP_FUNC_VIS -__int128_t __muloti4(__int128_t a, __int128_t b, int* overflow) { - const int N = (int)(sizeof(__int128_t) * CHAR_BIT); - const __int128_t MIN = (__int128_t)1 << (N - 1); - const __int128_t MAX = ~MIN; - *overflow = 0; - __int128_t result = a * b; - if (a == MIN) { - if (b != 0 && b != 1) - *overflow = 1; - return result; - } - if (b == MIN) { - if (a != 0 && a != 1) - *overflow = 1; - return result; - } - __int128_t sa = a >> (N - 1); - __int128_t abs_a = (a ^ sa) - sa; - __int128_t sb = b >> (N - 1); - __int128_t abs_b = (b ^ sb) - sb; - if (abs_a < 2 || abs_b < 2) - return result; - if (sa == sb) { - if (abs_a > MAX / abs_b) - *overflow = 1; - } else { - if (abs_a > MIN / -abs_b) - *overflow = 1; - } - return result; -} - -#endif diff --git a/lib/libcxxabi/src/stdlib_new_delete.cpp b/lib/libcxxabi/src/stdlib_new_delete.cpp deleted file mode 100644 index 698c5f7c29..0000000000 --- a/lib/libcxxabi/src/stdlib_new_delete.cpp +++ /dev/null @@ -1,262 +0,0 @@ -//===--------------------- stdlib_new_delete.cpp --------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -// -// This file implements the new and delete operators. -//===----------------------------------------------------------------------===// - -#define _LIBCPP_BUILDING_LIBRARY -#include "__cxxabi_config.h" -#include -#include - -#if !defined(_THROW_BAD_ALLOC) || !defined(_NOEXCEPT) || !defined(_LIBCXXABI_WEAK) -#error The _THROW_BAD_ALLOC, _NOEXCEPT, and _LIBCXXABI_WEAK libc++ macros must \ - already be defined by libc++. -#endif -// Implement all new and delete operators as weak definitions -// in this shared library, so that they can be overridden by programs -// that define non-weak copies of the functions. - -_LIBCXXABI_WEAK -void * -operator new(std::size_t size) _THROW_BAD_ALLOC -{ - if (size == 0) - size = 1; - void* p; - while ((p = ::malloc(size)) == 0) - { - // If malloc fails and there is a new_handler, - // call it to try free up memory. - std::new_handler nh = std::get_new_handler(); - if (nh) - nh(); - else -#ifndef _LIBCXXABI_NO_EXCEPTIONS - throw std::bad_alloc(); -#else - break; -#endif - } - return p; -} - -_LIBCXXABI_WEAK -void* -operator new(size_t size, const std::nothrow_t&) _NOEXCEPT -{ - void* p = 0; -#ifndef _LIBCXXABI_NO_EXCEPTIONS - try - { -#endif // _LIBCXXABI_NO_EXCEPTIONS - p = ::operator new(size); -#ifndef _LIBCXXABI_NO_EXCEPTIONS - } - catch (...) - { - } -#endif // _LIBCXXABI_NO_EXCEPTIONS - return p; -} - -_LIBCXXABI_WEAK -void* -operator new[](size_t size) _THROW_BAD_ALLOC -{ - return ::operator new(size); -} - -_LIBCXXABI_WEAK -void* -operator new[](size_t size, const std::nothrow_t&) _NOEXCEPT -{ - void* p = 0; -#ifndef _LIBCXXABI_NO_EXCEPTIONS - try - { -#endif // _LIBCXXABI_NO_EXCEPTIONS - p = ::operator new[](size); -#ifndef _LIBCXXABI_NO_EXCEPTIONS - } - catch (...) - { - } -#endif // _LIBCXXABI_NO_EXCEPTIONS - return p; -} - -_LIBCXXABI_WEAK -void -operator delete(void* ptr) _NOEXCEPT -{ - if (ptr) - ::free(ptr); -} - -_LIBCXXABI_WEAK -void -operator delete(void* ptr, const std::nothrow_t&) _NOEXCEPT -{ - ::operator delete(ptr); -} - -_LIBCXXABI_WEAK -void -operator delete(void* ptr, size_t) _NOEXCEPT -{ - ::operator delete(ptr); -} - -_LIBCXXABI_WEAK -void -operator delete[] (void* ptr) _NOEXCEPT -{ - ::operator delete(ptr); -} - -_LIBCXXABI_WEAK -void -operator delete[] (void* ptr, const std::nothrow_t&) _NOEXCEPT -{ - ::operator delete[](ptr); -} - -_LIBCXXABI_WEAK -void -operator delete[] (void* ptr, size_t) _NOEXCEPT -{ - ::operator delete[](ptr); -} - -#if !defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION) - -_LIBCXXABI_WEAK -void * -operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC -{ - if (size == 0) - size = 1; - if (static_cast(alignment) < sizeof(void*)) - alignment = std::align_val_t(sizeof(void*)); - void* p; -#if defined(_LIBCPP_WIN32API) - while ((p = _aligned_malloc(size, static_cast(alignment))) == nullptr) -#else - while (::posix_memalign(&p, static_cast(alignment), size) != 0) -#endif - { - // If posix_memalign fails and there is a new_handler, - // call it to try free up memory. - std::new_handler nh = std::get_new_handler(); - if (nh) - nh(); - else { -#ifndef _LIBCXXABI_NO_EXCEPTIONS - throw std::bad_alloc(); -#else - p = nullptr; // posix_memalign doesn't initialize 'p' on failure - break; -#endif - } - } - return p; -} - -_LIBCXXABI_WEAK -void* -operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT -{ - void* p = 0; -#ifndef _LIBCXXABI_NO_EXCEPTIONS - try - { -#endif // _LIBCXXABI_NO_EXCEPTIONS - p = ::operator new(size, alignment); -#ifndef _LIBCXXABI_NO_EXCEPTIONS - } - catch (...) - { - } -#endif // _LIBCXXABI_NO_EXCEPTIONS - return p; -} - -_LIBCXXABI_WEAK -void* -operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC -{ - return ::operator new(size, alignment); -} - -_LIBCXXABI_WEAK -void* -operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT -{ - void* p = 0; -#ifndef _LIBCXXABI_NO_EXCEPTIONS - try - { -#endif // _LIBCXXABI_NO_EXCEPTIONS - p = ::operator new[](size, alignment); -#ifndef _LIBCXXABI_NO_EXCEPTIONS - } - catch (...) - { - } -#endif // _LIBCXXABI_NO_EXCEPTIONS - return p; -} - -_LIBCXXABI_WEAK -void -operator delete(void* ptr, std::align_val_t) _NOEXCEPT -{ - if (ptr) -#if defined(_LIBCPP_WIN32API) - ::_aligned_free(ptr); -#else - ::free(ptr); -#endif -} - -_LIBCXXABI_WEAK -void -operator delete(void* ptr, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT -{ - ::operator delete(ptr, alignment); -} - -_LIBCXXABI_WEAK -void -operator delete(void* ptr, size_t, std::align_val_t alignment) _NOEXCEPT -{ - ::operator delete(ptr, alignment); -} - -_LIBCXXABI_WEAK -void -operator delete[] (void* ptr, std::align_val_t alignment) _NOEXCEPT -{ - ::operator delete(ptr, alignment); -} - -_LIBCXXABI_WEAK -void -operator delete[] (void* ptr, std::align_val_t alignment, const std::nothrow_t&) _NOEXCEPT -{ - ::operator delete[](ptr, alignment); -} - -_LIBCXXABI_WEAK -void -operator delete[] (void* ptr, size_t, std::align_val_t alignment) _NOEXCEPT -{ - ::operator delete[](ptr, alignment); -} - -#endif // !_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index aa4d0ad492..028dc21df9 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -1283,6 +1283,10 @@ pub const ClangArgIterator = extern struct { sanitize, linker_script, verbose_cmds, + exceptions, + no_exceptions, + rtti, + no_rtti, }; fn init(argv: []const [*:0]const u8) ClangArgIterator { diff --git a/src/all_types.hpp b/src/all_types.hpp index f51a9c0572..6c7b465025 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2262,6 +2262,8 @@ struct CodeGen { bool emit_asm; bool emit_llvm_ir; bool test_is_evented; + bool cpp_rtti; + bool cpp_exceptions; CodeModel code_model; Buf *root_out_name; diff --git a/src/analyze.hpp b/src/analyze.hpp index a7828663b0..ee069427bd 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -260,6 +260,7 @@ ZigLLVMDIType *get_llvm_di_type(CodeGen *g, ZigType *type); enum CSourceKind { CSourceKindAsm, CSourceKindC, + CSourceKindCpp, }; void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_path, bool translate_c, diff --git a/src/codegen.cpp b/src/codegen.cpp index d9e011c4f3..f02253fb1a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8448,6 +8448,8 @@ static bool detect_dynamic_link(CodeGen *g) { LinkLib *link_lib = g->link_libs_list.at(i); if (target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) continue; + if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) + continue; return true; } return false; @@ -8782,6 +8784,8 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_bool(&cache_hash, g->is_test_build); cache_bool(&cache_hash, g->is_single_threaded); cache_bool(&cache_hash, g->test_is_evented); + cache_bool(&cache_hash, g->cpp_rtti); + cache_bool(&cache_hash, g->cpp_exceptions); cache_int(&cache_hash, g->code_model); cache_int(&cache_hash, g->zig_target->is_native_os); cache_int(&cache_hash, g->zig_target->is_native_cpu); @@ -9181,7 +9185,7 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa } if (translate_c) { - if (source_kind == CSourceKindC) { + if (source_kind != CSourceKindAsm) { // this gives us access to preprocessing entities, presumably at // the cost of performance args.append("-Xclang"); @@ -9213,6 +9217,12 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa args.append("-isystem"); args.append(libcxx_include_path); + + if (target_abi_is_musl(g->zig_target->abi)) { + args.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } + args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); } // According to Rich Felker libc headers are supposed to go before C language headers. @@ -9232,6 +9242,7 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa switch (source_kind) { case CSourceKindC: + case CSourceKindCpp: if (g->zig_target->llvm_cpu_name != nullptr) { args.append("-Xclang"); args.append("-target-cpu"); @@ -9248,6 +9259,14 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa case CSourceKindAsm: break; } + if (source_kind == CSourceKindCpp) { + if (!g->cpp_rtti) { + args.append("-fno-rtti"); + } + if (!g->cpp_exceptions) { + args.append("-fno-exceptions"); + } + } for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) { args.append(g->zig_target->llvm_cpu_features_asm_ptr[i]); } @@ -9695,6 +9714,8 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose cache_bool(cache_hash, g->have_sanitize_c); cache_bool(cache_hash, want_valgrind_support(g)); cache_bool(cache_hash, g->function_sections); + cache_bool(cache_hash, g->cpp_rtti); + cache_bool(cache_hash, g->cpp_exceptions); cache_int(cache_hash, g->code_model); for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) { @@ -10529,6 +10550,8 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_bool(ch, g->emit_bin); cache_bool(ch, g->emit_llvm_ir); cache_bool(ch, g->emit_asm); + cache_bool(ch, g->cpp_rtti); + cache_bool(ch, g->cpp_exceptions); cache_usize(ch, g->version_major); cache_usize(ch, g->version_minor); cache_usize(ch, g->version_patch); @@ -10833,6 +10856,8 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_global_cache_dir(), false, child_progress_node); child_gen->root_out_name = buf_create_from_str(name); child_gen->disable_gen_h = true; + child_gen->cpp_rtti = parent_gen->cpp_rtti; + child_gen->cpp_exceptions = parent_gen->cpp_exceptions; child_gen->want_stack_check = WantStackCheckDisabled; child_gen->want_sanitize_c = WantCSanitizeDisabled; child_gen->verbose_tokenize = parent_gen->verbose_tokenize; diff --git a/src/install_files.h b/src/install_files.h index e3a5f95763..8e7431145e 100644 --- a/src/install_files.h +++ b/src/install_files.h @@ -1859,7 +1859,6 @@ static const char *ZIG_LIBCXXABI_FILES[] = { "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", }; @@ -1875,7 +1874,6 @@ static const char *ZIG_LIBCXX_FILES[] = { "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", diff --git a/src/link.cpp b/src/link.cpp index 9dfc01af8f..d9b784129d 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -14,6 +14,8 @@ #include "glibc.hpp" static const char *msvcrt_common_src[] = { + "misc" OS_SEP "_create_locale.c", + "misc" OS_SEP "_free_locale.c", "misc" OS_SEP "onexit_table.c", "misc" OS_SEP "register_tls_atexit.c", "stdio" OS_SEP "acrt_iob_func.c", @@ -632,12 +634,17 @@ static const char *build_libunwind(CodeGen *parent, Stage2ProgressNode *progress } c_file->args.append("-I"); c_file->args.append(path_from_libunwind(parent, "include")); - c_file->args.append("-fPIC"); + if (target_supports_fpic(parent->zig_target)) { + c_file->args.append("-fPIC"); + } c_file->args.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS"); c_file->args.append("-Wa,--noexecstack"); - if (parent->zig_target->is_native_os && parent->zig_target->is_native_cpu) { - c_file->args.append("-D_LIBUNWIND_IS_NATIVE_ONLY"); - } + + // This is intentionally always defined because the macro definition means, should it only + // build for the target specified by compiler defines. Since we pass -target the compiler + // defines will be correct. + c_file->args.append("-D_LIBUNWIND_IS_NATIVE_ONLY"); + if (parent->build_mode == BuildModeDebug) { c_file->args.append("-D_DEBUG"); } @@ -1129,6 +1136,12 @@ static const char *build_libcxxabi(CodeGen *parent, Stage2ProgressNode *progress 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("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + c_file->args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + + if (target_abi_is_musl(parent->zig_target->abi)) { + c_file->args.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } c_file->args.append("-I"); c_file->args.append(cxxabi_include_path); @@ -1138,7 +1151,9 @@ static const char *build_libcxxabi(CodeGen *parent, Stage2ProgressNode *progress c_file->args.append("-O3"); c_file->args.append("-DNDEBUG"); - c_file->args.append("-fPIC"); + if (target_supports_fpic(parent->zig_target)) { + c_file->args.append("-fPIC"); + } c_file->args.append("-nostdinc++"); c_file->args.append("-fstrict-aliasing"); c_file->args.append("-funwind-tables"); @@ -1170,8 +1185,15 @@ static const char *build_libcxx(CodeGen *parent, Stage2ProgressNode *progress_no 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; + if (parent->zig_target->os == OsWindows) { + // filesystem stuff isn't supported on Windows + if (buf_starts_with_str(src_path_buf, "src/filesystem/")) { + continue; + } + } else { + if (buf_starts_with_str(src_path_buf, "src/support/win32/")) { + continue; + } } CFile *c_file = heap::c_allocator.create(); @@ -1182,6 +1204,12 @@ static const char *build_libcxx(CodeGen *parent, Stage2ProgressNode *progress_no 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("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + c_file->args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + + if (target_abi_is_musl(parent->zig_target->abi)) { + c_file->args.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } c_file->args.append("-I"); c_file->args.append(cxx_include_path); @@ -1191,7 +1219,9 @@ static const char *build_libcxx(CodeGen *parent, Stage2ProgressNode *progress_no c_file->args.append("-O3"); c_file->args.append("-DNDEBUG"); - c_file->args.append("-fPIC"); + if (target_supports_fpic(parent->zig_target)) { + c_file->args.append("-fPIC"); + } c_file->args.append("-nostdinc++"); c_file->args.append("-fvisibility-inlines-hidden"); c_file->args.append("-std=c++14"); @@ -1923,6 +1953,8 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); } lj->args.append(build_musl(g, lj->build_dep_prog_node)); + } else if (g->libcpp_link_lib != nullptr) { + lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); } else { zig_unreachable(); } @@ -2411,6 +2443,13 @@ static void construct_linker_job_coff(LinkJob *lj) { break; } + // 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)); + lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); + } + if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) { if (g->libc_link_lib == nullptr && !g->is_dummy_so) { Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node); @@ -2427,6 +2466,9 @@ static void construct_linker_job_coff(LinkJob *lj) { if (buf_eql_str(link_lib->name, "c")) { continue; } + if (buf_eql_str(link_lib->name, "c++") || buf_eql_str(link_lib->name, "c++abi")) { + continue; + } bool is_sys_lib = is_mingw_link_lib(link_lib->name); if (have_windows_dll_import_libs && is_sys_lib) { continue; @@ -2562,6 +2604,12 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } + // 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)); + } + // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce if (g->out_type == OutTypeExe || is_dyn_lib) { Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); diff --git a/src/main.cpp b/src/main.cpp index ae44185d04..d2936c3b52 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -458,6 +458,8 @@ static int main0(int argc, char **argv) { bool only_preprocess = false; bool ensure_libc_on_non_freestanding = false; bool ensure_libcpp_on_non_freestanding = false; + bool cpp_rtti = true; + bool cpp_exceptions = true; ZigList llvm_argv = {0}; llvm_argv.append("zig (LLVM option parsing)"); @@ -720,6 +722,18 @@ static int main0(int argc, char **argv) { verbose_cc = true; verbose_link = true; break; + case Stage2ClangArgExceptions: + cpp_exceptions = true; + break; + case Stage2ClangArgNoExceptions: + cpp_exceptions = false; + break; + case Stage2ClangArgRtti: + cpp_rtti = true; + break; + case Stage2ClangArgNoRtti: + cpp_rtti = false; + break; } } // Parse linker args @@ -1303,6 +1317,8 @@ static int main0(int argc, char **argv) { LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); link_lib->provided_explicitly = true; } + g->cpp_rtti = cpp_rtti; + g->cpp_exceptions = cpp_exceptions; g->subsystem = subsystem; g->valgrind_support = valgrind_support; g->want_pic = want_pic; @@ -1449,6 +1465,8 @@ static int main0(int argc, char **argv) { } CodeGen *g = codegen_create(main_pkg_path, zig_root_source_file, &target, out_type, build_mode, override_lib_dir, libc, cache_dir_buf, cmd == CmdTest, root_progress_node); + g->cpp_rtti = cpp_rtti; + g->cpp_exceptions = cpp_exceptions; if (llvm_argv.length >= 2) codegen_set_llvm_argv(g, llvm_argv.items + 1, llvm_argv.length - 2); g->valgrind_support = valgrind_support; g->link_eh_frame_hdr = link_eh_frame_hdr; diff --git a/src/stage2.h b/src/stage2.h index 42ef1ba612..9549f45ffe 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -344,6 +344,10 @@ enum Stage2ClangArg { Stage2ClangArgSanitize, Stage2ClangArgLinkerScript, Stage2ClangArgVerboseCmds, + Stage2ClangArgExceptions, + Stage2ClangArgNoExceptions, + Stage2ClangArgRtti, + Stage2ClangArgNoRtti, }; // ABI warning diff --git a/src/target.cpp b/src/target.cpp index 63360fc029..59b1addc33 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -1229,6 +1229,13 @@ bool target_is_libc_lib_name(const ZigTarget *target, const char *name) { return false; } +bool target_is_libcpp_lib_name(const ZigTarget *target, const char *name) { + if (strcmp(name, "c++") == 0 || strcmp(name, "c++abi") == 0) + return true; + + return false; +} + size_t target_libc_count(void) { return array_length(libcs_available); } diff --git a/src/target.hpp b/src/target.hpp index 01f2c6b168..c3f8530e02 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -102,6 +102,7 @@ bool target_os_requires_libc(Os os); bool target_can_build_libc(const ZigTarget *target); const char *target_libc_generic_name(const ZigTarget *target); bool target_is_libc_lib_name(const ZigTarget *target, const char *name); +bool target_is_libcpp_lib_name(const ZigTarget *target, const char *name); bool target_supports_fpic(const ZigTarget *target); bool target_supports_clang_march_native(const ZigTarget *target); bool target_requires_pic(const ZigTarget *target, bool linking_libc); diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index 46f2275b69..94dee90142 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -158,6 +158,22 @@ const known_options = [_]KnownOpt{ .name = "###", .ident = "verbose_cmds", }, + .{ + .name = "fexceptions", + .ident = "exceptions", + }, + .{ + .name = "fno-exceptions", + .ident = "no_exceptions", + }, + .{ + .name = "frtti", + .ident = "rtti", + }, + .{ + .name = "fno-rtti", + .ident = "no_rtti", + }, }; const blacklisted_options = [_][]const u8{}; -- cgit v1.2.3 From a25874108470f97c5c58d72b2df49a9085c79b2e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Mar 2020 22:24:40 -0400 Subject: initial support of response files See #4833 It doesn't support comments or quotes yet. --- src-self-hosted/stage2.zig | 105 ++++++++++++++++++++++++++++++++++++++++----- src/error.cpp | 1 + src/stage2.h | 2 + 3 files changed, 97 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 028dc21df9..0613634c6a 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -114,6 +114,7 @@ const Error = extern enum { InvalidAbiVersion, InvalidOperatingSystemVersion, UnknownClangOption, + NestedResponseFile, }; const FILE = std.c.FILE; @@ -1259,6 +1260,7 @@ pub const ClangArgIterator = extern struct { argv_ptr: [*]const [*:0]const u8, argv_len: usize, next_index: usize, + root_args: ?*Args, // ABI warning pub const ZigEquivalent = extern enum { @@ -1289,7 +1291,13 @@ pub const ClangArgIterator = extern struct { no_rtti, }; - fn init(argv: []const [*:0]const u8) ClangArgIterator { + const Args = struct { + next_index: usize, + argv_ptr: [*]const [*:0]const u8, + argv_len: usize, + }; + + pub fn init(argv: []const [*:0]const u8) ClangArgIterator { return .{ .next_index = 2, // `zig cc foo` this points to `foo` .has_next = argv.len > 2, @@ -1300,22 +1308,74 @@ pub const ClangArgIterator = extern struct { .other_args_len = undefined, .argv_ptr = argv.ptr, .argv_len = argv.len, + .root_args = null, }; } - fn next(self: *ClangArgIterator) !void { + pub fn next(self: *ClangArgIterator) !void { assert(self.has_next); assert(self.next_index < self.argv_len); // In this state we know that the parameter we are looking at is a root parameter // rather than an argument to a parameter. self.other_args_ptr = self.argv_ptr + self.next_index; self.other_args_len = 1; // We adjust this value below when necessary. - const arg = mem.span(self.argv_ptr[self.next_index]); - self.next_index += 1; - defer { - if (self.next_index >= self.argv_len) self.has_next = false; - } + var arg = mem.span(self.argv_ptr[self.next_index]); + self.incrementArgIndex(); + + if (mem.startsWith(u8, arg, "@")) { + if (self.root_args != null) return error.NestedResponseFile; + + // This is a "compiler response file". We must parse the file and treat its + // contents as command line parameters. + const allocator = std.heap.c_allocator; + const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit + const resp_file_path = arg[1..]; + const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { + std.debug.warn("unable to read response file '{}': {}\n", .{ resp_file_path, @errorName(err) }); + process.exit(1); + }; + defer allocator.free(resp_contents); + // TODO is there a specification for this file format? Let's find it and make this parsing more robust + // at the very least I'm guessing this needs to handle quotes and `#` comments. + var it = mem.tokenize(resp_contents, " \t\r\n"); + var resp_arg_list = std.ArrayList([*:0]const u8).init(allocator); + defer resp_arg_list.deinit(); + { + errdefer { + for (resp_arg_list.span()) |item| { + allocator.free(mem.span(item)); + } + } + while (it.next()) |token| { + const dupe_token = try mem.dupeZ(allocator, u8, token); + errdefer allocator.free(dupe_token); + try resp_arg_list.append(dupe_token); + } + const args = try allocator.create(Args); + errdefer allocator.destroy(args); + args.* = .{ + .next_index = self.next_index, + .argv_ptr = self.argv_ptr, + .argv_len = self.argv_len, + }; + self.root_args = args; + } + const resp_arg_slice = resp_arg_list.toOwnedSlice(); + self.next_index = 0; + self.argv_ptr = resp_arg_slice.ptr; + self.argv_len = resp_arg_slice.len; + + if (resp_arg_slice.len == 0) { + self.resolveRespFileArgs(); + return; + } + self.has_next = true; + self.other_args_ptr = self.argv_ptr + self.next_index; + self.other_args_len = 1; // We adjust this value below when necessary. + arg = mem.span(self.argv_ptr[self.next_index]); + self.incrementArgIndex(); + } if (!mem.startsWith(u8, arg, "-")) { self.zig_equivalent = .positional; self.only_arg = arg.ptr; @@ -1352,7 +1412,7 @@ pub const ClangArgIterator = extern struct { process.exit(1); } self.only_arg = self.argv_ptr[self.next_index]; - self.next_index += 1; + self.incrementArgIndex(); self.other_args_len += 1; self.zig_equivalent = clang_arg.zig_equivalent; @@ -1374,7 +1434,7 @@ pub const ClangArgIterator = extern struct { process.exit(1); } self.second_arg = self.argv_ptr[self.next_index]; - self.next_index += 1; + self.incrementArgIndex(); self.other_args_len += 1; self.zig_equivalent = clang_arg.zig_equivalent; break :find_clang_arg; @@ -1386,7 +1446,7 @@ pub const ClangArgIterator = extern struct { process.exit(1); } self.only_arg = self.argv_ptr[self.next_index]; - self.next_index += 1; + self.incrementArgIndex(); self.other_args_len += 1; self.zig_equivalent = clang_arg.zig_equivalent; break :find_clang_arg; @@ -1406,6 +1466,28 @@ pub const ClangArgIterator = extern struct { process.exit(1); } } + + fn incrementArgIndex(self: *ClangArgIterator) void { + self.next_index += 1; + self.resolveRespFileArgs(); + } + + fn resolveRespFileArgs(self: *ClangArgIterator) void { + const allocator = std.heap.c_allocator; + if (self.next_index >= self.argv_len) { + if (self.root_args) |root_args| { + self.next_index = root_args.next_index; + self.argv_ptr = root_args.argv_ptr; + self.argv_len = root_args.argv_len; + + allocator.destroy(root_args); + self.root_args = null; + } + if (self.next_index >= self.argv_len) { + self.has_next = false; + } + } + } }; export fn stage2_clang_arg_iterator( @@ -1418,7 +1500,8 @@ export fn stage2_clang_arg_iterator( export fn stage2_clang_arg_next(it: *ClangArgIterator) Error { it.next() catch |err| switch (err) { - error.UnknownClangOption => return .UnknownClangOption, + error.NestedResponseFile => return .NestedResponseFile, + error.OutOfMemory => return .OutOfMemory, }; return .None; } diff --git a/src/error.cpp b/src/error.cpp index c246ea5f31..76b510ecf3 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -84,6 +84,7 @@ const char *err_str(Error err) { case ErrorInvalidAbiVersion: return "invalid C ABI version"; case ErrorInvalidOperatingSystemVersion: return "invalid operating system version"; case ErrorUnknownClangOption: return "unknown Clang option"; + case ErrorNestedResponseFile: return "nested response file"; } return "(invalid error)"; } diff --git a/src/stage2.h b/src/stage2.h index 9549f45ffe..ed7caebcb0 100644 --- a/src/stage2.h +++ b/src/stage2.h @@ -106,6 +106,7 @@ enum Error { ErrorInvalidAbiVersion, ErrorInvalidOperatingSystemVersion, ErrorUnknownClangOption, + ErrorNestedResponseFile, }; // ABI warning @@ -361,6 +362,7 @@ struct Stage2ClangArgIterator { const char **argv_ptr; size_t argv_len; size_t next_index; + size_t root_args; }; // ABI warning -- cgit v1.2.3