【发布时间】:2018-04-04 23:47:36
【问题描述】:
我有一个Makefile,它生成一个makefile.mk,它随后包含在主makefile 中。因为输入文件集(本例中为*.in)可以随时更改,所以我需要为每次运行make 重新生成一次makefile.mk。
一旦完成读取 makefile,make 将尝试重新制作任何过期或不存在的 [included makefile]。
[A]在读入所有 makefile 后,make 会将每个文件视为目标并尝试更新它。如果 makefile 有一条规则说明如何更新它 [...],它会在必要时更新。检查完所有 makefile 后,如果有任何实际更改,make 会重新开始并重新读取所有 makefile。
基于此,我认为以下应该可行:
include makefile.mk
makefile.mk:
echo "--- Begin makefile.mk ---"
@( \
shopt -q -s nullglob; \
INPUTS=(*.in); \
echo "all:" $${INPUTS//.in/.out}; \
for f in $$INPUTS; do \
echo "$${f/.in/.out}: $$f"; \
echo -e '\tcp $$< $$@'; \
done \
) | tee $@
echo "--- End makefile.mk ---"
(我知道我可以在这个简单的示例中使用模式规则和通配符。在实际中,makefile.mk 具有更复杂的依赖关系,并且由 Python 脚本生成。)
所以我们把它放到一个空目录中并运行它:
$ ls
Makefile
$ make
--- Begin makefile.mk ---
all:
--- End makefile.mk ---
make: Nothing to be done for 'all'.
到目前为止一切顺利。如果我们创建一个输入文件会发生什么?
$ touch test.in
$ ls
Makefile makefile.mk test.in
$ make
make: Nothing to be done for 'all'.
因为makefile.mk 没有依赖关系,所以它不会被更新。坏的!如何通过将其标记为假来强制它始终被视为过时:
.PHONY: makefile.mk
添加此行后,确实正在重新生成makefile.mk:
$ make
--- Begin makefile.mk ---
all: test.out
test.out: test.in
cp $< $@
--- End makefile.mk ---
make: Nothing to be done for 'all'.
但是,它似乎没有被重新阅读,因为 make 声称对于 all 没有什么可做的,即使它的先决条件 test.out 不存在。
第二次运行make 是正确的:
$ make
--- Begin makefile.mk ---
all: test.out
test.out: test.in
cp $< $@
--- End makefile.mk ---
cp test.in test.out
为什么make 不重新启动并阅读更新后的makefile.mk,正如文档所承诺的那样?
我找到了this related answer,这表明.PHONY 是罪魁祸首,并提供了这样的解决方案:
makefile.mk: force
...
.PHONY: force
force:
但是,如果我尝试这样做,make 会陷入无限循环,一遍又一遍地重新生成 makefile.mk。
【问题讨论】:
-
纯粹出于好奇:是什么让 Python 脚本生成比内部生成更有利?算术?字符串处理?
-
@Vroomfondel 从每个文件生成多个目标,具体取决于文件所在目录的名称,以及配方中依赖文件名的一部分的命令行参数。这一切都可以在 make 中完成,我一开始确实是这样做的,但它变得一团糟。它在 Python 中更具可读性和可维护性。
-
我检查了 GNU make 的代码。如果包含的 makefile 被标记为 PHONY 似乎是有意的,那么出于重新执行的目的,它不会被视为已更新。这通常是一件好事,因为标记为 PHONY 的文件总是过时的,这意味着 makefile 将执行无限递归的重新制作。但是,它可能应该以这种方式明确记录,我不相信它是。
-
@MadScientist 感谢您查找此内容!文档中有一些关于双冒号规则的内容,但不是关于 PHONY 的。不过有点奇怪:当标记为 PHONY 时,它确实重新制作了一次包含的文件,但随后无法重新读取它并继续使用旧版本。