双重评估将起作用。但更常见的方法是通过$$ 转义内部变量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,因此它不会将文本解释为一个makefile,它只是扩展值。所以第一行的赋值没有生效,因为我们还没有运行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
等等。基本上,每次通过循环时,您都使用 previous 循环中的 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 "$@"