Docs
CMake
CMake Basics
Overview
CMake is a cross-platform build system generator. It doesn't build your code directly—it generates native build files (Makefiles, Visual Studio projects, etc.) that then compile your code.
Learning Objectives
By the end of this section, you will understand:
- •CMake basics and CMakeLists.txt structure
- •Creating executables and libraries
- •Managing dependencies
- •Modern CMake practices (target-based)
- •Using CMake with external libraries
Why CMake?
┌─────────────────────────────────────────────────────────────────────────┐
│ CMake WORKFLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ CMakeLists.txt │ │
│ │ (Platform- │ │
│ │ Independent) │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ CMake │ │
│ │ Generator │ │
│ └────────┬────────┘ │
│ │ │
│ ┌───────┼───────┐ │
│ ▼ ▼ ▼ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │Make- │ │Visual│ │Ninja │ ... other build systems │
│ │file │ │Studio│ │ │ │
│ └──────┘ └──────┘ └──────┘ │
│ │
│ Write once, build anywhere! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Minimal CMakeLists.txt
# Minimum required CMake version
cmake_minimum_required(VERSION 3.16)
# Project name and version
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# Create an executable
add_executable(my_app main.cpp)
Project Structure
┌─────────────────────────────────────────────────────────────────────────┐
│ TYPICAL CMAKE PROJECT STRUCTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ my_project/ │
│ ├── CMakeLists.txt ◄── Root CMake file │
│ ├── src/ │
│ │ ├── CMakeLists.txt ◄── Source directory CMake │
│ │ ├── main.cpp │
│ │ ├── utils.cpp │
│ │ └── utils.hpp │
│ ├── include/ │
│ │ └── my_project/ │
│ │ └── public_header.hpp │
│ ├── tests/ │
│ │ ├── CMakeLists.txt ◄── Test directory CMake │
│ │ └── test_utils.cpp │
│ ├── libs/ ◄── Third-party libraries │
│ └── build/ ◄── Build output (generated) │
│ ├── Makefile │
│ └── ... │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Building with CMake
┌─────────────────────────────────────────────────────────────────────────┐
│ BUILD COMMANDS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ # Step 1: Create build directory │
│ mkdir build && cd build │
│ │
│ # Step 2: Generate build files │
│ cmake .. │
│ │
│ # Step 3: Build the project │
│ cmake --build . │
│ │
│ # Or use make directly (if Makefile generator) │
│ make │
│ │
│ # Build with parallel jobs │
│ cmake --build . -j4 │
│ │
│ # Build specific target │
│ cmake --build . --target my_app │
│ │
└─────────────────────────────────────────────────────────────────────────┘
CMake Commands
Creating Targets
# Executable
add_executable(app_name source1.cpp source2.cpp)
# Static library (.a / .lib)
add_library(mylib STATIC source1.cpp source2.cpp)
# Shared library (.so / .dll)
add_library(mylib SHARED source1.cpp source2.cpp)
# Header-only library (INTERFACE)
add_library(mylib INTERFACE)
Setting Properties
# C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Or per-target (modern way)
target_compile_features(my_app PRIVATE cxx_std_17)
# Compiler flags
target_compile_options(my_app PRIVATE -Wall -Wextra -Wpedantic)
# Definitions
target_compile_definitions(my_app PRIVATE DEBUG_MODE=1)
Include Directories
# Include paths
target_include_directories(my_app
PUBLIC ${PROJECT_SOURCE_DIR}/include # Propagates to dependents
PRIVATE ${PROJECT_SOURCE_DIR}/src # Only for this target
)
Linking
# Link libraries
target_link_libraries(my_app
PRIVATE mylib # Internal dependency
PUBLIC otherlib # Propagates to dependents
)
Visibility Keywords
┌─────────────────────────────────────────────────────────────────────────┐
│ TARGET PROPERTY VISIBILITY │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ PRIVATE │ │
│ │ Only affects the target itself │ │
│ │ Example: Implementation details, internal includes │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ PUBLIC │ │
│ │ Affects target AND anything that links to it │ │
│ │ Example: Public headers, API definitions │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ INTERFACE │ │
│ │ Only affects things that link to the target │ │
│ │ Example: Header-only libraries │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Example: │
│ ┌─────────┐ PUBLIC ┌─────────┐ links ┌─────────┐ │
│ │ LibA │───────►│ LibB │──────►│ App │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ App gets LibA's public properties! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Finding Packages
# Find system-installed package
find_package(Threads REQUIRED)
# Find with components
find_package(Boost 1.70 REQUIRED COMPONENTS filesystem system)
# Use the found package
target_link_libraries(my_app
PRIVATE
Threads::Threads
Boost::filesystem
Boost::system
)
Common Package Patterns
# OpenSSL
find_package(OpenSSL REQUIRED)
target_link_libraries(app PRIVATE OpenSSL::SSL OpenSSL::Crypto)
# CURL
find_package(CURL REQUIRED)
target_link_libraries(app PRIVATE CURL::libcurl)
# SQLite
find_package(SQLite3 REQUIRED)
target_link_libraries(app PRIVATE SQLite::SQLite3)
FetchContent (Download Dependencies)
include(FetchContent)
# Declare dependency
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.2
)
# Make it available
FetchContent_MakeAvailable(json)
# Use it
target_link_libraries(my_app PRIVATE nlohmann_json::nlohmann_json)
Variables and Cache
# Regular variable
set(MY_VAR "value")
# Cache variable (user-configurable)
set(MY_OPTION "default" CACHE STRING "Description of option")
# Boolean option
option(ENABLE_TESTS "Build tests" ON)
# Use variables
message(STATUS "MY_VAR = ${MY_VAR}")
# Conditional
if(ENABLE_TESTS)
add_subdirectory(tests)
endif()
Subdirectories
# Root CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(MyProject)
# Add subdirectories
add_subdirectory(src)
add_subdirectory(tests)
# src/CMakeLists.txt
add_library(mylib source.cpp)
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# tests/CMakeLists.txt
add_executable(tests test_main.cpp)
target_link_libraries(tests PRIVATE mylib)
Complete Example
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(Calculator VERSION 1.0.0 LANGUAGES CXX)
# Settings
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Options
option(BUILD_TESTS "Build unit tests" ON)
# Create library
add_library(calc_lib
src/calculator.cpp
src/operations.cpp
)
target_include_directories(calc_lib
PUBLIC ${PROJECT_SOURCE_DIR}/include
PRIVATE ${PROJECT_SOURCE_DIR}/src
)
target_compile_options(calc_lib PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra>
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# Create executable
add_executable(calculator src/main.cpp)
target_link_libraries(calculator PRIVATE calc_lib)
# Tests
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# Installation
install(TARGETS calculator DESTINATION bin)
install(DIRECTORY include/ DESTINATION include)
Generator Expressions
# Compiler-specific flags
target_compile_options(app PRIVATE
$<$<CXX_COMPILER_ID:GNU>:-Wall>
$<$<CXX_COMPILER_ID:MSVC>:/W4>
)
# Configuration-specific
target_compile_definitions(app PRIVATE
$<$<CONFIG:Debug>:DEBUG_MODE>
$<$<CONFIG:Release>:NDEBUG>
)
# Build interface vs install interface
target_include_directories(lib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
Best Practices
┌─────────────────────────────────────────────────────────────────────────┐
│ MODERN CMAKE BEST PRACTICES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ✓ DO ✗ DON'T │
│ ───────────────────────────── ───────────────────────────── │
│ • Use target_* commands • Use include_directories() │
│ • Use PRIVATE/PUBLIC/INTERFACE • Use link_libraries() │
│ • Use generator expressions • Use CMAKE_CXX_FLAGS │
│ • Use FetchContent for deps • Use file(GLOB ...) for sources │
│ • Create IMPORTED targets • Hardcode paths │
│ • Use cmake --build • Mix old and modern CMake │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ OLD (avoid): │ │
│ │ include_directories(include) │ │
│ │ link_libraries(mylib) │ │
│ │ add_definitions(-DDEBUG) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ MODERN (prefer): │ │
│ │ target_include_directories(app PRIVATE include) │ │
│ │ target_link_libraries(app PRIVATE mylib) │ │
│ │ target_compile_definitions(app PRIVATE DEBUG) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Useful Commands
| Command | Description |
|---|---|
cmake -S . -B build | Configure with source and build dirs |
cmake --build build | Build the project |
cmake --build build -j | Build with all CPU cores |
cmake --install build | Install the project |
cmake --build build --target clean | Clean build |
cmake -DCMAKE_BUILD_TYPE=Release .. | Release build |
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .. | For IDE/tools |
Summary
| Concept | Purpose |
|---|---|
cmake_minimum_required | Set minimum CMake version |
project() | Define project name and languages |
add_executable() | Create executable target |
add_library() | Create library target |
target_* commands | Modern, target-based configuration |
find_package() | Locate installed dependencies |
FetchContent | Download dependencies at configure time |
Next Steps
- •Practice with
examples.cpp(a complete CMake project) - •Complete the
exercises.cppchallenges - •Try building real projects with CMake
- •Explore CPack for packaging