Make tutorial

The program make reads a Makefile in the current directory and builds the targets according to the instructions in the Makefile.

Let's deconstruct a common makefile:

hello: hello.c
    cc -Wall -ansi -o hello hello.c

The first line contains a target filename (hello), followed by a list of files on which the target depends (hello.c). Whenever hello.c is newer than hello, make will run the command given on the second line.

Remember that make is picky about the tab at the beginning of the second line. Spaces won't work, it has to be a tab.

That is all you need to know about makefiles for this class. But if you are curious, keep reading.

Advanced Makefiles

GNU Make has lots of advanced features which allow for more general-purpose makefiles. We'll discuss just a couple here.

You may wonder why we have to type hello all over the place. This is a good thing to wonder, because one of the cardinal rules of programming is to not repeat yourself wherever possible. If you repeat yourself, and later you have to change something, you might not remember to change it everywhere and that would introduce bugs. Make has special substitutions that will keep us from repeating ourselves:

hello: hello.c
        cc -Wall -ansi -o $@ $<

$@ is replaced with the target name, which is hello. $< is replaced with the first dependency name, which is hello.c here. $* is replaced with all of the dependencies, which is also just hello.c here since we only have one dependency. Now if we want to change from hello to goodbye we only have to change in two places, not four.

Make can use generic rules based on patterns:

%: %.c
        cc -Wall -ansi -o $@ $<

In this example the % is replaced by the name of the program you're trying to compile. But you have to tell make what program to compile, so you type make hello, or make goodbye. If you type only make, make will complain that you don't have a default rule, which is its way of telling you that you didn't give it enough information.

In makefiles you can have more than one rule, and the first is the default rule. So, we can add a rule at the beginning that tells make which program we want to compile, like this:

default: hello
%: %.c
        cc -Wall -ansi -o $@ $<

The first rule says default is built by building hello. Now the pattern matches and the second rule is used to build hello. Now we can just type make and it will build hello, and if we want to change hello to goodbye we only have to change it in one place.

We can do even better, though. GNU Make has implicit rules which will be followed even if there is no matching rule in your makefile, and even when there is no makefile at all. In fact, if you have hello.c but no makefile, and you type make hello, make will run cc hello.c -o hello for you. But what if we want our options -Wall -ansi in there too? The make implicit rule is actually $(CC) $(CPPFLAGS) $(CFLAGS) $< -o $@. The variable CC has the default value cc, and the other variables are by default empty. So, if we define CFLAGS to be equal to -Wall -ansi, then make will use those flags when compiling our program. We can do this with a makefile that contains the following:

CFLAGS=-Wall -ansi
default: hello

Now if we type make, make will compile our program with the command cc -Wall -ansi hello.c -o hello which is just what we want.

The last thing I will discuss is the customary clean target. People usually provide a clean target to get rid of the binary output files so that everything is recompiled from scratch. In our case the clean target would look like this:

clean:
        rm hello

You would run this by typing make clean. Makefiles can be used for many tasks other than programming, as well. In fact I build these webpages using make to run a script that converts markdown into HTML.