【正文】
就可以了。這就是整個(gè)make的依賴性,make會一層又一層地去找文件的依賴關(guān)系,直到最終編譯出第一個(gè)目標(biāo)文件。并且 cc c 也會被推導(dǎo)出來,于是,我們的makefile再也不用寫得這么復(fù)雜。上面就是一個(gè)makefile的概貌,也是makefile的基礎(chǔ),下面還有很多makefile的相關(guān)細(xì)節(jié),準(zhǔn)備好了嗎?準(zhǔn)備好了就來。二、Makefile的文件名默認(rèn)的情況下,make命令會在當(dāng)前目錄下按順序找尋文件名為“GNUmakefile”、“makefile”、“Makefile”的文件,找到了解釋這個(gè)文件。和其它版本make兼容的相關(guān)命令是sinclude,其作用和這一個(gè)是一樣的。當(dāng)然,這個(gè)工作方式你不一定要清楚,但是知道這個(gè)方式你也會對make更為熟悉。(見上)prerequisites也就是目標(biāo)所依賴的文件(或依賴目標(biāo))。好吧,還是先來看幾個(gè)例子吧:clean:rm f *.o上面這個(gè)例子我不不多說了,這是操作系統(tǒng)Shell所支持的通配符。這是一個(gè)很靈活的功能。當(dāng)然,“偽目標(biāo)”的取名不能和文件名重名,不然其就失去了“偽目標(biāo)”的意義了。我們可以輸入“make cleanall”和“make cleanobj”和“make cleandiff”命令來達(dá)到清除不同種類文件的目的。而命令中的“$”和“$”則是自動化變量,“$”表示所有的依賴目標(biāo)集(也就是“ ”),“$”表示目標(biāo)集(也就是“ ”)。 rm f $。Makefile中,“”是注釋符,很像C/C++中的“//”,其后的本行字符都被注釋。有些時(shí)候,命令的出錯并不表示就是錯誤的。后面什么也不用跟,表示傳遞所有的變量。在這個(gè)命令包的使用中,命令包“runyacc”中的“$^”就是“”,“$”就是“”(有關(guān)這種以“$”開頭的特殊變量,我們會在后面介紹),make在執(zhí)行命令包時(shí),命令包中的每個(gè)命令會被依次獨(dú)立執(zhí)行。先看第一種方式,也就是簡單的使用“=”號,在“=”左側(cè)是變量,右側(cè)是變量的值,右側(cè)變量的值可以定義在文件的任何一處,也就是說,右側(cè)中的變量不一定非要是已定義好的值,其也可以使用后面定義的值。還是看一個(gè)示例吧:foo := bar := $(foo:.o=.c)這個(gè)示例中,我們先定義了一個(gè)“$(foo)”變量,而第二行的意思是把“$(foo)”中所有以“.o”字串“結(jié)尾”全部替換成“.c”,所以我們的“$(bar)”的值就是“ ”。如果你想在Makefile中設(shè)置這類參數(shù)的值,那么,你可以使用“override”指示符。當(dāng)然,“自動化變量”除外,如“$”等這種類量的自動化變量就屬于“規(guī)則型變量”,這種變量的值依賴于規(guī)則的目標(biāo)和依賴目標(biāo)的定義。endif表示一個(gè)條件語句的結(jié)束,任何一個(gè)條件表達(dá)式都應(yīng)該以endif結(jié)束。arg139。當(dāng)然,variablename同樣可以是一個(gè)函數(shù)的返回值。當(dāng)然,參數(shù)中我們還可以使用make的函數(shù)。libs_for_gcc = lgnunormal_libs =foo: $(objects)ifeq ($(CC),gcc)$(CC) o foo $(objects) $(libs_for_gcc)else$(CC) o foo $(objects) $(normal_libs)endif可見,在上面示例的這個(gè)規(guī)則中,目標(biāo)“foo”可以根據(jù)變量“$(CC)”值來選取不同的函數(shù)庫來編譯程序。當(dāng)然,默認(rèn)情況下,只有通過命令行設(shè)置的變量會被傳遞。四、追加變量值我們可以使用“+=”操作符給變量追加值,如:objects = objects += 于是,我們的$(objects)值變成:“ ”()使用“+=”操作符,可以模擬為下面的這種例子:objects = objects := $(objects) 所不同的是,用“+=”更為簡潔。請注意這里關(guān)于“”的使用,注釋符“”的這種特性值得我們注意,如果我們這樣定義一個(gè)變量:dir := /foo/bar directory to put the frobs indir這個(gè)變量的值是“/foo/bar”,后面還跟了4個(gè)空格,如果我們這樣使用這樣變量來指定別的目錄——“$(dir)/file”那么就完蛋了。先看一個(gè)例子:objects = program : $(objects)cc o program $(objects)$(objects) : 變量會在使用它的地方精確地展開,就像C/C++中的宏一樣,例如:foo = c : prog.$(foo)$(foo)$(foo) $(foo) prog.$(foo)展開后得到: : cc c 當(dāng)然,千萬不要在你的Makefile中這樣干,這里只是舉個(gè)例子來表明Makefile中的變量在使用處展開的真實(shí)樣子。在“define”和“endef”中的兩行就是命令序列。 $(MAKE)其等價(jià)于:subsystem:$(MAKE) C subdir定義$(MAKE)宏變量的意思是,也許我們的make需要一些參數(shù),所以定義成一個(gè)變量比較利于維護(hù)。如果你指定了UNIX風(fēng)格的目錄形式,首先,make會在SHELL所指定的路徑中找尋命令解釋器,如果找不到,其會在當(dāng)前盤符中的當(dāng)前目錄中尋找,如果再找不到,其會在PATH環(huán)境變量中所定義的所有路徑中尋找。make會一按順序一條一條的執(zhí)行命令,每條命令的開頭必須以[Tab]鍵開頭,除非,命令是緊跟在依賴規(guī)則后面的分號后的。因?yàn)檫@樣一來,我們的Makefile也要根據(jù)這些源文件重新生成,讓Makefile自已依賴于源文件?這個(gè)功能并不現(xiàn)實(shí),不過我們可以有其它手段來迂回地實(shí)現(xiàn)這一功能。這樣描述這三個(gè)東西,可能還是沒有說清楚,還是舉個(gè)例子來說明一下吧。隨便提一句,從上面的例子我們可以看出,目標(biāo)也可以成為依賴。五、偽目標(biāo)最早先的一個(gè)例子中,我們提到過一個(gè)“clean”的目標(biāo),這是一個(gè)“偽目標(biāo)”,clean:rm *.o temp正像我們前面例子中的“clean”一樣,即然我們生成了許多文件編譯文件,我們也應(yīng)該提供一個(gè)清除它們的“目標(biāo)”以備完整地重編譯而用。VPATH = src:../headers上面的的定義指定兩個(gè)目錄,“src”和“../headers”,make會按照這個(gè)順序進(jìn)行搜索。而“~hchen/test”則表示用戶hchen的宿主目錄下的test目錄。()二、規(guī)則的語法targets : prerequisitesmand...或是這樣: targets : prerequisites 。執(zhí)行生成命令。如果有文件沒有找到的話,make會生成一條警告信息,但不會馬上出現(xiàn)致命錯誤。注釋。一般的風(fēng)格都是:clean:rm edit $(objects)更為穩(wěn)健的做法是:.PHONY : cleanclean :rm edit $(objects)前面說過,.PHONY意思表示clean是一個(gè)“偽目標(biāo)”。我們在makefile一開始就這樣定義:objects = \ 于是,我們就可以很方便地在我們的makefile中以“$(objects)”的方式來使用這個(gè)變量了,于是我們的改良版makefile就變成下面這個(gè)樣子:objects = \ edit : $(objects)cc o edit $(objects) : cc c : cc c : cc c : cc c : cc c : cc c : cc c : cc c clean :rm edit $(objects)于是如果有新的 .o 文件加入,我們只需簡單地修改一下 objects 變量就可以了。如果找到,它會找文件中的第一個(gè)目標(biāo)文件(target),在上面的例子中,他會找到“edit”這個(gè)文件,并把這個(gè)文件作為最終的目標(biāo)文件。:)二、一個(gè)示例正如前面所說的,如果一個(gè)工程有3個(gè)頭文件,和8個(gè)C文件,我們?yōu)榱送瓿汕懊嫠龅哪侨齻€(gè)規(guī)則,我們的Makefile應(yīng)該是下面的這個(gè)樣子的。2)如果這個(gè)工程的某幾個(gè)C文件被修改,那么我們只編譯被修改的C文件,并鏈接目標(biāo)程序。然后再把大量的Object File合成執(zhí)行文件,這個(gè)動作叫作鏈接(link)。Makefile的編寫指導(dǎo)概述什么是makefile?或許很多Windows的程序員都不知道這個(gè)東西,因?yàn)槟切¦indows的IDE都為你做了這個(gè)工作,但我覺得要作一個(gè)好的和professional的程序員,makefile還是要懂。編譯時(shí),編譯器需要的是語法的正確,函數(shù)與變量的聲明的正確。3)如果這個(gè)工程的頭文件被改變了,那么我們需要編譯引用了這幾個(gè)頭文件的C文件,并鏈接目標(biāo)程序。edit : \ cc o edit \ : cc c : cc c : cc c : cc c : cc c : cc c : cc c : cc c clean :rm edit \ 反斜杠(\)是換行符的意思。如果edit文件不存在,或是edit所依賴的后面的 .o 文件的文件修改時(shí)間要比edit這個(gè)文件新,那么,他就會執(zhí)行后面所定義的命令來生成edit這個(gè)文件。關(guān)于變量更多的話題,我會在后續(xù)給你一一道來。而在rm命令前面加了一個(gè)小減號的意思就是,也許某些文件出現(xiàn)問題,但不要管,繼續(xù)做后面的事。Makefile中只有行注釋,和UNIX的Shell腳本一樣,其注釋是用“”字符,這個(gè)就像C/C++中的“//”一樣。它會繼續(xù)載入其它的文件,一旦完成makefile的讀取,make會再重試這些沒有找到,或是不能讀取的文件,如果還是不行,make才會出現(xiàn)一條致命信息。15步為第一個(gè)階段,67為第二個(gè)階段。 mandmand...targets是文件名,以空格分開,可以使用通配符。(這些都是Unix下的小知識了,make也支持)而在Windows或是MSDOS下,用戶沒有宿主目錄,那么波浪號所指的目錄則根據(jù)環(huán)境變量“HOME”而定。目錄由“冒號”分隔。 (以“make clean”來使用該目標(biāo))因?yàn)?,我們并不生成“clean”這個(gè)文件。所以,偽目標(biāo)同樣也可成為依賴。如果我們的targetparrtern定義成“%.o”,意思是我們的target集合中都是以“.o”結(jié)尾的,而如果我們的prereqparrterns定義成“%.c”,意思是對targetparrtern所形成的目標(biāo)集進(jìn)行二次定義,其計(jì)算方法是,取targetparrtern模式中的“%”(也就是去掉了[.o]這個(gè)結(jié)尾),并為其加上[.c]這個(gè)結(jié)尾,形成的新集合。GNU組織建議把編譯器為每一個(gè)源文件的自動生成的依賴關(guān)系放到一個(gè)文件中,為每一個(gè)“”的文件都生成一個(gè)“”的Makefile文件,[.d]文件中就存放對應(yīng)[.c]文件的依賴關(guān)系。在命令行之間中的空格或是空行會被忽略,但是如果該空格或空行是以Tab鍵開頭的,那么make會認(rèn)為其是一個(gè)空命令。MSDOS中,如果你定義的命令解釋器沒有找到,其會給你的命令解釋器加上諸如“.exe”、“.”、“.bat”、“.sh”等后綴。這兩個(gè)例子的意思都是先進(jìn)入“subdir”目錄,然后執(zhí)行make命令。這個(gè)命令包中的第一個(gè)命令是運(yùn)行Yacc程序,因?yàn)閅acc程序總是生成“”的文件,所以第二行的命令就是把這個(gè)文件改改名字。可見其就是一個(gè)“替代”的原理。還有一個(gè)比較有用的操作符是“?=”,先看示例:FOO ?= bar其含義是,如果FOO沒有被定義過,那么變量FOO的值就是“bar”,如果FOO先前被定義過,那么這條語將什么也不做,其等價(jià)于:ifeq ($(origin FOO), undefined)FOO = barendif三、變量高級用法這里介紹兩種變量的高級使用方法,第一種是變量值的替換。如果變量之前沒有定義過,那么,“+=”會自動變成“=”,如果前面有變量定義,那么“+=”會繼承于前次操作的賦值符。而定義在文件中的變量,如果要向下層Makefile傳遞,則需要使用exprot關(guān)鍵字來聲明。我們可以從上面的示例中看到三個(gè)關(guān)鍵字:ifeq、else和endif。如:ifeq ($(strip $(foo)),)textifemptyendif這個(gè)示例中使用了“strip”函數(shù),如果這個(gè)函數(shù)的返回值是空(Empty),那么textifempty就生效。注意,ifdef只是測試一個(gè)變量是否有值,其并不會把變量擴(kuò)展到當(dāng)前位置。語法是:ifneq (arg1, arg2) ifneq 39。else表示條件表達(dá)式為假的情況。八、目標(biāo)變量前面我們所講的在Makefile中定義的變量都是“全局變量”,在整個(gè)文件,我們都可以訪問這些變量。五、override 指示符如果有變量是通常make的命令行參數(shù)設(shè)置的,那么Makefile中對這個(gè)變量的賦值會被忽略。這里的“結(jié)尾”意思是“空格”或是“結(jié)束符”。二、變量中的變量在定義變量的值時(shí),我們可以使用其它變量來構(gòu)造變量的值,在Makefile中有兩種方式來在用變量定義變量的值。 : $(runyacc)我們可以看見,要使用這個(gè)命令包,我們就好像使用變量一樣。如果你要傳遞變量到下級Makefile中,那么你可以使用這樣的聲明:export variable ...如果你不想讓某些變量傳遞到下級Makefile中,那么你可以這