makefile自動生成依賴關係
手工編寫依賴關係不僅工作量大而且極易出現遺漏,更新也很難及時,修改源或標頭檔案後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''