我有以下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)
无法扩展,但在普通规则中,它们似乎扩展得很好。我究竟做错了什么?有没有更好的方法来实现sys
和usr
目标的作用?
答案 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 ...)
在配方执行时扩展。