16

我有一个用于运行测试的略显老套的 makefile:

### Run the tests

tests := tests/test1 tests/test2 ...

test: $(tests)

$(tests): %: %.c
    gcc -o $@ $(testflags) $<
    $@

它有效,但它使 Make 做一些我以前从未见过的事情。我的测试目前已损坏,并导致总线错误。Make 给出以下输出:

gcc -o tests/test1 [flags blah blah] tests/test1.c
tests/test1
make: *** [tests/test1] Bus error
make: *** Deleting file `tests/test1'

我对最后一行很好奇。我以前从未见过Make这样做。为什么Make会删除已编译的测试?

注意:我对这个示例进行了大量编辑以使其更简单。我可能引入了一些错误。

4

3 回答 3

18

因为目标可能没有正确构建。下次您make进行项目时,它将尝试重建目标。如果文件没有被删除,make就无法知道出了什么问题。 make无法知道失败来自测试而不是构建目标的过程。


在您的情况下是否需要这种行为取决于测试的性质。如果您计划修复测试以使其不会导致 a Bus error,则删除目标并不是什么大问题。如果您想稍后使用目标进行调试,则需要更改您的 make 过程。

不删除目标的一种方法是使用.PRECIOUS目标。


另一个可能是:

$(tests): %: %.c
    gcc -o $@ $(testflags) $<
    -$@

未经测试,但文档表明目标不会被删除:

当没有告知 make 忽略的错误发生时,这意味着当前目标无法正确重新制作,任何其他直接或间接依赖于它的目标也不能正确重新制作。不会对这些目标执行进一步的命令,因为它们的先决条件尚未实现。

和:

通常,当命令失败时,如果它完全更改了目标文件,则该文件已损坏并且无法使用——或者至少它没有完全更新。然而文件的时间戳表明它现在是最新的,所以下次 make 运行时,它不会尝试更新该文件。情况与命令被信号杀死时的情况相同;请参阅中断。所以通常正确的做法是如果在开始更改文件后命令失败,则删除目标文件。如果 .DELETE_ON_ERROR 显示为目标,make 将执行此操作。这几乎总是你想做的,但这不是历史惯例;所以为了兼容性,你必须明确地请求它。

于 2009-02-26T00:23:18.640 回答
14

避免这种行为的一种方法是将构建和测试执行分为两个步骤:

tests := tests/test1 tests/test2 ...

test: $(tests) runtests

$(tests): %: %.c
    gcc -o $@ $(testflags) $<

runtests: %.out: %
    $< | tee $@

(我的 make 语法可能有错误,任何人都可以更正它。)一般的想法是让测试运行生成一个输出文件,这样更容易make单独运行每个测试。

于 2009-02-26T00:24:45.643 回答
6

这是 make 的默认行为。当命令返回错误代码(例如非零返回)时,make 目标将被删除。.PRECIOUS 和 .IGNORE makefile 指令可以改变这种行为。

于 2009-02-26T00:33:58.117 回答