(二):隱含規則自動推導

上一節的Makefile勉強可用,但還寫的比較繁瑣,不夠簡潔。對每一個.c源文件,都需要寫一個生成其對應的.o目標文件的規則,如果有幾百個或上千個源文件,都手動來寫,還不是很麻煩,這也不夠自動化啊。 這樣,我們把生成.o目標文件的規則全部刪除掉,就是這樣一個Makefile文件:


target_bin : main.o debug.o ipc.o timer.o tools.o  
>---gcc -o target_bin main.o debug.o ipc.o timer.o tools.o  

clean:  
>---rm *.o target_bin

這下簡潔了不少,這樣也能用嗎?試試看吧先,make一下:

# make  
cc    -c -o main.o main.c  
cc    -c -o debug.o debug.c  
cc    -c -o ipc.o ipc.c  
cc    -c -o timer.o timer.c  
cc    -c -o tools.o tools.c  
gcc -o target_bin main.o debug.o ipc.o timer.o tools.o

原來醬紫都可以啊!!target_bin後面那一群依賴文件怎麼生成呢?不是沒有生成*.o目標文件的規則了嗎?再看屏幕編譯輸出內容:

cc    -c -o main.o main.c  
cc    -c -o debug.o debug.c  
cc    -c -o ipc.o ipc.c  
cc    -c -o timer.o timer.c  
cc    -c -o tools.o tools.c

怎麼長的和之前不太一樣呢,尤其是前面那個cc是何物?

其實make可以自動推導文件以及文件依賴關係後面的命令,於是我們就沒必要去在每一個*.o文件後都寫上類似的命令,因為,我們的 make 會自動推導依賴文件,並根據隱含規則自己推導命令。所以上面.o文件是由於make自動推導出的依賴文件以及命令來生成的。

下面來看看make是如何推導的。

命令make –p可以打印出很多默認變量和隱含規則。Makefile變量可以理解為C語言的宏,直接展開即可(後面會講到)。取出我們關心的部分:

# default  
OUTPUT_OPTION = -o $@  
# default  
CC = cc  
# default  
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) –c  
# Implicit Rules  
%.o: %.c  
#  commands to execute (built-in):  
>---$(COMPILE.c) $(OUTPUT_OPTION) $<

其中cc是一個符號鏈接,指向gcc,這就可以解釋為什麼我們看到的編譯輸出為cc,其實還是使用gcc在編譯。

# ll /usr/bin/cc    
lrwxrwxrwx. 1 root root 3 Dec  3  2013 /usr/bin/cc -> gcc

變量$(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH)都為空。所以%.o: %.c規則命令展開為

cc    -c -o $@ $<

再看屏幕輸出編譯內容,摘取一條:

cc    -c -o main.o main.c

是不是看出點什麼?$@和main.o對應,$<和main.c對應。其實$@和$<是兩個變量。$@為規則中的目標,$<為規則中的第一個依賴文件。%.o:%.c是一種稱為模式規則的特殊規則。因為main.o符合該模模式,再推導出依賴文件main.c,最終推導出整個規則為:

main.o : main.c:  
>--- cc    -c -o main.o main.c

其餘幾個目標也同樣推導。make自動推導的功能為我們減少了不少的Makefile代碼,尤其是對源文件比較多的大型工程,我們的Makefile可以不用寫得那麼繁瑣了。 最後,今天的Makefile相對於上一節進化成這個樣子了:

target_bin : main.o debug.o ipc.o timer.o tools.o  
>---gcc -o target_bin main.o debug.o ipc.o timer.o tools.o  

clean:  
>---rm *.o target_bin