如果命令失败 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
正确清理临时人工制品并对其进行管理是这种方法的明显缺点。