Makefile使用指南

什麼是Makefile

Makefile定義了一系列的規則來指定,哪些文件須要先編譯,哪些文件須要後編譯,哪些文件須要從新編譯,文件之間有哪些依賴等。Makefile有本身的書寫格式、關鍵字、函數。像C 語言有本身的格式、關鍵字和函數同樣。並且在Makefile中可使用系統shell所提供的任何命令來完成想要的工做。 Makefile帶來的好處就是——「自動化編譯」,一旦寫好,只須要一個make命令,整個工程徹底自動編譯,極大的提升了軟件開發的效率shell

makefile的文件名

默認的狀況下,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
複製代碼

Makefile的規則

顯示規則

target ... : prerequisites ...
    command
複製代碼
  • target:要生成的目標,也能夠是一個標籤
  • prerequisites:目標所依賴的列表
  • command:任意shell指令,通常用於生成target。必須以[Tab鍵]開頭,也能夠和prerequisites在一行,用分號作爲分隔

這是一個文件的依賴關係,也就是說,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操作系統

  • test不存在
  • test存在,但test1.o的修改時間比test新
  • test存在,但test2.o的修改時間比test新

把全部依賴關係都在makefile裏列舉出來以後,執行make命令的時候,就會根據依賴關係自動編譯連接了命令行

可是這裏的clean又是幹什麼的呢?它並無其餘依賴,那麼,make就不會自動去找文件的依賴性,也就不會自動執行其後所定義的命令。要執行其後的命令,就要在make命令後明顯得指出這個lable的名字,好比make clean。這樣的方法很是有用,咱們能夠在一個makefile中定義不用的編譯或是和編譯無關的命令,好比程序的打包,程序的備份,等等。code

隱晦規則

每一個.o文件的依賴文件默認會有同名的.c文件,好比有一個target是test.o,那麼test.c默認就是test.O的依賴文件,這個是makefile的隱晦規則,是make會自動推導出來的繼承

make是怎麼工做的?

在默認的方式下,也就是咱們只輸入make命令。那麼:遞歸

  1. make會在當前目錄下找名字叫「Makefile」或「makefile」的文件
  2. 若是找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到「test」這個文件,並把這個文件做爲最終的目標文件
  3. 若是test文件不存在,或是test所依賴的後面的 .o 文件的文件修改時間要比test這個文件新,那麼,他就會執行後面所定義的命令來生成test這個文件
  4. 若是test所依賴的.o文件也不存在,那麼make會在當前文件中找目標爲.o文件的依賴性,若是找到則再根據那一個規則生成.o文件

這就是整個make的依賴性,make會一層又一層地去找文件的依賴關係,直到最終編譯出第一個目標文件

makefile中使用變量

變量的基礎

爲了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=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」
複製代碼

讓make自動推導

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註釋

Makefile中只有行註釋,和UNIX的Shell腳本同樣,其註釋是用「#」字符,如:

# 這是在makefile中的註釋1
# 這是在makefile中的註釋2
複製代碼

引用其它的Makefile

在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
複製代碼
相關文章
相關標籤/搜索