Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

Channels ▼

Al Williams

Dr. Dobb's Bloggers

Makefile Madness

February 12, 2011

Most of the work I do these days is with the GNU toolset, and that's a good thing. Even if I'm coding for, say, an Atmel AVR or an ARM processor, I have the familiar or Linux/Unix-style C compiler and tools. Between Unix and Linux, I've used these tools for a very long time and they are very comfortable to me.

However, sometimes you get too comfortable. Tools grow and change over time, but how often do you learn new features. Sure, I know some of them exist, but I tend to stick with my old familiar patterns.

This week I was building some code that is targeting multiple outputs and to do it nicely I was forced to break out of my comfort zone and use a few features of Make (and gcc) that I haven't tried before.

I've mentioned before that I'm lazy, and that's certainly true. I like tools to do the work for me. But apparently I'm not as lazy as some programmers. For example, consider Make's default rules. Sure, I know they are there, but I don't mind typing a command line into a Makefile just to be sure I have complete control of things. But where I draw the line is having to manage the dependency graph of my code -- especially as this was a pretty complex program with lots of parts.

I usually just write a pretty basic makefile with explicit command lines and -- for a small project -- I just manually set up the dependencies. But for big projects, I like makedepends. This automatically scans your source files and tells you what .H files they rely on.

But this project was different. My plan was to have all my source code in one directory and then have separate output directories (and Makefiles) for each target build. It turns out, basic Make was not meant to handle that neither was makedepends.

I figured while I had the Make manual open to look up some new features, I'd bite the bullet and give up my command lines for default rules where possible.

I learned a few interesting things. First of all, Make has a way to set search paths for various items. You can set the VPATH variable and Make will look for prerequisites and targets that it can't find in the current directory. That turned out to be what I needed.

If you need more control you can use the vpath directive (which is lowercase, unlike the uppercase variable). If you like to keep your headers in a separate directory, for example, you could write: "vpath %.h ../headers"

That solved part of my problem. But I could never quite get makedepends to sort out the directory structure. I decided to use the gcc method of managing dependencies.

If you run gcc with the -MM option, any -D options you use to define preprocessor symbols, and a source file name, you'll get an output like this:

rfp.o: rfp.cpp rfp.h rs232.h iobase.h 
Oddly enough, that looks just like a Make rule. So you could -- if you weren't as lazy as I am -- run this command on each source file and make a separate Makefile for each source file (traditionally these files have a .d extension, as in rfp.d). The problem is, you have to remember to rebuild these .d files when something changes. And that's what Make is for!

The answer is contained in the Make manual. You want to make the .d file depend on the same files so that if any of them change, the .d file gets regenerated.

I modified the example in the help a bit, and put it in a file I can include with all my Makefiles:

%.d: %.c
	@set -e; rm -f $@; \
	$(CC) -MM $(CFLAGS) $< | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' > $@; 

%.d: %.cpp
	@set -e; rm -f $@; \
	$(CXX) -MM $(CXXFLAGS) $< |  sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' > $@; 


ifneq ($(MAKECMDGOALS),clean)
include $(DEPS)

This assumes you have all your source files named in a SRCS variable. The two rules cause gcc to scan .c and .cpp files for dependencies. The sed regular expression transforms the rules to this format:

rfp.o rfp.d: rfp.cpp rfp.h rs232.h iobase.h 

Then the include reads in all the generated Makefiles (unless you are running the clean target). This way you get rules for every C or C++ file that depends on the correct set of headers. Good thing I switched to the default rules or my sed command would have to be much more complex!

The only time I've had any trouble is when you delete a header. Probably a good idea to erase all the .d files in your Makefile's clean rule.

Old habits die hard. But a little work up front will pave the way for future laziness. If you want to read more about debugging Makefiles, check out John Graham-Cumming's DDJ article on the subject. What's your favorite Make trick?

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.