手工編寫依賴關系不僅工作量大而且極易出現遺漏,更新也很難及時,修改源或頭文件后makefile可能忘記修改。為了解決這個問題,可以用gcc的-M選項自動生成目標文件和源文件的依賴關系。-M選項會把包含的系統頭文件以及其所包含的其他系統頭文件也找出來了,如果我們不需要輸出系統頭文件的依賴關系時,可以用-MM選項。
下面我們以一個簡單的例子來說明如何自動生成依賴關系:
exm/
main.c
s.c
s.h
makefile文件內容如下:
all:a
src=$(wildcard *.c)
obj:=$(patsubst %.c,%.o,$(src))
ifneq($(MAKECMDGOALS),clean)
-include$(src:.c=.d)
endif
a:$(obj)
gcc$(obj)-o $@
%.d:%.c
set-e;rm -f $@; \
gcc-MM$(CPPFLAGS) $< > $@.
; \
sed's,$∗\.o[:]*,\1.o $@ : ,g' < $@.
> $@; \
rm-f$@.
%.o:%.c
@echo'Buildingfile: $<'
@echo'Invoking:GCC C Compiler'
gcc-O0-g3 -Wall -c -o "$@" "$<"
@echo'Finishedbuilding: $<'
@echo''
其中wildcard作用就是將指定目錄下.c文件全部找出,所以這里src=main.cs.c
patsubst作用是把$(src)中的.c全部換為.o,於是obj=main.os.o
include$(src:.c=.d)相當於includemain.ds.d
由於此時這兩個文件並不存在,所以會出現下面提示:
makefile:6:main.d:沒有那個文件或目錄
makefile:6:s.d:沒有那個文件或目錄
如果不想要這個提示,可以將include替換為-include
盡管一開始找不到.d文件,所以make會報警告。但是make會把include的文件名也當作目標來嘗試更新,而這些目標適用模式規則%.d:%c
注意,雖然在Makefile中這個命令寫了四行,但其實是一條命令,make只創建一個Shell進程執行這條命令,這條命令分為5個子命令,用;號隔開,並且為了美觀,用續行符\拆成四行來寫。執行步驟為:
1)set-e命令設置當前Shell進程為這樣的狀態:如果它執行的任何一條命令的退出狀態非零則立刻終止,不再執行后續命令。@表示makefile執行這條命令時不顯示出來
2)把原來的.d文件刪掉。
3)$<依賴的目標集(即*.c), -MM:表示生成文件依賴關系,$@:表示生成的目標文件(即*.d),$$:表示本身的ProcessID。注意,在Makefile中$有特殊含義,如果要表示它的字面意思則需要寫兩個$,所以Makefile中的四個$傳給Shell變成兩個$,兩個$在Shell中表示當前進程的id,一般用它給臨時文件起名,以保證文件名唯一。
4)這個sed命令比較復雜,就不細講了,主要作用是查找替換,並加入.d的依賴關系。
5)最后把臨時文件刪掉。
不管是Makefile本身還是被它包含的文件,只要有一個文件在make過程中被更新了,make就會重新讀取整個Makefile以及被它包含的所有文件,現在main.d、stack.d和maze.d都生成了,就可以正常包含進來了,相當於在Makefile中添了下面規則:
main.omain.d : main.c s.h
s.os.d : s.c s.h
當源或頭文件修改時,如果依賴關系發生變化,執行makefile時將更新具有依賴關系的.d文件,而.d文件的更新又促使make重新讀取makefile文件,把新的.d文件包括進來,於是新的依賴關系被建立。
除了上面方法外,還可使用GCC的-MMD-MP -MF -MT選項,如下,可起到同樣目的:
all:a
src=$(wildcard *.c)
obj:=$(patsubst %.c,%.o,$(src))
ifneq($(MAKECMDGOALS),clean)
-include$(src:.c=.d)
endif
a:$(obj)
gcc$(obj)-o $@
%.o:%.c
@echo'Buildingfile: $<'
@echo'Invoking:GCC C Compiler'
gcc-O0-g3 -Wall -c -fmessage-length=0 -MMD -MP-MF"$(@:%.o=%.d)"-MT"$(@:%.o=%.d)" -o "$@""$<"
@echo'Finishedbuilding: $<'
@echo''
