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.txtin 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
makeBuilds all targets defined in the generated Makefiles.
-
Using Ninja
ninjaBuilds all targets using the Ninja build system. Often faster than Make.
-
Using Visual Studio (from command line) Navigate to the build directory (where the
.slnfile was generated) and usemsbuild:msbuild YourProject.sln /p:Configuration=Release -
Building a Specific Target
make my_executable # or ninja my_libraryBuilds 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
INSTALLtarget: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 buildThis 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.txtfile in your build directory:rm build/CMakeCache.txtThis 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_packageto locate libraries andtarget_link_librariesto link them to your target.Boost::systemis 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_librarycreates static libraries. UseSHAREDkeyword or setBUILD_SHARED_LIBStoON. -
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 withmakeorninja, 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,UNIXto control logic. -
Cross-Compilation Setup Create a
toolchain.cmakefile:# 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_TYPEwhen configuring for Makefiles or Ninja, it defaults toDebug. For Visual Studio, it’s common to build all configurations or select one during the build step. Explicitly settingCMAKE_BUILD_TYPEis good practice. - Cache Invalidation: CMake caches variables. If you change compiler flags or paths, sometimes you need to delete
CMakeCache.txtor runcmake --build . --target cleanfollowed by re-configuring to ensure changes are picked up correctly. find_packageBehavior:find_packagesearches specific locations and uses hints. If it fails, checkCMAKE_PREFIX_PATHor provide hints viaHINTSinfind_packagecalls withinCMakeLists.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.Ninjais generally recommended for speed. target_link_librariesvslink_libraries: Prefertarget_link_librariesfor managing dependencies per target.link_librariesis a global command and can lead to harder-to-manage dependencies.- Installation Paths: Be mindful of
CMAKE_INSTALL_PREFIX. On Linux/macOS,/usr/localis 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()