It is hard to believe, but it seems to me that the common Makefile idiom "> $@" is wrong. In particular, a target whose rule has a command that fails but uses this redirection will fail the first time around but not subsequent times. This is because even though the command fails, the redirection "succeeds" in the sense of creating an up-to-date (albeit zero-length) target.
It seems to me that the correct thing to do is to redirect to a temporary and on success rename this temporary to the target.
Here's and example Makefile:
bad-target:
command-that-will-fail > $@
good-target:
command-that-will-fail > [email protected] || ( rm [email protected]; false )
mv [email protected] $@
clean:
rm -f bad-target good-target
And here's a sequence of commands illustrating the problem and its solution:
$ make clean
rm -f bad-target good-target
$ make bad-target
command-that-will-fail > bad-target
/bin/sh: command-that-will-fail: not found
make: *** [bad-target] Error 127
$ make bad-target
make: `bad-target' is up to date.
$ make good-target
command-that-will-fail > good-target.tmp || ( rm good-target.tmp; false )
/bin/sh: command-that-will-fail: not found
make: *** [good-target] Error 1
$ make good-target
command-that-will-fail > good-target.tmp || ( rm good-target.tmp; false )
/bin/sh: command-that-will-fail: not found
make: *** [good-target] Error 1
Redirection is done using either the ">" (greater-than symbol), or using the "|" (pipe) operator which sends the standard output of one command to another command as standard input. As we saw before, the cat command concatenates files and puts them all together to the standard output.
Just as the output of a command can be redirected to a file, so can the input of a command be redirected from a file. As the greater-than character > is used for output redirection, the less-than character < is used to redirect the input of a command.
So when you use 2>&1 you are basically saying “Redirect the stderr to the same place we are redirecting the stdout”. And that's why we can do something like this to redirect both stdout and stderr to the same place:"
On a command line, redirection is the process of using the input/output of a file or command to use it as an input for another file. It is similar but different from pipes, as it allows reading/writing from files instead of only commands. Redirection can be done by using the operators > and >> .
If you're using GNU make, you may also add the .DELETE_ON_ERROR
special target to your makefile. This will cause make to delete the output file if there is an error during execution of the commands for that file:
all: foo.o
foo.o:
echo bogus! > $@
exit 1
.DELETE_ON_ERROR:
Here's an example of this makefile in action:
$ gmake
echo bogus! > foo.o
exit 1
gmake: *** [foo.o] Error 1
gmake: *** Deleting file `foo.o'
This is easier to use than your version, since you need not modify every rule in the makefile.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With