References / useful Links
- GNU Make Manual.
- Recursive Make Considered Harmful, Peter Miller, 1998.
- Auto-Dependency Generation, Paul D. Smith, 2017.
- Makefile, Anonymous rm command executed at last, StackOverflow
- Writing a large build system with GNU make, by David Rothlisberger.
- Implementing non-recursive make, Emile van Bergen.
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.
- 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:
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.
- Make looks at
targetand visits the first child,
prerequisit_1. It can see that
prerequisit_4. So now,
prerequisit_1becomes the "target". If make determines that
prerequisit_1is newer than
prerequisit_4, it will build
prerequisit_4. We assume that it does and builds
prerequisit_4is built, all of
prerequisit_1's dependencies have been visited so
prerequisit_1can now be built using
- Make returns back to
targetand examines the next dependency,
prerequisit_5is older than
prerequisit_6is older than
prerequisit_5, the post-order traversal will result in make next trying to build
- All of
prerequisit_5's dependencies have been built so make can now build
prerequisit_2's dependencies have been built so make can now build
- Make returns back to
targetand examines the next dependency
prerequisit_3. There are two dependencies,
prerequisit_7. Make has already built
prerequisit_5so 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.
prerequisit_3's dependencies have been built so it can now be built.
- Make returns back to
targetand 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: prequisiteand the rule
prequisite: dependenciesexists), 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...
||The file name of the target of the rule. Remember a rule looks like
||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
fileZ.o. So, how does it construct
these objects? It looks through all of the rules it has encountered so far and tries to
file?.o with a target. It finds
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.
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:
%.o, where the
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
If two or more rules match Make will prefer more specific rules over more generic ones.