Valid
	XHTML 1.1! Valid CSS!
Created 2004-01-01   Modified 2009-05-29
Chelton Evans

proj Compilation : Know thy Compiler home

make utility
g++ options
linking
gdb ddd
Macros in make
Capturing Error Messages in Unix
Inlining in C++
Name space Collisions
Error Strategy
gprof
Mac

Intro

Makefiles inherently bring the programmer closer to the Os. Unix derives much of its flexibility from the command line where different configurations are made possible by entering in different command line options, the windows equivalent is much harder as it relies more on the Gui which isn't appropriate for automation.

I now use makefiles to manage dependencies and invoke the compiler. The only call of the os is the compiler. Although I have not programmed windows for years, it is conceivable that makefiles could be used with windows to generate dll's, especially if you have Cygwin.

make utility

Managing Modularity: Makefiles and Libraries

make expects a filename called Makefile . If your makefile is differently named use make -f <makefilename> option.

Makefiles have targets and target code. A target generally starts on its own line and the following line starts with a tab, then the target code. For example
clean:
    rm -rf html.o main
.

$make clean to run target clean.

The target can be either a tag or a file. The colon after the target lists the dependencies. When the target and dependencies are files make compares the time stamp of the target with the dependencies. If any of the dependencies have a newer time stamp than the target the target code is triggered, else nothing happens.

For templated files these need to be in the main programs compilation so that when triggered the main program is re compiled. However if the templated files have dependencies with standard compilation and linkage treat these as dependents of the main program.

dll's can be simulated by compiling more than one cpp file to an ojbect file. e.g.
g++ -g -I. f1.cpp f2.cpp -o fk.o

Problem: the compiler options after the first are not being recognized.
Solution: the option has to be flush against the equals sign.
    e.g. = -c   to   =-c

g++ options

-I<include path>
-l<library name> On Linux the library file is named lib<name>.so .
-L<library path>

Explicit Linking

/usr/lib/libglut.a on your compile line instead of -lglut .

Linking order

I found an example where linking that was not done as the last step produced and error. See NTL .

C++ Linking with directories

The benefits of C++ namespace are unquestionable, but its with interest that C++ header inclusions can also benefit by hierarchical structures with the use of directory prefixes. #include <NTL/ZZ.h> It makes it clear that the header is part of the NTL library and not some other library.

makefile example

File Structure:

     ---------cmdln---include
          |
          |   *
          ----html----include

     * the current directory

CC=gcc
CFLAGS=-Wall -I. -I./include
OBJ=cmdln.o html.o

all: main.cpp makefile $(OBJ)
   $(CC) $(CFLAGS) main.cpp -o main $(OBJ)

html.o: html.cpp include/html.h
   $(CC) $(CFLAGS) -c html.cpp
cmdln.o: ../cmdln.cpp ../cmdln/include/cmdln.h
   $(CC) $(CFLAGS) -I../cmdln/include -c ../cmdln.cpp

clean:
    rm -rf *.o main

The project is in /html directory. /cmdln is an adjacent directory. Both these files are compiled to object code relative to /hmtl. all depends on these object files. The object files themselves depend on the source files, so if their time stamp is newer than the compiled object files the object files are recompiled and all is triggered because an object file has changed so all/main is recompiled.

This is a standard compilation where object files are created in the target directory(/html in this case), and linked as the last step.

It is possible to automate the makefile further. Place all directories in the lookup path in one of the compiler options - e.g. CFLAGS=-Wall -I. -I./include -I../cmdln -I../cmdln/include and use makefile script to automate compilation, but the above is simple.

Macros in make

Variables can be overloaded at the command line by single quoting the variable name and new data. For example a generic makefile for compiling programs with the same compiler options is possible.

Makefile
CC=g++
CFLAGS=-Wall -I. -lGLU -lGL -lglut -L/usr/lib -I/usr/include/GL
PROG=opgl05
main: $(PROG).cpp
    $(CC) $(CFLAGS) $(PROG).cpp -o $(PROG)

Command line
$make 'PROG=opgl07'
Even better the version of make I use doesn't need the quotes.
$make PROG=opgl02 EXTRA=1

gprof

C++ -pg ...
$ gprof ./main

Capturing Error Messages in Unix

When thousands of error messages spew forth its good to read the first few.

$ make 2> /tmp/crap.txt; head --lines=15 /tmp/crap.txt

Note: this strategy is good when you do not have persistent warning messages cluttering the screen. I literally get rid of all warnings in my code - if that means cleaning up I do it. For others it could be switching a compiler flag.

Inlining in C++

GNU make is an excellent document and gives a much better description of the make utility. However like all fairy-tails there are problems. My g++ compiler can not handle inlined functions outside of a class in header files with the object file and .h file ideology. It can not handle the inline directive in the .cpp file either.

These collisions are important. To keep the object file and header file ideology only place inline stuff in the classes in the header files.

Previously I put everything in the header files, and for template classes this is probably mandatory. To reduce unnecessary compilation for a medium sized project I use the object file and header file myth.

Object files are assembled at the last step. Where ever the directories compile the object files to the current directory. I have experimented with using bash to manage object files but this is not portable and hence silly.

Name space Collisions

Namespace collisions are no fun. In windows MS 6.0 C++ compiler the .cpp was used to hide functions and data that did not go into the global namespace. So when I did the same in Unix I got a shock when I found the function in the .cpp file in the global namespace as it was exported in the object file.

I knew that this was traditionally the domain of the compiler, but with strict language standards the compiler option is not what it used to be, and I read pages of options without luck.

So I went to the language for solutions and used namespaces to hide local functions, they can still be exported into the global space but they will be wrapped in their namespace. Further this is portable code, so it addresses namespace collisions in a portable way! See Coding Conventions: Namespace Collisions .

Error Strategy

Most programmers these days regard catching errors as solely the domain of the programming language and not the compiler, and I am glad the programming has evolved this way. But there is always a different way of doing businesses. C++ lets you do this where other languages don't. Error management is the most important programming task.

Rather than use exceptions the technique of using assert conditions forces the programmer to act at compile time. I have almost never met a bug that can not be reproduced. The strategy is to produce code that is robust by capturing errors with assert statements. For production code this debug code is thrown away. While this may be unpopular, combined with type safety and other good programming practices it produces excellent code. However the programmer always has the responsibility of writing excellent testing code too (something has to be asserted on).

Note that I agree with exception code for writing libraries and perhaps file management in hostile environments, but for standalone applications I have never had the need to use exception handling yet. However I do like exception handling as it is a primitive mechanism that solves problems. It is just that it is another technique which I choose not to use because I believe in catching the errors with asserts.

Including #include<cassert> in the file headers, when NDEBUG is not defined the assert statement is compiled into the source code. To exclude the debug code from a build make -DNDEBUG defines NDEBUG and excludes the code surrounded by the #ifndef NDEBUG directives.

However if you use this option you need to recompile all files to do this. Because the makefile relies on timestamps, if you change a command line argument it does not rebuild all the files from scratch. So delete all files and rebuild with the new compiler directive.

I really believe in this type of error management. You can have computationally intensive error checking, but the final code is really fast. I am very grateful to my first real boss who taught me the technique. However I have really learned it over the years writing my own code, error strategy is your friend.

Mac

$ g++ <file.cpp> -framework GLUT -framework OpenGL
$ ./a.out