shellmakefilestdoutgnu-makeredirectstandardoutput

Elegant way to remove target file if a Make rule fails


I have a rule in my Makefile that roughly looks like this:

target_file: src_file
        some_tool src_file > target_file

(Of course, in reality I'm using $@ and $<, but for this question I prefer a more explicit style.)

The problem is that target_file is always created by the shell with a fresh timestamp, even if some_tool fails. In that case, an empty target_file exists, and even if I fix the underlying issue, it won't be rebuilt until I manually remove target_file or touch src_file.

Also, some_tool only writes to standard output, so I can't work around this through a cleaner approach such as some_tool ... -o target_file.

My current approach is to remove

target_file: src_file
        some_tool src_file > target_file || rm -f target_file

However, this has the disadvantage that Make won't notice if some_tool fails, because in that case rm takes over and returns exitcode 0 (succeess).

Another approach may be:

target_file: src_file
        some_tool src_file > target_file.tmp
        mv target_file.tmp target_file

But that kind of code is tedious and on failure leaves an annoying file target_file.tmp behind.

Is there a more elegant way to solve this problem?


Solution

  • You could use the special target .DELETE_ON_ERROR:

    If .DELETE_ON_ERROR is mentioned as a target anywhere in the makefile, then make will delete the target of a rule if it has changed and its recipe exits with a nonzero exit status, just as it does when it receives a signal.

    All it takes is one line:

    .DELETE_ON_ERROR:
    

    And all rules that fail will have their target removed.