Example: a makefile for Separate Compilation

Suppose you are writing a C++ program that has the following files:

  • Student.h: header file containing the declaration of the Student class and related functions
  • Student.cpp: the implementation of the methods and functions declared in Student.h
  • Course.h: header file containing the declaration of the Course class and related functions
  • Course.cpp: the implementation of the methods and functions declared in Course.h
  • course_test.cpp: some testing course for Course and Student, including a main function

Here is a makefile that helps compile and link these files:

test: course_test.o Student.o Course.o
        g++ -o test course_test.o Student.o Course.o   # indent must be a tab

# only need to link if course_test.cpp has changed
course_test: course_test.cpp
        g++ -c course_test.cpp                         # indent must be a tab

# only compile if Student.h or Student.cpp has changed since last compile
Student: Student.h Student.cpp
        g++ -c Student.cpp                             # indent must be a tab

# only compile if Course.h or Course.cpp has changed since last compile
Course: Course.h Course.cpp
        g++ -c Course.cpp                              # indent must be a tab

clean:
        rm -f test course_test.o Student.o Course.o    # indent must be a tab

# same compiler options as for the course makefile
CPPFLAGS = -std=c++14 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -g

This makefile consists of 5 rules, and one variable (CPPFLAGS) definition.

Consider this rule for create Course.o:

Course: Course.h Course.cpp
        g++ -c Course.cpp        # indent must be a tab

The first line of the rule gives the rules name, Compile, and then lists the dependencies of this rule. In this case, the dependencies say that the following statement only needs to run if Course.h or Course.cpp has changed since the last compilation. This can be a great time-saver in large programs: makefiles only re-run rules when necessary.

The second line of the rule is the code that will be run when the rule is executed. In this rule, only one statement is run, i.e. Course.cpp is re-compiled. The compiler options listed in the CPPFLAGS variable are automatically included when g++ is called.

One annoyance with makefiles is that the indentation of the second line must be done with a tab character. If you use spaces for the indent, then you will get an error.

For this particular program, the programmer would usually call make test like this:

$ make test
g++  -std=c++14 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -g  -c -o course_test.o course_test.cpp
g++  -std=c++14 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -g  -c -o Student.o Student.cpp
g++  -std=c++14 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -g  -c -o Course.o Course.cpp
g++ -o test course_test.o Student.o Course.o

This creates the executable file named test, which you run like this:

$ ./test
CMPT 100 has this many students: 3

If you were to call make test again without changing any of the .h or .cpp files, you get this:

$ make test
make: 'test' is up to date.

Thanks to the rule dependencies in the makefile, make realizes that there is no need to re-compile or re-link anything.

Now suppose you course_test.cpp to change the output message to CMPT has ?? students. After making the change, you get this:

$ make test
g++  -std=c++14 -Wall -Wextra -Werror -Wfatal-errors -Wno-sign-compare -Wnon-virtual-dtor -g  -c -o course_test.o course_test.cpp
g++ -o test course_test.o Student.o Course.o

$ ./test
CMPT 100 has 3 students

make notices that only course_test.cpp has changed, and so it only needs to re-compile course_test.cpp, and then re-link test. There was no need to re-compile Student.cpp and Course.cpp since they haven’t change since the last time they were compiled.

Finally, the clean rule in the makefile is used to get rid of all the .o files and test:

$ make clean
rm -f test course_test.o Student.o Course.o

This is standard rule in most makefiles, and us used to clean up any files when you want to re-compile and re-link everything afresh.

Source Code

All the files for runing this example are here: separateComp.zip.