免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
GNU make中文手冊-第四章:Makefile的規(guī)則
GNU make中文手冊-第四章:Makefile的規(guī)則作者: hew  發(fā)布日期:2006-3-21   查看數(shù):149  出自: http://www.linuxsky.net
第四章:Makefile的規(guī)則

--------------------------------------------------------------------------------

本章我們開始討論Makefile的一個重要內(nèi)容,Makefile的規(guī)則。

Makefile中,規(guī)則描述了何種情況下使用什么命令來重建一個特定的文件,此文件被稱為規(guī)則“目標(biāo)”(通常規(guī)則中的目標(biāo)只有一個)。規(guī)則所羅列的其他文件稱為“目標(biāo)”的依賴,而規(guī)則中的命令是用來更新或者創(chuàng)建此規(guī)則的目標(biāo)。

除了makefile的“終極目標(biāo)”所在的規(guī)則以外,其他規(guī)則的順序在makefile文件中沒有意義。“終極目標(biāo)”就是當(dāng)沒有使用make 命令行指定具體目標(biāo)時,make默認的哪一個目標(biāo)。它是makefile文件中第一個規(guī)則的目標(biāo)。如果在makefile中第一個規(guī)則有多個目標(biāo)的話,那 么多個目標(biāo)中的第一個將會被作為make的“終極目標(biāo)”。有兩種情況的例外:1. 目標(biāo)名是以點號“.”開始的其后不存在斜線“/”(“./”被認為是當(dāng)前目錄;“../”被認為是上一級目錄);2. 作為模式規(guī)則的目標(biāo)。此兩種情況的Makefile的第一個目標(biāo)都不會被作為“終極目標(biāo)”來對待。

“終極目標(biāo)”是執(zhí)行make的唯一目的,其所在的規(guī)則作為第一個規(guī)則。而其他的規(guī)則是在完成重建“終極目標(biāo)”的過程中被連帶出來的。所以這些目標(biāo)所在規(guī)則在Makefile中的順序無關(guān)緊要。

因此,我們書寫的makefile的第一個規(guī)則應(yīng)該就是重建整個程序或者多個程序的依賴關(guān)系和執(zhí)行命令的描述。

4.1 一個例子
我們來看一個規(guī)則的例子:



foo.o : foo.c defs.h # module for twiddling the frobs

cc -c -g foo.c



這是一個典型的規(guī)則??吹竭@個例子,大家也許能夠說出這個規(guī)則的各個部分之間的關(guān)系。不過我們還是要把這個例子拿出來討論。目的是讓我們更加明確的理解 Makefile的規(guī)則。本例第一行中,文件“foo.o”是規(guī)則需要重建的文件,而“foo.c”和“defs.h”是重建“foo.o”所要使用的文 件。我們把規(guī)則所需要重建的文件稱為規(guī)則的“目標(biāo)”(foo.o),而把重新目標(biāo)所需要的文件稱為“目標(biāo)”的“依賴”。規(guī)則中的第二行“cc -c -g foo.c”就是規(guī)則的“命令”。它描述了如何使用規(guī)則中的依賴文件重建目標(biāo)。

而且,上面的規(guī)則告訴我們了兩件事:

1. 如何確定目標(biāo)文件是否過期(需要重建目標(biāo)),過期是指目標(biāo)文件不存在或者目標(biāo)文件“foo.o”在時間戳上比依賴文件中的任何一個“foo.c”或者“defs.h”“老”。

2. 如何重建目標(biāo)文件“foo.o”。這個規(guī)則中使用cc編譯器。在命令中沒有明確的使用到依賴文件“defs.h”。我們假設(shè)在源文件“foo.c”中已經(jīng)包含了此頭文件。這也是為什么它作為目標(biāo)依賴出現(xiàn)的原因。

4.2 規(guī)則語法
通常規(guī)則的語法格式如下:



TARGETS : PREREQUISITES

COMMAND

...



或者是這樣:



TARGETS : PREREQUISITES ; COMMAND

COMMAND

...



規(guī)則中“TARGETS”可以是空格分開的多個文件名,也可以是一個標(biāo)簽(執(zhí)行清空的“clean”)。“TARGETS”的文件名可以使用通配符,格式 “A(M)”表示檔案文件(Linux下的靜態(tài)庫.a文件)的成員“M”(關(guān)于靜態(tài)庫的重建可參考 第十章 使用make更新靜態(tài)庫文件)。通常規(guī)則只有一個目標(biāo)文件(建議這么做),偶爾會在一個規(guī)則中需要多個目標(biāo)。

書寫規(guī)則是我們需要注意的幾點:

1. 規(guī)則的命令部分有兩種書寫方式:a. 命令可以和目標(biāo):依賴描述放在同一行。命令在依賴文件列表后并使用分號(;)和依賴文件列表分開。b. 命令在目標(biāo):依賴的描述的下一行,作為獨立的命令行。當(dāng)作為獨立的命令行時此行必須以[Tab]字符開始。在Makefile中,在第一個規(guī)則之后出現(xiàn)的 所有以[Tab]字符開始的行都會被當(dāng)作命令來處理。

2. Makefile中對“$”有特殊的含義(表示變量或者函數(shù)的引用),如果我們的規(guī)則如果需要“$”,需要書寫兩個連續(xù)的(“$$”)。

3. 在前邊我們也提到過,Makefile一個較長的行,可以使用反斜線“\”將其書寫到幾個獨立的物理行上。雖然make對Makefile文本行的最大長 度是沒有限制的,但是還是建議這樣做。不僅書寫方便而且更有利于別人的閱讀(這也是一個程序員修養(yǎng)的體現(xiàn))。

一個規(guī)則告訴“make”兩件事:1. 目標(biāo)在什么情況下已經(jīng)過期; 2. 在需要重建目標(biāo)的時候,怎么樣去重建這個目標(biāo)。目標(biāo)是否過期是由那些使用空格分開的規(guī)則的依賴文件所決定的。當(dāng)目標(biāo)文件不存在或者目標(biāo)文件的最后修改時間 比依賴文件中的任何一個都晚,則目標(biāo)就會被創(chuàng)建或者重建。也就是說執(zhí)行規(guī)則命令行的前提條件是:1。 目標(biāo)文件不存在; 2. 存在一個依賴的最后修改時間比目標(biāo)的最后修改時間晚。規(guī)則的中心思想就是:目標(biāo)文件的內(nèi)容是由依賴文件文件決定,依賴文件的任何一處改動,將導(dǎo)致目前已經(jīng) 存在的目標(biāo)文件的內(nèi)容過期。規(guī)則的命令為重建目標(biāo)提供了方法。這些命令運行在系統(tǒng)shell之上。

4.3 依賴的類型
GNU make的規(guī)則中可以使用兩種不同類型的依賴:1. 在以前章節(jié)所提到的規(guī)則中使用的是常規(guī)依賴,這是我們書寫的Makefile規(guī)則中最常用的一種。2. 另外一種在我們書寫Makefile時不會經(jīng)常使用,它比較特殊、稱之為“order-only”依賴。一個規(guī)則的常規(guī)依賴(通常是多個依賴文件)表明了 兩件事:首先,它決定了重建規(guī)則目標(biāo)所要執(zhí)行命令的順序;表明在更新這個規(guī)則的目標(biāo)(執(zhí)行此規(guī)則的命令行)之前必需要按照什么樣的順序、執(zhí)行那些命令來重 建這些依賴文件(對所有依賴文件的重建,使用明確或者隱含規(guī)則。就是說對于這樣的規(guī)則:A:B C,那么在重建目標(biāo)A之前,首先需要完成對它的依賴文件B和C的重建。重建B和C的過程就是執(zhí)行Makefile中文件B和C所在的規(guī)則)。其次,它確定 了一個依存關(guān)系;規(guī)則中如果依賴文件的任何一個比目標(biāo)文件新,則被認為規(guī)則的目標(biāo)已經(jīng)過期同時需要重建目標(biāo)。

通常,如果規(guī)則中依賴文件中的任何一個被更新,則規(guī)則的目標(biāo)相應(yīng)地也應(yīng)該被更新。

有時,我們需要定義一個這樣的規(guī)則,在更新目標(biāo)(目標(biāo)文件已經(jīng)存在)時只需要根據(jù)依賴文件中的部分來決定目標(biāo)是否需要被重建,而不是在依賴文件的任何一個 被修改后都重建目標(biāo)。為了實現(xiàn)這個目的,我們需要對依賴進行分類,一類是這些依賴文件的更新需要對應(yīng)更新目標(biāo)文件,另一類是這些依賴的更新不會導(dǎo)致目標(biāo)被 重建。第二類的依賴我們就稱他為:“order-only”依賴。在書寫規(guī)則時,“order-only”依賴使用管道符號“|”開始,作為目標(biāo)的一個依 賴文件。規(guī)則的依賴列表中管道符號“|”左邊的是常規(guī)依賴文件,所有出現(xiàn)在管道符號右邊的就是“order-only”依賴。這樣的規(guī)則書寫格式如下:



TARGETS : NORMAL-PREREQUISITES | ORDER-ONLY-PREREQUISITES



規(guī)則中常規(guī)依賴文件可以是空。允許對一個目標(biāo)聲明多行按正確順序依次追加的依賴。需要注意:規(guī)則依賴文件中如果一個文件被同時聲明為常規(guī)依賴和 “order-only”依賴,那么此文件被作為常規(guī)依賴處理(因為常規(guī)依賴所實現(xiàn)的動作是“order-only”依賴所實現(xiàn)的動作的一個超集)。



“order-only”依賴的使用舉例:

LIBS = libtest.a

foo : foo.c | $(LIBS)

$(CC) $(CFLAGS) $< -o $@ $(LIBS)

make在執(zhí)行這個規(guī)則時,如果目標(biāo)文件“foo”已經(jīng)存在。當(dāng)“foo.c”被修改以后,目標(biāo)“foo”將會被重建,但是當(dāng)“libtest.a”被修改以后。將不執(zhí)行規(guī)則的命令來重建目標(biāo)“foo”。

就是說,規(guī)則中依賴文件$(LIBS)只有在目標(biāo)文件不存在的情況下,才會參與規(guī)則的執(zhí)行。當(dāng)目標(biāo)文件存在時此依賴不會參與規(guī)則的執(zhí)行過程。

4.4 文件名使用通配符
Maekfile中表示一個單一的文件名時可使用通配符。可使用的通配符有:“*”、“?”和“[…]”。在Makefile中通配符的用法和含義和 Linux(unix)的Bourne shell完全相同。例如,“*.c”代表了當(dāng)前工作目錄下所有的以“.c”結(jié)尾的文件等。但是在Makefile中這些統(tǒng)配符并不是可以用在任何地方, Makefile中統(tǒng)配符可以出現(xiàn)在以下兩種場合:

1. 可以用在規(guī)則的目標(biāo)、依賴中,此時make會自動將其展開;

2. 可出現(xiàn)在規(guī)則的命令中,其展開是在shell在執(zhí)行此命令時完成。

除這兩種情況之外的其它上下文中,不能直接使用通配符。二是需要通過函數(shù)“wildcard”(可參考 7.3 文件名處理函數(shù) 一節(jié))來實現(xiàn)。

如果規(guī)則中的某一個文件的文件名包含作為統(tǒng)配符的字符(“*”、“.”字符),在使用文件時需要對文件名中的統(tǒng)配字符進行轉(zhuǎn)義處理,使用反斜線(\)來進 行通配符的轉(zhuǎn)義。例如“foo\*bar”,在Makefile中它表示了文件“foo*bar”。Makefile中對一些特殊字符的轉(zhuǎn)移和B- SHELL以及C語言中的基本上相同。

另外需要注意:在Linux(unix)中,以波浪線“~”開始的文件名有特殊含義。

單獨使用它或者其后跟一個斜線(~/),代表了當(dāng)前用戶的宿主目錄。(在shell下可以通過命令“echo ~(~\)”來查看)。例如“~/bin”代表“/home/username/bin/”(當(dāng)前用戶宿主目錄下的bin目錄)

波浪線之后跟一個單詞(~word),其代表由這個“word”所指定的用戶的宿主目錄。例如“~john/bin”就是代表用戶john的宿主目錄下的bin目錄。

在一些系統(tǒng)中(像MS-DOS和MS-Windows),用戶沒有各自的宿主目錄,此情況下可通過設(shè)置環(huán)境變量“HOME”來模擬。

4.4.1 統(tǒng)配符使用舉例
本節(jié)開始已經(jīng)提到過,通配符可被用在規(guī)則的命令中,它是在命令被執(zhí)行時由shell進行處理的。例如Makefile的清空過程文件規(guī)則:



clean:

rm -f *.o



通配符也可以用在規(guī)則的依賴文件名中??纯聪旅孢@個例子。執(zhí)行“make print”,執(zhí)行的結(jié)果是打印當(dāng)前工作目錄下所有的在上一次打印以后被修改過的“.c”文件。



print: *.c

lpr -p $?

touch print



兩點說明:1. 上述的規(guī)則中目標(biāo)“print”時一個空目標(biāo)文件。(不存在一個這樣的文件,此目標(biāo)不代表一個文件,它只是記錄了一個所要執(zhí)行的動作或者命令。參考 4.8 空目標(biāo)文件 一節(jié))。2. 自動環(huán)變量“$?”用在這里表示依賴文件列表中被改變過的所有文件。

變量定義中使用的通配符不會被展開(因此在定義變量不能按照這這種方式,下一小節(jié)將會詳細討論)。如果Makefile有這樣一句:“objects = *.o”。那么變量“objects”的值就是“*.o”,而不是使用空格分開的所有.o文件列表。如果需要變量“objects”代表所有的.o文件, 則需要是用函數(shù)“wildcard”來實現(xiàn)(objects = $(wildcar *.o))。

4.4.2 通配符存在的缺陷
上一小節(jié)已經(jīng)提到過在變量定義時使用通配符可能會導(dǎo)致意外的結(jié)果。本小節(jié)將此詳細地分析和討論。在書寫Makefile時,可能存在這種不正確使用通配符 的方法。這種看似正確的方式產(chǎn)生的結(jié)果可能并非你所期望得到的。假如在你的makefile中,期望能夠根據(jù)所有的.o文件生成可執(zhí)行文件“foo”。實 現(xiàn)如下:



objects = *.o



foo : $(objects)

cc -o foo $(CFLAGS) $(objects)



這里變量“objects”的值是一個字符串“*.o”。在重建“foo”的規(guī)則中對變量“objects”進行展開,目標(biāo)“foo”的依賴就是 “*.o”,即所有的.o文件的列表。如果工作目錄下已經(jīng)存在必需的.o文件,那么這些.o文件將成為目標(biāo)的依賴文件,目標(biāo)“foo”將根據(jù)規(guī)則被重建。

但是如果我們將工作目錄下所有的.o文件刪除,在執(zhí)行規(guī)則時將會得到一個類似于“沒有創(chuàng)建*.o文件的規(guī)則” 的錯誤提示。這當(dāng)然不是我們所期望的結(jié)果(可能在出現(xiàn)這個錯誤時會令你感到萬分迷惑!)。為了實現(xiàn)們的初衷,在對變量進行定義的時需要使用一些高級的技 巧,包括使用“wildcard”函數(shù)和實現(xiàn)字符串的置換。關(guān)于如何實現(xiàn)字符串的置換,我們將在后續(xù)進行詳細地討論。

4.4.3 函數(shù)wildcard
前邊提到過,在規(guī)則中,通配符會被自動展開。但在變量的定義和使用函數(shù)是,通配符不會被自動的展開。這種情況下需要通配符有效,要用到函數(shù) “wildcard”,其用法:$(wildcard PATTERN...) ;在Makefile中,它被展開未已經(jīng)存在的、空格分割的、匹配此模式的所有文件列表。如果不存在符合此模式的文件,那么函數(shù)會忽略模式并返回空。需要 注意的是:這種情況下規(guī)則中通配符的展開和上一小節(jié)匹配通配符的區(qū)別。

一般我們可以使用“$(wildcard *.c)”來獲取工作目錄下的所有的.c文件列表。復(fù)雜一些用法;可以使用“$(patsubst %.c,%.o,$(wildcard *.c))”,首先使用“wildcard”函數(shù)獲取工作目錄下的.c文件列表;之后將列表中所有文件名的后綴.c替換為.o。這樣我們就可以得到在當(dāng)前 目錄可生成的.o文件列表。因此在一個目錄下可以使用如下內(nèi)容的Makefile來將工作目錄下的所有的.c文件進行編譯并最后連接成為一個可執(zhí)行文件:



#sample Makefile

objects := $(patsubst %.c,%.o,$(wildcard *.c))



foo : $(objects)

cc -o foo $(objects)



這里我們使用了make的隱含規(guī)則來編譯.c的源文件。對變量的賦值也用到了一個特殊的符號(:=)。關(guān)于變量定義可參考 5.2 兩種變量定義 一節(jié)。函數(shù)“patsubst”可參考 7.2 文本處理函數(shù) 一節(jié)



4.5 目錄搜尋
在一個較大的工程中,一般會將源代碼和二進制文件(.o文件和可執(zhí)行文件)安排在不同的目錄來進行區(qū)分管理。這種情況下,我們需要使用make提供的目錄 自動搜索依賴文件功能(在指定的若干個目錄下搜索依賴文件)。書寫makefile時,指定依賴文件的搜索目錄。當(dāng)工程的目錄結(jié)構(gòu)發(fā)生變化時,我們就可以 不更改Makefile的規(guī)則,而只更改依賴文件的搜索目錄。

本節(jié)我們將詳細討論在書寫Makefile時如何使用這一特性。在自己的工程中靈活運用這一特性,將會起到事半功倍的效果。

4.5.1 一般搜索(變量VPATH)
make可識別一個特殊變量“VPATH”。通過變量“VPATH”可以指定依賴文件的搜索路徑,在規(guī)則的依賴文件在當(dāng)前目錄不存在時,make會在此變 量所指定的目錄下去尋找這些依賴文件。一般我們都是用此變量來說明規(guī)則中的依賴文件的搜索路徑。其實“VPATH”變量所指定的是Makefile中所有 文件的搜索路徑,包括依賴文件和目標(biāo)文件。

變量“VPATH”的定義中,使用空格或者冒號(:)將多個目錄分開。make搜索的目錄順序按照變量“VPATH”定義中順序進行(當(dāng)前目錄永遠是第一搜索目錄)。例如對變量的定義如下:



VPATH = src:../headers



它指定了兩個搜索目錄,“src”和“../headers”。對于規(guī)則“foo:foo.c”如果“foo.c”在“src”目錄下,此時此規(guī)則等價于“foo:src:/foo.c”。

通過“VPATH”變量指定的路徑在Makefile中對所有文件有效。當(dāng)需要為不類型的文件指定不同的搜索目錄時,需要使用另外一種方式。下一小節(jié)我們將會討論這種更高級的方式。

4.5.2 選擇性搜索(關(guān)鍵字vpath)
另一個設(shè)置文件搜索路徑的方法是使用make的“vpath”關(guān)鍵字(全小寫的)。它不是一個變量,是一個make的關(guān)鍵字,它所實現(xiàn)的功能和上一小節(jié)提 到的“VPATH”變量很類似,但是它更為靈活。它可以為不同類型的文件(由文件名區(qū)分)指定不同的搜索目錄。它的使用方法有三種:

1、vpath PATTERN DIRECTORIES

為符合模式“PATTERN”的文件指定搜索目錄“DIRECTORIES”。多個目錄使用空格或者冒號(:)分開。類似上一小節(jié)的“VPATH”

2、vpath PATTERN

清除之前為符合模式“PATTERN”的文件設(shè)置的搜索路徑。

3、vpath

清除所有已被設(shè)置的文件搜索路徑。



vapth使用方法中的“PATTERN”需要包含模式字符“%”。“%”意思是匹配一個或者多個字符,例如,“%.h”表示所有以“.h”結(jié)尾的文件。 如果在“PATTERN”中沒有包含模式字符“%”而是一個明確的文件名,就是指出了此文件所在的目錄,我們很少使用這種方式來為單獨的一個文件指定搜索 路徑。在“vpath”所指定的模式中我們可以使用反斜杠來對字符“%”的引用(和其他的特使字符的引用一樣)。

“PATTERN”表示了具有相同特征的一類文件,而“DIRECTORIES”則指定了搜索此類文件目錄。當(dāng)規(guī)則的依賴文件列表中出現(xiàn)的文件不能在當(dāng)前目錄下找到時,make程序?qū)⒁来卧?#8220;DIRECTORIES”所描述的目錄下尋找此文件。例如:



vpath %.h ../headers



其含義是:Makefile中出現(xiàn)的.h文件;如果不能在當(dāng)前目錄下找到,則到目錄“../headers”下尋找。注意:這里指定的路徑僅限于在 Makefile文件內(nèi)容中出現(xiàn)的.h文件。 并不能指定源文件中包含的頭文件所在的路徑(在.c源文件中所包含的頭文件需要使用GCC的命令行來說明)。

在Makefile中如果連續(xù)的多個vpath語句中使用了相同的“PATTERN”,make就對這些vpath語句一個一個進行處理,搜索某種模式文 件的目錄將是所有的通過vpath指定的符合此模式的目錄,其搜索目錄的順序由vpath語句在Makefile出現(xiàn)的先后次序來決定。多個具有相同 “PATTERN”的vpath語句之間相互獨立。下邊是兩種方式下,所有的.c文件的查找目錄的順序(不包含工作目錄,對工作目錄的搜索永遠處于最優(yōu)先 地位)比較:

vpath %.c foo

vpath % blish

vpath %.c bar



表示對所有的.c文件,make依次查找目錄:“foo”、blish”、“bar”。

而:

vpath %.c foo:bar

vpath % blish



對于所有的.c文件make將依次查找目錄:“foo”、“bar”、“blish”

4.5.3 目錄搜索的機制
規(guī)則中一個依賴文件可以通過目錄搜尋找到(使用前邊提到的一般搜索或者是選擇性搜索任一種),但是有可能此文件的完整路徑名(文件的相對路徑或者絕對路 徑,如:/home/Stallman/foo.c)卻并不是規(guī)則中列出的依賴(規(guī)則“foo : foo.c”,在執(zhí)行搜索后可能得到的依賴文件為:“../src/foo.c”。目錄“../src”是使用“VPATH”或“vpath”指定的); 因此使用目錄搜索所到的完整的文件路徑名可能需要廢棄。make在解析Makefile文件執(zhí)行規(guī)則時對文件路徑保存或廢棄所依據(jù)的算法如下:

1. 首先,如果規(guī)則的目標(biāo)文件在Makefile文件所在的目錄(工作目錄)下不存在,那么就執(zhí)行目錄搜尋。

2. 如果目錄搜尋成功,在指定的目錄下存在此規(guī)則的目標(biāo)。那么搜索到的完整的路徑名就被作為臨時的目標(biāo)文件被保存。

3. 對于規(guī)則中的所有依賴文件使用相同的方法處理。

4. 完成第三步的依賴處理后,make程序就可以決定規(guī)則的目標(biāo)是否需要重建,兩種情況時后續(xù)處理如下:

a) 規(guī)則的目標(biāo)不需要重建:那么通過目錄搜索得到的所有完整的依賴文件路徑名有效,同樣,規(guī)則的目標(biāo)文件的完整的路徑名同樣有效。就是說,當(dāng)規(guī)則的目標(biāo)不需要被重建時,規(guī)則中的所有的文件完整的路徑名有效。已經(jīng)存在的目標(biāo)文件所在的目錄不會被改變。

b) 規(guī)則的目標(biāo)需要重建:那么通過目錄搜索所得到的目標(biāo)文件的完整的路徑名無效,規(guī)則中的目標(biāo)文件將會被在工作目錄下重建。就是說,當(dāng)規(guī)則的目標(biāo)需要重建時, 規(guī)則的目標(biāo)文件會在工作目錄下被重建,而不是在目錄搜尋時所得到的目錄。這里,必須明確:此種情況只有目標(biāo)文件的完整路徑名失效,依賴文件的完整路徑名是 不會失效的。否則將無法重建目標(biāo)。

該算法看起來比較法雜,但它確實使make實現(xiàn)了我們所需要的東西。此算法使用純粹的語言描述可能顯得晦澀。本小節(jié)后續(xù)將使用一個例子來說明。使大家能夠 對此算法有明確的理解。對于其他版本的make則使用了一種比較簡單的算法:如果規(guī)則的目標(biāo)文件的完整路徑名存在(通過目錄搜索可以定位到目標(biāo)文件),無 論該目標(biāo)是否需要重建,都使用搜索到的目標(biāo)文件完整路徑名。

實際上,GNU make也可以實現(xiàn)這種功能。如果需要make在執(zhí)行時,將目標(biāo)文件在已存在的目錄存下進行重建,我們可以使用“GPATH”變量來指定這些目標(biāo)所在的目 錄。“GPATH”變量和“VPATH”變量具有相同的語法格式。make在執(zhí)行時,如果通過目錄搜尋得到一個過時的完整的目標(biāo)文件路徑名,而目標(biāo)存在的 目錄又出現(xiàn)在“GPATH”變量的定義列表中,則該目標(biāo)的完整路徑將不廢棄,目標(biāo)將在該路徑下被重建。

為了更清楚地描述此算法,我們使用一個例子來說明。存在一個目錄“prom”,“prom”的子目錄“src”下存在“sum.c”和“memcp.c”兩個源文件。在“prom”目錄下的Makefile部分內(nèi)容如下:



LIBS = libtest.a

VPATH = src



libtest.a : sum.o memcp.o

$(AR) $(ARFLAGS) $@ $^



首先,如果在兩個目錄(“prom”和“src”)都不存在目標(biāo)“libtest.a”,執(zhí)行make時將會在當(dāng)前目錄下創(chuàng)建目標(biāo)文件“libtest.a”。另外;如果“src”目錄下已經(jīng)存在“libtest.a”,以下兩種不同的執(zhí)行結(jié)果:

1) 當(dāng)它的兩個依賴文件“sum.c”和“memcp.c”沒有被更新的情況下我們執(zhí)行make,首先make程序會搜索到目錄“src”下的已經(jīng)存在的目標(biāo) “libtest.a”。由于目標(biāo)“libtest.a”的依賴文件沒有發(fā)生變化,所以不會重建目標(biāo)。并且目標(biāo)所在的目錄不會發(fā)生變化。

2) 當(dāng)我們修改了文件“sum.c”或者“memcp.c”以后執(zhí)行make。“libtest.a”和“sum.o”或者“memcp.o”文件將會被在當(dāng) 前目錄下創(chuàng)建(目標(biāo)完整路徑名被廢棄),而不是在“src”目錄下更新這些已經(jīng)存在的文件。此時在兩個目錄下(“prom”和“src”)同時存在文件 “libtest.a”。但只有“prom/libtest.a”是最新的庫文件。

當(dāng)在上邊的Makefile文件中使用“GPATH”指定目錄時,情況就不一樣了。首先看看怎么使用“GPATH”,改變后的Makefile內(nèi)容如下:

LIBS = libtest.a

GPATH = src

VPATH = src

LDFLAGS += -L ./. –ltest

…….

……



同樣;當(dāng)兩個目錄都不存在目標(biāo)文件“libtest.a”時,目標(biāo)將會在當(dāng)前目錄(“prom”目錄)下創(chuàng)建。如果“src”目錄下已經(jīng)存在目標(biāo)文件 “libtest.a”。當(dāng)其依賴文件任何一個被改變以后執(zhí)行make,目標(biāo)“libtest.a”將會被在“src”目錄下被更新(目標(biāo)完整路徑名不會 被廢棄)。

4.5.4 命令行和搜索目錄
make在執(zhí)行時,通過目錄搜索得到的目標(biāo)的依賴文件可能會在其它目錄(此時依賴文件為文件的完整路徑名),但是已經(jīng)存在的規(guī)則命令卻不能發(fā)生變化。因此,書寫命令時我們必須保證當(dāng)依賴文件在其它目錄下被發(fā)現(xiàn)時規(guī)則的命令能夠正確執(zhí)行。

處理這種問題的方式就是使用“自動化變量”(可參考 9.5.3 自動化變量 一小節(jié)),諸如“$^”等。規(guī)則命令行中的自動化變量“$^”代表所有的是的通過目錄搜索得到的依賴文件的完整路徑名(目錄+一般文件名)列表。“$@” 代表規(guī)則的目標(biāo)。所以對于一個規(guī)則我們可以進行如下的描述:



foo.o : foo.c

cc -c $(CFLAGS) $^ -o $@



變量“CFLAGS”是編譯.c文件時GCC的命令行選項,可以在Makefile中給它指定明確的值、也可以使用隱含的定義值。

規(guī)則的依賴文件列表中可以包含頭文件,而在命令行不需要使用這些頭文件(這些頭文件的作用只有在make程序決定目標(biāo)是否需要重建時才有意義)。我們可以使用另外一個變量來書代替“$^”,例如:



VPATH = src:../headers

foo.o : foo.c defs.h hack.h

cc -c $(CFLAGS) $< -o $@



自動化變量“$<”代表規(guī)則中通過目錄搜索得到的依賴文件列表的第一個依賴文件。關(guān)于自動化變量我們在后續(xù)有專門的討論。

4.5.5 隱含規(guī)則和搜索目錄
隱含規(guī)則同樣會為依賴文件搜索通過變量“VPATH”、或者關(guān)鍵字“vpath”指定的搜索目錄。例如:一個目標(biāo)文件“foo.o”在Makefile中 沒有重建它的明確規(guī)則,make會使用隱含規(guī)則來由已經(jīng)存在的“foo.c”來重建它。當(dāng)“foo.c”在當(dāng)前目錄下不存在時,make將會進行目錄搜 索。如果能夠在一個可以搜索的目錄中找到此文件,同樣make會使用隱含規(guī)則根據(jù)搜索到的文件完整的路徑名去重建目標(biāo),編譯這個.c源文件。

隱含規(guī)則中的命令行中就是使用自動化變量來解決目錄搜索可能帶來的問題;相應(yīng)的命令中的文件名都是使用目錄搜索得到的完整的路徑名。(可參考上一小節(jié))

4.5.6 庫文件和搜索目錄
Makefile中程序鏈接的靜態(tài)庫、共享庫同樣也可以有目錄搜索得到。這一特性需要我們在書規(guī)則的依賴是指定一個類似“-lNNAM”的依賴文件名(一 個奇怪的依賴文件名!一般依賴文件名應(yīng)該是一個普通文件的名字。庫文件的命名也應(yīng)該是“libNAME.a”而不是所寫的“-lNAME”。這是為什么, 熟悉GNU ld的話我想這就不難理解了,“-lNAME”的表示方式和ld的對庫的引用方式完全一樣,只是我們在書寫Makefile的規(guī)則時使用了這種書寫方式。 因此你不應(yīng)該感到奇怪)。下邊我們就來看看這種奇怪的依賴文件到底是什么。

當(dāng)規(guī)則中依賴文件列表中存在一個“-lNAME”形式的文件時。make將根據(jù)“NAME”首先搜索當(dāng)前系統(tǒng)可提供的共享庫,如果當(dāng)前系統(tǒng)不能提供這個共 享庫,則搜索它的靜態(tài)庫(當(dāng)然你可以在命令行中指定編譯或者連接選項來指定動態(tài)連接還是靜態(tài)連接,這里我們不討論)。來看一下詳細的過程。1. make在執(zhí)行規(guī)則時會在當(dāng)前目錄下搜索一個名字為“libNAME.so”的文件;2. 如果當(dāng)前工作目錄下不存在這樣一個文件,則make程序會繼續(xù)搜索使用“VPATH”或者“vpath”指定的搜索目錄。3. 還是不存在,make程序?qū)⑺阉飨到y(tǒng)默認目錄,順序是:“/lib”、“/usr/lib”和“PREFIX/lib”(在Linux系統(tǒng)中為 “/usr/local/lib”,其他的系統(tǒng)可能不同)。

如果“libNAME.so”通過以上的途徑最后還是沒有找到的話,那么make程序?qū)凑找陨系乃阉黜樞虿檎颐譃?#8220;libNAME.a”的文件。

假設(shè)你的系統(tǒng)中存在“/usr/lib/libcurses.a”(不存在“/usr/lib/libcurses.so”)這個庫文件??匆粋€例子:



foo : foo.c -lcurses

cc $^ -o $@



上例中,如果文件“foo.c”被修改或者“/usr/lib/libcurses.a”被更新,執(zhí)行規(guī)則時將使用命令“cc foo.c /usr/lib/libcurses.a -o foo”來完成目標(biāo)文件的重建。需要注意的是:如果“/usr/lib/libcurses.a”需要在執(zhí)行make的時生成,那么就不能這樣寫,因為 “-lNAME”只是告訴了鏈接器在生成目標(biāo)時需要鏈接那個庫文件。上例的中的“-lcurses”并沒有告訴make程序其依賴的庫文件應(yīng)該如何重建。 當(dāng)搜索的所有目錄中不存在庫“libcurses”時。Make將提示“沒有規(guī)則可以創(chuàng)建目標(biāo)“foo”需要的目標(biāo)“-lcurses”。如果在執(zhí)行 make時,出現(xiàn)這樣的提示信息,你應(yīng)該明確發(fā)生了什么錯誤,而不要因為錯誤而不知所措。

當(dāng)規(guī)則的依賴列表中出現(xiàn)了“-lNAME”格式的依賴,默認搜索的文件名為“libNAME.so”和“libNAME.a”,這是由變量 “.LIBPATTERNS”來指定的。“.LIBPATTERNS”的值一般是多個包含模式字符(%)的字(一個不包含空格的字符串),多個字之間使用 空格分開。在規(guī)則中出現(xiàn)“-lNAME”格式的的依賴時,首先使用這里的“NAME”代替變量“.LIBPATTERNS”的第一個字的模式字符(%)而 得到第一個庫文件名,根據(jù)這個文件名在搜索目錄下查找,如果能夠找到、就是用這個文件,否則使用“NAME”代替第二個字的模式字符,進行同樣的查找。默 認情況時,“.LIBPATTERNS”的值為:“lib%.so lib%.a”。這也是默認情況下在規(guī)則存在“-lNAME”格式的依賴時,鏈接生成目標(biāo)時使用“libNAME.so”和“libNAME.a”的原 因。

變量“.LIBPATTERNS”就是告訴鏈接器在執(zhí)行鏈接過程中對于出現(xiàn)“-LNAME”的文件如何展開。當(dāng)然我們也可以將此變量制空,取消鏈接器對“-lNAME”格式進行展開。



4.6 Makefile偽目標(biāo)
本節(jié)我們討論一個Makefile中的一個重要的特殊目標(biāo):偽目標(biāo)。

偽目標(biāo)是這樣一個目標(biāo):它不代表一個真正的文件名,在執(zhí)行make時可以指定這個目標(biāo)來執(zhí)行其所在規(guī)則定義的命令,有時我們也可以將一個偽目標(biāo)稱為標(biāo)簽。 使用偽目標(biāo)有兩點原因:1. 避免在我們的Makefile中定義的只執(zhí)行命令的的目標(biāo)(此目標(biāo)的目的為了執(zhí)行執(zhí)行一些列命令,而不需要創(chuàng)建這個目標(biāo))和工作目錄下的實際文件出現(xiàn)名字 沖突。2. 提高執(zhí)行make時的效率,特別是對于一個大型的工程來說,編譯的效率也許你同樣關(guān)心。以下就這兩個問題我們進行分析討論:

1. 如果我們需要書寫這樣一個規(guī)則:規(guī)則所定義的命令不是去創(chuàng)建目標(biāo)文件,而是使用make指定具體的目標(biāo)來執(zhí)一些特定的命令。像下邊那樣:

clean:

rm *.o temp



規(guī)則中“rm”不是創(chuàng)建文件“clean”的命令,只是刪除當(dāng)前目錄下的所有.o文件和temp文件。在工作目錄下不存在“clean”這個文件時,我們輸入“make clean”后,“rm *.o temp”總會被執(zhí)行。這是我們的初衷。

但當(dāng)前工作目錄下存在文件“clean”時情況就不一樣了,在我們輸入“make clean”時。規(guī)則沒有依賴文件,所以目標(biāo)被認為是最新的而不去執(zhí)行規(guī)則作定義的命令,命令“rm”將不會被執(zhí)行。這并不是我們的初衷。為了避免這個問 題,我們可以將目標(biāo)“clean”明確的聲明為偽目標(biāo)。將一個目標(biāo)聲明為偽目標(biāo)需要將它作為特殊目標(biāo).PHONY”的依賴。如下:

.PHONY : clean



這樣目標(biāo)“clean”就是一個偽目標(biāo),無論當(dāng)前目錄下是否存在“clean”這個文件。我們輸入“make clean”之后。“rm”命令都會被執(zhí)行。而且,當(dāng)一個目標(biāo)被聲明為偽目標(biāo)后,make在執(zhí)行此規(guī)則時不會試圖去查找隱含規(guī)則來創(chuàng)建這個目標(biāo)。這樣也提 高了make的執(zhí)行效率,同時我們也不用擔(dān)心由于目標(biāo)和文件名重名而使我們的期望失敗。在書寫偽目標(biāo)規(guī)則時,首先需要聲明目標(biāo)是一個偽目標(biāo),之后才是偽目 標(biāo)的規(guī)則定義。目標(biāo)“clean”書寫格式應(yīng)該如下:



.PHONY: clean

clean:

rm *.o temp



2. 偽目標(biāo)的另外一使用場合在make的并行和遞歸執(zhí)行過程中。此情況下一般存在一個變量,其定義為所有需要make的子目錄。對多個目錄進行make的實現(xiàn)方式可以在一個規(guī)則中可以使用shell的循環(huán)來完成。如下:



SUBDIRS = foo bar baz



subdirs:

for dir in $(SUBDIRS); do \

$(MAKE) -C $$dir; \

done



但這種實現(xiàn)方法存在以下幾個問題。1. 當(dāng)子目錄執(zhí)行make出現(xiàn)錯誤時,make不會退出。就是說,在對某一個目錄執(zhí)行make失敗以后,會繼續(xù)對其他的目錄進行make。在最終執(zhí)行失敗的情 況下,我們很難根據(jù)錯誤的提示定位出具體是是那個目錄下的Makefile出現(xiàn)錯誤。這給問題定位造成了很大的困難。為了避免這樣的問題,我們可以在命令 行部分加入錯誤的監(jiān)測,在命令執(zhí)行錯誤后make退出。不幸的是,如果在執(zhí)行make時使用了“-k”選項,此方式將失效。2. 另外一個問題就是使用這種shell的循環(huán)方式時,沒有用到make對目錄的并行處理功能,因為規(guī)則的命令是一條完整的shell命令,不能被并行的執(zhí) 行。

我們可以通過偽目標(biāo)方式來克服以上實現(xiàn)方式所存在的兩個問題。



SUBDIRS = foo bar baz



.PHONY: subdirs $(SUBDIRS)



subdirs: $(SUBDIRS)

$(SUBDIRS):

$(MAKE) -C $@

foo: baz



上邊的實現(xiàn)中使用了一個沒有命令行的規(guī)則“foo: baz”,用來限制子目錄的make順序。此規(guī)則的含義時在處理“foo”目錄之前,需要等待“baz”目錄處理完成。在書寫一個并行執(zhí)行make的Makefile時,目錄的處理順序是需要特別注意的。

一般情況下,一個偽目標(biāo)不作為一個另外一個目標(biāo)文件的依賴。這是因為當(dāng)一個目標(biāo)文件的依賴包含偽目標(biāo)時,每一次在執(zhí)行這個規(guī)則時偽目標(biāo)所定義的命令都會被 執(zhí)行(因為它是規(guī)則的依賴,重建規(guī)則目標(biāo)文件時需要首先重建它的依賴)。當(dāng)偽目標(biāo)沒有作為任何目標(biāo)(此目標(biāo)是一個可被創(chuàng)建或者已存在的文件)的依賴時,我 們只能通過make的命令行選項明確指定這個偽目標(biāo),來執(zhí)行它所定義的命令。例如我們的“make clean”。

Makefile中,偽目標(biāo)可以有自己的依賴。在一個目錄下如果需要創(chuàng)建多個可執(zhí)行程序,我們可以將所有程序的重建規(guī)則在一個Makefile中描述。因 為Makefile中第一個目標(biāo)是“終極目標(biāo)”,約定的做法是使用一個稱為“all”的偽目標(biāo)來作為終極目標(biāo),它的依賴文件就是那些需要創(chuàng)建的程序。下邊 就是一個例子:



#sample Makefile

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



執(zhí)行make時,目標(biāo)“all”被作為終極目標(biāo)。為了完成對它的更新,make會創(chuàng)建(不存在)或者重建(已存在)目標(biāo)“all”的所有依賴文件 (prog1、prog2和prog3)。當(dāng)需要單獨更新某一個程序時,我們可以通過make的命令行選項來明確指定需要重建的程序。(例如: “make prog1”)。

當(dāng)一個偽目標(biāo)作為另外一個偽目標(biāo)依賴時,make將其作為另外一個偽目標(biāo)的子例程來處理(可以這樣理解:其作為另外一個偽目標(biāo)的必須執(zhí)行的部分,就行C語言中的函數(shù)調(diào)用一樣)。下邊的例子就是這種用法:



.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff

rm program



cleanobj :

rm *.o



cleandiff :

rm *.diff



“cleanobj”和“cleandiff”這兩個偽目標(biāo)有點像“子程序”的意思(執(zhí)行目標(biāo)“clearall時會觸發(fā)它們所定義的命令被執(zhí)行”)。我 們可以輸入“make cleanall”和“make cleanobj”和“make cleandiff”命令來達到清除不同種類文件的目的。例子首先通過特殊目標(biāo)“.PHONY”聲明了多個偽目標(biāo),它們之間使用空各分割,之后才是各個偽 目標(biāo)的規(guī)則定義。

說明:

通常在清除文件的偽目標(biāo)所定義的命令中“rm”使用選項“–f”(--force)來防止在缺少刪除文件時出錯并退出,使“make clean”過程失敗。也可以在“rm”之前加上“-”來防止“rm”錯誤退出,這種方式時make會提示錯誤信息但不會退出。為了不看到這些討厭的信 息,需要使用上述的第一種方式。

另外make存在一個內(nèi)嵌隱含變量“RM”,它被定義為:“RM = rm –f”。因此在書寫“clean”規(guī)則的命令行時可以使用變量“$(RM)”來代替“rm”,這樣可以免出現(xiàn)一些不必要的麻煩!這是我們推薦的用法。

4.7 強制目標(biāo)(沒有命令或依賴的規(guī)則)
如果一個規(guī)則沒有命令或者依賴,而且它的目標(biāo)不是一個存在的文件名。在執(zhí)行此規(guī)則時,目標(biāo)總會被認為是最新的。就是說:這個規(guī)則一旦被執(zhí)行,make就認 為它的目標(biāo)已經(jīng)被更新過。這樣的目標(biāo)在作為一個規(guī)則的依賴時,因為依賴總被認為被更新過,因此作為依賴所在的規(guī)則定義的命令總會被執(zhí)行??匆粋€例子:



clean: FORCE

rm $(objects)

FORCE:



這個例子中,目標(biāo)“FORCE”符合上邊的條件。它作為目標(biāo)“clean”的依賴出現(xiàn),在執(zhí)行make時,它總被認為被更新過。所以“clean”所在的規(guī)則在被執(zhí)行時規(guī)則所定義的命令總會被執(zhí)行。這樣的一個目標(biāo)通常我們將其命名為“FORCE”。

上邊的例子中使用“FORCE”目標(biāo)的效果和我們指定“clean”為偽目標(biāo)效果相同。兩種方式相比較,使用“.PHONY”方式更加直觀高效。這種方式主要用在非GNU版本的make中。

在使用GNU make,盡量避免使用這種方式。在GNU make中我們推薦使用偽目標(biāo)方式。關(guān)于偽目標(biāo)可參考3.6 Makefile偽目標(biāo) 一節(jié)

4.8 空目標(biāo)文件
空目標(biāo)是偽目標(biāo)的一個變種;此目標(biāo)所在規(guī)則執(zhí)行的目的和偽目標(biāo)相同——通過make命令行指定終極目標(biāo)來執(zhí)行規(guī)則所定義的命令。和偽目標(biāo)不同的是:這個目標(biāo)可以是一個存在的文件,一般文件的具體內(nèi)容我們并不關(guān)心,通常此文件是一個空文件。

空目標(biāo)文件只是用來記錄上一次執(zhí)行此規(guī)則定義命令的時間。一般在這樣的規(guī)則中,命令部分都會使用“touch”在完成所有命令之后來更新目標(biāo)文件的時間 戳,記錄此規(guī)則命令的最后執(zhí)行時間。make時通過命令行將此目標(biāo)作為終極目標(biāo),當(dāng)前目錄下如果不存在這個文件,“touch”會在第一次執(zhí)行時創(chuàng)建一個 空的文件(命名為空目標(biāo)文件名)。

通常,一個空目標(biāo)文件應(yīng)該存在一個或者多個依賴文件。將這個目標(biāo)作為終極目標(biāo),在它所依賴的文件比它新時,此目標(biāo)所在規(guī)則的命令行將被執(zhí)行。就是說,如果空目標(biāo)的依賴文件被改變之后,空目標(biāo)所在規(guī)則中定義的命令會被執(zhí)行。看一個例子:



print: foo.c bar.c

lpr -p $?

touch print



執(zhí)行“make print”,當(dāng)目標(biāo)“print”的依賴文件任何一個被修改之后,命令“lpr –p $?”都會被執(zhí)行,打印這個被修改的文件。關(guān)于自動化變量“$?”可參考 9.5.3 自動化變量 一小節(jié)。

4.9 Makefile的特殊目標(biāo)
在Makefile中,有一些名字,當(dāng)它們作為規(guī)則的目標(biāo)出現(xiàn)時,具有特殊含義。它們是一些特殊的目標(biāo),GNU make所支持的特殊的目標(biāo)有:

.PHONY:

目標(biāo)“.PHONY”的所有的依賴被作為偽目標(biāo)。偽目標(biāo)時這樣一個目標(biāo):當(dāng)使用make命令行指定此目標(biāo)時,這個目標(biāo)所在規(guī)則定義的命令、無論目標(biāo)文件是否存在都會被無條件執(zhí)行。參考 3.6 Makefile偽目標(biāo) 一節(jié)

.SUFFIXES:

特殊目標(biāo)“SUFFIXES”的所有依賴指出了一系列在后綴規(guī)則中需要檢查的后綴名(就是當(dāng)前make需要處理的后綴)。參考 9.7 后綴規(guī)則 一節(jié)

.DEFAULT

Makefile中,目標(biāo)“.DEFAULT”所在規(guī)則定義的命令,被用在重建那些沒有具體規(guī)則的目標(biāo)(明確規(guī)則和隱含規(guī)則)。就是說一個文件作為某個規(guī) 則的依賴,但卻不是另外一個規(guī)則的目標(biāo)時。Make程序無法找到重建此文件的規(guī)則,此種情況時就執(zhí)行“.DEFAULT”所指定的命令。

.PRECIOUS

目標(biāo)“.PRECIOUS”的所有依賴文件在make過程中會被特殊處理:當(dāng)命令在執(zhí)行過程中被中斷時,make不會刪除它們(可參考 4.5 中斷make的執(zhí)行 一節(jié))。而且如果目標(biāo)的依賴文件是中間過程文件,同樣這些文件不會被刪除。這一點目標(biāo)“.PRECIOUS”和目標(biāo)“.SECONDAY”實現(xiàn)的功能相 同。參考 9.4 make隱含規(guī)則鏈 一節(jié)

另外,目標(biāo)“.PRECIOUS”的依賴文件也可以是一個模式,例如“%.o”。這樣可以保留有規(guī)則創(chuàng)建的中間過程文件。

.INTERMEDIATE

目標(biāo)“.INTERMEDIATE”的依賴文件在make時被作為中間過程文件對待。沒有任何依賴文件的目標(biāo)“.INTERMEDIATE”沒有意義。參考 9.4 make隱含規(guī)則鏈 一節(jié)

.SECONDARY

目標(biāo)“.SECONDARY”的依賴文件被作為中間過程文件對待。但這些文件不會被自動刪除(可參考 9.4 make隱含規(guī)則鏈 一節(jié))

沒有任何依賴文件的目標(biāo)“.SECONDARY”的含義是:將所有的文件作為中間過程文件(不會自動刪除任何文件)。

.DELETE_ON_ERROR

如果在Makefile中存在特殊目標(biāo)“.DELETE_ON_ERROR”,make在執(zhí)行過程中,如果規(guī)則的命令執(zhí)行錯誤,將刪除已經(jīng)被修改的目標(biāo)文件。參考 4.4 命令執(zhí)行的錯誤 一節(jié)

.IGNORE

如果給目標(biāo)“.IGNORE”指定依賴文件,則忽略創(chuàng)建這個文件所執(zhí)行命令的錯誤。給此目標(biāo)指定命令是沒有意義的。當(dāng)此目標(biāo)沒有依賴文件時,將忽略所有命令執(zhí)行的錯誤。參考 4.4 命令執(zhí)行的錯誤 一節(jié)

.LOW_RESOLUTION_TIME

目標(biāo)“.LOW_RESOLUTION_TIME”的依賴文件被make認為是低分辨率時間戳文件。給目標(biāo)“.LOW_RESOLUTION_TIME”指定命令是沒有意義的。

通常文件的時間輟都是高分辨率的,make在處理依賴關(guān)系時、對規(guī)則目標(biāo)-依賴文件的高分辨率的時間戳進行比較,判斷目標(biāo)是否過期。但是在系統(tǒng)中并沒有提 供一個修改文件高分辨率時間輟的機制(方式),因此類似“cp -p”這樣的命令在根據(jù)源文件創(chuàng)建目的文件時,所產(chǎn)生的目的文件的高分辨率時間輟的細粒度部分被丟棄(來源于源文件)??赡軙斐赡康奈募臅r間戳和源文 件的相等甚至不及源文件新。處理此類命令創(chuàng)建的文件時,需要將命令創(chuàng)建的文件作為目標(biāo)“.LOW_RESOLUTION_TIME”的依賴,聲明這個文件 是一個低分辨率時間輟的文件。例如:



.LOW_RESOLUTION_TIME: dst

dst: src

cp -p src dst



首先規(guī)則的命令“cp –p src dst”,所創(chuàng)建的文件“dst”在時間戳上稍稍比“src”晚(因為命令不能更新文件“dst”的細粒度時間)。因此make在判斷文件依賴關(guān)系時會出 現(xiàn)誤判,將文件作為目標(biāo)“.LOW_RESOLUTION_TIME”的依賴后,只要規(guī)則中目標(biāo)和依賴文件的時間戳中的初始時間相等,就認為目標(biāo)已經(jīng)過 期。這個特殊的目標(biāo)主要作用是,彌補系統(tǒng)在沒有提供修改文件高分辨率時間戳機制的情況下,某些命令在make中的一些缺陷。

對于靜態(tài)庫文件(文檔文件)成員的更新也存在這個問題。make在創(chuàng)建或者更新靜態(tài)庫時,會自動將靜態(tài)庫的所有成員作為目標(biāo)“.LOW_RESOLUTION_TIME”的依賴。

.SILENT

出現(xiàn)在目標(biāo)“.SILENT”的依賴列表中的文件,make在創(chuàng)建這些文件時,不打印出重建此文件所執(zhí)行的命令。同樣,給目標(biāo)“.SILENT”指定命令行是沒有意義的。

沒有任何依賴文件的目標(biāo)“.SILENT”告訴make在執(zhí)行過程中不打印任何執(zhí)行的命令?,F(xiàn)行版本make支持目標(biāo)“.SILENT”的這種功能和用法 是為了和舊版本的兼容。在當(dāng)前版本中如果需要禁命令執(zhí)行過程的打印,可以使用make的命令行參數(shù)“-s”或者“--silent”。參考 8.7 make的命令行選項 一節(jié)



.EXPORT_ALL_VARIABLES

此目標(biāo)應(yīng)該作為一個簡單的沒有依賴的目標(biāo),它的功能含義是將之后所有的變量傳遞給子make進程。參考 4.6 make的遞歸執(zhí)行 一節(jié)

.NOTPARALLEL

Makefile中,如果出現(xiàn)目標(biāo)“.NOPARALLEL”,則所有命令按照串行方式執(zhí)行,即使存在make的命令行參數(shù)“-j”。但在遞歸調(diào)用的字make進程中,命令可以并行執(zhí)行。此目標(biāo)不應(yīng)該有依賴文件,所有出現(xiàn)的依賴文件將被忽略。

所有定義的隱含規(guī)則后綴作為目標(biāo)出現(xiàn)時,都被視為一個特殊目標(biāo),兩個后綴串聯(lián)起來也是如此,例如“.c.o”。這樣的目標(biāo)被稱為后綴規(guī)則的目標(biāo),這種定義 方式是已經(jīng)過時的定義隱含規(guī)則的方法(目前,這種方式還被用在很多地方)。原則上,如果將其分為兩個部分、并將它們加到后綴列表中,任何目標(biāo)都可采用這種 方式來表示。實際中,后綴通常以“.”開始,因此,以上的這些特別目標(biāo)同樣是以“.”開始??蓞⒖?9.7 后綴規(guī)則 一節(jié)



4.10 多目標(biāo)
一個規(guī)則中可以有多個目標(biāo),規(guī)則所定義的命令對所有的目標(biāo)有效。一個具有多目標(biāo)的規(guī)則相當(dāng)于多個規(guī)則。規(guī)則中命令對不同的目標(biāo)的執(zhí)行效果不同,因為在規(guī)則的命令中可能使用自動環(huán)變量“$@”。多目標(biāo)規(guī)則意味著所有的目標(biāo)具有相同的依賴文件。多目標(biāo)通常用在以下兩種情況:

Ø 僅需要一個描述依賴關(guān)系的規(guī)則,而不需要在規(guī)則中定義命令。例如

kbd.o command.o files.o: command.h



這個規(guī)則實現(xiàn)了給同時給三個目標(biāo)文件指定一個依賴文件。

² 對于多個具有類似重建命令的目標(biāo)。重建這些目標(biāo)的命令并不需要是絕對相同,因為我們可以在命令行中使用make的自動環(huán)變量“$@”來引用具體一個目標(biāo),并完成對它的重建(可參考 9.5.3 自動化變量 一小節(jié))。例如規(guī)則:



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



例子中的“generate”根據(jù)命令行參數(shù)來決定輸出文件的類型。使用了make的字符串處理函數(shù)“subst”來根據(jù)目標(biāo)產(chǎn)生對應(yīng)的命令行選項。

雖然在多目標(biāo)的規(guī)則中,可以根據(jù)不同的目標(biāo)使用不同的命令(在命令行中使用自動化變量“$@”)。但是,多目標(biāo)的規(guī)則并不能做到根據(jù)目標(biāo)文件自動改變依賴 文件,就像我們在上邊例子中使用自動化變量“$@”來改變規(guī)則的命令一樣。需要實現(xiàn)這個目的是,要用到make的靜態(tài)模式。



4.11多規(guī)則目標(biāo)
Makefile中,一個文件可以作為多個規(guī)則的目標(biāo)出現(xiàn)。這種情況時,此目標(biāo)文件的所有依賴文件將會被合并成此目標(biāo)一個依賴文件列表,其中任何一個依賴文件比目標(biāo)更新(比較目標(biāo)文件和依賴文件的時間戳)時,make將會執(zhí)行特定的命令來重建這個目標(biāo)。

對于一個多規(guī)則的目標(biāo),重建此目標(biāo)的命令只能出現(xiàn)在一個規(guī)則中(可以是多條命令)。如果多個規(guī)則同時給出重建此目標(biāo)的命令,make將使用最后一個規(guī)則所 以的命令,同時提示錯誤信息(一個特殊的例外是:使用“.”開頭的多規(guī)則目標(biāo)文件,可以在多個規(guī)則中給出多個重建命令。這種方式只是為了和其他版本 make進行兼容,一般在GNU make中應(yīng)該避免使用這個功能)。某些情況下,需要對相同的目標(biāo)使用不同的規(guī)則中所定義的命令,這種情況我們可使用另外一種方式“雙冒號”規(guī)則來實現(xiàn)。

一個僅僅描述依賴關(guān)系的描述規(guī)則可用來給出一個或做多個目標(biāo)文件的依賴文件。例如,Makefile中通常存在一個變量,就像以前我們提到的 “objects”,它定義為所有的需要編譯生成的.o文件的列表。當(dāng)這些.o文件在其源文件所包含的頭文件“config.h”發(fā)生變化之后能夠自動的 被重建,我們可以使用多目標(biāo)像下邊那樣來書寫我們的Makefile:





objects = foo.o bar.o

foo.o : defs.h

bar.o : defs.h test.h

$(objects) : config.h



這樣做的好處是:我們可以在源文件中增加或者刪除了包含的頭文件以后不用修改已經(jīng)存在的Makefile的規(guī)則,只需要增加或者刪除某一個.o文件依賴的 頭文件。這種方式很簡單也很方便。對于一個大的工程來說,這樣做的好處是顯而易見的。在一個大的工程中,對于一個單獨目錄下的.o文件的依賴規(guī)則建議使用 此方式。規(guī)則中頭文件的依賴描述也可以使用GCC自動產(chǎn)生??蓞⒖?3.14 自動產(chǎn)生依賴 一節(jié)

另外,我們也可以通過一個變量來增加目標(biāo)的依賴文件,使用make的命令行來指定某一個目標(biāo)的依賴頭文件,例如:



extradeps=

$(objects) : $(extradeps)



它的意思是:如果我們執(zhí)行“make extradeps=foo.h”那么“foo.h”將作為所有的.o文件的依賴文件。當(dāng)然我們只執(zhí)行“make”的話,就沒有指定任何文件作為.o文件的依賴文件。

在多規(guī)則的目標(biāo)中,如果目標(biāo)的任何一個規(guī)則沒有定義重建此目標(biāo)的命令,make將會尋找一個合適的隱含規(guī)則來重建此目標(biāo)。關(guān)于隱含規(guī)則可參考 第九章 make的隱含規(guī)則



4.12 靜態(tài)模式
靜態(tài)模式規(guī)則是這樣一個規(guī)則:規(guī)則存在多個目標(biāo),并且不同的目標(biāo)可以根據(jù)目標(biāo)文件的名字來自動構(gòu)造出依賴文件。靜態(tài)模式規(guī)則比多目標(biāo)規(guī)則更通用,它不需要多個目標(biāo)具有相同的依賴。但是靜態(tài)模式規(guī)則中的依賴文件必須是相類似的而不是完全相同的。

4.12.1 靜態(tài)模式規(guī)則的語法
首先,我們來看一下靜態(tài)模式規(guī)則的基本語法:





TARGETS ...: TARGET-PATTERN: PREREQ-PATTERNS ...

COMMANDS

...



“TAGETS”列出了此規(guī)則的一系列目標(biāo)文件。像普通規(guī)則的目標(biāo)一樣可以包含通配符。關(guān)于通配符的使用可參考 3.4 文件名使用通配符 一節(jié)

“TAGET-PATTERN”和“PREREQ-PATTERNS”說明了如何為每一個目標(biāo)文件生成依賴文件。從目標(biāo)模式(TAGET- PATTERN)的目標(biāo)名字中抽取一部分字符串(稱為“莖”)。使用“莖”替代依賴模式(PREREQ-PATTERNS)中的相應(yīng)部分來產(chǎn)生對應(yīng)目標(biāo)的 依賴文件。下邊詳細說明這一替代的過程。

首先在目標(biāo)模式和依賴模式中,一般需要包含模式字符“%”。在目標(biāo)模式(TAGET-PATTERN)中“%”可以匹配目標(biāo)文件的任何部分,模式字符 “%”匹配的部分就是“莖”。目標(biāo)文件和目標(biāo)模式的其余部分必須精確的匹配??匆粋€例子:目標(biāo)“foo.o”符合模式“%.o”,其“莖”為“foo”。 而目標(biāo)“foo.c”和“foo.out”就不符合此目標(biāo)模式。

每一個目標(biāo)的依賴文件是使用此目標(biāo)的“莖”代替依賴模式(PREREQ-PATTERNS)中的模式字符“%”而得到。例如:上邊的例子中依賴模式 (PREREQ-PATTERNS)為“%.c”,那么使用“莖”“foo”替代依賴模式中的“%”得到的依賴文件就是“foo.c”。需要明確的一點 是:在模式規(guī)則的依賴列表中使用不包含模式字符“%”也是合法的。代表這個文件是所有目標(biāo)的依賴文件。

在模式規(guī)則中字符‘%’可以用前面加反斜杠“\”方法引用。引用“%”的反斜杠也可以由更多的反斜杠引用。引用“%”、“\”的反斜杠在和文件名比較或由 “莖”代替它之前會從模式中被刪除。反斜杠不會因為引用“%”而混亂。如,模式“the\%weird\\%pattern\\”是“the%weird \”+“%”+“pattern\\”構(gòu)成。最后的兩個反斜杠由于沒有任何轉(zhuǎn)義引用“%”所以保持不變。

我們來看一個例子,它根據(jù)相應(yīng)的.c文件來編譯生成“foo.o”和“bar.o”文件:

objects = foo.o bar.o



all: $(objects)



$(objects): %.o: %.c

$(CC) -c $(CFLAGS) $< -o $@



例子中,規(guī)則描述了所有的.o文件的依賴文件為對應(yīng)的.c文件,對于目標(biāo)“foo.o”,取其莖“foo”替代對應(yīng)的依賴模式“%.c”中的模式字符 “%”之后可得到目標(biāo)的依賴文件“foo.c”。這就是目標(biāo)“foo.o”的依賴關(guān)系“foo.o: foo.c”,規(guī)則的命令行描述了如何完成由“foo.c”編譯生成目標(biāo)“foo.o”。命令行中“$<”和“$@”是自動化變量,“$<” 表示規(guī)則中的第一個依賴文件,“$@”表示規(guī)則中的目標(biāo)文件(可參考 3.14 自動產(chǎn)生依賴 一節(jié))。以上的規(guī)則就是描述了以下兩個具體的規(guī)則:



foo.o : foo.c

$(CC) -c $(CFLAGS) foo.c -o foo.o

bar.o : bar.c

$(CC) -c $(CFLAGS) bar.c -o bar.o



在使用靜態(tài)模式規(guī)則時,指定的目標(biāo)必須和目標(biāo)模式相匹配,否則在執(zhí)行make時將會得到一個錯誤提示。如果存在一個文件列表,其中一部分符合某一種模式而 另外一部分符合另外一種模式,這種情況下我們可以使用“filter”函數(shù)(可參考 第七章 make的內(nèi)嵌函數(shù))來對這個文件列表進行分類,在分類之后對確定的某一類使用模式規(guī)則。例如:



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”函數(shù)過濾不符合“%.o”模式的文件名而至返回所有符合此模式的文件列表。第一條靜態(tài)模式規(guī)則描述了這些目標(biāo)文件是通過 編譯對應(yīng)的.c源文件來重建的。同樣第二條規(guī)則也是使用這種方式。

我們通過另外一個例子來看一下自動環(huán)變量“$*”在靜態(tài)模式規(guī)則中的使用方法:



bigoutput littleoutput : %output : text.g

generate text.g -$* > $@



當(dāng)執(zhí)行此規(guī)則的命令時,自動環(huán)變量“$*”被展開為“莖”。在這里就是“big”和“little”。

靜態(tài)模式規(guī)則在一個較大的工程中非常有用的。它可以對一個工程中的同類文件的重建規(guī)則進行一次定義,而實現(xiàn)對整個工程中此類文件指定相同的重建規(guī)則。比 如,可以用來描述整個工程中所有的.o文件的依賴規(guī)則和編譯命令。通常的做法是將生成同一類目標(biāo)的模式定義在一個make.rules的文件中。在工程各 個模塊的Makefile中包含此文件。

4.12.2 靜態(tài)模式和隱含規(guī)則
在Makefile中,靜態(tài)模式規(guī)則和被定義為隱含規(guī)則的模式規(guī)則都是我們經(jīng)常使用的兩種方式。兩者相同的地方都是用目標(biāo)模式和依賴模式來構(gòu)建目標(biāo)的規(guī)則中的文件依賴關(guān)系,兩者不同的地方是make在執(zhí)行時使用它們的時機。

隱含規(guī)則可被用在任何和它相匹配的目標(biāo)上,在Makefile中沒有為這個目標(biāo)指定具體的規(guī)則、存在規(guī)則但規(guī)則沒有命令行或者這個目標(biāo)的依賴文件可被搜尋到。當(dāng)存在多個隱含規(guī)則和目標(biāo)模式相匹配時,只執(zhí)行其中的一個規(guī)則。具體執(zhí)行哪一個規(guī)則取決于定義規(guī)則的順序。

相反的,靜態(tài)模式規(guī)則只能用在規(guī)則中明確指出的那些文件的重建過程中。不能用在除此之外的任何文件的重建過程中,并且它對指定的每一個目標(biāo)來說是唯一的。如果一個目標(biāo)存在于兩個規(guī)則,并且每一個規(guī)則中都定以了命令,make執(zhí)行時就會提示錯誤。

靜態(tài)模式規(guī)則相比隱含模式規(guī)則由以下兩個優(yōu)點:

² 不能根據(jù)文件名通過詞法分析進行分類的文件,我們可以明確列出這些文件,并使用靜態(tài)模式規(guī)則來重建其隱含規(guī)則。

² 對于無法確定工作目錄內(nèi)容,而且不能確定是否此目錄下的無關(guān)文件會使用錯誤的隱含規(guī)則而導(dǎo)致make失敗的情況。當(dāng)存在多個適合此文件的隱含規(guī)則時,使用 哪一個隱含規(guī)則取決于其規(guī)則的定義順序。這種情況下我們使用靜態(tài)模式規(guī)則就可以避免這些不確定因素,因為靜態(tài)模式中,指定的目標(biāo)文件有特定的規(guī)則來描述其 依賴關(guān)系和重建命令。

4.13 雙冒號規(guī)則
雙冒號規(guī)則就是使用“::”代替普通規(guī)則的“:”得到的規(guī)則。當(dāng)同一個文件作為多個規(guī)則的目標(biāo)時,雙冒號規(guī)則的處理和普通規(guī)則的處理過程完全不同(雙冒號規(guī)則允許在多個規(guī)則中為同一個目標(biāo)指定不同的重建目標(biāo)的命令)。

首先需要明確的是:Makefile中,一個目標(biāo)可以出現(xiàn)在多個規(guī)則中。但是這些規(guī)則必須是同一種規(guī)則,要么都是普通規(guī)則,要么都是雙冒號規(guī)則。而不允許一個目標(biāo)同時出現(xiàn)在兩種不同的規(guī)則中。雙冒號規(guī)則和普通規(guī)則的處理的不同點表現(xiàn)在以下幾個方面:

1. 雙冒號規(guī)則中,當(dāng)依賴文件比目標(biāo)更新時。規(guī)則將會被執(zhí)行。對于一個沒有依賴而只有命令行的雙冒號規(guī)則,當(dāng)引用此目標(biāo)時,規(guī)則的命令將會被無條件執(zhí)行。而普通規(guī)則,當(dāng)規(guī)則的目標(biāo)文件存在時,此規(guī)則的命令永遠不會被執(zhí)行(目標(biāo)文件永遠是最新的)。

2. 當(dāng)同一個文件作為多個雙冒號規(guī)則的目標(biāo)時。這些不同的規(guī)則會被獨立的處理,而不是像普通規(guī)則那樣合并所有的依賴到一個目標(biāo)文件。這就意味著對這些規(guī)則的處 理就像多個不同的普通規(guī)則一樣。就是說多個雙冒號規(guī)則中的每一個的依賴文件被改變之后,make只執(zhí)行此規(guī)則定義的命令,而其它的以這個文件作為目標(biāo)的雙 冒號規(guī)則將不會被執(zhí)行。

我們來看一個例子,在我們的Makefile中包含以下兩個規(guī)則:



Newprog :: foo.c

$(CC) $(CFLAGS) $< -o $@

Newprog :: bar.c

$(CC) $(CFLAGS) $< -o $@



如果“foo.c”文件被修改,執(zhí)行make以后將根據(jù)“foo.c”文件重建目標(biāo)“Newprog”。而如果“bar.c”被修改那么 “Newprog”將根據(jù)“bar.c”被重建?;叵胍幌?,如果以上兩個規(guī)則為普通規(guī)時出現(xiàn)的情況是什么?(make將會出錯并提示錯誤信息)

當(dāng)同一個目標(biāo)出現(xiàn)在多個雙冒號規(guī)則中時,規(guī)則的執(zhí)行順序和普通規(guī)則的執(zhí)行順序一樣,按照其在Makefile中的書寫順序執(zhí)行。

GNU make的雙冒號規(guī)則給我們提供一種根據(jù)依賴的更新情況而執(zhí)行不同的命令來重建同一目標(biāo)的機制。一般這種需要的情況很少,所以雙冒號規(guī)則的使用比較罕見。一般雙冒號規(guī)則都需要定義命令,如果一個雙冒號規(guī)則沒有定義命令,在執(zhí)行規(guī)則時將為其目標(biāo)自動查找隱含規(guī)則。

4.14 自動產(chǎn)生依賴
Makefile中,可能需要書寫一些規(guī)則來描述一個.o目標(biāo)文件和頭文件的依賴關(guān)系。例如,如果在main.c中使用“#include defs.h”,那么我們可能需要如下那樣的一個規(guī)則來描述當(dāng)頭文件“defs.h”被修改以后執(zhí)行make,目標(biāo)“main.o”應(yīng)該被重建。



main.o: defs.h



這樣,在一個比較大型的工程中。就需要在Makefile中書寫很多條類似于這樣的規(guī)則。并且,當(dāng)在源文件中加入或刪除頭文件后,也需要小心地去修改 Makefile。這是一件很費力、也很費時并且容易出錯誤的工作。為了避免這個令人討厭的問題,現(xiàn)代的c編譯器提供了通過查找源文件中的 “#include”來自動產(chǎn)生這種依賴的功能。“GCC”支持一個“-M”的選項來實現(xiàn)此功能。“GCC”將自動找尋源文件中包含的頭文件,并生成一個 依賴關(guān)系。例如,如果“main.c”只包含了頭文件“defs.h”,那么在Linxu下執(zhí)行下面的命令:



gcc -M main.c



其輸出是:



main.o : main.c defs.h



既然編譯器已經(jīng)提供了自動產(chǎn)生依賴關(guān)系的功能,那么我們就不需要去動手寫這些規(guī)則的依賴關(guān)系了。但是需要明確的是:在“main.c”中包含了其他的標(biāo)準 庫的頭文件,其輸出的依賴關(guān)系中也包含了標(biāo)準庫的頭文件。當(dāng)不需要依賴關(guān)系中不考慮標(biāo)準庫頭文件時,需要使用“-MM”參數(shù)。

需要注意的是,在使用“GCC”自動產(chǎn)生依賴關(guān)系時,所產(chǎn)生的規(guī)則中明確的指明了目標(biāo)是“main.o”文件。一次在通過.c文件直接產(chǎn)生可執(zhí)行文件時,作為過程文件的“main.o”的中間過程文件在使用完之后將不會被刪除。

在舊版本的make中,使用編譯器此項功能通常的做法是:在Makefile中書寫一個偽目標(biāo)“depend”的規(guī)則來定義自動產(chǎn)生依賴關(guān)系文件的命令。 輸入“make depend”將生成一個稱為“depend”的文件,其中包含了所有源文件的依賴規(guī)則描述。Makefile使用“include”指示符包含這個文 件。

在新版本的make中,推薦的方式是為每一個源文件產(chǎn)生一個描述其依賴關(guān)系的makefile文件。對于一個源文件“NAME.c”,對應(yīng)的這個 makefile文件為“NAME.d”。“NAME.d”中描述了文件“NAME.o”所要依賴的所有頭文件。采用這種方式,只有源文件在修改之后才會 重新使用命令生成新的依賴關(guān)系描述文件“NAME.o”。

我們可以使用如下的模式規(guī)則來自動生成每一個.c文件對應(yīng)的.d文件:



%.d: %.c

$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \

sed ‘s,\($*\)\.o[ :]*,\1.o $@ : ,g‘ < $@.$$$$ > $@; \

rm -f $@.$$$$



此規(guī)則的含義是:所有的.d文件依賴于同名的.c文件。

第一行;使用c編譯器自自動生成依賴文件($<)的頭文件的依賴關(guān)系,并輸出成為一個臨時文件,“$$$$”表示當(dāng)前進程號。如果$(CC)為 GNU的C編譯工具,產(chǎn)生的依賴關(guān)系的規(guī)則中,依賴頭文件包括了所有的使用的系統(tǒng)頭文件和用戶定義的頭文件。如果需要生成的依賴描述文件不包含系統(tǒng)頭文 件,可使用“-MM”代替“-M”。

第二行;使用sed處理第二行已產(chǎn)生的那個臨時文件并生成此規(guī)則的目標(biāo)文件。這里sed完成了如下的轉(zhuǎn)換過程。例如對已一個.c源文件。將編譯器產(chǎn)生的依賴關(guān)系:

main.o : main.c defs.h

轉(zhuǎn)成:

main.o main.d : main.c defs.h



這樣就將.d加入到了規(guī)則的目標(biāo)中,其和對應(yīng)的.o文件文件一樣依賴于對應(yīng)的.c源文件和源文件所包含的頭文件。當(dāng).c源文件或者頭文件被改變之后規(guī)則將會被執(zhí)行,相應(yīng)的.d文件同樣會被更新。

第三行;刪除臨時文件。

使用上例的規(guī)則就可以建立一個描述目標(biāo)文件依賴關(guān)系的.d文件。我們可以在Makefile中使用include指示符將描述將這個文件包含進來。在執(zhí)行 make時,Makefile所包含的所有.d文件就會被自動創(chuàng)建或者更新。Makefile中對當(dāng)前目錄下.d文件處理可以參考如下:



sources = foo.c bar.c

sinclude $(sources:.c=.d)



例子中,變量“sources”定義了當(dāng)前目錄下的需要編譯的源文件。變量引用變換“$(sources : .c=.d)”的功能是根據(jù)需要.c文件自動產(chǎn)生對應(yīng)的.d文件,并在當(dāng)前Makefile文件中包含這些.d文件。.d文件和其它的makefile文 件一樣,make在執(zhí)行時讀取并試圖重建他們。其實這些.d文件也是一些可被make解析的makefile文件。

需要注意的是include指示符的書寫順序,因為在這些.d文件中已經(jīng)存在規(guī)則。當(dāng)一個Makefile使用指示符include這些.d文件時,應(yīng)該 注意它應(yīng)該出現(xiàn)在終極目標(biāo)之后,以免.d文件中的規(guī)則被是Makefile的終極規(guī)則。關(guān)于這個前面我們已經(jīng)有了比較詳細的討論。
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Makefile教程: 跟我一起寫Makefile -- LearnMakefile
跟我一起寫Makefile 3—— 書寫規(guī)則
怎樣寫Makefile文件(C語言部分)
Makefile VPATH和vpath的使用
Makefile筆記
如何編寫Makefile一份由淺入深的Makefile全攻略(make是如何工作的)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服