(GNU) Make

Page Contents

References / useful Links

Makefile Basics

A Makefile descibes how your program is built by specifying everything that it depends on and building the dependencies first. A Makefile rule looks like this:

target_1 ... target_n: prerequisite_1 ... prerequisite_m
<tab>   command_1   # NOTE that each command
<tab>   ...         # is executed in its own
<tab>   command_j   # subshell.

Here:

  • Target is anything the must be made.
  • Prerequisite are things that must be built before the target can be built.
  • Commands are shell commands used to build the target. NOTE, each command executed in own subshell!
  • Rule is what's shown above - it defines how to build a target.

To performa a build, make will construct a directed acyclic graph (DAG) from the rules. It will then do a post order traversal (visit children first), building the leaf nodes first and going back up the graph.

A very simple example is shown below:

A Makefile DAG diagram

The makefile for the above diagram would look something like this:

target: prequisite_1 prequisite_1 prequisite_1
<tab>   commands_A

prequisite_1: prequisite_4
<tab>   commands_B

prequisite_2: prequisite_5
<tab>   commands_C

prequisite_3: prequisite_5 prequisite_7
<tab>   commands_D

prequisite_4:
<tab>   commands_E

prequisite_5: prequisite_6
<tab>   commands_F

prequisite_6:
<tab>   commands_G

prequisite_7:
<tab>   commands_H

The post-order traveral is shown by the orange-background numbers.

  1. Make looks at target and visits the first child, prerequisit_1. It can see that prerequisit_1 depends on prerequisit_4. So now, prerequisit_1 becomes the "target". If make determines that prerequisit_1 is newer than prerequisit_4, it will build prerequisit_4. We assume that it does and builds prerequisit_4 using commands_E.
  2. Once prerequisit_4 is built, all of prerequisit_1's dependencies have been visited so prerequisit_1 can now be built using commands_B.
  3. Make returns back to target and examines the next dependency, prerequisit_2. Assuming prerequisit_5 is older than prerequisit_2, and prerequisit_6 is older than prerequisit_5, the post-order traversal will result in make next trying to build prerequisit_6 using commands_F.
  4. All of prerequisit_5's dependencies have been built so make can now build prerequisit_5 using commands_F.
  5. All of prerequisit_2's dependencies have been built so make can now build prerequisit_2 using commands_C.
  6. Make returns back to target and examines the next dependency prerequisit_3. There are two dependencies, prerequisit_5 and prerequisit_7. Make has already built prerequisit_5 so it knows that it does not beed to build this again, so it ignores this path... nice! Thus all that is left is to look at prerequisit_7, asumming it is older than prerequisit_3. Assume it is, so make builds it.
  7. All of prerequisit_3's dependencies have been built so it can now be built.
  8. Make returns back to target and because all of its dependencies have been built, it can finally be built.

By doing this kind of traversal make ensures that everything that is need to be built is built, but not more than this. I.e., if some dependencies do not need to be refreshed, they are not rebuilt, helping to produce an efficient build.

The basic make process can be described like this:

  • Make finds files indicated by the targets and prerequisites.
  • If a prerequisite has a rule associated with it (i.e., make is looking at the rule target: prequisite and the rule prequisite: dependencies exists), make will try to update the prerequisite first.
  • When considering a target, if any prerequisite is newer than the target, make will attempt to make the prequisite(s) first.

Makefile Automatic Variables

This is just a summary of some of the more commonly used automatic variables. You can find a complete list here. The automatic variables do not have very user-friendly names so I have a few memory pegs I try to use to recall what they all mean...

Variable Meaning
$@ The file name of the target of the rule. Remember a rule looks like target: prerequisites. I like to remember this by thinking of the @ symbol as looking like a dart board or some kind of target. If the rule has multiple target $@ refers to whatever target caused the rule to be run.
$< The name of the first prerequisite. Memory peg: The shevron looks like it "points" to a target, so think prerequisite.
$? The names of all the prerequisites that are newer than the target. Memory peg: It's a question... what prerequisite is newer?
$^ The names of all the prerequisites. Memory peg: it's pointing up, so all of the above prerequisite.

Makefile Pattern Rules

In the example makefile in the introduction section, each target was explicitly written down. This would be fine for a small project, but if you have more than a few source files, listing each one independently will soon get tedious and error prone. Most of the time we just want to say "and object file is generated from a c/c++ source file with the same file base name". And luckily we can... to say this we would write something like the following.

%.o: %.cpp
<tab>   ...Commands...

my-target: fileA.o fileB.o ... fileZ.o

In the above example, Make knows that to build my-target it must build the object files fileA.o through fileZ.o. So, how does it construct these objects? It looks through all of the rules it has encountered so far and tries to match each file?.o with a target. It finds %.o, where % is a wild card. The % matches the portion file? of the prerequisite (this portion is called the stem), so Make can use this rule to construct the object file.

Lets take fileA.o as an example. Make will search for the file. If the file is as new or newer than the target it knows that this prerequisite does not need to be rebuilt. However, if it does, Make searches for a rule that will tell it how to build the prerequisite.

Make finds the rule %.o: %.c; commands: fileA.o matches %.o, where the % matches fileA. The prerequisite for this rule specified %.c, which substituting in the match will become fileA.c. As long as fileA.c is just a file and not generated by some other tool, Make will find no rule to create this file, so if the file doesn't exist, then Make reports an error, otherwise it will rebuild fileA.c if it is newer than the target fileA.o.

If two or more rules match Make will prefer more specific rules over more generic ones.