And the error itself gives the name of the Makefile and the line number at which the problem occurred, making it trivial to find the rule that has a problem.
For a final trick it's possible to make the Makefile check every single rule for a missing directory with a two line modification to the Makefile. Instead of adding $(call assert_target_directory) to every rule, just redefine the $(SHELL) variable to include $(call assert_target_directory). This does slow performance, but can be useful in tracking down a missing directory somewhere deep in nested Makefiles.
assert_target_directory = $(call assert,$(wildcard $(dir $@)),Target directory $(dir $@) missing)
OLD_SHELL := $(SHELL)
SHELL = $(call assert_target_directory)$(OLD_SHELL)
foo/all: ; @echo $@
GNU Make expands the value of $(SHELL) and hence performs a call to assert_target_directory for every rule that is run. That simple change means that every rule run is automatically checked.
The new value of $(SHELL) consists of a call to assert_target_directory (which always returned an empty string) followed by the old value of $(SHELL) stored in OLD_SHELL. Note how OLD_SHELL is defined using := so that SHELL doesn't refer to itself. OLD_SHELL contains the value of $(SHELL) at run time and can be safely used to redefine $(SHELL). If OLD_SHELL were defined using = then GNU Make would fail to run because of a circular reference SHELL, which would refer to OLD_SHELL which would refer to SHELL.
assert_target_directory works by calling $(wildcard) (a GNU Make function) with the name of the directory in which the current target being built is to be written. The target is defined by the GNU Make automatic variable $@ and the directory portion is extracted with $(dir). $(wildcard) simply checks to see if the directory exists or not and returns the name of the directory (if it exists), or the empty string if the directory is missing. Recall that the assert function interprets a non-empty string as true, and empty as false, to see that the assertion only fires if the directory is missing.
The trick of redefining SHELL so that some function is performed for every rule executed by the Makefile has uses beyond assertions. In a future article I'll show how redefining shell allows tracing of the execution path of a Makefile and even the generation of an XML document describing a run of Make.