Makefile 依赖项不适用于虚假目标
Posted
技术标签:
【中文标题】Makefile 依赖项不适用于虚假目标【英文标题】:Makefile dependencies don't work for phony target 【发布时间】:2012-11-30 21:52:16 【问题描述】:这是我的 Makefile 的简化版本:
.PHONY: all
all: src/server.coffee
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
我想运行 make
并且只有在 src/server.coffee
发生更改时才重新编译它。但是,每次我运行 make
时它都会重新编译:
$ make
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
$ make
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
如果我将 Makefile 更改为不使用虚假目标,它会按预期工作。新的 Makefile:
bin/server.js: src/server.coffee
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
结果:
$ make
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
$ make
make: `bin/server.js' is up to date.
为什么它不尊重我对虚假目标的依赖?我问的原因是因为实际上,我不会只是将单个文件编译到另一个文件中,所以我不想跟踪所有输出文件的名称以用作目标。
【问题讨论】:
【参考方案1】:当你想避免额外的工作时,你可能会使用"empty target",而不是一个虚假的目标(正如@cmotley 指出的那样,它正在完全按照它应该的方式工作)是"empty target":
空目标是虚假目标的变体;它用于保存您不时明确请求的操作的配方。与虚假目标不同,这个目标文件可以真实存在;但文件的内容无关紧要,通常是空的。
空目标文件的目的是记录最后一次执行规则配方的时间及其最后修改时间。这样做是因为配方中的命令之一是更新目标文件的触摸命令。
然而,在这种情况下,实际上不需要添加额外的空输出文件——您已经有了 CoffeeScript 编译的输出!正如您在问题中已经证明的那样,这符合更典型的 Makefile 模式。你可以做的是重构这种方法:
.PHONY: all
all: bin/server.js
bin/server.js: src/server.coffee
mkdir -p bin
./node_modules/.bin/coffee -c -o bin src/server.coffee
现在你已经拥有了你想要的两样东西:一个很好的传统“全部”目标,它是正确的虚假,以及一个不会做额外工作的构建规则。您还可以更好地使其更通用,以便您可以轻松添加更多文件:
.PHONY: all
all: bin/server.js bin/other1.js bin/other2.js
bin/%.js: src/%.coffee
mkdir -p bin
./node_modules/.bin/coffee -c -o bin $<
【讨论】:
明确一点,一个空目标必须是一个文件并且真的只是用于它的时间戳?是否有任何其他类型的.PHONY
允许目标规则具有配方主体(带有执行的命令)并且仅在依赖项需要更新时运行?让.PHONY
根据它是否有配方主体来做不同的事情,这似乎很糟糕。【参考方案2】:
根据 Make 文档:
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.
http://www.gnu.org/software/make/manual/html_node/Special-Targets.html
Make 无条件运行 PHONY 目标的配方 - 先决条件无关紧要。
【讨论】:
好的,这很简单。您是否知道任何好的解决方法来实现不必显式声明依赖于一组源文件的输出文件的目标? 您可以声明一些变量来跟踪源文件和目标文件。例如,SRC = $(wildcard ./src/*.c) 和 OBJECTS = $(patsubst %.c, %.o, $(SRC))。然后,$(OBJECTS) 可能是您的目标的先决条件。我认为这就是你想要做的。【参考方案3】:需要有一些目标文件来与 server.coffee 文件的修改时间进行比较。由于您没有具体的目标 make
无法知道输出是否比依赖项更新,因此它将始终构建 all
。
【讨论】:
啊,谢谢。我没有意识到 make 是如何计算需要重新编译哪些文件的。很有帮助【参考方案4】:正如其他人提到的,make 查看文件的时间戳以确定依赖项是否已更改。
如果您想“模拟”具有依赖关系的虚假目标,则必须创建一个具有该名称的真实文件并使用 touch
命令(在 Unix 系统上)。
我需要一个解决方案,仅在 makefile 更改时才清理目录(即编译器标志已更改,因此需要重新编译目标文件)。
这是我使用的(并在每次编译之前运行)一个名为 makefile_clean
的文件:
makefile_clean: makefile
@rm '*.o'
@sudo touch makefile_clean
touch
命令将上次修改的时间戳更新为当前时间。
【讨论】:
以上是关于Makefile 依赖项不适用于虚假目标的主要内容,如果未能解决你的问题,请参考以下文章