我有一个稍微讨厌的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删除已编译的测试?
注意:我非常重视这个例子以使其更简单。我可能会介绍一些错误。
答案 0 :(得分:14)
因为目标可能未正确构建。下次make
项目时,它将尝试重建目标。如果文件尚未删除,make
将无法知道出现问题。 make
无法知道失败来自测试,而不是来自构建目标的过程。
在您的情况下,这种行为是否合适取决于测试的性质。如果您计划修复测试以使其不会导致Bus error
,则删除目标并不是什么大问题。如果您想稍后使用目标进行调试,则需要对make过程进行更改。
不删除目标的一种方法是使用.PRECIOUS
目标。
另一个可能是:
$(tests): %: %.c
gcc -o $@ $(testflags) $<
-$@
未经测试,但documentation表示目标不会被删除:
当一个错误发生时,make没有被告知忽略,这意味着当前目标无法正确重建,也不能直接或间接地依赖它。不会为这些目标执行进一步的命令,因为它们的先决条件尚未实现。
和
通常,当命令失败时,如果它完全更改了目标文件,则该文件已损坏且无法使用 - 或者至少未完全更新。然而文件的时间戳表示它现在是最新的,所以下次make运行时,它不会尝试更新该文件。情况与命令被信号杀死时的情况相同;看到中断。因此,通常正确的做法是在开始更改文件后如果命令失败则删除目标文件。如果.DELETE_ON_ERROR作为目标出现,make将执行此操作。这几乎总是你想要做的事情,但这不是历史实践;因此,为了兼容性,您必须明确请求它。
答案 1 :(得分:11)
避免此行为的一种方法是将构建和测试执行分为两个步骤:
tests := tests/test1 tests/test2 ...
test: $(tests) runtests
$(tests): %: %.c
gcc -o $@ $(testflags) $<
runtests: %.out: %
$< | tee $@
(我的make语法可能存在错误,任何人都可以随意纠正它。)一般的想法是让测试运行生成一个输出文件,这使make
更容易单独运行每个测试。
答案 2 :(得分:6)
这是make的默认行为。 当命令返回错误代码(例如非零返回)时,则删除make目标。 .PRECIOUS和.IGNORE makefile指令可以改变这种行为。