GNU Make 使用手冊!

GNU Make 使用手冊(中譯版) html

翻譯:於鳳昌 node

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

GNU make Version 3.79 算法

April 2000 shell

Richard M. Stallman and Roland McGrath 數據庫

 

目錄 編程

1 make概述 windows

1.1 怎樣閱讀本手冊 安全

1.2 問題和BUG bash

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地址是:

bug-make@gnu.org

在郵件中請包含您使用的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、若是一個目標文件在makefile文件所在的目錄下不存在,則將會執行目錄搜尋。

二、 2、若是目錄搜尋成功,則路徑和所獲得的文件暫時做爲目標文件儲存。

三、 3、全部該目標的依賴用相同的方法考察。

四、 4、把依賴處理完成後,該目標可能須要或不須要從新建立:

一、 1、若是該目標不須要重建,目錄搜尋時所獲得的文件的路徑用做該目標全部依賴的路徑,同時包含該目標文件。簡而言之,若是make沒必要重建目標,則您使用經過目錄搜尋獲得的路徑。

二、 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            在‘t’中分離出路徑部分,稱爲‘d’,剩下部分稱爲‘n’。例如若是‘t’是‘src/foo.o’,那麼‘d’是‘src/’;‘n’是‘foo.o’。

二、            2            創建全部目標名匹配‘t’和‘n’的格式規則列表。若是目標格式中含有斜槓,則匹配‘t’,不然,匹配‘n’。

三、            3            若是列表中有一個規則不是萬用規則,則從列表中刪除全部非最終萬用規則。

四、            4            將沒有命令的規則也從列表中移走。

五、            5            對每一個列表中的格式規則:

一、  1  尋找stems’,也就是和目標格式中%匹配的‘t’或‘n’部分。

二、  2  使用stems’計算依賴名。若是目標格式不包含斜槓,則將‘d’添加在每一個依賴的前面。

三、  3  測試全部的依賴是否存在或可以建立。(若是任何文件在makefile中做爲目標或依賴被說起,則咱們說它應該存在。)若是全部依賴存在或可以建立,或沒有依賴,則可以使用該規則。

六、            6            若是到如今尚未發現能使用的規則,進一步試。對每個列表中的規則:

一、  1  若是規則是最終規則,則忽略它,繼續下一條規則。

二、  2  象上述同樣計算依賴名。

三、  3  測試全部的依賴是否存在或可以建立。

四、  4  對於不存在的依賴,按照該算法遞歸調用查找是否可以採用隱含規則建立。

五、  5  若是全部依賴存在或能使用隱含規則建立,則應用該規則。

七、            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): $$@.o 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                                         編輯器

相關文章
相關標籤/搜索