在makefile中正确使用调用函数

时间:2013-07-25 18:43:11

标签: 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))))))))

正如你所看到的,这个简单的测试程序循环通过所有者,狗等的不同组合。最终目标是拥有一个目录,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

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

Weird foreach + user-defined function behavior in Makefiles似乎回答了,但我并没有完全明白。在我看来,当调用该函数时,它用一个$填充所有实例,并且转义的函数变为$(BUILD_DIR)。然后它将代码“粘贴”到临时文件中,在完成所有调用后,它会对文件进行评估,将变量替换为正常。

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

B_D_$(1)_$(2)_$(3)_$(4)_$(5) = ~~~

3 个答案:

答案 0 :(得分:7)

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

$(foreach A,$(STUFF),$(eval $(call func,$A)))

然后您可以将其重写为:

$(foreach A,$(STUFF),$(info $(call func,$A)))

现在make将打印出完全 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

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

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

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

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

$@

如果您不想使用尾部斜杠,则必须使用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 $@)" > $@

答案 1 :(得分:2)

问题是,在接收时评估$$(BUILD_DIR)时,循环已经完成。解决方案是重写收据:

$$(BUILD_DIR)/dogInfo.txt:
    @echo "$$@"
    mkdir $$(@D)
    @echo "$$(@D)" > $$@

答案 2 :(得分:0)

我认为您的问题不一定与make有关。这个命令:

mkdir new/fred/doberman/yellow/small/3
如果其中一个父目录(例如,yellow)尚不存在,

将失败。在这种情况下它吐出的错误是你得到的错误,所以看起来很可能就是这种情况。如果你想要一个命令根据需要创建给定目录的所有父目录,你应该运行mkdir -p,如下所示:

mkdir -p $$(BUILD_DIR)

有关mkdir的详细说明,请参阅-p手册页。