Makefile自動生成頭文件依賴


前言

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

Makefile模板

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

CC = 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的機制。

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

即是說,我們需要提供生成規則文件的規則。例如,我們可以這樣動態生成頭文件依賴關系:

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

-include seq.d

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

seq.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選項,否則它會把系統依賴的頭文件也包含進來。例如執行下面一個命令:

    gcc -MM seq.c 

將輸出:

    seq.o : seq.c seq.h

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

seq.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)。

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

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

seq.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的模式匹配應用上,就完成自動生成頭文件依賴功能了:

%.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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM