Makefile Assertions


Unfortunately, GNU Make does not have any form of assertions built in, but they are easy to create using existing GNU Make functions.

Unfortunately, GNU Make does not have any form of assertions built in, but they are easy to create using existing GNU Make functions. There are even convenient assertion macros defined in the GNU Make Standard Library.

The GNU Make Standard Library (GMSL) project provides two assertion macros: assert and assert_exists. assert will output a fatal error if its first argument is false. In GMSL terms (and for GNU Make's $(if) function) true is any non-empty string, and false is an empty string. Thus if assert's argument is an empty string the assertion will cause a fatal error; the second argument to assert is printed as part of the error. For example, this Makefile breaks immediately with an assertions because $(FOO) and $(BAR) are the same:

include gmsl

FOO := foo
BAR := foo

$(call assert,$(call sne,$(FOO),$(BAR)),FOO and BAR should not be equal)

It prints the message:

Makefile:5: *** GNU Make Standard Library: Assertion failure: FOO and BAR should not be equal. Stop.

The macro uses another GMSL function, sne, which compares to strings and returns true if they are not equal, or false is they are the same. Because true is simply "not an empty string", it's easy to assert that a variable be defined:

include gmsl

$(call assert,$(FOO),FOO is not defined)

This can be used, for example, to check that a user has set all the necessary variables on the command-line; if FOO is set on the command-line of GNU Make then the assertion will not cause an error. The assertions could even be used to enforce that certain command-line flags are not used. Here's an example that prevents the user from setting -i (the ignore errors flag):

include gmsl

$(foreach o,$(MAKEFLAGS),$(call assert,$(call sne,-i,$o),You can't use the -i option))

ifneq ($(patsubst -%,-,$(firstword $(MAKEFLAGS))),-)
$(call assert,$(call sne,$(patsubst i%,i,$(patsubst %i,i,$(firstword $(MAKEFLAGS)))),i),You can't use the -i option)

It's more complex than the previous two examples because GNU Make can store the -i flag in the variable MAKEFLAGS (which contains a record of the command-line options passed to GNU Make) in two ways: as a flag in the familiar form -i, or as a block of single characters in the first word of MAKEFLAGS: e.g. the command-line flags -i -k results in MAKEFLAGS having the value ki. The first assert in the loop looks for -i, the second assert searches for i in the first word of MAKEFLAGS.

Because the presence or absence of files is so vital to GNU Make, the GMSL provides an assertion specifically design to warn if a file is missing: assert_exists. assert_exists has a single argument, the name of the file that must exist. For example, it's possible to check that the file foo.txt exists before any commands are run by the Makefile by adding an assertion at the start:

include gmsl

$(call assert_exists,foo.txt)

If the file does not exist then an assertion stops the Make:

Makefile:3: *** GNU Make Standard Library: Assertion failure: file 'foo.txt' missing. Stop.

A common problem in building real-world Makefiles that directory hierarchies must be constructed during or before the build. Using assert_exists it's possible to ensure that every directory exists before each rule runs. Using a combination of GNU Make and GMSL functions and macros the special assert_target_directory macro can be created:

include gmsl

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

foo/all: ; @$(call assert_target_directory)echo $@

By inserting $(call assert_target_directory) at the start of each rule's body (or the start of a pattern rule's body), GNU Make will automatically check that the directory in which the target is to be written exists. For example, if foo/ does not

About the author

CMCrossroads is a TechWell community.

Through conferences, training, consulting, and online resources, TechWell helps you develop and deliver great software every day.