Features¶
Although debugging GNU Makefiles is a little different than debugging, procedure-oriented programming languages, this debugger tries similar to other trepanning debuggers and gdb in general. So knowledge gained by learning this is transferable to those debuggers and vice versa.
Profiling¶
If you want to know where most of the time goes in building your system with Makefiles,
there is a --profile
option which times the targets.
This option creates Callgrind Profile Format output which can be read by KCachegrind, callgrind_annotate, or gprof2dot or other tools that understand this format.
You can get not only timings, but a graph of the target dependencies
checked. Below is an image rendered from a profiling of a remake
build:
Listing and Documenting Makefile Targets¶
Have you ever wanted rake tasks
for GNU Make? That is, you have
some strange Makefile
and you want to see the targets,
that you can run “make target-name” on?
There are two new options added to remake
to assist this:
--tasks
gives a list of targets withremake
descriptions--targets
gives a list of all targets
A target with a remake
description is just a one-line comment before the
the target in the Makefile that describes what the target does and starts with #:
If you do this, when either of these options is shown it will also be shown
with next to the target name when --tasks
is run.
Here is an example. Consider this Makefile
:
#: This is the main target
all:
@echo all here
#: Test things
check:
@echo check here
#: Build distribution
dist:
@echo dist here
Let’s run remake --tasks
:
$ remake --tasks
all This is the main target
check Test things
dist Build distribution
Many legacy [1] Makefiles don’t have descriptive comment in them
yet. So you can get a list of all targets using option
--targets
. But be warned, since GNU Make comes with lots of
implicit rule defaults, this list can be quite large.
Here is an example of runnint --targets
on the above file:
$ remake --targets -f comment.Makefile
.C
.C.o
.DEFAULT
... # about 70 more lines!
all # This is the main target
check # Test things
Makefile
dist # Build distribution
Searching for a Makefile in Parent Directories¶
When the -c
flag is given (or --search-parent
), if a Makefile or
goal target isn’t found in the current directory, remake
will search
in the parent directory for a Makefile. On finding a parent the
closest parent directory with a Makefile, remake
will set its current working
directory to the directory where the Makefile was found.
In this respect the short option -c
, is like -C
except no
directory need to be specified.
Here is a screenshot that shows make
behavior versus remake
:
Improved Execution Tracing¶
When the -x
flag is given (or --trace=normal
), any commands that
are about to be run are shown as seen in the Makefile
along with
set -x
tracing when run in a POSIX shell. Also, we override or
rather ignore, any non-echo prefix @
directive listed at the
beginning of target commands.
If different granularity of tracing is desired the --trace
option
has other settings. See the relevant parts of this manual for more information.
And, if you the most flexibility in tracing there is a built-in debugger.
Here is a screenshot that shows tracing:
Debugger¶
Features of the debugger:
- Inspect target properties
- See the current target stack
- Set breakpoints
- Set and expand GNU Make variables
- Load in Makefiles
- write a shell script containing the target commands with GNU Make variables expanded away, so the shell code can be run (and debugged) outside of make.
- Enter debugger at the outset, call it from inside a Makefile, or enter it upon the first error
See debugger for more information on the built-in debugger.
For Developers¶
If you are interested in learning about how GNU Make works, you might find it easier to start out working with this code.
First, some Doxygen comments have been added.
Second, it has been simplified as a result of the removal of lesser-used OS’s (from the standpoint of GNU Make use).
We don’t even attempt to support:
- VMS (whether on VAX or the OpenVMS variant)
- DOS (with or without EMX and DJGCC),
- native MS/Windows,
- acornOS
- Amiga,
- OS2
- MINIX,
- RiscOS
- Xenix
This is 2020, not the late 1970-80’s. Although GNU make is phasing some of these out, you can find C-preprocessor checks and C code in GNU Make for the above.
By eliminating support for the above, thousands of lines of code in support of the above has been removed.
And the remaining code is easier to read.
Sure, it has annoyed (and still annoys?) those who still work on and develop on the above. I get it. If it is any consolation, there is still GNU Make or GNU Make in older versions for such people.
However the way this code has been added makes already difficult-code to read even more difficult.
For example here is GNU Make 4.3 code from job.c
#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WINDOWS32)
remote_status_lose:
#endif
pfatal_with_name ("remote_status");
}
else
{
/* No remote children. Check for local children. */
#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WINDOWS32)
if (any_local)
{
#ifdef VMS
/* Todo: This needs more untangling multi-process support */
/* Just do single child process support now */
vmsWaitForChildren (&status);
pid = c->pid;
/* VMS failure status can not be fully translated */
status = $VMS_STATUS_SUCCESS (c->cstatus) ? 0 : (1 << 8);
/* A Posix failure can be exactly translated */
if ((c->cstatus & VMS_POSIX_EXIT_MASK) == VMS_POSIX_EXIT_MASK)
status = (c->cstatus >> 3 & 255) << 8;
#else
#ifdef WAIT_NOHANG
if (!block)
pid = WAIT_NOHANG (&status);
else
#endif
EINTRLOOP (pid, wait (&status));
#endif /* !VMS */
Can you spot which code is used in the most-often POSIX unixy case? In some cases
such as in the above, the most-often case is indented incorrectly because in
of one of less-frequent cases it is say in an else
clause (as appears above).
Note: If you have trouble parsing the above, the Pygments parser used in this document has trouble too. Even after adding mismatched braces in the above for context, I couldn’t get Pygments to parse this after specifying C source with C-preprocessor directives. So I gave up, and opted for the slightly shorter source code without some enclosing braces.
I understand how this ugly code hard-to-read code most likely came about in GNU Make. Been there and done that myself too.
In the early days to gain traction and support, a project wants to support lots of different platforms and OS’s, even obscure ones. To get going, you’ll probably do that in the most expedient way.
But again, that was then and this is now.
If there are folks in the affected communities that would like remake
added and are willing to code and do the testing, I am open to this. But it needs to be added in a more modular way than was done in the past.
Overall, I view this as a plus for developers who would like to extend GNU Make or understand the code.
[1] | As Ryan Davis explains: “legacy code” is any code you didn’t write. |