Atomic Rules in GNU Make

Summary:

A fundamental law of GNU Make physics is that each rule builds one and only one file (called a target).  OK, there are exceptions to that rule (which we'll see later), but nevertheless for any normal GNU Make rule, such as

a: b c

    @command

there's only one file mentioned to the left of the :.  And that's the file name that gets put into the $@ automatic variable.  And it's expected that command actual updates that file.

This article looks at what to do if command updates more than one file, and how to express that so that GNU Make knows that more than one file was updated and behaves correctly.

What not to do

Imagine a command that makes two files (a and b) from the same prerequisites in a single step.   In this article I'll simulate such a command with touch a b, but in reality it could be much more complex than that.

Here's what not to do:

.PHONY: all

all: a b

a b: c d

    touch a b

At a first glance that looks correct, it seems to be seeing that a and b are built from c and d by a single command.  If you actually run this in Make you'll get the output like:

touch a b

touch a b

The command was run twice.  In this case that's harmless, but for a real command that does real work running twice is almost certainly the wrong thing to do.   Also, if you use the -j option to run in parallel then you can end up with the command running more than once and simultaneously with itself.

This is because GNU Make actually inteprets the Makefile as:

.PHONY: all

all: a b

a: c d

    touch a b

b: c d

    touch a b

So, you end up with two separate rules (one that declares that it builds a and the other than says it builds b) that both build a and b.

Using pattern rules

GNU Make does have a way to build more than one target in a single rule using a pattern rule.  Pattern rules can have an arbitrary number of target patterns and will still be treated as a single rule.

For example,

%.foo %.bar %.baz:

    command

means that files with the extensions .foo, .bar and .baz (and of course the same prefix that will match against the %) will be built with a single invocation of command.

For example, suppose that the Makefile were:

.PHONY: all

all: a.foo a.bar a.baz

%.foo %.bar %.baz:

    command

then command would be invoked just once.  In fact it's enough to specify that just
one of the targets buildable by the pattern rule is required for the command to run:

.PHONY: all

all: a.foo

%.foo %.bar %.baz:

    command

This can be a very useful technique, for example, here's an actual rule from one of my Makefiles that builds a .lib and its associated .dll in one go:

$(OUT)/%.lib $(OUT)/%.dll: $(VERSION_RESOURCE)

   link /nologo /dll /fixed:no /incremental:no      \

        /map:'$(call to_dos,$(basename $@).map)'     \

        /out:'$(call to_dos,$(basename $@).dll)'     \

        /implib:'$(call to_dos,$(basename $@).lib)'  \

                 $(LOADLIBES) $(LDLIBS)              \

        /pdb:'$(basename $@).pdb'                    \

        /machine:x86                                 \

        $^

Of course, if the files don't have a common part then using a pattern rule won't work.  It doesn't work for the simple example I gave at the beginning.  But there is an alternative.

Using a sentinel file

A work around is to introduce a file that's used to indicate whether any of the targets have been built.  That turns multiple files into a single file.  Here's the original example rewritten:

.PHONY: all

all: a b

a b: .sentinel

    @:

.sentinel: c d

    touch a b

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.