Initial commit

This commit is contained in:
James Howard
2020-09-18 18:58:03 -04:00
commit 0bf74891a8
25 changed files with 3554 additions and 0 deletions

41
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: CI
on: [push]
jobs:
build-ubuntu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: configure
run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=debug ..
- name: build
run: cmake --build build
- name: test
run: cd build && ctest
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: configure
run: |
mkdir build
cd build
cmake ..
- name: build
run: cmake --build build
- name: test
run: |
cd build
set CTEST_OUTPUT_ON_FAILURE=1
ctest -C Debug

36
.gitignore vendored Normal file
View File

@@ -0,0 +1,36 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.x
# Build directory
build/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "external/doctest"]
path = external/doctest
url = https://github.com/onqtam/doctest.git

52
.travis.yml Normal file
View File

@@ -0,0 +1,52 @@
language: cpp
dist: xenial
notifications:
email: false
# Define builds on mulitple OS/compiler combinations.
# Feel free to add/remove entries from this list.
matrix:
include:
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- lcov
- g++-7
env:
- MATRIX_EVAL="CXX_COMPILER=g++-7; sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-7 90"
- os: osx
osx_image: xcode10.1
addons:
homebrew:
packages:
- lcov
update: true
env:
- MATRIX_EVAL="CXX_COMPILER=clang++"
before_install:
- eval "${MATRIX_EVAL}"
- PARENTDIR=$(pwd)
- mkdir $PARENTDIR/build
install:
- cd $PARENTDIR/build
- cmake $PARENTDIR -DCMAKE_BUILD_TYPE=Coverage -DCMAKE_CXX_COMPILER=$CXX_COMPILER
- make
script:
- make coverage
after_success:
- cd $PARENTDIR/build
- lcov --list coverage_out.info.cleaned # Show test report in travis log.
# Install coverals gem for uploading coverage to coveralls.
- gem install coveralls-lcov
- coveralls-lcov coverage_out.info.cleaned # uploads to coveralls
- bash <(curl -s https://codecov.io/bash) -f coverage_out.info.cleaned || echo "Codecov did not collect coverage reports"

81
CMakeLists.txt Normal file
View File

@@ -0,0 +1,81 @@
# This file specifies how the project should be built, using CMake.
# If you are unfamiliar with CMake, don't worry about all the details.
# The sections you might want to edit are marked as such, and
# the comments should hopefully make most of it clear.
#
# For many purposes, you may not need to change anything about this file.
cmake_minimum_required(VERSION 3.8.2)
# Set project name, version and laguages here. (change as needed)
# Version numbers are available by including "exampleConfig.h" in
# the source. See exampleConfig.h.in for some more details.
project(CPP_BOILERPLATE VERSION 1.2.3.4 LANGUAGES CXX)
# Options: Things you can set via commandline options to cmake (e.g. -DENABLE_LTO=[ON|OFF])
option(ENABLE_WARNINGS_SETTINGS "Allow target_set_warnings to add flags and defines.
Set this to OFF if you want to provide your own warning parameters." ON)
option(ENABLE_LTO "Enable link time optimization" ON)
option(ENABLE_DOCTESTS "Include tests in the library. Setting this to OFF will remove all doctest related code.
Tests in tests/*.cpp will still be enabled." ON)
# Include stuff. No change needed.
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
include(ConfigSafeGuards)
include(Colors)
include(CTest)
include(Doctest)
include(Documentation)
include(LTO)
include(Misc)
include(Warnings)
# Check for LTO support.
find_lto(CXX)
# --------------------------------------------------------------------------------
# Locate files (change as needed).
# --------------------------------------------------------------------------------
set(SOURCES # All .cpp files in src/
src/example.cpp
)
set(TESTFILES # All .cpp files in tests/
tests/main.cpp
)
set(LIBRARY_NAME engine) # Default name for the library built from src/*.cpp (change if you wish)
# --------------------------------------------------------------------------------
# Build! (Change as needed)
# --------------------------------------------------------------------------------
# Compile all sources into a library.
add_library(${LIBRARY_NAME} OBJECT ${SOURCES})
# Lib needs its header files, and users of the library must also see these (PUBLIC). (No change needed)
target_include_directories(${LIBRARY_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include)
# There's also (probably) doctests within the library, so we need to see this as well.
target_link_libraries(${LIBRARY_NAME} PUBLIC doctest)
# Set the compile options you want (change as needed).
target_set_warnings(${LIBRARY_NAME} ENABLE ALL AS_ERROR ALL DISABLE Annoying)
# target_compile_options(${LIBRARY_NAME} ... ) # For setting manually.
# Add an executable for the file app/main.cpp.
# If you add more executables, copy these lines accordingly.
add_executable(main app/main.cpp) # Name of exec. and location of file.
target_link_libraries(main PRIVATE ${LIBRARY_NAME}) # Link the executable to library (if it uses it).
target_set_warnings(main ENABLE ALL AS_ERROR ALL DISABLE Annoying) # Set warnings (if needed).
target_enable_lto(main optimized) # enable link-time-optimization if available for non-debug configurations
# Set the properties you require, e.g. what C++ standard to use. Here applied to library and main (change as needed).
set_target_properties(
${LIBRARY_NAME} main
PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
# Set up tests (see tests/CMakeLists.txt).
add_subdirectory(tests)

2411
Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

24
LICENSE Normal file
View File

@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>

109
README.md Normal file
View File

@@ -0,0 +1,109 @@
[![Project Status: Active The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/latest/active.svg)](http://www.repostatus.org/#active)
[![Build Status](https://travis-ci.org/bsamseth/cpp-project.svg?branch=master)](https://travis-ci.org/bsamseth/cpp-project)
[![Build status](https://ci.appveyor.com/api/projects/status/g9bh9kjl6ocvsvse/branch/master?svg=true)](https://ci.appveyor.com/project/bsamseth/cpp-project/branch/master)
[![Coverage Status](https://coveralls.io/repos/github/bsamseth/cpp-project/badge.svg?branch=master)](https://coveralls.io/github/bsamseth/cpp-project?branch=master)
[![codecov](https://codecov.io/gh/bsamseth/cpp-project/branch/master/graph/badge.svg)](https://codecov.io/gh/bsamseth/cpp-project)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/eb004322b0d146239a57eb242078e179)](https://www.codacy.com/app/bsamseth/cpp-project?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=bsamseth/cpp-project&amp;utm_campaign=Badge_Grade)
[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/bsamseth/cpp-project.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/bsamseth/cpp-project/context:cpp)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/bsamseth/cpp-project.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/bsamseth/cpp-project/alerts/)
[![license](https://img.shields.io/badge/license-Unlicense-blue.svg)](https://github.com/bsamseth/cpp-project/blob/master/LICENSE)
[![Lines of Code](https://tokei.rs/b1/github/bsamseth/cpp-project)](https://github.com/Aaronepower/tokei)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/bsamseth/cpp-project.svg)](http://isitmaintained.com/project/bsamseth/cpp-project "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/bsamseth/cpp-project.svg)](http://isitmaintained.com/project/bsamseth/cpp-project "Percentage of issues still open")
# Boiler plate for C++ projects
This is a boiler plate for C++ projects. What you get:
- Sources, headers and mains separated in distinct folders
- Use of modern [CMake](https://cmake.org/) for much easier compiling
- Setup for tests using [doctest](https://github.com/onqtam/doctest)
- Continuous testing with [Travis-CI](https://travis-ci.org/) and [Appveyor](https://www.appveyor.com), with support for C++17.
- Code coverage reports, including automatic upload to [Coveralls.io](https://coveralls.io/) and/or [Codecov.io](https://codecov.io)
- Code documentation with [Doxygen](http://www.stack.nl/~dimitri/doxygen/)
![Demo of usage](https://i.imgur.com/foymVfy.gif)
## Structure
``` text
.
├── CMakeLists.txt
├── app
│   └── main.cpp
├── include
│   ├── example.h
│   └── exampleConfig.h.in
├── src
│   └── example.cpp
└── tests
├── dummy.cpp
└── main.cpp
```
Sources go in [src/](src/), header files in [include/](include/), main programs in [app/](app), and
tests go in [tests/](tests/) (compiled to `unit_tests` by default).
If you add a new executable, say `app/hello.cpp`, you only need to add the following two lines to [CMakeLists.txt](CMakeLists.txt):
``` cmake
add_executable(main app/main.cpp) # Name of exec. and location of file.
target_link_libraries(main PRIVATE ${LIBRARY_NAME}) # Link the executable to lib built from src/*.cpp (if it uses it).
```
You can find the example source code that builds the `main` executable in [app/main.cpp](app/main.cpp) under the `Build` section in [CMakeLists.txt](CMakeLists.txt).
If the executable you made does not use the library in [src/](src), then only the first line is needed.
## Building
Build by making a build directory (i.e. `build/`), run `cmake` in that dir, and then use `make` to build the desired target.
Example:
``` bash
> mkdir build && cd build
> cmake .. -DCMAKE_BUILD_TYPE=[Debug | Coverage | Release]
> make
> ./main
> make test # Makes and runs the tests.
> make coverage # Generate a coverage report.
> make doc # Generate html documentation.
```
## .gitignore
The [.gitignore](.gitignore) file is a copy of the [Github C++.gitignore file](https://github.com/github/gitignore/blob/master/C%2B%2B.gitignore),
with the addition of ignoring the build directory (`build/`).
## Services
If the repository is activated with Travis-CI, then unit tests will be built and executed on each commit.
The same is true if the repository is activated with Appveyor.
If the repository is activated with Coveralls/Codecov, then deployment to Travis will also calculate code coverage and
upload this to Coveralls.io and/or Codecov.io
## Setup
### Using the GitHub template
Click the `Use this template` button to make a new repository from this template.
**NB**: GitHub templates do not carry over submodules, which means you need to add those back _before_ you can build the project. Run the following after you have generated your new project:
``` bash
> git clone https://github.com/<your-username>/<your-repo-name>
> git submodule add https://github.com/onqtam/doctest.git external/doctest
> git commit -a --amend --no-edit
> git push --force
```
### From command line
When starting a new project, you probably don't want the history of this repository. To start fresh you can use
the [setup script](setup.sh) as follows:
``` bash
> git clone --recurse-submodules https://github.com/bsamseth/cpp-project # Or use ssh-link if you like.
> cd cpp-project
> bash setup.sh
```
The result is a fresh Git repository with one commit adding all files from the boiler plate.

35
app/main.cpp Normal file
View File

@@ -0,0 +1,35 @@
// Executables must have the following defined if the library contains
// doctest definitions. For builds with this disabled, e.g. code shipped to
// users, this can be left out.
#ifdef ENABLE_DOCTEST_IN_LIBRARY
#define DOCTEST_CONFIG_IMPLEMENT
#include "doctest.h"
#endif
#include <iostream>
#include <stdlib.h>
#include "exampleConfig.h"
#include "example.h"
/*
* Simple main program that demontrates how access
* CMake definitions (here the version number) from source code.
*/
int main() {
std::cout << "C++ Boiler Plate v"
<< PROJECT_VERSION_MAJOR
<< "."
<< PROJECT_VERSION_MINOR
<< "."
<< PROJECT_VERSION_PATCH
<< "."
<< PROJECT_VERSION_TWEAK
<< std::endl;
std::system("cat ../LICENSE");
// Bring in the dummy class from the example source,
// just to show that it is accessible from main.cpp.
Dummy d = Dummy();
return d.doSomething() ? 0 : -1;
}

46
appveyor.yml Normal file
View File

@@ -0,0 +1,46 @@
#---------------------------------#
# environment configuration #
#---------------------------------#
# Build worker image (VM template)
image: Visual Studio 2017
clone_depth: 3
platform:
- Win32
- x64
configuration:
- Debug
- Release
# environment:
# matrix:
# - TOOLSET: v140
matrix:
fast_finish: false
# scripts that are called at very beginning, before repo cloning
init:
- cmd: cmake --version
- cmd: msbuild /version
install:
- git submodule update --init --recursive
before_build:
- cmake . -Bbuild -A%PLATFORM% -DCMAKE_BUILD_TYPE=%configuration%
build:
project: build/CPP_BOILERPLATE.sln
parallel: true
verbosity: minimal
test_script:
- cd build
- set CTEST_OUTPUT_ON_FAILURE=1
- ctest -C %configuration%
- cd ..

158
cmake/CodeCoverage.cmake Normal file
View File

@@ -0,0 +1,158 @@
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# 2018-03-31, Bendik Samseth
# - Relax debug output.
# - Keep a copy of the coverage output for later use.
# - Updated coverage exclude patterns.
#
# 2018-01-03, HenryRLee
# - Allow for *Clang compiler names, not just Clang.
#
# 2018-01-03, Bendik Samseth
# - Only check compiler compatibility if in a coverage build.
#
#
# USAGE:
# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here:
# http://stackoverflow.com/a/22404544/80480
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# INCLUDE(CodeCoverage)
#
# 3. Set compiler flags to turn off optimization and enable coverage:
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
#
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
# which runs your test executable and produces a lcov code coverage report:
# Example:
# SETUP_TARGET_FOR_COVERAGE(
# my_coverage_target # Name for custom target.
# test_driver # Name of the test driver executable that runs the tests.
# # NOTE! This should always have a ZERO as exit code
# # otherwise the coverage generation will not complete.
# coverage # Name of output directory.
# )
#
# 4. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
#
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests.
# MUST return ZERO always, even on errors.
# If not, no coverage report will be created!
# Param _outputname lcov output is generated as _outputname.info
# HTML report is generated in _outputname/index.html
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
IF(NOT LCOV_PATH)
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
ENDIF() # NOT LCOV_PATH
IF(NOT GENHTML_PATH)
MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
ENDIF() # NOT GENHTML_PATH
# Setup target
ADD_CUSTOM_TARGET(${_targetname}
# Cleanup lcov
${LCOV_PATH} --directory . --zerocounters
# Run tests
COMMAND ${_testrunner} ${ARGV3}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
COMMAND ${LCOV_PATH} --remove ${_outputname}.info '*/tests/*' '/usr/*' '*/external/*' '/Applications/*' --output-file ${_outputname}.info.cleaned
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
COMMAND ${LCOV_PATH} --list ${_outputname}.info.cleaned
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show info where to find the report
ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
COMMAND ;
COMMENT "${BoldMagenta}Open ./${_outputname}/index.html in your browser to view the coverage report.${ColourReset}"
)
ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
string(TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_tolower)
if (cmake_build_type_tolower STREQUAL "coverage")
# Check prereqs
FIND_PROGRAM( GCOV_PATH gcov )
FIND_PROGRAM( LCOV_PATH lcov )
FIND_PROGRAM( GENHTML_PATH genhtml )
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
IF(NOT GCOV_PATH)
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
ENDIF() # NOT GCOV_PATH
IF(NOT CMAKE_COMPILER_IS_GNUCXX)
IF(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES ".*Clang")
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc or Clang! Aborting...")
ENDIF()
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
SET(CMAKE_CXX_FLAGS_COVERAGE
"-g -O0 -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
SET(CMAKE_C_FLAGS_COVERAGE
"-g -O0 -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
# If unwanted files are included in the coverage reports, you can
# adjust the exclude patterns on line 83.
SETUP_TARGET_FOR_COVERAGE(
coverage # Name for custom target.
${TEST_MAIN} # Name of the test driver executable that runs the tests.
# NOTE! This should always have a ZERO as exit code
# otherwise the coverage generation will not complete.
coverage_out # Name of output directory.
)
else()
add_custom_target(coverage
COMMAND echo "${Red}Code coverage only available in coverage builds."
COMMAND echo "${Green}Make a new build directory and rerun cmake with -DCMAKE_BUILD_TYPE=Coverage to enable this target.${ColorReset}"
)
endif()

19
cmake/Colors.cmake Normal file
View File

@@ -0,0 +1,19 @@
IF(NOT WIN32)
string(ASCII 27 Esc)
set(ColorReset "${Esc}[m")
set(ColorBold "${Esc}[1m")
set(Red "${Esc}[31m")
set(Green "${Esc}[32m")
set(Yellow "${Esc}[33m")
set(Blue "${Esc}[34m")
set(Magenta "${Esc}[35m")
set(Cyan "${Esc}[36m")
set(White "${Esc}[37m")
set(BoldRed "${Esc}[1;31m")
set(BoldGreen "${Esc}[1;32m")
set(BoldYellow "${Esc}[1;33m")
set(BoldBlue "${Esc}[1;34m")
set(BoldMagenta "${Esc}[1;35m")
set(BoldCyan "${Esc}[1;36m")
set(BoldWhite "${Esc}[1;37m")
ENDIF()

View File

@@ -0,0 +1,20 @@
# guard against in-source builds
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.")
endif()
# guard against bad build-type strings
if (NOT CMAKE_BUILD_TYPE)
message(STATUS "No build type selected, default to Debug")
set(CMAKE_BUILD_TYPE "Debug")
endif()
string(TOLOWER "${CMAKE_BUILD_TYPE}" cmake_build_type_tolower)
string(TOUPPER "${CMAKE_BUILD_TYPE}" cmake_build_type_toupper)
if( NOT cmake_build_type_tolower STREQUAL "debug"
AND NOT cmake_build_type_tolower STREQUAL "release"
AND NOT cmake_build_type_tolower STREQUAL "profile"
AND NOT cmake_build_type_tolower STREQUAL "relwithdebinfo"
AND NOT cmake_build_type_tolower STREQUAL "coverage")
message(FATAL_ERROR "Unknown build type \"${CMAKE_BUILD_TYPE}\". Allowed values are Debug, Coverage, Release, Profile, RelWithDebInfo (case-insensitive).")
endif()

6
cmake/Doctest.cmake Normal file
View File

@@ -0,0 +1,6 @@
if(ENABLE_DOCTESTS)
add_definitions(-DENABLE_DOCTEST_IN_LIBRARY)
endif()
add_library(doctest INTERFACE)
target_include_directories(doctest INTERFACE ${PROJECT_SOURCE_DIR}/external/doctest/doctest)

15
cmake/Documentation.cmake Normal file
View File

@@ -0,0 +1,15 @@
# --------------------------------------------------------------------------------
# Documentation (no change needed).
# --------------------------------------------------------------------------------
# Add a make target 'doc' to generate API documentation with Doxygen.
# You should set options to your liking in the file 'Doxyfile.in'.
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile &> doxygen.log
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "${BoldMagenta}Generating API documentation with Doxygen (open ./html/index.html to view).${ColourReset}" VERBATIM
)
endif(DOXYGEN_FOUND)

249
cmake/LTO.cmake Normal file
View File

@@ -0,0 +1,249 @@
# Usage :
#
# Variable : ENABLE_LTO | Enable or disable LTO support for this build
#
# find_lto(lang)
# - lang is C or CXX (the language to test LTO for)
# - call it after project() so that the compiler is already detected
#
# This will check for LTO support and create a target_enable_lto(target [debug,optimized,general]) macro.
# The 2nd parameter has the same meaning as in target_link_libraries, and is used to enable LTO only for those build configurations
# 'debug' is by default the Debug configuration, and 'optimized' all the other configurations
#
# if ENABLE_LTO is set to false, an empty macro will be generated
#
# Then to enable LTO for your target use
#
# target_enable_lto(mytarget general)
#
# It is however recommended to use it only for non debug builds the following way :
#
# target_enable_lto(mytarget optimized)
#
# Note : For CMake versions < 3.9, target_link_library is used in it's non plain version.
# You will need to specify PUBLIC/PRIVATE/INTERFACE to all your other target_link_library calls for the target
#
# WARNING for cmake versions older than 3.9 :
# This module will override CMAKE_AR CMAKE_RANLIB and CMAKE_NM by the gcc versions if found when building with gcc
# License:
#
# Copyright (C) 2016 Lectem <lectem@gmail.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the 'Software') deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
macro(find_lto lang)
if(ENABLE_LTO AND NOT LTO_${lang}_CHECKED)
#LTO support was added for clang/gcc in 3.9
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 3.9)
cmake_policy(SET CMP0054 NEW)
message(STATUS "Checking for LTO Compatibility")
# Since GCC 4.9 we need to use gcc-ar / gcc-ranlib / gcc-nm
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_GCC_AR OR NOT CMAKE_GCC_RANLIB OR NOT CMAKE_GCC_NM)
find_program(CMAKE_GCC_AR NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar"
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ar-${_version}"
DOC "gcc provided wrapper for ar which adds the --plugin option"
)
find_program(CMAKE_GCC_RANLIB NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib"
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-ranlib-${_version}"
DOC "gcc provided wrapper for ranlib which adds the --plugin option"
)
# Not needed, but at least stay coherent
find_program(CMAKE_GCC_NM NAMES
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-nm"
"${_CMAKE_TOOLCHAIN_PREFIX}gcc-nm-${_version}"
DOC "gcc provided wrapper for nm which adds the --plugin option"
)
mark_as_advanced(CMAKE_GCC_AR CMAKE_GCC_RANLIB CMAKE_GCC_NM)
set(CMAKE_LTO_AR ${CMAKE_GCC_AR})
set(CMAKE_LTO_RANLIB ${CMAKE_GCC_RANLIB})
set(CMAKE_LTO_NM ${CMAKE_GCC_NM})
endif()
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_LTO_AR ${CMAKE_AR})
set(CMAKE_LTO_RANLIB ${CMAKE_RANLIB})
set(CMAKE_LTO_NM ${CMAKE_NM})
endif()
if(CMAKE_LTO_AR AND CMAKE_LTO_RANLIB)
set(__lto_flags -flto)
if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.7)
list(APPEND __lto_flags -fno-fat-lto-objects)
endif()
if(NOT DEFINED CMAKE_${lang}_PASSED_LTO_TEST)
set(__output_dir "${CMAKE_PLATFORM_INFO_DIR}/LtoTest1${lang}")
file(MAKE_DIRECTORY "${__output_dir}")
set(__output_base "${__output_dir}/lto-test-${lang}")
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo "void foo() {}"
COMMAND ${CMAKE_${lang}_COMPILER} ${__lto_flags} -c -xc -
-o "${__output_base}.o"
RESULT_VARIABLE __result
ERROR_QUIET
OUTPUT_QUIET
)
if("${__result}" STREQUAL "0")
execute_process(
COMMAND ${CMAKE_LTO_AR} cr "${__output_base}.a" "${__output_base}.o"
RESULT_VARIABLE __result
ERROR_QUIET
OUTPUT_QUIET
)
endif()
if("${__result}" STREQUAL "0")
execute_process(
COMMAND ${CMAKE_LTO_RANLIB} "${__output_base}.a"
RESULT_VARIABLE __result
ERROR_QUIET
OUTPUT_QUIET
)
endif()
if("${__result}" STREQUAL "0")
execute_process(
COMMAND ${CMAKE_COMMAND} -E echo "void foo(); int main() {foo();}"
COMMAND ${CMAKE_${lang}_COMPILER} ${__lto_flags} -xc -
-x none "${__output_base}.a" -o "${__output_base}"
RESULT_VARIABLE __result
ERROR_QUIET
OUTPUT_QUIET
)
endif()
if("${__result}" STREQUAL "0")
set(__lto_found TRUE)
endif()
set(CMAKE_${lang}_PASSED_LTO_TEST
${__lto_found} CACHE INTERNAL
"If the compiler passed a simple LTO test compile")
endif()
if(CMAKE_${lang}_PASSED_LTO_TEST)
message(STATUS "Checking for LTO Compatibility - works")
set(LTO_${lang}_SUPPORT TRUE CACHE BOOL "Do we have LTO support ?")
set(LTO_COMPILE_FLAGS -flto CACHE STRING "Link Time Optimization compile flags")
set(LTO_LINK_FLAGS -flto CACHE STRING "Link Time Optimization link flags")
else()
message(STATUS "Checking for LTO Compatibility - not working")
endif()
endif()
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(STATUS "Checking for LTO Compatibility - works (assumed for clang)")
set(LTO_${lang}_SUPPORT TRUE CACHE BOOL "Do we have LTO support ?")
set(LTO_COMPILE_FLAGS -flto CACHE STRING "Link Time Optimization compile flags")
set(LTO_LINK_FLAGS -flto CACHE STRING "Link Time Optimization link flags")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
message(STATUS "Checking for LTO Compatibility - works")
set(LTO_${lang}_SUPPORT TRUE CACHE BOOL "Do we have LTO support ?")
set(LTO_COMPILE_FLAGS /GL CACHE STRING "Link Time Optimization compile flags")
set(LTO_LINK_FLAGS -LTCG:INCREMENTAL CACHE STRING "Link Time Optimization link flags")
else()
message(STATUS "Checking for LTO Compatibility - compiler not handled by module")
endif()
mark_as_advanced(LTO_${lang}_SUPPORT LTO_COMPILE_FLAGS LTO_LINK_FLAGS)
set(LTO_${lang}_CHECKED TRUE CACHE INTERNAL "" )
if(CMAKE_GCC_AR AND CMAKE_GCC_RANLIB AND CMAKE_GCC_NM)
# THIS IS HACKY BUT THERE IS NO OTHER SOLUTION ATM
set(CMAKE_AR ${CMAKE_GCC_AR} CACHE FILEPATH "Forcing gcc-ar instead of ar" FORCE)
set(CMAKE_NM ${CMAKE_GCC_NM} CACHE FILEPATH "Forcing gcc-nm instead of nm" FORCE)
set(CMAKE_RANLIB ${CMAKE_GCC_RANLIB} CACHE FILEPATH "Forcing gcc-ranlib instead of ranlib" FORCE)
endif()
endif(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 3.9)
endif(ENABLE_LTO AND NOT LTO_${lang}_CHECKED)
if(ENABLE_LTO)
#Special case for cmake older than 3.9, using a library for gcc/clang, but could setup the flags directly.
#Taking advantage of the [debug,optimized] parameter of target_link_libraries
if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_LESS 3.9)
if(LTO_${lang}_SUPPORT)
if(NOT TARGET __enable_lto_tgt)
add_library(__enable_lto_tgt INTERFACE)
endif()
target_compile_options(__enable_lto_tgt INTERFACE ${LTO_COMPILE_FLAGS})
#this might not work for all platforms... in which case we'll have to set the link flags on the target directly
target_link_libraries(__enable_lto_tgt INTERFACE ${LTO_LINK_FLAGS} )
macro(target_enable_lto _target _build_configuration)
if(${_build_configuration} STREQUAL "optimized" OR ${_build_configuration} STREQUAL "debug" )
target_link_libraries(${_target} PRIVATE ${_build_configuration} __enable_lto_tgt)
else()
target_link_libraries(${_target} PRIVATE __enable_lto_tgt)
endif()
endmacro()
else()
#In old cmake versions, we can set INTERPROCEDURAL_OPTIMIZATION even if not supported by the compiler
#So if we didn't detect it, let cmake give it a try
set(__IPO_SUPPORTED TRUE)
endif()
else()
cmake_policy(SET CMP0069 NEW)
include(CheckIPOSupported)
# Optional IPO. Do not use IPO if it's not supported by compiler.
check_ipo_supported(RESULT __IPO_SUPPORTED OUTPUT output)
if(NOT __IPO_SUPPORTED)
message(STATUS "IPO is not supported or broken.")
else()
message(STATUS "IPO is supported")
endif()
endif()
if(__IPO_SUPPORTED)
macro(target_enable_lto _target _build_configuration)
if(NOT ${_build_configuration} STREQUAL "debug" )
#enable for all configurations
set_target_properties(${_target} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
if(${_build_configuration} STREQUAL "optimized" )
#blacklist debug configurations
set(__enable_debug_lto FALSE)
else()
#enable only for debug configurations
set(__enable_debug_lto TRUE)
endif()
get_property(DEBUG_CONFIGURATIONS GLOBAL PROPERTY DEBUG_CONFIGURATIONS)
if(NOT DEBUG_CONFIGURATIONS)
set(DEBUG_CONFIGURATIONS DEBUG) # This is what is done by CMAKE internally... since DEBUG_CONFIGURATIONS is empty by default
endif()
foreach(config IN LISTS DEBUG_CONFIGURATIONS)
set_target_properties(${_target} PROPERTIES INTERPROCEDURAL_OPTIMIZATION_${config} ${__enable_debug_lto})
endforeach()
endmacro()
endif()
endif()
if(NOT COMMAND target_enable_lto)
macro(target_enable_lto _target _build_configuration)
endmacro()
endif()
endmacro()

16
cmake/Misc.cmake Normal file
View File

@@ -0,0 +1,16 @@
# --------------------------------------------------------------------------------
# Misc (no change needed).
# --------------------------------------------------------------------------------
# Have CMake parse the config file, generating the config header, with
# correct definitions. Here only used to make version number available to
# the source code. Include "exampleConfig.h" (no .in suffix) in the source.
configure_file (
"${PROJECT_SOURCE_DIR}/include/exampleConfig.h.in"
"${PROJECT_BINARY_DIR}/exampleConfig.h"
)
# add the binary tree to the search path for include files
# so that we will find exampleConfig.h
include_directories("${PROJECT_BINARY_DIR}")
# Ask CMake to output a compile_commands.json file for use with things like Vim YCM.
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)

101
cmake/Warnings.cmake Normal file
View File

@@ -0,0 +1,101 @@
# MIT License
# Copyright (c) 2017 Lectem
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
function(target_set_warnings)
if(NOT ENABLE_WARNINGS_SETTINGS)
return()
endif()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(WMSVC TRUE)
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(WGCC TRUE)
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(WCLANG TRUE)
endif()
set(multiValueArgs ENABLE DISABLE AS_ERROR)
cmake_parse_arguments(this "" "" "${multiValueArgs}" ${ARGN})
list(FIND this_ENABLE "ALL" enable_all)
list(FIND this_DISABLE "ALL" disable_all)
list(FIND this_AS_ERROR "ALL" as_error_all)
if(NOT ${enable_all} EQUAL -1)
if(WMSVC)
# Not all the warnings, but WAll is unusable when using libraries
# Unless you'd like to support MSVC in the code with pragmas, this is probably the best option
list(APPEND WarningFlags "/W4")
elseif(WGCC)
list(APPEND WarningFlags "-Wall" "-Wextra" "-Wpedantic")
elseif(WCLANG)
list(APPEND WarningFlags "-Wall" "-Weverything" "-Wpedantic")
endif()
elseif(NOT ${disable_all} EQUAL -1)
set(SystemIncludes TRUE) # Treat includes as if coming from system
if(WMSVC)
list(APPEND WarningFlags "/w" "/W0")
elseif(WGCC OR WCLANG)
list(APPEND WarningFlags "-w")
endif()
endif()
list(FIND this_DISABLE "Annoying" disable_annoying)
if(NOT ${disable_annoying} EQUAL -1)
if(WMSVC)
# bounds-checked functions require to set __STDC_WANT_LIB_EXT1__ which we usually don't need/want
list(APPEND WarningDefinitions -D_CRT_SECURE_NO_WARNINGS)
# disable C4514 C4710 C4711... Those are useless to add most of the time
#list(APPEND WarningFlags "/wd4514" "/wd4710" "/wd4711")
#list(APPEND WarningFlags "/wd4365") #signed/unsigned mismatch
#list(APPEND WarningFlags "/wd4668") # is not defined as a preprocessor macro, replacing with '0' for
elseif(WGCC OR WCLANG)
list(APPEND WarningFlags -Wno-switch-enum)
if(WCLANG)
list(APPEND WarningFlags -Wno-unknown-warning-option -Wno-padded -Wno-undef -Wno-reserved-id-macro -fcomment-block-commands=test,retval)
if(NOT CMAKE_CXX_STANDARD EQUAL 98)
list(APPEND WarningFlags -Wno-c++98-compat -Wno-c++98-compat-pedantic)
endif()
if ("${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") # clang-cl has some VCC flags by default that it will not recognize...
list(APPEND WarningFlags -Wno-unused-command-line-argument)
endif()
endif(WCLANG)
endif()
endif()
if(NOT ${as_error_all} EQUAL -1)
if(WMSVC)
list(APPEND WarningFlags "/WX")
elseif(WGCC OR WCLANG)
list(APPEND WarningFlags "-Werror")
endif()
endif()
foreach(target IN LISTS this_UNPARSED_ARGUMENTS)
if(WarningFlags)
target_compile_options(${target} PRIVATE ${WarningFlags})
endif()
if(WarningDefinitions)
target_compile_definitions(${target} PRIVATE ${WarningDefinitions})
endif()
if(SystemIncludes)
set_target_properties(${target} PROPERTIES
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:${target},INTERFACE_INCLUDE_DIRECTORIES>)
endif()
endforeach()
endfunction(target_set_warnings)

28
include/example.h Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
/**
* This is a dummy class to demonstrate features of the boiler plate.
*/
class Dummy {
public:
/**
* Default constructor for Dummy (does nothing).
*/
Dummy();
/**
* Returns a bool.
* @return Always True.
*/
bool doSomething();
};
#ifdef ENABLE_DOCTEST_IN_LIBRARY
#include "doctest.h"
TEST_CASE("we can have tests in headers if we want")
{
Dummy d;
CHECK(d.doSomething() == true);
}
#endif

View File

@@ -0,0 +1,12 @@
/*
* This file contains definitions made in CMakeLists.txt
* that we want available in the source code.
* In the source code, include "exampleConfig.h" (no .in suffix).
* This header file will be generated, and filled with the
* right definition values. Change the namings as you wish,
* but make sure they match up with whats in CMakeLists.txt.
*/
#define PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define PROJECT_VERSION_TWEAK @PROJECT_VERSION_TWEAK@

22
setup.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
# Revert to first commit, add and commit everything as single commit.
git reset $(git commit-tree HEAD^{tree} -m "Bolier plate for C++ project added")
name=$(git config user.name)
email=$(git config user.email)
# If this script is called with an argument, use that as the amended author
# EX: ./setup.sh "Author Name <author_email@email.com>"
if [[ "$1" ]]; then
git commit --amend --author="$1"
else
git commit --amend --author="$name <$email>"
fi
# Initialize submodules: This should already be done when cloning, but there are ways to muck it
# up if you do things in the wrong order. So just to be sure, we do it now.
git submodule update --init --recursive
# Remove the remote (you probably want your own instead).
git remote remove origin

21
src/example.cpp Normal file
View File

@@ -0,0 +1,21 @@
#include "example.h"
Dummy::Dummy() {
}
bool Dummy::doSomething() {
// Do silly things, using some C++17 features to enforce C++17 builds only.
constexpr int digits[2] = {0, 1};
auto [zero, one] = digits;
return zero + one;
}
#ifdef ENABLE_DOCTEST_IN_LIBRARY
#include "doctest.h"
TEST_CASE("we can have tests written here, to test impl. details")
{
CHECK(true);
}
#endif

33
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.8.2)
# List all files containing tests. (Change as needed)
set(TESTFILES # All .cpp files in tests/
main.cpp
dummy.cpp
)
set(TEST_MAIN unit_tests) # Default name for test executable (change if you wish).
set(TEST_RUNNER_PARAMS "") # Any arguemnts to feed the test runner (change as needed).
# --------------------------------------------------------------------------------
# Make Tests (no change needed).
# --------------------------------------------------------------------------------
add_executable(${TEST_MAIN} ${TESTFILES})
target_link_libraries(${TEST_MAIN} PRIVATE ${LIBRARY_NAME} doctest)
set_target_properties(${TEST_MAIN} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
target_set_warnings(${TEST_MAIN} ENABLE ALL AS_ERROR ALL DISABLE Annoying) # Set warnings (if needed).
set_target_properties(${TEST_MAIN} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
add_test(
# Use some per-module/project prefix so that it is easier to run only tests for this module
NAME ${LIBRARY_NAME}.${TEST_MAIN}
COMMAND ${TEST_MAIN} ${TEST_RUNNER_PARAMS})
# Adds a 'coverage' target.
include(CodeCoverage)

11
tests/dummy.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include "doctest.h"
#include "example.h"
// Tests that don't naturally fit in the headers/.cpp files directly
// can be placed in a tests/*.cpp file. Integration tests are a good example.
TEST_CASE("complicated integration tests could be here")
{
Dummy d;
CHECK(d.doSomething() == true);
}

5
tests/main.cpp Normal file
View File

@@ -0,0 +1,5 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
// This is all that is needed to compile a test-runner executable.
// More tests can be added here, or in a new tests/*.cpp file.