手工編寫依賴關係不只工做量大並且極易出現遺漏,更新也很難及時,修改源或頭文件後makefile可能忘記修改。爲了解決這個問題,能夠用gcc的-M選項自動生成目標文件和源文件的依賴關係。-M選項會把包含的系統頭文件以及其所包含的其餘系統頭文件也找出來了,若是咱們不須要輸出系統頭文件的依賴關係時,能夠用-MM選項。ui
下面咱們以一個簡單的例子來講明如何自動生成依賴關係:atom
exm/spa
main.ccode
s.c進程
s.hclass
makefile文件內容以下:gcc
all:ased
src=$(wildcard *.c)file
obj:=$(patsubst %.c,%.o,$(src))gc
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''