| 第三章:Makefile 總述 3.1 Makefile的內容 在一個(gè)完整的Makefile中,包含了5個(gè)東西:顯式規則、隱含規則、變量的定義、指示符和注釋。關(guān)于“規則”、“變量”和“Makefile指示符”將在后續的章節進(jìn)行詳細的討論。本章討論的是一些基本概念。 ² 顯式規則:它描述了在何種情況下如何更新一個(gè)或者多個(gè)被稱(chēng)為目標的文件(Makefile的目標文件)。在書(shū)寫(xiě)Makefile是需要明確地給出目標文件、目標的依賴(lài)文件列表以及更新目標文件所需要的命令。 ² 隱含規則:它是make根據此類(lèi)目標文件的命名(典型的是文件名的后綴)而自動(dòng)推導出來(lái)的規則。make根據目標文件的名字,自動(dòng)產(chǎn)生目標的依賴(lài)文件并使用默認的命令來(lái)對目標進(jìn)行更新。 ² 變量定義:就是使用一個(gè)字符串代表一段文本串,當定義了變量以后,Makefile后續在需要使用此文本串的地方,通過(guò)引用這個(gè)變量來(lái)實(shí)現對文本串的使 用。第一章的的例子中,我們就定義了一個(gè)變量“objects”來(lái)表示一個(gè).o文件列表。關(guān)于變量的詳細討論可參考 第五章 Makefile中的變量 ² Makefile指示符:指示符指明在make程序讀取makefile文件過(guò)程中所要執行的一個(gè)動(dòng)作。其中包括: ² 讀取一個(gè)文件,讀取給定文件名的文件。參考 2.3 包含其它makefile文件一節 ² 決定(通常是根據一個(gè)變量的得值)處理或者忽略Makefile中的某一特定部分。參考 第六章Makefile的條件執行 ² 定義一個(gè)多行變量。參考 5.8 多行定義 一節 ² 注釋?zhuān)篗akefile中“#”字符后的內容被作為是注釋內容(和shell腳本一樣)處理。如果此行的第一個(gè)非空字符為“#”,那么此行為注釋行。注釋 行的結尾如果存在反斜線(xiàn)(\),那么下一行也被作為注釋行。一般在書(shū)寫(xiě)Makefile時(shí)推薦將注釋作為一個(gè)獨立的行,而不要和Makefile的有效行 放在一行中書(shū)寫(xiě)。當在Makefile中需要使用字符“#”時(shí),可以使用反斜線(xiàn)加“#”(\#)來(lái)實(shí)現,其表示將“#”作為一字符而不是注釋的開(kāi)始標志。 需要注意的地方: Makefile中第一個(gè)規則之后的所有以[Tab]字符開(kāi)始的的行,make程序都會(huì )將其給系統的shell程序去解釋執行。因此以[Tab]字符開(kāi)始 的注釋行也會(huì )被交給shell來(lái)處理,此命令行是否需要被執行(shell執行或者忽略)是由系統shell程序來(lái)判決的。 另外,在使用指示符“define”定義一個(gè)多行的變量或者命令包時(shí),其定義體(“define”和“endef”之間的內容)會(huì )被完整的展開(kāi)到 Makefile中引用此變量的地方(包含定義體中的注釋行);make在引用此變量的地方對所有的定義體進(jìn)行處理,決定是注釋還是有效內容。 Makefile中的變量可以和C語(yǔ)言中的宏(實(shí)質(zhì)一樣)一樣來(lái)理解。對一個(gè)變量引用的地方make所做的就是將這個(gè)變量根據定義進(jìn)行基于文本的展開(kāi),展 開(kāi)變量的過(guò)程不涉及到任何變量的具體含義和功能分析。 3.2 makefile文件的命名 默認的情況下,make會(huì )在工作目錄(執行make的目錄)下按照文件名順序尋找makefile文件讀取并執行,查找的文件名順序為:“GNUmakefile”、“makefile”、“Makefile”。 通常應該使用“makefile”或者“Makefile”作為一個(gè)makefile的文件名(我們推薦使用“Makefile”,首字母大寫(xiě)而比較顯 著(zhù),一般在一個(gè)目錄中和當前目錄的一些重要文件(README,Chagelist等)靠近,在尋找時(shí)會(huì )比較容易的發(fā)現它)。而 “GNUmakefile”是我們不推薦使用的文件名,因為以此命名的文件只有“GNU make”才可以識別,而其他版本的make程序只會(huì )在工作目錄下“makefile”和“Makefile”這兩個(gè)文件。 如果make程序在工作目錄下無(wú)法找到以上三個(gè)文件中的任何一個(gè),它將不讀取任何其他文件作為解析對象。但是根據make隱含規則的特性,我們可以通過(guò)命 令行指定一個(gè)目標,如果當前目錄下存在符合此目標的依賴(lài)文件,那么這個(gè)命令行所指定的目標將會(huì )被創(chuàng )建或者更新,參見(jiàn)注釋。 當makefile文件的命名不是這三個(gè)任何一個(gè)時(shí),需要通過(guò)make的“-f”或者“--file”選項來(lái)指定make讀取的makefile文件。給 make指定makefile文件的格式為:“-f NAME”或者“—file=NAME”,它指定文件“NAME”作為執行make時(shí)讀取的makefile文件。也可以通過(guò)多個(gè)“-f”或者“-- file”選項來(lái)指定多個(gè)需要讀取的makefile文件,多個(gè)makefile文件將會(huì )被按照指定的順序進(jìn)行連接并被make解析執行。當通過(guò)“-f” 或者“--file”指定make讀取makefile的文件時(shí),make就不再自動(dòng)查找這三個(gè)標準命名的makefile文件。 注釋?zhuān)和ㄟ^(guò)命令指定目標使用make的隱含規則: 當前目錄下不存在以“GNUmakefile”、“makefile”、“Makefile”命名的任何文件, 1. 當前目錄下存在一個(gè)源文件foo.c的,我們可以使用“make foo.o”來(lái)使用make的隱含規則自動(dòng)生成foo.o。當執行“make foo.o”時(shí)。我們可以看到其執行的命令為: cc –c –o foo.o foo.c 之后,foo.o將會(huì )被創(chuàng )建或者更新。 2. 如果當前目錄下沒(méi)有foo.c文件時(shí),就是make對.o文件目標的隱含規則中依賴(lài)文件不存在。如果使用命令“make foo.o”時(shí),將回到到如下提示: make: *** No rule to make target ‘foo.o’. Stop. 3. 如果直接使用命令“make”時(shí),得到的提示信息如下: make: *** No targets specified and no makefile found. Stop. 3.3 包含其它makefile文件 本節我們討論如何在一個(gè)Makefile中包含其它的makefile文件。Makefile中包含其它文件的關(guān)鍵字是“include”,和C語(yǔ)言對頭文件的包含方式一致。 “include”指示符告訴make暫停讀取當前的Makefile,而轉去讀取“include”指定的一個(gè)或者多個(gè)文件,完成以后再繼續當前Makefile的讀取。Makefile中指示符“include”書(shū)寫(xiě)在獨立的一行,其形式如下: include FILENAMES... FILENAMES是shell所支持的文件名(可以使用通配符)。 指示符“include”所在的行可以一個(gè)或者多個(gè)空格(make程序在處理時(shí)將忽略這些空格)開(kāi)始,切忌不能以[Tab]字符開(kāi)始(如果一行以 [Tab]字符開(kāi)始make程序將此行作為一個(gè)命令行來(lái)處理)。指示符“include”和文件名之間、多個(gè)文件之間使用空格或者[Tab]鍵隔開(kāi)。行尾 的空白字符在處理時(shí)被忽略。使用指示符包含進(jìn)來(lái)的Makefile中,如果存在變量或者函數的引用。它們將會(huì )在包含它們的Makefile中被展開(kāi)。 來(lái)看一個(gè)例子,存在三個(gè).mk文件,“$(bar)”被擴展為“bish bash”。則 include foo *.mk $(bar) 等價(jià)于 include foo a.mk b.mk c.mk bish bash make程序在處理指示符include時(shí),將暫停對當前使用指示符“include”的makefile文件的讀取,而轉去依此讀取由 “include”指示符指定的文件列表。直到完成所有這些文件以后再回過(guò)頭繼續讀取指示符“include”所在的makefile文件。 通常指示符“include”用在以下場(chǎng)合: 1. 有多個(gè)不同的程序,由不同目錄下的幾個(gè)獨立的Makefile來(lái)描述其創(chuàng )建或者更新規則。它們需要使用一組通用的變量定義或者模式規則。通用的做法是將這 些共同使用的變量或者模式規則定義在一個(gè)文件中(沒(méi)有具體的文件命名限制),在需要使用的Makefile中使用指示符“include”來(lái)包含此文件。 2. 當根據源文件自動(dòng)產(chǎn)生依賴(lài)文件時(shí);我們可以將自動(dòng)產(chǎn)生的依賴(lài)關(guān)系保存在另外一個(gè)文件中,主Makefile使用指示符“include”包含這些文件。這 樣的做法比直接在主Makefile中追加依賴(lài)文件的方法要明智的多。其它版本的make已經(jīng)使用這種方式來(lái)處理。 如果指示符“include”指定的文件不是以斜線(xiàn)開(kāi)始(絕對路徑,如/usr/src/Makefile...),而且當前目錄下也不存在此文件; make將根據文件名試圖在以下幾個(gè)目錄下查找:首先,查找使用命令行選項“-I”或者“--include-dir”指定的目錄,如果找到指定的文件, 則使用這個(gè)文件;否則依此搜索以下幾個(gè)目錄(如果其存在):“/usr/gnu/include”、“/usr/local/include”和 “/usr/include”。 當在這些目錄下都沒(méi)有找到“include”指定的文件時(shí),make將會(huì )提示一個(gè)包含文件未找到的告警提示,但是不會(huì )立刻退出。而是繼續處理 Makefile的內容。當完成讀取所有的makefile文件后,make將試圖使用規則來(lái)創(chuàng )建通過(guò)指示符“include”指定的但未找到的文件,當 不能創(chuàng )建它時(shí)(沒(méi)有創(chuàng )建這個(gè)文件的規則),make將提示致命錯誤并退出。會(huì )輸出類(lèi)似如下錯誤提示: Makefile:錯誤的行數:未找到文件名:提示信息(No such file or directory) Make: *** No rule to make target ‘ 我們可使用“-include”來(lái)代替“include”,忽略由于包含文件不存在或者無(wú)法創(chuàng )建時(shí)的錯誤提示(“-”的意思是告訴make,忽略此操作的錯誤。make繼續執行)。像下邊那樣: -include FILENAMES... 使用這種方式時(shí),當所要包含的文件不存在時(shí)不會(huì )有錯誤提示、make也不會(huì )退出;除此之外,和第一種方式效果相同。以下是這兩種方式的比較: 使用“include FILENAMES...”,make程序處理時(shí),如果“FILENAMES”列表中的任何一個(gè)文件不能正常讀取而且不存在一個(gè)創(chuàng )建此文件的規則時(shí)make程序將會(huì )提示錯誤并退出。 使用“-include FILENAMES...”的情況是,當所包含的文件不存在或者不存在一個(gè)規則去創(chuàng )建它,make程序會(huì )繼續執行,只有在因為makefile的目標的規則不存在時(shí),才會(huì )提示致命錯誤并退出。 為了和其它的make程序進(jìn)行兼容。也可以使用“sinclude”來(lái)代替“-include”(GNU所支持的方式)。 1.4 變量 MAKEFILES 如果當前環(huán)境定義了一個(gè)“MAKEFILES”的環(huán)境變量,make執行時(shí)首先將此變量的值作為需要讀入的Makefile文件,多個(gè)文件之間使用空格分 開(kāi)。類(lèi)似使用指示符“include”包含其它Makefile文件一樣,如果文件名非絕對路徑而且當前目錄也不存在此文件,make會(huì )在一些默認的目錄 去尋找。此情況和使用“include”的區別: 1. 環(huán)境變量指定的makefile文件中的“目標”不會(huì )被作為make執行的“終極目標”。就是說(shuō),這些文件中所定義規則的目標,make不會(huì )將其作為“終 極目標”來(lái)看待。如果在make的工作目錄下沒(méi)有一個(gè)名為“Makefile”、“makefile”或者“GNUmakefile”的文件,make同 樣會(huì )提示“make: *** No targets specified and no makefile found. Stop.”;而在make的工作目錄下存在這樣一個(gè)文件(“Makefile”、“makefile”或者“GNUmakefile”),那么make 執行時(shí)的“終極目標”就是當前目錄下這個(gè)文件中所定義的“終極目標”。 2. 環(huán)境變量所定義的文件列表,在執行make時(shí),如果不能找到其中某一個(gè)文件(不存在或者無(wú)法創(chuàng )建)。make不會(huì )提示錯誤,也不退出。就是說(shuō)環(huán)境變量“MAKEFILES”定義的包含文件是否存在不會(huì )導致make錯誤(這是比較隱蔽的地方)。 3. make在執行時(shí),首先讀取的是環(huán)境變量“MAKEFILES”所指定的文件列表,之后才是工作目錄下的makefile文件,“include”所指定 的文件是在make發(fā)現此關(guān)鍵字的時(shí)、暫停正在讀取的文件而轉去讀取“include”所指定的文件。 變量“MAKEFILES”主要用在“make”的遞歸調用過(guò)程中的的通信。實(shí)際應用中很少設置此變量。一旦設置了此變量,在多層make調用時(shí);由于每 一級make都會(huì )讀取“MAKEFILES”變量所指定的文件,這樣可能導致執行的混亂(可能不是你想看到的執行結果)。不過(guò),我們可以使用此環(huán)境變量來(lái) 指定一個(gè)定義通用的“隱含規則”和用的變量的文件,比如設置默認搜索路徑;通過(guò)這種方式設置的“隱含規則”和定義的變量可以被任何make進(jìn)程使用(有點(diǎn) 象C語(yǔ)言中的全局變量)。 也有人想讓login程序自動(dòng)的在自己的工作環(huán)境中設置此環(huán)境變量,編寫(xiě)的Makefile建立在此環(huán)境變量的基礎上。此想法可以肯定地說(shuō)不是一個(gè)好主 意。規勸大家千萬(wàn)不要這么干,否則你所編寫(xiě)的Makefile在其人的工作環(huán)境中肯定不能正常工作。因為別人的工作環(huán)境中可能沒(méi)有設置相同的環(huán)境變量 “MAKEFILES”。 推薦的做法實(shí):在需要包含其它makefile文件時(shí)使用指示符“include”來(lái)實(shí)現。 3.5 變量 MAKEFILE_LIST make程序在讀取多個(gè)makefile文件時(shí),包括由環(huán)境變量“MAKEFILES”指定、命令行指、當前工作下的默認的以及使用指示符 “include”指定包含的,在對這些文件進(jìn)行解析執行之前make讀取的文件名將會(huì )被自動(dòng)的追加到變量“MAKEFILE_LIST”的定義域中。 這樣我們就可以通過(guò)測試此變量的最后一個(gè)字來(lái)得知當前make程序正在處理的是具體的那個(gè)makefile文件。具體地說(shuō)就是一個(gè)makefile文件中 當使用指示符“include”包含另外一個(gè)文件之后,變量“MAKEFILE_LIST”的最后一個(gè)只可能是指示符“include”指定所要包含的那 個(gè)文件的名字。如果一個(gè)makefile的內容如下: name1 := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) include inc.mk name2 := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) all: @echo name1 = $(name1) @echo name2 = $(name2) 執行make,則看到的將是如下的結果: name1 = Makefile name2 = inc.mk 此例子中涉及到了make的函數的和變量定義的方式,這些將在后續的章節中有詳細的講述。 3.6 其他特殊變量 GNU make支持一個(gè)特殊的變量,此變量不能通過(guò)任何途經(jīng)給它賦值。此變量展開(kāi)以后是一個(gè)特定的值。第一個(gè)重要的特殊的變量是“.VARIABLES”。它被 展開(kāi)以后是此引用點(diǎn)之前、makefile文件中所定義的所有全局變量列表。包括:空變量(未賦值的變量)和make的內嵌變量,但不包含目標指定的變 量,目標指定變量值在特定目標的上下文有效。 3.7 makefile文件的重建 有時(shí),Makefile可由其它文件生成,比如RCS或SCCS文件。如果Makefile由其它文件重建,那么在make開(kāi)始解析Makefile時(shí)需要讀取的是更新后的Makefile、而不是那個(gè)沒(méi)有更新的Makefile。make的處理過(guò)程是這樣的: make在讀入所有makefile文件之后,首先將所讀取的每個(gè)makefile作為一個(gè)目標,試著(zhù)去更新它。如果存在一個(gè)更新特定makefile文 件明確規則或者隱含規則,則去更新這個(gè)makefile文件。在完成對所有的makefile文件的更新檢查動(dòng)作之后,如果之前所讀取的makefile 文件已經(jīng)被更新,那么make就清除本次執行的狀態(tài)重新讀取一遍所有的makefile文件(此過(guò)程中,同樣在讀取完成以后也會(huì )去試圖更新所有的已經(jīng)讀取 的makefile文件,但是一般這些文件不會(huì )再次被重建,因為它們在時(shí)間戳上已經(jīng)是最新的)。 實(shí)際應用中,我們會(huì )很明確的了解我們的那些makefile文件不需要重建。出于make效率的考慮,我們可以采用一些辦法來(lái)避免make在執行過(guò)程時(shí)查找重建makefile的隱含規則。例如我們可以書(shū)寫(xiě)一個(gè)明確的規則,將makefile文件作為目標,命令為空。 Makefile規則中,如果使用一個(gè)沒(méi)有依賴(lài)只有命令行的雙冒號規則去更新一個(gè)文件,那么每次執行make時(shí),此規則的目標文件將會(huì )被無(wú)條件的更新。而 假如此規則的目標文件是一個(gè)makefile文件,那么在執行make時(shí),將會(huì )導致這個(gè)makefile文件被無(wú)條件更新,時(shí)make的執行陷入到一個(gè)死 循環(huán)中(此makefile文件被不斷的更新、重新讀取、更新再重新讀取的過(guò)程)。為了防止進(jìn)入此循環(huán),make在遇到一個(gè)目標是makefile文件的 雙冒號規則時(shí),將忽略對這個(gè)規則的執行(其中包括了使用“MAKEFILES”指定、命令行選項指定、指示符“include”指定的需要make讀取的 所有makefile文件中定義的這一類(lèi)雙冒號規則)。 執行make時(shí),如果沒(méi)有使用“-f(--file)”選項指定一個(gè)文件,make程序將讀取缺省的文件。和使用“-f(--file)”選項不同, make無(wú)法確定工作目錄下是否存在缺省名稱(chēng)的makefile文件。如果缺省makefile文件不存在,但可以通過(guò)一個(gè)規則來(lái)創(chuàng )建它(此規則是隱含規 則),則會(huì )自動(dòng)創(chuàng )建缺省makefile文件,之后重新讀取它并開(kāi)始執行。 因此,如果不存在缺省makefile文件,make將按照搜索makefile文件的名稱(chēng)順序去創(chuàng )建它,直到創(chuàng )建成功或者超越其缺省的命名順序。需要明 確的一點(diǎn)是:執行make時(shí),如果不能成功地創(chuàng )建其缺省的makefile文件,并不一定會(huì )導致錯誤。運行make時(shí)一個(gè)makefile文件并不是必需 的。(關(guān)于這一點(diǎn)大家會(huì )在后續的閱讀過(guò)程中體會(huì )到) 當使用“-t(--touch)”選項來(lái)對Makefile目標文件進(jìn)行時(shí)間戳更新時(shí),對于哪些makefile文件的目標是無(wú)效的。就是說(shuō)即使執行 make時(shí)使用了選項“-t”,那些目標是makefile文件的規則同樣也會(huì )被make執行(而其它的規則不會(huì )被執行,make只是簡(jiǎn)單的更新規則目標 文件的時(shí)間戳);類(lèi)似還有選項“-q(—question)”和“-n(—just-print) ”,這主要是因為一個(gè)過(guò)時(shí)的makefile文件對其它目標的重建規則在當前看來(lái)可能是錯誤的。正因為如此,執行命令“make –f mfile –n foo”首先會(huì )試圖重建“mfile文件”、并重新讀取它,之后會(huì )打印出更新目標“foo”規則中所定義的命令但不執行此命令。 在這種情況下,如果我們不希望重建makefile文件。那么我們就需要在執行make時(shí),在命令行中將這個(gè)makefile文件中為一個(gè)最終目的,這樣 “–t”和其它的選項就對這個(gè)makefile文件的目標有效,防止執行這個(gè)makefile作為目標的規則。同樣,命令“make –f mfile –n mfile foo”會(huì )讀取文件“mfile”,打印出重建文件“mfile”的命令、重建“foo”的命令而實(shí)際不去執行此命令。并且所打印的用于更新“foo”目 標的命令是選項“-f”指定的、沒(méi)有被重建的“mfile”文件中所定義的命令。 3.8 重載另外一個(gè)makefile 有些情況下存在兩個(gè)比較類(lèi)似的makefile文件。其中一個(gè)(makefile-A)需要使用另外一個(gè)文件(makefile-B)中所定義的變量和規 則。我們可以在“makefile-A”中使用指示符“include”來(lái)包含“mkaefile-B”來(lái)達到目的,這種情況下,如果兩個(gè) makefile文件中存在相同目標,而其描述規則中使用不同的命令。相同目標有兩個(gè)不同的規則命令,這是makefile所不允許的。遇到這種情況,使 用指示符“include”顯然是行不通的。GNU make提供另外一種途徑來(lái)達到此目的。具體的做法如下: 在需要包含的makefile文件(makefile-A)中,我們可以使用一個(gè)稱(chēng)之為“所有匹配模式”的規則來(lái)描述在“makefile-A”中沒(méi)有明確定義的目標,make將會(huì )在給定的makefile文件中尋找沒(méi)有在當前Makefile中給出的目標更新規則。 看一個(gè)例子,如果存在一個(gè)命名為“Makefile”的makefile文件,其中描述目標“foo”的規則和其他的一些規,我們也可以書(shū)寫(xiě)一個(gè)內容如下命名為“GNUmakefile”的文件。 #sample GNUmakefile foo: frobnicate > foo %: force @$(MAKE) -f Makefile $@ force: ; 執行命令“make foo”,make將使用工作目錄下命名為“GNUmakefile”的文件并執行目標“foo”所在的規則,創(chuàng )建它的命令是:“frobnicate > foo”。如果我們執行另外一個(gè)命令“make bar”, “GUNmakefile”中沒(méi)有此目標的更新規則。那么,make將會(huì )使用“所有匹配模式”規則,執行命令“$(MAKE) -f Makefile bar”。如果文件“Makefile”中存在此目標更新規則的定義,那么這個(gè)規則會(huì )被執行。此過(guò)程同樣適用于其它 “GNUmakefile”中沒(méi)有給出的目標更新規則。此方式的靈活之處在于:如果在“Makefile”文件中存在同樣一一個(gè)目標“foo”的重建規 則,由于make執行時(shí)首先讀取文件“GUNmakefile”并在其中能夠找到目標“foo”的重建規則,所以make就不會(huì )去執行這個(gè)“所有模式匹配 規則”(上例中的目標是“%”的規則)。這樣就避免了使用指示符“include”包含一個(gè)makefile文件時(shí)所帶來(lái)的目標規則的重復定義問(wèn)題。 此種方式,模式規則的模式只使用了單獨的“%”(我們才稱(chēng)他為“所有模式匹配規則”),它可以匹配任何一個(gè)目標;它的依賴(lài)是“force”,保證了即使目 標文件已經(jīng)存在也會(huì )執行這個(gè)規則(文件已存在時(shí),需要根據它的依賴(lài)文件的修改情況決定是否需要重建這個(gè)目標文件);“force”規則中使用空命令是為了 防止make程序試圖尋找一個(gè)規則去創(chuàng )建目標“force”時(shí),又使用了模式規則“%: force”而陷入無(wú)限循環(huán)。 3.9 make如何解析makefile文件 GUN make的執行過(guò)程分為兩個(gè)階段。 第一階段:讀取所有的makefile文件(包括“MAKIFILES”變量指定的、指示符“include”指定的、以及命令行選項“-f(-- file)”指定的makefile文件),內建所有的變量、明確規則和隱含規則,并建立所有目標和依賴(lài)之間的依賴(lài)關(guān)系結構鏈表。 在第二階段:根據第一階段已經(jīng)建立的依賴(lài)關(guān)系結構鏈表決定哪些目標需要更新,并使用對應的規則來(lái)重建這些目標。 理解make執行過(guò)程的兩個(gè)階段是很重要的。它能幫助我們更深入的了解執行過(guò)程中變量以及函數是如何被展開(kāi)的。變量和函數的展開(kāi)問(wèn)題是書(shū)寫(xiě) Makefile時(shí)容易犯錯和引起大家迷惑的地方之一。本節將對這些不同的結構的展開(kāi)階段進(jìn)行簡(jiǎn)單的總結(明確變量和函數的展開(kāi)階段,對正確的使用變量非 常有幫助)。首先,明確以下基本的概念;在make執行的第一階段中如果變量和函數被展開(kāi),那么稱(chēng)此展開(kāi)是“立即”的,此時(shí)所有的變量和函數被展開(kāi)在需要 構建的結構鏈表的對應規則中(此規則在建立鏈表是需要使用)。其他的展開(kāi)稱(chēng)之為“延后”的。這些變量和函數不會(huì )被“立即”展開(kāi),而是直到后續某些規則須要 使用時(shí)或者在make處理的第二階段它們才會(huì )被展開(kāi)。 可能現在講述的這些還不能完全理解。不過(guò)沒(méi)有關(guān)系,通過(guò)后續章節內容的學(xué)習,我們會(huì )一步一步的熟悉make的執行過(guò)程。學(xué)習過(guò)程中可以回過(guò)頭來(lái)參考本節的內容。相信在看完本書(shū)之后,會(huì )對make的整個(gè)過(guò)程有全面深入的理解。 3.9.1 變量取值 變量定義解析的規則如下: IMMEDIATE = DEFERRED IMMEDIATE ?= DEFERRED IMMEDIATE := IMMEDIATE IMMEDIATE += DEFERRED or IMMEDIATE define IMMEDIATE DEFERRED Endef 當變量使用追加符(+=)時(shí),如果此前這個(gè)變量是一個(gè)簡(jiǎn)單變量(使用 :=定義的)則認為它是立即展開(kāi)的,其它情況時(shí)都被認為是“延后”展開(kāi)的變量。 3.9.2 條件語(yǔ)句 所有使用到條件語(yǔ)句在產(chǎn)生分支的地方,make程序會(huì )根據預設條件將正確地分支展開(kāi)。就是說(shuō)條件分支的展開(kāi)是“立即”的。其中包括:“ifdef”、“ifeq”、“ifndef”和“ifneq”所確定的所有分支命令。 3.9.3 規則的定義 所有的規則在make執行時(shí),都按照如下的模式展開(kāi): IMMEDIATE : IMMEDIATE ; DEFERRED DEFERRED 其中,規則中目標和依賴(lài)如果引用其他的變量,則被立即展開(kāi)。而規則的命令行中的變量引用會(huì )被延后展開(kāi)。此模板適合所有的規則,包括明確規則、模式規則、后綴規則、靜態(tài)模式規則。 3.10 總結 make的執行過(guò)程如下: 1. 依次讀取變量“MAKEFILES”定義的makefile文件列表 2. 讀取工作目錄下的makefile文件(根據命名的查找順序“GNUmakefile”,“makefile”,“Makefile”,首先找到那個(gè)就讀取那個(gè)) 3. 依次讀取工作目錄makefile文件中使用指示符“include”包含的文件 4. 查找重建所有已讀取的makefile文件的規則(如果存在一個(gè)目標是當前讀取的某一個(gè)makefile文件,則執行此規則重建此makefile文件,完成以后從第一步開(kāi)始重新執行) 5. 初始化變量值并展開(kāi)那些需要立即展開(kāi)的變量和函數并根據預設條件確定執行分支 6. 根據“終極目標”以及其他目標的依賴(lài)關(guān)系建立依賴(lài)關(guān)系鏈表 7. 執行除“終極目標”以外的所有的目標的規則(規則中如果依賴(lài)文件中任一個(gè)文件的時(shí)間戳比目標文件新,則使用規則所定義的命令重建目標文件) 8. 執行“終極目標”所在的規則 執行一個(gè)規則的過(guò)程: 對于一個(gè)存在的規則(明確規則和隱含規則)首先,make比較目標文件和所有的依賴(lài)文件的時(shí)間戳。如果目標的時(shí)間戳比所有依賴(lài)文件的時(shí)間戳更新(依賴(lài)文件 在上一次執行make之后沒(méi)有被修改),那么什么也不做。否則(依賴(lài)文件中的某一個(gè)或者全部在上一次執行make后已經(jīng)被修改過(guò)),規則所定義的重建目標 的命令將會(huì )被執行。這就是make工作的基礎,也是其執行規制所定義命令的依據。(后續討論規則時(shí)將會(huì )對此詳細地說(shuō)明) |
聯(lián)系客服
微信登錄中...
請勿關(guān)閉此頁(yè)面
