MY_LIB 0.1.0
Repository template for C++ projects
Loading...
Searching...
No Matches
Cpp Library Starter

Windows [Clang-cl & MSVC] Ubuntu & MacOS [Clang & GCC] Documentation build & deploy Static Badge

An opinionated template repository for C++20 static library.

‍I'm using it as a continuous learning process. Suggestions are always welcome !

Features

  • Dependencies downloaded with CPM. This template is also CPM-ready :
CPMAddPackage(
NAME my_lib
GITHUB_REPOSITORY my_lib_repo/my_lib
)
target_link_libraries(some_target PUBLIC my_lib)
  • Tests built with Doctest. Using Github Actions, tests are executed on :
    • Windows : msvc and clang-cl
    • Ubuntu : gcc and clang
    • MacOS : gcc and clang
  • Benchmarks built with Google Benchmark.

    ‍A target called "run_benchmarks" will run benchmarks with some predefined settings and store the result inside your source dir : benchmarks/data/....

    This is for continuous benchmarking. Why in the same repo ? Long story short, having it in the same repo made it easier to include it the documentation. For now, my side project to generate charts from these benchmarks is not yet finished. The goal is to include this tool in CI, generate interactive charts, and add them to the documentation.
  • Documentation built with Doxygen and Doxygen Awesome CSS. Using Github Actions, documentation is automatically built and published to github pages. Github pages is also automatically configured. No need to create it manually.

‍Documentation example available here.

‍M.CSS was initially used, but I prefer Doxygen Awesome CSS. It was also easier to integrate interactive charts in it.

  • A great deal of care has been taken in writing clear and robust CMake files. It should work on Windows, Linux, using WSL or not, by command-line or with an IDE like Visual Studio or Clion.
  • A verbose cmake output when built as the main project, but concise when consumed.
  • A clear, cross-platform & easily extendable CMakePreset.json is also provided.
  • Cross-compiler warnings enabled through CMake, but only if built as main project (to not bother consumer of this library).
  • Minimal CMake options used. Default values should be good enough, but options are provided just in case.

Getting started

Go to the github repository and click on Use this template

Once the setup is done, replace the following identifiers.

  • my_lib : cmake target, folder name
  • MY_LIB : cmake project name and cmake variables prefix
  • my_namespace : namespace, only in source files.
  • my_lib_source : in src/
  • my_lib_header : in include/my_lib

If you wish to reuse parts of the README.md, make sure to also replace :

  • TBlauwe/cpp_lib_starter with your github repository (so badges are linked to correct workflows).
  • https://tblauwe.github.io/cpp_lib_starter/ with your github pages link (so Documentation link badges redirects to your site).

Also, in the root CMakeLists.txt, make sure to replace project information to yours. They will be used for the generated documentation.

# ----- Project information
project(MY_LIB
VERSION 0.1.0
DESCRIPTION "Repository template for C++ projects"
HOMEPAGE_URL "https://github.com/TBlauwe/cpp_lib_starter"
LANGUAGES C CXX
)

It should be safe to do a "Replace All". Still, make sure that #include <my_lib/my_lib_header.hpp> are correctly replaced.

That should be it for building the main target, tests and benchmarks. Documentation requires a bit more installation user-side.

You also probably don't need docs/pages/reference.md.

CMake options

The following CMake options are available:

  • MY_LIB_SKIP_DEPENDENCIES : Disable automatic dependencies downloading with CPM
    • By default, if consumed it is set to true, false otherwise.
  • CPM_MY_DEPENDENCY_VERSION : Specify a dependency version. It is not something added by CPM but by me. It adds an option for overriding the version of a dependency. The value is a git tag, e.g master, v3.12, 1.0, etc.

Executables location are specified by variable ${PROJECT_EXE_DIR} in the main CMakeLists.txt. By default, it is set to : ${PROJECT_SOURCE_DIR}/bin. Note that it is not prefixed by MY_LIB. As of now, it is only set when the library is built as the main project. So I thought it would be nice to follow cmake convention like ${PROJECT_SOURCE_DIR}.

CMake utilities

Inside cmake/utility.cmake, you will find several functions and macros. Most aren't noteworthy. They are mainly used to organise and build prettier output. They are by no means necessary. If you don't like them, feel free to skip them.

Still, here are some noteworthy functions/macros.

Macro: use_version

Define an option to set the version for a library.

Usage :

use_version(NAME fmt VERSION "10.0.0")

This will add a cmake option CPM_FMT_VERSION set to 10.0.0. It will then be used by the following macro :

Macro: download_library

Download a library using CPM. It is a wrapper of CPMAddPackage and used the same. The reason to add this wrapper is to play nicely with options/version added through use_version and also with cmake output.

Usage :

download_library(
NAME fmt
TARGETS fmt fmt-header-only
GITHUB_REPOSITORY fmtlib/fmt
OPTIONS
"FMT_INSTALL OFF"
)

As you can see, it is almost exactly the same as CPMAddPackage, but we didn't have to specify a version. The conjunction of these two macros/functions may and are most likely overkill/useless. Still, I like the readability of these cmake files and verbosity.

TARGETS is a multi-value arguments where you can pass targets. By default, download_library will try to suppress warnings when building those targets (or the target of the same name as NAME if no TARGETS are provided).

‍Sadly, suppressing warnings like this doesn't work for now (see this article).

To prevent this behaviour, you can pass the options NO_SUPPRESS_WARNINGS, like so :

download_library(
NAME fmt
GITHUB_REPOSITORY fmtlib/fmt
OPTIONS
"FMT_INSTALL OFF"
NO_SUPPRESS_WARNINGS
)

Build

cmake -S . -B build
cmake --build build --target <a-target>

No need to pass options, default should be good. You can also use CMakePresets.json instead, see below.

Targets

Available targets are :

  • my_lib : main library
  • tests : tests
  • benchmarks : benchmarks
  • run_benchmarks : run benchmarks through a python script
  • docs : docs

CMakePresets.json

For cross-platform compiling, we use CMakePresets.json (see ref) to share settings.

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 to out/build/<preset-name>.
  • <a-target> : name of a target, see above.

Configuration presets

Here are some configurations provided:

// Windows specific
{"name": "x64-debug-msvc", "inherits": ["Base", "Ninja", "x64", "MSVC", "Debug"]},
{"name": "x64-release-msvc", "inherits": ["Base", "Ninja", "x64", "MSVC", "Release"]},
{"name": "x64-debug-clang-cl", "inherits": ["Base", "Ninja", "x64", "Clang-cl", "Debug"]},
{"name": "x64-release-clang-cl", "inherits": ["Base", "Ninja", "x64", "Clang-cl", "Release"]},
{"name": "x64-debug-gcc", "inherits": ["Base", "Ninja", "x64", "GCC", "Debug"]},
{"name": "x64-release-gcc", "inherits": ["Base", "Ninja", "x64", "GCC", "Release"]},
// For Windows Subsystem Linux (WSL), but should work on others
{"name": "x64-debug-clang", "inherits": ["Base", "Ninja", "x64", "Clang", "Debug", "wsl"]},
{"name": "x64-release-clang", "inherits": ["Base", "Ninja", "x64", "Clang", "Release", "wsl"]},
{"name": "x64-debug-gnu", "inherits": ["Base", "Ninja", "x64", "GCC", "Debug", "wsl"]},
{"name": "x64-release-gnu", "inherits": ["Base", "Ninja", "x64", "GCC", "Release", "wsl"]}

NOTE 1: 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-release-msvc", "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 !
}
},

A warning note

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).

Additional functionality

If you wish, they are some additional functionality that requires a bit more work from you.

CPM Download location

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.

CCache

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

Documentation is built with Doxygen and Doxygen Awesome CSS

  • docs : utility target to build the documentation
  • open_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 :

Instructions

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

‍Make sure to add doxygen to your path !

Writing docs

To help you write docs, this page is a reference of some commands.

Tests

The library used for testing is Doctest.

One target is provided : tests.

‍Tests are only configured when the project is the main project !

Benchmarks

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.

Running benchmarks

You can use provided run_benchmarks.py python script, to run benchmarks with a predefined set of options.

py run_benchmarks.py

Launch with -h, if you need to see available arguments.

By default, benchmarks' results will be outputted to a file with following name :

@PROJECT_NAME@_@PROJECT_VERSION@_@CMAKE_BUILD_TYPE@_@CMAKE_CXX_COMPILER_ID@_%Y-%m-%d_%H-%M

For example :

mylib_0.1.0_debug_clang_2023-08-02_20-12.json

You can specify the name with option -n or --name :

py run_benchmarks.py -n MyName

The goal is to be able to store results in the long run to compare evolution of performance.

Comparing benchmarks

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

Credits

CMake:

Benchmarks:

Tests:

Documentation:

Ressources used

These are the main ressources I used to organize CMake files:

TODOs

  • [ ] - Add interactive charts for continuous benchmarking
  • [ ] - Support shared library