Posted on 2010-11-19 14:03
幻海藍(lán)夢(mèng) 閱讀(18525)
評(píng)論(0) 編輯 收藏 所屬分類:
Linux 、
版本管理
原文: http://www.chinaunix.net/jh/7/16880.html
Make 程序最初設(shè)計(jì)是為了維護(hù)C程序文件防止不必要的重新編譯。在使用命令行編譯器的時(shí)
候,修改了一個(gè)工程中的頭文件,如何確保包含這個(gè)頭文件的所有文件都得到編譯?現(xiàn)在10機(jī)的版本生成是使用批處理程序,編譯那些文件依賴于程序的維護(hù)者,
在模塊之間相互引用頭文件的情況下,要將所有需要重新編譯的文件找出來(lái)是一件痛苦的事情;在找到這些文件之后,修改批處理進(jìn)行編譯。實(shí)際上這些工作可以讓
make程序來(lái)自動(dòng)完成,make工具對(duì)于維護(hù)一些具有相互依賴關(guān)系的文件特別有用,它對(duì)文件和命令的聯(lián)系(在文件改變時(shí)調(diào)用來(lái)更新其它文件的程序)提供
一套編碼方法。Make工具的基本概念類似于Proglog語(yǔ)言,你告訴make需要做什么,提供一些規(guī)則,make來(lái)完成剩下的工作。
1簡(jiǎn)介
make
工作自動(dòng)確定工程的哪部分需要重新編譯,執(zhí)行命令去編譯它們。雖然make多用于C程序,然而只要提供命令行的編譯器,你可以將其用于任何語(yǔ)言。實(shí)際
上,make工具的應(yīng)用范圍不僅于編程,你可以描述任和一些文件改變需要自動(dòng)更新另一些文件的任務(wù)來(lái)使用它。
1.1準(zhǔn)備工作
如果要使用
make,你必須寫一個(gè)叫做“makefile”的文件,這個(gè)文件描述工程中文件之間的關(guān)系,提供更新每個(gè)文件的命令。典型的工程是這樣的:可執(zhí)行文件靠
目標(biāo)文件來(lái)更新,目標(biāo)文件靠編譯源文件來(lái)更新。
Makefile寫好之后,每次更改了源文件后,只要執(zhí)行make就足夠了,所有必要的重新編譯將
執(zhí)行。Make程序利用makefile中的數(shù)據(jù)庫(kù)和文件的最后修改時(shí)間來(lái)確定那個(gè)文件需要更新;對(duì)于需要更新的文件,make執(zhí)行數(shù)據(jù)庫(kù)中記錄的命令。
可
以提供命令行參數(shù)給make來(lái)控制那個(gè)文件需要重新編譯。
1.2Makefile介紹
Makefile文件告訴make做什么,多數(shù)情況
是怎樣編譯和鏈接一個(gè)程序。
這里有一個(gè)簡(jiǎn)單的makefile,描述如何編譯鏈接由8個(gè)C文件和3個(gè)頭文件組成的一個(gè)編輯器:
edit : main.o kbd.o command.o display.o \
insert.o serach.o files.o utils.o
cc –o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc –c main.c
kdb.o : kbd.c defs.h command.h
cc –c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
將
長(zhǎng)行用\分開便于閱讀,這和使用一個(gè)長(zhǎng)行的作用是一樣的。使用這個(gè)makefile創(chuàng)建可執(zhí)行文件“edit”時(shí)運(yùn)行make就可以了;如果要將可執(zhí)行文
件和目標(biāo)文件刪除,執(zhí)行make clean
make重新編譯這個(gè)編輯器時(shí),每個(gè)更改的C文件必須重新編譯;如果頭文件更改了,每個(gè)包含頭文件的
C文件必須重新編譯;每次編譯產(chǎn)生一個(gè)對(duì)應(yīng)于原文件的目標(biāo)文件。最終,目標(biāo)文件鏈接在一起產(chǎn)生新的可執(zhí)行文件。
1.3規(guī)則簡(jiǎn)介
makefile
中的規(guī)則是這樣的:
TARGET … : DEPENDENCIES …
COMMAND
…
目標(biāo)(TARGET)程序產(chǎn)
生的文件,如可執(zhí)行文件和目標(biāo)文件;目標(biāo)也可以是要執(zhí)行的動(dòng)作,如“clean”。
依賴(DEPENDENCIES)是用來(lái)產(chǎn)生目標(biāo)的輸入文件,
一個(gè)目標(biāo)通常依賴于多個(gè)文件。
命令(COMMAND)是make執(zhí)行的動(dòng)作,一個(gè)可以有多個(gè)命令,每個(gè)占一行。注意:每個(gè)命令行的起始字符必須為
TAB字符!
有依賴關(guān)系規(guī)則中的命令通常在依賴文件變化時(shí)負(fù)責(zé)產(chǎn)生target文件,make執(zhí)行這些命令更新或產(chǎn)生target。規(guī)則可以沒(méi)有
依賴關(guān)系,如包含target “clean”的規(guī)則。
規(guī)則解釋如何和何時(shí)重做該規(guī)則中的文件,make根據(jù)依賴關(guān)系執(zhí)行產(chǎn)生或更新目標(biāo);規(guī)則也
說(shuō)明如何和何時(shí)執(zhí)行動(dòng)作。有的規(guī)則看起來(lái)很復(fù)雜,但都符合上述模式。
1.4make工作原理
缺省make從第一個(gè)target開始(第一
個(gè)非 ’.’ 開始的target),這稱作缺省目標(biāo)。在上述的makefile中,缺省目標(biāo)是更新執(zhí)行程序’edit’,將這個(gè)目標(biāo)置于最前面。當(dāng)執(zhí)行
make的時(shí)候,make程序從當(dāng)前目錄讀入makefile開始處理第一個(gè)規(guī)則;在例子中,這個(gè)規(guī)則是重新鏈接’edit’;在make處理這個(gè)規(guī)則之
前,必須處理’edit’所依賴的那些文件的規(guī)則,例子中是目標(biāo)文件。這些文件按照他們自己的規(guī)則處理:通過(guò)編譯源文件來(lái)更新每個(gè)’.o’文件;當(dāng)依賴關(guān)
系中的源文件或頭文件比目標(biāo)文件新,或目標(biāo)文件不存在時(shí),必須重新編譯。
其它的規(guī)則被處理是因?yàn)樗麄兊膖arget是目標(biāo)的依賴,和目標(biāo)沒(méi)有依賴
關(guān)系的規(guī)則不會(huì)被處理,除非指定make處理(如make clean)。
在重新編譯目標(biāo)文件之前,make會(huì)試圖更新它的依賴:源文件和頭文
件。例子中的makefile對(duì)源文件和頭文件未指定任何操作:’.c’和’.h’文件不是任何規(guī)則的目標(biāo)。確認(rèn)所有的目標(biāo)文件都是最新的之后,make
決定是否重新鏈接’edit’:如果’edit’不存在,或者任何一個(gè)目標(biāo)文件都比它新,則鏈接工作將進(jìn)行。
這樣,如果我們改變insert.c
運(yùn)行make,make會(huì)編譯這個(gè)文件來(lái)更新’insert.o’,然后鏈接’edit’;如果修改了’command.h’運(yùn)行
make,’kbd.o’,’command.o’,’files.o’會(huì)重新生成,鏈接’edit’。
1.5使用變量
在例子中,在規(guī)
則’edit’中,目標(biāo)文件被列出來(lái)兩次:
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
這
樣的重復(fù)容易出錯(cuò):假設(shè)工程中加入了一個(gè)新的目標(biāo)文件,可能只將其加入了一個(gè)列表中;通過(guò)使用變量可以消除這種風(fēng)險(xiǎn):變量允許一個(gè)預(yù)定義的字符串在多個(gè)地
方被替換。
在makefile中,可以寫這樣一行來(lái)定義’object’變量:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
于
是在需要目標(biāo)文件名列表的地方,使用$(object) 來(lái)代替變量的值。以下是使用了變量以后的makefile:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit $(objects)
1.6
簡(jiǎn)化命令
為每個(gè)文件寫出編譯命令不是必要的,因?yàn)閙ake可以自己來(lái)做;以’.c’文件更新’.o’文件有一個(gè)隱含的規(guī)則,使用’cc -c’命
令。Make將利用’cc –c main.c –o main.o’來(lái)將main.c編譯為main.o,因此在生成目標(biāo)文件的規(guī)則中,可以省略命令。
當(dāng)’.c’
文件以這樣的方式使用時(shí),將自動(dòng)加入到依賴關(guān)系中;由是在省略命令的前提下,可以將’.c’文件從依賴關(guān)系中省略。以下是簡(jiǎn)化過(guò)的makefile:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
.PHONY : clean
clean :
-rm edit $(objects)
1.7
另一種風(fēng)格
如果makefile中的目標(biāo)都是以隱含規(guī)則生成,可以將規(guī)則按照依賴關(guān)系分組:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
這
里’defs.h’作為所有目標(biāo)文件的依賴。這種風(fēng)格是好是壞取決于個(gè)人喜好,它非常緊湊,但是將每個(gè)目標(biāo)的依賴信息放在一起看起來(lái)更清楚一些。
1.8
清理
編寫規(guī)則不至于編譯程序。Makefile通常描述如何做其它事情:比如刪除目錄中的目標(biāo)文件和可執(zhí)行文件來(lái)清理目錄。例子中是這樣寫的:
clean:
rm edit $(objects)
實(shí)
際情況是,我們需要處理一些意外事件:存在一個(gè)叫做’clean’的文件;如果rm出錯(cuò),并不希望make過(guò)程停止下來(lái),修改過(guò)的版本如下:
.PHONY : clean
clean :
-rm edit $(objects)
這
樣的規(guī)則當(dāng)然不能放在makefile的開始,因?yàn)檫@并不是我們?nèi)笔∫龅墓ぷ鳌S捎?#8217;clean’并不是’edit’的依賴,在運(yùn)行make時(shí)沒(méi)有參數(shù)
時(shí),這條規(guī)則不會(huì)執(zhí)行;要執(zhí)行這個(gè)規(guī)則,必須運(yùn)行’make clean’。
2Makefile
Makefile中包含五種內(nèi)容:顯式規(guī)
則,隱式規(guī)則,變量定義,指令(directive)和注釋。
;顯式規(guī)則描述如何生成規(guī)則的目標(biāo),它列出
了目標(biāo)依賴的文件,指定了產(chǎn)生或更新目標(biāo)的命令
;隱式規(guī)則描述如何生成基于文件名的一類文件,說(shuō)明目標(biāo)可
能依賴于和其文件名類似的文件,指定了相應(yīng)的命令。
;指令類似與編譯器的偽指令,包含:
;
指示make讀入另一個(gè)makefile
;決定是否忽略makefile中的一部分
;
定義一個(gè)變量
;一行中‘#’開始是注釋,直到行末,除非遇到續(xù)行符號(hào)。在’define’和命令中不能有
注釋,其它情況下注釋可出現(xiàn)在任何地方。
2.1makefile名字
缺省情況下,make以下列名字查找
makefile:’GNUmakefile’,’makefile’和’Makefile’(注意大小寫)。通常你的makefile應(yīng)叫
做’makefile’或’Makefile’。’GNUmakefile’不推薦,除非你的makefile是為GNU的make定制的,其它的
make不認(rèn)為該名字是一個(gè)makefile的名字。
如果你使用非標(biāo)準(zhǔn)命名的makefile,必須用命令開關(guān)’-f ’ 或 ’—file’。
參數(shù)’ –f NAME’或’—file NAME’告訴make讀入NAME作為makefile。如果使用多個(gè)該開關(guān),所有的文件將按順序連接起來(lái)。
如果使用該選項(xiàng),標(biāo)準(zhǔn)的makefile名字不會(huì)自動(dòng)檢測(cè)。
2.2包含
‘include’指令告訴make暫停處理余下的內(nèi)容,讀入其它
makefile。語(yǔ)法如下:
include FILENAMES …
這一行起始可以有空格,但TAB字符不允許。如果文件名包含變量或
函數(shù),這些將被擴(kuò)展。
2.3‘MAKEFILE’變量
如果環(huán)境變量’MAKEFILE’已定義,make認(rèn)為它的值是一系列空格隔開的文
件名,這些文件在處理其它makefile前被make程序讀入。這類似于include指令;這些文件中的目標(biāo)不會(huì)影響缺省目標(biāo),而且如果文件未找到的
話,make并不認(rèn)為是錯(cuò)誤。
這個(gè)變量的主要用途是遞歸引用make程序時(shí)通訊
2.4如何重新生成makefile
有時(shí)候
makefile是從其它文件生成的,比如RCS或SCCS文件。如果makefile是由其它文件生成的,需要make讀入最新版本的
makefile。
在讀入所有makefile之后,make認(rèn)為每個(gè)makefile是一個(gè)目標(biāo),試圖去更新它;如果makefile中有一條
如何更新它的規(guī)則,或者有適用的隱式規(guī)則,需要的更新會(huì)進(jìn)行。所有的makefile檢查完之后,如果有的改變了,make重新開始再讀入(make會(huì)試
圖再做更新,但通常不會(huì)再改變了,因?yàn)橐呀?jīng)是最新的了)。
如果一個(gè)文件使用雙冒號(hào)規(guī)則,提供了命令但沒(méi)有依賴關(guān)系,文件始終會(huì)被更新。在
makefile的情況下,如果makefile雙冒號(hào)規(guī)則,提供了命令但沒(méi)有依賴關(guān)系,這樣makefile始終會(huì)重新生成,這會(huì)導(dǎo)致循環(huán):make只
是在不斷更新makefile,卻不干活。為避免這種情況,make不會(huì)重新生成那些只有命令沒(méi)有依賴關(guān)系的雙冒號(hào)規(guī)則的makefile。
如果
沒(méi)有使用’-f’或’--file’選項(xiàng),make會(huì)嘗試缺省的makefile文件名。和指明’-f’或’--file’選項(xiàng)不同,make不能確定這
些文件是否應(yīng)當(dāng)存在。然而,如果缺省makefile不存在但可以通過(guò)運(yùn)行make規(guī)則生成,你可能希望這些規(guī)則被運(yùn)行使得makefile可以使用。
因
此,如果沒(méi)有缺省makefile,make試圖按照makefile名查找的順序生成它,直到成功或名字用完。注意如果make 不能找到或生成
makefile,這并不是錯(cuò)誤;makefile不總是必需的。
當(dāng)使用’-t’或’--touch’選項(xiàng)時(shí),不希望使用過(guò)時(shí)的makefile
來(lái)決定那個(gè)目標(biāo)來(lái)touch。所以’-t’選項(xiàng)對(duì)makefile更新不起作用;類似’-q’(or ‘—question’)和’-n’(or ’—
just-print’)不阻止makefile的更新,因?yàn)檫^(guò)時(shí)的makefile會(huì)產(chǎn)生錯(cuò)誤的輸出。這
樣’make –f mfile –n foo’會(huì)更新’mfile’,讀入它,打印出更新’foo’需要執(zhí)行的命令但不運(yùn)行這些命令。與’foo’有關(guān)
的命令是更新過(guò)的’mfile’中的內(nèi)容。
但是有時(shí)不希望更新makefile,可以將makefile作為命令行的目標(biāo),當(dāng)makefile被
顯式指定為目標(biāo)時(shí),’-t’選項(xiàng)也適用于它們。
這樣’make –f mfile –n mfile foo’會(huì)讀入’mfile’,打印出更新
執(zhí)行的命令,’foo’的命令是當(dāng)前的’mfile’中的內(nèi)容。
2.5重載makefile
可以使用’include’指令來(lái)包含其它
makefile,增加目標(biāo)的變量定義。然而,make不允許同一個(gè)目標(biāo)有不同的命令,有其它的途徑可以達(dá)到目的。
假設(shè)
有’makefile’ 和’mfile’,’makfile’要包含’mfile’,但都有對(duì)于目標(biāo)’foo’的規(guī)則。這是可以在’makefile’
中寫一條匹配任意模式的規(guī)則,指明當(dāng)make在’makefile’中未找到目標(biāo)時(shí),搜索’mfile’:
foo:
frobnicate >; foo
%: force
@$(MAKE) -f mfile $@
force: ;
當(dāng)
執(zhí)行’make foo’時(shí),make找到’makefile’,執(zhí)行命令’ frobnicate >; foo’;執(zhí)行’make bar’
時(shí),在’makefile’中未找到相應(yīng)的規(guī)則,這時(shí)模式規(guī)則適用,執(zhí)行命令’make –f mfile bar’,’makefile’中未提及的其
它目標(biāo)也是類似的。
這種方法之所有工作是因?yàn)槟J揭?guī)則的模式是’%’,可以匹配任何的目標(biāo);這條規(guī)則的依賴是’force’,保證即使目標(biāo)存在命
令也會(huì)執(zhí)行;’force’規(guī)則的命令為空防止’make’為其搜索隱式規(guī)則-這樣會(huì)導(dǎo)致依賴循環(huán)。
3規(guī)則
makefile中的規(guī)則描述
如何生成特定的文件,即規(guī)則的目標(biāo)。規(guī)則列出了目標(biāo)的依賴文件,指定生成或更新目標(biāo)的命令。
規(guī)則的次序是不重要的,除非是確定缺省目標(biāo):缺省目標(biāo)
是第一個(gè)makefile中的第一個(gè)規(guī)則;如果第一個(gè)規(guī)則有多個(gè)目標(biāo),第一個(gè)目標(biāo)是缺省的。有兩個(gè)例外:以’.’開頭的目標(biāo)不是缺省目標(biāo);模式規(guī)則對(duì)缺省
目標(biāo)沒(méi)有影響。
通常我們所寫的地一個(gè)規(guī)則是編譯整個(gè)或makefile中指定的所有程序。
3.1例子
foo.o : foo.c defs.h # module for twiddling the frobs
cc -c -g foo.c
它
的目標(biāo)是’foo.o’,依賴于’foo.c’和’defs.h’,有一個(gè)命令’cc –c –g foo.c’。命令行以TAB字符開始標(biāo)識(shí)它是一個(gè)命
令。
這條規(guī)則說(shuō)明兩件事:
;如何決定’foo.o’是舊的:如果它不存在,或者’foo.c’或
者’defs.h’比它新。
;如何更新’foo.o’文件:通過(guò)運(yùn)行’cc’程序。命令未提
及’defs.h’,擔(dān)可以猜想’foo.c’包含了它,這是’defs.h’被置于依賴關(guān)系中的理由。
3.2規(guī)則的語(yǔ)法
語(yǔ)法如下:
TARGETS : DEPENDENCIES
COMMAND
...
或
者
TARGETS : DEPENDENCIES ; COMMAND
COMMAND
...
TARGETS
是以空格隔開的文件名,統(tǒng)配符可以使用。通常一個(gè)規(guī)則只有一個(gè)目標(biāo),偶爾也有多個(gè)。
命令行以TAB鍵開始。第一條命令可在依賴關(guān)系的下一行;或者
在同一行,在分號(hào)后面;兩種方式效果相同。
因?yàn)?#8217;$’符號(hào)被用做變量引用,如果要在規(guī)則中使用’$’符號(hào),必須寫兩個(gè):’$$’。可以用’\’符
號(hào)來(lái)分割一個(gè)長(zhǎng)行,這不是必須的,因?yàn)閙ake對(duì)行的長(zhǎng)度沒(méi)有限制。
3.3通配符
規(guī)則中的文件名可以包含統(tǒng)配符,如’*’,’?’。
文
件名前的字符’~’有特殊的含義。單獨(dú)使用,或跟隨一個(gè)’/’,代表用戶的home目錄,比如’~/bin’擴(kuò)展為/home/you/bin’;如
果’~’跟隨一個(gè)單詞,表示單詞指示的那個(gè)用戶的home目錄,如’~john/bin’擴(kuò)展為’/home/john/bin’。
通配符在目
標(biāo),依賴關(guān)系,命令中自動(dòng)擴(kuò)展,其它情況下,統(tǒng)配符的擴(kuò)展除非顯式使用’wildcard’函數(shù)。通配符的特殊意義可以使用’\’符號(hào)關(guān)閉。
例
子:
clean:
rm -f *.o
和
print: *.c
lpr -p $?
touch print
通
配符在定義變量時(shí)并不擴(kuò)展,例如:
objects = *.o
則objects的值是字符串’*.o’;但是如果你將objects用于
目標(biāo),依賴或命令中,擴(kuò)展會(huì)進(jìn)行。要將objects設(shè)置成擴(kuò)展過(guò)的內(nèi)容,使用:
objects := $(wildcard *.o)
3.3.1
通配符的缺陷
這是一個(gè)使用通配符的例子,但結(jié)果不是你所期望的。假設(shè)可執(zhí)行文件’foo’是從當(dāng)前目錄中的所有’.o’文件生成的:
objects = *.o
foo : $(objects)
cc -o foo $(CFLAGS) $(objects)
objects
變量的值是字符串’*.o’。通配符擴(kuò)展在規(guī)則’foo’中進(jìn)行,于是所有存在的’.o’文件成為’foo’的依賴而且在需要時(shí)重新編譯。
但如果
刪除了所有的’.o’文件呢?當(dāng)通配符不匹配任何文件時(shí),一切都保持原樣:則’foo’依賴于一個(gè)叫做’*.o’的文件;由于這個(gè)文件不大可能存
在,’make’程序會(huì)報(bào)告一個(gè)無(wú)法生成’*.o’文件的錯(cuò)誤,這不是期待的結(jié)果。
實(shí)際上可以用通配符獲得期望結(jié)果,但是需要復(fù)雜的技術(shù),包
括’wildcard’函數(shù)和字符串替換函數(shù)。
3.3.2wildcard函數(shù)
通配符自動(dòng)在規(guī)則中進(jìn)行。但是在變量賦值的和函數(shù)的參數(shù)中
通配符不會(huì)擴(kuò)展,如果在這些情況下需要通配符擴(kuò)展,必須使用’wildcard’函數(shù)。語(yǔ)法如下:
$(wildcard PATTERN...)
這
個(gè)在makefile任何地方出現(xiàn)的字符串,會(huì)被匹配任何一個(gè)文件名格式的以空格隔開的現(xiàn)有文件列表替換。如果沒(méi)有任何文件匹配一個(gè)模式,這個(gè)模式
從’wildcard’的輸出中忽略,注意,這和上述的通配符的處理是不一樣的。
‘wildcard’函數(shù)的一個(gè)功能是找出目錄中所有的’.c’
文件:
$(wildcard *.c)
可以通過(guò)替換后綴’.c’為’.o’從C文件列表得到目標(biāo)文件的列表:
$(patsubst %.c,%.o,$(wildcard *.c))
這
樣,上節(jié)中的makefile改寫為:
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
這
個(gè)makefile利用了編譯C程序的隱含規(guī)則,所以不需要對(duì)編譯寫出顯式的規(guī)則。(’:=’是’=’的一個(gè)變體)
注意:’PATTERN’是大
小寫敏感的。
3.4目錄搜索
對(duì)于大的系統(tǒng),通常將源文件和目標(biāo)文件放在不同的目錄中。目錄搜索功能可以讓make自動(dòng)在多個(gè)目錄中搜尋依
賴文件,當(dāng)你將文件重新分布是,不需要改變規(guī)則,更改搜索路徑即可。
3.4.1‘VPATH’
make變量’VPATH’列出make應(yīng)
當(dāng)搜索的目錄列表。很多情況下,當(dāng)前目錄不包含依賴文件,’VPATH’描述一個(gè)對(duì)所有文件的搜索列表,包含那些是規(guī)則的目標(biāo)的文件。
如果一個(gè)目
標(biāo)或者依賴文件在當(dāng)前目錄沒(méi)找到的話,’make’在’VPATH’中列出的目錄中查找同名的文件。如果找到的話,那個(gè)文件成為依賴文件;規(guī)則可以象這些
文件在當(dāng)前目錄中一樣來(lái)使用他們。
在’VPATH’變量中,目錄名以冒號(hào)或空格隔開;目錄列出的順序決定make查找的順序。(注:在
pSOSystem 2.5移植到Win32的GNU make目錄名必須使用分號(hào)隔開,以下均簡(jiǎn)稱Win32 GNU make)。舉例說(shuō)明:
VPATH = src:../headers 則
規(guī)則
foo.o : foo.c
被解釋為
foo.o : src/foo.c
假設(shè)’foo.c’在當(dāng)前目錄不存在,
在’src’目錄中可以找到。
3.4.2選擇性搜索
與’VPATH’變量相似但更具選擇性的是’vpath’指令(注意是小寫),可以指
定對(duì)于符合特定模式文件的查找路徑。這樣可以為不同類型的文件指定不同的搜索路徑。
‘vpath’指令共有三中形式:
;‘vpath PATTERN DIRECTORIES’
為
匹配PATTERN的文件名指定搜索路徑DIRECTORIES,目錄的分隔和’VPATH’的相同
;‘vpath PATTERN’
清
除為匹配PATTERN的文件名指定的搜索路徑
;‘vpath’
清除所有以前用’vpath’指
定的搜索路徑
‘vpath’的模式是包含’%’的字符串:這個(gè)字符串必須匹配需要搜索的依賴文件名,’%’字符匹配0個(gè)或多個(gè)任意字符。例
如:’%.h’匹配任何以’.h’結(jié)尾的文件(如果沒(méi)有%,則PATTERN必須和依賴文件完全一致,這種用法不太多)。
當(dāng)當(dāng)前目錄中不存在依賴
文件時(shí),如果’vpath’中的PATTERN匹配依賴文件名,則指令中DIRECTORIES列出的目錄和’VPATH’中同樣處理。舉例:
vpath %.h ../headers
告
訴make在當(dāng)前目錄中未找到的’.h’文件在../headers目錄中查找。
如果多個(gè)’vapth’的模式匹配依賴文件名,make將逐一處
理,在所有指定的目錄中搜索。Make按照’vapth’在makefile中的次序;來(lái)處理它們,多個(gè)相同模式的’vapth’是相互獨(dú)立的。
vpath %.c foo
vpath % blish
vpath %.c bar
將
按照’foo’,‘blish’,’bar’的次序查找’.c’文件。而
vpath %.c foo:bar
vpath % blish
按
照’foo’,’bar’,’blish’的順序搜索。
3.4.3使用自動(dòng)變量
目錄搜索的結(jié)果并不改變規(guī)則中的命令:命令按原樣被執(zhí)行。
因此,必須寫出與目錄搜索功相適應(yīng)的命令。這可以通過(guò)使用’$^’這樣的自動(dòng)變量來(lái)完成。’$^’表示規(guī)則中的所有依賴文件,包含它們所在的目錄名(參見
目錄搜索);’$@’表示目標(biāo)。例如:
foo.o : foo.c
cc -c $(CFLAGS) $^ -o $@
通
常情況下,依賴文件也包含頭文件,但命令中并不提及這些文件:變量’$<’表示第一個(gè)依賴文件:
VPATH = src:../headers
foo.o : foo.c defs.h hack.h
cc –c $(CFLAGS) $< -o $@
3.4.4
目錄搜索和隱含規(guī)則
使用’VPATH’和’vpath’指定目錄搜索也會(huì)影響隱含規(guī)則。例如:文件’foo.o’沒(méi)有顯式規(guī)則,make會(huì)考慮隱
式規(guī)則:如果’foo.c’存在則編譯它;如果這個(gè)文件不存在,則在相應(yīng)的目錄中查找;如果’foo.c’在任一的目錄中存在,則C編譯的隱式規(guī)則被應(yīng)
用。
隱式規(guī)則的命令使用自動(dòng)變量通常是必要的,這樣無(wú)需其它努力即可以使用目錄搜索得到的文件名。
3.5PHONY目標(biāo)
Phony
目標(biāo)并非實(shí)際的文件名:只是在顯式請(qǐng)求時(shí)執(zhí)行命令的名字。有兩種理由需要使用phony目標(biāo):避免和同名文件沖突,改善性能。
如果編寫一個(gè)規(guī)則,
并不產(chǎn)生目標(biāo)文件,則其命令在每次make該目標(biāo)時(shí)都執(zhí)行。例如:
clean:
rm *.o temp
因?yàn)?#8217;rm’命令并不
產(chǎn)生’clean’文件,則每次執(zhí)行’make clean’的時(shí)候,該命令都會(huì)執(zhí)行。如果目錄中出現(xiàn)了’clean’文件,則規(guī)則失效了:沒(méi)有依賴文
件,文件’clean’始終是最新的,命令永遠(yuǎn)不會(huì)執(zhí)行;為避免這個(gè)問(wèn)題,可使用’.PHONY’指明該目標(biāo)。如:
.PHONY : clean
這
樣執(zhí)行’make clean’會(huì)無(wú)視’clean’文件存在與否。
已知phony目標(biāo)并非是由其它文件生成的實(shí)際文件,make會(huì)跳過(guò)隱含規(guī)則
搜索。這就是聲明phony目標(biāo)會(huì)改善性能的原因,即使你并不擔(dān)心實(shí)際文件存在與否。完整的例子如下:
.PHONY : clean
clean :
rm *.o temp
phony
目標(biāo)不應(yīng)是真正目標(biāo)文件的依賴。如果這樣,每次make在更新此文件時(shí),命令都會(huì)執(zhí)行。只要phony目標(biāo)不是真正目標(biāo)的依賴,規(guī)則的命令只有在指定此目
標(biāo)時(shí)才執(zhí)行。
Phony目標(biāo)可以有依賴關(guān)系。當(dāng)一個(gè)目錄中有多個(gè)程序是,將其放在一個(gè)makefile中會(huì)更方便。因?yàn)槿笔∧繕?biāo)是
makefile中的第一個(gè)目標(biāo),通常將這個(gè)phony目標(biāo)叫做’all’,其依賴文件為各個(gè)程序:
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
這
樣,使用’make’將可以將三個(gè)程序都生成了。
當(dāng)一個(gè)phony目標(biāo)是另一個(gè)的依賴,其作用相當(dāng)于子程序,例如:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
3.6FORCE
目標(biāo)
當(dāng)規(guī)則沒(méi)有依賴關(guān)系也沒(méi)有命令,而且其目標(biāo)不是存在的文件名,make認(rèn)為此規(guī)則運(yùn)行時(shí)這個(gè)目標(biāo)總是被更新。這意味著如果規(guī)則依賴于此目標(biāo),
其命令總是被執(zhí)行。
clean: FORCE
rm $(objects)
FORCE:
例中目
標(biāo)’FORCE’滿足這種特殊條件,這樣依賴于它的目標(biāo)’clean’被強(qiáng)制執(zhí)行其命令。名字’FORCE’沒(méi)有特殊含義,只不過(guò)通常這樣用而已。這種方
式使用’FORCE’和’.PHONY : clean’效果相同。使用’.PHONY’更加明確高效,擔(dān)不是所有的’make’都支持;這樣許多
makefile中使用了’FORCE’。
3.7空目標(biāo)
空目標(biāo)(empty target)是phony目標(biāo)的變種:用來(lái)執(zhí)行顯式請(qǐng)求的
一個(gè)動(dòng)作。和phony目標(biāo)不同的是:這個(gè)目標(biāo)文件可以真實(shí)存在,擔(dān)文件的內(nèi)容無(wú)關(guān)緊要,通常是空的。空目標(biāo)文件的目的是利用其最后修改時(shí)間來(lái)記錄命令最
近一次執(zhí)行的時(shí)間,這是通過(guò)使用’touch’命令更新目標(biāo)文件來(lái)達(dá)到的。
print: foo.c bar.c
lpr -p $?
touch print
利
用這條規(guī)則,執(zhí)行’make print’時(shí)如果自上次’make print’之后任一文件改變了,’lpr’命令會(huì)執(zhí)行。自動(dòng)變量’$?’是為了只打
印出那些變化了的文件。
3.8內(nèi)建的特殊目標(biāo)
某些名字作為目標(biāo)存在時(shí)有特殊含義。
★.PHONY該目標(biāo)的依賴被認(rèn)為是phony
目標(biāo),處理這些目標(biāo)時(shí),命令無(wú)條件被執(zhí)行,不管文件名是否存在及其最后修改時(shí)間
★.SUFFIXES該目標(biāo)的依賴被認(rèn)為是一個(gè)后綴列表,在檢查后
綴規(guī)則時(shí)使用
★.DEFAULT該目標(biāo)的規(guī)則被使用在沒(méi)有規(guī)則(顯式的或隱含的)的目標(biāo)上。如果’DEFAULT’命令定義了,則對(duì)所有不是規(guī)則
目標(biāo)的依賴文件都會(huì)執(zhí)行該組命令
★.PRECIOUS該目標(biāo)的依賴文件會(huì)受到特別對(duì)待:如果make被kill或命令的執(zhí)行被中止,這些目標(biāo)并不
刪除;而且如果該目標(biāo)是中間文件,在不需要時(shí)不會(huì)被刪除。可以將隱含規(guī)則的目標(biāo)模式(如%.o)做為’.PRECIOUS’的依賴文件,這樣可以保存這些
規(guī)則產(chǎn)生的中間文件。
★.INTERMEDIATE該目標(biāo)的依賴文件被當(dāng)作中間文件;如果該目標(biāo)沒(méi)有依賴文件,則makefile中所有的目標(biāo)文
件均被認(rèn)為是中間文件。
★.IGNORE在執(zhí)行該目標(biāo)的依賴規(guī)則的命令時(shí),make會(huì)忽略錯(cuò)誤,此規(guī)則本身的命令沒(méi)有意義。如果該規(guī)則沒(méi)有依賴關(guān)
系,表示忽略所有命令執(zhí)行的錯(cuò)誤,這種用法只是為了向后兼容;由于會(huì)影響到所有的命令,所以不是特別有用,推薦使用其它更有選擇性忽略錯(cuò)誤的方法。
★.SILENT
在執(zhí)行該目標(biāo)的依賴規(guī)則的命令時(shí),make并不打印命令本身。該規(guī)則的命令沒(méi)有意義。在’.SILIENT’沒(méi)有依賴關(guān)系時(shí),表示執(zhí)行makefile中
的所有命令都不會(huì)打印,該規(guī)則只是為了向后兼容提供的。
★.EXPORT_ALL_VARIABLES只是作為一個(gè)目標(biāo)存在,指示make將所有
變量輸出到子進(jìn)程中。
定義的隱含規(guī)則的后綴作為目標(biāo)時(shí),也認(rèn)為它是特殊目標(biāo);兩個(gè)后綴的連接也是一樣,比如’.c.o’。這些目標(biāo)是后綴規(guī)則,一
中定義隱式規(guī)則的過(guò)時(shí)方法(但仍然廣泛使用)。后綴通常以’.’開始,所以特殊目標(biāo)也以’.’開始。
3.9一個(gè)規(guī)則多個(gè)目標(biāo)
一條有多個(gè)目
標(biāo)的規(guī)則和寫多條規(guī)則,每條一個(gè)目標(biāo)作用是等同的。同樣的命令應(yīng)用于所有目標(biāo),但其效用會(huì)因?qū)?shí)際目標(biāo)以’$@’代替而不同。規(guī)則中所有目標(biāo)的依賴關(guān)系是
一樣的。
這在兩種情況下有用:
★只有依賴關(guān)系,不需要命令。例如:
kbd.o command.o files.o: command.h
★
所有的目標(biāo)同樣的命令。命令不需要完全相同,因?yàn)樵诿钪锌梢允褂?#8217;$@’:
bigoutput littleoutput : text.g
generate text.g -$(subst output,,$@) >; $@
和
bigoutput : text.g
generate text.g -big >; bigoutput
littleoutput : text.g
generate text.g -little >; littleoutput
等
同。這里假設(shè)程序’generate’產(chǎn)生兩種輸出:一種使用’-big’選項(xiàng),一種使用’-little’選項(xiàng)。如果想象使用’$@’變化命令那樣來(lái)變
化依賴關(guān)系,不能通過(guò)多目標(biāo)的普通規(guī)則實(shí)現(xiàn),但是可以通過(guò)模式規(guī)則來(lái)實(shí)現(xiàn)。
3.10一個(gè)目標(biāo)多條規(guī)則
一個(gè)文件可以是多條規(guī)則的目標(biāo),所有
規(guī)則的依賴關(guān)系被合并。如果目標(biāo)比任一個(gè)依賴文件舊,命令被執(zhí)行。
一個(gè)文件只能有一組命令執(zhí)行。如果多個(gè)規(guī)則對(duì)于同一個(gè)文件都給出了命
令,make使用最后一組并打印錯(cuò)誤信息(特殊情況:如果文件名以’.’開始,并不打印錯(cuò)誤信息,這一點(diǎn)是為了和其它make兼容)。沒(méi)有任何理由需要將
makefile寫成這樣,這是make給出錯(cuò)誤信息的理由。
一條只有依賴關(guān)系的附加規(guī)則可以一次給出許多文件的附加依賴文件。例
如’objects’變量表示系統(tǒng)中編譯器的所有輸出.,說(shuō)明當(dāng)’config.h’更改時(shí)所有文件必須重做的簡(jiǎn)單方法如下:
objects = foo.o bar.o
foo.o : defs.h
bar.o : defs.h test.h
$(objects) : config.h
不
用改變實(shí)際目標(biāo)文件生成的規(guī)則,這條規(guī)則可以在需要增刪附加的依賴關(guān)系時(shí)插入或提出。另一個(gè)訣竅是附加的依賴關(guān)系可以用變量表示,在make執(zhí)行時(shí),可以
給變量賦值:
extradeps=
$(objects) : $(extradeps)
當(dāng)命令
`make extradeps=foo.h'執(zhí)行時(shí)會(huì)認(rèn)為’foo.h’是每個(gè)目標(biāo)文件的依賴文件,但簡(jiǎn)單的’make’命令不是這樣。
3.11
靜態(tài)模式規(guī)則
靜態(tài)模式規(guī)則(static pattern rules)可以指定多個(gè)目標(biāo),并且使用目標(biāo)名字來(lái)建議依賴文件的名字;比普通多目標(biāo)
規(guī)則更通用因?yàn)椴恍枰蕾囮P(guān)系是相同的:依賴關(guān)系必須類似但不需要相同。
3.11.1語(yǔ)法
TARGETS ...: TARGET-PATTERN: DEP-PATTERNS ...
COMMANDS
...
TARGETS
列表指出規(guī)則應(yīng)用的目標(biāo),可以包含通配符,于普通規(guī)則的目標(biāo)相同。TARGET-PATTERN和DEP-PATTERNS來(lái)表明目標(biāo)的依賴關(guān)系如何計(jì)
算:匹配TARGET-PATTERN的目標(biāo)從名字中抽出一部分,叫做詞干(stem),詞干被替換到DEP-PATTERNS來(lái)形成依賴文件名。
每
個(gè)模式通常包含一個(gè)’%’字符。當(dāng)TARGET-PATTERN匹配一個(gè)目標(biāo)時(shí),’%’字符可以匹配目標(biāo)名中的任何部分;這部分即是詞干,模式的其余部分
必須完全匹配。例如’foo.o’匹配’%.o’,’foo’是詞干;目標(biāo)’foo.c’和’foo.out’并不匹配這個(gè)模式。
目標(biāo)的依賴文件
名通過(guò)將DEP-PATTERNS中的’%’替換為詞干形成:如果依賴模式為’%.c’,在替換詞干’foo’可以得到’foo.c’。依賴模式中不包
含’%’也是合法的,此依賴文件對(duì)所有的目標(biāo)均有效。
如果需要在模式規(guī)則中使用’%’字符,必須在其前面加’\’字符,如果’%’前的’\’字符
是有實(shí)際意義的,必須在其前面加’\’,其它的’\’不必如此處理。如’the\%weird\\%pattern\\’在有效的’%’前
是’the%weird\’,其后是’pattern\\’。最后的’\\’保持原樣是因?yàn)槠洳⒉挥绊?#8217;%’字符。
以下例子從相應(yīng)的’.c’文件
編譯’foo.o’和’bar.o’:
objects = foo.o bar.o
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
每
個(gè)目標(biāo)必須匹配目標(biāo)模式,對(duì)于不匹配的目標(biāo)會(huì)給出警告。如果列表中只有部分文件匹配模式,可以使用filter函數(shù)移去不匹配的文件名:
files = foo.elc bar.o lose.o
$(filter %.o,$(files)): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
$(filter %.elc,$(files)): %.elc: %.el
emacs -f batch-byte-compile $<
例
子中`$(filter %.o,$(files))' 結(jié)果是
`bar.o lose.o’; `$(filter %.elc,$(files))' 的結(jié)果是`foo.elc'。以下例子說(shuō)明’$*’的使用:
bigoutput littleoutput : %output : text.g
generate text.g -$* >; $@
命
令`generate'執(zhí)行時(shí),’$*’擴(kuò)展為詞干’big’或’little’。
3.11.2靜態(tài)模式規(guī)則和隱式規(guī)則
靜態(tài)模式規(guī)則和隱
式規(guī)則在作為模式規(guī)則是具有很多共同點(diǎn),都有目標(biāo)模式和構(gòu)造依賴文件名的模式,不同之處在于make決定何時(shí)應(yīng)用規(guī)則的方法。
隱式規(guī)則可應(yīng)用于匹
配其模式的任何目標(biāo),但只限于沒(méi)有指定命令的目標(biāo),如果有多條可應(yīng)用的隱式規(guī)則,只有一條被使用,取決于規(guī)則的順序。
反之,靜態(tài)模式規(guī)則適用于規(guī)
則中明確目標(biāo)列表,不適用于其它目標(biāo)且總是適用于指定的每個(gè)目標(biāo)。如果有兩條沖突的規(guī)則,且都有命令,這是一個(gè)錯(cuò)誤。
靜態(tài)模式規(guī)則比隱式規(guī)則優(yōu)越
之處如下:
★可為一些不能按句法分類,但可以顯式列出的文件重載隱式規(guī)則
★不能判定目錄中的精確內(nèi)容,一些無(wú)關(guān)的文件可能導(dǎo)致make適
用錯(cuò)誤的隱式規(guī)則;最終結(jié)果可能依賴于隱式規(guī)則的次序。適用靜態(tài)模式規(guī)則時(shí),這種不確定性是不存在的:規(guī)則適用于明確指定的目標(biāo)。
3.12雙冒號(hào)
規(guī)則
雙冒號(hào)規(guī)則(Double-colon rules)的目標(biāo)后是’::’而不是’:’,當(dāng)一個(gè)目標(biāo)出現(xiàn)在多條規(guī)則中時(shí),其處理和普通規(guī)則的處
理不同。
當(dāng)一個(gè)目標(biāo)出現(xiàn)在多條規(guī)則中時(shí),所有規(guī)則必須是相同類型的:都是普通的或者都是雙冒號(hào)的。如果是雙冒號(hào),規(guī)則之間相互獨(dú)立;如果目標(biāo)需要
更新,則規(guī)則的命令被執(zhí)行;結(jié)果可能是沒(méi)有執(zhí)行,或者執(zhí)行了其中一些,或者所有的規(guī)則都執(zhí)行了。
同一目標(biāo)的雙冒號(hào)規(guī)則事實(shí)是完全孤立的,每條規(guī)則
被被單獨(dú)處理,就象不同目標(biāo)的規(guī)則一樣;規(guī)則按照在makefile中出現(xiàn)的次序被處理,此類規(guī)則真正有意義的是那些于命令執(zhí)行次序無(wú)關(guān)的。
這種
規(guī)則有時(shí)比較晦澀不是特別有用;它提供了一種機(jī)制:通過(guò)不同依賴文件的更新來(lái)對(duì)目標(biāo)進(jìn)行不同的處理,這種情形很罕見。每個(gè)這種規(guī)則應(yīng)當(dāng)提供命令,如果沒(méi)
有,適用的隱式規(guī)則將使用。
3.13自動(dòng)生成依賴關(guān)系
在makefile中,許多規(guī)則都是一些目標(biāo)文件依賴于一些頭文件。例
如:’main.c’ 通過(guò)’#include’使用’defs.h’,這樣規(guī)則:
main.o: defs.h
告訴make
在’defs.h’變化時(shí)更新’main.o’。在程序比較大時(shí),需要寫許多這樣的規(guī)則;而且當(dāng)每次增刪’#include’時(shí),必須小心的更新
makefile。許多現(xiàn)代的編譯器可以幫你寫這些規(guī)則,通常這是通過(guò)編譯器的’-M’選項(xiàng),例如命令:
cc –M main.c
輸出以
下內(nèi)容:
main.o : main.c defs.h
這樣就不必寫這些規(guī)則,有編譯器代勞了。
注意這樣的依賴關(guān)系中提
及’main.o’,不會(huì)被隱式規(guī)則認(rèn)為是中間文件,這意味這make在使用過(guò)它之后不會(huì)將其刪除。使用老的’make’程序時(shí),習(xí)慣做法是使
用’make depend’命令利用編譯器的功能產(chǎn)生依賴關(guān)系,該命令會(huì)產(chǎn)生一個(gè)’depend’文件包含所有自動(dòng)產(chǎn)生的依賴關(guān)系,然后在
makefile中使用’include’將其讀入。
使用GNU的make時(shí),重新生成makefile的功能使得這種做法變得過(guò)時(shí):從不需要顯
式請(qǐng)求更新依賴關(guān)系,因?yàn)樗偸侵匦律扇魏芜^(guò)時(shí)的makefile。
自動(dòng)依賴關(guān)系生成推薦的做法是對(duì)每個(gè)源文件做一個(gè)makefile。對(duì)每個(gè)
源文件’NAME.c’,有一個(gè)makefile ’NAME.d’,其中列出了目標(biāo)文件’NAME.o’依賴的所有文件,這樣在源文件更新時(shí),需要掃描
來(lái)產(chǎn)生新的依賴關(guān)系。例子是一個(gè)從’NAME.c’產(chǎn)生依賴關(guān)系文件’NAME.d’的模式規(guī)則:
%.d: %.c
$(SHELL) -ec '$(CC) -M $(CPPFLAGS) $< \
| sed '\''s/\($*\)\.o[ :]*/\1 $@/g'\'' >; $@'
-e
選項(xiàng)是當(dāng)$(CC)命令失敗時(shí)(exit狀態(tài)非0),shell立刻退出。通常shell的返回值是管道中最后一條命令(sed)的返回值,這樣make
不會(huì)注意到編譯器出錯(cuò)。
使用GNU的C編譯器時(shí)(gcc),可以用’-MM’選項(xiàng)來(lái)代替’-M’選項(xiàng),這樣省略系統(tǒng)頭文件的依賴關(guān)系。’sed’
命令的目的是將
main.o : main.c defs.h
轉(zhuǎn)換為
main.o main.d : main.c defs.h
這
樣使得每個(gè)’.d’文件依賴于’.o’文件相應(yīng)源文件和頭文件,make則可以在原文間或頭文件變化時(shí)更新依賴關(guān)系文件。
如果定義了生成’.d’
文件的規(guī)則,可以使用’include’指令來(lái)讀入所有的文件:
sources = foo.c bar.c
include $(sources:.c=.d)
例
中使用替換變量來(lái)將源文件列表’ foo.c bar.c’轉(zhuǎn)換為依賴關(guān)系文件的列表。因?yàn)?#8217;.d’文件和其它文件一樣,不需要更多工作,make會(huì)在需
要時(shí)重新生成它們。
4編寫命令
規(guī)則的命令是由一一執(zhí)行的shell命令組成。除了以分號(hào)隔開寫在依賴關(guān)系后的命令,每個(gè)命令行必須以
tab字符開始空行和注釋行可以出現(xiàn)在命令行中,處理時(shí)被忽略(注意:以tab字符開始的空行不是’空’行,是一條空命令)。
可以在命令中使用任
何程序,但這些程序是由$(SHELL)來(lái)執(zhí)行的。
4.1回顯
通常make打印出要執(zhí)行的命令,稱之為回顯,這和親自敲命令的現(xiàn)象是一樣
的。當(dāng)行之前有’@’字符時(shí),命令不再回顯,字符’@’在傳遞給shell前丟棄。典型的用法是只對(duì)打印命令有效,比如’echo’命令:
@echo About to make distribution files
當(dāng)
make使用’-n’或’—just-print’選項(xiàng)時(shí),顯示要發(fā)生的一切,但不執(zhí)行命令。只有在這種情況下,即使命令以’@’開始,命令行仍然顯示出
來(lái)。這個(gè)選項(xiàng)對(duì)查看make實(shí)際要執(zhí)行的動(dòng)作很有用。
‘-s’或’—silent’選項(xiàng)阻止make所有回顯,就象所有命令以’@’開始一樣;一
條沒(méi)有依賴關(guān)系的’.SILENT’規(guī)則有相同的作用,但是’@’更加靈活。
4.2執(zhí)行
在需要執(zhí)行命令更新目標(biāo)時(shí),make為每一行創(chuàng)建
一個(gè)子shell來(lái)執(zhí)行。這意味著諸如為進(jìn)程設(shè)置局部變量的shell命令’cd’(改變進(jìn)程的當(dāng)前目錄)不會(huì)影響以后的命令。如果需要’cd’影響下一
個(gè)命令,將它們放在一行上用分號(hào)隔開,這樣make認(rèn)為是一條命令傳遞給shell程序(注意:這需要shell支持):
foo : bar/lose
cd bar; gobble lose >; ../foo
另
一個(gè)形式使用續(xù)行符:
foo : bar/lose
cd bar; \
gobble lose >; ../foo
shell
程序的名字是通過(guò)’SHELL’變量來(lái)取得的。
(*UNIX)不象大多數(shù)變量,’SHELL’變量不是通過(guò)環(huán)境來(lái)設(shè)置的(即需要在
makefile中設(shè)置),因?yàn)?#8217;SHELL’環(huán)境是個(gè)人選擇的,如果不同人的選擇會(huì)影響makefile的功能的話,這樣很糟糕。
4.3并行執(zhí)
行
GNU make可以一次執(zhí)行幾條命令。通常make一次執(zhí)行一條命令,等待其返回,再執(zhí)行下一條。使用’-j’或’—jobs’可以同時(shí)執(zhí)行
多條命令。如果’-j’后梗一個(gè)正數(shù),表示一次可以執(zhí)行的命令條數(shù);如果’-j’之后沒(méi)有參數(shù),則不限制可執(zhí)行的命令數(shù)。缺省的數(shù)量是一。
一個(gè)討
厭的問(wèn)題是如果同時(shí)執(zhí)行多條命令,它們的輸出會(huì)混在一起;另一個(gè)問(wèn)題是兩個(gè)進(jìn)程不能從同一個(gè)設(shè)備獲得輸入。
4.4錯(cuò)誤
每條shell命令
返回時(shí),make會(huì)檢查其返回狀態(tài)。如果命令執(zhí)行成功,則下一條命令被執(zhí)行,最后一條命令執(zhí)行完后,規(guī)則執(zhí)行結(jié)束。
如果有錯(cuò)誤(返回非0狀
態(tài)),make放棄當(dāng)前規(guī)則,也可能是所有規(guī)則。
有時(shí)候命令執(zhí)行錯(cuò)誤并不是問(wèn)題,比如使用’mkdir’命令確保目錄存在:如果目錄一存在,
則’mkdir’會(huì)報(bào)告錯(cuò)誤,但仍希望make繼續(xù)。
要忽略命令的錯(cuò)誤,在命令之前使用’-‘字符,’-‘字符在傳遞給shell之前被丟棄:
clean:
-rm -f *.o
如
果使用’-i’或’—ignore-errors’選項(xiàng),make會(huì)忽略所有命令產(chǎn)生的錯(cuò)誤;一條沒(méi)有依賴關(guān)系的’.IGNORE’規(guī)則有相同的作用,
但’-‘更靈活。
在忽略錯(cuò)誤時(shí),make將錯(cuò)誤也認(rèn)為是成功,只是通知你命令的退出狀態(tài)和和錯(cuò)誤被忽略。如果make并未告知忽略錯(cuò)誤,在錯(cuò)誤發(fā)
生時(shí),表明該目標(biāo)不能成功更新,直接或間接依賴于此的目標(biāo)當(dāng)然也不能成功;這些目標(biāo)的命令不會(huì)被執(zhí)行,因?yàn)槠湎葲Q條件不滿足。
通常make會(huì)立即
以非0狀態(tài)退出。然而,如果給定’-k’或’—keep-going’選項(xiàng),make在退出前會(huì)處理其它的依賴關(guān)系,進(jìn)行必要的更新。例如,在編譯一個(gè)目
標(biāo)文件遇到錯(cuò)誤,’make -k’會(huì)繼續(xù)編譯其它的目標(biāo)文件。
通常認(rèn)為你的目的是更新指定的目標(biāo),當(dāng)make知道這是不可能時(shí),會(huì)立即報(bào)告失
敗;’-k’選項(xiàng)指示真正目的是測(cè)試更新程序的更多可能性:在編譯之前找出更多不相關(guān)的問(wèn)題。
如果命令失敗了,假設(shè)它更新的目標(biāo)文件,這個(gè)文件是
不完整的不能使用-至少不是完全更新的。但文件的最后修改時(shí)間表明停已經(jīng)是最新的,下一次make運(yùn)行時(shí),不會(huì)再更新這個(gè)文件。這種情況和命令被kill
相同;則通常情況下在命令失敗時(shí)將目標(biāo)刪除是正確的;當(dāng)’.DELETE_ON_ERROR’是目標(biāo)時(shí)make幫你做這件事。雖然你總是希望make這么
做,但這不是過(guò)去的習(xí)慣;所以必須顯式要求make這樣做(其它的make自動(dòng)這樣做)。
4.5中斷make
如果make執(zhí)行命令時(shí)遇到
錯(cuò)誤,可能會(huì)刪除命令更新的目標(biāo)文件: make檢查文件的修改時(shí)間是否變化。刪除目標(biāo)的目的是確保make下次執(zhí)行時(shí)重新生成它。為什么這樣做?假設(shè)在
編譯器運(yùn)行時(shí)按了’Ctrl-c’,此時(shí)編譯器寫生成目標(biāo)文件’foo.o’。’Ctrl-c’ kill了編譯器,留下一個(gè)不完整的文件,但它的修改時(shí)
間比源文件’foo.c’新;此時(shí)make也受到’Ctrl-c’信號(hào)刪除這個(gè)不完整的文件,如果make不這樣做,下次make運(yùn)行時(shí)認(rèn)
為’foo.o’不需要更新,會(huì)在鏈接時(shí)出現(xiàn)奇怪的錯(cuò)誤。
可以使用’.PRECIOUS’規(guī)則來(lái)防止目標(biāo)文件被刪除。在make更新目標(biāo)時(shí),會(huì)檢
測(cè)其是否為’.PRECIOUS’的依賴,決定在命令出錯(cuò)或中斷時(shí)是否刪除該目標(biāo)。如果你希望目標(biāo)的更新是原子操作,或是用來(lái)記錄修改時(shí)間,或必須一直存
在防止其它類型的錯(cuò)誤,這些理由使得你必須這樣做。
4.6遞歸使用
遞歸使用make就是在makefile中使用make命令。這種技術(shù)
在你將一個(gè)大系統(tǒng)分解為幾個(gè)子系統(tǒng),為每個(gè)自系統(tǒng)提供一個(gè)makefile時(shí)有用處。比如有一個(gè)子目錄’subdir’中有自己的makefile,希望
make在自目錄中運(yùn)行,可以這樣做:
subsystem:
cd subdir; $(MAKE)
或
者
subsystem:
$(MAKE) -C subdir
可以照抄這個(gè);例子來(lái)遞歸使用make
4.6.1‘MAKE’
變量
遞歸的make必須使用’MAKE’變量,不是顯式的make命令:
subsystem:
cd subdir; $(MAKE)
該
變量的值是被調(diào)用的make的名字。在命令中使用’MAKE’有特殊的功能:它改變了`-t' (`--touch'), `-n' (`--just-
print')和`-q' (`--question')選項(xiàng)的含義。使用上例來(lái)考慮’make –t’命令(’-t’選項(xiàng)將目標(biāo)標(biāo)記為最新但不運(yùn)行命
令),更加’-t’選項(xiàng)的功能,該命令將創(chuàng)建一個(gè)’subsystem’文件,實(shí)際希望的操作是運(yùn)
行’cd subdir; make –t’;但這會(huì)執(zhí)行命令,與’-t’的原意不符。
這個(gè)特殊功能做了期望的工作。當(dāng)命令行包
含變量’MAKE’時(shí),選項(xiàng)’-t’,’-n’和’-q’并不適用。不管這些導(dǎo)致不會(huì)執(zhí)行命令的標(biāo)志,包含’MAKE’變量的命令始終會(huì)執(zhí)行。正常
的’MAKEFLAGS’機(jī)制將這些標(biāo)志傳遞到子make,這樣打印命令的請(qǐng)求被傳播到子系統(tǒng)中。
4.6.2傳遞變量到子make
上級(jí)
(top-level)make中的變量可以顯式通過(guò)環(huán)境傳遞到子make中。在子make中,這些變量被缺省定義,但不會(huì)重載子makefile中的定
義除非使用’-e’選項(xiàng)。
為向下傳遞,或輸出變量,make在運(yùn)行命令時(shí)將其加入到環(huán)境變量中;子make,可以使用環(huán)境變量來(lái)初始化變量表。除
非顯式要求,make只輸出初始環(huán)境中或命令行設(shè)置的變量而且變量名只由字母,數(shù)字和下劃線組成。一些shell不能處理有其它字符的環(huán)境變量。
特
殊變量’SHELL’,’MAKEFLAGS’總是輸出,如果’MAKEFILE’變量有值,也會(huì)輸出。Make自動(dòng)通過(guò)’MAKEFLAGS’來(lái)輸出命
令行定義的變量。
如果想要輸出特定變量,使用’export’指令:
export VARIABLE ...
如果要阻止輸出一個(gè)
變量,使用’unexport’指令:
unexport VARIABLE ...
為方便起見,可以在定義變量時(shí)輸出它:
export VARIABLE = value
和
VARIABLE = value
export VARIABLE
作
用相同。
如果要輸出所有的變量,使用’export’指令本身就可以了。
變量’MAKELEVEL’在一級(jí)一級(jí)傳遞時(shí)會(huì)改變,這個(gè)變量的
值是表示嵌套層數(shù)的字符串,頂級(jí)’make’是,變量的值為’0’;子make的值為’1’;子子make的值為’2’,依此類推。
‘MAKELEVEL’
的用途是在條件指令中測(cè)試它,這樣寫出在遞歸運(yùn)行時(shí)和直接運(yùn)行時(shí)表現(xiàn)不同的makefile。