Makefile:一个空格引发的惨案
2019-10-17 本文已影响0人
Mum_Chen
从一个bug说起
最早发现makefile关于空格的"特性"是源于一个bug. 在某个项目中, 我需要大量拷贝相同的变量, 所以实现了一个clone的方法, 用于实现把src_xxx
的值拷贝到dest_xxx
.
我仿照当时的情况写了一个精简的版本. 代码如下:
# makefile
src_int = 10
src_char = "char"
# clone attributes from src to dest.
# arg1: destTarget
# arg2: srcTarget
# arg3: attr_list
define Clone
$(foreach att,$(3), $(eval $(1)_$(att) := $($(2)_$(att))))
endef
$(call Clone,targetA,src,int char)
$(call Clone, targetB, src, int char)
all: targetA targetB
targetA:
@echo "targetA: int=$(targetA_int) char=$(targetA_char)"
targetB:
@echo "targetB: int=$(targetB_int) char=$(targetB_char)"
运行后的结果如下:
$ make all
targetA: int=10 char=char
targetB: int= char=
从结果看到targetA克隆成功了, 但是targetB没有.
为什么会发生这个
其实问题的根源就在于脚本中的这两行:
$(call Clone,targetA,src,int char)
$(call Clone, targetB, src, int char)
targetB的克隆比targetA多了几个空格. Makefile的字符串替换包括空格.
在Clone函数中的$(eval $(1)_$(att) := $($(2)_$(att))))
这一段代码里的
$($(2)_$(att)))
在两种情况下被解析成为了不同变量.
对于targetA而言: $($(2)_$(att)))
被解析为了$(src_$(att))
.
对于targetB而言: $($(2)_$(att)))
被解析为了$( src_$(att))
.
后者比前者多了一个空格, 在makefile的上下文中找不到对应的变量, 所以赋值失败.
当时, 由于习惯使然, 我在逗号后面随手加入了一个空格. 然后我就发现我的构建脚本不能正常运行了.
如何解决
解决方案1: 改变逗号后面加空格的习惯.
然而这种方案过于反人类, 所以我放弃了.(但是修改起来比较快)
考虑到我就是一个写bug的, 不能这么严格的要求自己, 所以我找到了方案2.
解决方案2: 使用strip
函数进行变量的处理.
修改如下:
define Clone
$(foreach att,$(3), $(eval $(1)_$(att) := $($(2)_$(att))))
endef
define CloneFix
$(foreach att,$(3), $(eval $(strip $(1))_$(att) := $($(strip $(2))_$(att))))
endef
附录
完整的代码
src_int = 10
src_char = "char"
# arg1: destTarget
# arg2: srcTarget
# arg3: value_list
define Clone
$(foreach att,$(3), $(eval $(1)_$(att) := $($(2)_$(att))))
endef
define CloneFix
$(foreach att,$(3), $(eval $(strip $(1))_$(att) := $($(strip $(2))_$(att))))
endef
%_print:
@echo "$*: int=$($*_int) char=$($*_char)"
gap:
@echo
# arg1: target list
define Entry
$(foreach target,$(1), $(eval $(target): $(target)_print))
endef
$(call Entry, v1_a v1_b v1_c v1_d)
$(call Clone,v1_a,src,int char) # with not space
$(call Clone,v1_b,src, int char) # space in arg3(param)
$(call Clone,v1_c, src, int char) # space in arg2(src)
$(call Clone, v1_d,src, int char) # space in arg1(dest)
$(call Entry, v2_a v2_b v2_c v2_d)
$(call CloneFix,v2_a,src,int char) # with not space
$(call CloneFix,v2_b,src, int char) # space in arg3(param)
$(call CloneFix,v2_c, src, int char) # space in arg2(src)
$(call CloneFix, v2_d,src, int char) # space in arg1(dest)
.DEFAULT_GOAL := all
all: v1 gap v2
v1: $(addprefix v1_, a b c d)
v2: $(addprefix v2_, a b c d)
运行结果
$ make
v1_a: int=10 char=char
v1_b: int=10 char=char
v1_c: int= char=
v1_d: int=10 char=char
v2_a: int=10 char=char
v2_b: int=10 char=char
v2_c: int=10 char=char
v2_d: int=10 char=char
这个版本不管怎么按空格Clone
函数都能被正常的执行.