在閱讀大型工程C源碼時不可避免的需要了解makefile文件,它定義了源文件的編譯規則和鏈接規則,是閱讀與編寫源碼都必須了解的知識,本文通過學習陳皓寫的一份makefile中文教程,將其要點梳理如下,以備自己回顧之用。原始教程請參考陳皓博客或直接下載網友做好的PDF教程,直接百度即可。
一、概念
一個工程中的源文件不計數,其按類型、功能、模塊分別放在若干個目錄中,makefile 定義了一系列的規則來指定,哪些文件需要先編譯,哪些文件需要後編譯,哪些文件需要重新編譯,甚至於進行更復雜的功能操作。
make 是一個命令工具,是一個解釋 makefile 中指令的命令工具。
二、語法
2.1格式與組成
格式如下:
target ... : prerequisites ... command
說明:這是一個文件的依賴關系,也就是說,target 這一個或多個的目標文件依賴於 prerequisites 中的文件,其生成規則定義在 command 中。說白一點就是說, prerequisites 中如果有一個以上的文件比 target 文件要新的話,command 所定義的命令就會被執行。這就是 Makefile 的規則。命令必須以tab開頭。
組成:
Makefile 裡主要包含了五個東西:顯式規則、隱晦規則、變量定義、文件指示和注釋。顯式規則就是我們定義出來的,隱晦規則即自動推導規則(make 看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關系中),支持變量定義類似於宏替換,注釋提高可讀性,文件指示包括:引入其他makefile文件,指定makefile的有效部分,定義一個多行命令。
2.2規則
2.2.1規則的語法
targets : prerequisites command ... 或是這樣: targets : prerequisites ; command command
2.2.2 文件搜索
(1)當 make 需要去找尋文件的依賴關系時,你可以在文件前加上路徑,但最好的方法是把一個路徑告訴 make,讓make 自動去找。特殊變量“VPATH”就是完成這個功能的(如:VPATH = src:../headers,目錄用冒號分隔),make在當前目錄找不到就到變量VPATH路徑下找。
(2)另一個設置文件搜索路徑的方法是使用 make 的“vpath”關鍵字 (注意,它是全小寫的),格式為:vpath <pattern> <directories>,表示在directories下搜索符合pattern模式的文件。其中pattern需要使用%符號,用於表示匹配0或若干個字符。
2.2.3 多目標與靜態模式
Makefile 的規則中的目標可以不止一個,其支持多目標。當然,多個目標的生成規則的執行命令是同一個,不過好在我們的可以使用一個自動化變量“$@”,它表示目前規則中所有目標的集合。示例參見第三節例1.
靜態模式可以更加容易地定義多目標的規則,語法如下
<targets ...>: <target-pattern>: <prereq-patterns ...> <commands> ...
target-parrtern 指明了 targets 的模式,也就是的目標集模式。prereq-parrterns 是目標的依賴模式,它對 target-parrtern 形成的模式再進行一次依賴目標的定義。參見第三節例2。
2.2.4 自動生成依賴性
在添加刪除頭文件時,需要修改makefile文件內容,這是繁瑣且易錯的,所以大多數的C/C++編譯器都支持一個“-M”的選項,即自動找尋源文件中包含的頭文件,並生成一個依賴關系。而為了將編譯器自動生成的依賴關系文件與makefile關聯起來,GNU 組織建議把編譯器為每一個源文件自動生成的依賴關系放到一個文件中,如為“name.c”生成一個“name.d”的 Makefile 文件,[.d]文件中存放著對應[.c]文件的依賴關系,之後將.d文件包含在我們的主 Makefile 中,從而自動化地生成每個文件的依賴性。
產生[.d]文件的模式規則見第三節例3。
2.2.5命令的關聯
如果希望第二條命令在第一條命令的基礎上執行,那麼這兩條命令應該寫在同一行,用分號分隔,而不能寫在兩行。
2.2.6 隱含規則
“隱含規則”是一種慣例,如果我們不明確地寫下規則,那麼,make 就會在這些內建的規則中尋找所需要的規則和命令。“隱含規則”會使用一些我們系統變量,我們可以改變這些系統變量的值來定制隱含規則的運行時的參數;我們還可以通過“模式規則”的方式寫下自己的隱含規則。如果你確實不希望任何隱含規則推導,那麼,你就不要只寫出“依賴規則”,而不寫命令。
(1)隱含規則中所用到的變量
在隱含規則中的命令,基本上都是使用了一些預先設置的變量。1、你可以在你的 makefile 中改變這些變量的值,2、或是在 make 的命令行中傳入這些值,3、或是在你的環境變量中設置這些值,當然,4、你也可以利用 make 的“-R”或“--no–builtin-variables”參數來取消你所定義的變量對隱含規則的作用。
Make為不同語言建立了不同的編譯鏈接隱含規則,而這些規則都有自身所用到的命令變量和命令參數變量,通過改變這些變量的值可以改變隱含規則的執行方式。
下面列出常見隱含規則:
語言
推導規則
生成的命令
編譯C
“<n>.o”目標的依賴目標會自動推導為“<n>.c”
$(CC) –c $(CPPFLAGS) $(CFLAGS)
編譯 C++
“<n>.o”的目標的依賴目標會自動推導為“<n>.cc”或是“<n>.C”
$(CXX) –c (CPPFLAGS) $(CFLAGS)
編譯 Pascal
“<n>.o”的目標的依賴目標會自動推導為“<n>.p”
$(PC) –c $(PFLAGS)
編譯 Fortran /Ratfor
“<n>.o”的目標的依賴目標會自動推導為“<n>.r”或“<n>.F”或“<n>.f”
“.f” “$(FC) –c $(FFLAGS)”
“.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)”
“.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”
預處理 Fortran/Ratfor
“<n>.f”的目標的依賴目標會自動推導為“<n>.r”或“<n>.F”
“.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)”
“.r” “$(FC) –F $(FFLAGS) $(RFLAGS)”
編譯 Modula-2
“<n>.sym”的目標的依賴目標會自動推導為“<n>.def”
“<n.o>” 的目標的依賴目標會自動推導為 “<n>.mod”
$(M2C) $(M2FLAGS) $(DEFFLAGS)
$(M2C) $(M2FLAGS) $(MODFLAGS)
匯編和匯編預處理
“<n>.o” 的目標的依賴目標會自動推導為“<n>.s”
默認使用 C 預編譯器“cpp”
$(AS) $(ASFLAGS)
$(AS) $(ASFLAGS)
鏈接 Object 文件
“<n>”目標依賴於“<n>.o”,通過運行 C 的編譯器來運行鏈接程序生成( 一 般 是 “ld” )
$(CC) $(LDFLAGS) <n>.o $(LOADLIBES) $(LDLIBS)
……
下面列出隱含規則中所用到的變量:包括命令變量和參數變量兩種
命令變量
說明
命令參數變量
說明(沒指明默認值為空)
AR
函數庫打包程序
ARFLAGS
AR 命令的參數。默認值是“rv”。
AS
匯編語言編譯程序
ASFLAGS
匯編語言編譯器參數。
CC
C 語言編譯程序
CFLAGS
C 語言編譯器參數。
CXX
C++語言編譯程序
CXXFLAGS
C++語言編譯器參數。
CO
從 RCS 文件中擴展文件程序
COFLAGS
RCS 命令參數。
CPP
C 程序的預處理器
CPPFLAGS
C /Pascal預處理器參數。
FC
Fortran 和 Ratfor 的編譯器和預處理程序
FFLAGS
Fortran 語言編譯器參數。
GET
從 SCCS 文件中擴展文件的程序
GFLAGS
SCCS “get”程序參數。
PC
Pascal 語言編譯程序
PFLAGS
Pascal 語言編譯器參數。
LEX
Lex 方法分析器程序(針對於 C 或 Ratfor)
LFLAGS
Lex 文法分析器參數。
YACC
Yacc 文法分析器(針對於 C 程序)
YFLAGS
Yacc 文法分析器參數。
YACCR
Yacc 文法分析器(針對於 Ratfor 程序)
TEX
從 TeX 源文件創建 TeX DVI 文件的程序
RM
刪除文件命令
…
LDFLAGS
鏈接器參數。(如:“ld”)
(2)模式規則
可以使用模式規則來定義一個隱含規則。模式規則中,至少在規則的目標定義中要包含"%",否則,就是一般的規則。如果"%"定義在目標中,那麼,目標中的"%"的值決定了依賴目標中的"%"的值。示例如下:
%.tab.c %.tab.h: %.y bison -d $<
說明:這條規則告訴 make 把所有的[.y]文件都以"bison -d <n>.y"執行,然後生成"<n>.tab.c"和"<n>.tab.h"文件。(其中,"<n>"表示一個任意字符串)。
(3)後綴規則
後綴規則是一個比較老式的定義隱含規則的方法。為了和老版本的Makefile 兼容,GNU make 同樣兼容於這些東西。後綴規則有兩種方式:"雙後綴"和"單後綴"。
雙後綴規則定義了一對後綴:目標文件的後綴和依賴目標(源文件)的後綴。如".c.o"相當於"%o : %c"。單後綴規則只定義一個後綴,也就是源文件的後綴。如".c"相當於"% : %.c"。
後綴規則不允許任何的依賴文件,如果有依賴文件的話,那就不是後綴規則,那些後綴統統被認為是文件名。
後綴規則中所定義的後綴應該是 make 所認識的,默 認 的 後 綴 列 表是:.out, .a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h,.info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。而要讓 make知道一些特定的後綴,我們可以使用偽目標".SUFFIXES"來定義或是刪除,如:.SUFFIXES: .hack .win把後綴.hack 和.win 加入後綴列表中的末尾;.SUFFIXES: # 刪除默認的後綴;SUFFIXES: .c .o .h # 定義自己的後綴。
2.3變量
2.3.1 特殊符號及關鍵字
反斜槓(\):是換行符的意思,當一行過長不方便浏覽時使用反斜槓換行;另外還有轉義的作用,例外是$,如果想要使用$,需要用$$表示。
橫槓(-):加在命令前,表示也許某些文件出現問題,但不要管,繼續做後面的事。
井號(#):注釋符,makefile只支持單行注釋。可使用注釋符結合空變量定義一個空格變量:
nullstring := space := $(nullstring) # end of the line 其中,nullstring變量什麼都沒有,space為空格變量。
通配符*,?,[]:*號表示任意字符,?號任意一個字符,[]表示范圍內的字符。
%:表示匹配0或若干字符,用於pattern模式中。
objects := $(wildcard *.o):在變量中使用通配符需要使用關鍵字wildcard指定,否則只會類似於宏替換,不會展開。
Include(filename):把其它makefile文件包含到當前位置,但命令前不能用tab開頭。
VPATH:makefile中的特殊變量,用於指定make對依賴文件的搜索路徑
vpath:make的關鍵字,全小寫。類似於VPATH,但更靈活
override:在makefile中設置make命令中指定的變量的值,當make命令中指定了該變量的值時,makefile中該變量值被忽略。
2.3.2 變量
(1)自定義變量
在 makefile 中我們可以使用變量。makefile 的變量也就是一個字符串,類似於C 語言中的宏。不過變量名可以是任意大小寫,當定義了變量後,可以使用 “$(變量名)”的方式來使用這個變量了,其實就類似於宏替換。
變量的命名可以包含字符、數字,下劃線(可以是數字開頭) ,但不應該含有“:”、“#”、“=”或是空字符(空格、回車等)。變量是大小寫敏感的, 推薦使用大小寫搭配的變量名,如:MakeFlags。這樣可以避免和系統的變量沖突。
在變量賦值時,變量的值可以是其他變量的表達式,而其他變量的定義可以在文件的任何位置,即不要求其他變量一定要預先定義。但在遞歸定義時將會出現無限循環,因此引入操作符“:=”來表示只使用預先定義的變量的值,從而避免變量遞歸定義時的無限循環。此外,變量的值可以是嵌套的變量表達式,其賦值方式就是按宏替換的方式依次展開求值。
變量值替換:1“${var:a=b}”,其意思是,把變量“var”中所有以“a”字串“結尾”的“a”替換成“b”字串;2另外一種變量替換的技術是以“靜態模式”(bar := $(foo:%.o=%.c))
變量作用域:makefile中定義的變量都是全局變量(除了自動化變量),也可定義作用於特定目標或模式的局部變量,格式為:
目標/模式:變量賦值表達式
(2)自動化變量
這種變量會把模式中所定義的一系列的文件自動地挨個取出,直至所有的符合模式的文件都取完了。
$%:僅當目標是函數庫文件時,表示規則中的目標成員名。
$@:表示目前規則中所有的目標的集合
$<:依賴目標中的第一個目標名字。
$?:所有比目標新的依賴目標的集合。以空格分隔。
$^:所有的依賴目標的集合。以空格分隔。
$+:這個變量很像"$^",也是所有依賴目標的集合。只是它不去除重復的依賴目標。
$*:這 個 變 量 表 示 目 標 模 式 中 "%"及 其 之 前 的 部 分 。
這七個自動化變量還可以取得文件的目錄名或是在當前目錄下的符合模式的文件名, 只需要搭配上"D"或"F"字樣。
(3)命令序列變量
可以為命令序列指定一個變量,便於重用。定義方式為:
define 變量名 命令序列 endef
之後直接按變量引用方式($(變量名))引用即可。
(4)系統變量
MAKEFLAGS:保存make參數信息;
MAKELEVEL:保存了當前makefile在make嵌套執行的調用層數;
CFLAGS:環境變量,若系統環境中設置了該環境變量,則所有makefile文件都可以使用該變量設置的統一參數。若makefile文件中定義了該變量,將覆蓋環境變量中的值。
MAKECMDGOALS:make的環境變量,這個變量中存放所指定的終極目標的列表,如果在命令行上,你沒有指定目標,那麼,這個變量是空值。
2.3.3 偽目標
“偽目標”並不是一個文件,只是一個標簽。為了避免和文件重名的這種情況,我們可以使用一個特殊的標記“.PHONY”來顯示地指明一個目標是“偽目標”,如
.PHONY: clean clean: rm *.o temp
偽目標可以作為默認目標也可以有依賴文件,借由這個特性,我們可以一次性生成多個可執行文件。
2.4 表達式
2.4.1 賦值表達式
?=:用於變量定義,表示如果變量沒有被定義,則定義並賦值,如果定義了語句作廢。
+=:用於變量賦值時的追加,如果變量之前沒有定義過,那麼,“+=”會自動變成“=”,如果前面有變量定義,那麼“+=”會繼承於前次操作的賦值符(=或:=)。
2.4.2 條件表達式
格式為: <conditional-directive> <text-if-true> else <text-if-false> endif
其中<conditional-directive>表示條件關鍵字。這個關鍵字有四個:ifeq、ifneq、ifdef、ifndef。
2.5 函數
2.5.1 調用語法
${<function> <arguments>}
說明:<function>就是函數名,make 支持的函數不多。<arguments>是函數的參數,參數間以逗號“,”分隔,而函數名和參數之間以“空格”分隔。
2.5.2 常用函數
類別
函數名
功能
說明
字符串函數
$(subst <from>,<to>,<text> )
把字串<text>中的<from>字符串替換成<to>。
函數返回被替換過後的字符串。
$(patsubst <pattern>,<replacement>,<text> )
查找<text>中的單詞是否符合模式<pattern>,匹配則以<replacement>替換。
如果<replacement>中也包含%,那麼這個“%”將是<pattern>中的那個“%”所代表的字串。
$(strip <string> )
去掉<string>字串中開頭和結尾的空字符。
函數返回被替換過後的字符串。
$(findstring <find>,<in> )
在字串<in>中查找<find>字串。
如果找到,返回<find>,否則返回空字符串。
$(filter <pattern...>,<text> )
以<pattern>模式過濾<text>字符串中的單詞,保留符合<pattern>的單詞。
可以有多個模式。
$(filter-out <pattern...>,<text> )
以<pattern>模式過濾<text>字符串中的單詞,去除符合模式的單詞
可以有多個模式。返回不符合模式<pattern>的字串。
$(sort <list> )
給字符串<list>中的單詞排序(升序) 。
sort 函數會去掉<list>中相同的單詞。
$(word <n>,<text> )
取字符串<text>中第<n>個單詞。 (從一開始)
如果<n>比<text>中的單詞數要大,返回空字符串。
$(wordlist <s>,<e>,<text> )
從字符串<text>中取從<s>開始到<e>的單詞串。
如果<s>比<text>中的單詞數要大,返回空字符串。
$(words <text> )
統計<text>中字符串中的單詞個數。
取最後一個單詞: $(word
$(words <text> ),<text> )。
$(firstword <text> )
取字符串<text>中的第一個單詞。
文件操作函數
$(dir <names...> )
從文件名序列<names>中取出目錄部分。目錄部分是指最後一個反斜槓(“/”)之前的部分。
如果沒有反斜槓,那麼返回“./”
$(notdir <names...> )
從文件名序列<names>中取出非目錄部分。
非目錄部分是指最後一個反斜槓(“/”)之後的部分。
$(suffix <names...> )
從文件名序列<names>中取出各個文件名的後綴。
如果文件沒有後綴,則返回空字串。
$(basename <names...> )
從文件名序列<names>中取出各個文件名的前綴部分。
$(addsuffix <suffix>,<names...> )
把後綴<suffix>加到<names>中的每個單詞後面。
$(addprefix <prefix>,<names...> )
把前綴<prefix>加到<names>中的每個單詞後面。
$(join <list1>,<list2> )
把<list2>中的單詞對應地加到<list1>的單詞後面。
如果<list1>的單詞個數多於<list2>,多出部分保持原樣。反之復制到1中。
循環
$(foreach <var>,<list>,<text> )
把<list>中的單詞逐一取出到<var>變量中,再執行<text>包含的表達式。
返回值為text每次執行返回的字符串組合(以空格相隔)
條件
$(if <condition>,<then-part>,<else-part> )
call
$(call <expression>,<parm1>,<parm2>,<parm3>...)
<expression>中的變量($(1),$(2)…)會 被 <parm1> , <parm2>…依 次 取 代 。
返回值為<expression>的返回值
origin
$(origin <variable> )
返回變量來源
返回值為:undefined,default,environment,file,command line,override,automatic
Shell函數
$(shell 命令)
它的參數是操作系統Shell 的命令
返回值為shell函數所執行的操作系統命令的輸出
控制make的函數
$(error <text ...> )
產生一個致命的錯誤,<text ...>是錯誤信息。
$(warning <text ...> )
輸出一段警告信息, make 繼續執行。
2.6 make
2.6.1 指定makefile和目標
(1)指定makefile
make 命令會在當前目錄下按順序找尋文件名為“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解釋這個文件。若找不到,將會依次到make命令-I參數指定目錄和<prefix>/include(一般是:/usr/local/bin 或/usr/include)目錄查找。如果要指定特定的 Makefile,可以使用 make 的“-f”和“--file”參數,如:make -f Make.Linux。
(2)指定目標
一般來說,make 的最終目標是 makefile 中的第一個目標,而其它目標一般是由這個目標連帶出來的。而要指定其他目標作為最終目標,在 make 命令後直接跟目標的名字就可以完成(如前面提到的“makeclean”形式),即使是偽目標和隱含目標都可以作為最終目標。
GNU目標編寫習慣說明:
偽目標
功能
all
所有目標的目標,其功能一般是編譯所有的目標。
clean
刪除所有被 make 創建的文件。
Install
安裝已編譯好的程序,就是把目標執行文件拷貝到指定的目標中去。
列出改變過的源文件。
tar
把源程序打包備份,也就是一個tar文件。
dist
創建一個壓縮文件,一般是把 tar 文件壓成Z文件或是gz文件。
TAGS
更新所有的目標,以備完整地重編譯使用。
check、test
測試 makefile 的流程。
2.6.2 make的參數
下面列舉了所有 GNU make 3.80 版的參數定義:
參數
功能
-b,-m
忽略和其它版本 make 的兼容性。
-B,--always-make
認為所有的目標都需要更新(重編譯) 。
-C <dir>, --directory=<dir>
指定讀取 makefile 的目錄。如果有多個“-C”參數,後面的路徑以前面的作為相對路徑,並以最後目錄作被指定目錄。
—debug[=<options>]
輸出 make 的調試信息,它有幾種不同的級別可供選擇。
-e, --environment-overrides
指明環境變量的值覆蓋 makefile 中定義的變量的值。
-f=<file>
指定需要執行的 makefile。
-h
-i
在執行時忽略所有的錯誤
-I <dir>
指定一個被包含 makefile 的搜索目標。
-k
出錯也不停止運行。
-l <load>
指定 make 運行命令的負載。
-n
僅輸出執行過程中的命令序列
-o <file>
不重新生成的指定的<file>
-p
輸出 makefile 中的所有數據,包括所有的規則和變量。
-q
不運行命令,也不輸出。僅僅是檢查所指定的目標是否需要更新。
……
詳細參數信息可以查看make幫助文檔。
2.6.3 使用make更新函數庫文件
函數庫文件也就是對 Object 文件(程序編譯的中間文件)打包生成的文件。可以以如下格式指定函數庫文件及其組成:
${<function> <arguments>}
這不是一個命令,而是一個目標和依賴的定義。如果要指定多個 member,那就以空格分開,如:foolib(hack.o kludge.o)。
2.7其他
(1)顯示命令:在makefile中的命令前加@符號,則該命令在make執行時將不被顯示在屏幕上;而在make命令中使用參數-n或--just-print,則只在屏幕顯示執行的命令序列,而不會執行命令,常用來調試。
(2)環境變量MAKEFILES:類似於include(filename),但環境變量MAKEFILES中的目標(即文件中的第一個標識符)不起作用,且不理會其中定義的文件的錯誤。建議不要使用該環境變量,容易出一些莫名的錯誤。
(3)make 是在讀取 Makefile 時就計算條件表達式的值,並根據條件表達式的值來選擇語句,所以,你最好不要把自動化變量(如“$@”等)放入條件表達式中,因為自動化變量是在運行時才有的。
(4)make的嵌套執行:大型工程為了利於模塊編譯和維護,通常會在每一個模塊目錄中分別寫上makefile文件,而在總控makefile文件中指定模塊makefile的執行。而總控模塊中的變量可以通過聲明export variable傳遞到下級的makefile中,但不會覆蓋下級makefile中的變量,直接使用export表示傳遞所有變量。其中變量SHELL和MAKEFLAGS總是會自動傳遞到下層makefile中,而MAKEFLAGS中保存了make參數信息,一旦定義了MAKEFLAGS變量,它將自動傳遞到下級makefile中(除了幾個參數:C,f,h,o,W),若不想傳遞make參數,可以這樣:
subsystem: cd subdir && $(MAKE) MAKEFLAGS=
(5)命令出錯:make會檢查每條命令執行後的返回值(),如果不正確則會終止該條規則的執行。1如果不希望命令出錯而終止規則運行,可以在命令前加一個“-”表示忽略該命令出錯繼續執行後續命令;2也可以指定make命令參數-i或—ignore-errors表示忽略所有命令的錯誤;3而如果一個規則的目標是.IGNORE,則表示該條規則的所有命令都忽略錯誤。4而如果指定make的參數-k或—keep-going,則表示命令出錯時終止對應規則而繼續執行其他規則。
三、例子
例1 多目標示例1
bigoutput littleoutput : text.g generate text.g -$(subst output,,$@) > $@
說明:其中,-$(subst output,,$@)中的“$”表示執行一個 Makefile 的函數,函數名為 subst,後面的為參數。這裡的這個函數是截取字符串的意思,“$@”表示目標的集合,就像一個數組,“$@”依次取出目標,並執於命令。
例2 多目標示例2
objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@
說明:上面的例子中,指明了我們的目標從$object 中獲取,“%.o”表明要所有以“.o”結尾的目標,也就是“foo.o bar.o”,也就是變量$object 集合的模式,而依賴模式“%.c”則取模式“%.o”的“%”,也就是“foo bar”,並為其加下“.c”的後綴,於是,我們的依賴目標就是“foo.c bar.c”。而命令中的“$<”和“$@”則是自動化變量, “$<”表示所有的依賴目標集(也就是“foo.c bar.c”) ,“$@”表示目標集(也就是“foo.o bar.o”)。
例3 生成.d文件(源文件的依賴關系文件)的模式規則:
%.d: %.c @set -e; rm -f $@; \ $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$
說明:這個規則的意思是,所有的[.d]文件依賴於[.c]文件,“rm -f $@”的意思是刪除所有的目標,也就是[.d]文件,第二行的意思是,為每個依賴文件“$<”, 也就是[.c]文件生成依賴文件, “$@”表示模式“%.d”文件,如果有一個 C 文件是 name.c,那麼“%”就是“name”,“$$$$”意為一個隨機編號,第二行生成的文件有可能是“name.d.12345”,第三行使用sed 命令做了一個替換,關於 sed 命令的用法請參看相關的使用文檔。第四行就是刪除臨時文件。
http://xxxxxx/Linuxjc/1151466.html TechArticle