Introduction to make

Introduction to the Unix make utility
Compiling a C program without make
Maintaining text files without make
How make can simplify your work
How to write a Makefile
Additional information
Miscellaneous tips
How to write a Makefile

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

target: dependencies

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:

  • If sum.o or main.o is newer than my_sum, or if my_sum doesn't exist, or if sum.o or main.o are out-of-date targets, then execute the command cc sum.o main.o -o my_sum.
  • If sum.c or sum.h is newer than sum.o, or if sum.o doesn't exist, or if sum.c or sum.h are out-of-date targets, then execute the command cc -c sum.c.
  • If main.c or sum.h is newer than main.o, or if main.o doesn't exist, or if main.c or sum.h are out-of-date targets, then execute the command cc -c main.c.

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:

  • The make program will check to see if sum.o or main.o is newer than my_sum; neither one is.
  • However, main.o and sum.o are each themselves listed as targets in the makefile.
  • So, make checks to see if sum.c or sum.h is newer than sum.o — neither one is, and neither sum.c nor sum.h are listed as targets in the makefile. So, sum.o is up to date.
  • Then, make checks to see if main.c or sum.h is newer than main.o — main.c is newer, so the command cc -c main.c is executed. Now main.o is up to date.
  • Now that sum.o and main.o are up to date, main.o is newer than my_sum. So the cc sum.o main.o -o my_sum is executed. The my_sum is up to date with respect to its dependencies, so make is done.

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.