Sunday, May 18, 2008

Simple Makefile Tutorial

The purpose of this tutorial is to create and understand a simple Makefile without the use of autotools (autoconf, automake etc).

Make Utility

From the make manpage "The purpose of the make utility is to determine automatically which pieces of a large program need to be recompiled, and issue the commands to recompile them"

Normally, you would use the make utility when your program's source code includes more than one file. By virtue of this utility, if you make changes to a particular module, you will not be required to recompile the whole program. It will recompile only the changed modules and the modules that dependent on them.

The Makefile

The make utility takes a Makefile or description file as input. In this file you specify the modules to build and their corresponding dependencies.

When invoked, the make utility expects a file named Makefile to be present in the same directory and uses it as its description file.

Makefile Format

The dependencies in the Makefile are specified using the following syntax:

target: components
TAB rule

In the first line you specify the target name and its dependencies whereas the second line comprises of the rule(s) to build the target. Note that in the rules line the TAB is mandatory, otherwise make will not be able to parse correctly and an error will be generated.

Invoking make

make is invoked simply by entering make on the command line. It should be invoked from the same directory where you have defined your Makefile.


By default make will start building the first target defined in your Makefile. However you can also instruct make to build a specific target via the following syntax:

make target-name


Lets jump straight to an example and implement what we have learned. Below is a simple Makefile. It builds a program named test:

test: test.o
gcc -o test test.o

test.o: test.c
gcc -c test.c

The target test depends on test.o and it is built by issuing the command gcc -o test test.o. If test.o is modified then make will build test again. Similarly test.o is itself another target which depends on test.c. If test.c is modified then test.o is rebuilt using its corresponding rule.

In the above scenario we were only dealing with one source file. We would have been good by simply using gcc as well. Lets take a slightly more complicated example:

Suppose you are implementing a stack and you want to write a program; stacktest that tests this implementation. The stack is being implemented by wrapping around another container called vector.

Hence, stacktest depends on stack and stack depends on vector. The corresponding Makefile can be written as follows:

stacktest: stacktest.o stack.o vector.o
gcc -o stacktest stacktest.o stack.o vector.o

stacktest.o: stacktest.c stack.h
gcc -c stacktest.c

stack.o: stack.c stack.h vector.h
gcc -c stack.c

vector.o: vector.c vector.h
gcc -c vector.c

Adding the clean target

Traditionally each Makefile comes with a clean target. The purpose of this target is to remove all the object files and targets so that the next time you invoke make everything is re-compiled. Lets add a clean target in our stacktest example:

rm -f *.o
rm -f stacktest

As you can see, in our clean target we are removing all the object files and the stacktest binary. This target can be invoked as follows:

make clean

Hope you found this helpfull!


Hannes said...

Thanks for this simple tutorial! I finally understand what this strange make thing is all about.


good tutorials. thanks a lot. it helped me.