Copyright(c) 2011 fym
All Rights Reserved
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License.Version 0.1
本作者假定讀者有一些書(shū)寫(xiě)Makefile的功底
一、目標,依賴(lài),命令
也許大家覺(jué)得這個(gè)不重要,但今天我有了新的認識,所以寫(xiě)了下來(lái)。這三個(gè)就是Makefile的全部,但今天我要重點(diǎn)說(shuō)一下它的執行順序。每個(gè)Makefile都有且只有一個(gè)終極目標,下設若干子目標,make的規則會(huì )檢查目標與依賴(lài)的時(shí)間戳,依賴(lài)中的某一個(gè)比目標新,說(shuō)明目標已經(jīng)過(guò)時(shí),需要更新。這里要著(zhù)重說(shuō)一下,make會(huì )將目標的依賴(lài)及依賴(lài)的依賴(lài)全部展開(kāi),然后才能決定是否需要更新終極目標。
a.out
/ | \
/ | \
_ / _ \_
/ | \
hello.o world.o main.o
_/ | \_
/ | \
hello.c world.c main.c
對于這個(gè)例子,相信大家都能寫(xiě)出Makefile。make在解析Makefile時(shí),就會(huì )生成上面的依賴(lài)樹(shù),它會(huì )自底向上比較時(shí)間戳,若下層的新,則執行上層目標所對應的命令,然后,依次執行上上層的命令(因為,底層更新了,所以上面的都需要更新)。如果底層不比上層的新,則不執行上層目標所對應的命令(這就是make的聰明之處)。例如:我們更改了hello.c文件,make在解析Makefile時(shí),首先比較的不是a.out與hello.o誰(shuí)更新(如果make真的這么做,那么make就會(huì )直接退出,因為自你上次執行make時(shí),a.out會(huì )比hello.o更新,而我們修改hello.c,并不會(huì )影響hello.o的時(shí)間戳), 而是依賴(lài)樹(shù)的最底端,hello.c與hello.o的時(shí)間戳,因為hello.c的修改,目標hello.o的命令會(huì )被執行,進(jìn)而觸發(fā)a.out所對應的命令。目標werld.o與main.o所對應的命令當然不會(huì )被執行。make的輸出看上去是這個(gè)樣子的
gcc -c hello.c
gcc hello.o world.o main.o
在make 的info文檔中有這樣一段話(huà):(注意粗體字)
3.9 How `make' Reads a Makefile
===============================
GNU `make' does its work in two distinct phases. During the first
phase it reads all the makefiles, included makefiles, etc. and
internalizes all the variables and their values, implicit and explicit
rules, and constructs a dependency graph of all the targets and their
prerequisites. During the second phase, `make' uses these internal
structures to determine what targets will need to be rebuilt and to
invoke the rules necessary to do so.
4 Writing Rules
***************
A "rule" appears in the makefile and says when and how to remake
certain files, called the rule's "targets" (most often only one per
rule). It lists the other files that are the "prerequisites" of the
target, and "commands" to use to create or update the target.
The order of rules is not significant, except for determining the
"default goal": the target for `make' to consider, if you do not
otherwise specify one. The default goal is the target of the first
rule in the first makefile. If the first rule has multiple targets,
only the first target is taken as the default. There are two
exceptions: a target starting with a period is not a default unless it
contains one or more slashes, `/', as well; and, a target that defines
a pattern rule has no effect on the default goal. (*Note Defining and
Redefining Pattern Rules: Pattern Rules.)
二、偽目標
偽目標總被認為是過(guò)期的,總被認為需要被更新。偽目標又稱(chēng)標簽,之所以這樣叫,是為了與真實(shí)的文件進(jìn)行區別。那偽目標是如何工作的,我們可以將它當作普通文件,但不管出現在目標中,還是依賴(lài)中,都以為著(zhù)它是將要被更新的普通文件。
1、偽目標做目標,普通文件做依賴(lài)
這是最常見(jiàn)的一種,典型的all偽目標 .PHONY:all
all: prog1 prog2 #執行make后,會(huì )一次生成兩個(gè)可執行程序
prog1: a.c ...
command
...
prog2: b.c ...
command
...
分析:在這個(gè)Makefile中。.PHONY是第一個(gè)目標,但它以點(diǎn)開(kāi)頭,不會(huì )作為終極目標(在上面的英文中有解釋?zhuān)?,所以終極目標是all,make的職責所在就是重建這個(gè)all目標。因為all是偽目標,make命令總認為它需要被更新,而更新它,需要首先更新prog1 和 prog2 ,這就達到了一次make而生成兩個(gè)可執行文件。
2、偽目標做目標,偽目標又做依賴(lài)(雖然稱(chēng)為偽目標,但也可做依賴(lài),目標與依賴(lài)是相互轉化的,就像上面的hello.o,它是hello.c的目標,又是a.out的依賴(lài))
.PHONY: all subdir apps
all: subdir apps
subdir: prog1 prog2
apps: prog3 prog4
prog1: a.c
command...
prog2: b.c
command...
prog3: c.c
command...
prog4: d.c
command...
分析:all是終極目標,它依賴(lài)于subdir 和 apps ,因這兩個(gè)都是偽目標,即:他們需要被更新。subdir又依賴(lài)于prog1 和 prog2 這就回到了上面的那種情況。這種形式經(jīng)常出現在大的工程中,是對上面一種情況的擴展,我們可以修改all 或 subdir 或 apps 中的依賴(lài),來(lái)決定我們需要那些應用程序,裁剪掉那些應用程序。你應該聽(tīng)說(shuō)過(guò)內核裁剪與移植吧,所謂裁剪,就是修改Makefile的目標,不生成我們不需要的東東;所謂移植,就是修改Makefile的編譯參數,換成其他的編譯工具。
3、普通文件做目標,偽目標做依賴(lài)
all: prog
prog: a.c prt
command...
.PHONY: prt
prt:
@echo "this is prt"
分析:每次執行make時(shí),this is prt都會(huì )出現在屏幕上。盡管prog不需要被更新,但prt中的命令依然要被執行。make會(huì )這樣來(lái)解析,prog依賴(lài)于prt,而prt有需要被更新(因為它是偽目標),這意味著(zhù)this si prt會(huì )出現在屏幕上,prt更新完成后,prog目標中的command...就會(huì )被執行(我們假定a.c沒(méi)有被更改)。事實(shí)上我們那個(gè)文件也沒(méi)有更改,但是prog目標中的命令還是被執行了,info make中不建議這樣做
A phony target should not be a prerequisite of a real target file;
if it is, its commands are run every time `make' goes to update that
file.
總結:在偽目標中始終貫穿著(zhù)這樣一句話(huà):偽目標總被認為是需要被更新的。我也是依據這句話(huà)來(lái)分析上述偽目標的應用。偽目標可以與真實(shí)文件重名,典型的clean用法,因為make知道,我們只是想執行clean下的命令,而不是去重建一個(gè)叫做clean的文件,這也是我們有時(shí)把它解釋稱(chēng)標簽的原因。關(guān)于上述第二種情況,info make中有這樣一個(gè)解釋
When one phony target is a prerequisite of another, it serves as a
subroutine of the other. For example, here `make cleanall' will delete
the object files, the difference files, and the file `program':
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff