5.3.6 · Coding › Build Systems & Toolchain
Intuition Make ka ek-sentence soul
Make ek dependency-driven build tool hai: yeh kisi file ko tab hi rebuild karta hai jab uski koi dependency us se newer ho — taaki jo nahi badla usse kabhi recompile na karna pade.
Intuition Woh problem jo yeh solve karta hai
Socho ek project mein 50 .c files hain. Har baar ek line change karne par sab compile karna minutes waste karta hai. Make track karta hai kya kis par depend karta hai aur sirf minimum set of files ko rebuild karta hai. Core idea hai timestamp comparison : agar output apne input se purana hai, toh usse regenerate karo.
Make asliye ek file ka graph (DAG — Directed Acyclic Graph) ke baare mein sochta hai. Har node ek file hai; ek edge ka matlab hai "yeh file uss file se build hoti hai."
Make ke ek rule ke teen parts hote hain:
target: prerequisites
recipe ← MUST start with a literal TAB
Target : woh file jo create karni hai (ya ek label jo "happen" karna hai).
Prerequisites : woh files jinpar target depend karta hai (uske inputs).
Recipe : shell commands jo target ko prerequisites se build karti hain.
Common mistake TAB ki jagah spaces
Steel-man: editors tabs ko spaces mein auto-convert kar dete hain, aur recipe lagti hai indented, isliye sahi lagta hai. Fix: recipe line ek real TAB character se shuru honi chahiye. Classic error hai *** missing separator. Stop. — iska almost hamesha matlab hai "tumne spaces use kiye."
Yeh recursive hai: t ( P i ) check karne se pehle, Make pehle ensure karta hai ki har P i khud up-to-date hai (prerequisites pehle build karo). Yeh dependency DAG ka post-order traversal hai.
Definition Variable flavors
Recursive (=): har baar use hone par expand hota hai (lazy). CC = gcc
Simple (:=): ek baar, immediately expand hota hai (eager).
Conditional (?=): sirf tab assign karo agar pehle se set nahi hai.
Append (+=): existing value mein add karo.
:= vs = kyun matter karta hai
A = 1
B = $( A ) # recursive: B "expression yaad rakhta hai"
A = 2
# ab $(B) == 2
C := $( A ) # simple: C "abhi ki value snapshot karta hai"
A = 3
# ab $(C) == 2
Yeh step kyun? = expansion ko use hone tak defer karta hai, isliye B latest A re-read karta hai. := definition ke time par value lock kar deta hai.
Variables ko $(VAR) ya ${VAR} se use karo. Single character ke liye parens ki zaroorat nahi: $@.
Definition Automatic variables
Recipe ke andar, Make yeh per rule define karta hai:
$@ — target ka naam.
$< — pehla prerequisite .
$^ — saare prerequisites , space-separated, duplicates removed.
$? — sirf woh prerequisites jo target se newer hain (stale wale).
$* — woh stem jo pattern rule ke % se match hua.
Intuition Yeh kyun exist karte hain
Yeh tumhe ek generic rule likhne dete hain jo kaafi files ke liye kaam kare. Inke bina tumhe filenames target: prereqs line aur recipe dono mein repeat karni padti — aur woh sync se bahar ho jaate. Automatic variables guarantee karte hain ki recipe hamesha is rule ki actual files ko refer kare.
Worked example Ek pattern rule, poori tarah decode kiya
CC := gcc
CFLAGS := -Wall -O2
% .o : % .c # koi bhi X.o banao X.c se
$( CC ) $( CFLAGS ) -c $< -o $@
Target main.o ke liye main.c se match karte hue:
$< → main.c (Kyun? source pehla/single prerequisite hai — -c ko yahi chahiye).
$@ → main.o (Kyun? -o ko output naam chahiye = target).
$* → main (woh stem jo % se match hua).
$^ se linking
app : main.o utils.o math.o
$( CC ) $^ -o $@
$^ kyun? Linker ko saari object files chahiye. $^ expand hota hai main.o utils.o math.o mein, aur $@ hai app. Agar humne $< use kiya hota toh sirf main.o link hoti aur undefined-reference errors aate.
Worked example Sab kuch jodke
CC := gcc
CFLAGS := -Wall -Wextra -O2
TARGET := app
SRCS := main.c utils.c math.c
OBJS := $( SRCS:.c=.o ) # substitution: main.o utils.o math.o
.PHONY : all clean # yeh labels hain, files nahi
all : $( TARGET )
$( TARGET ) : $( OBJS )
$( CC ) $^ -o $@
% .o : % .c
$( CC ) $( CFLAGS ) -c $< -o $@
clean :
rm -f $( OBJS ) $( TARGET )
.PHONY kyun? Agar kabhi clean naam ki ek file exist kare, toh Make sochega ki clean target "up to date" hai aur use skip kar dega. .PHONY kehta hai "yeh target ek command hai, file nahi — hamesha run karo isse."
$(SRCS:.c=.o) kyun? Substitution reference: har word mein .c suffix ko .o se replace karta hai, isliye SRCS edit karne par OBJS automatically update ho jaata hai.
$(TARGET) ka $(OBJS) par depend karna bhool jaana
Steel-man: tum all: mein directly link command likh sakte ho, aur yeh ek baar kaam karta hai — toh theek lagta hai. Fix: $(OBJS) ko prerequisites mein list kiye bina, Make kabhi objects rebuild nahi karta jab .c change ho, aur tera binary silently stale ho jaata hai.
OBJS mein Recursive vs simple
Steel-man: OBJS = $(SRCS:.c=.o) := se identical lagta hai . Fix: deferred (=) expansion ke saath SRCS ki baad ki reassignment combine ho jaaye toh value unexpectedly change ho sakti hai. Derived lists ke liye := prefer karo taaki snapshot stable rahe.
Recall Answer padhne se pehle predict karo
Diya gaya:
% .o : % .c
gcc -c $< -o $@
aur target parser.o ke liye prerequisites parser.c parser.h hain, predict karo ki $<, $@, $^ kya expand honge.
Answer: $< = parser.c (pehla prereq), $@ = parser.o, $^ = parser.c parser.h (sab). .h $^ mein hai aur $< position mein sirf tab hota agar woh pehla hota — yahan .c pehla hai.
Make rule ke teen parts kya hain? Target, prerequisites, aur recipe.
Recipe line TAB se kyun shuru honi chahiye? Make literal TAB ko separator ke roop mein use karta hai jo line ko recipe mark karta hai; spaces se "missing separator" error aata hai.
Target T ke liye Make ka rebuild condition Rebuild karo agar T exist nahi karta YA koi bhi prerequisite T se newer hai (t(P_i) > t(T)).
= aur := mein antar= recursive/lazy hai (har use par expand hota hai); := simple/eager hai (definition par ek baar expand hota hai).
$@ ka kya matlab hai?Current rule ka target naam.
$< ka kya matlab hai?Pehla prerequisite.
$^ ka kya matlab hai?Saare prerequisites, space-separated, duplicates removed.
$? ka kya matlab hai?Sirf woh prerequisites jo target se newer hain (stale wale).
$* ka kya matlab hai?Woh stem jo pattern rule mein % se match hua.
.PHONY kya karta hai?Target ko real file nahi declare karta, isliye uski recipe hamesha run hoti hai chahe us naam ki file exist kare.
$(SRCS:.c=.o) kya produce karta hai?Ek substitution reference jo SRCS ke har word mein .c suffix ko .o se replace karta hai.
app: main.o utils.o mein, recipe $(CC) $^ -o $@, kya link hoga?main.o aur utils.o ko app mein link karta hai; $^=saari objects, $@=app.
Make dependency DAG par kaunsa traversal order use karta hai? Post-order — target se pehle prerequisites build karo.
?= kyun use karo?Variable ko sirf tab assign karo agar already defined nahi hai (overridable defaults ke liye acha hai).
Recall Feynman: ek 12-saal ke bachche ko explain karo
Socho tumhare paas ek sandwich-banane wala robot hai. Tum use batate ho: "Sandwich banane ke liye, pehle bread aur filling chahiye." Robot ek smart tarike se aalsi hai: agar sandwich already bani hai aur tab se kisi ne bread ya filling nahi chhuyi, toh woh doosri banane mein time waste nahi karega. Lekin agar tumne fresh bread kharidi (sandwich se newer!), toh woh jaanta hai purani sandwich out of date hai aur nayi banata hai. Target sandwich hai, prerequisites bread aur filling hain, aur recipe hai "filling ko bread ke beech rakh do." Chhote symbols jaise $@ sirf shortcuts hain taaki robot keh sake "jo cheez main bana raha hun" bina uska naam dobara likhe.
Mnemonic Automatic variables yaad rakho
"@ hai Aim, < hai leftmost (pehla), ^ hai poori hat (sab)."
$@ = Aim/target · $< = pehla (< left point karta hai) · $^ = sab (woh ^ hat sab kuch cover karta hai).
Compilation Pipeline — Make preprocess→compile→assemble→link stages orchestrate karta hai.
Linkers and Object Files — kyun $^ (saari .o) linker ko feed hoti hai.
Directed Acyclic Graphs (DAG) — Make ka dependency model ek DAG hai jo post-order traverse hota hai.
CMake and Build Generators — higher-level tools jo Makefiles generate karte hain.
Incremental Compilation — woh timestamp principle jo Make ko fast banata hai.
Shell Scripting Basics — recipes sirf shell commands hain.