无论文件是否更改都进行重建

Make rebuilds regardless of whether files are changed

我使用 make 构建一个不包含任何 C/C++ 文件的项目。

我的项目中有 python 目录,其中包含一些 python 代码,如果 python 中的任何内容发生更改,我将在构建过程中执行这些代码。

这是我的 Makefile:

.PHONY: all python

all: python

python: python/
    python python/code.py

但是,如果我连续调用 make 两次,它会一遍又一遍地构建所有内容。

我不太确定你期望发生什么,但你已经声明目标 python 是一个虚假目标并且 make 手册明确指出虚假目标 always 被认为已过时并总是重建:

The prerequisites of the special target .PHONY are considered to be phony targets. When it is time to consider such a target, make will run its recipe unconditionally, regardless of whether a file with that name exists or what its last-modification time is.

所以,很明显,当您 运行 您的 makefile 时,假目标 python 的配方将始终是 运行。

此外,如果您删除虚假设置,则此规则将永远不会 运行,因为您说的是目录 python 依赖于自身。文件永远不会过时。

我还应该指出,将目录列为先决条件并不是一个神奇的 shorthand 意思是“检查此目录中的每个文件”。它的字面意思是,检查目录的时间戳。目录的时间戳仅在该目录中的文件被重命名、删除或创建时更新。当该目录中的文件被修改时,它不会更新。

“如果 python 中的任何内容已更改” 并非 100% 明确。如果这意味着您在 python 中有一组 python 源文件,并且您希望每次更改其中一个时 运行 配方,如 MadScientist 解释得非常清楚,您不能使用 python 作为先决条件,这将不起作用,因为如果现有文件发生更改,python 的时间戳不会。声明 python 为 phony 没有帮助,它只是告诉 make 总是 运行 这个食谱。并且将 python 声明为自身的先决条件并没有真正意义,因为只有当某些先决条件比目标更新时,make 才会比较目标和先决条件的时间戳以及 运行s 配方。

一个选项是将所有这些 python 源文件声明为目标的先决条件,该目标将是一个真实文件,并且哪个时间戳将对应于配方的最后一次 运行。由于您没有说明配方的作用是什么,我们不知道我们可以基于哪个产品文件。因此,让我们为此创建一个空文件(如果有的话,用真实的产品文件替换它):

PYDIR := python
PYSRC := $(wildcard $(PYDIR)/*.py)

.PHONY: all
all: .python.done

.python.done: $(PYSRC)
    python $(PYDIR)/code.py
    touch $@

当然,如果您在 python 中有子目录,或者如果您的 python 源文件具有除 .py 之外的其他扩展名,或者存在除 [=43] 之外的其他文件=] 应该触发重建的源文件,或者如果某些源文件可以使用旧时间戳创建,或者如果某些源文件可以删除或任何其他更复杂的情况,这个简单的建议是不够​​的。

例如,如果可以在 python 中创建或删除文件(但不能在其子目录中创建或删除),并且您希望在发生这种情况时 运行 配方,您确实可以声明python 作为 .python.done 的先决条件,因为它的时间戳恰好在发生这种情况时发生变化:

.python.done: $(PYSRC) $(PYDIR)

如果您在 python 中有子目录,则必须将 wildcard 替换为:

PYSRC := $(shell find $(PYDIR) -type f -name '*.py')

如果要考虑 python 下的任何文件:

PYSRC := $(shell find $(PYDIR) -type f)

对我的问题的简洁明了的回答是,如果您不想保留构建的“产品”,make 是不可能的磁盘上的进程。原因如下:

  • 声明不是目录或文件的目标的唯一方法是使用一些特殊目标(参见 documentation). As per .PHONY targets documentation.PHONY 目标是用作“名称”要执行的配方”。因此,.PHONY 目标似乎适合此目的。
  • 但是,.PHONY 目标总是重新运行 从头开始​​,无论它们的依赖项是否已更改。

我没有坚持使用 make,而是编写了一个简单的 shell 脚本来调用 git 来了解 python 目录是否已更改并相应地构建。重要的是,在我的例子中,构建过程不会在本地磁盘上留下任何文件,因为所有生成的文件都会立即传输到远程。


对那些坚持使用 make 的人的补充说明:

  • make 需要构建过程的“产品”出现在磁盘上。否则,make 没有检测构建已经 运行.
  • 的机制
  • 不要使用 .PHONY 目标。相反,将构建的“产品”用作目标,将源文件用作依赖项。但是,要实现这一点,您首先需要拥有一些您想要保留的构建过程“产品”。不保留任何“产品”可能是完全合理的,但仍然要求构建过程 运行 只有在之前没有完成的情况下。