Last week's lab
taught you how to use the debugger with emacs and the g++ compiler.
If this editor and compiler are not installed on your machine, then
you may obtain them from
www.cs.colorado.edu/~main/cs1300/README.html.
.
This week's lab
introduces another important tool: The make facility.
This facility allows you to easily maintain and keep your files up to date
when you are working on a project that is split accross several cxx files
(and their associated header files). For each of your future assignments,
you should keep all of the files for that assignment in one directory, and
create a make file to manage the files in that directory.
-
Copy an Example Makefile
-
This week's lab exercise begins in the same place that last week's
exercise ended: In your
working subdirectory with the intarray and sinewave files. You'll
need to copy one more file from the compiler installation to this
directory:
copy c:\cs1300\manuals\lab\makefile
-
What's in a Makefile?
-
The file makefile is the usual input file for a tool called make. The
purpose of make is to help you maintain and update a collection of related
program files. The collection usually has header files, cxx files, and
compiled files--all of which depend on each other. For example, consider
last week's sinewave program.
The final product is an executable file named
sinewave.exe, which is created by linking together two other compiled files,
sinewave.o and intarray.o. The two compiled files were created by
compiling
sinewave.cxx
and
intarray.cxx
(both of which used the header file
intarray.h).
The complete dependencies among the files can be
drawn like this:
The upward arrows in the file express how each file is created. For
example, the file sinewave.o is created by compiling sinewave.cxx and also
includes intarray.h. To state the matter simply:
If sinewave.cxx or intarray.h changes, then sinewave.o must be regenerated
by giving the compiler command:
g++ -Wall -c -gstabs sinewave.cxx
This requirement
to sometimes regenerate sinewave.o is one of the dependencies
that the example makefile expresses. To see this dependency, use emacs
to open up the file named makefile.
Near the bottom of the file you'll find these two lines:
sinewave.o: sinewave.cxx intarray.h
g++ -Wall -c -gstabs sinewave.cxx
The first line is called a target line, which begins with a file name and
a colon. After the colon is a list of more file names. Here's how to
interpret the line: The file before the colon (called the target file)
depends on the other files (after the colon). Whenever one of the files
after the colon changes, the make tool knows that the target file needs
to be regenerated. After the target line, there is a series of commands
that tell exactly how to regenerate the target file. For the case of
sinewave.o, we only need the one g++ command to regenerate the file.
(Notice that we included the -c flag to indicate that we should only
compile and not create an executable file yet. We also included the
-gstabs flag in case we want to use the debugger.)
There is one
other peculiar requirement: The command lines (such as the g++ command)
must each begin with a tab (not with 8 spaces!).
As a second example of a dependency, the executable file sinewave.exe is
created by compiling together the object files sinewave.o and intarray.o.
If either of these two object files should change, then sinewave.exe also
needs to be recreated. Here is the appropriate target line and command
from our makefile:
sinewave.exe: sinewave.o intarray.o
g++ -Wall -gstabs sinewave.o intarray.o -o sinewave
This target line says that if sinewave.o or intarray.o should happen to
change, then the sinewave.exe must be regenerated with the g++ command that is
shown.
-
Using Make to Regenerate a Specified Target File
-
In order to illustrate how the make facility works, start by getting rid of
all the object files and executable files. You can do this with the
delete command:
del intarray.o
del sinewave.o
del sinewave.exe
There are two simple ways to use the make facility to automatically
regenerate
your files. The first approach regenerates a specific file. For example,
suppose you want to regenerate intarray.o. Then you can use the make
command, as shown here:
make -k intarray.o
The make command will find the dependency information in the makefile
file. It sees that intarray.o depends on other files, so it will first
ensure that those files are present (and regenerate them if necessary). In
this example, the two files intarray.h and intarray.cxx are necessary for
generating intarray.o. These two files are present, so the make command
proceeds to generate intarray.o, using the g++ command that is specified
in the makefile. When the g++ command is executed, it is displayed on the
screen, so you will see this appear on the screen:
g++ -Wall -c -gstabs intarray.cxx
After this command finishes, you should list the files in your directory,
where you will find the object file intarray.o is once again present.
-
Using Make without Specifying a Target File
-
You may also use the make command without specifying a file, like this:
make -k
Without a specified file, the make command will regenerate the first
target that it finds in makefile. Try this now, and you will see that
the executable file sinewave.exe is regenerated, since sinewave.exe is the first
target file in makefile. During the process of regenerating the sinewave.exe
file, the make command had to carry out several steps. In the first step,
the make command realizes that sinewave.exe depends on intarray.o and also on
sinewave.o. But the file sinewave.o is not present. So, the make command
first regenerates sinewave.o, and then it can proceed to regenerate the
executable file sinewave. On the screen you'll see the two steps
displayed:
g++ -Wall -c -gstabs sinewave.cxx
g++ -Wall -gstabs sinewave.o intarray.o -lm -o sinewave
-
Using Make from within Emacs
-
The best feature of the make command is how it automatically keeps track
of exactly which object files and executable files need to be recompiled.
As an example, you should now change one of your source files. I suggest
that you use emacs to
make a small change to the sinewave.cxx program, perhaps adding
another small output statement. Then save the new sinewave.cxx
(CTRL-x s) and
from within emacs give the compile command (ESC x compile RETURN).
What happens? The emacs compile command automatically issues the
"make -k" command. The make facility realizes that sinewave.cxx has changed, and
therefore the object file sinewave.o is regenerated with the command:
g++ -Wall -c -gstabs sinewave.cxx
Next, the make facility realizes that sinewave.o has just changed, and
therefore the executable file sinewave.exe is regenerated with:
g++ -Wall -gstabs sinewave.o intarray.o -lm -o sinewave
But, notice that the object file intarray.o was not recompiled. The
dependencies in the makefile were sufficient to show that intarray.o did
not need recreation.
-
Using Make with Special Targets
-
A makefile can also have special target lines that carry out special
actions rather than regenerate files. There are two such target lines at
the bottom of the example makefile:
clean:
rm $(EXPENDABLES)
all:
@make $(EXPENDABLES)
The special target clean simply removes all of the files that can be
generated. (These are called the "expendable" files because they are easy
to replace by regenerating them.) It does the removal with the rm
command (which is part of the compiler files that you downloaded).
The rm command removes one or more files.
The term $(EXPENDABLES) is a
macro expansion--meaning that the make command will look elsewhere in the file for the
meaning of the word EXPENDABLES.
It will find this at the top of the file, where
EXPENDABLES is defined to be the sequence of all files that we can
regenerate.
The special target "all" generates all expendable files.
The @ symbol in front of make command suppresses
printing of the command, so that "make. . ." will not be printed on the
screen.
The two special targets can be activated just like any other target. For
example:
make -k all
The make all command is useful when you want to make certain that all
files are up to date.