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
__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.
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 capture the value, and not the definition, of SHELL). Then SHELL itself is redefined to call the __BP_NEW_SHELL macro.
__BP_NEW_SHELL is where the interesting work is done. The last part of it is $(__BP_OLD_SHELL) which is the value of the original SHELL variable. After all, once we are done checking breakpoints, we'd like GNU Make to use the original shell. Before that there's a