An Interactive GNU Make Debugger

[article]

a single command. First it stores the result of __PROMPT in __INPUT and the it calls the __DEBUG function (which handles debugger commands) with two arguments: the command and its argument returns by __PROMPT in __INPUT.

__BREAK = $(eval __INPUT := $(__PROMPT))                                    \ 
           $(call __DEBUG,                                                   \ 
               $(word 1,$(__INPUT)),                                         \ 
               $(word 2,$(__INPUT)))                        
  

The core of the debugger is handled by the __DEBUG function. __DEBUG takes a single character command in its first argument ($1) and an optional argument to the command in $2. $1 is stored in the variable __c, and $2 in __a. Then __DEBUG examines __c to see whether it is one of the supported debugger commands (c, q, v, d, o, or h) (if not a call to $(warning) will output an error message).

__DEBUG consists of a set of nested $(if) statements that use the GMSL seq function to determine is the __c is a valid debugger command. If it is then $(if)'s first argument is expanded, if not then the next $(if) is examined. For example, the v command (which outputs the value of a variable) is handled like this: $(if $(call seq,$(__c),v),$(warning $(__a) has value '$($(__a))'), ... next if ... ). If the __c command is v then $(warning) is used to output the value of the variable named by __a (the $($(__a)) outputs the value of the variable whose name is stored in __a).

When __DEBUG is done it returns either $(true) or $(false) (the empty string). $(true) indicates that the debugger show stop prompting for commands and continue execution. (The q command is handled by calling GNU Make's $(error) function to cause a fatal error, which stops the Make).

__DEBUG =  $(eval __c = $(strip $1))                                         \ 
            $(eval __a = $(strip $2))                                         \ 
            $(if $(call seq,$(__c),c),                                        \ 
               $(true),                                                       \ 
               $(if $(call seq,$(__c),q),                                     \ 
                  $(error Debugger terminated build),                         \ 
                  $(if $(call seq,$(__c),v),                                  \ 
                     $(warning $(__a) has value '$($(__a))'),                 \ 
                     $(if $(call seq,$(__c),d),                               \ 
                        $(warning $(__a) is defined as '$(value $(__a))'),    \ 
                        $(if $(call seq,$(__c),o),                            \ 
                           $(warning $(__a) came from $(origin $(__a))),      \ 
                           $(if $(call seq,$(__c),h),                         \ 
                              $(warning c       continue)                     \ 
                              $(warning q       quit)                         \ 
                              $(warning v VAR   print value of $$(VAR))       \ 
                              $(warning o VAR   print origin of $$(VAR))      \ 
                              $(warning d VAR   print definition of $$(VAR)), \ 
                              $(warning Unknown command '$(__c)'))))))) 
  

Finally, we come to the definition of __BREAKPOINT (the breakpoint variable we used in the example above). It first outputs a banner containing information (see __BANNER below), then it loops asking for commands by calling __BREAK. The loop terminates if it either runs out of items in __LOOP (which is where the 32 command limit is defined, see above), or if a call to __BREAK returns $(true)).

__BREAKPOINT = $(__BANNER)                                                   \ 
                $(eval __TERMINATE := $(false))                               \ 
                $(foreach __HISTORY,                                          \ 
                    $(__LOOP),                                                \ 
                    $(if $(__TERMINATE),,                                     \ 
                       $(eval __TERMINATE := $(__BREAK)))) 

__BANNER is used to show that the debugger has stopped at a breakpoint and by examining GNU Make automatic variables it is able to give information about the current rule being built.

__BANNER = $(warning GNU Make Debugger Break)                                \ 
            $(if $^,                                                          \ 
               $(warning - Building '$@' from '$^'),                          \ 
               $(warning - Building '$@'))                                    \ 
            $(if $<,$(warning - First prerequisite is '$<'))                  \ 
            $(if $%,$(warning - Archive target is '$%'))                      \ 
            $(if $?,$(warning - Prequisites '$?' are newer than '$@')) 

ConclusionAnd there you have it. An interactive GNU Make debugger written entirely in GNU Make. Next time I'll build on this foundation to provide interactive tracing. In the meantime, I hope that this debugger helps you with your Makefile problems.

The Debugger for Copy and Paste

include gmsl 
  
 __LOOP := 1 2 3 4 

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.

AgileConnection 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

May 04
May 04
May 04
Jun 01