多行变量,即multiple line variables。多行变量通常定义成recursively expanded variable。
1. 变量定义中的条件语句是变量的一部分,不会按条件语句解析,只有在eval的时候,才解析条件语句。
如下:
$ cat Makefile
define abc =
ifeq (,)
else
endif
endef
$(info $(abc))
all:;
$ make
ifeq (,)
else
endif
make: 'all' is up to date.
可以看到,条件语句是变量的一部分,实际上变量的内容可以是任何字符串,不需要是一个合法的Makefile语句。在引用变量时,Make只会扩展变量和函数,其它内容Make一概不知,一概不管。其它合法的Makefile语句,在执行的eval的时候,才真正生效。
2. eval过程
eval动态解析一段Makefile内容。看如下代码:
================
$ cat Makefile
define compile
all:
echo $(a)
endef
a = 2
$(eval $(compile))
a = 3
$ make
echo 2
2
================
执行结果为2,因为在执行 $(eval $(compile))时,首先对compile进行引用,compile扩展的时候$(a)的值为2,所以eval执行的语句其实为:
all:
echo 2
修改一下,将echo $(a),修改为echo $$(a),则执行结果为3。compile扩展后,eval执行的语句变为:
all:
echo $(a)
此时a的值变成了3,所以最后结果变成为3。
3. 下面这一段代码存在bug
================
$ cat Makefile
define compile
all:
echo compile
endef
define BLD_APP
ifeq ($(compile),)
all:
echo default
else
$(eval $(compile))
endif
endef
$(eval $(BLD_APP))
================
首先,在扩展BLD_APP的时候,ifeq ($(compile),),这条语句存在问题,compile是多行变量,扩展之后,破坏了ifeq的语法,实际的扩展后的内容变成了:
ifeq (all:
echo compile,)
这里可以使用ifeq ($$(compile),),这样在扩展BLD_APP的时候,变成ifeq ($(compile),),再执行eval,就没问题。但是这里执行了变量扩展,如果compile里面有eval等函数,这可能产生副作用。如果不想扩展compile,则使用flavor或者origin函数,更为稳妥。
其次,$(eval $(compile)),这条语句可能产生副作用,它在引用BLD_APP就会执行eval扩展,如果BLD_APP引用多次,那么每次都会执行这条扩展,这可能导致重复定义规则。其实这里,直接使用$(compile)即可,这里不着急执行eval,而只需生成内容,由$(eval $(BLD_APP))的eval来执行这个eval操作。
所以,修复方法如下:
- ifeq ($(compile),)
+ ifeq ($(origin compile),undefined)
- $(eval $(compile))
+ $(compile)
所以,正确理解变量和函数扩展的时机,就能运用自如。