- 论坛徽章:
- 0
|
Makefile中的变量
在Makefile中,变量就是一个名字(像是C语言中的宏),代表一个文本字符串(变量的值)。在Makefile的目标、依赖、命令中引用一个变量的地方,变量会被它的值所取代(与C语言中宏引用的方式相同,因此其他版本的make也把变量称之为“宏”)。
当我们定义了一个变量之后,就可以在Makefile的很多地方使用这个变量。变量的引用方式是:使用“$(VARIABLE_NAME)”或者“${ VARIABLE_NAME }”来引用一个变量的定义。
美元符号“$”在Makefile中有特殊的含义,所有在命令或者文件名中使用“$”时需要用两个美元符号“$$”来表示。
对一个变量的引用可以在Makefile的任何上下文中,目标、依赖、命令、绝大多数指示符和新变量的赋值中。
变量引用的展开过程是严格的文本替换过程,就是说变量值的字符串被精确的展开在此变量被引用的地方。
Makefile中在对一些简单变量的应用,我们也可以不使用“()”和“{}”来标记变量名,而直接使用“$x”的格式来实现,此种用法仅限于变量名为单字符的情况。另外自动化变量也使用这种格式。
两种变量定义(赋值)
变量的这两种不同的风格的区别在于:1. 定义方式;2. 展开时机。
一 递归展开式变量
这一类型的变量的定义是通过“=”或者使用指示符“define”定义的变量。
这种变量的引用,在引用的地方是严格的文本替换过程,此变量值的字符串原模原样的出现在引用它的地方。
如果此变量定义中存在对其他变量的引用,这些被引用的变量会在它被展开的同时被展开。就是说在变量定义时,变量值中对其他变量的引用不会被替换展开,而是,变量在引用它的地方进行替换展开的同时,它所引用的其它变量才会被替换展开。
看一个例子:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
执行“make”将会打印出“Huh?”。整个变量的替换过程时这样的:首先“$(foo)”被替换为“$(bar)”,接下来“$(bar)”被替换为“$(ugh)”,最后“$(ugh)”被替换为“Hug?”。整个替换的过程是在执行“echo $(foo)”是进行的。
其优点是: 这种类型变量的定义时,可以引用其它的之前没有定义的变量(可能在后续部分定义,或者是通过make的命令行选项传递的变量)。看一个这样的例子:
CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar
“CFLAGS”会在命令中被展开为“-Ifoo -Ibar -O”。我们可以看到在“CFLAGS”定义中使用到了之后定义的变量“include_dirs”。
其缺点是:
(1). 使用此风格的变量定义,可能会由于出现变量的递归定义而导致make陷入到无限的变量展开过程中,最终使make执行失败。
CFLAGS = $(CFLAGS) –O
它将会导致make进入对变量“CFLAGS”的无限展过程中去(这种定义就是变量的递归定义)。
x = $(y)
y = $(x) $(z)
这种情况下同样会导致make陷入到无限的变量展开过程中。
(2). 这种风格变量的定义中如果使引用了某一个函数,那么函数总会在其被引用的地方被执行。
二 直接展开式变量
这种风格的变量使用“:=”来定义变量。
在使用“:=”定义变量时,变量值中对另外变量的引用或者函数的引用在定义时被展开(对变量进行替换)。所以在变量被定义以后就是一个实际所需要定义的文本串,其中不再包含任何对其它变量的引用。
CFLAGS := $(include_dirs) -O
include_dirs := -Ifoo -Ibar
由于在变量“include_dirs”的定义出现在“CFLAGS”定义之后。因此在“CFLAGS”的定义中,“include_dirs”的值为空。“CFLAGS”的值为“-O”而不是“-Ifoo -Ibar -O”。
需要注意的是对于不包含尾空格的变量的定义,就不能随便使用几个空格之后,在同行中放置它的注释内容。这是千万需要注意的。
例如下边的做法就是不正确的:
dir := /foo/bar #directory to put the frobs in
变量“dir”的值是“/foo/bar ”(后面有4个空格)。
注释内容推荐书写在独立的一行或者多行,这样就可以防止出现这种意外情况的发生。
三 “?=”操作符
GNU make中,有一个被称为条件赋值的赋值操作符“?=”。
被称为条件赋值是因为:只有此变量在之前没有赋值的情况下才会对这个变量进行赋值。
四 变量的替换引用
格式为“$(VAR:A=B)”(或者“${VAR:A=B}”),意思是,将变量“VAR”中所有“A”字符结尾的字替换为“B”结尾的字。“结尾”的含义是空格之前(变量值的多个字以空格分开)。而对于变量其它部分的“A”字符不进行替换。
例如:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
在这个定义中,变量“bar”的值就为“a.c b.c c.c”。使用变量的替换引用将变量“foo”以空格分开的值中的所有的字的尾字符“o”替换为“c”,其他部分不变。而且如果在变量“foo”中如果存在“o.o”时,那么变量“bar”的值为“a.c b.c c.c o.c”而不是“a.c b.c c.c c.c”。这一点需要明确。
另外一种引用替换的技术使用功能更强大的“patsubst”函数的所有功能。例如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
这个例子同样使变量“bar”的值为“a.c b.c c.c”。
五 变量的套嵌引用
一个变量名(文本串)之中可以包含对其它变量的引用。这种情况我们称之为“变量的套嵌引用”或者“计算的变量名”。
例如:
x = y
y = z
a := $($(x))
这个例子中,最终定义了“a”的值为“z”。来看一下变量的引用过程:首先最里边的变量引用“$(x)”被替换为变量名“y”(就是“$($(x))”被替换为了“$(y)”),之后“$(y)”被替换为“z”(就是a := z)。
看一个这样一个例子:
x = $(y)
y = z
z = Hello
a := $($(x))
这里的$($(x))被替换成了$($(y)),因为$(y)值是“z”,所以,最终结果是:a:=$(z),也就是“Hello”。
递归变量的套嵌引用过程,也可以包含变量的修改引用和函数调用。看下边的例子,使用了make的文本处理函数:
x = variable1
variable2 := Hello
y = $(subst 1,2,$(x))
z = y
a := $($($(z)))
此例同样的实现“a:=Hello”。“$($($(z)))”替换为“$($(y))”,之后再次被替换为“$($(subst 1,2,$(x)))”(“$(x)”的值是“variable1”,所以有“$($(subst 1,2,$(variable1)))”)。函数处理之后为“$(variable2)” 之后对它在进行替换展开。最终,变量“a”的值就是“Hello”。
计算变量的名字可以由一个或者多个变量引用同时加上字符串混合组成。例如:
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))
这个例子实现对变量“dirs”的定义,它的可能取值为“a_dirs”、“1_dirs”、“a_files”或者“a_files”四个值其中之一,具体依赖于“use_a”和“use_dirs”的定义。
六 如何设置变量
makefile中变量的设置(也可以称之为定义)是通过“=”(递归方式)或者“:=”(静态方式)来实现的。
定义一个变量时需要明确以下几点:
(1)变量名之中可以包含函数或者其它变量的引用,make在读入此行时根据已定义情况进行替换展开而产生实际的变量名。
(2)变量的定义值在长度上没有限制。变量定义较长时,一个好的做法就是将比较长的行分多个行来书写,除最后一行外行与行之间使用反斜杠(\)连接,表示一个完整的行。
(3)当引用一个没有定义的变量时,make默认它的值为空。
(4)一些特殊的变量在make中有内嵌固定的值,不过这些变量允许我们在Makefile中显式得重新给它赋值。
六 追加变量值
我们可以在开始给变量定义一个基本的值,后续可以不断地根据需要给它增加一些必要值。在Makefile中使用“+=”(追加方式)来实现对一个变量值的追加操作。
参考文献:
chinesegnumake-v3.80.pdf.译者:徐海兵.2004-09-11.
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/59740/showart_467802.html |
|