makefile經典文檔試譯

一。總述

二。依賴性檢查: make vs. 角本

三。隱含規則的基本用法

四。處理的依賴性

五。空規則

六。特殊目標

七。不明目標

八。make的保留字

九。安靜地運行命令

十。自動恢復SCCS文件

十一。傳遞參數:簡單的make宏

一.總述

我們將祥細介紹make程序,這將包括:
 隱藏依賴性檢查
 命令依賴性檢查
 模式匹配規則
 自動修補源代碼控制系統(SCCS)文件

這個版本的make可以與之前版本的makefile正常工作。依賴於加強版的makefile可能與其他版本的make應用不兼容。(make的加強請見相關資料)make組織了生成目標文件和可執行文件的過程。它可以讓編譯器一直化被消除對於在代碼被變動後沒被影響的不同模塊的不必要的重編譯。make讓編譯的過程更加簡化。你也可以用它來自動化地組織任何複雜或重複的非交互性工作。你也可以用make來更新和維護目標庫文件,運行測試工具,或是向文件系統或磁帶安裝文件。

與自動修補源代碼控制系統(SCCS)協同工作,可以用make來保證一個大型的軟件項目可以根據一整個系統的源代碼編譯出想要得到的版本。make從一個你創建的叫做makefile的文件裏面讀取信息,它裏面包括了你在編譯過程將要編譯哪些文件,以及如何去編譯它們的信息。當你寫完並測試完makefile之後,就可以不用顧及編譯處理過程的細節問題,make會把這一切料理好的。

二。依賴性檢查: make vs. 角本
儘管腳本可以在一些很細微的情況下保證在一致性,但是用腳本來編譯一個軟件項目通常都是不合適的。一方面,你不可能在當程序或模塊的一部分被變的時候,等着一個簡單的腳本來重新編譯每一個程序或目標模塊。另一方面,因爲不得不爲每一次迭代來編輯腳本就已經與一致性的目標相矛盾了。儘管用一個非常複雜的腳本來重編譯那些需要被編譯的模塊是有可能的,但是make會把這件事做的更好。

make可以讓你對編譯的內容跟編譯的方法寫一個簡單而且結構化的列表。它使用了一種依賴性檢查機制來比較源文件或由源文件得到的中間文件的模塊。make只在當一個或多個這樣的被稱做依賴性文件的前提文件在上一次編譯之後被更改之後重新編譯這些模塊。

make比較已有的目標模塊跟它的依賴文件的最後修改時間,來決定一個目標文件對於它的依賴文件是否已經過期。如果這個模塊不存在,或是模塊要比它的依賴文件生成的更早,那麼make就認爲這個模塊文件是過期的了,這樣就通過相應的命令來重新編譯它。一個模塊可以在當編譯生成它的命令變化的時候被認爲是過期的。

因爲make是做了一個完整的依賴性的掃描,對一個源文件的修改就會一致地影響到任意數量的中間文件或是處理過程。這一點使你可以指定一個自頂而下的層次性的步驟。你可以把makefile想象成一個配方.make讀出配方,決定哪些步驟需要被執行,並且只執行那些對於製造最終模塊需要的步驟。每一個要被編譯的文件,或是執行的步驟,都被叫做一個目標(target)。makefile對於某個目標的一條包括它的名字,它依賴於的文件列表和一系列編譯生成這個目標所需要的命令。

這一系列的命令就叫做規則。make把依賴性看作是先決條件的目標,在處理當前目標之前(在需要的情況下)更新它們。一個目標的規則不一定總要生成一個文件,不過如果它生成了一個文件,它就被稱爲目標文件。目標生成的每個文件(比如說,目標所依賴的文件)就被叫做依賴文件.如果對於一個目標的規則用那個名字沒有生成文件,那麼make就執行規則並認爲目標對之後的操作來說已經是最新的了(up-to-date)。make假定僅當它會對當前正在被處理的文件可能進行修改。如果一個源文件在被make操作的時候正在被另一個進程修改時,所生成的文件就有可能是處於一個不一致的狀態中。

編寫一個簡單的makefile對於一個目標項來說makefile的基本格式是:
target ...:[dependency...]
[command]
....

在第一行,目標名字列表以一個冒號結束。緊跟着的是一系列的依賴文件列表(如果有的情況下)。如果幾個目標被列舉出來,這就說明每一個這樣的目標會被用提供的規則單獨的編譯。後來的幾行都是以TAB開始的,這樣的起始被認爲是一行包含目標的命令行。有一個很常見的錯誤就是用SPACE來代替TAB,造成makefile文件的寫法錯誤。

以"#"開始的直到下一個新行開始的行被認爲是註釋行,它不會造成一個目標項的結束。目標項是以下一個非空的並以非TAB或#號開始的行標記結束的,或是以文件尾來標識。

一個很小的makefile可以由僅一個目標構成,比如:
test:
 ls test
 touch test

當你不帶參數執行make命令的時候,它會自動找一個名叫"makefile"的文件來進行編譯,如果它不存在的話,會找"Makefile"。如果這幾個文件都不在SCCS控制下的話,make就會檢查它的歷史文件來確定makefile。如果這已經是過期了的,make會得到最新的版本的。

如果make找到一個makefile,它就開始對makefile中出現的第一個目標項進行依賴性檢查。不然的話,你就必須要在命令行以參數的形式列舉出要被編譯的目標。make會在構造目標的過程中顯示所有的它運行的命令。如:
$make
ls test
test not found
touch test
ls test
test

在這個例子中,因爲test文件不存在,這樣就被認爲是過期了,make就執行目標項中的規則。如果你接着再運行一次make,那麼它就會告訴你目標已經是最新的了,這樣就跳過了規則的執行。如:
$make
'test' is up to date.

注意---如果命令行裏包含任何外殼原字符,比如說分號(重新項符號<,>,>>,|),代替符(*,?,[],$,=)或是引用符,退出符或註釋(“,‘,、,#,等),make會調用Bourne外殼來處理一個命令行。如果外核不需要解析命令行,那麼就讓exec()直接運行.

在一條規則中不同的兩行有一個很特別的性質就是,不同的命令行是被不同的進程或shell下執行的,這就是說比如有如下makefile:
test:
 cd /tmp
 pwd
執行的結果可能與你想象的不一樣,它的結果會是:
$make test
cd /tmp
pwd
/usr/tutorial/waite/arcana/minor/pentangles
而不是你所想象的最後一行應該是/tmp

你可以使用分號來指明命令是在一個單獨的shell中按順序執行,如:
test:
 cd /tmp;pwd
或者你也可以通過在makefile裏用''符來把命令輸入流定位到下一行中。新的這一行則被make看爲是一個空格。反斜線必須是一行的最後一個字符。而分號是在shell中所必須有的。如:
test:
 cd /tmp;
 pwd

三。隱含規則的基本用法:
當對於一個特定的目說沒有規則存在的時候,make就會試圖用一個隱含的規則來構建它。當make找到一個目標屬於的一類文件的規則之後,它將用在這個隱含着的規則來進行操作。

在你所寫的所有的makefile之外,make會繼續讀入一個默認的makefile,它位於  /usr/share/lib/make/make.rules,它包含一些目標項的隱藏規則,以及一些其他的信息。

注意 -- 隱藏規則是在早期的make裏面手動編寫的。一共有兩類隱含的規則:後綴名式和模式匹配式。對於與另一個文件有相同的名字不過後綴名不同的文件來說,後綴式規則對它們指明瞭一系列的命令根據這個後綴名來編譯這個文件。模式匹配規則則是根據一個與依賴性與wild-card模式相匹配的目標來選擇編譯規則。這樣的隱含規則在默認情況下是後綴名規則。在一些情況下,使用後綴名規則可以消除編寫一整個makefile文件的麻煩

比如說,通過一個單獨的functions.c的C語言文件來構建名爲functions.o的目標文件,你應該使用以下的命令:
$make functions.o
cc -c functions.c -o functions.o
這個規則對於從源文件nonesuch.c構建成nonesuch.o目標文件也同樣的有效。爲了從functions.c構建一個名爲functions的可執行文件(它沒有後綴名)你只需要用以下的命令:
$make functions
cc -o functions functions.c
這種從a.c構建a.o的規則被叫做.c.o後綴名規則(發音爲"dot-see-dot-oh")。這條規則纔對從a.c構建一個可執行的程序被叫做.c規則。

 

四。處理的依賴性:

在make命令開始之後,它通過深度依賴優先的法則遇到的處理順序來處理目標。比如如下的makefile:
batch: a b
 touch batch
b:
 touch b
a:
 touch a
c:
 echo "you won't see me"
make就從目標batch開始。因爲batch有一些依賴性還沒有被檢查,它們分別是a和b,make推延了生成batch直到它檢查了a和b的依賴性規則。
batch
a b
因爲a沒有依賴性了,make就處理它。如果文件不存在,則make會執行在目標項裏面的規則。
$make
touch a
.....
接下來,make回到父目標batch來,因爲還有一個沒有檢查依賴性的目標b,make又開始找b的依賴性。
.....
touch b
.....
最後,因爲所有的batch的依賴性都被檢查過了並已被構建了出來(如果需要的話),make就生成了batch:
batch

因爲它重構建了至少一個batch的依賴文件,make假設batch已經過期了,並重新構建batch,如果a或b當前沒有被構建,但是在當前目錄中已經存在且比batch要更新,make的時間版比較也會使batch被重新構建:
.....
touch batch
在依賴性掃描中沒有遇到的目標項就不會被處理。儘管makefile裏有一個關於c的目標項,所以它的規則就沒有被執行。你可以選擇一個代替的起始目標,比如用參數的形式把c傳遞給make.

在下一個例子中,batch目標沒有生成文件,取而代之的,它被用作一個組織一組目標的標籤。

batch: a b c
a: a1 a2
 touch a
b:
 touch b
c:
 touch c
a1:
 touch a1
a2:
 touch a2
在這種情況下,目標們按以下的順序並處理被檢查並被處理:
batch
a b c
a1 a2

本質上說,make接下來會:
1.檢查batch的依賴性,注意這裏有三個子目標,所以就推遲batch的生成
2.檢查a,這個第一依賴目標,注意到它有兩個自己的依賴目標,按例,make會繼續:
 a.檢查a1,如果需要的話,重新構造它。
 b.檢查a2,如果需要的話,重新構造它。
3.決定是否要重新構建a
4.檢查b並在需要的時候重建它.
5.檢查c並在需要的時候重建它。
6.在遍歷了它的依賴性樹之後,make會檢查並處理最頂部的目標batch。如果batch還有其他的規則,make會執行它。這裏batch沒有其他的規則了
這樣make就不執行任何操作,但是注意batch已經被重建了,任何依賴於batch的目標也會被重建的。

五。空規則:
如果一個目標項沒有規則,make會試圖從一個隱藏規則中選一條來構建它。如果make不能找到一個合適的隱藏規則來處理它,而且在SCCS歷史中沒有找到相應的規則,make就會認爲目標沒有相應的文件,這樣就把這種缺失的規則稱爲一個空規則。

注意 -- 你可以用一個空規則的依賴來強迫目標規則被執行。這種情況的一個更方便的叫法爲強制(FORCE)。對以下的makefile:

haste: FORCE
 echo "haste makes waste"
FORCE:
make會操作之下的規則,甚至當這個文件已經是最新的了也不例外:
$touch haste
$make haste
echo "haste makes waste"
haste makes waste

make有很多個內建的特殊目標用來達到很多特殊的功能。比如:.PRECIOUS目標會在make被中斷之後把make導引到保留庫文件中。

 

六。特殊目標:
 1.以"."開始
 2.沒有依賴性
 3.可以在makefile裏的任意一個位置出現。

七。不明目標:
如果一個文件的名字沒有在命令行中也不在依賴性列表中,且它:
 1.它不是當前工作目錄中的一個文件。
 2.它沒有目標以及依賴項
 3.它不屬於任何隱藏規則定義的文件類
 4.沒有任何SCCS歷史文件
 5.沒有爲./DEFAULT特殊目標定義規則
這樣make就會停止運行併發出一個錯誤信息。

如:
$make believe
make: Fatal error: Don't know how to make target 'believe'.
注意 - 儘管如果當-k參數起作用,make會繼續對其他不以這個有錯誤發生的目標爲依賴性的目標進行操作.

重複目標:
目標可以在一個makefile中不止一次出現,如:
foo: def_1
foo: dep_2
foo:
 touch foo
跟:
foo: dep_1 dep_2
 touch foo
完全相同。

然而,很多人認爲如果一個目標只出現過一次會比較好,這樣閱讀會比較輕鬆一些。


八。make的保留字:
.BUILT_LAST_MAKE_RUN .DEFAULT .DERIVED_SRC
.DONE .IGNORE .INIT
.KEEP_STATE .MAKE_VERSION .NO_PARALLEL
.PRECIOUS .RECURSIVE .SCCS_GET
.SILENT .SUFFIXES .WAIT
FORCE HOST_ARCH HOST_MACH
KEEP_STATE MAKE MAKEFLAGS
MFLAGS TARGET_ARCH TARGET_MACH
VERSION_1.0 VIRTUAL_ROOT VPATH

九。安靜地運行命令:
你可以對規則通過一行的行首添加一個"@"號限制止一個命令行的顯示數據。比如說,如下列的目標:
quiet:
 @echo you only see me once
就會出現如下的效果:
$make quiet
you only see me once
如果你要在一個特定的make運行中限制命令的顯示,你可以使用-s參數。如果你要限制所有的命令選擇顯示,你需要在你的makefile裏面加入.SLIENT。如:
.SLIENT
quiet:
 echo you only see me once

特定函數目標以"."爲開始符。目標名以一個"."開始的目標永遠不會被用作爲一個起始的目標,除非指明被要求爲命令行的一個參數。make僅僅會輸出一個錯誤信息並當命令返回一個非0的退出指令時停止。比如說,如果你有一個目標:
rmxyz:
 rm xyz
而且當前目錄下面沒有一個叫xyz的文件。make會在rm返回它的錯誤狀態值之後掛起。
$ls xyz
xyz not found
$make rmxyz
rm xyz
rm:xyz: No such file or directory
***Error code 1
make: Fatal error: Command failed for target 'rmxyz'
注意 - 如果-和#是前兩個字符,它們都會起到效果的。爲了不等待命令的退出代碼而繼續進行處理,你可以用一個"-"來作爲第一個非TAB字符:
rmxyz:
 -rm xyz
rm: xyz: No such file or directory
*** Error code 1 (ignored)


注意 - 除非你在測試一個makefile,在全局情形下,通常忽略非0的錯誤代碼都是不好的主意。


儘管建議這樣做通常是不好的,你可以通過-i參數來忽略所有的錯誤代碼。你也可以通過在makefile中包含.IGNORE這個特殊目標來忽略所有的退出代碼,儘管這樣的處理方式應該被避免掉。

如果你正在處理一列目標,而且你想讓make繼續下一個目標的操作而不是在遇到一個非0的返回代碼停下來的話,你可以使用-k參數。

十。自動恢復SCCS文件:
當源文件的名字在依賴性列表中,make會像其他目標一樣對待它們的。因爲源文件是假定存在於這個目錄裏面的,這樣就沒有必要再在makefile裏面添加一個目錄項。當一個目標沒有依賴性,但它存在於當前的目錄裏時,make假設這個文件已經是最新的了。但是,如果源文件是被SCCS所控制中,那麼make就會做一些附加的檢查來確保這個源文件是最新的了。

如果文件不存在,或者歷史文件更新一些,那麼make會自動地用下面的命令來恢復最新版本的文件:
sccs get -s filename -Gfilename
注意- 在其他版本的make裏面,自動的SCCS恢復僅是幾個特定的隱藏規則的一個特性。而且,與之前版本的make不同,make僅僅在SCCS目錄中查找歷史文件;在當前目錄下的歷史文件被忽略掉了。然而,如果源文件是對任何人都是可寫的,make不會恢復一到更新的版本上.
$ls SCCS/*
SCCS/s.functions.c
$rm -f functions.c
$make functions
sccs get -s functions.c -Gfunctions.c
cc -o functions functions.c

make檢查恢復版本的歷史文件的時間戳與歷史文件的時間戳。它不檢查看一下是否在當前目錄下的版本是最新的版本。這樣,如果哪個人作過一次按時間取操作(sccs get -c),make就不會發現這個事實,而且你會無意地構建一個更老版本的目標文件。爲了確保你編譯生成的目標是最新版本的,你應該在make之前,用sccs取得SCCS或是一個sccs清除命令。

禁止SCCS恢復
恢復SCCS文件的命令在默認makefile中被規則.SCCS_GET指定出來。爲了禁止自動恢復功能,只要簡單地在makefile中相應的目標項中加入一個空規則就可以了。

#禁止SCCS恢復
.SCCS_GET

十一。傳遞參數:簡單的make宏
make宏替換在當你想要從makefile中傳遞參數到命令行使你的工作變得很輕鬆。假設你要對你的程序編譯一個優化版本,利用cc -O 參數。你可以通過在makefile中加入make宏來達到這類性質,比如下面的例子:
functions: functions.c
 cc $(CFLAGS) -o functions functions.c

宏引入以某個值的佔位符的角色出現在makefile裏面,或是在makefile自己裏面,或是當成make命令的一個參數。如果你能定義一個CFLAGS宏,make會把你事先定義的宏值代換到指定的位置裏去。

$rm functions
$make functions "CFLAGS= -O"
cc -O -o functions functions.c
注意 - 在.c和.c.o隱含規則中都有CFLAGS的引入存在。
注意 - 命令行定義必須是一個單參數的,所以這樣的引用就如下例:
如果一個宏沒有被定義,make會把這樣的引用擴展爲一個空的字符串,你也可以通過在makefile裏面定義一個宏。一個典型的應用就是把CFLAGS設爲-O,這樣就可以默認生成優化了的目標代碼.
CFLAGS=-O
functions:functions.c
 cc $(CFLAGS) -o functions functions.c

一個在命令行參數中指出的宏定義會覆蓋在makefile裏面定義的宏。條件性定義的宏是這條規則的例外。

比如說,編譯一個正在用dbx或dbxtool調試的函數模塊的時候,你可以在命令行中定義CFLAGS的值爲-g:
$rm functions
$make CFLAGS=-g
cc -g -o functions functions.c
當用gprof來編譯一個外變量時,把-O和-pg同時送給CFLAGS。

一個宏引用在宏的名字不再是一個單個的字符的時候必須包含圓括號。如果宏名字只是一個字符,那麼圓括號可以被忽略。你可以用花括號來代替圓括號。比如,'$X','$(X),和'$'就是完全相同的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章