9
votes

GNU Make includes a special target called .DELETE_ON_ERROR. If this is included in your Makefile, Make will delete any target whose build sequence completes with a non-zero return status. This is helpful so that in subsequent invocations Make does not assume that the target has been properly built.

Here's a dummy example.

.DELETE_ON_ERROR:

out.dat:    in.dat
            touch out.dat
            false

Because false gives a non-zero return value, the build is considered failed and Make deletes the out.dat target. This is the advertised and expected behavior. However, this behavior does not seem to be preserved when the target is a directory. Consider another dummy example.

.DELETE_ON_ERROR:

outdir/:    in.dat
            mkdir outdir/
            false

In this case, the build fails again but Make does not remove the outdir directory. Is there any way I can instruct Make to do this?

1
I don't believe so but make doesn't handle directories as prerequisites at all well anyway so this isn't that big of a deal most of the time since you can't really depend on them meaning much anyway.Etan Reisner
No you can't, at least not without modifying the GNU make source code. Make will "unlink" the target and unlink only works on files: this is hardcoded in GNU make and not configurable.MadScientist
You could perhaps put something in the recipe to remove the directory in case of failure, without using Make's DELETE_ON_ERROR. Would that solve the problem?Beta
Note that a directory is basically like a file whose content is a map of name -> inode entries. So its mtime only changes when that map changes, i.e. when you add, remove, or move-replace a file or subdirectory inside it. Make is not designed to handle directories as target or prerequisites.Johan Boulé

1 Answers

1
votes

As noted in the comments, it is hard to use timestamps on directory. Few options:

  • proxy target (%.dir)
  • Atomic update using temporary folder.

Using proxy target, Makefile can be modified to incude a '%.done' target, which will embed the cleanup logic.

.PHONY: %.dir

outdir.dir:
    $(MAKE) outdir ; if [ $? -ne 0 ] ; then echo CLEANUP $@ ; rm -rf dir ; false ; fi

outdir: ...  # as before

And use the outdir.dir as a dependency. Not elegant, but will get the work done. May be possible to to convert into a rule (disclaimer: I did not test this approach).

.PHONY %.dir

%.dir:
    $(MAKE) $* ; if [ $? -ne 0 ] ; then echo CLEANUP $* ; rmd -rf $* ; false ; fi

Another variation is to change the outdir to add a "done" indicator file (if completed successfully), and use the proxy target to validate

%.dir:
    $(MAKE) $* ; if [ ! -f $*.done ] ; then rm -rf $* ; false ; fi

outdir:
   ... commands, any can fail.
   touch $*.done

As last resort (or first option, depending on your situation), consider, 'atomic' build for outdir - creating a temporary folder, and renaming it to outdir on success

outdir:
    rm -rf [email protected] $@
    mkdir [email protected]
    # Command to create outdir.new here
    mv [email protected] $@