A Quick Introduction to Makefiles

A Quick Introduction to Makefiles

Suppose you're writing some huge program with a bazillion files and headers. Wouldn't it be nice to somehow be able to compile the entire thing with a single incantation? (as opposed to typing "gcc -c fooX.c ..." for X in [0, 100])

Suppose you just edited one tiny header file. Wouldn't it be nice to be able to recompile only the files that are affected by the change?

Makefiles allow you to do precisely this. With only minimal knowledge of makefiles you can automagically recompile your program with the "make" command. Here's a quick and dirty intro :

All you need is a file called "makefile" or "Makefile".

Pound signs ("#") are comments to end of line

CC = gcc
means that the variable CC contains "gcc". You access this variable by doing a $(CC) wherever you need it.

myprogram:	fileA.o fileB.o fileC.o 

$(CC) -o executablename fileA.o fileB.o fileC.o -lm

This is a dependency and it means that the target "myprogram" should invoke the compiler whenever fileA.o, fileB.o, or fileC.o change. In particular, it invokes the compiler referred to in the variable "CC" and creates an executable called "executablename".

The dependency begins at the beginning of the line, and there is a tab between the colon and files. There is a single tab before the operations performed for a dependency (in this case the compiler invocation). Any extra spaces will cause make to gag.

Template Makefile

Here's a segment of the template Makefile from project 1.

# the name of the target program
TARGET  = mytest

# other source files and the associated object files (this can be blank)
SRC     = file1.c file2.c
OBJ     = $(SRC:.c=.o)

# special include directories

# special libraries (none for now)
LIB     =

# select the compiler and flags
CC      = /usr/local/bin/gcc
CFLAGS  = -ansi -g


# specify how to compile the .c files
        $(CC) $(CFLAGS) $(INCLUDE) -c $<

# if you type 'make' without arguments, this is the default
all: $(TARGET)

# specify how to compile the target
        $(CC) $(CFLAGS) $(INCLUDE) $(TARGET).c $(OBJ) $(LIB) -o $(TARGET)

# remove binaries
        rm -f $(OBJ) $(TARGET).o $(TARGET)

# remove binaries and other junk
        make clean
        rm -f core *~

# this will add the file dependencies below, i.e. it modifies this file
        makedepend -- $(CFLAGS) -- $(INCLUDE) -- $(SRC) $(TARGET).c

# DO NOT DELETE THIS LINE -- make depend depends on it.
file1.o: file1.c file1.h /usr/include/stdio.h
file2.o: file2.c file2.h file1.h /usr/include/stdio.h

The last few lines in the example represent the dependencies of the object files. So if file1.h changes, we know that file1.o and file2.o both have to be recompiled.

In the above example, if you want to compile a specific target, type "make file1.o" or whatever you wish. If you wanted to compile mytest, all you'd have to do is type in "make". If for example file2.h had been changed, it attempts to compile file2.o by using "/usr/local/bin/gcc -ansi -g -I. -c file2.c". After it has successfully compiled file2.c, it compiles mytest.c and links it with the other object file (file1.o and file2.o) to make the executable mytest.

/usr/local/bin/gcc -ansi -g -I. mytest.c file1.o file2.o -o mytest

More Advanced Stuff
Writing out dependencies yourself sucks. Do a "man makedepend" to figure out how it is automated.