Makefiles - Done Very Simply

LeftRight

The very simple Makefile contains dependencies, with commands to create the target from the prerequisites. The entries look like this:

target : prerequisites_1 prerequisites_2 ...
<tab>	commands to build target from the prerequisites
<tab>	other commands to associate with target ...

...repeated for each target or created file...

Suppose you have a simple project:

main.c  proj.h  sub1.c  sub2.c  sub3.c
where main.c depends on the sub[1-3].c files and all the *.c source files depend on proj.h. Example Makefile :

# this is a comment mainx : main.o sub1.o sub2.o sub3.o cc -o mainx main.o sub1.o sub2.o sub3.o main.o : main.c proj.h cc -c main.c sub1.o : sub1.c proj.h cc -c sub1.c sub2.o : sub2.c proj.h cc -c sub2.c sub3.o : sub3.c proj.h cc -c sub3.c
Now if you type make the following commands get executed:
% make
cc -c main.c
cc -c sub1.c
cc -c sub2.c
cc -c sub3.c
cc -o mainexe main.o sub1.o sub2.o sub3.o
If you edit proj.h and run make, the same commands are executed. However, if you edit sub2.c only the following commands are performed:
% make
cc -c sub2.c
cc -o mainexe main.o sub1.o sub2.o sub3.o

Even simpler Makefiles

The following Makefile, which shows nothing but the dependencies and how to build the executable, can be used to perform the same set of operations:

mainx : main.o sub1.o sub2.o sub3.o cc -o mainx main.o sub1.o sub2.o sub3.o main.o sub1.o sub2.o sub3.o : proj.h

This works because make knows how to create .o files from .c files. These are termed suffix rules or implicit rules. You can create your own or override the ones provided by make. This will be looked at in detail later.

How does make know which one to build?

If you type make, the program looks for a file named makefile in the current working directory and if it doesn't exist then looks for one named Makefile (this is the prefered name to use). It reads this file creating a dependency tree. The first target listed in the file is the default one to build when no target is given on the command-line.

make checks the time/date stamp of the target compared to the prerequisites. If the prerequisite is later than the target then make the associated actions.
Suppose you want to compile sub3.c and nothing else, then just type the target on the command-line:

% make sub3.o

Makefile tricks

Targets don't require prerequisites, which means that if they are targeted (on the command-line) they will be executed regardless. This is far more useful than realized. This gives you a way to execute often used groups of commands. The following Makefile adds the targets - help, clean, and the target clobber which depends on clean:


mainx : main.o sub1.o sub2.o sub3.o cc -o mainx main.o sub1.o sub2.o sub3.o main.o sub1.o sub2.o sub3.o : proj.h clobber : clean -rm -f mainx clean : -rm -f a.out core *.o help : @echo "" @echo "make - builds mainx" @echo "make clean - remove *.o files" @echo "make clobber - removes all generated files" @echo "make help - this info" @echo ""

Give the following command, which gives the following result

% make help

make            - builds mainexe
make clean      - remove *.o files
make clobber    - removes all generated files
make help       - this info

Notice that the Makefile contains a couple of special characters (@ and -) prior to the commands. First, we need to understand how make executes the commands in the Makefile. Each line that has an un-escaped new-line is echoed to the screen and is passed off to a shell child process shell which then executes it. If the shell returns a non-zero exit value make will then generally abort any further processing with a warning.

The ``-'' tells make to ignore any return value and to continue on to the next command. Disregarding return values can be set globally with either the ``-i'' option or the ``fake'' target .IGNORE in the Makefile.

The other special character ``@'' tells make not to echo the command to stdout. Echoing can be disabled globally with either the ``-s'' option or the fake target .SILENT.

LeftRight
Slide 3