有感于網(wǎng)絡(luò)上很多技術(shù)博客的排版方式不夠統(tǒng)一滚秩,并趨向于隨意专执,影響學(xué)習(xí)的效率和動(dòng)力,故而根據(jù)自己的理解將其重新編排如下郁油,不算原創(chuàng)本股,算作是自己的學(xué)習(xí)心得吧,也可供自己日后翻看桐腌。
一個(gè)Makefile例子
make 命令執(zhí)行時(shí)拄显,需要根據(jù)一些規(guī)則來決定按照怎么樣的方式去編譯和鏈接程序,這些規(guī)則就由 makefile 文件所指定案站。如果我們 makefile 文件寫的足夠好躬审,make 命令會(huì)自動(dòng)地根據(jù)當(dāng)前的文件修改的情況來確定哪些文件需要重編譯,從而自己編譯所需要的文件和鏈接目標(biāo)程序蟆盐。
首先承边,本文將給出一個(gè)makefile文件的示例,以便大家能有一個(gè)直觀感受舱禽,這個(gè)例子來源于GNU的make使用手冊(cè)炒刁。在這個(gè)例子中恩沽,我們的工程有8個(gè)c文件誊稚,和3個(gè)頭文件,我們要寫一個(gè)makefile來告訴make命令如何編譯和鏈接這幾個(gè)文件。例子如下:
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
這個(gè)例子里 make 的編碼規(guī)則如下:
a. 如果這個(gè)工程沒有編譯過里伯,那么我們的所有c文件都要編譯并被鏈接城瞎。
b. 如果這個(gè)工程的某幾個(gè)c文件被修改,那么我們只編譯被修改的c文件疾瓮,并鏈接目標(biāo)程序脖镀。
c. 如果這個(gè)工程的頭文件被改變了,那么我們需要編譯引用了這幾個(gè)頭文件的c文件狼电,并鏈接目標(biāo)程序蜒灰。
Makefile規(guī)則
在詳細(xì)拆解上一節(jié)的 Makefile 之前,先來看下 Makefile 的基本范式肩碟。
target ... :prerequisites ...
command
...
target可以是一個(gè) 1) object file(可執(zhí)行文件)强窖,2) 可執(zhí)行文件,還可以是個(gè)3) label(標(biāo)簽)削祈,關(guān)于標(biāo)簽這個(gè)特性翅溺,在后面的偽目標(biāo)章節(jié)還會(huì)有敘述。
prerequisites 就是髓抑,要生成那個(gè)target所需要的文件或是目標(biāo)咙崎。command 也就是 make 需要執(zhí)行的命令,可以是任意的
shell 命令吨拍。
這是一個(gè)文件的依賴關(guān)系褪猛,也就是說,target 這一個(gè)或多個(gè)的目標(biāo)文件依賴于 prerequisites 中的文件羹饰,其生成規(guī)則定義在 command中握爷。同時(shí),prerequisites 中如果有一個(gè)以上的文件比target文件要新的話严里,command 所定義的命令就會(huì)被執(zhí)行新啼。這就是Makefile的規(guī)則,也是 Makefile 中最核心的內(nèi)容刹碾。
有了這些規(guī)則后燥撞,再來分析上面的例子。在這個(gè) makefile 中迷帜,目標(biāo)文件(target)包含:
- 執(zhí)行文件
edit
- 中間目標(biāo)文件(
*.o
)
依賴文件(prerequisites)就是冒號(hào)后面的那些 .c
文件和 .h
文件物舒。每一個(gè) .o
文件都有一組依賴文件,而這些 .o
文件又是執(zhí)行文件 edit 的依賴文件戏锹。
在定義好依賴關(guān)系后冠胯,后續(xù)的那一行定義了如何生成目標(biāo)文件的系統(tǒng)命令,一定要以一個(gè)tab鍵作為開頭锦针。make會(huì)比較
targets 文件和 prerequisites 文件的修改日期荠察,如果 prerequisites 文件的日期要比targets文件的日期要新置蜀,或者 target 不存在的話,那么悉盆,make就會(huì)執(zhí)行后續(xù)定義的命令盯荤。
我們可以把這個(gè)內(nèi)容保存在名字為makefile
或Makefile
的文件中,然后在該目錄下直接輸入命令 make 就可以生成可執(zhí)行文件edit焕盟。如果要?jiǎng)h除執(zhí)行文件和所有的中間目標(biāo)文件秋秤,那么,只要簡(jiǎn)單地執(zhí)行一下 make clean
就可以了脚翘。注:反斜線(\)是換行符的意思灼卢,這樣比較便于閱讀。
這里要說明一點(diǎn)的是来农,clean 不是一個(gè)文件芥玉,它只不過是一個(gè)動(dòng)作名字,有點(diǎn)像C語言中的 lable 一樣备图,其冒號(hào)后什么也沒有灿巧,那么,make就不會(huì)去找它的依賴性揽涮,也就不會(huì)自動(dòng)執(zhí)行其后所定義的命令抠藕。要執(zhí)行其后的命令(不僅用于 clean,其他 lable 同樣適用)蒋困,就要在 make 命令后顯式指出這個(gè) lable 的名字盾似。這樣的方法非常有用,我們可以在一個(gè) makefile 中定義不用的編譯或是和編譯無關(guān)的命令雪标,比如程序的打包零院,程序的備份,等等村刨。
Make是如何工作的
在默認(rèn)的方式下告抄,也就是我們只輸入make命令。那么嵌牺,
- make 會(huì)在當(dāng)前目錄下找名字叫
Makefile
或makefile
的文件打洼。 - 如果找到,它會(huì)找文件中的第一個(gè)目標(biāo)文件(target)逆粹,在上面的例子中募疮,它會(huì)找到 edit 這個(gè)文件,并把這個(gè)文件作為最終的目標(biāo)文件僻弹。
- 如果 edit 文件不存在阿浓,或是 edit 所依賴的后面的
.o
文件的文件修改時(shí)間要比 edit 這個(gè)文件新,那么蹋绽,它就會(huì)執(zhí)行后面所定義的命令來生成 edit 這個(gè)文件芭毙。 - 如果 edit 所依賴的
.o
文件也不存在筋蓖,那么 make 會(huì)在當(dāng)前文件中找目標(biāo)為.o
文件的依賴性,如果找到則再根據(jù)那一個(gè)規(guī)則生成.o
文件稿蹲。(有點(diǎn)像是堆棧的過程) - 當(dāng)然扭勉,你的
.c
文件和.h
文件是存在的啦鹊奖,于是 make 會(huì)生成.o
文件苛聘,然后再用.o
文件生成 make 的終極任務(wù),也就是執(zhí)行文件 edit 了忠聚。
這就是整個(gè) make 的依賴性设哗,make 會(huì)一層又一層地去找文件的依賴關(guān)系,直到最終編譯出第一個(gè)目標(biāo)文件两蟀。在找尋的過程中网梢,如果出現(xiàn)錯(cuò)誤,比如最后被依賴的文件找不到赂毯,那么make就會(huì)直接退出战虏,并報(bào)錯(cuò),而對(duì)于所定義的命令的錯(cuò)誤党涕,或是編譯不成功烦感,這些都不在 make 職責(zé)范圍內(nèi)。
通過上述分析膛堤,我們知道手趣,像 clean 這種,沒有被第一個(gè)目標(biāo)文件直接或間接關(guān)聯(lián)肥荔,那么它后面所定義的命令將不會(huì)被自動(dòng)執(zhí)行绿渣,不過,我們可以顯示要 make 執(zhí)行燕耿。即命令make clean
中符,以此來清除所有的目標(biāo)文件,以便重編譯誉帅。
在Makefile中使用變量
在上面的例子中可以看到舟茶,后綴為.o
的一大串文件名寫了兩次,這樣比較費(fèi)時(shí)費(fèi)力堵第,而且如果文件有所增減吧凉,要修改的地方也非常多,對(duì)以后的維護(hù)造成困難踏志。在這種情形下阀捅,我們可以在Makefile里使用變量代替這一大串依賴文件,這里變量的使用方式基本類似于shell腳本里變量的使用方法针余。
我們可以在makefile一開始就這樣定義:
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
那么接下來我們就可以很方便地在我們的Makefile中以$(objects)
的方式來使用這個(gè)變量了饲鄙,于是如果有新的.o
文件加入凄诞,我們只需簡(jiǎn)單地修改一下 objects 變量就可以了。
讓 make 自動(dòng)推導(dǎo)
GNU的 make 很強(qiáng)大忍级,它可以自動(dòng)推導(dǎo)文件以及文件依賴關(guān)系后面的命令帆谍,于是我們就沒必要去在每一個(gè).o
文件后都寫上類似的命令。因?yàn)橹嵩郏覀兊膍ake會(huì)自動(dòng)識(shí)別汛蝙,并自己推導(dǎo)命令。
只要make看到一個(gè).o
文件朴肺,它就會(huì)自動(dòng)的把.c
文件加在依賴關(guān)系中窖剑,如果make找到一個(gè)FILENAME.o
,那么 FILENAME.c
戈稿,就會(huì)是FILENAME.o
的依賴文件西土。并且 cc -c FILENAME.c
也會(huì)被推導(dǎo)出來,于是鞍盗,我們的makefile 再也不用寫得這么復(fù)雜需了。我們的新makefile就可以這么寫了。
objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc = gcc
edit: $(objects)
cc -o edit $(objects)
main.o: defs.h
kbd.o: defs.h command.h
command.o: defs.h command.h
display.o: defs.h buffer.h
insert.o: defs.h buffer.h
search.o: defs.h buffer.h
files.o: defs.h buffer.h command.h
utils.o: defs.h
.PHONY: clean
clean:
rm edit $(objects)
這種方法般甲,也就是make的**肋乍。上面文件內(nèi)容中,“.PHONY”表示欣除,clean是個(gè)偽目標(biāo)文件住拭。