???作者:@malloc不出對象
?專欄:Linux的學習之路
??個人簡介:一名雙非本科院校大二在讀的科班編程菜鳥,努力編程只為趕上各位大佬的步伐????
前言
本篇文章我們將要講解的是項目自動化構建工具make與makefile。
一、make/makefile的背景
會不會寫makefile,從一個側面說明了一個人是否具備完成大型工程的能力,一個工程中的源文件不計數,其按類型、功能、模塊分別放在若干個目錄中,makefile定義了一系列的規(guī)則來指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至于進行更復雜的功能操作。makefile帶來的好處就是——“自動化編譯”,一旦寫好只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件開發(fā)的效率。
在Linux下,make命令主要用于自動化構建(build)軟件項目,它能夠根據程序員編寫的Makefile文件中的指令,自動編譯、鏈接、打包和安裝軟件。
具體而言,make完成以下工作:
1.根據Makefile文件中的規(guī)則,檢查每個源文件的修改時間和依賴關系,確定哪些文件需要重新編譯。
2.編譯源文件,生成目標文件。
3.鏈接目標文件,生成可執(zhí)行文件或庫文件。
4.打包可執(zhí)行文件或庫文件,以便于分發(fā)和安裝。
5.安裝可執(zhí)行文件或庫文件到指定的目錄中。
通過使用make命令,程序員可以更加方便和高效地管理項目中的代碼編譯、構建和部署等任務。
Makefile是一個文本文件,其中包含了一系列規(guī)則和指令,用于告訴make命令如何構建(build)軟件項目。
具體而言,Makefile完成以下工作:
1.定義變量:可以定義一些變量,用于存儲常量或者目錄路徑等信息。
2.定義規(guī)則:規(guī)則指定了如何生成一個或多個目標文件。每個規(guī)則包含了目標文件、依賴文件和生成命令。如果目標文件需要更新,make會根據規(guī)則自動執(zhí)行生成命令。
3.定義偽目標:偽目標是沒有實際文件對應的目標,只是一個執(zhí)行動作的標簽。偽目標可以用來執(zhí)行清理操作、運行測試腳本等操作。
4.定義命令:命令是指make需要執(zhí)行的操作。可以是編譯源代碼、鏈接目標文件、打包可執(zhí)行文件等操作。
5.定義依賴關系:依賴關系指明哪些文件是構建目標文件所必需的。當依賴文件被修改時,make會根據依賴關系重新構建目標文件。
通過編寫Makefile文件,程序員可以實現對項目的自動化構建,提高項目構建的效率和可靠性。
注:make是一個指令,makefile是一個文件!!
二、makefile的基本結構
makefile是一個圍繞依賴關系和依賴方法構建的一個自動化編譯的工具,我們想完成一件事,必須有正確的依賴關系和正確的依賴方法。
我們先來看看它的基本結構:
注意:依賴方法必須以tab(tab長度不定,我們一般tab以4字符為一個間隔)開頭,這是語法規(guī)定?。?/strong>
如果我們使用空格的話會出現下面的情況:
我們可以看到不符合Makefile規(guī)則是不能完成構建的。另外細心的讀者也可以發(fā)現如果使用tab符合Makefile規(guī)則的話是會顏色高亮的!!
下面我們來簡單使用make演示一下:
看了上圖那么我有一個問題,為什么直接使用make就執(zhí)行了第一個目標文件的依賴方法??而第二個目標文件clean卻要顯式的使用make clean?
這是因為編譯器會默認從上往下掃描,找到文件中的第一個目標文件,在上面的例子中,他會找到“myfile”這個目標文件,然后根據依賴方法得到可執(zhí)行程序??!而其他的目標文件則需要指令文件名才能進行編譯了。
下面我們來驗證一下,這次我將clean目標文件放在最前面:
我們發(fā)現此時make執(zhí)行的就是第一個目標文件的依賴方法了,而myfile需要make指明構建:
回到之前的例子,我們來看看下面出現的現象:
為什么make只能進行一次,然后就提示myfile這個可執(zhí)行程序已經是最新的了,不需要重新編譯了,而make clean卻能執(zhí)行多次??
這是因為clean的依賴文件列表為空,則意味著該目標文件不依賴于任何文件。這意味著在執(zhí)行該目標時,不需要檢查其依賴項是否已經更新,因為沒有依賴項需要檢查;而myfile可執(zhí)行程序它其實是需要根據依賴文件來判斷是否需要重新編譯的??!
Q:那么請問gcc是如何根據依賴文件來判斷是否需要重新編譯?
實際上gcc是根據依賴文件修改的時間來判斷是否需要重新編譯的,我們的源文件(依賴文件)對應一個時間戳,我們的目標文件也有一個時間戳,我們知道源文件(依賴文件)的創(chuàng)建時間一定比目標文件要早,這樣才能形成目標文件對吧;而在源文件(依賴文件)進行修改之后它的時間戳就進行了更新,此時
源文件(依賴文件)的時間戳比目標文件新,那么Make工具就會重新構建這個目標文件。
如下圖我簡單的展示一下:
下面我們來進行實驗一下:
修改依賴文件(源文件)之后:
Q:那么有沒有辦法不修改源文件也可以使目標文件能夠重新構建呢?
我們可以使用touch指令將依賴文件(源文件)更新到最新的時間戳,那么此時我們的依賴文件(源文件)比目標文件的時間戳更新,所以我們就能重新進行編譯了?。。?/p>
總結:如果這個目標文件不存在或者它的時間戳比依賴文件的時間戳更舊,那么 Make 工具會重新構建這個目標文件;否則,它會認為這個目標文件已經是最新的,不需要重新構建。
三、項目清理
工程是需要被清理的,像clean這種,沒有被第一個目標文件直接或間接關聯,那么它后面所定義的命令將不會被自動執(zhí)行,不過我們可以顯式要make執(zhí)行。即命令——“make clean”,以此來清除所有的目標文件,以便重新編譯。但是一般我們這種clean的目標文件,我們將它設置為偽目標,用 .PHONY 修飾,偽目標表示一個“虛擬目標”,它并不對應于任何實際的文件,而是表示一些需要執(zhí)行的命令。當執(zhí)行偽目標時,Make 不會檢查它是否存在于文件系統中,而是僅僅執(zhí)行對應的命令。我們記住它最重要的特性——總是執(zhí)行的。
我們來看看它的使用方式:
我們來看看現象:
我們發(fā)現在clean被.PHONY修飾之后能執(zhí)行多次,那之前我們寫的clean依賴的文件列表為空不是也能完成這個任務嗎?它們之間一樣嗎?
首先目標文件依賴的文件為空和.PHONY修飾的偽目標是兩個不同的概念。
目標文件依賴的文件為空:
- 當一個目標文件依賴的文件為空時,make 不會檢查這個依賴項是否存在或是否被更新。這通常用于創(chuàng)建某個目標文件,但其依賴項是通過其他方式處理或生成的情況。
.PHONY
修飾的偽目標:.PHONY
是一個特殊的目標,用于聲明一些偽目標。偽目標不是實際的文件名,而是 makefile 中定義的一組操作。.PHONY
目標聲明的目標通常不需要實際的文件作為依賴項。當你運行 make 時,它將忽略這些目標是否存在或是否被更新,而是直接執(zhí)行定義的操作。
總的來說,目標文件依賴的文件為空適用于依賴項是通過其他方式生成的情況,而.PHONY
適用于聲明一組操作而不需要實際文件作為依賴項的情況。
Q:"依賴項通過其他方式生成"是什么意思呢?
“依賴項通過其他方式生成” 的意思是在構建某個目標時,它的某個依賴項并不是一個實際的文件,而是通過其他方式生成的,比如:
1.通過命令行運行一些程序生成的結果。
2.通過其他的 Makefile 規(guī)則生成的結果。
3.通過復制或者下載遠程文件得到的結果。
在這些情況下,依賴項并不是一個實際的文件,因此無法使用文件的時間戳來判斷它是否需要重新構建。相反,你需要手動定義生成依賴項的規(guī)則,以確保它們能夠在構建目標之前正確地生成。
我們來看看具體的例子:
上述例子我們通過clean目標文件創(chuàng)建了一個新的文件touch-file,我們還可以進行一些命令行的其他指令等,依賴項通過其他方式生成還有其他的情況,這里我就不一一進行講解了,感興趣的讀者下來可以試試。
利用.PHONY修飾目標文件,我們也可以重新編譯之前的目標文件了,我們一起來看看:
.PHONY修飾目標文件后忽略目標是否存在或是否被更新,直接執(zhí)行定義的操作,只會更新目標文件的時間戳??!
雖然我們達到了重新編譯的目的,但其實我們是非常不推薦這種行為的,因為重新編譯需要消耗大量的時間(大型工程中),而且我們的文件內容并沒有進行修改,重新編譯也沒什么必要??!
四、makefile的依賴
我們來看看下面這個例子,它很好的體現了makefile的依賴關系:
我們平時還是不建議這些寫哈,因為沒什么必要我們只要得到目標文件就可以了。
五、如何快速編寫大型項目中的Makefile文件
我們在前面編寫的Makefile文件都只涉及一個源文件的編譯鏈接,但是如果在一個大型項目中我們有很多的源文件需要被合并鏈接,難道我們一個個的手動去添加嗎??
既然有這種問題的出現,那么必然會有對應的解決辦法。對于大型工程,我們一般采用模塊化的方式組織代碼,每個模塊對應一個或多個源文件。為了避免將全部源文件寫出來進行鏈接,可以使用通配符或變量來自動化處理源文件?。?!
以下是一個示例 Makefile,演示如何使用通配符和變量來構建一個大型工程:
# 編譯器
CC = gcc
# 列出Makefile所在目錄中所有的.c文件
SOURCES := $(wildcard *.c)
# 列出SOURCES中所有.c文件對應的.o文件
OBJECTS := $(patsubst %.c,%.o,$(SOURCES))
# 最后生成的目標文件
TARGET = myfile
# 目標文件依賴OBJECTS中的所有.o文件,變量使用時需要在變量名前加上"$"符號,并且最好用小括號或者大括號把變量括起來
$(TARGET): $(OBJECTS)
$(CC) $^ -o $@ # 所有依賴目標集$^利用CC編譯器,一個一個編譯成目標文件集$@
%.o: %.c # 所有的.o目標文件依賴于對應的.c文件
$(CC) -c $< -o $@ # 所有的.c文件一個一個的取出來,利用CC編譯器形成同名對應的.o文件
# 清理所有的.o文件以及目標文件
.PHONY:clean
clean:
rm -rf $(OBJECTS) $(TARGET)
我們的源文件如下圖:
Makefile中的內容:
上述圖中的CC、SOURCES、OBJECTS以及TARGET都是變量,在Makefile中變量其實也就是C/C++中的宏?。。?/p>
在OBJECTS變量中我們使用了一個patsubst
模式字符串替換函數。
使用格式:
$(patsubst \<pattern>,\<replacement>,\<text>)
。
返回:函數返回被替換過后的字符串。
功能:查找<text> 中的單詞(單詞以“空格”、“Tab”或“回車”“換行”分隔)是否符合模式 <pattern> ,如果匹配的話,則以 替換。這里, <pattern> 可以包括通配符 % ,表示任意長度的字串。如果<replacement> 中也包含 % ,那么,<replacement> 中的這個 % 將是 <pattern> 中的那個 % 所代表的字串。(可以用 \ 來轉義,以 % 來表示真實含義的 % 字符)
注: \$(objects:.o=.c)
和 $(patsubst \%.o,\%.c,\$(objects))
是一樣的,大家下來可以試試。
另外我們上述看到的\$^
、$@
、\$<
都被叫做自動化變量?。?br> 所謂自動化變量,就是這種變量會把模式中所定義的一系列的文件自動地挨個取出,直至所有的符合模式的文件都取完了。這種自動化變量只應出現在規(guī)則的命令中。
下面是所有的自動化變量及其說明:
$@ : 表示規(guī)則中的目標文件集。在模式規(guī)則中,如果有多個目標,那么, $@ 就是匹配于目標中模式定義的集合。
$% : 僅當目標是函數庫文件中,表示規(guī)則中的目標成員名。例如,如果一個目標是 foo.a(bar.o) ,那么, $% 就是 bar.o , $@ 就是 foo.a 。如果目標不是函數庫文件(Unix下是 .a ,Windows下是 .lib ),那么,其值為空。
$< : 依賴目標中的第一個目標名字。如果依賴目標是以模式(即 % )定義的,那么 $< 將是符合模式的一系列的文件集。注意,其是一個一個取出來的。
$? : 所有比目標新的依賴目標的集合。以空格分隔。
$^ : 所有的依賴目標的集合。以空格分隔。如果在依賴目標中有多個重復的,那么這個變量會去除重復的依賴目標,只保留一份。
$+ : 這個變量很像 $^ ,也是所有依賴目標的集合。只是它不去除重復的依賴目標。
$* :這個變量表示目標模式中 % 及其之前的部分。如果目標是 dir/a.foo.b ,并且目標的模式是 a.%.b ,那么, $* 的值就是 dir/foo 。這個變量對于構造有關聯的文件名是比較有效。如果目標中沒有模式的定義,那么 $* 也就不能被推導出,但是,如果目標文件的后綴是make所識別的,那么 $* 就是除了后綴的那一部分。例如:如果目標是 foo.c ,因為 .c 是make所能識別的后綴名,所以, $* 的值就是 foo 。這個特性是GNU make的,很有可能不兼容于其它版本的make,所以,你應該盡量避免使用 $* ,除非是在隱含規(guī)則或是靜態(tài)模式中。如果目標中的后綴是make所不能識別的,那么 $* 就是空值。
通過上述使用變量以及通配符我們已經配置好了一個簡單的自動化構建的Makefile文件,下面我們來檢測一下:
通過這種方式,我們就實現了自動化處理大量的源文件,避免了手動編譯的繁瑣。文章來源:http://www.zghlxwxcb.cn/news/detail-438741.html
本篇文章的Makefile就講到這里了,它是我們開發(fā)項目中非常重要的一個工具,它非常的靈活。一個Makefile寫的好不好決定了你的工作效率;另外,其實Makefile其實還有很多功能在這篇文章中并未進行展示,因為博主的精力和水平還不夠就講了大致的一部分的,如果有讀者感興趣的話可以拜讀一下這位巨佬的博客分享哦orz~????文章來源地址http://www.zghlxwxcb.cn/news/detail-438741.html
到了這里,關于【Linux】項目自動化構建工具make/makefile的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!