為什么需要自動生成頭文件依賴?
編譯單個源文件時,需要獲取文件中包含的頭文件的信息,但是一般的Makefile不會在規則中明確寫明文件依賴的頭文件,所以單獨修改頭文件后,不會導致包含頭文件的源文件重新編譯。如果每次手動的添加頭文件依賴,又會非常的繁瑣,所以需要一種自動生成依賴的方法。
編譯器中神奇的選項
- 使用$(CC)中的-M命令就可以完美的解決問題,因為-M選項可以將源文件依賴的所有頭文件,自動解析出來。
- 例子:在當前路徑下,編輯test.c和test.h文件,test.c如下所示,test.h為空
#include <stdio.h> #include "test.h" int main() { return 0; }
- 運行命令:gcc -M test.c,輸出如下:
test.o: test.c /usr/include/stdc-predef.h /usr/include/stdio.h \ /usr/include/features.h /usr/include/sys/cdefs.h \ /usr/include/bits/wordsize.h /usr/include/gnu/stubs.h \ /usr/include/gnu/stubs-64.h \ /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h \ /usr/include/bits/types.h /usr/include/bits/typesizes.h \ /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stdarg.h \ /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h test.h
- 運行命令:gcc -MM test.c,輸出如下:
test.o: test.c test.h
- 結論:gcc -MM 命令可以自動生成源文件對頭文件的依賴關系,且剔除掉庫里面的頭文件
解決方法
%.d: %.c @set -e; \ rm -f $@; \ $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$
- set -e : 后續命令只要執行失敗,直接結束全部流程,返回
- rm -f $@ : 刪除上一次編譯的殘留文件
- $(CC) -MM $(CPPFLAGS) $< > $@.$$$$ : 將生成的依賴關系輸出到指定的目錄中,其中$$$$表示一個隨機數,防止文件名重復
- sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@ : ;利用sed命令,將xxx.d添加到規則中的目標里面,形成多目標,如:將 main.o : main.o mian.h 變成main.o main.d : main.o mian.h。目的:頭文件更新后,d文件也需要同步更新
- rm -f $@.$$$$ : 刪除中間文件
提示:上述方法中隱含了,目標相同的多條規則會自動進行合並的機制,所以規則的目標一定要相同,才能使得原來的編譯規則(%.c:%.o)中添加對頭文件的依賴。