diff --git a/.circleci/config.yml b/.circleci/config.yml index 03793d37..43fcfe14 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,6 +25,13 @@ executors: CXX: g++ BUILD_FLAGS: -j CTEST_FLAGS: -j4 --output-on-failure + clang9: + docker: + - image: conanio/clang9 + environment: + CXX: clang++-9 + BUILD_FLAGS: -j + CTEST_FLAGS: -j4 --output-on-failure clang6: docker: @@ -50,10 +57,13 @@ commands: - run: make test - run: make checkperf - cmake_prep: + install_cmake: steps: - run: apt-get update -qq - run: apt-get install -y cmake + + cmake_prep: + steps: - checkout cmake_build: @@ -78,6 +88,7 @@ commands: - run: SIMDJSON_FORCE_IMPLEMENTATION=fallback ctest $CTEST_FLAGS -L per_implementation - run: ctest $CTEST_FLAGS -LE "acceptance|per_implementation" # Everything we haven't run yet, run now. + # we not only want cmake to build and run tests, but we want also a successful installation from which we can build, link and run programs cmake_install_test: # this version builds, install, test and then verify from the installation steps: @@ -91,60 +102,65 @@ jobs: description: Build and run tests on GCC 7 and AVX 2 with a cmake static build executor: gcc7 environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON } - steps: [ cmake_test_all, cmake_install_test ] + steps: [ install_cmake, cmake_test_all, cmake_install_test ] clang6: description: Build and run tests on clang 6 and AVX 2 with a cmake static build executor: clang6 environment: { CMAKE_FLAGS: -DSIMDJSON_GOOGLE_BENCHMARKS=ON } - steps: [ init_clang6, cmake_test_all, cmake_install_test ] - + steps: [ init_clang6, install_cmake, cmake_test_all, cmake_install_test ] + # libcpp + libcpp-clang9: + description: Build and run tests on clang 6 and AVX 2 with a cmake static build and libc++ + executor: clang9 + environment: { CMAKE_FLAGS: -DSIMDJSON_USE_LIBCPP=ON } + steps: [ cmake_test_all, cmake_install_test ] # sanitize sanitize-gcc9: description: Build and run tests on GCC 9 and AVX 2 with a cmake sanitize build executor: gcc9 environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: -j4 --output-on-failure -E checkperf } - steps: [ cmake_test_all ] + steps: [ install_cmake, cmake_test_all ] sanitize-clang6: description: Build and run tests on clang 6 and AVX 2 with a cmake sanitize build executor: clang6 environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF -DSIMDJSON_SANITIZE=ON, CTEST_FLAGS: -j4 --output-on-failure -E checkperf } - steps: [ init_clang6, cmake_test_all ] + steps: [ init_clang6, install_cmake, cmake_test_all ] # dynamic dynamic-gcc7: description: Build and run tests on GCC 7 and AVX 2 with a cmake dynamic build executor: gcc7 environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF } - steps: [ cmake_test, cmake_install_test ] + steps: [ install_cmake, cmake_test, cmake_install_test ] dynamic-clang6: description: Build and run tests on clang 6 and AVX 2 with a cmake dynamic build executor: clang6 environment: { CMAKE_FLAGS: -DSIMDJSON_BUILD_STATIC=OFF } - steps: [ init_clang6, cmake_test, cmake_install_test ] + steps: [ init_clang6, install_cmake, cmake_test, cmake_install_test ] # unthreaded unthreaded-gcc7: description: Build and run tests on GCC 7 and AVX 2 *without* threads executor: gcc7 environment: { CMAKE_FLAGS: -DSIMDJSON_ENABLE_THREADS=OFF } - steps: [ cmake_test, cmake_install_test ] + steps: [ install_cmake, cmake_test, cmake_install_test ] unthreaded-clang6: description: Build and run tests on Clang 6 and AVX 2 *without* threads executor: clang6 environment: { CMAKE_FLAGS: -DSIMDJSON_ENABLE_THREADS=OFF } - steps: [ init_clang6, cmake_test, cmake_install_test ] + steps: [ init_clang6, install_cmake, cmake_test, cmake_install_test ] # noexcept noexcept-gcc7: description: Build and run tests on GCC 7 and AVX 2 with exceptions off executor: gcc7 environment: { CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF } - steps: [ cmake_test, cmake_install_test ] + steps: [ install_cmake, cmake_test, cmake_install_test ] noexcept-clang6: description: Build and run tests on GCC 7 and AVX 2 with exceptions off executor: clang6 environment: { CMAKE_FLAGS: -DSIMDJSON_EXCEPTIONS=OFF } - steps: [ init_clang6, cmake_test, cmake_install_test ] + steps: [ init_clang6, install_cmake, cmake_test, cmake_install_test ] # # Misc. @@ -174,7 +190,10 @@ workflows: # full multi-implementation tests - gcc7 - clang6 - + + # libc++ + - libcpp-clang9 + # full single-implementation tests - sanitize-gcc9 - sanitize-clang6 diff --git a/CMakeLists.txt b/CMakeLists.txt index dc07a444..1205b6e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,11 +25,13 @@ if(MSVC) else() option(SIMDJSON_BUILD_STATIC "Build a static library" OFF) # turning it on disables the production of a dynamic library option(SIMDJSON_COMPETITION "Compile competitive benchmarks" ON) + option(SIMDJSON_USE_LIBCPP "Use the libc++ library" OFF) endif() option(SIMDJSON_GOOGLE_BENCHMARKS "compile the Google Benchmark benchmarks" OFF) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake") + # We compile tools, tests, etc. with C++ 17. Override yourself if you need on a target. set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -38,6 +40,7 @@ set(CMAKE_MACOSX_RPATH OFF) set(CMAKE_THREAD_PREFER_PTHREAD ON) set(THREADS_PREFER_PTHREAD_FLAG ON) + # LTO seems to create all sorts of fun problems. Let us # disable temporarily. #include(CheckIPOSupported) @@ -58,6 +61,17 @@ else() target_compile_options(simdjson-flags INTERFACE -Werror -Wall -Wextra -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self) endif() + +if(SIMDJSON_USE_LIBCPP) + target_link_libraries(simdjson-flags INTERFACE -stdlib=libc++ -lc++abi) + # instead of the above line, we could have used + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++ -lc++abi") + # The next line is needed empirically. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + # we update CMAKE_SHARED_LINKER_FLAGS, this gets updated later as well + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lc++abi") +endif(SIMDJSON_USE_LIBCPP) + # Optional flags option(SIMDJSON_IMPLEMENTATION_HASWELL "Include the haswell implementation" ON) if(NOT SIMDJSON_IMPLEMENTATION_HASWELL) diff --git a/examples/quickstart/CMakeLists.txt b/examples/quickstart/CMakeLists.txt index c6bb67e1..58c701ec 100644 --- a/examples/quickstart/CMakeLists.txt +++ b/examples/quickstart/CMakeLists.txt @@ -3,7 +3,13 @@ # # TODO haven't quite decided the right way to run quickstart on Windows. Needs README update. -if (NOT MSVC) +# +# Note: on macOS and other platforms, the 'command' described below may not work even if the cmake builds. +# For example, it may be necessary to specify the sysroot, which CMake does, but the 'command' does not +# handle such niceties. On a case-by-case basis it is fixable but it requires work that CMake knows how +# to do but that is not trivial. +# +IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") # TODO run amalgamate first! function(add_quickstart_test TEST_NAME SOURCE_FILE) # Second argument is C++ standard name diff --git a/include/simdjson/common_defs.h b/include/simdjson/common_defs.h index 2fc0d4a1..df0cef04 100644 --- a/include/simdjson/common_defs.h +++ b/include/simdjson/common_defs.h @@ -121,11 +121,49 @@ constexpr size_t DEFAULT_MAX_DEPTH = 1024; #endif // MSC_VER -// -// Backfill std::string_view using nonstd::string_view on C++11 -// -#if (!SIMDJSON_CPLUSPLUS17) + +// C++17 requires string_view. +#if SIMDJSON_CPLUSPLUS17 +#define SIMDJSON_HAS_STRING_VIEW +#endif + +// This macro (__cpp_lib_string_view) has to be defined +// for C++17 and better, but if it is otherwise defined, +// we are going to assume that string_view is available +// even if we do not have C++17 support. +#ifdef __cpp_lib_string_view +#define SIMDJSON_HAS_STRING_VIEW +#endif + +// Some systems have string_view even if we do not have C++17 support, +// and even if __cpp_lib_string_view is undefined, it is the case +// with Apple clang version 11. +// We must handle it. *This is important.* +#ifndef SIMDJSON_HAS_STRING_VIEW +#if defined __has_include +// do not combine the next #if with the previous one (unsafe) +#if __has_include () +// now it is safe to trigger the include +#include // though the file is there, it does not follow that we got the implementation +#if defined(_LIBCPP_STRING_VIEW) +// Ah! So we under libc++ which under its Library Fundamentals Technical Specification, which preceeded C++17, +// included string_view. +// This means that we have string_view *even though* we may not have C++17. +#define SIMDJSON_HAS_STRING_VIEW +#endif // _LIBCPP_STRING_VIEW +#endif // __has_include () +#endif // defined __has_include +#endif // def SIMDJSON_HAS_STRING_VIEW +// end of complicated but important routine to try to detect string_view. + +// +// Backfill std::string_view using nonstd::string_view on systems where +// we expect that string_view is missing. Important: if we get this wrong, +// we will end up with two string_view definitions and potential trouble. +// That is why we work so hard above to avoid it. +// +#ifndef SIMDJSON_HAS_STRING_VIEW SIMDJSON_PUSH_DISABLE_ALL_WARNINGS #include "simdjson/nonstd/string_view.hpp" SIMDJSON_POP_DISABLE_WARNINGS @@ -133,6 +171,7 @@ SIMDJSON_POP_DISABLE_WARNINGS namespace std { using string_view = nonstd::string_view; } -#endif // if (SIMDJSON_CPLUSPLUS < 201703L) +#endif // SIMDJSON_HAS_STRING_VIEW +#undef SIMDJSON_HAS_STRING_VIEW // We are not going to need this macro anymore. #endif // SIMDJSON_COMMON_DEFS_H