Channels ▼
RSS

Web Development

Debugging Makefiles


How Does Make Log File Output Relate?

In the previous section, a complete build (for example, where runme, foo.o, and bar.o were missing before typing make) produces no output. That's because all the commands in the Makefile were prefixed by @, which prevents GNU Make from echoing them.

GNU Make's -n option provides a way to echo all the commands without executing them. This is handy for dry runs of the Makefile. The output from make -n on the example Makefile is:


 g++ -c -o foo.o foo.c
 g++ -c -o bar.o bar.c
 cc foo.o bar.o -o runme


In this example, it's relatively easy to relate these lines to specific rules in the Makefile; however, it can be hard on large, real-world Makefiles. And since -n doesn't actually run these commands, it's no help when trying to figure out why a rule failed to execute correctly.

One solution is to replace every @ with $(AT). Setting AT to @ in the Makefile makes it behave as before. Setting AT to blank means that commands are echoed. For example, the Makefile can be modified to use $(AT) instead of @:


 .PHONY: all
 all: runme
 runme: foo.o bar.o
   $(AT)$(LINK.o) $^ -o $@
 foo.o: foo.c
 bar.o: bar.c
 %.o: %.c
   $(AT)$(COMPILE.C) -o $@ $<
 AT := @

This behaves exactly as before, except that it's possible to override the definition of AT on the command line, causing GNU Make to print the commands it is executing. Running make AT= effectively strips the @ from each command and produces the same output as make -n, except that the commands are actually run.

Of course, modifying every command in a Makefile might not be realistic.

The SHELL hack can be modified to cause GNU Make to output command information. Almost all shells have an -x option that causes them to echo the commands that they are about to execute. By modifying SHELL to include -x (and a $(warning...)) that outputs the location of the rule in the Makefile) and wrapping the modification in an ifdef, you can enable/disable detailed debugging information as needed. Here's the SHELL hack for command dumping:


 ifdef DUMP
 OLD_SHELL := $(SHELL)
 SHELL = 
    $(warning [$@])$(OLD_SHELL) -x
 endif

If DUMP is defined then SHELL is modified to add the -x option and output the name of the target being built. If foo.o, bar.o, and runme are missing, typing make DUMP=1 produces this output using the SHELL hack:


 Makefile:11: [foo.o]
 + g++ -c -o foo.o foo.c
 Makefile:11: [bar.o]
 + g++ -c -o bar.o bar.c
 Makefile:5: [runme]
 + cc foo.o bar.o -o
 runme

Each file being built is in square brackets with the name of the Makefile and the location where the rule to build that file can be found. The shell outputs the exact command to be used to build that file (prepended by +).

In just four lines of GNU Make code, you can turn an empty log file into one with complete file, line number, and command information. This is useful when debugging a failing rule. Just rerun the Makefile with DUMP=1 to find out what was being built and where it was in the Makefile. The only disadvantage to the SHELL hack is that it slows GNU Make. That's because GNU Make tries to avoid using the shell if it can; for example, if GNU Make is doing a simple g++ invocation, then it can avoid the shell and execute the command directly. If SHELL is modified in a Makefile, GNU Make disables this optimization.

Extra Resources

If these ideas whet your appetite for better GNU Make debugging, look at the GNU Make Debugger project (http://gmd.sf.net). GMD is an interactive debugger for GNU Make written entirely using GNU Make functions that provides breakpoints and the ability to examine the value and definition of any macro at runtime.

And if you do something really cool building on the SHELL hack, drop me a line. I'd love to hear about it.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 
Dr. Dobb's TV