One of my favorite makefile debugging tricks is this rule for printing out the value of a variable:
print-%: @echo '$*=$($*)'
Throw this into a GNU make makefile and then print any make variable you like by invoking targets like print-MAKE_VERSION:
ericm@chester:/tmp$ gmake print-MAKE_VERSION MAKE_VERSION=3.81
You can imagine how handy this is when diagnosing issues with your makefiles. Here’s how it works:
- print-% defines a pattern rule that matches any target that starts with the charactersprint-.
- In the context of a pattern rule, the $* variable expands to the stem of the target, that part which matched the % in the pattern. In my example above, that corresponds toMAKE_VERSION.
- GNU make variable expansion rules allow for variable references inside variable names, so$($*) expands first to $(MAKE_VERSION), and finally to the value of the MAKE_VERSIONvariable.
Makefile injection with -f
The print-% rule is a slick hack, but it’s a nuisance to have to modify a makefile just to use it. Worse, you might not even be able to modify the makefile. Fortunately, there’s a solution: the -fcommand-line option. You’re probably familiar with it — that’s how you tell gmake to use a different makefile than the default Makefile when it starts. For example, if you have a makefile namedbuild.mak:
gmake -f build.mak
What you may not know is that you can use multiple -f options on the command line. GNU make will read each file in turn, incorporating the contents of each just as if they were included with theinclude directive. We can create a simple makefile called printvar.mak containing nothing but our print-% rule, then inject it into any makefile we want like this:
gmake -f printvar.mak -f Makefile print-MAKE_VERSION
A shell script to save typing
The combination of the print-% rule and the -f command-line option is powerful, but it’s unwieldy — too many characters to type. The solution is a shell script wrapper:
#!/bin/bash filename="" if [ -f GNUmakefile ] ; then filename="GNUmakefile" elif [ -f makefile ] ; then filename="makefile" elif [ -f Makefile ] ; then filename="Makefile" fi if [ -n "$filename" ] ; then vars="" for n in $@ ; do vars="$vars print-$n" done gmake -f $filename -f printvar.mak $vars else echo "No makefile found" 1>&2 exit 1 fi
Save that in a file called printvars somewhere on your PATH and you can do things like this:
ericm@chester:/tmp$ printvars MAKE_VERSION COMPILE.cc MAKE_VERSION=3.81 COMPILE.cc=g++ -c
Advanced make variable diagnostics
Beyond simply printing the value of a variable, GNU make 3.81 has three built-in functions that allow introspection on variables, which you can add to the print-% rule for additional diagnostics.
First is the $(origin) function, which tells you how a variable was defined. For example, if a variable FOO was inherited from the environment, $(origin FOO) will give the resultenvironment. Variables defined in a makefile will give the result file, and so forth.
Finally is the $(value) function, which gives you the unexpanded value of the variable. For example, if you have variables like this:
$(value BAR) will give the result $(FOO), rather than the fully-expanded 123 that you might expect.
With these additions, the print-% rule now looks like this:
print-%: @echo '$*=$($*)' @echo ' origin = $(origin $*)' @echo ' flavor = $(flavor $*)' @echo ' value = $(value $*)'
And here’s how it looks in action:
ericm@chester:/tmp$ printvars MAKE_VERSION COMPILE.cc MAKE_VERSION=3.81 origin = default flavor = simple value = 3.81 COMPILE.cc=g++ -c origin = default flavor = recursive value = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c