22

I am trying to instruct GNU Make 3.81 to not stop if a command fails (so I prefix the command with -) but I also want to check the exit status on the next command and print a more informative message. However my Makefile below fails:

$ cat Makefile 
all:
    -/bin/false
    ([ $$? -eq 0 ] && echo "success!") || echo "failure!"
$
$ make
/bin/false
make: [all] Error 1 (ignored)
([ $? -eq 0 ] && echo "success!") || echo "failure!"
success!

Why does the Makefile above echo "success!" instead of "failure!" ?

update:

Following and expanding on the accepted answer, below is how it should be written:

failure:                                                                                                                                                                                                                                      
    @-/bin/false && ([ $$? -eq 0 ] && echo "success!") || echo "failure!"                                                                                                                                                                 
success:                                                                                                                                                                                                                                      
    @-/bin/true && ([ $$? -eq 0 ] && echo "success!") || echo "failure!"     
  • 2
    You might want to investigate the .ONESHELL: directive. – Jonathan Leffler Feb 10 '15 at 14:54
  • .ONESHELL will run all receipt block in one shell which has effect: if first command fails, next ones will be executed without problems. To prevent this .SHELLFLAGS = -ec should be used. But in this case you can not use - prefix more (for personal command of the receipt) because the make will write that error is ignored but still will fail all the block. So, || : is one the solution to ignore command. But it's not cross-platform (Windows has not || : or || true) – Paul-AG Jun 4 at 11:33
14

Each update command in a Makefile rule is executed in a separate shell. So $? does not contain the exit status of the previous failed command, it contains whatever the default value is for $? in a new shell. That's why your [ $? -eq 0 ] test always succeeds.

10

You don't need the test of $? since && works if $? is zero and || is proceed in case of a non-zero return value.

And you don't need the minus since at the return value to make is taken from the last proceed program call of the line. So this works fine

failure:

      @/bin/false && echo "success!" || echo "failure!" 

success:

      @/bin/true && echo "success!" || echo "failure!"

The opposite happens: If you wanna do your own message and want to break the make process anyway with a non-zero value, you need to write something like this:

failure:

      @/bin/false && echo "success!" || { echo "failure!"; exit 1; }
8

From the GNU make documentation:

When errors are to be ignored, because of either a ‘-’ or the ‘-i’ flag, make treats an error return just like success, except that it prints out a message that tells you the status code the shell exited with, and says that the error has been ignored.

To utilize make's exit status in a case like this, execute make from a script:

#!/bin/bash
make
([ $? -eq 0 ] && echo "success!") || echo "failure!"

And have your Makefile contain:

all:
    /bin/false

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.