diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ac78e3f..ff341bd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,7 @@ env: CONAN_SYSREQUIRES_MODE: enabled CONAN_USER_HOME: "${{ github.workspace }}/conan-cache" CONAN_USER_HOME_SHORT: "${{ github.workspace }}/conan-cache/short" + CLANG_TIDY_VERSION: "13.0.0" jobs: Test: @@ -72,21 +73,25 @@ jobs: gcov_executable: gcov developer_mode: On + # Windows msvc builds - os: windows-2022 compiler: msvc generator: "Visual Studio 17 2022" build_type: Debug developer_mode: On + - os: windows-2022 compiler: msvc generator: "Visual Studio 17 2022" build_type: Release developer_mode: On + - os: windows-2022 compiler: msvc generator: "Visual Studio 17 2022" build_type: Debug developer_mode: Off + - os: windows-2022 compiler: msvc generator: "Visual Studio 17 2022" @@ -95,10 +100,14 @@ jobs: package_generator: ZIP - - - 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 @@ -120,7 +129,8 @@ jobs: conan: true vcpkg: false ccache: true - clangtidy: true + clangtidy: ${{ env.CLANG_TIDY_VERSION }} + cppcheck: true diff --git a/.github/workflows/template-janitor.yml b/.github/workflows/template-janitor.yml index 3efe5ce2..61c9102d 100644 --- a/.github/workflows/template-janitor.yml +++ b/.github/workflows/template-janitor.yml @@ -61,11 +61,23 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Use testing variables if still a template + if: fromJson(steps.get_repo_meta.outputs.data).is_template == true + run: | + # This name is unsafe because it is not a valid C++ identifier + echo "NEW_PROJECT=my-unsafe.project" >> $GITHUB_ENV + + - name: Add safe replacement variable versions + run: | + # hyphens and dots in c++ identifiers are forbidden. Use underscores instead. + NEW_SAFE_PROJECT=$(echo ${{ env.NEW_PROJECT }} | sed "s/-/_/g" | sed "s/\./_/g" ) + echo "NEW_SAFE_PROJECT=$NEW_SAFE_PROJECT" >> $GITHUB_ENV + # Rename all cpp_starter_project occurences to current repository and remove this workflow - name: Insert new org and project run: | # rename the CMake project to match the github project - sed -i "s/myproject/${{ github.event.repository.name }}/gi" CMakeLists.txt configured_files/config.hpp.in src/main.cpp + sed -i "s/myproject/${{ env.NEW_SAFE_PROJECT }}/gi" CMakeLists.txt configured_files/config.hpp.in src/main.cpp test/CMakeLists.txt fuzz_test/CMakeLists.txt # Update URL placeholders for project sed -i "s|%%myurl%%|${{ fromJson(steps.get_repo_meta.outputs.data).html_url }}|gi" CMakeLists.txt @@ -120,7 +132,8 @@ jobs: - name: Test simple configuration to make sure nothing broke 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=OFF - + # Build it because we may have broken something in the cpp/hpp files + cmake --build build - uses: EndBug/add-and-commit@v4 # only commit and push if we are not a template project anymore! diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cafb95fa..727a39ae 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,9 +20,9 @@ stages: apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1E9377A2BA9EF27F .setup_cpp: &setup_cpp | - curl -LJO "https://github.com/aminya/setup-cpp/releases/download/v0.5.8/setup_cpp_linux" + curl -LJO "https://github.com/aminya/setup-cpp/releases/download/v0.10.0/setup_cpp_linux" chmod +x setup_cpp_linux - ./setup_cpp_linux --compiler $compiler --cmake true --ninja true --conan true --ccache true + ./setup_cpp_linux --compiler $compiler --cmake true --ninja true --conan true --ccache true --clangtidy true --clangformat true --cppcheck true source ~/.profile .test: &test | @@ -46,4 +46,4 @@ test_linux_gcc: script: - *setup_linux - *setup_cpp - - *test \ No newline at end of file + - *test diff --git a/CMakeLists.txt b/CMakeLists.txt index ac51fcd4..0d9d691f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.16...3.23) # Not ideal to use this global variable, but necessary to make sure # that tooling and projects use the same version @@ -22,10 +22,10 @@ set(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 +# Add project_options v0.20.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) +include(FetchContent) +FetchContent_Declare(_project_options URL https://github.com/aminya/project_options/archive/refs/tags/v0.20.0.zip) FetchContent_MakeAvailable(_project_options) include(${_project_options_SOURCE_DIR}/Index.cmake) @@ -41,6 +41,18 @@ project( HOMEPAGE_URL "%%myurl%%" LANGUAGES CXX C) +# This variable is set by project() in CMake 3.21+ +string( + COMPARE EQUAL + "${CMAKE_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}" + PROJECT_IS_TOP_LEVEL) +if(PROJECT_IS_TOP_LEVEL) + # Consider the CTest module, which creates targets and options! + # Only needed if you want to enable submissions to a CDash server. + include(CTest) +endif() + set(GIT_SHA "Unknown" CACHE STRING "SHA this build was generated from") @@ -93,14 +105,28 @@ dynamic_project_options( PCH_HEADERS # This is a list of headers to pre-compile, here are some common ones + ENABLE_CONAN # 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 # Override the defaults for CppCheck + CPPCHECK_OPTIONS + --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 + --suppress=passedByValue + --suppress=syntaxError + --inconclusive ) target_compile_features(project_options INTERFACE cxx_std_${CMAKE_CXX_STANDARD}) +# TODO: The INTERFACE library NAMESPACE ALIAS are missing! CK +add_library(myproject::project_options INTERFACE IMPORTED) +add_library(myproject::project_warnings INTERFACE IMPORTED) # configure files based on CMake configuration options add_subdirectory(configured_files) @@ -109,33 +135,42 @@ add_subdirectory(configured_files) add_subdirectory(src) # Adding the tests: -option(ENABLE_TESTING "Enable the tests" ON) +option(ENABLE_TESTING "Enable the tests" ${PROJECT_IS_TOP_LEVEL}) if(ENABLE_TESTING) enable_testing() - message("Building Tests. Be sure to check out test/constexpr_tests for constexpr -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") + 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 intro) +if(CMAKE_SKIP_INSTALL_RULES) + return() +elseif(NOT PROJECT_IS_TOP_LEVEL) + return() +endif() + # Add other targets that you want installed here, be default we just package the one executable # we know we want to ship -package_project(TARGETS intro) +package_project(TARGETS intro project_options project_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/CMakePresets.json b/CMakePresets.json index 5a4f2ebf..5871489e 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -38,14 +38,17 @@ } }, { - "name": "conf-linux-common", - "description": "Linux settings for gcc and clang toolchains", + "name": "conf-unixlike-common", + "description": "Unix-like OS settings for gcc and clang toolchains", "hidden": true, "inherits": "conf-common", "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Linux" + "type": "inList", + "string": "${hostSystemName}", + "list": [ + "Linux", + "Darwin" + ] }, "vendor": { "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { @@ -75,7 +78,7 @@ "CMAKE_CXX_COMPILER": "cl", "CMAKE_BUILD_TYPE": "RelWithDebInfo", "ENABLE_DEVELOPER_MODE": "ON" - } + } }, { "name": "windows-msvc-debug-user-mode", @@ -87,7 +90,7 @@ "CMAKE_CXX_COMPILER": "cl", "CMAKE_BUILD_TYPE": "Debug", "ENABLE_DEVELOPER_MODE": "OFF" - } + } }, { "name": "windows-msvc-release-user-mode", @@ -99,7 +102,7 @@ "CMAKE_CXX_COMPILER": "cl", "CMAKE_BUILD_TYPE": "RelWithDebInfo", "ENABLE_DEVELOPER_MODE": "OFF" - } + } }, { "name": "windows-clang-debug", @@ -134,10 +137,10 @@ } }, { - "name": "linux-gcc-debug", + "name": "unixlike-gcc-debug", "displayName": "gcc Debug", - "description": "Target Linux with the gcc compiler, debug build type", - "inherits": "conf-linux-common", + "description": "Target Unix-like OS with the gcc compiler, debug build type", + "inherits": "conf-unixlike-common", "cacheVariables": { "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", @@ -145,10 +148,10 @@ } }, { - "name": "linux-gcc-release", + "name": "unixlike-gcc-release", "displayName": "gcc Release", - "description": "Target Linux with the gcc compiler, release build type", - "inherits": "conf-linux-common", + "description": "Target Unix-like OS with the gcc compiler, release build type", + "inherits": "conf-unixlike-common", "cacheVariables": { "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", @@ -156,10 +159,10 @@ } }, { - "name": "linux-clang-debug", + "name": "unixlike-clang-debug", "displayName": "clang Debug", - "description": "Target Linux with the clang compiler, debug build type", - "inherits": "conf-linux-common", + "description": "Target Unix-like OS with the clang compiler, debug build type", + "inherits": "conf-unixlike-common", "cacheVariables": { "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++", @@ -167,10 +170,10 @@ } }, { - "name": "linux-clang-release", + "name": "unixlike-clang-release", "displayName": "clang Release", - "description": "Target Linux with the clang compiler, release build type", - "inherits": "conf-linux-common", + "description": "Target Unix-like OS with the clang compiler, release build type", + "inherits": "conf-unixlike-common", "cacheVariables": { "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++", @@ -220,32 +223,32 @@ "configurePreset": "windows-clang-release" }, { - "name": "test-linux-gcc-debug", + "name": "test-unixlike-gcc-debug", "displayName": "Strict", "description": "Enable output and stop on failure", "inherits": "test-common", - "configurePreset": "linux-gcc-debug" + "configurePreset": "unixlike-gcc-debug" }, { - "name": "test-linux-gcc-release", + "name": "test-unixlike-gcc-release", "displayName": "Strict", "description": "Enable output and stop on failure", "inherits": "test-common", - "configurePreset": "linux-gcc-release" + "configurePreset": "unixlike-gcc-release" }, { - "name": "test-linux-clang-debug", + "name": "test-unixlike-clang-debug", "displayName": "Strict", "description": "Enable output and stop on failure", "inherits": "test-common", - "configurePreset": "linux-clang-debug" + "configurePreset": "unixlike-clang-debug" }, { - "name": "test-linux-clang-release", + "name": "test-unixlike-clang-release", "displayName": "Strict", "description": "Enable output and stop on failure", "inherits": "test-common", - "configurePreset": "linux-clang-release" + "configurePreset": "unixlike-clang-release" } ] -} +} \ No newline at end of file diff --git a/conanfile.txt b/conanfile.txt index b8a44730..8757fda7 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,10 +1,9 @@ # 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 +catch2/2.13.9 +cli11/2.2.0 +spdlog/1.10.0 ftxui/2.0.0 [generators] diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f478b6f7..84e09040 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,15 +1,16 @@ -find_package(fmt CONFIG) -find_package(spdlog CONFIG) -find_package(docopt CONFIG) -find_package(ftxui CONFIG) +find_package(fmt CONFIG REQUIRED) +find_package(spdlog CONFIG REQUIRED) +find_package(CLI11 CONFIG REQUIRED) +find_package(ftxui CONFIG REQUIRED) # Generic test that uses conan libs add_executable(intro main.cpp) + target_link_libraries( intro PRIVATE project_options project_warnings - docopt::docopt + CLI11::CLI11 fmt::fmt spdlog::spdlog ftxui::screen diff --git a/src/main.cpp b/src/main.cpp index b7dbe507..27ef7f39 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,11 @@ #include #include #include +#include + #include -#include +#include #include // for ftxui #include // for Slider #include // for ScreenInteractive @@ -302,38 +304,41 @@ void game_iteration_canvas() refresh_ui.join(); } +// NOLINTNEXTLINE(bugprone-exception-escape) int main(int argc, const char **argv) { try { - static constexpr auto USAGE = - R"(intro - - Usage: - intro turn_based - intro loop_based - intro (-h | --help) - intro --version - Options: - -h --help Show this screen. - --version Show version. -)"; - - std::map args = docopt::docopt(USAGE, - { std::next(argv), std::next(argv, argc) }, - true,// show help if requested - fmt::format("{} {}", - myproject::cmake::project_name, - myproject::cmake::project_version));// version string, acquired - // from config.hpp via CMake - - if (args["turn_based"].asBool()) { + CLI::App app{ fmt::format("{} version {}", myproject::cmake::project_name, myproject::cmake::project_version) }; + + std::optional message; + app.add_option("-m,--message", message, "A message to print back out"); + bool show_version = false; + app.add_flag("--version", show_version, "Show version information"); + + bool is_turn_based = false; + auto *turn_based = app.add_flag("--turn_based", is_turn_based); + + bool is_loop_based = false; + auto *loop_based = app.add_flag("--loop_based", is_loop_based); + + turn_based->excludes(loop_based); + loop_based->excludes(turn_based); + + + CLI11_PARSE(app, argc, argv); + + if (show_version) { + fmt::print("{}\n", myproject::cmake::project_version); + return EXIT_SUCCESS; + } + + if (is_turn_based) { consequence_game(); } else { game_iteration_canvas(); } - // consequence_game(); } catch (const std::exception &e) { - fmt::print("Unhandled exception in main: {}", e.what()); + spdlog::error("Unhandled exception in main: {}", e.what()); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c6a32356..a5e24bc6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,11 +1,29 @@ -find_package(Catch2 REQUIRED) +cmake_minimum_required(VERSION 3.15...3.23) + +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, project_options, ... + + if(NOT TARGET myproject::project_options) + message(FATAL_ERROR "Requiered config package not found!") + return() # be strictly paranoid for Template Janitor github action! CK + endif() +endif() + +# ---- Dependencies ---- + +find_package(Catch2 CONFIG REQUIRED) -include(CTest) include(Catch) add_library(catch_main OBJECT catch_main.cpp) target_link_libraries(catch_main PUBLIC Catch2::Catch2) -target_link_libraries(catch_main PRIVATE project_options) +target_link_libraries(catch_main PRIVATE myproject::project_options) # 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 intro --help) @@ -17,9 +35,8 @@ add_test(NAME cli.has_help COMMAND intro --help) add_test(NAME cli.version_matches COMMAND intro --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 myproject::project_warnings myproject::project_options catch_main) # 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 @@ -38,7 +55,7 @@ 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 myproject::project_options myproject::project_warnings catch_main) catch_discover_tests( constexpr_tests @@ -56,7 +73,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) -target_link_libraries(relaxed_constexpr_tests PRIVATE project_options project_warnings catch_main) +target_link_libraries(relaxed_constexpr_tests PRIVATE myproject::project_options myproject::project_warnings catch_main) target_compile_definitions(relaxed_constexpr_tests PRIVATE -DCATCH_CONFIG_RUNTIME_STATIC_REQUIRE) catch_discover_tests(