Make Tutorial

Note

This is, much like the internet itself, and democracy, work-in-progress.

Todo

make runs other programs in the right order, handles intermediate files and generates files only if it’s necessary (i.e. if the sources have changed).

Todo

Documentation for GNU make: http://www.gnu.org/software/make/

Note

Do not use make for running LaTeX!

It may be a reasonably usable tool for this and many people use and recommend it, e.g.

But there is a much superior tool for that, see Using Latexmk!

make Without Makefile

Whenever you encounter a directory with a file called Makefile in it, you know you can run make to automatically compile/generate/do whatever is specified in the Makefile.

But you don’t even need such a file. You can also use make all by itself. To show an example for that, let’s create a file called hello.cpp with the following content:

#include <iostream>

int main()
{
  std::cout << "Hello, world!" << std::endl;
}

Now we can simply run …

make hello

… and it will magically be compiled (that is, if you have make and a C++ compiler installed).

Note

You can ignore the details for now, but if you are curious, this is the exact command that will be called by make:

g++     hello.cpp   -o hello

That means the C++ compiler is compiling (and linking) the source file hello.cpp to produce an executable file called hello (which you can run with ./hello by the way).

The command is automatically generated by this implicit rule:

%: %.cpp
	$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@

This uses the following variable declarations:

LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

LINK.cpp = $(LINK.cc)

The variable CXX is automatically set to the default C++ compiler (in our case g++). The other variables are by default undefined, but you can use them to specify any options you like.

You can find out about all the rules and variables that make considers by running make --print-data-base, but more about all that later.

A Simple Makefile

The command make hello was pretty simple, wasn’t it? But we can make it even simpler, but for that we first have to create a Makefile to specify all the necessary details. The most important things to write in there, are so-called rules. Those consist of targets, prerequisites and a recipe, which can have several lines.

target: prerequisites
	recipe
	...

A target starts at the beginning of a line and ends with a colon. On the right side of the colon, there are zero, one or several prerequisites (separated by spaces). Prerequisites are sometimes also called dependencies. On the following line, after a leading tab character, there can be a recipe, which will be used by make to update the target, if necessary. Recipes are sometimes also called commands.

But more about that later …

Let’s create a file called Makefile with the following content:

hello:

That’s it. Nothing more. Only one line. Only 6 characters.

This is a rule which contains only a target, i.e. the thing we want to get in the end. In our example that’s an executable named hello. There are no prerequisites and no recipe. When make is called without specifying a target (like we did before with make hello), make chooses the first target it encounters in the Makefile, which is in our case – what a coincidence! – hello. With our brand-new Makefile in place, we can now simply call:

make

… and it will do the same thing as before.

Todo

this can happen:

make: `hello' is up to date.

From now on, we will make our Makefile more and more elaborate and add many features, but the call to make will mostly stay that simple. That’s the beauty of it.

If you run make, it looks in the current directory for a file called Makefile or makefile. That’s nice, so we don’t have to specify explicitly which file should be used. If we do want to specify a different file, however, we can do so with the -f option, e.g.:

make -f MyOtherMakefile

Cleaning Up

Todo

make clean

Todo

clean-target, no prerequisites, one recipe

Todo

$(RM) vs rm

Adding Options

So using the built-in implicit rules is nice and easy, but what if we want to tweak some options?

Well, that’s no problem. Let’s say we want to add some options for the compiler, e.g. let’s enable some warnings. It’s always a good idea to enable compiler warnings! We do this by simply adding a variable to our Makefile:

CXXFLAGS = -Wall -Wextra -pedantic

hello:

When we now run make, we see that the actual command becomes:

g++ -Wall -Wextra -pedantic    hello.cpp   -o hello

So it worked, our command line option was put where it belongs.

There are a lot of pre-defined variables …

Todo

CXX, CXXFLAGS, CC, CFLAGS, CPPFLAGS, LDLIBS, LDFLAGS, …

Now we know how to add options to our Makefile permanently, but what if we want to try an option just once?

No problem, we can specify the directly at the command line as environment variables. For example, if we want to try if our program can also be compiled with a different compiler, we can do this:

CXX=clang++ make

clang++ -Wall -Wextra -pedantic    hello.cpp   -o hello

Let’s try something else. Let’s run the optimizer on our little program:

CXXFLAGS=-O2 make

g++ -Wall -Wextra -pedantic    hello.cpp   -o hello

But why? What happened to our -O2 setting?

Unfortunately, it was overwritten in our Makefile. If you want to allow specifying options in environment variables, you have to take care not to overwrite these variables. The easiest way to do this, is to use the += operator in your Makefile:

CXXFLAGS += -Wall -Wextra

hello:

CXXFLAGS=-O2 make

Et voilá:

g++ -O2 -Wall -Wextra -pedantic    hello.cpp   -o hello

TODO

make -p

make -j8