在makefile中正确使用调用函数

Nat*_*ton 2 makefile eval

我正在尝试编译具有不同优化级别的不同软件目录等.我创建了以下makefile来执行此操作:

OWNER           = betsy molly fred
DOG             = poodle mutt doberman
COLOUR          = brown red yellow
ATTR            = big small
LEGS            = 0 3

#we want every possible combination to be excercised
OUTPUT_STUFF = $(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),new/$(own)/$(dog)/$(col)/$(attr)/$(legs)/dogInfo.txt)))))

.PHONY: all

all: $(OUTPUT_STUFF)

define PROGRAM_template

own             = $(1)
dog             = $(2)
col             = $(3)
attr            = $(4)
legs            = $(5)

BUILD_DIR           = new/$(own)/$(dog)/$(col)/$(attr)/$(legs)

#for each build directory, we are going to put a file in it containing the build dir. string
$$(BUILD_DIR)/dogInfo.txt:
    @echo "$$@"
    mkdir $$(BUILD_DIR)
    @echo "$$(BUILD_DIR)" > $$(BUILD_DIR)/dogInfo.txt
endef

#call the function many times
$(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),$(eval $(call PROGRAM_template,$(own),$(dog),$(col),$(attr),$(legs))))))))
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,这个简单的测试程序循环通过所有者,狗等的不同组合.最终目标是有一个目录,new,所有所有者都是dirs,在那些,所有狗等.在底部是只是一个包含路径的文件.

当我运行它时,输出是:

new/betsy/poodle/brown/big/0/dogInfo.txt
mkdir new/fred/doberman/yellow/small/3
mkdir: cannot create directory `new/fred/doberman/yellow/small/3': No such file or directory
make: *** [new/betsy/poodle/brown/big/0/dogInfo.txt] Error 1
Run Code Online (Sandbox Code Playgroud)

因此,由于某种原因,目标是可以的,但看似完全相同的变量是我的循环中的最后一个.从根本上说,我不明白发生了什么.

Makefile中奇怪的foreach +用户定义的函数行为似乎回答了,但我并没有完全理解它.在我看来,当调用该函数时,它用一个$填充所有实例,并且转义的函数变为$(BUILD_DIR).然后它将代码"粘贴"到临时文件中,在完成所有调用后,它会评估文件,将变量替换为正常.

我想到的一个(丑陋)解决方案是每次都使BUILD_DIR变量不同:

B_D_$(1)_$(2)_$(3)_$(4)_$(5) = ~~~
Run Code Online (Sandbox Code Playgroud)

Mad*_*ist 7

亚历克斯是正确的(虽然我认为他的意思是食谱,而不是收据 :-)).调试复杂eval问题的最佳方法是使用调用替换eval函数info.所以如果你有类似的东西:

$(foreach A,$(STUFF),$(eval $(call func,$A)))
Run Code Online (Sandbox Code Playgroud)

然后你可以重写为:

$(foreach A,$(STUFF),$(info $(call func,$A)))
Run Code Online (Sandbox Code Playgroud)

现在,让打印出你究竟是什么EVAL将会解析.通常很清楚,查看makefile输出,问题是什么.在你的情况下,你会在输出中看到这样的东西(省略所有额外的变量设置):

BUILD_DIR = new/betsy/poodle/brown/big/0
$(BUILD_DIR)/dogInfo.txt:
            @echo "$$@"
            mkdir $(BUILD_DIR)
            @echo "$(BUILD_DIR)" > $(BUILD_DIR)/dogInfo.txt

BUILD_DIR = new/betsy/poodle/brown/big/3
$(BUILD_DIR)/dogInfo.txt:
            @echo "$$@"
            mkdir $(BUILD_DIR)
            @echo "$(BUILD_DIR)" > $(BUILD_DIR)/dogInfo.txt
Run Code Online (Sandbox Code Playgroud)

请注意每次如何设置全局变量BUILD_DIR.在make中,变量只有一个值(一次).当make正在读取makefile时,它会立即扩展目标和先决条件列表,因此当时的任何值BUILD_DIR都将用于目标/先决条件,因此这适用于您.

但是当make完成读取makefile时,值BUILD_DIR将始终是你设置它的最后一件事; 在这种情况下new/fred/doberman/yellow/small/3.现在make开始调用每个目标的配方,当它执行时,它将BUILD_DIR在配方中扩展,因此所有配方将获得相同的值.

正如Alex指出的那样,您应该确保您的配方使用自动变量,例如$@为每个规则正确设置的变量.如果你这样做,你会注意到你根本不需要重新定义规则,因为它实际上是所有目标的相同配方.如果你注意到这一点,你会注意到你不需要整个评估或调用复杂性.

您所要做的就是计算所有目标的名称,然后编写一个规则:

ALLDOGINFO = $(foreach own,$(OWNER),$(foreach dog,$(DOG),$(foreach col,$(COLOUR),$(foreach attr,$(ATTR),$(foreach legs,$(LEGS),new/$(own)/$(dog)/$(col)/$(attr)/$(legs)/dogInfo.txt)))))

$(ALLDOGINFO):
        @echo "$@"
        mkdir $(dir $@)
        @echo "$(dir $@)" > $@
Run Code Online (Sandbox Code Playgroud)

如果您不想使用尾部斜杠,则必须使用$(patsubst %/,%,$(dir $@)).