Inverse Dependency Makefiles

Wed Oct 30, 2019
~400 Words
Tags: programming, make

When working in C or C++, I typically use GNU make as a build system. The C/C++ ecosystem has not really come to a consensus on a preferred build system, but this is not a post about why you might (or might not) choose make. However, one weakness is that make does not provide tools to help with discovery or setting build configuration. I recently found a simple trick that helps in this regard.

For build configuration, I frequently use an extension to make that sets a value only if it is not set. Users can override the variable either by setting an environment variable, or on the command line. This provides the make recipes with the necessarily flexibility, but unfortunately users need to remember to set the variables before every build. Simply switching to a new terminal, which will reset your environment variables, can lead to build errors.

A part of the configuration in the makefile typically looks like the following:

# Configure input and output paths for the build
SRCDIR ?= .
VPATH = $(SRCDIR)
PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin

Another extension is to include other makefiles. This is not an extension that I have used, but was used by others to build more modular makefiles. One approach is to create a file called config.mk or similar, which is included. Users are then directed to edit config.mk to match their local configuration.

The simply trick – invert the dependency, and have config.mk call the main makefile. Not only does this create a stable build configuration, but it better supports out-of-tree builds. To illustrate, when building, I create a sub-directory called build (or whatever your preference), and create a makefile. The following file points to the source directory, configures any parameters that need to be overridden, and then otherwise acts like the original makefile.

# Required:  Point to the source directory
SRCDIR = ..

# Optional:  Override build parameters as desired for this build
PREFIX = $(HOME)/.local

# Required:  Include all of the targets of the original build
include $(SRCDIR)/makefile

There is nothing complicated in this trick, but simply turning the problem around has led to a more usable approach. A simple trick, but one that has improved the usability of my makefile based projects.

Places to join the discussion