├── .clang-format ├── .dockerignore ├── .github └── workflows │ ├── linux.yml │ ├── macos.yml │ ├── main.yml │ └── windows.yml ├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── Dockerfile ├── README.md ├── docker-compose.yml └── projectFolder ├── CMakeLists.txt ├── CMakePresets.json ├── scripts ├── build_cmake_debug.sh ├── build_debug.sh ├── create_release.sh ├── format.sh ├── git_hooks │ └── pre-commit ├── run_tests.sh ├── run_tests_in_Docker.sh └── windows │ ├── build_cmake_debug.ps1 │ ├── build_debug.ps1 │ ├── create_release.ps1 │ └── run_tests.ps1 ├── src ├── CMakeLists.txt ├── main.cpp └── utility │ ├── utility.cpp │ └── utility.h └── tests ├── CMakeLists.txt ├── test.h ├── test_cpp17.cpp ├── test_platforms.cpp └── test_utility.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | Language: Cpp 4 | IndentWidth: 4 5 | UseTab: Never 6 | BreakBeforeBraces: Allman 7 | AlwaysBreakBeforeMultilineStrings: true 8 | AlwaysBreakTemplateDeclarations: true 9 | NamespaceIndentation: All 10 | KeepEmptyLinesAtTheStartOfBlocks: true 11 | AllowShortFunctionsOnASingleLine: Empty 12 | SpaceBeforeParens: ControlStatements 13 | # Only available in clang-format v16 14 | # InsertNewlineAtEOF: true 15 | 16 | # Define the ordering of include categories 17 | IncludeCategories: 18 | - Regex: '.*' 19 | Priority: 1 20 | - Regex: '<.*>' 21 | Priority: 2 22 | 23 | # Disabling it cause Google style sets it 24 | DerivePointerAlignment: false 25 | PointerBindsToType: true 26 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortinger91/cpp-cmake-template/b886ec6a6b189929bd270b083d2cf4405b14b645/.dockerignore -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Build and test on Linux 2 | on: 3 | workflow_call: 4 | 5 | concurrency: 6 | group: "${{ github.ref }}-linux" 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | linux: 11 | runs-on: ubuntu-latest 12 | # Specify an image from hub.docker.com 13 | # container: 14 | # image: debian:10-slim 15 | # options: 16 | timeout-minutes: 60 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup sccache 20 | uses: hendrikmuhs/ccache-action@main 21 | with: 22 | key: ${{ github.job }} 23 | max-size: 1000M 24 | variant: sccache 25 | - name: Configure CMake and build 26 | run: ${{ github.workspace }}/projectFolder/scripts/create_release.sh withTests 27 | - name: Run tests 28 | run: ${{ github.workspace }}/projectFolder/scripts/run_tests.sh Release 29 | - uses: actions/upload-artifact@v3 30 | if: success() 31 | with: 32 | name: "linux-artifacts" 33 | retention-days: 28 34 | path: |- 35 | build/Release/bin/cpp-cmake-template 36 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: Build and test on macOS 2 | on: 3 | workflow_call: 4 | 5 | concurrency: 6 | group: "${{ github.ref }}-macos" 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | macos: 11 | runs-on: macos-latest 12 | timeout-minutes: 60 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Setup sccache 16 | uses: hendrikmuhs/ccache-action@main 17 | with: 18 | key: ${{ github.job }} 19 | max-size: 1000M 20 | variant: sccache 21 | - name: Configure CMake and build 22 | run: ${{ github.workspace }}/projectFolder/scripts/create_release.sh withTests 23 | - name: Run tests 24 | run: ${{ github.workspace }}/projectFolder/scripts/run_tests.sh Release 25 | - uses: actions/upload-artifact@v3 26 | if: success() 27 | with: 28 | name: "macos-artifacts" 29 | retention-days: 28 30 | path: |- 31 | build/Release/bin/cpp-cmake-template 32 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build and test all platforms 2 | on: 3 | push: 4 | paths-ignore: 5 | - '**.md' 6 | - .clang-format 7 | - .dockerignore 8 | - .gitignore 9 | - docker-compose.yml 10 | - Dockerfile 11 | workflow_dispatch: 12 | 13 | jobs: 14 | linux: 15 | permissions: 16 | contents: read 17 | uses: ./.github/workflows/linux.yml 18 | 19 | macos: 20 | permissions: 21 | contents: read 22 | uses: ./.github/workflows/macos.yml 23 | 24 | windows: 25 | permissions: 26 | contents: read 27 | uses: ./.github/workflows/windows.yml 28 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Build and test on Windows 2 | on: 3 | workflow_call: 4 | 5 | concurrency: 6 | group: "${{ github.ref }}-windows" 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | windows: 11 | runs-on: windows-latest 12 | timeout-minutes: 60 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Setup sccache 16 | uses: hendrikmuhs/ccache-action@main 17 | with: 18 | key: ${{ github.job }} 19 | max-size: 1000M 20 | variant: sccache 21 | - name: Configure CMake and build 22 | run: ${{ github.workspace }}/projectFolder/scripts/windows/create_release.ps1 withTests 23 | - name: Run tests 24 | run: ${{ github.workspace }}/projectFolder/scripts/windows/run_tests.ps1 Release 25 | - uses: actions/upload-artifact@v3 26 | if: success() 27 | with: 28 | name: "windows-artifacts" 29 | retention-days: 28 30 | path: |- 31 | build/Release/bin/Debug/cpp-cmake-template.exe 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | empty 34 | 35 | # CMake artifacts 36 | _CPack_Packages 37 | CPack* 38 | cmake_install.cmake 39 | CMakeCache.txt 40 | CMakeFiles 41 | CMakeScripts 42 | compile_commands.json 43 | CTestTestfile.cmake 44 | install_manifest.txt 45 | Makefile 46 | Testing 47 | 48 | # Visual Studio 49 | Debug*/ 50 | x64/* 51 | .vs/* 52 | *.sln 53 | *.vcxproj 54 | *.filters 55 | *.user 56 | *.db 57 | *.suo 58 | build/ 59 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "compilerPath": "/usr/bin/clang", 10 | "cStandard": "c17", 11 | "cppStandard": "c++17", 12 | "intelliSenseMode": "linux-clang-x64", 13 | "compileCommands": "${workspaceFolder}/build/Debug/compile_commands.json" 14 | }, 15 | { 16 | "name": "Mac", 17 | "includePath": [ 18 | "${workspaceFolder}/**" 19 | ], 20 | "defines": [], 21 | "compilerPath": "/usr/bin/clang", 22 | "cStandard": "c17", 23 | "cppStandard": "c++17", 24 | "intelliSenseMode": "macos-clang-arm64", 25 | "compileCommands": "${workspaceFolder}/build/Debug/compile_commands.json" 26 | } 27 | ], 28 | "version": 4 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-vscode.cpptools", 4 | "vadimcn.vscode-lldb", 5 | "llvm-vs-code-extensions.vscode-clangd", 6 | "twxs.cmake" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "DEBUG", 6 | "type": "lldb", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/build/Debug/bin/cpp-cmake-template", 9 | "args": [], 10 | "cwd": "${workspaceFolder}", 11 | "preLaunchTask": "build_debug" 12 | }, 13 | { 14 | "name": "DEBUG_WIN", 15 | "type": "cppvsdbg", 16 | "request": "launch", 17 | "program": "${workspaceFolder}/build/Debug/bin/Debug/cpp-cmake-template.exe", 18 | "args": [], 19 | "stopAtEntry": false, 20 | "cwd": "${workspaceFolder}", 21 | "preLaunchTask": "build_debug" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.sourceDirectory": "${workspaceFolder}/projectFolder" 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | // Build tasks 5 | { 6 | "label": "build_cmake_debug", 7 | "type": "shell", 8 | "presentation": { 9 | "focus": true, 10 | "showReuseMessage": false 11 | }, 12 | "group": { 13 | "kind": "build" 14 | }, 15 | 16 | "command": "${workspaceFolder}/projectFolder/scripts/build_cmake_debug.sh", 17 | 18 | "windows": { 19 | "command": "${workspaceFolder}/projectFolder/scripts/windows/build_cmake_debug.ps1" 20 | } 21 | }, 22 | { 23 | "label": "build_debug", 24 | "type": "shell", 25 | "presentation": { 26 | "focus": true, 27 | "showReuseMessage": false 28 | }, 29 | "group": { 30 | "kind": "build" 31 | }, 32 | 33 | "command": "${workspaceFolder}/projectFolder/scripts/build_debug.sh", 34 | 35 | "windows": { 36 | "command": "${workspaceFolder}/projectFolder/scripts/windows/build_debug.ps1" 37 | } 38 | }, 39 | { 40 | "label": "create_release", 41 | "type": "shell", 42 | "presentation": { 43 | "focus": true, 44 | "showReuseMessage": false 45 | }, 46 | "group": { 47 | "kind": "build" 48 | }, 49 | 50 | "command": "${workspaceFolder}/projectFolder/scripts/create_release.sh", 51 | 52 | "windows": { 53 | "command": "${workspaceFolder}/projectFolder/scripts/windows/create_release.ps1" 54 | } 55 | }, 56 | // Run tasks 57 | { 58 | "label": "run_debug_build", 59 | "type": "shell", 60 | "presentation": { 61 | "focus": true, 62 | "showReuseMessage": false 63 | }, 64 | "group": { 65 | "kind": "build" 66 | }, 67 | 68 | "command": "${workspaceFolder}/build/Debug/bin/cpp-cmake-template" 69 | }, 70 | { 71 | "label": "run_release_build", 72 | "type": "shell", 73 | "presentation": { 74 | "focus": true, 75 | "showReuseMessage": false 76 | }, 77 | "group": { 78 | "kind": "build" 79 | }, 80 | 81 | "command": "${workspaceFolder}/build/Release/bin/cpp-cmake-template" 82 | }, 83 | // Test tasks 84 | { 85 | "label": "run_tests", 86 | "type": "shell", 87 | "presentation": { 88 | "focus": true, 89 | "showReuseMessage": false 90 | }, 91 | "group": { 92 | "kind": "build" 93 | }, 94 | 95 | "command": "${workspaceFolder}/projectFolder/scripts/run_tests.sh", 96 | 97 | "windows": { 98 | "command": "${workspaceFolder}/projectFolder/scripts/windows/run_tests.ps1" 99 | } 100 | }, 101 | { 102 | "label": "run_tests_in_Docker", 103 | "type": "shell", 104 | "presentation": { 105 | "focus": true, 106 | "showReuseMessage": false 107 | }, 108 | "group": { 109 | "kind": "build" 110 | }, 111 | 112 | "command": "${workspaceFolder}/projectFolder/scripts/run_tests_in_Docker.sh" 113 | }, 114 | // Other tasks 115 | { 116 | "label": "format_code", 117 | "type": "shell", 118 | "presentation": { 119 | "focus": true, 120 | "showReuseMessage": false 121 | }, 122 | "group": { 123 | "kind": "build" 124 | }, 125 | 126 | "command": "${workspaceFolder}/projectFolder/scripts/format.sh" 127 | } 128 | ] 129 | } 130 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:12-slim 2 | 3 | # ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | # Adding all required packages to the image 6 | RUN apt-get update \ 7 | && apt-get install -y \ 8 | apt-utils \ 9 | git \ 10 | build-essential \ 11 | libtool autoconf pkg-config \ 12 | clang \ 13 | llvm \ 14 | libc++-dev libc++abi-dev \ 15 | cmake \ 16 | libboost-all-dev \ 17 | ccache \ 18 | && apt-get clean && rm -rf /var/lib/apt/lists/* 19 | 20 | # Enviroment variables 21 | ENV CC=/usr/bin/clang 22 | ENV CXX=/usr/bin/clang++ 23 | 24 | # Variable that can be overwritten at build time of the image 25 | ARG TEST_VAR_DOCKERFILE=test 26 | 27 | # ↑ Setup build environment 28 | # ↓ Build and compile project 29 | 30 | # Copying the actual folder of the project into the cointainer filesystem 31 | COPY projectFolder /projectFolder 32 | 33 | # Setting the working directory inside the project folder 34 | WORKDIR / 35 | 36 | RUN projectFolder/scripts/create_release.sh withTests 37 | 38 | # The command that is run by default when the container starts 39 | CMD ["./projectFolder/scripts/run_tests.sh", "Release"] 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![main](https://github.com/mortinger91/cpp-cmake-template/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/mortinger91/cpp-cmake-template/actions/workflows/main.yml) 2 | 3 | # cpp-cmake-template 4 | 5 | ## Template for a multi-platform C++ CMake project 6 | 7 | ### Main features: 8 | - CMake files 9 | - CTest unit tests framework 10 | - Github Actions that build and run the tests 11 | - Scripts and VSCode files for ease of use 12 | - Dockerfile for building and running the tests in a container 13 | - Support for compiler cache tools: ccache and sccache 14 | - Formatting via clang-format (that runs automatically in a git pre-commit hook) 15 | 16 | ### Requirements: 17 | - CMake 18 | - Clang 19 | - LLDB 20 | - clang-format 21 | - *(optional for compiler cache)* ccache 22 | - *(optional for IDE debugging)* VSCode 'CodeLLDB' extension 23 | - *(only on Windows)* MSVC compiler 24 | 25 | ## Instructions 26 | 27 | ### How to set up the project: 28 | Execute VSCode task `build_cmake_debug` or run this script: 29 | 30 | ./projectFolder/scripts/build_cmake_debug.sh 31 | 32 | This needs to run the first time and any time you make 33 | changes to a CMake file or add new source or test files. 34 | 35 | ### How to build the project: 36 | Execute VSCode task `build_debug` or run this script: 37 | 38 | ./projectFolder/scripts/build_debug.sh 39 | 40 | To build for release execute VSCode task `create_release` or run this script: 41 | 42 | ./projectFolder/scripts/create_release.sh 43 | 44 | All the artifacts will be in the build/ directory. 45 | 46 | ### How to debug the project: 47 | Launch VSCode `DEBUG` configuration for IDE debugging. 48 | If you want to debug in the command line run: 49 | 50 | lldb ./build/Debug/bin/cpp-cmake-template 51 | 52 | ### How to run the tests: 53 | Execute VSCode task `run_tests` or run this script: 54 | 55 | ./projectFolder/scripts/run_tests.sh 56 | 57 | If you want to build and run the tests in a fresh Docker container 58 | execute VSCode task `run_tests_in_Docker` or run this script: 59 | 60 | ./projectFolder/scripts/run_tests_in_Docker.sh 61 | 62 | ### How to add new files to the project: 63 | Add any new source file to a `projectFolder/src/dir` directory. 64 | You can create any new `projectFolder/src/dir` directories. 65 | Add any new test file to the `projectFolder/tests` directory. 66 | Both these operations does not require any changes to CMake files. 67 | 68 | __Remember to re-run `build_cmake_debug` after you have added new files or directories.__ 69 | 70 | ### Windows: 71 | Use the same VSCode tasks listed above or run the scripts in 72 | this directory: `projectFolder/scripts/windows`. 73 | 74 | ## Possible future improvements: 75 | 76 | - Fix Windows built files path 77 | - Static checker (BASH and cpp) 78 | - E2E tests (other than just building, also running the binary to see if there are any run-time errors) 79 | - CMake `install` to install the release build in the system 80 | - Better support for 3rd parties libraries 81 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | dev-container: 3 | 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | container_name: dev-container 8 | tty: true 9 | 10 | environment: 11 | - TEST_VAR_COMPOSE=test 12 | 13 | ports: 14 | - 8080:5555 15 | -------------------------------------------------------------------------------- /projectFolder/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # v3.19 is the miminum required version because 2 | # lower versions do not support presets 3 | cmake_minimum_required(VERSION 3.19 FATAL_ERROR) 4 | 5 | message("Looking for compiler caching tool") 6 | find_program(CCACHE "ccache") 7 | if(CCACHE) 8 | set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE}) 9 | set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) 10 | message("${CCACHE} will be used as the compiler caching tool") 11 | else() 12 | find_program(SCCACHE "sccache") 13 | if(SCCACHE) 14 | set(CMAKE_C_COMPILER_LAUNCHER ${SCCACHE}) 15 | set(CMAKE_CXX_COMPILER_LAUNCHER ${SCCACHE}) 16 | message("${SCCACHE} will be used as the compiler caching tool") 17 | else() 18 | message("Neither ccache nor sccache have been found in the system. No compiler caching tool will be used") 19 | endif(SCCACHE) 20 | endif(CCACHE) 21 | 22 | # Project 23 | set(PROJECT_DESCRIPTION "Template CMake C++") 24 | set(ONLINE_REPOSITORY "https://github.com/mortinger91/cpp-cmake-template") 25 | project( 26 | "cpp-cmake-template" 27 | DESCRIPTION ${PROJECT_DESCRIPTION} 28 | HOMEPAGE_URL ${ONLINE_REPOSITORY} 29 | ) 30 | 31 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 32 | # Clang compile commands are generated here: 33 | # build/Debug/compile_commands.json 34 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 35 | set(CMAKE_EXPORT_COMPILE_COMMANDS 1) 36 | endif() 37 | 38 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 39 | 40 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 41 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/int") 42 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/lib") 43 | 44 | add_executable(${PROJECT_NAME} src/main.cpp) 45 | add_subdirectory(src) 46 | target_link_libraries( 47 | ${PROJECT_NAME} 48 | PRIVATE 49 | ${PROJECT_NAME}_LIB 50 | ) 51 | 52 | option(BUILD_TESTS "Build tests" ON) 53 | if(BUILD_TESTS) 54 | add_subdirectory(tests) 55 | endif() 56 | -------------------------------------------------------------------------------- /projectFolder/CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 21, 6 | "patch": 3 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "Debug", 11 | "binaryDir": "${sourceDir}/../build/Debug", 12 | "cacheVariables": { 13 | "CMAKE_BUILD_TYPE": "Debug" 14 | } 15 | }, 16 | { 17 | "name": "Release", 18 | "binaryDir": "${sourceDir}/../build/Release", 19 | "cacheVariables": { 20 | "CMAKE_BUILD_TYPE": "Release" 21 | } 22 | } 23 | ], 24 | "buildPresets": [ 25 | { 26 | "name": "Debug", 27 | "configurePreset": "Debug" 28 | }, 29 | { 30 | "name": "Release", 31 | "configurePreset": "Release" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /projectFolder/scripts/build_cmake_debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | 3 | # Script used to build CMake Debug files 4 | 5 | echo "Started building Debug CMake files..." 6 | 7 | # Clearing build folder if it exists 8 | rm -r build/debug &> /dev/null 9 | 10 | echo "Installing pre-commit hook" 11 | rm ./.git/hooks/pre-commit 12 | cp ./projectFolder/scripts/git_hooks/pre-commit ./.git/hooks/pre-commit 13 | chmod +x ./.git/hooks/pre-commit 14 | 15 | cmake -SprojectFolder --preset Debug -DCMAKE_CXX_COMPILER=/usr/bin/clang++ -DCMAKE_C_COMPILER=/usr/bin/clang 16 | RESULT=$? 17 | echo "Finished building Debug CMake files!" 18 | exit $RESULT 19 | -------------------------------------------------------------------------------- /projectFolder/scripts/build_debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script used to build the project in Debug mode. 4 | # build_cmake_debug.sh must be run before this one: 5 | # - the first time the project is built 6 | # - whenever a new file is added or removed 7 | 8 | if [ "$(uname -s)" == "Linux" ]; then 9 | cpu_count=$(nproc) 10 | elif [ "$(uname -s)" == "Darwin" ]; then 11 | cpu_count=$(sysctl -n hw.ncpu) 12 | else 13 | exit 1 14 | fi 15 | 16 | echo "Bulding Debug version using ${cpu_count} threads..." 17 | cmake --build build/Debug/ -j$cpu_count 18 | RESULT=$? 19 | if [ $RESULT == 0 ]; then 20 | echo "SUCCESS: Finished building Debug!" 21 | else 22 | echo "FAIL: Error while building Debug!" 23 | fi 24 | exit $RESULT 25 | -------------------------------------------------------------------------------- /projectFolder/scripts/create_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script used to build CMake Release files. 4 | # Calling this script without args will not build tests. 5 | # To build test call this script with "withTests" argument 6 | 7 | echo "Started building Release CMAKE files..." 8 | 9 | # Clearing build folder if it exists 10 | rm -r build/Release &> /dev/null 11 | 12 | echo -n "Deciding if tests need to be built... " 13 | if [ -n "$1" ]; then 14 | if [ "$1" == "withTests" ]; then 15 | BUILD_TESTS="ON" 16 | echo "YES" 17 | fi 18 | else 19 | BUILD_TESTS="OFF" 20 | echo "NO" 21 | fi 22 | 23 | cmake -SprojectFolder --preset Release -DBUILD_TESTS=$BUILD_TESTS 24 | RESULT=$? 25 | if [ "$RESULT" -ne 0 ]; then 26 | exit $RESULT 27 | fi 28 | 29 | echo "Finished building Release CMAKE files!" 30 | 31 | if [ "$(uname -s)" == "Linux" ]; then 32 | cpu_count=$(nproc) 33 | elif [ "$(uname -s)" == "Darwin" ]; then 34 | cpu_count=$(sysctl -n hw.ncpu) 35 | else 36 | exit 1 37 | fi 38 | 39 | echo "Bulding Release version using ${cpu_count} threads..." 40 | cmake --build build/Release/ -j$cpu_count 41 | RESULT=$? 42 | if [ $RESULT == 0 ]; then 43 | echo "SUCCESS: Finished building Release!" 44 | else 45 | echo "FAIL: Error while building Release!" 46 | fi 47 | exit $RESULT 48 | -------------------------------------------------------------------------------- /projectFolder/scripts/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find projectFolder/src/ -iname *.h -o -iname *.cpp | xargs clang-format -i 4 | 5 | find projectFolder/tests/ -iname *.h -o -iname *.cpp | xargs clang-format -i 6 | 7 | # Check if any changes were made by clang-format 8 | if git diff --exit-code; then 9 | echo "Formatting is ok, no changes applied" 10 | else 11 | echo "Formatting is wrong, changes have been applied" 12 | exit 1 13 | fi 14 | -------------------------------------------------------------------------------- /projectFolder/scripts/git_hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Relative path to your custom script within your project folder 4 | custom_script_path="./projectFolder/scripts/format.sh" 5 | 6 | echo "pre-commit hook: Running ${custom_script_path}" 7 | 8 | # Run the custom script 9 | if [ -f "$custom_script_path" ]; then 10 | # Navigate to the project root directory before running the script 11 | project_root="$(git rev-parse --show-toplevel)" 12 | cd "$project_root" || exit 13 | 14 | # Run the custom script 15 | sh "$custom_script_path" 16 | 17 | # Check the exit status of the script 18 | if [ $? -ne 0 ]; then 19 | echo "Formatting script made changes. Re-stage modified files" 20 | exit 1 21 | fi 22 | else 23 | echo "${custom_script_path} not found. Commit aborted." 24 | exit 1 25 | fi 26 | -------------------------------------------------------------------------------- /projectFolder/scripts/run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script used to run tests. 4 | # Specify whether you want Release or Debug tests to be run. 5 | # Passing no args will run Debug tests 6 | 7 | echo "Deciding tests build type..." 8 | if [ -n "$1" ]; then 9 | if [ "$1" == "Debug" ] || [ "$1" == "Release" ]; then 10 | BUILD_TYPE="$1" 11 | else 12 | echo "Wrong build type argument" 13 | exit 1 14 | fi 15 | else 16 | BUILD_TYPE="Debug" 17 | fi 18 | echo "Picked build type: $BUILD_TYPE for tests" 19 | 20 | if [ "$(uname -s)" == "Linux" ]; then 21 | cpu_count=$(nproc) 22 | elif [ "$(uname -s)" == "Darwin" ]; then 23 | cpu_count=$(sysctl -n hw.ncpu) 24 | else 25 | exit 1 26 | fi 27 | 28 | echo "Running tests using ${cpu_count} threads..." 29 | ctest --output-on-failure --test-dir build/$BUILD_TYPE/tests -j$cpu_count 30 | RESULT=$? 31 | if [ $RESULT == 0 ]; then 32 | echo "SUCCESS: Tests completed successfully!" 33 | else 34 | echo "FAIL: Error while running the tests!" 35 | fi 36 | exit $RESULT 37 | -------------------------------------------------------------------------------- /projectFolder/scripts/run_tests_in_Docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -xe 2 | 3 | echo "Running tests in a docker container" 4 | 5 | docker compose down --volumes --rmi all 6 | docker compose up 7 | -------------------------------------------------------------------------------- /projectFolder/scripts/windows/build_cmake_debug.ps1: -------------------------------------------------------------------------------- 1 | # Script used to build CMake Debug files 2 | 3 | Write-Host "Started building Debug CMake files..." 4 | 5 | # Clearing build folder if it exists 6 | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue "build/debug" 7 | 8 | cmake -S projectFolder --preset Debug -G "Visual Studio 16 2019" -A x64 9 | 10 | Write-Host "Finished building Debug CMake files!" 11 | -------------------------------------------------------------------------------- /projectFolder/scripts/windows/build_debug.ps1: -------------------------------------------------------------------------------- 1 | # Script used to build the project in Debug mode. 2 | # build_cmake_debug.ps1 must be run before this one: 3 | # - the first time the project is built 4 | # - whenever a new file is added or removed 5 | 6 | try { 7 | $cpu_count = (Get-WmiObject -Class Win32_ComputerSystem).NumberOfLogicalProcessors 8 | } 9 | catch { 10 | $cpu_count = 1 11 | } 12 | 13 | Write-Host "Building Debug version using $($cpu_count) threads..." 14 | cmake --build build/Debug/ -j $cpu_count 15 | Write-Host "Finished building Debug!" 16 | -------------------------------------------------------------------------------- /projectFolder/scripts/windows/create_release.ps1: -------------------------------------------------------------------------------- 1 | # Script used to build CMake Release files. 2 | # Calling this script without args will not build tests. 3 | # To build tests, call this script with "withTests" argument 4 | 5 | Write-Host "Started building Release CMake files..." 6 | 7 | # Clearing build folder if it exists 8 | Remove-Item -Path "build/Release" -Recurse -ErrorAction SilentlyContinue 9 | 10 | Write-Host "Deciding if tests need to be built..." 11 | if ($args.Length -gt 0) { 12 | if ($args[0] -eq "withTests") { 13 | $BUILD_TESTS = "ON" 14 | Write-Host "Building with tests" 15 | } 16 | } else { 17 | $BUILD_TESTS = "OFF" 18 | Write-Host "Building without tests" 19 | } 20 | 21 | cmake -S projectFolder --preset Release -DBUILD_TESTS=$BUILD_TESTS 22 | 23 | Write-Host "Finished building Release CMake files!" 24 | 25 | try { 26 | $cpu_count = (Get-WmiObject -Class Win32_ComputerSystem).NumberOfLogicalProcessors 27 | } 28 | catch { 29 | $cpu_count = 1 30 | } 31 | 32 | Write-Host "Building Release version using $cpu_count threads..." 33 | cmake --build "build/Release" -j $cpu_count 34 | Write-Host "Finished building Release!" 35 | -------------------------------------------------------------------------------- /projectFolder/scripts/windows/run_tests.ps1: -------------------------------------------------------------------------------- 1 | # Script used to run tests. 2 | # Specify whether you want Release or Debug tests to be run. 3 | # Passing no args will run Debug tests 4 | 5 | Write-Host "Deciding tests build type..." 6 | 7 | if ($args.Length -gt 0) { 8 | $buildType = $args[0] 9 | if ($buildType -eq "Debug" -or $buildType -eq "Release") { 10 | $BUILD_TYPE = $buildType 11 | } else { 12 | Write-Host "Wrong build type argument" 13 | exit 1 14 | } 15 | } else { 16 | $BUILD_TYPE = "Debug" 17 | } 18 | 19 | Write-Host "Picked build type: $BUILD_TYPE for tests" 20 | 21 | try { 22 | $cpu_count = (Get-WmiObject -Class Win32_ComputerSystem).NumberOfLogicalProcessors 23 | } 24 | catch { 25 | $cpu_count = 1 26 | } 27 | 28 | Write-Host "Running tests using $cpu_count threads..." 29 | ctest --output-on-failure --test-dir "build/$BUILD_TYPE/tests" -j $cpu_count 30 | $RESULT = $LASTEXITCODE 31 | Write-Host "Finished running tests..." 32 | exit $RESULT 33 | -------------------------------------------------------------------------------- /projectFolder/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add here any file that you want to be part of the library target, 2 | # but it is not in a projectFolder/src/dir directory 3 | set(sources_list 4 | # anotherFolder/myfile.cpp 5 | ) 6 | # Get all the .cpp files in any projectFolder/src/dir directory 7 | file(GLOB directories */) 8 | foreach(dir ${directories}) 9 | if(IS_DIRECTORY ${dir}) 10 | string(FIND ${dir} "/" last_slash_pos REVERSE) 11 | math(EXPR string_start "${last_slash_pos}+1") 12 | string(SUBSTRING ${dir} ${string_start} -1 dir_stripped) 13 | file(GLOB_RECURSE sources ${dir_stripped}/*.cpp) 14 | list(APPEND sources_list ${sources}) 15 | endif() 16 | endforeach() 17 | # Create a library target that contains all of the sources 18 | add_library(${PROJECT_NAME}_LIB ${sources_list}) 19 | # Add the include directories to the library target. 20 | # Remember to add any include that is not in a projectFolder/src/dir directory 21 | target_include_directories( 22 | ${PROJECT_NAME}_LIB 23 | PUBLIC 24 | ${CMAKE_SOURCE_DIR}/src 25 | ) 26 | 27 | # Define the compiler flags based on the build type 28 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 29 | set(COMPILER_FLAGS 30 | -Wall 31 | -O0 32 | ) 33 | if(UNIX) 34 | list(APPEND COMPILER_FLAGS 35 | -Werror 36 | -Wpedantic 37 | -Wextra 38 | ) 39 | endif() 40 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release") 41 | set(COMPILER_FLAGS 42 | -DNDEBUG 43 | ) 44 | if(UNIX) 45 | list(APPEND COMPILER_FLAGS 46 | -O3 47 | -march=native 48 | -ffast-math 49 | ) 50 | endif() 51 | endif() 52 | # Set the compiler flags for the library target 53 | target_compile_options( 54 | ${PROJECT_NAME}_LIB 55 | PUBLIC 56 | ${COMPILER_FLAGS} 57 | ) 58 | 59 | # Define the preprocessor macros based on the build type. 60 | # This is equivalent to using #define in the source code 61 | # or using -D flags as a compiler option 62 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 63 | set(PREPROCESSOR_MACROS 64 | DEBUG 65 | ) 66 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release") 67 | set(PREPROCESSOR_MACROS 68 | RELEASE 69 | ) 70 | endif() 71 | if(LINUX) 72 | list(APPEND PREPROCESSOR_MACROS 73 | LINUX 74 | ) 75 | elseif(APPLE) 76 | list(APPEND PREPROCESSOR_MACROS 77 | MACOS 78 | ) 79 | elseif(WIN32) 80 | list(APPEND PREPROCESSOR_MACROS 81 | WINDOWS 82 | ) 83 | endif() 84 | # Set the preprocessor macros for the library target 85 | target_compile_definitions( 86 | ${PROJECT_NAME}_LIB 87 | PUBLIC 88 | ${PREPROCESSOR_MACROS} 89 | ) 90 | 91 | # Set the C++ language features for the library target 92 | target_compile_features( 93 | ${PROJECT_NAME}_LIB 94 | PUBLIC 95 | c_std_11 96 | cxx_std_17 97 | ) 98 | # Ensure that the C++ standard is mandatory 99 | # and disable compiler-specific extensions 100 | set_target_properties( 101 | ${PROJECT_NAME}_LIB 102 | PROPERTIES 103 | CXX_STANDARD_REQUIRED ON 104 | CXX_EXTENSIONS OFF 105 | ) 106 | 107 | # Define the linker options based on the build type 108 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 109 | set(LINKER_OPTIONS 110 | ) 111 | elseif(CMAKE_BUILD_TYPE STREQUAL "Release") 112 | set(LINKER_OPTIONS 113 | ) 114 | if(UNIX) 115 | list(APPEND LINKER_OPTIONS 116 | -flto # Enable link-time optimizations 117 | ) 118 | endif() 119 | if(LINUX) 120 | list(APPEND LINKER_OPTIONS 121 | -Wl,--strip-all # Strip debug symbols 122 | ) 123 | endif() 124 | endif() 125 | if(LINUX) 126 | list(APPEND LINKER_OPTIONS 127 | -Wl,--no-undefined # Fail if there are any unresolved symbols in the final linked output 128 | ) 129 | endif() 130 | # Set the linker options for the library target 131 | target_link_options( 132 | ${PROJECT_NAME}_LIB 133 | PUBLIC 134 | ${LINKER_OPTIONS} 135 | ) 136 | 137 | target_link_libraries( 138 | ${PROJECT_NAME}_LIB 139 | PUBLIC 140 | # Add libraries to link to the binary here 141 | ) 142 | -------------------------------------------------------------------------------- /projectFolder/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "utility/utility.h" 2 | #include 3 | 4 | int main() 5 | { 6 | std::cout << Utility::HelloWorld() << std::endl; 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /projectFolder/src/utility/utility.cpp: -------------------------------------------------------------------------------- 1 | #include "utility.h" 2 | 3 | namespace Utility 4 | { 5 | std::string HelloWorld() 6 | { 7 | return "Hello World!"; 8 | } 9 | } // namespace Utility 10 | -------------------------------------------------------------------------------- /projectFolder/src/utility/utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Utility 6 | { 7 | std::string HelloWorld(); 8 | } 9 | -------------------------------------------------------------------------------- /projectFolder/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | 3 | file(GLOB_RECURSE test_cpp_files *.cpp) 4 | 5 | foreach(test_cpp_file ${test_cpp_files}) 6 | string(FIND ${test_cpp_file} "/" last_slash_pos REVERSE) 7 | math(EXPR string_start "${last_slash_pos}+1") 8 | string(SUBSTRING ${test_cpp_file} ${string_start} -1 test_cpp_file_stripped) 9 | string(LENGTH ${test_cpp_file_stripped} str_len) 10 | math(EXPR new_str_len "${str_len}-4") 11 | string(SUBSTRING ${test_cpp_file_stripped} 0 ${new_str_len} test_name) 12 | 13 | add_executable(${test_name} ${test_cpp_file}) 14 | target_link_libraries(${test_name} ${PROJECT_NAME}_LIB) 15 | add_test(${test_name} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test_name}) 16 | endforeach() 17 | -------------------------------------------------------------------------------- /projectFolder/tests/test.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WIN32 9 | #include 10 | #endif 11 | namespace test 12 | { 13 | void assertVectorContentIsEqual(std::vector x, std::vector y) 14 | { 15 | std::string errorStringSize = 16 | "size of the vectors: size of the first one: " + 17 | std::to_string(x.size()) + 18 | " size of the second one: " + std::to_string(y.size()); 19 | std::cout << errorStringSize << std::endl; 20 | assert(x.size() == y.size() && 21 | "assertVectorContentIsEqual failed: the vectors does not have " 22 | "the same size!"); 23 | for (size_t i = 0; i < x.size(); i++) 24 | { 25 | if (x[i] != y[i]) 26 | { 27 | std::string errorString = 28 | "vectors' elements at index: " + std::to_string(i) + 29 | " do not match! first: " + std::to_string(x[i]) + 30 | " second: " + std::to_string(y[i]); 31 | std::cout << errorString << std::endl; 32 | } 33 | assert(x[i] == y[i] && 34 | "assertVectorContentIsEqual failed: vectors' elements do " 35 | "not match!"); 36 | } 37 | } 38 | } // namespace test 39 | 40 | #ifdef _WIN32 41 | #define assertTrue(expr) \ 42 | if (!(expr)) \ 43 | { \ 44 | std::cerr << "Assertion failed: " << #expr << " in " << __FILE__ \ 45 | << " line " << __LINE__ << std::endl; \ 46 | ExitProcess(1); \ 47 | } 48 | #else 49 | #define assertTrue(expr) \ 50 | if (!(expr)) \ 51 | { \ 52 | std::cerr << "Assertion failed: " << #expr << " in " << __FILE__ \ 53 | << " line " << __LINE__ << std::endl; \ 54 | std::abort(); \ 55 | } 56 | #endif 57 | 58 | #define ASSERT_THROW(condition) \ 59 | { \ 60 | if (!(condition)) \ 61 | { \ 62 | throw std::runtime_error( \ 63 | std::string(__FILE__) + std::string(":") + \ 64 | std::to_string(__LINE__) + std::string(" in ") + \ 65 | std::string(__PRETTY_FUNCTION__)); \ 66 | } \ 67 | } 68 | 69 | #define ASSERT_EQUAL(x, y) \ 70 | { \ 71 | if ((x) != (y)) \ 72 | { \ 73 | throw std::runtime_error( \ 74 | std::string(__FILE__) + std::string(":") + \ 75 | std::to_string(__LINE__) + std::string(" in ") + \ 76 | std::string(__PRETTY_FUNCTION__) + std::string(": ") + \ 77 | std::to_string((x)) + std::string(" != ") + \ 78 | std::to_string((y))); \ 79 | } \ 80 | } 81 | -------------------------------------------------------------------------------- /projectFolder/tests/test_cpp17.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | 4 | void testCpp17() 5 | { 6 | std::unique_ptr ptr; 7 | ptr = std::make_unique(10); 8 | assertTrue(*ptr == 10); 9 | } 10 | 11 | int main() 12 | { 13 | testCpp17(); 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /projectFolder/tests/test_platforms.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | 4 | void testValidPlatform() 5 | { 6 | std::string platform = ""; 7 | #ifdef LINUX 8 | platform = "Linux"; 9 | #endif 10 | #ifdef MACOS 11 | platform = "macOS"; 12 | #endif 13 | #ifdef WINDOWS 14 | platform = "Windows"; 15 | #endif 16 | assertTrue(platform != ""); 17 | std::cout << "Platform detected: " << platform << std::endl; 18 | } 19 | 20 | int main() 21 | { 22 | testValidPlatform(); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /projectFolder/tests/test_utility.cpp: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include "utility/utility.h" 3 | 4 | void testHelloWorld() 5 | { 6 | assertTrue(Utility::HelloWorld() == "Hello World!"); 7 | assertTrue(Utility::HelloWorld() != "I am Batman!"); 8 | } 9 | 10 | int main() 11 | { 12 | testHelloWorld(); 13 | 14 | return 0; 15 | } 16 | --------------------------------------------------------------------------------