Back to all posts

CMake Patterns

Published

Last Updated: 2025-02-13

I’ve been getting into C++, and everyone seems to do CMake slightly differently, and often some newer patterns are left out of examples. This is meant as a reference for common CMake templates and explanations.

Please note! I am not a CMake expert. A lot of these are probably out of personal preference. This is not a CMake tutorial, just a reference.

By the way, I use the CMake variable PROJECT_NAME throughout, which just equals whatever you put in your project() declaration.

FetchContent

This requires at least CMake 3.14, though you can remove FetchContent_MakeAvailable() and use down to CMake 3.11

include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)

# set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) # Set any options before download
FetchContent_Declare(
        raylib
        GIT_REPOSITORY "https://github.com/raysan5/raylib.git"
        GIT_TAG "5.5"
        GIT_PROGRESS TRUE
        GIT_SHALLOW TRUE
)

FetchContent_MakeAvailable(raylib)

target_link_libraries(${PROJECT_NAME} PUBLIC raylib)

Using FetchContent, we can tell CMake to automatically clone a dependency, build it, and make it available for us to link against. Not all libraries are set up to use this, but when they are, this pattern cleans up your file structure a lot. In the sample data, I am pulling Raylib version 5.5, showing download progress, and using a shallow clone, which only downloads the most recent data instead of everything. You can set variables for the fetched content to use, just declare them before FetchContent_Declare, or the settings may not apply.

Also, you only need to have include(FetchContent) and set(FETCHCONTENT_QUIET FALSE) once.

Use Glob to make Hierarchical Code

In CMake 3.12, logic was added so that, if the CONFIGURE_DEPENDS flag was set, CMake would re-scan the associated glob to check for file changes. However, the documentation warns that it is still unreliable, though it seems to be working with Ninja and Make.

# /src/CMakeLists.txt file(GLOB SOURCE_FILES CONFIGURE_DEPENDS *.cpp *.h)

target_sources(${PROJECT_NAME} PRIVATE ${SOURCE_FILES}) # add_subdirectory() for any sub-folders

And in your root CMake file:

# /CMakeLists.txt # — snip — add_executable(${PROJECT_NAME}) add_subdirectory(src) # Add src/CMakeLists.txt # Also add any other subdirectories you have the pattern in # — snip —

This will descend into the src directory, and add all the source files, and you can add_subdirectory in that file, as far down as you wish.

I find this is easier to work with, as I am used to very hierarchical organization in both code and folder structure. (Like Type/Javascript uses folder structure in imports). I encourage you to read the documentation to get a better idea about the pros and cons.