CMake Build System

CMake cheatsheet — configure, build, install projects. cmake -B build -DCMAKE_BUILD_TYPE=Release, cmake --build build -j4, cmake --install build. Full build system reference.

6 min read

What it is

CMake is a cross-platform, open-source build system generator that uses a platform-independent process to manage the build process of software. You reach for CMake when you need to compile code on different operating systems, with different compilers, and manage complex project dependencies.

Installation

Linux

sudo apt update
sudo apt install cmake
# or
sudo yum install cmake

macOS

Using Homebrew:

brew install cmake

Windows

Download the installer from the official CMake website (cmake.org) and follow the installation prompts. Ensure you add CMake to your system’s PATH during installation.

Core Concepts

  • CMakeLists.txt: The primary configuration file for a CMake project. It’s written in a human-readable scripting language.
  • Build System Generator: CMake itself doesn’t build your project. It generates build files (like Makefiles, Visual Studio solutions, Ninja files) that are then used by a native build tool to compile your code.
  • Build Type: Specifies the optimization and debugging level for the build (e.g., Debug, Release, RelWithDebInfo, MinSizeRel).
  • Targets: The output of the build process, such as executables, libraries (static or shared), or custom commands.
  • Variables: Used to configure the build process, control compiler flags, specify installation paths, etc.
  • Modules: CMake provides modules that encapsulate common tasks, like finding libraries (find_package) or setting up testing (EnableTesting).

Commands / Usage

CMake commands are typically invoked from the command line, often within a dedicated build directory. The general workflow is to run cmake to configure the project and then use the generated build tool (e.g., make, ninja, msbuild) to build it.

Configuring a Project

This is the first step, where CMake reads your CMakeLists.txt and generates the native build files.

  • Basic Configuration (in-source build - not recommended for production)

    cmake .
    

    Configures the project in the current directory.

  • Basic Configuration (out-of-source build - recommended) Create a build directory first:

    mkdir build
    cd build
    cmake ..
    

    Configures the project by looking for CMakeLists.txt in the parent directory. This keeps build artifacts separate from source code.

  • Specifying a Generator

    cmake -G "Unix Makefiles" ..
    # or
    cmake -G "Ninja" ..
    # or
    cmake -G "Visual Studio 16 2019" ..
    

    Explicitly chooses the build system generator to use. Common options are Unix Makefiles, Ninja, and various Visual Studio versions.

  • Specifying Installation Prefix

    cmake -DCMAKE_INSTALL_PREFIX=/usr/local ..
    

    Sets the default directory where installed targets will be placed.

  • Specifying Build Type

    cmake -DCMAKE_BUILD_TYPE=Release ..
    

    Configures the project for a specific build type (e.g., Debug, Release, RelWithDebInfo, MinSizeRel). This is crucial for performance and debugging.

  • Passing Cache Variables

    cmake -DBUILD_SHARED_LIBS=ON ..
    # or
    cmake -DSOME_CUSTOM_OPTION=TRUE ..
    

    Sets CMake cache variables. These can be used to control features, enable/disable components, or pass custom flags.

  • Specifying Toolchain File

    cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/my/toolchain.cmake ..
    

    Uses a toolchain file for cross-compilation or specific compiler/linker configurations.

Building a Project

After configuration, you use the native build tool.

  • Using Makefiles

    make
    

    Builds all targets defined in the generated Makefiles.

  • Using Ninja

    ninja
    

    Builds all targets using the Ninja build system. Often faster than Make.

  • Using Visual Studio (from command line) Navigate to the build directory (where the .sln file was generated) and use msbuild:

    msbuild YourProject.sln /p:Configuration=Release
    
  • Building a Specific Target

    make my_executable
    # or
    ninja my_library
    

    Builds only the specified target (e.g., an executable or library).

Installing a Project

Installs the built targets to the location specified by CMAKE_INSTALL_PREFIX.

  • Install using Makefiles

    make install
    
  • Install using Ninja

    ninja install
    
  • Install using Visual Studio Build the INSTALL target:

    msbuild YourProject.sln /p:Configuration=Release /t:INSTALL
    

Cleaning a Project

Removes build artifacts.

  • Clean using Makefiles

    make clean
    
  • Clean using Ninja

    ninja clean
    
  • Clean using CMake (removes build directory)

    rm -rf build
    

    This is often the most reliable way to do a "clean" build, as it removes all generated build files.

Caching and Re-configuring

CMake caches variables and build information.

  • Viewing the Cache

    cmake -LA ..
    

    Lists all cache variables (including advanced ones).

  • Editing the Cache

    ccmake ..
    

    Provides a curses-based interface to view and edit CMake cache variables.

  • Clearing the Cache Delete the CMakeCache.txt file in your build directory:

    rm build/CMakeCache.txt
    

    This forces CMake to re-evaluate all settings from scratch.

Common Patterns

  • Finding Libraries (e.g., Boost, OpenCV)

    cmake -DBOOST_ROOT=/path/to/boost ..
    

    In CMakeLists.txt:

    find_package(Boost REQUIRED COMPONENTS system filesystem)
    target_link_libraries(my_app PRIVATE Boost::system Boost::filesystem)
    

    This uses find_package to locate libraries and target_link_libraries to link them to your target. Boost::system is a modern CMake target.

  • Building Shared Libraries In CMakeLists.txt:

    add_library(my_shared_lib SHARED src/my_shared_lib.cpp)
    # Or globally:
    set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries")
    add_library(my_shared_lib src/my_shared_lib.cpp)
    

    By default, add_library creates static libraries. Use SHARED keyword or set BUILD_SHARED_LIBS to ON.

  • Installing Targets In CMakeLists.txt:

    install(TARGETS my_executable my_shared_lib
            DESTINATION bin
            RUNTIME DESTINATION bin
            LIBRARY DESTINATION lib
            ARCHIVE DESTINATION lib)
    

    Specifies where executables, shared libraries, and static libraries should be installed.

  • Running Tests with CTest In CMakeLists.txt:

    enable_testing()
    add_executable(my_test test/my_test.cpp)
    add_test(NAME MyTestCommand COMMAND my_test)
    

    Configure with cmake .., build with make or ninja, then run tests:

    ctest
    # or
    make test
    # or
    ninja test
    
  • Conditional Compilation Based on OS In CMakeLists.txt:

    if(WIN32)
        message("Building on Windows")
        target_sources(my_app PRIVATE win32_specific.cpp)
    elseif(APPLE)
        message("Building on macOS")
        target_link_libraries(my_app PRIVATE "-framework Cocoa")
    else()
        message("Building on Linux/Other Unix")
    endif()
    

    Use built-in variables like WIN32, APPLE, UNIX to control logic.

  • Cross-Compilation Setup Create a toolchain.cmake file:

    # toolchain.cmake
    set(CMAKE_SYSTEM_NAME Linux)
    set(CMAKE_SYSTEM_PROCESSOR arm)
    
    set(CMAKE_C_COMPILER   /path/to/cross/gcc)
    set(CMAKE_CXX_COMPILER /path/to/cross/g++)
    
    set(CMAKE_FIND_ROOT_PATH /path/to/cross/sysroot)
    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
    

    Configure with:

    cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake ..
    

Gotchas

  • Out-of-Source Builds are Essential: In-source builds (running cmake . in your source directory) can lead to a messy source tree with generated build files. Always create a separate build directory (mkdir build && cd build && cmake ..).
  • Build Types: If you don’t specify a CMAKE_BUILD_TYPE when configuring for Makefiles or Ninja, it defaults to Debug. For Visual Studio, it’s common to build all configurations or select one during the build step. Explicitly setting CMAKE_BUILD_TYPE is good practice.
  • Cache Invalidation: CMake caches variables. If you change compiler flags or paths, sometimes you need to delete CMakeCache.txt or run cmake --build . --target clean followed by re-configuring to ensure changes are picked up correctly.
  • find_package Behavior: find_package searches specific locations and uses hints. If it fails, check CMAKE_PREFIX_PATH or provide hints via HINTS in find_package calls within CMakeLists.txt.
  • Generator Differences: Generators (Unix Makefiles, Ninja, Visual Studio) have different capabilities and syntax. What works perfectly for one might need slight adjustments for another. Ninja is generally recommended for speed.
  • target_link_libraries vs link_libraries: Prefer target_link_libraries for managing dependencies per target. link_libraries is a global command and can lead to harder-to-manage dependencies.
  • Installation Paths: Be mindful of CMAKE_INSTALL_PREFIX. On Linux/macOS, /usr/local is common for user-installed software, while system packages go in /usr. On Windows, relative paths or specific application directories are more typical.
  • Compiler Warnings: CMake itself doesn’t enforce compiler warning levels. You need to explicitly add flags in your CMakeLists.txt, often conditionally based on compiler and build type.
    # Example: Enable all warnings and treat them as errors for Release builds
    if(CMAKE_BUILD_TYPE MATCHES "Release")
        target_compile_options(my_app PRIVATE -Wall -Wextra -Werror)
    endif()