Shell – Make mistakenly thinks rule succeeded because of 0-sized file generated by output redirect

io-redirectionmakeshell

In a makefile, I have several rules that look like this:

out.txt: foo.sh input.txt
  ./foo.sh -i input.txt > out.txt

If foo.sh fails, then out.txt will be created as a 0-sized file. If I run make again, it will mistakenly assume that the out.txt file was created successfully, and it won't run the rule again.

What's the right way to handle this sort of scenario?

Best Answer

You can request that make delete the target file if the rule fails, by defining a special target named .DELETE_ON_ERROR. It doesn't need to do anything or have any dependencies, so just add this to your makefile:

.DELETE_ON_ERROR:

Then you get the following:

$ cat Makefile
.DELETE_ON_ERROR:
foo:
    false > foo

$ make
false > foo
make: *** [foo] Error 1
make: *** Deleting file `foo'
zsh: exit 2     make

$ stat foo
stat: cannot stat `foo': No such file or directory

From Errors in Recipes:

Usually when a recipe line fails, if it has changed the target file at all, the file is corrupted and cannot be used—or at least it is not completely updated. Yet the file's time stamp says that it is now up to date, so the next time make runs, it will not try to update that file. The situation is just the same as when the shell is killed by a signal; see Interrupts. So generally the right thing to do is to delete the target file if the recipe fails after beginning to change the file. make will do this if .DELETE_ON_ERROR appears as a target. This is almost always what you want make to do, but it is not historical practice; so for compatibility, you must explicitly request it.

Related Question