tags:

views:

30

answers:

2

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

A: 

Yes, this is definitely something to bear in mind.

But sometimes you're OK with the command failing and you don't want to retry it, because the error would only repeat itself.

In that case leaving an empty result is OK. Just don't make subsequent processing count on there being meaningful content in that file.

reinierpost
+1  A: 

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.

Eric Melski