Make|自動生成依賴關係

Make一般是在Unix環境下使用的自動化編譯工具。他本身不是編譯器,而是將衆多C/C++源文件組織起來,確定其編譯方式和編譯順序的工具。一旦我們寫好的Makefile配置文件,那麼無論多麼複雜的工程我們都可以用一條make命令來解決。事實上,儘管通常和C/C++搭配起來使用,make也能應用到其他的編程語言之中。

在使用make過程中的第一個核心問題是處理文件依賴的問題。例如:

foo.o : foo.c defs.h       # foo模塊
    cc -c -g foo.c

這裏foo.o依賴於foo.cdefs.h。當後面兩個文件發生變化時,make會自動運行cc -c -g foo.c命令更新foo.o文件。但是,隨着項目擴大。這種文件之間的依賴關係會變得非常複雜,一個小的改動可能會涉及到衆多依賴關係的修改。因此有必要在項目的開始就引入自動構建依賴關係的工具鏈。

跟我一起寫Makefile:書寫規則這篇教程中,提到了編譯器的一個特性:大多數的C/C++編譯器都支持一個"-M"的選項,即自動尋找源文件中包含的頭文件,並生成一個依賴關係。例如如果我們執行

cc -M main.c

其輸出是:

main.o: main.c defs.h

注意如果你用的是GNU的C/C++編譯器,你得用"-MM"參數,不然,"-M"參數會把一些標準庫頭文件也引入進來。

這篇教程裏面詳細闡述瞭如果在Makefile中使用這一特性的方法,綜合而來就是:

# 對於每個.c源文件,建立一個描述其依賴關係的.d依賴文件
%.d: %.c
    @set -e; rm -f $@; \
         $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
         sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
         rm -f $@.$$$$

上述命令中sed命令的作用是在依賴關係對中,在左側加上.d文件本身。即

main.o: main.c defs.h

轉換成

main.o main.d : main.c defs.h

然後將生成的依賴關係文件include進來

sources = foo.c bar.c
include $(sources:.c=.d)

在教程中還提到,這個include要放在默認目標之後,避免include載入的文件的目標替換了默認目標。

走完上面的流程,會得到一個類似的如下內容的文件:

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

sources = main.c foo.c bar.c
objs = $(sources:.c=.o)
include $(sources:.c=.d)

main: $(objs)
    $(CC) -o main $(objs)

.PHONY : clean
clean:
    @rm -f *.d *.o
    @rm -f ./main

不過按照這個Makefile第一次執行的時候會產生一個問題:第一次執行時,.d文件尚未生成,這裏的include導入的文件不存在,會產生如下的錯誤信息

Makefile:8: main.d: No such file or director
make: *** No rule to make target 'main.d'.  Stop.

最後是通過面向google的debug找到了Autodependencies with GNU make這篇2001年的文章,細緻地闡述了這個問題。解決的關鍵在於在include前面添加一個dash(-),其作用是:如果include的對象不存在,make繼續執行,後續make會自動生成.d文件,然後執行include。這篇新的教程提供的完整Makefile示例如下(和前面的形式有不同,但是思路是一致的):

OBJS := foo.o bar.o

# link
proggie: $(OBJS)
    gcc $(OBJS) -o proggie

# pull in dependency info for *existing* .o files
-include $(OBJS:.o=.d)

# compile and generate dependency info
%.o: %.c
    gcc -c $(CFLAGS) $*.c -o $*.o
    gcc -MM $(CFLAGS) $*.c > $*.d

# remove compilation products
clean:
    rm -f proggie *.o *.d
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章