变量在读取makefile的过程中被扩展,除了:recipes,=定义变量的右边,define语法定义变量的body。
变量名字不能包含:: # = whitespace,这4种字符,其它都可以。
变量名字是大小写敏感的。
为了替换一个变量的值,有两种写法:$(foo) 或 ${foo}
variable references可以发生在任何context:targets,prerequisites,recipes,most directives,new variable values。
$跟一个非 $ ( { 字符,那么这个单字符被解析为变量名,如$x,但是这种方法不推荐。
recursively expanded variable 递归扩展变量。
使用=,或者define定义的变量
simply expanded variables 简单扩展变量。
使用:=,或者::=定义,这二者是一样的,后者是POSIX的标准。
递归扩展变量的值是按字面量安装的(installed verbatim),如果它引用其它变量,当这个变量被引用时,都会扩展这些变量。
foo = $(bar)
bar = $(ugh)
ugh = Huh?
all:;echo $(foo)
输出Huh?,解析过程:$(foo)扩展成$(bar),$(bar)被扩展成$(ugh),然后被扩展成Huh?
递归扩展变量如果引用它自己,将是一个死循环,make将给出出错信息:
1 2 3 4 5 6 7 8 9 10 | $ cat Makefile a := 2 a = $(a) 3 all: $(a) echo all $ make Makefile:3: *** Recursive variable 'a' references itself (eventually). Stop. |
由于是按字面量安装的,上述依赖使用$(a)时,a的值已经是 "$(a) 3"了,因此这里会导致死循环。
递归扩展变量的缺点是引用的时候每次都会扩展。
简单扩展变量,对于所有引用的其它变量,只会扫描扩展一次,且变量的值是简单扩展变量处变量的值。
个人观点:
递归扩展,简单扩展和deferred,immediate是两个概念,但是又有一定关联。
后者指变量引用的时候,是立即的,还是延后的,这依赖与变量使用的context,比如对于target和prerequisite,变量引用是立即的。
前者指扩展方式。
格式 $(var:a=b) 或 ${var:a=b)
将var中的end of word的a替换为b。
at the end of a word,意味着a要么出现空白字符的前面,或者在值的最后,其它地方的不会被替换。
得到变量的一个变种,一个例子足以说明问题:
foo := a.o b.o c.o
bar := $(foo:.o=.c)
上述a和b不能含%,也支持含%的,这是Another type of substitution reference
bar := $(foo:%.o=%.c)
Variables may be referenced inside the name of a variable. This is called a computed
variable name or a nested variable reference
x = y
y = z
a := $($(x))
References to recursively-expanded variables within a variable name are re-expanded in
the usual fashion
变量名中引用recursively-expanded variables是按正常的方法重新扩展
1 2 3 4 | x = $(y) y = z z = Hello a := $($(x)) |
$($(x))成为$($(y)),成为$(z),最终扩展为Hello
环境变量中的变量,成为make变量
变量名跟一个赋值符 = := ::= ,后面跟变量的值
objects = main.o foo.o bar.o utils.o
变量名周边的和等号后边的whitespace被忽略。
没有设置的变量,默认是空字符串。
如果你想设置一个变量的值,除非它还没有被设置,可以使用 ?=
shell assignment operator !=
file_list != find . -name '*.c'
shell执行的结果放到变量中,结尾的换行符被替换为移除,中间的换行符被替换为空格,注意变量是recursively-expanded类型的。
+=
如果变量未定义过,那么+=的类型是recursively-expanded的,否则它和原来的类型相同。
如果一个变量在命令行中设置,那么makefile中的赋值都将被忽略。如果你想改写命令行中的变量,那么可以使用override语法:
override variable = value
override variable := value
override variable += more text
override有最高的优先级,后面没有使用override的赋值都将被忽略。
1 2 3 4 5 6 7 8 9 10 11 | $ cat Makefile override a := 2 a += 3 all: echo "$(a)" $ make echo "2" 2 |
可以看到a的值是2
define语法,它允许换行符出现在变量的值中,主要用在canned sequences of commands和eval函数。
define variable =
echo foo
echo $(bar)
endef
赋值符可以是 = := += 等,赋值符可以省略,默认为=
define可以嵌套
如果你想清除一个变量,把它们定义成empty足够了,它们和未定义变量的扩展结果是一样的。但是如果使用origin那么它和未定义变量有区别。这种情况下,可以使用undefine 语法:
undefine foo
如果你想undefine一个命令行变量
override undefine CFLAGS
环境变量会变转成make变量,但是make中的变量赋值和命令行变量会覆盖环境变量。
-e选项,环境变量覆盖make中赋值的变量。
调用子make时,环境变量会传递给子make。
make中的变量通常是全局的,一个例外是自动变量,另外一个例外是target-specific variable
其作用域,仅在target所在的rule。
语法:
target ... : variable-assignment
可以使用 override, export, private等语法
注意:依赖继承 target-specific variable
GNU make还支持pattern-specific variable
语法:
pattern ... : variable-assignment
pattern是一个% pattern
例子:
%.o : CFLAGS = -O
前面讲到target-specific variable会被依赖继承,如果不想让依赖继承,可以使用private修饰符。
prog: private EXTRA_CFLAGS = -L/usr/local/lib
MAKEFILE_LIST,当前被解析的所有makefile,按照被包含的顺序。
.VARIABLES,扩展成当前定义的所有全局变量