Make命令完全詳解教程


Make命令完全詳解教程

 

無論是在Linux還是在Unix環境中,make都是一個非常重要的編譯命令。不管是自己進行項目開發還是安裝應用軟件,我們都經常要用到make或make install。利用make工具,我們可以將大型的開發項目分解成為多個更易於管理的模塊,對於一個包括幾百個源文件的應用程序,使用make和makefile工具就可以簡潔明快地理順各個源文件之間紛繁復雜的相互關系。而且如此多的源文件,如果每次都要鍵入gcc命令進行編譯的話,那對程序員來說簡直就是一場災難。而make工具則可自動完成編譯工作,並且可以只對程序員在上次編譯后修改過的部分進行編譯。因此,有效的利用make和makefile工具可以大大提高項目開發的效率。同時掌握make和makefile之后,您也不會再面對着Linux下的應用軟件手足無措了。

 

 

 

 

 

 

 

 

 

一、Make程序的命令行選項和參數

Make命令參數的典型序列如下所示:

make [-f makefile文件名][選項][宏定義][目標]

這里用[]括起來的表示是可選的。命令行選項由破折號“–”指明,后面跟選項,如

make –e

如果需要多個選項,可以只使用一個破折號,如

make –kr

也可以每個選項使用一個破折號,如

make –k –r

甚至混合使用也行,如

make –e –kr

Make命令本身的命令行選項較多,這里只介紹在開發程序時最為常用的三個,它們是:

–k:

如果使用該選項,即使make程序遇到錯誤也會繼續向下運行;如果沒有該選項,在遇到第一個錯誤時make程序馬上就會停止,那么后面的錯誤情況就不得而知了。我們可以利用這個選項來查出所有有編譯問題的源文件。

–n:

該選項使make程序進入非執行模式,也就是說將原來應該執行的命令輸出,而不是執行。

–f :

指定作為makefile的文件的名稱。 如果不用該選項,那么make程序首先在當前目錄查找名為makefile的文件,如果沒有找到,它就會轉而查找名為Makefile的文件。如果您在Linux下使用GNU Make的話,它會首先查找GNUmakefile,之后再搜索makefile和Makefile。按照慣例,許多Linux程序員使用Makefile,因為這樣能使Makefile出現在目錄中所有以小寫字母命名的文件的前面。所以,最好不要使用GNUmakefile這一名稱,因為它只適用於make程序的GNU版本。

當我們想構建指定目標的時候,比如要生成某個可執行文件,那么就可以在make命令行中給出該目標的名稱;如果命令行中沒有給出目標的話,make命令會設法構建makefile中的第一個目標。我們可以利用這一特點,將all作為makefile中的第一個目標,然后將讓目標作為all所依賴的目標,這樣,當命令行中沒有給出目標時,也能確保它會被構建。

 

 

 

 

 

 

 

 

二、Makefile概述

上面提到,make命令對於構建具有多個源文件的程序有很大的幫助。事實上,只有make命令還是不夠的,前面說過還必用須makefile告訴它要做什么以及怎么做才行,對於程序開發而言,就是告訴make命令應用程序的組織情況。

我們現在對makefile的位置和數量簡單說一下。一般情況下,makefile會跟項目的源文件放在同一個目錄中。另外,系統中可以有多個makefile,一般說來一個項目使用一個makefile就可以了;如果項目很大的話,我們就可以考慮將它分成較小的部分,然后用不同的makefile來管理項目的不同部分。

make命令和Makefile配合使用,能給我們的項目管理帶來極大的便利,除了用於管理源代碼的編譯之外,還用於建立手冊頁,同時還能將應用程序安裝到指定的目錄。

因為Makefile用於描述系統中模塊之間的相互依賴關系,以及產生目標文件所要執行的命令,所以,一個makefile由依賴關系和規則兩部分內容組成。下面分別加以解釋。

依賴關系由一個目標和一組該目標所依賴的源文件組成。這里所說的目標就是將要創建或更新的文件,最常見的是可執行文件。規則用來說明怎樣使用所依賴得文件來建立目標文件。

當make命令運行時,會讀取makefile來確定要建立的目標文件或其他文件,然后對源文件的日期和時間進行比較,從而決定使用那些規則來創建目標文件。一般情況下,在建立起最終的目標文件之前,肯定免不了要建立一些中間性質的目標文件。這時,Make命令也是使用makefile來確定這些目標文件的創建順序,以及用於它們的規則序列。

2.1.Makefile中的依賴關系

make程序自動生成和維護通常是可執行模塊或應用程序的目標,目標的狀態取決於它所依賴的那些模塊的狀態。Make的思想是為每一塊模塊都設置一個時間標記,然后根據時間標記和依賴關系來決定哪一些文件需要更新。一旦依賴模塊的狀態改變了,make就會根據時間標記的新舊執行預先定義的一組命令來生成新的目標。

依賴關系規定了最終得到的應用程序跟生成它的各個源文件之間的關系。如下面的圖1描述了可執行文件main對所有的源程序文件及其編譯產生的目標文件之間的依賴關系,見下圖:

 

圖1  模塊間的依賴關系

就圖1而言,我們可以說可執行程序main依賴於main.o、f1.o和ff1.o。與此同時,main.o依賴於main.c和def1.h;f1.o依賴於f1.c、def1.h和def2.h;而ff1.o則依賴於ff1.c、def2.h和def3. h。在makefile中,我們可以用目標名稱,加冒號,后跟空格鍵或tab鍵,再加上由空格鍵或tab鍵分隔的一組用於生產目標模塊的文件來描述模塊之間的依賴關系對於上例來說,可以作以下描述:

main: main.o f1.o f2.o

main.o: main.c def1.h

f1.o: f1.c def1.h def2.h

f2.o: f2.c def2.h def3.h

不難發現,上面的各個源文件跟各模塊之間的關系具有一個明顯的層次結構,如果def2.h發生了變化,那么就需要更新f1.o和f2.o,而f1.o和f2.o發生了變化的話,那么main也需要隨之重新構建。

默認時,make程序只更新makefile中的第一個目標,如果希望更新多個目標文件的話,可以使用一個特殊的目標all,假如我們想在一個makefile中更新main和hello這兩個程序文件的話,可以加入下列語句達到這個目的:

all: main hello

實際上,makefile是以相關行為基本單位的,相關行用來描述目標、模塊及規則(即命令行)三者之間的關系。一個相關行格式通常為:冒號左邊是目標(模塊)名;冒號右邊是目標所依賴的模塊名;緊跟着的規則(即命令行)是由依賴模塊產生目標所使用的命令。相關行的格式為:

目標:[依賴模塊][;命令]

習慣上寫成多行形式,如下所示:

目標:[依賴模塊]

命令

命令

目標(TARGET)程序產生的文件,如可執行文件和目標文件;目標也可以是要執行的動作,如“clean”。

依賴(DEPENDENCIES)是用來產生目標的輸入文件,一個目標通常依賴於多個文件。

命令(COMMAND)是make執行的動作,一個可以有多個命令,每個占一行。注意:每個命令行的起始字符必須為TAB字符!

有依賴關系規則中的命令通常在依賴文件變化時負責產生target文件,make執行這些命令更新或產生target。規則可以沒有依賴關系,如包含target “clean”的規則。

規則解釋如何和何時重做該規則中的文件,make根據依賴關系執行產生或更新目標;規則也說明如何和何時執行動作。有的規則看起來很復雜,但都符合上述模式。

需要注意的是,如果相關行寫成一行,“命令”之前用分號“;”隔開,如果分成多行書寫的話,后續的行務必以tab字符為先導。對於makefile而言,空格字符和tab字符是不同的。所有規則所在的行必須以tab鍵開頭,而不是空格鍵。初學者一定對此保持警惕,因為這是新手最容易疏忽的地方,因為幾個空格鍵跟一個tab鍵在肉眼是看不出區別的,但make命令卻能明察秋毫。

此外,如果在makefile文件中的行尾加上空格鍵的話,也會導致make命令運行失敗。所以,大家一定要小心了,免得耽誤許多時間。

例,一個名為prog的程序由三個C源文件filea.c、fileb.c和filec.c以及庫文件LS編譯生成,這三個文件還分別包含自己的頭文件a.h 、b.h和c.h。通常情況下,C編譯器將會輸出三個目標文件filea.o、fileb.o和filec.o。假設filea.c和fileb.c都要聲明用到一個名為defs的文件,但filec.c不用。即在filea.c和fileb.c里都有這樣的聲明:

 #include "defs"

  那么下面的文檔就描述了這些文件之間的相互聯系:

line 

1   #It is a example for describing makefile

2   prog : filea.o fileb.o filec.o

3   cc filea.o fileb.o filec.o -LS -o prog

4   filea.o : filea.c a.h defs

5   cc -c filea.c

6   fileb.o : fileb.c b.h defs

7   cc -c fileb.c

8   filec.o : filec.c c.h

9    cc -c filec.c

這個描述文檔就是一個簡單的makefile文件。

從上面的例子注意到,第一個字符為 # 的行為注釋行。第一個非注釋行指定prog由三個目標文件filea.o、fileb.o和filec.o鏈接生成。第三行描述了如何從prog所依賴的文件建立可執行文件。接下來的4、6、8行分別指定三個目標文件,以及它們所依賴的.c和.h文件以及defs文件。而5、7、9行則指定了如何從目標所依賴的文件建立目標。

 當filea.c或a.h文件在編譯之后又被修改,則 make 工具可自動重新編譯filea.o,如果在前后兩次編譯之間,filea.C 和a.h 均沒有被修改,而且 test.o 還存在的話,就沒有必要重新編譯。這種依賴關系在多源文件的程序編譯中尤其重要。通過這種依賴關系的定義,make 工具可避免許多不必要的編譯工作。當然,利用 Shell 腳本也可以達到自動編譯的效果,但是,Shell 腳本將全部編譯任何源文件,包括哪些不必要重新編譯的源文件,而 make 工具則可根據目標上一次編譯的時間和目標所依賴的源文件的更新時間而自動判斷應當編譯哪個源文件。

2.2.Makefile文件舉例

根據圖1的依賴關系,這里給出了一個完整的makefile文件,這個例子很簡單,由四個相關行組成,我們將其命名為mymakefile1。文件內容如下所示:

main: main.o f1.o f2.o

gcc -o main main.o f1.o f2.o

main.o: main.c def1.h

gcc -c main.c

f1.o: f1.c def1.h def2.h

gcc -c f1.c

f2.o: f2.c def2.h def3.h

gcc -c f2.c

注意,由於我們這里沒有使用缺省名makefile 或者Makefile ,所以一定要在make命令行中加上-f選項。如果在沒有任何源碼的目錄下執行命令“make -f Mymakefile1”的話,將收到下面的消息:

make: *** No rule to make target ‘main.c’, needed by ‘main.o’. Stop.

Make命令將makefile中的第一個目標即main作為要構建的文件,所以它會尋找構建該文件所需要的其他模塊,並判斷出必須使用一個稱為main.c的文件。因為迄今尚未建立該文件,而makefile又不知道如何建立它,所以只好報告錯誤。好了,現在建立這個源文件,為簡單起見,我們讓頭文件為空,創建頭文件的具體命令如下:

$ touch def1.h

$ touch def2.h

$ touch def3.h

我們將main函數放在main.c文件中,讓它調用function2和function3,但將這兩個函數的定義放在另外兩個源文件中。由於這些源文件含有#include命令,所以它們肯定依賴於所包含的頭文件。如下所示:

/* main.c */

#include <STDLIDEF2.H>

#include “def1.h”

extern void function2();

extern void function3();

int main()

{

function2();

function3();

exit (EXIT_SUCCESS);

}

/* f1.c */

#include “def1.h”

#include “def2.h”

void function2() {

}

/* f2.c */

#include “def2.h”

#include “def3.h”

void function3()

建好源代碼后,再次運行make程序,看看情況如何:

$ make -f Mymakefile1

gcc -c main.c

gcc -c f1.c

gcc -c f2.c

gcc -o main main.o f1.o f2.o

$

好了,這次順利通過了。這說明Make命令已經正確處理了makefile描述的依賴關系,並確定出了需要建立哪些文件,以及它們的建立順序。雖然我們在makefile 中首先列出的是如何建立main,但是make還是能夠正確的判斷出這些文件的處理順序,並按相應的順序調用規則部分規定的相應命令來創建這些文件。當這些命令執行時,make程序會按照執行情況來顯示這些命令。

如今,我們對def2.h加以變動,來看看makefile能否對此作出相應的回應:

$ touch def2.h

$ make -f Mymakefile1

gcc -c f1.c

gcc -c f2.c

gcc -o main main.o f1.o f2.o

$

這說明,當Make命令讀取makefile 后,只對受def2.h的變化的影響的模塊進行了必要的更新,注意它的更新順序,它先編譯了C程序,最后連接生產了可執行文件。現在,讓我們來看看刪除目標文件后會發生什么情況,先執行刪除,命令如下:

$ rm f1.o

然后運行make命令,如下所示:

$ make -f Mymakefile1

gcc -c f1.c

gcc -o main main.o f1.o f2.o

$

很好,make的行為讓我們非常滿意。

2.3.Makefile中的宏

在makefile中可以使用諸如XLIB、UIL等類似於Shell變量的標識符,這些標識符在makefile中稱為“宏”,它可以代表一些文件名或選項。宏的作用類似於C語言中的define,利用它們來代表某些多處使用而又可能發生變化的內容,可以節省重復修改的工作,還可以避免遺漏。

Make的宏分為兩類,一類是用戶自己定義的宏,一類是系統內部定義的宏。用戶定義的宏必須在makefile或命令行中明確定義,系統定義的宏不由用戶定義。我們首先介紹第一種宏。

這里是一個包含宏的makefile文件,我們將其命名為mymakefile2,如下所示:

all: main

# 使用的編譯器

CC = gcc

#包含文件所在目錄

INCLUDE = .

# 在開發過程中使用的選項

CFLAGS = -g -Wall –ansi

# 在發行時使用的選項

# CFLAGS = -O -Wall –ansi

main: main.o f1.o f2.o

$(CC) -o main main.o f1.o f2.o

main.o: main.c def1.h

$(CC) -I$(INCLUDE) $(CFLAGS) -c main.c

f1.o: f1.c def1.h def2.h

$(CC) -I$(INCLUDE) $(CFLAGS) -c f1.c

f2.o: f2.c def2.h def3.h

$(CC) -I$(INCLUDE) $(CFLAGS) -c f2.c

我們看到,在這里有一些注釋。在makefile中,注釋以#為開頭,至行尾結束。注釋不僅可以幫助別人理解我們的makefile,如果時間久了,有些東西我們自己也會忘掉,它們對makefile的編寫者來說也是很有必要的。

現在言歸正傳,先看一下宏的定義。我們既可以在make命令行中定義宏,也可以在makefile中定義宏。

2.4.在makefile中定義宏的基本語法是:

宏標識符=值列表

其中,宏標識符即宏的名稱通常全部大寫,但它實際上可以由大、小寫字母、阿拉伯數字和下划線構成。等號左右的空白符沒有嚴格要求,因為它們最終將被make刪除。至於值列表,既可以是零項,也可以是一項或者多項。如:

LIST_VALUE = one two three

當一個宏定義之后,我們就可以通過$(宏標識符)或者${宏標識符}來訪問這個標識符所代表的值了。

在makefile中,宏經常用作編譯器的選項。很多時候,處於開發階段的應用程序在編譯時是不用優化的,但是卻需要調試信息;而正式版本的應用程序卻正好相反,沒有調試信息的代碼不僅所占內存較小,進過優化的代碼運行起來也更快。

對於Mymakefile1來說,它假定所用的編譯器是gcc,不過在其他的UNIX系統上,更常用的編譯器是cc或者c89,而非gcc。如果你想讓自己的makefile適用於不同的UNIX操作系統,或者在一個系統上使用其他種類的編譯器,這時就不得不對這個makefile中的多處進行修改。

但對於mymakefile2來說則不存在這個問題,我們只需修改一處,即宏定義的值就行了。除了在makefile中定義宏的值之外,我們還可以在make命令行中加以定義,如:

$ make CC=c89

當命令行中的宏定義跟makefile中的定義有沖突時,以命令行中的定義為准。當在makefile文件之外使用時,宏定義必須作為單個參數進行傳遞,所以要避免使用空格,但是更妥當的方法是使用引號,如:

$ make “CC =   c89”

這樣就不必擔心空格所引起的問題了。現在讓我們將前面的編譯結果刪掉,來測試一下mymakefile2的工作情況。命令如下所示:

$ rm *.o main

$ make -f Mymakefile2

gcc -I. -g -Wall -ansi -c main.c

gcc -I. -g -Wall -ansi -c f1.c

gcc -I. -g -Wall -ansi -c f2.c

gcc -o main main.o f1.o f2.o

$

就像我們看到的那樣,Make程序會用相應的定義來替換宏引用$(CC )、$(CFLAGS )和$(INCLUDE),這跟C語言中的宏的用法比較相似。

上面介紹了用戶定義的宏,現在介紹make的內部宏。常用的內部宏有:

$? :比目標的修改時間更晚的那些依賴模塊表。

$@ :當前目標的全路徑名。可用於用戶定義的目標名的相關行中。

$< :比給定的目標文件時間標記更新的依賴文件名。

$* :去掉后綴的當前目標名。例如,若當前目標是pro.o,則$*表示pro。

Makefile文件作為一種描述文檔一般需要包含以下內容:

  ◆ 宏定義

  ◆ 源文件之間的相互依賴關系

  ◆ 可執行的命令

Makefile中允許使用簡單的宏指代源文件及其相關編譯信息,在Linux中也稱宏為變量。在引用宏時只需在變量前加$符號,但值得注意的是,如果變量名的長度超過一個字符,在引用時就必須加圓括號()。

下面都是有效的宏引用:

  $(CFLAGS)

  $2

  $Z

  $(Z)

其中最后兩個引用是完全一致的。

需要注意的是一些宏的預定義變量,在Unix系統中,$*、$@、$?和$<四個特殊宏的值在執行命令的過程中會發生相應的變化,而在GNU make中則定義了更多的預定義變量。關於預定義變量的詳細內容,宏定義的使用可以使我們脫離那些冗長乏味的編譯選項,為編寫makefile文件帶來很大的方便。

# Define a macro for the object files

   OBJECTS= filea.o fileb.o filec.o

   # Define a macro for the library file

   LIBES= -LS

   # use macros rewrite makefile

   prog: $(OBJECTS)

   cc $(OBJECTS) $(LIBES) -o prog

此時如果執行不帶參數的make命令,將連接三個目標文件和庫文件LS;但是如果在make命令后帶有新的宏定義:

  make "LIBES= -LL -LS"

則命令行后面的宏定義將覆蓋makefile文件中的宏定義。若LL也是庫文件,此時make命令將連接三個目標文件以及兩個庫文件LS和LL。

 在Unix系統中沒有對常量NULL作出明確的定義,因此我們要定義NULL字符串時要使用下述宏定義:

STRINGNAME=

Make命令

在make命令后不僅可以出現宏定義,還可以跟其他命令行參數,這些參數指定了需要編譯的目標文件。其標准形式為:

target1 [target2 …]:[:][dependent1 …][;commands][#…]

[(tab) commands][#…]

方括號中間的部分表示可選項。Targets和dependents當中可以包含字符、數字、句點和"/"符號。除了引用,commands中不能含有"#",也不允許換行。

 在通常的情況下命令行參數中只含有一個":",此時command序列通常和makefile文件中某些定義文件間依賴關系的描述行有關。如果與目標相關連的那些描述行指定了相關的command序列,那么就執行這些相關的command命令,即使在分號和(tab)后面的aommand字段甚至有可能是NULL。如果那些與目標相關連的行沒有指定command,那么將調用系統默認的目標文件生成規則。

 

如果命令行參數中含有兩個冒號"::",則此時的command序列也許會和makefile中所有描述文件依賴關系的行有關。此時將執行那些與目標相關連的描述行所指向的相關命令。同時還將執行build-in規則。

如果在執行command命令時返回了一個非"0"的出錯信號,例如makefile文件中出現了錯誤的目標文件名或者出現了以連字符打頭的命令字符串,make操作一般會就此終止,但如果make后帶有"-i"參數,則make將忽略此類出錯信號。

Make命本身可帶有四種參數:標志、宏定義、描述文件名和目標文件名。其標准形式為:

     Make [flags] [macro definitions] [targets]

 

Unix系統下標志位flags選項及其含義為:

 -f file 指定file文件為描述文件,如果file參數為"-"符,那么描述文件指向標准輸入。如果沒有"-f"參數,則系統將默認當前目錄下名為makefile或者名為Makefile的文件為描述文件。在Linux中, GNU make 工具在當前工作目錄中按照GNUmakefile、makefile、Makefile的順序搜索 makefile文件。

-i   忽略命令執行返回的出錯信息。

-s   沉默模式,在執行之前不輸出相應的命令行信息。

-r   禁止使用build-in規則。

-n   非執行模式,輸出所有執行命令,但並不執行。

-t   更新目標文件。

-q   make操作將根據目標文件是否已經更新返回"0"或非"0"的狀態信息。

-p   輸出所有宏定義和目標文件描述。

-d   Debug模式,輸出有關文件和檢測時間的詳細信息。

Linux下make標志位的常用選項與Unix系統中稍有不同,下面我們只列出了不同部分:

-c dir   在讀取 makefile 之前改變到指定的目錄dir。

-I dir   當包含其他 makefile文件時,利用該選項指定搜索目錄。

-h   help文擋,顯示所有的make選項。

-w   在處理 makefile 之前和之后,都顯示工作目錄。

Linux下make標志位的常用選項與Unix系統中稍有不同,下面我們只列出了不同部分:

-c dir   在讀取 makefile 之前改變到指定的目錄dir。

-I dir   當包含其他 makefile文件時,利用該選項指定搜索目錄。

-h   help文擋,顯示所有的make選項。

-w   在處理 makefile 之前和之后,都顯示工作目錄。

通過命令行參數中的target ,可指定make要編譯的目標,並且允許同時定義編譯多個目標,操作時按照從左向右的順序依次編譯target選項中指定的目標文件。如果命令行中沒有指定目標,則系統默認target指向描述文件中第一個目標文件。

 通常,makefile 中還定義有 clean 目標,可用來清除編譯過程中的中間文件,例如:

  

clean:

rm -f *.o

運行 make clean 時,將執行 rm -f *.o 命令,最終刪除所有編譯過程中產生的所有中間文件。

2.5.清理

編寫規則不至於編譯程序。Makefile通常描述如何做其它事情:比如刪除目錄中的目標文件和可執行文件來清理目錄。例子中是這樣寫的:

clean:

rm edit $(objects)

實際情況是,我們需要處理一些意外事件:存在一個叫做’clean’的文件;如果rm出錯,並不希望make過程停止下來,修改過的版本如下:

.PHONY : clean

clean :

-rm edit $(objects)

這樣的規則當然不能放在makefile的開始,因為這並不是我們缺省要做的工作。由於’clean’並不是’edit’的依賴,在運行make時沒有參數時,

這條規則不會執行;要執行這個規則,必須運行’make clean’。

 

 

 

 

三、規則

makefile中的規則描述如何生成特定的文件,即規則的目標。規則列出了目標的依賴文件,指定生成或更新目標的命令。

規則的次序是不重要的,除非是確定缺省目標:缺省目標是第一個makefile中的第一個規則;如果第一個規則有多個目標,第一個目標是缺省的。

有兩個例外:以’.’開頭的目標不是缺省目標;模式規則對缺省目標沒有影響。

通常我們所寫的地一個規則是編譯整個或makefile中指定的所有程序。

3.1.例子

foo.o : foo.c defs.h # module for twiddling the frobs

cc -c -g foo.c

它的目標是’foo.o’,依賴於’foo.c’和’defs.h’,有一個命令’cc –c –g foo.c’。命令行以TAB字符開始標識它是一個命令。

這條規則說明兩件事:

8.如何決定’foo.o’是舊的:如果它不存在,或者’foo.c’或者’defs.h’比它新。

9.如何更新’foo.o’文件:通過運行’cc’程序。命令未提及’defs.h’,擔可以猜想’foo.c’包含了它,這是’defs.h’被置於依賴關系中的理由。

3.2.規則的語法

語法如下:

TARGETS : DEPENDENCIES

COMMAND

...

或者

TARGETS : DEPENDENCIES ; COMMAND

COMMAND

...

TARGETS是以空格隔開的文件名,統配符可以使用。通常一個規則只有一個目標,偶爾也有多個。

命令行以TAB鍵開始。第一條命令可在依賴關系的下一行;或者在同一行,在分號后面;兩種方式效果相同。

因為’$’符號被用做變量引用,如果要在規則中使用’$’符號,必須寫兩個:’$$’。可以用’’符號來分割一個長行,這不是必須的,因為make對行的

長度沒有限制。

3.3.通配符

規則中的文件名可以包含統配符,如’*’,’?’。

文件名前的字符’~’有特殊的含義。單獨使用,或跟隨一個’/’,代表用戶的home目錄,比如’~/bin’擴展為/home/you/bin’;如果’~’跟隨一個單詞,

表示單詞指示的那個用戶的home目錄,如’~john/bin’擴展為’/home/john/bin’。

通配符在目標,依賴關系,命令中自動擴展,其它情況下,統配符的擴展除非顯式使用’wildcard’函數。通配符的特殊意義可以使用’’符號關閉。

例子:

clean:

rm -f *.o

print: *.c

lpr -p $?

touch print

通配符在定義變量時並不擴展,例如:

objects = *.o

則objects的值是字符串’*.o’;但是如果你將objects用於目標,依賴或命令中,擴展會進行。要將objects設置成擴展過的內容,使用:

objects := $(wildcard *.o)

3.3.1.通配符的缺陷

這是一個使用通配符的例子,但結果不是你所期望的。假設可執行文件’foo’是從當前目錄中的所有’.o’文件生成的:

objects = *.o

foo : $(objects)

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

objects變量的值是字符串’*.o’。通配符擴展在規則’foo’中進行,於是所有存在的’.o’文件成為’foo’的依賴而且在需要時重新編譯。

但如果刪除了所有的’.o’文件呢?當通配符不匹配任何文件時,一切都保持原樣:則’foo’依賴於一個叫做’*.o’的文件;由於這個文件不大可能存在,

’make’程序會報告一個無法生成’*.o’文件的錯誤,這不是期待的結果。

實際上可以用通配符獲得期望結果,但是需要復雜的技術,包括’wildcard’函數和字符串替換函數。

3.3.2.wildcard函數

通配符自動在規則中進行。但是在變量賦值的和函數的參數中通配符不會擴展,如果在這些情況下需要通配符擴展,必須使用’wildcard’函數。

語法如下:

$(wildcard PATTERN...)

這個在makefile任何地方出現的字符串,會被匹配任何一個文件名格式的以空格隔開的現有文件列表替換。如果沒有任何文件匹配一個模式,

這個模式從’wildcard’的輸出中忽略,注意,這和上述的通配符的處理是不一樣的。

‘wildcard’函數的一個功能是找出目錄中所有的’.c’文件:

$(wildcard *.c)

可以通過替換后綴’.c’為’.o’從C文件列表得到目標文件的列表:

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

這樣,上節中的makefile改寫為:

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

foo : $(objects)

cc -o foo $(objects)

這個makefile利用了編譯C程序的隱含規則,所以不需要對編譯寫出顯式的規則。(’:=’是’=’的一個變體)

注意:’PATTERN’是大小寫敏感的。

3.4.目錄搜索

對於大的系統,通常將源文件和目標文件放在不同的目錄中。目錄搜索功能可以讓make自動在多個目錄中搜尋依賴文件,當你將文件重新分布是,

不需要改變規則,更改搜索路徑即可。

3.4.1.‘VPATH’

make變量’VPATH’列出make應當搜索的目錄列表。很多情況下,當前目錄不包含依賴文件,’VPATH’描述一個對所有文件的搜索列表,包含那些

是規則的目標的文件。

如果一個目標或者依賴文件在當前目錄沒找到的話,’make’在’VPATH’中列出的目錄中查找同名的文件。如果找到的話,那個文件成為依賴文件;

規則可以象這些文件在當前目錄中一樣來使用他們。

在’VPATH’變量中,目錄名以冒號或空格隔開;目錄列出的順序決定make查找的順序。(注:在pSOSystem 2.5移植到Win32的GNU make目錄

名必須使用分號隔開,以下均簡稱Win32 GNU make)。舉例說明:

VPATH = src:../headers 則規則

foo.o : foo.c

被解釋為

foo.o : src/foo.c

假設’foo.c’在當前目錄不存在,在’src’目錄中可以找到。

3.4.2.選擇性搜索

與’VPATH’變量相似但更具選擇性的是’vpath’指令(注意是小寫),可以指定對於符合特定模式文件的查找路徑。這樣可以為不同類型的文件指定不同的搜索路徑。

‘vpath’指令共有三中形式:

  • ‘vpath PATTERN DIRECTORIES’  為匹配PATTERN的文件名指定搜索路徑DIRECTORIES,目錄的分隔和’VPATH’的相同
  • ‘vpath PATTERN’  清除為匹配PATTERN的文件名指定的搜索路徑
  • ‘vpath’   清除所有以前用’vpath’指定的搜索路徑

‘vpath’的模式是包含’%’的字符串:這個字符串必須匹配需要搜索的依賴文件名,’%’字符匹配0個或多個任意字符。例如:’%.h’匹配任何以’.h’結尾的文件(如果沒有%,則PATTERN必須和依賴文件完全一致,這種用法不太多)。

當當前目錄中不存在依賴文件時,如果’vpath’中的PATTERN匹配依賴文件名,則指令中DIRECTORIES列出的目錄和’VPATH’中同樣處理。舉例:

vpath %.h ../headers

告訴make在當前目錄中未找到的’.h’文件在../headers目錄中查找。

如果多個’vapth’的模式匹配依賴文件名,make將逐一處理,在所有指定的目錄中搜索。Make按照’vapth’在makefile中的次序;來處理它們,多個相同模式的’vapth’是相互獨立的。

vpath %.c foo

vpath % blish

vpath %.c bar

將按照’foo’,‘blish’,’bar’的次序查找’.c’文件。而

vpath %.c foo:bar

vpath % blish

按照’foo’,’bar’,’blish’的順序搜索。

3.4.3.使用自動變量

目錄搜索的結果並不改變規則中的命令:命令按原樣被執行。因此,必須寫出與目錄搜索功相適應的命令。這可以通過使用’$^’這樣的自動變量來

完成。’$^’表示規則中的所有依賴文件,包含它們所在的目錄名(參見目錄搜索);’$@’表示目標。例如:

foo.o : foo.c

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

通常情況下,依賴文件也包含頭文件,但命令中並不提及這些文件:變量’$<’表示第一個依賴文件:

VPATH = src:../headers

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

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

3.4.4.目錄搜索和隱含規則

使用’VPATH’和’vpath’指定目錄搜索也會影響隱含規則。例如:文件’foo.o’沒有顯式規則,make會考慮隱式規則:如果’foo.c’存在則編譯它;如果這個文件不存在,則在相應的目錄中查找;如果’foo.c’在任一的目錄中存在,則C編譯的隱式規則被應用。

隱式規則的命令使用自動變量通常是必要的,這樣無需其它努力即可以使用目錄搜索得到的文件名。

3.5.PHONY目標

Phony目標並非實際的文件名:只是在顯式請求時執行命令的名字。有兩種理由需要使用phony目標:避免和同名文件沖突,改善性能。

如果編寫一個規則,並不產生目標文件,則其命令在每次make該目標時都執行。例如:

clean:

rm *.o temp

因為’rm’命令並不產生’clean’文件,則每次執行’make clean’的時候,該命令都會執行。如果目錄中出現了’clean’文件,則規則失效了:沒有依賴文件,文件’clean’始終是最新的,命令永遠不會執行;為避免這個問題,可使用’.PHONY’指明該目標。如:

.PHONY : clean

這樣執行’make clean’會無視’clean’文件存在與否。

已知phony目標並非是由其它文件生成的實際文件,make會跳過隱含規則搜索。這就是聲明phony目標會改善性能的原因,即使你並不擔心實際文件存在與否。完整的例子如下:

.PHONY : clean

clean :

rm *.o temp

phony目標不應是真正目標文件的依賴。如果這樣,每次make在更新此文件時,命令都會執行。只要phony目標不是真正目標的依賴,規則的命令只有在指定此目標時才執行。

Phony目標可以有依賴關系。當一個目錄中有多個程序是,將其放在一個makefile中會更方便。因為缺省目標是makefile中的第一個目標,通常將這個phony目標叫做’all’,其依賴文件為各個程序:

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’將可以將三個程序都生成了。

當一個phony目標是另一個的依賴,其作用相當於子程序,例如:

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff

rm program

cleanobj :

rm *.o

cleandiff :

rm *.diff

3.6.FORCE目標

當規則沒有依賴關系也沒有命令,而且其目標不是存在的文件名,make認為此規則運行時這個目標總是被更新。這意味着如果規則依賴於此目標,其命令總是被執行。

clean: FORCE

rm $(objects)

FORCE:

例中目標’FORCE’滿足這種特殊條件,這樣依賴於它的目標’clean’被強制執行其命令。名字’FORCE’沒有特殊含義,只不過通常這樣用而已。這種方式使用’FORCE’和’.PHONY : clean’效果相同。使用’.PHONY’更加明確高效,擔不是所有的’make’都支持;這樣許多makefile中使用了’FORCE’。

3.7.空目標

空目標(empty target)是phony目標的變種:用來執行顯式請求的一個動作。和phony目標不同的是:這個目標文件可以真實存在,擔文件的內容無關緊要,通常是空的。空目標文件的目的是利用其最后修改時間來記錄命令最近一次執行的時間,這是通過使用’touch’命令更新目標文件來達到的。

print: foo.c bar.c

lpr -p $?

touch print

利用這條規則,執行’make print’時如果自上次’make print’之后任一文件改變了,’lpr’命令會執行。自動變量’$?’是為了只打印出那些變化了的文件。

3.8.內建的特殊目標

某些名字作為目標存在時有特殊含義。

PHONY 該目標的依賴被認為是phony目標,處理這些目標時,命令無條件被執行,不管文件名是否存在及其最后修改時間

SUFFIXES 該目標的依賴被認為是一個后綴列表,在檢查后綴規則時使用

DEFAULT 該目標的規則被使用在沒有規則(顯式的或隱含的)的目標上。如果’DEFAULT’命令定義了,則對所有不是規則目標的依賴文件都會執行該組命令

PRECIOUS 該目標的依賴文件會受到特別對待:如果make被kill或命令的執行被中止,這些目標並不刪除;而且如果該目標是中間文件,在不需要時不會被刪除。可以將隱含規則的目標模式(如%.o)做為’.PRECIOUS’的依賴文件,這樣可以保存這些規則產生的中間文件。

INTERMEDIATE 該目標的依賴文件被當作中間文件;如果該目標沒有依賴文件,則makefile中所有的目標文件均被認為是中間文件。

IGNORE 在執行該目標的依賴規則的命令時,make會忽略錯誤,此規則本身的命令沒有意義。如果該規則沒有依賴關系,表示忽略所有命令執行的錯誤,這種用法只是為了向后兼容;由於會影響到所有的命令,所以不是特別有用,推薦使用其它更有選擇性忽略錯誤的方法。

SILENT 在執行該目標的依賴規則的命令時,make並不打印命令本身。該規則的命令沒有意義。在’.SILIENT’沒有依賴關系時,表示執行makefile中的所有命令都不會打印,該規則只是為了向后兼容提供的。

EXPORT_ALL_VARIABLES 只是作為一個目標存在,指示make將所有變量輸出到子進程中。

定義的隱含規則的后綴作為目標時,也認為它是特殊目標;兩個后綴的連接也是一樣,比如’.c.o’。這些目標是后綴規則,一中定義隱式規則的過時方法(但仍然廣泛使用)。后綴通常以’.’開始,所以特殊目標也以’.’開始。

3.9.一個規則多個目標

一條有多個目標的規則和寫多條規則,每條一個目標作用是等同的。同樣的命令應用於所有目標,但其效用會因將實際目標以’$@’代替而不同。規則中所有目標的依賴關系是一樣的。

這在兩種情況下有用:

★只有依賴關系,不需要命令。例如:

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

所有的目標同樣的命令。命令不需要完全相同,因為在命令中可以使用

’$@’:

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等同。這里假設程序’generate’產生兩種輸出:一種使用’-big’選項,一種使用’-little’選項。如果想象使用’$@’變化命令那樣來變化依賴關系,不能通過多目標的普通規則實現,但是可以通過模式規則來實現。

3.10.一個目標多條規則

一個文件可以是多條規則的目標,所有規則的依賴關系被合並。如果目標比任一個依賴文件舊,命令被執行。

一個文件只能有一組命令執行。如果多個規則對於同一個文件都給出了命令,make使用最后一組並打印錯誤信息(特殊情況:如果文件名以’.’開始,並不打印錯誤信息,這一點是為了和其它make兼容)。沒有任何理由需要將makefile寫成這樣,這是make給出錯誤信息的理由。

一條只有依賴關系的附加規則可以一次給出許多文件的附加依賴文件。例如’objects’變量表示系統中編譯器的所有輸出.,說明當’config.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’是每個目標文件的依賴文件,但簡單的’make’命令不是這樣。

3.11.靜態模式規則

靜態模式規則(static pattern rules)可以指定多個目標,並且使用目標名字來建議依賴文件的名字;比普通多目標規則更通用因為不需要依賴關系是相同的:依賴關系必須類似但不需要相同。

3.11.1.語法

TARGETS ...: TARGET-PATTERN: DEP-PATTERNS ...

COMMANDS

...

TARGETS列表指出規則應用的目標,可以包含通配符,於普通規則的目標相同。TARGET-PATTERN和DEP-PATTERNS來表明目標的依賴關系如何計算:匹配TARGET-PATTERN的目標從名字中抽出一部分,叫做詞干(stem),詞干被替換到DEP-PATTERNS來形成依賴文件名。

每個模式通常包含一個’%’字符。當TARGET-PATTERN匹配一個目標時,’%’字符可以匹配目標名中的任何部分;這部分即是詞干,模式的其余部分必須完全匹配。例如’foo.o’匹配’%.o’,’foo’是詞干;目標’foo.c’和’foo.out’並不匹配這個模式。

目標的依賴文件名通過將DEP-PATTERNS中的’%’替換為詞干形成:如果依賴模式為’%.c’,在替換詞干’foo’可以得到’foo.c’。依賴模式中不包含’%’也是合法的,此依賴文件對所有的目標均有效。

如果需要在模式規則中使用’%’字符,必須在其前面加’’字符,如果’%’前的’’字符是有實際意義的,必須在其前面加’’,其它的’’不必如此處理。如’the\%weird\%pattern’在有效的’%’前是’the%weird’,其后是’pattern’。最后的’’保持原樣是因為其並不影響’%’字符。

以下例子從相應的’.c’文件編譯’foo.o’和’bar.o’:

objects = foo.o bar.o

$(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’; `$(filter %.elc,$(files))' 的結果是`foo.elc'。以下例子說明’$*’的使用:

bigoutput littleoutput : %output : text.g

generate text.g -$* > $@

命令`generate'執行時,’$*’擴展為詞干’big’或’little’。

3.11.2.靜態模式規則和隱式規則

靜態模式規則和隱式規則在作為模式規則是具有很多共同點,都有目標模式和構造依賴文件名的模式,不同之處在於make決定何時應用規則的方法。

隱式規則可應用於匹配其模式的任何目標,但只限於沒有指定命令的目標,如果有多條可應用的隱式規則,只有一條被使用,取決於規則的順序。反之,靜態模式規則適用於規則中明確目標列表,不適用於其它目標且總是適用於指定的每個目標。如果有兩條沖突的規則,且都有命令,這是一個錯誤。

靜態模式規則比隱式規則優越之處如下:

可為一些不能按句法分類,但可以顯式列出的文件重載隱式規則

不能判定目錄中的精確內容,一些無關的文件可能導致make適用錯誤的隱式規則;最終結果可能依賴於隱式規則的次序。適用靜態模式規則時,這種不確定性是不存在的:規則適用於明確指定的目標。

3.12.雙冒號規則

雙冒號規則(Double-colon rules)的目標后是’::’而不是’:’,當一個目標出現在多條規則中時,其處理和普通規則的處理不同。

當一個目標出現在多條規則中時,所有規則必須是相同類型的:都是普通的或者都是雙冒號的。如果是雙冒號,規則之間相互獨立;如果目標需要更新,則規則的命令被執行;結果可能是沒有執行,或者執行了其中一些,或者所有的規則都執行了。

同一目標的雙冒號規則事實是完全孤立的,每條規則被被單獨處理,就象不同目標的規則一樣;規則按照在makefile中出現的次序被處理,此類規則真正有意義的是那些於命令執行次序無關的。

這種規則有時比較晦澀不是特別有用;它提供了一種機制:通過不同依賴文件的更新來對目標進行不同的處理,這種情形很罕見。每個這種規則應當提供命令,如果沒有,適用的隱式規則將使用。

3.13.自動生成依賴關系

在makefile中,許多規則都是一些目標文件依賴於一些頭文件。例如:’main.c’ 通過’#include’使用’defs.h’,這樣規則:

main.o: defs.h

告訴make在’defs.h’變化時更新’main.o’。在程序比較大時,需要寫許多這樣的規則;而且當每次增刪’#include’時,必須小心的更新makefile。許多現代的編譯器可以幫你寫這些規則,通常這是通過編譯器的’-M’選項,例如命令:

cc –M main.c

輸出以下內容:

main.o : main.c defs.h

這樣就不必寫這些規則,有編譯器代勞了。

注意這樣的依賴關系中提及’main.o’,不會被隱式規則認為是中間文件,這意味這make在使用過它之后不會將其刪除。使用老的’make’程序時,習慣做法是使用’make depend’命令利用編譯器的功能產生依賴關系,該命令會產生一個’depend’文件包含所有自動產生的依賴關系,然后在makefile中

使用’include’將其讀入。

使用GNU的make時,重新生成makefile的功能使得這種做法變得過時:從不需要顯式請求更新依賴關系,因為它總是重新生成任何過時的makefile。

自動依賴關系生成推薦的做法是對每個源文件做一個makefile。對每個源文件’NAME.c’,有一個makefile ’NAME.d’,其中列出了目標文件’NAME.o’依賴的所有文件,這樣在源文件更新時,需要掃描來產生新的依賴關系。例子是一個從’NAME.c’產生依賴關系文件’NAME.d’的模式規則:

%.d: %.c

$(SHELL) -ec '$(CC) -M $(CPPFLAGS) $<

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

-e選項是當$(CC)命令失敗時(exit狀態非0),shell立刻退出。通常shell的返回值是管道中最后一條命令(sed)的返回值,這樣make不會注意到編譯器出錯。

使用GNU的C編譯器時(gcc),可以用’-MM’選項來代替’-M’選項,這樣省略系統頭文件的依賴關系。’sed’命令的目的是將

main.o : main.c defs.h

轉換為

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

這樣使得每個’.d’文件依賴於’.o’文件相應源文件和頭文件,make則可以在原文間或頭文件變化時更新依賴關系文件。

如果定義了生成’.d’文件的規則,可以使用’include’指令來讀入所有的文件:

sources = foo.c bar.c

include $(sources:.c=.d)

例中使用替換變量來將源文件列表’ foo.c bar.c’轉換為依賴關系文件的列表。因為’.d’文件和其它文件一樣,不需要更多工作,make會在需要時重新生成它們。

四、編寫命令

規則的命令是由一一執行的shell命令組成。除了以分號隔開寫在依賴關系后的命令,每個命令行必須以tab字符開始空行和注釋行可以出現在命令行中,處理時被忽略(注意:以tab字符開始的空行不是’空’行,是一條空命令)。可以在命令中使用任何程序,但這些程序是由$(SHELL)來執行的。

4.1.回顯

通常make打印出要執行的命令,稱之為回顯,這和親自敲命令的現象是一樣的。當行之前有’@’字符時,命令不再回顯,字符’@’在傳遞給shell前丟棄。

典型的用法是只對打印命令有效,比如’echo’命令:

@echo About to make distribution files

當make使用’-n’或’—just-print’選項時,顯示要發生的一切,但不執行命令。只有在這種情況下,即使命令以’@’開始,命令行仍然顯示出來。

這個選項對查看make實際要執行的動作很有用。

‘-s’或’—silent’選項阻止make所有回顯,就象所有命令以’@’開始一樣;一條沒有依賴關系的’.SILENT’規則有相同的作用,但是’@’更加靈活。

4.2.執行

在需要執行命令更新目標時,make為每一行創建一個子shell來執行。這意味着諸如為進程設置局部變量的shell命令’cd’(改變進程的當前目錄)不會影響以后的命令。如果需要’cd’影響下一個命令,將它們放在一行上用分號隔開,這樣make認為是一條命令傳遞給shell程序(注意:這需要shell支持):

foo : bar/lose

cd bar; gobble lose > ../foo

另一個形式使用續行符:

foo : bar/lose

cd bar;

gobble lose > ../foo

shell程序的名字是通過’SHELL’變量來取得的。

(*UNIX)不象大多數變量,’SHELL’變量不是通過環境來設置的(即需要在makefile中設置),因為’SHELL’環境是個人選擇的,如果不同人的選擇會影響makefile的功能的話,這樣很糟糕。

4.3.並行執行

GNU make可以一次執行幾條命令。通常make一次執行一條命令,等待其返回,再執行下一條。使用’-j’或’—jobs’可以同時執行多條命令。如果’-j’后梗一個正數,表示一次可以執行的命令條數;如果’-j’之后沒有參數,則不限制可執行的命令數。缺省的數量是一。

一個討厭的問題是如果同時執行多條命令,它們的輸出會混在一起;另一個問題是兩個進程不能從同一個設備獲得輸入。

4.4.錯誤

每條shell命令返回時,make會檢查其返回狀態。如果命令執行成功,則下一條命令被執行,最后一條命令執行完后,規則執行結束。

如果有錯誤(返回非0狀態),make放棄當前規則,也可能是所有規則。

有時候命令執行錯誤並不是問題,比如使用’mkdir’命令確保目錄存在:如果目錄一存在,則’mkdir’會報告錯誤,但仍希望make繼續。

要忽略命令的錯誤,在命令之前使用’-‘字符,’-‘字符在傳遞給shell之前被丟棄:

clean:

-rm -f *.o

如果使用’-i’或’—ignore-errors’選項,make會忽略所有命令產生的錯誤;一條沒有依賴關系的’.IGNORE’規則有相同的作用,但’-‘更靈活。

在忽略錯誤時,make將錯誤也認為是成功,只是通知你命令的退出狀態和和錯誤被忽略。如果make並未告知忽略錯誤,在錯誤發生時,表明該目標不能成功更新,直接或間接依賴於此的目標當然也不能成功;這些目標的命令不會被執行,因為其先決條件不滿足。

通常make會立即以非0狀態退出。然而,如果給定’-k’或’—keep-going’選項,make在退出前會處理其它的依賴關系,進行必要的更新。例如,在編譯一個目標文件遇到錯誤,’make -k’會繼續編譯其它的目標文件。

通常認為你的目的是更新指定的目標,當make知道這是不可能時,會立即報告失敗;’-k’選項指示真正目的是測試更新程序的更多可能性:在編譯之前找出更多不相關的問題。

如果命令失敗了,假設它更新的目標文件,這個文件是不完整的不能使用-至少不是完全更新的。但文件的最后修改時間表明停已經是最新的,下一次make運行時,不會再更新這個文件。這種情況和命令被kill相同;則通常情況下在命令失敗時將目標刪除是正確的;當’.DELETE_ON_ERROR’是目標時make幫你做這件事。雖然你總是希望make這么做,但這不是過去的習慣;所以必須顯式要求make這樣做(其它的make自動這樣做)。

4.5.中斷make

如果make執行命令時遇到錯誤,可能會刪除命令更新的目標文件: make檢查文件的修改時間是否變化。刪除目標的目的是確保make下次執行時重新生成它。為什么這樣做?假設在編譯器運行時按了’Ctrl-c’,此時編譯器寫生成目標文件’foo.o’。’Ctrl-c’ kill了編譯器,留下一個不完整的文件,但它的修改時間比源文件’foo.c’新;此時make也受到’Ctrl-c’信號刪除這個不完整的文件,如果make不這樣做,下次make運行時認為’foo.o’不需要更新,會在鏈接時出現奇怪的錯誤。

可以使用’.PRECIOUS’規則來防止目標文件被刪除。在make更新目標時,會檢測其是否為’.PRECIOUS’的依賴,決定在命令出錯或中斷時是否刪除該目標。如果你希望目標的更新是原子操作,或是用來記錄修改時間,或必須一直存在防止其它類型的錯誤,這些理由使得你必須這樣做。

4.6.遞歸使用

遞歸使用make就是在makefile中使用make命令。這種技術在你將一個大系統分解為幾個子系統,為每個自系統提供一個makefile時有用處。比如有一個子目錄’subdir’中有自己的makefile,希望make在自目錄中運行,可以這樣做:

subsystem:

cd subdir; $(MAKE)

或者

subsystem:

$(MAKE) -C subdir

可以照抄這個;例子來遞歸使用make

4.6.1.‘MAKE’變量

遞歸的make必須使用’MAKE’變量,不是顯式的make命令:

subsystem:

cd subdir; $(MAKE)

該變量的值是被調用的make的名字。在命令中使用’MAKE’有特殊的功能:它改變了`-t' (`--touch'), `-n' (`--just-print')和`-q' (`--question')選項的含義。使用上例來考慮’make –t’命令(’-t’選項將目標標記為最新但不運行命令),更加’-t’選項的功能,該命令將創建一個’subsystem’文件,實際希望的操作是運行’cd subdir; make –t’;但這會執行命令,與’-t’的原意不符。

這個特殊功能做了期望的工作。當命令行包含變量’MAKE’時,選項’-t’,’-n’和’-q’並不適用。不管這些導致不會執行命令的標志,包含’MAKE’變量的命令始終會執行。正常的’MAKEFLAGS’機制將這些標志傳遞到子make,這樣打印命令的請求被傳播到子系統中。

4.6.2.傳遞變量到子make

上級(top-level)make中的變量可以顯式通過環境傳遞到子make中。在子make中,這些變量被缺省定義,但不會重載子makefile中的定義除非使用’-e’選項。為向下傳遞,或輸出變量,make在運行命令時將其加入到環境變量中;子make,可以使用環境變量來初始化變量表。除非顯式要求,make只輸出初始環境中或命令行設置的變量而且變量名只由字母,數字和下划線組成。一些shell不能處理有其它字符的環境變量。

特殊變量’SHELL’,’MAKEFLAGS’總是輸出,如果’MAKEFILE’變量有值,也會輸出。Make自動通過’MAKEFLAGS’來輸出命令行定義的變量。

如果想要輸出特定變量,使用’export’指令:

export VARIABLE ...

如果要阻止輸出一個變量,使用’unexport’指令:

unexport VARIABLE ...

為方便起見,可以在定義變量時輸出它:

export VARIABLE = value

VARIABLE = value

export VARIABLE

作用相同。

如果要輸出所有的變量,使用’export’指令本身就可以了。

變量’MAKELEVEL’在一級一級傳遞時會改變,這個變量的值是表示嵌套層數的字符串,頂級’make’是,變量的值為’0’;子make的值為’1’;子子make的值為’2’,依此類推。

‘MAKELEVEL’的用途是在條件指令中測試它,這樣寫出在遞歸運行時和直接運行時表現不同的makefile。

 

 

 

 

 

 

 

附:

以下內容拷貝自GNU Make Manual

 

命令行參數

`-b'

`-m'

These options are ignored for compatibility with other versions of

`make'.

 

`-C DIR'

`--directory=DIR'

Change to directory DIR before reading the makefiles. If multiple

`-C' options are specified, each is interpreted relative to the

previous one: `-C / -C etc' is equivalent to `-C /etc'. This is

typically used with recursive invocations of `make' (*note

Recursive Use of `make': Recursion.).

 

`-d'

`--debug'

Print debugging information in addition to normal processing. The

debugging information says which files are being considered for

remaking, which file-times are being compared and with what

results, which files actually need to be remade, which implicit

rules are considered and which are applied--everything interesting

about how `make' decides what to do.

 

`-e'

`--environment-overrides'

Give variables taken from the environment precedence over

variables from makefiles. *Note Variables from the Environment:

Environment.

 

`-f FILE'

`--file=FILE'

`--makefile=FILE'

Read the file named FILE as a makefile.

 

`-h'

`--help'

Remind you of the options that `make' understands and then exit.

`-i'

`--ignore-errors'

Ignore all errors in commands executed to remake files.

 

`-I DIR'

`--include-dir=DIR'

Specifies a directory DIR to search for included makefiles. If several `-I' options are

used to specify several directories, the directories are searched

in the order specified.

 

`-j [JOBS]'

`--jobs=[JOBS]'

Specifies the number of jobs (commands) to run simultaneously.

With no argument, `make' runs as many jobs simultaneously as

possible. If there is more than one `-j' option, the last one is

effective.

`-k'

`--keep-going'

Continue as much as possible after an error. While the target that

failed, and those that depend on it, cannot be remade, the other

dependencies of these targets can be processed all the same.

 

`-l [LOAD]'

`--load-average[=LOAD]'

`--max-load[=LOAD]'

Specifies that no new jobs (commands) should be started if there

are other jobs running and the load average is at least LOAD (a

floating-point number). With no argument, removes a previous load

limit. *Note Parallel Execution: Parallel.

 

`-n'

`--just-print'

`--dry-run'

`--recon'

Print the commands that would be executed, but do not execute them.

`-o FILE'

`--old-file=FILE'

`--assume-old=FILE'

Do not remake the file FILE even if it is older than its

dependencies, and do not remake anything on account of changes in

FILE. Essentially the file is treated as very old and its rules

are ignored.

 

`-p'

`--print-data-base'

Print the data base (rules and variable values) that results from

reading the makefiles; then execute as usual or as otherwise

specified. This also prints the version information given by the

`-v' switch (see below). To print the data base without trying to

remake any files, use `make -p -f /dev/null'.

 

`-q'

`--question'

"Question mode". Do not run any commands, or print anything; just

return an exit status that is zero if the specified targets are

already up to date, one if any remaking is required, or two if an

error is encountered.

 

`-r'

`--no-builtin-rules'

Eliminate use of the built-in implicit rules.You can still define your own by writing

pattern rules. The `-r' option also clears out the default list

of suffixes for suffix rules .But you can still define your own suffixes with a

rule for `.SUFFIXES', and then define your own suffix rules.

 

`-s'

`--silent'

`--quiet'

Silent operation; do not print the commands as they are executed.

`-S'

`--no-keep-going'

`--stop'

Cancel the effect of the `-k' option. This is never necessary

except in a recursive `make' where `-k' might be inherited from

the top-level `make' via `MAKEFLAGS' or if you set `-k' in `MAKEFLAGS' in your

environment.

 

`-t'

`--touch'

Touch files (mark them up to date without really changing them)

instead of running their commands. This is used to pretend that

the commands were done, in order to fool future invocations of

`make'.

 

`-v'

`--version'

Print the version of the `make' program plus a copyright, a list

of authors, and a notice that there is no warranty; then exit.

`-w'

`--print-directory'

Print a message containing the working directory both before and

after executing the makefile. This may be useful for tracking

down errors from complicated nests of recursive `make' commands.

 

`--no-print-directory'

Disable printing of the working directory under `-w'. This option

is useful when `-w' is turned on automatically, but you do not

want to see the extra messages.

 

`-W FILE'

`--what-if=FILE'

`--new-file=FILE'

`--assume-new=FILE'

Pretend that the target FILE has just been modified. When used

with the `-n' flag, this shows you what would happen if you were

to modify that file. Without `-n', it is almost the same as

running a `touch' command on the given file before running `make',

except that the modification time is changed only in the

imagination of `make'.

 

`--warn-undefined-variables'

Issue a warning message whenever `make' sees a reference to an

undefined variable. This can be helpful when you are trying to

debug makefiles which use variables in complex ways.

 

 

指令

`define VARIABLE'

`endef'

Define a multi-line, recursively-expanded variable.

*Note Sequences::.

 

`ifdef VARIABLE'

`ifndef VARIABLE'

`ifeq (A,B)'

`ifeq "A" "B"'

`ifeq 'A' 'B''

`ifneq (A,B)'

`ifneq "A" "B"'

`ifneq 'A' 'B''

`else'

`endif'

Conditionally evaluate part of the makefile.

 

`include FILE'

Include another makefile.

 

`override VARIABLE = VALUE'

`override VARIABLE := VALUE'

`override VARIABLE += VALUE'

`override define VARIABLE'

`endef'

Define a variable, overriding any previous definition, even one

from the command line.

`export'

Tell `make' to export all variables to child processes by default.

 

`export VARIABLE'

`export VARIABLE = VALUE'

`export VARIABLE := VALUE'

`export VARIABLE += VALUE'

`unexport VARIABLE'

Tell `make' whether or not to export a particular variable to child

processes.

`vpath PATTERN PATH'

Specify a search path for files matching a `%' pattern.

*Note The `vpath' Directive: Selective Search.

 

`vpath PATTERN'

Remove all search paths previously specified for PATTERN.

 

`vpath'

Remove all search paths previously specified in any `vpath'

directive.

 

函數

`$(subst FROM,TO,TEXT)'

Replace FROM with TO in TEXT.

 

`$(patsubst PATTERN,REPLACEMENT,TEXT)'

Replace words matching PATTERN with REPLACEMENT in TEXT.

 

`$(strip STRING)'

Remove excess whitespace characters from STRING.

 

`$(findstring FIND,TEXT)'

Locate FIND in TEXT.

 

`$(filter PATTERN...,TEXT)'

Select words in TEXT that match one of the PATTERN words.

 

`$(filter-out PATTERN...,TEXT)'

Select words in TEXT that *do not* match any of the PATTERN words.

 

`$(sort LIST)'

Sort the words in LIST lexicographically, removing duplicates.

 

`$(dir NAMES...)'

Extract the directory part of each file name.

 

`$(notdir NAMES...)'

Extract the non-directory part of each file name.

 

`$(suffix NAMES...)'

Extract the suffix (the last `.' and following characters) of each

file name.

 

`$(basename NAMES...)'

Extract the base name (name without suffix) of each file name.

 

`$(addsuffix SUFFIX,NAMES...)'

Append SUFFIX to each word in NAMES.

 

`$(addprefix PREFIX,NAMES...)'

Prepend PREFIX to each word in NAMES.

 

`$(join LIST1,LIST2)'

Join two parallel lists of words.

 

`$(word N,TEXT)'

Extract the Nth word (one-origin) of TEXT.

 

`$(words TEXT)'

Count the number of words in TEXT.

 

`$(firstword NAMES...)'

Extract the first word of NAMES.

 

`$(wildcard PATTERN...)'

Find file names matching a shell file name pattern (*not* a `%'

pattern).

 

`$(shell COMMAND)'

Execute a shell command and return its output.

 

`$(origin VARIABLE)'

Return a string describing how the `make' variable VARIABLE was

defined.

 

`$(foreach VAR,WORDS,TEXT)'

Evaluate TEXT with VAR bound to each word in WORDS, and

concatenate the results.

 

 

自動變量

`$@'

The file name of the target.

 

`$%'

The target member name, when the target is an archive member.

 

`$<'

The name of the first dependency.

 

`$?'

The names of all the dependencies that are newer than the target,

with spaces between them. For dependencies which are archive

members, only the member named is used

with spaces between them. For dependencies which are archive

members, only the member named is used

`$^'

`$+'

The names of all the dependencies, with spaces between them. For

dependencies which are archive members, only the member named is

used. The value of `$^' omits duplicate

dependencies, while `$+' retains them and preserves their order.

 

`$*'

The stem with which an implicit rule matches

 

`$(@D)'

`$(@F)'

The directory part and the file-within-directory part of `$@'.

 

`$(*D)'

`$(*F)'

The directory part and the file-within-directory part of `$*'.

 

`$(%D)'

`$(%F)'

The directory part and the file-within-directory part of `$%'

 

`$(<D)'

`$(<F)'

The directory part and the file-within-directory part of `$<'

 

`$(^D)'

`$(^F)'

The directory part and the file-within-directory part of `$^'

 

`$(+D)'

`$(+F)'

The directory part and the file-within-directory part of `$+'

 

`$(?D)'

`$(?F)'

The directory part and the file-within-directory part of `$?'

 

特殊變量

`MAKEFILES'

Makefiles to be read on every invocation of `make'.

 

`VPATH'

Directory search path for files not found in the current directory.

 

`SHELL'

The name of the system default command interpreter, usually

`/bin/sh'. You can set `SHELL' in the makefile to change the

shell used to run commands.

`MAKE'

The name with which `make' was invoked. Using this variable in

commands has special meaning.

 

`MAKELEVEL'

The number of levels of recursion (sub-`make's).

 

`MAKEFLAGS'

The flags given to `make'. You can set this in the environment or

a makefile to set flags.

 

`SUFFIXES'

The default list of suffixes before `make' reads any makefiles.

 


免責聲明!

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



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