Using a Makefile
CS 362 - Fall 2006 - Ms. Katz

Working with Multiple Source Files

Your C++ source files are growing. You have been using functions to develop your programs in small pieces, but now you will be writing your own classes with their separate header and implementation files. Programs without classes can also be stored in multiple files. Much like the class header files, you must include a prototype for any functions you use in one file but are defined in another file so that the compiler can determine whether the parameters are appropriate. The linker will collect the object files into one executable file.

To make this process easier and scalable to larger projects, you should use a Makefile in each project directory. The Makefile describes how to create an up-to-date version of an executable. When you type make, only those source files (ending in .cxx or .cpp) that have changed (or that depend on files that changed) are compiled and the object files (ending in .o) are linked together to create a new executable.

Every directory you use to build programs should have one copy of Makefile. Whenever you start to develop a new project, you should edit the Makefile that indicates what object files should be linked to create the executable. Don't start your Makefile from scratch; copy and then modify an existing one such as the ones described here. If this is a course project, be sure to generate the executable name your instructor expects.

Power Accompanied by Danger

Makefiles give you the power to list which files depend on others. Instead of recompiling every file in a directory, only those files that depend on the files you changed will be recompiled. Or you can clean up all the object files and do a clean compile. It's less typing and less to remember. Compiling only what is affected by your changes saves time.

You are writing that command to clean up. If you don't get it right, your source files could be deleted. Be very careful.

Example with Multiple Executable Targets

The Makefile is here as Makefile.txt. Note that in your directory it should have no file extension. This is a slightly more complex example than you'll usually need so that you can see the flexibility and power of makefiles.

For this example, two projects are being developed in the same directory. The other project has all the code in one file named other.cpp. The executable for it will be placed in a file named other. The more involved version, theProject, has five files theProject.cpp, class1.h, class1.cpp, class2.h, and class2.cpp. The executable for it will be placed in a file named theProject.

If the Makefile is in the directory with the source files, you can type make and the default (first) target will be created. Or you could type make theProject to create the theProject target. In this case, the default target is described on the line that starts with all.

The line
   all: theProject other
means that to create all, the targets theProject and other must be created.

The lines
   theProject: $(ProjectObjects)
          g++ $(CFLAGS) $(ProjectObjects) -o theProject
mean that to create theProject, the ProjectObjects must be created, and then they must be linked, using this compile command, to create the theProject executable.

The ProjectObjects are not final products; they are intermediate object files. They are listed near the top of the file and end in .o. Be very careful that these end in .o rather than .cpp or .cxx. The dependencies section near the bottom notes that those objects depend on their respective .h files. The make program uses the general rules in the no need to change part to create the object files (theProject.o, class1.o, and class2.o) from the source files (theProject.cpp, class1.cpp, class1.h, class2.cpp, and class2.h) using the g++ compiler.

After the object files are created, the theProject target is created by linking the objects together and creating the executable file named theProject. At this point, you could run the program by typing ./theProject but the make program is following the rules and dependencies to create the other target. When both theProject and other have been created, all is complete and the make program ends.

Note that there is a clean target. This describes how to clean up the directory removing the object and executable files. To use it, you would type make clean, but be very careful here to not remove your source files.

In the targets and other places where you need multiple lines, the white-space between the front of the line and the command must be tabs rather than spaces.



# This is a simple makefile that compiles multiple C++ source files

# set the names here to be the names of your source files with the
# .cxx or .cpp replaced by .o
# Be *** SURE *** to put the .o files here rather than the source files

ProjectObjects =  theProject.o class1.o class2.o
OtherProjectObjects =  other.o

#------------ no need to change between these lines -------------------
CFLAGS = -g -Wall
.SUFFIXES: .cxx .cpp

.cxx.o:
        g++ $(CFLAGS) -c $<

.cpp.o:
        g++ $(CFLAGS) -c $<
#------------ no need to change between these lines -------------------


#------------ targets --------------------------------------------
# describe how to create the targets - often there will be only one target

all: theProject other

theProject: $(ProjectObjects)
        g++ $(CFLAGS) $(ProjectObjects) -o theProject 

other: $(OtherProjectObjects)
        g++ $(CFLAGS) $(OtherProjectObjects) -o other 

clean:
        rm -f $(ProjectObjects) $(OtherProjectObjects) theProject other

#------------ dependencies --------------------------------------------
# put the .o that depends on a .h, then colon, then TAB, then the .h

class1.o:       class1.h
class2.o:       class2.h
theProject.o:   class1.h class2.h

A Simpler Version

The example above is a multi-target Makefile, but you could use an even simpler Makefile to create an a.out executable. In that case, just change the objects at the top and the dependencies at the bottom.