An Interactive GNU Make Debugger


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 5 6 7 8 9 10 11 12 13 14 

About the author

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

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