Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to handle pipes and their exit status in a makefile

If a command fails in make, such as gcc, it exits...

gcc
gcc: fatal error: no input files
compilation terminated.
make: *** [main.o] Error 4

However, if I have a pipe the exit status of the last command in the pipe is taken. As an example, gcc | cat, will not fail because cat succeeds.

I'm aware the exit codes for the whole pipe are stored in the PIPESTATUS array and I could get the error code 4 with ${PIPESTATUS[0]}. How should I structure my makefile to handle a piped command and exit on failure as normal?


As in the comments, another example is gcc | grep something. Here, I assume the most desired behavior is still for gcc and only gcc to cause failure and not grep if it doesn't find anything.

like image 495
jozxyqk Avatar asked Aug 14 '14 11:08

jozxyqk


2 Answers

You should be able to tell make to use bash instead of sh and get bash to have set -o pipefail set so it exits with the first failure in the pipeline.

In GNU Make 3.81 (and presumably earlier though I don't know for sure) you should be able to do this with SHELL = /bin/bash -o pipefail.

In GNU Make 3.82 (and newer) you should be able to do this with SHELL = /bin/bash and .SHELLFLAGS = -o pipefail -c (though I don't know if adding -c to the end like that is necessary or if make will add that for you even when you specify .SHELLFLAGS.

From the bash man page:

The return status of a pipeline is the exit status of the last command, unless the pipefail option is enabled. If pipefail is enabled, the pipeline's return status is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands exit successfully. If the reserved word ! precedes a pipeline, the exit status of that pipeline is the logical negation of the exit status as described above. The shell waits for all commands in the pipeline to terminate before returning a value.

like image 135
Etan Reisner Avatar answered Sep 22 '22 17:09

Etan Reisner


I would go for pipefail. But if you really don't want (or if you want to only fail on the first process -- not in case of failure from the rest of the pipe):

SHELL=bash

all:
        gcc | cat ; exit "$${PIPESTATUS[0]}"

The only advantage compared to @jozxyqk self answer is that you don't loose the exit status code.

like image 21
Sylvain Leroux Avatar answered Sep 19 '22 17:09

Sylvain Leroux