美文网首页程序员
Makefile:一个空格引发的惨案

Makefile:一个空格引发的惨案

作者: Mum_Chen | 来源:发表于2019-10-17 15:15 被阅读0次

    从一个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函数都能被正常的执行.

    相关文章

      网友评论

        本文标题:Makefile:一个空格引发的惨案

        本文链接:https://www.haomeiwen.com/subject/ewlumctx.html