Makefile規則③規則語法、依賴、通配符、目錄搜尋、目標


規則語法

  通常規則的語法格式如下:

    TARGETS : PREREQUISITES

      COMMAND

      ...

  或者:

    TARGETS : PREREQUISITES ; COMMAND

      COMMAND

      ...

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

  書寫規則是我們需要注意的幾點:

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

    2. Makefile 中符號“ $”有特殊的含義(表示變量或者函數的引用),在規則中需要使用符號“ $”的地方,需要書寫兩個連續的(“ $$”)。

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

  依賴的類型

    在 GNU make 的規則中可以使用兩種不同類型的依賴:

      1. 以前章節所提到的規則中使用的是常規依賴,這是書寫 Makefile 規則時最常用的一種。

      2. 另外一種在我們書寫 Makefile 時不會經常使用,它比較特殊、稱之為“ order-only”依賴。

    一個規則的常規依賴(通常是多個依賴文件)表明了兩件事:

      首先,它決定了重建此規則目標所要執行規則(確切的說是執行命令)的順序;表明在更新這個規則的目標(執行此規則的命令行)之前需要按照什么樣的順序、執行那些規則(命令)來重建這些依賴文件(對所有依賴文件的重建,使用明確或者隱含規則。就是說對於這樣的規則: A:B C,那么在重建目標 A 之前,首先需要完成對它的依賴文件 B 和 C 的重建。重建 B 和 C 的過程就是執行 Makefile 中以文件 B 和 C 為目標的規則)。

    其次,它確定了一個依存關系;規則中如果依賴文件的任何一個比目標文件新,則認為規則的目標已經過期而需要重建目標文件。

    有時,需要定義一個這樣的規則,在更新目標( 目標文件已經存在)時只需要根據依賴文件中的部分來決定目標是否需要被重建,而不是在依賴文件的任何一個被修改后都重建目標。為了實現這一目的,相應的就需要對規則的依賴進行分類,一類是在這些依賴文件被更新后,需要更新規則的目標;另一類是更新這些依賴的,可不需要更新規則的目標。我們把第二類稱為:“ order-only”依賴。書寫規則時,“ order-only”依賴使用管道符號“ |”開始,作為目標的一個依賴文件。規則依賴列表中管道符號“ |”左邊的是常規依賴,管道符號右邊的就是“ order-only”依賴。這樣的規則書寫格式如下:

      TARGETS : NORMAL-PREREQUISITES | ORDER-ONLY-PREREQUISITES

文件名使用通配符

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

  1. 可以用在規則的目標、依賴中, make 在讀取 Makefile 時會自動對其進行匹配處理(通配符展開);

  2. 可出現在規則的命令中,通配符的通配處理是在 shell 在執行此命令時完成的。除這兩種情況之外的其它上下文中,不能直接使用通配符。而是需要通過函數“ wildcard”來實現。

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

  變量定義中使用的通配符不會被統配處理(因此在變量定義中不能使用通配符,否則在某些情況下會出現非預期的結果,下一小節將會詳細討論)。在 Makefile 有這樣一個變量定義:“ objects = *.o”。它表示變量“ objects”的值是字符串“ *.o”(並不是期望的空格分開的.o 文件列表)。當需要變量“ objects”代表所有.o 文件列表示,需要使用函數“ wildcard”( objects = $(wildcar *.o))。

目錄搜尋

  一般搜索(變量VPATH)

    GNU make 可以識別一個特殊變量“ VPATH”。通過變量“ VPATH”可以指定依賴文件的搜索路徑,當規則的依賴文件在當前目錄不存在時, make 會在此變量所指定的目錄下去尋找這些依賴文件。通常我們都是用此變量來指定規則的依賴文件的搜索路徑。其實“ VPATH”變量所指定的是 Makefile 中所有文件的搜索路徑,包括了規則的依賴文件和目標文件。

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

   選擇性搜索(關鍵字vpath)

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

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

       2、 vpath PATTERN清除之前為符合模式“ PATTERN”的文件設置的搜索路徑。

       3、 vpath清除所有已被設置的文件搜索路徑。

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

Makefile偽目標

  偽目標是這樣一個目標:它不代表一個真正的文件名,在執行 make 時可以指定這個目標來執行其所在規則定義的命令,有時也可以將一個偽目標稱為標簽。使用偽目標有兩點原因:

     1. 避免在我們的 Makefile 中定義的只執行命令的目標(此目標的目的為了執行執行一些列命令,而不需要創建這個目標)和工作目錄下的實際文件出現名字沖突。

     2. 提高執行 make 時的效率,特別是對於一個大型的工程來說,編譯的效率也許你同樣關心。

  如果我們需要書寫這樣一個規則:規則所定義的命令不是去創建目標文件,而是通過 make 命令行明確指定它來執一些特定的命令。像常見的 clean 目標:

  clean:

    rm *.o temp

  規則中“ rm”不是創建文件“ clean”的命令,而是刪除當前目錄下的所有.o 文件和 temp文件。當工作目錄下不存在“ clean”這個文件時,我們輸入“ make clean”,“ rm *.otemp”總會被執行。 但是如果在當前工作目錄下存在文件“ clean”,情況就不一樣了,同樣我們輸入“ make clean”,由於這個規則沒有任何依賴文件,所以目標被認為是最新的而不去執行規則所定義的命令,因此命令“ rm”將不會被執行。這並不是我們的初衷。為了解決這個問題,我們需要將目標“ clean”聲明為偽目標。將一個目標聲明為偽目標的方法是將它作為特殊目標.PHONY”的依賴。如下:

  .PHONY : clean

  這樣目標“ clean”就被聲明為一個偽目標,無論在當前目錄下是否存在“ clean”這個文件。我們輸入“ make clean”之后。“ rm”命令都會被執行。而且,當一個目標被聲明為偽目標后, make 在執行此規則時不會去試圖去查找隱含規則來創建它。這樣也提高了 make 的執行效率,同時也不用擔心由於目標和文件名重名而使我們的期望失敗。

  偽目標的另外一種使用場合是在 make 的並行和遞歸執行過程中。

強制目標(沒有命令或依賴的規則)

  如果一個規則沒有命令或者依賴,並且它的目標不是一個存在的文件名。在執行此規則時,目標總會被認為是最新的。就是說:這個規則一旦被執行, make 就認為它的目標已經被更新過。這樣的目標在作為一個規則的依賴時,因為依賴總被認為被更新過,因此作為依賴所在的規則中定義的命令總會被執行。看一個例子:

  clean: FORCE

    rm $(objects)

  FORCE:

  這個例子中,目標“ FORCE”符合上邊的條件。它作為目標“ clean”的依賴,在執行make 時,總被認為被更新過。因此“ clean”所在規則在被執行時其所定義的命令總會被執行。這樣的一個目標通常我們將其命名為“ FORCE”。

空目標文件

  空目標文件是偽目標的一個變種;此目標所在規則執行的目的和偽目標相同——通過 make 命令行指定將其作為終極目標來執行此規則所定義的命令。和偽目標不同的是:這個目標可以是一個存在的文件,但文件的具體內容我們並不關心,通常此文件是一個空文件。

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

多目標

  一個規則中可以有多個目標,規則所定義的命令對所有的目標有效。一個具有多目標的規則相當於多個規則。規則的命令對不同的目標的執行效果不同,因為在規則的命令中可能使用了自動環變量“ $@”。多目標規則意味着所有的目標具有相同的依賴文件。

  雖然在多目標的規則中,可以根據不同的目標使用不同的命令(在命令行中使用自動化變量“ $@”)。但是,多目標的規則並不能做到根據目標文件自動改變依賴文件(像上邊例子中使用自動化變量“ $@”改變規則的命令一樣)。需要實現這個目的是,要用到make的靜態模式。

 靜態模式

  靜態模式規則是這樣一個規則:規則存在多個目標,並且不同的目標可以根據目標文件的名字來自動構造出依賴文件。相當於是一個過濾器。

雙冒號規則

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

  首先需要明確的是: Makefile 中,一個目標可以出現在多個規則中。但是這些規則必須是同一類型的規則,要么都是普通規則, 要么都是雙冒號規則。而不允許一個目標同時出現在兩種不同類型的規則中。

  雙冒號規則和普通規則的處理的不同點表現在以下幾個方面:

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

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

 

《完》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM