GMAKE cheap trick #4 for non-recursive make

One of the tedious sides of non-recursive make is that all makefiles share the same namespace. Every variable or pseudo-target name must be unique across all subprojects, or must be a well-known global variable/target, to which a subproject’s bits and pieces are appended. Appending to global variables doesn’t alway have the desired effect, either.

For example, these kinds of rules/assignments work (assuming EXPORT_PGMS is used and owned by

EXPORT_PGMS += rsaencrypt
RSAENCRYPT_testfiles = foo.dat bar.dat

all : rsaencrypt
clean .PHONY: rsaencrypt.clean
rsaencrypt.clean :; rm -f ${RSAENCRYPT_testfiles }

But these do not:

CFLAGS = -g # problem: previous CFLAGS wiped out
CFLAGS += -Imock # oops: now ALL subprojects see this flag
clean:; rm -f test.out # fails; "clean:" actions can only be defined once
clean::; rm -f test.out # fails if "clean:" is defined anywhere.

A second tedious side is that the current working directory will not be the one where the Makefile and source files live. That means that every file reference must have a directory path. The obvious change makes the makefile harder to read, and hence more prone to mistakes …

rsaencrypt.source = ${rsaencrypt}/alpha.c ${rsaencrypt}/beta.c ${rsaencrypt}/gamma.c \
                    ${rsaencrypt}/delta.c ${rsaencrypt}/beta.c ${rsaencrypt}/epsilon.c \

One approach is:

rsaencrypt.source = alpha.c beta.c gamma.c delta.c epsilons.c digamma.c : ${rsaencrypt.source:%=${mydir}/%}
# ... or without the extra variable: : $(addprefix ${rsaencrypt}/, alpha.c beta.c gamma.c delta.c epsilons.c digamma.c)

However, there’s a little abbreviation trick that can help. All of my makefiles begin like this:

~ := rsaencrypt
. := $(word 1,${$_} .)

There’s nothing special about the choice of characters; any of $! $+ $- $, $` $_ will also work. The object is to have an unobtrusive single-character abbreviation, avoiding letters, digits and gmake’s own $@ $< $^ $* : = # .

The $(word...) expression sets $. to . if no parent makefile has set ${rsaencrypt}. Long variable names that are just following cross-project conventions can be prefixed with “$~…”:

$~source = $./alpha.c $./beta.c $./gamma.c $./delta.c $./epsilons.c $./digamma.c
$ : ${$~source}

Note the :=, not =. That means that, as sub-makefiles are included, the definitions of $~ and $. change from that point on. Which also means that include ${subdir}/submakefile lines must come at the bottom of the makefile, and successive include directives can’t use $. for the pathname. Well, nothing’s perfec. 🙂

About mischasan

I've had the privilege to work in a field where abstract thinking has concrete value. That applies at the macro level --- optimizing actions on terabyte database --- or the micro level --- fast parallel string searches in memory. You can find my documents on production-system radix sort (NOT just for academics!) and some neat little tricks for developers, on my blog My e-mail sig (since 1976): Engineers think equations approximate reality. Physicists think reality approximates the equations. Mathematicians never make the connection.
This entry was posted in make, non-recursive make. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s