我正在尝试为不同的软件目录编译不同的优化级别等。我创建了以下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) = ~~~
答案 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
手册页。