make是一個(gè)命令工具,它解釋 Makefile 中的指令(應(yīng)該說(shuō)是規(guī)則)韭脊。在 Makefile文件中描述了整個(gè)工程所有文件的編譯順序、編譯規(guī)則单旁。
準(zhǔn)備知識(shí):編譯沪羔,鏈接,靜態(tài)庫(kù)象浑,共享庫(kù)
- 編譯:把高級(jí)語(yǔ)言所書寫的代碼轉(zhuǎn)換成機(jī)器可識(shí)別的指令任内,此時(shí)還不能夠被執(zhí)行,編譯器通過(guò)檢查高級(jí)語(yǔ)言的語(yǔ)法融柬,函數(shù)和變量的聲明是否正確死嗦!如果正確則產(chǎn)生中間目標(biāo)文件(目標(biāo)文件在Liunx中默認(rèn)后綴為“.o”)
- 鏈接:將多.o 文件,或者.o 文件和庫(kù)文件鏈接成為可被操作系統(tǒng)執(zhí)行的可執(zhí)行程序
- 靜態(tài)庫(kù):又稱為文檔文件(Archive File) 粒氧。它是多個(gè).o文件的集合越除。Linux中靜態(tài)庫(kù)文件的后綴為“.a”
- 共享庫(kù):也是多個(gè).o 文件的集合,但是這些.o 文件時(shí)有編譯器按照一種特殊的方式生成(共享庫(kù)已經(jīng)具備了可執(zhí)行條件)
在執(zhí)行 make 之前外盯,需要一個(gè)命名為 Makefile 的特殊文件(本文的后續(xù)將使用Makefile 作為這個(gè)特殊文件的文件名)來(lái)告訴 make 需要做什么(完成什么任務(wù))摘盆,該怎么做。
當(dāng)使用make 工具進(jìn)行編譯時(shí)饱苟,工程中以下幾種文件在執(zhí)行make 時(shí)將會(huì)被編譯(重新編譯):
- 所有的源文件沒(méi)有被編譯過(guò)孩擂,則對(duì)各個(gè) C 源文件進(jìn)行編譯并進(jìn)行鏈接,生成最后的可執(zhí)行程序箱熬;
- 每一個(gè)在上次執(zhí)行 make 之后修改過(guò)的 C 源代碼文件在本次執(zhí)行make 時(shí)將會(huì)被重新編譯类垦;
- 頭文件在上一次執(zhí)行make 之后被修改。則所有包含此頭文件的 C 源文件在本次執(zhí)make 時(shí)將會(huì)被重新編譯城须。
Makefile規(guī)則介紹
一個(gè)簡(jiǎn)單的 Makefile 描述規(guī)則組成:
TARGET... : PREREQUISITES...
COMMAND
...
...
- target:規(guī)則的目標(biāo)蚤认。通常是最后需要生成的文件名或者為了實(shí)現(xiàn)這個(gè)目的而必需的中間過(guò)程文件名「夥ィ可以是.o文件砰琢、也可以是最后的可執(zhí)行程序的文件名等。另外,目標(biāo)也可以是一個(gè)make執(zhí)行的動(dòng)作的名稱陪汽,如目標(biāo)“clean”(目標(biāo)“clean”不是一個(gè)文件训唱,它僅僅代表執(zhí)行一個(gè)動(dòng)作的標(biāo)識(shí)),我們稱這樣的目標(biāo)是“偽目標(biāo)”挚冤。
- prerequisites:規(guī)則的依賴雪情。生成規(guī)則目標(biāo)所需要的文件名列表。通常一個(gè)目標(biāo)依賴于一個(gè)或者多個(gè)文件你辣。
- command:規(guī)則的命令行。是規(guī)則所要執(zhí)行的動(dòng)作(任意的shell 命令或者是可在shell 下執(zhí)行的程序)尘执。它限定了make 執(zhí)行這條規(guī)則時(shí)所需要的動(dòng)作舍哄。
這也是書寫 Makefile 中容易產(chǎn)生,而且比較隱蔽的錯(cuò)誤誊锭。
命令就是在任何一個(gè)目標(biāo)的依賴文件發(fā)生變化后重建目標(biāo)的動(dòng)作描述表悬。一個(gè)目標(biāo)可以沒(méi)有依賴而只有動(dòng)作(指定的命令)。比如Makefile 中的目標(biāo)“clean”丧靡,此目標(biāo)沒(méi)有依賴蟆沫,只有命令。它所定義的命令用來(lái)刪除 make 過(guò)程產(chǎn)生的中間文件(進(jìn)行清理工作)温治。
在 Makefile 中“規(guī)則”就是描述在什么情況下饭庞、如何重建規(guī)則的目標(biāo)文件,通常規(guī)則中包括了目標(biāo)的依賴關(guān)系(目標(biāo)的依賴文件)和重建目標(biāo)的命令熬荆。make 執(zhí)行重建目標(biāo)的命令舟山,來(lái)創(chuàng)建或者重建規(guī)則的目標(biāo)(此目標(biāo)文件也可以是觸發(fā)這個(gè)規(guī)則的上一個(gè)規(guī)則中的依賴文件)。規(guī)則包含了文件之間的依賴關(guān)系和更新此規(guī)則目標(biāo)所需要的命令卤恳。
一個(gè) Makefile 文件中通常還包含了除規(guī)則以外的很多東西(后續(xù)我們會(huì)一步一步的展開)累盗。一個(gè)最簡(jiǎn)單的Makefile 可能只包含規(guī)則。規(guī)則在有些 Makefile 中可能看起來(lái)非常復(fù)雜突琳,但是無(wú)論規(guī)則的書寫是多么的復(fù)雜若债,它都符合規(guī)則的基本格式。
make 程序根據(jù)規(guī)則的依賴關(guān)系拆融,決定是否執(zhí)行規(guī)則所定義的命令的過(guò)程我們稱之為執(zhí)行規(guī)則蠢琳。
簡(jiǎn)單的示例
一個(gè)簡(jiǎn)單的Makefile,來(lái)描述如何創(chuàng)建最終的可執(zhí)行文件“edit”镜豹,此可執(zhí)行文件依賴于8個(gè)C源文件和3個(gè)頭文件挪凑。Makefile文件的內(nèi)容如下:
#sample Makefile
edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
首先書寫時(shí),可以將一個(gè)較長(zhǎng)行使用反斜線(\ )來(lái)分解為多行逛艰。但需要注意:反斜線之后不能有空格(這也是大 家最容易犯的錯(cuò)誤躏碳,錯(cuò)誤比較隱蔽)。
在完成了這個(gè)Maekfile以后;需要?jiǎng)?chuàng)建可執(zhí)行程序“edit”菇绵,所要做的就是在包含此Makefile的目錄(當(dāng)然也在代碼所在的目錄)下輸入命令“make”肄渗。刪除已經(jīng)此目錄下之前使用“make”生成的文件(包括那些中間過(guò)程的.o文件),也只需要輸入命令“make clean”就可以了咬最。
make如何工作
默認(rèn)的情況下翎嫡,make執(zhí)行的是Makefile中的第一個(gè)規(guī)則,此規(guī)則的第一個(gè)目標(biāo)稱之為“最終目的”或者“終極目標(biāo)”(就是一個(gè)Makefile最終需要更新或者創(chuàng)建的目標(biāo))永乌。
上例的 Makefile惑申,目標(biāo)edit在Makefile中是第一個(gè)目標(biāo),因此它就是make的終極目標(biāo)翅雏。當(dāng)修改了任何C源文件或者頭文件后圈驼,執(zhí)行 make 將會(huì)重建終極目標(biāo)edit。
當(dāng)在shell提示符下輸入“make”命令以后望几。make
讀取當(dāng)前目錄下的Makefile文件
绩脆,并將Makefile文件
中的第一個(gè)目標(biāo)作為其執(zhí)行的終極目標(biāo),開始處理第一個(gè)規(guī)則(終極目標(biāo)所在的規(guī)則)橄抹。在上例中靴迫,第一個(gè)規(guī)則就是目標(biāo)edit所在的規(guī)則。規(guī)則描述了edit的依賴關(guān)系楼誓,并定義了鏈接.o文件
生成目標(biāo)edit的命令玉锌;make
在執(zhí)行這個(gè)規(guī)則所定義的命令之前,首先處理目標(biāo)edit的所有的依賴文件(例子中的那些.o文件
)的更新規(guī)則(以這些.o文件
為目標(biāo)的規(guī)則)疟羹。對(duì)這些.o文件
為目標(biāo)的規(guī)則處理有下列三種情況:
- 目標(biāo).o 文件不存在芬沉,使用其描述規(guī)則創(chuàng)建它;
- 目標(biāo).o 文件存在阁猜,目標(biāo).o 文件所依賴的.c 源文件丸逸、.h 文件中的任何一個(gè)比目標(biāo).o文件“更新”(在上一次 make 之后被修改)。則根據(jù)規(guī)則重新編譯生成它剃袍;
- 目標(biāo).o 文件存在黄刚,目標(biāo).o 文件比它的任何一個(gè)依賴文件(的.c 源文件、.h 文件) “更新”(它的依賴文件在上一次make 之后沒(méi)有被修改)民效,則什么也不做憔维。
這些.o 文件
所在的規(guī)則之所以會(huì)被執(zhí)行,是因?yàn)檫@些.o 文件
出現(xiàn)在終極目標(biāo)的依賴列表中畏邢。在 Makefile
中一個(gè)規(guī)則的目標(biāo)如果不是終極目標(biāo)所依賴的(或者終極目標(biāo)的依賴文件所依賴的)业扒,那么這個(gè)規(guī)則將不會(huì)被執(zhí)行,除非明確指定執(zhí)行這個(gè)規(guī)則(可以通過(guò)make
的命令行指定重建目標(biāo)舒萎,那么這個(gè)目標(biāo)所在的規(guī)則就會(huì)被執(zhí)行程储,例如make clean
)。在編譯或者重新編譯生成一個(gè).o文件
時(shí),make
同樣會(huì)去尋找它的依賴文件的重建規(guī)則(是這樣一個(gè)規(guī)則:這個(gè)依賴文件在規(guī)則中作為目標(biāo)出現(xiàn))章鲤,在這里就是.c 和.h 文件的重建規(guī)則摊灭。在上例的Makefile
中沒(méi)有哪個(gè)規(guī)則的目標(biāo)是.c或者.h 文件,所以沒(méi)有重建.c 和.h 文件的規(guī)則败徊。
完成了對(duì).o 文件的創(chuàng)建(第一次編譯)或者更新之后帚呼,make 程序?qū)⑻幚斫K極目標(biāo)“edit”所在的規(guī)則,分為以下三種情況:
- 目標(biāo)文件“edit”不存在皱蹦,則執(zhí)行規(guī)則以創(chuàng)建目標(biāo)“edit”煤杀。
- 目標(biāo)文件“edit”存在,其依賴文件中有一個(gè)或者多個(gè)文件比它“更新”沪哺,則根據(jù)規(guī)則重新鏈接生成“edit”沈自。
- 目標(biāo)文件“edit”存在,它比它的任何一個(gè)依賴文件都“更新”凤粗,則什么也不做。
上例中今豆,如果更改了源文件“insert.c”后執(zhí)行make嫌拣,“insert.o”將被更新,之后終極目標(biāo)“edit”將會(huì)被重生成呆躲;如果我們修改了頭文件“command.h”之后運(yùn)行“make”异逐,那么“kbd.o”、“command.o”和“files.o”將會(huì)被重新編譯插掂,之后同樣終極目標(biāo)“edit”也將被重新生成灰瞻。
指定變量
objects
作為一個(gè)變量,它代表所有的.o文件的列表辅甥。在定義了此變量后酝润,我們就可以在需要使用這些.o文件列表的地方使用“$(objects)”來(lái)表示.o文件的列表,而不需要羅列所有的.o文件璃弄。
make如何解析makefile文件
GUN make 的執(zhí)行過(guò)程分為兩個(gè)階段要销。
- 讀取所有的 makefile 文件(包括“MAKIFILES”變量指定的、指示符“include”指定的夏块、以及命令行選項(xiàng)“-f(--file)”指定的 makefile 文件)疏咐,內(nèi)建所有的變量、明確規(guī)則和隱含規(guī)則脐供,并建立所有目標(biāo)和依賴之間的依賴關(guān)系結(jié)構(gòu)鏈表浑塞。
- 根據(jù)第一階段已經(jīng)建立的依賴關(guān)系結(jié)構(gòu)鏈表決定哪些目標(biāo)需要更新,并使用對(duì)應(yīng)的規(guī)則來(lái)重建這些目標(biāo)政己。
總結(jié)
make 的執(zhí)行過(guò)程如下:
- 依次讀取變量
MAKEFILES
定義的makefile文件列表
- 讀取工作目錄下的makefile文件(根據(jù)命名的查找順序
GNUmakefile
酌壕,makefile
,Makefile
,首先找到哪個(gè)就讀取哪個(gè)) - 依次讀取工作目錄
makefile文件
中使用指示符include
包含的文件 - 查找重建所有已讀取的
makefile文件
的規(guī)則(如果存在一個(gè)目標(biāo)是當(dāng)前讀取的 某一個(gè)makefile文件
仅孩,則執(zhí)行此規(guī)則重建此makefile文件
托猩,完成以后從第一步開始重新執(zhí)行) - 初始化變量值并展開那些需要立即展開的變量和函數(shù)并根據(jù)預(yù)設(shè)條件確定執(zhí)行分支
- 根據(jù)終極目標(biāo)以及其他目標(biāo)的依賴關(guān)系建立依賴關(guān)系鏈表
- 執(zhí)行除終極目標(biāo)以外的所有的目標(biāo)的規(guī)則(規(guī)則中如果依賴文件中任一個(gè) 文件的時(shí)間戳比目標(biāo)文件新,則使用規(guī)則所定義的命令重建目標(biāo)文件)
- 執(zhí)行終極目標(biāo)所在的規(guī)則
說(shuō)明:
執(zhí)行一個(gè)規(guī)則的過(guò)程是這樣的:
對(duì)于一個(gè)存在的規(guī)則(明確規(guī)則和隱含規(guī)則)
首先辽慕,make程序?qū)⒈容^目標(biāo)文件和所有的依賴文件的時(shí)間戳京腥。如果目標(biāo)的時(shí)間戳比所有依賴文件的時(shí)間戳更新(依賴文件在上一次執(zhí)行make之后沒(méi)有被修改),那么什么也不做溅蛉。否則(依賴文件中的某一個(gè)或者全部在上一次執(zhí)行make后已經(jīng)被修改過(guò))公浪,規(guī)則所定義的重建目標(biāo)的命令將會(huì)被執(zhí)行。這就是make工作的基礎(chǔ)船侧,也是其執(zhí)行規(guī)制所定 義命令的依據(jù)欠气。