在makefile中处理管道及其退出状态的最佳方法

时间:2014-08-14 11:18:48

标签: linux bash makefile

如果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如果找不到任何内容。

4 个答案:

答案 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

正确清理临时文物并对其进行管理是这种方法的一个明显缺点。