首页 文章

从foreach中包含的目标配方中的文件中读取变量

提问于
浏览
0

我有一个可以创建多个目标的顶级Makefile,我有一个for循环 . 在这个文件中,我试图读取一个文本文件,其版本号仅在子makefile运行时创建 . 我有它工作,但它看起来很丑,所以我认为可能有更好的方法来做到这一点 . 这是顶级Makefile:

T1=_release
T2=_debug
T3=_test


all: bin/mybin_release.bin
debug: bin/mybin_debug.bin
debug: export DBG:=1
test: bin/mybin_test.bin
test: export TST:=1

define build_foo

bin/mybin$$($(1)).bin: bin/abc$$($(1)).bin bin/xyz$$($(1)).bin
    cat $$^ > $$@
    VER=$$$$(cat bin/rev.txt) && mv bin/mybin$$($(1)).bin bin/mybin$$($(1))_$$$${VER}.bin

bin/abc$$($(1)).bin:
    $(MAKE) -C mod1    # <--- rev.txt is produced here

bin/xyz$$($(1)).bin:
    $(MAKE) -C mod2

endef

$(foreach suffix, T1 T2 T3, $(eval $(call build_foo,$(suffix))))

clean:
    rm bin/*.bin bin/*.txt

请注意尝试在 VER 中获取文件内容 . 有没有更好/正确的方法来做到这一点?我不能使用 eval 因为它在开始时运行而 rev.txt 仅在子make运行时才会被创建 . 另外,这是构建多个目标(使用foreach)的一种不错的方法吗?我使用导出的变量来修改子makefile构建的目标 .

1 回答

  • 1

    据我所知,这个 bin/rev.txt 文件和 bin/abcXXX.bin 都是在运行 $(MAKE) -C mod1 时生成的 . 所以,对所有人来说都是一样的 . 关于什么:

    include version.mk
    
    version.mk: bin/rev.txt
        { printf 'VER = '; cat $<; } > $@
    
    bin/rev.txt:
        $(MAKE) -C mod1
    

    演示:

    $ ls
    Makefile
    $ cat Makefile
    include version.mk
    
    all:
        touch $(VER).txt
    
    version.mk: rev.txt
        { printf 'VER = '; cat $<; } > $@
    
    rev.txt:
        echo "1.2.3" > $@
    $ make --quiet
    Makefile:1: version.mk: No such file or directory
    $ ls
    1.2.3.txt  Makefile  rev.txt  version.mk
    

    说明:

    version.mk 是make将要查找的第二个makefile . 根据GNU make documentation, section 3.5

    为此,在阅读所有makefile之后,make会将每个作为目标目标并尝试更新它 . 如果一个makefile有一条规则说明如何更新它(在那个makefile或另一个中找到)或者隐含规则适用于它(参见使用隐式规则),它将在必要时更新 . 在检查完所有的makefile之后,如果实际上已经更改了makefile,make会以一个干净的平板开始并再次读取所有的makefile . (它也会尝试再次更新它们,但通常这不会再次更改它们,因为它们已经是最新的 . )

    注意:在编写makefile时, $(MAKE) -C mod1 将运行几次,这是一种浪费 . 您可以使用GNU make pattern规则的特异性:当它们有多个目标时,make会认为所有目标都是通过一次调用配方生成的 . 例:

    $ cat Makefile
    all: a.bin b.bin c.bin
    
    a.%in b.%in c.%in:
        @echo 'building a.bin b.bin c.bin'
    $ make all
    building a.bin b.bin c.bin
    

    看到?配方仅执行一次以构建3个目标 . 唯一的问题是 % 通配符必须至少匹配一个字符 . 所以,在你的情况下,你可以做类似的事情( % 匹配的字符是 bbin/ ):

    T1        := _release
    T2        := _debug
    T3        := _test
    suffixes  := T1 T2 T3
    pattern   := $(foreach suffix,$(suffixes),%in/abc$($(suffix))) %in/rev.txt
    
    $(pattern):
        $(MAKE) -C mod1
    

    这将告诉make $(MAKE) -C mod1 一次构建所有 bin/abcXXX.binbin/rev.txt . 与 $(MAKE) -C mod2 相同 .

    总而言之,通过将所有这些功能粘合在一起,您可能完全摆脱了 build_foo 通用规则 . 就像是:

    T1         := _release
    T2         := _debug
    T3         := _test
    suffixes   := T1 T2 T3
    patternabc := $(foreach suffix,$(suffixes),%in/abc$($(suffix)).bin) %in/rev.txt
    patternxyz := $(foreach suffix,$(suffixes),%in/xyz$($(suffix)).bin)
    
    include version.mk
    
    all:   bin/mybin_release_$(VER).bin
    debug: bin/mybin_debug_$(VER).bin
    debug: export DBG:=1
    test:  bin/mybin_test_$(VER).bin
    test:  export TST:=1
    
    version.mk: bin/rev.txt
        { printf 'VER = '; cat $<; } > $@
    
    $(patternabc):
        $(MAKE) -C mod1
    
    $(patternxyz):
        $(MAKE) -C mod2
    
    bin/mybin%_$(VER).bin: bin/abc%.bin bin/xyz%.bin
        cat $^ > $@
    

相关问题