Makefile----shell函式和make的執行
shell函式也不像其它的函式。顧名思義,它的引數應該就是作業系統Shell的命令。它
和反引號“`”是相同的功能。這就是說,shell函式把執行作業系統命令後的輸出作為函
數返回。於是,我們可以用作業系統命令以及字串處理命令awk,sed等等命令來生成
一個變數,如:
contents := $(shell cat foo)
files := $(shell echo *.c)
注意,這個函式會新生成一個Shell程式來執行命令,所以你要注意其執行效能,如果你
的Makefile中有一些比較複雜的規則,並大量使用了這個函式,那麼對於你的系統性能
是有害的。特別是Makefile的隱晦的規則可能會讓你的shell函式執行的次數比你想像的
多得多。
二 控制make的函式
make提供了一些函式來控制make的執行。通常,你需要檢測一些執行Makefile時的執行
時資訊,並且根據這些資訊來決定,你是讓make繼續執行,還是停止。
$(error ;)
產生一個致命的錯誤,;是錯誤資訊。注意,error函式不會在一被使用
就會產生錯誤資訊,所以如果你把其定義在某個變數中,並在後續的指令碼中使用這個變
量,那麼也是可以的。例如:
示例一:
ifdef ERROR_001
$(error error is $(ERROR_001))
endif
示例二:
ERR = $(error found an error!)
.PHONY: err
err: ; $(ERR)
示例一會在變數ERROR_001定義了後執行時產生error呼叫,而示例二則在目標err被
執行時才發生error呼叫。
$(warning ;)
這個函式很像error函式,只是它並不會讓make退出,只是輸出一段警告資訊,而
make繼續執行。
make的執行:
一般來說,最簡單的就是直接在命令列下輸入make命令,make命令會找當前目錄的
makefile來執行,一切都是自動的。但也有時你也許只想讓make重編譯某些檔案,而不
是整個工程,而又有的時候你有幾套編譯規則,你想在不同的時候使用不同的編譯規
則,等等。本章節就是講述如何使用 make命令的。
一、make的退出碼
make命令執行後有三個退出碼:
0 —— 表示成功執行。
1 —— 如果make執行時出現任何錯誤,其返回1。
2 —— 如果你使用了make的“-q”選項,並且make使得一些目標不需要更新,那麼返回2。
Make的相關引數我們會在後續章節中講述。
二、指定Makefile
前面我們說過,GNU make找尋預設的Makefile的規則是在當前目錄下依次找三個檔案——
“GNUmakefile”、“makefile”和“Makefile”。其按順序找這三個檔案,一旦找到,就開始
讀取這個檔案並執行。
當前,我們也可以給make命令指定一個特殊名字的Makefile。要達到這個功能,我們要
使用make的“-f”或是“--file”引數(“--makefile”引數也行)。例如,我們有個
makefile的名字是“hchen.mk”,那麼,我們可以這樣來讓make來執行這個檔案:
make –f hchen.mk
如果在make的命令列是,你不只一次地使用了“-f”引數,那麼,所有指定的makefile將
會被連在一起傳遞給make執行。
三、指定目標
一般來說,make的最終目標是makefile中的第一個目標,而其它目標一般是由這個目標
連帶出來的。這是make的預設行為。當然,一般來說,你的makefile中的第一個目標是
由許多個目標組成,你可以指示make,讓其完成你所指定的目標。要達到這一目的很簡
單,需在make命令後直接跟目標的名字就可以完成(如前面提到的“make clean”形式)
任何在makefile中的目標都可以被指定成終極目標,但是除了以“-”打頭,或是包含了
“=”的目標,因為有這些字元的目標,會被解析成命令列引數或是變數。甚至沒有被我們
明確寫出來的目標也可以成為make的終極目標,也就是說,只要make可以找到其隱含規
則推導規則,那麼這個隱含目標同樣可以被指定成終極目標。
有一個make的環境變數叫“MAKECMDGOALS”,這個變數中會存放你所指定的終極目標的列
表,如果在命令列上,你沒有指定目標,那麼,這個變數是空值。這個變數可以讓你使
用在一些比較特殊的情形下。比如下面的例子:
sources = foo.c bar.c
ifneq ( $(MAKECMDGOALS),clean)
include $(sources:.c=.d)
endif
基於上面的這個例子,只要我們輸入的命令不是“make clean”,那麼makefile會自動包
含“foo.d”和“bar.d”這兩個makefile。
使用指定終極目標的方法可以很方便地讓我們編譯我們的程式,例如下面這個例子:
.PHONY: all
all: prog1 prog2 prog3 prog4
從這個例子中,我們可以看到,這個makefile中有四個需要編譯的程式——“prog1”,
“prog2”, “prog3”和 “prog4”,我們可以使用“make all”命令來編譯所有的目標(如
果把all置成第一個目標,那麼只需執行“make”),我們也可以使用 “make prog2”來單
獨編譯目標“prog2”。
即然make可以指定所有makefile中的目標,那麼也包括“偽目標”,於是我們可以根據這
種性質來讓我們的makefile根據指定的不同的目標來完成不同的事。在Unix世界中,軟
件釋出時,特別是GNU這種開源軟體的釋出時,其makefile都包含了編譯、安裝、打包等
功能。我們可以參照這種規則來書寫我們的makefile中的目標。
“all”
這個偽目標是所有目標的目標,其功能一般是編譯所有的目標。
“clean”
這個偽目標功能是刪除所有被make建立的檔案。
“install”
這個偽目標功能是安裝已編譯好的程式,其實就是把目標執行檔案拷貝到指定
的目標中去。
“print”
這個偽目標的功能是例出改變過的原始檔。
“tar”
這個偽目標功能是把源程式打包備份。也就是一個tar檔案。
“dist”
這個偽目標功能是建立一個壓縮檔案,一般是把tar檔案壓成Z檔案。或是gz檔案。
“TAGS”
這個偽目標功能是更新所有的目標,以備完整地重編譯使用。
“check”和“test”
這兩個偽目標一般用來測試makefile的流程。
當然一個專案的makefile中也不一定要書寫這樣的目標,這些東西都是GNU的東西,但是
我想,GNU搞出這些東西一定有其可取之處(等你的 UNIX下的程式檔案一多時你就會發
現這些功能很有用了),這裡只不過是說明了,如果你要書寫這種功能,最好使用這種
名字命名你的目標,這樣規範一些,規範的好處就是——不用解釋,大家都明白。而且如
果你的makefile中有這些功能,一是很實用,二是可以顯得你的makefile很專業(不是
那種初學者的作品)。
四、檢查規則
有時候,我們不想讓我們的makefile中的規則執行起來,我們只想檢查一下我們的命
令,或是執行的序列。於是我們可以使用make命令的下述引數:
“-n”
“--just-print”
“--dry-run”
“--recon”
不執行引數,這些引數只是列印命令,不管目標是否更新,把規則和連帶規則下的
命令打印出來,但不執行,這些引數對於我們除錯makefile很有用處。
“-t”
“--touch”
這個引數的意思就是把目標檔案的時間更新,但不更改目標檔案。也就是說,make
假裝編譯目標,但不是真正的編譯目標,只是把目標變成已編譯過的狀態。
“-q”
“--question”
這個引數的行為是找目標的意思,也就是說,如果目標存在,那麼其什麼也不會輸
出,當然也不會執行編譯,如果目標不存在,其會打印出一條出錯資訊。
“-W ;”
“--what-if=;”
“--assume-new=;”
“--new-file=;”
這個引數需要指定一個檔案。一般是是原始檔(或依賴檔案),Make會根據規則推
導來執行依賴於這個檔案的命令,一般來說,可以和“-n”引數一同使用,來檢視這個依
賴檔案所發生的規則命令。
另外一個很有意思的用法是結合“-p”和“-v”來輸出makefile被執行時的資訊(這個將在
後面講述)。