An opinionated template repository for C++20 static library, with an emphasis on quick setup to share library with other projects using CPM.
Note
I'm using it as a continuous learning process. Suggestions are always welcome !
-
Quick setup : Click
Use this template
, fill a few fields and a project/repository will be generated based on available information.- Thanks to Tmplr, there are no leftover, no replacement needed. For example, a barebone
README.md
is generated, with working links, instructions, etc.
- Thanks to Tmplr, there are no leftover, no replacement needed. For example, a barebone
-
Documentation generated with Doxygen and Doxygen Awesome CSS.
- Github Pages is automatically setup (just need to allow deployement from github actions)
- Documentation is built during CI and pushed to github pages
-
Dependencies downloaded with CPM.
Project generated from this template are also CPM-ready
CPMAddPackage( NAME my_lib GITHUB_REPOSITORY my_lib_repo/my_lib ) target_link_libraries(some_target PUBLIC my_lib)
-
Tests written with Catch2 and executed during CI.
CI Executions
Platform Toolchains Windows msvc & clang-cl Ubuntu gcc & clang MacOS gcc & clang -
Benchmarks written with Google Benchmark.
-
(WIP) Continuous benchmarks done through a two-step process .
Execute and store benchmarks results.
This step is partially automated. It was chosen to not automatically run them in CI to ensure consistent results over time (at least when it comes to github actions).
Therefore, a cmake utility target, called "run_benchmarks" is available to automate this step. It will run benchmarks with some predefined settings and store the result in
benchmarks/data/...
.- Automated generation of various widgets (charts, tables) to showcase your benchmarks, using BenchDocs. They are also automatically integrated in your documentation.
-
Robust and subjectively clear CMake files.
- They should work on Windows, Linux, using WSL or not, by command-line or with an IDE like Visual Studio or Clion.
-
Verbose when needed. CMake's output is only verbose when built as the main project, but concise when consumed.
-
A Cross-platform and modular CMakePreset.json provided.
-
Cross-compiler warnings enabled through CMake.
-
Minimal CMake options used. Default values should be good enough, but options are provided just in case.
-
EXPERIMENTAL. Automatically includes example files in your readme with cmake.
On the github repository, click on Use this template
and fill the fields
There are two steps left :
- Allow deployement of Github Pages through github actions
- [OPTIONAL] - Clean workflow files
Go to your repository on github and follow these steps :
- Click on
Settings
- Click on
Pages
in the sectionCode and automation
- In 'Build and deployement', change
Source
toGitHub Actions
, instead ofDeploy from a branch
.
Otherwise, GitHub Pages's deployement will always failed.
Deploy Github Pages without commiting
If you wish to deploy the documentation now, you can trigger the workflow from github by following these steps :
- Click on
Actions
- In the left pane, Click on
Documentation build & deploy
- To the right, click on
Run workflow
Note
I need to improve the template so this is not necessary. It is a workaround so that the user do not need to create a personal account token. They are maybe another way but I haven't found it. Any help is appreciated !
Modifications of workflows file needs additional permissions. Without them, we can't add them during generation. But, it seems to require additional steps from the user. To circumvent that, all workflows files are initially present. But we add to alter them slightly, so they can run only when relevant :
- All workflows should not run in the template repository
if: github.repository != 'TBlauwe/cpp_lib_starter'
- Workflow
init.yaml
should run only once, when there is a.tmplr.yml
.
- name: Check file existence
id: check_files
uses: andstor/file-existence-action@v2
with:
files: '.tmplr.yml'`
- Other workflows should run only when the project is generated, meaning when there is a root
CMakeLists.txt
.
- name: Check file existence
id: check_files
uses: andstor/file-existence-action@v2
with:
files: 'CMakeLists.txt'
After the generation, you can remove init.yaml
, as it is not needed anymore, idem for the checks in other workflows.
Use following command in the root directory :
cmake -S . -B build
cmake --build build --target <a-target>
Targets available :
Targets | Type | Description |
---|---|---|
<your_project_name> |
Library | Build main library |
tests |
Executable | Build executable to run tests |
benchmarks |
Executable | Build executable to run benchmarks |
continuous_benchmarking |
Utility | Execute a python script to run benchmarks for continuous benchmarking |
docs |
Utility | Build documentation |
open_docs |
Utility | Open documentation in default browser |
update_code_blocks |
Utility | Update code blocks by replacing its content with the relevant file. |
You can also use CMakePresets.json
instead, see below
cmake --preset <preset-name>
cmake --build <preset-build-directory> --target <a-target>
<preset-name>
: name of a configuration preset, see below for a list.<preset-build-directory>
: build folder. By default and for all configuration presets, it is set toout/build/<preset-name>
.<a-target>
: name of a target, see above.
Available presets :
Targets | Inherits | Note |
---|---|---|
x64-msvc-debug |
Base , Ninja , x64 , MSVC , Debug |
Windows specific (Visual studio) |
x64-msvc-release |
Base , Ninja , x64 , MSVC , Release |
Windows specific (Visual studio) |
x64-clang-cl-debug |
Base , Ninja , x64 , Clang-cl , Debug |
Windows specific (Visual studio) |
x64-clang-cl-release |
Base , Ninja , x64 , Clang-cl , Release |
Windows specific (Visual studio) |
x64-gcc-debug |
Base , Ninja , x64 , GCC , Debug |
Windows specific (CLion) |
x64-gcc-release |
Base , Ninja , x64 , GCC , Release |
Windows specific (Clion) |
x64-clang-debug |
Base , Ninja , x64 , Clang , Debug |
Unix, MacOs, WSL |
x64-clang-release |
Base , Ninja , x64 , Clang , Release |
Unix, MacOs, WSL |
x64-gnu-debug |
Base , Ninja , x64 , GCC , Debug , wsl |
Unix, MacOs, WSL |
x64-gnu-release |
Base , Ninja , x64 , GCC , Release , wsl |
Unix, MacOs, WSL |
Note
Clang-cl
refers to the clang toolchain provided by Visual Studio 2022
Thanks to the structure of the file (credits to DirectXTK), you can easily add other configurations, by inheritings relevant configurations.
{"name": "x86-msvc-release", "inherits": ["Base", "Ninja", "x86", "MSVC", "Release"]},
If you need to specify some cache variables for CMake, you can add them to the base
configuration :
{
"name": "Base",
"hidden": true,
"displayName": "Default config",
"description": "Default build",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"installDir": "${sourceDir}/out/install/${presetName}",
"cacheVariables":
{
// HERE !
}
},
Warning
Some configurations may not work if some binaries and libraries are not in your PATH
.
For example, by default with Visual Studio 2022, all windows specific configurations works but GCC
.
Vice-versa, only GCC
works in CLion but not the others (unless you tweak your path).
If you wish, they are some additional functionality that requires a bit more work from you.
By default, CPM download source files in the output directory. If you have several projects that use the same libraries, it may be favorable to download them in one place.
CPM documentation is far more comprehensive, but you can set the cmake variable CPM_SOURCE_CACHE
to an adequate location. Personally, I recommend to set it in your path, rather than in your .cmake files or in your preset.
By default and when configured as the main project, if CCache is installed, it will be activated. Otherwise it will be ignored.
To install CCache on windows, you can use chocolatey (need elevated privileges) like so :
choco install ccache
Documentation is built with Doxygen and Doxygen Awesome CSS
docs
: utility target to build the documentationopen_docs
: utility target to open docs without the hassle of finding it.
Documentation is only built when the project is the main project !
Documentation is built through github actions and push in github pages when commiting on master. You don't need to setup your github pages, it's done automatically. If you wish to built it localy, the following tools are needed :
- Doxygen, 1.9.6+ recommended.
On Ubuntu :
sudo apt-get install doxygen
sudo apt-get install graphviz
On MacOs :
brew install doxygen
brew install graphviz
On windows using chocolatey (need elevated privileges) :
choco install doxygen.install
choco install graphviz
Warning
Make sure doxygen is in your path !
To help you write docs, this page is a reference of some commands.
Let's start by a use case : You want your main readme.md to include a cpp file to showcase how to get started. Doxygen won't be able to help us here (unless I'm missing something).
So a simple cmake function defined in cmake/include_code_block.cmake
will allow you to include any file in a markdown file, without using a template.
Suppose we have a markdown file, called README.md
, that contains this :
<!--BEGIN_INCLUDE="examples/00_GettingStarted/main.cpp"-->
'''cpp
#include <{{tmplr.repo_name}}/{{tmplr.repo_name}}.h>
'''
<!--END_INCLUDE-->
By running include_code_block("README.md")
, the content between the two balises will be replaced by the content of the file specified, examples/00_GettingStarted/main.cpp
in this case.
Now a problem I faced is when to call this function. Ideally only when necessary, but it turned out there are several cases where it needs to be updated, but we don't want to do it at every cmake invocation.
I choose to define a custom target called update_code_blocks
that you can manually run, or that is automatically run when building docs.
The library used for testing is Catch2.
One target is provided : tests
.
Tests are only configured when the project is the main project !
The library used for benchmarking is Google benchmark.
One target is provided benchmarks
.
If you want to pass more options to tune the benchmarking, see Google benchmark usage guide.
To compare two benchmarks, you can use the following command :
py benchmarks_tools/compare.py benchmarks <baseline> <comparison>
Replace <baseline>
and <comparison>
with .json
files obtained when running your benchmarks.
To use this tools, requirement must be installed :
cd benchmarks_tools
pip3 install -r requirements.txt
CMake:
Benchmarks:
Tests:
Documentation:
Utility:
These are the main ressources I used to organize CMake files:
- "Modern CMake" by CliUtils
- "Deep CMake For Library Authors" by Craig Scott
- "cmake-init" by FriendlyAnon
- - Support shared library
- - Semantic versionning from Github
- - Release/publish action
- - Add interactive charts for continuous benchmarking
- - Add section for continuous benchmarking
- - Install rules needs testing
- - Simplify download_library / remove use_version / Rename to add mark_include_system