Makefile自動生成頭文件依賴

前言

Makefile自動生成頭文件依賴是很經常使用的功能,本文的目的是想盡可能詳細說明其中的原理和過程。html

Makefile模板

首先給出一個本人在小項目中經常使用的Makefile模板,支持自動生成頭文件依賴。c++

makefileCC      = gcc 
CFLAGS  = -Wall -O
INCLUDEFLAGS = 
LDFLAGS = 
OBJS    = seq.o
TARGETS = test_seq 

.PHONY:all 
all : $(TARGETS)

test_seq:test_seq.o $(OBJS)
    $(CC) -o $@ $^ $(LDFLAGS)

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

%.d:%.c
    @set -e; rm -f $@; $(CC) -MM $< $(INCLUDEFLAGS) > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

-include $(OBJS:.o=.d)

.PHONY:clean 
clean:
    rm -f $(TARGETS) *.o *.d *.d.*

基礎知識

在進行下一步以前,首先須要瞭解make的執行步驟:正則表達式

  1. 讀入Makefile
  2. 讀入被include的其它Makefile
  3. 初始化Makefile中的變量
  4. 推導隱晦規則,並分析全部規則
  5. 爲全部目標建立依賴關係鏈
  6. 根據依賴關係,決定哪些目標須要從新生成
  7. 執行生成命令

如何動態生成依賴關係?

從上面make的執行過程當中可看出,要動態生成依賴關係,只能利用第2步讀入其它Makefile的機制。那麼,咱們是否能夠先把生成的依賴關係保存到文件,而後再把該文件的內容包含進來?
答案是Yes! 只要利用include的機制。shell

include關鍵字是用於讀入其它Makefile文件。當該文件不存在時,make會尋找是否有生成它的規則,若是有,則執行其生成命令,而後再嘗試讀入。在include前加減號"-"能夠上make忽略其產生的錯誤,並不輸出任何錯誤信息。編程

便是說,咱們須要提供生成規則文件的規則。例如,咱們能夠這樣動態生成頭文件依賴關係:code

makefileseq.d : seq.c
    @echo 「seq.o seq.d : seq.c seq.h" > $@

-include seq.d

當make執行時,Makefile中的內容將是這樣子(指內存上的數據):htm

makefileseq.d : seq.c
    @echo 「seq.o seq.d : seq.c seq.h" > $@

seq.o seq.d : seq.c seq.h

特別注意的是,因爲對seq.c和seq.h的修改須要更新seq.d的內容(由於依賴關係可能已變化),所以seq.d也要在依賴關係的目標列表中。進程

自動生成頭文件依賴

基於上面的例子,如今能夠開始討論如何自動生成頭文件依賴。內存

自動生成依賴關係

大多數c/c++編譯器提供了-M選項,可自動尋找源文件依賴的頭文件,並生成依賴規則。對於gcc,須要使用-MM選項,不然它會把系統依賴的頭文件也包含進來。例如執行下面一個命令:文檔

shellgcc -MM seq.c

將輸出:

makefileseq.o : seq.c seq.h

但咱們須要結果是seq.d也要包含在目標列表中,因此還須要對它進行文本處理。所以,上面的例子可改成:

makefileseq.d : seq.c
    @set -e; \
    gcc -MM $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

-include seq.d

生成規則中的執行命令解釋

第一個命令@set -e。@關鍵字告訴make不輸出該行命令;set -e的做用是,當後面的命令的返回值非0時,當即退出。

那麼爲何要把幾個命令寫在」同一行「(是對於make來講,由於\的做用就是鏈接行),並用分號隔開每一個命令?由於在Makefile這樣作才能使上一個命令做用於下一個命令。這裏是想要set -e做用於後面的命令。

第二個命令gcc -MM $< > $@.$$$$, 做用是根據源文件生成依賴關係,並保存到臨時文件中。內建變量$<的值爲第一個依賴文件(那seq.c),$$$$爲字符串"$$",因爲makefile中全部的$字符都是特殊字符(即便在單引號之中!),要獲得普通字符$,須要用$$來轉義; 而$$是shell的特殊變量,它的值爲當前進程號;使用進程號爲後綴的名稱建立臨時文件,是shell編程經常使用作法,這樣可保證文件惟一性。

第三個命令做用是將目標文件加入依賴關係的目錄列表中,並保存到目標文件。關於正則表達式部分就不說了,惟一要注意的是內建變量$*$*的值爲第一個依賴文件去掉後綴的名稱(這裏便是seq)。

第四個命令是將該臨時文件刪除。

若是把內建變量都替換成其值後,實際內容是這樣子:

makefileseq.d : seq.c
    @set -e; \
    gcc -MM seq.c > seq.d.$$$$; \
    sed 's,\(seq\)\.o[ :]*,\1.o seq.d : ,g' < seq.d.$$$$ > seq.d; \
    rm -f seq.d.$$$$

-include seq.d

Makefile的模式匹配

最後,再把Makefile的模式匹配應用上,就完成自動生成頭文件依賴功能了:

makefile%.d : %.c
    @set -e; \
    gcc -MM $@ > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$

-include seq.d

參考資料

<跟我一塊兒寫Makefile> by 陳晧
GNU make官方文檔 http://www.gnu.org/software/make/manual/make.html

相關文章
相關標籤/搜索