Make Lab                
This lab concerns the make command. Around here, make and gmake
(the GNU version of make) are the same thing.
                
cmake review                
In this class we tend to use cmake.
As you know, cmake reads a CMakeLists.txt
which describes
the project, and produces a Makefile
. We’re not going to discuss
cmake in this lab, rather, we’re going to talk about what goes
into a Makefile
and how it works.
                
How a Makefile
works                
Consider a program that has two source files, alpha.cc
and
main.cc
.
                
alpha.cc
: the C++ source file contains function alpha()
alpha.h
: the C++ header file that a declaration for alpha()
,
but no actual code.
main.cc
: the C++ source file that contains main()
.
main.cc
contains #include "alpha.h"
and uses the function
alpha()
.
alpha.o
: the compiled, machine-language version of alpha.cc
main.o
: the compiled, machine-language version of main.cc
prog
: the combination of alpha.o
, main.o
, and any
required library functions (cout, exit(), etc.).
We can show their dependencies like this:
                
alpha.cc ──> alpha.o ────┐
∧ │
│ │
│ │
alpha.h │
│ │
│ │
∨ ∨
main.cc ──> main.o ──> prog
The arrows represent dependencies. For example, alpha.o
depends on
alpha.cc
; if alpha.cc
changes, then alpha.o
is out-of-date,
and so we need to recompile alpha.cc
to create alpha.o
.
Similarly, if main.o
or alpha.o
change (probably from a
recompilation due to a C++ source file changing) then we need to
recreate prog
by linking together alpha.o
, main.o
, and any
required library symbols.
                
If alpha.h
changes, then we need to recompile both alpha.cc
and
main.cc
.
                
If this were all that there were to our project, we would just compile
all the *.cc
files every time, because computers are fast. However,
consider a large project, with hundreds of source files.
Compilation (.cc
⇒ .o
) is slow, whereas linking (merging *.o
into an executable prog
) is fast. Therefore, it’s worth the bother
to keep the .o
files around and only recreate them as necessary.
                
A Makefile
describes, textually, the diagram above. It reduces
those dependencies to rules, and gives commands for creating one
file from another. Here is a Makefile
fragment that would describe
the relationships shown above:
                
prog: main.o alpha.o
g++ -Wall main.o alpha.o -o prog
main.o: main.cc alpha.h
g++ -Wall -c main.cc
alpha.o: alpha.cc alpha.h
g++ -Wall -c alpha.cc
For example, the first rule, says that prog depends on main.o
and alpha.o
, and then gives the command that would (re-)create prog
from main.o
and alpha.o
. Of course, if those files don’t
yet exist, or are out of date, then see the rules for those files. It’s
recursive!
                
Makefile
s can also have variables, which get assigned:
                
FOO=whatever you want
To use them in a rule, use the variable name like this: $(FOO)
                
When you type the shell command make, it reads the Makefile
,
figures out what’s out of date, and executes the commands as needed.
Every file has a modification time associated with it, which make
inspects, so it knows which files are older than which other files.
                
Your task                
- Copy the directory
~cs253/Lab/Make
to a convenient place in your home directory.
- Follow the instructions in the
Makefile
.
How to submit your work:                
In Canvas, check in the
file
Makefile
to the assignment “Lab04”.
It’s due 10:00:00ᴘᴍ MT Saturday, with a 24-hour late period for a 25% penalty.
                
How to receive negative points:                
Turn in someone else’s work.