Programming in Linux (part 2)

This handout continues with a summary of the commands you will use when compiling C++ programs on a Linux system. For further information on each of these, consult the corresponding man page (for example, do man g++). Many of the utilities come from the GNU project, which doesn't like to maintain man pages, so frequently there will be more information in the info system (do info g++, for example).

Compiling and Linking

The C++ compiler is g++. If you just have a single source file, for example myprog.cc, then you may compile and link it in one step with the command
g++ myprog.cc -o myprog
If there are no errors, this will produce an executable named myprog in the current directory. To run it, type
./myprog
(the ./ prefix tells the command shell where to find the program, since by default it may not look in the current directory for programs).

If your program consists of several source files, you will need to compile each of them into an object file and then link them together into an executable. For example, suppose that myprog2.cc uses a class that is implemented in myclass.cc (there should also be an interface in myclass.h, but the #include line in myprog.cc handles that; by the way, most facilities from the Standard Library will be automatically linked with your program, so the #include is all that you need to use them). You will use g++, with the -c (for ``compile'') option, once on each source file to produce the object files, which have a .o extension:

g++ -c myprog2.cc
g++ -c myclass.cc
Then the object files need to be linked together with the command
g++ myprog2.o myclass.o -o myprog2
This process is considerably simplified by writing a make file (see below).

There are many options you may supply to g++; see the documentation for a list (with full descriptions in the info viewer). Some of the most common are -Wall, which tells the compiler to print all of the warnings of possibly incorrect code that it finds, -g, which causes the compiler to insert debugging information in the compiled code (for use with gdb), and -O, which turns on some of the code optimizations (unlike some compilers, g++ allows -O to be used with -g, although optimized code might do some unexpected things while debugging). For example, the file myprog.cc could be compiled with warnings and debugging information by executing

g++ -Wall -g myprog.cc -o myprog

Project Management

As noted above, compiling all of the files that comprise a large project can involve lots of repetitive typing of commands. In addition, after editing some of the source files, you should only recompile those that have changed. This is not as trivial as it may seem, since editing a header file will require recompiling all of the implementation files that include it, directly or indirectly. The solution to all of these problems is to create a make file, which is a compact description of all of the pieces of a multi-file project. The make command takes this file and executes all of the required steps to bring the project up to date.

Here is a simple make file, which should be stored in the project directory under the name Makefile:

# Make file for myprog2
myprog2 : myprog2.o myclass.o
	g++ myprog2.o myclass.o -o myprog2

myprog2.o : myprog2.cc myclass.h
	g++ -c myprog2.cc

myclass.o : myclass.cc myclass.h
	g++ -c myclass.cc

The first line is a comment; make ignores any line that starts with #. The next two lines describe how to make the full program (make will use the first such entry in the make file as the default target to be made, so it is a good idea to put the entry to link your program first). The line with the colon describes the conditions under which the rule will be applied. It says that the file myprog2 depends on the files myprog2.o and myclass.o; if myprog2 doesn't exist, or is older than either of the .o files, then the body of the rule will be used to produce an up-to-date version of myprog2. The body of the rule, the line with g++, gives the command to execute. It is important to note that this line starts with a TAB character; it will not work if the line starts with spaces.

The following entries describe how to produce the .o files, if they don't exist or are out-of-date. As before, the line with the colon describes the dependencies of each file: myprog2.o depends on both myprog2.cc and myclass.h, so it will need to be recompiled if either of those files has been changed. In general, the list of dependencies for an object file will be the corresponding source file plus all of the (non-standard) header files that it includes. There is a tool called makedepend which will automate the process of generating these dependency lists; see man makedepend.

Instead of creating a make file such as the above from scratch for each project, it is a good idea to start with a make file template and customize it. Here is a simple example:

# Makefile template
#
# Fill in the definitions of the variables:
# SRCS = a list of your .cc files
# HDRS = a list of your .h files
# PROG = the name of the executable you are making
#
# Then execute "make depend" followed by "make"
#
# Use "make print" to send nicely formatted listings to the Linux lab printer
# Use "make clean" to remove the executable and other files generated by make

SRCS = myprog2.cc myclass.cc
HDRS = myclass.h
PROG = myprog

# You should not need to change anything below this line.

OBJS = $(SRCS:.cc=.o)

CXX = g++
CXXFLAGS = -Wall -g -I/home/libs/dataStr/ftsoftds/include
LIBS = -L/home/libs/dataStr/ftsoftds/lib -lezdraw -L/usr/X11R6/lib -lXaw -lXmu -lXt -lX11

DEPFILE = .depends
PRINTER = JSC276

all : $(PROG)

$(PROG) : $(OBJS)
	$(CXX) $(CXXFLAGS) $(OBJS) -o $(PROG) $(LIBS)

depend :
	$(CXX) $(CXXFLAGS) -MM $(SRCS) > $(DEPFILE)

print :
	a2ps -A virtual $(HDRS) $(SRCS) | lpr -P $(PRINTER)

clean :
	rm -f $(PROG) $(OBJS) $(DEPFILE) core

sinclude $(DEPFILE)
When you execute make depend, it will compute a series of rules (stored in the hidden file .depends) which will be included in the make file which cover building the object files from the sources, taking into account any header file dependencies. I also included targets print and clean in this example; doing make print will send a nicely formatted listing of your source programs to the laser printer in 276, while make clean will remove all of the files that may have been generated in previous compiles, so that you can do a clean compile of all of the parts of the program (or so you can reduce disk usage when you no longer need to run the program).