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 a TechWell community.

Through conferences, training, consulting, and online resources, TechWell helps you develop and deliver great software every day.