Make a Makefile
file to make make make files¶
The make utility is a general-purpose powerhouse whenever you need to, well, make a file or set of files based on some other files. It is of course not the only way to ‘make’ things in the English sense of the word; however, when you want to automate some process where one file is derived from others—building a program for example—you will benefit greatly from knowing a little bit about make. It can be taught to make almost anything, but it also comes standard knowing some very useful tricks, particularly when it comes to working with C programs.
A simple C program typically consists of one file named
program.c
, and you can run the C compiler to translate it into
executable machine code in a file named program
. The default
rules for make include this pattern, so if you run make
program
and your working directory contains a program.c
,
the right thing will happen.
A C program of any significant complexity will usually be divided into
multiple files. For example, you might break out some functions into
their own C file, put your main
function in its own C file, and
have a header that is included in each of them. The default rules can
handle what to do in this situation, but not how to guess which files
should go together, so you will have to tell make a little
about your project. When you run make, it will look in the
working directory for a file named Makefile
, and if that exists
will read it to learn your custom rules.
You can make your Makefile
with any text editor. The simplest
sort of rule just lists which files can be made, and what other files
they depend on. To build a C program with multiple source files, it is
advantageous to compile each source file (with a .c
extension)
separately into an object file (with a .o
extension), and then
finally link all of the object files together into one program. It is implied
that an object file depends on a source file with the same name, and that
the program overall depends on the source file or object file with the same
name, but any other dependencies (such as headers and other object files)
should be listed.
program: functions.o
program.o: functions.h
functions.o: functions.h
Each line starts with a target file, a colon, and then a list of source files. Whenever you ask to build one of the target files, make will check when all the source files were last modified. (One of the nice things about make is that if the target was made more recently than any of the sources was modified, make will know that nothing needs to be done!) If the target does not exist yet or it needs to be remade, make will use its builtin recipe to create it fresh.
People who use make a lot for a lot of different projects usually
add in some useful phony targets, so you can not only say make program
to make program
, but you can say make all
to make everything
in this project or make clean
to delete extra files (like those intermediate
.o
object files). These are called ‘phony’ targets, because make all
doesn’t actually make a file named all
, it’s just a name for what you want
to happen.
all: program
.PHONY: all
program: functions.o
program.o: functions.h
functions.o: functions.h
.PHONY: clean
clean:
rm -f program program.o functions.o
The all
target should always come first. Whatever rule comes first
in the Makefile
will be the default if you run just make
without specifying a target, so putting all
in that position makes
sense. That way most of the time you only need to type four letters to
make sure everything is up to date. The way you specify what ‘all’
means for this project is by listing the things you want to build after
the colon, so all
depends on them.
The clean
target doesn’t depend on anything, but we do have
to specify the recipe explicitly since there is no useful default. On
the line after clean:
, indent one tab (yes, it must be the tab
character, not eight spaces or something else that looks similar) and then
type shell commands. In our case, we want to remove (using rm)
the built files, going back to the basic project with just its source
files. The -f
option to rm tells it not to complain
if it is asked to delete something that already doesn’t exist.
There are other phony targets that people commonly provide, and in fact configuring your build system can become a whole rabbit hole, but this is enough for nearly all of our needs.