|
3.4 e Makefile 語法 語法
3.4.1 初識 Makefile
Makefile 文件是由一些列的規(guī)則組合而成的,格式如下:
target(目標(biāo)文件) ...: prerequisites(依賴的文件) ...
command(命令)
...
...
比如 3.3.2 中寫的 Makefile 的規(guī)則:
main.o:main.c
gcc -c main.c
這條規(guī)則的 main.o 是目標(biāo)文件(將要生成的文件),main.c 是依賴的文件(生成 main.o 需要的文件),
“gcc -c main.c”是生成 main.o 需要運行的命令。e Makefile 中每行的腳本如果有縮進(jìn)的情況,必須使用
“ Tab ” 鍵縮進(jìn),切記不能使用空格縮進(jìn)(這是 e Makefile 的語法要求),大家一定要切記!
下面我們來分析一下圖 3.3.2 章節(jié)中寫的 Makefile 文件,腳本如下:
1 1 main:main.o calc.o
2 2 gcc -o main main.o calc.o
3 3 main.o:main.c
4 4 gcc -c main.c
5 5 calc.o:calc.c
6 6 gcc -c calc.c
7 7
8 8 clean:
9 9 rm -rf *.o
10 rm -rf main
該腳本一共有 4 條規(guī)則,1、2 行是第一條規(guī)則,3、4 行是第二條規(guī)則,5、6 是第三條規(guī)則 8、9、10
是第四條規(guī)則。我們在運行 make 命令的時候,會解析當(dāng)前目錄下的這個 Makefile 文件里面的規(guī)則,首先
解析第一條規(guī)則,第一條規(guī)則中的目標(biāo)文件是 main,只要完成了該目標(biāo)文件的更新,整個 Makefile 的功能
就完成了。在第一次編譯的時候,由于目標(biāo)文件 main 不存在,則會解析第一條規(guī)則,第一條規(guī)則依賴文件
main.o、calc.o,make 命令會檢查當(dāng)前目錄下是否有這兩個.o 文件,經(jīng)過檢查發(fā)現(xiàn)沒有,然后 make 會在
Makefile 中查找分別以 main.o、calc.o 為目標(biāo)的規(guī)則(第二條,第三條規(guī)則)。執(zhí)行第二條規(guī)則依賴的文
件是 main.c,make 命令檢查發(fā)現(xiàn)當(dāng)前目錄下有這個文件,然后執(zhí)行第二條規(guī)則的命令“gcc -c main.c”
生成 main.o 文件。然后執(zhí)行第三條規(guī)則,第三條規(guī)則的目標(biāo)文件是 calc.o,依賴的文件是 calc.c,make
命令檢查發(fā)現(xiàn)當(dāng)前目錄下存在該文件,然后執(zhí)行第三條規(guī)則的命令“gcc -c calc.c”生成 calc.o 文件,
至此第一條規(guī)則依賴的 main.o、calc.o;兩個文件已經(jīng)生成了,然后運行第一條規(guī)則的命令“gcc -o main
main.o calc.o”生成 main 文件。因為 make 命令運行的時候會從 Makefile 的第一條規(guī)則開始解析,然后
根據(jù)第一條規(guī)則的依賴文件去遍歷文件中的“對應(yīng)規(guī)則”,然后在根據(jù)“對應(yīng)規(guī)則”的依賴文件去遍歷“對
應(yīng)的規(guī)則”,采用這樣遞歸的方式會遍歷出完成第一條規(guī)則所需要的所有規(guī)則。下面我們來看看第四條規(guī)
則的目標(biāo)文件是 clean,我們通過查看發(fā)現(xiàn)該規(guī)則與第一條規(guī)則沒有關(guān)聯(lián),所以我們在運行 make 命令的時
候,不會遍歷到該規(guī)則。我們可以在終端輸入“make clean”命令來運行第四條規(guī)則,第四條規(guī)則沒有依
賴的文件,所以執(zhí)行運行命令“rm -rf *.o”和“rm -rf main”,這兩條命令的功能是刪除以.o 問結(jié)尾的
所有文件,刪除文件 main,運行如下圖所示:
通過上圖可以看到 main.o、mcalc.o 和 main 三個文件已經(jīng)刪除了。通過該規(guī)則我們可以清除編譯產(chǎn)生
的文件,實現(xiàn)工程的清理。
我們再來總結(jié)一下 make 命令的執(zhí)行過程:
1.make 命令會在當(dāng)前目錄下查找以 Makefile 命名的文件
2.找到 Makefile 文件,就會按照 Makefile 里面的規(guī)則去編譯,并生成最終文件
3.當(dāng)發(fā)現(xiàn)目標(biāo)文件不存在或者所依賴的文件比目標(biāo)文件新(修改時間),就會執(zhí)行規(guī)則對應(yīng)的命令來更新。
我們可以看到 make 是一個工具,他會通過 Makefile 文件里面的內(nèi)容來執(zhí)行具體的編譯過程。
3.4.2 Makefile 的變量
在 3.3.2 章節(jié)中的 Makefile 第一條規(guī)則:
main:main.o calc.o
gcc -o main main.o calc.o
在該規(guī)則中 main.o、calc.o 這兩個文件我們輸入了兩次,由于我們的額 Makefile 文件內(nèi)容比較少,
如果 Makefile 復(fù)雜的情況下,這種重復(fù)的輸入就會非常占用時間,而且修改起來也會很麻煩,為了解決這
個問題,Makefile 可以使用變量。Makefile 的變量是一個字符串。比如上面的規(guī)則我們聲明一個變量,叫
objects,objs 或者是 OBJ,反正不管是什么,只要能夠表示 main.o、calc.o 就行了,我們修改上面的規(guī)
則
1 1 objects = main.o calc.o
2 2 main ( objects)
3 3 gcc -o main $( objects)
我們來分析下修改后的規(guī)則,首先第一行是我們定義了一個變量 objects,并給賦值“main.o calc.o”,
第二行、第三行用到了變量 objects。Makefile 中的變量引用方式是“$(變量名)”,變量 objects 的賦值
使用“=”,Makefile 中變量的賦值還可以使用“:=”、“?=”、“+=”,這四種賦值的區(qū)別如下:
1. “= = ” 賦值符
我們先在用戶根目錄的 work 目錄下創(chuàng)建一個 Makefile 腳本,輸入下面的內(nèi)容:
1 ceshi1 = test
2 ceshi2 = $(ceshi1)
3 ceshi1 = temp
4
5 out:
6 @echo ceshi2 (ceshi2)
第一行我們定義了變量并賦值“test”,第二行定義了變量 ceshi2 并賦值變量 ceshi1,第三行修改變量
ceshi1 的值為“temp”,第五行、第六行是輸出變量 ceshi2 的值。我們在終端輸入“make out”命令,如
下圖所示:
在上圖可以看到變量 ceshi2 的值是 temp,也就是變量 ceshi1 最后一次的賦值。
2. “ := ” 賦值符
我們修改“=”賦值符中的代碼,第二行的“=”改成“:=”,代碼如下:
1 ceshi1 = test
2 ceshi2 := $(ceshi1)
3 ceshi1 = temp
4
5 out:
6 @echo ceshi2 (ceshi2)
我們在終端輸入“make out”命令,如下圖所示:
我們可以看到上圖的運行結(jié)果輸出變量 ceshi2 的值是 test,雖然在第三行我們修改了變量 ceshi1 的
值,通過本實驗我們可以看到“:=”賦值符的功能了。
3. “ ?= ” 賦值符
ceshi ?= test
“?=”賦值符的作用是如果前面沒有給變量 ceshi 賦值,那么變量就賦值“test”,如果前面已經(jīng)賦值了,
就使用前面的賦值。
4. “ += ” 賦值符
objs = main.o
objs += calc.o
上面的腳本最后變量 objs 的值是“main.o calc.o”,“+=”賦值符的功能是實現(xiàn)變量的追加。
3.4.3 條件判斷
使用條件判斷,可以讓 make 根據(jù)運行時的不同情況選擇不同的執(zhí)行分支。條件表達(dá)式可以是比較變量
的值,或是比較變量和常量的值。其語法有下面兩種:
1.
<條件比較>
[條件為真時執(zhí)行的腳本]
endif
2.
<條件比較>
[條件為真時執(zhí)行的腳本]
else
[條件為假時執(zhí)行的腳本]
endif
條件比較用到的比較關(guān)鍵字有:ifeq、ifneq、ifdef、ifndef。
ifeq 表示如果比較相等,語法如下:
ifeq(<參數(shù) 1>, <參數(shù) 2>)
ifneq 表示如果不相等,語法如下:
ifneq(<參數(shù) 1>, <參數(shù) 2>)
ifdef 表示如果定義了變量,語法如下:
ifdef <變量名>
ifndef 表示如果沒有定義變量,語法如下:
ifndef <變量名>
3.4.4 使用函數(shù)
在 Makefile 中可以使用函數(shù)來處理變量,從而讓我們的命令或是規(guī)則更為的靈活和具有智能。make 所
支持的函數(shù)也不算很多,不過已經(jīng)足夠我們的操作了。函數(shù)調(diào)用后,函數(shù)的返回值可以當(dāng)做變量來使用。
函數(shù)的調(diào)用很像變量的使用,也是以“$”來標(biāo)識的,語法如下:
$(<函數(shù)名> <參數(shù)集合>)
或者:
${<函數(shù)名> <參數(shù)集合>}
函數(shù)名和參數(shù)集合之間以空格分隔,參數(shù)集合的參數(shù)通過逗號分隔。函數(shù)調(diào)用以“$”開頭,以圓括號或花
括號把函數(shù)名和參數(shù)括起。感覺很像一個變量。函數(shù)中的參數(shù)可以使用變量。為了風(fēng)格的統(tǒng)一,函數(shù)和變
量的括號最好一樣,如使用“$(subst a,b,$(x))”這樣的形式,而不是“$(subst a,b,${x})”的形式。
因為統(tǒng)一會更清楚,也會減少一些不必要的麻煩。
接下來我們介紹幾個常用的函數(shù),其它的函數(shù)可以參考文檔《跟我一起寫 Makefile》。
t 1.subst 函數(shù)
$(subst ,,)
此函數(shù)的功能是把字串中的字符串替換成,函數(shù)返回被替換過后的字符串。如下示例:
$(subst ee,EE,feet on the street)
以上腳本實現(xiàn)把字符串“feet on the street”中的“ee”字符串替換成“EE”字符串,替換后的字符串
為“feet on the strEEt”。
. 2. t patsubst 函數(shù)
$(patsubst ,,)
此函數(shù)的功能是查找中的單詞(單詞以“空格”、“Tab”或“回車”“換行”分隔)是否符合模式
,如果匹配的話,則以替換。這里可以包括通配符“%”,表示任意長
度的字串。如果中也包含“%”,那么中的這個“%”將是中的那個
“%”所代表的字串。(可以用“\”來轉(zhuǎn)義,以“\%” 來表示真實含義的“%”字符)。函數(shù)返回被替換
過后的字符串。如下示例:
$(patsubst %.c,%.o,x.c bar.c)
以上腳本實現(xiàn)把字串“x.c bar.c”符合模式[%.c]的單詞替換成[%.o],返回結(jié)果是“x.o bar.o”
p 3.strip 函數(shù)
$(strip )
此函數(shù)的功能是去掉字串中開頭和結(jié)尾的空字符,函數(shù)返回被去掉空格的字符串值。如下示例:
$(strip a b c )
以上腳本實現(xiàn)把字串“a b c ”去掉開頭和結(jié)尾的空格,結(jié)果是“a b c”。
. 4. g findstring 函數(shù)
$(findstring ,)
此函數(shù)的功能是在字串中查找字串,如果找到,那么返回,否則返回空字符串,如下示
例:
$(findstring a,a b c)
$(findstring a,b c)
以上腳本,第一個返回“a”字符串,第二個返回空字符串。
r 5.dir 函數(shù)
$(dir )
此函數(shù)的功能是從文件名序列中取出目錄部分。目錄部分是指最后一個反斜杠(“/”)之前的部
分。如果沒有反斜杠,那么返回“./”。返回文件名序列的目錄部分,如下示例:
$(dir src/foo.c hacks)
以上腳本運行結(jié)果返回“src/”。
. 6. r notdir 函數(shù)
$(notdir )
此函數(shù)的功能是從文件名序列中取出非目錄部分。非目錄部分是指最后一個反斜杠(“/”)之后
的部分,返回文件名序列的非目錄部分,如下示例:
$(notdir src/foo.c)
以上腳本返回字符串“foo.c”
. 7. h foreach 函數(shù)
$(foreach ,,)
此函數(shù)的功能是把參數(shù)中的單詞逐一取出放到參數(shù)所指定的變量中,然后再執(zhí)行所包含
的表達(dá)式。每一次會返回一個字符串,循環(huán)過程中,的所返回的每個字符串會以空格分隔,
最后當(dāng)整個循環(huán)結(jié)束時,所返回的每個字符串所組成的整個字符串(以空格分隔)將會是 foreach 函
數(shù)的返回值。所以,最好是一個變量名,可以是一個表達(dá)式,而中一般會使用這
個參數(shù)來依次枚舉中的單詞。如下示例:
names := a b c d
files := $(foreach n,$(names),$(n).o)
以上腳本實現(xiàn)$(name)中的單詞會被挨個取出,并存到變量“n”中,“$(n).o”每次根據(jù)“$(n)”計算出
一個值,這些值以空格分隔,最后作為 foreach 函數(shù)的返回,所以$(files)的值是“a.o b.o c.o d.o”。
(注意,foreach 中的參數(shù)是一個臨時的局部變量,foreach 函數(shù)執(zhí)行完后,參數(shù)的變量將不
在作用,其作用域只在 foreach 函數(shù)當(dāng)中)。
更多內(nèi)容關(guān)注迅為電子
|
|