Makefile語法
1、Makefile規則格式:
目標…... : 依賴檔案集合……
命令 1
命令 2
……
例如:
main : main.o input.o calcu.o gcc -o main main.o input.o calcu.o
命令列表中的每條命令必須以 TAB 鍵開始,不能使用空格!
Make的執行過程:
1、 make 命令會在當前目錄下查詢以 Makefile(makefile 其實也可以)命名的檔案。
2、當找到 Makefile 檔案以後就會按照 Makefile 中定義的規則去編譯生成最終的目標檔案。
3、當發現目標檔案不存在,或者目標所依賴的檔案比目標檔案新(也就是最後修改時間比
目標檔案晚)的話就會執行後面的命令來更新目標。
總結一下, Makefile 中規則用來描述在什麼情況下使用什麼命令來構建一個特定的檔案,
這個檔案就是規則的“目標”,為了生成這個“目標”而作為材料的其它檔案稱為“目標”的依
賴,規則的命令是用來建立或者更新目標的。
除了 Makefile 的“終極目標”所在的規則以外,其它規則的順序在 Makefile 中是沒有意義
的,“終極目標”就是指在使用 make 命令的時候沒有指定具體的目標時, make 預設的那個目
標,它是 Makefile 檔案中第一個規則的目標,如果 Makefile 中的第一個規則有多個目標,那麼
這些目標中的第一個目標就是 make 的“終極目標”。
2、Makefile變數
跟 C 語言一樣 Makefile 也支援變數的,先看一下前面的例子:
main: main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
上述 Makefile 語句中, main.o input.o 和 calcue.o 這三個依賴檔案,我們輸入了兩遍,我們
這個 Makefile 比較小,如果 Makefile 複雜的時候這種重複輸入的工作就會非常費時間,而且非
常容易輸錯,為了解決這個問題, Makefile 加入了變數支援。不像 C 語言中的變數有 int、 char
等各種型別, Makefile 中的變數都是字串!類似 C 語言中的巨集。使用變數將上面的程式碼修改,
修改以後如下所示
#Makefile 變數的使用 objects = main.o input.o calcu.o main: $(objects) gcc -o main $(objects)
①賦值符"="
使用“=”在給變數的賦值的時候,不一定要用已經定義好的值,也可以使用後面定義的值,
比如如下程式碼:
name = yang currname = $(name) name = YJL print: @echo "name = $(currname)"
執行結果:
從上圖中可以看到 curname 的值不是“yang”,竟然是“YJL”,也就是變數“name”
最後一次賦值的結果,這就是賦值符“=”的神奇之處!藉助另外一個變數,可以將變數的真實
值推到後面去定義。也就是變數的真實值取決於它所引用的變數的最後一次有效值
②賦值符“:=”
name = yang currname := $(name) name = YJL print: @echo "name = $(currname)"
這裡只做了一個修改,"currname = $(name)"修改為"currname := $(name)",執行結果如下:
③賦值符“?=”
currname = yang currname ?= YJL print: @echo "name = $(currname)"
執行結果:
currname ?= YJL print: @echo "name = $(currname)"
執行結果:
“?=”是一個很有用的賦值符,如果變數 currname 前面沒有被賦值,那麼此變數就是“YJL”,
如果前面已經賦過值了,那麼就使用前面賦的值。
④變數追加“+=”
Makefile 中的變數是字串,有時候我們需要給前面已經定義好的變數新增一些字串進
去,此時就要使用到符號“+=”,比如如下所示程式碼:
objects = main.o inpiut.o
objects += calcu.o
一開始變數 objects 的值為“main.o input.o”,後面我們給他追加了一個“calcu.o”,因此變
量 objects 變成了“main.o input.o calcu.o”,這個就是變數的追加。
3、Makefile模式規則
在 3.3.2 小節中我們編寫了一個 Makefile 檔案用來編譯工程,這個 Makefile 的內容如下:
main: main.o input.o calcu.o gcc -o main main.o input.o calcu.o main.o: main.c gcc -c main.c input.o: input.c gcc -c input.c calcu.o: calcu.c gcc -c calcu.c clean: rm *.o rm main
上述 Makefile 中第 3~8 行是將對應的.c 原始檔編譯為.o 檔案,每一個 C 檔案都要寫一個對
應的規則,如果工程中 C 檔案很多的話顯然不能這麼做。為此,我們可以使用 Makefile 中的模
式規則,通過模式規則我們就可以使用一條規則來將所有的.c 檔案編譯為對應的.o 檔案。
模式規則中,至少在規則的目標定定義中要包涵“%”,否則就是一般規則,目標中的“%”
表示對檔名的匹配,“%”表示長度任意的非空字串,比如“%.c”就是所有的以.c 結尾的
檔案,類似與萬用字元, a.%.c 就表示以 a.開頭,以.c 結束的所有檔案。
舉個例子:
把上面的Makefile簡化:
objects = main.o input.o calcu.o main: $(objects) gcc -o main $(objects)
%.o : %.c gcc -c $<
clean: rm *.o rm main
第6行:"gcc -c $<"又涉及到另外一個知識點,自動化變數。
4、Makefile自動化變數
上面講的模式規則中,目標和依賴都是一系列的檔案,每一次對模式規則進行解析的時候
都會是不同的目標和依賴檔案,而命令只有一行,如何通過一行命令來從不同的依賴檔案中生
成對應的目標?自動化變數就是完成這個功能的!所謂自動化變數就是這種變數會把模式中所
定義的一系列的檔案自動的挨個取出,直至所有的符合模式的檔案都取完,自動化變數只應該
出現在規則的命令中, 常用的自動化變數如表3.4.4.1 :
自動化變數 | 描述 |
$@ | 規則中的目標集合,在模式規則中,如果有多個目標的話,“$@”表示匹配模 式中定義的目標集合。 |
$% | 當目標是函式庫的時候表示規則中的目標成員名,如果目標不是函式庫檔案, 那麼其值為空。 |
$< | 依賴檔案集合中的第一個檔案,如果依賴檔案是以模式(即“%” )定義的,那麼 “$<”就是符合模式的一系列的檔案集合。 |
$? | 所有比目標新的依賴目標集合,以空格分開。 |
$^ | 所有依賴檔案的集合,使用空格分開,如果在依賴檔案中有多個重複的檔案, “$^”會去除重複的依賴檔案,值保留一份。 |
$+ | 和“$^”類似,但是當依賴檔案存在重複的話不會去除重複的依賴檔案。 |
$* | 這個變量表示目標模式中"%"及其之前的部分,如果目標是 test/a.test.c,目標模 式為 a.%.c,那麼“$*”就是 test/a.test。 |
表 3.4.4.1 中的 7 個自動化變數中,常用的三種: $@、 $<和$^
5、Makefile偽目標
Makefile 有一種特殊的目標——偽目標,一般的目標名都是要生成的檔案,而偽目標不代
表真正的目標名,在執行 make 命令的時候通過指定這個偽目標來執行其所在規則的定義的命
令。
使用偽目標的主要是為了避免 Makefile 中定義的只執行命令的目標和工作目錄下的實際文
件出現名字衝突,有時候我們需要編寫一個規則用來執行一些命令,但是這個規則不是用來創
建檔案的,比如在前面的“示例程式碼 3.4.4.1”中有如下程式碼用來完成清理工程的功能:
clean:
rm *.o
rm main
上述規則中並沒有建立檔案 clean 的命令,因此工作目錄下永遠都不會存在檔案 clean,當
我們輸入“make clean”以後,後面的“rm *.o”和“rm main”總是會執行。可是如果我們“手
賤”,在工作目錄下建立一個名為“clean”的檔案,那就不一樣了,當執行“make clean”的時
候,規則因為沒有依賴檔案,所以目標被認為是最新的,因此後面的 rm 命令也就不會執行,我
們預先設想的清理工程的功能也就無法完成。為了避免這個問題,我們可以將 clean 宣告為偽
目標,宣告方式如下:
.PHONY : clean
我們使用偽目標來更改“示例程式碼 3.4.4.1”,修改完成以後如下:
objects = main.o input.o calcu.o main: $(objects) gcc -o main $(objects) .PHONY : clean %.o : %.c gcc -c $< clean: rm *.o rm main
上述程式碼第 5 行宣告 clean 為偽目標,宣告 clean 為偽目標以後不管當前目錄下是否存在名
為“clean”的檔案,輸入“make clean”的話規則後面的 rm 命令都會執行。
6、Makefile條件判斷
在 C 語言中我們通過條件判斷語句來根據不同的情況來執行不同的分支, Makefile 也支援
條件判斷,語法有兩種如下:
<條件關鍵字>
<條件為真時執行的語句>
endif
以及:
<條件關鍵字>
<條件為真時執行的語句>
else
<條件為假時執行的語句>
endif
其中條件關鍵字有 4 個: ifeq、 ifneq、 ifdef 和 ifndef,這四個關鍵字其實分為兩對、 ifeq 與
ifneq、 ifdef 與 ifndef,先來看一下 ifeq 和 ifneq, ifeq 用來判斷是否相等, ifneq 就是判斷是否不
相等, ifeq 用法如下:
ifeq (<引數 1>, <引數 2>)
ifeq ‘<引數 1 >’ ,‘ <引數 2>’
ifeq “<引數 1>” , “<引數 2>”
ifeq “<引數 1>” , ‘<引數 2>’
ifeq ‘<引數 1>’ , “<引數 2>”
上述用法中都是用來比較“引數 1”和“引數 2”是否相同,如果相同則為真,“引數 1”和
“引數 2”可以為函式返回值。 ifneq 的用法類似,只不過 ifneq 是用來了比較“引數 1”和“參
數 2”是否不相等,如果不相等的話就為真。
ifdef 和 ifndef 的用法如下:
ifdef <變數名>
如果“變數名”的值非空,那麼表示表示式為真,否則表示式為假。“變數名”同樣可以是
一個函式的返回值。 ifndef 用法類似,但是含義使用者 ifdef 相反
7、Makefile函式使用
Makefile 支援函式,類似 C 語言一樣, Makefile 中的函式是已經定義好的,我們直接使用,
不支援我們自定義函式。 make 所支援的函式不多,但是絕對夠我們使用了,函式的用法如下:
$(函式名 引數集合)
或者
${函式名 引數集合}
可以看出,呼叫函式和呼叫普通變數一樣,使用符號“$”來標識。引數集合是函式的多個
引數,引數之間以逗號“,”隔開,函式名和引數之間以“空格”分隔開,函式的呼叫以“$”開
頭。接下來我們介紹幾個常用的函式,其它的函式大家可以參考《跟我一起寫 Makefile》這份
文件。
1、函式 subst
函式 subst 用來完成字串替換,呼叫形式如下:
$(subst <from>,<to>,<text>)
此函式的功能是將字串<text>中的<from>內容替換為<to>,函式返回被替換以後的字元
串,比如如下示例:
$(subst zzk,ZZK,my name is zzk)
把字串“my name is zzk”中的“zzk”替換為“ZZK”,替換完成以後的字串為“my name
is ZZK”。
2、函式 patsubst
函式 patsubst 用來完成模式字串替換,使用方法如下:
$(patsubst <pattern>,<replacement>,<text>)
此函式查詢字串<text>中的單詞是否符合模式<pattern>,如果匹配就用<replacement>來
替換掉, <pattern>可以使用包括萬用字元“%”,表示任意長度的字串,函式返回值就是替換後
的字串。如果<replacement>中也包涵“%”,那麼<replacement>中的“%”將是<pattern>中的
那個“%”所代表的字串,比如:
$(patsubst %.c,%.o,a.c b.c c.c)
將字串“a.c b.c c.c”中的所有符合“%.c”的字串,替換為“%.o”,替換完成以後的字
符串為“a.o b.o c.o”。
3、函式 dir
函式 dir 用來獲取目錄,使用方法如下:
$(dir <names…>)
此函式用來從檔名序列<names>中提取出目錄部分,返回值是檔名序列<names>的目錄
部分,比如:
$(dir </src/a.c>)
提取檔案“/src/a.c”的目錄部分,也就是“/src”。
4、函式 notdir
函式 notdir 看名字就是知道去除檔案中的目錄部分,也就是提取檔名,用法如下:
$(notdir <names…>)
此函式用與從檔名序列<names>中提取出檔名非目錄部分,比如:
$(notdir </src/a.c>)
提取檔案“/src/a.c”中的非目錄部分,也就是檔名“a.c”。
5、函式 foreach
foreach 函式用來完成迴圈,用法如下:
$(foreach <var>, <list>,<text>)
此函式的意思就是把引數<list>中的單詞逐一取出來放到引數<var>中,然後再執行<text>所
包含的表示式。每次<text>都會返回一個字串,迴圈的過程中, <text>中所包含的每個字串
會以空格隔開,最後當整個迴圈結束時, <text>所返回的每個字串所組成的整個字串將會是
函式 foreach 函式的返回值。
6、函式 wildcard
萬用字元“%”只能用在規則中,只有在規則中它才會展開,如果在變數定義和函式使用時,
萬用字元不會自動展開,這個時候就要用到函式 wildcard,使用方法如下:
$(wildcard PATTERN…)
比如:
$(wildcard *.c)
上面的程式碼是用來獲取當前目錄下所有的.c 檔案,類似“%”。
關於 Makefile 的相關內容就講解到這裡,本節只是對 Makefile 做了最基本的講解,確保大
家能夠完成後續的學習, Makefile 還有大量的知識沒有提到,有興趣的可以自行參考《跟我一
起寫 Makefile》這份文件來深入學習 Makefile。
本文參考正點原子《I.MX6U 嵌入式 Linux 驅動開發指南》