久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

GNU make中文手冊-第五章:規(guī)則的命令

 todaytomo 2006-12-30
GNU make中文手冊-第五章:規(guī)則的命令 作者: hew  發(fā)布日期: 2006-3-21    查看數(shù): 142   出自: http://www.
第五章:規(guī)則的命令

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

規(guī)則的命令由一些shell命令行組成,他們被一條一條的執(zhí)行,。規(guī)則中除了第一條緊跟在依賴列表之后使用分號隔開的命令以外,,其它的每一行命令行必需一 [Tab]字符開始。多個命令行之間可以有空行和注釋行(所謂空行,,就是沒有包含任何字符的一行,。如果以[Tab]鍵開始而其后沒有命令的行,此行不是空 行,。是空命令行),,在執(zhí)行規(guī)則時他們將被忽略。

可能用戶使用了多個不同的shell,。但是在make處理Makefile過程是,,如果沒有明確指定,那么對所有規(guī)則中命令行的解析使用“/bin/sh”來完成,。

使用的shell決定了規(guī)則中的命令的語法和處理機制,。當使用默認的“/bin/sh”時,命令中字符“#”到行末的內(nèi)容被認為是注釋,。當然了“#”可以不在此行的行首,,此時“#”之前的內(nèi)容不會被作為注視處理。

另外在make解析makefile文件時,,對待注釋也是采用同樣的處理方式,。我們的shell腳本也一樣。

5.1 命令回顯
通常,,make在執(zhí)行命令行之前會把要執(zhí)行的命令行進行輸出,。我們稱之為“回顯”,就好像我們輸入命令執(zhí)行一樣,。

如果要執(zhí)行的命令行以字符“@”開始,,則make在執(zhí)行時這個命令就不會被回顯。典型的用法是我們在使用“echo”命令輸出一些信息時,。如:



@echo 開始編譯XXX模塊......



當make執(zhí)行時,,將輸出“開始編譯XXX模塊......”這個信息。如果在命令行之前沒有字符“@”,,那么,,make的輸出就是:



echo編譯XXX模塊......

編譯XXX模塊......



另外,如果使用make的命令行參數(shù)“-n”或“--just-print”,,那么make執(zhí)行時只顯示所要執(zhí)行的命令,,但不會真正的去執(zhí)行這些命令。只 有在這種情況下make才會打印出所有make需要執(zhí)行的命令,其中也包括了使用“@”字符開始的命令,。這個選項對于我們調(diào)試Makefile非常有用,, 使用這個選項我們可以按執(zhí)行順序打印出Makefile中所有需要執(zhí)行的命令。

而make參數(shù)“-s”或“--slient”則是禁止所有執(zhí)行命令的顯示,,就好像所有的命令行均使用“@”開始一樣,。在Makefile中使用沒有依賴 的特殊目標“.SILENT”也可以禁止命令的回顯,但是它的缺點是不如“@”靈活,。因此我們在書寫Makefile時,,推薦使用“@”來控制命令的回 顯。

5.2 命令的執(zhí)行
規(guī)則中,,當目標需要被重建時,。此規(guī)則所定義的命令將會被執(zhí)行,如果是多行命令,,那么make就為每一行命令使用一個獨立的子shell去執(zhí)行,。因此,多行命令之間的執(zhí)行是相互獨立的,,相互之間不存在依賴,。

而在Makefile中書寫在同一行中的多個命令屬于一個完整的shell命令行,書寫在獨立行的一條命令是一個獨立的shell命令行,。所以需要注意: 在一個規(guī)則的命令中,,命令行“cd”改變目錄不會對其后的命令的執(zhí)行產(chǎn)生影響。就是說其后的命令執(zhí)行的工作目錄不會是之前使用“cd”進入的那個目錄,。如 果要實現(xiàn)這個目的,,就不能把“cd”和其后的命令放在兩行來書寫。而應該把這兩條命令寫在一行上,,用分號分隔,。這樣它們才是一個完整的shell命令行。 如:



foo : bar/lose

cd bar; gobble lose > ../foo



如果希望把一個完整的shell命令行書寫在多行上,,需要使用反斜杠(\)來對處于多行的命令進行連接,,表示他們是一個完整的shell命令行。例如上例我們以也可以這樣書寫:



foo : bar/lose

cd bar; \

gobble lose > ../foo



make對所有規(guī)則命令的解析使用環(huán)境變量“SHELL”所指定的那個程序,,在GNU make中,,默認的程序是“/bin/sh”。

不像其他絕大多數(shù)變量,,它們的值可以直接從同名的系統(tǒng)環(huán)境變量那里獲得,。make的環(huán)境變量“SHELL”沒有使用系統(tǒng)環(huán)境變量的定義。因為系統(tǒng)環(huán)境變量 “SHELL”指定那個程序被用來作為用戶和系統(tǒng)交互的接口程序,,它對于不存在直接交互過程的make顯然不合適,。在make的環(huán)境變量中“SHELL” 會被重新賦值,;它作為一個變量我們也可以在Makefile中明確地給它賦值(指出解釋程序的名字,當明確指定時需要使用完整的路徑名,。如 “/bin/sh”),,變量“SHELL”其值默認為“/bin/sh”。

(在MS-DOS下有些不同,, MS-DOS不存在SHELL環(huán)境變量。在MS-DOS下make的方法省略了,,有興趣地可以自行參考info make)

5.3 并發(fā)執(zhí)行命令
GUN make可以同時執(zhí)行多條命令,。通常情況下,一個時刻只有一個命令在執(zhí)行,,下一個命令在當前命令執(zhí)行完成之后才能夠被執(zhí)行,。不過可以通過make的命令行 選項“-j”或者“--job”來告訴make在同一時刻可以允許多條命令同時被執(zhí)行(注意,在MS-DOS中此選項無效,,因為它是單任務操作系統(tǒng)),。

如果選項“-j”之后存在一個整數(shù),其含義是告訴make在同一時刻可允許同時執(zhí)行命令的數(shù)目,。這個數(shù)字被稱為“job slots”,。當“-j”選項之后沒有出現(xiàn)一個數(shù)字時,那么同一時刻執(zhí)行的命令數(shù)目沒有要求,。使用默認的“job slots”,,其值為1。表示make將串行的執(zhí)行規(guī)則的命令(同一時刻只能有一條命令被執(zhí)行),。

并行執(zhí)行命令所帶來的問題是顯而易見:

1. 同一時刻的多個被執(zhí)行的命令同時輸出,,造成輸出到終端的信息的交替,顯得凌亂,。當出現(xiàn)問題是很難命令執(zhí)行失敗時很難根據(jù)命令的輸出信息來定位錯誤,。

2. 在同一時刻可能會存在多個命令執(zhí)行進程讀取標準輸入,但是對于標注輸入來設備來說,,在同一時刻只能有一個進程訪問輸入設備,。就是說在某個時間點,make 只能保證這個時刻正在執(zhí)行的進程中的一個進程讀取標準輸入流,,而其它的進程的標準輸入流將變得無效,。因此當同一時刻存在多個執(zhí)行命令的進程需要讀取標準輸 入流時其它的將會出輸入流無效導致致命錯誤(通常此進程會得到操作系統(tǒng)的管道破裂信號而被終止)。



這是因為:執(zhí)行中的命令在什么時候會讀取標準輸入流(終端輸入或重定向的標準輸入)是不可預測的,。而得到標準輸入的順序總是按照先來先獲得的原則。那個命 令首先被執(zhí)行,,那么它就可以首先得到標準輸入設備,。而其它后續(xù)需要獲取標準輸入設備的命令執(zhí)行進程,,由于不能得到標準輸入而產(chǎn)生致命錯誤。再 Makefile規(guī)則中如果存在很多命令需要讀取標準輸入設備,,而它們又被允許并行執(zhí)行時,,就會出現(xiàn)這樣的錯誤。



對這個問題的解決,。我們可以修改Makefile的規(guī)則命令使之在執(zhí)行過程中不使用標準輸入設備,。當然也可以實現(xiàn)在只存在一個命令在執(zhí)行時會訪問標準輸入流的Makefile,。

3. 會導致make的遞歸調(diào)用出現(xiàn)問題,??蓞⒖?5.6 make的遞歸執(zhí)行 一節(jié),。

Make在執(zhí)行一個命令時,,如果某一條命令執(zhí)行失?。ū灰粋€信號中止,,或非零退出),,且該條命令產(chǎn)生的錯誤不可忽略(,,那么其它的用于重建同一目標的命令 執(zhí)行將會被終止,。此種情況下,如果make沒有使用“-k”或“--keep-going”選項,,make將停止繼續(xù)執(zhí)行直接退出,。另外:如果make在 執(zhí)行時,由某種原因(包括信號)被中止,,此時它的子進程(那些執(zhí)行規(guī)則命令行的shell子進程)正在運行,,那么make將等到所有這些子進程結(jié)束之后才 真正退出。

在執(zhí)行make時,,如果系統(tǒng)運行于重負荷狀態(tài)下,,我們需要控制(減輕)系統(tǒng)在執(zhí)行make時的負荷??梢允褂?#8220;-l”選項告訴make限制當前運行的任務 的數(shù)量(make所限制的只是它本身所需要占用的系統(tǒng)負載,,而不能通過它去控制其它的任務所占用的系統(tǒng)負載)。“-l”或“--max-load”選項一 般后邊需要跟一個浮點數(shù),。例如:

-l 2.5



它的意思是告訴make當系統(tǒng)平均負荷高于2.5時,,不再啟動任何執(zhí)行命令的子任務。不帶浮點數(shù)的“-l”選項用于取消前面通“-l”給定的負荷限制,。

更為準確一點就是:每一次,,make在啟動一項任務之前,當前系統(tǒng)至少一項make的子任務正在運行,。首先make會檢查當前系統(tǒng)的負荷,;如果當前系統(tǒng)的負荷高于通過“-l”選項指定的值,那么make就不會在其他任務完成之前啟動任何任務,。缺省情況下沒有負荷限制,。

5.4 命令執(zhí)行的錯誤
通常,;規(guī)則中的命令在運行結(jié)束后,make會檢測命令執(zhí)行的返回狀態(tài),,如果返回成功,,那么就在另外一個子shell下執(zhí)行下一條命令。規(guī)則中的所有命令執(zhí) 行完成之后,,這個規(guī)則就執(zhí)行完成了,。如果一個規(guī)則中的某一個命令出錯(返回狀態(tài)非0),make就會放棄對當前規(guī)則的執(zhí)行,,也有可能會終止所有規(guī)則的執(zhí) 行,。

在一些情況下,規(guī)則中的一個命令的執(zhí)行失敗并不代表規(guī)則執(zhí)行的錯誤,。例如我們使用“mkdir”命令來確保存在一個目錄。當此目錄不存在使我們就建立這個 目錄,,當目錄存在時那么“mkdir”就會執(zhí)行失敗,。其實我們并不希望mkdir在執(zhí)行失敗后終止規(guī)則的執(zhí)行。為了忽略一些無關命令執(zhí)行失敗的情況,,我們 可以在命令之前加一個減號“-”(在[Tab]字符之后),,來告訴make忽略此命令的執(zhí)行失敗。命令中的“-”號會在在shell解析并執(zhí)行此命令之前 被去掉,,shell所解釋的只是純粹的命令,,“-”字符是由make來處理的。例如對于“clean”目標我們就可以這么寫:



clean:

-rm *.o



其含義是:即使執(zhí)行“rm”刪除文件失敗,,make也繼續(xù)執(zhí)行,。

在執(zhí)行make時,如果使用命令行選項“-i”或者“—ignore-errors”,, make將會忽略所有規(guī)則中命令執(zhí)行執(zhí)行的錯誤,。沒有依賴的特殊目標“.IGNORE”在Makefile中有同樣的效果。但是“.IGNORE”的方式 已經(jīng)很少使用,,因為它沒有在命令行之前使用“-”字符方式靈活,。

當使用make的“-i”選項或者使用“-”字符來忽略命令執(zhí)行錯誤時,make始終會把命令的執(zhí)行結(jié)果作為成功來對待,。但會提示錯誤信息,,同時提示這個錯誤被忽略。

當沒有使用這種方式來通知make忽略命令的執(zhí)行錯誤時,,而在錯誤發(fā)生時,,就意味著定義這個命令的規(guī)則的目標不能被正確重建,同樣,,和此目標相關的其它目標也不會被正確重建,。因此,,由于先決條件不能建立,后續(xù)的命令將不會執(zhí)行,。

在發(fā)生這樣情況時,,一般make會立刻退出并返回一個非0狀態(tài),表示執(zhí)行失敗,。像對待命令執(zhí)行的錯誤一樣,,我們可以使用make的命令行選項“-k”或者 “--keep-going”來通知make,當出現(xiàn)錯誤時不立即退出,,而是繼續(xù)后續(xù)命令的執(zhí)行,。直到無法繼續(xù)執(zhí)行命令時才異常退出。例如:使用“-k” 參數(shù),,在重建一個.o文件目標時出現(xiàn)錯誤,,make不會立即退出。雖然make已經(jīng)知道因為這個錯誤而無法完成終極目標的重建,,但還是繼續(xù)完成其它后續(xù)的 依賴文件的重建,。直到執(zhí)行最后鏈接時才錯誤退出。

一般“-k”參數(shù)在實際中應用,。它的用途主要在:當同時修改了工程中的多個文件后,,“-k”參數(shù)可以幫助我們確認對那些文件的修改是正確的(可以被編 譯),那些文件的修改是不不正確的(不能正確編譯),。例如我們修改了工程中的20個源文件,,修改完成之后使用“-k”參數(shù)來進行make,它可以一次性找 出修改的20個文件中哪些是不能被編譯的,。

通常情況下,,執(zhí)行失敗的命令一旦改變了它所在規(guī)則的目標文件,則這個改變了的目標可能不是一個被正確重建的文件,。但是這個文件的時間戳已經(jīng)被更新過了(這 種情況也會發(fā)生在使用一個信號來強制中止命令執(zhí)行的時候),。因此在下一次執(zhí)行make時,由于時間戳更新它不會被再次重建,。因此終極目標的重建很難保證是 正確的,。為了避免這種錯誤的出現(xiàn),應該在一次make執(zhí)行失敗之后使用“make clean”來清除已經(jīng)重建的所有目標,,之后再執(zhí)行make,。我們也可以讓make自動完成這個動作,實現(xiàn)這個目的我們只需要Makefile中定義特殊 目標“.DELETE_ON_ERROR”,。但是這個做法存在不兼容,。推薦的做法是:在make執(zhí)行失敗時,修改錯誤之后執(zhí)行make之前,,使用 “make clean”明確的刪除第一次錯誤重建的所有目標,。

本節(jié)的最后,,需要說明的是:雖然make提供了命令行選項來忽略命令執(zhí)行的錯誤。建議對于此選項謹慎使用,。因為在一個大型的工程中,,可能需要對成千個源文 件進行編譯。編譯過程中的任何一個文件的編譯錯誤都不能被忽略,。否則有可能最后完成的終極目標可能就是一個讓你感到迷惑的東西,,或者在運行時會產(chǎn)生一些莫 名奇妙的現(xiàn)象。這需要程序員來保證其書寫的Makefile的規(guī)則中的命令在執(zhí)行時不會發(fā)生錯誤,。特別需要注意哪些實現(xiàn)特殊目的規(guī)則的命令書寫,。當所有命 令都可以被正確執(zhí)行時,我們就沒有必要為了避免一些討厭的錯誤而使用“-i”選項,,可以使用其它的方式來實現(xiàn),。例如刪除命令我們可以這樣寫: “$(RM)”或者“rm -f”,創(chuàng)建目錄的命令可以這樣寫:“mkdir –p ”等等,。



5.5 中斷make的執(zhí)行
make在執(zhí)行命令時如果收到一個致命信號(結(jié)束make),,make將會刪除命令重建的規(guī)則目標文件。其依據(jù)是此目標文件的當前時間戳是否和make開始時的時間戳相同,。

刪除這個目標文件的目的是為了確保下一次make時目標文件能夠被正確重建。其原因我們上一節(jié)已經(jīng)有所討論,。假設正在編譯文件是鍵入“Ctrl-c”,,在 這時編譯器已經(jīng)開始寫文件“foo.o”,但是“Ctrl-c”產(chǎn)生的信號關閉了編譯器,。這種情況時文件“foo.o”可能是不完整的,,但這個內(nèi)容不完整 的“foo.o”文件的時間戳比源程序‘foo.c’的時間戳新。如果在make收到終止信號后不刪除文件“foo.o”而直接退出,,那么下次執(zhí)行 make時make將認為該文件已是最新而不會去重建文件它,。最后在鏈接程序生成終極目標時可能由于某一個.o文件的不完整,導致出現(xiàn)一些奇怪的令人難以 理解的錯誤信息,。

同時,,我們可以在Makefile中將一個目標文件作為特殊目標“.PRECIOUS”的依賴,這樣可以防止make在重建這個目標時異常終止時對這個目 標文件的刪除動作,。每次make在重建一個目標之前,,首先判斷該目標文件是否出現(xiàn)在特殊目標“.PRECIOUS”的依賴列表中,決定在終止信號發(fā)生時需 不需要刪除這個目標文件,。不刪除這種目標文件的原因可能是:1. 目標的重建動作是一個原子的不可被中斷的過程,;2. 目標文件的存在僅僅為了記錄其重建時間(不關心其內(nèi)容無);3. 這個目標文件必須一直存在來防止其它麻煩,。

5.6 make的遞歸執(zhí)行
make的遞歸調(diào)用指的是:在Makefile中使用“make”作為一個命令來執(zhí)行本身或者其它makefile文件,。遞歸調(diào)用在一個村在有多級子目錄 的項目中非常有用,。例如,當前目錄下存在一個“subdir”子目錄,,這個子目錄中有描述這個目錄編譯規(guī)則的makefile文件,,在執(zhí)行make時需要 從上層目錄(當前目錄)開始并完成它所有子目錄的編譯。那么在當前目錄下可以使用這樣一個規(guī)則來實現(xiàn)對它的子目錄的編譯:



subsystem:

cd subdir && $(MAKE)



其等價于規(guī)則:



subsystem:

$(MAKE) -C subdir



我們對這兩個規(guī)則的命令進行簡單說明,,規(guī)則中“$(MAKE)”是對變量“MAKE”(下一小節(jié)將詳細討論)的引用(關于變量可參考 第六章 Makefile中的變量 ),。第一個規(guī)則命令的意思是:進入子目錄,然后在子目錄下執(zhí)行make,。第二個規(guī)則時用了make的“-C”選項,,同樣是首先進入子目錄而后再執(zhí)行 make。

書寫這樣的規(guī)則應該來說對于我們不是什么大問題,,但是其中有一些需要我們深入了解的東西,。首先需要了解它如何工作、上層make(在當前目錄下運行的 make進程)和下層make(subdir目錄下運行的make進程)之間存在的聯(lián)系,。也許會發(fā)現(xiàn)這兩個規(guī)則的實現(xiàn),,使用偽目標更能提高效率。

在make的遞歸調(diào)用中,,需要了解變量“CURDIR”,,此變量代表了make當前的工作路徑。如果使用“-C”選項進入一個子目錄后,,此變量將被重新賦 值,。總之,,如果在Makefile中沒有對此變量進行顯式的賦值操作,,那么它代表make的當前工作目錄。我們也可以在Makefile為這個變量賦一個 新的值,。此時這變量將不再代表make的工作目錄,。

5.6.1 變量MAKE
在使用make的遞歸調(diào)用時,在Makefile中規(guī)則的命令行中應該使用變量“MAKE”來代替直接使用“make”,。像上一小節(jié)的例子:



subsystem:

cd subdir && $(MAKE)



變量“MAKE”的值是“make”程序的文件名,。如果其值為“/bin/make”那么上邊規(guī)則的命令就為“cd subdir && /bin/make”。這樣做的好處是:當我們使用一個其它版本的make程序時,,可以保證最上層使用的make程序和其子目錄下執(zhí)行的make保持一 致,。

另外使用此變量的另外一個特點是:當規(guī)則命令行中變量MAKE是,它可以改變make的“-t”(“--touch”),,“-n”(“--just- print”)和“-q”(“--question”)命令行選項的效果,。它所實現(xiàn)的功能和在規(guī)則中命令行首使用字符“+”的效果相同(可參考 9.3 替代命令的執(zhí)行 一節(jié))。

在規(guī)則的命令行中使用“make”代替了“$(MAKE)”以后,上例子規(guī)則的命令行為:“cd subdir && make”,。在我們執(zhí)行“make -t”(“-t”選項用來更新所有目標的時間戳,,而不執(zhí)行任何規(guī)則的命令,參考 9.7 make的命令行選項 一節(jié)),,結(jié)果是僅僅創(chuàng)建一個名為“subsystem”的文件,,不會進入到目錄“subdir”去更新此目錄下文件的時間戳。我們使用“-t”命令行參數(shù) 的初衷是對規(guī)則中的目標文件的時間戳進行更新,。而如果使“cd subdir && $(MAKE)”作為規(guī)則的命令行,,執(zhí)行“make -t”就可以實現(xiàn)我們的初衷。

變量“MAKE”的這個特點是:在規(guī)則的命令行中如果使用變量“MAKE”,,標志“-t”,、“-n”和“-q”在這個命令的執(zhí)行中不起作用。盡管這些選項 是告訴make不執(zhí)行規(guī)則的命令行,,但包含變量“MAKE”的命令行除外,,它們會被正常執(zhí)行。同時,,執(zhí)行make時的命令行選項參數(shù)被通過一個變量 “MAKEFLAGS”傳遞給子目錄下的make程序,。

例如,當使用make的命令行選項“-t”來更新目標的時間戳或者“-n”選項打印命令時,,這些選項將會被賦值給變量“MAKEFLAGS”被傳遞到下一 級的make程序中,。在下一級子目錄中執(zhí)行的make,這些選項會被附加作為make的命令行參數(shù)來執(zhí)行,,和在此目錄下使用“make -t”或者“make -n”有相同的效果,。

5.6.2 變量和遞歸
在make的遞歸執(zhí)行過程中,上層make可以明確指定將一些變量的定義通過環(huán)境變量的方式傳遞給子make過程,。沒有明確指定需要傳遞的變量,上層 make不會將其所執(zhí)行的Makefile中定義的變量傳遞給子make過程,。使用環(huán)境變量傳遞上層所定義的變量時,,上層所傳遞給子make過程的變量定 義不會覆蓋子make過程所執(zhí)行makefile文件中的同名變量定義。

如果子make過程所執(zhí)行Makefile中存在同名變量定義,,則上層傳遞的變量定義不會覆蓋子Makefile中定義的值,。就是說如果上層make傳遞 的變量和子make所執(zhí)行的Makefile中存在重復的變量定義,則以子Makefile中的變量定義為準,。除非使用make的“-e”選項,。

我們在本屆的第一段中提到,當上層make過程要將所執(zhí)行的Makefile中的變量傳遞給子make過程時,,需要明確地指出,。在GNU make中,實現(xiàn)此功能的指示符是“export”。當一個變量使用“export”進行聲明后,,變量和變量的值將被加入到當前工作的環(huán)境變量中,,以后 make所執(zhí)行的所有規(guī)則的命令都可以使用這個變量。而當沒有使用指示符“export”對任何變量進行聲明的情況下,,上層make只將那些已經(jīng)初始化的 環(huán)境變量(在執(zhí)行make之前已經(jīng)存在的環(huán)境變量)和使用命令行指定的變量(如命令“make CFLAGS +=-g”或者“make –e CFLAGS +=-g”)傳遞給子make程序,,通常這些變量由字符、數(shù)字和下劃線組成,。需要注意的是:有些shell不能處理那些名字中包含(除字母,、數(shù)字、下劃線 以外)其他字符的變量,。

兩個特殊的變量“SHELL”和“MAKEFLAGS”除非使用指示符“unexport”對它們進行聲明,,否則在整個make的執(zhí)行過程中它們會始終被 自動的傳遞給子make。另外一個變量“MAKEFILES”,,如果此變量有值(不為空)那么同樣他會被自動的傳遞給子make,。在沒有使用關鍵字 “export”聲明的變量,make執(zhí)行時不會被自動傳遞給子make過程,,因此下層Makefile中可以定義和上層同名的變量,,這樣不會引起變量定 義沖突。

上層Makefile中定義的某一個變量需要傳遞給子make時,,應該在上層Makefile中使用指示符“export”對此變量進行聲明,。格式如下:



export VARIABLE ...



當不希望將一個變量傳遞給子make時,可以使用指示符“unexport”來聲明這個變量,。格式如下:



unexport VARIABLE ...



在以上的兩種格式,,指示符“export”或者“unexport”的參數(shù)(變量部分),如果它是對一個變量或者函數(shù)的引用,,這些變量或者函數(shù)將會被立即展開,。并賦值給export或者unexport的變量。例如:



Y = Z

export X=$(Y)”



等價于“export X=Z”,。在這里進行展開是為了保證傳遞給子make的此變量的值有效,。

“export”更方便的用法是在定義此變量時同時對它進行聲明。如下的幾個例子:

1.

export VARIABLE = value



等效于:



VARIABLE = value

export VARIABLE



2.



export VARIABLE := value

等效于:



VARIABLE := value

export VARIABLE



3.

export VARIABLE += value



等效于:



VARIABLE += value

export VARIABLE



其實在Makefile中指示符“export”和“unexport”的功能和在shell下功能相同,。

另外一個不帶任何參數(shù)的指示符“export”指示符:



export



含義是將此Makefile中定義的所有變量傳遞給子make過程,。如果不需要傳遞其中一個變量,可以使用指示符“unexport”來聲明這個變量,,這 個被聲明的變量就不會被傳遞給子make,。使用“export”將所有定義的變量傳遞給子Makefile時,那些名字中包含其它字符(除字母,、數(shù)字和下 劃線以外的字符)的變量可能不會被傳遞給子make,,對這類特殊命名的變量傳遞需要明確的使用“export”指示符對它進行聲明。

需要說明的是:單獨使用“export”來導出所有變量的行為是老版本GNU make所默認的。但是在新版本的GNU make中取消了這一默認的行為,。因此在編寫和老版本GNU make兼容的Makefile時,,需要使用特殊目標“.EXPORT_ALL_VARIABLES”來代替“export”,此特殊目標的功和不帶參數(shù) 的“export”相同,。它會被老版本的make忽略,,只有新版本的make能夠識別這個特殊目標。

因為,,如果在老版本的GNU make中使用指示符“export”,,將會出現(xiàn)語法錯誤。例如為了和老版本兼容可以這樣來聲明一些變量:



.EXPORT_ALL_VARIABLES:

VARIABLE1=var1

VARIABLE2=var2



這樣對于不同版本的make來說都是兼容的,,其含義是將特殊目標“.EXPORT_ALL_VARIABLES”的依賴中的所有變量全部傳遞給子make,。

和指示符“export”相似,也可以使用單獨的“unexport”指示符來禁止一個變量的向下傳遞,。這一動作也是現(xiàn)行版本make所默認的,,因此我們 就沒有必要在上層的Makefile中使用它。在多級的make遞歸調(diào)用中,,我么可以在中間的Makefile中使用它來限制上層傳遞來的變量再向下傳 遞,。需要明確的是,不能使用“export”或者“unexport”來實現(xiàn)命令中使用的變量控制功能,。就是說,,不能做到用這兩個指示符來限制某個(些) 變量在執(zhí)行特定命令時有效,而對于其它的命令則無效,。在Makefile中,,最后一個出現(xiàn)的指示符“export”或者“unexport”決定整個 make運行過程中變量是否進行傳遞。

在多級遞歸調(diào)用的make執(zhí)行過程中,。變量“MAKELEVEL”代表了調(diào)用的深度,。在make一級級的執(zhí)行過程中變量“MAKELEVEL”的值不斷的 發(fā)生變化,通過它的值我們可以了解當前make遞歸調(diào)用的深度,。最上一級時“MAKELEVEL”的值為“0”,、下一級時為“1”、再下一級為 “2”.......例如:

Main目錄下的Makefile清單如下:

#maindir Makefile

………

………

.PHONY :test

test:

@echo “main makelevel = $(MAKELEVEL)”

@$(MAKE) –C subdir dislevel



#subdir Makefile

………..

………..

.PHONY : test

test :

@echo “subdir makelevel = $(MAKELEVEL)”



在maindir 目錄下執(zhí)行“make test”,。將顯式如下信息:

main makelevel = 0

make[1]: Entering directory `/…../ subdir ‘

subdir makelevel = 1

make[1]: Leaving directory `/…../ subdir ‘



在主控的Makefile中MAKELEVEL 為“0”,,而在subdir的Makefile中,,MAKELEVEL為“1”,。

這個變量主要用在條件測試指令中。例如:我們可以通過測試此變量的值來決定是否執(zhí)行遞歸方式的make調(diào)用或者其他方式的make調(diào)用,。我們希望一個子目 錄必須被上層make進行調(diào)用才能可以執(zhí)行此目錄下的Makefile,,而不允許直接在其所在的目錄下執(zhí)行make。我們可以這樣實現(xiàn):



.......

$(ifeq $(MAKELEVEL),0)

all : msg

else

all : other

endif



……

…...



msg:

@echo ”Can not make in this directory!”

……

……



當在包含次條件判斷的Makefile所在的目錄下執(zhí)行make時,將會得到提示“Can not make in this directory!”,。

5.6.3 命令行選項和遞歸
在make的遞歸執(zhí)行過程中,。最上層(可以稱之為主控)make的命令行選項“-k”、“-s”等被自動的通過環(huán)境變量“MAKEFLAGS”傳遞給子 make進程,。變量“MAKEFLAGS”的值會被主控make自動的設置為包含執(zhí)行make時的命令行選項的字符串,。在主控執(zhí)行make時使用“-k” 和“-s”選項,那么“MAKEFLAGS”的值就為“ks”,。子make進程處理時,,會把此環(huán)境變量的值作為執(zhí)行的命令行選項,因此子make進程就使 用“-k”和“-s”這兩個命令行選項,。

同樣,,在執(zhí)行make時命令行中給定了一個變量的定義(如“make CFLAGS+=-g”),此變量和它的值(CFLAGS+=-g)也會借助環(huán)境變量“MAKEFLAGS”傳遞給子make進程,??梢越柚鷐ake的環(huán) 境變量“MAKEFLAGS” 傳遞我們在主控make所使用的命令行選項給子make進程。需要注意的是有幾個特殊的命令行選項例外,,分別是:“-C”,、“-f”、“-o”和“- W”,。這些命令行選項不會被賦值給變量“MAKEFLAGS”,。

Make命令行選項中一個比較特殊的是“-j”選項。在支持這個選項的操作系統(tǒng)上,,如果給它指定了一個數(shù)值“N”(多任務的系統(tǒng)unix,、Linux支 持,MS-DOS不支持),,那么主控make和子make進程會在執(zhí)行過程使用通信機制來限制系統(tǒng)在同一時刻(包括所有的遞歸調(diào)用的make進程,,否則, 將會導致make任務的數(shù)目數(shù)目無法控制而使別的任務無法到的執(zhí)行)的任務的執(zhí)行數(shù)目不大于“N”,。另外,,當使用的操作系統(tǒng)不能支持make執(zhí)行過程中的 父子間通信,那么無論在執(zhí)行主控make時指定的任務數(shù)目“N”是多少,,變量“MAKEFLAGS”中選項“-j”的數(shù)目會都被設置為“1”,,這樣可以確 保系統(tǒng)正常運轉(zhuǎn)。

執(zhí)行多級的make調(diào)用時,,如果不希望“MAKEFLAGS”的值傳遞給子make,,就需要在執(zhí)行子make時對它重新進行賦值。例如:



subsystem:

cd subdir && $(MAKE) MAKEFLAGS=



此規(guī)則取消了子make執(zhí)行式的命令行選項(將變量的值賦為空),。

在執(zhí)行make的同時可以通過命令行來定義一個變量,,像上例中的那樣,;前邊已經(jīng)提到過,這種變量是借助環(huán)境“MAKEFLAGS”來傳遞給多級調(diào)用的子 make進程的,。其實真正的命令行中的 變量定義 是通過另外一個變量“MAKEOVRRIDES”來記錄的,,變量“MAKEFLAGS”引用此變量,因而命令行中的變量定義就可以被記錄在環(huán)境變量 “MAKEFLAGS”中被傳遞下去,。當不希望將上層make在命令行中定義的變量傳遞給子make時,,就可以在上層Makefile中把 “MAKEOVERRIDES”賦空來實現(xiàn)(MAKEOVERRIDES=)。這種方式一般很少使用,,建議非萬不得已您還是最好不使用這種方式(為了和 POSIX2.0兼容,,當Makefile中出現(xiàn)“.POSIX”這個特殊的目標時,在上層Makefile中修改變量“MAKEOVERRIDES”對 子make不會產(chǎn)生任何影響),。另外一些系統(tǒng)中對環(huán)境變量的長度存在一個上限,,因此當“MAKEFLAGS”的值超過一定的數(shù)目時,執(zhí)行過程出現(xiàn)了類似 “Arg list too long”的錯誤提示,。

歷史原因,,在make中存在另外一個和“MAKEFLAGS”相類似的變量“MFLAGS”。現(xiàn)行版本中此變量保留的原因是和老版本的兼容需要,。和 “MAKEFLAGS”不同點是:1. 此變量在make的遞歸調(diào)用時不包含命令行選項中的變量定義部分(就是說此變量的定義沒有包含對“MAKEOVERRIDES”的引用),;2. 此變量的值(除為空的情況)是以“-”開始的,而“MAKEFLAGS”的值只有在長命令選項格式(如:“--warn-undefined- variables”)時才以“-”開頭,。傳統(tǒng)得此變量一般被明確的使用在make遞歸調(diào)用命令中,。像下邊那樣:



subsystem:

cd subdir && $(MAKE) $(MFLAGS)



在現(xiàn)行的make版本中,變量“MFLAGS”已經(jīng)成為一個多余部分,。在書寫和老版本make兼容的Makefile時可能需要這個變量,。當然它在目前的版本上也能夠正常的工作。

在某些特殊的場合,,可能需要為所有的make進程指定一個統(tǒng)一的命令行選項,。比如說需要給所有的運行的make指定“-k”選項。實現(xiàn)這個目的,,我們可以 在執(zhí)行make之前設置一個系統(tǒng)環(huán)境變量(存在于當前系統(tǒng)的環(huán)境中)“MAKEFLAGS=k”,,或者在主控Makefile中將它的值賦為“k”。需要 注意的是:不能通過變量“MFLAGS”來實現(xiàn),。

make在執(zhí)行時,,首先將會對變量“MAKEFLAGS”的值(系統(tǒng)環(huán)境中或者在Makefile中設置的)進行分析。當變量的值不是以連字符(“-”) 開始時,,將變量的值按字分開,,字之間使用空格分開。將這些字作為命令行的選項對待(除了選項“-C”,、“-f”,、“-h”、“-o”和“-W”以及他們的 長格式,,如果其中包含無效的選項也不會提示錯誤),。

最后需要強調(diào)的是:當把“MAKEFLAGS”設置到你的系統(tǒng)環(huán)境變量中時,要小心謹慎,!將一些調(diào)試選項或者特殊選項設置為此變量值的一部分,,在執(zhí)行 make時,會對make的正常執(zhí)行產(chǎn)生影響,,甚至是破壞性的影響,。例如變量“MAKEFLAGS”中包含選項“t”、“n”,、“q”這三個的任何一個,, 你在執(zhí)行make時產(chǎn)生的結(jié)果可能并不是你所要達到的目的。建議大家最好不要隨便更改“MAKEFLAGS”的值,,更不要把它設置為系統(tǒng)的環(huán)境變量來使 用,。否則可能會產(chǎn)生一些奇怪甚至讓你感到不解的現(xiàn)象。

1.6.4 -w選項
在多級make遞歸調(diào)用過程中,,選項“-w”或者“--print-directory”可以讓make在開始編譯一個目錄之前和完成此目錄的編譯之后給 出相應的提示信息,,方便開發(fā)人員能夠跟蹤make的執(zhí)行過程。例如,,在目錄“/u/gnu/make”目錄下執(zhí)行“make -w”,,將會看到如下的一些信息:

在開始執(zhí)行之前我們將看到:



make: Entering directory `/u/gnu/make‘.



而在完成之后我們同樣將會看到:



make: Leaving directory `/u/gnu/make‘.



通常,選項“-w”會被自動打開,。在主控Makefile中當如果使用“-C”參數(shù)來為make指定一個目錄或者使用“cd”進入一個目錄時,,“-w”選 項會被自動打開。主控make可以使用選項“-s”(“--slient”)來禁止此選項的自動打開,。另外,,make的命令行選項“--no-print -directory”,將禁止所有關于目錄信息的打印,。

5.7 定義命令包
在書寫Makefile時,,可能有多個規(guī)則會使用相同的一組命令。就像c語言程序中需要經(jīng)常使用到函數(shù)“printf”,。這時我們就會想能不能將這樣一組 命令進行類似c語言函數(shù)一樣的封裝,,以后在我們需要用到的地方可以通過它的名字(c語言中的函數(shù)名)來對這一組命令進行引用。這樣就可減少重復工作,,提高 了效率,。在GNU make中,可以使用指示符“define”來完成這個功能,。通過“define”來定義這樣一組命令,,同時用一個變量(作為一個變量,,不能和 Makefile中其它常規(guī)的變量命名出現(xiàn)沖突)來代表這一組命令。通常我們把使用“define”定義的一組命令稱為一個命令包,。定義一個命令包的語法 以“define”開始,,以“endef”結(jié)束,例如:



define run-yacc

yacc $(firstword $^)

mv y.tab.c $@

endef



這里,,“run-yacc”是這個命令包的名字,。在“define”和“endef”之間的命令就是命令包的主體。需要說明的是:使用“define”定 義的命令包中,,命令體中變量和函數(shù)的引用不會展開,。命令體中所有的內(nèi)容包括“$”、“(”,、“)”等都是變量“run-yacc”的定義,。它和c語言中宏 的使用方式一樣。

例子中,,命令包中第一個命令是對引用它所在規(guī)則中的第一個依賴文件(函數(shù)“firstword”,,可參考 8.2 文本處理函數(shù) 一節(jié))運行yacc程序。yacc程序總是生成一個命名為“y.tab.c”的文件,。第二行的命令就是把這個文件名改為規(guī)則目標的名字,。

定義了這樣一個命令包后,后續(xù)應該如何使用它,?前面已經(jīng)提到,,命令包是使用一個變量來表示的。因此我們就可以按使用變量的方式來使用它,。當在規(guī)則的命令行 中使用這個變量時,,命令包所定義的命令體就會對它進行替代。由于使用“define”定義的變量屬于遞歸展開式變量,,因此,,命令包中所有命令中對其它變量 的引用,在規(guī)則被執(zhí)行時會被完全展開,。例如這樣一個規(guī)則:



foo.c : foo.y

$(run-yacc)



此規(guī)則在執(zhí)行時,,我們來看一下命令包中的變量的替換過程:1. 命令包重中的“$^”會被“foo.y”替換;2. “$@”被“foo.c”替換,。大家應該對“$<”和“$@”不陌生吧,,如果陌生可以 參考 自動化變量 一小節(jié)。

當在一個規(guī)則中引用一個已定義的命令包時,,命令包中的命令體會被原封不動的展開在引用它的地方(和 c語言中的宏一樣),。這些命令就成為規(guī)則的命令。因此我們也可在定義命令包時使用前綴來控制單獨的一個命令行(例如“@”,,“-”和“+”),。例如:



define frobnicate

@echo "frobnicating target $@"

frob-step-1 $< -o $@-step-1

frob-step-2 $@-step-1 -o $@

endef



此命令包的第一行命令執(zhí)行前不會被回顯,,其它的命令在執(zhí)行前都被回顯。

另一方面,,如果一個規(guī)則在引用此命令包之前使用了命令的前綴字符,。那么這個前綴字符將會被添加到命令包定義的所有命令行之中。例如:



frob.out: frob.in

@$(frobnicate)



這個規(guī)則執(zhí)行時不會回顯任何將要執(zhí)行的命令,。

5.8 空命令
有時可能存在這樣的一個需求,需要定義一個什么也不做的規(guī)則(沒有命令行),。前面我們已經(jīng)看到過這樣的用法,。這樣的規(guī)則,只有目標文件(可以存在依賴文件)而沒有命令行,??梢韵襁@樣定義:



target: ;



這就是一個空命令的規(guī)則,為目標“target”定義了一個空命令,。也可以命令行的格式來定義空命令,,需要注意的是命令行必須以[Tab]字符開始。一般在定義空命令時,,建議不使用命令行的方式,,因為看起來空命令行和空行在感覺上沒有區(qū)別。

大家會奇怪為什么要定義一個沒有命令的規(guī)則,。其唯一的原因是,,使用空命令行可以防止make在執(zhí)行時圖重建這個目標而查找隱含命令(包括了使用隱含規(guī)則中 的命令和“.DEFAULT”指定的命令。關于隱含規(guī)則可參考 第10章 使用隱含規(guī)則),。這一點和偽目標有相同之處,。在使用空命令的目標時,需要說明的是:實現(xiàn)一個沒有實際文件的目標,,這個目標只是作為一個標簽,,來完成它的依 賴文件的重建動作。實現(xiàn)這個目的,,首先應該想到偽目標而不是空命令目標,。因為一個實際不存在的目標文件的依賴文件,可能不會被正確的重建,。

所以,,對于空命令規(guī)則,最好不要給它指定依賴文件,。避免特殊情況下產(chǎn)生錯誤的情況,。定義一個空命令規(guī)則,建議使用上例的格式,。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多