第六章 Makefile中的变量
6 .1 变量的引用
变量的引用方式是:“ $ ( VARIABLE_NAME )”或者“ ${ VARIABLE_NAME } ”来引用一个变量的定义。 美元符号“ $ ”在 Makefile 中有特殊的含义,所有在命令或者文件名中使用“ $ ”时需要用两个美元符号“ $$ ”来表示。
objects = program.o foo.o utils.o
program : $(objects)
cc -o program $(objects)
$(objects) : defs.h
6 .2 两种变量的定义
1) 递归展开式变量
在引用的地方是严格的文本替换过程
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
2) 直接展开式变量
变量值中对其他量或者函数的引用在定义变量时被展开
x := foo
y := $(x) bar
x := later
3) 如何定义一个空格
nullstring :=
space := $(nullstring) # end of the line
这里,变量“ space ”就表示一个空格。在“ space ”定义行中的注释使得我们的目的更清晰(明确地描述一个空格字符比较困难),注释和变量引用“ $(nullstring) ”之间存在一个空格。
dir := /foo/bar # directory to put the frobs in
变量“ dir ”的值是“ /foo/bar ”(后面有 4 个空格)
4) “ ?= ”操作符
FOO ?= bar
含义是:如果变量“ FOO ”在之前没有定义,就给它赋值“ bar ”。否则不改变它的值。
6 .3 变量的高级用法
1) 变量的替换引用
格式为“ $(VAR:A=B) ”(或者“ ${VAR:A=B} ”),意思是,替换变量“ VAR ”中所有“ A ”字符结尾的字为“ B ”结尾的字。
foo := a.o b.o c.o
bar := $(foo:.o=.c)
在这个定义中,变量“ bar ”的值就为“ a.c b.c c.c ”。
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
2) 变量的套嵌引用
一个变量名(文本串)之中可以包含对其它变量的引用。
x = y
y = z
z = u
a := $($($(x)))
这个例子最终是定义了“ a ”的值为“ u ”。
使用嵌套的变量引用的唯一限制是,不能通过指定部分需要调用的函数名称(调用的函数包括了函数名本身和执行的参数)来实现对这个函数的调用。这是因为套嵌引用在展开之前已经完成了对函数名的识别测试。
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 ”的执行结果赋值变量“ foo ”。在这里使用了套嵌引用方式来实现,但是本例的结果是:变量“ foo ”的值为字符串“ sort a d b g q c ”或者“ strip a d g q c ”。这是目前版本的 make 在处理套嵌变量引用时的限制。
6 .4 变量取值
一个变量可以通过以下几种方式来获得值:
² 在运行 make 时通过命令行选项来取代一个已定义的变量值。
² 在 makefile 文件中通过赋值的方式或者使用“ define ”来为一个变量赋值。
² 将变量设置为系统环境变量。所有系统环境变量都可以被 make 使用。
² 自动化变量,在不同的规则中自动化变量会被赋予不同的值。它们每一个都有单一的习惯性用法。
² 一些变量具有固定的值。
6 .5 追加变量值
1. 如果被追加值的变量之前没有定义,那么,“ += ”会自动变成“ = ”,此变量就被定义为一个递归展开式的变量。如果之前存在这个变量定义,那么“ += ”就继承之前定义时的变量风格。
2. 直接展开式变量的追加过程:变量使用“ := ”定义,之后“ += ”操作将会首先替换展开之前此变量的值,尔后在末尾添加需要追加的值,并使用“ := ”重新给此变量赋值。实际的过程像下边那样:
variable := value
variable += more
就是:
variable := value
variable := $(variable) more
3. 递归展开式变量的追加过程:一个变量使用“ = ”定义,之后“ += ”操作时不对之前此变量值中的任何引用进行替换展开,而是按照文本的扩展方式(之前等号右边的文本未发生变化)替换,尔后在末尾添加需要追加的值,并使用“ = ”给此变量重新赋值。实际的过程和上边的相类似:
variable = value
variable += more
相当于:
temp = value
variable = $(temp) more
当然了,上边的过程并不会存在中间变量:“ temp ”,使用它的目的时方便描述。
6 .6 override指示符
如果不希望命令行指定的变量值替代在 Makefile 中的变量定义,那么我们需要在 Makefile 中使用指示符“ override ”来对这个变量进行声明。
像下边那样:
override VARIABLE = VALUE
或者:
override VARIABLE := VALUE
也可以对变量使用追加方式:
override VARIABLE += MORE TEXT
对于追加方式需要说明的是:变量在定义时使用了“ override ”,则后续对它值进行追加时,也需要使用带有“ override ”指示符的追加方式。否则对此变量值的追加不会生效。
其存在的目的是为了使用户可以改变或者追加那些使用 make 的命令行指定的变量的定义。 我们可能会有这样的需求;可以通过命令行来指定一些附加的编译参数,对一些通用的参数或者必需的编译参数在 Makefile 中指定,而在命令行中指定一些特殊的参数。对于这种需求,我们就需要使用指示符“ override ”来实现。
6 .7 define指示符
1. “ define ”定义变量的语法格式:以指示符“ define ”开始,“ endef ”结束,之间的所有内容就是所定义变量的值。所要定义的变量名字和指示符“ define ”在同一行,使用空格分开;指示符所在行的下一行开始一直到“ endif ”所在行的上一行之间的若干行,是变量值。
define two-lines
echo foo
echo $(bar)
endef
如果将变量“ two-lines ”作为命令包执行时,其相当于:
two-lines = echo foo; echo $(bar)
它把变量“ two-lines ”的值作为一个完整的 shell 命令行来处理 ,保证了变量完整。
2. 变量的风格:使用“ define ”定义的变量和使用“ = ”定义的变量一样,属于“递归展开”式的变量,两者只是在语法上不同。因此“ define ”所定义的变量值中,对其它变量或者函数引用不会在定义变量时进行替换展开,其展开是在“ define ”定义的变量被展开的同时完成的。
6 .8 系统环境变量
使用环境变量需要注意以下几点:
1. 在 Makefile 中对一个变量的定义或者以 make 命令行形式对一个变量的定义,都将覆盖同名的环境变量(注意:它并不改变系统环境变量定义,被修改的环境变量只在 make 执行过程有效)。而 make 使用“ -e ”参数时, Makefile 和命令行定义的变量不会覆盖同名的环境变量, make 将使用系统环境变量中这些变量的定义值。
2. make 的递归调用中,所有的系统环境变量会被传递给下一级 make 。默认情况下,只有环境变量和通过命令行方式定义的变量才会被传递给子 make 进程。 在 Makefile 中定义的普通变量需要传递给子 make 时需要使用“ export ”指示符来对它声明。
3. 一个比较特殊的是环将变量“ SHELL ”。在系统中这个环境变量的用途是用来指定用户和系统的交互接口,显然对于 make 是不合适的。因此 make 的执行环境变量“ SHELL ”没有使用同名的环境变量定义,而是“ /bin/sh ”。 make 默认“ /bin/sh ”作为它的命令行解释程序( make 在执行之前将变量“ SHELL ”设置为“ /bin/sh ”)。
6 .9 目标指定变量
目标指定的变量值只在指定它的目标的上下文中有效,对于其他的目标没有影响。
设置一个目标指定变量的语法为:
TARGET ... : VARIABLE-ASSIGNMENT
或者:
TARGET ... : override VARIABLE-ASSIGNMENT
使用目标指定变量可以实现对于不同的目标文件使用不同的编译参数。
目标指定的变量变量会作用到由这个目标所引发的所有的规则中去。
prog : CFLAGS = -g
prog : prog.o foo.o bar.o
6 .10 模式指定变量
而模式指定变量定义是将一个变量值指定到所有符合此模式的目标上。 对于同一个变量如果使用追加方式,通常对于一个目标,它的局部变量值是:(为所有规则定义的全局值 ) + (引发它所在规则被执行的目标所指定的值 ) + (它所符合的模式指定值 ) + (此目标所指定的值 )。
设置一个模式指定变量的语法和设置目标变量的语法相似:
PATTERN ... :
VARIABLE-ASSIGNMENT
或者:
PATTERN ... :
override VARIABLE-ASSIGNMENT
例如我们可以为所有的 .o 文件指定变量“ CFLAGS ”的值:
%.o : CFLAGS += -O