如何在Automake脚本中实现GNU Make式的简单变量展开?
GNU Make有两种变量展开风格:递归展开和简单展开。其中VARIABLE:=VALUE是简单展开,定义时就会解析右侧表达式,后续修改原变量不会影响它的值。但直接在Makefile.am中使用这种写法时,Automake生成的Makefile会调整变量定义顺序,导致简单展开的语义失效。
问题示例
直接在Makefile中的表现
运行以下Makefile代码,GNU Make会输出common:
a = common b := $(a) a = A $(info $(b))
因为b是简单展开变量,定义时就取了a当时的值common,后续修改a不影响b。
放入Makefile.am后的表现
把相同代码放到Makefile.am中,Automake生成的Makefile会调整变量顺序,变成:
a = A b := $(a) ... $(info $(b))
此时Make运行会输出A,简单展开的语义被破坏——这是因为Automake会收集所有变量定义,按类型重新排序输出,打乱了原本的定义顺序。
实现简单展开语义的方法
要在Makefile.am中保留原本的变量定义顺序,从而实现真正的简单展开,可以用$(eval ...)包裹变量定义逻辑,让Automake将其作为原始Make代码直接保留,不做顺序调整:
$(eval a = common) $(eval b := $(a)) $(eval a = A) $(info $(b))
这样生成的Makefile会原封不动保留这几行代码,Make运行时会按定义顺序执行,b会被赋值为common,符合预期的简单展开语义。
原理说明
Automake在处理Makefile.am时,会对普通的变量定义进行归类整理,比如把所有递归展开变量(=定义的)放在一起,简单展开变量(:=定义的)放在一起,导致原本的定义顺序被打乱。而$(eval ...)是GNU Make的内置函数,Automake不会解析其内部内容,只会将整个$(eval ...)块原封不动输出到生成的Makefile中,因此能严格保留变量定义的顺序,实现简单展开的语义。
内容的提问来源于stack exchange,提问作者dais

