Makefile 是什么
一個(gè)正式的軟件工程中,源文件按類(lèi)型、功能、模塊等分別放在不同的目錄下,如果每次都在命令行這樣: gcc a.c b.c c.c -o test ,顯然是非常影響效率的,那么這時(shí)候就需要 Makefile 來(lái)進(jìn)行管理,在 Makefile 中指定哪些文件先編譯,那些文件后編譯,在什么情況下編譯哪些文件等操作。
Makefile 就是一個(gè)用來(lái)幫助我們編譯的工具,和 Windows 下的 IDE 類(lèi)似,只不過(guò) Makefile 需要我們自己動(dòng)手編寫(xiě),一個(gè)好的 Makefile 可以極大的提升工作的效率。
Makefile 規(guī)則
target ... : prerequisites ...
command
...
target 就是我們編譯文件要生成的目標(biāo), prerequisites 就是我們編譯文件需要的依賴(lài), command 就是用依賴(lài)生成目標(biāo)所需要執(zhí)行的命令。
比如我們平時(shí)使用的 gcc a.c b.c -o test
這里的 test 就是我們要生成的目標(biāo), a.c 就是我們生成目標(biāo)需要的依賴(lài),而 gcc a.c -o test 則是命令。將這行命令用 Makefile 的方式來(lái)寫(xiě)就是:
test:a.c b.c
gcc a.c b.c -o test
Makefile 中的命令必須用 tab 開(kāi)始,不能是空格。
Makefile 可以自動(dòng)推導(dǎo)文件以及文件依賴(lài)關(guān)系后面的命令,在后面的示例中我們可以看到目標(biāo)的依賴(lài)基本都是 .o 文件而不是 .c 文件,原因正是 Makefile 強(qiáng)大的自動(dòng)推導(dǎo)功能。
通常 Makefile 中還會(huì)有一個(gè)名為 clean 的目標(biāo),用來(lái)清除編譯后產(chǎn)生的各種文件。一般情況下 Makefile 會(huì)根據(jù)依賴(lài)和目標(biāo)的新舊來(lái)決定是否編譯,但是如果不小心修改了目標(biāo)而造成目標(biāo)比依賴(lài)新的情況的話(huà),Makefile 會(huì)因?yàn)槟繕?biāo)比依賴(lài)新而忽略這個(gè)目標(biāo)下的命令,這個(gè)時(shí)候顯然會(huì)造成問(wèn)題,一個(gè)解決的辦法就是使用 clean 這樣的目標(biāo)來(lái)清除編譯后的文件,然后 make 重新編譯。
clean 這個(gè)目標(biāo)有點(diǎn)特殊,他是不需要依賴(lài)的,因此也叫偽目標(biāo)。一般使用方式如下:
clean:
rm *.o test -f
Makefile 使用
make 命令執(zhí)行時(shí),需要一個(gè) Makefile 文件(文件名為 Makefile 、 makefile 、 *.mk ),以告訴 make 命令需要怎么樣的去編譯和鏈接程序。執(zhí)行時(shí)只用在命令行輸入 make , Makefile 就會(huì)自動(dòng)執(zhí)行第一個(gè)目標(biāo)下的命令。而是否執(zhí)行命令則取決于依賴(lài),如果沒(méi)有目標(biāo)文件或是目標(biāo)后的依賴(lài)文件比目標(biāo)文件新,Makefile 就會(huì)執(zhí)行其下面的命令。
Makefile 中使用 # 注釋,只注釋 # 后的一行。
Makefile 中引用其他 Makefile,用 include 指令來(lái)引用。引用的效果就是原地展開(kāi)。
Makefile 命令前面加 @ 來(lái)靜默執(zhí)行,即執(zhí)行命令時(shí)不打印命令本身。
Makefile 變量
Makefile 中的變量和 shell 腳本中非常相似,都是直接定義,不需要類(lèi)型,引用時(shí)用 $(var) 。
偽目標(biāo)( .PHONY ):偽目標(biāo)形式上是一個(gè)目標(biāo),但是不需要依賴(lài),偽目標(biāo)一般只是為了執(zhí)行目標(biāo)下面的命令(比如 clean 就是偽目標(biāo))。
Makefile 中的幾種變量賦值運(yùn)算符
= 賦值,可以被賦值為變量的值,解析時(shí)取這個(gè)變量最后的值。
:= 也是賦值,被賦值為變量時(shí)解析為變量在這行語(yǔ)句時(shí)的值,即變量如果后面改變這里的值也不改變。
?= 如果變量前面并沒(méi)有賦值過(guò)則執(zhí)行這條賦值,如果前面已經(jīng)賦值過(guò)了則本行被忽略。
+= 用來(lái)給一個(gè)已經(jīng)賦值的變量接續(xù)賦值,意思就是把這次的值加到原來(lái)的值的后面。
關(guān)于 = 和 := ,比如 B=$(A)bcd ,那么 B 的值取決于變量 A 最后一次被賦值的值,即使 A 在 B 之后再次被賦值,變量 B 仍然會(huì)隨著 A 的改變而改變。而 := 則只看之前 A 最后被賦值的值。
Makefile 的環(huán)境變量
Makefile 中用 export 導(dǎo)出的就是環(huán)境變量。一般情況下要求環(huán)境變量名用大寫(xiě),普通變量名用小寫(xiě)。
環(huán)境變量和普通變量不同,可以這樣理解:環(huán)境變量類(lèi)似于整個(gè)工程中所有 Makefile 之間可以共享的全局變量,而普通變量只是當(dāng)前本 Makefile 中使用的局部變量。所以要注意:定義了一個(gè)環(huán)境變量會(huì)影響到工程中別的 Makefile 文件,因此要小心。
Makefile 中的自動(dòng)變量
自動(dòng)變量是 Makefile 中提前預(yù)定義的特殊意義的符號(hào),類(lèi)似 C 語(yǔ)言中的宏 __LINE__ 等,提前被定義并被賦予了特殊含義。
$@ 目標(biāo)文件名,比如上文的 test 。
$< 第一個(gè)依賴(lài)文件名,如果依賴(lài)目標(biāo)是以模式(即“ % “)定義的,那么” $< “將是符合模式的一系列的文件集。注意,其是一個(gè)一個(gè)取出來(lái)的。
$^ 依賴(lài)的文件集合,比如上文的 a.c b.c 。
此外還可以向 Makefile 傳參, $# 存放傳遞參數(shù)個(gè)數(shù), $1 存放第一個(gè)參數(shù)的字符串, $2 存放第二個(gè)參數(shù)的字符串……
其他
通配符:比如在當(dāng)前文件夾下有 1.c 2.c 12.c test.c 1.h 。
% 若干個(gè)任意字符,和 * 很相似,但是 % 一般只用于規(guī)則描述中,又叫做規(guī)則通配符。
* 若干個(gè)任意字符 *.c 匹配 1.c 2.c 12.c test.c 。
? 1個(gè)任意字符 ?.c 匹配 1.c 2.c 。
[] 將 [] 中的字符依次去和外面的結(jié)合匹配 [12].c 匹配 1.c 2.c 。
Makefile 與 shell 腳本非常相似,shell 腳本中能使用的 Makefile 也能使用,比如 awk 等工具