26

这是我的 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 更改为不使用虚假目标,它会按预期工作。新生成文件:

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.

为什么它不尊重我对虚假目标的依赖?我问的原因是因为实际上,我不会只是将单个文件编译到另一个文件中,所以我不想跟踪所有输出文件的名称以用作目标。

4

5 回答 5

20

而不是一个虚假的目标(正如@cmotley 指出的那样,它正在完全按照它应该的方式工作)当你想避免额外的工作时你可能使用的是一个“空目标”

空目标是虚假目标的变体;它用于保存您不时明确请求的操作的配方。与虚假目标不同,这个目标文件可以真实存在;但文件的内容无关紧要,通常是空的。

空目标文件的目的是记录最后一次执行规则配方的时间及其最后修改时间。这样做是因为配方中的命令之一是用于更新目标文件的触摸命令。

但是,在这种情况下,实际上不需要添加额外的空输出文件——您已经有了 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 $<
于 2013-12-21T18:11:43.693 回答
19

根据 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 目标的配方 - 先决条件无关紧要。

于 2012-12-13T03:48:49.200 回答
5

需要有一些目标文件来与 server.coffee 文件的修改时间进行比较。由于您没有具体的目标make,因此无法知道输出是否比依赖项更新,因此它将始终 build all

于 2012-12-13T03:47:27.883 回答
4

正如其他人提到的,make 查看文件的时间戳以确定依赖项是否已更改。

如果您想“模拟”具有依赖关系的虚假目标,则必须创建一个具有该名称的真实文件并使用该touch命令(在 Unix 系统上)。

我需要一个解决方案,仅在 makefile 更改时才清理目录(即编译器标志已更改,因此需要重新编译目标文件)。

这是我使用的文件(并在每次编译之前运行),文件名为makefile_clean

makefile_clean: makefile
    @rm '*.o'
    @sudo touch makefile_clean

touch命令将上次修改的时间戳更新为当前时间。

于 2015-02-13T15:50:47.433 回答
0

感谢@anonymous-penguin,但在我看来,将那些只是一个标志的无用文件放在文件夹/tmp中是一个更好的主意。

Dockerfile这是makefile,当且仅当文件被更改时,它将构建一个docker镜像。

# Configable variable
BUILD_FLAG_FILE = /tmp/ACMHomepage/Dockerfile_builded

# State variable
REBUILD = false

.PHONY: build
build: $(BUILD_FLAG_FILE)

$(BUILD_FLAG_FILE): Dockerfile
    $(eval REBUILD = true)
    @echo "Build image $(DB_NAME)..."
    @docker build -t $(DB_NAME) .
    @mkdir -p $(dir $(BUILD_FLAG_FILE))
    @touch $(BUILD_FLAG_FILE)

如您所见,它将创建一个空文件作为/tmp/ACMHomepage/Dockerfile_builded. 如果构建了 docker 映像,变量REBUILD将为true,这很有用!

于 2022-03-03T09:00:27.943 回答