上一節的Makefile寫得中規中矩,比較繁瑣,是為了講清楚基本概念,其實Makefile有很多靈活的寫法,可以寫得更簡潔,同時減少出錯的可能。本節我們來看看這樣一個例子還有哪些改進的餘地。
一個目標依賴的所有條件不一定非得寫在一條規則中,也可以拆開寫,例如:
main.o: main.h stack.h maze.h main.o: main.c gcc -c main.c
就相當於:
main.o: main.c main.h stack.h maze.h gcc -c main.c
如果一個目標拆開寫多條規則,其中只有一條規則允許有命令列表,其它規則應該沒有命令列表,否則make
會報警告並且採用最後一條規則的命令列表。
這樣我們的例子可以改寫成:
main: main.o stack.o maze.o gcc main.o stack.o maze.o -o main main.o: main.h stack.h maze.h stack.o: stack.h main.h maze.o: maze.h main.h main.o: main.c gcc -c main.c stack.o: stack.c gcc -c stack.c maze.o: maze.c gcc -c maze.c clean: -rm main *.o .PHONY: clean
這不是比原來更繁瑣了嗎?現在可以把提出來的三條規則刪去,寫成:
main: main.o stack.o maze.o gcc main.o stack.o maze.o -o main main.o: main.h stack.h maze.h stack.o: stack.h main.h maze.o: maze.h main.h clean: -rm main *.o .PHONY: clean
這就比原來簡單多了。可是現在main.o
、stack.o
和maze.o
這三個目標連編譯命令都沒有了,怎麼編譯的呢?試試看:
$ make cc -c -o main.o main.c cc -c -o stack.o stack.c cc -c -o maze.o maze.c gcc main.o stack.o maze.o -o main
現在解釋一下前三條編譯命令是怎麼來。如果一個目標在Makefile中的所有規則都沒有命令列表,make
會嘗試在內建的隱含規則(Implicit Rule)資料庫中查找適用的規則。make
的隱含規則資料庫可以用make -p
命令打印,打印出來的格式也是Makefile的格式,包括很多變數和規則,其中和我們這個例子有關的隱含規則有:
# default OUTPUT_OPTION = -o $@ # default CC = cc # default COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c %.o: %.c # commands to execute (built-in): $(COMPILE.c) $(OUTPUT_OPTION) $<
#
號在Makefile中表示單行註釋,就像C語言的//
註釋一樣。CC
是一個Makefile變數,用CC = cc
定義和賦值,用$(CC)
取它的值,其值應該是cc
。Makefile變數像C的宏定義一樣,代表一串字元,在取值的地方展開。cc
是一個符號連結,通常指向gcc
,在有些UNIX系統上可能指向另外一種C編譯器。
$ which cc /usr/bin/cc $ ls -l /usr/bin/cc lrwxrwxrwx 1 root root 20 2008-07-04 05:59 /usr/bin/cc -> /etc/alternatives/cc $ ls -l /etc/alternatives/cc lrwxrwxrwx 1 root root 12 2008-11-01 09:10 /etc/alternatives/cc -> /usr/bin/gcc
CFLAGS
這個變數沒有定義,$(CFLAGS)
展開是空,CPPFLAGS
和TARGET_ARCH
也是如此。這樣$(COMPILE.c)
展開應該是cc␣空␣空␣空␣-c
,去掉所有的“空”得到cc␣␣␣␣-c
,注意中間留下4個空格,所以%.o: %.c
規則的命令$(COMPILE.c)␣$(OUTPUT_OPTION)␣$<
展開之後是cc␣␣␣␣-c␣-o␣$@␣$<
,和上面的編譯命令已經很接近了。
$@
和$<
是兩個特殊的變數,$@
的取值為規則中的目標,$<
的取值為規則中的第一個條件。%.o: %.c
是一種特殊的規則,稱為模式規則(Pattern Rule)。現在回顧一下整個過程,在我們的Makefile中以main.o
為目標的規則都沒有命令列表,所以make
會查找隱含規則,發現隱含規則中有這樣一條模式規則適用,main.o
符合%.o
的模式,現在%
就代表main
(稱為main.o
這個名字的Stem),再替換到%.c
中就是main.c
。所以這條模式規則相當於:
main.o: main.c cc -c -o main.o main.c
隨後,在處理stack.o
目標時又用到這條模式規則,這時又相當於:
stack.o: stack.c cc -c -o stack.o stack.c
maze.o
也同樣處理。這三條規則可以由make
的隱含規則推導出來,所以不必寫在Makefile中。
先前我們寫Makefile都是以目標為中心,一個目標依賴于若干條件,現在換個角度,以條件為中心,Makefile還可以這麼寫:
main: main.o stack.o maze.o gcc main.o stack.o maze.o -o main main.o stack.o maze.o: main.h main.o maze.o: maze.h main.o stack.o: stack.h clean: -rm main *.o .PHONY: clean
我們知道,寫規則的目的是讓make
建立依賴關係圖,不管怎麼寫,只要把所有的依賴關係都描述清楚了就行。對於多目標的規則,make
會拆成幾條單目標的規則來處理,例如
target1 target2: prerequisite1 prerequisite2 command $< -o $@
這樣一條規則相當於:
target1: prerequisite1 prerequisite2 command prerequisite1 -o target1 target2: prerequisite1 prerequisite2 command prerequisite1 -o target2
注意兩條規則的命令列表是一樣的,但$@
的取值不同。