Dumping Every Makefile Variable

[article]
Summary:

What if you want to print out every variable defined in a Makefile? In this tip I'm going to show you how and introduce GNU Make's powerful functions.

        print-%:
                @echo $* = $($*)
 

But what if you want to print out every variable defined in a Makefile? In this tip I'm going to show you how and introduce GNU Make's powerful functions.

Consider the following example Makefile (cunningly named Makefile):

        X=$(YS) hate $(ZS)
        Y=dog
        YS=$(Y)$(S)
        Z=cat
        ZS=$(Z)$(S)
        S=s

        all:
 

It sets five variables: X, Y, Z, S, YS, and ZS. Using the special rule shown above it's possible to type commands like

        gmake print-X
 

to get the value of X once for each variable you are interested in. In GNU Make 3.80 the authors introduced a couple of new features that make it feasible to print out the value of all the variables defined in a Makefile with a single rule. But be prepared, this tip requires unleashing the power of GNU Make's functions. A full reference to GNU Make's functions can be found in the GNU Make manual here.

Create a Makefile named helper.mak containing the following:

        .PHONY: printvars
        printvars:
                @$(foreach V,$(sort $(.VARIABLES)),
                   $(if $(filter-out environment% default automatic,
                   $(origin $V)),$(warning $V=$($V) ($(value $V)))))
 

Before understanding how this works we can try it out on the sample Makefile above:

        gmake -f Makefile -f helper.mak printvars
 

That command directs GNU Make to load Makefile and then load helper.mak and then “build” printvars. Here's the output:

        helper.mak:3: MAKEFILE_LIST= Makefile helper.mak ( Makefile helper.mak)
        helper.mak:3: MAKEFLAGS= ()
        helper.mak:3: S=s (s)
        helper.mak:3: SHELL=/bin/sh (/bin/sh)
        helper.mak:3: X=dogs hate cats ($(YS) hate $(ZS))
        helper.mak:3: Y=dog (dog)
        helper.mak:3: YS=dogs ($(Y)$(S))
        helper.mak:3: Z=cat (cat)
        helper.mak:3: ZS=cats ($(Z)$(S))
 

GNU Make has thrown in three extra variables that weren't explicitely defined (MAKEFILE_LIST, MAKEFLAGS and SHELL), but the rest are all the variables defined in Makefile. Each line shows the name of the variable, its fully substituted value and the way in which is was defined.

Now to understand how this works. It's a lot easier to understand the long complex line used to print out the variables if we reformat it a bit, like this:

        $(foreach V,
             $(sort $(.VARIABLES)),
   	     $(if 
                 $(filter-out environment% default automatic,
                     $(origin $V)),
                 $(warning $V=$($V) ($(value $V)))
              )
         )
 

Start by finding .VARIABLES. That's a new feature of GNU Make 3.80: it's a variable whose value is a list of the names of all the variables defined in the Makefile. The first thing we do is sort it into order: $(sort $(.VARIABLES)). Then we go through the list element by element (i.e. variable name by variable name) setting a variable called V to the name of the variable we are considering: $(foreach V, $(sort (.VARIABLES)),...).

For each variable name we decide whether to print or ignore it based on where the variable was defined. If it was one of GNU Make's built-in variables (like $@ or $(CC)) or came from the environment we don't want to print it out. To make that decision we use $(if).

If $(if)'s predicate is true then we do $(warning $V=$($V) ($(value $V))) to output a warning containing the name of the variable, it's fully expanded value, and its defined value.

The other cool feature in GNU Make 3.80 is the $(value) function which outputs the value of a variable without expanding it. In our Makefile example above YS will have the value dogs when used in the form $(YS), but $(value YS) would return $(Y)$(S): it shows us how YS is defined, not its final value. That's a very useful debugging feature.

So now for the hard part: how to decide which variables to print and which to ignore. $(if) will print the variable information if its predicate is not an empty string. GNU Make is very string-centric

About the author

John Graham-Cumming's picture John Graham-Cumming

John Graham-Cumming is Co-Founder at Electric Cloud, Inc . Prior to joining Electric Cloud, John was a Venture Consultant with Accel Partners, VP of Internet Technology at Interwoven, Inc. (IWOV), VP of Engineering at Scriptics Corporation (acquired by Interwoven), and Chief Architect at Optimal Networks, Inc. John holds BA and MA degrees in Mathematics and Computation and a Doctorate in Computer Security from Oxford University. John is the creator of the highly acclaimed open source POPFile project. He also holds two patents in network analysis and has others pending.

CMCrossroads is one of the growing communities of the TechWell network.

Featuring fresh, insightful stories, TechWell.com is the place to go for what is happening in software development and delivery.  Join the conversation now!

Upcoming Events

Oct 12
Oct 21
Nov 09
Nov 09