diff --git a/.github/actions/setup_cache/action.yml b/.github/actions/setup_cache/action.yml new file mode 100644 index 0000000..197e557 --- /dev/null +++ b/.github/actions/setup_cache/action.yml @@ -0,0 +1,35 @@ + +name: 'setup_cache' +description: 'sets up the shared cache' +inputs: + compiler: + required: true + type: string + build_type: + required: true + type: string + generator: + required: true + type: string + developer_mode: + required: true + type: string + + +runs: + using: "composite" + steps: + - name: Cache + uses: actions/cache@v2 + with: + # You might want to add .ccache to your cache configuration? + path: | + ~/.cache/pip + ${{ env.HOME }}/.cache/vcpkg/archives + ${{ env.XDG_CACHE_HOME }}/vcpkg/archives + ${{ env.LOCALAPPDATA }}\vcpkg\archives + ${{ env.APPDATA }}\vcpkg\archives + key: ${{ runner.os }}-${{ inputs.compiler }}-${{ inputs.build_type }}-${{ inputs.generator }}-${{ inputs.developer_mode }}-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('./vcpkg.json')}} + restore-keys: | + ${{ runner.os }}-${{ inputs.compiler }}-${{ inputs.build_type }} + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24d570f..d5b5b4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,17 +1,18 @@ name: ci on: pull_request: + release: + types: [published] + push: tags: branches: - main + - develop env: - # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) - # Conan cache environment variables - CONAN_SYSREQUIRES_MODE: enabled - CONAN_USER_HOME: "${{ github.workspace }}/conan-cache" - CONAN_USER_HOME_SHORT: "${{ github.workspace }}/conan-cache/short" + CLANG_TIDY_VERSION: "15.0.2" + VERBOSE: 1 jobs: Test: @@ -32,103 +33,132 @@ jobs: - macos-10.15 - windows-2019 compiler: - # you can specify the version after `-` like "llvm-13.0.0". - - llvm - - gcc-8 + # you can specify the version after `-` like "llvm-15.0.2". + - llvm-15.0.2 + - gcc-11 generator: - "Ninja Multi-Config" build_type: - Release - Debug - developer_mode: + package_maintainer_mode: - ON - OFF - large_tests: + build_shared: - OFF exclude: # mingw is determined by this author to be too buggy to support - os: windows-2019 - compiler: gcc-8 - # These have anonalous failures, not sure what is up with no access to a physical Mac. - - os: macos-10.15 - compiler: gcc-8 - build_type: Debug - developer_mode: ON + compiler: gcc-11 + + include: + # Add appropriate variables for gcov version required. This will intentionally break + # if you try to use a compiler that does not have gcov set + - compiler: gcc-11 + gcov_executable: gcov + enable_ipo: On + + - compiler: llvm-15.0.2 + enable_ipo: Off + gcov_executable: "llvm-cov gcov" + - os: macos-10.15 - compiler: llvm - build_type: Release - developer_mode: ON + enable_ipo: Off + enable_cppcheck: OFF + enable_clang_tidy: OFF + - os: ubuntu-20.04 + enable_clang_tidy: On + enable_cppcheck: On - include: - # setup gcov variables for extra compiler versions - - compiler: llvm - gcov: "llvm-cov gcov" - - compiler: gcc-8 - gcov: "gcov-8" - - # setup preferred package generators - - developer_mode: OFF - build_type: Release + # Set up preferred package generators, for given build configurations + - build_type: Release + package_maintainer_mode: OFF package_generator: TBZ2 - # This exists solely to make sure a non-multiconfig build works - os: ubuntu-20.04 - compiler: gcc - gcov: gcov + compiler: gcc-11 generator: "Unix Makefiles" build_type: Debug - developer_mode: On + gcov_executable: gcov + package_maintainer_mode: On + enable_ipo: Off + enable_clang_tidy: On + enable_cppcheck: On - - os: windows-2019 + + # Windows msvc builds + - os: windows-2022 compiler: msvc - generator: "Visual Studio 16 2019" + generator: "Visual Studio 17 2022" build_type: Debug - developer_mode: On - - os: windows-2019 + package_maintainer_mode: On + enable_ipo: On + enable_clang_tidy: Off + enable_cppcheck: Off + + + - os: windows-2022 compiler: msvc - generator: "Visual Studio 16 2019" + generator: "Visual Studio 17 2022" build_type: Release - developer_mode: On - - os: windows-2019 + package_maintainer_mode: On + enable_ipo: On + enable_clang_tidy: Off + enable_cppcheck: Off + + + - os: windows-2022 compiler: msvc - generator: "Visual Studio 16 2019" + generator: "Visual Studio 17 2022" build_type: Debug - developer_mode: Off - - os: windows-2019 + package_maintainer_mode: Off + enable_clang_tidy: Off + enable_cppcheck: Off + + + - os: windows-2022 compiler: msvc - generator: "Visual Studio 16 2019" + generator: "Visual Studio 17 2022" build_type: Release - developer_mode: Off + package_maintainer_mode: Off package_generator: ZIP + enable_clang_tidy: Off + enable_cppcheck: Off + - os: windows-2022 + compiler: msvc + generator: "Visual Studio 17 2022" + build_type: Release + package_maintainer_mode: On + enable_ipo: On + build_shared: On + enable_clang_tidy: Off + enable_cppcheck: Off steps: + - name: Check for llvm version mismatches + if: ${{ contains(matrix.compiler, 'llvm') && !contains(matrix.compiler, env.CLANG_TIDY_VERSION) }} + uses: actions/github-script@v3 + with: + script: | + core.setFailed('There is a mismatch between configured llvm compiler and clang-tidy version chosen') + - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - name: Cache - uses: actions/cache@v2 + + - name: Setup Cache + uses: ./.github/actions/setup_cache with: - path: | - ~/vcpkg - ./build/vcpkg_installed - ${{ env.CONAN_USER_HOME }} - ~/.cache/pip - ${{ env.HOME }}/.cache/vcpkg/archives - ${{ env.XDG_CACHE_HOME }}/vcpkg/archives - ${{ env.LOCALAPPDATA }}\vcpkg\archives - ${{ env.APPDATA }}\vcpkg\archives - key: ${{ runner.os }}-${{ matrix.compiler }}-${{matrix.build_type}}-${{matrix.generator}}-${{matrix.developer_mode}}-${{ hashFiles('**/CMakeLists.txt') }}-${{ hashFiles('./conanfile.txt')}}-${{ hashFiles('./vcpkg.json')}} - restore-keys: | - ${{ runner.os }}-${{ matrix.compiler }}-${{matrix.build_type}}-${{matrix.generator}}-${{matrix.developer_mode}} - - - run: pip3 install conan cmake ninja + compiler: ${{ matrix.compiler }} + build_type: ${{ matrix.build_type }} + package_maintainer_mode: ${{ matrix.package_maintainer_mode }} + generator: ${{ matrix.generator }} - name: Setup Cpp uses: aminya/setup-cpp@v1 @@ -136,12 +166,12 @@ jobs: compiler: ${{ matrix.compiler }} vcvarsall: ${{ contains(matrix.os, 'windows' )}} - cmake: false - ninja: false - conan: false + cmake: true + ninja: true vcpkg: false ccache: true - clangtidy: true + clangtidy: ${{ env.CLANG_TIDY_VERSION }} + cppcheck: true @@ -150,7 +180,7 @@ jobs: - name: Configure CMake run: | - cmake -S . -B ./build -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -DENABLE_DEVELOPER_MODE:BOOL=${{matrix.developer_mode}} -DENABLE_LARGE_TESTS:BOOL=${{matrix.large_tests}} -DOPT_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} + cmake -S . -B ./build -G "${{matrix.generator}}" -Djson2cpp_ENABLE_CLANG_TIDY:BOOL=${{ matrix.enable_clang_tidy }} -Djson2cpp_ENABLE_CPPCHECK:BOOL=${{ matrix.enable_cppcheck }} -Djson2cpp_ENABLE_IPO=${{matrix.enable_ipo }} -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -Djson2cpp_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.package_maintainer_mode}} -Djson2cpp_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} - name: Build # Execute the build. You can specify a specific target with "--target " @@ -164,11 +194,7 @@ jobs: # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: | ctest -C ${{matrix.build_type}} - pwd - which gcov - gcov --version - gcovr --version - gcovr -j ${{env.nproc}} --root ../ --print-summary --xml-pretty --xml coverage.xml . --verbose --gcov-executable '${{matrix.gcov}}' + gcovr -j ${{env.nproc}} --delete --root ../ --print-summary --xml-pretty --xml coverage.xml . --gcov-executable '${{ matrix.gcov_executable }}' - name: Windows - Test and coverage if: runner.os == 'Windows' @@ -176,7 +202,7 @@ jobs: run: | OpenCppCoverage.exe --export_type cobertura:coverage.xml --cover_children -- ctest -C ${{matrix.build_type}} - - name: Build Package + - name: CPack if: matrix.package_generator != '' working-directory: ./build run: | @@ -190,6 +216,7 @@ jobs: files: | build/*-*${{ matrix.build_type }}*-*.* + - name: Publish Tagged Release uses: softprops/action-gh-release@v1 if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.package_generator != '' }} @@ -197,6 +224,7 @@ jobs: files: | build/*-*${{ matrix.build_type }}*-*.* + - name: Publish to codecov uses: codecov/codecov-action@v2 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..aaa309d --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,102 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ main, develop ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ main, develop ] + schedule: + - cron: '38 0 * * 5' + + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://git.io/codeql-language-support + compiler: + # you can specify the version after `-` like "llvm-13.0.0". + - gcc-11 + generator: + - "Ninja Multi-Config" + build_type: + - Debug + packaging_maintainer_mode: + - ON + + + steps: + - uses: actions/checkout@v2 + + - name: Setup Cache + uses: ./.github/actions/setup_cache + with: + compiler: ${{ matrix.compiler }} + build_type: ${{ matrix.build_type }} + packaging_maintainer_mode: ${{ matrix.packaging_maintainer_mode }} + generator: ${{ matrix.generator }} + + + - name: Setup Cpp + uses: aminya/setup-cpp@v1 + with: + compiler: ${{ matrix.compiler }} + vcvarsall: ${{ contains(matrix.os, 'windows' )}} + + cmake: true + ninja: true + vcpkg: false + ccache: true + clangtidy: false + + cppcheck: false + + gcovr: false + opencppcoverage: false + + # make sure coverage is only enabled for Debug builds, since it sets -O0 to make sure coverage + # has meaningful results + - name: Configure CMake + run: | + cmake -S . -B ./build -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -Djson2cpp_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -Djson2cpp_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + + - name: Build + # Execute the build. You can specify a specific target with "--target " + run: | + cmake --build ./build --config ${{matrix.build_type}} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ae17ef..932de36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,35 +1,41 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.21) -# Used as a tool to make sure that the standard used by tools -# and by the projects cannot get out of sync -set(USER_CXX_STANDARD 17) +# This template attempts to be "fetch_content"-able +# so that it works well with tools like CPM or other +# manual dependency management -# uncomment to set a default CXX standard for the external tools like clang-tidy and cppcheck -# and the targets that do not specify a standard. -# If not set, the latest supported standard for your compiler is used -# You can later set fine-grained standards for each target using `target_compile_features` -# Note: linking together projects compiled with different C++ standards may work, but -# it is not recommended because of possible issues with ABI -set(CMAKE_CXX_STANDARD ${USER_CXX_STANDARD}) +# Only set the cxx_standard if it is not set by someone else +if (NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 20) +endif() # strongly encouraged to enable this globally to avoid conflicts between # -Wpedantic being enabled and -std=c++20 and -std=gnu++20 for example # when compiling with PCH enabled set(CMAKE_CXX_EXTENSIONS OFF) -# Add project_options v0.13.1 -# https://github.com/cpp-best-practices/project_options -include(FetchContent) -FetchContent_Declare( - _project_options - URL https://github.com/lefticus/project_options/archive/refs/heads/make_cppcheck_flags_configurable.zip) -# URL https://github.com/cpp-best-practices/project_options/archive/refs/tags/v0.13.1.zip) -FetchContent_MakeAvailable(_project_options) -include(${_project_options_SOURCE_DIR}/Index.cmake) +# Set the project name and language +project( + json2cpp + VERSION 0.0.1 + DESCRIPTION "" + HOMEPAGE_URL "%%myurl%%" + LANGUAGES CXX C) + +include(cmake/PreventInSourceBuilds.cmake) +include(ProjectOptions.cmake) -# uncomment to enable vcpkg: -# # Setup vcpkg - should be called before defining project() -# run_vcpkg() + +json2cpp_setup_options() + +json2cpp_global_options() +include(Dependencies.cmake) +json2cpp_setup_dependencies() + +json2cpp_local_options() + +# don't know if this should be set globally from here or not... +set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(GIT_SHA "Unknown" @@ -40,96 +46,72 @@ string( 8 GIT_SHORT_SHA) -# Set the project name and language -project( - json2cpp - VERSION 0.0.1 - DESCRIPTION "Compiles JSON files into `static constexpr` C++ files" - LANGUAGES CXX C) +target_compile_features(json2cpp_options INTERFACE cxx_std_${CMAKE_CXX_STANDARD}) -if(GENERATOR_IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) - # Make sure that all supported configuration types have their - # associated conan packages available. You can reduce this - # list to only the configuration types you use, but only if one - # is not forced-set on the command line for VS - set(CMAKE_CONFIGURATION_TYPES - Debug - Release - RelWithDebInfo - MinSizeRel) -endif() +add_library(json2cpp_headers INTERFACE) +target_include_directories(json2cpp_headers INTERFACE "${CMAKE_SOURCE_DIR}/include") +add_library(json2cpp::json2cpp_headers ALIAS json2cpp_headers) -include(${_project_options_SOURCE_DIR}/src/DynamicProjectOptions.cmake) - -# defaulted_project_options sets recommended defaults and provides user and developer -# modes and full GUI support for choosing options at configure time - -# for more flexibility, look into project_options() macro - -# Any default can be overridden -# set(_DEFAULT ) - set default for both user and developer modes -# set(_DEVELOPER_DEFAULT ) - set default for developer mode -# set(_USER_DEFAULT ) - set default for user mode - -# Initialize project_options variable related to this project -# This overwrites `project_options` and sets `project_warnings` -# uncomment the options to enable them: -dynamic_project_options( - # Note: PCH is disabled by default in developer mode because these headers become - # globally included and they can mask other errors - PCH_HEADERS - - # This is a list of headers to pre-compile, here are some common ones - # CONAN_OPTIONS # Extra options to pass to conan - # MSVC_WARNINGS # Override the defaults for the MSVC warnings - # CLANG_WARNINGS # Override the defaults for the CLANG warnings - # GCC_WARNINGS # Override the defaults for the GCC warnings - CPPCHECK_OPTIONS - --enable=style,performance,warning,portability - --inline-suppr - # We cannot act on a bug/missing feature of cppcheck - --suppress=internalAstError - # if a file does not have an internalAstError, we get an unmatchedSuppression error - --suppress=unmatchedSuppression - --suppress=passedByValue - --inconclusive) - -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9") - target_link_libraries(project_options INTERFACE stdc++fs) -endif() +add_library(json2cpp::json2cpp_options ALIAS json2cpp_options) +add_library(json2cpp::json2cpp_warnings ALIAS json2cpp_warnings) -target_compile_features(project_options INTERFACE cxx_std_${USER_CXX_STANDARD}) +#add_library(json2cpp::json2cpp_options INTERFACE IMPORTED) +#add_library(json2cpp::json2cpp_warnings INTERFACE IMPORTED) -option(ENABLE_LARGE_TESTS "Enable tests and tools that use the Energy+ schema file" ON) +# configure files based on CMake configuration options # Adding the src: add_subdirectory(src) +# Don't even look at tests if we're not top level +if(NOT PROJECT_IS_TOP_LEVEL) + return() +endif() + # Adding the tests: -option(ENABLE_TESTING "Enable the tests" ON) -if(ENABLE_TESTING) - enable_testing() - message("Building Tests. Be sure to check out test/constexpr_tests for constexpr -testing") +include(CTest) + +if(BUILD_TESTING) add_subdirectory(test) endif() -option(ENABLE_FUZZING "Enable the fuzz tests" OFF) -if(ENABLE_FUZZING) - message("Building Fuzz Tests, using fuzzing sanitizer https://www.llvm.org/docs/LibFuzzer.html") + +if(json2cpp_BUILD_FUZZ_TESTS) + message(AUTHOR_WARNING "Building Fuzz Tests, using fuzzing sanitizer https://www.llvm.org/docs/LibFuzzer.html") add_subdirectory(fuzz_test) endif() # If MSVC is being used, and ASAN is enabled, we need to set the debugger environment # so that it behaves well with MSVC's debugger, and we can run the target from visual studio if(MSVC) - get_all_targets(all_targets) + get_all_installable_targets(all_targets) + message("all_targets=${all_targets}") set_target_properties(${all_targets} PROPERTIES VS_DEBUGGER_ENVIRONMENT "PATH=$(VC_ExecutablePath_x64);%PATH%") endif() # set the startup project for the "play" button in MSVC set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT json2cpp) +if(CMAKE_SKIP_INSTALL_RULES) + return() +endif() + +include(cmake/PackageProject.cmake) + +# Add other targets that you want installed here, by default we just package the one executable +# we know we want to ship +json2cpp_package_project( + TARGETS + json2cpp + json2cpp_options + json2cpp_warnings + # FIXME: this does not work! CK + # PRIVATE_DEPENDENCIES_CONFIGURED project_options project_warnings +) + +# Experience shows that explicit package naming can help make it easier to sort +# out potential ABI related issues before they start, while helping you +# track a build to a specific GIT SHA set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}-${GIT_SHORT_SHA}-${CMAKE_SYSTEM_NAME}-${CMAKE_BUILD_TYPE}-${CMAKE_CXX_COMPILER_ID}-${CMAKE_CXX_COMPILER_VERSION}" ) diff --git a/Dependencies.cmake b/Dependencies.cmake new file mode 100644 index 0000000..2aa1347 --- /dev/null +++ b/Dependencies.cmake @@ -0,0 +1,48 @@ +include(cmake/CPM.cmake) + +# Done as a function so that updates to variables like +# CMAKE_CXX_FLAGS don't propagate out to other +# targets +function(json2cpp_setup_dependencies) + + # For each dependency, see if it's + # already been provided to us by a parent project + + if(NOT TARGET fmtlib::fmtlib) + cpmaddpackage("gh:fmtlib/fmt#9.1.0") + endif() + + if(NOT TARGET spdlog::spdlog) + cpmaddpackage( + NAME + spdlog + VERSION + 1.11.0 + GITHUB_REPOSITORY + "gabime/spdlog" + OPTIONS + "SPDLOG_FMT_EXTERNAL ON") + endif() + + if(NOT TARGET Catch2::Catch2WithMain) + cpmaddpackage("gh:catchorg/Catch2@3.3.2") + endif() + + if(NOT TARGET CLI11::CLI11) + cpmaddpackage("gh:CLIUtils/CLI11@2.3.2") + endif() + + if(NOT TARGET ftxui::screen) + cpmaddpackage("gh:ArthurSonzogni/FTXUI#e23dbc7473654024852ede60e2121276c5aab660") + endif() + + if(NOT TARGET nlohmann_json::nlohmann_json) + cpmaddpackage("gh:nlohmann/json@3.11.2") + endif() + + if(NOT TARGET ValiJSON::valijson) + cpmaddpackage("gh:tristanpenman/valijson@1.0") + endif() + + +endfunction() diff --git a/ProjectOptions.cmake b/ProjectOptions.cmake new file mode 100644 index 0000000..b0424ba --- /dev/null +++ b/ProjectOptions.cmake @@ -0,0 +1,174 @@ +include(cmake/SystemLink.cmake) +include(cmake/LibFuzzer.cmake) +include(CMakeDependentOption) +include(CheckCXXCompilerFlag) + + +macro(json2cpp_setup_options) + option(json2cpp_ENABLE_LARGE_TESTS ON) + + option(json2cpp_ENABLE_HARDENING "Enable hardening" ON) + option(json2cpp_ENABLE_COVERAGE "Enable coverage reporting" OFF) + cmake_dependent_option( + json2cpp_ENABLE_GLOBAL_HARDENING + "Attempt to push hardening options to built dependencies" + ON + json2cpp_ENABLE_HARDENING + OFF) + + + if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND NOT WIN32) + set(SUPPORTS_UBSAN ON) + else() + set(SUPPORTS_UBSAN OFF) + endif() + + if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND WIN32) + set(SUPPORTS_ASAN OFF) + else() + set(SUPPORTS_ASAN ON) + endif() + + json2cpp_check_libfuzzer_support(LIBFUZZER_SUPPORTED) + option(json2cpp_BUILD_FUZZ_TESTS "Enable fuzz testing executable" ${LIBFUZZER_SUPPORTED}) + + + if(NOT PROJECT_IS_TOP_LEVEL OR json2cpp_PACKAGING_MAINTAINER_MODE) + option(json2cpp_ENABLE_IPO "Enable IPO/LTO" OFF) + option(json2cpp_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) + option(json2cpp_ENABLE_USER_LINKER "Enable user-selected linker" OFF) + option(json2cpp_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" OFF) + option(json2cpp_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) + option(json2cpp_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" OFF) + option(json2cpp_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) + option(json2cpp_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) + option(json2cpp_ENABLE_UNITY_BUILD "Enable unity builds" OFF) + option(json2cpp_ENABLE_CLANG_TIDY "Enable clang-tidy" OFF) + option(json2cpp_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) + option(json2cpp_ENABLE_PCH "Enable precompiled headers" OFF) + option(json2cpp_ENABLE_CACHE "Enable ccache" OFF) + else() + option(json2cpp_ENABLE_IPO "Enable IPO/LTO" ON) + option(json2cpp_WARNINGS_AS_ERRORS "Treat Warnings As Errors" ON) + option(json2cpp_ENABLE_USER_LINKER "Enable user-selected linker" OFF) + option(json2cpp_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) + option(json2cpp_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) + option(json2cpp_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" ${SUPPORTS_UBSAN}) + option(json2cpp_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) + option(json2cpp_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) + option(json2cpp_ENABLE_UNITY_BUILD "Enable unity builds" OFF) + option(json2cpp_ENABLE_CLANG_TIDY "Enable clang-tidy" ON) + option(json2cpp_ENABLE_CPPCHECK "Enable cpp-check analysis" ON) + option(json2cpp_ENABLE_PCH "Enable precompiled headers" OFF) + option(json2cpp_ENABLE_CACHE "Enable ccache" ON) + endif() + + if(NOT PROJECT_IS_TOP_LEVEL) + mark_as_advanced( + json2cpp_ENABLE_IPO + json2cpp_WARNINGS_AS_ERRORS + json2cpp_ENABLE_USER_LINKER + json2cpp_ENABLE_SANITIZER_ADDRESS + json2cpp_ENABLE_SANITIZER_LEAK + json2cpp_ENABLE_SANITIZER_UNDEFINED + json2cpp_ENABLE_SANITIZER_THREAD + json2cpp_ENABLE_SANITIZER_MEMORY + json2cpp_ENABLE_UNITY_BUILD + json2cpp_ENABLE_CLANG_TIDY + json2cpp_ENABLE_CPPCHECK + json2cpp_ENABLE_COVERAGE + json2cpp_ENABLE_PCH + json2cpp_ENABLE_CACHE) + endif() +endmacro() + +macro(json2cpp_global_options) + if(json2cpp_ENABLE_IPO) + include(cmake/InterproceduralOptimization.cmake) + json2cpp_enable_ipo() + endif() + + if(json2cpp_ENABLE_HARDENING AND json2cpp_ENABLE_GLOBAL_HARDENING) + include(cmake/Hardening.cmake) + set(ENABLE_UBSAN_MINIMAL_RUNTIME NOT json2cpp_ENABLE_SANITIZER_UNDEFINED) + json2cpp_enable_hardening(json2cpp_options ON ${ENABLE_UBSAN_MINIMAL_RUNTIME}) + endif() +endmacro() + +macro(json2cpp_local_options) + if (PROJECT_IS_TOP_LEVEL) + include(cmake/StandardProjectSettings.cmake) + endif() + + add_library(json2cpp_warnings INTERFACE) + add_library(json2cpp_options INTERFACE) + + include(cmake/CompilerWarnings.cmake) + json2cpp_set_project_warnings( + json2cpp_warnings + ${json2cpp_WARNINGS_AS_ERRORS} + "" + "" + "" + "") + + if(json2cpp_ENABLE_USER_LINKER) + include(cmake/Linker.cmake) + configure_linker(json2cpp_options) + endif() + + include(cmake/Sanitizers.cmake) + json2cpp_enable_sanitizers( + json2cpp_options + ${json2cpp_ENABLE_SANITIZER_ADDRESS} + ${json2cpp_ENABLE_SANITIZER_LEAK} + ${json2cpp_ENABLE_SANITIZER_UNDEFINED} + ${json2cpp_ENABLE_SANITIZER_THREAD} + ${json2cpp_ENABLE_SANITIZER_MEMORY}) + + set_target_properties(json2cpp_options PROPERTIES UNITY_BUILD ${json2cpp_ENABLE_UNITY_BUILD}) + + if(json2cpp_ENABLE_PCH) + target_precompile_headers( + json2cpp_options + INTERFACE + + + ) + endif() + + if(json2cpp_ENABLE_CACHE) + include(cmake/Cache.cmake) + json2cpp_enable_cache() + endif() + + include(cmake/StaticAnalyzers.cmake) + if(json2cpp_ENABLE_CLANG_TIDY) + json2cpp_enable_clang_tidy(json2cpp_options ${json2cpp_WARNINGS_AS_ERRORS}) + endif() + + if(json2cpp_ENABLE_CPPCHECK) + json2cpp_enable_cppcheck(${json2cpp_WARNINGS_AS_ERRORS} "" # override cppcheck options + ) + endif() + + if(json2cpp_ENABLE_COVERAGE) + include(cmake/Tests.cmake) + json2cpp_enable_coverage(json2cpp_options) + endif() + + if(json2cpp_WARNINGS_AS_ERRORS) + check_cxx_compiler_flag("-Wl,--fatal-warnings" LINKER_FATAL_WARNINGS) + if(LINKER_FATAL_WARNINGS) + # This is not working consistently, so disabling for now + # target_link_options(json2cpp_options INTERFACE -Wl,--fatal-warnings) + endif() + endif() + + if(json2cpp_ENABLE_HARDENING AND NOT json2cpp_ENABLE_GLOBAL_HARDENING) + include(cmake/Hardening.cmake) + set(ENABLE_UBSAN_MINIMAL_RUNTIME NOT json2cpp_ENABLE_SANITIZER_UNDEFINED) + json2cpp_enable_hardening(json2cpp_options OFF ${ENABLE_UBSAN_MINIMAL_RUNTIME}) + endif() + +endmacro() diff --git a/cmake/CPM.cmake b/cmake/CPM.cmake new file mode 100644 index 0000000..a3086b7 --- /dev/null +++ b/cmake/CPM.cmake @@ -0,0 +1,33 @@ +set(CPM_DOWNLOAD_VERSION 0.38.1) + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +function(download_cpm) + message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") + file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} + ) +endfunction() + +if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) + download_cpm() +else() + # resume download if it previously failed + file(READ ${CPM_DOWNLOAD_LOCATION} check) + if("${check}" STREQUAL "") + download_cpm() + endif() + unset(check) +endif() + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/cmake/Cache.cmake b/cmake/Cache.cmake new file mode 100644 index 0000000..9d5283c --- /dev/null +++ b/cmake/Cache.cmake @@ -0,0 +1,33 @@ +# Enable cache if available +function(json2cpp_enable_cache) + set(CACHE_OPTION + "ccache" + CACHE STRING "Compiler cache to be used") + set(CACHE_OPTION_VALUES "ccache" "sccache") + set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) + list( + FIND + CACHE_OPTION_VALUES + ${CACHE_OPTION} + CACHE_OPTION_INDEX) + + if(${CACHE_OPTION_INDEX} EQUAL -1) + message( + STATUS + "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are ${CACHE_OPTION_VALUES}" + ) + endif() + + find_program(CACHE_BINARY NAMES ${CACHE_OPTION_VALUES}) + if(CACHE_BINARY) + message(STATUS "${CACHE_BINARY} found and enabled") + set(CMAKE_CXX_COMPILER_LAUNCHER + ${CACHE_BINARY} + CACHE FILEPATH "CXX compiler cache used") + set(CMAKE_C_COMPILER_LAUNCHER + ${CACHE_BINARY} + CACHE FILEPATH "C compiler cache used") + else() + message(WARNING "${CACHE_OPTION} is enabled but was not found. Not using it") + endif() +endfunction() diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 0000000..af30c14 --- /dev/null +++ b/cmake/CompilerWarnings.cmake @@ -0,0 +1,115 @@ +# from here: +# +# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md + +function( + json2cpp_set_project_warnings + project_name + WARNINGS_AS_ERRORS + MSVC_WARNINGS + CLANG_WARNINGS + GCC_WARNINGS + CUDA_WARNINGS) + if("${MSVC_WARNINGS}" STREQUAL "") + set(MSVC_WARNINGS + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + /permissive- # standards conformance mode for MSVC compiler. + ) + endif() + + if("${CLANG_WARNINGS}" STREQUAL "") + set(CLANG_WARNINGS + -Wall + -Wextra # reasonable and standard + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + -Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output (ie printf) + -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation + ) + endif() + + if("${GCC_WARNINGS}" STREQUAL "") + set(GCC_WARNINGS + ${CLANG_WARNINGS} + -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist + -Wduplicated-cond # warn if if / else chain has duplicated conditions + -Wduplicated-branches # warn if if / else branches have duplicated code + -Wlogical-op # warn about logical operations being used where bitwise were probably wanted + -Wuseless-cast # warn if you perform a cast to the same type + ) + endif() + + if("${CUDA_WARNINGS}" STREQUAL "") + set(CUDA_WARNINGS + -Wall + -Wextra + -Wunused + -Wconversion + -Wshadow + # TODO add more Cuda warnings + ) + endif() + + if(WARNINGS_AS_ERRORS) + message(TRACE "Warnings are treated as errors") + list(APPEND CLANG_WARNINGS -Werror) + list(APPEND GCC_WARNINGS -Werror) + list(APPEND MSVC_WARNINGS /WX) + endif() + + if(MSVC) + set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) + else() + message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") + # TODO support Intel compiler + endif() + + # use the same warning flags for C + set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") + + set(PROJECT_WARNINGS_CUDA "${CUDA_WARNINGS}") + + target_compile_options( + ${project_name} + INTERFACE # C++ warnings + $<$:${PROJECT_WARNINGS_CXX}> + # C warnings + $<$:${PROJECT_WARNINGS_C}> + # Cuda warnings + $<$:${PROJECT_WARNINGS_CUDA}>) +endfunction() diff --git a/cmake/Cuda.cmake b/cmake/Cuda.cmake new file mode 100644 index 0000000..2aff318 --- /dev/null +++ b/cmake/Cuda.cmake @@ -0,0 +1,48 @@ +# ! target_link_cuda +# A function that links Cuda to the given target +# +# # Example +# add_executable(main_cuda main.cu) +# target_compile_features(main_cuda PRIVATE cxx_std_17) +# target_link_libraries(main_cuda PRIVATE project_options project_warnings) +# target_link_cuda(main_cuda) +# +macro(json2cpp_target_link_cuda target) + # optional named CUDA_WARNINGS + set(oneValueArgs CUDA_WARNINGS) + cmake_parse_arguments( + _cuda_args + "" + "${oneValueArgs}" + "" + ${ARGN}) + + # add CUDA to cmake language + enable_language(CUDA) + + # use the same C++ standard if not specified + if("${CMAKE_CUDA_STANDARD}" STREQUAL "") + set(CMAKE_CUDA_STANDARD "${CMAKE_CXX_STANDARD}") + endif() + + # -fPIC + set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE ON) + + # We need to explicitly state that we need all CUDA files in the + # ${target} library to be built with -dc as the member functions + # could be called by other libraries and executables + set_target_properties(${target} PROPERTIES CUDA_SEPARABLE_COMPILATION ON) + + if(APPLE) + # We need to add the path to the driver (libcuda.dylib) as an rpath, + # so that the static cuda runtime can find it at runtime. + set_property(TARGET ${target} PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) + endif() + + if(WIN32 AND "$ENV{VSCMD_VER}" STREQUAL "") + message( + WARNING + "Compiling Cuda on Windows outside the Visual Studio Commant prompt or without running `vcvarsall.bat x64` probably fails" + ) + endif() +endmacro() diff --git a/cmake/Doxygen.cmake b/cmake/Doxygen.cmake new file mode 100644 index 0000000..7a75f14 --- /dev/null +++ b/cmake/Doxygen.cmake @@ -0,0 +1,54 @@ +# Enable doxygen doc builds of source +function(json2cpp_enable_doxygen DOXYGEN_THEME) + # If not specified, use the top readme file as the first page + if((NOT DOXYGEN_USE_MDFILE_AS_MAINPAGE) AND EXISTS "${PROJECT_SOURCE_DIR}/README.md") + set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "${PROJECT_SOURCE_DIR}/README.md") + endif() + + # set better defaults for doxygen + is_verbose(_is_verbose) + if(NOT ${_is_verbose}) + set(DOXYGEN_QUIET YES) + endif() + set(DOXYGEN_CALLER_GRAPH YES) + set(DOXYGEN_CALL_GRAPH YES) + set(DOXYGEN_EXTRACT_ALL YES) + set(DOXYGEN_GENERATE_TREEVIEW YES) + # svg files are much smaller than jpeg and png, and yet they have higher quality + set(DOXYGEN_DOT_IMAGE_FORMAT svg) + set(DOXYGEN_DOT_TRANSPARENT YES) + + # If not specified, exclude the vcpkg files and the files CMake downloads under _deps (like project_options) + if(NOT DOXYGEN_EXCLUDE_PATTERNS) + set(DOXYGEN_EXCLUDE_PATTERNS "${CMAKE_CURRENT_BINARY_DIR}/vcpkg_installed/*" "${CMAKE_CURRENT_BINARY_DIR}/_deps/*") + endif() + + if("${DOXYGEN_THEME}" STREQUAL "") + set(DOXYGEN_THEME "awesome-sidebar") + endif() + + if("${DOXYGEN_THEME}" STREQUAL "awesome" OR "${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") + # use a modern doxygen theme + # https://github.com/jothepro/doxygen-awesome-css v1.6.1 + FetchContent_Declare(_doxygen_theme + URL https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v1.6.1.zip) + FetchContent_MakeAvailable(_doxygen_theme) + if("${DOXYGEN_THEME}" STREQUAL "awesome" OR "${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") + set(DOXYGEN_HTML_EXTRA_STYLESHEET "${_doxygen_theme_SOURCE_DIR}/doxygen-awesome.css") + endif() + if("${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") + set(DOXYGEN_HTML_EXTRA_STYLESHEET ${DOXYGEN_HTML_EXTRA_STYLESHEET} + "${_doxygen_theme_SOURCE_DIR}/doxygen-awesome-sidebar-only.css") + endif() + else() + # use the original doxygen theme + endif() + + # find doxygen and dot if available + find_package(Doxygen REQUIRED OPTIONAL_COMPONENTS dot) + + # add doxygen-docs target + message(STATUS "Adding `doxygen-docs` target that builds the documentation.") + doxygen_add_docs(doxygen-docs ALL ${PROJECT_SOURCE_DIR} + COMMENT "Generating documentation - entry file: ${CMAKE_CURRENT_BINARY_DIR}/html/index.html") +endfunction() diff --git a/cmake/Hardening.cmake b/cmake/Hardening.cmake new file mode 100644 index 0000000..acbaa18 --- /dev/null +++ b/cmake/Hardening.cmake @@ -0,0 +1,91 @@ +include(CheckCXXCompilerFlag) + +macro( + json2cpp_enable_hardening + target + global + ubsan_minimal_runtime) + + message(STATUS "** Enabling Hardening (Target ${target}) **") + + if(MSVC) + set(NEW_COMPILE_OPTIONS + "${NEW_COMPILE_OPTIONS} /sdl /DYNAMICBASE /guard:cf /NXCOMPAT") + message(STATUS "*** MSVC flags: /sdl /DYNAIMCBASE /guard:cf /NXCOMPAT") + + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang|GNU") + set(NEW_CXX_DEFINITIONS "${NEW_CXX_DEFINITIONS} -D_GLIBCXX_ASSERTIONS") + message(STATUS "*** GLIBC++ Assertions (vector[], string[], ...) enabled") + + set(NEW_COMPILE_OPTIONS + "${NEW_COMPILE_OPTIONS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3") + message(STATUS "*** g++/clang _FORTIFY_SOURCE=3 enabled") + + # check_cxx_compiler_flag(-fpie PIE) + #if(PIE) + # set(NEW_COMPILE_OPTIONS ${NEW_COMPILE_OPTIONS} -fpie) + # set(NEW_LINK_OPTIONS ${NEW_LINK_OPTIONS} -pie) + # + # message(STATUS "*** g++/clang PIE mode enabled") + #else() + # message(STATUS "*** g++/clang PIE mode NOT enabled (not supported)") + #endif() + + check_cxx_compiler_flag(-fstack-protector-strong STACK_PROTECTOR) + if(STACK_PROTECTOR) + set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -fstack-protector-strong") + message(STATUS "*** g++/clang -fstack-protector-strong enabled") + else() + message(STATUS "*** g++/clang -fstack-protector-strong NOT enabled (not supported)") + endif() + + check_cxx_compiler_flag(-fcf-protection CF_PROTECTION) + if(CF_PROTECTION) + set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -fcf-protection") + message(STATUS "*** g++/clang -fcf-protection enabled") + else() + message(STATUS "*** g++/clang -fcf-protection NOT enabled (not supported)") + endif() + + check_cxx_compiler_flag(-fstack-clash-protection CLASH_PROTECTION) + if(CLASH_PROTECTION) + if(LINUX OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -fstack-clash-protection") + message(STATUS "*** g++/clang -fstack-clash-protection enabled") + else() + message(STATUS "*** g++/clang -fstack-clash-protection NOT enabled (clang on non-Linux)") + endif() + else() + message(STATUS "*** g++/clang -fstack-clash-protection NOT enabled (not supported)") + endif() + endif() + + if(${ubsan_minimal_runtime}) + check_cxx_compiler_flag("-fsanitize=undefined -fno-sanitize-recover=undefined -fsanitize-minimal-runtime" + MINIMAL_RUNTIME) + if(MINIMAL_RUNTIME) + set(NEW_COMPILE_OPTIONS + "${NEW_COMPILE_OPTIONS} -fsanitize=undefined -fno-sanitize-recover=undefined -fsanitize-minimal-runtime") + message(STATUS "*** ubsan minimal runtime enabled") + else() + message(STATUS "*** ubsan minimal runtime NOT enabled (not supported)") + endif() + else() + message(STATUS "*** ubsan minimal runtime NOT enabled (not requested)") + endif() + + message(STATUS "** Hardening Compiler Flags: ${NEW_COMPILE_OPTIONS}") + message(STATUS "** Hardening Linker Flags: ${NEW_LINK_OPTIONS}") + message(STATUS "** Hardening Compiler Defines: ${NEW_CXX_DEFINITIONS}") + + if(${global}) + message(STATUS "** Setting hardening options globally for all dependencies") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NEW_COMPILE_OPTIONS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${NEW_LINK_OPTIONS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NEW_CXX_DEFINITIONS}") + else() + target_compile_options(${target} INTERFACE ${NEW_COMPILE_OPTIONS}) + target_link_options(${target} INTERFACE ${NEW_LINK_OPTIONS}) + target_compile_definitions(${target} INTERFACE ${NEW_CXX_DEFINITIONS}) + endif() +endmacro() diff --git a/cmake/InterproceduralOptimization.cmake b/cmake/InterproceduralOptimization.cmake new file mode 100644 index 0000000..cc051c8 --- /dev/null +++ b/cmake/InterproceduralOptimization.cmake @@ -0,0 +1,9 @@ +macro(json2cpp_enable_ipo) + include(CheckIPOSupported) + check_ipo_supported(RESULT result OUTPUT output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) + else() + message(SEND_ERROR "IPO is not supported: ${output}") + endif() +endmacro() diff --git a/cmake/LibFuzzer.cmake b/cmake/LibFuzzer.cmake new file mode 100644 index 0000000..2932f82 --- /dev/null +++ b/cmake/LibFuzzer.cmake @@ -0,0 +1,17 @@ +function(json2cpp_check_libfuzzer_support var_name) + set(LibFuzzerTestSource + " +#include + +extern \"C\" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { + return 0; +} + ") + + include(CheckCXXSourceCompiles) + + set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer") + set(CMAKE_REQUIRED_LINK_OPTIONS "-fsanitize=fuzzer") + check_cxx_source_compiles("${LibFuzzerTestSource}" ${var_name}) + +endfunction() diff --git a/cmake/Linker.cmake b/cmake/Linker.cmake new file mode 100644 index 0000000..604dec0 --- /dev/null +++ b/cmake/Linker.cmake @@ -0,0 +1,31 @@ +macro(json2cpp_configure_linker project_name) + include(CheckCXXCompilerFlag) + + set(USER_LINKER_OPTION + "lld" + CACHE STRING "Linker to be used") + set(USER_LINKER_OPTION_VALUES "lld" "gold" "bfd" "mold") + set_property(CACHE USER_LINKER_OPTION PROPERTY STRINGS ${USER_LINKER_OPTION_VALUES}) + list( + FIND + USER_LINKER_OPTION_VALUES + ${USER_LINKER_OPTION} + USER_LINKER_OPTION_INDEX) + + if(${USER_LINKER_OPTION_INDEX} EQUAL -1) + message( + STATUS + "Using custom linker: '${USER_LINKER_OPTION}', explicitly supported entries are ${USER_LINKER_OPTION_VALUES}") + endif() + + if(NOT ENABLE_USER_LINKER) + return() + endif() + + set(LINKER_FLAG "-fuse-ld=${USER_LINKER_OPTION}") + + check_cxx_compiler_flag(${LINKER_FLAG} CXX_SUPPORTS_USER_LINKER) + if(CXX_SUPPORTS_USER_LINKER) + target_compile_options(${project_name} INTERFACE ${LINKER_FLAG}) + endif() +endmacro() diff --git a/cmake/PackageProject.cmake b/cmake/PackageProject.cmake new file mode 100644 index 0000000..22a5c5d --- /dev/null +++ b/cmake/PackageProject.cmake @@ -0,0 +1,190 @@ +# Uses ycm (permissive BSD-3-Clause license) and ForwardArguments (permissive MIT license) + +function(json2cpp_package_project) + cmake_policy(SET CMP0103 NEW) # disallow multiple calls with the same NAME + + set(_options ARCH_INDEPENDENT # default to false + ) + set(_oneValueArgs + # default to the project_name: + NAME + COMPONENT + # default to project version: + VERSION + # default to semver + COMPATIBILITY + # default to ${CMAKE_BINARY_DIR} + CONFIG_EXPORT_DESTINATION + # default to ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${NAME} suitable for vcpkg, etc. + CONFIG_INSTALL_DESTINATION) + set(_multiValueArgs + # recursively found for the current folder if not specified + TARGETS + # a list of public/interface include directories or files + PUBLIC_INCLUDES + # the names of the INTERFACE/PUBLIC dependencies that are found using `CONFIG` + PUBLIC_DEPENDENCIES_CONFIGURED + # the INTERFACE/PUBLIC dependencies that are found by any means using `find_dependency`. + # the arguments must be specified within double quotes (e.g. " 1.0.0 EXACT" or " CONFIG"). + PUBLIC_DEPENDENCIES + # the names of the PRIVATE dependencies that are found using `CONFIG`. Only included when BUILD_SHARED_LIBS is OFF. + PRIVATE_DEPENDENCIES_CONFIGURED + # PRIVATE dependencies that are only included when BUILD_SHARED_LIBS is OFF + PRIVATE_DEPENDENCIES) + + cmake_parse_arguments( + _PackageProject + "${_options}" + "${_oneValueArgs}" + "${_multiValueArgs}" + "${ARGN}") + + # Set default options + include(GNUInstallDirs) # Define GNU standard installation directories such as CMAKE_INSTALL_DATADIR + + # set default packaged targets + if(NOT _PackageProject_TARGETS) + get_all_installable_targets(_PackageProject_TARGETS) + message(STATUS "package_project: considering ${_PackageProject_TARGETS} as the exported targets") + endif() + + # default to the name of the project or the given name + if("${_PackageProject_NAME}" STREQUAL "") + set(_PackageProject_NAME ${PROJECT_NAME}) + endif() + # ycm args + set(_PackageProject_NAMESPACE "${_PackageProject_NAME}::") + set(_PackageProject_VARS_PREFIX ${_PackageProject_NAME}) + set(_PackageProject_EXPORT ${_PackageProject_NAME}) + + # default version to the project version + if("${_PackageProject_VERSION}" STREQUAL "") + set(_PackageProject_VERSION ${PROJECT_VERSION}) + endif() + + # default compatibility to SameMajorVersion + if("${_PackageProject_COMPATIBILITY}" STREQUAL "") + set(_PackageProject_COMPATIBILITY "SameMajorVersion") + endif() + + # default to the build directory + if("${_PackageProject_CONFIG_EXPORT_DESTINATION}" STREQUAL "") + set(_PackageProject_CONFIG_EXPORT_DESTINATION "${CMAKE_BINARY_DIR}") + endif() + set(_PackageProject_EXPORT_DESTINATION "${_PackageProject_CONFIG_EXPORT_DESTINATION}") + + # use datadir (works better with vcpkg, etc) + if("${_PackageProject_CONFIG_INSTALL_DESTINATION}" STREQUAL "") + set(_PackageProject_CONFIG_INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/${_PackageProject_NAME}") + endif() + # ycm args + set(_PackageProject_INSTALL_DESTINATION "${_PackageProject_CONFIG_INSTALL_DESTINATION}") + + # Installation of the public/interface includes + if(NOT + "${_PackageProject_PUBLIC_INCLUDES}" + STREQUAL + "") + foreach(_INC ${_PackageProject_PUBLIC_INCLUDES}) + # make include absolute + if(NOT IS_ABSOLUTE ${_INC}) + set(_INC "${CMAKE_CURRENT_SOURCE_DIR}/${_INC}") + endif() + # install include + if(IS_DIRECTORY ${_INC}) + # the include directories are directly installed to the install destination. If you want an `include` folder in the install destination, name your include directory as `include` (or install it manually using `install()` command). + install(DIRECTORY ${_INC} DESTINATION "./") + else() + install(FILES ${_INC} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + endif() + endforeach() + endif() + + # Append the configured public dependencies + if(NOT + "${_PackageProject_PUBLIC_DEPENDENCIES_CONFIGURED}" + STREQUAL + "") + set(_PUBLIC_DEPENDENCIES_CONFIG) + foreach(DEP ${_PackageProject_PUBLIC_DEPENDENCIES_CONFIGURED}) + list(APPEND _PUBLIC_DEPENDENCIES_CONFIG "${DEP} CONFIG") + endforeach() + endif() + list(APPEND _PackageProject_PUBLIC_DEPENDENCIES ${_PUBLIC_DEPENDENCIES_CONFIG}) + # ycm arg + set(_PackageProject_DEPENDENCIES ${_PackageProject_PUBLIC_DEPENDENCIES}) + + # Append the configured private dependencies + if(NOT + "${_PackageProject_PRIVATE_DEPENDENCIES_CONFIGURED}" + STREQUAL + "") + set(_PRIVATE_DEPENDENCIES_CONFIG) + foreach(DEP ${_PackageProject_PRIVATE_DEPENDENCIES_CONFIGURED}) + list(APPEND _PRIVATE_DEPENDENCIES_CONFIG "${DEP} CONFIG") + endforeach() + endif() + # ycm arg + list(APPEND _PackageProject_PRIVATE_DEPENDENCIES ${_PRIVATE_DEPENDENCIES_CONFIG}) + + # Installation of package (compatible with vcpkg, etc) + install( + TARGETS ${_PackageProject_TARGETS} + EXPORT ${_PackageProject_EXPORT} + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT lib + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_PackageProject_NAME}" COMPONENT dev) + + # install the usage file + set(_targets_str "") + foreach(_target ${_PackageProject_TARGETS}) + set(_targets_str "${_targets_str} ${_PackageProject_NAMESPACE}${_target}") + endforeach() + set(USAGE_FILE_CONTENT + "The package ${_PackageProject_NAME} provides CMake targets: + + find_package(${_PackageProject_NAME} CONFIG REQUIRED) + target_link_libraries(main PRIVATE ${_targets_str}) + ") + install(CODE "MESSAGE(STATUS \"${USAGE_FILE_CONTENT}\")") + file(WRITE "${_PackageProject_EXPORT_DESTINATION}/usage" "${USAGE_FILE_CONTENT}") + install(FILES "${_PackageProject_EXPORT_DESTINATION}/usage" + DESTINATION "${_PackageProject_CONFIG_INSTALL_DESTINATION}") + + unset(_PackageProject_TARGETS) + + # download ForwardArguments + FetchContent_Declare( + _fargs + URL https://github.com/polysquare/cmake-forward-arguments/archive/8c50d1f956172edb34e95efa52a2d5cb1f686ed2.zip) + FetchContent_GetProperties(_fargs) + if(NOT _fargs_POPULATED) + FetchContent_Populate(_fargs) + endif() + include("${_fargs_SOURCE_DIR}/ForwardArguments.cmake") + + # prepare the forward arguments for ycm + set(_FARGS_LIST) + cmake_forward_arguments( + _PackageProject + _FARGS_LIST + OPTION_ARGS + "${_options};" + SINGLEVAR_ARGS + "${_oneValueArgs};EXPORT_DESTINATION;INSTALL_DESTINATION;NAMESPACE;VARS_PREFIX;EXPORT" + MULTIVAR_ARGS + "${_multiValueArgs};DEPENDENCIES;PRIVATE_DEPENDENCIES") + + # download ycm + FetchContent_Declare(_ycm URL https://github.com/robotology/ycm/archive/refs/tags/v0.13.0.zip) + FetchContent_GetProperties(_ycm) + if(NOT _ycm_POPULATED) + FetchContent_Populate(_ycm) + endif() + include("${_ycm_SOURCE_DIR}/modules/InstallBasicPackageFiles.cmake") + + install_basic_package_files(${_PackageProject_NAME} "${_FARGS_LIST}") + + include("${_ycm_SOURCE_DIR}/modules/AddUninstallTarget.cmake") +endfunction() diff --git a/cmake/PreventInSourceBuilds.cmake b/cmake/PreventInSourceBuilds.cmake new file mode 100644 index 0000000..0af1d2b --- /dev/null +++ b/cmake/PreventInSourceBuilds.cmake @@ -0,0 +1,19 @@ +# +# This function will prevent in-source builds +# +function(json2cpp_assure_out_of_source_builds) + # make sure the user doesn't play dirty with symlinks + get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) + get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) + + # disallow in-source builds + if("${srcdir}" STREQUAL "${bindir}") + message("######################################################") + message("Warning: in-source builds are disabled") + message("Please create a separate build directory and run cmake from there") + message("######################################################") + message(FATAL_ERROR "Quitting configuration") + endif() +endfunction() + +json2cpp_assure_out_of_source_builds() diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake new file mode 100644 index 0000000..ad5fc61 --- /dev/null +++ b/cmake/Sanitizers.cmake @@ -0,0 +1,90 @@ +function( + json2cpp_enable_sanitizers + project_name + ENABLE_SANITIZER_ADDRESS + ENABLE_SANITIZER_LEAK + ENABLE_SANITIZER_UNDEFINED_BEHAVIOR + ENABLE_SANITIZER_THREAD + ENABLE_SANITIZER_MEMORY) + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(SANITIZERS "") + + if(${ENABLE_SANITIZER_ADDRESS}) + list(APPEND SANITIZERS "address") + endif() + + if(${ENABLE_SANITIZER_LEAK}) + list(APPEND SANITIZERS "leak") + endif() + + if(${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR}) + list(APPEND SANITIZERS "undefined") + endif() + + if(${ENABLE_SANITIZER_THREAD}) + if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) + message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") + else() + list(APPEND SANITIZERS "thread") + endif() + endif() + + if(${ENABLE_SANITIZER_MEMORY} AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + message( + WARNING + "Memory sanitizer requires all the code (including libc++) to be MSan-instrumented otherwise it reports false positives" + ) + if("address" IN_LIST SANITIZERS + OR "thread" IN_LIST SANITIZERS + OR "leak" IN_LIST SANITIZERS) + message(WARNING "Memory sanitizer does not work with Address, Thread or Leak sanitizer enabled") + else() + list(APPEND SANITIZERS "memory") + endif() + endif() + elseif(MSVC) + if(${ENABLE_SANITIZER_ADDRESS}) + list(APPEND SANITIZERS "address") + endif() + if(${ENABLE_SANITIZER_LEAK} + OR ${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} + OR ${ENABLE_SANITIZER_THREAD} + OR ${ENABLE_SANITIZER_MEMORY}) + message(WARNING "MSVC only supports address sanitizer") + endif() + endif() + + list( + JOIN + SANITIZERS + "," + LIST_OF_SANITIZERS) + + if(LIST_OF_SANITIZERS) + if(NOT + "${LIST_OF_SANITIZERS}" + STREQUAL + "") + if(NOT MSVC) + target_compile_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) + target_link_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) + else() + string(FIND "$ENV{PATH}" "$ENV{VSINSTALLDIR}" index_of_vs_install_dir) + if("${index_of_vs_install_dir}" STREQUAL "-1") + message( + SEND_ERROR + "Using MSVC sanitizers requires setting the MSVC environment before building the project. Please manually open the MSVC command prompt and rebuild the project." + ) + endif() + target_compile_options(${project_name} INTERFACE /fsanitize=${LIST_OF_SANITIZERS} /Zi /INCREMENTAL:NO) + target_compile_definitions(${project_name} INTERFACE _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION) + target_link_options(${project_name} INTERFACE /INCREMENTAL:NO) + endif() + endif() + endif() + +endfunction() + + + diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake new file mode 100644 index 0000000..b9f4123 --- /dev/null +++ b/cmake/StandardProjectSettings.cmake @@ -0,0 +1,45 @@ +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") + set(CMAKE_BUILD_TYPE + RelWithDebInfo + CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui, ccmake + set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS + "Debug" + "Release" + "MinSizeRel" + "RelWithDebInfo") +endif() + +# Generate compile_commands.json to make it easier to work with clang based tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Enhance error reporting and compiler messages +if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + if(WIN32) + # On Windows cuda nvcc uses cl and not clang + add_compile_options($<$:-fcolor-diagnostics> $<$:-fcolor-diagnostics>) + else() + add_compile_options(-fcolor-diagnostics) + endif() +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(WIN32) + # On Windows cuda nvcc uses cl and not gcc + add_compile_options($<$:-fdiagnostics-color=always> + $<$:-fdiagnostics-color=always>) + else() + add_compile_options(-fdiagnostics-color=always) + endif() +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER 1900) + add_compile_options(/diagnostics:column) +else() + message(STATUS "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler.") +endif() + + +# run vcvarsall when msvc is used +include("${CMAKE_CURRENT_LIST_DIR}/VCEnvironment.cmake") +run_vcvarsall() diff --git a/cmake/StaticAnalyzers.cmake b/cmake/StaticAnalyzers.cmake new file mode 100644 index 0000000..954104d --- /dev/null +++ b/cmake/StaticAnalyzers.cmake @@ -0,0 +1,109 @@ +macro(json2cpp_enable_cppcheck WARNINGS_AS_ERRORS CPPCHECK_OPTIONS) + find_program(CPPCHECK cppcheck) + if(CPPCHECK) + + if(CMAKE_GENERATOR MATCHES ".*Visual Studio.*") + set(CPPCHECK_TEMPLATE "vs") + else() + set(CPPCHECK_TEMPLATE "gcc") + endif() + + if("${CPPCHECK_OPTIONS}" STREQUAL "") + # Enable all warnings that are actionable by the user of this toolset + # style should enable the other 3, but we'll be explicit just in case + set(CMAKE_CXX_CPPCHECK + ${CPPCHECK} + --template=${CPPCHECK_TEMPLATE} + --enable=style,performance,warning,portability + --inline-suppr + # We cannot act on a bug/missing feature of cppcheck + --suppress=cppcheckError + --suppress=internalAstError + # if a file does not have an internalAstError, we get an unmatchedSuppression error + --suppress=unmatchedSuppression + # noisy and incorrect sometimes + --suppress=passedByValue + # ignores code that cppcheck thinks is invalid C++ + --suppress=syntaxError + --suppress=preprocessorErrorDirective + --inconclusive) + else() + # if the user provides a CPPCHECK_OPTIONS with a template specified, it will override this template + set(CMAKE_CXX_CPPCHECK ${CPPCHECK} --template=${CPPCHECK_TEMPLATE} ${CPPCHECK_OPTIONS}) + endif() + + if(NOT + "${CMAKE_CXX_STANDARD}" + STREQUAL + "") + set(CMAKE_CXX_CPPCHECK ${CMAKE_CXX_CPPCHECK} --std=c++${CMAKE_CXX_STANDARD}) + endif() + if(${WARNINGS_AS_ERRORS}) + list(APPEND CMAKE_CXX_CPPCHECK --error-exitcode=2) + endif() + else() + message(${WARNING_MESSAGE} "cppcheck requested but executable not found") + endif() +endmacro() + +macro(json2cpp_enable_clang_tidy target WARNINGS_AS_ERRORS) + + find_program(CLANGTIDY clang-tidy) + if(CLANGTIDY) + if(NOT + CMAKE_CXX_COMPILER_ID + MATCHES + ".*Clang") + + get_target_property(TARGET_PCH ${target} INTERFACE_PRECOMPILE_HEADERS) + + if("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND") + get_target_property(TARGET_PCH ${target} PRECOMPILE_HEADERS) + endif() + + if(NOT ("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND")) + message( + SEND_ERROR + "clang-tidy cannot be enabled with non-clang compiler and PCH, clang-tidy fails to handle gcc's PCH file") + endif() + endif() + + # construct the clang-tidy command line + set(CLANG_TIDY_OPTIONS + ${CLANGTIDY} + -extra-arg=-Wno-unknown-warning-option + -extra-arg=-Wno-ignored-optimization-argument + -extra-arg=-Wno-unused-command-line-argument + -p) + # set standard + if(NOT + "${CMAKE_CXX_STANDARD}" + STREQUAL + "") + if("${CLANG_TIDY_OPTIONS_DRIVER_MODE}" STREQUAL "cl") + set(CLANG_TIDY_OPTIONS ${CLANG_TIDY_OPTIONS} -extra-arg=/std:c++${CMAKE_CXX_STANDARD}) + else() + set(CLANG_TIDY_OPTIONS ${CLANG_TIDY_OPTIONS} -extra-arg=-std=c++${CMAKE_CXX_STANDARD}) + endif() + endif() + + # set warnings as errors + if(${WARNINGS_AS_ERRORS}) + list(APPEND CLANG_TIDY_OPTIONS -warnings-as-errors=*) + endif() + + message("Also setting clang-tidy globally") + set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_OPTIONS}) + else() + message(${WARNING_MESSAGE} "clang-tidy requested but executable not found") + endif() +endmacro() + +macro(json2cpp_enable_include_what_you_use) + find_program(INCLUDE_WHAT_YOU_USE include-what-you-use) + if(INCLUDE_WHAT_YOU_USE) + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) + else() + message(${WARNING_MESSAGE} "include-what-you-use requested but executable not found") + endif() +endmacro() diff --git a/cmake/SystemLink.cmake b/cmake/SystemLink.cmake new file mode 100644 index 0000000..000a9ad --- /dev/null +++ b/cmake/SystemLink.cmake @@ -0,0 +1,83 @@ +# Include a system directory (which suppresses its warnings). +function(target_include_system_directories target) + set(multiValueArgs INTERFACE PUBLIC PRIVATE) + cmake_parse_arguments( + ARG + "" + "" + "${multiValueArgs}" + ${ARGN}) + + foreach(scope IN ITEMS INTERFACE PUBLIC PRIVATE) + foreach(lib_include_dirs IN LISTS ARG_${scope}) + if(NOT MSVC) + # system includes do not work in MSVC + # awaiting https://gitlab.kitware.com/cmake/cmake/-/issues/18272# + # awaiting https://gitlab.kitware.com/cmake/cmake/-/issues/17904 + set(_SYSTEM SYSTEM) + endif() + if(${scope} STREQUAL "INTERFACE" OR ${scope} STREQUAL "PUBLIC") + target_include_directories( + ${target} + ${_SYSTEM} + ${scope} + "$" + "$") + else() + target_include_directories( + ${target} + ${_SYSTEM} + ${scope} + ${lib_include_dirs}) + endif() + endforeach() + endforeach() + +endfunction() + +# Include the directories of a library target as system directories (which suppresses their warnings). +function( + target_include_system_library + target + scope + lib) + # check if this is a target + if(TARGET ${lib}) + get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES) + if(lib_include_dirs) + target_include_system_directories(${target} ${scope} ${lib_include_dirs}) + else() + message(TRACE "${lib} library does not have the INTERFACE_INCLUDE_DIRECTORIES property.") + endif() + endif() +endfunction() + +# Link a library target as a system library (which suppresses its warnings). +function( + target_link_system_library + target + scope + lib) + # Include the directories in the library + target_include_system_library(${target} ${scope} ${lib}) + + # Link the library + target_link_libraries(${target} ${scope} ${lib}) +endfunction() + +# Link multiple library targets as system libraries (which suppresses their warnings). +function(target_link_system_libraries target) + set(multiValueArgs INTERFACE PUBLIC PRIVATE) + cmake_parse_arguments( + ARG + "" + "" + "${multiValueArgs}" + ${ARGN}) + + foreach(scope IN ITEMS INTERFACE PUBLIC PRIVATE) + foreach(lib IN LISTS ARG_${scope}) + target_link_system_library(${target} ${scope} ${lib}) + endforeach() + endforeach() +endfunction() diff --git a/cmake/Tests.cmake b/cmake/Tests.cmake new file mode 100644 index 0000000..6ac64e5 --- /dev/null +++ b/cmake/Tests.cmake @@ -0,0 +1,6 @@ +function(json2cpp_enable_coverage project_name) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + target_compile_options(${project_name} INTERFACE --coverage -O0 -g) + target_link_libraries(${project_name} INTERFACE --coverage) + endif() +endfunction() diff --git a/cmake/Utilities.cmake b/cmake/Utilities.cmake new file mode 100644 index 0000000..6fa78b2 --- /dev/null +++ b/cmake/Utilities.cmake @@ -0,0 +1,139 @@ +# find a subtring from a string by a given prefix such as VCVARSALL_ENV_START +function( + find_substring_by_prefix + output + prefix + input) + # find the prefix + string(FIND "${input}" "${prefix}" prefix_index) + if("${prefix_index}" STREQUAL "-1") + message(SEND_ERROR "Could not find ${prefix} in ${input}") + endif() + # find the start index + string(LENGTH "${prefix}" prefix_length) + math(EXPR start_index "${prefix_index} + ${prefix_length}") + + string( + SUBSTRING "${input}" + "${start_index}" + "-1" + _output) + set("${output}" + "${_output}" + PARENT_SCOPE) +endfunction() + +# A function to set environment variables of CMake from the output of `cmd /c set` +function(set_env_from_string env_string) + # replace ; in paths with __sep__ so we can split on ; + string( + REGEX + REPLACE ";" + "__sep__" + env_string_sep_added + "${env_string}") + + # the variables are separated by \r?\n + string( + REGEX + REPLACE "\r?\n" + ";" + env_list + "${env_string_sep_added}") + + foreach(env_var ${env_list}) + # split by = + string( + REGEX + REPLACE "=" + ";" + env_parts + "${env_var}") + + list(LENGTH env_parts env_parts_length) + if("${env_parts_length}" EQUAL "2") + # get the variable name and value + list( + GET + env_parts + 0 + env_name) + list( + GET + env_parts + 1 + env_value) + + # recover ; in paths + string( + REGEX + REPLACE "__sep__" + ";" + env_value + "${env_value}") + + # set env_name to env_value + set(ENV{${env_name}} "${env_value}") + + # update cmake program path + if("${env_name}" EQUAL "PATH") + list(APPEND CMAKE_PROGRAM_PATH ${env_value}) + endif() + endif() + endforeach() +endfunction() + +function(get_all_targets var) + set(targets) + get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR}) + set(${var} + ${targets} + PARENT_SCOPE) +endfunction() + +function(get_all_installable_targets var) + set(targets) + get_all_targets(targets) + foreach(_target ${targets}) + get_target_property(_target_type ${_target} TYPE) + if(NOT + ${_target_type} + MATCHES + ".*LIBRARY|EXECUTABLE") + list(REMOVE_ITEM targets ${_target}) + endif() + endforeach() + set(${var} + ${targets} + PARENT_SCOPE) +endfunction() + +macro(get_all_targets_recursive targets dir) + get_property( + subdirectories + DIRECTORY ${dir} + PROPERTY SUBDIRECTORIES) + foreach(subdir ${subdirectories}) + get_all_targets_recursive(${targets} ${subdir}) + endforeach() + + get_property( + current_targets + DIRECTORY ${dir} + PROPERTY BUILDSYSTEM_TARGETS) + list(APPEND ${targets} ${current_targets}) +endmacro() + +function(is_verbose var) + if("CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "VERBOSE" + OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "DEBUG" + OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "TRACE") + set(${var} + ON + PARENT_SCOPE) + else() + set(${var} + OFF + PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/VCEnvironment.cmake b/cmake/VCEnvironment.cmake new file mode 100644 index 0000000..a95cb46 --- /dev/null +++ b/cmake/VCEnvironment.cmake @@ -0,0 +1,71 @@ +include("${CMAKE_CURRENT_LIST_DIR}/Utilities.cmake") + +macro(detect_architecture) + # detect the architecture + string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" CMAKE_SYSTEM_PROCESSOR_LOWER) + if(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x86 OR CMAKE_SYSTEM_PROCESSOR_LOWER MATCHES "^i[3456]86$") + set(VCVARSALL_ARCH x86) + elseif( + CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x64 + OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x86_64 + OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL amd64) + set(VCVARSALL_ARCH x64) + elseif(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL arm) + set(VCVARSALL_ARCH arm) + elseif(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL arm64 OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL aarch64) + set(VCVARSALL_ARCH arm64) + else() + if(CMAKE_HOST_SYSTEM_PROCESSOR) + set(VCVARSALL_ARCH ${CMAKE_HOST_SYSTEM_PROCESSOR}) + else() + set(VCVARSALL_ARCH x64) + message(STATUS "Unkown architecture CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR_LOWER} - using x64") + endif() + endif() +endmacro() + +# Run vcvarsall.bat and set CMake environment variables +function(run_vcvarsall) + # if MSVC but VSCMD_VER is not set, which means vcvarsall has not run + if(MSVC AND "$ENV{VSCMD_VER}" STREQUAL "") + + # find vcvarsall.bat + get_filename_component(MSVC_DIR ${CMAKE_CXX_COMPILER} DIRECTORY) + find_file( + VCVARSALL_FILE + NAMES vcvarsall.bat + PATHS "${MSVC_DIR}" + "${MSVC_DIR}/.." + "${MSVC_DIR}/../.." + "${MSVC_DIR}/../../../../../../../.." + "${MSVC_DIR}/../../../../../../.." + PATH_SUFFIXES "VC/Auxiliary/Build" "Common7/Tools" "Tools") + + if(EXISTS ${VCVARSALL_FILE}) + # detect the architecture + detect_architecture() + + # run vcvarsall and print the environment variables + message(STATUS "Running `${VCVARSALL_FILE} ${VCVARSALL_ARCH}` to set up the MSVC environment") + execute_process( + COMMAND + "cmd" "/c" ${VCVARSALL_FILE} ${VCVARSALL_ARCH} # + "&&" "call" "echo" "VCVARSALL_ENV_START" # + "&" "set" # + OUTPUT_VARIABLE VCVARSALL_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # parse the output and get the environment variables string + find_substring_by_prefix(VCVARSALL_ENV "VCVARSALL_ENV_START" "${VCVARSALL_OUTPUT}") + + # set the environment variables + set_env_from_string("${VCVARSALL_ENV}") + + else() + message( + WARNING + "Could not find `vcvarsall.bat` for automatic MSVC environment preparation. Please manually open the MSVC command prompt and rebuild the project. + ") + endif() + endif() +endfunction() diff --git a/cmake/_FORTIFY_SOURCE.hpp b/cmake/_FORTIFY_SOURCE.hpp new file mode 100644 index 0000000..f513278 --- /dev/null +++ b/cmake/_FORTIFY_SOURCE.hpp @@ -0,0 +1,8 @@ +#ifdef _FORTIFY_SOURCE +#if _FORTIFY_SOURCE < 3 +#undef _FORTIFY_SOURCE +#define _FORTIFY_SOURCE 3 +#endif +#else +#define _FORTIFY_SOURCE 3 +#endif diff --git a/conanfile.txt b/conanfile.txt deleted file mode 100644 index b60b4e3..0000000 --- a/conanfile.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Docs at https://docs.conan.io/en/latest/reference/conanfile_txt.html - -[requires] -catch2/2.13.8 -docopt.cpp/0.6.3 -fmt/8.1.1 -spdlog/1.9.2 -nlohmann_json/3.10.5 -valijson/0.6 -# imgui-sfml/2.5@bincrafters/stable -# sdl2/2.0.1 - -[generators] -cmake_find_package_multi diff --git a/fuzz_test/CMakeLists.txt b/fuzz_test/CMakeLists.txt index 750d757..990dbe9 100644 --- a/fuzz_test/CMakeLists.txt +++ b/fuzz_test/CMakeLists.txt @@ -6,8 +6,8 @@ find_package(fmt) add_executable(fuzz_tester fuzz_tester.cpp) target_link_libraries( fuzz_tester - PRIVATE project_options - project_warnings + PRIVATE json2cpp::json2cpp_options + json2cpp::json2cpp_warnings fmt::fmt -coverage -fsanitize=fuzzer,undefined,address) diff --git a/include/json2cpp/json2cpp.hpp b/include/json2cpp/json2cpp.hpp index 33d47f6..f66f29c 100644 --- a/include/json2cpp/json2cpp.hpp +++ b/include/json2cpp/json2cpp.hpp @@ -103,7 +103,18 @@ template struct data_variant constexpr explicit value_t(std::nullptr_t) : null_{} {} }; - enum struct selected_type { empty, boolean, binary, array, object, integer, uinteger, floating_point, string, nullish }; + enum struct selected_type { + empty, + boolean, + binary, + array, + object, + integer, + uinteger, + floating_point, + string, + nullish + }; value_t value{ monostate{} }; selected_type selected{ selected_type::empty }; diff --git a/include/json2cpp/json2cpp_adapter.hpp b/include/json2cpp/json2cpp_adapter.hpp index b923a35..5d1e9a9 100644 --- a/include/json2cpp/json2cpp_adapter.hpp +++ b/include/json2cpp/json2cpp_adapter.hpp @@ -53,10 +53,10 @@ SOFTWARE. #include #include -#include -#include -#include #include +#include +#include +#include namespace valijson { namespace adapters { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc83906..e97b5ab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,23 +1,20 @@ -find_package(fmt CONFIG) -find_package(spdlog CONFIG) -find_package(docopt CONFIG) -find_package(nlohmann_json CONFIG) -find_package(ValiJSON CONFIG) - # Generic test that uses conan libs add_executable(json2cpp main.cpp json2cpp.cpp) -target_link_libraries( +add_executable(json2cpp::json2cpp ALIAS json2cpp) +target_link_libraries(json2cpp PRIVATE json2cpp_options json2cpp_warnings) + +target_link_system_libraries( json2cpp - PRIVATE project_options - project_warnings - docopt::docopt - fmt::fmt - spdlog::spdlog - nlohmann_json::nlohmann_json) + PRIVATE + CLI11::CLI11 + fmt::fmt + spdlog::spdlog + nlohmann_json::nlohmann_json) + install(TARGETS json2cpp) install(DIRECTORY ../include DESTINATION .) -if(ENABLE_LARGE_TESTS) +if(json2cpp_ENABLE_LARGE_TESTS) set(BASE_NAME "${CMAKE_CURRENT_BINARY_DIR}/schema") add_custom_command( DEPENDS json2cpp @@ -26,15 +23,16 @@ if(ENABLE_LARGE_TESTS) WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") add_executable(schema_validator schema_validator.cpp "${BASE_NAME}.cpp") - target_link_libraries( + add_executable(json2cpp::schema_validator ALIAS schema_validator) + target_link_libraries(schema_validator PRIVATE json2cpp_options json2cpp_warnings) + target_link_system_libraries( schema_validator - PRIVATE project_options - project_warnings - docopt::docopt - fmt::fmt - spdlog::spdlog - ValiJSON::valijson - nlohmann_json::nlohmann_json) + PRIVATE + CLI11::CLI11 + fmt::fmt + spdlog::spdlog + ValiJSON::valijson + nlohmann_json::nlohmann_json) target_include_directories(schema_validator PRIVATE "${CMAKE_SOURCE_DIR}/include") target_include_directories(schema_validator PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/src/main.cpp b/src/main.cpp index 76e7895..12fc259 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,39 +28,27 @@ SOFTWARE. #include #include "json2cpp.hpp" -#include +#include #include -static constexpr auto USAGE = - R"(json2cpp - - Copyright 2022 Jason Turner - - Usage: - json2cpp - json2cpp (-h | --help) - json2cpp --version - Options: - -h --help Show this screen. - --version Show version. -)"; - - int main(int argc, const char **argv) { try { - std::map args = docopt::docopt(USAGE, - { std::next(argv), std::next(argv, argc) }, - true,// show help if requested - "json2cpp 0.0.1 Copyright 2022 Jason Turner");// version string + CLI::App app("json2cpp version 0.0.1"); - std::string document_name = args.at("").asString(); - std::filesystem::path filename = args.at("").asString(); - std::filesystem::path output_filename = args.at("").asString(); + std::string document_name; + std::filesystem::path input_file_name; + std::filesystem::path output_base_name; - compile_to(document_name, filename, output_filename); + bool show_version = false; + app.add_flag("--version", show_version, "Show version information"); + app.add_option("", document_name); + app.add_option("", input_file_name); + app.add_option("", output_base_name); + CLI11_PARSE(app, argc, argv); + compile_to(document_name, input_file_name, output_base_name); } catch (const std::exception &e) { spdlog::error("Unhandled exception in main: {}", e.what()); } diff --git a/src/schema_validator.cpp b/src/schema_validator.cpp index 6411f35..bc6becd 100644 --- a/src/schema_validator.cpp +++ b/src/schema_validator.cpp @@ -25,16 +25,16 @@ SOFTWARE. #include #include #ifdef __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif #include #ifdef __GNUC__ - #pragma GCC diagnostic pop +#pragma GCC diagnostic pop #endif #include -#include +#include #include #include @@ -45,23 +45,6 @@ SOFTWARE. #include "schema.hpp" -static constexpr auto USAGE = - R"(schema_validator - - Copyright 2022 Jason Turner - - Usage: - schema_validator [--internal] - schema_validator (-h | --help) - schema_validator --walk [--internal] - schema_validator --version - Options: - -h --help Show this screen. - --version Show version. - --internal Use internal schema - --walk Just walk the schema and count objects (perf test) -)"; - bool validate(const std::filesystem::path &schema_file_name, const std::filesystem::path &file_to_validate) { @@ -192,16 +175,27 @@ template void walk(const JSON &objects) int main(int argc, const char **argv) { try { - std::map args = docopt::docopt(USAGE, - { std::next(argv), std::next(argv, argc) }, - true,// show help if requested - "schema_validator 0.0.1 Copyright 2022 Jason Turner");// version string + CLI::App app("schema_validator version 0.0.1"); + + std::string document_name; + std::filesystem::path schema_file_name; + std::filesystem::path document_to_validate; + + bool do_walk = false; + bool internal = false; + bool show_version = false; + app.add_option("", schema_file_name); + auto *doc = app.add_option("", document_to_validate); + app.add_flag("--version", show_version, "Show version information"); + app.add_flag("--walk", do_walk, "Just walk the schema and count objects (perf test)")->excludes(doc); + app.add_flag("--internal", internal, "Use internal schema"); - if (args.at("--walk").asBool()) { - if (args.at("--internal").asBool()) { + CLI11_PARSE(app, argc, argv); + + if (do_walk) { + if (internal) { walk(compiled_json::energyplus_schema::get()); } else { - std::filesystem::path schema_file_name = args.at("").asString(); spdlog::info("Creating nlohmann::json object"); nlohmann::json schema; spdlog::info("Opening json file"); @@ -213,15 +207,10 @@ int main(int argc, const char **argv) return EXIT_SUCCESS; } - - std::filesystem::path schema = args.at("").asString(); - std::filesystem::path doc = args.at("").asString(); - - - if (args.at("--internal").asBool()) { - validate_internal(doc); + if (internal) { + validate_internal(document_to_validate); } else { - validate(schema, doc); + validate(schema_file_name, document_to_validate); } } catch (const std::exception &e) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ae381b6..73d698e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,12 +1,24 @@ -find_package(Catch2 REQUIRED) -find_package(ValiJSON REQUIRED) +cmake_minimum_required(VERSION 3.15...3.23) -include(CTest) -include(Catch) +project(CmakeConfigPackageTests LANGUAGES CXX) + +# ---- Test as standalone project the exported config package ---- + +if(PROJECT_IS_TOP_LEVEL OR TEST_INSTALLED_VERSION) + enable_testing() + + find_package(myproject CONFIG REQUIRED) # for intro, json2cpp_options, ... + + if(NOT TARGET myjson2cpp_options) + message(FATAL_ERROR "Requiered config package not found!") + return() # be strictly paranoid for Template Janitor github action! CK + endif() +endif() + +# ---- Dependencies ---- + +include(${Catch2_SOURCE_DIR}/extras/Catch.cmake) -add_library(catch_main OBJECT catch_main.cpp) -target_link_libraries(catch_main PUBLIC Catch2::Catch2) -target_link_libraries(catch_main PRIVATE project_options) set(BASE_NAME "${CMAKE_CURRENT_BINARY_DIR}/test_json") add_custom_command( @@ -19,7 +31,7 @@ add_executable(tests tests.cpp "${BASE_NAME}.cpp") target_include_directories(tests PRIVATE "${CMAKE_SOURCE_DIR}/include") target_include_directories(tests PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") -target_link_libraries(tests PRIVATE project_warnings project_options catch_main) +target_link_libraries(tests PRIVATE json2cpp_warnings json2cpp_options Catch2::Catch2WithMain) # automatically discover tests that are defined in catch based test files you can modify the unittests. Set TEST_PREFIX # to whatever you want, or use different for different binaries @@ -28,7 +40,7 @@ catch_discover_tests( TEST_PREFIX "unittests." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX @@ -71,9 +83,12 @@ target_include_directories(valijson_tests PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") target_link_libraries( valijson_tests - PRIVATE project_warnings - project_options - catch_main + PRIVATE json2cpp_warnings + json2cpp_options) + +target_link_system_libraries( + valijson_tests + PRIVATE Catch2::Catch2WithMain ValiJSON::valijson) # automatically discover tests that are defined in catch based test files you can modify the unittests. Set TEST_PREFIX @@ -83,7 +98,7 @@ catch_discover_tests( TEST_PREFIX "unittests." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX @@ -93,7 +108,8 @@ catch_discover_tests( # Add a file containing a set of constexpr tests add_executable(constexpr_tests constexpr_tests.cpp "${BASE_NAME}_impl.hpp") -target_link_libraries(constexpr_tests PRIVATE project_options project_warnings catch_main) +target_link_libraries(constexpr_tests PRIVATE json2cpp_options json2cpp_warnings Catch2::Catch2WithMain) + target_include_directories(constexpr_tests PRIVATE "${CMAKE_SOURCE_DIR}/include") target_include_directories(constexpr_tests PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") @@ -102,7 +118,7 @@ catch_discover_tests( TEST_PREFIX "constexpr." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX @@ -113,7 +129,7 @@ catch_discover_tests( # Disable the constexpr portion of the test, and build again this allows us to have an executable that we can debug when # things go wrong with the constexpr testing add_executable(relaxed_constexpr_tests constexpr_tests.cpp "${BASE_NAME}_impl.hpp") -target_link_libraries(relaxed_constexpr_tests PRIVATE project_options project_warnings catch_main) +target_link_libraries(relaxed_constexpr_tests PRIVATE json2cpp_options json2cpp_warnings Catch2::Catch2WithMain) target_compile_definitions(relaxed_constexpr_tests PRIVATE -DCATCH_CONFIG_RUNTIME_STATIC_REQUIRE) target_include_directories(relaxed_constexpr_tests PRIVATE "${CMAKE_SOURCE_DIR}/include") target_include_directories(relaxed_constexpr_tests PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") @@ -123,7 +139,7 @@ catch_discover_tests( TEST_PREFIX "relaxed_constexpr." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX @@ -131,9 +147,7 @@ catch_discover_tests( OUTPUT_SUFFIX .xml) - - -if(ENABLE_LARGE_TESTS) +if(json2cpp_ENABLE_LARGE_TESTS) set(BASE_NAME "${CMAKE_CURRENT_BINARY_DIR}/schema") add_custom_command( DEPENDS json2cpp @@ -143,7 +157,7 @@ if(ENABLE_LARGE_TESTS) # Add a file containing a set of constexpr_schema tests add_executable(constexpr_schema_tests constexpr_schema_tests.cpp "${BASE_NAME}_impl.hpp") - target_link_libraries(constexpr_schema_tests PRIVATE project_options project_warnings catch_main) + target_link_libraries(constexpr_schema_tests PRIVATE json2cpp_options json2cpp_warnings Catch2::Catch2WithMain) target_include_directories(constexpr_schema_tests PRIVATE "${CMAKE_SOURCE_DIR}/include") target_include_directories(constexpr_schema_tests PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") @@ -159,7 +173,7 @@ if(ENABLE_LARGE_TESTS) TEST_PREFIX "constexpr_schema." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX @@ -170,7 +184,7 @@ if(ENABLE_LARGE_TESTS) # Disable the constexpr_schema portion of the test, and build again this allows us to have an executable that we can debug when # things go wrong with the constexpr_schema testing add_executable(relaxed_constexpr_schema_tests constexpr_schema_tests.cpp "${BASE_NAME}_impl.hpp") - target_link_libraries(relaxed_constexpr_schema_tests PRIVATE project_options project_warnings catch_main) + target_link_libraries(relaxed_constexpr_schema_tests PRIVATE json2cpp_options json2cpp_warnings Catch2::Catch2WithMain) target_compile_definitions(relaxed_constexpr_schema_tests PRIVATE -DCATCH_CONFIG_RUNTIME_STATIC_REQUIRE) target_include_directories(relaxed_constexpr_schema_tests PRIVATE "${CMAKE_SOURCE_DIR}/include") target_include_directories(relaxed_constexpr_schema_tests PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") @@ -187,7 +201,7 @@ if(ENABLE_LARGE_TESTS) TEST_PREFIX "relaxed_constexpr_schema." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX diff --git a/test/catch_main.cpp b/test/catch_main.cpp index 28ceb8c..b831403 100644 --- a/test/catch_main.cpp +++ b/test/catch_main.cpp @@ -1,3 +1,3 @@ #define CATCH_CONFIG_MAIN// This tells the catch header to generate a main -#include +#include diff --git a/test/constexpr_schema_tests.cpp b/test/constexpr_schema_tests.cpp index a3c52eb..6440cb1 100644 --- a/test/constexpr_schema_tests.cpp +++ b/test/constexpr_schema_tests.cpp @@ -1,5 +1,5 @@ #include "schema_impl.hpp" -#include +#include #include TEST_CASE("Basic test of very large energyplus JSON schema") diff --git a/test/constexpr_tests.cpp b/test/constexpr_tests.cpp index 94d9b29..2eb1c38 100644 --- a/test/constexpr_tests.cpp +++ b/test/constexpr_tests.cpp @@ -1,5 +1,5 @@ #include "test_json_impl.hpp" -#include +#include TEST_CASE("Can read object size") diff --git a/test/tests.cpp b/test/tests.cpp index bcb8f18..e6d8135 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -1,5 +1,5 @@ #include "test_json.hpp" -#include +#include TEST_CASE("Can read object size") { diff --git a/test/valijson_tests.cpp b/test/valijson_tests.cpp index 517e029..80e25e5 100644 --- a/test/valijson_tests.cpp +++ b/test/valijson_tests.cpp @@ -1,13 +1,13 @@ #include "allof_integers_and_numbers.schema.hpp" #include "array_doubles_10_20_30_40.hpp" #include "array_integers_10_20_30_40.hpp" -#ifdef __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif -#include -#ifdef __GNUC__ - #pragma GCC diagnostic pop +#include +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop #endif #include #include @@ -26,7 +26,7 @@ TEST_CASE("Can load a valijson schema") // Parse JSON schema content using valijson Schema mySchema; SchemaParser parser; - json2cppJsonAdapter mySchemaAdapter(compiled_json::allof_integers_and_numbers_schema::get()); + const json2cppJsonAdapter mySchemaAdapter(compiled_json::allof_integers_and_numbers_schema::get()); CHECK_NOTHROW(parser.populateSchema(mySchemaAdapter, mySchema)); } @@ -42,11 +42,11 @@ TEST_CASE("Validation fails where expected") // Parse JSON schema content using valijson Schema mySchema; SchemaParser parser; - json2cppJsonAdapter mySchemaAdapter(compiled_json::allof_integers_and_numbers_schema::get()); + const json2cppJsonAdapter mySchemaAdapter(compiled_json::allof_integers_and_numbers_schema::get()); CHECK_NOTHROW(parser.populateSchema(mySchemaAdapter, mySchema)); Validator validator; - json2cppJsonAdapter myTargetAdapter(compiled_json::array_doubles_10_20_30_40::get()); + const json2cppJsonAdapter myTargetAdapter(compiled_json::array_doubles_10_20_30_40::get()); REQUIRE_FALSE(validator.validate(mySchema, myTargetAdapter, nullptr)); } @@ -63,11 +63,11 @@ TEST_CASE("Can validate a document") // Parse JSON schema content using valijson Schema mySchema; SchemaParser parser; - json2cppJsonAdapter mySchemaAdapter(compiled_json::allof_integers_and_numbers_schema::get()); + const json2cppJsonAdapter mySchemaAdapter(compiled_json::allof_integers_and_numbers_schema::get()); CHECK_NOTHROW(parser.populateSchema(mySchemaAdapter, mySchema)); Validator validator; - json2cppJsonAdapter myTargetAdapter(compiled_json::array_integers_10_20_30_40::get()); + const json2cppJsonAdapter myTargetAdapter(compiled_json::array_integers_10_20_30_40::get()); REQUIRE(validator.validate(mySchema, myTargetAdapter, nullptr)); }