Separate Compilation

When a program is built from multiple source files, it is inefficient and, for very large programs, inconvenient to re-compile every source file each time we make a small change in one. Fortunately, we do not need to re-compile source files that have not changed so long as we compile and link in separate steps.

The hello_driver program consists of two implementation files

and a single header file

The main() is in hello_driver.cpp but two functions used by the program are defined in hello_util.cpp. Since there are only two implementation files, you would probably compile this as follows:

g++ -Wall -ansi -o hello_driver hello_driver.cpp hello_util.cpp

However, you could separate the compilation and linking steps:

g++ -Wall -ansi -c hello_driver.cpp
g++ -Wall -ansi -c hello_util.cpp
g++ -Wall -o hello_driver hello_driver.o hello_util.o

Note that if, say, only hello_util.cpp had been modified, we could omit the first line (the compilation of hello_driver.cpp), only compiling the implementation file that had changed.

If you had to type these commands everytime you built your program, it would be a nuisance, but it's very easy to accomplish the same thing using a makefile:

hello_driver: hello_driver.o hello_util.o
     g++ -Wall -o hello_driver hello_driver.o hello_util.o

hello_driver.o: hello_driver.cpp hello_util.h
     g++ -Wall -ansi -c hello_driver.cpp

hello_util.o: hello_util.cpp hello_util.h
     g++ -Wall -ansi -c hello_util.cpp

Here is how make would process this makefile assuming that only the file hello_util.cpp was updated since the last build:

  1. The first target is hello_driver, so that is what will be built.
  2. hello_driver has two dependencies: hello_driver.o and hello_util.o; look for rules to build these.
  3. hello_driver.o depends on hello_driver.cpp and hello_util.h, neither of which has changed since the last build, so there is nothing to do for this target.
  4. hello_util.o depends on hello_util.cpp and hello_util.h. hello_util.cpp has changed (has a more recent modification date and time) than hello_util.o, so run the recipe to produce an up-to-date hello_util.o.
  5. Finished with the dependencies for hello_driver, so go back to it's rule. hello_util.o was just built, so it is newer than hello_driver, so execute the recipe, linking hello_driver.o and hello_util.o to produce an up-to-date excecutable hello_driver.

The makefile for hello_driver is called Makefile.hello. You can tell make to process this makefile with the command:

make -f Makefile.hello

Try changing the modification date of a source file using touch and check that the makefile is functioning as you would expect. For example, the second invocation of make only compiles hello_driver.cpp and re-links the executable:

linux1[7]% make -f Makfile.hello 
g++ -Wall -ansi -c hello_driver.cpp
g++ -Wall -ansi -c hello_util.cpp
g++ -Wall -o hello_driver hello_driver.o hello_util.o
linux1[8]% touch hello_driver.cpp
linux1[9]% make -f Makfile.hello
g++ -Wall -ansi -c hello_driver.cpp
g++ -Wall -o hello_driver hello_driver.o hello_util.o
linux1[10]%