Makefiles give you an easy way to compile and otherwise manage large programs. You can do crazy stuff with makefiles, but for now you can use them to cut down on your typing and compile times, by reducing the "g++ -O blahblah.c -o blahblah" commands to simply 'make'. A makefile is a list of targets and their dependencies:

target1 : dependency dependency dependency ...
commands to build target1 from dependencies
commands to build target1 from dependencies

target2 : ...

Generally this file is called 'makefile' (usually lowercase, sometimes 'Makefile'), and the command used to process makefiles is 'make' or 'gmake'. 'gmake' is the GNU version of make, which is likely to be more standard across different platforms. If gmake is available, you're better off using it. (And if it's not available, make probably is gmake.)

Usually the target is an object file or a program, though it can really be anything (like a text file or even a 'dummy' target which never actually gets built). Following the colon is a space-delimited list of dependencies, usually the source and header files for the program (these can also be other targets). On subsequent lines, there is a tab (this must be a tab; spaces will not work) followed by a command to execute. This command is supposed to build the target from the dependencies. Here's an example:

hello : hello.h
g++ -o hello

The purpose of the list of dependencies is to keep track of what targets need to be rebuilt when you run 'make'. In this case, if a program 'hello' already exists, then its date is compared to and hello.h. If either or hello.h has changed since hello was last built, then hello is recompiled, otherwise make assumes that hello is up to date and does nothing.

You can have any number of dependencies in a makefile, and make considers the first one the default target (if you run 'make' with no arguments, it builds the default. You can also run 'make hello' to make the target called hello). Consider this makefile:

default : lab13

lab13 : lab13.o openfiles.o
g++ lab13.o openfiles.o -o lab13

openfiles.o : openfiles.h
g++ -o openfiles.o -c

lab13.o : openfiles.h
g++ -o lab13.o -c

In this makefile we split the compiling process into two steps: generation of the *.o object files (compilation) and generation of the lab13 executable (linking). The -c argument to g++ means 'just make an .o file'. This is a little more typing but has the benefit that we don't need to recompile every time we make a change to, and vice versa - we need only take the openfiles.o object file along with the newly recompiled lab13.o and link them together (linking is very fast compared to compiling).

Since this sort of thing is common, there are usually default rules which make this automatic. Here is the makefile that I use for most of my projects:

TARG = program_name
OFILES = support_obj1.o support_obj2.o support_obj3.o ...

CPP = g++
CPPFLAGS = -g -O2 -Wall

default : $(TARG)

$(TARG) : $(TARG).o $(OFILES)

This makefile uses variables to promote reusability; you can use this same makefile for a different project by simply changing the program name and the list of support .o files. (For the previous project, we would set TARG = lab13 and OFILES = openfiles.o). If you're paying close attention, you'll notice that there is no rule to build the .o files from their respective .cc files. This is because make has an 'implicit rule' about how to do this; when a dependency is a .o file and no rule exists, it knows to make the .o file by running:

$(CPP) $(CPPFLAGS) -o whatever.o -c

By setting the CPP variable to g++ and the CPPFLAGS variable to some flags we like (-O2 to optimize, -g to include debugging symbols, -Wall to warn us about possible errors), we can shorten most makefiles to just these few lines, and compile and manage big programs with just 'make'.

To read all about make:

man make


man gmake

Carnage Melon
Other 15-211 Stuff