Dynamic Breakpoints in the GNU Make Debugger

[article]

for more information on this read my previous article).

1> c 
Building foo 
Building bar 
Makefile:7: GNU Make Debugger Break 
Makefile:7: - Building 'all' from 'foo bar' 
Makefile:7: - First prerequisite is 'foo' 
Makefile:7: - Prequisites 'foo bar' are newer than 'all' 
1> 

The Easy Part

To add the breakpoint functions to the GNU Make Debugger I first altered the code that handles the keyboard to recognize the b, r and l commands and call GNU Make macros called __BP_SET, __BP_UNSET and __BP_LIST.

The targets for which breakpoints are defined is simply a GMSL set of target names. Initially there are no breakpoints and so the list, called _ BREAKPOINTS is empty:

__BREAKPOINTS := $(empty_set)

Setting and removing breakpoints is a matter of calling the GMSL functions set_insert and set_remove to add or remove an element from
__BREAKPOINTS:

__BP_SET =  $(eval __BREAKPOINTS := $(call set_insert,$1,
	    $(__BREAKPOINTS)))  \              
	    $(warning Breakpoint set on `$1')  
__BP_UNSET =$(if $(call set_is_member,$1,$(__BREAKPOINTS)),\       
    	    $(eval __BREAKPOINTS := $(call set_remove,$1,
	    $(__BREAKPOINTS)))\                
            $(warning Breakpoint on `$1' removed), \                
            $(warning Breakpoint on `$1' not found))  

Both these macros use the GNU Make function $(eval) to reset the value of __BREAKPOINTS ($(eval FOO) will evaluate its argument FOO as if it were a piece of text during parsing the Makefile: this means that at run time you can change variable values or define new rules).

__BP_UNSET used the GMSL function set_is_member to determine if the breakpoint being removed was actually defined and output a helpful message in the case that the user tries to remove a non-existent breakpoint (which may be caused by a typing error on their part).

Listing the current breakpoints is simply a matter of outputting the contents of the set stored in __BREAKPOINTS. Since that set is just a list with no duplicates __BP_LIST feeds its value into the GNU Make function $(addprefix) and $(addsuffix) to put quotation marks around the target names:

__BP_LIST = $(if $(__BREAKPOINTS),  		       \               
	      $(warning Current target breakpoints:    \                   
	 	$(addsuffix ',$(addprefix `,
	      $(__BREAKPOINTS)))),                     \               
	      $(warning No target breakpoints set))   

__BP_LIST uses the GNU Make $(if) function to choose between listing the breakpoints if there are any, or saying No target breakpoints set is the __BREAKPOINTS set is empty. $(if) will evaluate its second argument if $(__BREAKPOINTS) is a non-empty string and its third is there are no breakpoints. That's because GNU Make considers 'true' to be any non-empty string and 'false' the empty string.

The Trick

To get GNU Make to break into the debugger it has to expand the $(__BREAKPOINT) variable (which outputs information about the breakpoint and prompts for commands). But for that to happen we need a way to check which breakpoints are defined every time a rule is about to run and then expand $(__BREAKPOINT) if necessary.

Luckily it's possible to do this by modifying GNU Make's own SHELL variable. SHELL stores the name of the shell that should be used to execute commands (it's usually something like /bin/sh on Linux), and is defined by default. But it can be overriden like any other variable by writing SHELL = /my/shell in a Makefile; all commands will be run using that shell.

The SHELL variable is also expanded every time a command is about to run inside a rule. That makes it ideal for the purpose of checking breakpoints. Here's the actual code in the GNU Make Debugger that uses SHELL for breakpoint handling:

__BP_OLD_SHELL := $(SHELL) 
__BP_NEW_SHELL = $(if $(call seq,$(__BP_FLAG),$@),      \                      
	$(call $1,),                                    \                 
	$(__BP_CHECK))$(__BP_OLD_SHELL) 
SHELL = $(call __BP_NEW_SHELL,$1) 

First the real value of SHELL is stored in __BP_OLD_SHELL (you'll note that the GNU Make := operator is used to

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

Sep 22
Sep 24
Oct 12
Nov 09