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.
make
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
Example
======
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:
clean:
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!