makefile的变量定义有三种方式
1. 立即赋值 a:=b
2. 延迟赋值 a=b
3. 条件赋值 a?=b
4. 附加赋值 a+=b
它们之间的区别是,
第一种方式,会立即计算b的值,并赋值给a;
第二种方式,相当于C++和java的引用。如果后面b的值改变了,那么a的值也会改变;
第三种方式,如果a没有定义,则相当于a=b ,否则不执行任何操作;
第四种方式,将b的值添加到a原有的值后面,再赋值给a。
$(var) //表示取变量var的值,记得当变量名多于一个字符时,使用”()”.
define function
xxx // 具体的内容
endef
需要指出的是,虽然形式上类似函数,但是实际内容只是字符串替换,这与C中的宏函数是一样的。
makefile的解析分为两个阶段,第一阶段生成规则和依赖关系,第二阶段执行规则;
只有立即展开的变量会在第一阶段计算,而延后展开的变量会在第二阶段计算。
立即与延后展开的规则 |
||
表达式 |
何时扩展a |
何时扩展b |
a=b |
立即 |
延后 |
a?=b |
立即 |
延后 |
a:=b |
立即 |
立即 |
a+=b |
立即 |
取决于之间a的定义方式 |
define a |
立即 |
延后 |
a : b |
立即 |
立即 |
有两个特点
1. 做两次变量展开;以$(eval var),首先会对var做一次变量展开,然后对展开后的结果,再执行一次变量展开。详细的描述请各位参考链接
2. $(eval var)返回为空。这个特性被大量使用于宏定义define中。
看下面这个例子:
define import_target
include $(1)
_PREFIXID := $(if$2, $2, TARGET)
$(_ PREFIXID)
endef
按上面定义的宏,当去计算a:=$(call import_target)时,几乎总是会报错。
原因是宏定义只是简单的做字符串替换,一经替换后,原来看起来是一行语句,一下子就变成多行,从而导致makefile解析错误。
于是只能使用“\”将各个语句连接为一行。
define import_target
include $(1) \
_PREFIXID := $(if $2, $2,TARGET) \
$(_ PREFIXID)
endef
这样做相当于
include $(1) _PREFIXID := $(if $2, $2, TARGET) $(_ PREFIXID)
显然,肯定还是报解析错误。
最终解决方案,将各个子语句使用$(eval )包裹,
define import_target
$(eval include $(1)) \
$(eval _PREFIXID := $(if $2,$2, TARGET)) \
$(_ PREFIXID)
endef
解析时,makefile先对$(1)做展开,假设结果为xxx,这是第一次;然后执行include xxx,这是第二次展开。执行完后,整个$(eval include $(1))表达式返回值为空。这样解析错误解决了,而且 import_target的返回结果又正好是_ PREFIXID的值。
格式:$(call func,param1, param2,..)
实现自定义函数调用。func使用define定义。 call会自动将param1的值赋值给func中的$1变量,将param2赋值给$2变量,依次类推。
变量赋值a:= b, 不会将b前面的空格赋值给a
大部分函数调用,特别是$(call func, param) 如果参数前面有空格,则会将空格连同参数一起传入。因此要特别小心。
使用$(strip var)是个好习惯,可以避免不小心引入前置或者后置的空格导致的问题。