概述
——linux
什麼是makefile?或許不少Winodws的程序員都不知道這個東西,由於那些Windows的IDE都爲你作了這個工做,但我以爲要做一個 好的和professional的程序員,makefile仍是要懂。這就好像如今有這麼多的HTML的編輯器,但若是你想成爲一個專業人士,你仍是要了 解HTML的標識的含義。特別在Unix下的軟件編譯,你就不能不本身寫makefile了,會不會寫makefile,從一個側面說明了一我的是否具有 完成大型工程的能力。程序員
由於,makefile關係到了整個工程的編譯規則。一個工程中的源文件不計數,其按類型、功能、模塊分別放在若干個目錄中,makefile定義 了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要後編譯,哪些文件須要從新編譯,甚至於進行更復雜的功能操做,由於makefile就像一個 Shell腳本同樣,其中也能夠執行操做系統的命令。編程
makefile帶來的好處就是——「自動化編譯」,一旦寫好,只須要一個make命令,整個工程徹底自動編譯,極大的提升了軟件開發的效率。 make是一個命令工具,是一個解釋makefile中指令的命令工具,通常來講,大多數的IDE都有這個命令,好比:Delphi的 make,Visual C++的nmake,Linux下GNU的make。可見,makefile都成爲了一種在工程方面的編譯方法。編輯器
如今講述如何寫makefile的文章比較少,這是我想寫這篇文章的緣由。固然,不一樣產商的make各不相同,也有不一樣的語法,但其本質都是在「文件依賴性」上作文章,這裏,我僅對GNU的make進行講述,個人環境是RedHat Linux 8.0,make的版本是3.80。必竟,這個make是應用最爲普遍的,也是用得最多的。並且其仍是最遵循於IEEE 1003.2-1992 標準的(POSIX.2)。函數
在這篇文檔中,將以C/C++的源碼做爲咱們基礎,因此必然涉及一些關於C/C++的編譯的知識,相關於這方面的內容,還請各位查看相關的編譯器的文檔。這裏所默認的編譯器是UNIX下的GCC和CC。工具
關於程序的編譯和連接
——————————ui
在此,我想多說關於程序編譯的一些規範和方法,通常來講,不管是C、C++、仍是pas,首先要把源文件編譯成中間代碼文件,在Windows下也 就是 .obj 文件,UNIX下是 .o 文件,即 Object File,這個動做叫作編譯(compile)。而後再把大量的Object File合成執行文件,這個動做叫做連接(link)。spa
編譯時,編譯器須要的是語法的正確,函數與變量的聲明的正確。對於後者,一般是你須要告訴編譯器頭文件的所在位置(頭文件中應該只是聲明,而定義應 該放在C/C++文件中),只要全部的語法正確,編譯器就能夠編譯出中間目標文件。通常來講,每一個源文件都應該對應於一箇中間目標文件(O文件或是OBJ 文件)。操作系統
連接時,主要是連接函數和全局變量,因此,咱們可使用這些中間目標文件(O文件或是OBJ文件)來連接咱們的應用程序。連接器並無論函數所在的源 文件,只管函數的中間目標文件(Object File),在大多數時候,因爲源文件太多,編譯生成的中間目標文件太多,而在連接時須要明顯地指出中間目標文件名,這對於編譯很不方便,因此,咱們要給 中間目標文件打個包,在Windows下這種包叫「庫文件」(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。命令行
總結一下,源文件首先會生成中間目標文件,再由中間目標文件生成執行文件。在編譯時,編譯器只檢測程序語法,和函數、變量是否被聲明。若是函數未被 聲明,編譯器會給出一個警告,但能夠生成Object File。而在連接程序時,連接器會在全部的Object File中找尋函數的實現,若是找不到,那到就會報連接錯誤碼(Linker Error),在VC下,這種錯誤通常是:Link 2001錯誤,意思說是說,連接器未能找到函數的實現。你須要指定函數的Object File.
好,言歸正傳,GNU的make有許多的內容,閒言少敘,仍是讓咱們開始吧。
Makefile 介紹
———————
make命令執行時,須要一個 Makefile 文件,以告訴make命令須要怎麼樣的去編譯和連接程序。
首先,咱們用一個示例來講明Makefile的書寫規則。以便給你們一個感興認識。這個示例來源於GNU的make使用手冊,在這個示例中,咱們的工程有8個C文件,和3個頭文件,咱們要寫一個Makefile來告訴make命令如何編譯和連接這幾個文件。咱們的規則是:
1)若是這個工程沒有編譯過,那麼咱們的全部C文件都要編譯並被連接。
2)若是這個工程的某幾個C文件被修改,那麼咱們只編譯被修改的C文件,並連接目標程序。
3)若是這個工程的頭文件被改變了,那麼咱們須要編譯引用了這幾個頭文件的C文件,並連接目標程序。
只要咱們的Makefile寫得夠好,全部的這一切,咱們只用一個make命令就能夠完成,make命令會自動智能地根據當前的文件修改的狀況來肯定哪些文件須要重編譯,從而本身編譯所須要的文件和連接目標程序。
1、Makefile的規則
在講述這個Makefile以前,仍是讓咱們先來粗略地看一看Makefile的規則。
target ... : prerequisites ...
command
...
...
target也就是一個目標文件,能夠是Object File,也能夠是執行文件。還能夠是一個標籤(Label),對於標籤這種特性,在後續的「僞目標」章節中會有敘述。
prerequisites就是,要生成那個target所須要的文件或是目標。
command也就是make須要執行的命令。(任意的Shell命令)
這是一個文件的依賴關係,也就是說,target這一個或多個的目標文件依賴於prerequisites中的文件,其生成規則定義在 command中。說白一點就是說,prerequisites中若是有一個以上的文件比target文件要新的話,command所定義的命令就會被執 行。這就是Makefile的規則。也就是Makefile中最核心的內容。
說到底,Makefile的東西就是這樣一點,好像個人這篇文檔也該結束了。呵呵。還不盡然,這是Makefile的主線和核心,但要寫好一個Makefile還不夠,我會之後面一點一點地結合個人工做經驗給你慢慢到來。內容還多着呢。:)
2、一個示例
正如前面所說的,若是一個工程有3個頭文件,和8個C文件,咱們爲了完成前面所述的那三個規則,咱們的Makefile應該是下面的這個樣子的。
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的易讀。咱們能夠把這個內容保存在文件爲「Makefile」或「makefile」的 文件中,而後在該目錄下直接輸入命令「make」就能夠生成執行文件edit。若是要刪除執行文件和全部的中間目標文件,那麼,只要簡單地執行一下 「make clean」就能夠了。
在這個makefile中,目標文件(target)包含:執行文件edit和中間目標文件(*.o),依賴文件(prerequisites)就 是冒號後面的那些 .c 文件和 .h文件。每個 .o 文件都有一組依賴文件,而這些 .o 文件又是執行文件 edit 的依賴文件。依賴關係的實質上就是說明了目標文件是由哪些文件生成的,換言之,目標文件是哪些文件更新的。
在定義好依賴關係後,後續的那一行定義瞭如何生成目標文件的操做系統命令,必定要以一個Tab鍵做爲開頭。記住,make並無論命令是怎麼工做的, 他只管執行所定義的命令。make會比較targets文件和prerequisites文件的修改日期,若是prerequisites文件的日期要比 targets文件的日期要新,或者target不存在的話,那麼,make就會執行後續定義的命令。
這裏要說明一點的是,clean不是一個文件,它只不過是一個動做名字,有點像C語言中的lable同樣,其冒號後什麼也沒有,那麼,make就不 會自動去找文件的依賴性,也就不會自動執行其後所定義的命令。要執行其後的命令,就要在make命令後明顯得指出這個lable的名字。這樣的方法很是有 用,咱們能夠在一個makefile中定義不用的編譯或是和編譯無關的命令,好比程序的打包,程序的備份,等等。
3、make是如何工做的
在默認的方式下,也就是咱們只輸入make命令。那麼,
一、make會在當前目錄下找名字叫「Makefile」或「makefile」的文件。
二、若是找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到「edit」這個文件,並把這個文件做爲最終的目標文件。
三、若是edit文件不存在,或是edit所依賴的後面的 .o 文件的文件修改時間要比edit這個文件新,那麼,他就會執行後面所定義的命令來生成edit這個文件。
四、若是edit所依賴的.o文件也不存在,那麼make會在當前文件中找目標爲.o文件的依賴性,若是找到則再根據那一個規則生成.o文件。(這有點像一個堆棧的過程)
五、固然,你的C文件和H文件是存在的啦,因而make會生成 .o 文件,而後再用 .o 文件生命make的終極任務,也就是執行文件edit了。
這就是整個make的依賴性,make會一層又一層地去找文件的依賴關係,直到最終編譯出第一個目標文件。在找尋的過程當中,若是出現錯誤,好比最後 被依賴的文件找不到,那麼make就會直接退出,並報錯,而對於所定義的命令的錯誤,或是編譯不成功,make根本不理。make只管文件的依賴性,即, 若是在我找了依賴關係以後,冒號後面的文件仍是不在,那麼對不起,我就不工做啦。
經過上述分析,咱們知道,像clean這種,沒有被第一個目標文件直接或間接關聯,那麼它後面所定義的命令將不會被自動執行,不過,咱們能夠顯示要make執行。即命令——「make clean」,以此來清除全部的目標文件,以便重編譯。
因而在咱們編程中,若是這個工程已被編譯過了,當咱們修改了其中一個源文件,好比file.c,那麼根據咱們的依賴性,咱們的目標file.o會被 重編譯(也就是在這個依性關係後面所定義的命令),因而file.o的文件也是最新的啦,因而file.o的文件修改時間要比edit要新,因此edit 也會被從新連接了(詳見edit目標文件後定義的命令)。
而若是咱們改變了「command.h」,那麼,kdb.o、command.o和files.o都會被重編譯,而且,edit會被重連接。
4、makefile中使用變量
在上面的例子中,先讓咱們看看edit的規則:
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
咱們能夠看到[.o]文件的字符串被重複了兩次,若是咱們的工程須要加入一個新的[.o]文件,那麼咱們須要在兩個地方加(應該是三個地方,還有一 個地方在clean中)。固然,咱們的makefile並不複雜,因此在兩個地方加也不累,但若是makefile變得複雜,那麼咱們就有可能會忘掉一個 須要加入的地方,而致使編譯失敗。因此,爲了makefile的易維護,在makefile中咱們可使用變量。makefile的變量也就是一個字符 串,理解成C語言中的宏可能會更好。
好比,咱們聲明一個變量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正無論什麼啦,只要可以表示obj文件就好了。咱們在makefile一開始就這樣定義:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
因而,咱們就能夠很方便地在咱們的makefile中以「$(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)
因而若是有新的 .o 文件加入,咱們只需簡單地修改一下 objects 變量就能夠了。
關於變量更多的話題,我會在後續給你一一道來。
5、讓make自動推導
GNU的make很強大,它能夠自動推導文件以及文件依賴關係後面的命令,因而咱們就不必去在每個[.o]文件後都寫上相似的命令,由於,咱們的make會自動識別,並本身推導命令。
只要make看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關係中,若是make找到一個whatever.o,那麼 whatever.c,就會是whatever.o的依賴文件。而且 cc -c whatever.c 也會被推導出來,因而,咱們的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)
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)
這種方法,也就是make的「隱晦規則」。上面文件內容中,「.PHONY」表示,clean是個僞目標文件。
關於更爲詳細的「隱晦規則」和「僞目標文件」,我會在後續給你一一道來。
即然咱們的make能夠自動推導命令,那麼我看到那堆[.o]和[.h]的依賴就有點不爽,那麼多的重複的[.h],能不能把其收攏起來,好吧,沒有問題,這個對於make來講很容易,誰叫它提供了自動推導命令和文件的功能呢?來看看最新風格的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 //其實只要把.h文件當成索引看就行了,根據索引把文件添加到前面去
display.o insert.o search.o files.o : buffer.h //by gundamfj
.PHONY : clean
clean :
rm edit $(objects)
這種風格,讓咱們的makefile變得很簡單,但咱們的文件依賴關係就顯得有點凌亂了。魚和熊掌不可兼得。還看你的喜愛了。我是不喜歡這種風格的,一是文件的依賴關係看不清楚,二是若是文件一多,要加入幾個新的.o文件,那就理不清楚了。
7、清空目標文件的規則
每一個Makefile中都應該寫一個清空目標文件(.o和執行文件)的規則,這不只便於重編譯,也很利於保持文件的清潔。這是一個「修養」(呵呵,還記得個人《編程修養》嗎)。通常的風格都是:
clean:
rm edit $(objects)
更爲穩健的作法是:
.PHONY : clean
clean :
-rm edit $(objects)
前面說過,.PHONY意思表示clean是一個「僞目標」,。而在rm命令前面加了一個小減號的意思就是,也許某些文件出現問題,但不要管,繼續 作後面的事。固然,clean的規則不要放在文件的開頭,否則,這就會變成make的默認目標,相信誰也不肯意這樣。不成文的規矩是——「clean歷來 都是放在文件的最後」。
上面就是一個makefile的概貌,也是makefile的基礎,下面還有不少makefile的相關細節,準備好了嗎?準備好了就來Makefile 總述
———————
1、Makefile裏有什麼?
Makefile裏主要包含了五個東西:顯式規則、隱晦規則、變量定義、文件指示和註釋。
一、顯式規則。顯式規則說明了,如何生成一個或多的的目標文件。這是由Makefile的書寫者明顯指出,要生成的文件,文件的依賴文件,生成的命令。
二、隱晦規則。因爲咱們的make有自動推導的功能,因此隱晦的規則可讓咱們比較粗糙地簡略地書寫Makefile,這是由make所支持的。
三、變量的定義。在Makefile中咱們要定義一系列的變量,變量通常都是字符串,這個有點你C語言中的宏,當Makefile被執行時,其中的變量都會被擴展到相應的引用位置上。
四、文件指示。其包括了三個部分,一個是在一個Makefile中引用另外一個Makefile,就像C語言中的include同樣;另外一個是指根據 某些狀況指定Makefile中的有效部分,就像C語言中的預編譯#if同樣;還有就是定義一個多行的命令。有關這一部分的內容,我會在後續的部分中講 述。
五、註釋。Makefile中只有行註釋,和UNIX的Shell腳本同樣,其註釋是用「#」字符,這個就像C/C++中的「//」同樣。若是你要在你的Makefile中使用「#」字符,能夠用反斜框進行轉義,如:「\#」。
最後,還值得一提的是,在Makefile中的命令,必需要以[Tab]鍵開始。
2、Makefile的文件名
默認的狀況下,make命令會在當前目錄下按順序找尋文件名爲「GNUmakefile」、「makefile」、「Makefile」的文件,找 到了解釋這個文件。在這三個文件名中,最好使用「Makefile」這個文件名,由於,這個文件名第一個字符爲大寫,這樣有一種顯目的感受。最好不要用 「GNUmakefile」,這個文件是GNU的make識別的。有另一些make只對全小寫的「makefile」文件名敏感,可是基本上來講,大多 數的make都支持「makefile」和「Makefile」這兩種默認文件名。
固然,你可使用別的文件名來書寫Makefile,好比:「Make.Linux」,「Make.Solaris」,「Make.AIX」等,如 果要指定特定的Makefile,你可使用make的「-f」和「--file」參數,如:make -f Make.Linux或make --file Make.AIX。
3、引用其它的Makefile
在Makefile使用include關鍵字能夠把別的Makefile包含進來,這很像C語言的#include,被包含的文件會原模原樣的放在當前文件的包含位置。include的語法是:
include <filename>
filename能夠是當前操做系統Shell的文件模式(能夠保含路徑和通配符)
在include前面能夠有一些空字符,可是毫不能是[Tab]鍵開始。include和<filename>能夠用一個或多個空格隔 開。舉個例子,你有這樣幾個Makefile:a.mk、b.mk、c.mk,還有一個文件叫foo.make,以及一個變量$(bar),其包含了 e.mk和f.mk,那麼,下面的語句:
include foo.make *.mk $(bar)
等價於:
include foo.make a.mk b.mk c.mk e.mk f.mk
make命令開始時,會把找尋include所指出的其它Makefile,並把其內容安置在當前的位置。就好像C/C++的#include指令 同樣。若是文件都沒有指定絕對路徑或是相對路徑的話,make會在當前目錄下首先尋找,若是當前目錄下沒有找到,那麼,make還會在下面的幾個目錄下 找:
一、若是make執行時,有「-I」或「--include-dir」參數,那麼make就會在這個參數所指定的目錄下去尋找。
二、若是目錄<prefix>/include(通常是:/usr/local/bin或/usr/include)存在的話,make也會去找。
若是有文件沒有找到的話,make會生成一條警告信息,但不會立刻出現致命錯誤。它會繼續載入其它的文件,一旦完成makefile的讀 取,make會再重試這些沒有找到,或是不能讀取的文件,若是仍是不行,make纔會出現一條致命信息。若是你想讓make不理那些沒法讀取的文件,而繼 續執行,你能夠在include前加一個減號「-」。如:
-include <filename>
其表示,不管include過程當中出現什麼錯誤,都不要報錯繼續執行。和其它版本make兼容的相關命令是sinclude,其做用和這一個是同樣的。
4、環境變量 MAKEFILES
若是你的當前環境中定義了環境變量MAKEFILES,那麼,make會把這個變量中的值作一個相似於include的動做。這個變量中的值是其它 的Makefile,用空格分隔。只是,它和include不一樣的是,從這個環境變中引入的Makefile的「目標」不會起做用,若是環境變量中定義的 文件發現錯誤,make也會不理。
可是在這裏我仍是建議不要使用這個環境變量,由於只要這個變量一被定義,那麼當你使用make時,全部的Makefile都會受到它的影響,這毫不 是你想看到的。在這裏提這個事,只是爲了告訴你們,也許有時候你的Makefile出現了怪事,那麼你能夠看看當前環境中有沒有定義這個變量。
5、make的工做方式
GNU的make工做時的執行步驟入下:(想來其它的make也是相似)
一、讀入全部的Makefile。
二、讀入被include的其它Makefile。
三、初始化文件中的變量。
四、推導隱晦規則,並分析全部規則。
五、爲全部的目標文件建立依賴關係鏈。
六、根據依賴關係,決定哪些目標要從新生成。
七、執行生成命令。
1-5步爲第一個階段,6-7爲第二個階段。第一個階段中,若是定義的變量被使用了,那麼,make會把其展開在使用的位置。但make並不會徹底 立刻展開,make使用的是拖延戰術,若是變量出如今依賴關係的規則中,那麼僅當這條依賴被決定要使用了,變量纔會在其內部展開。
固然,這個工做方式你不必定要清楚,可是知道這個方式你也會對make更爲熟悉。有了這個基礎,後續部分也就容易看懂了。
書寫規則
————
規則包含兩個部分,一個是依賴關係,一個是生成目標的方法。
在Makefile中,規則的順序是很重要的,由於,Makefile中只應該有一個最終目標,其它的目標都是被這個目標所連帶出來的,因此必定要 讓make知道你的最終目標是什麼。通常來講,定義在Makefile中的目標可能會有不少,可是第一條規則中的目標將被確立爲最終的目標。若是第一條規 則中的目標有不少個,那麼,第一個目標會成爲最終的目標。make所完成的也就是這個目標。
好了,仍是讓咱們來看一看如何書寫規則。
1、規則舉例
foo.o : foo.c defs.h # foo模塊
cc -c -g foo.c
看到這個例子,各位應該不是很陌生了,前面也已說過,foo.o是咱們的目標,foo.c和defs.h是目標所依賴的源文件,而只有一個命令「cc -c -g foo.c」(以Tab鍵開頭)。這個規則告訴咱們兩件事:
一、文件的依賴關係,foo.o依賴於foo.c和defs.h的文件,若是foo.c和defs.h的文件日期要比foo.o文件日期要新,或是foo.o不存在,那麼依賴關係發生。
二、若是生成(或更新)foo.o文件。也就是那個cc命令,其說明了,如何生成foo.o這個文件。(固然foo.c文件include了defs.h文件)
2、規則的語法
targets : prerequisites
command
...
或是這樣:
targets : prerequisites ; command
command
...
targets是文件名,以空格分開,可使用通配符。通常來講,咱們的目標基本上是一個文件,但也有多是多個文件。
command是命令行,若是其不與「targetrerequisites」在一行,那麼,必須以[Tab鍵]開頭,若是和prerequisites在一行,那麼能夠用分號作爲分隔。(見上)
prerequisites也就是目標所依賴的文件(或依賴目標)。若是其中的某個文件要比目標文件要新,那麼,目標就被認爲是「過期的」,被認爲是須要重生成的。這個在前面已經講過了。
若是命令太長,你可使用反斜框(‘\’)做爲換行符。make對一行上有多少個字符沒有限制。規則告訴make兩件事,文件的依賴關係和如何成成目標文件。
通常來講,make會以UNIX的標準Shell,也就是/bin/sh來執行命令。
3、在規則中使用通配符
若是咱們想定義一系列比較相似的文件,咱們很天然地就想起使用通配符。make支持三各通配符:「*」,「?」和「[...]」。這是和Unix的B-Shell是相同的。
波浪號(「~」)字符在文件名中也有比較特殊的用途。若是是「~/test」,這就表示當前用戶的$HOME目錄下的test目錄。而 「~hchen/test」則表示用戶hchen的宿主目錄下的test目錄。(這些都是Unix下的小知識了,make也支持)而在Windows或是 MS-DOS下,用戶沒有宿主目錄,那麼波浪號所指的目錄則根據環境變量「HOME」而定。
通配符代替了你一系列的文件,如「*.c」表示因此後綴爲c的文件。一個須要咱們注意的是,若是咱們的文件名中有通配符,如:「*」,那麼能夠用轉義字符「\」,如「\*」來表示真實的「*」字符,而不是任意長度的字符串。
好吧,仍是先來看幾個例子吧:
clean:
rm -f *.o
上面這個例子我不很少說了,這是操做系統Shell所支持的通配符。這是在命令中的通配符。
print: *.c
lpr -p $?
touch print
上面這個例子說明了通配符也能夠在咱們的規則中,目標print依賴於全部的[.c]文件。其中的「$?」是一個自動化變量,我會在後面給你講述。
objects = *.o
上面這個例子,表示了,通符一樣能夠用在變量中。並非說[*.o]會展開,不!objects的值就是「*.o」。Makefile中的變量其實 就是C/C++中的宏。若是你要讓通配符在變量中展開,也就是讓objects的值是全部[.o]的文件名的集合,那麼,你能夠這樣:
objects := $(wildcard *.o)
這種用法由關鍵字「wildcard」指出,關於Makefile的關鍵字,咱們將在後面討論。
4、文件搜尋
在一些大的工程中,有大量的源文件,咱們一般的作法是把這許多的源文件分類,並存放在不一樣的目錄中。因此,當make須要去找尋文件的依賴關係時,你能夠在文件前加上路徑,但最好的方法是把一個路徑告訴make,讓make在自動去找。
Makefile文件中的特殊變量「VPATH」就是完成這個功能的,若是沒有指明這個變量,make只會在當前的目錄中去找尋依賴文件和目標文件。若是定義了這個變量,那麼,make就會在噹噹前目錄找不到的狀況下,到所指定的目錄中去找尋文件了。
VPATH = src:../headers
上面的的定義指定兩個目錄,「src」和「../headers」,make會按照這個順序進行搜索。目錄由「冒號」分隔。(固然,當前目錄永遠是最高優先搜索的地方)
另外一個設置文件搜索路徑的方法是使用make的「vpath」關鍵字(注意,它是全小寫的),這不是變量,這是一個make的關鍵字,這和上面提到 的那個VPATH變量很相似,可是它更爲靈活。它能夠指定不一樣的文件在不一樣的搜索目錄中。這是一個很靈活的功能。它的使用方法有三種:
一、vpath <pattern> <directories>
爲符合模式<pattern>的文件指定搜索目錄<directories>。
二、vpath <pattern>
清除符合模式<pattern>的文件的搜索目錄。
三、vpath
清除全部已被設置好了的文件搜索目錄。
vapth使用方法中的<pattern>須要包含「%」字符。「%」的意思是匹配零或若干字符,例如,「%.h」表示全部以「.h」 結尾的文件。<pattern>指定了要搜索的文件集,而<directories>則指定了<pattern>的 文件集的搜索的目錄。例如:
vpath %.h ../headers
該語句表示,要求make在「../headers」目錄下搜索全部以「.h」結尾的文件。(若是某文件在當前目錄沒有找到的話)
咱們能夠連續地使用vpath語句,以指定不一樣搜索策略。若是連續的vpath語句中出現了相同的<pattern>,或是被重複了的<pattern>,那麼,make會按照vpath語句的前後順序來執行搜索。如:
vpath %.c foo
vpath % blish
vpath %.c bar
其表示「.c」結尾的文件,先在「foo」目錄,而後是「blish」,最後是「bar」目錄。
vpath %.c foo:bar
vpath % blish
而上面的語句則表示「.c」結尾的文件,先在「foo」目錄,而後是「bar」目錄,最後纔是「blish」目錄。
5、僞目標
最先先的一個例子中,咱們提到過一個「clean」的目標,這是一個「僞目標」,
clean:
rm *.o temp
正像咱們前面例子中的「clean」同樣,即然咱們生成了許多文件編譯文件,咱們也應該提供一個清除它們的「目標」以備完整地重編譯而用。 (以「make clean」來使用該目標)
由於,咱們並不生成「clean」這個文件。「僞目標」並非一個文件,只是一個標籤,因爲「僞目標」不是文件,因此make沒法生成它的依賴關係 和決定它是否要執行。咱們只有經過顯示地指明這個「目標」才能讓其生效。固然,「僞目標」的取名不能和文件名重名,否則其就失去了「僞目標」的意義了。
固然,爲了不和文件重名的這種狀況,咱們可使用一個特殊的標記「.PHONY」來顯示地指明一個目標是「僞目標」,向make說明,無論是否有這個文件,這個目標就是「僞目標」。
.PHONY : clean
只要有這個聲明,無論是否有「clean」文件,要運行「clean」這個目標,只有「make clean」這樣。因而整個過程能夠這樣寫:
.PHONY: clean
clean:
rm *.o temp
僞目標通常沒有依賴的文件。可是,咱們也能夠爲僞目標指定所依賴的文件。僞目標一樣能夠做爲「默認目標」,只要將其放在第一個。一個示例就是,若是 你的Makefile須要一口氣生成若干個可執行文件,但你只想簡單地敲一個make完事,而且,全部的目標文件都寫在一個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
咱們知道,Makefile中的第一個目標會被做爲其默認目標。咱們聲明瞭一個「all」的僞目標,其依賴於其它三個目標。因爲僞目標的特性是,總 是被執行的,因此其依賴的那三個目標就老是不如「all」這個目標新。因此,其它三個目標的規則老是會被決議。也就達到了咱們一口氣生成多個目標的目的。 「.PHONY : all」聲明瞭「all」這個目標爲「僞目標」。
隨便提一句,從上面的例子咱們能夠看出,目標也能夠成爲依賴。因此,僞目標一樣也可成爲依賴。看下面的例子:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
「make clean」將清除全部要被清除的文件。「cleanobj」和「cleandiff」這兩個僞目標有點像「子程序」的意思。咱們能夠輸入「make cleanall」和「make cleanobj」和「make cleandiff」命令來達到清除不一樣種類文件的目的。