In the post Compiling C and C++ Programs, I’ve described how to compiling C and C++ programs with commands gcc and g++ respectively. It’s enough with simple programs. But in the situation where the program contains lots of source files and the dependence between the source files are complicated, using compile commands directly to maintain the program becomes quite impractical and MakeFile is on need.

The Basic MakeFile

The basic makefile is composed of:

1
2
target: dependencies
    command

When you finish writing the MakeFile, you can use following basic command to compile the program:

1
make

This will look for a file named makefile/Makefile/MakeFile in current directory, and then make the MakeFile. If there are several MakeFiles, use following command to specify the desired MakeFile:

1
make -f theMakeFile

Organizing the project on dependencies

When a program contains numerous source files, if you still recompile the whole program every time you modify some source file, it will cost much time. To avoid that, you can make the use of different targets to organize the program correctly according to the dependency relationship between source files. Now the MakeFile will take care of the process of compiling and update the program by recompiling things modified or to be modified.

For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
all: executable

executable: main.o source1.o source2.o
    g++ main.o source1.o source2.o -o executable

main.o: main.cc
    g++ -c main.cc

source1.o: source1.cc
    g++ -c source1.cc

source2.o: source2.cc
    g++ -c source2.cc

In the example, the target all has only dependencies, but no commands. In order for make to execute correctly, it has to meet all the dependencies of the called target (in this case all). Each of the dependencies are searched through all the targets available and executed if found.

Using variables and comments

You can also use variables when writing Makefiles. It comes in handy in situations where you want to change the compiler, or the compiler options.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#Now we define some constants CC and CFLAGS.
#CC specifies which compiler to use
#CFLAGS is the list of flags to pass to the compilation command.

CC = g++
CFLAGS = -c -Wall

all: executable

executable: main.o source1.o source2.o
    $(CC) main.o source1.o source2.o -o executable

main.o: main.cc
    $(CC) $(CFLAGS) main.cc

source1.o: source1.cc
    $(CC) $(CFLAGS) source1.cc

source2.o: source2.cc

Simple macros

1
2
3
4
5
6
7
8
9
10
11
12
CC = g++
CFLAGS = -c
DEPS = header.h
OBJ = main.o source1.o source2.o

all: executable

executable: $(OBJ)
    $(CC) $^ -o $@

%.o: %.cc $(DEPS)
    $(CC) $(CFLAGS) $< -o $@

In this example, we create macro DEPS and OBJ. DEPS is the set of .h files on which the .c files depend. OBJ is the list of object files. %.o means file ending in the .o suffix, %.cc is in similar case. $@ says to put the output of the compilcation in the file named on the left side of the :, while $^ represents the right side of :. $< is the first item in the dependencies list.

Last example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
IDIR = ../include
CC = g++
CFLAGS = -c -Wall -I$(IDIR)

ODIR = obj

LDIR = ../lib
LIBS = -lm
LDFLAGS =

_DEPS = header.h
DEPS = $(patsubst %, $(IDIR)/%, $(_DEPS))

SOURCES = main.cc source1.cc source2.cc
_OBJECTS = $(SOURCES:.cc=.o)
OBJECTS = $(patsubst %, $(ODIR)/%, $(_OBJ))

EXECUTABLE = executable

all: $(SOURCES) $(EXECUTABLE)

$(EXECUTABLE): $(OBJECTS)
    $(CC) $(LDFLAGS) $^ -o $@ $(LIBS)

$(ODIR)/%.o: %.cc $(DEPS)
    $(CC) $(CFLAGS) $< -o $@

.PHONY: clean

clean:
    rm -rf $(ODIR)/*.o *~ $(IDIR)/*~ $(EXECUTABLE)

In this example, the MakeFile should be located in the src directory. Note that it also includes a rule for cleaning up your source, object directories and the executable if you type make clean. The .PHONY rule keeps make from doing something with a file named clean.

Until now, you know basic usage of MakeFile. For advanced usage, check this post: More On Makefile And Autodependencies.