【问题标题】:How to force make to always update and re-read an included makefile?如何强制 make 始终更新并重新读取包含的 makefile?
【发布时间】:2018-04-04 23:47:36
【问题描述】:

我有一个Makefile,它生成一个makefile.mk,它随后包含在主makefile 中。因为输入文件集(本例中为*.in)可以随时更改,所以我需要为每次运行make 重新生成一次makefile.mk

include documentation 声明:

一旦完成读取 makefile,make 将尝试重新制作任何过期或不存在的 [included makefile]。

How Makefiles Are Remade:

[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 时,它确实重新制作了一次包含的文件,但随后无法重新读取它并继续使用旧版本。

标签: makefile gnu-make


【解决方案1】:

我看到了两种解决方法:

  • 对生成的makefile使用唯一的后缀,这样make运行时就永远不存在,比如:

    UNIQUE := $(shell date +%s.%N)
    include makefile.mk.$(UNIQUE)
    makefile.mk.$(UNIQUE): […]
    
  • 像以前使用force 一样生成makefile.mk),但使其包含(和makefile 的其他部分)以某些make 变量为条件,因此默认情况下不执行。用相反的条件保护makefile.mk 的配方。然后使用此变量递归调用 make 以激活包含并禁用重建规则。

Thomas 编辑:使用内置的 MAKE_RESTARTS 变量可以非常有效地解决无限循环。

include makefile.mk

ifndef MAKE_RESTARTS
makefile.mk: .FORCE
    @( \
        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 $@

.PHONY: .FORCE
.FORCE:
endif

【讨论】:

  • 哈!我试图实施您的第二个建议,并偶然发现了 MAKE_RESTARTS variable,这正是我所需要的。我将使用有效的解决方案编辑您的答案。
  • 我想我们应该更新制作手册,在另一部分提及MAKE_RESTARTS
  • 是的,这很有用。 Savannah 不让我提交补丁,所以我提交了a bug
猜你喜欢
  • 2022-01-20
  • 2023-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-30
  • 2015-07-04
  • 2011-08-20
  • 2011-08-09
相关资源
最近更新 更多