diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..c7270da --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: lefticus +patreon: lefticus diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd72be7..01dfa78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ on: pull_request: release: types: [published] + push: tags: branches: @@ -10,10 +11,8 @@ on: - develop env: - # 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: @@ -34,17 +33,19 @@ jobs: - macos-10.15 - windows-2019 compiler: - # you can specify the version after `-` like "llvm-13.0.0". - - llvm-13.0.0 + # 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 + build_shared: + - OFF exclude: # mingw is determined by this author to be too buggy to support @@ -56,42 +57,99 @@ jobs: # if you try to use a compiler that does not have gcov set - compiler: gcc-11 gcov_executable: gcov - - compiler: llvm-13.0.0 + enable_ipo: On + + - compiler: llvm-15.0.2 + enable_ipo: Off gcov_executable: "llvm-cov gcov" + - os: macos-10.15 + enable_ipo: Off + enable_cppcheck: OFF + enable_clang_tidy: OFF + + - os: ubuntu-20.04 + enable_clang_tidy: On + enable_cppcheck: On + + # Set up preferred package generators, for given build configurations - build_type: Release - developer_mode: OFF + package_maintainer_mode: OFF package_generator: TBZ2 + # This exists solely to make sure a non-multiconfig build works + - os: ubuntu-20.04 + compiler: gcc-11 + generator: "Unix Makefiles" + build_type: Debug + gcov_executable: gcov + package_maintainer_mode: On + enable_ipo: Off + enable_clang_tidy: On + enable_cppcheck: On + + # Windows msvc builds - os: windows-2022 compiler: msvc generator: "Visual Studio 17 2022" build_type: Debug - developer_mode: On + package_maintainer_mode: On + enable_ipo: On + enable_clang_tidy: Off + enable_cppcheck: Off + + - os: windows-2022 compiler: msvc generator: "Visual Studio 17 2022" build_type: Release - developer_mode: On + package_maintainer_mode: On + enable_ipo: On + enable_clang_tidy: Off + enable_cppcheck: Off + + - os: windows-2022 compiler: msvc generator: "Visual Studio 17 2022" build_type: Debug - developer_mode: Off + package_maintainer_mode: Off + enable_clang_tidy: Off + enable_cppcheck: Off + + - os: windows-2022 compiler: msvc 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 - name: Setup Cache @@ -99,7 +157,7 @@ jobs: with: compiler: ${{ matrix.compiler }} build_type: ${{ matrix.build_type }} - developer_mode: ${{ matrix.developer_mode }} + package_maintainer_mode: ${{ matrix.package_maintainer_mode }} generator: ${{ matrix.generator }} - name: Setup Cpp @@ -110,25 +168,19 @@ jobs: cmake: true ninja: true - conan: true vcpkg: false ccache: true - clangtidy: true + clangtidy: ${{ env.CLANG_TIDY_VERSION }} + cppcheck: true - gcovr: 5.0 + gcovr: true opencppcoverage: true - - name: Cleanup Conan system packages (they are not properly cached) - run: | - conan remove -f '*/system' - - # 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}} -DENABLE_DEVELOPER_MODE:BOOL=${{matrix.developer_mode}} -DOPT_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} + cmake -S . -B ./build -G "${{matrix.generator}}" -Dtravels_ENABLE_CLANG_TIDY:BOOL=${{ matrix.enable_clang_tidy }} -Dtravels_ENABLE_CPPCHECK:BOOL=${{ matrix.enable_cppcheck }} -Dtravels_ENABLE_IPO=${{matrix.enable_ipo }} -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -Dtravels_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.package_maintainer_mode}} -Dtravels_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 " @@ -156,6 +208,15 @@ jobs: run: | cpack -C ${{matrix.build_type}} -G ${{matrix.package_generator}} + - name: Publish Snapshot Release + uses: softprops/action-gh-release@v1 + if: ${{ (github.ref == 'refs/heads/main') && matrix.package_generator != '' }} + with: + tag_name: "snapshot-${{ github.sha }}" + files: | + build/*-*${{ matrix.build_type }}*-*.* + + - name: Publish Tagged Release uses: softprops/action-gh-release@v1 if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.package_generator != '' }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 05955f2..b932d20 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -20,12 +20,6 @@ on: schedule: - cron: '38 0 * * 5' -env: - # Conan cache environment variables - CONAN_SYSREQUIRES_MODE: enabled - CONAN_USER_HOME: "${{ github.workspace }}/conan-cache" - CONAN_USER_HOME_SHORT: "${{ github.workspace }}/conan-cache/short" - jobs: analyze: @@ -49,8 +43,8 @@ jobs: - "Ninja Multi-Config" build_type: - Debug - developer_mode: - - OFF + packaging_maintainer_mode: + - ON steps: @@ -61,7 +55,7 @@ jobs: with: compiler: ${{ matrix.compiler }} build_type: ${{ matrix.build_type }} - developer_mode: ${{ matrix.developer_mode }} + packaging_maintainer_mode: ${{ matrix.packaging_maintainer_mode }} generator: ${{ matrix.generator }} @@ -73,7 +67,6 @@ jobs: cmake: true ninja: true - conan: true vcpkg: false ccache: true clangtidy: false @@ -83,15 +76,11 @@ jobs: gcovr: false opencppcoverage: false - - name: Cleanup Conan system packages (they are not properly cached) - run: | - conan remove -f '*/system' - # 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}} -DENABLE_DEVELOPER_MODE:BOOL=${{matrix.developer_mode}} -DOPT_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} + cmake -S . -B ./build -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -Dtravels_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -Dtravels_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/CMakeLists.txt b/CMakeLists.txt index 524539c..ceb6bef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,45 +1,42 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.21) -# Not ideal to use this global variable, but necessary to make sure -# that tooling and projects use the same version -set(CMAKE_CXX_STANDARD 20) +# This template attempts to be "fetch_content"-able +# so that it works well with tools like CPM or other +# manual dependency management + +# 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) -include(FetchContent) -# Note: by default ENABLE_DEVELOPER_MODE is True -# This means that all analysis (sanitizers, static analysis) -# is enabled and all warnings are treated as errors -# if you want to switch this behavior, change TRUE to FALSE -set(ENABLE_DEVELOPER_MODE - TRUE - CACHE BOOL "Enable 'developer mode'") - -# Change this to false if you want to disable warnings_as_errors in developer mode -set(OPT_WARNINGS_AS_ERRORS_DEVELOPER_DEFAULT TRUE) - -# Add project_options v0.17.0 -# https://github.com/cpp-best-practices/project_options -FetchContent_Declare(_project_options - URL https://github.com/cpp-best-practices/project_options/archive/refs/tags/v0.17.0.zip) -FetchContent_MakeAvailable(_project_options) -include(${_project_options_SOURCE_DIR}/Index.cmake) - -# uncomment to enable vcpkg: -# # Setup vcpkg - should be called before defining project() -# run_vcpkg() - # Set the project name and language project( travels VERSION 0.0.1 DESCRIPTION "" - HOMEPAGE_URL "https://github.com/cpp-best-practices/travels" + HOMEPAGE_URL "%%myurl%%" LANGUAGES CXX C) +include(cmake/PreventInSourceBuilds.cmake) +include(ProjectOptions.cmake) + + +travels_setup_options() + +travels_global_options() +include(Dependencies.cmake) +travels_setup_dependencies() + +travels_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" CACHE STRING "SHA this build was generated from") @@ -49,73 +46,13 @@ string( 8 GIT_SHORT_SHA) -get_property(BUILDING_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(BUILDING_MULTI_CONFIG) - if(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 - message(TRACE "Setting up multi-config build types") - set(CMAKE_CONFIGURATION_TYPES - Debug - Release - RelWithDebInfo - MinSizeRel - CACHE STRING "Enabled build types" FORCE) - else() - message(TRACE "User chose a specific build type, so we are using that") - set(CMAKE_CONFIGURATION_TYPES - ${CMAKE_BUILD_TYPE} - CACHE STRING "Enabled build types" FORCE) - endif() -endif() - -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=cppcheckError - --suppress=internalAstError - --suppress=syntaxError - # if a file does not have an internalAstError, we get an unmatchedSuppression error - --suppress=unmatchedSuppression - # There are too many false positives with this check - --suppress=passedByValue - --inconclusive +target_compile_features(travels_options INTERFACE cxx_std_${CMAKE_CXX_STANDARD}) -) - -target_compile_features(project_options INTERFACE cxx_std_${CMAKE_CXX_STANDARD}) +add_library(travels::travels_options ALIAS travels_options) +add_library(travels::travels_warnings ALIAS travels_warnings) -# We don't have any way of ensuring that all static dependencies are compiled with -# address sanitizer enabled. -target_compile_definitions(project_options INTERFACE _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION) +#add_library(travels::travels_options INTERFACE IMPORTED) +#add_library(travels::travels_warnings INTERFACE IMPORTED) # configure files based on CMake configuration options add_subdirectory(configured_files) @@ -123,34 +60,52 @@ add_subdirectory(configured_files) # 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) + message(AUTHOR_WARNING "Building Tests. Be sure to check out test/constexpr_tests.cpp for constexpr 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(travels_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 travels) +set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT intro) -# Add other targets that you want installed here, be default we just package the one executable +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 -package_project(TARGETS travels) +travels_package_project( + TARGETS + travels + travels_options + travels_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 diff --git a/Dependencies.cmake b/Dependencies.cmake new file mode 100644 index 0000000..e6c445d --- /dev/null +++ b/Dependencies.cmake @@ -0,0 +1,59 @@ +include(cmake/CPM.cmake) +include(FetchContent) + +# Done as a function so that updates to variables like +# CMAKE_CXX_FLAGS don't propagate out to other +# targets +function(travels_setup_dependencies) + + FetchContent_Declare( + lodepng + GIT_REPOSITORY https://github.com/lvandeve/lodepng.git + GIT_TAG master + ) + + FetchContent_GetProperties(lodepng) + if(NOT lodepng_POPULATED) + FetchContent_Populate(lodepng) + add_library(lodepng STATIC ${lodepng_SOURCE_DIR}/lodepng.cpp) + target_link_libraries(lodepng PRIVATE travels_options) + target_include_directories(lodepng PUBLIC ${lodepng_SOURCE_DIR}) + endif() + + # 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() + +endfunction() diff --git a/ProjectOptions.cmake b/ProjectOptions.cmake new file mode 100644 index 0000000..8f16553 --- /dev/null +++ b/ProjectOptions.cmake @@ -0,0 +1,172 @@ +include(cmake/SystemLink.cmake) +include(cmake/LibFuzzer.cmake) +include(CMakeDependentOption) +include(CheckCXXCompilerFlag) + + +macro(travels_setup_options) + option(travels_ENABLE_HARDENING "Enable hardening" ON) + option(travels_ENABLE_COVERAGE "Enable coverage reporting" OFF) + cmake_dependent_option( + travels_ENABLE_GLOBAL_HARDENING + "Attempt to push hardening options to built dependencies" + ON + travels_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() + + travels_check_libfuzzer_support(LIBFUZZER_SUPPORTED) + option(travels_BUILD_FUZZ_TESTS "Enable fuzz testing executable" ${LIBFUZZER_SUPPORTED}) + + + if(NOT PROJECT_IS_TOP_LEVEL OR travels_PACKAGING_MAINTAINER_MODE) + option(travels_ENABLE_IPO "Enable IPO/LTO" OFF) + option(travels_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) + option(travels_ENABLE_USER_LINKER "Enable user-selected linker" OFF) + option(travels_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" OFF) + option(travels_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) + option(travels_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" OFF) + option(travels_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) + option(travels_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) + option(travels_ENABLE_UNITY_BUILD "Enable unity builds" OFF) + option(travels_ENABLE_CLANG_TIDY "Enable clang-tidy" OFF) + option(travels_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) + option(travels_ENABLE_PCH "Enable precompiled headers" OFF) + option(travels_ENABLE_CACHE "Enable ccache" OFF) + else() + option(travels_ENABLE_IPO "Enable IPO/LTO" ON) + option(travels_WARNINGS_AS_ERRORS "Treat Warnings As Errors" ON) + option(travels_ENABLE_USER_LINKER "Enable user-selected linker" OFF) + option(travels_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) + option(travels_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) + option(travels_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" ${SUPPORTS_UBSAN}) + option(travels_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) + option(travels_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) + option(travels_ENABLE_UNITY_BUILD "Enable unity builds" OFF) + option(travels_ENABLE_CLANG_TIDY "Enable clang-tidy" ON) + option(travels_ENABLE_CPPCHECK "Enable cpp-check analysis" ON) + option(travels_ENABLE_PCH "Enable precompiled headers" OFF) + option(travels_ENABLE_CACHE "Enable ccache" ON) + endif() + + if(NOT PROJECT_IS_TOP_LEVEL) + mark_as_advanced( + travels_ENABLE_IPO + travels_WARNINGS_AS_ERRORS + travels_ENABLE_USER_LINKER + travels_ENABLE_SANITIZER_ADDRESS + travels_ENABLE_SANITIZER_LEAK + travels_ENABLE_SANITIZER_UNDEFINED + travels_ENABLE_SANITIZER_THREAD + travels_ENABLE_SANITIZER_MEMORY + travels_ENABLE_UNITY_BUILD + travels_ENABLE_CLANG_TIDY + travels_ENABLE_CPPCHECK + travels_ENABLE_COVERAGE + travels_ENABLE_PCH + travels_ENABLE_CACHE) + endif() +endmacro() + +macro(travels_global_options) + if(travels_ENABLE_IPO) + include(cmake/InterproceduralOptimization.cmake) + travels_enable_ipo() + endif() + + if(travels_ENABLE_HARDENING AND travels_ENABLE_GLOBAL_HARDENING) + include(cmake/Hardening.cmake) + set(ENABLE_UBSAN_MINIMAL_RUNTIME NOT travels_ENABLE_SANITIZER_UNDEFINED) + travels_enable_hardening(travels_options ON ${ENABLE_UBSAN_MINIMAL_RUNTIME}) + endif() +endmacro() + +macro(travels_local_options) + if (PROJECT_IS_TOP_LEVEL) + include(cmake/StandardProjectSettings.cmake) + endif() + + add_library(travels_warnings INTERFACE) + add_library(travels_options INTERFACE) + + include(cmake/CompilerWarnings.cmake) + travels_set_project_warnings( + travels_warnings + ${travels_WARNINGS_AS_ERRORS} + "" + "" + "" + "") + + if(travels_ENABLE_USER_LINKER) + include(cmake/Linker.cmake) + configure_linker(travels_options) + endif() + + include(cmake/Sanitizers.cmake) + travels_enable_sanitizers( + travels_options + ${travels_ENABLE_SANITIZER_ADDRESS} + ${travels_ENABLE_SANITIZER_LEAK} + ${travels_ENABLE_SANITIZER_UNDEFINED} + ${travels_ENABLE_SANITIZER_THREAD} + ${travels_ENABLE_SANITIZER_MEMORY}) + + set_target_properties(travels_options PROPERTIES UNITY_BUILD ${travels_ENABLE_UNITY_BUILD}) + + if(travels_ENABLE_PCH) + target_precompile_headers( + travels_options + INTERFACE + + + ) + endif() + + if(travels_ENABLE_CACHE) + include(cmake/Cache.cmake) + travels_enable_cache() + endif() + + include(cmake/StaticAnalyzers.cmake) + if(travels_ENABLE_CLANG_TIDY) + travels_enable_clang_tidy(travels_options ${travels_WARNINGS_AS_ERRORS}) + endif() + + if(travels_ENABLE_CPPCHECK) + travels_enable_cppcheck(${travels_WARNINGS_AS_ERRORS} "" # override cppcheck options + ) + endif() + + if(travels_ENABLE_COVERAGE) + include(cmake/Tests.cmake) + travels_enable_coverage(travels_options) + endif() + + if(travels_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(travels_options INTERFACE -Wl,--fatal-warnings) + endif() + endif() + + if(travels_ENABLE_HARDENING AND NOT travels_ENABLE_GLOBAL_HARDENING) + include(cmake/Hardening.cmake) + set(ENABLE_UBSAN_MINIMAL_RUNTIME NOT travels_ENABLE_SANITIZER_UNDEFINED) + travels_enable_hardening(travels_options OFF ${ENABLE_UBSAN_MINIMAL_RUNTIME}) + endif() + +endmacro() diff --git a/README_dependencies.md b/README_dependencies.md deleted file mode 100644 index 51bc71a..0000000 --- a/README_dependencies.md +++ /dev/null @@ -1,236 +0,0 @@ -## Dependencies - -Note about install commands: -- for Windows, we use [choco](https://chocolatey.org/install). -- for MacOS, we use [brew](https://brew.sh/). -- In case of an error in cmake, make sure that the dependencies are on the PATH. - - -### Too Long, Didn't Install - -This is a really long list of dependencies, and it's easy to mess up. That's why: - -#### Docker -We have a Docker image that's already set up for you. See the [Docker instructions](#docker-instructions). - -#### Setup-cpp - -We have [setup-cpp](https://github.com/aminya/setup-cpp) that is a cross-platform tool to install all the compilers and dependencies on the system. - -Please check [the setup-cpp documentation](https://github.com/aminya/setup-cpp) for more information. - -For example, on Windows, you can run the following to install llvm, cmake, ninja, ccache, conan, and cppcheck. -```ps1 -# windows example (open shell as admin) -curl -LJO "https://github.com/aminya/setup-cpp/releases/download/v0.5.7/setup_cpp_windows.exe" -./setup_cpp_windows --compiler llvm --cmake true --ninja true --ccache true --conan true --cppcheck true - -RefreshEnv.cmd # reload the environment -``` - -### Necessary Dependencies -1. A C++ compiler that supports C++17. -See [cppreference.com](https://en.cppreference.com/w/cpp/compiler_support) -to see which features are supported by each compiler. -The following compilers should work: - - * [gcc 7+](https://gcc.gnu.org/) -
- Install command - - - Debian/Ubuntu: - - sudo apt install build-essential - - - Windows: - - choco install mingw -y - - - MacOS: - - brew install gcc -
- - * [clang 6+](https://clang.llvm.org/) -
- Install command - - - Debian/Ubuntu: - - bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" - - - Windows: - - Visual Studio 2019 ships with LLVM (see the Visual Studio section). However, to install LLVM separately: - - choco install llvm -y - - llvm-utils for using external LLVM with Visual Studio generator: - - git clone https://github.com/zufuliu/llvm-utils.git - cd llvm-utils/VS2017 - .\install.bat - - - MacOS: - - brew install llvm -
- - * [Visual Studio 2019 or higher](https://visualstudio.microsoft.com/) -
- Install command + Environment setup - - On Windows, you need to install Visual Studio 2019 because of the SDK and libraries that ship with it. - - Visual Studio IDE - 2019 Community (installs Clang too): - - choco install -y visualstudio2019community --package-parameters "add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --includeOptional --passive --locale en-US" - - Put MSVC compiler, Clang compiler, and vcvarsall.bat on the path: - - choco install vswhere -y - refreshenv - - # change to x86 for 32bit - $clpath = vswhere -products * -latest -prerelease -find **/Hostx64/x64/* - $clangpath = vswhere -products * -latest -prerelease -find **/Llvm/bin/* - $vcvarsallpath = vswhere -products * -latest -prerelease -find **/Auxiliary/Build/* - - $path = [System.Environment]::GetEnvironmentVariable("PATH", "User") - [Environment]::SetEnvironmentVariable("Path", $path + ";$clpath" + ";$clangpath" + ";$vcvarsallpath", "User") - refreshenv - -
- - -2. [Conan](https://conan.io/) -
- Install Command - - - Via pip - https://docs.conan.io/en/latest/installation.html#install-with-pip-recommended - - pip install --user conan - - - Windows: - - choco install conan -y - - - MacOS: - - brew install conan - -
- -3. [CMake 3.15+](https://cmake.org/) -
- Install Command - - - Debian/Ubuntu: - - sudo apt-get install cmake - - - Windows: - - choco install cmake -y - - - MacOS: - - brew install cmake - -
- -### Optional Dependencies -#### C++ Tools - * [Doxygen](http://doxygen.nl/) -
- Install Command - - - Debian/Ubuntu: - - sudo apt-get install doxygen - sudo apt-get install graphviz - - - Windows: - - choco install doxygen.install -y - choco install graphviz -y - - - MacOS: - - brew install doxygen - brew install graphviz - -
- - - * [ccache](https://ccache.dev/) -
- Install Command - - - Debian/Ubuntu: - - sudo apt-get install ccache - - - Windows: - - choco install ccache -y - - - MacOS: - - brew install ccache - -
- - - * [Cppcheck](http://cppcheck.sourceforge.net/) -
- Install Command - - - Debian/Ubuntu: - - sudo apt-get install cppcheck - - - Windows: - - choco install cppcheck -y - - - MacOS: - - brew install cppcheck - -
- - - * [include-what-you-use](https://include-what-you-use.org/) -
- Install Command - - Follow instructions here: - https://github.com/include-what-you-use/include-what-you-use#how-to-install -
- -#### GUI libraries -This project can be made to work with several optional GUI frameworks. - -If desired, you should install the following optional dependencies as -directed by their documentation, linked here: - -- [FLTK](https://www.fltk.org/doc-1.4/index.html) -- [GTKMM](https://www.gtkmm.org/en/documentation.html) -- [QT](https://doc.qt.io/) - -The following dependencies can be downloaded automatically by CMake and Conan. -All you need to do to install them is to turn on a CMake flag during -configuration. -If you run into difficulty using them, please refer to their documentation, -linked here: - -- [NANA](http://nanapro.org/en-us/documentation/) -- [SDL](http://wiki.libsdl.org/FrontPage) -- [IMGUI](https://github.com/ocornut/imgui/tree/master/docs): - This framework depends on SFML, and if you are using Linux, you may need - to install several of SFML's dependencies using your package manager. See - [the SFML build tutorial](https://www.sfml-dev.org/tutorials/2.5/compile-with-cmake.php) - for specifics. - - diff --git a/README_docker.md b/README_docker.md index b1f5bf5..9e4ba9a 100644 --- a/README_docker.md +++ b/README_docker.md @@ -17,7 +17,7 @@ If you want to build this container using some other versions of gcc and clang, you may do so with the `GCC_VER` and `LLVM_VER` arguments: ```bash -docker build --tag=myproject:latest --build-arg GCC_VER=10 --build-arg LLVM_VER=11 . +docker build --tag=travels:latest --build-arg GCC_VER=10 --build-arg LLVM_VER=11 . ``` The CC and CXX environment variables are set to GCC version 11 by default. diff --git a/README_troubleshooting.md b/README_troubleshooting.md deleted file mode 100644 index 182f7f0..0000000 --- a/README_troubleshooting.md +++ /dev/null @@ -1,57 +0,0 @@ -## Troubleshooting - -### Update Conan -Many problems that users have can be resolved by updating Conan, so if you are -having any trouble with this project, you should start by doing that. - -To update conan: - - pip install --user --upgrade conan - -You may need to use `pip3` instead of `pip` in this command, depending on your -platform. - -### Clear Conan cache -If you continue to have trouble with your Conan dependencies, you can try -clearing your Conan cache: - - conan remove -f '*' - -The next time you run `cmake` or `cmake --build`, your Conan dependencies will -be rebuilt. If you aren't using your system's default compiler, don't forget to -set the CC, CXX, CMAKE_C_COMPILER, and CMAKE_CXX_COMPILER variables, as -described in the 'Build using an alternate compiler' section above. - -### Identifying misconfiguration of Conan dependencies - -If you have a dependency 'A' that requires a specific version of another -dependency 'B', and your project is trying to use the wrong version of -dependency 'B', Conan will produce warnings about this configuration error -when you run CMake. These warnings can easily get lost between a couple -hundred or thousand lines of output, depending on the size of your project. - -If your project has a Conan configuration error, you can use `conan info` to -find it. `conan info` displays information about the dependency graph of your -project, with colorized output in some terminals. - - cd build - conan info . - -In my terminal, the first couple lines of `conan info`'s output show all of the -project's configuration warnings in a bright yellow font. - -For example, the package `spdlog/1.5.0` depends on the package `fmt/6.1.2`. -If you were to modify the file `conanfile.py` so that it requires an -earlier version of `fmt`, such as `fmt/6.0.0`, and then run: - -```bash -conan remove -f '*' # clear Conan cache -rm -rf build # clear previous CMake build -cmake -S . -B ./build # rebuild Conan dependencies -conan info ./build -``` - -...the first line of output would be a warning that `spdlog` needs a more recent -version of `fmt`. - - diff --git a/build_examples.sh b/build_examples.sh deleted file mode 100644 index 3f91ca3..0000000 --- a/build_examples.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -# This script is intended to be run from an Ubuntu Docker image (or other Debian-derived distros). -# It uses `apt-get` to install dependencies required by the examples, - -set -euo pipefail - -function install_fltk() { - apt-get install -y --no-install-recommends libfltk1.3-dev libgl1-mesa-dev fluid -} - -function install_gtkmm() { - apt-get install -y --no-install-recommends pkg-config libgtkmm-3.0-dev -} - -function install_imgui() { - apt-get install -y --no-install-recommends pkg-config libgl1-mesa-dev -} - -function install_nana() { - # CMakeLists.txt is 'supposed' to install all of these automatically, but for - # some reason it doesn't always work. Installing them manually does work. - apt-get install -y --no-install-recommends libjpeg8-dev libpng-dev \ - libasound2-dev alsa-utils alsa-oss libx11-dev libxft-dev libxcursor-dev - # Note: Nana's headers trigger the -Wshadow warning, and cannot be compiled with -Werror. - # Supposedly, this is fixed in the develop-1.8 branch, but I cannot confirm. -} - -function install_qt() { - apt-get install -y --no-install-recommends qt5-default qtbase5-dev -} - -function install_sdl() { - pip install mako -} - -# call the above functions to install the required dependencies. As an example for qt: -# install_qt - -# build with: -cmake -S . -B "./build" -cmake --build "./build" \ No newline at end of file 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..a2fac2e --- /dev/null +++ b/cmake/Cache.cmake @@ -0,0 +1,33 @@ +# Enable cache if available +function(travels_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..a986845 --- /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( + travels_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..0c2cbfd --- /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(travels_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..e58f4a5 --- /dev/null +++ b/cmake/Doxygen.cmake @@ -0,0 +1,54 @@ +# Enable doxygen doc builds of source +function(travels_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..c179cb0 --- /dev/null +++ b/cmake/Hardening.cmake @@ -0,0 +1,91 @@ +include(CheckCXXCompilerFlag) + +macro( + travels_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..d865a7c --- /dev/null +++ b/cmake/InterproceduralOptimization.cmake @@ -0,0 +1,9 @@ +macro(travels_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..1aff308 --- /dev/null +++ b/cmake/LibFuzzer.cmake @@ -0,0 +1,17 @@ +function(travels_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..afbd70d --- /dev/null +++ b/cmake/Linker.cmake @@ -0,0 +1,31 @@ +macro(travels_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..a27f988 --- /dev/null +++ b/cmake/PackageProject.cmake @@ -0,0 +1,190 @@ +# Uses ycm (permissive BSD-3-Clause license) and ForwardArguments (permissive MIT license) + +function(travels_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..6f2546d --- /dev/null +++ b/cmake/PreventInSourceBuilds.cmake @@ -0,0 +1,19 @@ +# +# This function will prevent in-source builds +# +function(travels_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() + +travels_assure_out_of_source_builds() diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake new file mode 100644 index 0000000..f5c80b1 --- /dev/null +++ b/cmake/Sanitizers.cmake @@ -0,0 +1,90 @@ +function( + travels_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..0f18819 --- /dev/null +++ b/cmake/StaticAnalyzers.cmake @@ -0,0 +1,109 @@ +macro(travels_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(travels_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(travels_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..bf61ace --- /dev/null +++ b/cmake/Tests.cmake @@ -0,0 +1,6 @@ +function(travels_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 e133578..0000000 --- a/conanfile.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Docs at https://docs.conan.io/en/latest/reference/conanfile_txt.html - -[requires] -catch2/2.13.9 -docopt.cpp/0.6.3 -#fmt/8.1.1 -spdlog/1.10.0 -nlohmann_json/3.10.5 -lodepng/cci.20200615 -ftxui/3.0.0 - -[generators] -cmake_find_package_multi diff --git a/fuzz_test/CMakeLists.txt b/fuzz_test/CMakeLists.txt index 750d757..0347733 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 travels::travels_options + travels::travels_warnings fmt::fmt -coverage -fsanitize=fuzzer,undefined,address) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2de9c83..107d0fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,19 +27,16 @@ add_executable( tile_set.hpp game_components.cpp) -target_link_libraries( - travels - PRIVATE project_options - project_warnings - docopt::docopt - fmt::fmt - spdlog::spdlog - lodepng::lodepng - nlohmann_json::nlohmann_json) +target_link_libraries(travels PRIVATE travels_options travels_warnings) target_link_system_libraries( travels PRIVATE + CLI11::CLI11 + fmt::fmt + spdlog::spdlog + lodepng + nlohmann_json::nlohmann_json ftxui::screen ftxui::dom ftxui::component) diff --git a/src/game_components.cpp b/src/game_components.cpp index 07d5fde..2f7e718 100644 --- a/src/game_components.cpp +++ b/src/game_components.cpp @@ -46,8 +46,8 @@ Game_Map load_tiled_map(const std::filesystem::path &map_json)// NOLINT cognitiv const auto map_file = load_json(map_json); - Size tile_size{ map_file["tilewidth"], map_file["tileheight"] }; - Size map_size{ map_file["width"], map_file["height"] }; + const Size tile_size{ map_file["tilewidth"], map_file["tileheight"] }; + const Size map_size{ map_file["width"], map_file["height"] }; Game_Map map{ map_size }; @@ -62,7 +62,7 @@ Game_Map load_tiled_map(const std::filesystem::path &map_json)// NOLINT cognitiv result.emplace_back(parent_path / tsj_image_path, tile_size, start_gid); for (const auto &tile : tsj["tiles"]) { - std::size_t tile_id = tile["id"]; + const std::size_t tile_id = tile["id"]; bool passable = true; @@ -115,7 +115,7 @@ Game_Map load_tiled_map(const std::filesystem::path &map_json)// NOLINT cognitiv std::size_t cur_y = 0; for (const auto &tile : layer["data"]) { - std::size_t tileid = tile; + const std::size_t tileid = tile; points[Point{ cur_x, cur_y }].push_back( Layer_Info{ .tileid = tileid, .background = background, .foreground = foreground }); @@ -142,7 +142,7 @@ Game_Map load_tiled_map(const std::filesystem::path &map_json)// NOLINT cognitiv const auto &tile_pixels = tile_sets[0].at(tile.tileid); for (std::size_t cur_y = 0; cur_y < pixels.size().height; ++cur_y) { for (std::size_t cur_x = 0; cur_x < pixels.size().width; ++cur_x) { - Point current_pixel{ cur_x, cur_y }; + const Point current_pixel{ cur_x, cur_y }; if (first_tile && !tile.foreground) { pixels.at(current_pixel) = tile_pixels.at(current_pixel); diff --git a/src/game_hacking_lesson_01.cpp b/src/game_hacking_lesson_01.cpp index efed6c4..851f34a 100644 --- a/src/game_hacking_lesson_01.cpp +++ b/src/game_hacking_lesson_01.cpp @@ -137,7 +137,7 @@ Game_Map make_map()// NOLINT cognitive complexity map.locations.at(special_location).enter_action = [](Game &game, Point, Direction) { game.last_message = "You opened the door! Now change the call to `play_game` to start lesson 02"; - Menu menu{ { "Continue Game", [](Game &menu_action_game) { menu_action_game.clear_menu(); } }, + const Menu menu{ { "Continue Game", [](Game &menu_action_game) { menu_action_game.clear_menu(); } }, { "Exit Game", [](Game &menu_action_game) { menu_action_game.exit_game = true; } } }; game.set_menu(menu); }; @@ -187,4 +187,4 @@ Game make_lesson() return retval; } -}// namespace lefticus::travels::hacking::lesson_01 \ No newline at end of file +}// namespace lefticus::travels::hacking::lesson_01 diff --git a/src/game_hacking_lesson_02.cpp b/src/game_hacking_lesson_02.cpp index 4d84205..2272563 100644 --- a/src/game_hacking_lesson_02.cpp +++ b/src/game_hacking_lesson_02.cpp @@ -187,7 +187,7 @@ Game_Map make_map()// NOLINT cognitive complexity map.locations.at(special_location).enter_action = [](Game &game, Point, Direction) { game.last_message = "You opened the door! Now change the call to `play_game` to start lesson 03"; - Menu menu{ { "Continue Game", [](Game &menu_action_game) { menu_action_game.clear_menu(); } }, + const Menu menu{ { "Continue Game", [](Game &menu_action_game) { menu_action_game.clear_menu(); } }, { "Exit Game", [](Game &menu_action_game) { menu_action_game.exit_game = true; } } }; game.set_menu(menu); }; @@ -235,4 +235,4 @@ Game make_lesson() return retval; } -}// namespace lefticus::travels::hacking::lesson_02 \ No newline at end of file +}// namespace lefticus::travels::hacking::lesson_02 diff --git a/src/main.cpp b/src/main.cpp index c967528..828801e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,20 +3,13 @@ #include #include -#include +#include #include // for Slider #include // for ScreenInteractive #include - -#ifdef _MSC_VER -#pragma warning(disable : 4189) -#endif #include -#ifdef _MSC_VER -#pragma warning(default : 4189) -#endif #include "bitmap.hpp" @@ -274,8 +267,6 @@ void play_game(Game &game, std::shared_ptr> log_sink)// NOL auto last_time = std::chrono::steady_clock::now(); - std::string last_character; - auto container = ftxui::Container::Vertical({}); auto key_press = lefticus::travels::CatchEvent(container, [&](const ftxui::Event &event) { @@ -452,27 +443,19 @@ std::vector resource_search_directories() int main(int argc, const char **argv) { try { - static constexpr auto USAGE = - R"(travels - - Usage: - travels - travels (-h | --help) - travels --version - Options: - -h --help Show this screen. - --version Show version. -)"; + CLI::App app{ fmt::format("{} version {}", travels::cmake::project_name, travels::cmake::project_version) }; - spdlog::set_level(spdlog::level::trace); + bool show_version = false; + app.add_flag("--version", show_version, "Show version information"); + + CLI11_PARSE(app, argc, argv); - std::map args = docopt::docopt(USAGE, - { std::next(argv), std::next(argv, argc) }, - true,// show help if requested - fmt::format("{} {}", - travels::cmake::project_name, - travels::cmake::project_version));// version string, acquired - // from config.hpp via CMake + if (show_version) { + fmt::print("{}\n", travels::cmake::project_version); + return EXIT_SUCCESS; + } + + spdlog::set_level(spdlog::level::trace); // to start the lessons, comment out this line auto game = lefticus::travels::make_game(resource_search_directories()); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index abbf2bc..b67379e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,13 +1,23 @@ -find_package(Catch2 REQUIRED) +cmake_minimum_required(VERSION 3.15...3.23) -enable_testing() +project(CmakeConfigPackageTests LANGUAGES CXX) -#include(CTest) -include(Catch) +# ---- Test as standalone project the exported config package ---- -add_library(catch_main OBJECT catch_main.cpp) -target_link_libraries(catch_main PUBLIC Catch2::Catch2) -target_link_libraries(catch_main PRIVATE project_options) +if(PROJECT_IS_TOP_LEVEL OR TEST_INSTALLED_VERSION) + enable_testing() + + find_package(travels CONFIG REQUIRED) # for intro, project_options, ... + + if(NOT TARGET travels_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) # Provide a simple smoke test to make sure that the CLI works and can display a --help message add_test(NAME cli.has_help COMMAND travels --help) @@ -19,9 +29,20 @@ add_test(NAME cli.has_help COMMAND travels --help) add_test(NAME cli.version_matches COMMAND travels --version) set_tests_properties(cli.version_matches PROPERTIES PASS_REGULAR_EXPRESSION "${PROJECT_VERSION}") - add_executable(tests tests.cpp) -target_link_libraries(tests PRIVATE project_warnings project_options catch_main) +target_link_libraries( + tests + PRIVATE travels::travels_warnings + travels::travels_options + Catch2::Catch2WithMain) + +if(WIN32 AND BUILD_SHARED_LIBS) + add_custom_command( + TARGET tests + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ + COMMAND_EXPAND_LISTS) +endif() # 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 @@ -30,7 +51,7 @@ catch_discover_tests( TEST_PREFIX "unittests." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX @@ -40,14 +61,18 @@ catch_discover_tests( # Add a file containing a set of constexpr tests add_executable(constexpr_tests constexpr_tests.cpp) -target_link_libraries(constexpr_tests PRIVATE project_options project_warnings catch_main) +target_link_libraries( + constexpr_tests + PRIVATE travels::travels_warnings + travels::travels_options + Catch2::Catch2WithMain) catch_discover_tests( constexpr_tests TEST_PREFIX "constexpr." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX @@ -58,7 +83,11 @@ 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) -target_link_libraries(relaxed_constexpr_tests PRIVATE project_options project_warnings catch_main) +target_link_libraries( + relaxed_constexpr_tests + PRIVATE travels::travels_warnings + travels::travels_options + Catch2::Catch2WithMain) target_compile_definitions(relaxed_constexpr_tests PRIVATE -DCATCH_CONFIG_RUNTIME_STATIC_REQUIRE) catch_discover_tests( @@ -66,7 +95,7 @@ catch_discover_tests( TEST_PREFIX "relaxed_constexpr." REPORTER - xml + XML OUTPUT_DIR . OUTPUT_PREFIX diff --git a/test/constexpr_tests.cpp b/test/constexpr_tests.cpp index 6d95f0d..b52529c 100644 --- a/test/constexpr_tests.cpp +++ b/test/constexpr_tests.cpp @@ -1,14 +1,3 @@ -#include +#include -constexpr unsigned int Factorial(unsigned int number)// NOLINT(misc-no-recursion) -{ - return number <= 1 ? number : Factorial(number - 1) * number; -} - -TEST_CASE("Factorials are computed with constexpr", "[factorial]") -{ - STATIC_REQUIRE(Factorial(1) == 1); - STATIC_REQUIRE(Factorial(2) == 2); - STATIC_REQUIRE(Factorial(3) == 6); - STATIC_REQUIRE(Factorial(10) == 3628800); -} +TEST_CASE("Constexpr test", "[sample_tests]") { STATIC_REQUIRE(true); } diff --git a/test/tests.cpp b/test/tests.cpp index a130a5d..22a0265 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -1,14 +1,4 @@ -#include +#include -unsigned int Factorial(unsigned int number)// NOLINT(misc-no-recursion) -{ - return number <= 1 ? number : Factorial(number - 1) * number; -} -TEST_CASE("Factorials are computed", "[factorial]") -{ - REQUIRE(Factorial(1) == 1); - REQUIRE(Factorial(2) == 2); - REQUIRE(Factorial(3) == 6); - REQUIRE(Factorial(10) == 3628800); -} +TEST_CASE("Sample test", "[samples]") { REQUIRE(true); }