5.3 Unix/GNU Toolkit


 

The two basic tools for rebuilding a system written in C++ are the g++ compiler and the make utility. The compiler, and its associated preprocessor, performs compiling and linking tasks depending on how it is used. The make utility uses a structured file of dependency information created by the developer and rebuilds the system according to this information. One of the basic steps of the make utility is, of course, to use the C++ compiler, in our case g++, to recompile source code files.

The g++ Compiler

The general syntax for invoking the g++ compiler is

   g++  {Flags} {Paths} -o {Executable} {SourceName(s)} {LibraryName(s)}

The bracketed terms represent places in the command where optional arguments to the compiler may be given. The brackets are not included in an actual command line; they are used here only to identify the parts of the command line.

{SourceName(S)} is a list of one or more code files to be compiled. The -o {Executable} option allows the name of the compiled executable file output by the compiler to be specified. The default name of this file is "a.out".

The {Flags} in the command-line represents the setting of various compiler options. Typical compiler flags are:

       -g      generate information for the debugger
        -O      (upper case letter O) turn on code optimization
        -w      turn off all warning messages
        -Wall   turn on all warning messages
        -c      produce only an object file; don't link.

The complete list of compiler options is extensive.

A program often uses existing routines and classes. Examples of these include system routines (e.g., open a file), windowing-system routines (e.g., pop up a menu), general-purpose classes (e.g., a class for string manipulation), or other collections of application specific classes (e.g., for graphics).

To use existing routines and classes the compiler must know where to find the include files that define the functions and classes being used and where to find the precompiled libraries of object files. The compiler (actually the preprocessor cpp) has a standard set of directories in the file system where it looks to find include files that are named by the #include directives. The compiler also has a standard set of directories in the file system where it looks to find libraries of precompiled object files that contain the code for the functions and classes defined in the include files. It is often necessary to direct the compiler to look in additional directories for include files or libraries. This information is given in the {PATHS} part of the general command shown above. Each additional directory to search is specified by a path name prefixed by either "-I" (for a directory containing include files) or "-L" (for a directory containing libraries. For example:

 -I/usr/local/include -I../../include -L/usr/local/lib -L/home/cs2704/lib

specifies two additional include-file paths and two additional library-file paths. The include-file path -I/usr/local/include specifies an absolute path name (one starting at the root of the file system), while the include file path -I../../include specifies a path relative to the current working directory. Both of the library search paths are absolute, although relative names may also be used when appropriate.

There are two rules of good usage for organizing and naming the search paths for include files and libraries. First, directories containing include files and libraries should be named "include" and "lib", respectively. This is a common and well-understood convention. Second, the -I paths on the command line should be grouped together and appear before all of the -L paths. This is common practice.

The compiler must also be told which non-standard libraries to search. The file name for a library has a standard form:

               lib{something}.a


where the .a suffix stands for the term archive and the "lib" prefix is required. The "{something}" gives the discriminating name for the library. Some common libraries are:

       libm.a          math library
        libc.a          c language library
        libg++.a        g++ library
        libXm.a         X-based motif library


A library to search is given to the compiler in the abbreviated form

       -l{something}


For example, to search the standard g++ class library, the term -lg++ would be used. Note that the required prefix (lib) and required suffix (.a) need not, and indeed cannot, be given.

The make Utility

The make utility is a powerful tool for managing the creation and maintenance of systems and libraries. The actions of the make utility are controlled by a "makefile" that describes the dependencies among parts of the system, and the rules needed to rebuild parts of the system. Using the information in the makefile and the creation/modification times recorded in the file system for each file, make uses the dependency information to determine which parts of the system need to be rebuilt and, using the rule information, can dictate how to rebuild those parts.

 

Rules


A basic element in a makefile is a rule. The general format of a rule is:

       {target}:      {dependency list}
                 {tab} {command list}

The {target} names an object file or a complete system that make will build or a utility function to be performed by make. The target must be followed by the required colon character (:) . The {dependency list} is a list of other elements on which the given target depends. The {command list} is a list of commands to be performed when the {target} is being built or performed by make. The {tab} is a required character that must begin the line containing the command list.

Both the dependency list and the command list must be a single line. Either list, however, may be quite long. The standard technique for making the list readable, while still ensuring that the make utility will view it as a single line is to employ a backslash ("\") character immediately preceeding the carriage return that ends the current line and begins a new line. The carriage return character will cause a second line when displayed on the screen or printed but, when read by the make utility, these two characters will appear as a blank. Examples of this will be seen below.

The first kind of targets are those that name system components that make will build. A common target of this form names an object file to be built by compiling one or more code files. For example, a rule to build the FileChooser.o object file might appear as:

       FileChooser.o:  FileChooser.cc, FileChooser.h, Directory.h, \{cr}
                        Selector.h, File.h {cr}
                {tab}   g++ {...} -o FileChooser.o FileChooser.cc {...}

In this example, the target is named FileChooser.o. Since the list of dependencies is long, the backslash character is used to break the single logical "line" into two physical lines. The {tab} notation indicates that a tab character must begin the line containing the command. In this case, a command to compile FileChooser.cc using the g++ compiler is shown in abbreviated form. The notation {...} shows where additional parts of the command would appear.

The second type of target names a complete system or library to be built by make. For example, a target such as

       Program:  FileChooser.o Directory.o Frame.o {...}
                  g++ {...} -o Program *.o {...}

describes how to build an executable program named Program by having g++ link all of the object files in the current directory. Note that g++ can be used to compile code files as well as link object files. The target Program can be refered to in the invocation of make as

       make Program 

which will cause the make utility to find and perform the actions required by a rule whose target is named Program in a file named makefile. Alternatively, if Program is the first or only target in the makefile, then the same actions will occur simply by using the command make with no arguments.

The third type of target names a utility function performed by the makefile. A common target of this form is the "clean" target that cleans up after the make utility by removing intermediate files produced during the running of make but not needed for longer-term use. A typical example is

       clean:
                rm *.o core ./tmp/*

which deletes all object files in the current directory, a core file that might have been produced when a test program crashed, and all files in a subdirectory named tmp.


Variables


A makefile may contain variables whose value may be refered to more than once in the makefile. For example, the makefile may need to give the list of include files on each of a series of compile commands or it may have to give the long list of all of the object files in two or more places in the makefile. To avoid this repetition, a variable is defined once and used in the makefile wherever its value is needed.

Variables are similar to simple macros; the value of a variable is a simple string that is inserted verbatim wherever the value of the variable is referenced.

By convention, variables are given suggestive names that are written in capital letters, such as OBJECTS, OBJDIR, TMPFILE. The value of a variable is a string of any length that is assigned to a variable using an equals sign (=) operator. For example:

       CC= g++
        COMPLIBS= -lgen -ldl -lsocket -lnsl -lg++
        CCFLAGS= -Wall -O
        INCLUDEPATH= -I/usr/include/X11
        LIBPATH= -L/usr/lib/X11

        OBJECTS=  Base.o         BasicFrame.o  Button.o         \
                  Counter.o      Date.o        Directory.o      \
                  File.o         FileQuery.o   FileNavigator.o  \
                  FileChooser.o  Frame.o       Location.o       \
                  Message.o      Selector.o    Shape.o          \
                  TextBox.o      Timer.o       Program.o

This example defines several make variables: OBJECTS is a list of .o files written over several lines using the \{cr} technique; CC defines what the C++ compiler is called on this system - variables like this one are often defined to aid in porting the software among different platforms that may use different compilers; COMPLIBS is a list of libraries to include when an executable program is built; CCFLAGS defines options to the g++ compiler indicating that all warning messages should be displayed (-Wall) and that is should optimize the compiled code (-O); INCLUDEPATH is a compiler option specifying where to look for header files (in this case header for the X windows system); LIBPATH specifies where to look in the file system to find the X windows system library.

The value of a variable is obtained by enclosing the variable name in parentheses preceded by a dollar sign, as in $(OBJECTS) or $(CC). Several examples of using the values of variables are these:

       LDLIBS = -lwx_motif -lXm -lXt -lX11 -lm $(COMPLIBS)
        PATHS = $(INCLUDEPATH) $(LIBPATH)

        Program.o:  Program.cc
                $(CC) -c $(CCFLAGS) -o $@ Program.cc

        all:    $(OBJDIR) $(OBJECTS) 
                $(CC) $(CCFLAGS) $(PATHS) -o Program $(OBJECTS)  $(LDLIBS)

The first two examples show how one variable can be used in defining the value of another variable. In each case, the value of one variable is simply concatenated with the other characters around it to form the value of the variable being defined. The third example shows a rule where the command list uses several variables: the name of the compiler to use (CC) and the flags to be passed to the compiler (CCFLGS). The symbol $@ is a built-in make variable that refers to the target's name without any suffix - in this case $@ would be "Program" because Program.o is the target and the .o suffix is removed to define the current value of $@. The final example illustrates that variables can be used in both the command list and the dependency list. In all three parts of this example, the target "all" depends on everything that is named in the values of OBJDIR and OBJECTS.

Other References

Other material on the make utility may be found by following these links:

 




©1998 Prentice-Hall, Inc.
A Simon & Schuster Company
Upper Saddle River, New Jersey 07458

Legal Statement