Makefile Assertions

exist the Makefile above results in the following error:

Makefile:6: *** GNU Make Standard Library: Assertion failure: Target directory foo/ missing. Stop.

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.

include gmsl

assert_target_directory = $(call assert,$(wildcard $(dir $@)),Target directory $(dir $@) missing)

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.

About the author

John Graham-Cumming's picture John Graham-Cumming

John Graham-Cumming is Co-Founder at Electric Cloud, Inc . Prior to joining Electric Cloud, John was a Venture Consultant with Accel Partners, VP of Internet Technology at Interwoven, Inc. (IWOV), VP of Engineering at Scriptics Corporation (acquired by Interwoven), and Chief Architect at Optimal Networks, Inc. John holds BA and MA degrees in Mathematics and Computation and a Doctorate in Computer Security from Oxford University. John is the creator of the highly acclaimed open source POPFile project. He also holds two patents in network analysis and has others pending.

AgileConnection is one of the growing communities of the TechWell network.

Featuring fresh, insightful stories, is the place to go for what is happening in software development and delivery.  Join the conversation now!