Make

Make concepts

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

Additional dependencies

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.

Default actions

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.

Additional dependencies

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

Macros

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.

Default rules

The default rules cover file types such as

The default rules use more complex macros 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

Arbitrary shell commands

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

Tools

makedepend

The software makedepend will take a list of C files and include files and build a makefile for them.

imake

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

Autoconf

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.


Jan Newmarch (http://jan.newmarch.name)
jan@newmarch.name
Last modified: Tue May 22 13:19:51 EST 2001
Copyright ©Jan Newmarch