• Stars
    star
    1,147
  • Rank 40,451 (Top 0.8 %)
  • Language CMake
  • License
    MIT License
  • Created over 5 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A collection of as simple as possible, modern CMake projects

Modern CMake Examples Mentioned in Awesome CMake

Overview

This repository is a collection of as simple as possible CMake projects (with a focus on installing). The idea is to try and help understand exactly what each part of a CMakeLists.txt file does and why it is needed.

This is basically intended as a series of reminders to help me remember how to use CMake 🤦

Please see the Core Example README for steps on using the example libraries and the Installing README for an overview of installing CMake libraries. The More Example section contains slightly more complex examples and will continue to grow.

Disclaimer

I am NOT a CMake expert - these examples may contain gaffs, faux pas, mistakes etc etc.. Please take everything with a pinch of salt and if you recognize a blatant error or mistake please feel free to create an issue or PR.

Background

For the longest time I just didn't grok installing in CMake1.

I didn't understand why you'd ever want to do it, or what it was useful for. When I started looking into how to do it I could not make head nor tail of all the various install commands. While trying to figure all this stuff out I was immersing myself in trying to learn Modern CMake (targets, targets targets...) and how these two things are related.

The examples in this repo are the culmination of many months of sporadic research to try and understand CMake more fully and write better CMake scripts.

I'm sharing my journey so far to hopefully help some other poor soul who is in the same boat I'm in. With any luck there will be something someone finds useful here.

For an explanation2 of what (in the context of CMake) installing is, please see the installing section and take a look at the various example projects for context.

  1. I recently discovered a kindred spirit on reddit
  2. My interpretation?

Miscellaneous

While using CMake over the last several months I've stumbled across a few useful little bits and bobs that I feel are worth recording/sharing.

Less cd-ing

To run CMake from your source directory (instead of having to mkdir build && cd build) you can pass -S and the path to your source folder (most likely just . for where you currently are) and -B to specify the build folder.

cd <project/root>
cmake -S . -B build/

You then just need to remember to call

cmake --build build/

to actually build your project.

Note: The -S option was added to CMake in version 3.13. Before then you could use the undocumented -H option. I'd recommend sticking with -S now if you can 🙂.

compile_commands.json

You should absolutely use -DCMAKE_EXPORT_COMPILE_COMMANDS=ON when generating your project to have CMake create a compile_commands.json file for you. This is useful for all sorts of tools (clang-tidy, cppcheck, oclint, include-what-you-use etc. etc...).

# when configuring from the root CMakeLists.txt of your project
cmake -S . -B build/ -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

Note: CMAKE_EXPORT_COMPILE_COMMANDS is only supported for Make and Ninja generators. The good news is it's pretty simple to use Ninja on Windows in place of Visual Studio/MSBuild - for instructions please see this repo.

TLDR: Add -G Ninja to the above command to use Ninja.

Defines

Sometimes it's really useful to be able to set defines at the command line when running the CMake generator. An easy way to do this is to add a simple generator expression to your CMakeLists.txt file in target_compile_definitions.

target_compile_definitions(
    ${PROJECT_NAME} PRIVATE
    $<$<BOOL:${YOUR_DEFINE}>:YOUR_DEFINE>)

In your code you can then use this define for some sort of conditional compilation.

#ifdef YOUR_DEFINE
// something useful
#endif // YOUR_DEFINE

And when invoking cmake you can pass a CMake variable like so if you want that macro to be defined.

# from the src/ (root) folder
cmake -S . -B build/ -DYOUR_DEFINE

If you don't pass the variable then the generator expression will evaluate to false and no define will be added.

Extra Output

Sometimes when building with CMake to diagnose an issue you might want more info about exactly what's being compiled. You can see everything that's passed to the compiler when building with the --verbose (-v) flag.

# from the src/ (root) folder
cmake --build build/ -v

This works for an array of generators (Make, Visual Studio, Ninja etc.).

CONFIG

You'll notice all of the find_package commands include the CONFIG keyword after the package name (and REQUIRED). This is to let CMake know we're explicitly using a CMake <package>-config.cmake file and not a FindModule command (all these examples use the more modern config approach so including CONFIG in the find_package command should be preferred).

DEBUG_POSTFIX

It is possible to set a property called DEBUG_POSTFIX on a given target to have a string appended to the name of the debug version of the library (usually d or -debug, but as far as I know it can be anything).

set_target_properties(
    ${PROJECT_NAME} PROPERTIES DEBUG_POSTFIX "d")

This is incredibly useful when installing libraries as it means if you build and install the Debug configuration and then build and install the Release configuration, the Debug version of the library won't be overridden (you'll just have your-libraryd.lib and your-library.lib) in the lib/ folder. This works for single and multi-config generators.

FetchContent

Check out the fetch-content example for a simple demonstration using the commands and see the links below by Sascha Offe, Kuba Sejdak and Michael Hirsch for more information.

ExternalProject_Add

Check out the external-project-add example for an (opinionated) introduction on how to take advantage of the command.

cmake-helpers

I've put together some little wrappers to reduce the amount of boilerplate needed when installing libraries. These can be pulled in using FetchContent. Please see cmake-helpers for more details.

Project Structure

When creating a library that is going to be used via installing (find_package) and/or add_subdirectory/FetchContent, it is wise to ensure the include paths for files are the same for both. For this reason it's (in my humble opinion) best to format your directory structure like so:

|-- <library-name>/
    |-- include/
        |-- <library-name>/
            |-- file1.h
            |-- file2.h
            |-- etc...
    |-- src/
        |-- file1.cpp
        |-- file2.cpp
        |-- etc...
    |-- CMakeLists.txt

When you specify target_include_directories (see below), have it point to the include/ folder so all includes wind up looking like #include "library-name/file1.h" as opposed to just #include "file1.h". This helps to compartmentalize the library files (similar to a namespace).

target_include_directories(
    ${PROJECT_NAME}
    INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
              $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)

The install command for the interface (.h) files then looks like this:

install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/${PROJECT_NAME}
        DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})

Now the include paths for the library will always be consistent whether using find_package or FetchContent.

Aside: For more information on approaches to project structure checkout out Pitchfork by vector-of-bool.

CMake Resources

I've been attempting to learn CMake for a while and have built up quite a list of articles/blogs/documentation that have helped inform my understanding up to this point. Please see them listed below (mainly for my benefit so I have them all in one place).

Articles

Documentation

Stack Overflow

YouTube

Books

Other

  • Cpplang Slack #cmake channel
    • There's some super helpful people on there, the search functionality is great too (someone likely will have had your problem before 😉).
  • vector-of-bool
    • Was incredibly kind in answering some of my dumb CMake questions - thank you!

More Repositories

1

sdl-bgfx-imgui-starter

A starter project for graphics applications using SDL, bgfx and Dear ImGui
C
299
star
2

unity-portal-rendering

Super small example of using offscreen render targets to achieve a portal effect in Unity
C#
251
star
3

cpp-handle-container

An implementation of a weak handle interface to a packed vector in C++
C++
13
star
4

c-handle-container

little test driven handle library in c
C
11
star
5

sokol-experiment

C
8
star
6

as

A header-only linear algebra math library in C++
C++
7
star
7

imgui.cmake

Independently maintained CMake build scripts for Dear ImGui.
CMake
6
star
8

switch-corresponding

Extension for VSCode to switch between interface and implementation file (.h/cpp and .m etc)
TypeScript
5
star
9

pikuma-3d-graphics

Computer graphics programming course
C
4
star
10

clang-tidy-and-clang-format

Document one way of how to setup clang-tidy and clang-format on Windows
C++
4
star
11

sdl-bgfx-imgui-as_1d-nonlinear-transformations

Inspired by "Math for Game Programmers: Fast and Funky 1D Nonlinear Transformations" by Squirrel Eiserloh
C++
3
star
12

opengl-sdl

C++
2
star
13

clang-experiments

Random investigations and experiments using Clang (mostly on Windows)
C++
2
star
14

vulkan-experiments

Test project to learn about Vulkan
C++
2
star
15

cpp-sky-down-example

A simple example using the polymorphic library
C++
1
star
16

cmake-helpers

Reusable, useful CMake utilities
CMake
1
star
17

as-c-math

C
1
star
18

breakout-tdd

C++
1
star
19

cpp-packed-hashtable

Experimental library to combine hash table lookup speed with vector iteration performance
C++
1
star
20

pr0g.github.io

HTML
1
star
21

bit-field-enum-class

A single header to provide bitwise operators for enum class types.
C++
1
star
22

sdl-bgfx-imgui-marching-cubes

Based off of sdl-bgfx-imgui-starter project - random experiments with marching cubes
C++
1
star
23

c-polymorphism

Little experiment to implement polymorphism in C
C
1
star
24

gtest-template

Starting point for a gtest project
CMake
1
star
25

as-camera

A lightweight camera library for simple graphics applications
C++
1
star
26

c-to-cpp-range-compile-times

Comparison of compile times between an C and C++ rangified code
C++
1
star
27

as-camera-input

C++
1
star