如果命令失败 make, 如 gcc,它退出...
gcc
gcc: fatal error: no input files
compilation terminated.
make: *** [main.o] Error 4
但是,如果我有一个管道,则会获取管道中最后一个命令的退出状态。举个例子, gcc | cat,不会失败,因为 cat 成功。
我知道整个管道的退出代码存储在 PIPESTATUS 数组,我可以得到错误代码4 ${PIPESTATUS[0]}。我应该如何构建我的makefile来处理管道命令并正常退出?
在评论中,另一个例子是 gcc | grep something。在这里,我假设最理想的行为仍然是 gcc 而且只有 gcc 导致失败而不是 grep 如果没找到任何东西。
你应该能告诉make使用 bash 代替 sh 得到 bash 具有 set -o pipefail 设置,以便它在管道中的第一次失败时退出。
在 GNU Make 3.81(大概早些时候,虽然我不确定)你应该能够做到这一点 SHELL = /bin/bash -o pipefail。
在 GNU Make 3.82(和更新)你应该能够做到这一点 SHELL = /bin/bash 和 .SHELLFLAGS = -o pipefail -c (虽然我不知道是否加入 -c 到目前为止这是必要的,或者即使你指定make也会为你添加 .SHELLFLAGS。
来自 bash 手册页:
管道的返回状态是最后一个的退出状态
命令,除非启用了pipefail选项。如果是pipefail
启用后,管道的返回状态是最后一个的值
(最右边)命令以非零状态退出,如果全部退出则为零
命令退出成功。如果保留字!先于a
管道,该管道的退出状态是逻辑否定
退出状态如上所述。 shell等待所有命令
在管道中在返回值之前终止。
我会去的 pipefail。但如果你真的不想要(要么 如果你只想在第一个进程上失败 - 不是在管道的其余部分发生故障的情况下:
SHELL=bash
all:
gcc | cat ; exit "$${PIPESTATUS[0]}"
与@jozxyqk自我回答相比,唯一的优点是您不会丢失退出状态代码。
只需添加您的开头即可 makefile 命令:
SHELL=/bin/bash -o pipefail
现在,您可以生成 errors.err 来自对象的文件(第一规则),不用担心它会被可执行文件覆盖(第二规则)。
%.o : %.c
gcc $(CFLAGS) $(CPPFLAGS) $^ -o $@ 2>&1 | tee errors.err
%.x : %.o $(OBJECTS)
gcc $(LDLIBS) $^ -o $@ 2>&1 | tee errors.err
没有它, make 从规则1中获取没有错误,并运行规则2,覆盖它。你最终只会有一行 errors.err 声明没有要运行的目标文件 gcc
gcc: error: program.o: No such file or directory
一种合理且可移植的方法是重构构建作业以使用文件而不是管道。例如:
foo:
gcc >$@.log
grep success $@.log
cat $@.log
rm $@.log
显示后删除日志文件显然没有必要;这只是一个通用模板。牛肉是重新定向以取代管道。你甚至可以将它重构为多个食谱:
foo: foo.tmp foo.log
grep success $@.log
mv $< $@
%.tmp %.log:
gcc -o $*.tmp >$*.log
正确清理临时人工制品并对其进行管理是这种方法的明显缺点。