phpmysql網(wǎng)站河南關(guān)鍵詞排名顧問
文章目錄
- 一、背景
- 二、Makefile編譯過程
- 三、變量
- 四、變量賦值
- 1、"="是最普通的等號
- 2、“:=” 表示直接賦值
- 3、“?=” 表示如果該變量沒有被賦值,
- 4、"+="和寫代碼是一樣的,
- 五、預(yù)定義變量
- 六、函數(shù)
- **通配符**
- 七、偽目標(biāo) .PHONY
- 八、其他常用功能
- 代碼清理clean
- 九、嵌套執(zhí)行Makefile
- 十、指定頭文件路徑
- 十一、指定庫文件路徑
- 十二、基本使用
- 十三、推導(dǎo)過程
- 十四、適度擴(kuò)展語法
一、背景
? 會不會寫makefile,從一個側(cè)面說明了一個?是否具備完成大型工程的能力
? 一個工程中的源文件不計(jì)數(shù),其按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規(guī)則來指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至于進(jìn)行更復(fù)雜的功能操作
? makefile帶來的好處就是?“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件開發(fā)的效率。
? make是一個命令工具,是一個解釋makefile中指令的命令工具,一般來說,大多數(shù)的IDE都有這個命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make??梢?#xff0c;makefile都成為了一種在工程方面的編譯方法。
? make是一條命令,makefile是一個文件,兩個搭配使用,完成項(xiàng)目自動化構(gòu)建。
二、Makefile編譯過程
Makefile文件中的命令有一定規(guī)范,一旦該文件編寫好以后在Linux命令行中執(zhí)行一條make命令即可自動編譯整個工程。不同廠家的make可能會稍有不同,并且語法上也有區(qū)別,不過基本思想都差不多,主要還是落在目標(biāo)依賴上,最廣泛使用的是GNUmake。
2、語法規(guī)則
目標(biāo) ... : 依賴 ...命令1命令2. . .
"Makefile的核心規(guī)則,類似于一位廚師做菜,目標(biāo)就是做好一道菜,那么所謂的依賴就是各種食材,各種廚具等等,然后需要廚師好的技術(shù)方法類似于命令,才能作出一道好菜。
??同時這些依賴也有可能此時并不存在,需要現(xiàn)場制作,或者是由其他廚師做好,那么這個依賴就成為了其他規(guī)則的目標(biāo),該目標(biāo)也會有他自己的依賴和命令。這樣就形成了一層一層遞歸依賴組成了Makefile文件。
??Makefile并不會關(guān)心命令是如何執(zhí)行的,僅僅只是會去執(zhí)行所有定義的命令,和我們平時直接輸入命令行是一樣的效果。"
- 目標(biāo)即要生成的文件。如果目標(biāo)文件的更新時間晚于依賴文件更新時間,則說明依賴文件沒有改動,目標(biāo)文件不需要重新編譯。否則會進(jìn)行重新編譯并更新目標(biāo)文件。
- 默認(rèn)情況下Makefile的第一個目標(biāo)為終極目標(biāo)。
- 依賴:即目標(biāo)文件由哪些文件生成。
- 命令:即通過執(zhí)行命令由依賴文件生成目標(biāo)文件。注意每條命令之前必須有一個tab(此文檔編輯器默認(rèn)是空格,復(fù)制下來的代碼需要把命令代碼的縮進(jìn)改為tab制表符)保持縮進(jìn),這是語法要求。
- all:Makefile文件默認(rèn)只生成第一個目標(biāo)文件即完成編譯,但是我們可以通過all 指定所需要生成的目標(biāo)文件。
例如下面的例子:
all: target1 target2 target3
target1:
# 編譯規(guī)則1
target2:
# 編譯規(guī)則2
target3:
# 編譯規(guī)則3
all被設(shè)置為第一個目標(biāo),并且target1、target2和target3被列為all的依賴。當(dāng)你在命令行中運(yùn)行make時,make命令會尋找并執(zhí)行all目標(biāo)規(guī)則,這將依次執(zhí)行target1、target2和target3的編譯規(guī)則。
因此,通過在Makefile中設(shè)置all作為默認(rèn)目標(biāo)規(guī)則,你可以簡化構(gòu)建過程,只需運(yùn)行make命令即可執(zhí)行整個編譯過程,無需顯式指定目標(biāo)
三、變量
$符號表示取變量的值,當(dāng)變量名多于一個字符時,使用"( )"
$符的其他用法
$^ 表示所有的依賴文件
$@ 表示生成的目標(biāo)文件
$< 代表第一個依賴文件
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))ALL: hello.outhello.out: $(OBJ)gcc $< -o $@%.o: %.cgcc -c $< -o $@
四、變量賦值
1、"="是最普通的等號
在Makefile中容易搞錯賦值等號,
使用 “=”進(jìn)行賦值,變量的值是整個Makefile中最后被指定的值。
VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA
經(jīng)過上面的賦值后,最后VIR_B的值是AA B,而不是A B,在make時,會把整個Makefile展開,來決定變量的值(類似于宏定義)
2、“:=” 表示直接賦值
賦予當(dāng)前位置的值。
VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA
最后BIR_B的值是A B,即根據(jù)當(dāng)前位置進(jìn)行賦值。因此相當(dāng)于“=”,“:=”才是真正意義上的直接賦值
3、“?=” 表示如果該變量沒有被賦值,
賦值予等號后面的值。
VIR ?= new_value
如果VIR在之前沒有被賦值,那么VIR的值就為new_value。
VIR := old_value
VIR ?= new_value
這種情況下,VIR的值就是old_value
4、"+="和寫代碼是一樣的,
表示將符號后面的值添加到前面的變量上
五、預(yù)定義變量
CC:c編譯器的名稱,默認(rèn)值為cc。
cpp c預(yù)編譯器的名稱默認(rèn)值為$(CC) -E
CC = gcc
回顯問題,Makefile中的命令都會被打印出來。如果不想打印命令部分 可以使用@去除回顯
@echo "clean done!"
六、函數(shù)
通配符
SRC = $(wildcard ./*.c)
匹配目錄下所有.c 文件,并將其賦值給SRC變量。
OBJ = $(patsubst %.c, %.o, $(SRC))
這個函數(shù)有三個參數(shù),意思是取出SRC中的所有值,然后將.c 替換為.o 最后賦值給OBJ變量。
示例:如果目錄下有很多個.c 源文件,就不需要寫很多條規(guī)則語句了,而是可以像下面這樣寫
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))ALL: test.exe #生成執(zhí)行文件test.exe: $(OBJ)gcc $(OBJ) -o test%.o: %.cgcc -c $< -o $@
這里先將所有.c 文件編譯為 .o 文件,這樣后面更改某個 .c 文件時,其他的 .c 文件將不在編譯,而只是編譯有更改的 .c 文件,可以大大提高大項(xiàng)目中的編譯速度。
七、偽目標(biāo) .PHONY
偽目標(biāo)只是一個標(biāo)簽,clean是個偽目標(biāo)沒有依賴文件,只有用make來調(diào)用時才會執(zhí)行
當(dāng)目錄下有與make 命令 同名的文件時 執(zhí)行make 命令就會出現(xiàn)錯誤。
解決辦法就是使用偽目標(biāo)
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))ALL: test.exetest.exe: $(OBJ)gcc $< -o $@$(OBJ): $(SRC)gcc -c $< -o $@clean:rm -rf $(OBJ) test.exe.PHONY: clean ALL通常也會把ALL設(shè)置成偽目標(biāo)
八、其他常用功能
代碼清理clean
我們可以編譯一條屬于自己的clean語句,來清理make命令所產(chǎn)生的所有文件,列如
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))ALL: test.exetest.exe: $(OBJ)gcc $< -o $@$(OBJ): $(SRC)gcc -c $< -o $@clean:rm -rf $(OBJ) test.exe
九、嵌套執(zhí)行Makefile
在一些大工程中,會把不同模塊或不同功能的源文件放在不同的目錄中,我們可以在每個目錄中都寫一個該目錄的Makefile這有利于讓我們的Makefile變的更加簡潔,不至于把所有東西全部寫在一個Makefile中。
??列如在子目錄subdir目錄下有個Makefile文件,來指明這個目錄下文件的編譯規(guī)則。外部總Makefile可以這樣寫
subsystem:cd subdir && $(MAKE)
其等價于:
subsystem:$(MAKE) -C subdir
定義$(MAKE)宏變量的意思是,也許我們的make需要一些參數(shù),所以定義成一個變量比較有利于維護(hù)。兩個例子意思都是先進(jìn)入"subdir"目錄,然后執(zhí)行make命令
??我們把這個Makefile叫做總控Makefile,總控Makefile的變量可以傳遞到下級的Makefile中,但是不會覆蓋下層Makefile中所定義的變量,除非指定了 "-e"參數(shù)。
??如果傳遞變量到下級Makefile中,那么可以使用這樣的聲明export
??如果不想讓某些變量傳遞到下級Makefile,可以使用unexport
export variable = value
等價于
variable = value
export variable
等價于
export variable := value
等價于
variable := value
export variable
如果需要傳遞所有變量,那么只要一個export就行了。后面什么也不用跟,表示傳遞所有變量
十、指定頭文件路徑
一般都是通過"-I"(大寫i)來指定,假設(shè)頭文件在:
/home/develop/include
則可以通過-I指定:
-I/home/develop/include
將該目錄添加到頭文件搜索路徑中
在Makefile中則可以這樣寫:
CFLAGS=-I/home/develop/include
然后在編譯的時候,引用CFLAGS即可,如下
yourapp:*.cgcc $(CFLAGS) -o yourapp
十一、指定庫文件路徑
與上面指定頭文件類似只不過使用的是"-L"來指定
LDFLAGS=-L/usr/lib -L/path/to/your/lib
告訴鏈接器要鏈接哪些庫文件,使用"-l"(小寫L)如下:
LIBS = -lpthread -liconv
十二、基本使用
實(shí)例代碼
#include <stdio.h>
int main()
{ printf("hello Makefile!\n"); return 0;
}
Makefile文件
myproc:myproc.cgcc -o myproc myproc.c.PHONY:clean
clean:rm -f myproc
依賴關(guān)系
? 上面的文件myproc,它依賴myproc.c
依賴方法
? gcc -o myproc myproc.c
,就是與之對應(yīng)的依賴關(guān)系
項(xiàng)目清理
? 工程是需要被清理的
? 像clean這種,沒有被第一個目標(biāo)文件直接或間接關(guān)聯(lián),那么它后面所定義的命令將不會被自動執(zhí)行,不過,我們可以顯示要make執(zhí)行。即命令?“make clean”,以此來清除所有的目標(biāo)文件,以便重編譯。
? 但是一般我們這種clean的目標(biāo)文件,我們將它設(shè)置為偽目標(biāo),用 .PHONY 修飾,偽目標(biāo)的特性是,總是被執(zhí)行的。
? 可以將我們的 hello 目標(biāo)文件聲明成偽目標(biāo),測試一下。
什么叫做總是被執(zhí)行?
$ stat XXXFile: ‘XXX’Size: 987 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 1321125 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ whb) Gid: ( 1000/ whb)
Access: 2024-10-25 17:05:30.430619002 +0800
Modify: 2024-10-25 17:05:25.940595116 +0800
Change: 2024-10-25 17:05:25.940595116 +0800?件 = 內(nèi)容 + 屬性
Modify: 內(nèi)容變更,時間更新
Change:屬性變更,時間更新
Access:常指的是?件最近?次被訪問的時間。在Linux的早期版本中,每當(dāng)?件被訪問時,其atime都會更新。但這種機(jī)制會導(dǎo)致?量的IO操作。具體更新原則,不做過多解釋。
📌 結(jié)論:
.PHONY:讓make忽略源文件和可執(zhí)行目標(biāo)文件的M時間對比
十三、推導(dǎo)過程
myproc:myproc.o gcc myproc.o -o myproc
myproc.o:myproc.sgcc -c myproc.s -o myproc.o
myproc.s:myproc.i gcc -S myproc.i -o myproc.s
myproc.i:myproc.c gcc -E myproc.c -o myproc.i .PHONY:clean
clean: rm -f *.i *.s *.o myproc
編譯
$ make
gcc -E myproc.c -o myproc.i
gcc -S myproc.i -o myproc.s
gcc -c myproc.s -o myproc.o
gcc myproc.o -o myproc
make是如何工作的,在默認(rèn)的方式下,也就是我們只輸入make命令。那么:
- make會在當(dāng)前目錄下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它會找文件中的第一個目標(biāo)文件(target),在上面的例子中,他會找到 myproc 這個文件,并把這個文件作為最終的目標(biāo)文件。
- 如果 myproc 文件不存在,或是 myproc 所依賴的后面的 myproc.o 文件的文件修改時間要比 myproc 這個文件新(可以用 touch 測試),那么,他就會執(zhí)行后面所定義的命令來生成myproc 這個文件。
- 如果 myproc 所依賴的 myproc.o 文件不存在,那么 make 會在當(dāng)前文件中找目標(biāo)為myproc.o 文件的依賴性,如果找到則再根據(jù)那一個規(guī)則生成 myproc.o 文件。(這有點(diǎn)像一個堆棧的過程)
- 當(dāng)然,你的C文件和H文件是存在的啦,于是 make 會生成 myproc.o 文件,然后再用 myproc.o 文件聲明 make 的終極任務(wù),也就是執(zhí)行文件 hello 了。
- 這就是整個make的依賴性,make會一層又一層地去找文件的依賴關(guān)系,直到最終編譯出第一個目標(biāo)文件。
- 在找尋的過程中,如果出現(xiàn)錯誤,比如最后被依賴的文件找不到,那么make就會直接退出,并報錯,而對于所定義的命令的錯誤,或是編譯不成功,make根本不理。
- make只管文件的依賴性,即,如果在我找了依賴關(guān)系之后,冒號后面的文件還是不在,那么對不起,我就不工作啦。
十四、適度擴(kuò)展語法
BIN=proc.exe # 定義變量
CC=gcc
#SRC=$(shell ls *.c) # 采?shell命令??式,獲取當(dāng)前所有.c?件名
SRC=$(wildcard *.c) # 或者使? wildcard 函數(shù),獲取當(dāng)前所有.c?件名
OBJ=$(SRC:.c=.o) # 將SRC的所有同名.c 替換 成為.o 形成?標(biāo)?件列表
LFLAGS=-o # 鏈接選項(xiàng)
FLAGS=-c # 編譯選項(xiàng)
RM=rm -f # 引?命令$(BIN):$(OBJ)@$(CC) $(LFLAGS) $@ $^ # $@:代表?標(biāo)?件名。 $^: 代表依賴?件列表@echo "linking ... $^ to $@"
%.o:%.c # %.c 展開當(dāng)前?錄下所有的.c。 %.o: 同時展開同
名.o@$(CC) $(FLAGS) $< # %<: 對展開的依賴.c?件,?個?個的交給gcc。@echo "compling ... $< to $@" # @:不回顯命令.PHONY:clean
clean:$(RM) $(OBJ) $(BIN) # $(RM): 替換,?變量內(nèi)容替換它.PHONY:test
test:@echo $(SRC)@echo $(OBJ)