Makefile的隐含规则.docx
- 文档编号:24118282
- 上传时间:2023-05-24
- 格式:DOCX
- 页数:17
- 大小:25.50KB
Makefile的隐含规则.docx
《Makefile的隐含规则.docx》由会员分享,可在线阅读,更多相关《Makefile的隐含规则.docx(17页珍藏版)》请在冰豆网上搜索。
Makefile的隐含规则
Makefile隱含規則
————
在我們使用Makefile時,有一些我們會經常使用,而且使用頻率非常高的東西,比如,我們編譯C/C++的來源程式為中間目的檔案(Unix下是[.o]文件,Windows下是[.obj]文件)。
本章講述的就是一些在Makefile中的“隱含的”,早先約定了的,不需要我們再寫出來的規則。
“隱含規則”也就是一種慣例,make會按照這種“慣例”心照不喧地來運行,那怕我們的Makefile中沒有書寫這樣的規則。
例如,把[.c]檔編譯成[.o]檔這一規則,你根本就不用寫出來,make會自動推導出這種規則,並生成我們需要的[.o]文件。
“隱含規則”會使用一些我們系統變數,我們可以改變這些系統變數的值來定制隱含規則的運行時的參數。
如系統變數“CFLAGS”可以控制編譯時的編譯器參數。
我們還可以通過“模式規則”的方式寫下自己的隱含規則。
用“尾碼規則”來定義隱含規則會有許多的限制。
使用“模式規則”會更回得智慧和清楚,但“尾碼規則”可以用來保證我們Makefile的相容性。
我們瞭解了“隱含規則”,可以讓其為我們更好的服務,也會讓我們知道一些“約定俗成”了的東西,而不至於使得我們在運行Makefile時出現一些我們覺得莫名其妙的東西。
當然,任何事物都是矛盾的,水能載舟,亦可覆舟,所以,有時候“隱含規則”也會給我們造成不小的麻煩。
只有瞭解了它,我們才能更好地使用它。
一、使用隱含規則
如果要使用隱含規則生成你需要的目標,你所需要做的就是不要寫出這個目標的規則。
那麼,make會試圖去自動推導產生這個目標的規則和命令,如果make可以自動推導生成這個目標的規則和命令,那麼這個行為就是隱含規則的自動推導。
當然,隱含規則是make事先約定好的一些東西。
例如,我們有下面的一個Makefile:
foo :
foo.o bar.o
cc –o foo foo.o bar.o $(CFLAGS) $(LDFLAGS)
我們可以注意到,這個Makefile中並沒有寫下如何生成foo.o和bar.o這兩目標的規則和命令。
因為make的“隱含規則”功能會自動為我們自動去推導這兩個目標的依賴目標和生成命令。
make會在自己的“隱含規則”庫中尋找可以用的規則,如果找到,那麼就會使用。
如果找不到,那麼就會報錯。
在上面的那個例子中,make調用的隱含規則是,把[.o]的目標的依賴檔置成[.c],並使用C的編譯命令“cc –c $(CFLAGS) [.c]”來生成[.o]的目標。
也就是說,我們完全沒有必要寫下下面的兩條規則:
foo.o :
foo.c
cc –c foo.c $(CFLAGS)
bar.o :
bar.c
cc –c bar.c $(CFLAGS)
因為,這已經是“約定”好了的事了,make和我們約定好了用C編譯器“cc”生成[.o]檔的規則,這就是隱含規則。
當然,如果我們為[.o]檔書寫了自己的規則,那麼make就不會自動推導並調用隱含規則,它會按照我們寫好的規則忠實地執行。
還有,在make的“隱含規則庫”中,每一條隱含規則都在庫中有其順序,越靠前的則是越被經常使用的,所以,這會導致我們有些時候即使我們顯示地指定了目標依賴,make也不會管。
如下面這條規則(沒有命令):
foo.o :
foo.p
依賴檔“foo.p”(Pascal程式的原始檔案)有可能變得沒有意義。
如果目錄下存在了“foo.c”檔,那麼我們的隱含規則一樣會生效,並會通過“foo.c”調用C的編譯器生成foo.o文件。
因為,在隱含規則中,Pascal的規則出現在C的規則之後,所以,make找到可以生成foo.o的C的規則就不再尋找下一條規則了。
如果你確實不希望任何隱含規則推導,那麼,你就不要只寫出“依賴規則”,而不寫命令。
二、隱含規則一覽
這裡我們將講述所有預先設置(也就是make內建)的隱含規則,如果我們不明確地寫下規則,那麼,make就會在這些規則中尋找所需要規則和命令。
當然,我們也可以使用make的參數“-r”或“--no-builtin-rules”選項來取消所有的預設置的隱含規則。
當然,即使是我們指定了“-r”參數,某些隱含規則還是會生效,因為有許多的隱含規則都是使用了“尾碼規則”來定義的,所以,只要隱含規則中有“尾碼清單”(也就一系統定義在目標.SUFFIXES的依賴目標),那麼隱含規則就會生效。
默認的尾碼列表是:
.out, .a, .ln, .o, .c, .cc, .C, .p, .f, .F, .r, .y, .l, .s, .S, .mod, .sym, .def, .h, .info, .dvi, .tex, .texinfo, .texi, .txinfo, .w, .ch .web, .sh, .elc, .el。
具體的細節,我們會在後面講述。
還是先來看一看常用的隱含規則吧。
1、編譯C程式的隱含規則。
“
2、編譯C++程式的隱含規則。
“
(建議使用“.cc”作為C++原始檔案的尾碼,而不是“.C”)
3、編譯Pascal程式的隱含規則。
“
4、編譯Fortran/Ratfor程式的隱含規則。
“
“.f” “$(FC) –c $(FFLAGS)”
“.F” “$(FC) –c $(FFLAGS) $(CPPFLAGS)”
“.f” “$(FC) –c $(FFLAGS) $(RFLAGS)”
5、預處理Fortran/Ratfor程式的隱含規則。
“
這個規則只是轉換Ratfor或有預處理的Fortran程式到一個標準的Fortran程式。
其使用的命令是:
“.F” “$(FC) –F $(CPPFLAGS) $(FFLAGS)”
“.r” “$(FC) –F $(FFLAGS) $(RFLAGS)”
6、編譯Modula-2程式的隱含規則。
“
“$(M2C) $(M2FLAGS) $(DEFFLAGS)”。
“
“$(M2C) $(M2FLAGS) $(MODFLAGS)”。
7、彙編和彙編預處理的隱含規則。
“
“$(AS) $(ASFLAGS)”。
“
“$(AS) $(ASFLAGS)”。
8、連結Object檔的隱含規則。
“
“$(CC) $(LDFLAGS)
這個規則對於只有一個原始檔案的工程有效,同時也對多個Object檔(由不同的原始檔案生成)的也有效。
例如如下規則:
x :
y.o z.o
並且“x.c”、“y.c”和“z.c”都存在時,隱含規則將執行如下命令:
cc -c x.c -o x.o
cc -c y.c -o y.o
cc -c z.c -o z.o
cc x.o y.o z.o -o x
rm -f x.o
rm -f y.o
rm -f z.o
如果沒有一個原始檔案(如上例中的x.c)和你的目標名字(如上例中的x)相關聯,那麼,你最好寫出自己的生成規則,不然,隱含規則會報錯的。
9、Yacc C程式時的隱含規則。
“
“$(YACC) $(YFALGS)”。
(“Yacc”是一個語法分析器,關於其細節請查看相關資料)
10、Lex C程式時的隱含規則。
“
“$(LEX) $(LFALGS)”。
(關於“Lex”的細節請查看相關資料)
11、Lex Ratfor程式時的隱含規則。
“
“$(LEX) $(LFALGS)”。
12、從C程式、Yacc檔或Lex檔創建Lint庫的隱含規則。
“
“$(LINT) $(LINTFALGS) $(CPPFLAGS) -i”。
對於“
三、隱含規則使用的變數
在隱含規則中的命令中,基本上都是使用了一些預先設置的變數。
你可以在你的makefile中改變這些變數的值,或是在make的命令列中傳入這些值,或是在你的環境變數中設置這些值,無論怎麼樣,只要設置了這些特定的變數,那麼其就會對隱含規則起作用。
當然,你也可以利用make的“-R”或“--no–builtin-variables”參數來取消你所定義的變數對隱含規則的作用。
例如,第一條隱含規則——編譯C程式的隱含規則的命令是“$(CC) –c $(CFLAGS) $(CPPFLAGS)”。
Make默認的編譯命令是“cc”,如果你把變數“$(CC)”重定義成“gcc”,把變數“$(CFLAGS)”重定義成“-g”,那麼,隱含規則中的命令全部會以“gcc –c -g $(CPPFLAGS)”的樣子來執行了。
我們可以把隱含規則中使用的變數分成兩種:
一種是命令相關的,如“CC”;一種是參數相的關,如“CFLAGS”。
下面是所有隱含規則中會用到的變數:
1、關於命令的變數。
AR
函式程式庫打包程式。
默認命令是“ar”。
AS
組合語言編譯器。
默認命令是“as”。
CC
C語言編譯器。
默認命令是“cc”。
CXX
C++語言編譯器。
默認命令是“g++”。
CO
從 RCS檔中擴展檔程式。
默認命令是“co”。
CPP
C程式的前置處理器(輸出是標準輸出設備)。
默認命令是“$(CC) –E”。
FC
Fortran 和 Ratfor 的編譯器和預處理程式。
默認命令是“f77”。
GET
從SCCS檔中擴展檔的程式。
默認命令是“get”。
LEX
Lex方法分析器程式(針對於C或Ratfor)。
默認命令是“lex”。
PC
Pascal語言編譯器。
默認命令是“pc”。
YACC
Yacc文法分析器(針對於C程式)。
默認命令是“yacc”。
YACCR
Yacc文法分析器(針對於Ratfor程式)。
默認命令是“yacc –r”。
MAKEINFO
轉換Texinfo原始檔案(.texi)到Info檔程式。
默認命令是“makeinfo”。
TEX
從TeX原始檔案創建TeX DVI檔的程式。
默認命令是“tex”。
TEXI2DVI
從Texinfo原始檔案創建軍TeX DVI 檔的程式。
默認命令是“texi2dvi”。
WEAVE
轉換Web到TeX的程式。
默認命令是“weave”。
CWEAVE
轉換C Web 到 TeX的程式。
默認命令是“cweave”。
TANGLE
轉換Web到Pascal語言的程式。
默認命令是“tangle”。
CTANGLE
轉換C Web 到 C。
默認命令是“ctangle”。
RM
刪除檔命令。
默認命令是“rm –f”。
2、關於命令參數的變數
下面的這些變數都是相關上面的命令的參數。
如果沒有指明其預設值,那麼其預設值都是空。
ARFLAGS
函式程式庫打包程式AR命令的參數。
預設值是“rv”。
ASFLAGS
組合語言編譯器參數。
(當明顯地調用“.s”或“.S”文件時)。
CFLAGS
C語言編譯器參數。
CXXFLAGS
C++語言編譯器參數。
COFLAGS
RCS命令參數。
CPPFLAGS
C前置處理器參數。
( C 和 Fortran 編譯器也會用到)。
FFLAGS
Fortran語言編譯器參數。
GFLAGS
SCCS “get”程式參數。
LDFLAGS
連結器參數。
(如:
“ld”)
LFLAGS
Lex文法分析器參數。
PFLAGS
Pascal語言編譯器參數。
RFLAGS
Ratfor 程式的Fortran 編譯器參數。
YFLAGS
Yacc文法分析器參數。
四、隱含規則鏈
有些時候,一個目標可能被一系列的隱含規則所作用。
例如,一個[.o]的檔生成,可能會是先被Yacc的[.y]文件先成[.c],然後再被C的編譯器生成。
我們把這一系列的隱含規則叫做“隱含規則鏈”。
在上面的例子中,如果檔[.c]存在,那麼就直接調用C的編譯器的隱含規則,如果沒有[.c]檔,但有一個[.y]檔,那麼Yacc的隱含規則會被調用,生成[.c]檔,然後,再調用C編譯的隱含規則最終由[.c]生成[.o]檔,達到目標。
我們把這種[.c]的檔(或是目標),叫做中間目標。
不管怎麼樣,make會努力自動推導生成目標的一切方法,不管中間目標有多少,其都會執著地把所有的隱含規則和你書寫的規則全部合起來分析,努力達到目標,所以,有些時候,可能會讓你覺得奇怪,怎麼我的目標會這樣生成?
怎麼我的makefile發瘋了?
在預設情況下,對於中間目標,它和一般的目標有兩個地方所不同:
第一個不同是除非中間的目標不存在,才會引發中間規則。
第二個不同的是,只要目標成功產生,那麼,產生最終目標過程中,所產生的中間目的檔案會被以“rm -f”刪除。
通常,一個被makefile指定成目標或是依賴目標的檔不能被當作仲介。
然而,你可以明顯地說明一個檔或是目標是仲介目標,你可以使用偽目標“.INTERMEDIATE”來強制聲明。
(如:
.INTERMEDIATE :
mid )
你也可以阻止make自動刪除中間目標,要做到這一點,你可以使用偽目標“.SECONDARY”來強制聲明(如:
.SECONDARY :
sec)。
你還可以把你的目標,以模式的方式來指定(如:
%.o)成偽目標“.PRECIOUS”的依賴目標,以保存被隱含規則所生成的中間檔。
在“隱含規則鏈”中,禁止同一個目標出現兩次或兩次以上,這樣一來,就可防止在make自動推導時出現無限遞迴的情況。
Make會優化一些特殊的隱含規則,而不生成中間文件。
如,從文件“foo.c”生成目的程式“foo”,按道理,make會編譯生成中間檔“foo.o”,然後連結成“foo”,但在實際情況下,這一動作可以被一條“cc”的命令完成(cc –o foo foo.c),於是優化過的規則就不會生成中間檔。
五、定義模式規則
你可以使用模式規則來定義一個隱含規則。
一個模式規則就好像一個一般的規則,只是在規則中,目標的定義需要有"%"字元。
"%"的意思是表示一個或多個任意字元。
在依賴目標中同樣可以使用"%",只是依賴目標中的"%"的取值,取決於其目標。
有一點需要注意的是,"%"的展開發生在變數和函數的展開之後,變數和函數的展開發生在make載入Makefile時,而模式規則中的"%"則發生在運行時。
1、模式規則介紹
模式規則中,至少在規則的目標定義中要包含"%",否則,就是一般的規則。
目標中的"%"定義表示對檔案名的匹配,"%"表示長度任意的非空字串。
例如:
"%.c"表示以".c"結尾的檔案名(檔案名的長度至少為3),而"s.%.c"則表示以"s."開頭,".c"結尾的檔案名(檔案名的長度至少為5)。
如果"%"定義在目標中,那麼,目標中的"%"的值決定了依賴目標中的"%"的值,也就是說,目標中的模式的"%"決定了依賴目標中"%"的樣子。
例如有一個模式規則如下:
%.o :
%.c ;
其含義是,指出了怎麼從所有的[.c]文件生成相應的[.o]檔的規則。
如果要生成的目標是"a.o b.o",那麼"%c"就是"a.c b.c"。
一旦依賴目標中的"%"模式被確定,那麼,make會被要求去匹配目前的目錄下所有的檔案名,一旦找到,make就會規則下的命令,所以,在模式規則中,目標可能會是多個的,如果有模式匹配出多個目標,make就會產生所有的模式目標,此時,make關心的是依賴的檔案名和生成目標的命令這兩件事。
2、模式規則示例
下面這個例子表示了,把所有的[.c]檔都編譯成[.o]文件.
%.o :
%.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
其中,"$@"表示所有的目標的挨個值,"$<"表示了所有依賴目標的挨個值。
這些奇怪的變數我們叫"自動化變數",後面會詳細講述。
下面的這個例子中有兩個目標是模式的:
%.tab.c %.tab.h:
%.y
bison -d $<
這條規則告訴make把所有的[.y]文件都以"bison -d
(其中,"
如果我們的執行程式"foo"依賴於檔"parse.tab.o"和"scan.o",並且檔"scan.o"依賴於檔"parse.tab.h",如果"parse.y"檔被更新了,那麼根據上述的規則,"bison -d parse.y"就會被執行一次,於是,"parse.tab.o"和"scan.o"的依賴檔就齊了。
(假設,"parse.tab.o"由"parse.tab.c"生成,和"scan.o"由"scan.c"生成,而"foo"由"parse.tab.o"和"scan.o"連結生成,而且foo和其[.o]檔的依賴關係也寫好,那麼,所有的目標都會得到滿足)
3、自動化變數
在上述的模式規則中,目標和依賴檔都是一系例的檔,那麼我們如何書寫一個命令來完成從不同的依賴檔生成相應的目標?
因為在每一次的對模式規則的解析時,都會是不同的目標和依賴檔。
自動化變數就是完成這個功能的。
在前面,我們已經對自動化變數有所提涉,相信你看到這裡已對它有一個感性認識了。
所謂自動化變數,就是這種變數會把模式中所定義的一系列的檔自動地挨個取出,直至所有的符合模式的檔都取完了。
這種自動化變數只應出現在規則的命令中。
下面是所有的自動化變數及其說明:
$@
表示規則中的目的檔案集。
在模式規則中,如果有多個目標,那麼,"$@"就是匹配於目標中模式定義的集合。
$%
僅當目標是函式程式庫檔中,表示規則中的目標成員名。
例如,如果一個目標是"foo.a(bar.o)",那麼,"$%"就是"bar.o","$@"就是"foo.a"。
如果目標不是函式程式庫檔(Unix下是[.a],Windows下是[.lib]),那麼,其值為空。
$<
依賴目標中的第一個目標名字。
如果依賴目標是以模式(即"%")定義的,那麼"$<"將是符合模式的一系列的檔集。
注意,其是一個一個取出來的。
$?
所有比目標新的依賴目標的集合。
以空格分隔。
$^
所有的依賴目標的集合。
以空格分隔。
如果在依賴目標中有多個重複的,那個這個變數會去除重複的依賴目標,只保留一份。
$+
這個變數很像"$^",也是所有依賴目標的集合。
只是它不去除重複的依賴目標。
$*
這個變數表示目標模式中"%"及其之前的部分。
如果目標是"dir/a.foo.b",並且目標的模式是"a.%.b",那麼,"$*"的值就是"dir/a.foo"。
這個變數對於構造有關聯的檔案名是比較有較。
如果目標中沒有模式的定義,那麼"$*"也就不能被推導出,但是,如果目的檔案的尾碼是make所識別的,那麼"$*"就是除了尾碼的那一部分。
例如:
如果目標是"foo.c",因為".c"是make所能識別的尾碼名,所以,"$*"的值就是"foo"。
這個特性是GNU make的,很有可能不相容於其它版本的make,所以,你應該儘量避免使用"$*",除非是在隱含規則或是靜態模式中。
如果目標中的尾碼是make所不能識別的,那麼"$*"就是空值。
當你希望只對更新過的依賴檔進行操作時,"$?
"在顯式規則中很有用,例如,假設有一個函式程式庫檔叫"lib",其由其它幾個object文件更新。
那麼把object檔打包的比較有效率的Makefile規則是:
lib :
foo.o bar.o lose.o win.o
ar r lib $?
在上述所列出來的自動量變數中。
四個變數($@、$<、$%、$*)在擴展時只會有一個檔,而另三個的值是一個檔列表。
這七個自動化變數還可以取得檔的目錄名或是在目前的目錄下的符合模式的檔案名,只需要搭配上"D"或"F"字樣。
這是GNU make中老版本的特性,在新版本中,我們使用函數"dir"或"notdir"就可以做到了。
"D"的含義就是Directory,就是目錄,"F"的含義就是File,就是檔。
下面是對於上面的七個變數分別加上"D"或是"F"的含義:
$(@D)
表示"$@"的目錄部分(不以斜杠作為結尾),如果"$@"值是"dir/foo.o",那麼"$(@D)"就是"dir",而如果"$@"中沒有包含斜杠的話,其值就是"."(目前的目錄)。
$(@F)
表示"$@"的檔部分,如果"$@"值
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Makefile 隐含 规则