5.0 使用变量
# 使用变量
在 Makefile 中的定义的变量,就像是 C/C++ 语言中的宏一样,他代表了一个文本字串,在 Makefile 中执行的时候其会自动原模原样地展开在所使用的地方。其与 C/C++ 所不同的是,** 可以在 Makefile 中改变其值。** 在 Makefile 中,变量可以使用在 “目标”,“依赖目标”, “命令” 或是 Makefile 的其它部分中。
# 变量的基础
变量在声明时需要给予初值,而在使用时,需要给在变量名前加上 $ 符号,但最好用小括号 () 或是大括号 {} 把变量给包括起来。如果要使用真实的 $ 字符,那么你需要用 $$ 来表示。
# 变量中的变量 (通常使用方法二)
用变量来定义变量的方法一:
简单的使用 = 号,在 = 左侧是变量,右侧是变量的值,右侧变量的值可以定义在文件的任何一处,也就是说,右侧中的变量不一定非要是已定义好的值,其也可以使用后面定义的值。如:
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:
echo $(foo)
2
3
4
5
6
我们执行 “make all” 将会打出变量 $(foo) 的值是 Huh? ( $(foo) 的值是 $(bar) , $(bar) 的值是 $(ugh) , $(ugh) 的值是 Huh? )可见,变量是可以使用后面的变量来定义的。
这种方法的优点:可以把变量的真实值推到后面来定义,如
CFLAGS = $(include_dirs) -O
include_dirs = -Ifoo -Ibar
2
当 CFLAGS 在命令中被展开时,会是 -Ifoo -Ibar -O 。
这种方法的缺点:但这种形式也有不好的地方,那就是递归定义,如:
CFLAGS = $(CFLAGS) -O
用变量来定义变量的方法二:
为了避免上面的这种方法,我们可以使用 make 中的另一种用变量来定义变量的方法。这种方法使用的是 := 操作符,如:
x := foo
y := $(x) bar
x := later
2
3
其等价于:
y := foo bar
x := later
2
优点:这种方法,前面的变量不能使用后面的变量,只能使用前面已定义好了的变量。
使用变量中的变量来获得 space:
nullstring :=
space := $(nullstring) # end of the line
2
nullstring 是一个 Empty 变量,其中什么也没有,而我们的 space 的值是一个空格。因为在操作符的右边是很难描述一个空格的,这里采用的技术很管用,先用一个 Empty 变量来标明变量的值开始了,而后面采用 “#” 注释符来表示变量定义的终止,这样,我们可以定义出其值是一个空格的变量。
请注意这里关于 “#” 的使用,注释符 “#” 的这种特性值得我们注意,如果我们这样定义一个变量:
dir := /foo/bar # directory to put the frobs in
dir 这个变量的值是 “/foo/bar”,后面还跟了 4 个空格,如果我们这样使用这个变量来指定别的目录 ——“$(dir)/file” 那么就完蛋了。
?= : 如果变量没有被定义过,就使用该定义,否则什么也不做。
FOO ?= bar
等价于:
ifeq ($(origin FOO), undefined)
FOO = bar
endif
2
3
# 变量的高级用法
高级用法一:变量值的替换
我们可以替换变量中的共有的部分,其格式是 $(var:a=b) 或是 ${var:a=b} ,其意思是,把变量 “var” 中所有以 “a” 字串 “结尾” 的 “a” 替换成 “b” 字串。这里的 “结尾” 意思是 “空格” 或是 “结束符”。
举例:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
2
这个示例中,我们先定义了一个 $(foo) 变量,而第二行的意思是把 $(foo) 中所有以 .o 字串 “结尾” 全部替换成 .c ,所以我们的 $(bar) 的值就是 “a.c b.c c.c”。
另外一种变量替换的技术是以静态模式(参见前面章节)定义的,如:
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
2
但是这要求变量 $(foo) 中所有的值都要以.o 结尾并且模式中必须包含一个 % 字符。
高级用法二:把变量的值再当成变量
x = y
y = z
a := $($(x))
2
3
# 追加变量
使用 += 操作符给变量追加值:
- 如果变量之前没有定义过,那么,
+=会自动变成= - 如果前面有变量定义,那么
+=会继承于前次操作的赋值符- 如果前一次的是
:=,那么+=会以:=作为其赋值符 - 如果前一次的是
=,那么+=会以=作为其赋值符
(由于前次的赋值符是=,所以+=也会以=来做为赋值,那么岂不会发生变量的递补归定义,这是很不好的,所以 make 会自动为我们解决这个问题,我们不必担心这个问题。)
- 如果前一次的是
举例:
variable := value
variable += more
2
等价于:
variable := value
variable := $(variable) more
2
# override 指令
override 同样是针对于系统环境传入的变量,或是 make 命令行指定的变量。
如果有变量是通常 make 的命令行参数设置的,那么 Makefile 中对这个变量的赋值会被忽略。如果你想在 Makefile 中设置这类参数的值,那么,你可以使用 “override” 指令。其语法是:
override <variable> = <value>
override <variable> := <value>
override <variable> += <more text>
2
3
对于多行的变量定义,我们用 define 指令,在 define 指令前,也同样可以使用 override 指令,如:
override define foo
bar
endef
2
3
# 目标变量
前面我们所讲的在 Makefile 中定义的变量都是 “全局变量”,在整个文件,我们都可以访问这些变量。当然,“自动化变量” 除外,如 $< 等这种类量的自动化变量就属于 “规则型变量”,这种变量的值依赖于规则的目标和依赖目标的定义。
同样可以为某个目标设置局部变量,这种变量被称为 “Target-specific Variable”,它可以和 “全局变量” 同名,因为它的作用范围只在这条规则以及连带规则中,所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值。
语法:
<target ...> : <variable-assignment>
<target ...> : overide <variable-assignment>
2
<variable-assignment> 可以是前面讲过的各种赋值表达式,如 = 、 := 、 += 或是 ?= 。第二个语法是针对于 make 命令行带入的变量,或是系统环境变量。
这个特性非常的有用,** 当我们设置了这样一个变量,这个变量会作用到由这个目标所引发的所有的规则中去。** 如:
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
$(CC) $(CFLAGS) prog.o foo.o bar.o
prog.o : prog.c
$(CC) $(CFLAGS) prog.c
foo.o : foo.c
$(CC) $(CFLAGS) foo.c
bar.o : bar.c
$(CC) $(CFLAGS) bar.c
2
3
4
5
6
7
8
9
10
11
12
在这个示例中,不管全局的 $(CFLAGS) 的值是什么,在 prog 目标,以及其所引发的所有规则中(prog.o foo.o bar.o 的规则), $(CFLAGS) 的值都是 -g
# 模式变量
通过上面的目标变量中,我们知道,变量可以定义在某个目标上。模式变量的好处就是,我们可以给定一种 “模式”,可以把变量定义在符合这种模式的所有目标上。
make 的 “模式” 一般是至少含有一个 % 的,所以,我们可以以如下方式给所有以 .o 结尾的目标定义目标变量:
%.o : CFLAGS = -O
同样,模式变量的语法和 “目标变量” 一样:
<pattern ...> : <variable-assignment>
<pattern ...> : override <variable-assignment>
2
override 同样是针对于系统环境传入的变量,或是 make 命令行指定的变量。