5.3.8 · Coding › Build Systems & Toolchain
Intuition 30-second mental model
CMake ek meta build-system hai: yeh tumhara code compile nahi karta . Yeh real build files (Makefiles, Ninja files, Visual Studio projects) generate karta hai ek portable description se jo tum CMakeLists.txt mein likhte ho. Tum targets (jo cheezein build karni hain) aur unke beech ke relationships describe karte ho; CMake tumhare liye compiler flags, include paths aur link order khud figure out kar leta hai.
WHY it exists: raw Makefiles likhna platform-specific aur error-prone hota hai. CMake tumhe ek baar kya chahiye bolne deta hai, aur yeh Linux, macOS, Windows par sahi build instructions produce karta hai.
Target ek buildable unit hai. Do basic tarah ke targets hote hain:
executable — add_executable se produce hota hai (ek program jo tum run kar sako).
library — add_library se produce hota hai (code jise doosre targets link karte hain).
Baaki sab kuch (include dirs, compile flags, dependencies) ko target_* commands use karke target ke saath attach kiya jaata hai. Modern CMake "target-centric " hai — tum almost kabhi global variables set nahi karte.
Definition CMakeLists.txt
Woh script jise CMake padhta hai. Yeh imperative hoti hai (commands top-to-bottom run hoti hain) aur tumhare project ke root mein rehti hai (aur optionally subdirectories mein jo add_subdirectory ke zariye pull in hote hain).
Worked example Sabse chhota valid CMakeLists.txt
cmake_minimum_required ( VERSION 3.16) # line 1
project (Calc LANGUAGES CXX) # line 2
add_executable (calc main.cpp) # line 3
Line 1 kyun? CMake ka behaviour ("policies") versions ke beech change hota hai. Minimum version declare karne se builds reproducible bante hain aur ek hard error silenced hoti hai. Yeh pehla hona chahiye.
Line 2 kyun? project() project ko naam deta hai, listed LANGUAGES ke liye compiler set up karta hai (yahan C++), aur ${PROJECT_SOURCE_DIR} jaise variables define karta hai. Iske bina CMake C aur C++ assume karta hai aur tumhe warn karta hai.
Line 3 kyun? Yeh main.cpp se build hone wala calc naam ka ek target declare karta hai. Target name ek internal handle hai; actual file calc (ya calc.exe) hoti hai.
Intuition "Out-of-source" builds kyun?
Tum CMake ko ek alag build/ folder se run karte ho taaki generated junk tumhare source ko kabhi pollute na kare.
cmake -S . -B build # configure: CMakeLists padho, build files build/ mein generate karo
cmake --build build # build: actually compiler invoke karo
Do phases mein KYA hota hai: configure (CMake tumhara script run karta hai) phir build (generator compiler run karta hai). CMakeLists.txt edit karna configure ko automatically re-trigger karta hai.
Definition target_link_libraries (TLL)
target_link_libraries(<target> <PUBLIC|PRIVATE|INTERFACE> <deps...>)
CMake ko batata hai ki <target> <deps> par depend karta hai . CMake phir:
pehle dependency build karta hai (sahi link order automatic hai),
use pull in karne ka linker flag add karta hai,
keyword ke according dependency ke include dirs / flags propagate karta hai.
Intuition PUBLIC / PRIVATE / INTERFACE — keywords ke peeche WHY
Poochho: "kya mera header is dependency ko expose karta hai, ya sirf meri .cpp?"
PRIVATE — main ise sirf apni implementation mein use karta hoon. Mere users ko mat dena.
INTERFACE — main khud use nahi karta, lekin mere users ko chahiye (header-only).
PUBLIC — dono: main bhi use karta hoon AUR mere users bhi karenge.
Yeh transitive propagation control karta hai: agar app mathlib ko fmt ke saath PUBLIC-ly link karta hai, toh app ko automatically fmt bhi mil jaata hai. Galat choose karna = broken ya bloated builds.
Worked example Library ke saath two-target project
Files: math/add.cpp, math/add.h, app/main.cpp
cmake_minimum_required ( VERSION 3.16)
project (Calc LANGUAGES CXX)
add_library (mathlib STATIC math/add.cpp) # (A)
target_include_directories (mathlib PUBLIC math) # (B)
add_executable (calc app/main.cpp) # (C)
target_link_libraries (calc PRIVATE mathlib) # (D)
A kyun? Reusable code ko ek target ke roop mein bundle karo, loose files ke roop mein nahi. Ab kaafi saare programs ise reuse kar sakte hain.
B PUBLIC kyun? add.h mathlib ke interface ka hissa hai — jo bhi mathlib link kare use #include "add.h" karna padega. PUBLIC ka matlab hai "mujhe bhi yeh dir chahiye aur mere users ko bhi." calc isliye apna include_directories likhe bina add.h dhundh sakta hai .
D PRIVATE kyun? calc ek executable hai — kuch bhi iske against link nahi hota, toh iske aage propagation bekar hai. PRIVATE sahi, minimal choice hai.
Worked example Teen-level chain
add_library (util STATIC util.cpp)
add_library (core STATIC core.cpp)
add_executable (app app.cpp)
target_link_libraries (core PUBLIC util) # core ke headers util expose karte hain
target_link_libraries (app PRIVATE core)
Forecast: Kya app util link karega?
Reason it out: core → util PUBLIC hai, toh util core ke interface ka hissa hai. Jab app core link karta hai, core ke PUBLIC parts (including util) app mein flow karte hain.
Verify: ✅ Haan — app core aur util dono link karta hai, aur util ke include dirs bhi paata hai.
Ab ise flip karo: agar yeh target_link_libraries(core PRIVATE util) hota, toh util core ki .cpp ke andar chhupa rehta. app core link karta lekin util ke headers nahi dekh paata (sahi, kyunki use unki zaroorat nahi honi chahiye).
Common mistake "Main bas woh .cpp file har executable mein add kar dunga jise chahiye."
Sahi kyun lagta hai: compile ho jaata hai! add_executable(app a.cpp shared.cpp) kaam karta hai.
Galat kyun hai: shared.cpp har target ke liye recompile hoti hai, aur uska interface define karne ki koi single jagah nahi hai — tum har jagah include dirs copy-paste karte ho.
Fix: shared code ko ek baar add_library mein wrap karo; target_link_libraries use karo.
include_directories() aur link_libraries() use karo."
Sahi kyun lagta hai: purane tutorials mein dikhta hai; global hai aur "just works."
Galat kyun hai: yeh directory-scoped, non-target commands hain. Yeh settings har target mein leak karte hain, wrong flags aur hard-to-debug builds cause karte hain.
Fix: hamesha PUBLIC/PRIVATE/INTERFACE ke saath target_* versions prefer karo.
add_library aur target_link_libraries ka order link order se match karna chahiye."
Sahi kyun lagta hai: raw g++ order-sensitive hota hai (-lcore -lutil).
Galat kyun hai: CMake ek dependency graph banata hai aur link order khud compute karta hai.
Fix: bas declare karo kya kis par depend karta hai ; linker order karne ke liye CMake par trust karo.
Common mistake "Header-only lib ke link ko PRIVATE mark karna."
Sahi kyun lagta hai: "Main koi .cpp compile nahi karta, toh yeh sirf mera hai."
Galat kyun hai: ek INTERFACE/header-only library ki koi implementation nahi hoti ; uska poora point users ko flags dena hai. PRIVATE = kisi ko nahi milte.
Fix: INTERFACE use karo.
Recall Expand se pehle answer karo
CMake ke do build phases kya hain? → configure phir build .
Kaun sa command runnable vs linkable cheez banata hai? → add_executable vs add_library.
Tum apne header mein dependency expose kar rahe ho. PUBLIC, PRIVATE, ya INTERFACE? → PUBLIC .
Header-only library? → add_library(... INTERFACE) + INTERFACE link karo.
CMake link order kaise decide karta hai? → dependency graph se, automatically.
CMake ka role ek phrase mein Ek meta build-system jo native build files generate karta hai; yeh compile nahi karta.
CMakeLists.txt mein pehla command kya hona chahiye cmake_minimum_required(VERSION x.y).
Runnable program build karne ka command add_executable(name sources...).
Linkable library build karne ka command add_library(name [STATIC|SHARED|INTERFACE] sources...).
Targets ke beech dependency declare karne ka command target_link_libraries(target <KEYWORD> deps...).
TLL mein PRIVATE ka matlab Sirf meri implementation mein use hota hai; users tak propagate nahi hota.
TLL mein PUBLIC ka matlab Mujhe bhi use hota hai AUR jo mujhe link kare unhe bhi propagate hota hai.
TLL mein INTERFACE ka matlab Mujhe use nahi hota; sirf mere users tak propagate hota hai (header-only).
STATIC vs SHARED library STATIC (.a/.lib) binary mein copy hoti hai; SHARED (.so/.dll) runtime par load hoti hai.
Out-of-source build commands cmake -S . -B build phir cmake --build build.
target_include_directories ko include_directories se kyun prefer karo Pehla target-scoped hai aur sahi se propagate karta hai; doosra har target mein leak karta hai.
Agar A, B ko PUBLIC link kare aur C, A ko link kare, toh kya C ko B dikhega Haan — PUBLIC deps A se C tak transitive hote hain.
Recall Feynman: 12-saal ke bachche ko samjhao
Socho tum LEGO se build kar rahe ho. CMake instruction booklet writer hai, builder nahi.
Tum use batate ho: "Mujhe ek spaceship (program) chahiye aur use engine block (library) chahiye." CMake exact step-by-step order likh deta hai taaki ek robot (compiler) ise kisi bhi table par build kar sake — tumhare desk par, tumhare dost ke, ek factory mein. Jab tum kehte ho ki engine block "apne saath wires laata hai jo poori spaceship ko chahiye," woh hai PUBLIC : woh wires share ho jaate hain. Agar wires engine ke andar chhupe rehte hain, woh hai PRIVATE . Tum sirf describe karte ho ki kya kisse connect hai; CMake cheezein bolne ka messy order handle karta hai.
"PUB share karta hai, PRIV chhupata hai, INTER deta hai."
PUBLIC = users ke saath shared, PRIVATE = meri .cpp mein chhupa, INTERFACE = mera kuch nahi, main sirf deta hoon.
Makefiles — woh low-level files jo CMake generate karta hai.
Ninja build system — ek faster generator backend (cmake -G Ninja).
Static vs Dynamic Linking — STATIC/SHARED ka link/runtime par actually kya matlab hai.
Compilation Pipeline preprocess-compile-assemble-link — TLL link stage control karta hai.
Header files and Include Guards — kyun include dir propagation matter karta hai.
Package Management find_package — external libs target kaise bante hain jise tum link kar sako.
configure phase generates