如果make
中的命令失败,例如gcc
,则会退出...
gcc
gcc: fatal error: no input files
compilation terminated.
make: *** [main.o] Error 4
但是,如果我有一个管道,则会获取管道中最后一个命令的退出状态。例如,gcc | cat
不会失败,因为cat
成功。
我知道整个管道的退出代码存储在PIPESTATUS
数组中,我可以使用${PIPESTATUS[0]}
获取错误代码4。我应该如何构建我的makefile以处理管道命令并在正常情况下退出?
与评论中一样,另一个例子是gcc | grep something
。在这里,我假设最期望的行为仍然适用于gcc
,只有gcc
会导致失败而不是grep
如果找不到任何内容。
答案 0 :(得分:9)
您应该能够告诉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
添加到最后这样做是必要的,或者即使你指定.SHELLFLAGS
,make也会为你添加。
来自bash
手册页:
管道的返回状态是最后一个的退出状态 命令,除非启用了pipefail选项。如果是pipefail 启用后,管道的返回状态是最后一个的值 (最右边)命令以非零状态退出,如果全部退出则为零 命令退出成功。如果保留字!先于a 管道,该管道的退出状态是逻辑否定 退出状态如上所述。 shell等待所有命令 在管道中在返回值之前终止。
答案 1 :(得分:5)
我会选择pipefail
。但如果你真的不想要(或,如果你只想在第一个进程中失败 - 不管其他管道出现故障):
SHELL=bash
all:
gcc | cat ; exit "$${PIPESTATUS[0]}"
与@jozxyqk自我回答相比,唯一的优势是您不会丢失退出状态代码。
答案 2 :(得分:1)
只需添加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
答案 3 :(得分:1)
合理且可移植的方法是重构构建作业以使用文件而不是管道。例如:
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
正确清理临时文物并对其进行管理是这种方法的一个明显缺点。