對makefile文件的要點作了總結,包括變數、表達式、函數、規則等方面,並介紹了make命令的相關用法。 ...
在閱讀大型工程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 命令的用法請參看相關的使用文檔。第四行就是刪除臨時文件。