Makefiles
Written By Peter Jay Salzman
Last updated: 22 July 2001
============================
This section is not essential; it's intended to make life simpler and speed
up the development of your programs. Makefiles are not essential to the
course, and you may skip it if you please.
A makefile must reside in the same directory as your source code files and
be named either "makefile" or "Makefile". It doesn't make a difference
whether you use makefile or Makefile, but I prefer Makefile.
Every line of a makefile is one of three types:
1. Assignments
2. Target lines
3. Rule lines
Assignments
-----------
An assignment is the setting of a variable. By convention, all variables
in a makefile are uppercase. The following line assigns the string
"gcc" to the variable CC:
CC = gcc
Spaces are irrelevant around the = sign. Even if the string has spaces in
it, you don't need to quote the string. This is valid:
LIBS = "-lncurses -lm -lsdl"
The one rare case you want to be careful about is if the string contains a
character which may get interpreted by the shell. For example, an asterisk
has special meaning to the shell, so you may want to quote it, as below:
MYFACE = ":*)"
When you want to use a variable, enclose it between ${}. So the following
line prints the string "gcc":
CC = gcc
echo ${CC}
Target Lines
------------
The target line begins with a target (one word), followed by a colon and
optionally followed by a list of dependencies. The target line must NOT
begin with a space or TAB character. It must start with a word. Here is
an example of a target line with dependencies:
all: main.o main.h mycode.o mycode.h
and here is a target line without dependencies:
clean:
Rule Lines
----------
Each target line is followed by zero or more rule lines. Each rule line
MUST start with a TAB character, immediately followed by any valid unix
command. Here are some example rule lines:
gcc -g -o myfile myfile.c
or
rm -rf core *.o
or
echo "hello"
Dependencies
------------
An executable's dependencies are the files used to create the executable.
For example, the executable mycode would probably have dependencies on
mycode.c and possibly mycode.h.
When your Makefile compiles a program, it keeps track of whether the
executable is newer or older than the list of dependencies. Consider the
following:
OBJS = mycode.o
CC = gcc
CFLAGS = -Wall -O3
all: ${OBJS}
${CC} ${CFLAGS} ${OBJS} -o mycode
mycode.o: mycode.c
${CC} -c ${CFLAGS} mycode.c
When you type "make all", make will look at the listed dependencies,
which is simply mycode.o (do you see why?). It then looks for a target
named mycode.o and notes the dependencies of mycode.o, which is simply
mycode.c.
If mycode.c has been edited since the last time mycode.o was generated,
make recompiles mycode.c using the rules under the mycode.o target.
If mycode.c has NOT been edited since the last time mycode.o was generated,
then it continues.
Next, make looks to see if mycode.o has been edited since the last time
mycode was generated. If mycode.o is newer than mycode, it recompiles
mycode by executing the rule lines listed under the all target.
This purpose of this whole business is to compile ONLY those files which
have been changed and to NOT compile anything that hasn't been modified.
Note that the rule to make dependents must come AFTER you declare them to be
a dependance. So this is legal:
all: mycode1.o mycode2.o
gcc -Wall mycode1.o mycode2.o -o CoolProgram
mycode1.o:
gcc -c -Wall mycode1.c
mycode2.o:
gcc -c -Wall mycode2.c
while the following is NOT legal. mycode1.o is listed as a dependence of
all. therefore its rule has to come after all, not before all.
mycode1.o:
gcc -c -Wall mycode1.c
all: mycode1.o mycode2.o
gcc -Wall mycode1.o mycode2.o -o CoolProgram
mycode2.o:
gcc -c -Wall mycode2.c
One last thing before we move on. If you simply type "make", the very
first target in the Makefile will be made. So in the above example, "make"
is the same thing as "make all".
Putting It All Together
-----------------------
Here is a simple Makefile that compiles our mycode.c file:
CC = gcc
CLFAGS = -O3 -g -Wall
all: mycode.c
${CC} ${CFLAGS} mycode.c -o mycode
clean:
rm -rf core *.o
date:
DATE = `date`
echo ${DATE}
When you type "make all", the system will execute the following command:
gcc -O3 -g -Wall mycode.c -o mycode
Can you see why? make will helpfully print every command to the screen as
it executes the command so you can see exactly what it's doing.
When you type "make clean", the system will run the follwing command:
rm -rf core *.o
When you type "make date", the system will run the following commands:
DATE = `date`
echo ${DATE}
Why Use Make?
-------------
In this small example, we only get a moderate gain in usefulness. Typing
"make" is sure better than typing
gcc -g -Wall -O3 mycode.c -o mycode
but the real benefit comes when you have a multiple file program. Here is
the makefile for our example multi-filed program above:
CC = gcc
OBJS = mycode1.o mycode2.o
CFLAGS = -c -O3 -g -Wall
LFLAGS = -O3 -g -Wall
all: ${OBJS}
${CC} ${LFLAGS} ${OBJS} -o MultiFileCode
mycode1.o: mycode1.c
${CC} ${CFLAGS} mycode1.c
mycode2.o: mycode2.c
${CC} ${CFLAGS} mycode2.o
Now, instead of typing:
gcc -c -O3 -g -Wall mycode1.c
gcc -c -O3 -g -Wall mycode2.c
gcc -g -Wall mycode1.o mycode2.o
all you need to do is type:
make all
Suffix Rules
------------
There are ways of simplifying your makefile. One important way is to use a
suffix rule. In the example above, mycode1.o and mycode2.o were generated
by very similar rules. The only thing that's different is the filename.
If you add the following line to your makefile:
.c.o:
${CC} ${CFLAGS} $<
make will know how to compile all .c files into .o files without a separate
entry for each filename. For example, using the suffix rule, the above
makefile becomes:
CC = gcc
OBJS = mycode1.o mycode2.o
CFLAGS = -c -O3 -g -Wall
LFLAGS = -O3 -g -Wall
all: ${OBJS}
${CC} ${LFLAGS} ${OBJS}
.c.o:
${CC} ${CFLAGS} $<
Even with only two files, this is a vast savings in typing!
Other uses for makefiles
------------------------
By the way, makefiles are useful for just about anything you want to
automate. I use a program called LaTeX quite a bit. Here is the makefile
I use for my LaTeX documents:
TSTAMP = `/bin/date +'%b-%d-%H-%M'`
all: oral.tex
latex oral.tex
dvips -o oral.ps oral.dvi
backup:
cd ..; tar -cv ./orals | gzip > /usr/local/backup/orals/${TSTAMP}.tgz
The last target (backup) creates a backup of my work, calls it something
like "Aug-10-12-36.tgz" and puts it in a directory I use for storing
backups of my important work.