给出以下Makefile:
PROGRAMS := aprogram
SYSTEMS := linux windows
ARCHS := 386 amd64
define PROGRAM_template =
CUR_PROG := _build/bin/$(1)_$(2)_$(3)/$(1)
$(CUR_PROG): export GOOS = $(2)
$(CUR_PROG): export GOARCH = $(3)
$(CUR_PROG):
@echo "$(CUR_PROG)"
PROG_TARGETS += $(CUR_PROG)
endef
$(foreach prog,$(PROGRAMS),$(foreach sys,$(SYSTEMS),$(foreach arch,$(ARCHS),$(eval $(call PROGRAM_template,$(prog),$(sys),$(arch))))))
all: $(PROG_TARGETS)
输出是:
[0] % make all
_build/bin/aprogram_linux_386/aprogram
_build/bin/aprogram_linux_amd64/aprogram
_build/bin/aprogram_windows_386/aprogram
如果我添加另一个架构fakearch
,则输出为:
[0] % make all
_build/bin/aprogram_linux_386/aprogram
_build/bin/aprogram_linux_amd64/aprogram
_build/bin/aprogram_linux_fakearch/aprogram
_build/bin/aprogram_windows_386/aprogram
_build/bin/aprogram_windows_amd64/aprogram
这让我觉得只是没有执行最后一次迭代。我该如何纠正?
答案 0 :(得分:2)
你需要eval
define中的temp变量,因为make会在函数调用中同时展开所有引用。
PROGRAMS := aprogram
SYSTEMS := linux windows
ARCHS := 386 amd64
define PROGRAM_template =
$(eval CUR_PROG := _build/bin/$(1)_$(2)_$(3)/$(1))
$(CUR_PROG): export GOOS = $(2)
$(CUR_PROG): export GOARCH = $(3)
$(CUR_PROG):
@echo "$(CUR_PROG)"
PROG_TARGETS += $(CUR_PROG)
endef
$(foreach prog,$(PROGRAMS),$(foreach sys,$(SYSTEMS),$(foreach arch,$(ARCHS),$(eval $(call PROGRAM_template,$(prog),$(sys),$(arch))))))
all: $(PROG_TARGETS)
答案 1 :(得分:2)
双重评估将有效。但更常见的方法是通过CUR_PROG
转义来推迟内部变量$$
的扩展,如下所示:
PROG_TARGETS :=
define PROGRAM_template =
CUR_PROG := _build/bin/$(1)_$(2)_$(3)/$(1)
$$(CUR_PROG): export GOOS = $(2)
$$(CUR_PROG): export GOARCH = $(3)
$$(CUR_PROG):
@echo "$$(CUR_PROG)"
PROG_TARGETS += $$(CUR_PROG)
endef
原因是您首先使用call
,然后使用eval
。 call
函数会在eval
看到它们之前扩展其参数。
你的循环中有这个:
$(eval $(call PROGRAM_template,$(prog),$(sys),$(arch)))
为了扩展这个make,首先会扩展内部功能:
$(call PROGRAM_template,$(prog),$(sys),$(arch))
这会将PROGRAM_template
扩展为简单的字符串扩展:请记住,这不是eval
所以它不会将文本解释为make文件,它只是在扩展值。因此,第一行中的分配不起作用,因为我们还没有运行eval
。在原始实现中,第一次循环,CUR_PROG
在call
之前没有任何值,因此call
会扩展为:
CUR_PROG := _build/bin/aprogram_linux_386/aprogram
: export GOOS = linux
: export GOARCH = 386
:
@echo ""
PROG_TARGETS +=
然后将该字符串提供给eval
进行评估,但除了设置CUR_PROG
之外,它基本上都是无操作。
下一次循环时,CUR_PROG
仍然具有前一个值,所以当call调用扩展你得到的字符串时:
CUR_PROG := _build/bin/aprogram_linux_amd64/aprogram
_build/bin/aprogram_linux_386/aprogram: export GOOS = linux
_build/bin/aprogram_linux_386/aprogram: export GOARCH = amd64
_build/bin/aprogram_linux_386/aprogram:
@echo "_build/bin/aprogram_linux_386/aprogram"
PROG_TARGETS += _build/bin/aprogram_linux_386/aprogram
等。基本上,每次循环都会使用上一个循环中CUR_PROG
值的值,因为扩展发生在call
函数中,但是重新分配变量直到eval
函数才会发生。
通过转发CUR_PROG
,确保call
不会展开它,这意味着它将留给eval
展开。例如,在call
扩展完成后我的上述版本结果将是:
CUR_PROG := _build/bin/aprogram_linux_386/aprogram
$(CUR_PROG): export GOOS = linux
$(CUR_PROG): export GOARCH = 386
$(CUR_PROG):
@echo "$(CUR_PROG)"
PROG_TARGETS += $(CUR_PROG)
这就是你想要的。
用于理解eval
的有用调试工具是将其替换为info
函数;这将导致make打印出eval看到的字符串,这有助于可视化发生的事情:
$(foreach ...,$(info $(call PROGRAM_template,$(prog),$(sys),$(arch))))))
此解决方案的另一个选择是根本不使用CUR_PROG
变量。在这个例子中,您可以完全从define
中取出配方。这也可行:
define PROGRAM_template =
PROG_TARGETS += _build/bin/$(1)_$(2)_$(3)/$(1)
_build/bin/$(1)_$(2)_$(3)/$(1): export GOOS = $(2)
_build/bin/$(1)_$(2)_$(3)/$(1): export GOARCH = $(3)
endef
$(foreach prog,$(PROGRAMS),$(foreach sys,$(SYSTEMS),$(foreach arch,$(ARCHS),$(eval $(call PROGRAM_template,$(prog),$(sys),$(arch))))))
$(PROG_TARGETS):
@echo "$@"