Here, we will use a program that deals with some geometric shapes to illustrate writing a make file to compile a program.
The program is made up of 5 source files:
main.cpp, a main program.
Point.h, header file for the Point class.
Point.cpp, implementation file for the Point class.
Rectangle.h, header file for the Rectangle class.
Rectangle.cpp, implementation file for the Rectangle class.
These files are available to download.
Let's review what we need to do to compile this program
separately (i.e., to intermediate object files) and
then link it into an executable named
To just compile source code, use the
-c flag with the
% g++ -c main.cpp % g++ -c Point.cpp % g++ -c Rectangle.cpp
This will generate the object files:
Then, to link the object files (
.o) into an executable, we
use the compiler again (although this time it will just pass the
.o files on to the linking stage):
% g++ -o main main.o Point.o Rectangle.o
We would like to know what files need to be regenerated when we change certain parts of our program. We can determine this by creating a dependency chart.
Let's start from our goal (i.e., to have an executable named
To generate main from those files, we link them together.
.o) files depend on the
You generate these object files by compiling the corresponding
.h) depend on header files (
.h) that they include:
Notice that there may be additional indirect include dependencies
Rectangle.cpp depends on
.hfiles aren't used to generate other files, unlike
Here is the complete dependency chart...
Dependency Chart for
Dependencies go downward.
How to "get" or generate files goes upward.
.ofiles) and some things indirectly (i.e., you need the
.cppfiles to generate the
.ofiles to generate the executable).
With such a dependency chart, we can answer questions about what needs to be regenerated if certain files change.
For example, suppose we change the file
main.cpp...What has to be regenerated?
Answer: First, recompile
main.cpp to get
main.o. Then, relink all the object files together
again to get the executable
Now, suppose we changed
.cppfile changes or a header file included (directly or indirectly) by a
.cppfile changes, we have to regenerate the corresponding
Since it is tedious to recompile pieces of a program when something changes, people often use the make utility instead.
Make needs a make file that encodes both the dependencies between files and the commands needed to generate files.
When you run the make utility, it examines the modification times of files and determines what needs to be regenerated. Files that are older than the files they depend on must be regenerated. Regenerating one file may cause others to become old, so that several files end up being regenerated.
A make file should be named either Makefile (first letter uppercase) or makefile (all lowercase). The make file should be in the same directory as the source code files.
Finally, it is easiest if the make file is in the same directory as the source code files.
You can create a make file in any text editor.
For this example, we will examine the parts of this Makefile, which generates the executable
main. Go ahead a download that make file.
Simple make files have at least 2 parts:
Make files may have comments that (hopefully) add to readability. Any
line starting with a pound sign (
#) is a comments. Note
that our make file has some comments.
CXX = g++
In general, the form for setting a variable is:
VARNAME = value
(The space around the
= is optional for our version of
make, but adds to readability).
The second variable says what flags to pass to the C++ compiler...
CXXFLAGS = -Wall -g
Here, the variable includes the flag for all warnings and the one that adds debugging information (so that the executable can later be run under a debugger).
The 2 variables,
CXXFLAGS, are the
ones that our version of make expects to be set for the C++
compiler and flags to the C++ compiler, respectively.
For each target, there are typically 1 or 2 lines in a make file. Those lines specify:
By default, the first target in the make file is the one that gets
generated when you just say "
make" at the commandline, so
it should be the name of the executable.
For a target's dependency line, the target file name should be listed,
followed by a colon (
:) and the files it depends on:
main: main.o Point.o Rectangle.o
For a target that is an executable, we just list the object files it depends on.
On the next line, there should be a command that generates the target:
<Tab>$(CXX) $(CXXFLAGS) -o main main.o Point.o Rectangle.o
<Tab>(i.e., hit the Tab key)...it cannot start with a regular space!
Note that the variables
are used, which means that the command is really:
g++ -Wall -g -o main main.o Point.o Rectangle.o
$) and then the variable name in parentheses.
For a target that is an object file, we list all files it depends on.
This means its corresponding
.cpp file, plus any header
.cpp file includes (i.e., include directly or
indirectly, but not system header files like
aren't going to change).
So, the dependencies line looks like:
main.o: main.cpp Point.h Rectangle.h
The generation command for this target is:
<Tab>$(CXX) $(CXXFLAGS) -c main.cpp
main.otarget more simply. Make already knows how to generate certain kinds of files. Let's take advantage of make's smarts for the next target...
The next object file,
Point.o depends on
however, make already knows that
.o files can be
generated from their corresponding
.cpp files, so all we need
.hfiles a file depends on, some cannot, so you should list the include dependencies.
We don't need any generation line since we set up the
will use them to figure out a compilation command.
Finally, there is only one target left,
Taking advantage of make's smarts, it only requires:
Rectangle.o: Rectangle.h Point.h
Rectangle.o depends on
indirectly (i.e., recall that
Rectangle.h, which includes
With a make file for our executable, it is easy to compile that executable.
Try using the make file we've given you. First, remove the old executable and object files (if there are any):
% rm main *.o
Run make with the command:
Remember that this will cause make to generate the first
target in the make file, which is
Since nothing has been compiled/linked, it will do:
g++ -Wall -g -c main.cpp g++ -Wall -g -c -o Point.o Point.cpp g++ -Wall -g -c -o Rectangle.o Rectangle.cpp g++ -Wall -g -o main main.o Point.o Rectangle.o
I.e., compile the 3
.cpp files into object files, and then
link the object files into an executable named
(If you have trouble, be sure that the make file is named Makefile and is in the same directory as the source code files.)
Now, type the make command again (let's explicitly tell it what target to generate this time) and you get:
% make main make: `main' is up to date.
Here, make tells you nothing has changed, so it has no work to do.
Next, suppose we change the program by adding some code to the end of
main.cpp) to move the rectangle
and then print out its new top right point:
Editr1.move(2, 3); cout << "\nRectangle r1 moved by 2, 3--top right now at " << r1.get_top_right().get_x() << ", " << r1.get_top_right().get_y() << endl;
main.cpp, adding those lines, and then save to disk.
After the change, when we use make:
% make g++ -Wall -g -c main.cpp g++ -Wall -g -o main main.o Point.o Rectangle.o
it doesn't bother regenerating
Rectangle.o, since the files those depend on have not
Finally, try changing
Point.h and then re-make...
% make g++ -Wall -g -c main.cpp g++ -Wall -g -c -o Point.o Point.cpp g++ -Wall -g -c -o Rectangle.o Rectangle.cpp g++ -Wall -g -o main main.o Point.o Rectangle.o
Since all the
.cpp files depend on that header (either
directly or indirectly), they all have to be recompiled and linked
Other versions of make may use different variables for C++
In a make file, if you need to continue a line, you cannot just
continue it on the next line. You must end a line with a
(backslash, not the forward slash) to tell make that the line
continues. Only break lines where space would normally go.
So, the first target could have been written:
main: main.o \ Point.o \ Rectangle.o <Tab>$(CXX) $(CXXFLAGS) -o main \ main.o Point.o Rectangle.o
We have only described the basics of make here. Most versions of make have additional capabilities.