四、makefile中使用變量
在上面的例子中,先讓我們看看edit的規(guī)則:
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
我們可以看到[.o]文件的字符串被重復(fù)了兩次,如果我們的工程需要加入一個(gè)新的[.o]文件,那么我們需要在兩個(gè)地方加(應(yīng)該是三個(gè)地方,還有一個(gè)地方在clean中)。當(dāng)然,我們的makefile并不復(fù)雜,所以在兩個(gè)地方加也不累,但如果makefile變得復(fù)雜,那么我們就有可能會(huì)忘掉一個(gè)需要加入的地方,而導(dǎo)致編譯失敗。所以,為了makefile的易維護(hù),在makefile中我們可以使用變量。makefile的變量也就是一個(gè)字符串,理解成C語言中的宏可能會(huì)更好。
比如,我們聲明一個(gè)變量,叫objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,反正不管什么啦,只要能夠表示obj文件就行了。我們在makefile一開始就這樣定義:
objects = main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
于是,我們就可以很方便地在我們的makefile中以“$(objects)”的方式來使用這個(gè)變量了,于是我們的改良版makefile就變成下面這個(gè)樣子:
objects = main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
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 $(objects)
于是如果有新的 .o 文件加入,我們只需簡單地修改一下 objects 變量就可以了。
關(guān)于變量更多的話題,我會(huì)在后續(xù)給你一一道來。
五、讓make自動(dòng)推導(dǎo)
GNU的make很強(qiáng)大,它可以自動(dòng)推導(dǎo)文件以及文件依賴關(guān)系后面的命令,于是我們就沒必要去在每一個(gè)[.o]文件后都寫上類似的命令,因?yàn)?,我們的make會(huì)自動(dòng)識別,并自己推導(dǎo)命令。
只要make看到一個(gè)[.o]文件,它就會(huì)自動(dòng)的把[.c]文件加在依賴關(guān)系中,如果make找到一個(gè)whatever.o,那么whatever.c,就會(huì)是whatever.o的依賴文件。并且 cc -c whatever.c 也會(huì)被推導(dǎo)出來,于是,我們的makefile再也不用寫得這么復(fù)雜。我們的是新的makefile又出爐了。
objects = main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
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的“隱晦規(guī)則”。上面文件內(nèi)容中,“.PHONY”表示,clean是個(gè)偽目標(biāo)文件。
關(guān)于更為詳細(xì)的“隱晦規(guī)則”和“偽目標(biāo)文件”,我會(huì)在后續(xù)給你一一道來。
六、另類風(fēng)格的makefile
即然我們的make可以自動(dòng)推導(dǎo)命令,那么我看到那堆[.o]和[.h]的依賴就有點(diǎn)不爽,那么多的重復(fù)的[.h],能不能把其收攏起來,好吧,沒有問題,這個(gè)對于make來說很容易,誰叫它提供了自動(dòng)推導(dǎo)命令和文件的功能呢?來看看最新風(fēng)格的makefile吧。
objects = main.o kbd.o command.o display.o /
insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
.PHONY : clean
clean :
rm edit $(objects)
這種風(fēng)格,讓我們的makefile變得很簡單,但我們的文件依賴關(guān)系就顯得有點(diǎn)凌亂了。魚和熊掌不可兼得。還看你的喜好了。我是不喜歡這種風(fēng)格的,一是文件的依賴關(guān)系看不清楚,二是如果文件一多,要加入幾個(gè)新的.o文件,那就理不清楚了。
七、清空目標(biāo)文件的規(guī)則
每個(gè)Makefile中都應(yīng)該寫一個(gè)清空目標(biāo)文件(.o和執(zhí)行文件)的規(guī)則,這不僅便于重編譯,也很利于保持文件的清潔。這是一個(gè)“修養(yǎng)”(呵呵,還記得我的《編程修養(yǎng)》嗎)。一般的風(fēng)格都是:
clean:
rm edit $(objects)
更為穩(wěn)健的做法是:
.PHONY : clean
clean :
-rm edit $(objects)
前面說過,.PHONY意思表示clean是一個(gè)“偽目標(biāo)”,。而在rm命令前面加了一個(gè)小減號的意思就是,也許某些文件出現(xiàn)問題,但不要管,繼續(xù)做后面的事。當(dāng)然,clean的規(guī)則不要放在文件的開頭,不然,這就會(huì)變成make的默認(rèn)目標(biāo),相信誰也不愿意這樣。不成文的規(guī)矩是——“clean從來都是放在文件的最后”。
上面就是一個(gè)makefile的概貌,也是makefile的基礎(chǔ),下面還有很多makefile的相關(guān)細(xì)節(jié),準(zhǔn)備好了嗎?準(zhǔn)備好了就來。
評論