5.3.8Build Systems & Toolchain

CMake — CMakeLists.txt, add_executable, add_library, target_link_libraries

2,026 words9 min readdifficulty · medium

1. The core vocabulary


2. A minimal, complete project (derive it piece by piece)


3. Libraries and linking — the heart of it

Figure — CMake — CMakeLists.txt, add_executable, add_library, target_link_libraries

4. Transitive dependency derivation (Forecast-then-Verify)


5. Common mistakes (Steel-man each)


6. Active recall

Recall Answer before expanding
  • What are CMake's two build phases? → configure then build.
  • Which command builds something runnable vs linkable? → add_executable vs add_library.
  • You expose a dependency in your header. PUBLIC, PRIVATE, or INTERFACE? → PUBLIC.
  • Header-only library? → add_library(... INTERFACE) + link INTERFACE.
  • How does CMake decide link order? → from the dependency graph, automatically.
CMake's role in one phrase
A meta build-system that generates native build files; it doesn't compile.
What must be the first command in CMakeLists.txt
cmake_minimum_required(VERSION x.y).
Command to build a runnable program
add_executable(name sources...).
Command to build a linkable library
add_library(name [STATIC|SHARED|INTERFACE] sources...).
Command to declare a dependency between targets
target_link_libraries(target <KEYWORD> deps...).
PRIVATE meaning in TLL
Used only in my implementation; not propagated to users.
PUBLIC meaning in TLL
Used by me AND propagated to anyone linking me.
INTERFACE meaning in TLL
Not used by me; only propagated to my users (header-only).
STATIC vs SHARED library
STATIC (.a/.lib) copied into the binary; SHARED (.so/.dll) loaded at runtime.
Out-of-source build commands
cmake -S . -B build then cmake --build build.
Why prefer target_include_directories over include_directories
Former is target-scoped & propagates correctly; latter leaks into every target.
If A links B PUBLIC and C links A, does C see B
Yes — PUBLIC deps are transitive through A to C.
Recall Feynman: explain to a 12-year-old

Imagine you're building with LEGO. CMake is the instruction booklet writer, not the builder. You tell it: "I want a spaceship (program) and it needs the engine block (library)." CMake writes down the exact step-by-step order so a robot (the compiler) can build it on any table — your desk, your friend's, a factory. When you say the engine block "comes with its own wires that the whole spaceship needs," that's PUBLIC: those wires get shared. If the wires stay hidden inside the engine, that's PRIVATE. You only describe what connects to what; CMake handles the messy order of bolting things together.


Connections

  • Makefiles — the low-level files CMake generates.
  • Ninja build system — a faster generator backend (cmake -G Ninja).
  • Static vs Dynamic Linking — what STATIC/SHARED actually mean at link/runtime.
  • Compilation Pipeline preprocess-compile-assemble-link — TLL controls the link stage.
  • Header files and Include Guards — why include dir propagation matters.
  • Package Management find_package — how external libs become targets you can link.

Concept Map

read by

configure phase generates

build phase invokes

declares

via add_executable

via add_library

attach flags to

links

linked into

isolates junk from

CMakeLists.txt script

CMake meta build-system

Generated build files

Compiler

Target buildable unit

Executable

Library

target_* commands

target_link_libraries

Out-of-source build

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, CMake khud kuch compile nahi karta — woh ek meta build-system hai. Tum CMakeLists.txt me sirf describe karte ho ki kya banana hai, aur CMake usse asli build files (Makefile ya Ninja) generate karta hai jo har platform pe sahi chalti hai. Do main cheezein banti hain: add_executable se ek chalne wala program, aur add_library se ek library jise dusre targets link karte hain. Isko "target-centric" soch — har cheez (include dirs, flags, dependencies) target ke saath target_* commands se jodi jaati hai, global variables se nahi.

Sabse important concept hai target_link_libraries ke saath PUBLIC / PRIVATE / INTERFACE. Simple sawaal pucho: "yeh dependency mere header me dikhti hai ya sirf mere .cpp me?" Agar sirf .cpp me use hoti hai → PRIVATE (apne paas rakho, users ko mat do). Agar header me expose hoti hai → PUBLIC (mujhe bhi chahiye aur mere users ko bhi). Agar tumhare paas khud koi .cpp hi nahi (header-only lib) → INTERFACE (sirf users ko deta hoon). Yeh keyword decide karta hai ki dependency transitively aage propagate hogi ya nahi.

Yaad rakho mnemonic: "PUB shares, PRIV hides, INTER gives." Aur ek badi galti se bacho — purane tutorials wale include_directories() aur link_libraries() global commands mat use karo; woh har target me leak ho jaate hain. Hamesha target_include_directories aur target_link_libraries use karo sahi keyword ke saath. Link order ki tension mat lo — CMake dependency graph se khud sahi order nikalta hai, tum sirf batao kaun kis pe depend karta hai. Build aise: cmake -S . -B build phir cmake --build build — out-of-source taaki source folder ganda na ho.

Go deeper — visual, from zero

Test yourself — Build Systems & Toolchain

Connections