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
Additional information

The previous page told you the basics you need to know to build makefiles that work. This page will tell you some more information you need to know to create and use useful makefiles.

Contents:
Naming makefiles
Default target
Non-file targets
Variables
Subshells
Comments
Breaking up long lines

Naming makefiles

On the last page, you may have wondered, "How did make know to use my file called makefile? I didn't pass any arguments to make".

Makefiles can have any name — makefile, my_program.mk, xyzpdq. The .mk extension is a convention, but is not mandatory. To tell make which makefile to use, use make's -f argument:

    make -f (name of makefile, e.g. my_program.mk)

If you don't use -f, then make will look for a file in the current directory called makefile. If that doesn't exist, it will look for a file in the current directory called Makefile. If it can't find any of these, it will print an error message.

So, it's often convenient to just name your makefile makefile. This saves typing.

Default target

A makefile typically contains several statements. How does make know which one(s) to look at? If you want make to execute a specific target or targets, then type make (one or more targets). If no target is specified on the command line, make look at the first target in the makefile.

For example, using the makefile from the previous page, we could rebuild sum.o and main.o by typing make sum.o main.o. This wouldn't rebuild my_sum, even if it was out of date.

If you use the -f option, too, it must come first: e.g. make -f my_program.mk foo.o.

Non-file targets

A target need not be a file: It may be simply a label, with dependencies.

Examples:

Since, as mentioned above, the first target in the makefile is the one looked at if none is specified on the command line, often people will have a first target called all in their makefile that looks like:

all: my_program my_other_program

my_program: my_program.o
	cc my_program.o -o my_program

my_other_program: my_other_program.o
	cc my_other_program.o -o my_other_program

...

It is a convention to name this first target all, to illustrate what the default behavior of the makefile is.

Another example of a non-file target is the clean target. Again, the name clean is just a convention. This is a target with no dependencies listed — so its rules are executed unconditionally. People often use this as a handy way to clean up all the derived files created by their makefile. For example, the makefile from the previous page might be extended to look like this:

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

clean:
	rm -rf sum.o main.o my_sum
Then, if you type make, or make my_sum, then the my_sum target is looked at. However, if you specifically type make clean, then the command rm -rf sum.o main.o my_sum is executed.

In our text-file example, a complete makefile might look like:

all: part_1.html part_2.html part_3.html \
	part_4.html part_5.html part_6.html

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_3.html: part_3.txt footer.txt
	cat part_3.txt footer.txt > part_3.html

part_4.html: part_4.txt footer.txt
	cat part_4.txt footer.txt > part_4.html

part_5.html: part_5.txt footer.txt
	cat part_5.txt footer.txt > part_5.html

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

clean:
	rm -f part_1.html part_2.html part_3.html \
		part_4.html part_5.html part_6.html

Variables

You can use variables in your makefiles. This example creates and uses a variable called CC:

CC=gcc -g -Wall

sum.o: sum.c sum.h
	${CC} -c sum.c

main.o: main.c sum.h
	${CC} -c main.c

my_sum: sum.o main.o
	${CC} sum.o main.o -o my_sum

This is convenient if, say, you want to use a different compiler, or change some compiler arguments, etc., without having to edit every compile line in the makefile.

The format is VARIABLE=value. It is a convention, but not required, that variable names are all in uppercase. It is important that you use the curly braces when referring to a variable after it's defined. This is different from the Unix shell, so omitting the curly braces is a common mistake.

Subshells

This is simply a gotcha: Every rule statement is executed as a separate Unix shell command. For example, the following makefile probably will not produce the desired results:

my_output_file: some_file.txt
	cd /tmp
	touch temporary_file.txt
	...

The first rule line will be a shell that cd's to /tmp and exits; the second one will be a brand-new shell which creates temporary_file.txt — in the current directory, not in /tmp. To create /tmp/temporary_file.txt, you might do the following:

my_output_file: some_file.txt
	cd /tmp; touch temporary_file.txt
	...

Comments

Comments are preceded by a # character, just as in shell scripts. For example:

# Here is how I build foo.o
foo.o: foo.c
	cc -c -g foo.c

# Here is how I build bar.o
bar.o: bar.c
	cc -c -g bar.c

...

Breaking up long lines

You can extend a line by putting a backslash as the last character. For example:

# Here is how I build my program from a dozen object files
my_program: file_1.o file_2.o file_3.o file_4.o \
		file_5.o file_6.o file_7.o file_8.o \
		file_9.o file_10.o file_11.o file_12.o
	cc file_1.o file_2.o file_3.o file_4.o \
		file_5.o file_6.o file_7.o file_8.o \
		file_9.o file_10.o file_11.o file_12.o \
		-o my_program

...