Makefile學(xué)習(xí)
參考自《跟我一起寫Makefile》陳皓
Makefile 的語法規(guī)則
基本語法
target ... : prerequisites ...
command
...
...
翻譯成中文大概就是:
一系列目標(biāo)文件:一系列依賴文件
執(zhí)行的一些命令
target 也就是一個目標(biāo)文件,可以是 Object File 墓懂,也可以是執(zhí)行文件掂碱。還可以是一個標(biāo)簽。
prerequisites 就是要生成那個 target 所需要的文件或是目標(biāo)。
command 也就是 make 需要執(zhí)行的任意的 shell 命令挣轨。
默認(rèn)情況下, make 命令會在當(dāng)前目錄下按順序找尋文件名為"GNUmakefile" "makefile" "Makefile"的文件能曾,并解釋這個文件。一般使用"Makefile"這個文件名凌简。
如果要指定特定的 Makefile上炎,你可以使用 make 的"-f"和"--file"參數(shù),如:make -f Make.Linux 或 make --file Make.AIX雏搂。
更新函數(shù)庫文件
liba(a.o ...):a.o ...
ar cr liba a.o ...
條件表達(dá)式
條件表達(dá)式的語法為:
<conditional-directive>
<text-if-true>
endif
以及:
<conditional-directive>
<text-if-true>
else <conditional-directive>
<text-if-true>
else
<text-if-false>
endif
其中<conditional-directive>表示條件關(guān)鍵字藕施,這個關(guān)鍵字有四種:
- ifeq
ifeq (<arg1>, <arg2>)
ifeq '<arg1>' '<arg2>'
ifeq "<arg1>" "<arg2>"
ifeq "<arg1>" '<arg2>'
ifeq '<arg1>' "<arg2>"
比較參數(shù)"arg1"和"arg2"的值是否相同。
- ifneq
ifneq (<arg1>, <arg2>)
ifneq '<arg1>' '<arg2>'
ifneq "<arg1>" "<arg2>"
ifneq "<arg1>" '<arg2>'
ifneq '<arg1>' "<arg2>"
比較參數(shù)"arg1"和"arg2"的值是否相同凸郑,如果不同裳食,則為真。
- ifdef
ifdef <variable-name>
如果變量<variable-name>的值非空芙沥,那到表達(dá)式為真诲祸。否則,表達(dá)式為假而昨。當(dāng)然救氯,<variable-name>同樣可以是一個函數(shù)的返回值。注意歌憨,ifdef 只是測試一個變量是否有值着憨,其并不會把變量擴(kuò)展到當(dāng)前位置。
- ifndef
ifndef <variable-name>
和 ifdef 是意思相反务嫡。
偽目標(biāo)
"偽目標(biāo)"并不是一個文件享扔,只是一個標(biāo)簽,由于"偽目標(biāo)"不是文件植袍,所以 make 無法生成它的依賴關(guān)系和決定它是否要執(zhí)行惧眠。我們只有通過顯示地指明這個"目標(biāo)"才能讓其生效。當(dāng)然于个,"偽目標(biāo)"的取名不能和文件名重名氛魁,不然其就失去了"偽目標(biāo)"的意義了。
當(dāng)然,為了避免和文件重名的這種情況秀存,我們可以使用一個特殊的標(biāo)記".PHONY"來顯示地指明一個目標(biāo)是"偽目標(biāo)"捶码,向 make 說明,不管是否有這個文件或链,這個目標(biāo)就是"偽目標(biāo)"惫恼。
.PHONY : clean
多目標(biāo)
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) > $@
上述規(guī)則等價于:
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.ggenerate text.g -little > littleoutput
靜態(tài)模式
<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
...
targets 定義了一系列的目標(biāo)文件,可以有通配符。是目標(biāo)的一個集合澳盐。
target-parrtern 是指明了 targets 的模式,也就是的目標(biāo)集模式祈纯。
prereq-parrterns 是目標(biāo)的依賴模式,它對 target-parrtern 形成的模式再進(jìn)行一次依賴目標(biāo)的定義。
注釋
Makefile 中使用'#'字符作為注釋符叼耙,在'#'之后的是注釋部分腕窥,當(dāng)你真正想用到'#'字符時,可以用反斜杠進(jìn)行轉(zhuǎn)義筛婉,如'\#'簇爆。
Makefile 使用變量
普通變量
a = main.o # 定義變量a的值為main.o
var = aa
$a # 調(diào)用變量
$(a) # 調(diào)用變量
${a}
$(var) # 調(diào)用變量
${var}
多行變量
define two-lines
echo foo
echo $(bar)
endef
自動化變量
$@ 表示規(guī)則中的目標(biāo)文件集。在模式規(guī)則中爽撒,如果有多個目標(biāo)入蛆,那么,"$@"就是匹配于目標(biāo)中模式定義的集合硕勿。
$% 僅當(dāng)目標(biāo)是函數(shù)庫文件中安寺,表示規(guī)則中的目標(biāo)成員名。例如首尼,如果一個目標(biāo)是"foo.a(bar.o)"挑庶,那么,"$%"就是"bar.o"软能,"$@"就是"foo.a"迎捺。如果目標(biāo)不是函數(shù)庫文件(Unix 下是[.a],Windows 下是[.lib])查排,那么凳枝,其值為空。
$< 依賴目標(biāo)中的第一個目標(biāo)名字跋核。如果依賴目標(biāo)是以模式(即"%")定義的岖瑰,那么"$<"將是符合模式的一系列的文件集。注意砂代,其是一個一個取出來的蹋订。
$? 所有比目標(biāo)新的依賴目標(biāo)的集合。以空格分隔刻伊。
$^ 所有的依賴目標(biāo)的集合露戒。以空格分隔椒功。如果在依賴目標(biāo)中有多個重復(fù)的,那個這個變量會去除重復(fù)的依賴目標(biāo)智什,只保留一份动漾。
$+ 這個變量很像"$^",也是所有依賴目標(biāo)的集合荠锭。只是它不去除重復(fù)的依賴目標(biāo)旱眯。
$* 這個變量表示目標(biāo)模式中"%"及其之前的部分。如果目標(biāo)是"dir/a.foo.b"证九,并且目標(biāo)的模式是"a.%.b"删豺,那么,"$*"的值就是"dir/a.foo"甫贯。這個變量對于構(gòu)造有關(guān)聯(lián)的文件名是比較有較。如果目標(biāo)中沒有模式的定義看蚜,那么"$*"也就不能被推導(dǎo)出叫搁,但是,如果目標(biāo)文件的后綴是 make 所識別的供炎,那么"$*"就是除了后綴的那一部分渴逻。例如:如果目標(biāo)是"foo.c",因為".c"是 make 所能識別的后綴名音诫,所以惨奕,"$*"的值就是"foo"。這個特性是 GNU make 的竭钝,很有可能不兼容于其它版本的 make梨撞,所以,你應(yīng)該盡量避免使用"$*"香罐,除非是在隱含規(guī)則或是靜態(tài)模式中卧波。如果目標(biāo)中的后綴是 make 所不能識別的,那么"$*"就是空值庇茫。
$(@D) 表示"$@"的目錄部分(不以斜杠作為結(jié)尾)港粱,如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir"旦签,而如果"$@"中沒有包含斜杠的話查坪,其值就是"."(當(dāng)前目錄)。
$(@F) 表示"$@"的文件部分宁炫,如果"$@"值是"dir/foo.o"偿曙,那么"$(@F)"就是"foo.o"。
$(F)羔巢、$(D)遥昧、$(%D)覆醇、$(%F)、$(<D)炭臭、$(<F)永脓、$(D)**、**$(F)鞋仍、$(+D)常摧、$(+F)、$(?D)威创、$(?F)** 同理分別表示對應(yīng)文件的目錄部分和文件部分落午。
變量賦值
= 是最基本的賦值
:= 是覆蓋之前的值
?= 是如果沒有被賦值過就賦予等號后面的值
+= 是追加等號后面的值
Makefile 使用函數(shù)
函數(shù)調(diào)用語法
$(<function> <arguments>)
${<function> <arguments>}
字符串處理函數(shù)
- 字符串替換函數(shù)——subst
$(subst <from>, <to>, <text>)
功能:把字符串<text>中的<from>字符串替換成<to>。
返回:函數(shù)返回被替換過后的字符串肚豺。
- 模式字符串替換函數(shù)——patsubst
$(patsubst <pattern>, <replacement>, <text>)
功能:查找<text>中的單詞(單詞以"空格"溃斋、"Tab"、"回車"或"換行"分隔)是否符合模式<pattern>吸申,如果匹配的話,則以<replacement>替換梗劫。這里,<pattern>可以包括通配符"%",表示任意長度的字符串截碴。如果<replacement>中也包含"%"梳侨,那么,<replacement>中的這個"%"將是<pattern>中的那個"%"所代表的字符串日丹。(可以用""來轉(zhuǎn)義走哺,以"%"來表示真實含義的"%"字符)
返回:函數(shù)返回被替換過后的字符串。
- 去空格函數(shù)——strip
$(strip <string>)
功能:去掉<string>字符串中開頭和結(jié)尾的空字符哲虾。
返回:返回被去掉空格的字符串值丙躏。
- 查找字符串函數(shù)——findstring
$(findstring <find>, <in>)
功能:在字符串<in>中查找<find>字符串。
返回:如果找到束凑,那么返回<find>彼哼,否則返回空字符串。
- 過濾函數(shù)——filter
$(filter <pattern...>, <text>)
功能:以<pattern>模式過濾<text>字符串中的單詞湘今,保留符合模式<pattern>的單詞敢朱。可以有多個模式摩瞎。
返回:返回符合模式<pattern>的字符串拴签。
- 反過濾函數(shù)——filter-out
$(filter-out <pattern...>, <text>)
功能:以<pattern>模式過濾<text>字符串中的單詞,去除符合模式<pattern>的單詞旗们◎玖ǎ可以有多個模式。
返回:返回不符合模式<pattern>的字符串上渴。
- 排序函數(shù)——sort
$(sort <list>)
功能:給字符串<list>中的單詞排序(升序)岸梨。
返回:返回排序后的字符串喜颁。
- 取單詞函數(shù)——word
$(word <n>, <text>)
功能:取字符串<text>中第<n>個單詞。(從一開始)
返回:返回字符串<text>中第<n>個單詞曹阔。如果<n>比<text>中的單詞數(shù)要大,那么返回空字符串半开。
- 取單詞串函數(shù)——wordlist
$(wordlist <s>, <e>, <text>)
功能:從字符串<text>中取從<s>開始到<e>的單詞串。<s>和<e>是一個數(shù)字赃份。
返回:返回字符串<text>中從<s>到<e>的單詞字符串寂拆。如果<s>比<text>中的單詞數(shù)要大,那么返回空字符串抓韩。如果<e>大于<text>的單詞數(shù)纠永,那么返回從<s>開始,到<text>結(jié)束的單詞串谒拴。
- 單詞個數(shù)統(tǒng)計函數(shù)——words
$(words <text>)
功能:統(tǒng)計<text>中字符串中的單詞個數(shù)尝江。
返回:返回<text>中的單詞數(shù)。
- 首單詞函數(shù)——firstword
$(firstword <text>)
功能:取字符串<text>中的第一個單詞英上。
返回:返回字符串<text>的第一個單詞炭序。
文件名操作函數(shù)
- 取目錄函數(shù)——dir
$(dir <names...>)
功能:從文件名序列<names>中取出目錄部分。目錄部分是指最后一個反斜杠("/")之前的部分善延。如果沒有反斜杠少态,那么返回"./"城侧。
返回:返回文件名序列<names>的目錄部分易遣。
- 取文件函數(shù)——notdir
$(notdir <names...>)
功能:從文件名序列<names>中取出非目錄部分。非目錄部分是指最后一個反斜杠("/")之后的部分嫌佑。
返回:返回文件名序列<names>的非目錄部分豆茫。
- 取后綴函數(shù)——suffix
$(suffix <names...>)
功能:從文件名序列<names>中取出各個文件名的后綴。
返回:返回文件名序列<names>的后綴序列屋摇,如果文件沒有后綴揩魂,則返回空字符串。
- 取前綴函數(shù)——basename
$(basename <names...>)
功能:從文件名序列<names>中取出各個文件名的前綴部分炮温。
返回:返回文件名序列<names>的前綴序列火脉,如果文件沒有前綴,則返回空字符串柒啤。
- 加后綴函數(shù)——addsuffix
$(addsuffix <suffix>, <names...>)
功能:把后綴<suffix>加到<names>中的每個單詞后面倦挂。
返回:返回加過后綴的文件名序列。
- 加前綴函數(shù)——addprefix
$(addprefix <prefix>, <names...>)
功能:把前綴<prefix>加到<names>中的每個單詞前面担巩。
返回:返回加過前綴的文件名序列方援。
- 連接函數(shù)——join
$(join <list1>, <list2>)
功能:把<list2>中的單詞對應(yīng)地加到<list1>的單詞后面。如果<list1>的單詞個數(shù)要比<list2>的多涛癌,那么犯戏,<list1>中的多出來的單詞將保持原樣送火。如果<list2>的單詞個數(shù)要比<list1>多,那么先匪,<list2>多出來的單詞將被復(fù)制到<list2>中种吸。
返回:返回連接過后的字符串。
foreach函數(shù)
$(foreach <var>, <list>, <text>)
功能:把參數(shù)<list>中的單詞逐一取出放到參數(shù)<var>所指定的變量中,然后再執(zhí)行<text>所包含的表達(dá)式胚鸯。每一次<text>會返回一個字符串,循環(huán)過程中, <text>的所返回的每個字符串會以空格分隔,最后當(dāng)整個循環(huán)結(jié)束時,<text>所返回的每個字符串所組成的整個字符串(以空格分隔)將會是 foreach 函數(shù)的返回值骨稿。
返回:<text>所返回的每個字符串所組成的整個字符串(以空格分隔)。
if函數(shù)
$(if <condition>, <then-part>)
$(if <condition>, <then-part>, <else-part>)
功能:<condition>參數(shù)是 if 的表達(dá)式,如果其返回的為非空字符串姜钳,那么這個表達(dá)式就相當(dāng)于返回真坦冠,于是,<then-part>會被計算哥桥,否則<else-part>會被計算辙浑。
返回:如果<condition>為真(非空字符串),那個<then-part>會是整個函數(shù)的返回值拟糕,如果<condition>為假(空字符串)判呕,那么<else-part>會是整個函數(shù)的返回值,此時如果<else-part>沒有被定義送滞,那么侠草,整個函數(shù)返回空字符串。
所以犁嗅,<then-part>和<else-part>只會有一個被計算边涕。
call函數(shù)
$(call <expression>, <parm1>, <parm2>, <parm3> ...)
功能:當(dāng) make 執(zhí)行這個函數(shù)時,<expression>參數(shù)中的變量褂微,如$(1)功蜓,$(2),$(3)等宠蚂,會被參數(shù)<parm1>式撼,<parm2>,<parm3>依次取代求厕。
返回:<expression>的返回值就是 call 函數(shù)的返回值著隆。
origin函數(shù)
$(origin <variable>)
注意,<variable>是變量的名字呀癣,不應(yīng)該是引用美浦。所以你最好不要在<variable>中使用"$"字符。Origin 函數(shù)會以其返回值來告訴你這個變量的"出生情況"十艾,下面抵代,是 origin函數(shù)的返回值:
"undefined"
如果<variable>從來沒有定義過,origin 函數(shù)返回這個值"undefined"。"default"
如果<variable>是一個默認(rèn)的定義忘嫉,比如"CC"這個變量荤牍。"environment"
如果<variable>是一個環(huán)境變量案腺,并且當(dāng) Makefile 被執(zhí)行時,"-e"參數(shù)沒有被打開康吵。"file"
如果<variable>這個變量被定義在 Makefile 中劈榨。"command line"
如果<variable>這個變量是被命令行定義的。"override"
如果<variable>是被 override 指示符重新定義的晦嵌。"automatic"
如果<variable>是一個命令運行中的自動化變量同辣。
shell函數(shù)
$(shell <shell script>)
功能:shell 函數(shù)也不像其它的函數(shù)。顧名思義惭载,它的參數(shù)應(yīng)該就是操作系統(tǒng) Shell 的命令旱函。它和反引號"`"是相同的功能。
返回:shell 函數(shù)把執(zhí)行操作系統(tǒng)命令后的輸出作為函數(shù)返回描滔。
注意棒妨,這個函數(shù)會新生成一個 Shell 程序來執(zhí)行命令,所以你要注意其運行性能含长,如果你的 Makefile 中有一些比較復(fù)雜的規(guī)則券腔,并大量使用了這個函數(shù),那么對于你的系統(tǒng)性能是有害的拘泞。特別是 Makefile 的隱晦的規(guī)則可能會讓你的 shell 函數(shù)執(zhí)行的次數(shù)比你想像的多得多纷纫。
控制make的函數(shù)
- error函數(shù)
$(error <text ...>)
產(chǎn)生一個致命的錯誤,make 退出陪腌,<text ...>是錯誤信息辱魁。注意,error 函數(shù)不會在一被使用就會產(chǎn)生錯誤信息偷厦,所以如果你把其定義在某個變量中商叹,并在后續(xù)的腳本中使用這個變量燕刻,那么也是可以的只泼。
- warning函數(shù)
$(warning <text ...>)
這個函數(shù)很像 error 函數(shù),只是它并不會讓 make 退出卵洗,只是輸出一段警告信息请唱,而 make 繼續(xù)執(zhí)行。
Makefile 自動推導(dǎo)
只要 make 看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關(guān)系中过蹂,并且
$(CC) -c [.c]
也會被推導(dǎo)出來
- 我的個人主頁:http://www.techping.cn/
- 我的個人站點博客:http://www.techping.cn/blog/wordpress/
- 我的CSDN博客:http://blog.csdn.net/techping
- 我的簡書:http://www.reibang.com/users/b2a36e431d5e/timeline
- 我的GitHub:https://github.com/techping
歡迎相互follow~