├── .gitignore ├── LICENSE.md ├── README.md ├── cookiecutter.json └── {{cookiecutter.project_slug}} ├── .gitignore ├── CMakeLists.txt ├── README.md ├── cmake ├── base.cmake ├── cache.cmake ├── compilers.cmake ├── conan.cmake ├── project_options.cmake ├── sanitizers.cmake ├── static_analyzers.cmake └── warnings.cmake └── src ├── CMakeLists.txt └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © `2020` 5 | `Tarcísio Eduardo Moreira Crocomo ` 6 | 7 | Permission is hereby granted, free of charge, to any person 8 | obtaining a copy of this software and associated documentation 9 | files (the “Software”), to deal in the Software without 10 | restriction, including without limitation the rights to use, 11 | copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the 13 | Software is furnished to do so, subject to the following 14 | conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | OTHER DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | C++ Cookiecutter Template 2 | ========================= 3 | 4 | This is a [cookiecutter](https://github.com/cookiecutter/cookiecutter) template 5 | for a C++ project using [CMake](https://cmake.org) and [Conan](conan.io). It 6 | is heavily influenced by a [similar project by Jason 7 | Turner](https://github.com/lefticus/cpp_starter_project). The main differences 8 | are: 9 | 10 | - This is a cookiecutter template. 11 | - This has no sample code other than an empty `main.cpp`. 12 | - This has no default libraries already defined as dependencies. 13 | 14 | 15 | How to use 16 | ========== 17 | 18 | First, setup your own project with `cookiecutter`: 19 | 20 | ```bash 21 | cookiecutter gh:tarcisioe/cpp-template 22 | ``` 23 | 24 | This will create a directory with the name you defined in `project_slug`. 25 | Enter that directory, then define the dependencies you wish to download and 26 | build with `conan` in `CMakeLists.txt` by uncommenting `PACKAGES` and listing 27 | them right after that: 28 | 29 | ```cmake 30 | conan( 31 | PACKAGES 32 | fmt/0.6.10 33 | ) 34 | ``` 35 | 36 | Finally, create your build directory: 37 | 38 | ```bash 39 | cmake -B build 40 | ``` 41 | 42 | To link your dependencies, add them to your targets (e.g. `main` in 43 | `src/CMakeLists.txt`) like this: 44 | 45 | ```cmake 46 | target_link_libraries( 47 | main 48 | PUBLIC 49 | (...) 50 | CONAN_PKG::fmt 51 | ) 52 | ``` 53 | 54 | Then, build your project: 55 | 56 | ```bash 57 | cmake --build build 58 | ``` 59 | 60 | Enjoy your project with your dependencies, a compile database already generated, 61 | warnings enabled, etc. :) 62 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "Cpp Template", 3 | "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}" 4 | } 5 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project({{cookiecutter.project_slug}} CXX) 3 | 4 | include(cmake/base.cmake) 5 | include(cmake/project_options.cmake) 6 | include(cmake/conan.cmake) 7 | 8 | conan( 9 | # PACKAGES 10 | ) 11 | 12 | add_subdirectory(src) 13 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/README.md: -------------------------------------------------------------------------------- 1 | {{cookiecutter.project_name}} 2 | {% for _ in cookiecutter.project_name %}={% endfor %} 3 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cmake/base.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | # Enable compile database for tools that rely on it. 4 | # This creates a build/compile_commands.json that informs tools such as 5 | # autocomplete or linter of compiler settings for each file. 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 7 | 8 | # Set a default build type if none was provided. 9 | if(NOT CMAKE_BUILD_TYPE) 10 | message(STATUS "No build type specified. Defaulting to Debug.") 11 | set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type." FORCE) 12 | set_property( 13 | CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 14 | "Debug" 15 | "Release" 16 | "MinSizeRel" 17 | "RelWithDebInfo" 18 | FORCE 19 | ) 20 | endif() 21 | 22 | include(cmake/cache.cmake) 23 | include(cmake/static_analyzers.cmake) 24 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cmake/cache.cmake: -------------------------------------------------------------------------------- 1 | option(ENABLE_CCACHE "Enable ccache if available." ON) 2 | if(NOT ENABLE_CCACHE) 3 | return() 4 | endif() 5 | 6 | set(CACHE_OPTION "ccache" CACHE STRING "Compiler cache to be used.") 7 | set(CACHE_OPTION_VALUES "ccache" "sccache") 8 | set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) 9 | list(FIND CACHE_OPTION_VALUES ${CACHE_OPTION} CACHE_OPTION_INDEX) 10 | 11 | if(${CACHE_OPTION_INDEX} EQUAL -1) 12 | message(STATUS "Using custom compiler cache system: ${CACHE_OPTION}, explicitly supported entries are ${CACHE_OPTION_VALUES}") 13 | endif() 14 | 15 | find_program(CACHE_BINARY ${CACHE_OPTION}) 16 | if(CACHE_BINARY) 17 | message(STATUS "${CACHE_OPTION} found and enabled.") 18 | set(CMAKE_CXX_COMPILER_LAUNCHER ${CACHE_BINARY}) 19 | else() 20 | message(WARNING "${CACHE_OPTION} is enabled but was not found. Not using it.") 21 | endif() 22 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cmake/compilers.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | # Define variables for each supported compiler, so that checking is easier 4 | # later. 5 | 6 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 7 | set(USING_CLANG TRUE) 8 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 9 | set(USING_GCC TRUE) 10 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 11 | set(USING_MSVC TRUE) 12 | endif() 13 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cmake/conan.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | set(CONAN_CMAKE_PATH "${CMAKE_BINARY_DIR}/conan.cmake") 4 | set(CONAN_CMAKE_URL "https://github.com/conan-io/cmake-conan/raw/v0.16.1/conan.cmake") 5 | 6 | function(ensure_conan_cmake) 7 | # Ensure conan.cmake is downloaded. 8 | if(NOT EXISTS "${CONAN_CMAKE_PATH}") 9 | message(STATUS "Downloading conan.cmake from ${CONAN_CMAKE_URL}") 10 | file(DOWNLOAD "${CONAN_CMAKE_URL}" "${CONAN_CMAKE_PATH}") 11 | endif() 12 | endfunction() 13 | 14 | macro(run_conan) 15 | # Run conan. This needs to be a macro because `conan_cmake_run` includes 16 | # files. 17 | # 18 | # Keyword args: 19 | # PACKAGES: List of packages to download with Conan. 20 | # EXTRA_OPTIONS: Extra options for conan. 21 | 22 | # Parse keyword arguments 23 | cmake_parse_arguments( 24 | CONAN 25 | "" 26 | "" 27 | "PACKAGES;EXTRA_OPTIONS" 28 | ${ARGN} 29 | ) 30 | 31 | message("${PACKAGES}") 32 | 33 | conan_cmake_run( 34 | REQUIRES 35 | ${CONAN_PACKAGES} 36 | OPTIONS 37 | ${CONAN_EXTRA_OPTIONS} 38 | BASIC_SETUP 39 | CMAKE_TARGETS 40 | BUILD 41 | missing 42 | ) 43 | endmacro() 44 | 45 | macro(conan) 46 | # Wrapper to run conan ensuring that `conan.cmake` is present. 47 | # Arguments are forwarded to `run_conan`. 48 | 49 | ensure_conan_cmake() 50 | include("${CONAN_CMAKE_PATH}") 51 | run_conan(${ARGN}) 52 | endmacro() 53 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cmake/project_options.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | # A target that has the basic compiler options we wish to set for every other 4 | # target. 5 | # This doesn't produce any object, but transitively enables important options 6 | # for the projects when "linked" against, without setting them globally. 7 | add_library( 8 | project_options 9 | INTERFACE 10 | ) 11 | 12 | # Enable the latest complete C++ standard. 13 | target_compile_features( 14 | project_options 15 | INTERFACE 16 | cxx_std_20 17 | ) 18 | 19 | # Enable the base warnings. 20 | include(cmake/warnings.cmake) 21 | set_target_base_warnings(project_options) 22 | 23 | include(cmake/sanitizers.cmake) 24 | enable_sanitizers(project_options) 25 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cmake/sanitizers.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | option(SANITIZER_ADDRESS "Enable address sanitizer." FALSE) 4 | option(SANITIZER_UB "Enable undefined behaviour sanitizer." FALSE) 5 | option(SANITIZER_THREAD "Enable thread sanitizer." FALSE) 6 | 7 | include(cmake/compilers.cmake) 8 | 9 | function(enable_sanitizers target) 10 | # Enable sanitizers for a given target. 11 | # 12 | # Args: 13 | # target: the target to enable sanitizers for. 14 | 15 | if(NOT (USING_GCC OR USING_CLANG)) 16 | return() 17 | endif() 18 | 19 | # Collect enabled sanitizers 20 | set(sanitizer_list "") 21 | 22 | if(SANITIZER_ADDRESS) 23 | list(APPEND sanitizer_list "address") 24 | endif() 25 | 26 | if(SANITIZER_UB) 27 | list(APPEND sanitizer_list "undefined") 28 | endif() 29 | 30 | if(SANITIZER_THREAD) 31 | list(APPEND sanitizer_list "thread") 32 | endif() 33 | 34 | # Generate a proper value for the compiler flag 35 | list(JOIN sanitizer_list "," sanitizers) 36 | 37 | # Set options on the target. 38 | if(NOT "${sanitizers}" STREQUAL "") 39 | target_compile_options( 40 | ${target} 41 | INTERFACE 42 | -fsanitize=${sanitizers} 43 | ) 44 | target_link_libraries( 45 | ${target} 46 | INTERFACE 47 | -fsanitize=${sanitizers} 48 | ) 49 | endif() 50 | endfunction() 51 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cmake/static_analyzers.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | option(ENABLE_CPPCHECK "Enable static analysis with cppcheck" OFF) 4 | option(ENABLE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF) 5 | 6 | include(cmake/compilers.cmake) 7 | 8 | macro(enable_program) 9 | # Enable a cmake-supported program to run during build, such as static 10 | # analysers. 11 | # 12 | # Keyword args: 13 | # NAME: The name of the program 14 | # VARIABLE: The CMake variable to populate with the program's executable 15 | # path. 16 | # CMAKE_VARIABLE: The CMake variable to populate with the full command 17 | # line for the program (e.g. CMAKE_CXX_CLANG_TIDY). 18 | # FLAGS: A list of flags to be passed to the program. 19 | 20 | cmake_parse_arguments( 21 | PROGRAM 22 | "" 23 | "CMAKE_VARIABLE;NAME;VARIABLE" 24 | "FLAGS" 25 | ${ARGN} 26 | ) 27 | 28 | find_program(${PROGRAM_VARIABLE} ${PROGRAM_NAME}) 29 | 30 | if(${PROGRAM_VARIABLE}) 31 | set(${PROGRAM_CMAKE_VARIABLE} ${${PROGRAM_VARIABLE}} ${PROGRAM_FLAGS}) 32 | else() 33 | message(SEND_ERROR "${PROGRAM_NAME} requested but executable not found.") 34 | endif() 35 | endmacro() 36 | 37 | 38 | if(ENABLE_CPPCHECK) 39 | enable_program( 40 | NAME 41 | cppcheck 42 | VARIABLE 43 | CPPCHECK 44 | FLAGS 45 | --suppress=missingInclude --enable=all --inconclusive -i 46 | CMAKE_VARIABLE 47 | CMAKE_CXX_CPPCHECK 48 | ) 49 | endif() 50 | 51 | if(ENABLE_CLANG_TIDY) 52 | if(NOT USING_CLANG) 53 | # Warnings conflict when we use clang-tidy with GCC (or MSVC, maybe). 54 | message( 55 | SEND_ERROR 56 | "clang-tidy can only be enabled when clang is the compiler." 57 | ) 58 | else() 59 | enable_program( 60 | NAME 61 | clang-tidy 62 | VARIABLE 63 | CLANG_TIDY 64 | CMAKE_VARIABLE 65 | CMAKE_CXX_CLANG_TIDY 66 | ) 67 | endif() 68 | endif() 69 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/cmake/warnings.cmake: -------------------------------------------------------------------------------- 1 | include_guard() 2 | 3 | option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors." ON) 4 | 5 | include(cmake/compilers.cmake) 6 | 7 | # Thanks @lefticus for the translation of the MSVC warnings, phew! 8 | set(MSVC_BASE_WARNINGS 9 | # Baseline 10 | /W4 11 | 12 | # Keep sorted after this point 13 | /w14242 # 'identfier': conversion from 'type1' to 'type1', possible loss 14 | # of data 15 | /w14254 # 'operator': conversion from 'type1:field_bits' to 16 | # 'type2:field_bits', possible loss of data 17 | /w14263 # 'function': member function does not override any base class 18 | # virtual member function 19 | /w14265 # 'classname': class has virtual functions, but destructor is not 20 | # virtual instances of this class may not be destructed correctly 21 | /w14287 # 'operator': unsigned/negative constant mismatch 22 | /w14296 # 'operator': expression is always 'boolean_value' 23 | /w14311 # 'variable': pointer truncation from 'type1' to 'type2' 24 | /w14545 # expression before comma evaluates to a function which is missing 25 | # an argument list 26 | /w14546 # function call before comma missing argument list 27 | /w14547 # 'operator': operator before comma has no effect; expected 28 | # operator with side-effect 29 | /w14549 # 'operator': operator before comma has no effect; did you intend 30 | # 'operator'? 31 | /w14555 # expression has no effect; expected expression with side- effect 32 | /w14619 # pragma warning: there is no warning number 'number' 33 | /w14640 # Enable warning on thread un-safe static member initialization 34 | /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may 35 | # cause unexpected runtime behavior. 36 | /w14905 # Wide string literal cast to 'LPSTR' 37 | /w14906 # String literal cast to 'LPWSTR' 38 | /w14928 # Illegal copy-initialization; more than one user-define conversion 39 | # has been implicitly applied 40 | /we4289 # nonstandard extension used: 'variable': loop control variable 41 | # declared in the for-loop is used outside the for-loop scope 42 | ) 43 | 44 | set(CLANG_BASE_WARNINGS 45 | # Baseline 46 | -Wall 47 | -Wextra 48 | -Wpedantic # Warn if non-standard C++ is used. 49 | 50 | # Keep sorted after this point 51 | -Wconversion # Warn if type conversions may lose data. 52 | -Wdouble-promotion # Warn if float is being implicitly promoted to double. 53 | -Wformat=2 # Warn on security issues on printf-like formatting functions. 54 | -Wnon-virtual-dtor # Warn if a class with virtual member functions does not 55 | # have a virtual destructor. No "always make destructors 56 | # virtual"! 57 | -Wnull-dereference 58 | -Wold-style-cast # Warn on C-style casts (e.g. (int)x). 59 | -Woverloaded-virtual # Warn if a virtual function is overloaded. 60 | -Wshadow # Warn if a declaration shadows something from an outer scope. 61 | -Wsign-conversion # Warn on sign conversions. 62 | -Wunused # Warn on unused symbols. 63 | ) 64 | 65 | set(GCC_BASE_WARNINGS 66 | ${CLANG_BASE_WARNINGS} 67 | -Wduplicated-branches 68 | -Wduplicated-cond 69 | -Wlogical-op # Warn if logical operations are being used where bitwise is 70 | # likely wanted. 71 | -Wmisleading-indentation # Warn if indentation implies non-existent blocks. 72 | -Wuseless-cast # Warn if something is being cast to its own type. 73 | ) 74 | 75 | function(set_target_warnings target) 76 | # Set warnings for a target 77 | # 78 | # Args: 79 | # target: The target to set the warnings on. 80 | # Keyword args: 81 | # UNKNOWN_MESSAGE: A message to print as a warning if the compiler is unknown. 82 | # MSVC: The list of MSVC warning flags to set. 83 | # CLANG: The list of Clang warning flags to set. 84 | # GCC: The list of GCC warning flags to set. 85 | 86 | # Parse keyword arguments 87 | cmake_parse_arguments( 88 | PARSE_ARGV 89 | 1 90 | WARNINGS 91 | "" 92 | "TARGET;UNKNOWN_MESSAGE" 93 | "MSVC;CLANG;GCC" 94 | ) 95 | 96 | if(USING_MSVC) 97 | set(warnings ${WARNINGS_MSVC}) 98 | elseif(USING_CLANG) 99 | set(warnings ${WARNINGS_CLANG}) 100 | elseif(USING_GCC) 101 | set(warnings ${WARNINGS_GCC}) 102 | else() 103 | # Unknown compiler, warn user. 104 | message(WARNING "${UNKNOWN_MESSAGE}") 105 | endif() 106 | 107 | target_compile_options( 108 | ${target} 109 | INTERFACE 110 | ${warnings} 111 | ) 112 | endfunction() 113 | 114 | function(set_target_base_warnings target) 115 | # Set the base warnings as defined in the constants. 116 | set_target_warnings( 117 | ${target} 118 | MSVC 119 | ${MSVC_BASE_WARNINGS} 120 | CLANG 121 | ${CLANG_BASE_WARNINGS} 122 | GCC 123 | ${GCC_BASE_WARNINGS} 124 | UNKNOWN_MESSAGE 125 | "Compiler is not known, cannot set warnings." 126 | ) 127 | 128 | # Set the warnings-as-errors flag depending on the compiler. 129 | if(WARNINGS_AS_ERRORS) 130 | set_target_warnings( 131 | ${target} 132 | MSVC 133 | "/WX" 134 | CLANG 135 | "-Werror" 136 | GCC 137 | "-Werror" 138 | UNKNOWN_MESSAGE 139 | "Compiler is not known, cannot set warnings as errors." 140 | ) 141 | endif() 142 | endfunction() 143 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(main) 2 | 3 | target_sources( 4 | main 5 | PRIVATE 6 | main.cpp 7 | ) 8 | 9 | target_link_libraries( 10 | main 11 | PRIVATE 12 | project_options 13 | ) 14 | -------------------------------------------------------------------------------- /{{cookiecutter.project_slug}}/src/main.cpp: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | } 4 | --------------------------------------------------------------------------------