In a multi-file project, you can compile all of the files together
cc -o file file1.c file2.c file3.c ...
If you make a change to any one of them, then you will compile them
all again, and this will waste time.
It is better to compile each one separately to an object module, and then just link them together
cc -c file1.c
cc -c file2.c
cc -c file3.c
...
cc -o file file1.o fle2.o file3.o ...
Then if you change any one of the files, it may be enough to just
recompile that single file, and relink. e.g if file2.c
is changed,
cc -o file2.c
cc -o file file1.o fle2.o file3.o ...
make
can help reduce the amount of work you have to
do in looking after this. It uses (by default) a file called
Makefile
that contain a set of rules to rebuild the
executable file from its components whenever a component changes.
A full Makefile
for the above could be
file : file1.o file2.o file3.o
cc -o file file1.o file2.o file3.o
# note the line above starts with tab
file1.o : file1.c
cc -c file1.c
file2.o : file2.c
cc -c file2.c
file3.o : file3.c
cc -c file3.c
Comments
make
Suppose there is a common header file mytypes.h
that
defines data structures, macros, etc, that are used by the source
files. Then this should be added to each list
file : file1.o file2.o file3.o
cc -o file file1.o file2.o file3.o
# note the line above starts with tab
file1.o : file1.c mytypes.h
cc -c file1.c
file2.o : file2.c mytypes.h
cc -c file2.c
file3.o : file3.c mytypes.h
cc -c file3.c
Note that in file1.c
should be a
#include "mytypes.h"
, and similarly with the
other source files.
Some actions occur frequently. For example, generating a .o
file from a .c
file. When make
starts it builds
a table of these actions and applies them if no action is given. So the
first Makefile
can be rewritten as
file : file1.o file2.o file3.o
cc -o file file1.o file2.o file3.o
In order to create file1.o
it applies the default rule
and looks for a file1.c
.
Since it finds it, it conditionally compiles it.
An alternative way of specifying dependencies is to add them to
already existing ones. So mytypes.h
can be added by
file : file1.o file2.o file3.o
cc -o file file1.o file2.o file3.o
file1.o :: mytypes.h
file2.o :: mytypes.h
file3.o :: mytypes.h
The simplest macros are just variables that can be assigned values. These can be used for readability and to reduce mistakes
OBJS = file1.o \
file2.o \
file3.o
file : $(OBJS)
cc -o file $(OBJS)
There is a set of common macro names that are used by some of the rules.
These include CC
and CFLAGS
CC = gcc
CFLAGS = -g
OBJS = file1.o \
file2.o \
file3.o
file : $(OBJS)
$(CC) $(CFLAGS) -o file $(OBJS)
The conditional compiles will also be done using
CC
and CFLAGS
.
The default rules cover file types such as
$<
and $@
. These stand for things like the target $@
,
the dependency $<
, the dependency without an extension
$*
. For example, the rule for Java is
.java.class :
javac $<
You can define your own rules, or use these other macros in your
makefile
The makefile can be used for all sorts of operations as well as compiling and building programs. The actions can be any shell commands
clean :
rm -rf core *~ *.o
will clean up core dumps, backup files from emacs
and
any object modules, recursively from the current directory.
The action may be built from several shell commands. Each line of
action is run by a separate shell. So you may need to run in a
subshell or use continuations. For example, to run
make
in subdirectories,
SUBDIRS = dir1 dir 2 dir3
all :
for dir in $(SUBDIRS); \
do \
(cd $$dir; \
make); \
done
The software makedepend
will take a list of C files
and include files and build a makefile for them.
Different versions of Unix do differ. For example,
imake
is one way of handling these. It takes a simple
dependency specification, and produces a makefile that is customised
for your OS and compiler.
For example
SRCS = file1.c file2.c file3.c
OBJS = file1.o file2.o file3.o
NormalProgramTarget(file, $OBJS, NullParameter,
NullParameter, NullParameter)
DependTarget()
You then run the command xmkmf
to generate a 700 line
Makefile
A tool with similar function is autoconf
. This uses a
file configure.in
and produces a Makefile
.
It uses a mixture of shell scripts and M4 commands. M4 is a macro
processor, that gives a simple language.