Makefile定義了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要後編譯,哪些文件須要從新編譯,文件之間有哪些依賴等。Makefile有本身的書寫格式、關鍵字、函數。像C 語言有本身的格式、關鍵字和函數同樣。並且在Makefile中可使用系統shell所提供的任何命令來完成想要的工做。 Makefile帶來的好處就是——「自動化編譯」,一旦寫好,只須要一個make命令,整個工程徹底自動編譯,極大的提升了軟件開發的效率shell
默認的狀況下,make命令會在當前目錄下按順序找尋文件名爲「GNUmakefile」、「makefile」、「Makefile」的文件,找到了解釋這個文件。在這三個文件名中,最好不要用「GNUmakefile」,這個文件是GNU的make識別的。有另一些make只對全小寫的「makefile」文件名敏感,可是基本上來講,大多數的make都支持「makefile」和「Makefile」這兩種默認文件名。bash
固然,你可使用別的文件名來書寫Makefile,好比:「Make.Linux」,「Make.Solaris」,「Make.AIX」等,若是要指定特定的Makefile,你可使用make的「-f」和「--file」參數,如:函數
make -f Make.Linux
make --file Make.AIX
複製代碼
target ... : prerequisites ...
command
複製代碼
這是一個文件的依賴關係,也就是說,target依賴於prerequisites中的文件,其生成規則定義在command中。說白一點就是說,prerequisites中若是有一個以上的文件比target文件要新的話,command所定義的命令就會被執行(command必定要以Tab鍵開始,不然編譯器沒法識別command)。這就是Makefile的規則,也是Makefile中最核心的內容。ui
舉個栗子:spa
test : test1.o test2.o
cc -o test test1.o test2.o
test1.o : test1.c test1.h
cc -c test1.c
test2.o : test2.c test2.h
cc -c test2.c
clean :
rm test test1.o test2.o
複製代碼
在這裏,test是最終的目標,生成test,依賴於test1.o和test2.o也就是說,有如下狀況發生時,會執行cc -o test test1.o test2.o指令,以從新生成target操作系統
把全部依賴關係都在makefile裏列舉出來以後,執行make命令的時候,就會根據依賴關係自動編譯連接了命令行
可是這裏的clean又是幹什麼的呢?它並無其餘依賴,那麼,make就不會自動去找文件的依賴性,也就不會自動執行其後所定義的命令。要執行其後的命令,就要在make命令後明顯得指出這個lable的名字,好比make clean
。這樣的方法很是有用,咱們能夠在一個makefile中定義不用的編譯或是和編譯無關的命令,好比程序的打包,程序的備份,等等。code
每一個.o文件的依賴文件默認會有同名的.c文件,好比有一個target是test.o,那麼test.c默認就是test.O的依賴文件,這個是makefile的隱晦規則,是make會自動推導出來的繼承
在默認的方式下,也就是咱們只輸入make命令。那麼:遞歸
這就是整個make的依賴性,make會一層又一層地去找文件的依賴關係,直到最終編譯出第一個目標文件
爲了makefile的易維護,在makefile中咱們可使用變量。makefile的變量也就是一個字符串,好比咱們能夠定義一個objects變量,並經過$(objects)的方式來使用這個變量。變量相似與C語言的宏定義,在執行的時候,變量的值會被擴展到被使用的地方
objects = test1.o test2.o
test : $(objects)
cc -o test $(objects)
# --------------------------------------
test : test1.o test2.o
cc -o test test1.o test2.o
複製代碼
第一種寫法和第二種寫法的做用徹底是同樣的
在定義變量的值時,咱們可使用其它變量來構造變量的值,在Makefile中有兩種方式來在用變量定義變量的值。
先看第一種方式,也就是簡單的使用「=」號,在「=」左側是變量,右側是變量的值,右側變量的值能夠定義在文件的任何一處,也就是說,右側中的變量不必定非要是已定義好的值,其也可使用後面定義的值。如:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:
echo $(foo) #咱們執行「make all」將會打出變量$(foo)的值是「Huh?」
複製代碼
這個功能有好的地方,也有很差的地方,好的地方是,咱們能夠把變量的真實值推到後面來定義,很差的地方,那就是遞歸定義:
A = $(B)
B = $(A)
#這會讓make陷入無限的變量展開過程當中去,固然,咱們的make是有能力檢測這樣的定義,並會報錯
複製代碼
爲了不上面的這種方法,咱們可使用make中的另外一種用變量來定義變量的方法。這種方法使用的是「:=」操做符:
x := foo
y := $(x) bar
x := later
#這種方法,前面的變量不能使用後面的變量,只能使用前面已定義好了的變量,上下兩種方式是等價的
y := foo bar
x := later
複製代碼
咱們可使用「+=」操做符給變量追加值,如:
objects = main.o foo.o bar.o utils.o
objects += another.o
複製代碼
因而,咱們的$(objects)值變成:「main.o foo.o bar.o utils.o another.o」(another.o被追加進去了)
若是變量以前沒有定義過,那麼,「+=」會自動變成「=」,若是前面有變量定義,那麼「+=」會繼承於前次操做的賦值符。若是前一次的是「:=」,那麼「+=」會以「:=」做爲其賦值符,如:
variable := value
variable += more
複製代碼
等價於:
variable := value
variable := $(variable) more
複製代碼
但若是是這種狀況:
variable = value
variable += more
複製代碼
因爲前次的賦值符是「=」,因此「+=」也會以「=」來作爲賦值,那麼豈不會發生變量的遞補歸定義,這是很很差的,因此make會自動爲咱們解決這個問題,咱們沒必要擔憂這個問題。
咱們能夠替換變量中的共有的部分,其格式是「{var:a=b}」,其意思是,把變量「var」中全部以「a」字串「結尾」的「a」替換成「b」字串。這裏的「結尾」意思是「空格」或是「結束符仍是看一個示例吧:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
# 把「$(foo)」中全部以「.o」字串「結尾」所有替換成「.c」,最終bar的值是a.c b.c c.c
複製代碼
另一種變量替換的技術是以「靜態模式」(參見前面章節)定義的,如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
# 這依賴於被替換字串中的有相同的模式,模式中必須包含一個「%」字符,這個例子一樣讓$(bar)變量的值爲「a.c b.c c.c」
複製代碼
x = y
y = z
a := $($(x))
# 在這個例子中,$(x)的值是「y」,因此$($(x))就是$(y),因而$(a)的值就是「z」
複製代碼
GNU的make很強大,它能夠自動推導文件以及文件依賴關係後面的命令。
只要make看到一個[.o]文件,它就會自動的把[.c]文件加在依賴關係中,若是make找到一個whatever.o,那麼whatever.c,就會是whatever.o的依賴文件。而且 cc -c whatever.c 也會被推導出來,因而,咱們的makefile不再用寫得這麼複雜
每一個Makefile中都應該寫一個清空目標文件(.o和執行文件)的規則,這不只便於重編譯,也很利於保持文件的清潔,通常風格是:
.PHONY : clean
clean :
rm test $(objects)
複製代碼
Makefile中只有行註釋,和UNIX的Shell腳本同樣,其註釋是用「#」字符,如:
# 這是在makefile中的註釋1
# 這是在makefile中的註釋2
複製代碼
在Makefile使用include關鍵字能夠把別的Makefile包含進來,make命令開始時,會把找尋include所指出的其它Makefile,並把其內容安置在當前的位置。這很像C語言的#include,被包含的文件會原模原樣的放在當前文件的包含位置。include的語法是:
include <filename>
# 在include前面能夠有一些空字符,可是毫不能是[Tab]鍵開始
# filename能夠是當前操做系統Shell的文件模式(能夠保含路徑和通配符)
-include <filename>
# 不管include過程當中出現什麼錯誤,都不要報錯繼續執行。上面那條指令如果找不到include的目標文件,會報錯
複製代碼
最先先的一個例子中,咱們提到過一個「clean」的目標。
clean:
rm *.o temp
複製代碼
僞目標不會自動被執行,只能顯式地調用執行。可是上面僞目標的寫法有一個缺陷,如果當前目錄下存在有一個文件名爲"clean",那麼根據咱們的規則,command將不會被執行,由於目標已經存在了,爲了解決這個問題,咱們可使用一個特殊的標記「.PHONY」來顯示地指明一個目標是「僞目標」,向make說明,不論是否有這個文件,這個目標就是「僞目標」
.PHONY : clean
clean:
rm *.o temp
複製代碼
經過.PHONY,不管是否存在「clean」文件,咱們的command都將會被執行了
每當命令運行完後,make會檢測每一個命令的返回碼,若是命令返回成功,那麼make會執行下一條命令,當規則中全部的命令成功返回後,這個規則就算是成功完成了。若是一個規則中的某個命令出錯了(命令退出碼非零),那麼make就會終止執行當前規則,這將有可能終止全部規則的執行。
有些時候,命令的出錯並不表示就是錯誤的。例如mkdir命令,咱們必定須要創建一個目錄,若是目錄不存在,那麼mkdir就成功執行,萬事大吉,若是目錄存在,那麼就出錯了。咱們之因此使用mkdir的意思就是必定要有這樣的一個目錄,因而咱們就不但願mkdir出錯而終止規則的運行。
爲了作到這一點,忽略命令的出錯,咱們能夠在Makefile的命令行前加一個減號「-」(在Tab鍵以後),標記爲無論命令出不出錯都認爲是成功的。如:
clean:
-rm -f *.o
複製代碼