A makefile has a simple syntax. It looks as follows:
target: dependencies
	rules
	rules
	rules

As I mentioned earlier, make is designed for files that are derived from other files — for example, object (.o) files are derived from source files (e.g. .c files). Any file (a target) that is derived from another one (a dependency) is said to be derived from it, or to depend on it. The steps you perform to create one file from another are called rules.

For example, to build our C program, we did the following:

cc -c sum.c
cc -c main.c
cc sum.o main.o -o my_sum

The first statement, cc -c sum.c, creates sum.o. So, sum.o is a target. It depends on sum.c. The rule to create sum.o is cc -c sum.c.

The key feature of the make program is dependency checking. What make does with a makefile's statements is: If any of dependencies is newer than target, or if the target doesn't exist yet, then execute the rules. On the other hand, if the target file is newer than its dependencies (by looking at the files' timestamps), then nothing is done. This is how make implements build avoidance — by using timestamps.

For each dependency, make also checks to see if that dependency is in turn a target. In this way you can have a tree of dependencies.

If a target has no dependencies, then its rules are executed unconditionally.

A target that is newer than its dependencies is said to be up to date; otherwise, it is said to be out of date.

Note that sum.c and main.c both include sum.h. If any changes were made to that header file, then we'd want both source files to be re-compiled. So sum.h should be an additional dependency for sum.o and main.o.

(sum.c and main.c also include stdio.h. We could list stdio.h as yet another dependency for sum.o and main.o; or, we could assume that, being a static system file, it won't change. Most people tend to take the latter approach.)

So, we could create a file called, say, makefile, with the following contents:

my_sum: sum.o main.o
	cc sum.o main.o -o my_sum

sum.o: sum.c sum.h
	cc -c sum.c

main.o: main.c sum.h
	cc -c main.c

(Note: Each rule must be preceded by a tab — not spaces. make depends on this.)

This says to the make program the following:

Now, after editing any one of our source files, we can just type make, to re-build the my_sum program.

Remember that make doesn't rebuild what doesn't need rebuilding, based on timestamps. If we edit just main.c, then type make, only the first and third rules will be executed:

If you've already run make, and the program compiled and linked successfully, and then you type make again without having edited any of the files, then makewill do nothing at all — because everything is up to date.


Here is what a makefile for our HTML files might look like:

part_1.html: part_1.txt footer.txt
	cat part_1.txt footer.txt > part_1.html
part_2.html: part_2.txt footer.txt
	cat part_2.txt footer.txt > part_2.html
...
part_6.html: part_6.txt footer.txt
	cat part_6.txt footer.txt > part_6.html

I include this example to show you that make can be used for many other things than compiling programs. You can use it any time one file is derived from another — in this case, .html files are derived from .txt files by using the Unix cat command.