GNU make中无法解释的行为

时间:2016-01-06 10:56:13

标签: macos bash makefile gnu-make

我有以下Makefile:

SHELLS   = $(call adjust, profile bash zsh)
adjust = $(foreach arg, $(1), $(DIR)/$(DOT)$(arg))

sys:
    $(eval DIR  = /etc)
    $(eval DOT  = )
usr:
    $(eval DIR  = $(wildcard ~))
    $(eval DOT  = .)

# Installation Recipes {{{1
shell-sys: $(SHELLS) | sys
    @echo $(SHELLS)
shell-usr: $(SHELLS) | usr
    @echo $(SHELLS)

$(SHELLS): $(DIR)/$(DOT)%: $(wildcard %/*)
    @echo $(SHELLS)

现在我运行make shell-sys并期望得到以下结果:

$ make shell-sys
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh
/etc/profile /etc/bash /etc/zsh

但我得到的是以下内容:

$ make shell-sys
/profile /bash /zsh
/profile /bash /zsh
/profile /bash /zsh
/etc/profile /etc/bash /etc/zsh

内部静态模式规则$(DIR)$(DOT)无法扩展,但在普通规则中,它们似乎扩展得很好。我究竟做错了什么?有没有更好的方法来实现sysusr目标的作用?

1 个答案:

答案 0 :(得分:1)

在运行任何配方(shell命令)之前, make 将读取整个Makefile。 虽然它这样做,但它扩展了依赖关系。 食谱本身只是作为递归扩展变量存储起来。 在此阶段结束时, make 具有依赖关系图。

在您的情况下, make 展开

shell-sys: $(SHELLS) | sys
鉴于$(SHELLS)/profile /bash /zsh的空虚,

$DIR变为$DOT。 您可以通过提供 make --warn命令行参数来获得有关 make 扩展这些空变量的一些提示。 因此,就好像你写了:

shell-sys: /profile /bash /zsh | sys
    @echo $(SHELLS)

/profile /bash /zsh: /%:
    @echo $(SHELLS)

(我怀疑你的磁盘上有一个名为%的文件夹,所以我假设$(wildcard ...)扩展为空。)

一旦它的小手上有一个依赖图, make 可以走路, 随时执行食谱。 你要求它建立shell-sys, 所以它首先尝试构建/profile。 [大概。依赖于依赖性的隐式排序的 Makefile 被破坏了恕我直言。]

制作现在构建/profile。 它知道如何做到这一点,你早些时候告诉过它。 它扩大了配方。 @echo $(SHELLS)变为@echo /profile /bash /zsh使echo /profile /bash /zsh传递给shell,而不会因为@前缀而告诉您已执行此操作。 echo尽职尽责地打印/profile /bash /zsh

同样针对目标/bash/zsh

接下来我们开始构建sys制作扩展sys的配方。 扩展中的每个单独行都将传递给shell的新调用。 然而,扩张是空的, 所以没有调用shell。 由于副作用 DIR变为/etc

最后是shell-sys的食谱。 希望您现在可以看到它扩展到@echo /etc/profile /etc/bash /etc/zsh

你的配方中有很多不喜欢的东西, 但我能给出的最大建议是不要让$(eval ...)在配方执行时扩展。