Make को समझना

August 1, 2026 · विज़िटर

make लगभग हर embedded और systems प्रोजेक्ट में दिखता है जिसे मैंने छुआ है — Yocto recipes, kernel builds, U-Boot, छोटे-छोटे फ़र्मवेयर repos। यह उन टूल्स में से भी एक है जिन्हें लोग सालों तक बस नक़ल करके इस्तेमाल करते हैं, पिछले प्रोजेक्ट से एक Makefile कॉपी करते हुए, बिना कभी उसके पीछे के model को ठीक से समझे। यहाँ वह model सीधे-सीधे रखा गया है।

Make है क्या?

Make एक build automation टूल है: यह एक फ़ाइल (Makefile) पढ़ता है जो targets का एक सेट, हर target जिन prerequisites पर निर्भर करता है, और उन prerequisites से वह target बनाने के लिए ज़रूरी recipe (shell commands) बताती है। इस विवरण के आधार पर, make यह पता लगाता है कि कौन-से targets पुराने (out of date) हो चुके हैं, और सिर्फ़ उन्हीं recipes को चलाता है जो उन्हें अपडेट करने के लिए ज़रूरी हैं — इससे ज़्यादा कुछ नहीं।

main: main.o utils.o
	gcc -o main main.o utils.o

main.o: main.c
	gcc -c main.c

make चलाने पर, यह main.o तभी बनाता है जब main.c, main.o से नया हो (या main.o अभी मौजूद ही न हो), फिर main को तभी link करता है जब main.o या utils.o बदला हो। कुछ भी अनावश्यक रूप से दोबारा नहीं बनता।

यह किसके लिए है?

Make को Stuart Feldman ने अप्रैल 1976 में Bell Labs में लिखा था। उन्हीं के अनुसार, इसकी वजह बनी एक सहकर्मी — Steve Johnson, जो yacc के लेखक हैं — जिन्होंने एक सुबह एक ऐसे प्रोग्राम को debug करने में गँवा दी जो असल में सही था; बस binary को source बदलने के बाद फिर से relink नहीं किया गया था। Feldman को इसके लिए 2003 का ACM Software System Award मिला। उस दोपहर से निकला टूल — dependencies को एक बार track करो, और मशीन को यह पता लगाने दो कि दोबारा क्या करने की ज़रूरत है — software tooling में सबसे ज़्यादा नक़ल किए गए विचारों में से एक बन गया। GNU Make, जो वर्शन लगभग हर कोई आज इस्तेमाल करता है, GNU Project का एक free reimplementation है। (1) (5) (6)

व्यवहार में, यह किसी भी ऐसे व्यक्ति के लिए है जो छोटे-छोटे टुकड़ों से कोई non-trivial artifact जोड़ रहा हो — एक compiled binary, कोई document, generated फ़ाइलों का एक सेट — जहाँ हर बदलाव पर सब कुछ शुरू से दोबारा बनाना बेकार या धीमा साबित हो। इसमें C/C++ प्रोजेक्ट्स, kernel और bootloader builds, LaTeX documents, और ऐसे बहुत-से ad-hoc automation शामिल हैं जिनका code compile करने से कोई लेना-देना ही नहीं है।

इसका इस्तेमाल कब करना चाहिए?

Make तब अपनी उपयोगिता साबित करता है जब एक साथ तीन बातें सच हों:

आपस में कोई निर्भरता न रखने वाली मुट्ठी-भर फ़ाइलों के लिए, अक्सर एक shell script ज़्यादा simple और ईमानदार होती है। Make तभी से फ़ायदा देना शुरू करता है जब "क्या इसे वाकई दोबारा बनाने की ज़रूरत थी?" — यह सवाल हाथ से पूछने के बजाय अपने-आप पूछे जाने लायक बन जाए।

यह toolchain में कहाँ बैठता है?

Make आमतौर पर compiler से एक लेयर ऊपर और इंसान द्वारा सीधे चलाई जाने वाली चीज़ से एक लेयर नीचे बैठता है। एक सामान्य C प्रोजेक्ट में: source files → (compiler, जिसे Make चलाता है) → object files → (linker, जिसे Make चलाता है) → अंतिम binary। Make खुद कुछ भी compile या link नहीं करता — यह हर recipe में shell commands के रूप में वे टूल्स चलाता है जो यह काम करते हैं।

यह उच्च-स्तरीय build-configuration टूल्स के नीचे बैठने की प्रवृत्ति भी रखता है। उदाहरण के लिए, CMake, Autotools, और Yocto का BitBake, Make की जगह नहीं लेते — बल्कि वे अपने असली execution backend के तौर पर Makefiles (या उसके बराबर कुछ) generate करते हैं। इसलिए वे प्रोजेक्ट्स भी जो कभी सीधे आपको कोई Makefile नहीं दिखाते, अक्सर pipeline में कहीं-न-कहीं एक चला ही रहे होते हैं।

यह इस तरह काम क्यों करता है?

मुख्य design choice — file modification times की तुलना करके यह तय करना कि क्या पुराना (stale) हो चुका है — यही वह वजह है जो Make को बड़े trees पर भी तेज़ बनाती है: एक timestamp चेक करना सस्ता है, इसलिए एक-लाइन के बदलाव के बाद किसी विशाल प्रोजेक्ट का rebuild लगभग सब कुछ छोड़ सकता है और फिर भी सही रह सकता है, बशर्ते उसे दिया गया dependency graph सही हो

यही आख़िरी शर्त लगभग हर उस "Make टूटा हुआ है" वाली शिकायत की जड़ है, जिससे लोगों का सामना होता है, और इस पर ठहरकर सोचना ज़रूरी है: Make उतना ही सही होता है, जितना सही वह graph है जो उसे बताया गया है। अगर कोई .c फ़ाइल किसी header को #include करती है, जो संबंधित .o फ़ाइल के prerequisite के रूप में सूचीबद्ध नहीं है, तो उस header को edit करने पर rebuild ट्रिगर नहीं होगा — इसलिए नहीं कि Make भ्रमित है, बल्कि इसलिए कि उसे कभी बताया ही नहीं गया कि यह dependency मौजूद है। यही वजह है कि असली दुनिया की Makefiles लगभग हमेशा हाथ से लिखी header lists के बजाय compiler द्वारा generate किए गए dependency step (GCC/Clang के साथ -MMD -MP) के साथ जोड़ी जाती हैं — compiler को असली include graph पहले से पता होता है, इसलिए वही इसका एकमात्र भरोसेमंद स्रोत है। (4)

यह असल में काम कैसे करता है?

Targets, prerequisites, recipes। मूल इकाई यह है:

target: prerequisite1 prerequisite2
	recipe line 1
	recipe line 2

Recipe की लाइनों को स्पेस से नहीं, tab से indent किया जाना चाहिए — यह एक मशहूर historical खामी है जो आज भी लोगों को उलझाती है।

Automatic variables दोहराव को कम करते हैं:

%.o: %.c
	gcc -c $< -o $@

$@ target है, $< पहला prerequisite है — यह अकेला pattern rule हर .o को उसकी मिलती-जुलती .c फ़ाइल से बना सकता है, बिना हर फ़ाइल के लिए एक अलग लाइन लिखे।

.PHONY targets उन नामों को चिह्नित करते हैं जो असली फ़ाइलों से मेल नहीं खाते — जैसे clean, all, test — ताकि अगर संयोग से उस नाम की कोई फ़ाइल मौजूद भी हो, तो Make भ्रमित न हो, और timestamp चेक करने के बजाय हमेशा recipe चलाए: (2)

.PHONY: clean
clean:
	rm -f *.o main

Variables हर जगह flags दोहराने से बचाते हैं और प्रोजेक्ट्स के बीच reuse आसान बनाते हैं:

CC = gcc
CFLAGS = -Wall -O2

main.o: main.c
	$(CC) $(CFLAGS) -c main.c

Automatic dependency generation ऊपर बताए गए gap को पाटता है:

CFLAGS = -Wall -O2 -MMD -MP
-include $(wildcard *.d)

%.o: %.c
	$(CC) $(CFLAGS) -c $<

-MMD compiler से हर object के लिए एक .d फ़ाइल generate करवाता है, जो उस compile के दौरान असल में शामिल हुए हर header को सूचीबद्ध करती है। -include उन फ़ाइलों को वापस Makefile में जोड़ देता है, ताकि headers बदलने पर भी dependency graph सही बना रहे — बिना किसी के इसे हाथ से maintain किए। (3)

अंतिम विचार

Make की ज़्यादातर quirks जैसी लगने वाली चीज़ें — timestamp comparisons, tabs पर ज़िद, और missing dependency का ज़ोर से नहीं बल्कि चुपचाप fail होने का तरीका — 1976 में लिए गए एक design decision से निकलती हैं: model को simple और check को सस्ता रखो, और भरोसा करो कि Makefile हक़ीक़त को सही-सही describe करती है। एक बार यह नज़रिया अपना लेने पर, "Make अजीब बर्ताव कर रहा है" वाले ज़्यादातर पल असल में "मैंने जो graph दिया था वह अधूरा था" निकलते हैं — जिसे ठीक करना कहीं ज़्यादा आसान है।

संबंधित लेख

संदर्भ

  1. Feldman, S. I. (1979). Make — A Program for Maintaining Computer Programs. Software: Practice and Experience, 9(4), 255–265. — Feldman का अपना paper, जो Make का परिचय देता है, मूल motivation समेत।
  2. GNU Make Manual — GNU Make के syntax और semantics के लिए पूरा, authoritative reference।
  3. GNU Make Manual: Automatic Prerequisites — maintainers के अपने शब्दों में -MMD/-include pattern।
  4. GCC: Preprocessor Options (-M family)-MMD और -MP अंदर से क्या करते हैं।
  5. Make (software) — Wikipedia — Make के विभिन्न implementations (BSD Make, GNU Make, NMAKE, आदि) का सामान्य इतिहास और वंशावली।
  6. Stuart Feldman — Wikipedia — Bell Labs में Feldman के काम और 2003 के ACM Software System Award की पृष्ठभूमि।

टिप्पणियाँ