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.
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 make
s 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.
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
.
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 |
---|---|
|
|
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).
|
$(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.
|
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.
|
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) ".
|