Skip to content

Commit

Permalink
Multiple .cmake files were condensed into single cpp-init.cmake
Browse files Browse the repository at this point in the history
  • Loading branch information
nerudaj committed Feb 21, 2025
1 parent efbd3f2 commit 575f507
Show file tree
Hide file tree
Showing 17 changed files with 295 additions and 691 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
runs-on: windows-2022
strategy:
matrix:
folder: [ fetch_test, root_dir_test, packages_test ]
folder: [ fetch_test ]
steps:
- uses: actions/checkout@v4

Expand Down
206 changes: 114 additions & 92 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,113 +1,105 @@
# cpp-init

This repo hosts configuration files for my C++ projects. In addition to `.clang-format`/`.clang-tidy` files, several CMake scripts exist for easy project setup and maintenance.

# Table of Contents

- [Quickstart example](#quickstart-example)
- [CPM Packages](#cpm-packages)
- [bootstrap.cmake](#bootstrapcmake)
- [bootstrap_cpm](#bootstrap_cpm)
- [glob_headers_and_sources](#glob_headers_and_sources)
- [get_git_version](#get_git_version)
- [fetch_prebuilt_dependency](#fetch_prebuilt_dependency)
- [fetch_headeronly_dependency](#fetch_headeronly_dependency)
- [cpp.cmake](#cppcmake)
- [apply_compile_options](#apply_compile_options)
- [enable_autoformatter](#enable_autoformatter)
- [enable_linter](#enable_linter)
- [macros.cmake](#macroscmake)
- [make_static_library](#make_static_library)
- [make_executable](#make_executable)

## Quickstart example

First, get the `get_cpp_init.cmake` script from the Releases page. Then you can include it in your project and it will fetch the rest of the scripts for you:
This repo hosts common configuration files I use in my C++ projects, namely:

```cmake
cmake_minimum_required ( VERSION 3.26 )
* `.clang-format`
* `.clang-tidy`
* `cmake/cpp-init.cmake`

# Use the script from releases to fetch everything
include ( "${PATH_TO}/get_cpp_init.cmake" )
The `cpp-init.cmake` is an opinionated CMake helper that makes CMake much simpler by enforcing a bunch of rules on project structure.

# Read git tags for version
get_git_version ( PROJECT_VERSION_VARIABLE GIT_FULL_VERSION )
## Table of Contents

# Enable dependency management with CPM
bootstrap_cpm()
* [Project Structure](#project-structure)
* [Quickstart Example](#quickstart-example)
* [Documentation](#documentation)
* [CMake Package Manager](#cmake-package-manager)

project ( demo VERSION ${GIT_PROJECT_VERSION} )
## Project Structure

CPMAddPackage( "gh:nlohmann/[email protected]" )
For each compilable target, the recommended folder structure is this:

```
\root
- \include
- File.hpp
- \src
- File.cpp
- CMakeLists.txt
```

# globs all .hpp files from "${CMAKE_CURRENT_SOURCE_DIR}/include"
# and all .cpp files from "${CMAKE_CURRENT_SOURCE_DIR}/src"
# including subfolders
glob_headers_and_sources ( HEADERS SOURCES )
Scripts present in this repo are conditioned to follow this structure, with a little bit of leevay. The `src` folder can also be called `source`. Allowed extensions for header files are `hpp` and `h`. Header files must always be in a folder `include` or `private_include` (the latter is for headers that should not be accessible by dependent targets).

add_executable ( ${PROJECT_NAME}
${HEADERS}
${SOURCES}
)
These scripts also rely on globbing (recursively scanning) for source files, instead of you having to manually specify them in CMakeLists.txt. This makes project maintenance much easier, but if you delete a file, `cmake --build` command will not detect it automatically and will fail compilation.

target_link_libraries ( ${PROJECT_NAME}
PRIVATE_LINK nlohmann::json
)
If you delete a source file, you always need to manually reconfigure. Lastly, scripts here are tailored towards MSVC. PRs for your favorite platforms are welcomed.

### Semantic versioning

# Applies compiler switches and options I commonly use
apply_compile_options ( ${PROJECT_NAME} )
This project also promotes [semantic versioning](https://semver.org/). You can easily read the current version from the git tags, insert it into your project declaration and it will propagate further into packaging, etc.

If you're new to git, you can mark the current commit with version like this:

```sh
git tag v0.1.0
git push --tags
```

## Quickstart Example

Suppose you want to make a simple executable with a bunch of header and source files. Follow this folder structure:

# Applies .clang-format
enable_autoformatter ( ${PROJECT_NAME} )
```
\root
- \cmake
- cpp-init.cmake
- \include
- HeaderA.hpp
- HeaderB.hpp
- \src
- ImplA.cpp
- ImplB.cpp
- Main.cpp
- CMakeLists.txt
```

Obtain the `cpp-init.cmake` by downloading the latest version from the [Releases](https://github.com/nerudaj/cpp-init/releases/latest) page.

With macros, this can get even shorter:
Put the following code in the `CMakeLists.txt`:

```cmake
cmake_minimum_required ( VERSION 3.26 )
# Use the script from releases to fetch everything
include ( "${PATH_TO}/get_cpp_init.cmake" )
include ( "cmake/cpp-init.cmake" )
# Read git tags for version
get_git_version ( PROJECT_VERSION_VARIABLE GIT_FULL_VERSION )
cpp_init()
get_version_from_git ( PROJECT_VERSION_VARIABLE GIT_FULL_VERSION )
# Enable dependency management with CPM
bootstrap_cpm()
project ( Example VERSION ${PROJECT_VERSION_VARIABLE} )
project ( demo VERSION ${GIT_PROJECT_VERSION} )
CPMAddPackage ( "gh:nlohmann/[email protected]" )
# Convenience macro for CPMAddPackage call
cpm_add_nlohmann_json ()
# Assumes there is at least `src` directory in the current one (also looks for `include`)
# It globs them, adds them as sources, enables autoformatter and applies compile options.
make_executable ( ${PROJECT_NAME} DEPS nlohmann::json )
make_executable ( ${PROJECT_NAME} DEPS nlohmann_json::nlohmann_json )
```

Assuming you're placing all `.cmake` files into a `cmake` subfolder, you can set a variable `CPP_INIT_ROOT_DIR` to `${CMAKE_CURRENT_SOURCE_DIR}/cmake` prior to including `get_cpp_init.cmake`. This will copy all related CMake scripts into your `cmake` folder so you can add them to your version control.
The `cpp_init()`, `get_version_from_git()`, and `make_executable()` are all from the `cpp-init.cmake` and are documented below.

## CPM Packages
This example autodetects header and source files in the appropriate folders, creates an executable target called `${PROJECT_NAME}` and links nlohmann JSON library to it. It also reads project semantic version from git tags and applies it to the project.

There are a few macros that make it easier to add certain well known (well known to me) C++ packages. Here's an overview:
## Documentation

| Library name | Version | Macro | Linkable target |
| --- | --- | --- | --- |
| base64 | master | cpm_add_base64 | base64 |
| Catch2 | 3.7.1 | cpm_add_catch2 | Catch2::Catch2 |
| cxxopts | 3.11.3 | cpm_add_cxxopts | cxxopts |
| dgm-lib | 2.3.1 | cpm_add_dgmlib | dgm-lib |
| Eigen | master | cpm_add_eigen | Eigen3::Eigen |
| EnTT | 3.14.0 | cpm_add_entt | EnTT::EnTT |
| FakeIt | 2.4.1 | cpm_add_fakeit | FakeIt::FakeIt |
| fsm-lib | 2.1.0 | cpm_add_fsmlib | fsm-lib |
| nlohmann json | 3.0.0 | cpm_add_nlohmann_json | nlohmann::json |
| SFML | 2.6.1 | cpm_add_sfml | sfml-main sfml-audio sfml-graphics sfml-window sfml-network |
| SFML 3 | 3.0.0 | cpm_add_sfml3 | SFML::Main SFML::Audio SFML::Graphics SFML::Window SFML::Network |

## bootstrap.cmake

This is a script full of useful utility functions:
This section describes utility functions present in the `cpp-init.cmake` file.

### download_file_if_not_there

If a file with a TARGET name is not yet present, this function attepts to download it from a given URL. If not successful, empty file is created instead.

```cmake
download_file_if_not_there (
"http://myurl.com/file.txt"
"${CMAKE_BINARY_DIR}/file.txt"
)
```

### bootstrap_cpm

Expand All @@ -122,8 +114,20 @@ bootstrap_cpm ( 0.30.2 ) # version number is optional
CPMAddPackage( "gh:nlohmann/[email protected]" )
```

### set_cpp23_x64

Sets the required C++ standard to 23 and sets the target platform to x64.

### cpp_init

Shorthand for calling `bootstrap_cpm` and `set_cpp23_x64`.

### glob_headers_and_sources

In current source directory, recursively looks for .h/.hpp files in folders `include` and `private_include`, adds them to IDE through `source_group` call and saves them in `HEADERS_OUTVARNAME` variable.

Also looks for .cpp files in `source` and `src` folders, adds them to IDE and saves them in `SOURCES_OUTVARNAME`.

If the `${CMAKE_CURRENT_SOURCE_DIR}` contains subfolders `include` and `src`, this will recursively glob all `.hpp` files in the `include` folder and all `.cpp` files in the `src` folder. It will add them to your IDE filters through `source_group` call and it will populate the output variables.

```cmake
Expand Down Expand Up @@ -163,32 +167,33 @@ CPM is great, but you pay with long build times. Many libraries on GitHub come p
fetch_prebuilt_dependency (
SFML
URL https://github.com/SFML/SFML/releases/download/2.6.1/SFML-2.6.1-windows-vc17-64-bit.zip
CACHE_DIR "C:/deps" # can download to outside location
CACHE_DIR "C:/deps" # optional: can download to given location
)
# unpacked archive contents are in ${SFML_FOLDER}
```

Unlike `FetchContent_MakeAvailable`, this only downloads and unpacks the archive. It does not look for `CMakeLists.txt` inside the archive, nor it calls `add_subdirectory`.

### fetch_headeronly_dependency

For headeronly libraries, it is even easier (the `CACHE_DIR` parameter can still be used).
Downloads a single file, presumably a headeronly library.

```cmake
fetch_headeronly_dependency (
NLOHMANN_JSON
JSON
URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.hpp
CACHE_DIR "C:/deps" # optional: can download to given location
)
# json.hpp is located in ${NLOHMANN_JSON_FOLDER}
# json.hpp is located in ${JSON_FOLDER}
```

## cpp.cmake

### apply_compile_options

**NOTE:** Currently only supported with MSVC generator.

Applies a set of useful compiler diagnostics and settings.
Applies a set of useful compiler diagnostics and settings. For MSVC, postfixes all artifacts compiled under Debug with `-d` postfix.

```cmake
apply_compile_options ( ${TARGET} )
Expand All @@ -204,14 +209,12 @@ enable_autoformatter ( ${TARGET} )

### enable_linter

Copies `.clang-tidy` to `${CMAKE_CURRENT_SOURCE_DIR}` and adds them to the specified target sources. For MSVC, it turns on static code analysis with Clang tidy.
Copies `.clang-tidy` to `${CMAKE_CURRENT_SOURCE_DIR}` and adds them to the specified target sources. For MSVC, it turns on static code analysis with clang-tidy. Note that MSVC ships with some old clang-tidy implementation so you likely need to install newer one yourself.

```cmake
enable_linter ( ${TARGET} )
```

## macros.cmake

### make_static_library

A shorthand for calling `glob_headers_and_sources`, `apply_compile_options` and bunch of other options. Declares `include` folder as public include directory.
Expand All @@ -232,3 +235,22 @@ make_static_library ( myLib DEPS nlohmann_json::nlohmann_json )
### make_executable

Similar as `make_static_library`, just for executables.

## CMake Package Manager

CPM is great, but I can never remember the author nor the CMake target I can link against. Here's a list of bunch of libraries I frequently use, might be useful for you as well:

| Library name | Version | CPM expression | Linkable target |
| --- | --- | --- | --- |
| base64 | master | `gh:tobiaslocker/base64#master` | base64 |
| Catch2 | 3.7.1 | `gh:catchorg/Catch2#v3.7.1` | Catch2::Catch2 |
| cxxopts | 3.2.1 | `jarro2783/cxxopts#3.2.1` | cxxopts |
| dgm-lib | 2.5.0 | `gh:nerudaj/dgm-lib#v2.5.0` | dgm-lib |
| dgm-lib 3 | 3.0.0-rc1 | `gh:nerudaj/dgm-lib#v3.0.0-rc1` | dgm::dgm |
| Eigen | master | `gl:libeigen/eigen#master` | Eigen3::Eigen |
| EnTT | 3.14.0 | `gh:skypjack/entt#v3.14.0` | EnTT::EnTT |
| FakeIt | 2.4.1 | `gh:eranpeer/FakeIt#2.4.1` | FakeIt::FakeIt |
| fsm-lib | 2.1.0 | `gh:nerudaj/fsm-lib#v2.1.0` | fsm-lib |
| nlohmann json | 3.11.3 | `gh:nlohmann/json#v3.11.3` | nlohmann::json |
| SFML | 2.6.1 | `gh:SFML/SFML#2.6.1` | sfml-main sfml-audio sfml-graphics sfml-window sfml-network |
| SFML 3 | 3.0.0 | `gh:SFML/SFML#3.0.0` | SFML::Main SFML::Audio SFML::Graphics SFML::Window SFML::Network |
7 changes: 7 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
cpp-init v0.6 changelog:
* Multiple .cmake files were condensed into single `cpp-init.cmake`
* C++ standard and generator platform is no longer set by default, you have to call `set_cpp23_x64` or `cpp_init`
* `fetch_prebuilt_dependency` no longer calls `add_subdirectory` on unpacked archive
* `get_git_version` renamed to `get_version_from_git`
* Fixed `glob_sources_and_headers` not recognizing files in `private_include`

cpp-init v0.5 changelog:
* Private headers are allowed by placing them into `private_include` folder
* `source` folder is allowed instead of `src`
Expand Down
Loading

0 comments on commit 575f507

Please sign in to comment.