Web Development

Debugging Makefiles

What's the Value of Macro X?

Frequently, Makefile debugging requires getting the value of a Make macro. Because Make doesn't have a debugger, it's hard to find out the value of a macro without a great deal of searching through Makefiles for macro definitions.

If you follow the definitions of X, Y, S, YS, and ZS, you can discover that X has the value dogs hate cats. In a real Makefile, unwinding the definition of a macro is next to impossible. The simplest way to find out the value of a macro in a Makefile is to use the $(warning...) function. When GNU Make encounters $(warning...), it prints the message contained in the warning along with the name of the Makefile and line number. $(warning...) is GNU Make's printf-style of debugging.

Just adding $(warning X is $(X)) as the last line of this Makefile causes GNU Make to print:

Makefile:11: X is dogs hate cats

But the printf-style is just as inflexible in Make as it is in other languages. The Makefile has to be constantly modified and rerun while digging down to discover the cause of a problem. What's needed is a more interactive approach.

You can add a GNU Make pattern rule that enables the name (or names) of the macros to be printed to be specified on the GNU Make command line. This approach is more flexible than modifying the Makefile; all you need is a single pattern rule to print the value of any macro. Adding this line to the previous example Makefile means that it's possible to type makeprint-X to get the value of X, or even makeprint-Xprint-XSprint-Y to get the values of a number of macros:

print-%: ; @echo $* is $($*)

This works because GNU Make attempts to build the goals specified on the command line. If you type makeprint-X, the print-X goal matches the print-% rule, with % matching the name of the variable (in this case, X). The part matched by % is stored in the GNU Make automatic variable $*; $* is used twice in the command for the print-% rule: First it's used to print the name of the variable, then it's used to get that variable's value by writing $($*). When $($*) is seen, GNU Make expands $* (for example, to X), then expands the resulting macro reference (for example, $(X)).

There are two problems with the print-%-style:

If the macro's value changes inside a Makefile (which is an unusual, but not impossible, event), print-% only gives the final value of the macro (the value of the macro once all the Makefiles have been parsed). The only good solution to this is to revert to the $(warning...) style.

Also, GNU Make lets macros have local scope in the form of target-specific macros. The example Makefile can be modified to include a target-specific value for the X macro in the scope of the all rule:

.PHONY: all
all: X = $(ZS) love $(YS)
all:
@echo done

If all is being built, then X has a different value in that rule than anywhere else in the Makefile. The print-% method can be extended to print the value of target-specific macros:

First, the print-% rule has been modified so that when it runs, it prints the same message as before, but stops GNU Make with an error. This is required because to get the target-specific value of the macro you need to tell GNU Make to build those targets, and since you don't actually want GNU Make to build (you just want the target-specific macro scope to be set up), the fatal error created with $(error...) stops GNU Make once it has printed the value of the macro requested.

To get the target-specific macro scope, the improved version specifies that the print-% rule is a prerequisite of the target for which there may be a target-specific macro. This is done using normal GNU Make syntax. For example, if you want to print the value of X as it's seen from within the all rule, type make all print-X. The second line in this improvement translates to:

all: print-X

The $(filter-out...) first removes everything on the command line (stored in the GNU Make variable MAKECMDGOALS) that doesn't match the pattern print-% (for example, just the all in allprint-X); the $(filter...) does the opposite: It just keeps the things that do match print-% (for example, just the print-X in allprint-X).

Because target-specific macros are inherited by any prerequisites of a target, writing all:print-X means that the print-X rule (which is handled by the pattern rule print-%) will have any target-specific macros defined for all. That's exactly what you want, and typing makeallprint-X on the aforementioned example outputs:

Makefile:12: *** X is cats love dogs. Stop.

The target-specific macro-aware version of print-% can still output the global value of a macro by omitting any targets on the command line. For example, typing makeprint-X outputs the global value of X:

Makefile:12: *** X is dogs hate cats. Stop.

In addition to the value of a macro, it can also be helpful to find out where it was defined (for example, was it defined in a Makefile, from the environment, or perhaps overridden on the Make command line?).

GNU Make provides the $(origin...) function that returns a string describing how a macro was defined. A modification to the print-% rule makes it print this information in addition to the value of the macro:

print-%: ; @$(error $* is $($*) (from $(origin $*)))

And so makeprint-X now prints:

Makefile:12: *** X is dogs hate cats (from file). Stop.

The (from file) indicates that X was defined in a Makefile. If X had come from the environment, it would read (from environment). If X were overridden on the Make command linefor example, by typing make print-X X=fooyou would see this output:

Makefile:12: *** X is foo (from command line). Stop.

One final enhancement to print-% is to get it to print the definition as well as the value of a macro. GNU Make's confusingly named $(value...) function does just that:

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!