当我执行这个GNU Makefile时:
foo: BUILD_DIR = foo_dir
bar: BUILD_DIR = bar_dir
BINARY = $(BUILD_DIR)/my_binary
.PHONY: foo
foo: $(BINARY)
.PHONY: bar
bar: $(BINARY)
$(BINARY):
@echo $(BINARY)
@echo $@
我明白了:
$ make foo
foo_dir/my_binary # <= this is what I want
/my_binary # <= this is NOT what I want
$ make bar
bar_dir/my_binary # <= this is what I want
/my_binary # <= this is NOT what I want
相反,我想:
$ make foo
foo_dir/my_binary
foo_dir/my_binary
$ make bar
bar_dir/my_binary
bar_dir/my_binary
我该怎么做?我使用GNU Make 3.81。
答案 0 :(得分:1)
这里的技巧是了解 make 如何解析Makefile。 首先, make 读取Makefile, 随之而来的是依赖图。 它扩展了一些宏, 同时存储其他人以供日后扩展。
foo: $(BINARY)
make 扩展此依赖关系行。
请注意,此时 make 没有构建目标的概念,
所以目标特定变量还没有进入讨论。
${BINARY}
是$(BUILD_DIR)/my_binary
,
但是${BUILD_DIR}
是空的。
在构建foo
之前,您已告知 make ,
它必须建立/my_binary
。
$(BINARY): @echo $(BINARY) @echo $@
现在,当 make 读取此片段时,它会再次展开目标行。
再次,
因为您没有为${BUILD_DIR}
提供默认值,
依赖项行中${BINARY}
的扩展为/my_binary
。
食谱
(那个shell命令块)
存储为单个递归扩展变量。
现阶段没有扩大。
这样做的结果是你告诉 make 如何构建/my_binary
,
foo
取决于它。
第二阶段 make 查看您要求它制作的内容,
它检查它现在在内存中的依赖图。
它检查它是否知道如何构建所有内容
(这是可能生成可怕的make: *** No rule to make target 'blah'. Stop.
消息的地方),
并检查哪些目标已经是最新的。
您要求它构建foo
,
所以它尽职尽责决定先建立/my_binary
。
Make 在此阶段不会注意到配方(shell命令)。
最后阶段实际上是执行所需的食谱, 按要求的顺序。 正是在这一点上, make 通过扩展早先收集的配方来获取所需的shell命令。
@echo $(BINARY) @echo $@
我们正在建设中,
目标特定变量有效。
我们正在建设foo
,
所以现在${BUILD_DIR}
是foo_dir
。
$@
是/my_binary
。
配方的扩展是
@echo foo_dir/my_binary
@echo /my_binary
只有在这次扩张之后
make
执行每个结果行,
逐一,
在单独的shell调用中。
这个扩展 - 整个配方 - 执行之前 - 任何事情都很重要!
(例如${BINARY}
可以扩展为多行。)
啧。
您遇到问题,因为 make 意外地扩展了变量的非特定目标版本。
--warn
提供断言:
BUILD_DIR = $(error Ooops, unexpected expansion of $$BUILD_DIR)
有很多方法可以做你想做的事。 我是静态模式规则的忠实粉丝。 如果您的目标及其依赖关系可以通过 make 的noddy模式匹配来描述,那么这些工作就很顺利。
.PHONY: foo bar
foo bar: %: %_dir/my_binary
echo $@
这应该用宏来整理。 希望您现在能够更好地了解如何解析它:
targets := foo bar
.PHONY: ${targets}
${targets}: %: %_dir/my_binary
echo $@
哦,我应该提一下,配方中的$*
会扩展到模式中匹配的%
。
这通常很有帮助(尽管在这种情况下很$*
与$@
相同)。