GCC+Make 自動生成 Makefile 依賴


本文內容基於 GNU MAKE。

BASIS

一些 makefile 的基礎知識。

wildcard

假設當前目錄下有文件 a.cpp 和 b.cpp,定義:

eg1=*.cpp
eg2=$(wildcard *.cpp)

rm $(eg1) 的展開為 rm *.cpprm $(eg2) 的展開為 rm a.cpp b.cpp

.PHONY

.PHONY 用於表示其后的目標文件是一個偽目標文件。

在 makefile 的一般格式 targets : prerequisitions 中,targets 為目標文件,一般是實際存在的文件,如 a.o 等;但有些規則並不生成實際存在的文件或不生成文件 targets 中指定的文件,這樣的目標文件稱為偽目標文件。

典型例子如常用於清理中間文件的偽目標文件 clean 。通常其生成命令並不生成一個名為“clean”的文件。此時若不將其指定為偽目標文件,且項目當前目錄下恰好存在一個目錄或文件名為“clean”,則 make 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 在第一次 make 時由於不存在依賴會導致編譯失敗,第二次即可恢復正常,因為此時依賴文件 dependencies.d 就已經建立了。

其他

以下 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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM