由于输出重定向生成的大小为 0 的文件,make 错误地认为规则成功

Lor*_*ein 5 shell make io-redirection

在 makefile 中,我有几个如下所示的规则:

out.txt: foo.sh input.txt
  ./foo.sh -i input.txt > out.txt
Run Code Online (Sandbox Code Playgroud)

如果foo.sh失败,out.txt则将创建为 0 大小的文件。如果我再次运行make,它会错误地认为out.txt文件创建成功,并且不会再次运行规则。

处理这种情况的正确方法是什么?

Mic*_*zek 11

您可以要求,使删除目标文件,如果规则失败,通过定义一个特殊的目标命名.DELETE_ON_ERROR。它不需要执行任何操作或具有任何依赖项,因此只需将其添加到您的 makefile 中:

.DELETE_ON_ERROR:
Run Code Online (Sandbox Code Playgroud)

然后你会得到以下信息:

.DELETE_ON_ERROR:
Run Code Online (Sandbox Code Playgroud)

食谱错误

通常,当配方行失败时,如果它完全更改了目标文件,则该文件已损坏且无法使用——或者至少它没有完全更新。然而文件的时间戳表示它现在是最新的,所以下次make运行时,它不会尝试更新该文件。这种情况就和shell被信号杀死时一样;见中断。所以通常正确的做法是如果在开始更改文件后配方失败,则删除目标文件。make如果.DELETE_ON_ERROR出现为目标,将执行此操作。这几乎总是你想做的make,但这不是历史实践;所以为了兼容性,你必须明确要求它。

  • `.DELETE_ON_ERROR` 的优点是它会自动应用于每个规则。不过它也有缺点:它是 GNU make 特有的;如果构建被猛烈中断(电源故障,无法屏蔽的信号),它不会触发;它删除了部分文件,这对于调查失败的构建很有用。 (3认同)

Gil*_*il' 7

只要有可能,makefile 规则应该首先在一个临时名称下创建它们的目标,然后将其移动到位。这样,如果构建过程因任何原因中断,则不会出现无法与完全写入的文件区分开的半写入目标文件。

out.txt: foo.sh input.txt
        ./foo.sh -i input.txt >$@.tmp
        mv -f $@.tmp $@
Run Code Online (Sandbox Code Playgroud)

mv -f $@.tmp $@ 是一个常见的 makefile 习惯用法。

Juliano 的回答显示了一个变体,其中临时文件的名称是动态生成的。如果可能有多个进程生成相同的目标或者目录可以被其他用户写入,则需要动态名称生成。构建树很少出现这种情况(如果这些是问题,典型的 makefile 中发生的很多事情都会中断),因此通常不需要额外的复杂性。

  • 那么,这种情况多久发生一次?如果你使用 `kill -9`ing `make` 你应该得到你所得到的,如果有像电源故障这样的异常情况,我希望有人会做一个干净的构建 (2认同)