Several solutions to the problem of portability were used over the years. The simplest was to include header files with each package that abstract away the differences between platforms using a plethora of multi-nested #define and #ifdef statements. It made code very hard to read. Other alternatives asked the user to run an interactive configuration script that prompted the user to answer questions such as ``Is this machine big-endian?'' and ``Are you POSIX Compliant?'' These configuration scripts tended to become very long, verbose, and tedious for users to go through. Worse of all, they did not guarantee that the user would really answer the questions correctly. To answer some of them correctly one had to be a Unix expert to begin with. More sophisticated solutions used the X11 Imake utility which abstracted the differences using preprocessing (via cpp) of several pre-written template files. Imake's usefulness never extended beyond that of the X11 domain of applications [Haemer94].
All of these solutions suffered from one major problem -- they were static. That is, the portability offered was only as good as what the programmers of the package included. They could not be easily changed to accommodate new operating systems or even new minor revisions of existing operating systems. In addition, they could never account for partially installed or misinstalled systems. For example, operating systems such as Solaris and IRIX require the installation of special software packages in order to use Motif or NFS, respectively. System administrators could choose to install these packages or not. It is even possible (and unfortunately quite common), for systems to claim to have a particular feature but not to implement it correctly. Finally, Unix systems are as good as the administrators who maintain them. Often, complex installations tend to have poor configurations. A good solution to portability must be able to handle all of these cases.
The Free Software Foundation (FSF) solved these problems using a dynamic, automatic configuration system called Autoconf [MacKenzie95], which I plan to use with FiST. Autoconf is a large collection of highly portable M4 macros and Bourne shell scripts that perform on-the-fly feature tests to determine differences among systems.
For example, in order to find out if one has the proper Motif libraries to link X11 applications with, Autoconf provides a simple test that can be used as follows: AC_CHECK_LIB(Xm). The test in turn is implement as a small shell script that writes a test C program on the fly, and tries to compile and link it. If it succeeds, it knows for certain that the Motif library libXm is available. If the test is successful, then Autoconf modifies the auto-generated Makefile and adds to it the line LIBS += -lXm. The Makefile generated is guaranteed to link with the Motif library if and only if it exists.
Another example is the Autoconf macro AC_FUNC_ALLOCA. It runs tests that check for the existence of the alloca(3) library call. This particular library call is known to have many broken implementations on various systems. Autoconf therefore performs additional tests to validate the correct behavior of the call! If successful, Autoconf will add the line #define HAVE_ALLOCA_H to the autogenerated header file it creates, "config.h". An application can include this locally created header file and use the definitions within to ensure that the proper headers and their associated definitions get included, and nothing more.
Autoconf's standard M4 tests include easy facilities to extend them: you can supply actions to be performed if the test failed or succeeded, you can include existing tests in other tests you write, you can cache previous results and reuse them, and so on. The basic set of Autoconf tests have been used by large and very complex packages such as the GNU HURD, gcc, emacs, gdb, LATEX, Tcl/Tk, and many more. Autoconf can make it easy to port applications to over one hundred Unix variants known, and by its nature automatically handles new ones as they spring into existence.