CMake
The CMake idea
CMake is a build system generator.
It does not usually compile your C++ code by itself. Instead, you describe your
project in CMakeLists.txt, and CMake generates files for a real build tool,
such as Makefiles, Ninja files, Visual Studio projects, or Xcode projects.
The main idea is:
This lets the same C++ project build on different operating systems and with different compilers.
Main functionality
CMake is mainly used to:
- define executables with
add_executable - define libraries with
add_library - connect files, include folders, and compiler options to targets
- find external packages with
find_package - link libraries with
target_link_libraries - create build configurations such as Debug and Release
- organize tests, install rules, and generated files
Modern CMake focuses on targets.
A target is something CMake can build, like an executable or a library.
Example:
Build it:
Meaning:
-S .means the source folder is the current folder-B buildmeans put generated build files in thebuildfoldercmake --build buildasks CMake to run the real build tool
Responsibility: CMake, Ninja, Make, compiler
CMake, Ninja, Make, and the compiler have different jobs.
flowchart TD
A[CMakeLists.txt] --> B[Configure step<br/>cmake -S . -B build]
B --> C[Generated build files<br/>build.ninja or Makefile]
C --> D[Build step<br/>cmake --build build]
D --> E[Ninja or Make runs commands]
E --> F[Compiler creates object files]
F --> G[Linker creates final binary]
G --> H[Executable or library]
Short responsibility split:
CMakeLists.txt: describes the projectcmake: configures the project and generates build filesNinjaorMake: executes the build steps in the correct order- compiler: turns
.cppfiles into object files - linker: connects object files and libraries into the final binary
CMake is the planner.
Ninja or Make is the worker that follows the generated plan.
The compiler and linker are the tools that actually create the binary.
Configure step vs build step
CMake usually has two main steps: configure and build.
Configure step
Configure means: read the project description and prepare the build folder.
Command:
During configure, CMake:
- reads
CMakeLists.txt - checks which compiler is available
- checks project options
- finds dependencies with
find_package - creates generated build files, such as
build.ninjaorMakefile - writes cache values into
build/CMakeCache.txt
Configure does not normally compile your .cpp files.
Build step
Build means: use the generated build files to compile and link the program.
Command:
During build, Ninja or Make:
- checks which source files changed
- compiles
.cppfiles into object files - links object files into an executable or library
- runs only the commands needed for the current changes
The short difference:
What Ninja or Makefile does
Ninja and Make are build tools.
They read generated build files and decide which commands must run.
Their main jobs are:
- compile only files that changed
- run build steps in the correct dependency order
- run multiple compile jobs in parallel when possible
- call the compiler with the right flags
- call the linker to create the final executable or library
For example, if only robot.cpp changed, Ninja or Make should rebuild
robot.cpp, then relink the final executable. It should not rebuild every file
in the project.
With CMake, you usually do not call Ninja or Make directly.
Use:
CMake will call the correct generated build tool for that build folder.
How to learn CMake
Learn CMake in this order:
- Build one executable from one
main.cpp. - Add more
.cppand.hfiles to the same executable. - Create a library with
add_library. - Link the library to an executable with
target_link_libraries. - Add include folders with
target_include_directories. - Learn Debug and Release builds.
- Learn
find_packagefor external dependencies. - Learn testing with CTest or GoogleTest.
- Learn
CMakePresets.jsonafter the basics are clear.
For normal projects, focus first on these commands:
Avoid learning old global commands first, such as include_directories() and
link_libraries(). Modern CMake usually prefers target-based commands because
they keep settings attached to the library or executable that needs them.