GNU Make 使用手冊!

GNU Make 使用手冊(中譯版)

翻譯:於鳳昌

譯者注:本人在閱讀Linux源代碼過程中發現如果要全面瞭解Linux的結構、理解Linux的編程總體設計及思想必須首先全部讀通Linux源代碼中各級的Makefile文件。目前,在網上雖然有一些著作,但都不能全面的解釋Linux源代碼中各級的Makefile文件,因此本人認真閱讀了GNU Make 使用手冊(3.79)版原文,在此基礎上翻譯了該手冊,以滿足對Linux源代碼有興趣或者希望採用GCC編寫程序但對缺乏GNU Make全面瞭解之人士的需要。本人是業餘愛好不是專業翻譯人士,如果有問題請通過電子信箱與我聯繫共同商討,本人的E-mail爲:[email protected] 注意在文章中出現的斜體加粗字表示章節。

GNU make Version 3.79

April 2000

Richard M. Stallman and Roland McGrath

 

目錄

1 make概述

1.1 怎樣閱讀本手冊

1.2 問題和BUG

2 Makefile文件介紹

2.1 規則的格式

2.2一個簡單的Makefile文件

2.3make處理Makefile文件的過程

2.4使用變量簡化Makefile文件

2.5讓make推斷命令

2.6另一種風格的Makefile文件

2.7在目錄中刪除文件的規則

3         3         編寫Makefile文件

3.1Makefile文件的內容

3.2Makefile文件的命名

3.3包含其它的Makefile文件

3.4變量MAKEFILES

3.5Makefile文件重新生成的過程

3.6重載其它Makefile文件

3.7make讀取Makefile文件的過程

4 編寫規則

4.1規則的語法

4.2在文件名中使用通配符

4.2.1通配符例子

4.2.2使用通配符的常見錯誤

4.2.3函數wildcard

4.3在目錄中搜尋依賴

4.3.1VPATH:所有依賴的搜尋路徑

4.3.2vpath指令

4.3.3目錄搜尋過程

4.3.4編寫搜尋目錄的shell命令

4.3.5目錄搜尋和隱含規則

4.3.6連接庫的搜尋目錄

4.4假想目標

4.5沒有命令或依賴的規則

4.6使用空目錄文件記錄事件

4.7內建的特殊目標名

4.8具有多個目標的規則

4.9具有多條規則的目標

4.10靜態格式規則

4.10.1靜態格式規則的語法

4.10.2靜態格式規則和隱含規則

4.11雙冒號規則

4.12自動生成依賴

5 在規則中使用命令

5.1命令回顯

5.2執行命

5.3並行執行

5.4命令錯誤

5.5中斷或關閉make

5.6遞歸調用make

5.6.1變量MAKE的工作方式

5.6.2與子make通訊的變量

5.6.3與子make通訊的選項

5.6.4`--print-directory'選項

5.7定義固定次序命令

5.8使用空命令

6         6         使用變量

6.1變量引用基礎

6.2變量的兩個特色

6.3變量高級引用技術

6.3.1替換引用

6.3.2嵌套變量引用

6.4變量取值

6.5設置變量

6.6爲變量值追加文本

6.7override指令

6.8定義多行變量

6.9環境變量

6.10特定目標變量的值

6.11特定格式變量的值

7 Makefile文件的條件語句

7.1條件語句的例子

7.2條件語句的語法

7.3測試標誌的條件語句

8 文本轉換函數

8.1函數調用語法

8.2字符串替換和分析函數

8.3文件名函數

8.4函數foreach

8.5函數if

8.6函數call

8.7函數origin

8.8函數shell

8.9控制Make的函數

9         9         運行make

9.1指定Makefile文件的參數

9.2指定最終目標的參數

9.3代替執行命令

9.4避免重新編譯文件

9.5變量重載

9.6測試編譯程序

9.7選項概要

10 使用隱含規則

10.1使用隱含規則

10.2隱含規則目錄

10.3隱含規則使用的變量

10.4隱含規則鏈

10.5定義與重新定義格式規則

10.5.1格式規則簡介

10.5.2格式規則的例子

10.5.3自動變量

10.5.4格式匹配

10.5.5萬用規則

10.5.6刪除隱含規則

10.6定義最新類型的缺省規則

10.7過時的後綴規則

10.8隱含規則搜尋算法

11 使用make更新檔案文件

11.1檔案成員目標

11.2檔案成員目標的隱含規則

11.2.1更新檔案成員的符號索引表

11.3使用檔案的危險

11.4檔案文件的後綴規則

12 GNU make的特點

13 不兼容性和失去的特點

14 Makefile文件慣例

14.1makefile文件的通用慣例

14.2makefile文件的工具

14.3指定命令的變量

14.4安裝路徑變量

14.5用戶標準目標

14.6安裝命令分類

15快速參考

16make產生的錯誤

17複雜的Makefile文件例子

   附錄  名詞翻譯對照表

1 Make 概述

Make 可自動決定一個大程序中哪些文件需要重新編譯,併發布重新編譯它們的命令。本版本GNU Make使用手冊由Richard M. Stallman and Roland McGrath編著,是從Paul D. Smith撰寫的V3.76版本發展過來的。

GNU Make符合IEEE Standard 1003.2-1992 (POSIX.2) 6.2章節的規定。

因爲C語言程序更具有代表性,所以我們的例子基於C語言程序,但Make並不是僅僅能夠處理C語言程序,它可以處理那些編譯器能夠在Shell命令下運行的的各種語言的程序。事實上,GNU Make不僅僅限於程序,它可以適用於任何如果一些文件變化導致另外一些文件必須更新的任務。

如果要使用Make,必須先寫一個稱爲Makefile的文件,該文件描述程序中各個文件之間的相互關係,並且提供每一個文件的更新命令。在一個程序中,可執行程序文件的更新依靠OBJ文件,而OBJ文件是由源文件編譯得來的。

一旦合適的Makefile文件存在,每次更改一些源文件,在shell命令下簡單的鍵入:

make

就能執行所有的必要的重新編譯任務。Make程序根據Makefile文件中的數據和每個文件更改的時間戳決定哪些文件需要更新。對於這些需要更新的文件,Make基於Makefile文件發佈命令進行更新,進行更新的方式由提供的命令行參數控制。具體操作請看運行Make章節。

1.1怎樣閱讀本手冊

如果您現在對Make一無所知或者您僅需要了解對make 的普通性介紹,請查閱前幾章內容,略過後面的章節。前幾章節是普通介紹性內容,後面的章節是具體的專業、技術內容。

如果您對其它Make程序十分熟悉,請參閱GNU Make的特點不兼容性和失去的特點部分,GNU Make的特點這一章列出了GNU Makemake程序的擴展,不兼容和失去的特點一章解釋了其它Make程序有的特徵而GNU Make缺乏的原因。

對於快速瀏覽者,請參閱選項概要、快速參考內建的特殊目標名部分。

1.2問題和BUG

如果您有關於GNU Make的問題或者您認爲您發現了一個BUG,請向開發者報告;我們不能許諾我們能幹什麼,但我們會盡力修正它。在報告BUG之前,請確定您是否真正發現了BUG,仔細研究文檔後確認它是否真的按您的指令運行。如果文檔不能清楚的告訴您怎麼做,也要報告它,這是文檔的一個BUG

在您報告或者自己親自修正BUG之前,請把它分離出來,即在使問題暴露的前提下儘可能的縮小Makefile文件。然後把這個Makefile文件和Make給出的精確結果發給我們。同時請說明您希望得到什麼,這可以幫助我們確定問題是否出在文檔上。

一旦您找到一個精確的問題,請給我們發E-mail,我們的E-mail地址是:

[email protected]

在郵件中請包含您使用的GNU Make的版本號。您可以利用命令‘make--version’得到版本號。同時希望您提供您的機器型號和操作系統類型,如有可能的話,希望同時提供config.h文件(該文件有配置過程產生)。

2 Makefile文件介紹

Make程序需要一個所謂的Makefile文件來告訴它幹什麼。在大多數情況下,Makefile文件告訴Make怎樣編譯和連接成一個程序。

本章我們將討論一個簡單的Makefile文件,該文件描述怎樣將8C源程序文件和3個頭文件編譯和連接成爲一個文本編輯器。Makefile文件可以同時告訴Make怎樣運行所需要的雜亂無章的命令(例如,清除操作時刪除特定的文件)。如果要看更詳細、複雜的Makefile文件例子,請參閱複雜的Makefile文件例子一章。

Make重新編譯這個編輯器時,所有改動的C語言源文件必須重新編譯。如果一個頭文件改變,每一個包含該頭文件的C語言源文件必須重新編譯,這樣才能保證生成的編輯器是所有源文件更新後的編輯器。每一個C語言源文件編譯後產生一個對應的OBJ文件,如果一個源文件重新編譯,所有的OBJ文件無論是剛剛編譯得到的或原來編譯得到的必須從新連接,形成一個新的可執行文件。

2.1 規則的格式

一個簡單的Makefile文件包含一系列的“規則”,其樣式如下:

目標(target): 依賴(prerequiries)

<tab>命令(command)

       

       

目標(target)通常是要產生的文件的名稱,目標的例子是可執行文件或OBJ文件。目標也可是一個執行的動作名稱,諸如‘clean’(詳細內容請參閱假想目標一節)。

依賴是用來輸入從而產生目標的文件,一個目標經常有幾個依賴。

命令是Make執行的動作,一個規則可以含有幾個命令,每個命令佔一行。注意:每個命令行前面必須是一個Tab字符,即命令行第一個字符是Tab這是不小心容易出錯的地方。

通常,如果一個依賴發生變化,則需要規則調用命令對相應依賴和服務進行處理從而更新或創建目標。但是,指定命令更新目標的規則並不都需要依賴,例如,包含和目標‘clern’相聯繫的刪除命令的規則就沒有依賴。

規則一般是用於解釋怎樣和何時重建特定文件的,這些特定文件是這個詳盡規則的目標。Make需首先調用命令對依賴進行處理,進而才能創建或更新目標。當然,一個規則也可以是用於解釋怎樣和何時執行一個動作,詳見編寫規則一章。

一個Makefile文件可以包含規則以外的其它文本,但一個簡單的Makefile文件僅僅需要包含規則。雖然真正的規則比這裏展示的例子複雜,但格式卻是完全一樣。

2.2一個簡單的Makefile文件

一個簡單的Makefile文件,該文件描述了一個稱爲文本編輯器(edit)的可執行文件生成方法,該文件依靠8OBJ文件(.o文件),它們又依靠8C源程序文件和3個頭文件。

在這個例子中,所有的C語言源文件都包含‘defs.h’ 頭文件,但僅僅定義編輯命令的源文件包含‘command.h’頭文件,僅僅改變編輯器緩衝區的低層文件包含‘buffer.h’頭文件。

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

 

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 main.o kbd.o command.o display.o /

           insert.o search.o files.o utils.o

我們把每一個長行使用反斜槓-新行法分裂爲兩行或多行,實際上它們相當於一行,這樣做的意圖僅僅是爲了閱讀方便。

使用Makefile文件創建可執行的稱爲‘edit’的文件,鍵入:make

使用Makefile文件從目錄中刪除可執行文件和目標,鍵入:make clean

在這個Makefile文件例子中,目標包括可執行文件‘edit’和OBJ文件‘main.o’及‘kdb.o’。依賴是C語言源文件和C語言頭文件如‘main.c’和‘def.h’等。事實上,每一個OBJ文件即是目標也是依賴。所以命令行包括‘cc -c main.c’和‘cc -c kbd.c’。

當目標是一個文件時,如果它的任一個依賴發生變化,目標必須重新編譯和連接。任何命令行的第一個字符必須是‘Tab’字符,這樣可以把Makefile文件中的命令行與其它行分別開來。(一定要牢記:Make並不知道命令是如何工作的,它僅僅能向您提供保證目標的合適更新的命令。Make的全部工作是當目標需要更新時,按照您制定的具體規則執行命令。

目標‘clean’不是一個文件,僅僅是一個動作的名稱。正常情況下,在規則中‘clean’這個動作並不執行,目標‘clean’也不需要任何依賴。一般情況下,除非特意告訴make執行‘clean’命令,否則‘clean’命令永遠不會執行。注意這樣的規則不需要任何依賴,它們存在的目的僅僅是執行一些特殊的命令。象這些不需要依賴僅僅表達動作的目標稱爲假想目標。詳細內容參見假想目標;參閱命令錯可以瞭解rm或其它命令是怎樣導致make忽略錯誤的。

2.3 make處理makefile文件的過程

缺省情況下,make開始於第一個目標(假想目標的名稱前帶‘.’)。這個目標稱爲缺省最終目標(即make最終更新的目標,具體內容請看指定最終目標的參數一節)。

在上節的簡單例子中,缺省最終目標是更新可執行文件‘edit’,所以我們將該規則設爲第一規則。這樣,一旦您給出命令:

make

make就會讀當前目錄下的makefile文件,並開始處理第一條規則。在本例中,第一條規則是連接生成‘edit’,但在make全部完成本規則工作之前,必須先處理‘edit’所依靠的OBJ文件。這些OBJ文件按照各自的規則被處理更新,每個OBJ文件的更新規則是編譯其源文件。重新編譯根據其依靠的源文件或頭文件是否比現存的OBJ文件更‘新’,或者OBJ文件是否存在來判斷。

         其它規則的處理根據它們的目標是否和缺省最終目標的依賴相關聯來判斷。如果一些規則和缺省最終目標無任何關聯則這些規則不會被執行,除非告訴Make強制執行(如輸入執行make clean命令)。

         OBJ文件重新編譯之前,Make首先檢查它的依賴C語言源文件和C語言頭文件是否需要更新。如果這些C語言源文件和C語言頭文件不是任何規則的目標,make將不會對它們做任何事情。Make也可以自動產生C語言源程序,這需要特定的規則,如可以根據BisonYacc產生C語言源程序。

         OBJ文件重新編譯(如果需要的話)之後,make決定是否重新連接生成edit可執行文件。如果edit可執行文件不存在或任何一個OBJ文件比存在的edit可執行文件‘新’,則make重新連接生成edit可執行文件。

         這樣,如果我們修改了‘insert.c’文件,然後運行makemake將會編譯‘insert.c’文件更新‘insert.o’文件,然後重新連接生成edit可執行文件。如果我們修改了‘command.h’文件,然後運行makemake將會重新編譯‘kbd.o’和‘command.o’文件,然後重新連接生成edit可執行文件。

2.4使用變量簡化makefile文件

在我們的例子中,我們在‘edit’的生成規則中把所有的OBJ文件列舉了兩次,這裏再重複一遍:

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

這樣的兩次列舉有出錯的可能,例如在系統中加入一個新的OBJ文件,我們很有可能在一個需要列舉的地方加入了,而在另外一個地方卻忘記了。我們使用變量可以簡化makefile文件並且排除這種出錯的可能。變量是定義一個字符串一次,而能在多處替代該字符串使用(具體內容請閱讀使用變量一節)。

makefile文件中使用名爲objects, OBJECTS, objs, OBJS, obj, OBJ的變量代表所有OBJ文件已是約定成俗。在這個makefile文件我們定義了名爲objects的變量,其定義格式如下:

objects = main.o kbd.o command.o display.o /

          insert.o search.o files.o utils.o

然後,在每一個需要列舉OBJ文件的地方,我們使用寫爲`$(objects)'形式的變量代替(具體內容請閱讀使用變量一節)。下面是使用變量後的完整的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)

2.5 make推斷命令

編譯單獨的C語言源程序並不需要寫出命令,因爲make可以把它推斷出來:make有一個使用‘CC c’命令的把C語言源程序編譯更新爲相同文件名的OBJ文件的隱含規則。例如make可以自動使用‘cc -c main.c -o main.o’命令把‘main.c編譯 ‘main.o’。因此,我們可以省略OBJ文件的更新規則。詳細內容請看使用隱含規則一節。

如果C語言源程序能夠這樣自動編譯,則它同樣能夠自動加入到依賴中。所以我們可在依賴中省略C語言源程序,進而可以省略命令。下面是使用隱含規則和變量objects的完整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)

這是我們實際編寫makefile文件的例子。(和目標‘clean’聯繫的複雜情況在別處闡述。具體參見假想目標命令錯誤兩節內容。)因爲隱含規則十分方便,所以它們非常重要,在makefile文件中經常使用它們。

2.6 另一種風格的makefile文件

當時在makefile文件中使用隱含規則創建OBJ文件時,採用另一種風格的makefile文件也是可行的。在這種風格的makefile文件中,可以依據依賴分組代替依據目標分組。下面是採用這種風格的makefile文件:

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是所有OBJ文件的共同的一個依賴;command.hbufffer.h是具體列出的OBJ文件的共同依賴。

雖然採用這種風格編寫makefile文件更具風味:makefile文件更加短小,但一部分人以爲把每一個目標的信息放到一起更清晰易懂而不喜歡這種風格。

2.7 在目錄中刪除文件的規則

編譯程序並不是編寫make規則的唯一事情。Makefile文件可以告訴make去完成編譯程序以外的其它任務,例如,怎樣刪除OBJ文件和可執行文件以保持目錄的‘乾淨’等。下面是刪除利用make規則編輯器的例子:

clean:

        rm edit $(objects)

在實際應用中,應該編寫較爲複雜的規則以防不能預料的情況發生。更接近實用的規則樣式如下:

.PHONY : clean

clean :

        -rm edit $(objects)

這樣可以防止make因爲存在名爲’clean’的文件而發生混亂,並且導致它在執行rm命令時發生錯誤(具體參見假想目標命令錯誤兩節內容)。

諸如這樣的規則不能放在makefile文件的開始,因爲我們不希望它變爲缺省最終目標。應該象我們的makefile文件例子一樣,把關於edit的規則放在前面,從而把編譯更新edit可執行程序定爲缺省最終目標。

3 編寫makefile文件

make編譯系統依據的信息來源於稱爲makefile文件的數據庫。

3.1 makefile文件的內容

makefile文件包含5方面內容:具體規則、隱含規則、定義變量、指令和註釋。規則、變量和指令將在後續章節介紹。

l         l         具體規則用於闡述什麼時間或怎樣重新生成稱爲規則目標的一個或多個文件的。它列舉了目標所依靠的文件,這些文件稱爲該目標的依賴。具體規則可能同時提供了創建或更新該目標的命令。詳細內容參閱編寫規則一章。

l         l         隱含規則用於闡述什麼時間或怎樣重新生成同一文件名的一系列文件的。它描述的目標是根據和它名字相同的文件進行創建或更新的,同時提供了創建或更新該目標的命令。詳細內容參閱使用隱含規則一節。

l         l         定義變量是爲一個變量賦一個固定的字符串值,從而在以後的文件中能夠使用該變量代替這個字符串。注意在makefile文件中定義變量佔一獨立行。在上一章的makefile文件例子中我們定義了代表所有OBJ文件的變量objects(詳細內容參閱使用變量簡化makefile文件一節)。

l         l         指令是make根據makefile文件執行一定任務的命令。這些包括如下幾方面:

n         n         讀其它makefile文件(詳細內容參見包含其它的makefile文件)。

n         n         判定(根據變量的值)是否使用或忽略makefile文件的部分內容(詳細內容參閱makefile文件的條件語句一節)。

n         n         定義多行變量,即定義變量值可以包含多行字符的變量(詳細內容參見定義多行變量一節)。

l         l         以‘#’開始的行是註釋行。註釋行在處理時將被make忽略,如果一個註釋行在行尾是‘/’則表示下一行繼續爲註釋行,這樣註釋可以持續多行。除在define指令內部外,註釋可以出現在makefile文件的任何地方,甚至在命令內部(這裏shell決定什麼是註釋內容)。

3.2 makfile文件的命名

缺省情況下,當make尋找makefile文件時,它試圖搜尋具有如下的名字的文件,按順序:‘GNUmakefile’、‘makefile’和‘Makefile’。

通常情況下您應該把您的makefile文件命名爲‘makefile’或‘Makefile’。(我們推薦使用‘Makefile’,因爲它基本出現在目錄列表的前面,後面挨着其它重要的文件如‘README’等。)。雖然首先搜尋‘GNUmakefile’,但我們並不推薦使用。除非您的makefile文件是特爲GNU make編寫的,在其它make版本上不能執行,您才應該使用‘GNUmakefile’作爲您的makefile的文件名。

如果make不能發現具有上面所述名字的文件,它將不使用任何makefile文件。這樣您必須使用命令參數給定目標,make試圖利用內建的隱含規則確定如何重建目標。詳細內容參見使用隱含規則一節。

如果您使用非標準名字makefile文件,您可以使用‘-f’或‘--file’參數指定您的makefile文件。參數‘-f name’或‘--file=name’能夠告訴make讀名字爲‘name’的文件作爲makefile文件。如果您使用 ‘-f’或‘--file’參數多於一個,意味着您指定了多個makefile文件,所有的makefile文件按具體的順序發生作用。一旦您使用了‘-f’或‘--file’參數,將不再自動檢查是否存在名爲‘GNUmakefile’、‘makefile’或‘Makefile’的makefile文件。

3.3 包含其它的makefile文件

include指令告訴make暫停讀取當前的makefile文件,先讀完include指令指定的makefile文件後再繼續。指令在makefile文件佔單獨一行,其格式如下:

include filenames...

filenames可以包含shell文件名的格式。

include指令行,行開始處的多餘的空格是允許的,但make處理時忽略這些空格,注意該行不能以Tab字符開始(因爲,以Tab字符開始的行,make認爲是命令行)。include和文件名之間以空格隔開,兩個文件名之間也以空格隔開,多餘的空格make處理時忽略,在該行的尾部可以加上以‘#’爲起始的註釋。文件名可以包含變量及函數調用,它們在處理時由make進行擴展(具體內容參閱使用變量一節)。

例如,有三個‘.mk’文件:‘a.mk’、‘b.mk’和‘c.mk’,變量$(bar)擴展爲bish bash,則下面的表達是:

include foo *.mk $(bar)

和‘include foo a.mk b.mk c.mk bish bash’等價。

    make遇見include指令時, make就暫停讀取當前的makefile文件,依次讀取列舉的makefile文件,讀完之後,make再繼續讀取當前makefile文件中include指令以後的內容。

使用include指令的一種情況是幾個程序分別有單獨的makefile文件,但它們需要一系列共同的變量定義(詳細內容參閱設置變量),或者一系列共同的格式規則(詳細內容參閱定義與重新定義格式規則)。

另一種使用include指令情況是需要自動從源文件爲目標產生依賴的情況,此時,依賴在主makefile文件包含的文件中。這種方式比其它版本的make把依賴附加在主makefile文件後部的傳統方式更顯得簡潔。具體內容參閱自動產生依賴

如果makefile文件名不以‘/’開頭,並且在當前目錄下也不能找到,則需搜尋另外的目錄。首先,搜尋以‘-|’或‘--include-dir’參數指定的目錄,然後依次搜尋下面的目錄(如果它們存在的話):prefix/include' (通常爲 /usr/local/include') /usr/gnu/include', /usr/local/include', /usr/include'

如果指定包含的makefile文件在上述所有的目錄都不能找到,make將產生一個警告信息,注意這不是致命的錯誤。處理完include指令包含的makefile文件之後,繼續處理當前的makefile文件。一旦完成makefile文件的讀取操作,make將試圖創建或更新過時的或不存在的makefile文件。詳細內容參閱makefile文件重新生成的過程。只有在所有make尋求丟失的makefile文件的努力失敗後,make才能斷定丟失的makefile文件是一個致命的錯誤。

如果您希望對不存在且不能重新創建的makefile文件進行忽略,並且不產生錯誤信息,則使用-include指令代替include指令,格式如下:

-include filenames...

這種指令的作用就是對於任何不存在的makefile文件都不會產生錯誤(即使警告信息也不會產生)。如果希望保持和其它版本的make兼容,使用sinclude指令代替-include指令。

3.4 變量MAKEFILES

如果定義了環境變量MAKEFILESmake認爲該變量的值是一列附加的makefile文件名,文件名之間由空格隔開,並且這些makefile文件應首先讀取。Make完成這個工作和上節完成include指令的方式基本相同,即在特定的目錄中搜尋這些文件。值得注意的是,缺省最終目標不會出現在這些makefile文件中,而且如果一些makefile文件沒有找到也不會出現任何錯誤信息。

環境變量MAKEFILES主要在make遞歸調用過程中起通訊作用(詳細內容參閱遞歸調用make)。在make頂級調用之前設置環境變量並不是十分好的主意,因爲這樣容易將makefile文件與外界的關係弄的更加混亂。然而如果運行make而缺少makefile文件時,環境變量MAKEFILESmakefile文件可以使內置的隱含規則更好的發揮作用,如搜尋定義的路徑等(詳細內容參閱在目錄中搜尋依賴)。

一些用戶喜歡在登錄時自動設置臨時的環境變量MAKEFILES,而makefile文件在該變量指定的文件無效時才使用。這是非常糟糕的主意,應爲許多makefile文件在這種情況下運行失效。最好的方法是直接在makefile文件中寫出具體的include指令(詳細內容參看上一節)

3.5 makefile文件重新生成的過程

有時makefile文件可以由其它文件重新生成,如從RCSSCCS文件生成等。如果一個makefile文件可以從其它文件重新生成,一定注意讓make更新makefile文件之後再讀取makefile文件。

完成讀取所有的makefile文件之後,make檢查每一個目標,並試圖更新它。如果對於一個makefile文件有說明它怎樣更新的規則(無論在當前的makefile文件中或其它makefile文件中),或者存在一條隱含規則說明它怎樣更新(具體內容參見使用隱含規則),則在必要的時候該makefile文件將會自動更新。在所有的makefile文件檢查之後,如果發現任何一個makefile文件發生變化,make就會清空所有記錄,並重新讀入所有makefile文件。(然後再次試圖更新這些makefile文件,正常情況下,因爲這些makefile文件已被更新,make將不會再更改它們。)

如果您知道您的一個或多個makefile文件不能重新創建,也許由於執行效率緣故,您不希望make按照隱含規則搜尋或重建它們,您應使用正常的方法阻止按照隱含規則檢查它們。例如,您可以寫一個具體的規則,把這些makefile文件當作目標,但不提供任何命令(詳細內容參閱使用空命令)。

如果在makefile文件中指定依據雙冒號規則使用命令重建一個文件,但沒有提供依賴,則一旦make運行就會重建該文件(詳細內容參見雙冒號規則)。同樣,如果在makefile文件中指定依據雙冒號規則使用命令重建的一個makefile文件,並且不提供依賴,則一旦make運行就會重建該makefile文件,然後重新讀入所有makefile文件,然後再重建該makefile文件,再重新讀入所有makefile文件,如此往復陷入無限循環之中,致使make不能再完成別的任務。如果要避免上述情況的發生,一定注意不要依據雙冒號規則使用命令並且不提供依賴重建任何makefile文件。

如果您沒有使用‘-f’或‘--file’指定makefile文件,make將會使用缺省的makefile文件名(詳細內容參見3.2節內容)。不象使用‘-f’或‘--file’選項指定具體的makefile文件,這時make不能確定makefile文件是否存在。如果缺省的makefile文件不存在,但可以由運行的make依據規則創建,您需要運行這些規則,創建要使用的makefile文件。

如果缺省的makefile文件不存在,make將會按照搜尋的次序將它們試着創建,一直到將makefile文件成功創建或make將所有的文件名都試過來。注意make不能找到或創建makefile文件不是錯誤,makefile文件並不是運行make必須的。

因爲即使您使用‘-t’特別指定,‘-t’或‘--touch’選項對更新makefile文件不產生任何影響, makefile文件仍然會更新,所以當您使用‘-t’或‘--touch’選項時,您不要使用過時的makefile文件來決定‘touch’哪個目標(具體含義參閱代替執行命令)。同樣,因爲‘-q' (或 ‘--question') 和 ‘-n' (或 ‘--just-print')也能不阻止更新makefile文件,所以過時的makefile文件對其它的目標將產生錯誤的輸出結果。如,‘make -f mfile -n foo’命令將這樣執行:更新‘mfile’,然後讀入,再輸出更新‘foo’的命令和依賴,但並不執行更新‘foo’,注意,所有回顯的更新‘foo’的命令是在更新後的‘mfile’中指定的。

在實際使用過程中,您一定會遇見確實希望阻止更新makefile文件的情況。如果這樣,您可以在makefile文件命令行中將需要更新的makefile文件指定爲目標,如此則可阻止更新makefile文件。一旦makefile文件名被明確指定爲一個目標,選項‘-t’等將會對它發生作用。如這樣設定,‘make -f mfile -n foo’命令將這樣執行:讀入‘mfile’,輸出更新‘foo’的命令和依賴,但並不執行更新‘foo’。回顯的更新‘foo’的命令包含在現存的‘mfile’中。

3.6 重載其它makefile文件

有時一個makefile文件和另一個makefile文件相近也是很有用的。您可以使用‘include’指令把更多的makefile文件包含進來,如此可加入更多的目標和定義的變量。然而如果兩個makefile文件對相同的目標給出了不同的命令,make就會產生錯誤。

在主makefile文件(要包含其它makefile文件的那個)中,您可以使用通配符格式規則說明只有在依靠當前makefile文件中的信息不能重新創建目標時,make才搜尋其它的makefile文件,詳細內容參見定義與重新定義格式規則

例如:如果您有一個說明怎樣創建目標‘foo’(和其它目標)的makefile文件稱爲‘Makefile’,您可以編寫另外一個稱爲‘GNUmakefile’的makefile文件包含以下語句:

foo:

        frobnicate > foo

 

%: force

        @$(MAKE) -f Makefile $@

force: ;

如果鍵入‘make foo’,make就會找到‘GNUmakefile’,讀入,然後運行‘frobnicate > foo’。如果鍵入‘make bar’,make發現無法根據‘GNUmakefile’創建‘bar’,它將使用格式規則提供的命令:‘make f Makefile bar’。如果在‘Makefile’中提供了‘bar’更新的規則,make就會使用該規則。對其它‘GNUmakefile’不提供怎樣更新的目標make也會同樣處理。這種工作的方式是使用了格式規則中的格式匹配符‘%’,它可以和任何目標匹配。該規則指定了一個依賴‘force’,用來保證命令一定要執行,無論目標文件是否存在。我們給出的目標‘force’時使用了空命令,這樣可防止make按照隱含規則搜尋和創建它,否則,make將把同樣的匹配規則應用到目標‘force’本身,從而陷入創建依賴的循環中。

3.7 make讀取makefile文件的過程

GNU make把它的工作明顯的分爲兩個階段。在第一階段,make讀取makefile文件,包括makefile文件本身、內置變量及其值、隱含規則和具體規則、構造所有目標的依靠圖表和它們的依賴等。在第二階段,make使用這些內置的組織決定需要重新構造的目標以及使用必要的規則進行工作。

瞭解make兩階段的工作方式十分重要,因爲它直接影響變量、函數擴展方式;而這也是編寫makefile文件時導致一些錯誤的主要來源之一。下面我們將對makefile文件中不同結構的擴展方式進行總結。我們稱在make工作第一階段發生的擴展是立即擴展:在這種情況下,makemakefile文件進行語法分析時把變量和函數直接擴展爲結構單元的一部分。我們把不能立即執行的擴展稱爲延時擴展。延時擴展結構直到它已出現在上下文結構中或make已進入到了第二工作階段時才執行展開。

您可能對這一部分內容不熟悉。您可以先看完後面幾章對這些知識熟悉後再參考本節內容。

變量賦值

變量的定義語法形式如下:

immediate = deferred

immediate ?= deferred

immediate := immediate

immediate += deferred or immediate

 

define immediate

  deferred

endef

對於附加操作符‘+=’,右邊變量如果在前面使用(:=)定義爲簡單擴展變量則是立即變量,其它均爲延時變量。

條件語句

整體上講,條件語句都按語法立即分析,常用的有:ifdefifeqifndefinneq

定義規則

規則不論其形式如何,都按相同的方式擴展。

immediate : immediate ; deferred

         deferred

目標和依賴部分都立即擴展,用於構造目標的命令通常都是延時擴展。這個通用的規律對具體規則、格式規則、後綴規則、靜態格式規則和簡單依賴定義都適用。

4編寫規則

makefile文件中的規則是用來說明何時以及怎樣重建特定文件的,這些特定的文件稱爲該規則的目標(通常情況下,每個規則只有一個目標)。在規則中列舉的其它文件稱爲目標的依賴,同時規則還給出了目標創建、更新的命令。一般情況下規則的次序無關緊要,但決定缺省最終目標時卻是例外。缺省最終目標是您沒有另外指定最終目標時,make認定的最終目標。缺省最終目標是makefile文件中的第一條規則的目標。如果第一條規則有多個目標,只有第一個目標被認爲是缺省最終目標。有兩種例外的情況:以句點(‘.’)開始的目標不是缺省最終目標(如果該目標包含一個或多個斜槓‘/’,則該目標也可能是缺省最終目標);另一種情況是格式規則定義的目標不是缺省最終目標(參閱定義與重新定義格式規則)。

所以,我們編寫makefile文件時,通常將第一個規則的目標定爲編譯全部程序或是由makefile文件表述的所有程序(經常設定一個稱爲‘all’的目標)。參閱指定最終目標的參數

4.1規則的語法

通常一條規則形式如下:

targets : prerequisites

        command

        ...

或:

targets : prerequisites ; command

        command

        ...

目標(target)是文件的名稱,中間由空格隔開。通配符可以在文件名中使用(參閱在文件名中使用通配符),‘am)’形式的文件名錶示成員m在文件a中(參閱檔案成員目標)。一般情況下,一條規則只有一個目標,但偶爾由於其它原因一條規則有多個目標(參閱具有多個目標的規則)。

命令行以Tab字符開始,第一個命令可以和依賴在一行,命令和依賴之間用分號隔開,也可以在依賴下一行,以Tab字符爲行的開始。這兩種方法的效果一樣,參閱在規則中使用命令

因爲美元符號已經用爲變量引用的開始符,如果您真希望在規則中使用美元符號,您必須連寫兩次,‘$$’(參閱使用變量)。您可以把一長行在中間插入‘/’使其分爲兩行,也就是說,一行的尾部是’/’的話,表示下一行是本行的繼續行。但這並不是必須的,make沒有對makefile文件中行的長度進行限制。一條規則可以告訴make兩件事情:何時目標已經過時,以及怎樣在必要時更新它們。

判斷目標過時的準則和依賴關係密切,依賴也由文件名構成,文件名之間由空格隔開,通配符和檔案成員也允許在依賴中出現。一個目標如果不存在或它比其中一個依賴的修改時間早,則該目標已經過時。該思想來源於目標是根據依賴的信息計算得來的,因此一旦任何一個依賴發生變化,目標文件也就不再有效。目標的更新方式由命令決定。命令由shell解釋執行,但也有一些另外的特點。參閱在規則中使用命令

4.2 在文件名中使用通配符

一個簡單的文件名可以通過使用通配符代表許多文件。Make中的通配符和Bourne shell中的通配符一樣是‘*’、‘?’和‘[]’。例如:‘*.C’指在當前目錄中所有以‘.C’結尾的文件。

字符‘~’在文件名的前面也有特殊的含義。如果字符‘~’單獨或後面跟一個斜槓‘/’,則代表您的home目錄。如‘~/bin’擴展爲‘/home/bin’。如果字符‘~’後面跟一個字,它擴展爲home目錄下以該字爲名字的目錄,如‘~John/bin’表示‘home/John/bin’。在一些操作系統(如ms-dosms-windows)中不存在home目錄,可以通過設置環境變量home來模擬。

在目標、依賴和命令中的通配符自動擴展。在其它上下文中,通配符只有在您明確表明調用通配符函數時才擴展。

通配符另一個特點是如果通配符前面是反斜槓‘/’,則該通配符失去通配能力。如‘foo/*bar’表示一個特定的文件其名字由‘foo’、‘*’和‘bar’構成。

4.2.1通配符例子

    通配符可以用在規則的命令中,此時通配符由shell擴展。例如,下面的規則刪除所有OBJ文件:

clean

         rm f  *.o

    通配符在規則的依賴中也很有用。在下面的makefile規則中,‘make print’將打印所有從上次您打印以後又有改動的‘.c’文件:

print: *.c

        lpr -p $?

        touch print

本規則使用‘ptint’作爲一個空目標文件(參看使用空目標文件記錄事件);自動變量‘$?’用來打印那些已經修改的文件,參看自動變量

當您定義一個變量時通配符不會擴展,如果您這樣寫:

objects = *.o

變量objects的值實際就是字符串‘*.o’。然而,如果您在一個目標、依賴和命令中使用變量objects的值,通配符將在那時擴展。使用下面的語句可使通配符擴展:

objects=$(wildcard *.o)

詳細內容參閱函數wildcard

4.2.2使用通配符的常見錯誤

下面有一個幼稚使用通配符擴展的例子,但實際上該例子不能完成您所希望完成的任務。假設可執行文件‘foo’由在當前目錄的所有OBJ文件創建,其規則如下:

objects = *.o

 

foo : $(objects)

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

由於變量objects的值爲字符串‘*.o’,通配符在目標‘foo’的規則下擴展,所以每一個OBJ文件都會變爲目標‘foo’的依賴,並在必要時重新編譯自己。

但如果您已刪除了所有的OBJ文件,情況又會怎樣呢?因沒有和通配符匹配的文件,所以目標‘foo’就依靠了一個有着奇怪名字的文件‘*.o’。因爲目錄中不存在該文件,make將發出不能創建‘*.o’的錯誤信息。這可不是所要執行的任務。

實際上,使用通配符獲得正確的結果是可能的,但您必須使用稍微複雜一點的技術,該技術包括使用函數wildcard和替代字符串等。詳細內容將在下一節論述。

微軟的操作系統(MS-DOSMS-WINDOWS)使用反斜槓分離目錄路徑,如:

C:/foo/bar/bar.c

這和Unix風格‘c:/foo/bar/bar.c’等價(‘c:’是驅動器字母)。當make在這些系統上運行時,不但支持在路徑中存在反斜槓也支持Unix風格的前斜槓。但是這種對反斜槓的支持不包括通配符擴展,因爲通配符擴展時,反斜槓用作引用字符。所以,在這些場合您必須使用Unix風格的前斜槓。

4.2.3函數wildcard

通配符在規則中可以自動擴展,但設置在變量中或在函數的參數中通配符一般不能正常擴展。如果您需要在這些場合擴展通配符,您應該使用函數wildcard,格式如下:

$(wildcard pattern...)

可以在makefile文件的任何地方使用該字符串,應用時該字符串被一列在指定目錄下存在的並且文件名和給出的文件名的格式相符合的文件所代替,文件名中間由空格隔開。如果沒有和指定格式一致的文件,則函數wildcard的輸出將會省略。注意這和在規則中通配符擴展的方式不同,在規則中使用逐字擴展方式,而不是省略方式(參閱上節)。

使用函數wildcard得到指定目錄下所有的C語言源程序文件名的命令格式爲:

$(wildcard *.c)

我們可以把所獲得的C語言源程序文件名的字符串通過將‘.c’後綴變爲‘.o’轉換爲OBJ文件名的字符串,其格式爲:

$(patsubst %.c,%.o,$(wildcard *.c))

這裏我們使用了另外一個函數:patsubst,詳細內容參閱字符串替換和分析函數

這樣,一個編譯特定目錄下所有C語言源程序並把它們連接在一起的makefile文件可以寫成如下格式:

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

 

foo : $(objects)

        cc -o foo $(objects)

這裏使用了編譯C語言源程序的隱含規則,因此沒有必要爲每個文件寫具體編譯規則。 ‘:=’是‘=’的變異,對‘:=’的解釋,參閱兩種風格的變量

4.3在目錄中搜尋依賴

對於大型系統,把源文件安放在一個單獨的目錄中,而把二進制文件放在另一個目錄中是十分常見的。Make 的目錄搜尋特性使自動在幾個目錄搜尋依賴十分容易。當您在幾個目錄中重新安排您的文件,您不必改動單獨的規則,僅僅改動一下搜尋路徑即可。

4.3.1 VPATH:所有依賴的搜尋路徑

make變量VPATH的值指定了make搜尋的目錄。經常用到的是那些包含依賴的目錄,並不是當前的目錄;但VPATH指定了make對所有文件都適用的目錄搜尋序列,包括了規則的目標所需要的文件。

如果一個作爲目標或依賴的文件在當前目錄中不存在,make就會在VPATH指定的目錄中搜尋該文件。如果在這些目錄中找到要尋找的文件,則就象這些文件在當前目錄下存在一樣,規則把這些文件指定爲依賴。參閱編寫搜尋目錄的shell命令

VPATH變量定義中,目錄的名字由冒號或空格分開。目錄列舉的次序也是make 搜尋的次序。在MS-DOSMS-WINDOWS系統中,VPATH變量定義中的目錄的名字由分號分開,因爲在這些系統中,冒號用爲路徑名的一部分(通常在驅動器字母后面)。例如:

    VPATH = src:../headers

指定了兩個目錄,‘src’和‘…/headers’,make也按照這個次序進行搜尋。使用該VPATH的值,下面的規則,

    foo.o : foo.c

在執行時就象如下寫法一樣會被中斷:

foo.o : src/foo.c

然後在src目錄下搜尋foo.c

4.3.2 vpath指令

vpath指令(注意字母是小寫)和VPATH變量類似,但卻更具靈活性。vpath指令允許對符合一定格式類型的文件名指定一個搜尋路徑。這樣您就可以對一種格式類型的文件名指定一個搜尋路徑,對另外格式類型的文件名指定另外一個搜尋路徑。總共由三種形式的vpath指令:

vpath pattern directories

對一定格式類型的文件名指定一個搜尋路徑。搜尋的路徑由一列要搜尋的目錄構成,目錄由冒號(在MS-DOSMS-WINDOWS系統中用分號)或空格隔開,和VPATH變量定義要搜尋的路徑格式一樣。

vpath pattern

清除和一定類型格式相聯繫的搜尋路徑。

vpath

清除所有前面由vapth指令指定的搜尋路徑。

一個vpath的格式pattern是一個包含一個’%’的字符串。該字符串必須和正搜尋的一個依賴的文件名匹配,字符%可和任何字符串匹配(關於格式規則,參閱定義與重新定義格式規則)。例如,%.h和任何文件名以.h結尾的文件匹配。如果不使用‘%’,格式必須與依賴精確匹配,這種情況很少使用。

vpath指令格式中的字符‘%’可以通過前面的反斜槓被引用。引用其它字符‘%’的反斜槓也可以被更多的反斜槓引用。引用字符‘%’和其它反斜槓的反斜槓在和文件名比較之前和格式是分開的。如果反斜槓所引用的字符‘%’沒有錯誤,則該反斜槓不會運行帶來任何危害。

如果vpath指令格式和一個依賴的文件名匹配,並且在當前目錄中該依賴不存在,則vpath指令中指定的目錄和VPATH變量中的目錄一樣可以被搜尋。例如:

vpath %.h ../headers

將告訴make如果在當前目錄中以‘.h’結尾文件不存在,則在‘../headers’目錄下搜尋任何以‘.h’結尾依賴。

如果有幾個vpath指令格式和一個依賴的文件名匹配,則make一個接一個的處理它們,搜尋所有在指令中指定的目錄。Make按它們在makefile文件中出現的次序控制多個vpath指令,多個指令雖然有相同的格式,但它們是相互獨立的。以下代碼:

vpath %.c foo

vpath %   blish

vpath %.c bar

表示搜尋`.c'文件先搜尋目錄`foo'、然後`blish',最後`bar';如果是如下代碼:

vpath %.c foo:bar

vpath %   blish

表示搜尋`.c'文件先搜尋目錄foo'、然後‘bar',最後‘blish'

4.3.3目錄搜尋過程

    當通過目錄搜尋找到一個文件,該文件有可能不是您在依賴列表中所列出的依賴;有時通過目錄搜尋找到的路徑也可能被廢棄。Make決定對通過目錄搜尋找到的路徑保存或廢棄所依據的算法如下:

1、1、如果一個目標文件在makefile文件所在的目錄下不存在,則將會執行目錄搜尋。

2、2、如果目錄搜尋成功,則路徑和所得到的文件暫時作爲目標文件儲存。

3、3、所有該目標的依賴用相同的方法考察。

4、4、把依賴處理完成後,該目標可能需要或不需要重新創建:

1、1、如果該目標不需要重建,目錄搜尋時所得到的文件的路徑用作該目標所有依賴的路徑,同時包含該目標文件。簡而言之,如果make不必重建目標,則您使用通過目錄搜尋得到的路徑。

2、2、如果該目標需要重建,目錄搜尋時所得到的文件的路徑將廢棄,目標文件在makefile文件所在的目錄下重建。簡而言之,如果make要重建目標,是在makefile文件所在的目錄下重建目標,而不是在目錄搜尋時所得到的文件的路徑下。

該算法似乎比較複雜,但它卻可十分精確的解釋實際您所要的東西。

其它版本的make使用一種比較簡單的算法:如果目標文件在當前目錄下不存在,而它通過目錄搜尋得到,不論該目標是否需要重建,始終使用通過目錄搜尋得到的路徑。

實際上,如果在GNU make中使您的一些或全部目錄具備這種行爲,您可以使用GPATH變量來指定這些目錄。

GPATH變量和VPATH變量具有相同的語法和格式。如果通過目錄搜尋得到一個過時的目標,而目標存在的目錄又出現在GPATH變量,則該路徑將不廢棄,目標將在該路徑下重建。

4.3.4編寫目錄搜尋的shell命令

即使通過目錄搜尋在其它目錄下找到一個依賴,不能改變規則的命令,這些命令同樣按照原來編寫的方式執行。因此,您應該小心的編寫這些命令,以便它們可以在make能夠在發現依賴的目錄中處理依賴。

藉助諸如‘$^’的自動變量可更好的使用shell命令(參閱自動變量)。例如,‘$^’的值代表所有的依賴列表,幷包含尋找依賴的目錄;‘$@’的值是目標。

foo.o : foo.c

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

變量CFLAGS存在可以方便您利用隱含規則指定編譯C語言源程序的旗標。我們這裏使用它是爲了保持編譯C語言源程序一致性。參閱隱含規則使用的變量

依賴通常情況下也包含頭文件,因自動變量‘$<’的值是第一個依賴,因此這些頭文件您可以不必在命令中提及,例如:

VPATH = src:../headers

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

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

4.3.5 目錄搜尋和隱含規則

搜尋的目錄是由變量VPATH或隱含規則引入的vpath指令指定的(詳細參閱使用隱含規則)。例如,如果文件‘foo.o’沒有具體的規則,make則使用隱含規則:如文件foo.c存在,make使用內置的規則編譯它;如果文件foo.c不在當前目錄下,就搜尋適當的目錄,如在別的目錄下找到foo.cmake同樣使用內置的規則編譯它。

隱含規則的命令使用自動變量是必需的,所以隱含規則可以自然地使用目錄搜尋得到的文件。

4.3.6 連接庫的搜尋目錄

對於連接庫文件,目錄搜尋採用一種特別的方式。這種特別的方式來源於個玩笑:您寫一個依賴,它的名字是‘-|name’的形式。(您可以在這裏寫一些奇特的字符,因爲依賴正常是一些文件名,庫文件名通常是‘libname.a’ 的形式,而不是‘-|name’ 的形式。)

當一個依賴的名字是‘-|name’的形式時,make特別地在當前目錄下、與vpath匹配的目錄下、VPATH指定的目錄下以及‘/lib, /usr/lib', 和 ‘prefix/lib'(正常情況爲`/usr/local/lib',但是MS-DOSMS-Windows版本的make的行爲好像是prefix定義爲DJGPP安裝樹的根目錄的情況)目錄下搜尋名字爲‘libname.so'的文件然後再處理它。如果沒有搜尋到‘libname.so'文件,然後在前述的目錄下搜尋‘libname.a'文件。

例如,如果在您的系統中有‘/usr/lib/libcurses.a'的庫文件,則:

foo : foo.c -lcurses

        cc $^ -o $@

如果‘foo’比‘foo.c’更舊,將導致命令‘cc foo.c /usr/lib/libcurses.a -o foo'執行。

缺省情況下是搜尋‘libname.so' 和‘libname.a'文件,具體搜尋的文件及其類型可使用.LIBPATTERNS變量指定,這個變量值中的每一個字都是一個字符串格式。當尋找名爲-|name’的依賴時,make首先用name替代列表中第一個字中的格式部分形成要搜尋的庫文件名,然後使用該庫文件名在上述的目錄中搜尋。如果沒有發現庫文件,則使用列表中的下一個字,其餘以此類推。

.LIBPATTERNS變量缺省的值是"lib%.so lib%.a'",該值對前面描述的缺省行爲提供支持。您可以通過將該值設爲空值從而徹底關閉對連接庫的擴展。

4.4假想目標

假想目標並不是一個真正的文件名,它僅僅是您制定的一個具體規則所執行的一些命令的名稱。使用假想目標有兩個原因:避免和具有相同名稱的文件衝突和改善性能。

如果您寫一個其命令不創建目標文件的規則,一旦由於重建而提及該目標,則該規則的命令就會執行。這裏有一個例子:

clean:

        rm *.o temp

因爲rm命令不創建名爲‘clean’的文件,所以不應有名爲‘clean’的文件存在。因此不論何時您發佈`make clean'指令,rm命令就會執行

假想目標能夠終止任何在目錄下創建名爲‘clean’的文件工作。但如在目錄下存在文件clean,因爲該目標clean沒有依賴,所以文件clean始終會認爲已經該更新,因此它的命令將永不會執行。爲了避免這種情況,您應該使用象如下特別的.PHONY目標格式將該目標具體的聲明爲一個假想目標:

.PHONY : clean

一旦這樣聲明,‘make clean’命令無論目錄下是否存在名爲‘clean’的文件,該目標的命令都會執行。

因爲make知道假想目標不是一個需要根據別的文件重新創建的實際文件,所以它將跳過隱含規則搜尋假想目標的步驟(詳細內容參閱使用隱含規則)。這是把一個目標聲明爲假想目標可以提高執行效率的原因,因此使用假想目標您不用擔心在目錄下是否有實際文件存在。這樣,對前面的例子可以用假想目標的寫出,其格式如下:

.PHONY: clean

clean:

        rm *.o temp

另外一個使用假想目標的例子是使用make的遞歸調用進行連接的情況:此時,makefile文件常常包含列舉一系列需要創建的子目錄的變量。不用假想目標完成這種任務的方法是使用一條規則,其命令是一個在各個子目錄下循環的shell命令,如下面的例子:

subdirs:

        for dir in $(SUBDIRS); do /

          $(MAKE) -C $$dir; /

        done

但使用這個方法存在下述問題:首先,這個規則在創建子目錄時產生的任何錯誤都不及時發現,因此,當一個子目錄創建失敗時,該規則仍然會繼續創建剩餘的子目錄。雖然該問題可以添加監視錯誤產生並退出的shell命令來解決,但非常不幸的是如果make使用了‘-k’選項,這個問題仍然會產生。第二,也許更重要的是您使用了該方法就失去使用make並行處理的特點能力。

使用假想目標(如果一些子目錄已經存在,您則必須這樣做,否則,不存在的子目錄將不會創建)則可以避免上述問題:

SUBDIRS = foo bar baz

 

.PHONY: subdirs $(SUBDIRS)

 

subdirs: $(SUBDIRS)

 

$(SUBDIRS):

        $(MAKE) -C $

 

foo: baz

此時,如果子目錄‘baz’沒有創建完成,子目錄’foo’將不會創建;當試圖使用並行創建時這種關係的聲明尤其重要。

一個假想目標不應該是一個實際目標文件的依賴,如果這樣,make每次執行該規則的命令,目標文件都要更新。只要假想目標不是一個真實目標的依賴,假想目標的命令只有在假想目標作爲特別目標時纔會執行(參閱指定最終目標的參數)。

假想目標也可以有依賴。當一個目錄下包含多個程序時,使用假想目標可以方便的在一個makefile文件中描述多個程序的更新。重建的最終目標缺省情況下是makefile文件的第一個規則的目標,但將多個程序作爲假想目標的依賴則可以輕鬆的完成在一個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

這樣,您可以重建所有程序,也可以參數的形式重建其中的一個或多個(如‘make prog1 prog3')。

當一個假想目標是另一個假想目標的依賴,則該假想目標將作爲一個假想目標的子例程。例如,這裏‘make cleanall'用來刪除OBJ文件、diff文件和程序文件:

.PHONY: cleanall cleanobj cleandiff

 

cleanall : cleanobj cleandiff

        rm program

 

cleanobj :

        rm *.o

 

cleandiff :

        rm *.diff

4.5 沒有命令或依賴的規則

如果一個規則沒有依賴、也沒有命令,而且這個規則的目標也不是一個存在的文件,則make認爲只要該規則運行,該目標就已被更新。這意味着,所有以這種規則的目標爲依賴的目標,它們的命令將總被執行。這裏舉一個例子:

clean: FORCE

        rm $(objects)

FORCE:

這裏的目標‘FORCR’滿足上面的特殊條件,所以以其爲依賴的目標‘clean’將總強制它的命令執行。關於‘FORCR’的名字沒有特別的要求,但‘FORCR’是習慣使用的名字。

也許您已經明白,使用‘FORCR’的方法和使用假想目標(.PHONY: clean)的結果一樣,但使用假想目標更具體更靈活有效,由於一些別的版本的make不支持假想目標,所以‘FORCR’出現在許多makefile文件中。參閱假想目標

4.6使用空目標文件記錄事件

空目標是一個假想目標變量,它用來控制一些命令的執行,這些命令可用來完成一些經常需要的具體任務。但又不象真正的假想目標,它的目標文件可以實際存在,但文件的內容與此無關,通常情況下,這些文件沒有內容。

空目標文件的用途是用來記錄規則的命令最後一次執行的時間,也是空目標文件最後更改的時間。它之所以能夠這樣執行是因爲規則的命令中有一條用於更新目標文件的‘touch’命令。另外,空目標文件應有一些依賴(否則空目標文件沒有存在的意義)。如果空目標比它的依賴舊,當您命令重建空目標文件時,有關的命令纔會執行。下面有一個例子:

print: foo.c bar.c

        lpr -p $?

        touch print

按照這個規則,如果任何一個源文件從上次執行‘make print'以來發生變化,鍵入‘make print'則執行lpr命令。自動變量‘$?’用來打印那些發生變化的文件(參閱自動變量)。

4.7 內建的特殊目標名

一些名字作爲目標使用則含有特殊的意義:

l         l         .PHONY

特殊目標.PHONY的依賴是假想目標。假想目標是這樣一些目標,make無條件的執行它命令,和目錄下是否存在該文件以及它最後一次更新的時間沒有關係。詳細內容參閱假想目標

l         l         .SUFFIXES

特殊目標.SUFFIXES的依賴是一列用於後綴規則檢查的後綴。詳細內容參閱過時的後綴規則

l         l         .DEFAULT

.DEFAULT指定一些命令,這些命令用於那些沒有找到規則(具體規則或隱含規則)更新的目標。詳細內容參閱定義最新類型的-缺省規則。如果.DEFAULT指定了一些命令,則所有提及到的文件只能作爲依賴,而不能作爲任何規則的目標;這些指定的命令也只按照他們自己的方式執行。詳細內容參閱隱含規則搜尋算法

l         l         .PRECIOUS

特殊目標.PRECIOUS的依賴將按照下面給定的特殊方式進行處理:如果在執行這些目標的命令的過程中,make被關閉或中斷,這些目標不能被刪除,詳細內容參閱關閉和中斷make;如果目標是中間文件,即使它已經沒有任何用途也不能被刪除,具體情況和該目標正常完成一樣,參閱隱含規則鏈;該目標的其它功能和特殊目標.SECONDARY的功能重疊。如果規則的目標格式與依賴的文件名匹配,您可以使用隱含規則的格式(如‘%.O’)列舉目標作爲特殊目標.PRECIOUS的依賴文件來保存由這些規則創建的中間文件。

l         l         .INTERMEDIATE

特殊目標.INTERMEDIATE的依賴被處理爲中間文件。詳細內容參見隱含規則鏈.INTERMEDIATE如果沒有依賴文件,它將不會發生作用。

l         l         .SECONDARY

特殊目標.SECONDARY的依賴被處理爲中間文件,但它們永遠不能自動刪除。詳細內容參見隱含規則鏈.SECONDARY如果沒有依賴文件,則所有的makefile文件中的目標都將被處理爲中間文件。

l         l         .DELETE_ON_ERROR

如果在makefile文件的某處.DELETE_ON_ERROR作爲一個目標被提及,則如果該規則發生變化或它的命令沒有正確完成而退出,make將會刪除該規則的目標,具體行爲和它受到了刪除信號一樣。詳細內容參閱命令錯誤

l         l         .IGNORE

如果您特別爲目標.IGNORE指明依賴,則MAKE將會忽略處理這些依賴文件時執行命令產生的錯誤。如果.IGNORE作爲一個沒有依賴的目標提出來,MAKE將忽略處理所有文件時產生的錯誤。.IGNORE命令並沒有特別的含義,.IGNORE的用途僅是爲了和早期版本的兼容。因爲.IGNORE影響所有的命令,所以它的用途不大;我們推薦您使用其它方法來忽略特定命令產生的錯誤。詳細內容參閱命令錯誤

l         l         .SILENT

如果您特別爲.SILENT指明依賴,則在執行之前MAKE將不會回顯重新構造文件的命令。如果.SILENT作爲一個沒有依賴的目標提出來,任何命令在執行之前都不會打印。.SILENT並沒有特別的含義,其用途僅是爲了和早期版本的兼容。我們推薦您使用其它方法來處理那些不打印的命令。詳細內容參閱命令回顯。如果您希望所有的命令都不打印,請使用‘-s’或‘-silent’選項(詳細參閱選項概要)

l         l         .EXPORT_ALL_VARIABLES

如該特殊目標簡單的作爲一個目標被提及,MAKE將缺省地把所有變量都傳遞到子進程中。參閱使與子MAKE通信的變量

l         l         .NOTPARALLEL

如果.NOTPARALLEL作爲一個目標提及,即使給出‘-j’選項,make也不使用並行執行。但遞歸調用的make命令仍可並行執行(在調用的makefile文件中包含.NOTPARALLEL的目標的例外)。.NOTPARALLEL的任何依賴都將忽略。

任何定義的隱含規則後綴如果作爲目標出現都會視爲一個特殊規則,即使兩個後綴串聯起來也是如此,例如‘.c.o’。這些目標稱爲後綴規則,這種定義方法是過時的定義隱含規則的方法(目前仍然廣泛使用的方法)。原則上,如果您要把它分爲兩個並把它們加到後綴列表中,任何目標名都可採用這種方法指定。實際上,後綴一般以‘.’開始,因此,這些特別的目標同樣以‘.’開始。具體參閱過時的後綴規則

4.8 具有多個目標的規則

具有多個目標的規則等同於寫多條規則,這些規則除了目標不同之外,其餘部分完全相同。相同的命令應用於所有目標,但命令執行的結果可能有所差異,因此您可以在命令中使用‘$@’分配不同的實際目標名稱。這條規則同樣意味着所有的目標有相同的依賴。

在以下兩種情況下具有多個目標的規則相當有用:

l         l         您僅僅需要依賴,但不需要任何命令。例如:

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

爲三個提及的目標文件給出附加的共同依賴。

l         l         所有的目標使用相同的命令。但命令的執行結果未必完全相同,因爲自動變量‘$@’可以在重建時指定目標(參閱自動變量)。例如:

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

這裏我們假設程序可以產生兩種輸出文件類型:一種給出‘-big’,另一種給出‘-little’。參閱字符串代替和分析函數,對函數subst的解釋。

如果您喜歡根據目標變換依賴,象使用變量‘$@’變換命令一樣。您不必使用具有多個目標的規則,您可以使用靜態格式規則。詳細內容見下文。

4.9 具有多條規則的目標

一個目標文件可以有多個規則。在所有規則中提及的依賴都將融合在一個該目標的依賴列表中。如果該目標比任何一個依賴‘舊’,所有的命令將執行重建該目標。

但如果一條以上的規則對同一文件給出多條命令,make將使用最後給出的規則,同時打印錯誤信息。(作爲特例,如果文件名以點‘.’開始,不打印出錯信息。這種古怪的行爲僅僅是爲了和其它版本的make兼容)。您沒有必要這樣編寫您的makefile文件,這正是make給您發出錯誤信息的原因。

一條特別的依賴規則可以用來立即給多條目標文件提供一些額外的依賴。例如,使用名爲‘objects’的變量,該變量包含系統產生的所有輸出文件列表。如果‘congfig.h’發生變化所有的輸出文件必須重新編譯,可以採用下列簡單的方法編寫:

objects = foo.o bar.o

foo.o : defs.h

bar.o : defs.h test.h

$(objects) : config.h

這些可以自由插入或取出而不影響實際指定的目標文件生成規則,如果您希望斷斷續續的爲目標添加依賴,這是非常方便的方法。

另外一個添加依賴的方法是定義一個變量,並將該變量作爲make命令的參數使用。詳細內容參閱變量重載。例如:

extradeps=

$(objects) : $(extradeps)

命令`make extradeps=foo.h'含義是將‘foo.h’作爲所有OBJ文件的依賴,如果僅僅輸入‘make’命令則不是這樣。

如果沒有具體的規則爲目標的生成指定命令,那麼make將搜尋合適的隱含規則進而確定一些命令來完成生成或重建目標。詳細內容參閱使用隱含規則

4.10 靜態格式規則

靜態格式規則是指定多個目標並能夠根據每個目標名構造對應的依賴名的規則。靜態格式規則在用於多個目標時比平常的規則更常用,因爲目標可以不必有完全相同的依賴;也就是說,這些目標的依賴必須類似,但不必完全相同。

4.10.1 靜態格式規則的語法

這裏是靜態格式規則的語法格式:

targets ...: target-pattern: dep-patterns ...

        commands

        ...

目標列表指明該規則應用的目標。目標可以含有通配符,具體使用和平常的目標規則基本一樣(參閱在文件名中使用通配符)。

目標的格式和依賴的格式是說明如何計算每個目標依賴的方法。從匹配目標格式的目標名中依據格式抽取部分字符串,這部分字符串稱爲徑。將徑分配到每一個依賴格式中產生依賴名。

每一個格式通常包含字符‘%’。目標格式匹配目標時,‘%’可以匹配目標名中的任何字符串;這部分匹配的字符串稱爲徑;剩下的部分必須完全相同。如目標‘foo.o’匹配格式‘%.o’,字符串‘foo’稱爲徑。而目標‘foo.c’和‘foo.out’不匹配格式。

每個目標的依賴名是使用徑代替各個依賴中的‘%’產生。如,如果一個依賴格式爲‘%.c’,把徑‘foo’代替依賴格式中的‘%’生成依賴的文件名‘foo.c’。在依賴格式中不包含‘%’也是合法的,此時對所有目標來說,依賴是相同的。

在格式規則中字符‘%’可以用前面加反斜槓‘/’方法引用。引用‘%’的反斜槓也可以由更多的反斜槓引用。引用‘%’、‘/’的反斜槓在和文件名比較或由徑代替它之前從格式中移走。反斜槓不會因爲引用‘%’而混亂。如,格式`the/%weird//%pattern//'`the%weird/' 加上字符‘%',後面再和字符串 ‘pattern//'連接。最後的兩個反斜槓由於不能影響任何統配符‘%’所以保持不變。

這裏有一個例子,它將對應的‘.c’文件編譯成‘foo.o’和‘bar.o’。

objects = foo.o bar.o

 

all: $(objects)

 

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

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

這裏‘$<’是自動變量,控制依賴的名稱,‘$@’也是自動變量,掌握目標的名稱。詳細內容參閱自動變量

每一個指定目標必須和目標格式匹配,如果不符則產生警告。如果您有一列文件,僅有其中的一部分和格式匹配,您可以使用filter函數把不符合的文件移走(參閱字符串替代和分析函數):

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))'的結果是bar.o lose.o',第一個靜態格式規則是將相應的C語言源文件編譯更新爲OBJ文件,‘$(filter %.elc,$(files))' 的結果是foo.elc',它由‘foo.el’構造。

另一個例子是闡明怎樣在靜態格式規則中使用‘$*’:

bigoutput littleoutput : %output : text.g

        generate text.g -$* > $@

當命令generate執行時,$*擴展爲徑,即‘big’或‘little’二者之一。

4.10.2靜態格式規則和隱含規則

靜態格式規則和定義爲格式規則的隱含規則有很多相同的地方(詳細參閱定義與重新定義格式規則)。雙方都有對目標的格式和構造依賴名稱的格式,差異是make使用它們的時機不同。

隱含規則可以應用於任何於它匹配的目標,但它僅僅是在目標沒有具體規則指定命令以及依賴可以被搜尋到的情況下應用。如果有多條隱含規則適合,僅有執行其中一條規則,選擇依據隱含規則的定義次序。

相反,靜態格式規則用於在規則中指明的目標。它不能應用於其它任何目標,並且它的使用方式對於各個目標是固定不變的。如果使用兩個帶有命令的規則發生衝突,則是錯誤。

靜態格式規則因爲如下原因可能比隱含規則更好:

l         l         對一些文件名不能按句法分類的但可以給出列表的文件,使用靜態格式規則可以重載隱含規則鏈。

l         l         如果不能精確確定使用的路徑,您不能確定一些無關緊要的文件是否導致make使用錯誤的隱含規則(因爲隱含規則的選擇根據其定義次序)。使用靜態格式規則則沒有這些不確定因素:每一條規則都精確的用於指定的目標上。

4.11雙冒號規則

雙冒號規則是在目標名後使用‘::’代替‘:’的規則。當同一個目標在一條以上的規則中出現時,雙冒號規則和平常的規則處理有所差異。

當一目標在多條規則中出現時,所有的規則必須是同一類型:要麼都是雙冒號規則,要麼都是普通規則。如果他們都是雙冒號規則,則它們之間都是相互獨立的。如果目標比一個雙冒號規則的依賴‘舊’,則該雙冒號規則的命令將執行。這可導致具有同一目標雙冒號規則全部或部分執行。

雙冒號規則實際就是將具有相同目標的多條規則相互分離,每一條雙冒號規則都獨立的運行,就像這些規則的目標不同一樣。

對於一個目標的雙冒號規則按照它們在makefile文件中出現的順序執行。然而雙冒號規則真正有意義的場合是雙冒號規則和執行順序無關的場合。

雙冒號規則有點模糊難以理解,它僅僅提供了一種在特定情況下根據引起更新的依賴文件不同,而採用不同方式更新目標的機制。實際應用雙冒號規則的情況非常罕見。

每一個雙冒號規則都應該指定命令,如果沒有指定命令,則會使用隱含規則。詳細內容參閱使用隱含規則

4.12 自動生成依賴

在爲一個程序編寫的makefile文件中,常常需要寫許多僅僅是說明一些OBJ文件依靠頭文件的規則。例如,如果‘main.c’通過一條#include語句使用‘defs.h’,您需要寫入下的規則:

main.o: defs.h

您需要這條規則讓make知道如果‘defs.h’一旦改變必須重新構造‘main.o’。由此您可以明白對於一個較大的程序您需要在makefile文件中寫很多這樣的規則。而且一旦添加或去掉一條#include語句您必須十分小心地更改makefile文件。

爲避免這種煩惱,現代C編譯器根據原程序中的#include語句可以爲您編寫這些規則。如果需要使用這種功能,通常可在編譯源程序時加入‘-M’開關,例如,下面的命令:

cc -M main.c

產生如下輸出:

main.o : main.c defs.h

這樣您就不必再親自寫這些規則,編譯器可以爲您完成這些工作。

注意,由於在makefile文件中提及構造‘main.o’,因此‘main.o’將永遠不會被隱含規則認爲是中間文件而進行搜尋,這同時意味着make不會在使用它之後自動刪除它;參閱隱含規則鏈

對於舊版的make程序,通過一個請求命令,如‘make depend’,利用編譯器的特點生成依賴是傳統的習慣。這些命令將產生一個‘depend’文件,該文件包含所有自動生成的依賴;然後makefile文件可以使用include命令將它們讀入(參閱包含其它makefile文件)。

GNU make中,重新構造makefile文件的特點使這個慣例成爲了過時的東西――您永遠不必具體告訴make重新生成依賴,因爲GNU make總是重新構造任何過時的makefile文件。參閱Makefile文件的重新生成的過程

我們推薦使用自動生成依賴的習慣是把makefile文件和源程序文件一一對應起來。如,對每一個源程序文件‘name.c’有一名爲‘name.d’的makefile文件和它對應,該makefile文件中列出了名爲‘name.o’的OBJ文件所依賴的文件。這種方式的優點是僅在源程序文件改變的情況下才有必要重新掃描生成新的依賴。

這裏有一個根據C語言源程序‘name.c’生成名爲‘name.d’依賴文件的格式規則:

%.d: %.c

        set -e; $(CC) -M $(CPPFLAGS) $< /

                  | sed 's//($*/)/.o[ :]*//1.o $@ : /g' > $@; /

                [ -s $@ ] || rm -f $@

關於定義格式規則的信息參閱定義與重新定義格式規則。‘-e’開關是告訴shell如果$(CC)命令運行失敗(非零狀態退出)立即退出。正常情況下,shell退出時帶有最後一個命令在管道中的狀態(sed,因此make不能注意到編譯器產生的非零狀態。

對於GNU C編譯器您可以使用‘-MM’開關代替‘-M’,這是省略了有關係統頭文件的依賴。詳細內容參閱《GNU CC使用手冊》中控制預處理選項

命令Sed的作用是翻譯(例如):

main.o : main.c defs.h

到:

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

這使每一個‘.d’文件和與之對應的‘.o’文件依靠相同的源程序文件和頭文件,據此,Make可以知道如果任一個源程序文件和頭文件發生變化,則必須重新構造依賴文件。

一旦您定義了重新構造‘.d’文件的規則,您可以使用使用include命令直接將它們讀入,(參閱包含其它makefile文件),例如:

sources = foo.c bar.c

include $(sources:.c=.d)

(這個例子中使用一個代替變量參照從源程序文件列表foo.c bar.c'翻譯到依賴文件列表‘foo.d bar.d'。詳細內容參閱替換引用。)所以,‘.d’的makefile文件和其它makefile文件一樣,即使沒用您的任何進一步的指令,make同樣會在必要的時候重新構建它們。參閱Makefile文件的重新生成過程

5在規則中使用命令

規則中的命令由一系列shell命令行組成,它們一條一條的按順序執行。除第一條命令行可以分號爲開始附屬在目標-依賴行後面外,所有的命令行必須以TAB開始。空白行與註釋行可在命令行中間出現,處理時它們被忽略。(但是必須注意,以TAB開始的‘空白行’不是空白行,它是空命令,參閱使用空命令。)

用戶使用多種不同的shell程序,如果在makefile文件中沒有指明其它的shell,則使用缺省的‘/bin/sh’解釋makefile文件中的命令。參閱命令執行

使用的shell種類決定了是否能夠在命令行上寫註釋以及編寫註釋使用的語法。當使用‘/bin/sh’作爲shell,以‘#’開始的註釋一直延伸到該行結束。‘#’不必在行首,而且‘#’不是註釋的一部分。

5.1 命令回顯

正常情況下make在執行命令之前首先打印命令行,我們因這樣可將您編寫的命令原樣輸出故稱此爲回顯。

以‘@’起始的行不能回顯,‘@’在傳輸給shell時被丟棄。典型的情況,您可以在makefile文件中使用一個僅僅用於打印某些內容的命令,如echo命令來顯示makefile文件執行的進程:

@echo About to make distribution files

當使用make時給出‘-n’或‘--just-print’標誌,則僅僅回顯命令而不執行命令。參閱選項概要。在這種情況下也只有在這種情況下,所有的命令行都回顯,即使以‘@’開始的命令行也回顯。這個標誌對於在不使用命令的情況下發現make認爲哪些是必要的命令非常有用。

-s’或‘--silent’標誌可以使make阻止所有命令回顯,好像所有的行都以‘@’開始一樣。在makefile文件中使用不帶依賴的特別目標‘.SILENT’的規則可以達到相同的效果(參閱內建的特殊目標名)。因爲‘@’使用更加靈活以至於現在已基本不再使用特別目標.SILENT

5.2執行命令

需要執行命令更新目標時,每一命令行都會使用一個獨立的子shell環境,保證該命令行得到執行。(實際上,make可能走不影響結果的捷徑。)

請注意這意味着設置局部變量的shell命令如cd等將不影響緊跟着的命令行;如果您需要使用cd命令影響到下一個命令,請把這兩個命令放到一行,它們中間用分號隔開,這樣make將認爲它們是一個單一的命令行,把它們放到一起傳遞給shell,然後按順序執行它們。例如:

foo : bar/lose

        cd bar; gobble lose > ../foo

如果您喜歡將一個單一的命令分割成多個文本行,您必須用反斜槓作爲每一行的結束,最後一行除外。這樣,多個文本行通過刪除反斜槓按順序組成一新行,然後將它傳遞給shell。如此,下面的例子和前面的例子是等同的:

foo : bar/lose

        cd bar;  /

        gobble lose > ../foo

用作shell的程序是由變量SHELL指定,缺省情況下,使用程序‘/bin/sh’作爲shell

MS_DOS上運行,如果變量SHELL沒有指定,變量COMSPEC的值用來代替指定shell

MS_DOS上運行和在其它系統上運行,對於makefile文件中設置變量SHELL的行的處理也不一樣。因爲MS_DOSshell,‘command.com’,功能十分有限,所以許多make用戶傾向於安裝一個代替的shell。因此,在MS_DOS上運行,make檢測變量SHELL的值,並根據它指定的Unix風格或DOS風格的shell變化它的行爲。即使使用變量SHELL指向‘command.com’ ,make依然檢測變量SHELL的值。

如果變量SHELL指定Unix風格的shell,在MS_DOS上運行的make將附加檢查指定的shell是否能真正找到;如果不能找到,則忽略指定的shell。在MS_DOS上,GNU make按照下述步驟搜尋shell:

1、在變量SHELL指定的目錄中。例如,如果makefile指明`SHELL = /bin/sh'make將在當前路徑下尋找子目錄‘/bin’。

2在當前路徑下。

3、按順序搜尋變量PATH指定的目錄。

在所有搜尋的目錄中,make首先尋找指定的文件(如例子中的‘sh’)。如果該文件沒有存在,make將在上述目錄中搜尋帶有確定的可執行文件擴展的文件。例如:.exe', .com', .bat', .btm', .sh'文件和其它文件等。

如果上述過程中能夠成功搜尋一個shell,則變量SHELL的值將設置爲所發現shell的全路徑文件名。然而如果上述努力全部失敗,變量SHELL的值將不改變,設置shell的行的有效性將被忽略。這是在make運行的系統中如果確實安裝了Unix風格的shellmake僅支持指明的Unix風格shell特點的原因。

注意這種對shell的擴展搜尋僅僅限制在makefile文件中設置變量SHELL的情況。如果在環境或命令行中設置,希望您指定shell的全路徑文件名,而且全路徑文件名需象在Unix系統中運行的一樣準確無誤。

經過上述的DOS特色的處理,而且您還把 ‘sh.exe’安裝在變量PATH指定的目錄中,或在makefile文件內部設置`SHELL = /bin/sh' (和多數Unixmakefile文件一樣),則MS_DOS上的運行效果和在Unix上運行完全一樣。

不像其它大多數變量,變量SHELL從不根據環境設置。這是因爲環境變量SHELL是用來指定您自己選擇交互使用的shell程序。如果變量SHELL在環境中設置,它將影響makefile文件的功能,這是非常不划算的,參閱環境變量。然而在MS-DOSMS-WINDOWS中在環境中設置變量SHELL的值是要使用的,因爲在這些系統中,絕大多數用戶並不設置該變量的值,所以make很可能特意爲該變量指定要使用的值。在MS-DOS上,如果變量SHELL的設置對於make不合適,您可以設置變量MAKESHELL用來指定make使用的shell;這種設置將使變量SHELL的值失效。

5.3 並行執行

GNU make可以同時執行幾條命令。正常情況下,make一次執行一個命令,待它完成後在執行下一條命令。然而,使用‘-j’和‘--jobs’選項將告訴make同時執行多條命令。在MS-DOS上,‘-j’選項沒有作用,因爲該系統不支持多進程處理。

如果‘-j’選項後面跟一個整數,該整數表示一次執行的命令的條數;這稱爲job slots數。如果‘-j’選項後面沒有整數,也就是沒有對job slots的數目限制。缺省的job slots數是一,這意味着按順序執行(一次執行一條命令)。同時執行多條命令的一個不太理想的結果是每條命令產生的輸出與每條命令發送的時間對應,即命令產生的消息回顯可能較爲混亂。

另一個問題是兩個進程不能使用同一設備輸入,所以必須確定一次只能有一條命令從終端輸入,make只能保證正在運行的命令的標準輸入流有效,其它的標準輸入流將失效。這意味着如果有幾個同時從標準輸入設備輸入的話,對於絕大多數子進程將產生致命的錯誤(即產生一個‘Broken pipe’信號)。

命令對一個有效的標準輸入流(它從終端輸入或您爲make改造的標準輸入設備輸入)的需求是不可預測的。第一條運行的命令總是第一個得到標準輸入流,在完成一條命令後第一條啓動的另一條命令將得到下一個標準輸入流,等等。

如果我們找到一個更好替換方案,我們將改變make的這種工作方式。在此期間,如果您使用並行處理的特點,您不應該使用任何需要標準輸入的命令。如果您不使用該特點,任何需要標準輸入的命令將都能正常工作。

最後,make的遞歸調用也導致出現問題。更詳細的內容參閱與子make通訊的選項

如果一個命令失敗(被一個信號中止,或非零退出),且該條命令產生的錯誤不能忽略(參閱命令錯誤),剩餘的構建同一目標的命令行將會停止工作。如果一條命令失敗,而且‘-k’或‘--keep-going’選項也沒有給出(參閱選項概要),make將放棄繼續執行。如果make由於某種原因(包括信號)要中止,此時又子進程正在運行,它將等到這些子進程結束之後再實際退出。

當系統正滿負荷運行時,您或許希望在負荷輕的時再添加任務。這時,您可以使用‘-|’選項告訴make根據平均負荷限制同一時刻運行的任務數量。‘-|’或‘--max-load’選項一般後跟一個浮點數。例如:

-| 2.5

將不允許make在平均負荷高於2.5時啓動一項任務。‘-|’選項如果沒有跟數據,則取消前面‘-|’給定的負荷限制。

更精確地講,當make啓動一項任務時,而它此時已經有至少一項任務正在運行,則它將檢查當前的平均負荷;如果不低於‘-|’選項給定的負荷限制時,make將等待直到平均負荷低於限制或所有其它任務完成後再啓動其它任務。

缺省情況下沒有負荷限制。

5.4命令錯誤

在每一個shell命令返回後,make檢查該命令退出的狀態。如果該命令成功地完成,下一個命令行就會在新的子shell環境中執行,當最後一個命令行完成後,這條規則也宣告完成。如果出現錯誤(非零退出狀態),make將放棄當前的規則,也許是所有的規則。

有時一個特定的命令失敗並不是出現了問題。例如:您可能使用mkdir命令創建一個目錄存在,如果該目錄已經存在,mkdir將報告錯誤,但您此時也許要make繼續執行。

要忽略一個命令執行產生的錯誤,請使用字符‘-’(在初始化TAB的後面)作爲該命令行的開始。字符‘-’在命令傳遞給shell執行時丟棄。例如:

clean:

        -rm -f *.o

這條命令即使在不能刪除一個文件時也強制rm繼續執行。

在運行make時使用‘-i’或‘--ignore-errors’選項,將會忽略所有規則的命令運行產生的錯誤。在makefile文件中使用如果沒有依賴的特殊目標.IGNORE規則,也具有同樣的效果。但因爲使用字符‘-’更靈活,所以該條規則已經很少使用。

一旦使用‘-’或‘-i’選項,運行命令時產生的錯誤被忽略,此時make象處理成功運行的命令一樣處理具有返回錯誤的命令,唯一不同的地方是打印一條消息,告訴您命令退出時的編碼狀態,並說明該錯誤已經被忽略。如果發生錯誤而make並不說明其被忽略,則暗示當前的目標不能成功重新構造,並且和它直接相關或間接相關的目標同樣不能重建。因爲前一個過程沒有完成,所以不會進一步執行別的命令。

在上述情況下,make一般立即放棄任務,返回一個非零的狀態。然而,如果指定‘-k’或‘--keep-goning’選項,make則繼續考慮這個目標的其它依賴,如果有必要在make放棄返回非零狀態之前重建它們。例如,在編譯一個OBJ文件發生錯誤後,即使make已經知道將所有OBJ文件連接在一起是不可能的,make -k'選項也繼續編譯其它OBJ文件。詳細內容參閱選項概要。通常情況下,make的行爲基於假設您的目的是更新指定的目標,一旦make得知這是不可能的,它將立即報告失敗。‘-k’選項是告訴make真正的目的是測試程序中所有變化的可行性,或許是尋找幾個獨立的問題以便您可以在下次編譯之前糾正它們。這是Emacs編譯命令缺省情況下傳遞‘-k’選項的原因。

通常情況下,當一個命令運行失敗時,如果它已經改變了目標文件,則該文件很可能發生混亂而不能使用或該文件至少沒有完全得到更新。但是,文件的時間戳卻表明該文件已經更新到最新,因此在make下次運行時,它將不再更新該文件。這種狀況和命令被髮出的信號強行關閉一樣,參閱中斷或關閉make。因此,如果在開始改變目標文件後命令出錯,一般應該刪除目標文件。如果.DELETE_ON_ERROR作爲目標在makefile文件中出現,make將自動做這些事情。這是您應該明確要求make執行的動作,不是以前的慣例;特別考慮到兼容性問題時,您更應明確提出這樣的要求。

5.5中斷或關閉make

如果make在一條命令運行時得到一個致命的信號, 則make將根據第一次檢查的時間戳和最後更改的時間戳是否發生變化決定它是否刪除該命令要更新的目標文件。

刪除目標文件的目的是當make下次運行時確保目標文件從原文件得到更新。爲什麼?假設正在編譯文件時您鍵入Ctrl-c,而且這時已經開始寫OBJ文件‘foo.o’,Ctrl-c關閉了該編譯器,結果得到不完整的OBJ文件‘foo.o’的時間戳比源程序‘foo.c’的時間戳新,如果make收到Ctrl-c的信號而沒有刪除OBJ文件‘foo.o’,下次請求make更新OBJ文件‘foo.o’時,make將認爲該文件已更新到最新而沒有必要更新,結果在linkerOBJ文件連接爲可執行文件時產生奇怪的錯誤信息。

您可以將目標文件作爲特殊目標.PRECIOUS的依賴從而阻止make這樣刪除該目標文件。在重建一個目標之前,make首先檢查該目標文件是否出現在特殊目標.PRECIOUS的依賴列表中,從而決定在信號發生時是否刪除該目標文件。您不刪除這種目標文件的原因可能是:目標更新是一種原子風格,或目標文件存在僅僅爲了記錄更改時間(其內容無關緊要),或目標文件必須一直存在,用來防止其它類型的錯誤等。

5.6遞歸調用make

遞歸調用意味着可以在makefile文件中將make作爲一個命令使用。這種技術在包含大的系統中把makefile分離爲各種各樣的子系統時非常有用。例如,假設您有一個子目錄‘subdir’,該目錄中有它自己的makefile文件,您希望在該子目錄中運行make時使用該makefile文件,則您可以按下述方式編寫:

  subsystem:

         cd subdir && $(MAKE)

, 等同於這樣寫 (參閱選項概要):

subsystem:

        $(MAKE) -C subdir

您可以僅僅拷貝上述例子實現make的遞歸調用,但您應該瞭解它們是如何工作的,它們爲什麼這樣工作,以及子make和上層make的相互關係。

爲了使用方便,GNU make把變量CURDIR的值設置爲當前工作的路徑。如果‘-C’選項有效,它將包含的是新路徑,而不是原來的路徑。該值和它在makefile中設置的值有相同的優先權(缺省情況下,環境變量CURDIR不能重載)。注意,操作make時設置該值無效。

5.6.1 變量MAKE的工作方式

遞歸調用make的命令總是使用變量MAKE,而不是明確的命令名‘make’,如下所示:

subsystem:

         cd subdir && $(MAKE)

該變量的值是調用make的文件名。如果這個文件名是‘/bin/make’,則執行的命令是`cd subdir && /bin/make'。如果您在上層makefile文件時用特定版本的make,則執行遞歸調用時也使用相同的版本。

在命令行中使用變量MAKE可以改變‘-t' (--touch'), -n' (--just-print'), 或 ‘-q' (--question')選項的效果。如果在使用變量MAKE的命令行首使用字符‘+’也會起到相同的作用。參閱代替執行命令

設想一下在上述例子中命令‘make -t’的執行過程。(‘-t’選項標誌目標已經更新,但卻不執行任何命令,參閱代替執行命令。)按照通常的定義,命令‘make t’在上例中僅僅創建名爲‘subsystem’的文件而不進行別的工作。您實際要求運行‘cd subdir && make t’幹什麼?是執行命令或是按照‘-t’的要求不執行命令?

Make的這個特點是這樣的:只要命令行中包含變量MAKE,標誌`-t', `-n' `-q'將不對本行起作用。雖然存在標誌不讓命令執行,但包含變量MAKE的命令行卻正常運行,make實際上是通過變量MAKEFLAGS將標誌值傳遞給了子make(參閱與子make通訊的選項)。所以您的驗證文件、打印命令的請求等都能傳遞給子系統。

5.6.2與子make通訊的變量

通過明確要求,上層make變量的值可以藉助環境傳遞給子make,這些變量能在子make中缺省定義,在您不使用‘-e’開關的情況下,傳遞的變量的值不能代替子make使用的makefile文件中指定的值(參閱命令概要)。

向下傳遞、或輸出一個變量時,make將該變量以及它的值添加到運行每一條命令的環境中。子make,作爲響應,使用該環境初始化它的變量值表。參閱環境變量

除了明確指定外,make僅向下輸出在環境中定義並初始化的或在命令行中設置的變量,而且這些變量的變量名必須僅由字母、數字和下劃線組成。一些shell不能處理名字中含有字母、數字和下劃線以外字符的環境變量。特殊變量如SHELLMAKEFLAGS一般總要向下輸出(除非您不輸出它們)。即使您把變量MAKEFILE設爲其它的值,它也向下輸出。

Make自動傳遞在命令行中定義的變量的值,其方法是將它們放入MAKEFLAGS變量中。詳細內容參閱下節。Make缺省創造的變量的值不能向下傳遞,子make可以自己定義它們。如果您要將指定變量輸出給子make,請用export指令,格式如下:

export variable ...

您要將阻止一些變量輸出給子make,請用unexport指令,格式如下:

unexport variable ...

爲方便起見,您可以同時定義並輸出一個變量:

export variable = value

下面的格式具有相同的效果:

variable = value

export variable

以及

export variable := value

具有相同的效果:

variable := value

export variable

同樣,

export variable += value

亦同樣:

variable += value

export variable

參閱爲變量值追加文本

您可能注意到exportunexport指令在makeshell中的工作方式相同,如sh

如果您要將所有的變量都輸出,您可以單獨使用export

export

這告訴make exportunexport沒有提及的變量統統輸出,但任何在unexport提及的變量仍然不能輸出。如果您單獨使用export作爲缺省的輸出變量方式,名字中含有字母、數字和下劃線以外字符的變量將不能輸出,這些變量除非您明確使用export指令提及才能輸出。

單獨使用export的行爲是老闆本GNU make缺省定義的行爲。如果您的makefile依靠這些行爲,而且您希望和老闆本GNU make兼容,您可以爲特殊目標.EXPORT_ALL_VARIABLES 編寫一條規則代替export指令,它將被老闆本GNU make忽略,但如果同時使用export指令則報錯。

同樣,您可以單獨使用unexport告訴make缺省不要輸出變量,因爲這是缺省的行爲,只有前面單獨使用了export(也許在一個包括的makefile中)您纔有必要這樣做。您不能同時單獨使用exportunexport指令實現對某些命令輸出對其它的命令不輸出。最後面的一條指令(exportunexport)將決定make的全部運行結果。

作爲一個特點,變量MAKELEVEL的值在從一個層次向下層傳遞時發生變化。該變量的值是字符型,它用十進制數表示層的深度。‘0’代表頂層make,‘1’代表子make,‘2’代表子--make,以此類推。Make爲一個命令建立一次環境,該值增加1

該變量的主要作用是在一個條件指令中測試(參閱makefile文件的條件語句);採用這種方法,您可以編寫一個makefile,如果遞歸調用採用一種運行方式,由您控制直接執行採用另一種運行方式。

您可以使用變量MAKEFILES使所有的子make使用附加的makefile文件。變量MAKEFILES的值是makefile文件名的列表,文件名之間用空格隔開。在外層makefile中定義該變量,該變量的值將通過環境向下傳遞;因此它可以作爲子make的額外的makefile文件,在子make讀正常的或指定的makefile文件前,將它們讀入。參閱變量MAKEFILES

5.6.3與子make通訊的選項

諸如‘-s’和‘-k’標誌通過變量MAKEFLAGS自動傳遞給子make。該變量由make自動建立,幷包含make收到的標誌字母。所以,如果您是用‘make ks’變量MAKEFLAGS就得到值‘ks’。

作爲結果,任一個子make都在它的運行環境中爲變量MAKEFLAGS賦值;作爲響應,make使用該值作爲標誌並進行處理,就像它們作爲參數被給出一樣。參閱選項概要

同樣,在命令行中定義的變量也將藉助變量MAKEFLAGS傳遞給子make。變量MAKEFLAGS值中的字可以包含‘=’,make將它們按變量定義處理,其過程和在命令行中定義的變量一樣。參閱變量重載

選項`-C', `-f', `-o', 和 ‘-W’不能放入變量MAKEFLAGS中;這些選項不能向下傳遞。

-j’選項是一個特殊的例子(參閱並行執行)。如果您將它設置爲一些數值‘N’,而且您的操作系統支持它(大多數Unix系統支持,其它操作系統不支持),父make和所有子make通訊保證在它們中間同時僅有‘N’個任務運行。注意,任何包含遞歸調用的任務(參閱代替執行命令)不能計算在總任務數內(否則,我們僅能得到‘N’個子make運行,而沒有多餘的時間片運行實在的工作)。

如果您的操作系統不支持上述通訊機制,那麼‘-j 1’將放到變量MAKEFLAGS中代替您指定的值。這是因爲如果‘-j’選項傳遞給子make,您可能得到比您要求多很多的並行運行的任務數。如果您給出‘-j’選項而沒有數字參數,意味着儘可能並行處理多個任務,這樣向下傳遞,因爲倍數的無限制性所以至多爲1

如果您不希望其它的標誌向下傳遞,您必須改變變量MAKEFLAGS的值,其改變方式如下:

subsystem:

        cd subdir && $(MAKE) MAKEFLAGS=

該命令行中定義變量的實際上出現在變量MAKEOVERRIDES中,而且變量MAKEFLAGS包含了該變量的引用值。如果您要向下傳遞標誌,而不向下傳遞命令行中定義的變量,這時,您可以將變量MAKEOVERRIDES的值設爲空,格式如下:

MAKEOVERRIDES =

這並不十分有用。但是,一些系統對環境的大小有固定限制,而且該值較小,將這麼多的信息放到變量MAKEFLAGS的值中可能超過該限制。如果您看到‘Arg list too long'的錯誤信息,很可能就是由於該問題造成的。(按照嚴格的POSIX.2的規定,如果在makefile文件定義特殊目標‘.POSIX’,改變變量MAKEOVERRIDES的值並不影響變量MAKEFLAGS。也許您並不關心這些。)

爲了和早期版本兼容,具有相同功能的變量MFLAGS也是存在的。除了它不能包含命令行定義變量外,它和變量MAKEFLAGS有相同的值,而且除非它是空值,它的值總是以短線開始(MAKEFLAGS只有在和多字符選項一起使用時才以短線開始,如和‘--warn-undefined-variables’連用)。變量MFLAGS傳統的使用在明確的遞歸調用make的命令中,例如:

subsystem:

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

但現在,變量MAKEFLAGS使這種用法變得多餘。如果您要您的makefile文件和老版本的make程序兼容,請使用這種方式;這種方式在現代版本make中也能很好的工作。

如果您要使用每次運行make都要設置的特定選項,例如‘-k’選項(參閱選項概要),變量MAKEFLAGS十分有用。您可以簡單的在環境中將給變量MAKEFLAGS賦值,或在makefile文件中設置變量MAKEFLAGS,指定的附加標誌可以對整個makefile文件都起作用。(注意:您不能以這種方式使用變量MFLAGS,變量MFLAGS存在僅爲和早期版本兼容,採用其它方式設置該變量make將不予解釋。)

make解釋變量MAKEFLAGS值的時候(不管在環境中定義或在makefile文件中定義),如果該值不以短線開始,則make首先爲該值假設一個短線;接着將該值分割成字,字與字間用空格隔開,然後將這些字進行語法分析,好像它們是在命令行中給出的選項一樣。(‘-C', -f',-h',-o',-W'選項以及它們的長名字版本都將忽略,對於無效的選項不產生錯誤信息。)

如果您在環境中定義變量MAKEFLAGS,您不要使用嚴重影響make運行,破壞makefile文件的意圖以及make自身的選項。例如‘-t', -n', 和‘-q'選項,如果將它們中的一個放到變量MAKEFLAGS的值中,可能產生災難性的後果,或至少產生讓人討厭的結果。

5.6.4 --print-directory’選項

如果您使用幾層make遞歸調用,使用‘-w’或‘--print-directory’選項,通過顯示每個make開始處理以及處理完成的目錄使您得到比較容易理解的輸出。例如,如果使用‘make w’命令在目錄‘/u/gnu/make’中運行make,則make將下面格式輸出信息:

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

說明進入目錄中,還沒有進行任何任務。下面的信息:

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

說明任務已經完成。

通常情況下,您不必具體指明這個選項,因爲make已經爲您做了:當您使用‘-C’選項時,‘-w’選項已經自動打開,在子make中也是如此。如果您使用‘-s’選項,‘-w’選項不會自動打開,因爲‘-s’選項是不打印信息,同樣使用`--no-print-directory'選項‘-w’選項也不會自動打開。

5.7定義固定次序命令

在創建各種目標時,相同次序的命令十分有用時,您可以使用define指令定義固定次序的命令,並根據這些目標的規則引用固定次序。固定次序實際是一個變量,因此它的名字不能和其它的變量名衝突。

下面是定義固定次序命令的例子:

define run-yacc

yacc $(firstword $^)

mv y.tab.c $@

endef

run-yacc是定義的變量的名字;endef標誌定義結束;中間的行是命令。define指令在固定次序中不對變量引用和函數調用擴展;字符‘$’、圓括號、變量名等等都變成您定義的變量的值的一部分。定義多行變量一節對指令define有詳細解釋。

在該例子中,對於任何使用該固定次序的規則,第一個命令是對其第一個依賴運行Yacc命令,Yacc命令執行產生的輸出文件一律命名爲‘y.tab.c’;第二條命令,是將該輸出文件的內容移入規則的目標文件中。

在使用固定次序時,規則中命令使用的變量應被確定的值替代,您可以象替代其它變量一樣替代這些變量(詳細內容參閱變量引用基礎)。因爲由define指令定義的變量是遞歸擴展的變量,所以在使用時所有變量引用才擴展。例如:

foo.c : foo.y

        $(run-yacc)

當固定次序‘run-yacc’運行時,‘foo.y’將代替變量‘$^’,‘foo.c’將代替變量‘$@’。

這是一個現實的例子,但並不是必要的,因爲make有一條隱含規則可以根據涉及的文件名的類型確定所用的命令。參閱使用隱含規則

在命令執行時,固定次序中的每一行被處理爲和直接出現在規則中的命令行一樣,前面加上一個Tabmake也特別爲每一行請求一個獨立的子shell。您也可以在固定次序的每一行上使用影響命令行的前綴字符(`@', `-', `+'),參閱在規則中使用命令。例如使用下述的固定次序:

@echo "frobnicating target $@"

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

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

endef

make將不回顯第一行,但要回顯後面的兩個命令行。

另一方面,如果前綴字符在引用固定次序的命令行中使用,則該前綴字符將應用到固定次序的每以行中。例如這個規則:

frob.out: frob.in

        @$(frobnicate)

將不回顯固定次序的任何命令。具體內容參閱命令回顯

5.8 使用空命令

定義什麼也不幹的命令有時很有用,定義空命令可以簡單的給出一個僅僅含有空格而不含其它任何東西的命令即可。例如:

target: ;

爲字符串‘target’定義了一個空命令。您也可以使用以Tab字符開始的命令行定義一個空命令,但這由於看起來空白容易造成混亂。

也許您感到奇怪,爲什麼我們定義一個空命令?唯一的原因是爲了阻止目標更新時使用隱含規則提供的命令。(參閱使用隱含規則以及定義最新類型的缺省規則

也許您喜愛爲實際不存在的目標文件定義空命令,因爲這樣它的依賴可以重建。然而這樣做並不是一個好方法,因爲如果目標文件實際存在,則依賴有可能不重建,使用假想目標是較好的選擇,參閱假想目標

6 使用變量

變量是在makefile中定義的名字,其用來代替一個文本字符串,該文本字符串稱爲該變量的值。在具體要求下,這些值可以代替目標、依賴、命令以及makefile文件中其它部分。(在其它版本的make中,變量稱爲宏(macros)。)

makefile文件讀入時,除規則中的shell命令、使用‘=’定義的‘=’右邊的變量、以及使用define指令定義的變量體此時不擴展外,makefile文件其它各個部分的變量和函數都將擴展。

變量可以代替文件列表、傳遞給編譯器的選項、要執行的程序、查找源文件的目錄、輸出寫入的目錄,或您可以想象的任何文本。

變量名是不包括‘:',#',='、前導或結尾空格的任何字符串。然而變量名包含字母、數字以及下劃線以外的其它字符的情況應儘量避免,因爲它們可能在將來被賦予特別的含義,而且對於一些shell它們也不能通過環境傳遞給子make(參閱與子make通訊的變量)。變量名是大小寫敏感的,例如變量名‘foo', FOO', 和 ‘Foo'代表不同的變量。

使用大寫字母作爲變量名是以前的習慣,但我們推薦在makefile內部使用小寫字母作爲變量名,預留大寫字母作爲控制隱含規則參數或用戶重載命令選項參數的變量名。參閱變量重載

一少部分的變量使用一個標點符號或幾個字符作爲變量名,這些變量是自動變量,它們又特定的用途。參閱自動變量

6.1 變量引用基礎

寫一個美元符號後跟用圓括號或大括號括住變量名則可引用變量的值:‘$(foo)' 和 ‘${foo}'都是對變量‘foo’的有效引用。‘$’的這種特殊作用是您在命令或文件名中必須寫‘$$’纔有單個‘$’的效果的原因。

變量的引用可以用在上下文的任何地方:目標、依賴、命令、絕大多數指令以及新變量的值等等。這裏有一個常見的例子,在程序中,變量保存着所有OBJ文件的文件名:

objects = program.o foo.o utils.o

program : $(objects)

        cc -o program $(objects)

 

$(objects) : defs.h

變量的引用按照嚴格的文本替換進行,這樣該規則

foo = c

prog.o : prog.$(foo)

        $(foo)$(foo) -$(foo) prog.$(foo)

可以用於編譯C語言源程序‘prog.c’。因爲在變量分配時,變量值前面的空格被忽略,所以變量foo的值是‘C’。(不要在您的makefile文件這樣寫!)

美元符號後面跟一個字符但不是美元符號、圓括號、大括號,則該字符將被處理爲單字符的變量名。因此可以使用‘$x’引用變量x。然而,這除了在使用自動變量的情況下,在其它實際工作中應該完全避免。參閱自動變量

6.2 變量的兩個特色

GNU make中可以使用兩種方式爲變量賦值,我們將這兩種方式稱爲變量的兩個特色(two flavors)。兩個特色的區別在於它們的定義方式和擴展時的方式不同。

變量的第一個特色是遞歸調用擴展型變量。這種類型的變量定義方式:在命令行中使用‘=’定義(參閱設置變量)或使用define指令定義(參閱定義多行變量)。變量替換對於您所指定的值是逐字進行替換的;如果它包含對其它變量的引用,這些引用在該變量替換時(或在擴展爲其它字符串的過程中)才被擴展。這種擴展方式稱爲遞歸調用型擴展。例如:

foo = $(bar)

bar = $(ugh)

ugh = Huh?

 

all:;echo $(foo)

將回顯‘Huh?':‘$(foo)’擴展爲‘$(bar)’,進一步擴展爲‘$(ugh)’,最終擴展爲‘Huh?’。

這種特色的變量是其它版本make支持的變量類型,有缺點也有優點。大多數人認爲的該類型的變量的優點是:

CFLAGS = $(include_dirs) -O

include_dirs = -Ifoo -Ibar

即能夠完成希望它完成的任務:當‘CFLAGS’在命令中擴展時,它將最終擴展爲‘-Ifoo -Ibar’。其最大的缺點是不能在變量後追加內容,如在:

CFLAGS = $(CFLAGS) -O

在變量擴展過程中可能導致無窮循環(實際上make偵測到無窮循環就會產生錯誤信息)。

它的另一個缺點是在定義中引用的任何函數時(參閱文本轉換函數)變量一旦展開函數就會立即執行。這可導致make運行變慢,性能變壞;並且導致通配符與shell函數(因不能控制何時調用或調用多少次)產生不可預測的結果。

爲避免該問題和遞歸調用擴展型變量的不方便性,出現了另一個特色變量:簡單擴展型變量。

簡單擴展型變量在命令行中用‘:=’定義(參閱設置變量)。簡單擴展型變量的值是一次掃描永遠使用,對於引用的其它變量和函數在定義的時候就已經展開。簡單擴展型變量的值實際就是您寫的文本擴展的結果。因此它不包含任何對其它變量的引用;在該變量定義時就包含了它們的值。所以:

x := foo

y := $(x) bar

x := later

等同於:

y := foo bar

x := later

引用一個簡單擴展型變量時,它的值也是逐字替換的。這裏有一個稍複雜的例子,說明了‘:=’和shell函數連接用法(參閱函數shell)。該例子也表明了變量MAKELEVEL的用法,該變量在層與層之間傳遞時值發生變化。(參閱與子make通訊的變量,可獲得變量MAKELEVEL關於的信息。)

ifeq (0,${MAKELEVEL})

cur-dir   := $(shell pwd)

whoami    := $(shell whoami)

host-type := $(shell arch)

MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}

endif

按照這種方法使用‘:=’的優點是看起來象下述的典型的‘下降到目錄’的命令:

${subdirs}:

      ${MAKE} cur-dir=${cur-dir}/$@ -C $@ all

簡單擴展型變量因爲在絕大多數程序設計語言中可以象變量一樣工作,因此它能夠使複雜的makefile程序更具有預測性。它們允許您使用它自己的值重新定義(或它的值可以被一個擴展函數以某些方式處理),它們還允許您使用更有效的擴展函數(參閱文本轉換函數)。

您可以使用簡單擴展型變量將控制的前導空格引入到變量的值中。前導空格字符一般在變量引用和函數調用時被丟棄。簡單擴展型變量的這個特點意味着您可以在一個變量的值中包含前導空格,並在變量引用時保護它們。象這樣:

nullstring :=

space := $(nullstring) # end of the line

這裏變量space的值就是一個空格,註釋‘# end of the line’包括在這裏爲了讓人更易理解。因爲尾部的空格不能從變量值中分離出去,僅在結尾留一個空格也有同樣的效果(但是此時相當難讀),如果您在變量值後留一個空格,象這樣在行的結尾寫上註釋清楚表明您的打算是很不錯的主意。相反,如果您在變量值後不要空格,您千萬記住不要在行的後面留下幾個空格再隨意放入註釋。例如:

dir := /foo/bar    # directory to put the frobs in

這裏變量dir的值是‘/foo/bar ’(四個尾部空格),這不是預期的結果。(假設‘/foo/bar’是預期的值)。

另一個給變量賦值的操作符是‘?=’,它稱爲條件變量賦值操作符,因爲它僅僅在變量還沒有定義的情況下有效。這聲明:

FOO ?= bar

和下面的語句嚴格等同(參閱函數origin

ifeq ($(origin FOO), undefined)

  FOO = bar

endif

注意,一個變量即使是空值,它仍然已被定義,所以使用‘?=’定義無效。

6.3變量引用高級技術

本節內容介紹變量引用的高級技術。

6.3.1替換引用

替換引用是用您指定的變量替換一個變量的值。它的形式‘$(var:a=b)’(或‘${var:a=b}’),它的含義是把變量var的值中的每一個字結尾的ab替換。

我們說‘在一個字的結尾’,我們的意思是a一定在一個字的結尾出現,且a的後面要麼是空格要麼是該變量值的結束,這時的a被替換,值中其它地方的a不被替換。例如:

foo := a.o b.o c.o

bar := $(foo:.o=.c)

將變量‘bar’的值設爲‘a.c b.c c.c’。參閱變量設置

替換引用實際是使用擴展函數patsubst的簡寫形式(參閱字符串替換和分析函數)。我們提供替換引用也是使擴展函數patsubstmake的其它實現手段兼容的措施。

另一種替換引用是使用強大的擴展函數patsubst。它的形式和上述的‘$(var:a=b)’一樣,不同在於它必須包含單個‘%’字符,其實這種形式等同於‘$(patsubst a,b,$(var))’。有關於函數patsubst擴展的描述參閱字符串替換和分析函數。例如:

foo := a.o b.o c.o

bar := $(foo:%.o=%.c)

社值變量‘bar'的值爲‘a.c b.c c.c'

6.3.2嵌套變量引用(計算的變量名)

嵌套變量引用(計算的變量名)是一個複雜的概念,僅僅在十分複雜的makefile程序中使用。絕大多數情況您不必考慮它們,僅僅知道創建名字中含有美元標誌的變量可能有奇特的結果就足夠了。然而,如果您是要把一切搞明白的人或您實在對它們如何工作有興趣,請認真閱讀以下內容。

變量可以在它的名字中引用其它變量,這稱爲嵌套變量引用(計算的變量名)。例如:

x = y

y = z

a := $($(x))

定義阿a爲‘z’:‘$(x)’在‘$($(x))’中擴展爲‘y’,因此‘$($(x))’擴展爲‘$(y)’,最終擴展爲‘z’。這裏對引用的變量名的陳述不太明確;它根據‘$(x)’的擴展進行計算,所以引用‘$(x)’是嵌套在外層變量引用中的。

前一個例子表明了兩層嵌套,但是任何層次數目的嵌套都是允許的,例如,這裏有一個三層嵌套的例子:

x = y

y = z

z = u

a := $($($(x)))

這裏最裏面的‘$(x)’ 擴展爲‘y’,因此‘$($(x))’擴展爲‘$(y)’,‘$(y)’ 擴展爲‘z’,最終擴展爲‘u’。

在一個變量名中引用遞歸調用擴展型變量,則按通常的風格再擴展。例如:

x = $(y)

y = z

z = Hello

a := $($(x))

定義的a是‘Hello’:‘$($(x))’擴展爲‘$($(y))’,‘$($(y))’變爲‘$(z), $(z)’最終擴展爲‘Hello’。

嵌套變量引用和其它引用一樣也可以包含修改引用和函數調用(參閱文本轉換函數)。例如,使用函數subst(參閱字符串替換和分析函數):

x = variable1

variable2 := Hello

y = $(subst 1,2,$(x))

z = y

a := $($($(z)))

定義的a是‘Hello’。任何人也不會寫象這樣令人費解的嵌套引用程序,但它確實可以工作:‘$($($(z)))’ 擴展爲‘$($(y))’,‘$($(y))’變爲‘$(subst 1,2,$(x))’。它從變量‘x’得到值‘variable1,變換替換爲‘variable2’,所以整個字符串變爲‘$( variable2)’,一個簡單的變量引用,它的值爲‘Hello’。

嵌套變量引用不都是簡單的變量引用,它可以包含好幾個變量引用,同樣也可包含一些固定文本。例如,

a_dirs := dira dirb

1_dirs := dir1 dir2

 

a_files := filea fileb

1_files := file1 file2

 

ifeq "$(use_a)" "yes"

a1 := a

else

a1 := 1

endif

 

ifeq "$(use_dirs)" "yes"

df := dirs

else

df := files

endif

 

dirs := $($(a1)_$(df))

根據設置的use_ause_dirs的輸入可以將dirs這個相同的值分別賦給a_dirs, 1_dirs, a_files 1_files

嵌套變量引用也可以用於替換引用:

a_objects := a.o b.o c.o

1_objects := 1.o 2.o 3.o

 

sources := $($(a1)_objects:.o=.c)

根據a1的值,定義的sources可以是`a.c b.c c.c' `1.c 2.c 3.c'

使用嵌套變量引用唯一的限制是它們不能只部分指定要調用的函數名,這是因爲用於識別函數名的測試在嵌套變量引用擴展之前完成。例如:

ifdef do_sort

func := sort

else

func := strip

endif

 

bar := a d b g q c

 

foo := $($(func) $(bar))

則給變量‘foo’的值賦爲‘sort a d b g q c' 或 ‘strip a d b g q c',而不是將‘a d b g q c’作爲函數sortstrip的參數。如果在將來去掉這種限制是一個不錯的主意。

您也可以變量賦值的左邊使用嵌套變量引用,或在define指令中。如:

dir = foo

$(dir)_sources := $(wildcard $(dir)/*.c)

define $(dir)_print

lpr $($(dir)_sources)

endef

該例子定義了變量‘dir',foo_sources', 和‘foo_print'

注意:雖然嵌套變量引用和遞歸調用擴展型變量都是用在複雜的makefile文件中,但二者不同(參閱變量的兩個特色)。

6.4變量取值

變量有以下幾種方式取得它們的值:

l                          l         您可以在運行make時爲變量指定一個重載值。參閱變量重載

l                          l         您可以在makefile文件中指定值,即變量賦值(參閱設置變量)或使用逐字定義變量(參閱定義多行變量)。

l                          l         把環境變量變爲make的變量。參閱環境變量

l                          l         自動變量可根據規則提供值,它們都有簡單的習慣用法,參閱自動變量

l                          l         變量可以用常量初始化。參閱隱含規則使用的變量

6.5設置變量

makefile文件中設置變量,編寫以變量名開始後跟‘=’或‘:=’的一行即可。任何跟在‘=’或‘:=’後面的內容就變爲變量的值。例如:

objects = main.o foo.o bar.o utils.o

定義一個名爲objects的變量,變量名前後的空格和緊跟‘=’的空格將被忽略。

使用‘=’定義的變量是遞歸調用擴展型變量;以‘:=’定義的變量是簡單擴展型變量。簡單擴展型變量定義可以包含變量引用,而且變量引用在定義的同時就被立即擴展。參閱變量的兩種特色

變量名中也可以包含變量引用和函數調用,它們在該行讀入時擴展,這樣可以計算出能夠實際使用的變量名。

變量值的長度沒有限制,但受限於計算機中的實際交換空間。當定義一個長變量時,在合適的地方插入反斜槓,把變量值分爲多個文本行是不錯的選擇。這不影響make的功能,但可使makefile文件更加易讀。

絕大多數變量如果您不爲它設置值,空字符串將自動作爲它的初值。雖然一些變量有內建的非空的初始化值,但您可隨時按照通常的方式爲它們賦值(參閱隱含規則使用的變量。)另外一些變量可根據規則自動設定新值,它們被稱爲自動變量。參閱自動變量

如果您喜歡僅對沒有定義過的變量賦給值,您可以使用速記符‘?=’代替‘=’。下面兩種設置變量的方式完全等同(參閱函數origin):

FOO ?= bar

ifeq ($(origin FOO), undefined)

FOO = bar

endif

6.6 爲變量值追加文本

爲已經定以過的變量的值追加更多的文本一般比較有用。您可以在獨立行中使用‘+=’來實現上述設想。如:

objects += another.o

這爲變量objects的值添加了文本‘another.o’(其前面有一個前導空格)。這樣:

objects = main.o foo.o bar.o utils.o

objects += another.o

變量objects 設置爲‘main.o foo.o bar.o utils.o another.o'

使用 `+=' 相同於:

objects = main.o foo.o bar.o utils.o

objects := $(objects) another.o

對於使用複雜的變量值,不同方法的差別非常重要。如變量在以前沒有定義過,則‘+=’的作用和‘=’相同:它定義一個遞歸調用型變量。然而如果在以前有定義,‘+=’的作用依賴於您原始定義的變量的特色,詳細內容參閱變量的兩種特色

當您使用‘+=’爲變量值附加文本時,make的作用就好象您在初始定義變量時就包含了您要追加的文本。如果開始您使用‘:=’定義一個簡單擴展型變量,再用‘+=’對該簡單擴展型變量值追加文本,則該變量按新的文本值擴展,好像在原始定義時就將追加文本定義上一樣,詳細內容參閱設置變量。實際上,

variable := value

variable += more

等同於:

variable := value

variable := $(variable) more

另一方面,當您把‘+=’和首次使用無符號‘=’定義的遞歸調用型變量一起使用時,make的運行方式會有所差異。在您引用遞歸調用型變量時,make並不立即在變量引用和函數調用時擴展您設定的值;而是將它逐字儲存起來,將變量引用和函數調用也儲存起來,以備以後擴展。當您對於一個遞歸調用型變量使用‘+=’時,相當於對一個不擴展的文本追加新文本。

variable = value

variable += more

粗略等同於:

temp = value

variable = $(temp) more

當然,您從沒有定義過叫做temp的變量,如您在原始定義變量時,變量值中就包含變量引用,此時可以更爲深刻地體現使用不同方式定義的的重要性。拿下面常見的例子,

CFLAGS = $(includes) -O

...

CFLAGS += -pg # enable profiling

第一行定義了變量CFLAGS,而且變量CFLAGS引用了其它變量,includes。(變量CFLAGS用於C編譯器的規則,參閱隱含規則目錄。)由於定義時使用‘=’,所以變量CFLAGS是遞歸調用型變量,意味着‘$(includes) -O’在make處理變量CFLAGS定義時是不擴展的;也就是變量includes在生效之前不必定義,它僅需要在任何引用變量CFLAGS之前定義即可。如果我們試圖不使用‘+=’爲變量CFLAGS追加文本,我們可能按下述方式:

CFLAGS := $(CFLAGS) -pg # enable profiling

這似乎很好,但結果絕不是我們所希望的。使用‘:=’重新定義變量CFLAGS爲簡單擴展型變量,意味着make在設置變量CFLAGS之前擴展了‘$(CFLAGS) -pg’。如果變量includes此時沒有定義,我們將得到‘-0 -pg’,並且以後對變量includes的定義也不會有效。相反,使用‘+=’ 設置變量CFLAGS我們得到沒有擴展的‘$(CFLAGS) 0 -pg’,這樣保留了對變量includes的引用,在後面一個地方如果變量includes得到定義,‘$(CFLAGS)’仍然可以使用它的值。

6.7 override指令

如果一個變量設置時使用了命令參數(參閱變量重載),那麼在makefile文件中通常的對該變量賦值不會生效。此時對該變量進行設置,您需要使用override指令,其格式如下:

override variable = value

override variable := value

爲該變量追加更多的文本,使用:

override variable += more text

參閱爲變量值追加文本

override指令不是打算擴大makefile和命令參數衝突,而是希望用它您可以改變和追加哪些設置時使用了命令參數的變量的值。

例如,假設您在運行C編譯器時總是使用‘-g’開關,但您允許用戶像往常一樣使用命令參數指定其它開關,您就可以使用override指令:

override CFLAGS += -g

您也可以在define指令中使用override指令,下面的例子也許就是您想要得:

override define foo

bar

endef

關於define指令的信息參閱下節。

6.8定義多行變量

設置變量值的另一種方法時使用define指令。這個指令有一個特殊的用法,既可以定義包含多行字符的變量。這使得定義命令的固定次序十分方便(參閱定義固定次序命令)。

define指令同一行的後面一般是變量名,當然,也可以什麼也沒有。變量的值由下面的幾行給出,值的結束由僅僅包含endef的一行標示出。除了上述在語法上的不同之外,define指令象‘=’一樣工作:它創建了一個遞歸調用型變量(參閱變量的兩個特色)。變量的名字可以包括函數調用和變量引用,它們在指令讀入時擴展,以便能夠計算出實際的變量名。

define two-lines

echo foo

echo $(bar)

endef

變量的值在通常的賦值語句中只能在一行中完成,但在define指令中在define指令行以後endef行之前中間所有的行都是變量值的一部分(最後一行除外,因爲標示endef那一行不能認爲是變量值的一部分)。前面的例子功能上等同於:

two-lines = echo foo; echo $(bar)

因爲兩命令之間用分號隔開,其行爲很接近於兩個分離的shell命令。然而,注意使用兩個分離的行,意味着make請求shell兩次,每一行都在獨立的子shell中運行。參閱執行命令

如果您希望使用define指令的變量定義比使用命令行定義的變量優先,您可以把define指令和override指令一塊使用:

override define two-lines

foo

$(bar)

endef

參閱override指令

6.9 環境變量

make使用的變量可以來自make的運行環境。任何make能夠看見的環境變量,在make開始運行時都轉變爲同名同值的make變量。但是,在makefile文件中對變量的具體賦值,或使用帶有參數的命令,都可以對環境變量進行重載(如果明確使用‘-e’標誌,環境變量的值可以對makefile文件中的賦值進行重載,參閱選項概要,但是這在實際中不推薦使用。)

這樣,通過在環境中設置變量CFLAGS,您可以實現在絕大多數makefile文件中的所有C源程序的編譯使用您選擇的開關。因爲您知道沒有makefile將該變量用於其它任務,所以這種使用標準簡潔含義的變量是安全的(但這也是不可靠的,一些makefile文件可能設置變量CFLAGS,從而使環境中變量CFLAGS的值失效)。當使用遞歸調用的make時,在外層make環境中定義的變量,可以傳遞給內層的make(參閱遞歸調用make)。缺省方式下,只有環境變量或在命令行中定義的變量才能傳遞給內層make。您可以使用export指令傳遞其它變量,參閱與子make通訊的變量

環境變量的其它使用方式都不推薦使用。將makefile的運行完全依靠環境變量的設置、超出makefile文件的控制範圍,這種做法是不明智的,因爲不同的用戶運行同一個makefile文件有可能得出不同的結果。這和大部分makefile文件的意圖相違背。

變量SHELL在環境中存在,用來指定用戶對交互的shell的選擇,因此使用變量SHELL也存字類似的問題。這種根據選定值影響make運行的方式是很不受歡迎的。所以,make將忽略環境中變量SHELL的值(在MS-DOS MS-Windows中運行例外,但此時變量SHELL通常不設置值,參閱執行命令)。

6.10 特定目標變量的值

make中變量的值一般是全局性的;既,無論它們在任何地方使用,它們的值是一樣的(當然,您重新設置除外);自動變量是一個例外(參閱自動變量)。

另一個例外是特定目標變量的值,這個特點允許您可以根據make建造目標的變化改變變量的定義。象自動變量一樣,這些值只能在一個目標的命令腳本的上下文起作用。

可以象這樣設置特定目標變量的值:

target ... : variable-assignment

或這樣:

target ... : override variable-assignment

target ...’中可含有多個目標,如此,則設置的特定目標變量的值可在目標列表中的任一個目標中使用。‘variable-assignment’使用任何賦值方式都是有效的:遞歸調用型(‘=’)、靜態(‘:=’)、追加(‘+=’)或條件(‘?=’)。所有出現在‘variable-assignment’中的變量能夠在特定目標target ...的上下文中使用:也就是任何以前爲特定目標target ...定義的特定目標變量的值在這些特定目標中都是有效的。注意這種變量值和全局變量值相比是局部的值:這兩種類型的變量不必有相同的類型(遞歸調用vs.靜態)。

特定目標變量的值和其它makefile變量具有相同的優先權。一般在命令行中定義的變量(和強制使用‘-e’情況下的環境變量)的值佔據優先的地位,而使用override指令定義的特定目標變量的值則佔據優先地位。

特定目標變量的值有另外一個特點:當您定義一個特定目標變量時,該變量的值對特定目標target ...所有依賴有效,除非這些依賴用它們自己的特定目標變量的值將該變量重載。例如:

prog : CFLAGS = -g

prog : prog.o foo.o bar.o

將在目標prog的命令腳本中設置變量CFLAGS的值爲‘-g’,同時在創建`prog.o', `foo.o', `bar.o'的命令腳本中變量CFLAGS的值也是-g’,以及prog.o',foo.o', bar.o'的依賴的創建命令腳本中變量CFLAGS的值也是-g’。

6.11 特定格式變量的值

除了特定目標變量的值(參閱上小節)外,GNU make也支持特定格式變量的值。使用特定格式變量的值,可以爲匹配指定格式的目標定義變量。在爲目標定義特定目標變量後將搜尋按特定格式定義的變量,在爲該目標的父目標定義的特定目標變量前也要搜尋按特定格式定義的變量。

設置特定格式變量格式如下:

pattern ... : variable-assignment

或這樣:

pattern ... : override variable-assignment

這裏的‘pattern’是%-格式。象特定目標變量的值一樣,‘pattern ...’中可含有多個格式,如此,則設置的特定格式變量的值可在匹配列表中的任一個格式中的目標中使用。‘variable-assignment’使用任何賦值方式都是有效的,在命令行中定義的變量的值佔據優先的地位,而使用override指令定義的特定格式變量的值則佔據優先地位。例如:

%.o : CFLAGS = -O

搜尋所有匹配格式%.o的目標,並將它的變量CFLAGS的值設置爲‘-0’。

7 makefile文件的條件語句

一個條件語句可以導致根據變量的值執行或忽略makefile文件中一部分腳本。條件語句可以將一個變量與其它變量的值相比較,或將一個變量與一字符串常量相比較。條件語句用於控制make實際看見的makefile文件部分,不能用於在執行時控制shell命令。

7.1條件語句的例子

下述的條件語句的例子告訴make如果變量CC的值是‘gcc’時使用一個數據庫,如不是則使用其它數據庫。它通過控制選擇兩命令行之一作爲該規則的命令來工作。‘CC=gcc’作爲make改變的參數的結果不僅用於決定使用哪一個編譯器,而且決定連接哪一個數據庫。

libs_for_gcc = -lgnu

normal_libs =

 

foo: $(objects)

ifeq ($(CC),gcc)

        $(CC) -o foo $(objects) $(libs_for_gcc)

else

        $(CC) -o foo $(objects) $(normal_libs)

endif

該條件語句使用三個指令:ifeqelseendif

Ifeq指令是條件語句的開始,並指明條件。它包含兩個參數,它們被逗號分開,並被擴在圓括號內。運行時首先對兩個參數變量替換,然後進行比較。在makefile中跟在ifeq後面的行是符合條件時執行的命令;否則,它們將被忽略。

如果前面的條件失敗,else指令將導致跟在其後面的命令執行。在上述例子中,意味着當第一個選項不執行時,和第二個選項連在一起的命令將執行。在條件語句中,else指令是可選擇使用的。

Endif指令結束條件語句。任何條件語句必須以endif指令結束,後跟makefile文件中的正常內容。

上例表明條件語句工作在原文水平:條件語句的行根據條件要麼被處理成makefile文件的一部分或要麼被忽略。這是makefile文件重大的語法單位(例如規則)可以跨越條件語句的開始或結束的原因。

當變量CC的值是gcc,上例的效果爲:

foo: $(objects)

        $(CC) -o foo $(objects) $(libs_for_gcc)

當變量CC的值不是gcc而是其它值的時候,上例的效果爲:

foo: $(objects)

        $(CC) -o foo $(objects) $(normal_libs)

相同的結果也能使用另一種方法獲得:先將變量的賦值條件化,然後再使用變量:

libs_for_gcc = -lgnu

normal_libs =

 

ifeq ($(CC),gcc)

  libs=$(libs_for_gcc)

else

  libs=$(normal_libs)

endif

 

foo: $(objects)

        $(CC) -o foo $(objects) $(libs)

7.2條件語句的語法

對於沒有else指令的條件語句的語法爲:

conditional-directive

text-if-true

endif

text-if-true’可以是任何文本行,在條件爲‘真’時它被認爲是makefile文件的一部分;如果條件爲‘假’,將被忽略。完整的條件語句的語法爲:

conditional-directive

text-if-true

else

text-if-false

endif

如果條件爲‘真’,使用‘text-if-true’;如果條件爲‘假’,使用‘text-if-false’。‘text-if-false’可以是任意多行的文本。

關於‘conditional-directive’的語法對於簡單條件語句和複雜條件語句完全一樣。有四種不同的指令用於測試不同的條件。下面是指令表:

ifeq (arg1, arg2)

ifeq 'arg1' 'arg2'

ifeq "arg1" "arg2"

ifeq "arg1" 'arg2'

ifeq 'arg1' "arg2"

         擴展參數arg1arg2中的所有變量引用,並且比較它們。如果它們完全一致,則使用‘text-if-true’,否則使用‘text-if-false’(如果存在的話)。您經常要測試一個變量是否有非空值,當經過複雜的變量和函數擴展得到一個值,對於您認爲是空值,實際上有可能由於包含空格而被認爲不是空值,由此可能造成混亂。對於此,您可以使用strip函數從而避免空格作爲非空值的干擾。例如:

ifeq ($(strip $(foo)),)

text-if-empty

endif

即使$(foo)中含有空格,也使用‘text-if-empty’。

ifneq (arg1, arg2)

ifneq 'arg1' 'arg2'

ifneq "arg1" "arg2"

ifneq "arg1" 'arg2'

ifneq 'arg1' "arg2"

擴展參數arg1arg2中的所有變量引用,並且比較它們。如果它們不同,則使用‘text-if-true’,否則使用‘text-if-false’(如果存在的話)。

ifdef variable-name

如果變量‘variable-name’是非空值,‘text-if-true’有效,否則,‘text-if-false’有效(如果存在的話)。變量從沒有被定義過則變量是空值。注意ifdef僅僅測試變量是否有值。它不能擴展到看變量是否有非空值。因而,使用ifdef測試所有定義過的變量都返回‘真’,但那些象‘foo=’情況除外。測試空值請使用ifeq($(foo),)。例如:

bar =

foo = $(bar)

ifdef foo

frobozz = yes

else

frobozz = no

endif

設置‘frobozz'的值爲‘yes', 而::

foo =

ifdef foo

frobozz = yes

else

frobozz = no

endif

設置‘frobozz' 爲‘no'

ifndef variable-name

如果變量‘variable-name’是空值,‘text-if-true’有效,否則,‘text-if-false’有效(如果存在的話)。

在指令行前面允許有多餘的空格,它們在處理時被忽略,但是不允許有Tab(如果一行以Tab開始,那麼該行將被認爲是規則的命令行)。除此之外,空格和Tab可以插入到行的任何地方,當然指令名和參數中間除外。以‘#’開始的註釋可以在行的結尾。

在條件語句中另兩個有影響的指令是elseendif。這兩個指令以一個單詞的形式出現,沒有任何參數。在指令行前面允許有多餘的空格,空格和Tab可以插入到行的中間,以‘#’開始的註釋可以在行的結尾。

條件語句影響make使用的makefile文件。如果條件爲‘真’,make讀入‘text-if-true’包含的行;如果條件爲‘假’,make讀入‘text-if-false’包含的行(如果存在的話);makefile文件的語法單位,例如規則,可以跨越條件語句的開始或結束。

當讀入makefile文件時,Make計算條件的值。因而您不能在測試條件時使用自動變量,因爲他們是命令執行時才被定義(參閱自動變量)。

爲了避免不可忍受的混亂,在一個makefile文件中開始一個條件語句,而在另外一個makefile文件中結束這種情況是不允許的。然而如果您試圖引入包含的makefile文件不中斷條件語句,您可以在條件語句中編寫include指令。

7.3測試標誌的條件語句

您可以使用變量MAKEFLAGSfindstring函數編寫一個條件語句,用它來測試例如‘-t’等的make命令標誌(參閱字符串替換和分析的函數)。這適用於僅使用touch標誌不能完全更改文件的時間戳的場合。

findstring函數檢查一個字符串是否爲另一個字符串的子字符串。如果您要測試‘-t’標誌,使用‘-t’作爲第一個字符串,將變量MAKEFLAGS的值作爲另一個字符串。例如下面的例子是安排使用‘ranlib t’完成一個檔案文件的更新:

archive.a: ...

ifneq (,$(findstring t,$(MAKEFLAGS)))

        +touch archive.a

        +ranlib -t archive.a

else

        ranlib archive.a

endif

前綴‘+’表示這些命令行是遞歸調用行,即使是用‘-t’標誌它們一樣要執行。參閱遞歸調用make

8 文本轉換函數

函數允許您在makefile文件中處理文本、計算文件、操作使用命令等。在函數調用時您必須指定函數名以及函數操作使用的參數。函數處理的結果將返回到makefile文件中的調用點,其方式和變量替換一樣。

8.1函數調用語法

函數調用和變量引用類似,它的格式如下:

$(function arguments)

或這樣:

${function arguments}

這裏‘function’是函數名,是make內建函數列表中的一個。當然您也可以使用創建函數call創建的您自己的函數。‘arguments’是該函數的參數。參數和函數名之間是用空格或Tab隔開,如果有多個參數,它們之間用逗號隔開。這些空格和逗號不是參數值的一部分。包圍函數調用的定界符,無論圓括號或大括號,可以在參數中成對出現,在一個函數調用中只能有一種定界符。如果在參數中包含變量引用或其它的函數調用,最好使用同一種定界符,如寫爲‘$(subst a,b,$(x))', 而不是 `$(subst a,b,${x})'。這是因爲這種方式不但比較清楚,而且也有在一個函數調用中只能有一種定界符的規定。

爲每一個參數寫的文本經過變量替換或函數調用處理,最終得到參數的值,這些值是函數執行必須依靠的文本。另外,變量替換是按照變量在參數中出現的次序進行處理的。

逗號和不成對出現的圓括號、大括號不能作爲文本出現在參數中,前導空格也不能出現在第一個參數中。這些字符不能被變量替換處理爲參數的值。如果需要使用這些字符,首先定義變量commaspace,它們的值是單獨的逗號和空格字符,然後在需要的地方因用它們,如下例:

comma:= ,

empty:=

space:= $(empty) $(empty)

foo:= a b c

bar:= $(subst $(space),$(comma),$(foo))

# bar is now `a,b,c'.

這裏函數subst的功能是將變量foo中的空格用逗號替換,然後返回結果。

8.2字符串替換和分析函數

這裏有一些用於操作字符串的函數:

$(subst from,to,text)

在文本‘text’中使用‘to’替換每一處‘from’。例如:

$(subst ee,EE,feet on the street)

結果爲‘fEEt on the street’。

$(patsubst pattern,replacement,text)

尋找‘text’中符合格式‘pattern’的字,用‘replacement’替換它們。這裏‘pattern’中包含通配符‘%’,它和一個字中任意個數的字符相匹配。如果‘replacement’中也含有通配符‘%’,則這個‘%’被和‘pattern’中通配符‘%’匹配的文本代替。在函數patsubst中的‘%’可以用反斜槓(/)引用。引用字符‘%’的反斜槓可以被更多反斜槓引用。引用字符‘%’和其它反斜槓的反斜槓在比較文件名或有一個stem(徑)代替它之前從格式中移出。使用反斜槓引用字符‘%’不會帶來其它麻煩。例如,格式the/%weird//%pattern//'the%weird/' 加上通配符‘%'然後和字符串‘pattern//'連接。最後的兩個反斜槓由於不能影響任何統配符‘%’所以保持不變。在字之間的空格間被壓縮爲單個空格,前導以及結尾空格被丟棄。例如:

$(patsubst %.c,%.o,x.c.c bar.c)

的結果爲:‘x.c.o bar.o'。替換引用是實現函數patsubst功能一個簡單方法:

$(var:pattern=replacement)

等同於 :

$(patsubst pattern,replacement,$(var))

另一個通常使用的函數patsubst的簡單方法是:替換文件名的後綴。

$(var:suffix=replacement)

等同於:

$(patsubst %suffix,%replacement,$(var))

例如您可能有一個OBJ文件的列表:

objects = foo.o bar.o baz.o

要得到這些文件的源文件,您可以簡單的寫爲:

$(objects:.o=.c)

代替規範的格式:

$(patsubst %.o,%.c,$(objects))

$(strip string)

去掉前導和結尾空格,並將中間的多個空格壓縮爲單個空格。這樣,‘$(strip a b c )'結果爲‘a b c’。函數strip和條件語句連用非常有用。當使用ifeqifneq把一些值和空字符串‘’比較時,您通常要將一些僅由空格組成的字符串認爲是空字符串(參閱makefile中的條件語句)。如此下面的例子在實現預期結果時可能失敗:

.PHONY: all

ifneq   "$(needs_made)" ""

all: $(needs_made)

else

all:;@echo 'Nothing to make!'

endif

在條件指令ifneq中用函數調用‘$(strip $(needs_made))'代替變量引用‘$(needs_made)'將不再出現問題。

$(findstring find,in)

在字符串‘in’中搜尋‘find’,如果找到,則返回值是‘find’,否則返回值爲空。您可以在一個條件中使用該函數測試給定的字符串中是否含有特定的子字符串。這樣,下面兩個例子:

$(findstring a,a b c)

$(findstring a,b c)

將分別產生值‘a’和‘’。對於函數findstring的特定用法參閱測試標誌的條件語句

$(filter pattern...,text)

返回在‘text’中由空格隔開且匹配格式‘pattern...’的字,對於不符合格式‘pattern...’的字移出。格式用‘%’寫出,和前面論述過的函數patsubst的格式相同。函數filter可以用來變量分離類型不同的字符串。例如:

sources := foo.c bar.c baz.s ugh.h

foo: $(sources)

        cc $(filter %.c %.s,$(sources)) -o foo

表明foo' 依靠foo.c',bar.c',baz.s' ugh.h'但僅有foo.c',bar.c' baz.s' 指明用命令編譯。

$(filter-out pattern...,text)

返回在‘text’中由空格隔開且不匹配格式‘pattern...’的字,對於符合格式‘pattern...’的字移出。只是函數filter的反函數。例如:

objects=main1.o foo.o main2.o bar.o

mains=main1.o main2.o

下面產生不包含在變量‘mains’中的OBJ文件的文件列表:

$(filter-out $(mains),$(objects))

$(sort list)

將‘list’中的字按字母順序排序,並取掉重複的字。輸出是由單個空格隔開的字的列表。

$(sort foo bar lose)

返回值是‘bar foo lose’。順便提及,由於函數sort可以取掉重複的字,您就是不關心排序也可以使用它的這個特點。

這裏有一個實際使用函數substpatsubst的例子。假設一個makefile文件使用變量VPATH指定make搜尋依賴文件的一系列路徑(參閱VPATH:依賴搜尋路徑)。這個例子表明怎樣告訴C編譯器在相同路徑列表中搜尋頭文件。

變量VPATH的值是一列用冒號隔開的路徑名,如‘src:../headers'。首先,函數subst將冒號變爲空格:

$(subst :, ,$(VPATH))

這產生值‘src ../headers'。然後,函數patsubst爲每一個路徑名加入‘-|’標誌,這樣這些路徑可以加到變量CFLAGS中,就可以自動傳遞給C編譯器:

override CFLAGS += $(patsubst %,-I%,$(subst :, ,$(VPATH)))

結果是在以前給定的變量CFLAGS的值後追加文本‘-Isrc -I../headers’。Override指令的作用是即使以前使用命令參數指定變量CFLAGS的值,新值也能起作用。參閱override指令

8.3文件名函數

其中幾個內建的擴展函數和拆分文件名以及列舉文件名相關聯。下面列舉的函數都能執行對文件名的特定轉換。函數的參數是一系列的文件名,文件名之間用空格隔開(前導和結尾空格被忽略)。列表中的每一個文件名都採用相同的方式轉換,而且結果用單個空格串聯在一起。

$(dir names...)

抽取‘names’中每一個文件名的路徑部分,文件名的路徑部分包括從文件名的開始到最後一個斜槓(含斜槓)之前的一切字符。如果文件名中沒有斜槓,路徑部分是‘./’。如:

$(dir src/foo.c hacks)

產生的結果爲 ‘src/ ./’。

$(notdir names...)

抽取‘names’中每一個文件名中除路徑部分外一切字符(真正的文件名)。如果文件名中沒有斜槓,則該文件名保持不變,否則,將路徑部分移走。一個文件名如果僅包含路徑部分(以斜槓結束的文件名)將變爲空字符串。這是非常不幸的,因爲這意味着在結果中如果有這種文件名存在,兩文件名之間的空格將不是由相同多的空格隔開。但現在我們並不能看到其它任何有效的代替品。例如:

$(notdir src/foo.c hacks)

產生的結果爲‘foo.c hacks’。

$(suffix names...)

抽取‘names’中每一個文件名的後綴。如果文件名中(或含有斜槓,且在最後一個斜槓後)含有句點,則後綴是最後那個句點以後的所有字符,否則,後綴是空字符串。如果結果爲空意味着‘names’沒有帶後綴文件名,如果文件中含有多個文件名,則結果列出的後綴數很可能比原文件名數目少。例如:

$(suffix src/foo.c src-1.0/bar.c hacks)

產生的結果是‘.c .c’。

$(basename names...)

抽取‘names’中每一個文件名中除後綴外一切字符。如果文件名中(或含有斜槓,且在最後一個斜槓後)含有句點,則基本名字是從開始到最後一個句點(不包含)間的所有字符。如果沒有句點,基本名字是整個文件名。例如:

$(basename src/foo.c src-1.0/bar hacks)

產生的結果爲‘src/foo src-1.0/bar hacks’。

$(addsuffix suffix,names...)

參數‘names’作爲一系列的文件名,文件名之間用空格隔開;suffix作爲一個單位。將Suffix(後綴)的值附加在每一個獨立文件名的後面,完成後將文件名串聯起來,它們之間用單個空格隔開。例如:

$(addsuffix .c,foo bar)

結果爲‘foo.c bar.c’。

$(addprefix prefix,names...)

參數‘names’作爲一系列的文件名,文件名之間用空格隔開;prefix作爲一個單位。將preffix(前綴)的值附加在每一個獨立文件名的前面,完成後將文件名串聯起來,它們之間用單個空格隔開。例如:

$(addprefix src/,foo bar)

結果爲‘src/foo src/bar’。

$(join list1,list2)

將兩個參數串聯起來:兩個參數的第一個字串聯起來形成結果的第一個字,兩個參數的第二個字串聯起來形成結果的第二個字,以此類推。如果一個參數比另一個參數的字多,則多餘的字原封不動的拷貝到結果上。例如,‘$(join a b,.c .o)'產生‘a.c b.o'。字之間多餘的空格不再保留,它們由單個空格代替。該函數可將函數dirnotdir的結果合併,產生原始給定的文件列表。

$(word n,text)

返回‘text’中的第n個字。N的合法值從1開始。如果n比‘text’中的字的數目大,則返回空值。例如:

$(word 2, foo bar baz)

返回 ‘bar’。

$(wordlist s,e,text)

返回‘text’中的從第s個字開始到第e個字結束的一列字。Se的合法值從1開始。如果s比‘text’中的字的數目大,則返回空值;如果e比‘text’中的字的數目大,則返回從第s個字開始到‘text’結束的所有字;如果se大,不返回任何值。例如:

$(wordlist 2, 3, foo bar baz)

返回`bar baz'

$(words text)

返回‘text’中字的數目。這樣‘text’中的最後一個字是‘$(word $(words text),text)’。

$(firstword names...)

參數‘names’作爲一系列的文件名,文件名之間用空格隔開;返回第一個文件名,其餘的忽略。例如:

$(firstword foo bar)

產生結果‘foo’。 雖然 $(firstword text) $(word 1,text)的作用相同,但第一個函數因爲簡單而保留下來。

$(wildcard pattern)

參數‘pattern’是一個文件名格式,典型的包含通配符(和shel中的文件名一樣)。函數wildcard的結果是一列和格式匹配的且文件存在的文件名,文件名之間用一個空格隔開,參閱在文件名中使用通配符

8.4函數foreach

函數foreach和其它函數非常不同,它導致一個文本塊重複使用,而且每次使用該文本塊進行不同的替換;它和shell sh中的命令forC-shell csh中的命令foreach類似。

函數foreach語法如下:

$(foreach var,list,text)

前兩個參數,‘var’和‘list’,將首先擴展,注意最後一個參數‘text’此時不擴展;接着,對每一個‘list’擴展產生的字,將用來爲‘var’擴展後命名的變量賦值;然後‘text’引用該變量擴展;因此它每次擴展都不相同。

結果是由空格隔開的‘text’ 在‘list’中多次擴展的字組成的新的‘list’。‘text’多次擴展的字串聯起來,字與字之間由空格隔開,如此就產生了函數foreach的返回值。

這是一個簡單的例子,將變量‘files’的值設置爲 ‘dirs’中的所有目錄下的所有文件的列表:

dirs := a b c d

files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))

這裏‘text’是‘$(wildcard $(dir)/*)’。第一個爲變量dir發現的值是‘a’,所以產生函數foreach結果的第一個字爲‘$(wildcard a/*)’;第二個重複的值是‘b’,所以產生函數foreach結果的第二個字爲‘$(wildcard b/*)’;第三個重複的值是‘c’,所以產生函數foreach結果的第三個字爲‘$(wildcard c/*)’;等等。該例子和下例有共同的結果:

files := $(wildcard a/* b/* c/* d/*)

如果‘text’比較複雜,您可以使用附加變量爲它命名,這樣可以提高程序的可讀性:

find_files = $(wildcard $(dir)/*)

dirs := a b c d

files := $(foreach dir,$(dirs),$(find_files))

這裏我們使用變量find_file。我們定義變量find_file時,使用了‘=’,因此該變量爲遞歸調用型變量,這樣變量find_file所包含的函數調用將在函數foreach控制下在擴展;對於簡單擴展型變量將不是這樣,在變量find_file定義時就調用函數wildcard

函數foreach對變量‘var’沒有長久的影響,它的值和變量特色在函數foreach調用結束後將和前面一樣,其它從‘list’得到的值僅在函數foreach執行時起作用,它們是暫時的。變量‘var’在函數foreach執行期間是簡單擴展型變量,如果在執行函數foreach之前變量‘var’沒有定義,則函數foreach調用後也沒有定義。參閱變量的兩個特色

當使用複雜變量表達式產生變量名時應特別小心,因爲許多奇怪的字符作爲變量名是有效的,但很可能不是您所需要的,例如:

files := $(foreach Esta escrito en espanol!,b c ch,$(find_files))

如果變量find_file擴展引用名爲‘Esta escrito en espanol!’變量,上例是有效的,但它極易帶來錯誤。

8.5函數if

函數if對在函數上下文中擴展條件提供了支持(相對於GNU make makefile文件中的條件語句,例如ifeq指令,參閱條件語句的語法)。

一個函數if的調用,可以包含兩個或三個參數:

$(if condition,then-part[,else-part])

第一個參數‘condition’,首先把前導、結尾空格去掉,然後擴展。如果擴展爲非空字符串,則條件‘condition’爲‘真’;如果擴展爲空字符串,則條件‘condition’爲‘假’。

如果條件‘condition’爲‘真’,那麼計算第二個參數‘then-part’的值,並將該值作爲整個函數if的值。

如果條件‘condition’爲‘假’,第三個參數如果存在,則計算第三個參數‘else-part’的值,並將該值作爲整個函數if的值;如果第三個參數不存在,函數if將什麼也不計算,返回空值。

注意僅能計算‘then-part’和‘else-part’二者之一,不能同時計算。這樣有可能產生副作用(例如函數shell的調用)。

8.6函數call

函數call是唯一的創建新的帶有參數函數的函數。您可以寫一個複雜的表達是作爲一個變量的值,然後使用函數call用不同的參數調用它。

函數call的語法爲:

$(call variable,param,param,...)

make擴展該函數時,它將每一個參數‘param’賦值給臨時變量$(1)$(2)等;變量$(0)的值是變量‘variable’。對於參數‘param’的數量無沒有最大數目限制,也沒有最小數目限制,但是如果使用函數call而沒有任何參數,其意義不大。

變量‘variable’在這些臨時變量的上下文中被擴展爲一個make變量,這樣,在變量‘variable’中對變量‘$(1)’的引用決定了調用函數call時對第一個參數‘param’的使用。

注意變量‘variable’是一個變量的名稱,不是對該變量的引用,所以,您不能採用‘$’和圓括號的格式書寫該變量,當然,如果您需要使用非常量的文件名,您可以在文件名中使用變量引用。

如果變量名是內建函數名,則該內建函數將被調用(即使使用該名稱的make變量已經存在)。函數call在給臨時變量賦值以前首先擴展參數,這意味着,變量‘variable’對內建函數的調用採用特殊的規則進行擴展,象函數foreachif,它們的擴展結果和您預期的結果可能不同。下面的一些例子能夠更清楚的表達這一點。

該例子時使用宏將參數的順序翻轉:

reverse = $(2) $(1)

 

foo = $(call reverse,a,b)

這裏變量foo的值是‘b a’。

下面是一個很有意思的例子:它定義了一個宏,使用該宏可以搜尋變量PATH包含的所有目錄中的第一個指定類型的程序:

pathsearch = $(firstword $(wildcard $(addsufix /$(1),$(subst :, ,$(PATH)))))

 

LS := $(call pathsearch,ls)

現在變量LS的值是‘/bin/ls’或其它的類似的值。

在函數call中可以使用嵌套。每一次遞歸調用都可以爲它自己的局部變量‘$(1)’等賦值,從而代替上一層函數call賦的值。例如:這實現了映像函數功能。

map = $(foreach a,$(2),$(call $(1),$(a)))

現在您可以映像(map)僅有一個參數的函數,如函數origin,一步得到多個值:

o = $(call map,origin,o map MAKE)

最後變量o包含諸如‘file file default’這樣的值。

警告:在函數call的參數中使用空格一定要十分小心。因爲在其它函數中,第二個或接下來的參數中的空格是不刪除的,這有可能導致非常奇怪的結果。當您使用函數call時,去掉參數中任何多餘的空格纔是最安全的方法。

8.7函數origin

函數origin不想一般函數,它不對任何變量的值操作;它僅僅告訴您一些關於一個變量的信息;它特別的告訴您變量的來源。

函數origin的語法:

$(origin variable)

注意變量‘variable’是一個查詢變量的名稱,不是對該變量的引用所以,您不能採用‘$’和圓括號的格式書寫該變量,當然,如果您需要使用非常量的文件名,您可以在文件名中使用變量引用。

函數origin的結果是一個字符串,該字符串變量是怎樣定義的:

undefined'

如果變量‘variable’從沒有定義。

default'

變量‘variable’是缺省定義,通常和命令CC等一起使用,參閱隱含規則使用的變量。注意如果您對一個缺省變量重新進行了定義,函數origin將返回後面的定義。

environment'

變量‘variable’作爲環境變量定義,選項‘-e’沒有打開(參閱選項概要

environment override'

變量‘variable’作爲環境變量定義,選項‘-e’已打開(參閱選項概要)。

file'

變量‘variable’在makefile中定義。

command line'

變量‘variable’在命令行中定義。

override'

變量‘variable’在makefile中用override指令定義(參閱override指令)。

automatic'

變量‘variable’是自動變量,定義它是爲了執行每個規則中的命令(參閱自動變量)。

這種信息的基本用途(其它用途是滿足您的好奇心)是使您要了解變量值的依據。例如,假設您有一個名爲‘foo’的makefile文件,它包含了另一個名爲‘bar’的makefile文件,如果在環境變量中已經定義變量‘bletch’,您希望運行命令‘make f bar’在makefile文件‘bar’中重新定義變量‘bletch’。但是makefile文件‘foo’在包括makefile文件‘bar’之前已經定義了變量‘bletch’,而且您也不想使用override指令定義,那麼您可以在makefile文件‘foo’中使用override指令,因爲override指令將會重載任何命令行中的定義,所以其定義的優先權超越以後在makefile文件‘bar’中的定義。因此makefile文件‘bar’可以包含:

ifdef bletch

ifeq "$(origin bletch)" "environment"

bletch = barf, gag, etc.

endif

endif

如果變量‘bletch’在環境中定義,這裏將重新定義它。

即使在使用選項‘-e’的情況下,您也要對來自環境的變量‘bletch’重載定義,則您可以使用如下內容:

ifneq "$(findstring environment,$(origin bletch))" ""

bletch = barf, gag, etc.

endif

如果‘$(origin bletch)’返回‘environment’或‘environment override’,這裏將對變量‘bletch’重新定義。參閱字符串替換和分析函數

8.8 函數shell

除了函數wildcard之外,函數shell和其它函數不同,它是make與外部環境的通訊工具。函數shell和在大多數shell中後引號(’)執行的功能一樣:它用於命令的擴展。這意味着它起着調用shell命令和返回命令輸出結果的參數的作用。Make僅僅處理返回結果,再返回結果替換調用點之前,make將每一個換行符或者一對回車/換行符處理爲單個空格;如果返回結果最後是換行符(和回車符),make將把它們去掉。由函數shell調用的命令,一旦函數調用展開,就立即執行。在大多數情況下,當makefile文件讀入時函數shell調用的命令就已執行。例外情況是在規則命令行中該函數的調用,因爲這種情況下只有在命令運行時函數才能擴展,其它調用函數shell的情況和此類似。

這裏有一些使用函數shell的例子:

contents := $(shell cat foo)

將含有文件foo的目錄設置爲變量contents的值,是用空格(而不是換行符)分離每一行。

files := $(shell echo *.c)

將‘*.c’的擴展設置爲變量files的值。除非make使用非常怪異的shell,否則這條語句和‘wildcard *.c’的結果相同。

8.9 控制make的函數

這些函數控制make的運行方式。通常情況下,它們用來向用戶提供makefile文件的信息或在偵測到一些類型的環境錯誤時中斷make運行。

$(error text...)

通常‘text’是致命的錯誤信息。注意錯誤是在該函數計算時產生的,因此如果您將該函數放在命令的腳本中或遞歸調用型變量賦值的右邊,它直到過期也不能計算。‘text’將在錯誤產生之前擴展,例如:

ifdef ERROR1

$(error error is $(ERROR1))

endif

如果變量ERROR01已經定義,在將makefile文件讀入時產生致命的錯誤。或,

ERR = $(error found an error!)

 

.PHONY: err

err: ; $(ERR)

如果err目標被調用,在make運行時產生致命錯誤。

$(warning text...)

該函數和函數error工作的方式類似,但此時make不退出,即雖然‘text’擴展並顯示結果信息,但make仍然繼續執行。擴展該函數的結果是空字符串。

9 運行make

講述編譯程序的makefile文件,可以由多種方式實現。最簡單的方式是編譯所有過期的文件,對於通常所寫的makefile文件,如果不使用任何參數運行make,那麼將這樣執行。

但是您也許僅僅更新一部分文件;您也許需要使用不同的編譯器或不同的編譯選項;您也許僅僅希望找出過時的文件而不更新它們。這些只有通過在運行make時給出參數才能實現。退出make狀態有三種情況:

0

表示make成功完成退出。

2

退出狀態爲2表示make運行中遇到錯誤,它將打印信息描述錯誤。

1

退出狀態爲1表示您運行make時使用了‘-q’標誌,並且make決定一些文件沒有更新。參閱代替執行命令

9.1 指定makefile文件的參數

指定makefile文件名的方法是使用‘-f’或‘--file’選項(‘--makefile’也能工作)。例如,‘-f altmake’說明名爲‘altmake’的文件作爲makefile文件。

如果您連續使用‘-f’標誌幾次,而且每一個‘-f’後面都帶有參數,則所有指定的文件將連在一起作爲makefile文件。

如果您不使用‘-f’或‘--file’選項,缺省的是按次序尋找GNUmakefile', makefile', Makefile'使用這三個中第一個能夠找到的存在文件或能夠創建的文件,參閱編寫makefile文件

9.2指定最終目標的參數

最終目標(gaol)是make最終努力更新的目標。其它更新的目標是因爲它們作爲最終目標的依賴,或依賴的依賴,等等以此類推。

缺省情況下,makefile文件中的第一個目標是最終目標(不計算那些以句點開始的目標)。因此,makefile文件的第一個編譯目標是對整個程序或程序組描述。如果第一個規則同時擁有幾個目標,只有該規則的第一個目標是缺省的最終目標。

您可以使用make的參數指定最終目標。方法是使用目標的名字作爲參數。如果您指定幾個最終目標,make按您命名時的順序一個接一個的處理它們。

任何在makefile文件中出現的目標都能作爲最終目標(除了以‘-’開始或含有‘=’的目標,它們一種解析爲開關,另一種是變量定義)。即使在makefile文件中沒有出現的目標,按照隱含規則可以說明怎樣生成,也能指定爲最終目標。

Make將在命令行中使用特殊變量MAKECMGOALS設置您指定的最終目標。如果沒有在命令行指定最終目標,該變量的值爲空值。注意該變量值能在特殊場合下使用。

一個合適的例子是在清除規則中避免刪除包括‘.d’的文件(參閱自動產生依賴),因這樣make不會一創建它們,就立即又刪除它們:

sources = foo.c bar.c

 

ifneq ($(MAKECMDGOALS),clean)

include $(sources:.c=.d)

endif

指定最終目標的一個用途是僅僅編譯程序的一部分或程序組中的幾個程序。如是這樣,您可以將您希望變異的文件指定爲最終目標。例如,在一個路徑下包含幾個程序,一個makefile文件以下面的格式開始:

.PHONY: all

all: size nm ld ar as

如果您僅對程序size編譯,則您可以使用‘make size’命令,這樣就只有您指定的程序才重新編譯。

指定最終目標的另一個用途是編譯產生哪些沒有正常生成的文件。例如,又一個文件需要調試,或一個版本的程序需要編譯進行測試,然而該文件不是makefile文件規則中缺省最終目標的依賴,此時,可以使用最終目標參數指定它們。

指定最終目標的另一個用途是運行和一個假想目標(參閱假想目標)或空目標(使用空目標記錄事件)相聯繫的命令。許多makefile文件包含一個假想目標‘clean’刪除除了原文件以外的所有文件。正常情況下,只有您具體指明使用‘make clean’命令,make才能執行上述任務。下面列出典型的假想目標和空目標的名稱。對GNU make軟件包使用的所有標準目標名參閱用戶標準目標

all'

創建makefile文件的所有頂層目標。

`clean'

刪除所有make正常創建的文件。

`mostlyclean'

象假象目標‘clean’,但避免刪除人們正常情況下不重新建造的一少部分文件。例如,用於GCC的目標‘mostlyclean’不刪除‘libgcc.a’,因爲重建它的情況十分稀少,而且創建它又需要很多時間。

`distclean'

`realclean'

`clobber'

這些目標可能定義爲比目標‘clean’ 刪除更多的文件。例如,刪除配置文件或爲編譯正常創建的準備文件,甚至makefile文件自身不能創建的文件。

install

向命令搜尋目錄下拷貝可執行文件;向可執行文件尋找目錄下拷貝可執行文件使用的輔助文件。

print

打印發生變化的文件列表。

tar

創建源文件的壓縮‘tar’文件。

shar

爲源文件創建一個shell的檔案文件。

dist

爲源文件創建一個發佈文件。這可能是‘tar’文件, ‘shar’文件,或多個上述的壓縮版本文件。

TAGS

更新該程序的‘tags’標籤。

`check'

`test'

對該makefile文件創建的程序執行自我測試。

9.3 代替執行命令

makefile文件告訴make怎樣識別一個目標是否需要更新以及怎樣更新每一個目標。但是更新目標並不是您一直需要的,一些特定的選項可以用來指定make的其它活動:

`-n'

`--just-print'

`--dry-run'

`--recon'

No-op’。make的這項活動是打印用於創建目標所使用的命令,但並不執行它們。

`-t'

`--touch'

touch’。這項活動是做更新標誌,實際卻不更改它們。換句話說,make假裝編譯了目標,但實際對它們沒有一點兒改變。

`-q'

`--question'

question’。這項活動是暗中察看目標是否已經更新;但是任何情況下也不執行命令。換句話說,即不編譯也不輸出。

`-W file'

`--what-if=file'

`--assume-new=file'

`--new-file=file'

What if’。每一個‘-W’標誌後跟一個文件名。所有文件名的更改時間被make記錄爲當前時間,但實際上更改時間保持不變。如果您要更新文件,您可以使用‘-W’標誌和‘-n’標誌連用看看將發生什麼。

使用標誌‘-n’,make打印那些正常執行的命令,但卻不執行它們。

使用標誌‘-t’,make忽略規則中的命令,對那些需要更新的目標使用‘touch’命令。如果不使用‘-s’或.SILENT,‘touch’命令同樣打印。爲了提高執行效率,make並不實際調用程序touch,而是使touch直接運行。

使用標誌‘-q’,make不打印輸出也不執行命令,如果所有目標都已經更新到最新,make的退出狀態是0;如果一部分需要更新,退出狀態是1;如果make遇到錯誤,退出狀態是2,因此您可以根據沒有更新的目標尋找錯誤。

在運行make時對以上三個標誌如果同時兩個或三個將產生錯誤。標誌‘-n’、‘-t’和‘-s’對那些以字符‘+’開始的命令行和包含字符串‘$(MAKE)' 或‘${MAKE}'命令行不起作用。注意僅有這些以字符‘+’開始的命令行和包含字符串‘$(MAKE)' 或‘${MAKE}'命令行運行時不注意這些選項。參閱變量MAKE的工作方式

-W’標誌有一下兩個特點:

l         l         如果同時使用標誌‘-n’或‘-q’,如果您更改一部分文件,看看make將會做什麼。

l         l         沒有使用標誌‘-n’或‘-q’,如果make運行時採用標誌‘-W’,則make假裝所有文件已經更新,但實際上不更改任何文件。

注意選項‘-p’和‘-v’允許您得到更多的make信息或正在使用的makefile文件的信息(參閱選項概要)。

9.4避免重新編譯文件

有時您可能改變了一個源文件,但您並不希望編譯所有依靠它的文件。例如,假設您在一個許多文件都依靠的頭文件種添加了一個宏或一個聲明,按照保守的規則,make認爲任何對於該頭文件的改變,需要編譯所有依靠它的文件,但是您知道那是不必要的,並且您沒有等待它們完全編譯的時間。

如果您提前瞭解改變頭文件以前的問題,您可以使用‘-t’選項。該標誌告訴make不運行規則中的命令,但卻將所有目標的時間戳改到最新。您可按下述步驟實現上述計劃:

1、用make命令重新編譯那些需要編譯的源文件;

2、更改頭文件;

3、使用‘make t’命令改變所有目標文件的時間戳,這樣下次運行make時就不會因爲頭文件的改變而編譯任何一個文件。

如果在重新編譯那些需要編譯的源文件前已經改變了頭文件,則按上述步驟做已顯得太晚了;作爲補救措施,您可以使用‘-o file’標誌,它能將指定的文件的時間戳假裝改爲以前的時間戳(參閱選項概要)。這意味着該文件沒有更改,因此您可按下述步驟進行:

1、使用‘make -o file’命令重新編譯那些不是因爲改變頭文件而需要更新的文件。如果涉及幾個頭文件,您可以對每個頭文件都使用‘-o’標誌進行指定。

2、使用‘make t’命令改變所有目標文件的時間戳。

9.5變量重載

使用‘=’定義的變量:‘v=x’將變量v的值設爲x。如果您用該方法定義了一個變量,在makefile文件後面任何對該變量的普通賦值都將被make忽略,要使它們生效應在命令行將它們重載。

最爲常見的方法是使用傳遞附加標誌給編譯器的靈活性。例如,在一個makefile文件中,變量CFLAGS已經包含了運行C編譯器的每一個命令,因此,如果僅僅鍵入命令make時,文件‘foo.c’將按下面的方式編譯:

cc -c $(CFLAGS) foo.c

這樣您在makefile文件中對變量CFALAGS設置的任何影響編譯器運行的選項都能生效,但是每次運行make時您都可以將該變量重載,例如:如果您說‘make CFLAGS='-g -O'’,任何C編譯器都將使用‘cc -c -g -O’編譯程序。這還說明了在重載變量時,怎樣使用shell命令中的引用包括空格和其它特殊字符在內的變量的值。

變量CFALAGS僅僅是您可以使用這種方式重載的許多標準變量中的一個,這些標準變量的完整列表見隱含規則使用的變量

您也可以編寫makefile察看您自己的附加變量,從而使用戶可通過更改這些變量控制make運行時的其它面貌。

當您使用命令參數重載變量時,您可以定義遞歸調用擴展型變量或簡單擴展型變量。上例中定義的是遞歸調用擴展型變量,如果定義簡單擴展型變量,請使用‘:=’代替‘=’。注意除非您在變量值中使用變量引用或函數調用,這兩種變量沒有任何差異。

利用這種方式也可以改變您在makfile文件中重載的變量。在makfile文件中重載的變量是使用override指令,是和‘override variable = value’相似的命令行。詳細內容參閱override指令

9.6 測試編譯程序

正常情況下,在執行shell命令時一旦有錯誤發生,make立即退出返回非零狀態;不會爲任何目標繼續運行命令。錯誤表明make不能正確的創建最終目標,並且make一發現錯誤就立即報告。

當您編譯您修改過的程序時,這不是您所要的結果。您希望make能夠經可能的試着編譯每一個程序,並儘可能的顯示每一個錯誤。

這種情況下,您可以使用‘-k’或‘--keep-going’選項。這種選項告訴make遇到錯誤返回非零狀態之前,繼續尋找該目標的依賴,如果有必要則重新創建它們。例如,在編譯一個目標文件時發現錯誤,即使make已經知道連接它們已是不可能的, make k’也將繼續編譯其它目標文件。除在shell命令失敗後繼續運行外,即使發在make不知道如何創建的目標和依賴文件以後,‘make k’也將儘可能的繼續運行。在沒有‘-k’選項時,這些錯誤將是致命的(參閱選項概要)。

通常情況下,make的行爲是基於假設您的目標是使最終目標更新;一旦它發現這是不可能的它就立即報告錯誤。選項‘-k’說真正的目標是儘可能測試改變對程序的影響,發現存在的問題,以便在下次運行之前您可以糾正它們。這是Emacs M-x compile命令缺省傳遞‘-k’選項的原因。

9.7 選項概要

下面是所有make能理解的選項列表:

`-b'

`-m'

和其它版本make兼容時,這些選項被忽略。

`-C dir'

`--directory=dir'

在將makefile讀入之前,把路徑切換到‘dir’下。如果指定多個‘-C’選項,每一個都是相對於前一個的解釋:‘-C/-C etc’等同於‘-C/etc’。該選項典型用在遞歸調用make過程中,參閱遞歸調用make

-d

在正常處理後打印調試信息。調試信息說明哪些文件用於更新,哪個文件作爲比較時間戳的標準以及比較的結果,哪些文件實際上需要更新,需要考慮、使用哪些隱含規則等等----一切和make決定最終幹什麼有關的事情。‘-d’選項等同於‘--debug=a’選項(參見下面內容)。

`--debug[=options]'

在正常處理後打印調試信息。可以選擇各種級別和類型的輸出。如果沒有參數,打印‘基本’級別的調試信息。以下是可能的參數,僅僅考慮第一個字母,各個值之間使用逗號或空格隔開:

a (all)

顯示所有調試信息,該選項等同於‘-d’選項。

b (basic)

基本調試信息打印每一個已經過時的目標,以及它們重建是否成功。

v (verbose)

比‘基本’級高一個的等級的調試信息。包括makefile文件的語法分析結果,沒有必要更新的依賴等。該選項同時包含基本調試信息。

i (implicit)

打印隱含規則搜尋目標的信息。該選項同時包含基本調試信息。

j (jobs)

打印各種子命令調用的詳細信息。

m (makefile)

以上選項不包含重新創建makefile文件的信息。該選項包含了這方面的信息。注意,選項‘all’也不包含這方面信息。該選項同時包含基本調試信息。

`-e'

`--environment-overrides'

設置從環境中繼承來的變量的優先權高於makefile文件中的變量的優先權。參閱環境變量

`-f file'

`--file=file'

`--makefile=file'

將名爲‘file’的文件設置爲makefile文件。參閱編寫makefile文件

`-h'

`--help'

向您提醒make 能夠理解的選項,然後退出。

`-i'

`--ignore-errors'

忽略重建文件執行命令時產生的所有錯誤。

`-I dir'

`--include-dir=dir'

指定搜尋包含makefile文件的路徑‘dir’。參閱包含其它makefile文件。如果同時使用幾個‘-I’選項用於指定路徑,則按照指定的次序搜尋這些路徑。

`-j [jobs]'

`--jobs[=jobs]'

指定同時執行的命令數目。如果沒有參數make將同時執行儘可能多的任務;如果有多個‘-j’選項,則僅最後一個選項有效。詳細內容參閱並行執行。注意在MS-DOS下,該選項被忽略。

`-k'

`--keep-going'

在出現錯誤後,儘可能的繼續執行。當一個目標創建失敗,則所有依靠它的目標文件將不能重建,而這些目標的其它依賴則可繼續處理。參閱測試編譯程序

`-l [load]'

`--load-average[=load]'

`--max-load[=load]'

指定如果有其它任務正在運行,並且平均負載已接近或超過‘load’(一個浮點數),則此時不啓動新任務。如果沒有參數則取消以前關於負載的限制。參閱並行執行

`-n'

`--just-print'

`--dry-run'

`--recon'

打印要執行的命令,但卻不執行它們。參閱代替執行命令

`-o file'

`--old-file=file'

`--assume-old=file'

即使文件file比它的依賴‘舊’,也不重建該文件。不要因爲文件file的改變而重建任何其它文件。該選項本質上是假裝將該文件的時間戳改爲舊的時間戳,以至於依靠它的規則被忽略。參閱避免重新編譯文件

`-p'

`--print-data-base'

打印數據庫(規則和變量的值),這些數據來自讀入makefile文件的結果;然後象通常那樣執行或按照別的指定選項執行。如果同時給出‘-v’開關,則打印版本信息(參閱下面內容)。使用‘make qp’則打印數據庫後不試圖重建任何文件。使用‘make p f/dev/null’則打印預定義的規則和變量的數據庫。數據庫輸出中包含文件名,以及命令和變量定義的行號信息。它是在複雜環境中很好的調試工具。

`-q'

`--question'

‘問題模式’。不打印輸出也不執行命令,如果所有目標都已經更新到最新,make的退出狀態是0;如果一部分需要更新,退出狀態是1;如果make遇到錯誤,退出狀態是2,參閱代替執行命令

`-r'

`--no-builtin-rules'

排除使用內建的隱含規則(參閱使用隱含規則)。您仍然可以定義您自己的格式規則(參閱定義和重新定義格式規則)。選項‘-r’同時也清除了缺省的後綴列表和後綴規則(參閱過時的後綴規則)。但是您可以使用.SUFFIXES規則定義您自己的後綴。注意,使用選項‘-r’僅僅影響規則;缺省變量仍然有效(參閱隱含規則使用的變量);參閱下述的選項‘-R’。

`-R'

`--no-builtin-variables'

排除使用內建的規則變量(參閱隱含規則使用的變量)。當然,您仍然可以定義自己的變量。選項‘-R’自動使選項‘-r’生效;因爲它去掉了隱含規則所使用的變量的定義,所以隱含規則也就失去了存在的意義。

`-s'

`--silent'

`--quiet'

沉默選項。不回顯那些執行的命令。參閱命令回顯

`-S'

`--no-keep-going'

`--stop'

使選項‘-k’失效。除非在遞歸調用make時,通過變量MAKEFLAGS從上層make繼承選項‘-k’,或您在環境中設置了選項‘-k’,否則沒有必要使用該選項。

`-t'

`--touch'

標誌文件已經更新到最新,但實際沒有更新它們。這是假裝那些命令已經執行,用於愚弄將來的make調用。參閱代替執行命令

`-v'

`--version'

打印make程序的版本信息,作者列表和沒有擔保的注意信息,然後退出。

`-w'

`--print-directory'

打印執行makefile文件時涉及的所有工作目錄。這對於跟蹤make遞歸調用時複雜嵌套產生的錯誤非常有用。參閱遞歸調用make。實際上,您很少需要指定該選項,因爲make已經替您完成了指定。參閱--print-directory’選項

`--no-print-directory'

在指定選項‘-w’的情況下,禁止打印工作路徑。這個選項在選項‘-w’自動打開而且您不想看多餘信息時比較有用。參閱--print-directory’選項

`-W file'

`--what-if=file'

`--new-file=file'

`--assume-new=file'

假裝目標文件已經更新。在使用標誌‘n’時,它將向您表明更改該文件會發生什麼。如果沒有標誌‘n’它和在運行make之前對給定的文件使用touch命令的結果幾乎一樣,但使用該選項make只是在的想象中更改該文件的時間戳。參閱代替執行命令

`--warn-undefined-variables'

make看到引用沒有定義的變量時,發佈一條警告信息。如果您按照複雜方式使用變量,當您調試您的makefile文件時,該選項非常有用。

10 使用隱含規則

重新創建目標文件的一些標準方法是經常使用的。例如,一個傳統的創建OBJ文件的方法是使用C編譯器,如cc,編譯C語言源程序。

隱含規則能夠告訴make怎樣使用傳統的技術完成任務,這樣,當您使用它們時,您就不必詳細指定它們。例如,有一條編譯C語言源程序的隱含規則,文件名決定運行哪些隱含規則;另如,編譯C語言程序一般是使用‘.c’文件,產生‘.o’文件。因此, make據此和文件名的後綴就可以決定使用編譯C語言源程序的隱含規則。一系列的隱含規則可按順序應用;例如,make可以從一個‘.y’文件,藉助‘.c’文件,重建一個‘.o’文件,參閱隱含規則鏈。內建隱含規則的命令需要使用變量,通過改變這些變量的值,您就可以改變隱含規則的工作方式。例如,變量CFLAGS控制隱含規則用於編譯C程序傳遞給C編譯器的標誌,參閱隱含規則使用的變量。通過編寫格式規則,您可以創建您自己的隱含規則。參閱定義和重新定義格式規則

後綴規則是對定義隱含規則最有限制性。格式規則一般比較通用和清楚,但是後綴規則卻要保持兼容性。參閱過時的後綴規則

10.1 使用隱含規則

允許make對一個目標文件尋找傳統的更新方法,您所有做的是避免指定任何命令。可以編寫沒有命令行的規則或根本不編寫任何規則。這樣,make將根據存在的源文件的類型或要生成的文件類型決定使用何種隱含規則。

例如,假設makefile文件是下面的格式:

foo : foo.o bar.o

        cc -o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)

因爲您提及了文件‘foo.o’,但是您沒有給出它的規則,make將自動尋找一條隱含規則,該規則能夠告訴make怎樣更新該文件。無論文件‘foo.o’存在與否,make都會這樣執行。

如果能夠找到一條隱含規則,則它就能夠對命令和一個或多個依賴(源文件)提供支持。如果您要指定附加的依賴,例如頭文件,但隱含規則不能支持,您需要爲目標‘foo.o’寫一條不帶命令行的規則。

每一條隱含規則都有目標格式和依賴格式;也許多條隱含規則有相同的目標格式。例如,有數不清的規則產生‘.o’文件:使用C編譯器編譯‘.C’文件;使用Pascal編譯器編譯‘.p’文件;等等。實際應用的規則是那些依賴存在或可以創建的規則。所以,如果您有一個‘.C’文件,make將運行C編譯器;如果您有一個‘.p’文件,make將運行Pascal編譯器;等等。

當然,您編寫一個makefile文件時,您知道您要make使用哪一條隱含規則,以及您知道make將選擇哪一條規則,因爲您知道那個依賴文件是假設存在的。預定義的隱含規則列表的詳細內容參閱隱含規則目錄

首先,我們說一條隱含規則可以應用,該規則的依賴必須‘存在或可以創建’。一個文件‘可以創建’是說該文件在makefile中作爲目標或依賴被提及,或者該文件可以經過一條隱含規則的遞歸調用後能夠創建。如果一條隱含規則的依賴是另一條隱含規則的結果,我們說產生了‘鏈’。參閱隱含規則鏈

總體上說,make爲每一個目標搜尋隱含規則,爲沒有命令行的雙冒號規則搜尋隱含規則。僅作爲依賴被提及的文件,將被認爲是一個目標,如果該目標的規則沒有指定任何內容, make將爲它搜尋隱含規則。對於詳細的搜尋過程參閱隱含規則的搜尋算法

注意,任何具體的依賴都不影響對隱含規則的搜尋。例如,認爲這是一條具體的規則:

foo.o: foo.p

文件foo.p不是首要條件,這意味着make按照隱含規則可以從一個Pascal源程序(‘.p’文件)創建OBJ文件,也就是說一個‘.o’文件可根據‘.p’文件進行更新。但文件foo.p並不是絕對必要的;例如,如果文件foo.c也存在,按照隱含規則則是從文件foo.c重建foo.o,這是因爲C編譯規則在預定義的隱含規則列表中比Pascal規則靠前,參閱隱含規則目錄

如果您不希望使用隱含規則創建一個沒有命令行的目標,您可以通過添加分號爲該目標指定空命令。參閱使用空命令

10.2隱含規則目錄

這裏列舉了預定義的隱含規則的目錄,這些隱含規則是經常應用的,當然如果您在makefile文件中重載或刪除後,這些隱含規則將會失去作用,詳細內容參閱刪除隱含規則。選項‘-r’或‘--no-builtin-rules’將刪除所有預定義的隱含規則。

並不是所有的隱含規則都是預定義的,在make中很多預定義的隱含規則是後綴規則的擴展,因此,那些預定義的隱含規則和後綴規則的列表相關(特殊目標.SUFFIXES的依賴列表)。缺省的後綴列表爲:.out,  .a, .ln,  .o,  .c,  .cc,  .C,  .p,  .f,  .F, .r,  .y,  .l, .s,  .S,  .mod,  .sym, .def,  .h,  .info,  .dvi,  .tex,  .texinfo,  .texi,  .txinfo,  .w,  .ch .web,  .sh,  .elc,  .el。所有下面描述的隱含規則,如果它們的依賴中有一個出現在這個後綴列表中,則是後綴規則。如果您更改這個後綴列表,則只有那些由一個或兩個出現在您指定的列表中的後綴命名的預定義後綴規則起作用;那些後綴沒有出現在列表中的規則被禁止。對於詳細的關於後綴規則的描述參閱過時的後綴規則

Compiling C programs(編譯C程序)

n.o' 自動由n.c' 使用命令 ‘$(CC) -c $(CPPFLAGS) $(CFLAGS)'生成 。

Compiling C++ programs (編譯C++程序)

n.o'自動由n.cc' n.C'使用命令‘$(CXX) -c $(CPPFLAGS) $(CXXFLAGS)’生成。 我們鼓勵您對C++源文件使用後綴.cc' 代替後綴‘.C’。

Compiling Pascal programs (編譯Pascal程序)

n.o'自動由n.p'使用命令$(PC) -c $(PFLAGS)'生成。

Compiling Fortran and Ratfor programs (編譯Fortran Ratfor程序)

n.o'自動由n.r', n.F'n.f' 運行Fortran編譯器生成。使用的精確命令如下:

`.f'

`$(FC) -c $(FFLAGS)'.

`.F'

`$(FC) -c $(FFLAGS) $(CPPFLAGS)'.

`.r'

`$(FC) -c $(FFLAGS) $(RFLAGS)'.

Preprocessing Fortran and Ratfor programs (預處理Fortran Ratfor程序)

n.f' 自動從 n.r'或‘n.F'得到。該規則僅僅是與處理器把一個Ratfor 程序或能夠預處理的 Fortran 程序轉變爲標準的 Fortran 程序。使用的精確命令如下:

`.F'

`$(FC) -F $(CPPFLAGS) $(FFLAGS)'.

`.r'

`$(FC) -F $(FFLAGS) $(RFLAGS)'.

Compiling Modula-2 programs(編譯Modula-2程序)

n.sym'自動由n.def'使用命令‘$(M2C) $(M2FLAGS) $(DEFFLAGS)'生成。 n.o' n.mod'生成;命令爲:$(M2C) $(M2FLAGS) $(MODFLAGS)'

Assembling and preprocessing assembler programs (彙編以及預處理彙編程序)

n.o'自‘n.S'運行C編譯器,cpp,生成。命令爲:$(CPP) $(CPPFLAGS)'

Linking a single object file (連接一個簡單的OBJ文件)

n' 自動由n.o' 運行C編譯器中的連接程序 linker (通常稱爲 ld)生成。命令爲: $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)'。該規則對僅有一個源程序的簡單程序或對同時含有多個OBJ文件(可能來自於不同的源文件)的程序都能正常工作。如果同時含有多個OBJ文件,則其中必有一個OBJ文件的名字和可執行文件名匹配。例如:

x: y.o z.o

x.c', y.c' z.c' 都存在時則執行:

cc -c x.c -o x.o

cc -c y.c -o y.o

cc -c z.c -o z.o

cc x.o y.o z.o -o x

rm -f x.o

rm -f y.o

rm -f z.o

對於更復雜的情況,例如沒有一個OBJ文件的名字和可執行文件名匹配,您必須爲連接寫一條具體的命令。每一種能自動生成‘.o’的文件,可以在沒有‘-c’選項的情況下使用編譯器($(CC)',$(FC)'或‘$(PC)' C編譯器‘$(CC)'也適用於彙編程序)自動連接。當然也可以使用OBJ文件作爲中間文件,但編譯、連接一步完成速度將快很多。

Yacc for C programs (由Yacc生成C程序)

n.c'自動由n.y'使用命令‘$(YACC) $(YFLAGS)'運行 Yacc生成。

Lex for C programs (由Lex生成C程序)

n.c'自動由‘n.l' 運行 Lex生成。命令爲:‘$(LEX) $(LFLAGS)'

Lex for Ratfor programs (由Lex生成Rator程序)

n.r'自動由‘n.l' 運行 Lex生成。命令爲:‘$(LEX) $(LFLAGS)'。 對於所有的Lex文件,無論它們產生C代碼或Ratfor 代碼,都使用相同的後綴‘.l’進行轉換,在特定場合下,使用make自動確定您使用哪種語言是不可能的。如果make使用‘.l’文件重建一個OBJ文件,它必須猜想使用哪種編譯器。它很可能猜想使用的是 C 編譯器, 因爲C 編譯器更加普遍。如果您使用 Ratfor語言, 請確保在makefile文件中提及了n.r',使make知道您的選擇。否則,如果您專用Ratfor語言,不使用任何C 文件, 請在隱含規則後綴列表中將‘.c’剔除:

.SUFFIXES:

.SUFFIXES: .o .r .f .l ...

Making Lint Libraries from C, Yacc, or Lex programs(由C, Yacc, Lex程序創建Lint庫)

n.ln' 可以從n.c' 運行lint產生。命令爲:‘ $(LINT) $(LINTFLAGS) $(CPPFLAGS) i’。用於C程序的命令和用於n.y'n.l'程序相同。

TeX and WebTeX Web

n.dvi'可以從n.tex' 使用命令‘$(TEX)'得到。n.tex'可以從n.web'使用命令‘$(WEAVE)'得到;或者從n.w' (n.ch'如果n.ch'存在或可以建造) 使用命令‘$(CWEAVE)'n.p' 可以從n.web'使用命令‘$(TANGLE)'產生。n.c' 可以從n.w' (n.ch'如果n.ch'存在或可以建造) 使用命令‘$(CTANGLE)'得到。

Texinfo and InfoTexinfoInfo

n.dvi'可以從n.texinfo',n.texi', n.txinfo', 使用命令‘$(TEXI2DVI) $(TEXI2DVI_FLAGS)'得到。n.info'可以從n.texinfo',n.texi', n.txinfo', 使用命令‘$(MAKEINFO) $(MAKEINFO_FLAGS)'創建。

RCS

文件n'必要時可以從名爲n,v'RCS/n,v'RCS文件中提取。具體命令是:‘$(CO) $(COFLAGS)'。文件n'如果已經存在,即使RCS文件比它新,它不能從RCS文件中提取。用於RCS的規則是最終的規則,參閱萬用規則,所以RCS不能夠從任何源文件產生,它們必須存在。

SCCS

文件n'必要時可以從名爲s.n'SCCS/s.n'SCCS文件中提取。具體命令是:‘$(GET) $(GFLAGS)'。用於SCCS的規則是最終的規則,參閱萬用規則,所以SCCS不能夠從任何源文件產生,它們必須存在。SCCS的優點是,文件 n' 可以從文件 n.sh'拷貝並生成可執行文件(任何人都可以)。這用於shell的腳本,該腳本在SCCS內部檢查。因爲RCS 允許保持文件的可執行性,所以您沒有必要將該特點用於RCS文件。我們推薦您避免使用SCCSRCS不但使用廣泛,而且是免費的軟件。選擇自由軟件代替相當的(或低劣的)收費軟件,是您對自由軟件的支持。

通常情況下,您要僅僅改變上表中的變量,需要參閱下面的文檔。

隱含規則的命令實際使用諸如COMPILE.c, LINK.p, PREPROCESS.S等等變量,它們的值包含以上列出的命令。Make按照慣例進行處理,如,編譯‘.x’源文件的規則使用變量‘COMPILE.x’;從‘.x’源文件生成可執行文件使用變量‘LINK.x’;預處理‘.x’源文件使用變量‘PREPROCESS.x’。

任何產生OBJ文件的規則都使用變量‘OUTPUT_OPTION’;make依據編譯時間選項定義該變量的值是‘-o $@’或空值。當源文件分佈在不同的目錄中,您應該使用‘-O’選項保證輸出到正確的文件中;使用變量VPATH時同樣(參閱爲依賴搜尋目錄)。一些系統的編譯器不接受針對OBJ文件的‘-o’開關;如果您在這樣的系統上運行,並使用了變量VPATH,一些文件的編譯輸出可能會放到錯誤的地方。解決辦法是將變量OUTPUT_OPTION值設爲:‘; mv $*.o $@’。

10.3隱含規則使用的變量

內建隱含規則的命令對預定義變量的使用是開放的;您可以在makefile文件中改變變量的值,也可以使用make的運行參數或在環境中改變,如此,在不對這些規則本身重新定義的情況下,就可以改變這些規則的工作方式。您還可以使用選項‘-R’或‘--no-builtin-variables’刪除所有隱含規則使用的變量。

例如,編譯C程序的命令實際是‘$(CC) -c $(CFLAGS) $(CPPFLAGS)’,變量缺省的值是‘cc’或空值,該命令實際是‘cc c’。如重新定義變量‘CC’的值爲‘ncc’,則所有隱含規則將使用‘ncc’作爲編譯C語言源程序的編譯器。通過重新定義變量‘CFLAGS’的值爲‘-g’,則您可將‘-g’選項傳遞給每個編譯器。所有的隱含規則編譯C程序時都使用‘$CC’獲得編譯器的名稱,並且都在傳遞給編譯器的參數中都包含‘$(CFLAGS)’。

隱含規則使用的變量可分爲兩類:一類是程序名變量(象cc),另一類是包含程序運行參數的變量(象CFLAGS)。(‘程序名’可能也包含一些命令參數,但是它必須以一個實際可以執行的程序名開始。) 如果一個變量值中包含多個參數,它們之間用空格隔開。

這裏是內建規則中程序名變量列表:

AR

檔案管理程序;缺省爲:ar'.

AS

彙編編譯程序;缺省爲:as'.

CC

C語言編譯程序;缺省爲:cc'.

CXX

C++編譯程序;缺省爲:g++'.

CO

RCS文件中解壓縮抽取文件程序;缺省爲:co'.

CPP

帶有標準輸出的C語言預處理程序;缺省爲:$(CC) -E'.

FC

Fortran 以及 Ratfor 語言的編譯和預處理程序;缺省爲:f77'.

GET

SCCS文件中解壓縮抽取文件程序;缺省爲:get'.

LEX

Lex 語言轉變爲 C Ratfor程序的程序;缺省爲:lex'.

PC

Pascal 程序編譯程序;缺省爲:pc'.

YACC

Yacc語言轉變爲 C程序的程序;缺省爲:yacc'.

YACCR

Yacc語言轉變爲 Ratfor程序的程序;缺省爲:yacc -r'.

MAKEINFO

Texinfo 源文件轉換爲信息文件的程序;缺省爲:makeinfo'.

TEX

TeX源產生TeX DVI文件的程序;缺省爲:tex'.

TEXI2DVI

Texinfo源產生TeX DVI 文件的程序;缺省爲:texi2dvi'.

WEAVE

Web翻譯成TeX的程序;缺省爲:weave'.

CWEAVE

CWeb翻譯成TeX的程序;缺省爲:cweave'.

TANGLE

Web翻譯成 Pascal的程序;缺省爲:tangle'.

CTANGLE

Web翻譯成C的程序;缺省爲:ctangle'.

RM

刪除文件的命令;缺省爲:rm -f'.

這裏是值爲上述程序附加參數的變量列表。在沒有註明的情況下,所有變量的值爲空值。

ARFLAGS

用於檔案管理程序的標誌,缺省爲:rv'.

ASFLAGS

用於彙編編譯器的額外標誌 (當具體調用.s'.S'文件時)

CFLAGS

用於C編譯器的額外標誌。

CXXFLAGS

用於C++編譯器的額外標誌。

COFLAGS

用於RCS co程序的額外標誌。

CPPFLAGS

用於C預處理以及使用它的程序的額外標誌 (C Fortran 編譯器)

FFLAGS

用於Fortran編譯器的額外標誌。

GFLAGS

用於SCCS get程序的額外標誌。

LDFLAGS

用於調用linker(‘ld’)的編譯器的額外標誌。

LFLAGS

用於Lex的額外標誌。

PFLAGS

用於Pascal編譯器的額外標誌。

RFLAGS

用於處理Ratfor程序的Fortran編譯器的額外標誌。

YFLAGS

用於Yacc的額外標誌。Yacc

10.4 隱含規則鏈

有時生成一個文件需要使用多個隱含規則組成的序列。例如,從文件‘n.y’生成文件‘n.o’,首先運行隱含規則Yacc,其次運行規則cc。這樣的隱含規則序列稱爲隱含規則鏈。

如果文件‘n.c’存在或在makefile文件中提及,則不需要任何特定搜尋:make首先發現通過C編譯器編譯‘n.c’可生成該OBJ文件,隨後,考慮生成‘n.c’時,則使用運行Yacc的規則。這樣可最終更新‘n.c’和‘n.o’。

即使在文件‘n.c’不存在或在makefile文件中沒有提及的情況下,make也能想象出在文件‘n.y’和‘n.o’缺少連接!這種情況下,‘n.c’稱爲中間文件。一旦make決定使用中間文件,它將把中間文件輸入數據庫,好像中間文件在makefile文件中提及一樣;按照隱含規則的描述創建中間文件。

中間文件和其它文件一樣使用自己的規則重建,但是中間文件和其它文件相比有兩種不同的處理方式。

第一個不同的處理方式是如果中間文件不存在make的行爲不同:平常的文件b如果不存在,make認爲一個目標依靠文件b,它總是創建文件b,然後根據文件b更新目標;但是文件b若是中間文件,make很可能不管它而進行別的工作,即不創建文件b,也不更新最終目標。只有在文件b的依賴比最終目標‘新’時或有其它原因時,才更新最終目標。

第二個不同點是make在更新目標創建文件b後,如果文件b不再需要,make將把它刪除。所以一箇中間文件在make運行之前和make運行之後都不存在。Make向您報告刪除時打印一條‘rm f’命令,表明有文件被刪除。

通常情況下,任何在makefile文件中提及的目標和依賴都不是中間文件。但是,您可以特別指定一些文件爲中間文件,其方法爲:將要指定爲中間文件的文件作爲特殊目標 .INTERMEDIATE的依賴。這種方法即使對採用別的方法具體提及的文件也能生效。

您通過將文件標誌爲secondary文件可以阻止自動刪除中間文件。這時,您將您需要保留的中間文件指定爲特殊目標 .SECONDARY的依賴即可。對於secondary文件,make不會因爲它不存在而去創建它,也不會自動刪除它。secondary文件必須也是中間文件。

您可以列舉一個隱含規則的目標格式(例如%.o)作爲特殊目標 .PRECIOUS的依賴,這樣您就可以保留那些由隱含規則創建的文件名匹配該格式的中間文件。參閱中斷和關閉make

一個隱含規則鏈至少包含兩個隱含規則。例如,從‘RCS/foo.y,v’創建文件‘foo’需要運行RCSYacccc,文件foo.yfoo.c是中間文件,在運行結束後將被刪掉。

沒有一條隱含規則可以在隱含規則鏈中出現兩次以上(含兩次)。這意味着,make不會簡單的認爲從文件‘foo.o.o’創建文件foo不是運行linker兩次。這還可以強制make在搜尋一個隱含規則鏈時阻止無限循環。

一些特殊的隱含規則可優化隱含規則鏈控制的特定情況。例如,從文件foo.c創建文件foo可以被擁有編譯和連接的規則鏈控制,它使用foo.o作爲中間文件。但是對於這種情況存在一條特別的規則,使用簡單的命令cc可以同時編譯和連接。因爲優化規則在規則表中的前面,所以優化規則和一步一步的規則鏈相比,優先使用優化規則。

10.5定義與重新定義格式規則

您可以通過編寫格式規則定義隱含規則。該規則看起來和普通規則類似,不同之處在於格式規則的目標中包含字符‘%’(只有一個)。目標是匹配文件名的格式;字符‘%’可以匹配任何非空的字符串,而其它字符僅僅和它們自己相匹配。依賴用‘%’表示它們的名字和目標名關聯。

格式‘%.o : %.c’是說將任何‘stem.c’文件編譯爲‘stem.o’文件。

在格式規則中使用的‘%’擴展是在所有變量和函數擴展以後進行的,它們是在makefile文件讀入時完成的。參閱使用變量轉換文本函數

10.5.1格式規則簡介

格式規則是在目標中包含字符‘%’(只有一個)的規則,其它方面看起來和普通規則相同。目標是可以匹配文件名的格式,字符‘%’可以匹配任何非空的字符串,而其它字符僅僅和它們自己相匹配。

例如‘%.c’匹配任何以‘.c’結尾的文件名;‘s.%.c’匹配以‘s.’開始並且以‘.c’結尾的文件名,該文件名至少包含5個字符(因爲‘%’至少匹配一個字符)。匹配‘%’的子字符串稱爲stem()。依賴中使用‘%’表示它們的名字中含有和目標名相同的stem。要使用格式規則,文件名必須匹配目標的格式,而且符合依賴格式的文件必須存在或可以創建。下面規則:

%.o : %.c ; command...

表明要創建文件‘n.o’,使用‘n.c’作爲它的依賴,而且文件‘n.c’ 必須存在或可以創建。

在格式規則中,依賴有時不含有‘%’。這表明採用該格式規則創建的所有文件都是採用相同的依賴。這種固定依賴的格式規則在有些場合十分有用。

格式規則的依賴不必都包含字符‘%’,這樣的規則是一個有力的常規通配符,它爲任何匹配該目標格式規則的文件提供創建方法。參閱定義最新類型的缺省規則

格式規則可以有多個目標,不象正常的規則,這種規則不能扮演具有相同依賴和命令的多條不同規則。如果一格式規則具有多個目標,make知道規則的命令對於所有目標來說都是可靠的,這些命令只有在創建所目標時才執行。當爲匹配一目標搜尋格式規則時,規則的目標格式和規則要匹配的目標不同是十分罕見的,所以make僅僅擔心目前對文件給出命令和依賴是否有問題。注意該文件的命令一旦執行,所有目標的時間戳都會更新。

格式規則在makefile文件中的次序很重要,因爲這也是考慮它們的次序。對於多個都能使用的規則,使用最先出現的規則。您親自編寫的規則比內建的規則優先。注意依賴存在或被提及的規則優先於依賴需要經過隱含規則鏈生成的規則。

10.5.2格式規則的例子

這裏有一些實際在make中預定義的格式規則例子,第一個,編譯‘.c’文件生成‘.o’文件的規則:

%.o : %.c

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

定義了一條編譯‘x.c’文件生成‘x.o’文件的規則,命令使用自動變量‘$@’和‘$<’ 替換任何情況使用該規則的目標文件和源文件。參閱自動變量

第二個內建的例子:

% :: RCS/%,v

        $(CO) $(COFLAGS) $<

定義了在子目錄‘RCS’中根據相應文件‘x.v’生成文件‘x’的規則。因爲目標是‘%’,只要相對應的依賴文件存在,該規則可以應用於任何文件。雙冒號表示該規則是最終規則,它意味着不能是中間文件。參閱萬用規則

下面的格式規則有兩個目標:

%.tab.c %.tab.h: %.y

        bison -d $<

這告訴make執行命令‘bison -d x.y’將創建‘x.tab.c’和‘x.tab.h’。如果文件foo依靠文件‘parse.tab.o’和‘scan.o’,而文件‘scan.o’又依靠文件‘parse.tab.h’,當‘parse.y’發生變化,命令‘bison -d parse.y’執行一次。‘parse.tab.o’和‘scan.o’的依賴也隨之更新。(假設文件‘parse.tab.o’由文件‘parse.tab.c’編譯生成,文件‘scan.o’由文件‘scan.c’生成,當連接‘parse.tab.o’、‘scan.o’和其它依賴生成文件foo時,上述規則能夠很好執行。)

10.5.3自動變量

假設您編寫一個編譯‘.c’文件生成‘.o’文件的規則:您怎樣編寫命令‘CC’,使它能夠操作正確的文件名?您當然不能將文件名直接寫進命令中,因爲每次使用隱含規則操作的文件名都不一樣。

您應該使用make的另一個特點,自動變量。這些變量在規則每次執行時都基於目標和依賴產生新值。例如您可以使用變量‘$@’代替目標文件名,變量‘$<’代替依賴文件名。

下面是自動變量列表:

$@

規則的目標文件名。如果目標是一個檔案成員,則變量‘$@ 檔案文件的文件名。對於有多個目標的格式規則(參閱格式規則簡介),變量‘$@是那個導致規則命令運行的目標文件名。

$%

當目標是檔案成員時,該變量是目標成員名,參閱使用make更新檔案文件。例如,如果目標是foo.a(bar.o)',則$%'的值是bar.o'$@'的值是foo.a'。如果目標不是檔案成員,則$%'是空值。

$<

第一個依賴的文件名。如果目標更新命令來源於隱含規則,該變量的值是隱含規則添加的第一個依賴。參閱使用隱含規則

$?

所有比目標‘新’的依賴名,名字之間用空格隔開。對於爲檔案成員的依賴,只能使用已命名的成員。參閱使用make更新檔案文件

$^

所有依賴的名字,名字之間用空格隔開。對於爲檔案成員的依賴,只能使用已命名的成員。參閱使用make更新檔案文件。對同一個目標來說,一個文件只能作爲一個依賴,不管該文件的文件名在依賴列表中出現多少次。所以,如果在依賴列表中,同一個文件名出現多次,變量‘$^’的值仍然僅包含該文件名一次。

$+

該變量象$^',但是,超過一次列出的依賴將按照它們在makefile文件中出現的次序複製。這主要的用途是對於在按照特定順序重複庫文件名很有意義的地方使用連接命令。

$*

和隱含規則匹配的stem(),參閱格式匹配。如果一個目標爲dir/a.foo.b',目標格式規則爲:a.%.b' ,則stemdir/foo'。在構建相關文件名時stem 十分有用。在靜態格式規則中,stem是匹配目標格式中字符‘%’的文件名中那一部分。在一個沒有stem具體規則中;變量$*' 不能以該方法設置。如果目標名以一種推薦的後綴結尾(參閱過時的後綴規則),變量$*'設置爲目標去掉該後綴後的部分。例如,如果目標名是foo.c',則變量$*' 設置爲foo', 因爲.c' 是一個後綴。GNU make 處理這樣奇怪的事情是爲了和其它版本的make兼容。在隱含規則和靜態格式規則以外,您應該儘量避免使用變量$*'在具體規則中如果目標名不以推薦的後綴結尾,則變量‘$*’在該規則中設置爲空值。

當您希望僅僅操作那些改變的依賴,變量‘$?' 即使在具體的規則中也很有用。例如,假設名爲‘lib’的檔案文件包含幾個OBJ文件的拷貝,則下面的規則僅將發生變化的OBJ文件拷貝到檔案文件:

lib: foo.o bar.o lose.o win.o

        ar r lib $?

在上面列舉的變量中,有四個變量的值是單個文件名。三個變量的值是文件名列表。這七個變量都有僅僅存放文件的路徑名或僅僅存放目錄下文件名的變體。變量的變體名是由變量名追加字母‘D’或‘F’構成。這些變體在GNU make中處於半廢狀態,原因是使用函數T dirnotdir 能夠得到相同的結果。參閱文件名函數。注意,F'變體省略所有在dir函數中總是輸出的結尾斜槓這裏是這些變體的列表:

`$(@D)'

目標文件名中的路徑部分,結尾斜槓已經移走。如果變量`$@'的值`dir/foo.o',變體 `$(@D)'的值`dir'。 如果變量`$@'的值不包含斜槓,則變體的值是`.'

`$(@F)'

目標文件名中的真正文件名部分。如果變量`$@'的值`dir/foo.o',變體  `$(@F)'的值` foo.o '`$(@F)' 等同於 `$(notdir $@)'

`$(*D)'

`$(*F)'

stem(徑)中的路徑名和文件名;在這個例子中它們的值分別爲:`dir' `foo'

`$(%D)'

`$(%F)'

檔案成員名中的路徑名和文件名;這僅對採用‘archive(member)’形式的檔案成員目標有意義,並且當成員包含路徑名時纔有用。參閱檔案成員目標

`$(<D)'

`$(<F)'

第一個依賴名中的路徑名和文件名。

`$(^D)'

`$(^F)'

所有依賴名中的路徑名和文件名列表。

`$(?D)'

`$(?F)'

所有比目標‘新’的依賴名中的路徑名和文件名列表。

注意,在我們討論自動變量時,我們使用了特殊格式的慣例;我們寫"the value of$<'", 而不是"the variable <" ;和我們寫普通變量,例如變量 objects CFLAGS一樣。我們認爲這種慣例在這種情況下看起來更加自然。這並沒有其它意義,變量$<'的變量名爲 < 和變量$(CFLAGS)' 實際變量名爲CFLAGS一樣。您也可以使用$(<)'代替$<'

10.5.4格式匹配

目標格式是由前綴、後綴和它們之間的通配符%組成,它們中的任一個或兩個都可以是空值。格式匹配一個文件名只有該文件名是以前綴開始,後綴結束,而且兩者不重疊的條件下,纔算匹配。前綴、後綴之間的文本成爲徑(stem)。當格式‘%.o’匹配文件名‘test.o’時,徑(stem)是‘test’。格式規則中的依賴將徑(stem)替換字符%,從而得出文件名。對於上例中,如果一個依賴爲‘%.c’,則可擴展爲‘test.c’。

當目標格式中不包含斜槓(實際並不是這樣),則文件名中的路徑名首先被去除,然後,將其和格式中的前綴和後綴相比較。在比較之後,以斜槓結尾的路徑名,將會加在根據格式規則的依賴規則產生的依賴前面。只有在尋找隱含規則時路徑名才被忽略,在應用時路徑名絕不能忽略。例如,‘e%t’和文件名‘src/eat’匹配,stem()是‘src/a’。當依賴轉化爲文件名時,stem中的路徑名將加在前面,stem()的其餘部分替換‘%’。使用stem(徑) ‘src/a’和依賴格式規則‘c%r’匹配得到文件名‘src/car’。

10.5.5萬用規則

一個格式規則的目標僅僅包含‘%’,它可以匹配任何文件名,我們稱這些規則爲萬用規則。它們非常有用,但是make使用它們的耗時也很多,因爲make必須爲作爲目標和作爲依賴列出的每一個文件都考慮這樣的規則。

假設makefile文件提及了文件foo.c。爲了創建該目標,make將考慮是通過連接一個OBJ文件‘foo.c.o’創建,或是通過使用一步的C編譯連接程序從文件foo.c.c創建,或是編譯連接Pascal程序foo.c.p創建,以及其它的可能性等。

我們知道make考慮的這些可能性是很可笑的,因爲foo.c就是一個C語言源程序,不是一個可執行程序。如果make考慮這些可能性,它將因爲這些文件諸如foo.c.ofoo.c.p等都不存在最終拒絕它們。但是這些可能性太多,所以導致make的運行速度極慢。

爲了加快速度,我們爲make考慮匹配萬用規則的方式設置了限制。有兩種不同類型的可以應用的限制,在您每次定義一個萬用規則時,您必須爲您定義的規則在這兩種類型中選擇一種。

一種選擇是標誌該萬用規則是最終規則,即在定義時使用雙冒號定義。一個規則爲最終規則時,只有在它的依賴存在時才能應用,即使依賴可以由隱含規則創建也不行。換句話說,在最終規則中沒有進一步的鏈。

例如,從RCSSCCS文件中抽取原文件的內建的隱含規則是最終規則,則如果文件foo.c,v' 不存在,make絕不會試圖從一箇中間文件‘foo.c,v.o’或‘RCS/SCCS/s.foo.c,v’在創建它。 RCS SCCS 文件一般都是最終源文件,它不能從其它任何文件重新創建,所以,make可以記錄時間戳,但不尋找重建它們的方式。

如果您不將萬用規則標誌爲最終規則,那麼它就是非最終規則。一個非最終萬用規則不能用於指定特殊類型數據的文件。如果存在其它規則(非萬用規則)的目標匹配一文件名,則該文件名就是指定特殊類型數據的文件名。

例如,文件名foo.c' 和格式規則 `%.c : %.y' (該規則運行Yacc)。無論該規則是否實際使用(如果碰巧存在文件foo.y’,該規則將運行),和目標匹配的事實就能足夠阻止任何非最終萬用規則在文件foo.c上使用。這樣,make 考慮就不試圖從文件foo.c.o',foo.c.c', foo.c.p'等創建可執行的foo.c'

內建的特殊僞格式規則是用來認定一些特定的文件名,處理這些文件名的文件時不能使用非最終萬用規則。這些僞格式規則沒有依賴和命令,它們用於其它目的時被忽略。例如,內建的隱含規則:

%.p :

存在可以保證Pascal源程序如‘foo.p' 匹配特定的目標格式,從而阻止浪費時間尋找‘foo.p.o' 或‘foo.p.c'

在後綴規則中,爲後綴列表中的每一個有效後綴都創建了僞格式規則,如‘%.p' 。參閱過時的後綴規則

10.5.6刪除隱含規則

通過定義新的具有相同目標和依賴但不同命令的規則,您可以重載內建的隱含規則(或重載您自己定義的規則)。一旦定義新的規則,內建的規則就被代替。 新規則在隱含規則次序表中的位置由您編寫規則的地方決定。

通過定義新的具有相同目標和依賴但不含命令的規則,您可以刪除內建的隱含規則。例如,下面的定義規則將刪除運行彙編編譯器的隱含規則:

%.o : %.s

10.6 定義最新類型的缺省規則

您通過編寫不含依賴的最終萬用格式規則,您可以定義最新類型的缺省規則。參閱萬用規則。這和其它規則基本一樣,特別之處在於它可以匹配任何目標。因此,這樣的規則的命令可用於所有沒有自己的命令的目標和依賴,以及用於那些沒有其它隱含規則可以應用的目標和依賴。

例如,在測試makefile時,您可能不關心源文件是否含有真實數據,僅僅關心它們是否存在。那麼,您可以這樣做:

%::

        touch $@

這導致所有必需的源文件(作爲依賴)都自動創建。

您可以爲沒有規則的目標以及那些沒有具體指定命令的目標定義命令。要完成上述任務,您需要爲特殊目標.DEFAULT 編寫規則。這樣的規則可以在所有具體規則中用於沒有作爲目標出現以及不能使用隱含規則的依賴。自然,如果您不編寫定義則沒有特殊目標.DEFAULT 的規則。

如果您使用特殊目標.DEFAULT 而不帶任何規則和命令:

.DEFAULT:

則以前爲目標.DEFAULT定義的命令被清除。如此make的行爲和您從來沒有定義目標.DEFAULT一樣。

如果您不需要一個目標從萬用規則和目標.DEFAULT 中得到命令,也不想爲該目標執行任何命令,您可以在定義時使用空命令。參閱使用空命令

您可以使用最新類型規則重載另外一個makefile文件的一部分內容。參閱重載其它makefile文件

10.7 過時的後綴規則

後綴規則是定義隱含規則的過時方法。後綴規則因爲格式規則更爲普遍和簡潔而被廢棄。它們在GNU make中得到支持是爲了和早期的makefile文件兼容。它們分爲單後綴和雙後綴規則。

雙後綴規則被一對後綴定義:目標後綴和源文件後綴。它可以匹配任何文件名以目標後綴結尾的文件。相應的隱含依賴通過在文件名中將目標後綴替換爲源文件後綴得到。一個目標和源文件後綴分別爲‘.o’和‘.c’雙後綴規則相當於格式規則`%.o : %.c'

單後綴規則被單後綴定義,該後綴是源文件的後綴。它匹配任何文件名,其相應的依賴名是將文件名添加源文件後綴得到。源文件後綴爲‘.c’的單後綴規則相當於格式規則‘% : %.c’。

通過比較規則目標和定義的已知後綴列表識別後追規則。當make見到一個目標後綴是已知後綴的規則時,該規則被認爲是一個單後綴規則。當make見到一個目標後綴包含兩個已知後綴的規則時,該規則被認爲是一個雙後綴規則。

例如,‘.o’和‘.c’都是缺省列表中的已知後綴。所以,如果您定義一個規則,其目標是‘.c.o’,則make認爲是一個雙後綴規則,源文件後綴是‘.c’,目標後綴是‘.o’。這裏有一個採用過時的方法定義編譯C語言程序的規則:

.c.o:

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

後綴規則不能有任何屬於它們自己的依賴。如果它們有依賴,它們將不是作爲後綴規則使用,而是以令人啼笑皆非的方式處理正常的文件。例如,規則:

.c.o: foo.h

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

告訴從依賴foo.h生成文件名爲‘.c.o’的文件,並不是象格式規則:

%.o: %.c foo.h

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

告訴從.c'文件生成 .o' 文件.c'的方法:創建所有.o' 文件使用該格式規則,而且同時使用依賴文件foo.h'

沒有命令的後綴規則也沒有意義。它們並不沒有命令的格式規則那樣移去以前的規則(參閱刪除隱含規則)。他們僅僅簡單的在數據庫中加入後綴或雙後綴作爲一個目標。

已知的後綴是特殊目標‘.SUFFIXES’簡單的依賴名。通過爲特殊目標‘.SUFFIXES’編寫規則加入更多的依賴,您可以添加您自己的已知後綴。例如:

.SUFFIXES: .hack .win

.hack' .win'添加到了後綴列表中。

如果您希望排除缺省的已知後綴而不是僅僅的添加後綴,那麼您可以爲特殊目標‘.SUFFIXES’編寫沒有依賴的規則。通過這種方式,可以完全排除特殊目標‘.SUFFIXES’存在的依賴。接着您可以編寫另外一個規則添加您要添加的後綴。例如,

.SUFFIXES:            # 刪除缺省後綴

.SUFFIXES: .c .o .h   # 定義自己的後綴列表

標誌-r'--no-builtin-rules'也能把缺省的後綴列表清空。

變量SUFFIXESmake讀入任何makefile文件之前定義缺省的後綴列表。您可以使用特殊目標‘.SUFFIXES’改變後綴列表,但這不能改變變量SUFFIXES的值。

10.8隱含規則搜尋算法

這裏是make爲一個目標‘t’搜尋隱含規則的過程。這個過程用於任何沒有命令的雙冒號規則,用於任何不含命令的普通規則的目標,以及用於任何不是其它規則目標的依賴。這個過程也能用於來自隱含規則的依賴遞歸調用該過程搜尋規則鏈。

在本算法中不提及任何後綴規則,因爲後綴規則在makefile文件讀入時轉化爲了格式規則。

對於個是‘archive(member)’的檔案成員目標,下述算法重複兩次,第一次使用整個目標名‘t’,如果第一次運行沒有發現規則,則第二次使用‘(member)’作爲目標‘t’。

1、            1            在‘t’中分離出路徑部分,稱爲‘d’,剩下部分稱爲‘n’。例如如果‘t’是‘src/foo.o’,那麼‘d’是‘src/’;‘n’是‘foo.o’。

2、            2            建立所有目標名匹配‘t’和‘n’的格式規則列表。如果目標格式中含有斜槓,則匹配‘t’,否則,匹配‘n’。

3、            3            如果列表中有一個規則不是萬用規則,則從列表中刪除所有非最終萬用規則。

4、            4            將沒有命令的規則也從列表中移走。

5、            5            對每個列表中的格式規則:

1、  1  尋找stems’,也就是和目標格式中%匹配的‘t’或‘n’部分。

2、  2  使用stems’計算依賴名。如果目標格式不包含斜槓,則將‘d’添加在每個依賴的前面。

3、  3  測試所有的依賴是否存在或能夠創建。(如果任何文件在makefile中作爲目標或依賴被提及,則我們說它應該存在。)如果所有依賴存在或能夠創建,或沒有依賴,則可使用該規則。

6、            6            如果到現在還沒有發現能使用的規則,進一步試。對每一個列表中的規則:

1、  1  如果規則是最終規則,則忽略它,繼續下一條規則。

2、  2  象上述一樣計算依賴名。

3、  3  測試所有的依賴是否存在或能夠創建。

4、  4  對於不存在的依賴,按照該算法遞歸調用查找是否能夠採用隱含規則創建。

5、  5  如果所有依賴存在或能使用隱含規則創建,則應用該規則。

7、            7            如果沒有隱含規則,則如有用於目標‘.DEFAULT’規則,則應用該規則。在這種情況下,將目標‘.DEFAULT’的命令給與‘t’。

一旦找到可以應用的規則,對每一個匹配的目標格式(無論是‘t’或‘n’)使用stems’替換%,將得到的文件名儲存起來直到執行命令更新目標文件‘t’。在這些命令執行以後,把每一個儲存的文件名放入數據庫,並且標誌已經更新,其時間戳和目標文件‘t’一樣。

如果格式規則的命令爲創建‘t’執行,自動變量將設置爲相應的目標和依賴(參閱自動變量)。

11使用make更新檔案文件

檔案文件是包含子文件的文件,這些子文件有各自的文件名,一般將它們稱爲成員;檔案文件和程序ar一塊被提及,它們的主要用途是作爲連接的例程庫。

11.1檔案成員目標

獨立的檔案文件成員可以在make中用作目標或依賴。按照下面的方式,您可以在檔案文件‘archive’中指定名爲‘member’的成員:

archive(member)

這種結構僅僅在目標和依賴中使用,絕不能在命令中應用!絕大多數程序都不在命令中支持這個語法,而且也不能對檔案成員直接操作。只有程序ar和那些爲操作檔案文件設計的程序才能這樣做。所以合法的更新檔案成員的命令一定使用ar。例如,下述規則表明藉助拷貝文件‘hack.o’在檔案‘foolib’中創建成員‘hack.o:

foolib(hack.o) : hack.o

        ar cr foolib hack.o

實際上,幾乎所有的檔案成員目標是採用這種方式更新的,並且有一條隱含規則爲您專門更新檔案成員目標。注意:如果檔案文件沒有直接存在,程序ar的‘c’標誌是需要的。

在相同的檔案中同時指定幾個成員,您可以在圓括號中一起寫出所有的成員名。例如:

foolib(hack.o kludge.o)

等同於:

foolib(hack.o) foolib(kludge.o)

您還可以在檔案成員引用中使用shell類型的通配符。參閱在文件名中使用通配符。例如,‘foolib(*.o)' 擴展爲在檔案‘foolib’中所有存在以‘.o’結尾的成員。也許相當於:foolib(hack.o) foolib(kludge.o)'

11.2 檔案成員目標的隱含規則

對目標‘a(m)’表示名爲‘m’的成員在檔案文件‘a’中。

Make爲這種目標搜尋隱含規則時,是用它另外一個的特殊特點:make認爲匹配‘(m)’的隱含規則也同時匹配‘a(m)’。

該特點導致一個特殊的規則,它的目標是‘(%)’。該規則通過將文件‘m’拷貝到檔案中更新目標‘a(m)’。例如,它通過將文件‘bar.o’拷貝到檔案‘foo.a’中更新檔案成員目標‘foo.a(bar.o)’。

如果該規則和其它規則組成鏈,功能十分強大。‘make "foo.a(bar.o)"'(注意使用雙引號是爲了保護圓括號可被shell解釋)即使沒有makefile文件僅存在文件‘bar.c’就可以保證以下命令執行:

cc -c bar.c -o bar.o

ar r foo.a bar.o

rm -f bar.o

這裏make假設文件‘bar.o’是中間文件。參閱隱含規則鏈

諸如這樣的隱含規則是使用自動變量‘$%’編寫的,參閱自動變量

檔案成員名不能包含路徑名,但是在makefile文件中路徑名是有用的。如果您寫一個檔案成員規則‘foo.a(dir/file.o)’,make將自動使用下述命令更新:

ar r foo.a dir/file.o

它的結果是拷貝文件‘dir/file.o’進入名爲‘file.a’的檔案中。在完成這樣的任務時使用自動變量%D%F

11.2.1更新檔案的符號索引表

用作庫的檔案文件通常包含一個名爲‘__.SYMDEF’ 特殊的成員,成員‘__.SYMDEF’包含由所有其它成員定義的外部符號名的索引表。在您更新其它成員後,您必須更新成員‘__.SYMDEF’,從而使成員‘__.SYMDEF’可以合適的總結其它成員。要完成成員‘__.SYMDEF’的更新需要運行ranlib程序:

ranlib archivefile

正常情況下,您應該將該命令放到檔案文件的規則中,把所有檔案文件的成員作爲該規則的依賴。例如:

libfoo.a: libfoo.a(x.o) libfoo.a(y.o) ...

        ranlib libfoo.a

上述程序的結果是更新檔案成員x.o',y.o', 等等, 然後通過運行程序ranlib更新符號索引表表成員__.SYMDEF’。更新成員的規則這裏沒有列出,多數情況下,您可以省略它們,使用隱含規則把文件拷貝到檔案中,具體描述見以前的內容。

使用GNU ar程序時這不是必要的,因爲它自動更新成員__.SYMDEF

11.3 使用檔案的危險

同時使用並行執行(-j開關,參閱並行執行)和檔案應該十分小心。如果多個命令同時對相同的檔案文件操作,它們相互不知道,有可能破壞文件。將來的make版本可能針對該問題提供一個機制,即將所有操作相同檔案文件的命令串行化。但是現在,您必須在編寫您自己的makefile文件時避免該問題,或者採用其它方式,或者不使用選項-j

11.4 檔案文件的後綴規則

爲處理檔案文件,您可以編寫一個特殊類型的後綴規則。關於所有後綴的擴展請參閱過時的後綴規則。檔案後綴規則在GNU make中已被廢棄,因爲用於檔案的格式規則更加通用(參閱檔案成員目標的隱含規則),但是爲了和其它版本的make兼容,它們仍然被保留。

編寫用於檔案的後綴規則,您可以簡單的編寫一個用於目標後綴‘.a’的後綴規則即可。例如,這裏有一個用於從C語言源文件更新檔案庫的過時後綴規則:

.c.a:

        $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o

        $(AR) r $@ $*.o

        $(RM) $*.o

這和下面的格式規則工作完全一樣:

(%.o): %.c

        $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o

        $(AR) r $@ $*.o

        $(RM) $*.o

實際上,這僅僅是make看到一個以‘.a’作爲後綴的後綴規則時,它所做的工作。任何雙後綴規則‘.x.a' 被轉化爲一個格式規則,該格式規則的目標格式是‘(%.o)' ,依賴格式是‘%.x'.

因爲您可能要使用‘.a' 作爲一個文件類型的後綴,make也以正常方式轉換檔案後綴規則爲格式規則,參閱過時的後綴規則這樣一個雙後綴規則‘.x.a' 產生兩個格式規則:‘(%.o): %.x' 和‘%.a: %.x'.

12 GNU make的特點

這裏是GNU make的特點的總結,用於比較其它版本的make。我們以4.2 BSD 中的make的特點爲基準。如果您要編寫一個可移植的makefile文件,您不要使用這裏列出的make的特點,也不要使用不兼容性和失去的特點中列出的內容。

許多特點在System V 中的make也存在。

  • 變量VPATH 以及它特殊的意義。參閱在目錄中搜尋依賴。這個特點存在於System V 中的make,但沒有事實證明。4.3 BSD make也含有該特點(據說是模仿System V中變量VPATFH的特點)

  • 包含其它makefile文件。參閱包含其它makefile文件允許使用一個指令包含多個文件是GNU的擴展。

  • 通過環境,變量可以讀入和通訊,參閱環境變量

  • 通過變量MAKEFLAGS 在遞歸調用make時可以傳遞選項。參閱和子make通訊選項

  • 在檔案引用中自動變量$% 設置爲成員名。參閱自動變量

  • 自動變量$@, $*, $<, $%, $? 有變體形式如$(@F)$(@D)。我們把此概念化,並使用它對自動變量$^ 進行了明顯擴展。參閱自動變量

  • 變量引用。參閱變量引用基礎

  • 命令行選項-b'-m',接受和忽略。在System V make中,這些選項實際起作用。

  • 即使指定選項-n',-q'或‘-t',也能通過變量MAKE執行地歸調用make的命令。參閱遞歸調用make

  • 在後綴規則中支持後綴.a'。參閱用於檔案文件的後綴規則。這個特點在GNU make中幾乎不用,因爲規則鏈更加通用的特點(參閱隱含規則鏈)允許一個格式規則用於在檔案中安裝成員已經足夠(參閱用於檔案成員目標的隱含規則)。

  • 在命令中行排列和反斜槓-新行結合依舊保留,當命令打印時,它們出現的格式和它們在makefile文件中基本一樣,不同之處是去掉了初始化空白。

下面的特點被各種不同版本的make吸收,但哪些版本吸收了哪些特點並不十分清楚。

  • 在格式規則中使用‘%’。已經有幾個不同版本的make使用了該特點。我們不能確認是誰發明了它,但它發展很快。參閱定義與重新定義格式規則

  • 規則鏈以及隱含中間文件。這個特點首先由Stu Feldman 在它的make版本中實現,並用於AT&T 第八版Unix研究中。後來AT&T貝爾實驗室的Andrew Hume 在它的mk程序中應用(這裏稱爲“傳遞閉合”)。我們並不清楚是從他們那裏得到這個特點或是同時我們自己開發出來的。參閱隱含規則鏈

  • 自動變量包含當前目標的所有依賴的列表。我們一點也不知道是誰做的。參閱自動變量。自動變量$+ 是變量$^的簡單擴展。

  • "what if" 標誌(GNU make中的-W') Andrew Hume mk中發明的。參閱代替執行命令

  • 並行執行的概念在許多版本的make中存在,儘管System V BSD 並沒有實現。參閱執行命令

  • 使用格式替換改變變量引用來自於SunOS 4。參閱變量引用基礎。在GNU make中,這個功能在變換語法和SunOS 4兼容之前由函數patsubst提供。不知道誰是權威,因爲GNU make 使用函數 patsubst SunOS 4 發佈之前。

  • 在命令行前面的‘+’字符有特殊重要的意義(參閱代替執行命令)。這是由IEEE Standard 1003.2-1992 (POSIX.2)定義的。

  • 使用+=語法爲變量追加值來自於SunOS 4 make。參閱爲變量值追加文本

  • 語法archive(mem1 mem2...)'在單一檔案文件中列舉多個成員來自於SunOS 4 make.。參閱檔案成員目標

  • -include指令包括makefile文件,並且對於不存在的文件也不產生錯誤。該特點with來自於SunOS 4 make(但是SunOS 4 make 在單個指令中指定多個makefile文件。) 該特點和SGI make sinclude 相同,

剩餘的特點是由GNU make發明的:

  • 使用‘-v'`--version'選項打印版本和拷貝權信息。

  • 使用‘-h' 或‘--help' 選項總結make的選項。

  • 簡單擴展型變量。參閱變量的兩特特色

  • 在遞歸調用make時,通過變量MAKE自動傳遞命令行變量。參閱遞歸調用make

  • 使用命令選項‘-C' 或‘--directory'改變路徑。參閱選項概要

  • 定義多行變量。參閱定義多行變量

  • 使用特殊目標.PHONY聲明假想目標。AT&T 貝爾實驗室Andrew Hume 使用不同的語法在它的mk程序中也實現了該功能。這似乎是並行的發現。參閱假想目標

  • 調用函數操作文本。參閱用於轉換文本的函數

  • 使用‘-o'或‘--old-file'選項假裝文件是舊文件。參閱避免重新編譯文件

  • 條件執行。該特點已在不同版本make中已經實現很長時間了;它似乎是C與處理程序和類似的宏語言的自然擴展,而不是革命性的概念。參閱makefile文件中的條件語句

  • 指定包含的makefile文件的搜尋路徑。參閱包含其它makefile文件

  • 使用環境變量指定額外的makefile文件。參閱變量MAKEFILES

  • 從文件名中去除前導斜槓`./' ,因此,./file' file' 是指同一個文件。

  • 使用特別搜尋方法搜尋形式如‘-lname’的庫依賴。參閱連接庫搜尋目錄

  • 允許後綴規則中的後綴包含任何字符(參閱過時的後綴規則)。在其它版本的make中後綴必須以‘.’開始,並且不能包含‘/’字符。

  • 包吹跟蹤當前make級別適用的變量MAKWFILES的值,參閱遞歸調用make

  • 將任何在命令行中給出的目標放入變量MAKECMDGOALS。參閱指定最終目標的參數

  • 指定靜態格式規則。參閱靜態格式規則

  • 提供選擇性vpath搜尋。參閱在目錄中搜尋依賴

  • 提供可計算的變量引用。參閱量引用基礎

  • 更新makefile文件。參閱重建makefile文件。System V make 中有非常非常有限的來自於該功能的形式,它用於爲make檢查SCCS文件。

  • 各種新建的隱含規則。參閱隱含規則目錄

  • 內建變量`MAKE_VERSION' 給出make的版本號。

13 不兼容性和失去的特點

其它版本的make程序也有部分特點在GNU make中沒有實現。POSIX.2 標準 (IEEE Standard 1003.2-1992)規定不需要這些特點。

  • file((entry))' 形式的目標代表一個檔案文件的成員file。選擇該成員不使用文件名,而是通過一個定義連接符號entyOBJ文件。該特點沒有被GNU make 吸收因爲該非標準組件將爲make加入檔案文件符號表的內部知識。參閱更新檔案符號索引表

  • 在後綴規則中以字符‘~’結尾的後綴在System V make中有特別的含義;它們指和文件名中沒有‘~’的文件通訊的SCCS 文件。例如,後綴規則‘.c~.o'將從名爲s.n.c'SCCS文件中抽取文件n.o'。爲了完全覆蓋,需要這種整系列的後綴規則,參閱過時的後綴規則。在GNU make中,這種整系列的後綴規則由勇於從SCCS文件抽取的兩個格式規則掌管,它們可和通用的規則結合成規則鏈,參閱隱含規則鏈

  • System V make, 字符串‘$$@'又奇特的含義,在含有多個規則的依賴中,它代表正在處理的特殊目標。這在GNU make沒有定義,因爲字符串$$'代表一個平常的字符‘$'。使用靜態格式規則可以實現該功能的一部分(參閱靜態格式規則)。System V make 中的規則:

$(targets): [email protected] lib.a

GNU make 中可以用靜態格式規則代替:

$(targets): %: %.o lib.a

  • System V 4.3 BSD make中, 通過VPATH搜尋(參閱爲依賴搜尋目錄發現的文件,它們的文件名改變後加入到命令字符串中。我們認爲使用自動變量更簡單明瞭,所以不引進該特點。

  • 在一些Unix make中,自動變量$*出現在規則的依賴中有令人驚奇的特殊特點:擴展爲該規則的目標全名。我們不能明白Unix make 在心中對這是怎樣考慮的,它和正常的變量$*定義完全不同。

  • 在一些Unix make中,隱含規則搜尋(參閱使用隱含規則)明顯是爲所有目標做的,而不僅僅爲那些沒有命令的目標。這意味着:

foo.o:

       cc -c foo.c

Unix make 有直覺知道foo.o' 依靠foo.c'。我們認爲這樣的用法易導致混亂。Make中依賴的屬性已經定義好(至少對於GNU make是這樣),再做這樣的事情不合規矩。

  • GNU make不包含任何編譯以及與處理EFL程序的隱含規則。如果我們聽說誰使用EFL,我們樂意把它們加入。

  • SVR4 make中,一條後綴規則可以不含命令,它的處理方式和它含有空命令的處理方式一樣(參閱使用空命令)。例如:

.c.a:

將重載內建的後綴規則.c.a' 。我們覺得對沒有命令的規則簡單的爲目標添加依賴更爲簡潔。上述例子和在GNU make中下例的行爲相同。

.c.a: ;

  • 一些版本的make 調用shell使用‘-e'標誌,而不是‘-k'標誌 (參閱測試程序編譯)。標誌‘-e'告訴shell 一旦程序運行返回非零狀態就立即退出。我們認爲根據每一命令行是否需要需要特殊處理直接寫入命令中更爲清楚。

14 makefile文件慣例

本章描述爲GNU make編寫makefile文件的慣例。使用Automake將幫助您按照這些慣例編寫makefile文件。

14.1 makefile文件的通用慣例

任何makefile文件都應該包含這行:

SHELL = /bin/sh

避免在系統中變量SHELL可能繼承環境中值的麻煩。(在GNU make中這從來不是問題。)

不同的make程序有不同的後綴列表和隱含規則,這有可能造成混亂或錯誤的行爲。因此最好的辦法是設置後綴列表,在該列表中,僅僅包含您在特定makefile文件中使用的後綴。例如:

.SUFFIXES:

.SUFFIXES: .c .o

第一行清除了後綴列表,第二行定義了在該makefile中可能被隱含規則使用的後綴。

不要假設‘.' 是命令執行的路徑。當您在創建程序過程中,需要運行僅是您程序包中一部分的程序時,請確認如果該程序是要創建程序的一部分使用‘./’,如果該程序是源代碼中不變的部分使用‘$(srcdir)’。沒有這些前綴,僅僅在當前路徑下搜索。

建造目錄(build directory )‘./’和源代碼目錄(source directory) $(srcdir)’的區別是很重要的,因爲用戶可以在‘configure’中使用‘--srcdir’選項建造一個單獨的目錄。下面的規則:

foo.1 : foo.man sedscript

        sed -e sedscript foo.man > foo.1

如果創建的目錄不是源代碼目錄將失敗,因爲文件‘foo.man’和‘sedscript’在源代碼目錄下。

在使用GNU make時,依靠變量‘VPATH’搜尋源文件在單個從屬性文件存在情況下可以很好地工作,因爲make中自動變量‘$<’中含有源文件的存在路徑。(許多版本的make僅在隱含規則中設值變量‘$<’。)例如這樣的makefile文件目標:

foo.o : bar.c

        $(CC) -I. -I$(srcdir) $(CFLAGS) -c bar.c -o foo.o

將被替換爲:

foo.o : bar.c

        $(CC) -I. -I$(srcdir) $(CFLAGS) -c $< -o $@

這是爲了保證變量‘VPATH’正確的工作。目標含有多個依賴時,使用名了的‘$(srcdir)’是最容易的保證該規則很好工作的方法。例如,以上例子中的目標‘foo.l’最好寫爲:

foo.1 : foo.man sedscript

        sed -e $(srcdir)/sedscript $(srcdir)/foo.man > $@

GNU的分類中通常包含一些不是源文件的文件——例如,‘Info’文件、從Autoconf, Automake, Bison Flex中輸出的文件等。這些文件在源文件目錄下,它們也應該在源文件目錄下,不應該在建造目錄下。因此makefile規則應在源文件目錄下更新它們。

然而,如果一個文件沒有在分類中出現,makefile文件不應把它們放到源文件目錄下,因爲按照通常情況創建一個程序,不應該以任何方式更改源文件目錄。

試圖建造的創建和安裝目標,至少(以及它們的子目標)可在並行的make中正確的工作。

14.2 makefile文件的工具

編寫在shell sh中運行而不在csh中運行的makefile文件命令(以及shell的腳本,例如‘configure),不要使用任何kshbash的特殊特點。

用於創建和安裝的configure腳本和Makefile 規則除下面所列出工具外不應該直接使用其它的任何工具:

cat cmp cp diff echo egrep expr false grep install-info

ln ls mkdir mv pwd rm rmdir sed sleep sort tar test touch true

壓縮程序gzip可在dist規則中使用。

堅持使用用於這些程序的通用選項,例如,不要使用mkdir -p',它可能比較方便,但是其它大多數系統卻不支持它。

避免在makefile中創造符號連接是非常不錯的注意,因爲一些系統不支持這種做法。

用於創建和安裝的Makefile 規則可以使用編譯器以及相關的程序,但應該通過make變量使用它們,這樣可以方便用戶使用別的進行替換。這裏有按照我們的觀念編寫一些程序:

ar bison cc flex install ld ldconfig lex

make makeinfo ranlib texi2dvi yacc

請使用下述make變量運行這些程序:

$(AR) $(BISON) $(CC) $(FLEX) $(INSTALL) $(LD) $(LDCONFIG) $(LEX)

$(MAKE) $(MAKEINFO) $(RANLIB) $(TEXI2DVI) $(YACC)

使用ranlibldconfig,您應該確定如果系統中不存在要使用的程序不會引起任何副作用。安排忽略這些命令產生的錯誤,並且打印信息告訴用戶該命令運行失敗並不意味着存在問題。(AutoconfAC_PROG_RANLIB'宏可在這方面幫助您。)如果您使用符號連接,對於不支持符號連接的系統您應該有一個低效率運行方案。

附加的工具也可通過make變量使用:

chgrp chmod chown mknod

它在makefile中(或腳本中),您知道包含這些工具的特定系統中它都可以很好的工作。

14.3 指定命令的變量

Makefile文件應該爲重載的特定命令、選項等提供變量。

特別在您運行大部分工具時都應該應用變量,如果您要使用程序Bison, 名爲BISON 的變量它的缺省值設置爲:‘BISON = bison,在您需要使用程序Bison時,您可以使用$(BISON)引用。

文件管理器工具如ln, rm, mv等等,不必要使用這種方式引用,因爲用戶不可能使用別的程序替換它們。

每一個程序變量應該和用於向該程序提供選項的選項變量一起提供。在程序名變量後添加FLAGS'表示向該程序提供選項的選項變量--例如, BISONFLAGS(名爲CFLAGS的變量向C編譯器提供選項, 名爲YFLAGS的變量向yacc提供選項,名爲LFLAGS的變量向lex提供選項等是這個規則例外,但因爲它們是標準所以我們保留它們。) 在任何進行預處理的編譯命令中使用變量CPPFLAGS ,在任何進行連接的編譯命令中使用變量LDFLAGS 和直接使用程序ld一樣。

對於C編譯器在編譯特定文件時必須使用的選項,不應包含在變量CFLAGS中,因爲用戶希望他們能夠自由的指定變量CFLAGS。 要獨立於變量CFLAGS安排向C編譯器傳遞這些必要的選項, 可以將這些選項寫入編譯命令行中或隱含規則的定義中,如下例:

CFLAGS = -g

ALL_CFLAGS = -I. $(CFLAGS)

.c.o:

        $(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $<

變量CFLAGS中包括選項‘-g’,因爲它對於一些編譯並不是必需的,您可以認爲它是缺省推薦的選項。如果數據包創建使用GCC作爲編譯器,則變量CFLAGS中包括選項‘-o,而且以它爲缺省值。

將變量CFLAGS放到編譯命令的最後,在包含編譯選項其它變量的後邊,因此用戶可以使用變量CFLAGS對其它變量進行重載。

每次調用C編譯器都用到變量CFLAGS ,無論進行編譯或連接都一樣。

任何Makefile文件都定義變量INSTALL,變量INSTALL是將文件安裝到系統中的基本命令。

任何Makefile文件都定義變量INSTALL_PROGRAM INSTALL_DATA(它們的缺省值都是$(INSTALL)) 在實際安裝程序時,不論可執行程序或非可執行程序,一般都使用它們作爲命令。下面是使用這些變量的例子:

$(INSTALL_PROGRAM) foo $(bindir)/foo

$(INSTALL_DATA) libfoo.a $(libdir)/libfoo.a

您可以隨意將變量DESTDIR預先設置爲目標文件名。這樣做允許安裝程序創建隨後在實際目標文件系統中安裝文件的快照。不要再makefile文件中設置變量DESTDIR,也不要包含在安裝文件中。用變量DERSTDIR改變上述例子:

$(INSTALL_PROGRAM) foo $(DESTDIR)$(bindir)/foo

$(INSTALL_DATA) libfoo.a $(DESTDIR)$(libdir)/libfoo.a

在安裝命令中一般使用文件名而不是路徑名作爲第二個參數。對每一個安裝文件都使用單獨的命令。

14.4安裝路徑變量

安裝目錄經常以變量命名,所以在非標準地方安裝也很容易,這些變量的標準名字將在下面介紹。安裝目錄依據標準文件系統佈局,變量的變體已經在SVR4, 4.4BSD, Linux, Ultrix v4, 以及其它現代操作系統中使用。

以下兩個變量設置安裝文件的根目錄,所有的其它安裝目錄都是它們其中一個的子目錄,沒有任何文件可以直接安裝在這兩個根目錄下。

`prefix'

前綴是用於構造以下列舉變量的缺省值。變量prefix缺省值是/usr/local'。建造完整的GNU系統時,變量prefix的缺省值是空值,/usr' 是符號連接符/'(如果您使用Autoconf,應將它寫爲@prefix@')使用不同於創建程序時變量prefix的值運行make install',不會重新編譯程序。

`exec_prefix'

前綴是用於構造以下列舉變量的缺省值。變量exec_prefix缺省值是$(prefix). (如果您使用Autoconf,應將它寫爲`@exec_prefix@') 一般情況下。變量$(exec_prefix) 用於存放包含機器特定文件的目錄,(例如可執行文件和例程庫),變量$(prefix) 直接存放其它目錄。使用不同於創建程序時變量exec_prefix的值運行make install',不會重新編譯程序。

可執行程序安裝在以下目錄中:

`bindir'

這個目錄下用於安裝用戶可以運行的可執行程序。其正常的值是/usr/local/bin',但是使用時應將它寫爲$(exec_prefix)/bin' (如果您使用Autoconf, 應將它寫爲@bindir@')

`sbindir'

這個目錄下用於安裝從shell中調用執行的可執行程序。它僅僅對系統管理員有作用。它的正常的值是/usr/local/sbin',但是使用時應將它寫爲$(exec_prefix)/sbin' (如果您使用Autoconf, 應將它寫爲@sbindir@')

`libexecdir'

這個目錄下用於安裝其它程序調用的可執行程序。其正常的值是/usr/local/libexec',但是使用時應將它寫爲$(exec_prefix)/libexec'(如果您使用Autoconf, 應將它寫爲@libexecdir@')

程序執行時使用的數據文件可分爲兩類:

  • 程序可以正常更改的文件和不能正常更改的文件(雖然用戶可以編輯其中的一部分文件)。

  • 體系結構無關文件,指這些文件可被所有機器共享;體系相關文件,指僅僅可以被相同類型機器、操作系統共享的文件;其它是永遠不能被兩個機器共享的文件。

這可產生六種不同的可能性。我們極力反對使用體系相關的文件,當然OBJ文件和庫文件除外。使用其它體系無關的數據文件更加簡潔,並且,這樣做也不是很難。

所以,這裏有 Makefile變量用於指定路徑:

`datadir'

這個目錄下用於安裝只讀型體系無關數據文件。其正常的值是/usr/local/share'但是使用時應將它寫爲$(prefix)/share'(如果您使用Autoconf, 應將它寫爲@datadir@') 作爲例外,參閱下述的變量$(infodir)'$(includedir)'

`sysconfdir'

這個目錄下用於安裝從屬於單個機器的只讀數據文件,這些文件是:用於配置主機的文件。郵件服務、網絡配置文件,/etc/passwd',等等都屬於這裏的文件。所有該目錄下的文件都是平常的ASCII文本文件。其正常的值是/usr/local/etc', 但是使用時應將它寫爲$(prefix)/etc'. (如果您使用Autoconf, 應將它寫爲@sysconfdir@'.) 不要在這裏安裝可執行文件(它們可能屬於$(libexecdir)'$(sbindir)')。也不要在這裏安裝那些在使用時要更改的文件(這些程序用於改變系統拒絕的配置)。它們可能屬於$(localstatedir)'

`sharedstatedir'

這個目錄下用於安裝程序運行中要發生變化的體系無關數據文件。其正常的值是/usr/local/com',但是使用時應將它寫爲$(prefix)/com' (如果您使用Autoconf, 應將它寫爲@sharedstatedir@')

`localstatedir'

這個目錄下用於安裝程序運行中要發生變化的數據文件。但他們屬於特定的機器。用戶永遠不需要在該目錄下更改文件配置程序包選項;將這些配置信息放在分離的文件中,這些文件將放入$(datadir)'$(sysconfdir)'中,$(localstatedir)'正常的值是/usr/local/var'但是使用時應將它寫爲$(prefix)/var' (如果您使用Autoconf, 應將它寫爲@localstatedir@')

`libdir'

這個目錄下用於存放OBJ文件和庫的OBJ代碼。不要在這裏安裝可執行文件,它們可能應屬於$(libexecdir)'。變量libdir正常的值是/usr/local/lib',但是使用時應將它寫爲$(exec_prefix)/lib'(如果您使用Autoconf, 應將它寫爲 @libdir@')

`infodir'

這個目錄下用於安裝軟件包的 Info 文件。缺省情況下其值是/usr/local/info',但是使用時應將它寫爲$(prefix)/info'. (如果您使用Autoconf, 應將它寫爲@infodir@'.)

`lispdir'

這個目錄下用於安裝軟件包的Emacs Lisp 文件。缺省情況下其值是 /usr/local/share/emacs/site-lisp',但是使用時應將它寫爲$(prefix)/share/emacs/site-lisp'。如果您使用Autoconf, 應將它寫爲@lispdir@'。爲了保證@lispdir@'工作,您需要將以下幾行加入到您的configure.in'文件中:

lispdir='${datadir}/emacs/site-lisp'

AC_SUBST(lispdir)

`includedir'

這個目錄下用於安裝用戶程序中C#include'預處理指令包含的頭文件。其正常的值是/usr/local/include',但是使用時應將它寫爲$(prefix)/include' (如果您使用Autoconf, 應將它寫爲@includedir@') GCC外的大多數編譯器不在目錄/usr/local/include'搜尋頭文件,因此這種安裝方式僅僅適用於GCC。有時,這也不是問題,因爲一部分庫文件僅僅依靠GCC才能工作。但也有一部分庫文件依靠其他編譯器,它們將它們的頭文件安裝到兩個地方,一個由變量 includedir 指定,另一個由變量oldincludedir指定。

`oldincludedir'

這個目錄下用於安裝#include'的頭文件,這些頭文件用於GCC外的其它C語言編譯器。其正常的值是/usr/include'(如果您使用Autoconf, 應將它寫爲 @oldincludedir@') Makefile命令變量oldincludedir 的值是否爲空,如果是空值,它們不在試圖使用它,它們還刪除第二次安裝的頭文件。一個軟件包在該目錄下替換已經存在的頭文件,除非頭文件來源於同一個軟件包。例如,如果您的軟件包Foo 提供一個頭文件foo.h',則它在變量oldincludedir指定的目錄下安裝的條件是 (1) 這裏沒有投文件foo.h' (2) 來源於軟件包Foo的頭文件foo.h'已經在該目錄下存在。要檢查頭文件foo.h'是否來自於軟件包Foo,將一個magic字符串放到文件中--作爲命令的一部分--然後使用正則規則(grep)查找該字符串。

Unix風格的幫助文件安裝在以下目錄中:

`mandir'

安裝該軟件包的頂層幫助(如果有)目錄。其正常的值是/usr/local/man',但是使用時應將它寫爲$(prefix)/man' (如果您使用Autoconf, 應將它寫爲@mandir@')

`man1dir'

這個目錄下用於安裝第一層幫助。其正常的值是$(mandir)/man1'

`man2dir'

這個目錄下用於安裝第一層幫助。其正常的值是$(mandir)/man2'

`...'

不要將任何GNU 軟件的主要文檔作爲幫助頁。應該編寫使用手冊。幫助頁僅僅是爲了人們在Unix上方便運行GNU軟件,它是附屬的運行程序。

`manext'

文件名錶示對已安裝的幫助頁的擴展。它包含一定的週期,後跟適當的數字,正常爲‘1’。

`man1ext'

文件名錶示對已安裝的幫助頁第一部分的擴展。

`man2ext'

文件名錶示對已安裝的幫助頁第二部分的擴展。

`...'

使用這些文件名代替`manext'。如果該軟件包的幫助頁需要安裝使用手冊的多個章節。

最後您應該設置一下變量:

`srcdir'

這個目錄下用於安裝要編譯的原文件。該變量正常的值由shell腳本configure插入。(如果您使用Autoconf, 應將它寫爲srcdir = @srcdir@'.)

例如:

# 用於安裝路徑的普通前綴。

# 注意:該路經在您開始安裝時必須存在

prefix = /usr/local

exec_prefix = $(prefix)

# 這裏放置`gcc'命令調用的可執行文件。

bindir = $(exec_prefix)/bin

# 這裏放置編譯器使用的目錄。

libexecdir = $(exec_prefix)/libexec

#這裏放置Info文件。

infodir = $(prefix)/info

如果您的程序要在標準用戶指定的目錄中安裝大量的文件,將該程序的文件放入到特意指定的子目錄中是很有必要的。如果您要這樣做,您應該寫安裝規則創建這些子目錄。

不要期望用戶在上述列舉的變量值中包括這些子目錄,對於安裝目錄使用一套變量名的辦法使用戶能夠對於不同的GNU軟件包指定精確的值,爲了使這種做法有用,所有的軟件包必須設計爲當用戶使用時它們能夠聰明的工作。

14.5用戶標準目標

所有的GNU程序中,在makefile中都有下列目標:

`all'

編譯整個程序。這應該是缺省的目標。該目標不必重建文檔文件,Info文件已正常情況下應該包括在各個發佈的文件中,DVI文件只有在明確請求情況下才重建。缺省時,make規則編譯和連接使用選項-g',所以程序調試只是象徵性的。對於不介意缺少幫助的用戶如果他們希望將可執行程序和幫助分開,可以從中剝離出可執行程序。

`install'

編譯程序並將可執行程序、庫文件等拷貝到爲實際使用保留的文件名下。如果是證實程序是否適合安裝的簡單測試,則該目標應該運行該測試程序。不要在安裝時剝離可執行程序,魔鬼很可能關心那些使用install-strip目標來剝離可執行程序的人。如果這是可行的,編寫的install目標規則不應該更改程序建造的目錄下的任何東西,僅僅提供make all'一切都能完成。這是爲了方便用戶命名和在其它系統安裝建造程序,如果要安裝程序的目錄不存在,該命令應能創建所有這些目錄,這包括變量prefixexec_prefix特別指定的目錄和所有必要的子目錄。完成該任務的方法是藉助下面描述的目標installdirs。在所有安裝幫助頁的命令前使用-'使make 能夠忽略這些命令產生的錯誤,這可以確保在沒有Unix幫助頁的系統上安裝該軟件包時能夠順利進行。安裝Info文件的方法是使用變量$(INSTALL_DATA)Info文件拷貝到變量$(infodir)'中(參閱指定命令的變量),如果 install-info程序存在則運行它。 install-info是一個編輯Infodir'文件的程序,它可以爲Info文件添加或更新菜單;它是Texinfo軟件包的一部分。這裏有一個安裝Info文件的例子:

$(DESTDIR)$(infodir)/foo.info: foo.info

        $(POST_INSTALL)

# 可能在‘.’下有新的文件,在srcdir中沒有。

        -if test -f foo.info; then d=.; /

         else d=$(srcdir); fi; /

        $(INSTALL_DATA) $$d/foo.info $(DESTDIR)$@; /

#如果 install-info程序存在則運行它。

# 使用‘if'代替在命令行前的‘-'

# 這樣,我們可以注意到運行install-info產生的真正錯誤。

# 我們使用‘$(SHELL) -c' 是因爲在一些shell

# 遇到未知的命令不會運行失敗。

        if $(SHELL) -c 'install-info --version' /

           >/dev/null 2>&1; then /

          install-info --dir-file=$(DESTDIR)$(infodir)/dir /

                       $(DESTDIR)$(infodir)/foo.info; /

        else true; fi

在編寫install目標時,您必須把所有的命令歸位三類:正常的命令、 安裝前命令和安裝後命令。參閱安裝命令分類

`uninstall'

刪除所有安裝的文件--有‘install’目標拷貝的文件。該規則不應更改編譯產生的目錄,僅僅刪除安裝文件的目錄。反安裝命令象安裝命令一樣分爲三類,參閱安裝命令分類

`install-strip'

和目標install類似,但在安裝時僅僅剝離出可執行文件。 在許多情況下,該目標的定義非常簡單:

install-strip:

        $(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' /

                install

正常情況下我們不推薦剝離可執行程序進行安裝,只有您確信這些程序不會產生問題時才能這樣。剝離安裝一個實際執行的可執行文件同時保存那些在這種場合存在BUG的可執行文件是顯而易見的。

`clean'

刪除所有當前目錄下的文件,這些文件正常情況下是那些‘建立程序’創建的文件。不要刪除那些記錄配置的文件,同時也應該保留那些‘建立程序’能夠修改的文件,正常情況下要刪除的那些文件不包括這些文件,因爲發佈文件是和這些文件一起創建的。如果.dvi'文件不是文件發佈文件的一部分,則使用目標‘clean’將同時刪除.dvi'文件。

`distclean'

刪除所有當前目錄下的文件,這些文件正常情況下是那些‘建立程序’或‘配置程序’創建的文件。如果您不解包源程序,‘建立程序’不會創建任何其它文件,make distclean'將僅在文件發佈文件中留下原有的文件。

`mostlyclean'

和目標clean'類似,但是避免刪除人們正常情況下不編譯的文件。例如,用於GCC的目標mostlyclean不刪除文件libgcc.a',因爲在絕大多數情況下它都不需要重新編譯。

`maintainer-clean'

幾乎在當前目錄下刪除所有能夠使用該makefile文件可以重建的文件。使用該目標刪除的文件包括使用目標distclean,刪除的文件加上從Bison產生的C語言源文件和標誌列表、 Info文件等等。我們說“幾乎所有文件”的原因是運行命令make maintainer-clean'不刪除腳本configure'即使腳本configure'可以使用Makefile文件創建。更確切地說,運行make maintainer-clean'不刪除爲了運行腳本configure'以及開始建立程序的涉及的所有文件。這是運行make maintainer-clean'刪除所有能夠重新創建文件時唯一不能刪除的一類文件。目標maintainer-clean'由該軟件包的養護程序使用,不能被普通用戶使用。您可以使用特殊的工具重建被目標make maintainer-clean'刪除的文件。因爲這些文件正常情況下包含在發佈的文件中,我們並不關心它們是否容易重建。如果您發現您需要對全部發布的文件重新解包,您不能責怪我們。要幫助make 的用戶意識到這一點,用於目標maintainer-clean 應以以下兩行爲開始:

@echo‘該命令僅僅用於養護程序;’

@echo‘它刪除的所有文件都能使用特殊工具重建。’

`TAGS'

 更新該程序的標誌表。

`info'

產生必要的Info文件。最好的方法是編寫象下面規則:

info: foo.info

 

foo.info: foo.texi chap1.texi chap2.texi

        $(MAKEINFO) $(srcdir)/foo.texi

您必須在makefile文件中定以變量MAKEINFO。它將運行makeinfo程序,該程序是發佈程序中Texinfo的一部分。正常情況下,一個GNU發佈程序和Info文件一起創建,這意味着Info文件存在於源文件的目錄下。當用戶建造一個軟件包,一般情況下,make不更新Info文件,因爲它們已經更新到最新了。

`dvi'

創建DVI文件用於更新Texinfo文檔。例如:

dvi: foo.dvi

 

foo.dvi: foo.texi chap1.texi chap2.texi

        $(TEXI2DVI) $(srcdir)/foo.texi

您必須在makefile文件中定義變量TEXI2DVI。它將運行程序texi2dvi,該程序是發佈的Texinfo一部分。要麼僅僅編寫依靠文件,要麼允許GNU make提供命令,二者必選其一。

`dist'

爲程序創建一個tar文件。創建tar文件可以將其中的文件名以子目錄名開始,這些子目錄名可以是用於發佈的軟件包名。另外,這些文件名中也可以包含版本號,例如,發佈的GCC 1.40版的tar文件解包的子目錄爲gcc-1.40'。最方便的方法是創建合適的子目錄名,如使用incp等作爲子目錄,在它們的下面安裝適當的文件,然後把tar文件解包到這些子目錄中。使用gzip壓縮這些tar文件,例如,實際的GCC 1.40版的發佈文件叫gcc-1.40.tar.gz'。目標dist明顯的依靠所有的發佈文件中不是源文件的文件,所以你應確保發佈中的這些文件已經更新。參閱GNU標準編碼創建發佈文件

`check'

執行自我檢查。用戶應該在運行測試之前,應該先建立程序,但不必安裝這些程序;您應該編寫一個自我測試程序,在程序已建立但沒有安裝時執行。

以下目標建議使用習慣名,對於各種程序它們很有用:

installcheck

執行自我檢查。用戶應該在運行測試之前,應該先建立、安裝這些程序。您不因該假設$(bindir)'在搜尋路徑中。

installdirs

添加名爲installdirs'目標對於創建文件要安裝的目錄以及它們的父目錄十分有用。腳本mkinstalldirs'是專爲這樣處理方便而編寫的;您可以在Texinfo軟件包中找到它,您可以象這樣使用規則:

# 確保所有安裝目錄(例如 $(bindir))

# 都實際存在,如果沒有則創建它們。

installdirs: mkinstalldirs

        $(srcdir)/mkinstalldirs $(bindir) $(datadir) /

                                $(libdir) $(infodir) /

                                $(mandir)

該規則並不更改編譯時創建的目錄,它僅僅創建安裝目錄。

14.6 安裝命令分類

編寫已安裝目標,您必須將所有命令分爲三類:正常的命令、安裝前命令和安裝後命令。

正常情況下,命令把文件移動到合適的地方,並設置它們的模式。它們不會改變任何文件,僅僅把它們從軟件包中完整地抽取出來。

安裝前命令和安裝後命令可能更改一些文件,如,它們編輯配置文件後數據庫文件。

安裝前命令在正常命令之前執行,安裝後命令在正常命令執行後執行。

安裝後命令最普通的用途是運行install-info程序。 這種工作不能由正常命令完成,因爲它更改了一個文件(Info 目錄),該文件不能全部、單獨從軟件包中安裝。它是一個安裝後命令,因爲它需要在正常命令安裝軟件包中的Info文件後才能執行。

許多程序不需要安裝前命令,但是我們提供這個特點,以便在需要時可以使用。

要將安裝規則的命令分爲這三類,應在命令中間插入category lines(分類行)。 分類行指定了下面敘述的命令的類別。

分類行包含一個Tab、一個特殊的make變量引用,以及行結尾的隨機註釋。您可以使用三個變量,每一個變量對應一個類別;變量名指定了類別。分類行不能出現在普通的執行文件中,因爲這些make變量被由正常的定義(您也不應在makefile文件中定義)。

這裏有三種分類行,後面的註釋解釋了它的含義:

        $(PRE_INSTALL)     # 以下是安裝前命令

        $(POST_INSTALL)    # 以下是安裝後命令

        $(NORMAL_INSTALL)  # 以下是正常命令

如果在安裝規則開始您沒有使用分類行,則在第一個分類行出現之前的所有命令都是正常命令。如果您沒有使用任何分類行,則所有命令都是正常命令。

這是反安裝的分類行

        $(PRE_UNINSTALL)     #以下是反安裝前命令

        $(POST_UNINSTALL)    #以下是反安裝後命令

        $(NORMAL_UNINSTALL)  #以下是正常命令

反安裝前命令的典型用法是從Info目錄刪除全部內容。

如果目標installuninstall 有依賴作爲安裝程序的子程序,那麼您應該使用分類行先啓動每一個依賴的命令,再使用分類行啓動主目標的命令。無論哪一個依賴實際執行,這種方式都能保證每一條命令都放置到了正確的分類中。

安裝前命令和安裝後命令除了對於下述命令外,不能運行其它程序:

basename bash cat chgrp chmod chown cmp cp dd diff echo

egrep expand expr false fgrep find getopt grep gunzip gzip

hostname install install-info kill ldconfig ln ls md5sum

mkdir mkfifo mknod mv printenv pwd rm rmdir sed sort tee

test touch true uname xargs yes

按照這種方式區分命令的原因是爲了創建二進制軟件包。典型的二進制軟件包包括所有可執行文件、必須安裝的其它文件以及它自己的安裝文件——所以二進制軟件包不需要運行任何正常命令。但是安裝二進制軟件包需要執行安裝前命令和安裝後命令。

建造二進制軟件包的程序通過抽取安裝前命令和安裝後命令工作。這裏有一個抽取安裝前命令的方法:

make -n install -o all /

      PRE_INSTALL=pre-install /

      POST_INSTALL=post-install /

      NORMAL_INSTALL=normal-install /

  | gawk -f pre-install.awk

這裏文件pre-install.awk'可能包括:

$0 ~ /^/t[ /t]*(normal_install|post_install)[ /t]*$/ {on = 0}

on {print $0}

$0 ~ /^/t[ /t]*pre_install[ /t]*$/ {on = 1}

安裝前命令的結果文件是象安裝二進制軟件包的一部分shell腳本一樣執行。

15 快速參考

這是對指令、文本操作函數以及GNU make能夠理解的變量等的彙總。對於其他方面的總結參閱特殊的內建目標名隱含規則目錄選項概要

這裏是GNU make是別的指令的總結:

define variable

endef

定義多行遞歸調用擴展型變量。參閱定義固定次序的命令

ifdef variable

ifndef variable

ifeq (a,b)

ifeq "a" "b"

ifeq 'a' 'b'

ifneq (a,b)

ifneq "a" "b"

ifneq 'a' 'b'

else

endif

makefile文件中的條件擴展,參閱makefile文件中的條件語句

include file

-include file

sinclude file

包含其它makefile文件,參閱包含其它makefile文件

override variable = value

override variable := value

override variable += value

override variable ?= value

override define variable

endef

定義變量、對以前的定義重載、以及對在命令行中定義的變量重載。參閱override指令

export

告訴make缺省向子過程輸出所有變量,參閱與子make通訊的變量

export variable

export variable = value

export variable := value

export variable += value

export variable ?= value

unexport variable

告訴make是否向子過程輸出一個特殊的變量。參業與子make通訊的變量

vpath pattern path

制定搜尋匹配‘%’格式的文件的路徑。參閱vpath指令

vpath pattern

去除以前爲‘pattern’指定的所有搜尋路徑。

vpath

去除以前用vpath指令指定的所有搜尋路徑。

這裏是操作文本函數的總結,參閱文本轉換函數

$(subst from,to,text)

在‘text’中用‘to’代替‘from’,參閱字符串替換與分析函數

$(patsubst pattern,replacement,text)

在‘text’中用‘replacement’代替匹配‘pattern’字,參閱字符串替換與分析函數

$(strip string)

從字符串中移去多餘的空格。參閱字符串替換與分析函數

$(findstring find,text)

確定‘find’在‘text’中的位置。參閱字符串替換與分析函數

$(filter pattern...,text)

在‘text’中選擇匹配‘pattern’的字。參閱字符串替換與分析函數

$(filter-out pattern...,text)

在‘text’中選擇不匹配‘pattern’的字。參閱字符串替換與分析函數

$(sort list)

將‘list’中的字按字母順序排序,並刪除重複的字。參閱字符串替換與分析函數

$(dir names...)

從文件名中抽取路徑名。參閱文件名函數

$(notdir names...)

從文件名中抽取路徑部分。參閱文件名函數

$(suffix names...)

從文件名中抽取非路徑部分。參閱文件名函數

$(basename names...)

從文件名中抽取基本文件名。參閱文件名函數

$(addsuffix suffix,names...)

爲‘names’中的每個字添加後綴。參閱文件名函數

$(addprefix prefix,names...)

爲‘names’中的每個字添加前綴。參閱文件名函數

$(join list1,list2)

連接兩個並行的字列表。參閱文件名函數

$(word n,text)

從‘text’中抽取第n個字。參閱文件名函數

$(words text)

計算‘text’中字的數目。參閱文件名函數

$(wordlist s,e,text)

返回‘text’中se之間的字。參閱文件名函數

$(firstword names...)

在‘names…’中的第一個字。參閱文件名函數

$(wildcard pattern...)

尋找匹配shell文件名格式的文件名。參閱wildcard函數

$(error text...)

該函數執行時,make產生信息爲‘text’的致命錯誤。參閱控制make的函數

$(warning text...)

該函數執行時,make產生信息爲‘text’的警告。參閱控制make的函數

$(shell command)

執行shell命令並返回它的輸出。參閱函數shell

$(origin variable)

返回make變量‘variable’的定義信息。參閱函數origin

$(foreach var,words,text)

將列表列表words中的每一個字對應後接var中的每一個字,將結果放在text中。參閱函數foreach

$(call var,param,...)

使用對$(1), $(2)...對變量計算變量 var ,變量$(1), $(2)...分別代替參數 param 第一個、第二個…的值。參閱函數call

這裏是對自動變量的總結,完整的描述參閱自動變量

$@

目標文件名。

$%

當目標是檔案成員時,表示目標成員名。

$<

第一個依賴名。

$?

所有比目標‘新’的依賴的名字,名字之間用空格隔開。對於爲檔案成員的依賴,只能使用命名的成員。參閱使用make更新檔案文件

$^

$+

所有依賴的名字,名字之間用空格隔開。對於爲檔案成員的依賴,只能使用命名的成員。參閱使用make更新檔案文件。變量 $^ 省略了重複的依賴,而變量 $+ 則按照原來次序保留重複項,

$*

和隱含規則匹配的stem()。參閱格式匹配

$(@D)

$(@F)

變量$@.中的路徑部分和文件名部分。

$(*D)

$(*F)

變量$*中的路徑部分和文件名部分。

$(%D)

$(%F)

變量$%中的路徑部分和文件名部分。

$(<D)

$(<F)

變量$<中的路徑部分和文件名部分。

$(^D)

$(^F)

變量$^中的路徑部分和文件名部分。

$(+D)

$(+F)

變量$+中的路徑部分和文件名部分。

$(?D)

$(?F)

變量$?中的路徑部分和文件名部分。

以下是GNU make使用變量:

MAKEFILES

每次調用make要讀入的Makefiles文件。參閱變量MAKEFILES

VPATH

對在當前目錄下不能找到的文件搜索的路徑。參閱VPATH: 所有依賴的搜尋路徑

SHELL

系統缺省命令解釋程序名,通常是`/bin/sh'。您可以在makefile文件中設值變量SHELL改變運行程序使用的shell。參閱執行命令

MAKESHELL

改變量僅用於MS-DOSmake使用的命令解釋程序名,該變量的值比變量SHELL的值優先。參閱執行命令

MAKE

調用的make名。在命令行中使用該變量有特殊的意義。參閱變量MAKE的工作方式。

MAKELEVEL

遞歸調用的層數(makes)。參閱與子make通訊的變量

MAKEFLAGS

make提供標誌。您可以在環境或makefile文件中使用該變量設置標誌。參閱與子make通訊的變量。在命令行中不能直接使用該變量,應爲它的內容不能在shell中正確引用,但總是允許遞歸調用make時通過環境傳遞給子make

MAKECMDGOALS

該目標是在命令行中提供給make的。設置該變量對make的行爲沒有任何影響。參閱特別目標的參數

CURDIR

設置當前工作目錄的路徑名,參閱遞歸調用make

SUFFIXES

在讀入任何makefile文件之前的後綴列表。

.LIBPATTERNS

定義make搜尋的庫文件名,以及搜尋次序。參閱連接庫搜尋目錄

16 make產生的錯誤

這裏是您可以看到的由make產生絕大多數普通錯誤列表,以及它們的含義和修正它們信息。

有時make產生的錯誤不是致命的,如一般在命令腳本行前面存在前綴的情況下,和在命令行使用選向‘-k’的情況下產生的錯誤幾乎都不是致命錯誤。使用字符串***作前綴將產生致命的錯誤。

錯誤信息前面都使用前綴,前綴的內容是產生錯誤的程序名或makefile文件中存在錯誤的文件名和包含該錯誤的行的行號和。

在下述的錯誤列表中,省略了普通的前綴:

`[foo] Error NN'

`[foo] signal description'

這些錯誤並不是真的make的錯誤。它們意味着make調用的程序返回非零狀態值,錯誤碼(Error NN),這種情況make解釋爲失敗,或非正常方式退出(一些類型信號),參閱命令錯誤。如果信息中沒有附加***,則是子過程失敗,但在makefile文件中的這條規則有特殊前綴,因此make忽略該錯誤。

`missing separator. Stop.'

`missing separator (did you mean TAB instead of 8 spaces?). Stop.'

這意味着make在讀取命令行時遇到不能理解的內容。GNU make 檢查各種分隔符(:, =, 字符TAB,等) 從而幫助確定它在命令行中遇到了什麼類型的錯誤。這意味着,make不能發現一個合法的分隔符。出現該信息的最可能的原因是您(或許您的編輯器,絕大部分是ms-windows的編輯器)在命令行縮進使用了空格代替了字符Tab。這種情況下,make將使用上述的第二種形式產生錯誤信息。一定切記,任何命令行都以字符Tab開始,八個空格也不算數。參閱規則的語法

`commands commence before first target. Stop.'

`missing rule before commands. Stop.'

這意味着在makefile中似乎以命令行開始:以Tab字符開始,但不是一個合法的命令行(例如,一個變量的賦值)。任何命令行必須和一定的目標相聯繫。產生第二種的錯誤信息是一行的第一個非空白字符爲分號,make對此的解釋是您遺漏了規則中的"target: prerequisite" 部分,參閱規則的語法

`No rule to make target `xxx'.'

`No rule to make target `xxx', needed by `yyy'.'

這意味着make決定必須建立一個目標,但卻不能在makefile文件中發現任何用於創建該目標的指令,包括具體規則和隱含規則。如果您希望創建該目標,您需要另外爲改目標編寫規則。其它關於該問題產生原因可能是makefile文件是草稿(如文件名錯)或破壞了源文件樹(一個文件不能按照計劃重建,僅僅由於一個依賴的問題)。

`No targets specified and no makefile found. Stop.'

`No targets. Stop.'

前者意味着您沒有爲命令行提供要創建的目標,make不能讀入任何makefile文件。後者意味着一些makefile文件被找到,但沒有包含缺省目標以及命令行等。GNU make在這種情況下無事可做。參閱指定makefile文件的參數

`Makefile `xxx' was not found.'

`Included makefile `xxx' was not found.'

在命令行中指定一個makefile文件(前者)或包含的makefile 文件(後者)沒有找到。

`warning: overriding commands for target `xxx''

`warning: ignoring old commands for target `xxx''

GNU make允許命令在一個規則中只能對一個命令使用一次(雙冒號規則除外)。如果您爲一個目標指定一個命令,而該命令在目標定義是已經定義過,這種警告就會產生;第二個信息表明後來設置的命令將改寫以前對該命令的設置。參閱具有多條規則的目標

`Circular xxx <- yyy dependency dropped.'

這意味着make檢測到一個相互依靠一個循環:在跟蹤目標xxx的依賴yyy 時發現,依賴yyy的依賴中一個又以xxx爲依賴。

`Recursive variable `xxx' references itself (eventually). Stop.'

這意味着您定義一個正常(遞歸調用性)make變量xxx,當它擴展時,它將引用它自身。無論對於簡單擴展型變量(:=)或追加定義(+=),這也都是不能允許的,參閱使用變量

`Unterminated variable reference. Stop.'

這意味着您在變量引用或函數調用時忘記寫右括號。

`insufficient arguments to function `xxx'. Stop.'

這意味着您在調用函數是您密友提供需要數目的參數。關於函數參數的詳細描述參閱文本轉換函數

`missing target pattern. Stop.'

`multiple target patterns. Stop.'

`target pattern contains no `%'. Stop.'

這些錯誤信息是畸形的靜態格式規則引起的。第一條意味着在規則的目標部分沒有規則,第二條意味着在目標部分有多個規則,第三條意味着沒有包含格式符%。參閱靜態格式規則語法

`warning: -jN forced in submake: disabling jobserver mode.'

該條警告和下條警告是在make檢測到在能與子make通訊的系統中包含並行處理的錯誤(參閱與子make通訊選項)。該警告信息是如果遞歸調用一個make過程,而且還使用了‘-jn’選項(這裏n大於1)。這種情況很可能發生,例如,如果您設置環境變量MAKE爲‘make j2’。這種情況下,子make不能與其它make過程通訊, 而且還簡單假裝它由兩個任務。

`warning: jobserver unavailable: using -j1. Add `+' to parent make rule.'

爲了保證make過程之間通訊,父層make將傳遞信息給子make。這可能導致問題,因爲子過程有可能不是實際的一個make,而父過程僅僅認爲子過程是一個make而將所有信息傳遞給子過程。父過程是採用正常的算法決定這些的(參閱變量MAKE的工作方式)。如果makefile文件構建了這樣的父過程,它並不知道子過程是否爲make,那麼,子過程將拒收那些沒有用的信息。這種情況下,子過程就會產生該警告信息,然後按照它內建的次序方式進行處理。

17 複雜的makfile文件例子

這是一個用於GNU tar程序的makefile文件;這是一箇中等複雜的makefile文件。

因爲‘all’是第一個目標,所以它是缺省目標。該makefile文件一個有趣的地方是testpad.h'是由testpad程序創建的源文件,而且該程序自身由testpad.c'編譯得到的。

如果您鍵入make'`make all',則make創建名爲tar'可執行文件, 提供遠程訪問磁帶的進程rmt',和名爲tar.info'Info文件。

如果您鍵入make install',則make不但創建tar',rmt',和‘tar.info',而且安裝它們。

如果您鍵入make clean', make刪除所有.o'文件,以及tar',rmt',testpad', testpad.h',core’文件。

如果您鍵入make distclean', make不僅刪除make clean'刪除的所有文件,而且包括文件‘TAGS', Makefile', 和‘config.status' 文件。(雖然不明顯,但該 makefile (config.status')是用戶用configure程序產生的,該程序是由發佈的tar文件提供,但這裏不進行說明。)

如果您鍵入make realclean', make刪除make distclean '刪除的所有文件,而且包括由‘tar.texinfo'產生的Info文件。

另外,目標shardist創造了發佈文件的核心。

# 自動從makefile.in產生

# 用於GNU tar 程序的Unix Makefile

# Copyright (C) 1991 Free Software Foundation, Inc.

 

# 本程序是自由軟件;在遵照GNU條款的情況下

# 您可以重新發布它或更改它

# 普通公衆許可證 ...

...

...

 

SHELL = /bin/sh

 

#### 啓動系統配置部分 ####

 

srcdir = .

 

# 如果您使用gcc, 您應該在運行

# 和它一起創建的固定包含的腳本程序以及

# 使用-traditional選項運行gcc中間選擇其一。 

# 另外的ioctl調用在一些系統上不能正確編譯

CC = gcc -O

YACC = bison -y

INSTALL = /usr/local/bin/install -c

INSTALLDATA = /usr/local/bin/install -c -m 644

 

# 您應該在DEFS中添加的內容:

# -DSTDC_HEADERS        如果您有標準C的頭文件和

#                       庫文件。

# -DPOSIX               如果您有POSIX.1的頭文件和

#                       庫文件。

# -DBSD42               如果您有sys/dir.h (除非

#                       您使用-DPOSIX), sys/file.h,

#                       st_blocks`struct stat'中。

# -DUSG                 如果您有System V/ANSI C

#                       字符串和內存控制函數

#                       和頭文件, sys/sysmacros.h,

#                       fcntl.h, getcwd, no valloc,

#                       ndir.h (除非

#                       您使用-DDIRENT)

# -DNO_MEMORY_H         如果USGSTDC_HEADERS 但是不

#                       包括memory.h.

# -DDIRENT              如果USG而且您又用dirent.h

#                       代替ndir.h

# -DSIGTYPE=int         如果您的信號控制器

#                       返回int,void.

# -DNO_MTIO             如果您缺少sys/mtio.h

#                       (magtape ioctls).

# -DNO_REMOTE           如果您沒有一個遠程shell

#                       rexec.

# -DUSE_REXEC           對遠程磁帶使用rexec

#                       操作代替

#                       分支rshremsh.

# -DVPRINTF_MISSING     如果您缺少函數vprintf

#                       (但是有_doprnt).

# -DDOPRNT_MISSING      如果您缺少函數 _doprnt.

#                       同樣需要定義

#                       -DVPRINTF_MISSING.

# -DFTIME_MISSING       如果您缺少系統調用ftime

# -DSTRSTR_MISSING      如果您缺少函數strstr

# -DVALLOC_MISSING      如果您缺少函數valloc

# -DMKDIR_MISSING       如果您缺少系統調用mkdir

#                       rmdir

# -DRENAME_MISSING      如果您缺少系統調用rename

# -DFTRUNCATE_MISSING   如果您缺少系統調用ftruncate

#                      

# -DV7                  Unix版本7 (沒有

#                       進行長期測試).

# -DEMUL_OPEN3          如果您缺少3-參數版本

#                       open, 並想通過您有的系統調用

#                       仿真它。

# -DNO_OPEN3            如果您缺少3-參數版本的open

#                       並要禁止tar k選項

#                       代替仿真open.

# -DXENIX               如果您有sys/inode.h

#                       並需要它包含94

 

DEFS =  -DSIGTYPE=int -DDIRENT -DSTRSTR_MISSING /

        -DVPRINTF_MISSING -DBSD42

# 設置爲rtapelib.o ,除非使它爲空時

# 您定義了NO_REMOTE,

RTAPELIB = rtapelib.o

LIBS =

DEF_AR_FILE = /dev/rmt8

DEFBLOCKING = 20

 

CDEBUG = -g

CFLAGS = $(CDEBUG) -I. -I$(srcdir) $(DEFS) /

        -DDEF_AR_FILE=/"$(DEF_AR_FILE)/" /

        -DDEFBLOCKING=$(DEFBLOCKING)

LDFLAGS = -g

 

prefix = /usr/local

# 每一個安裝程序的前綴。

#正常爲空或`g'

binprefix =

 

# 安裝tar的路徑

bindir = $(prefix)/bin

 

# 安裝info文件的路徑.

infodir = $(prefix)/info

 

#### 系統配置結束部分 ####

 

SRC1 =  tar.c create.c extract.c buffer.c /

        getoldopt.c update.c gnu.c mangle.c

SRC2 =  version.c list.c names.c diffarch.c /

        port.c wildmat.c getopt.c

SRC3 =  getopt1.c regex.c getdate.y

SRCS =  $(SRC1) $(SRC2) $(SRC3)

OBJ1 =  tar.o create.o extract.o buffer.o /

        getoldopt.o update.o gnu.o mangle.o

OBJ2 =  version.o list.o names.o diffarch.o /

        port.o wildmat.o getopt.o

OBJ3 =  getopt1.o regex.o getdate.o $(RTAPELIB)

OBJS =  $(OBJ1) $(OBJ2) $(OBJ3)

AUX =   README COPYING ChangeLog Makefile.in  /

        makefile.pc configure configure.in /

        tar.texinfo tar.info* texinfo.tex /

        tar.h port.h open3.h getopt.h regex.h /

        rmt.h rmt.c rtapelib.c alloca.c /

        msd_dir.h msd_dir.c tcexparg.c /

        level-0 level-1 backup-specs testpad.c

 

all:    tar rmt tar.info

 

tar:    $(OBJS)

        $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)

 

rmt:    rmt.c

        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ rmt.c

 

tar.info: tar.texinfo

        makeinfo tar.texinfo

 

install: all

        $(INSTALL) tar $(bindir)/$(binprefix)tar

        -test ! -f rmt || $(INSTALL) rmt /etc/rmt

        $(INSTALLDATA) $(srcdir)/tar.info* $(infodir)

 

$(OBJS): tar.h port.h testpad.h

regex.o buffer.o tar.o: regex.h

# getdate.y 8個變換/減少衝突。

 

testpad.h: testpad

        ./testpad

 

testpad: testpad.o

        $(CC) -o $@ testpad.o

 

TAGS:   $(SRCS)

        etags $(SRCS)

 

clean:

        rm -f *.o tar rmt testpad testpad.h core

 

distclean: clean

        rm -f TAGS Makefile config.status

 

realclean: distclean

        rm -f tar.info*

 

shar: $(SRCS) $(AUX)

        shar $(SRCS) $(AUX) | compress /

          > tar-`sed -e '/version_string/!d' /

                     -e 's/[^0-9.]*/([0-9.]*/).*//1/' /

                     -e q

                     version.c`.shar.Z

 

dist: $(SRCS) $(AUX)

        echo tar-`sed /

             -e '/version_string/!d' /

             -e 's/[^0-9.]*/([0-9.]*/).*//1/' /

             -e q

             version.c` > .fname

        -rm -rf `cat .fname`

        mkdir `cat .fname`

        ln $(SRCS) $(AUX) `cat .fname`

        tar chZf `cat .fname`.tar.Z `cat .fname`

        -rm -rf `cat .fname` .fname

 

tar.zoo: $(SRCS) $(AUX)

        -rm -rf tmp.dir

        -mkdir tmp.dir

        -rm tar.zoo

        for X in $(SRCS) $(AUX) ; do /

            echo $$X ; /

            sed 's/$$/^M/' $$X /

            > tmp.dir/$$X ; done

        cd tmp.dir ; zoo aM ../tar.zoo *

        -rm -rf tmp.dir

腳註

(1)

MS-DOS MS-Windows編譯的GNU Make和將前綴定義爲DJGPP樹體系的根的行爲相同。

(2)

MS-DOS, 當前工作目錄的值是global, 因此改變它將影響這些系統中隨後的命令行。

(3)

texi2dvi 使用TeX 進行真正的格式化工作. TeX沒有和 Texinfo一塊發佈。


本文檔使用版本1.54texi2html 翻譯器於2000719日產生。

 

 

 

本文檔的版權所有,不允許用於任何商業行爲。

名詞翻譯對照表

archive                                         檔案

archive member targets                            檔案成員目標

arguments of functions                            函數參數

automatic variables                               自動變量

backslash (/)                                     反斜槓

basename                                       基本文件名

binary packages                                  二進制包

compatibility                                    兼容性

data base                                       數據庫

default directries                                 缺省目錄

default goal                                     缺省最終目標

defining variables verbatim                        定義多行變量

directive                                       指令

dummy pattern rule                               僞格式規則

echoing of commands                            命令回顯

editor                                         編輯器

empty commands                                 空命令

empty targets                                     空目標

environment                                      環境

explicit rule                                      具體規則

file name functions                                文件名函數

file name suffix                                   文件名後綴

flags                                            標誌

flavors of variables                                變量的特色

functions                                        函數

goal                                            最終目標

implicit rule                                      隱含規則

incompatibilities                                  不兼容性

intermediate files                                 中間文件

match-anything rule                               萬用規則

options                                          選項

parallel execution                                  並行執行

pattern rule                                       格式規則

phony targets                                     假想目標

prefix                                           前綴

prerequisite                                      依賴

recompilation                                    重新編譯

rule                                            規則

shell command                                   shell命令

static pattern rule                                  靜態格式規則

stem                                           

sub-make                                        make

subdirectories                                     子目錄

suffix                                            後綴

suffix rule                                        後綴規則

switches                                         開關

target                                           目標

value                                          

variable                                         變量

wildcard                                        通配符

word                                          

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章