Three distinct steps in compiling C++ code to an executable:
- Compiling the code to Assembly code - This is usually not seen by the programmer when using modern compilers, it is combined with the next step
- Assembling the Assembly code to object code - Each source file is compiled and assembled into a binary file called an object file, consisting of machine language code, however it is not yet executable. Object code has an extension of *.o . If you compiled helloWorld.cpp, the object code would be helloWorld.o
- Linking the all the object code files together into an executable
The Linking phase does several things.
Suppose we have the following set of files that form a complete program:
main1.cpp mylib.cpp mylib.h openfile.cpp openfile.h
Assuming that the .cpp files have the appropriate #include statements for the header files, the following command will compile it to an executable file called myprogram:
g++ main1.cpp mylib.cpp openfile.cpp -o myprogram
If we change some code in openfile.cpp, for example, we can recompile the project using the above command.
There was really no need to recompile the other .cpp files, but we recompiled everything anyway.
For larger projects, given the number of times we would likely compile the code before it is correct, this can add up to a lot of wasted time and cpu cycles. A more intelligent way to compile the project would be handy.
Enter the makefile:
There is a Unix command called ‘make’ that reads a text file called a makefile and uses the information in the makefile to recompile only those parts of your project that have changed, and links the result to a new executable.
The make file must list the required source code files, and show all the dependencies among the files. For each source file we must list the files that it depends upon, so that changes to any of these dependencies will trigger a recompilation of that source file.
We would then recompile with the command:
make -f <makeFileName>
The command make, if not given any parameters will look for a file named makefile, or Makefile, or GNUmakefile, and use it to recompile the project. This is another good reason to put each project in its own directory, so you can create a makefile called makefile, and compile the project by simply typing make.
From the previous example, this is what the makefile would look like:
# myprogram.make -- this is a comment line, ignored by make utility myprogram : main1.o mylib.o openfile.o g++ -o myprogram main1.o mylib.o openfile.o # Above, we are saying myprogram depends on main1.o, mylib.o and # openfile.o and to create myprogram we give the g++ command as shown on # the next line which starts with a TAB although you cannot see that. # Note that the command : g++ -o myprogram main1.o mylib.o openfile.o # creates an executable file named myprogram from the 3 object files # respectively. main1.o: main1.cpp openfile.h mylib.h g++ -c main1.cpp # Above we are saying main1.o depends on main1.cpp openfile.h and # mylib.h and to compile only main1.cpp if and only if main1.cpp or # openfile.h or mylib.h have changed since the last creation of main1. # The -c parameter tells the compiler to produce object code only, do # not run the Linker. mylib.o: mylib.cpp mylib.h g++ -c mylib.cpp # Above we are saying mylib.o depends on mylib.cpp and mylib.h # so if either mylib.cpp or mylib.h CHANGED since creating mylib.o # compile mylib.cpp again openfile.o: openfile.cpp openfile.h g++ -c openfile.cpp # Above we are saying openfile.o depends on openfile.cpp and openfile.h # so if either openfile.cpp or openfile.h has CHANGED since creating # openfile.o, compile only (again) openfile.cpp clean: rm *.o myprogram # Above we are stating how to run the rule for clean, no dependencies, # what we want is when we ask to do a "make -f lab1.make clean" # that will not do anything except remove executable and object files # so we can "clean out" our directory of unneeded large files. # We only do a make clean when we want to clean up the files. # END OF MAKE FILE
Once you create the make file shown above, save it in the directory that contains your source-code files.
Now at the command line (the terminal window), we want to tell the make utility to actually “make” or “build” the program. If you saved your makefile as “makefile”, the command is:
$ make
If you named it something else, such as myprogram.make, the command is:
$ make -f myprogram.make
If each .o is successfully created then the executable program, myprogram, should be created.
To actually run the program we would “execute” myprogram with the command:
$ ./myprogram
If you change ANY file involved in the creation of the program, any .cpp file or any .h file, then you MUST “make” the program again by executing the make command.
Remember, the command “makes” or builds the program, but it does so wisely. The make utility will only rebuild those files that are used to build the targets if the dependency files have changed since the previous “make” of the program.
The command:
$ make clean
Deletes only the executable file and all *.o files.
Some example make files:
#account.make # example make file for use of the class Account # files: source files: account.cpp accountmain.cpp # header file: account.h # executable file: account # # first define target file : account # dependencies are the object files that build the program account: account.o accountmain.o g++ -o account account.o accountmain.o # now define how each object file is a target and list dependencies and how # to build that object file if any dependencies change account.o: account.cpp account.h g++ -c account.cpp accountmain.o: accountmain.cpp account.h g++ -c accountmain.cpp clean: rm account account.o accountmain.o # end of makefile
# example2.make # example make file using variables # first name a variable objects for all object files objects = account.o main1.o # name a variable 'sources' for all source files sources = account.cpp main1.cpp # now give target as example2 with objects as variable dependencies + command line example2: $(objects) g++ -o example2 $(objects) # list the dependencies for object files - those header files which help build objects account.o: account.h main1.o: account.h # how to build all object files from all dependent source files $(objects): $(sources) g++ -c $(sources) clean: rm $(objects) example2
NOTE: For a complete list of options for the make command, read man make