Makefile學(xué)習(xí)筆記
概述
什么是makefile?或許很多Windows程序員都不知道這個(gè)東西,因?yàn)槟切¦indows端IDE都為你做了這個(gè)工作,但我覺得要做一個(gè)professional的程序員,makefile還是要懂。這就好像現(xiàn)在有這么多的HTML的編輯器澳腹,但如果你想成為一個(gè)專業(yè)人士,你還是要了解HTML的能力杨何。特別在Unix下的軟件編譯酱塔,你就不能不自己寫makefile,會(huì)不會(huì)寫makefile危虱,從一個(gè)側(cè)面說明了一個(gè)程序員是否具備完成大型工程的能力羊娃。
因?yàn)閙akefile關(guān)系到整個(gè)工程的編譯規(guī)則。一個(gè)工程中的源文件不計(jì)其數(shù)埃跷,其按類型蕊玷、功能、模塊分別放在若干個(gè)目錄中弥雹,makefile定義了一系列的規(guī)則來指定垃帅,哪些文件需要先編譯,哪些文件需要重新編譯剪勿,甚至于進(jìn)行更復(fù)雜的功能操作贸诚,因?yàn)閙akefile就像一個(gè)shell腳本一樣,其中也可以執(zhí)行操作系統(tǒng)的命令窗宦。
makefile帶來的好處就是“自動(dòng)化編譯”赦颇,一旦寫好,只需要一個(gè)make命令赴涵,整個(gè)工程完全自動(dòng)編譯,極大的提高了軟件開發(fā)的效率订讼。make是一個(gè)命令工具髓窜,是一個(gè)解釋makefile中指令的命令工具,一般來說,大多數(shù)的IDE都有這個(gè)命令寄纵,比如:Delphi的make鳖敷,Visual C++的nmake,Linux下GNU 的make程拭《猓可見,makefile都成為了一種在工程方面的編譯方法恃鞋。
不同廠商的make各不相同崖媚,也有不同的語法,但是本質(zhì)都是在文件依賴性上做文章恤浪,這里僅對(duì)GNU的make進(jìn)行講述畅哑,版本make3.80.該make是應(yīng)用最為廣泛的,也是用的最多的水由,而且還是最遵循與IEEE 1003.2-1992標(biāo)準(zhǔn)的(POSIX2)荠呐。
這篇文檔中,以C/C++源碼作為講述基礎(chǔ)砂客,其中必然涉及一些關(guān)于C/C++編譯的知識(shí)泥张,相關(guān)內(nèi)容可查閱相關(guān)編譯器文檔獲得,這里默認(rèn)為gcc和g++鞠值。
關(guān)于程序的編譯和鏈接
一般來說媚创,無論是C、C++齿诉,首先要把源文件編譯成中間代碼文件筝野,Windows下.obj或Unix下.o,即Object File粤剧,這個(gè)動(dòng)作叫做編譯(compile)歇竟。然后再把大量的Object File合成執(zhí)行文件,這個(gè)動(dòng)作叫做鏈接(link)抵恋。
編譯時(shí)焕议,編譯器需要的是語法的正確,函數(shù)與變量聲明的正確弧关。對(duì)于后者盅安,通常是你需要告訴編譯器頭文件的所在位置(頭文件中應(yīng)該只是聲明,而定義應(yīng)該放在C/C++文件中)世囊,只要所有語法正確别瞭,編譯器就可以編譯出中間目標(biāo)文件。一般來說株憾,每個(gè)源文件都應(yīng)該對(duì)應(yīng)于中間目標(biāo)文件蝙寨。
鏈接時(shí)晒衩,主要是鏈接函數(shù)和全局變量,所以墙歪,我們可以使用這些中間目標(biāo)文件來鏈接我們的應(yīng)用程序听系。連接器并不管函數(shù)所在的源文件,只管函數(shù)的中間目標(biāo)文件虹菲,在大多數(shù)時(shí)候靠胜,由于源文件太多,編譯生成的中間目標(biāo)文件太多毕源,而在鏈接時(shí)需要明確的指出中間目標(biāo)文件名浪漠,這對(duì)于編譯很不方便,所以我們要給中間目標(biāo)文件打個(gè)包脑豹,在Windows下這種包叫庫文件(Library File, * .lib)郑藏,在Unix下是Archive File,* .a瘩欺。
總結(jié)一下必盖,源文件首先會(huì)生成中間目標(biāo)文件,再由中間目標(biāo)文件生成執(zhí)行文件俱饿。在編譯時(shí)歌粥,編譯器只檢測(cè)程序語法,和函數(shù)拍埠、變量是否被聲明失驶。如果函數(shù)未被聲明,編譯器會(huì)給出一個(gè)警告枣购,但可以生成Object File嬉探。而在鏈接程序時(shí),鏈接器會(huì)在所有的Object File中找尋函數(shù)的實(shí)現(xiàn)棉圈。如果找不到涩堤,那就會(huì)報(bào)鏈接錯(cuò)誤碼(Linker Error),在VC下分瘾,這種錯(cuò)誤一般是:Link2001胎围,意思是,鏈接器未能找到函數(shù)實(shí)現(xiàn)德召,你需要指定函數(shù)的Object File白魂。
Makefile介紹
make命令執(zhí)行時(shí),需要一個(gè)Makefile文件上岗,以告訴make命令需要怎樣去編譯和鏈接程序福荸。
首先,我們用一個(gè)示例來說明Makefile的書寫規(guī)則肴掷。以便給大家一個(gè)感性認(rèn)識(shí)逞姿。這個(gè)示例來源于GNU的make使用手冊(cè)辞嗡,在這個(gè)示例中捆等,我們的工程有8個(gè)C文件滞造,和3個(gè)頭文件,我們要寫一個(gè)Makefile來告訴make命令如何編譯和鏈接這幾個(gè)文件栋烤。
我們的規(guī)則是:
- 如果這個(gè)工程沒有編譯過谒养,那么我們的所有C文件都要編譯并被鏈接;
- 如果這個(gè)工程的某幾個(gè)C文件被修改明郭,那么我們只編譯被修改的C文件买窟;
- 如果這個(gè)工程的頭文件被改變了,那么我們需要編譯引用了這幾個(gè)頭文件的C文件薯定,并鏈接目標(biāo)程序始绍。
只要我們的Makefile寫的夠好,所有的這一切话侄,我們只用一個(gè)make命令就可以完成亏推,make命令會(huì)自動(dòng)地根據(jù)當(dāng)前的文件修改的情況來確定哪些文件需要重編譯,從而自己編譯所需要的文件和鏈接目標(biāo)程序年堆。
一吞杭、Makefile的規(guī)則
target ... : prerequisites ...
command
...
target是一個(gè)目標(biāo)文件,可以是Object File变丧,也可以是執(zhí)行文件芽狗,還可以是一個(gè)標(biāo)簽(Label)。對(duì)于標(biāo)簽這種特性痒蓬,在后續(xù)的偽目錄章節(jié)中會(huì)有敘述童擎。
prequisites是生成target所需要的文件。
command是make需要執(zhí)行的命令(任意的Shell命令)攻晒。
這是一個(gè)文件的依賴關(guān)系顾复,也就是說,target中的一個(gè)或多個(gè)目標(biāo)文件依賴于prerequisites中的文件炎辨,其生成規(guī)則定義在command中捕透。因此,prerequisites中如果有一個(gè)及以上的文件比target要新的話碴萧,command中定義的命令就會(huì)被執(zhí)行乙嘀。這就是Makefile的最核心規(guī)則。
二破喻、一個(gè)示例
正如前面所說的虎谢,如果一個(gè)工程有3個(gè)頭文件,和8個(gè)C文件曹质,我們?yōu)榱送瓿汕懊嫠龅哪侨齻€(gè)規(guī)則婴噩,我們的Makefile應(yīng)該是下面的這個(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 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
反斜杠(\)是換行符的意思。這樣可以使Makefile易讀几莽。
把這個(gè)內(nèi)容保存在文件名為Makefile或makefile的文件中迅办,然后在該目錄下,直接輸入命令make命令就可以生成執(zhí)行文件edit章蚣。如果要?jiǎng)h除執(zhí)行文件和所有的中間目標(biāo)文件站欺,那么只要簡(jiǎn)單地執(zhí)行一下make clean就可以了。
在這個(gè)makefile中纤垂,目標(biāo)文件(target)包含:執(zhí)行文件edit和中間目標(biāo)文件* .o矾策,依賴文件(prerequisites)就是冒號(hào)后面的那些 .c和 .h文件。每一個(gè) .o文件都有一組依賴文件峭沦,而這些 .o文件又是執(zhí)行文件edit的依賴文件贾虽。依賴關(guān)系實(shí)質(zhì)上就是說明了目標(biāo)文件是由哪些文件生成的,換言之吼鱼,目標(biāo)文件是被哪些文件更新的蓬豁。
在定義好依賴依賴關(guān)系后,后續(xù)的那一行定義了如何生成文件的操作系統(tǒng)命令蛉抓,一定要以一個(gè)TAB鍵開頭庆尘。make并不管命令是怎么工作的,他只管執(zhí)行所定義的命令巷送。make會(huì)比較targets文件和prereuisites文件的修改日期驶忌,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的話笑跛,make就會(huì)執(zhí)行后續(xù)指令付魔。
clean不是一個(gè)文件,而只是一個(gè)動(dòng)作名飞蹂。其冒號(hào)后什么也沒有几苍,make就不會(huì)自動(dòng)去找文件依賴性,也就不會(huì)自動(dòng)執(zhí)行其后所定義的命令陈哑。要執(zhí)行其后的命令妻坝,就要在make命令后明顯的指出這個(gè)label的名字。這樣的方法非常有用惊窖,使我們可以在一個(gè)makefile中定義不用的編譯或是和編譯無關(guān)的命令刽宪,比如程序打包和程序的備份等等。