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
- hello_driver.cpp
- hello_util.cpp
and a single header file
- hello_util.h
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:
- The first target is hello_driver, so that is what will be built.
- hello_driver has two dependencies: hello_driver.o and hello_util.o; look for rules to build these.
- 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.
- 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.
- 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]%