When we have larger projects, it is much more efficient to compile
only the modified files rather than all files.
Makefiles let us
define these dependencies and rebuild our project with simple
Suppose we have a program made up of some files:
bar.h. The dependencies among
these are as follows (suppose):
From this diagram, we can infer the following properties of the build process:
foo.his modified, both
main.cppneed to be recompiled, and the binary
myprogramneeds to be linked.
foo.cppis modified, it needs to be recompiled, and the binary needs to be linked.
main.cppis modified, it needs to be recompiled, and the binary needs to be linked.
Notice that when
main.cpp is modified, for example, neither
bar.cpp need to be recompiled. This is because they do
#include anything from
main.cpp (not even recursively from
Here is a
Makefile that handles building the project shown in the
diagram. We would name this file
Makefile and put it in the same
directory as our source files (though the source files could be
Note: The space before the g++ commands must be a TAB! All actions must be indented by honest-to-god \t tabs.
We have a lot of repetition in the compiler flags (
...). Let’s use a variable to clean that up. We’ll also use a
variable for the compiler command, in case we change compilers later
(on a different operating system, for example).
Using a Makefile
We can run the
make command, and give a target (goal), as follows:
Or more likely:
make program first reads the
Makefile (if you named it
something use, use
make -f mymakefile), and determines the
dependencies of the specified target. If the target file is newer than
all of the dependencies (by checking modified time on the files), then
there is nothing to do. If one or more of the dependencies are newer,
then the target is regenerated.
Actually, all dependencies are checked in the same way first,
recursively. So if
foo.h has been modified more recently than either
myprogram (original target) or
foo.o, then all the
out-of-date targets are rebuilt.
Useful non-file targets
Most targets are filenames (e.g.,
foo.o). However, we
sometimes want some other targets just for convenience. One special
all target typically has no associated actions, but just depends
on the target you’re most often going to use. Now, if you just run
make with no target, it will assume the
all target, which in this
case is actually the
We can also make “phony” targets that have no dependencies but just
perform useful actions. One common example is the
In this example, we can run
make clean to delete all the generated
files, including the program binary. By doing this, we can run
clean followed by
make to ensure we are rebuilding the entire
project from scratch.