Build Configuration for the GABI Library

Introduction

The GABI Library has been designed so that the sources are as portable as possible, without a single #if, #ifdef or #ifndef. To build the libraries, however, is somewhat more difficult; how to invoke the compiler varies enormously from one system to another, from one compiler to another, and even from one library to another.

The purpose of this document is to describe the different techniques used to ensure the portability of the build process, so that in fact, for any given system, it is sufficient to set up a few variables specifying the system, and invoke make in the root directory for everything to be built correctly.

Preconditions concerning the tool chain used

While a great deal of effort has been expended to make the C++ source code as portable as possible, and it is the goal that it can be compiled with just about any C++ compiler, writing truely portable makefiles, which will be correctly understood and interpreted by makes as varied as nmake or GNU's make is beyond me. In addition, there are cases, such as the generation of main() for the test programs, which are simply easy to do as a shell script—provided we have a reasonably compatible shell available.

Rather than introducing yet another layer of complexity in order to manage portability in the tool chain, I have standardized on the Unix tool chain, or at least, a portable subset of it, with GNU make. It may or may not be the best tool chain available, you may or may not like it, but it has one overwhelming advantage here: you can install it on just about any platform where it isn't already available. In fact, for Unix based machines (including Mac?), with the exception of GNU make, it is already there; if GNU make isn't already present on your machine, you can get it at the GNU Make site; it is easy to build and to install.

For Windows, you will need some sort of Unix tool kit and the GNU Make. By far the simplest way to get these is from Cygwin; their tool kit installs easily and automatically over the network, and already includes GNU Make, so you don't have to install that separately. In the past, I've found the MKS Toolkit significantly better than Cygwin (although I've not used it recently), although it is expensive, and collegues have reported to me that UWin is also quite good. However, CygWin is largely sufficient for this, as is completely free, so if you are uninterested in the Unix tool chain other than for the building of the library, that's probably the way to go.

What we build

In theory, ultimately, we will build eight versions of each library, one for each combination of the following three binary attributes:

Option Value  
debugging debug version Full debugging information, no (or minimal) optimization, NDEBUG not defined.
optimized version No debugging information, maximum optimization, NDEBUG defined.
threading single thread No support for threading, functions are not multi-thread safe.
mutli-thread Full support for multi-threaded applications, functions and objects fulfill our multi-thread guarantees.
linking static The generated library is designed to be linked statically (".a" under Unix, ".lib" under Windows).
dynamic The generated library is designed to be linked dynamically (".so" under Unix, ".dll" under Windows).

In practice, neither the multi-thread versions or dynamic linking are there yet. To tell the truth, I'm not sure if we'll ever be there; I rather suspect that there is never an occasion to make a dynamic object out of one of the libraries here (although there might be occasions to put them all in one dynamic library). And with regards to threading, most of the objects in the libraries automatically give he desired threading guarantee (that described in Thread-safety for SGI STL), since they typically don't use any static variables. (All singletons in the libraries have been designed to avoid threading problems as long as static initialization occurs before entering main() and they are not accessed by multiple threads before entering main.)

Each version of the library has a different name. In addition, the threading and the debugging options require different object files (generated by means of different compiler options, see below); these are kept in separate directories. The naming conventions for distinguishing between dynamicly and staticly linked libraries are defined by the system, as are the global naming conventions for libraries. The library name is thus built up from several parts: prefixnamesuffix, where the prefix and suffix will depend on the system conventions, and whether the library is to be dynamicly or staticly linked. The name part is built up from the basic name and the other options, as follows:

debugging threading name
debug single thread name-d
multi-thread nameMT-d
optimized single thread name
multi-thread nameMT

The name above is used both to build the name of the final library, and as the name of the directory containing the object files used to build it.

For example, if the basic name of the library is Xyz, we would get the following variations for the librarys:

debugging threading linking library name
Unix
debug single thread static libXyz-d.a
dynamic libXyz-d.so
multi-thread static libXyzMT-d.a
dynamic libXyzMT-d.so
optimized single thread static libXyz.a
dynamic libXyz.so
multi-thread static libXyzMT.a
dynamic libXyzMT.so
Windows
debug single thread static Xyz-d.lib
dynamic Xyz-d.dll
multi-thread static XyzMT-d.lib
dynamic XyzMT-d.dll
optimized single thread static Xyz.lib
dynamic Xyz.dll
multi-thread static XyzMT.lib
dynamic XyzMT.dll

The corresponding directories for the object files are Xyz-d, Xyz, XyzMT-d and XyzMT.

Where we specify the dependent commands

As explained in Configration Options for the GABI Livrary, the basic configuration is defined by means of three variables: arch, syst and comp. In the Makefiles directory, there is a subdirectory, conf which in turn contains subdirectories for each configuration, in the form arch/syst/comp; each of these final subdirectories must contain a file system.mk, which specifies the system dependant aspects of the make. (For information on how code dependencies are managed, see the Configration Options for the GABI Livrary, mentioned above.) This file, included by all of the other makefiles, directly or indirectly, must provide the following definitions:

name contents
Basic options
These position the variants described in Configration Options for the GABI Livrary.
systemFamily A generic name for the system, currently, either posix or mswindows.
iostreams Either classic or standard, depending on the version of iostreams available with your compiler.
threading The threading model available on your system, or nothreads if no threading is available, or if it isn't supported by the compiler (the case for g++ 2.95.2, for example).
Filename conventions
For the most part, these will be in fact defined in a system dependent included file, e.g. $(makefileDir)/unix.mk or $(makefileDir)/windows.mk.
pathSep The separator which is used in a list of pathnames, e.g. ":" for Unix, ";" for Windows.
libraryPrefix Anything which normally precedes the name of the library in the filename, e.g. "lib" for Unix.
librarySuffix The text which normally follows a library name in the filename, e.g. ".a" for Unix, ".lib" for Windows.
dynamicObjectPrefix Like libraryPrefix, but for dyanmicly linked objects. (My Unix heritage is showing in the name here.)
dynamicObjectSuffix Like librarySuffix, but for dyanmicly linked objects, e.g.: ".so" for Unix, ".dll" for Windows.
objectPrefix Anything which normally precedes the name of an object file in the filename.
objectSuffix The text which normally follows the component name in a filename, e.g. ".o" for Unix, ".obj" for Windows.
binaryPrefix Anything which normally precedes the name of an executable in the filename.
binaryPrefix Anything which normally follows the name of an executable in the filename, e.g. ".exe" for Windows.
Compiler options
Various options, etc., which will be used to construct the compiler command line.
cppBasicOptions The basic options which should always be given, for all compilations.
cppDebug Any additional options needed to generate the debug versions, e.g. for symbol tables, to inhibit inlining, etc.
cppOptimize Any additional options needed for optimization.
cppMTOptions Any additional options needed for multi-threading. (Logically, we also need a variable for additional options for single threading, but this seems to be the universal default.)
includeFlag The option which should precede the specification of an additional include path, typically either "-I" or "/I". Note that the include path is appended directly, without any intermediate space. If space is needed, it must be provided int the variable definition. Also, it is supposed that the compiler will look for include files in the order given.
defineFlag The option which should precede the definition of a symbol on the command line, typically either "-D" or "/D". Note that the definition is appended directly, without any intermediate space. If space is needed, it must be provided int the variable definition.
Command Sequences
Various sequences of commands used to build different types of objects, see chapter 5.7 in the GNU Make Manual. When these commands are used, a number of variables are set and are available, see below.
compileToObject The sequence of commands used to compile a single source file to a single object file.
compileToDependency The sequence of commands used to generate the dependencies. Typically, this is done by invoking the compiler (with the correct flags!) with a -M option, or something similar; in some cases, it is necessary to use a special script (which can also be placed in this directory).

Long term, I'd like to write a shell script which would do this, at least for our files. It's a lot easier than it would seem at first, since we have no conditional compilations, and we systematically use the "..." form of the includes for our own files.

buildLibrary The sequence of commands used to build a library from a list of object files. The target files are the prerequisites (The automatic variable "$^.)
buildLibraryFrom The sequence of commands used to build a library from all of the object files in a given directory. In this case, the prerequisite is the directory, and not the object files, so the list of files would be something like "$^/$(objectPrefix)*$(objectSuffix)".
buildDynamicObjectFrom The sequence of commands used to build a dynamic object (DLL, SO) from all of the object files in a given directory. In this case, the prerequisite is the directory, and not the object files, so the list of files would be something like "$^/$(objectPrefix)*$(objectSuffix)".