GCC+Make 自動生成 Makefile 依賴
目錄
- BASIS
- wildcard
- .PHONY
- 靜態模式
- 常用自動變數
- 自動生成依賴(GCC)
- -M 引數
- 編寫 Makefile
- Makefile 細節說明
- 其他
本文內容基於 GNU MAKE。
BASIS
一些 makefile 的基礎知識。
wildcard
假設當前目錄下有檔案 a.cpp 和 b.cpp,定義:
eg1=*.cpp eg2=$(wildcard *.cpp)
則 rm $(eg1)
的展開為 rm *.cpp
,rm $(eg2)
的展開為 rm a.cpp b.cpp
.PHONY
.PHONY
用於表示其後的目標檔案是一個偽目標檔案。
在 makefile 的一般格式 targets : prerequisitions
中,targets
為目標檔案,一般是實際存在的檔案,如 a.o
等;但有些規則並不生成實際存在的檔案或不生成檔案 targets
中指定的檔案,這樣的目標檔案稱為偽目標檔案。
典型例子如常用於清理中間檔案的偽目標檔案 clean
。通常其生成命令並不生成一個名為“clean”的檔案。此時若不將其指定為偽目標檔案,且專案當前目錄下恰好存在一個目錄或檔名為“clean”,則 make clean
靜態模式
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
...
直接用例子說明
objects = foo.o bar.o
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
等價於
foo.o : foo.c $(CC) -c $(CFLAGS) foo.c -o foo.o bar.o : bar.c $(CC) -c $(CFLAGS) bar.c -o bar.o
常用自動變數
本節摘自:陳皓《跟我一起寫 Makefile》
$@
表示規則中的目標檔案集。在模式規則中,如果有多個目標,那麼,"$@"就是匹配於目標中模式定義的集合。$<
依賴目標中的第一個目標名字。如果依賴目標是以模式(即"%")定義的,那麼"$<"將是符合模式的一系列的檔案集。注意,其是一個一個取出來的。$^
所有的依賴目標的集合。以空格分隔。如果在依賴目標中有多個重複的,那個這個變數會去除重複的依賴目標,只保留一份。$+
這個變數很像"$^",也是所有依賴目標的集合。只是它不去除重複的依賴目標$*
目標模式中萬用字元%
之前的部分,例如目標是dir/a.foo.b
,模式為a.%.b
,則$*
的值就是dir/a.foo
此外,自動變數後可加 D 或 F 以實現取目錄部分或檔案部分。例如,當 $@
為 dir/foo.o
時,$(@D)
為 dir
,$(@F)
為 foo.o
。對用當前目錄,取目錄時值為 .
。
自動生成依賴(GCC)
-M 引數
GCC 的 -M
和 -MM
引數可以生成指定原始檔的依賴項。如,在 a.cpp 中 include a.h 和 b.h,則執行以下命令
g++ -MM a.cpp
會得到輸出如下
a.o : a.h b.h
-MM
生成的依賴項不包含標準庫等依賴,而 -M
包含。
編寫 Makefile
GNU 建議對每個原始檔生成一個 .d
字尾的依賴說明檔案,內容即上節編譯器生成的內容。
直接 include 上一節中生成的依賴就可以通過 make 的自動推導進行編譯了,但如果需要指定編譯引數、在 make 過程中加入回顯,可以考慮在 .d
直接加入命令。
對每個原始檔建立 .d
依賴檔案會使得專案檔案翻倍,因此此處將所有的 .d
檔案合併為一個檔案。這樣做帶來的缺點是每次 make 依賴項都需要全部重新生成。
下面直接給出 makefile。
sources=a.cpp b.cpp c.cpp
# 替換字尾
dependencies=$(sources:.cpp=.d)
$(dependencies) : %.d : %.cpp
@set -e; \
g++ -MM $(flags) $< > $@.$$$$; \
sed 's,$(*F).o,$*.o,g' < $@.$$$$ > $@; \
echo ' @g++ -c $< -o $(@:.d=.o) $(flags)' >> $@; \
cat $@ >> dependencies.d; \
rm -f $@.$$$$; rm -f $@
cleand:
rm -f dependencies.d; \
dependencies.d : cleand $(dependencies)
include dependencies.d
Makefile 細節說明
*.d
檔案是生成的臨時依賴項,最終會被刪除。$$$$
展開為隨機的數字,用於建立臨時檔案。
$(depencencies)
的生成命令中,第三行用於在單個檔案的依賴項中補全路徑。例如,執行 g++ -MM src/a.cpp
後,GCC 給出的依賴項會是 a.o : xxx
,但專案中需要 src/a.o : xxx
的形式以避免命名衝突。sed
是 Linux 指令,此處用於進行文字替換。
生成命令的第四行向依賴檔案中寫入編譯命令,此處也可以使用 echo 報告進度。第五行將該檔案的依賴寫入總的依賴檔案。
最後 include 總的依賴檔案。第一次執行 make 時依賴檔案不存在,因此定義生成生成依賴檔案的規則。生成前需要先刪除舊的依賴檔案,因為生成時使用的是追加方式。
其他
以下 Makefile 是對每個原始檔生成 .d
依賴檔案的版本。
$(dependencies) : %.d : %.cpp
@set -e; rm -f $@; \
g++ -MM $(flags) $< >> $@; \
sed 's,\($*\)\.o[ :]*,\1.o : $@ ,g' < $@ > $@.$$$$; \
sed 's,$(*F).o,$*.o,g' < $@.$$$$ > $@; \
echo ' @g++ -c $< -o $(@:.d=.o) $(flags)' >> $@; \
rm -f $@.$$$$
include $(dependencies)