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.
Look at this example Makefile:
X=$(YS) hate $(ZS) Y=dog YS=$(Y)$(S) Z=cat ZS=$(Z)$(S) S=s .PHONY: all all: @echo done
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 make print-X to get the value of X, or even make print-X print-XS print-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 make print-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:
print-%: ; @$(error $* is $($*)) $(filter-out print-%, $(MAKECMDGOALS)): $(filter print-%, $(MAKECMDGOALS))
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:
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 all print-X); the $(filter...) does the opposite: It just keeps the things that do match print-% (for example, just the print-X in all print-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 make all print-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 make print-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 make print-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 linefor example, by typing make print-X X=fooyou 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:
print-%: ; @$(error $* is $($*) ($(value $*)) (from $(origin $*)))
Now typing make print-X gives this useful debugging output:
Makefile:12: *** X is dogs hate cats ($(YS) hate $(ZS)) (from file). Stop.
For more about GNU Make macro debugging, see my article "Makefile Debugging: Tracing Macro Values" (www.jgc.org), which shows how to make GNU Make divulge every place a macro is used in a Makefile.