Makefile文件的作用是指導make程序該如何工作。
make的工作原理
當我們只輸入make命令的工作流程是:
1. make會在當前目錄下找名字叫“Makefile”或“makefile”的文件;
2. 如果找到,它會找文件中的第一個目標文件(target),在上面的例子中,他會找到“main”這個文件,並把這個文件作為最終的目標文件;
3. 如果main文件不存在,或是main所依賴的后面的 .o 文件的文件修改時間要比main這個文件新,那么,make會執行下面定義的命令來生成main文件;
4. 如果main所依賴的.o文件也存在,那么make會在當前文件中找目標為.o文件的依賴性,如果找到再根據命令生成.o文件(這是一個遞歸的過程);
如果在找尋的過程中,出現了被依賴的文件找不到的錯誤,那么make就會直接退出,並報錯。
如果在一條依賴鏈中,比如:A依賴B,B依賴C,C依賴D。那么當D更新后,make發現D比C新則會重新構建C,以此類推,最終A也會被更新。
Makefile文件的語法組成
基本的結構形式:
1 target: prerequisites 2 command 3 command 4 ...
說明:
target:可以是任何類型的文件,也可以是一個標簽(Label),或叫作“偽目標”,這個我們一會兒再講。
prerequisites:就是要生成target所需要的文件、目標。
command:當prerequisites比target要新,就會執行這里定義的動作(任意的Shell命令)
其實就是一個文件的依賴關系處理,也就是說,target目標依賴於prerequisites中的文件,其生成規則定義在command中。說白一點就是說,prerequisites中如果有任何一個及以上的文件比target文件要新的話,command所定義的命令就會被執行。這就是Makefile的規則。也是Makefile中最核心的內容。
Makefile中使用變量
我們通過腳本實驗來了解定義變量的幾種形式:
1 .PHONY: target 2 3 VAR_0:=$(VAR) 4 VAR_1=$(VAR) 5 VAR="hello world" 6 VAR2:=$(VAR) 7 VAR="hello world2" 8 VAR3="hello world3" 9 VAR3?=$(VAR) 10 VAR_0+="abc" 11 VAR_1+="abc" 12 13 target: 14 @echo $(VAR_0) 15 @echo $(VAR_1) 16 @echo $(VAR) 17 @echo $(VAR2) 18 @echo $(VAR3)
$ make
abc
hello world2 abc
hello world2
hello world
hello world3
= 直接賦值,比較直觀。值得說的是當賦值的是變量時,如果引用的變量不存在,那么賦值的是空字符串。
:= 延遲引用變量,也就是說只有當腳本中使用到VAR2變量時,make才會去找被引用的VAR變量的值。
?= 條件賦值,被稱為條件賦值是因為:只有此變量在之前沒有被賦值的情況下才會對這個變量進行賦值。
+= 追加賦值,上面的例子的輸出很明顯了。
Makefile中也可以直接使用shell進程的環境變量,比如可以在Makefile中輸出@echo $(PATH)等等。
make自動推導
首先讓我在本地創建幾個文件:
$ vim lib1.h
1 #ifndef __LIB1_H__ 2 #define __LIB1_H__ 3 void lib1(); 4 #endif
$ vim lib1.c
1 #include <stdio.h> 2 void lib1() 3 { 4 printf("this is lib1\n"); 5 }
$ vim lib2.h
1 #ifndef __LIB2_H__ 2 #define __LIB2_H__ 3 void lib2(); 4 #endif
$ vim lib2.c
1 #include <stdio.h> 2 void lib2() 3 { 4 printf("this is lib2\n"); 5 }
$ vim main.c
1 #include "lib1.h" 2 #include "lib2.h" 3 4 int main(int argc, char *argv[]) { 5 lib1(); 6 lib2(); 7 return 0; 8 }
$ vim Makefile
1 .PHONY: clean 2 3 CC=gcc 4 CFLAGS=-O3 5 OBJS=main.o lib1.o lib2.o 6 LIB=libtest.a 7 BIN=main 8 9 $(BIN): $(LIB) $(BIN).o 10 $(CC) $(CFLAGS) -o $@ $(BIN).o -L. -Wl,-Bstatic -ltest -Wl,-Bdynamic 11 echo $? 12 13 %.o: %.c 14 $(CC) -c -o $*.o $*.c 15 16 $(LIB): lib1.o lib2.o 17 ar crv $@ $^ 18 19 clean: 20 rm -rf $(BIN) 21 rm -rf $(OBJS) 22 rm -rf $(LIB)
$ make
大家可以自行改動代碼進行測試!
說一下.PHONY的作用,.PHONY后面寫的是偽目標,也就是說這種目標只是占用一個符號一個名字而已,無論當前目錄下是否有clean文件,不會對比是否最新,只要執行make clean,clean目標下面定義的命令永遠都會執行!
$@:表示目標文件名稱
$<:prerequisites依賴列表中的第一個依賴的名字
$?:所有比目標新的依賴文件名稱的集合,以空格分隔
$^:當前目標中依賴的所有文件,它並不關心這些文件是不是比目標文件新。然而,重復的依賴文件名會被移除。這會在你需要將所有的依賴文件輸出到屏幕時變得非常有用
$+:很像$^,也是所有依賴文件的集合,但是它不去除重復的依賴
$*:匹配目標模式中“%”之前的部分
好方法和技巧:
1. 引入外部Makefile
2. 變量值替換
從一個已有的宏創建一個新宏並非不可能。例如宏SRC代表一系列的源文件,你希望生成一個對應的目標文件宏OBJ。要這樣做,你只需要指定OBJ = SRC,除了擴展名不同以外:OBJ = $(SRC:.c=.o)
陷阱:
1. 環境變量 MAKEFILES
2. 萬能通配符的陷阱
3. 環境變量 VPATH
本文會繼續不斷打磨、完善!