GNU make中文手冊-第六章:Makefile中的變量
作者: hew 發(fā)布日期:
2006-3-21
查看數(shù):
162
出自: http://www.
第六章:Makefile中的變量
--------------------------------------------------------------------------------
在Makefile中,變量就是一個(gè)名字(像是C語言中的宏),代表一個(gè)文本字符串(變量的值)。在Makefile的目標(biāo)、依賴、命令中引用一個(gè)變量的
地方,變量會(huì)被它的值所取代(與C語言中宏引用的方式相同,因此其他版本的make也把變量稱之為“宏”)。在Makefile中變量的特征有以下幾點(diǎn):
1. Makefile中變量和函數(shù)的展開(除規(guī)則的命令行以外),是在make讀取makefile文件時(shí)進(jìn)行的,這里的變量包括了使用“=”定義和使用指示符“define”定義的。
2. 變量可以用來代表一個(gè)文件名列表、編譯選項(xiàng)列表、程序運(yùn)行的選項(xiàng)參數(shù)列表、搜索源文件的目錄列表、編譯輸出的目錄列表和所有我們能夠想到的事物。
3. 變量名是不包括“:”、“#”、“=”、前置空白和尾空白的任何字符串。需要注意的是,盡管在GNU
make中沒有對變量的命名有其它的限制,但定義一個(gè)包含除字母、數(shù)字和下劃線以外的變量的做法也是不可取的,因?yàn)槌帜浮?shù)字和下劃線以外的其它字符可
能會(huì)在以后的make版本中被賦予特殊含義,并且這樣命名的變量對于一些shell來說不能作為環(huán)境變量使用。
4.
變量名是大小寫敏感的。變量“foo”、“Foo”和“FOO”指的是三個(gè)不同的變量。Makefile傳統(tǒng)做法是變量名是全采用大寫的方式。推薦的做法
是在對于內(nèi)部定義定義的一般變量(例如:目標(biāo)文件列表objects)使用小寫方式,而對于一些參數(shù)列表(例如:編譯選項(xiàng)CFLAGS)采用大寫方式,這
并不是要求的。但需要強(qiáng)調(diào)一點(diǎn):對于一個(gè)工程,所有Makefile中的變量命名應(yīng)保持一種風(fēng)格,否則會(huì)顯得你是一個(gè)蹩腳的程序員(就像代碼的變量命名風(fēng)
格一樣)。
5. 另外有一些變量名只包含了一個(gè)或者很少的幾個(gè)特殊的字符(符號(hào))。稱它們?yōu)樽詣?dòng)化變量。像“$<”、“$@”、“$?”、“$*”等。
6.1 變量的引用
當(dāng)我們定義了一個(gè)變量之后,就可以在Makefile的很多地方使用這個(gè)變量。變量的引用方式是:使用“$(VARIABLE_NAME)”或者“${
VARIABLE_NAME }”來引用一個(gè)變量的定義。例如:“$(foo)
”或者“${foo}”就是取變量“foo”的值。美元符號(hào)“$”在Makefile中有特殊的含義,所有在命令或者文件名中使用“$”時(shí)需要用兩個(gè)美元
符號(hào)“$$”來表示。對一個(gè)變量的引用可以在Makefile的任何上下文中,目標(biāo)、依賴、命令、絕大多數(shù)指示符和新變量的賦值中。這里有一個(gè)例子,其中
變量保存了所有.o文件的列表:
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
變量引用的展開過程是嚴(yán)格的文本替換過程,就是說變量值的字符串被精確的展開在此變量被引用的地方。因此規(guī)則:
foo = c
prog.o : prog.$(foo)
$(foo) $(foo) -$(foo) prog.$(foo)
被展開后就是:
prog.c : prog.c
cc -c prog.c
通過這個(gè)例子我們可以看到變量的展開過程完全和c語言中的宏展開的過程一樣,是一個(gè)嚴(yán)格的文本替換過程。上例中在變量“foo”被展開過程中,其值中的前
導(dǎo)空格會(huì)被忽略。這里舉這個(gè)例子的目的是為了讓我們更清楚地了解變量的展開方式,而不是建議大家按照這樣的方式來書寫Makefile。在實(shí)際書寫時(shí),千
萬不要使用這種方式。否則將會(huì)給你帶來很多不必要的麻煩。
注意:Makefile中在對一些簡單變量的應(yīng)用,我們也可以不使用“()”和“{}”來標(biāo)記變量名,而直接使用“$x”的格式來實(shí)現(xiàn),此種用法僅限于變
量名為單字符的情況。另外自動(dòng)化變量也使用這種格式。對于一般多字符變量的引用必須使用括號(hào)了標(biāo)記,否則make將把變量名的首字母作為作為引用(“$
PATH”在Makefile中實(shí)際上是“$(P)ATH”)。這一點(diǎn)和shell中變量的引用方式不同。shell中變量的引用可以是“${xx}”或
者“$xx”格式。但在Makefile中多字符變量名的引用只能是“$(xx)”或者“${xx}”格式。
一般在我們書寫Makefile時(shí),各部分變量引用的格式我們建議如下:
1. make變量(Makefile中定義的或者是make的環(huán)境變量)的引用使用“$(VAR)”格式,無論“VAR”是單字符變量名還是多字符變量名。
2. 出現(xiàn)在規(guī)則命令行中shell變量(一般為執(zhí)行命令過程中的臨時(shí)變量,它不屬于Makefile變量,而是一個(gè)shell變量)引用使用shell的“$tmp”格式。
3. 對出現(xiàn)在命令行中的make變量我們同樣使用“$(CMDVAR)” 格式來引用。
例如:
# sample Makefile
……
SUBDIRS := src foo
.PHONY : subdir
Subdir :
@for dir in $(SUBDIRS); do \
$(MAKE) –C $$dir || exit 1; \
done
……
6.2 兩種變量定義(賦值)
GNU make中,一個(gè)變量的定義有兩種方式(或者稱為風(fēng)格)。我們把這兩種方式定義的變量可以看作變量的兩種不同風(fēng)格。變量的這兩種不同的風(fēng)格的區(qū)別在于:1. 定義方式;2. 展開時(shí)機(jī)。下邊我們分別對這兩種不同的風(fēng)格進(jìn)行詳細(xì)地討論。
6.2.1 遞歸展開式變量
第一種風(fēng)格的變量就是遞歸方式擴(kuò)展的變量。這一類型的變量的定義是通過“=”或者使用指示符“define”定義的變量。對這種變量的引用,在引用的地方
是嚴(yán)格的文本替換過程,此變量值的字符串原模原樣的出現(xiàn)在引用它的地方。如果此變量定義中存在對其他變量的引用,這些被引用的變量會(huì)在它被展開的同時(shí)被展
開。就是說在變量定義時(shí),變量值中對其他變量的引用不會(huì)被替換展開。而是,變量在引用它的地方進(jìn)行替換展開的同時(shí),它所引用的其它變量才會(huì)被替換展開。語
言的描述可能比較晦澀,讓我們來看一個(gè)例子:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
執(zhí)行“make”將會(huì)打印出“Huh?”。整個(gè)變量的替換過程時(shí)這樣的:首先“$(foo)”被替換為“$(bar)”,接下來“$(bar)”被替換為
“$(ugh)”,最后“$(ugh)”被替換為“Hug?”。整個(gè)替換的過程是在執(zhí)行“echo $(foo)”是進(jìn)行的。
這種類型的變量是其它版本的make所支持的類型。我們可以把這種類型的變量稱為“遞歸展開”式變量。此類型的變量存有它的優(yōu)點(diǎn)同時(shí)也存在其缺點(diǎn)。其優(yōu)點(diǎn)是:
這種類型變量的定義時(shí),可以引用其它的之前沒有定義的變量(可能在后續(xù)部分定義,或者是通過make的命令行選項(xiàng)傳遞的變量)。看一個(gè)這樣的例子:
CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar
“CFLAGS”會(huì)在命令中被展開為“-Ifoo -Ibar -O”。我們可以看到在“CFLAGS”定義中使用到了之后定義的變量“include_dirs”。
其缺點(diǎn)是:
1. 使用此風(fēng)格的變量定義,可能會(huì)由于出現(xiàn)變量的遞歸定義而導(dǎo)致make陷入到無限的變量展開過程中,最終使make執(zhí)行失敗。例如,接上邊的例子,我們給這個(gè)變量追加值:
CFLAGS = $(CFLAGS) –O
它將會(huì)導(dǎo)致make進(jìn)入對變量“CFLAGS”的無限展過程中去(這種定義就是變量的遞歸定義)。因?yàn)橐坏┖罄m(xù)同樣存在對“CLFAGS”定義的追加,展
開過程將是套嵌的、不能終止的(在發(fā)生這種情況時(shí),make會(huì)提示錯(cuò)誤信息并結(jié)束)。一般在書寫Makefile時(shí),使用這種追加變量值的方法也很少使用
(也不是我們推薦的方式)。我們可以來看另外一個(gè)例子:
x = $(y)
y = $(x) $(z)
這種情況下同樣會(huì)導(dǎo)致make陷入到無限的變量展開過程中。因此我們在使用這種風(fēng)格的變量,當(dāng)一個(gè)變量定義需要引用其它的變量時(shí)需要特別的注意,防止變量
的展開進(jìn)入無限的循環(huán)。當(dāng)出現(xiàn)這樣的錯(cuò)誤時(shí),首先檢查你的Makefile中變量是否出現(xiàn)了遞歸定義。這是它的一個(gè)缺點(diǎn)。
2. 第二個(gè)缺點(diǎn):這種風(fēng)格變量的定義中如果使引用了某一個(gè)函數(shù),那么函數(shù)總會(huì)在其被引用的地方被執(zhí)行。
這是因?yàn)檫@種風(fēng)格變量的定義中,對函數(shù)引用的替換展開發(fā)生在展開它自身的時(shí)候,而不是在定義它的時(shí)候。這樣所帶來的問題是,可能可能會(huì)使make的執(zhí)行效
率降低,同時(shí)對某些變量和函數(shù)的引用出現(xiàn)問題。特別是當(dāng)變量定義中引用了“shell”和“wildcard”函數(shù)的情況,可能出現(xiàn)不可控制或者難以預(yù)料
的錯(cuò)誤,因?yàn)槲覀儫o法確定它在何時(shí)會(huì)被展開。
6.2.2 直接展開式變量
為了避免“遞歸展開式”變量存在的問題和不方便。在GNU
make中可以使用另外一種風(fēng)格的變量,我們將它稱為“直接展開”式。這種風(fēng)格的變量使用“:=”來定義變量。在使用“:=”定義變量時(shí),變量值中對另外
變量的引用或者函數(shù)的引用在定義時(shí)被展開(對變量進(jìn)行替換)。所以在變量被定義以后就是一個(gè)實(shí)際所需要定義的文本串,其中不再包含任何對其它變量的引用。
因此
x := foo
y := $(x) bar
x := later
就等價(jià)于:
y := foo bar
x := later
需要注意的是:此風(fēng)格變量在定義時(shí)就完成了對所引用的變量的展開,因此它不能實(shí)現(xiàn)對其后定義變量的引用。例如:
CFLAGS := $(include_dirs) -O
include_dirs := -Ifoo -Ibar
由于在變量“include_dirs”的定義出現(xiàn)在“CFLAGS”定義之后。因此在“CFLAGS”的定義中,“include_dirs”的值為
空。“CFLAGS”的值為“-O”而不是“-Ifoo -Ibar
-O”。這一點(diǎn)也是直接展開式和遞歸展開式變量的不同點(diǎn)。注意這里的兩個(gè)變量都是“直接展開”式的。大家不妨試試將其中某一個(gè)變量使用遞歸展開式定義后看
一下又會(huì)出現(xiàn)什么樣的結(jié)果。
下邊讓我們來看一個(gè)復(fù)雜一點(diǎn)的例子。分析一下直接展開式變量定義(:=)的用法,這里也用到了make的shell函數(shù)和變量“MAKELEVEL”(此變量在make的遞歸調(diào)用時(shí)代表make的調(diào)用深度)。
其中包括了make的函數(shù)、條件表達(dá)式和一個(gè)系統(tǒng)變量“MAKELEVEL”的使用:
ifeq (0,${MAKELEVEL})
cur-dir := $(shell pwd)
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} host-type=${host-type} whoami=${whoami}
endif
第一行是一個(gè)條件判斷,說明如果是頂層Makefile,就定義下列變量。否則不定義任何變量。第二、三、四、五行分別定義了一個(gè)變量,在進(jìn)行變量定義時(shí)對引用到的其它變量和函數(shù)展開。最后結(jié)束定義。由于直接展開式的這個(gè)優(yōu)點(diǎn)我們就可以書寫這樣一個(gè)規(guī)則:
${subdirs}:
${MAKE} cur-dir=${cur-dir}/$@ -C $@ all
它實(shí)現(xiàn)了在不同子目錄下變量“cur_dir”使用不同的值(為當(dāng)前工作目錄)。
在復(fù)雜的Makefile中,推薦使用直接展開式變量。因?yàn)檫@種風(fēng)格變量的使用方式和大多數(shù)編程語言中的變量使用方式基本上相同。它可以使一個(gè)比較復(fù)雜的
Makefile在一定程度上具有可預(yù)測性。而且這種變量允許我們利用之前所定義的值來重新定義它(比如使用某一個(gè)函數(shù)來對它以前的值進(jìn)行處理并重新賦
值),此方式在Makefile中經(jīng)常用到。盡量避免和減少遞歸方式的變量的使用。
6.2.3 如何定義一個(gè)空格
使用直接擴(kuò)展式變量定義我們可以實(shí)現(xiàn)將一個(gè)前導(dǎo)空格定義在變量值中。一般變量值中的前導(dǎo)空格字符在變量引用和函數(shù)調(diào)用時(shí)被丟棄。直接展開式變量在定義時(shí)對
引用的其它變量或函數(shù)進(jìn)行展開的特點(diǎn),使我們可以實(shí)現(xiàn)在一個(gè)變量中包含前導(dǎo)空格并在引用此變量時(shí)對空格加以保護(hù)。像這樣:
nullstring :=
space := $(nullstring) # end of the line
這里,變量“space”就正好表示一個(gè)一個(gè)空格。“space”定義的行中的注釋在這里使得我們的目的更清晰(明確地描述一個(gè)空格字符比較困難),明確
的指定我們需要的是一個(gè)空格。這是一個(gè)很好地實(shí)現(xiàn)方式。使用變量“nullstring”標(biāo)明變量值的開始,采用“#”注釋來結(jié)束,中間是一個(gè)空格字符。
make對變量進(jìn)行處理時(shí)變量值中尾空格是不被忽略的,因此定義一個(gè)包含一個(gè)或者多個(gè)空格的變量定義時(shí),上邊的實(shí)現(xiàn)就是一個(gè)簡單并且非常直觀的方式。但是
需要注意的是對于不包含尾空格的變量的定義,就不能隨便使用幾個(gè)空格之后,在同行中放置它的注釋內(nèi)容。這是千萬需要注意的。例如下邊的做法就是不正確的:
dir := /foo/bar # directory to put the frobs in
變量“dir”的值是“/foo/bar ”(后面有4個(gè)空格),這可能并不是你想要實(shí)現(xiàn)的。假如一個(gè)特定的文件以它作為路徑來表示“$(dir)/file”,那么大錯(cuò)特錯(cuò)了。
這里順便提醒大家,在書寫Makefile時(shí)。注釋內(nèi)容推薦書寫在獨(dú)立的一行或者多行,這樣就可以防止出現(xiàn)這種意外情況的發(fā)生,而且注釋行獨(dú)立的行書寫時(shí)
也使得你的Makefile更加清晰,便于別人的預(yù)讀。對于特殊的定義,比如定義包含一個(gè)或者多個(gè)空格空格的變量時(shí)進(jìn)行詳細(xì)地說明和注釋。
6.2.4 “?=”操作符
GNU make中,還有一個(gè)被稱為條件賦值的賦值操作符“?=”。被稱為條件賦值是因?yàn)椋褐挥写俗兞吭谥皼]有賦值的情況下才會(huì)對這個(gè)變量進(jìn)行賦值。例如:
FOO ?= bar
其等價(jià)于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif
含義是:如果變量“FOO”在沒有定義過,就給它賦值“bar”。否則不改變它的值。
6.3 變量的高級(jí)用法
本節(jié)討論一些變量的高級(jí)用法,這些高級(jí)的用法使我們可以更靈活的使用變量。
6.3.1 變量的替換引用
對于一個(gè)已經(jīng)定義的變量,可以使用“替換引用”將其值使用指定的字符(字符串)進(jìn)行替換。格式為“$(VAR:A=B)”(或者“${VAR:A=
B}”),意思是,替換變量“VAR”中所有“A”字符結(jié)尾的字為“B”結(jié)尾的字。“結(jié)尾”的含義是空格之前(變量值的多個(gè)字以空格分開)。而對于變量其
它部分的“A”字符不進(jìn)行替換。例如:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
在這個(gè)定義中,變量“bar”的值就為“a.c b.c
c.c”。使用變量的替換引用將變量“foo”以空格分開的值中的所有的字的尾字符“o”替換為“c”,其他部分不變。而且如果在變量“foo”中如果存
在“o.o”時(shí),那么變量“bar”的值為“a.c b.c c.c o.c”而不是“a.c b.c c.c c.c”。這一點(diǎn)需要明確。
變量的替換引用其實(shí)是函數(shù)“patsubst”(參考文本處理函數(shù)一節(jié))的一個(gè)簡化實(shí)現(xiàn)。在GNU make中同時(shí)提供了這兩種方式來實(shí)現(xiàn)同樣的目的,以兼容其它版本make。
另外一種引用替換的技術(shù)使用功能更強(qiáng)大的“patsubst”函數(shù)的所有功能。它的格式和上面“$(VAR:A=B)”的格式相類似,不過需要在這里的
“A”和“B”中需要包含模式字符“%”。只是它就和“$(patsubst A,B $(VAR))”所實(shí)現(xiàn)功能相同。例如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
這個(gè)例子同樣使變量“bar”的值為“a.c b.c c.c”。這種格式的替換引用方式比第一種方式更為通用。
6.3.2 變量的套嵌引用
計(jì)算的變量名是一個(gè)比較復(fù)雜的概念,僅用在那些復(fù)雜的Makefile中。通常我們不需要對它的計(jì)算過程有深入地了解,只要知道當(dāng)一個(gè)被引用的變量名之中
含有“$”時(shí),可以得到另外一個(gè)值。你如果是一個(gè)比較喜歡追根問底的人,或者你想弄清楚make計(jì)算變量的過程。就可以參考本節(jié)的內(nèi)容。
一個(gè)變量名(文本串)之中可以包含對其它變量的引用。這種情況我們稱之為“變量的套嵌引用”或者“計(jì)算的變量名”。例如:
先看一個(gè)例子:
x = y
y = z
a := $($(x))
這個(gè)例子中,最終定義了“a”的值為“z”。來看一下變量的引用過程:首先最里邊的變量引用“$(x)”被替換為變量名“y”(就是“$($(x))”被
替換為了“$(y)”),之后“$(y)”被替換為“z”(就是a :=
z)。這個(gè)例子中(a:=$($(x)))所引用的變量名不是明確聲明的,而是由$(x)擴(kuò)展得到。這里“$(x)”相對于外層的引用就是套嵌的變量引
用。
上個(gè)例子我們可以看到是一個(gè)兩層的套嵌引用,具有多層的套嵌引用在Makefile中也是允許的。下邊我們在來看一個(gè)三層套嵌引用的例子:
x = y
y = z
z = u
a := $($($(x)))
這個(gè)例子的最終是定義了“a”的值為“u”。它的擴(kuò)展過程和上邊第一個(gè)例子的過程相同。首先“$(x)”被替換為“y”,則“$($(x))”就是“$(y)”,“$(y)”在北替換為“z”,所有就是“a:=$(z)”;“$(z)”最后被替換為“u”。
以上兩個(gè)套嵌引用的例子中沒有使用到遞歸展開式變量。遞歸展開式變量的變量名的計(jì)算過程,也是按照相同的方式被擴(kuò)展的。我們看一個(gè)這樣一個(gè)例子:
x = $(y)
y = z
z = Hello
a := $($(x))
此例最終實(shí)現(xiàn)了“a:=Hello”這么一個(gè)定義。其中“$($(x))”被替換成了“$($(y))”,“$($(y)) ”在被替換為“$(z)”,最終就是“a:=Hello”。
這里的$($(x))被替換成了$($(y)),因?yàn)?(y)值是“z”,所以,最終結(jié)果是:a:=$(z),也就是“Hello”。
遞歸變量的套嵌引用過程,也可以包含變量的修改引用和函數(shù)調(diào)用??聪逻叺睦?,使用了make的文本處理函數(shù):
x = variable1
variable2 := Hello
y = $(subst 1,2,$(x))
z = y
a := $($($(z)))
此例同樣的實(shí)現(xiàn)“a:=Hello”。“$($($(z)))”替換為“$($(y))”,之后再次被替換為“$($(subst
1,2,$(x)))”(“$(x)”的值是“variable1”,所以有“$($(subst
1,2,$(variable1)))”)。函數(shù)處理之后為“$(variable2)”
之后對它在進(jìn)行替換展開。最終,變量“a”的值就是“Hello”。從上邊的例子中我們看到,計(jì)算的變量名的引用過程存在多層套嵌過程,也是用了文本處理
函數(shù)。這個(gè)復(fù)雜的計(jì)算變量的過程,會(huì)使很多人感到混亂甚至迷惑。上例中所要實(shí)現(xiàn)的目的沒有直接使用“a:=Hello”來的直觀明了。我們在書寫
Makefile時(shí),應(yīng)盡量避免使用套嵌的變量引用方式。在一些必需的地方,也最好不要使用高于兩級(jí)的套嵌引用。使用套嵌的變量引用時(shí),如果涉及到遞歸展
開式的變量引用時(shí)需要特別注意。如果處理不當(dāng)可能會(huì)導(dǎo)致遞歸展開錯(cuò)誤,或者出現(xiàn)一些那一預(yù)料的現(xiàn)象。
一個(gè)計(jì)算的變量名可以不是對一個(gè)完整、單一的其它變量的引用。其中可以包含多個(gè)變量的引用,也可以包含一些文本字符串。就是說,計(jì)算變量的名字可以由一個(gè)或者多個(gè)變量引用同時(shí)加上字符串混合組成。例如:
a_dirs := dira dirb
1_dirs := dir1 dir2
a_files := filea fileb
1_files := file1 file2
ifeq "$(use_a)" "yes"
a1 := a
else
a1 := 1
endif
ifeq "$(use_dirs)" "yes"
df := dirs
else
df := files
endif
dirs := $($(a1)_$(df))
這個(gè)例子實(shí)現(xiàn)對變量“dirs”的定義,它的可能取值為“a_dirs”、“1_dirs”、“a_files”或者“a_files”四個(gè)值其中之一,具體依賴于“use_a”和“use_dirs”的定義。
計(jì)算的變量名也可以使用上一小節(jié)我們討論過的“變量的替換引用”。例如:
a_objects := a.o b.o c.o
1_objects := 1.o 2.o 3.o
sources := $($(a1)_objects:.o=.c)
這個(gè)例子實(shí)現(xiàn)對變量“sources”的定義,它的可能取值為“a.c b.c c.c”和“1.c 2.c 3.c”,具體依賴于“a1”的定義。大家自己分析一下計(jì)算變量名的過程。
使用嵌套的變量引用的唯一限制是,不能通過指定部分需要調(diào)用的函數(shù)名稱(調(diào)用的函數(shù)包括了函數(shù)名本身和執(zhí)行的參數(shù))來實(shí)現(xiàn)對這個(gè)函數(shù)的調(diào)用。這是因?yàn)樘浊?
引用在展開之前已經(jīng)完成了對函數(shù)名的識(shí)別測試。語言的描述可能比較難理解。我們來看一個(gè)例子,此例子試圖將函數(shù)執(zhí)行的結(jié)果賦值給一個(gè)變量:
ifdef do_sort
func := sort
else
func := strip
endif
bar := a d b g q c
foo := $($(func) $(bar))
此例的本意是將“sort”或者“strip”(依賴于是否定義了變量“do_sort”)以“a d b g q
c”的執(zhí)行結(jié)果賦值變量“foo”。在這里使用了套嵌引用方式來實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)的結(jié)果是:變量“foo”的值為字符串“sort a d b g q
c”或者“strip a d g q c”。這是目前版本的make在處理套嵌變量引用時(shí)的限制。
計(jì)算的變量名可以用在:1. 一個(gè)使用賦值操作符定義的變量的左值部分;2. 使用“define”定義的變量名中。例如:
dir = foo
$(dir)_sources := $(wildcard $(dir)/*.c)
define $(dir)_print
lpr $($(dir)_sources)
endef
在這個(gè)例子中我們定義了三個(gè)變量:“dir”,“foo_sources”和“foo_print”。
計(jì)算的變量名在進(jìn)行替換時(shí)的順序是:從最里層的變量引用開始,逐步向外進(jìn)行替換。一層層展開直到最后計(jì)算出需要應(yīng)用的具體的變量,之后進(jìn)行替換展開得到實(shí)際的引用值。
變量的套嵌引用(計(jì)算的變量名)在我們的Makefile中應(yīng)該盡量避免使用。在必需的場合使用時(shí)掌握的原則是:套嵌使用的層數(shù)越少越好,使用多個(gè)兩層套
嵌引用代替一個(gè)多層的套嵌引用。如果在你的Makefile中存在一個(gè)層次很深的套嵌引用。會(huì)給其他人閱讀造成很大的困難。而且變量的多級(jí)套嵌引用在某些
時(shí)候會(huì)使簡單問題復(fù)雜化。
作為一個(gè)優(yōu)秀的程序員,在面對一個(gè)復(fù)雜問題時(shí),應(yīng)該是尋求一種盡可能簡單、直接并且高效的處理方式來解決,而不是將一個(gè)簡單問題在實(shí)現(xiàn)上復(fù)雜化。如果想在簡單問題上突出自己使用某種語言的熟練程度,是一種非常愚蠢、且不成熟的行為。
注意:
套嵌引用的變量和遞歸展開的變量在本質(zhì)上存在區(qū)別。套嵌的引用就是使用一個(gè)變量表示另外一個(gè)變量,或者更多的層次;而遞歸展開的變量表示當(dāng)一個(gè)變量存在對
其它變量的引用時(shí),對這變量替換的方式。遞歸展開在另外一個(gè)角度描述了這個(gè)變量在定義是賦予它的一個(gè)屬性或者風(fēng)格。并且我們可以在定義個(gè)一個(gè)遞歸展開式的
變量時(shí)使用套嵌引用的方式,但是建議你的實(shí)際編寫Makefile時(shí)要盡量避免這種復(fù)雜的用法。
6.4 變量取值
一個(gè)變量可以通過以下幾種方式來獲得值:
² 在運(yùn)行make時(shí)通過命令行選項(xiàng)來取代一個(gè)已定義的變量值。
² 在makefile文件中通過賦值的方式(參考如何設(shè)置變量一節(jié))或者使用“define”來為一個(gè)變量賦值。
² 將變量設(shè)置為系統(tǒng)環(huán)境變量。所有系統(tǒng)環(huán)境變量都可以被make使用。
² 自動(dòng)化變量,在不同的規(guī)則中自動(dòng)化變量會(huì)被賦予不同的值。它們每一個(gè)都有單一的習(xí)慣性用法。
² 一些變量具有固定的值。
6.5 如何設(shè)置變量
Makefile中變量的設(shè)置(也可以稱之為定義)是通過“=”(遞歸方式)或者“:=”(靜態(tài)方式)來實(shí)現(xiàn)的。“=”和“:=”左邊的是變量名,右邊是變量的值。下邊就是一個(gè)變量的定義語句:
objects = main.o foo.o bar.o utils.o
這個(gè)語句定義了一個(gè)變量“objects”,其值為一個(gè).o文件的列表。變量名兩邊的空格和“=”之后的空格在make處理時(shí)被忽略。
使用“=”定義的變量稱之為“遞歸展開”式變量;使用“:=”定義的變量稱為“直接展開”式變量,“直接展開”式的變量如果其值中存在對其變量或者函數(shù)的引用,在定義時(shí)這些引用將會(huì)被進(jìn)行替換展開。
定義一個(gè)變量時(shí)需要明確以下幾點(diǎn):
1. 變量名之中可以包含函數(shù)或者其它變量的引用,make在讀入此行時(shí)根據(jù)已定義情況進(jìn)行替換展開而產(chǎn)生實(shí)際的變量名。
2.
變量的定義值在長度上沒有限制。不過還是需要考慮你的實(shí)際情況,保證你的機(jī)器上有足夠的可用的交換空間來處理一個(gè)超常的變量值。變量定義較長時(shí),一個(gè)好的
做法就是將比較長的行分多個(gè)行來書寫,除最后一行外行與行之間使用反斜杠(\)連接,表示一個(gè)完整的行。這樣的書寫方式對make的處理不會(huì)造成任何影
響,便于后期修改維護(hù)而且使得你的Makefile更清晰。例如上邊的例子就可以這樣寫:
ojects = main.o foo.o \
bar.o utils.o
3. 當(dāng)引用一個(gè)沒有定義的變量時(shí),make默認(rèn)它的值為空。
4. 一些特殊的變量在make中有內(nèi)嵌固定的值(可參考 9.3 隱含變量 一節(jié)),不過這些變量允許我們在Makefile中顯式得重新給它賦值。
5. 還存在一些由兩個(gè)符號(hào)組成的特殊變量,稱之為自動(dòng)環(huán)變量。它們的值不能在Makefile中進(jìn)行顯式的修改。這些變量使用在規(guī)則中時(shí),不同的規(guī)則中它們會(huì)被賦予不同的值。
6. 如果你希望實(shí)現(xiàn)這樣一個(gè)操作,僅對一個(gè)之前沒有定義過的變量進(jìn)行賦值。那么可以使用速記符“?=”(條件方式)來代替“=”或者“:=”來實(shí)現(xiàn)(可參考 5.2.4 “?=”操作符 一小節(jié))。
6.6 追加變量值
通常對于一個(gè)通用變量在定義之后的其他一個(gè)地方,需要給它的值進(jìn)行追加。這也是非常有用的。我們可以在開始給它定義一個(gè)基本的值,后續(xù)可以不斷地根據(jù)需要給它增加一些必要值。在Makefile中使用“+=”(追加方式)來實(shí)現(xiàn)對一個(gè)變量值的追加操作。像下邊那樣:
objects += another.o
這個(gè)操作把字符串“another.o”添加到變量“objects”原有值的末尾,使用空格將其分開。因此我們可以看到:
objects = main.o foo.o bar.o utils.o
objects += another.o
上邊的兩個(gè)操作之后變量“objects”的值成:“main.o foo.o bar.o utils.o another.o”。使用“+=”操作符,就相當(dāng)于:
objects = main.o foo.o bar.o utils.o
objects := $(objects) another.o
但是,這兩種方式可能在簡單一些的Makefile有相同的效果,復(fù)雜的Makefile中它們之間的差異就會(huì)導(dǎo)致一些問題。為了方便我們調(diào)試,了解這兩種實(shí)現(xiàn)的差異還是很有必要的。
1. 如果被追加值的變量之前沒有定義,那么,“+=”會(huì)自動(dòng)變成“=”,此變量就被定義為一個(gè)遞歸展開式的變量。如果之前存在這個(gè)變量定義,那么“+=”就繼承之前定義時(shí)的變量風(fēng)格。
2. 直接展開式變量的追加過程:變量使用“:=”定義,之后“+=”操作將會(huì)首先替換展開之前此變量的值,爾后在末尾添加需要追加的值,并使用“:=”重新給此變量賦值。實(shí)際的過程像下邊那樣:
variable := value
variable += more
就是:
variable := value
variable := $(variable) more
3.
遞歸展開式變量的追加過程:一個(gè)變量使用“=”定義,之后“+=”操作時(shí)不對之前此變量值中的任何引用進(jìn)行替換展開,而是按照文本的擴(kuò)展方式(之前等號(hào)右
邊的文本未發(fā)生變化)替換,爾后在末尾添加需要追加的值,并使用“=”給此變量重新賦值。實(shí)際的過程和上邊的相類似:
variable = value
variable += more
相當(dāng)于:
temp = value
variable = $(temp) more
當(dāng)然了,上邊的過程并不會(huì)存在中間變量:“temp”,這里只是使用它來描述得更形象。這種情況時(shí)如果“value”中存在某種引用,情況就有些不同了??次覀兺ǔR粋€(gè)會(huì)用到的例子:
CFLAGS = $(includes) -O
...
CFLAGS += -pg # enable profiling
第一行定義了變量“CFLAGS”,它是一個(gè)遞歸展開式的變量。因此make在處理它的定義時(shí)不會(huì)對其值中的引用“$(includes)”進(jìn)行展開,它
的替換展開是在變量“CFLAGS”被引用的規(guī)則中。因此,變量“include”可以在“CFLAGS”之前沒有定義,只要它在實(shí)際引用
“CFLAGS”之前定義就可以了。如果給“CFLAGS”追加值使用“:=”操作符,我們按照下邊那樣實(shí)現(xiàn):
CFLAGS := $(CFLAGS) -pg # enable profiling
這樣似乎好像很正確,但是實(shí)際上它在有些情況時(shí)卻不是你所要實(shí)現(xiàn)的。我們來看看,因?yàn)?#8220;:=”操作符定義的是直接展開式變量,因此變量值中對其它變量或者
函數(shù)的引用會(huì)在定義時(shí)進(jìn)行展開。在這種情況下,如果變量“includes”在之前沒有進(jìn)行定義的話,變量“CFLAGS”的值為“-O
-pg”($(includes)被替換展開為空字符)。而其后出現(xiàn)的“includes”的定義對“CFLAGS”將不產(chǎn)生影響。相反的情況,如果我們
在這里使用“+=”實(shí)現(xiàn):
CFLAGS += -pg # enable profiling
那么變量“CFLAGS”的值就是文本串“$(includes) –O
-pg”,因?yàn)橹?#8220;CFLAGS”定義為遞歸展開式,所以追加值時(shí)不會(huì)對其值的引用進(jìn)行替換展開。因此變量“includes”只要出現(xiàn)在規(guī)則對
“CFLAGS”的引用之前定義,它都可以對“CFLAGS”的值起作用。對于遞歸展開式變量的追加,make程序會(huì)同樣會(huì)按照遞歸展開式的定義來實(shí)現(xiàn)對
變量的重新賦值,不會(huì)發(fā)生遞歸展開式變量展開過程的無限循環(huán)。
6.7 override 指示符
通常在執(zhí)行make時(shí),如果通過命令行定義了一個(gè)變量,那么它將替代在Makefile中出現(xiàn)的同名變量的定義。就是說,對于一個(gè)在Makefile中使
用常規(guī)方式(使用“=”、“:=”或者“define”)定義的變量,我們可以在執(zhí)行make時(shí)通過命令行方式重新指定這個(gè)變量的值,命令行指定的值將替
代出現(xiàn)在Makefile中此變量的值。為了防止命令行變量定義的值替代Makefile中變量定義的值。需要在Makefile中使用指示符
“override”來聲明這個(gè)變量,像下邊那樣:
override VARIABLE = VALUE
或者:
override VARIABLE := VALUE
也可以對變量使用追加方式:
override VARIABLE += MORE TEXT
對于追加方式需要說明的是:變量在定義時(shí)使用了“override”,則后續(xù)對它值進(jìn)行追加時(shí),也需要使用帶有“override”指示符的追加方式。否則對此變量值的追加不會(huì)生效。
指示符“override”并不是用來調(diào)整Makefile和執(zhí)行時(shí)命令參數(shù)的沖突,其存在的目的是為了使用戶可以改變或者追加那些使用make的命令行
指定的變量的定義。從另外一個(gè)角度來說,就是實(shí)現(xiàn)了在Makefile中增加或者修改命令行參數(shù)的一種機(jī)制。我們可能會(huì)有這樣的需求;可以通過命令行來指
定一些附加的編譯參數(shù),對一些通用的參數(shù)或者必需的編譯參數(shù)我們可以在Makefile中指定,而在命令行中可以指定一些特殊的參數(shù)。對待這種需求,我們
可以使用指示符“override”來實(shí)現(xiàn)。
例如無論命令行指定那些編譯參數(shù),必須打開調(diào)試開關(guān)“-g”,我們的Makefile對“CFLAGS”應(yīng)該這樣寫:
override CFLAGS += -g
無論通過命令行指定那些編譯選項(xiàng),“-g”參數(shù)始終存在。
對于使用“define”定義的變量我們同樣也可以使用“override”進(jìn)行聲明。例如:
override define foo
bar
endef
最后我們來看一個(gè)例子:
# sample Makefile
EXEF = foo
override CFLAGS += -Wall –g
.PHONY : all debug test
all : $(EXEF)
foo : foo.c
………..
………..
$(EXEF) : debug.h
$(CC) $(CFLAGS) $(addsuffix .c,$@) –o $@
debug :
@echo ”CFLAGS = $(CFLAGS)”
執(zhí)行:make CFLAGS=-O2 將顯式結(jié)果為編譯“foo”的過程是“cc –O2 –Wall –g foo.c –o
foo”。執(zhí)行“make CFLAGS=-O2 debug”可以查看到變量“CFLAGS”的值為“–O2 –Wall
–g”。另外,這個(gè)例子中,如果把變量“CFLAGS”之前的指示符“override”去掉以后使用相同的命令將得到不同的結(jié)果。大家試試看!
6.8 多行定義
定義變量的另外一種方式是使用“define”指示符。它定義一個(gè)包含多行字符串的變量,我們就是利用它的這個(gè)特點(diǎn)實(shí)現(xiàn)一個(gè)完整命令包的定義。使用“define”定義的命令包可以作為“eval”函數(shù)的參數(shù)來使用。
本文的前些章節(jié)已經(jīng)不止一次的提到并使用了“define”。相信大家已經(jīng)有所了解。本節(jié)就“define”定義變量從以下幾個(gè)方面來討論:
1.
“define”定義變量的語法格式:以指示符“define”開始,“endif”結(jié)束,之間的所有內(nèi)容就是所定義變量的值。所要定義的變量名字指示符
“define”的同一行之后;指示符所在行的下一行開始一直到“end”所在行的上一行之間的若干行,是變量的值定義。
define two-lines
echo foo
echo $(bar)
endef
如果將變量“two-lines”作為命令包執(zhí)行時(shí),其相當(dāng)于:
two-lines = echo foo; echo $(bar)
我想大家對這個(gè)命令的執(zhí)行應(yīng)該是比較熟悉的。它把變量“two-lines”的值作為一個(gè)完整的shell命令行來處理(是使用分號(hào)“;”分開的在同一行
中的兩個(gè)命令而不是作為兩個(gè)命令行來處理),保證了變量完整。(關(guān)于完整命令行的執(zhí)行可參考 5.2 命令的執(zhí)行 一節(jié))
2.
變量的風(fēng)格:使用“define”定義的變量和使用“=”定義的變量一樣,屬于“遞歸展開”式的變量,兩者只是在語法上不同。因此“define”所定義
的變量值中,對其它變量或者函數(shù)引用不會(huì)在定義時(shí)替換展開,其展開是在“define”定義的變量被引用時(shí)進(jìn)行的。
3. 可以套嵌引用。因?yàn)槭沁f歸展開式變量,所以在存在嵌套引用時(shí)“$(x)”將是變量的值的一部分。
4. 變量值中可以包含:換行符、空格等特殊符號(hào)(注意如果定義中某一行是以[Tab]字符開始時(shí),當(dāng)引用此變量時(shí)這一行會(huì)被作為命令行來處理)。
5. 可以使用“override”在定義時(shí)聲明變量:這樣可以防止變量的值被命令行指定的值替代。例如:
override define two-lines
foo
$(bar)
endef
6.9 系統(tǒng)環(huán)境變量
make在運(yùn)行時(shí),系統(tǒng)的所有環(huán)境變量對它都是可見的。在Makefile中,可以引用任何已定義的系統(tǒng)環(huán)境變量。(這里我們區(qū)分系統(tǒng)環(huán)境變量和make
的環(huán)境變量,系統(tǒng)環(huán)境變量是這個(gè)系統(tǒng)所有用戶所擁有的,而make的環(huán)境變量只是對于make的一次執(zhí)行過程有效,以下正文中出現(xiàn)沒有限制的“環(huán)境變量”
時(shí)默認(rèn)指的是“系統(tǒng)環(huán)境變量”,在特殊的場合我們會(huì)區(qū)分兩者)正因?yàn)槿绱?,我們就可以設(shè)置一個(gè)命名為“CFLAGS”的環(huán)境變量,用它來指定一個(gè)默認(rèn)的編
譯選項(xiàng)。我們就可以在所有的Makefile中直接使用這個(gè)變量來對c源代碼就行編譯。通常這種方式是比較安全的,但是它的前提是大家都明白這個(gè)變量所代
表的含義,沒有人在Makefile中把它作其他的用途。當(dāng)然了,你也可以在你的Makefile中根據(jù)你的需要對它進(jìn)行重新定義。
使用環(huán)境變量我們需要注意以下幾點(diǎn):
1.
在Makefile中對一個(gè)變量的定義或者以make命令行形式對一個(gè)變量的定義,都將覆蓋同名的環(huán)境變量(注意:它并不改變系統(tǒng)環(huán)境變量定義,被修改的
環(huán)境變量只在make執(zhí)行過程有效)。而make使用“-e”參數(shù)時(shí),Makefile和命令行定義的變量不會(huì)覆蓋同名的環(huán)境變量,make將使用系統(tǒng)環(huán)
境變量中這些變量的定義值。
2.
make的遞歸調(diào)用中,所有的系統(tǒng)環(huán)境變量會(huì)被傳遞給下一級(jí)make。默認(rèn)情況下,只有環(huán)境變量和通過命令行方式定義的變量才會(huì)被傳遞給子make進(jìn)程。
在Makefile中定義的普通變量需要傳遞給子make時(shí)需要使用“export”指示符來對它聲明。
3.
一個(gè)比較特殊的是環(huán)將變量“SHELL”。在系統(tǒng)中這個(gè)環(huán)境變量的用途是用來指定用戶和系統(tǒng)的交互接口,顯然對于make是不合適的。因此在make的執(zhí)
行環(huán)境變量“SHELL”的取值不使用同名的環(huán)境變量定義,而默認(rèn)使用“/bin/sh”作為它的命令行解釋程序(make執(zhí)行之前設(shè)置它為
“/bin/sh”)。
這里不推薦使用環(huán)境變量來完成普通變量的工作,特別是在make的遞歸調(diào)用中。任何一個(gè)環(huán)境變量的錯(cuò)誤定義都對系統(tǒng)上的所有make產(chǎn)生影響,甚至是毀壞
性的。因?yàn)榄h(huán)境變量具有全局的特征。所以盡量不要污染環(huán)境變量,造成環(huán)境變量名字污染。我想大多數(shù)系統(tǒng)管理員都明白環(huán)境變量對系統(tǒng)是多么的重要。
我們來看一個(gè)例子,結(jié)束本節(jié)。假如我們的機(jī)器名為“server-cc”;我們的Makefile內(nèi)容如下:
# sample Makefile
HOSTNAME = server-http
…………
…………
.PHONY : debug
debug :
@echo “hostname is : $( HOSTNAME)”
@echo “shell is $(SHELL)”
1. 執(zhí)行“make debug”將顯示:
hostname is : server-http
shell is /bin/sh
2. 執(zhí)行“make –e debug”;將顯示:
hostname is : server-cc
shell is /bin/sh
3. 執(zhí)行“make –e HOSTNAEM=server-ftp”;將顯示:
hostname is : server-ftp
shell is /bin/sh
記住:除非必須,否則在你的Makefile中不要重置環(huán)境變量“SHELL”的值。因?yàn)橐粋€(gè)不正確的命令行解釋程序可能會(huì)導(dǎo)致規(guī)則定義的命令執(zhí)行失敗,甚至是無法執(zhí)行!當(dāng)需要重置它時(shí),必須有充分的理由和配套的規(guī)則命令來適應(yīng)這個(gè)新指定的命令行解釋程序。
6.10目標(biāo)指定變量
在Makefile中定義一個(gè)變量,這個(gè)變量對此Makefile的所有規(guī)則都是有效的。它就像是一個(gè)“全局的”變量(僅限于定義它的那個(gè)
Makefile中的所有規(guī)則,如果需要對其它的Makefile中的規(guī)則有效,就需要使用“export”對它進(jìn)行聲明)。當(dāng)然“自動(dòng)化變量”除外。
另外一個(gè)特殊的變量就是所謂的“目標(biāo)指定變量(Target-specific
Variable)”。此特性允許對于相同變量根據(jù)目標(biāo)指定不同的值,有點(diǎn)類似于自動(dòng)化變量。目標(biāo)指定的變量值只在指定它的目標(biāo)的上下文中有效,對于其他
的目標(biāo)不產(chǎn)生影響。就是說目標(biāo)指定的變量具有“局部性”。
設(shè)置一個(gè)目標(biāo)指定變量的語法為:
TARGET ... : VARIABLE-ASSIGNMENT
或者:
TARGET ... : override VARIABLE-ASSIGNMENT
一個(gè)多目標(biāo)指定的變量的作用域是所有這些目標(biāo)的上下文,它包括了和這個(gè)目標(biāo)相關(guān)的所有執(zhí)行過程。
目標(biāo)指定變量的一些特點(diǎn):
1. “VARIABLE-ASSIGNMENT”可以使用任何一個(gè)有效的賦值方式,“=”(遞歸)、“:=”(靜態(tài))、“+=”(追加)或者“?=”(條件)。
2.
使用目標(biāo)指定變量值時(shí),目標(biāo)指定的變量值不會(huì)影響同名的那個(gè)全局變量的值。就是說目標(biāo)指定一個(gè)變量值時(shí),如果在Makefile中之前已經(jīng)存在此變量的定
義(非目標(biāo)指定的),那么對于其它目標(biāo)全局變量的值沒有變化。變量值的改變只對指定的這些目標(biāo)可見。
3.
目標(biāo)指定變量和普通變量具有相同的優(yōu)先級(jí)。就是說,當(dāng)我們使用make命令行的方式定義變量時(shí),命令行中的定義將替代目標(biāo)指定的同名變量定義(和普通的變
量一樣會(huì)被覆蓋)。另外當(dāng)使用make的“-e”選項(xiàng)時(shí),同名的環(huán)境變量也將覆蓋目標(biāo)指定的變量定義。因此為了防止目標(biāo)指定的變量定義被覆蓋,可以使用第
二種格式,使用指示符“override”對目標(biāo)指定的變量進(jìn)行聲明。
4. 目標(biāo)指定的變量和同名的全局變量屬于兩個(gè)不同的變量,它們在定義的風(fēng)格(遞歸展開式和直接展開式)上可以不同。
5. 目標(biāo)指定的變量變量會(huì)作用到由這個(gè)目標(biāo)所引發(fā)的所有的規(guī)則中去。例如:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
這個(gè)例子中,無論Makefile中的全局變量“CFLAGS”的定義是什么。對于目標(biāo)“prog”以及其所引發(fā)的所有(包含目標(biāo)為“prog.o”、“foo.o”和“bar.o”的所有規(guī)則)規(guī)則,變量“CFLAGS”值都是“-g”。
使用目標(biāo)指定變量我們可以在Makefile實(shí)現(xiàn),對于不同的目標(biāo)文件使用不同的編譯參數(shù)??匆粋€(gè)例子:
# sample Makefile
CUR_DIR = $(shell pwd)
INCS := $(CUR_DIR)/include
CFLAGS := -Wall –I$(INCS)
EXEF := foo bar
.PHONY : all clean
all : $(EXEF)
foo : foo.c
foo : CFLAGS+=-O2
bar : bar.c
bar : CFLAGS+=-g
………..
………..
$(EXEF) : debug.h
$(CC) $(CFLAGS) $(addsuffix .c,$@) –o $@
clean :
$(RM) *.o *.d $(EXES)
這個(gè)Makefile文件實(shí)現(xiàn)了在編譯程序“foo”使用優(yōu)化選項(xiàng)“-O2”但不使用調(diào)試選項(xiàng)“-g”,而在編譯“bar”時(shí)采用了“-g”但沒有“-O2”。這就是目標(biāo)指定變量的靈活之處。目標(biāo)指定變量的其它特性大家可以修改這個(gè)簡單的Makefile來進(jìn)行驗(yàn)證!
6.11模式指定變量
GNU make中,除了支持上一節(jié)所討論的模式指定變量之外(參考上一節(jié)),還支持另外一種方式:模式指定變量(Pattern-specific
Variable)。使用目標(biāo)定變量定義時(shí),此變量被定義在某個(gè)具體目標(biāo)和由它所引發(fā)的規(guī)則的目標(biāo)上。而模式指定變量定義是將一個(gè)變量值指定到所有符合特
定模式的目標(biāo)上去。對于同一個(gè)變量如果使用追加方式,通常一個(gè)目標(biāo)的局部變量值的順序是:(為所有規(guī)則定義的全局值)+(引發(fā)它所在規(guī)則被執(zhí)行的目標(biāo)所指
定值)+(它所符合的模式指定值)+(此目標(biāo)所指定的值)。這個(gè)大家也不需要深入了解。
設(shè)置一個(gè)模式指定變量的語法和設(shè)置目標(biāo)變量的語法相似:
PATTERN ... : VARIABLE-ASSIGNMENT
或者:
PATTERN ... : override VARIABLE-ASSIGNMENT
和目標(biāo)指定變量語法的唯一區(qū)別就是:這里的目標(biāo)是一個(gè)或者多個(gè)“模式”目標(biāo)(包含模式字符“%”)。例如我們可以為所有的.o文件指定變量“CFLAGS”的值:
%.o : CFLAGS += -O
它指定了所有.o文件的編譯選項(xiàng)包含“-O”選項(xiàng),不改變對其它類型文件的編譯選項(xiàng)。
需要說明的是:在使用模式指定的變量定義時(shí)。目標(biāo)文件一般除了模式字符(%)以外需要包含某種文件名的特征字符(例如:“a%”、“%.o”、“%.a”等)。當(dāng)單獨(dú)使用“%”作為目標(biāo)時(shí),指定的變量會(huì)對任何類型的目標(biāo)文件都有效 |
|