Makefiles

Compiling and using a make file:

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.

  1. It creates ‘relocatable’ code that can be loaded anywhere in memory that the operating system wishes to load it. That is, all of its addresses are given as offsets relative to the beginning of the code.
  2. It combines all the object files together, and ‘fixes up’ the addresses in the code that refer to objects or variables in the other modules. These addresses cannot be determined until all the object files are combined.




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

courses/cs203-201201/lectures/internal_link.txt · Last modified: 2012/01/18 17:24 by jones
VCCS Top Events Extended Site Search Login Vassar Science Web Vassar Home Driven by DokuWiki Valid XHTML 1.0