6.4 What to check for
Deciding what to check for is really the central part of writing
`configure.in'. Once you've read the Autoconf reference manual,
the "how"s of writing a particular test should be fairly clear. The
"when"s might remain a mystery -- and it's just as easy to check for too
many things as it is to check for too few.
One notable area of divergence between various Unix-like systems is that
the same programs don't exist on all systems, and, even when they do,
they don't always work in the same way. For these problems we
recommend, when possible, following the advice of the GNU Coding
Standards: use the most common options from a relatively limited set of
programs. Failing that, try to stick to programs and options specified
by POSIX, perhaps augmenting this approach by doing checks for known
problems on platforms you care about.
Checking for tools and their differences is usually a fairly small part
of a `configure' script; more common are checks for functions,
libraries, and the like.
Except for a few core libraries like `libc' and, usually,
`libm' and libraries like `libX11' which typically aren't
considered system libraries, there isn't much agreement about library
names or contents between Unix systems. Still, libraries are easy to
handle, because decisions about libraries almost always only affect the
various `Makefile's. That means that checking for another library
typically doesn't require major (or even, sometimes, any) changes to the
source code. Also, because adding a new library test has a small impact
on the development cycle -- effectively just re-running `configure'
and then a relink -- you can effectively adopt a lax approach to
libraries. For instance, you can just make things work on the few
systems you immediately care about and then handle library changes on an
as-needed basis.
Suppose you do end up with a link problem. How do you handle it? The
first thing to do is use nm to look through the system libraries
to see if the missing function exists. If it does, and it is in a
library you can use then the solution is easy -- just add another
AC_CHECK_LIB . Note that just finding the function in a library
is not enough, because on some systems, some "standard" libraries are
undesirable; `libucb' is the most common example of a library which
you should avoid.
If you can't find the function in a system library then you have a
somewhat more difficult problem: a non-portable function. There are
basically three approaches to a missing function. Below we talk about
functions, but really these same approaches apply, more or less, to
typedefs, structures, and global variables.
The first approach is to write a replacement function and either
conditionally compile it, or put it into an appropriately-named file and
use AC_REPLACE_FUNCS . For instance, Tcl uses
AC_REPLACE_FUNCS(strstr) to handle systems that have no
strstr function.
The second approach is used when there is a similar function with a
different name. The idea here is to check for all the alternatives and
then modify your source to use whichever one might exist. The idiom
here is to use break in the second argument to
AC_CHECK_FUNCS ; this is used both to skip unnecessary tests and
to indicate to the reader that these checks are related. For instance,
here is how libgcj checks for inet_aton or
inet_addr ; it only uses the first one found:
|
AC_CHECK_FUNCS(inet_aton inet_addr, break)
|
Code to use the results of these checks looks something like:
|
#if HAVE_INET_ATON
... use inet_aton here
#else
#if HAVE_INET_ADDR
... use inet_addr here
#else
#error Function missing!
#endif
#endif
|
Note how we've made it a compile-time error if the function does not
exist. In general it is best to make errors occur as early as possible
in the build process.
The third approach to non-portable functions is to write code such that
these functions are only optionally used. For instance, if you are
writing an editor you might decide to use mmap to map a file into
the editor's memory. However, since mmap is not portable, you
would also write a function to use the more portable read .
Handling known non-portable functions is only part of the problem,
however. The pragmatic approach works fairly well, but it is somewhat
inefficient if you are primarily developing on a more modern system,
like GNU/Linux, which has few functions missing. In this case the
problem is that you might not notice non-portable constructs in your
code until it has largely been finished.
Unfortunately, there's no high road to solving this problem. In the
end, you need to have a working knowledge of the range of existing Unix
systems. Knowledge of standards such as POSIX and XPG can be useful
here, as a first cut -- if it isn't in POSIX, you should at least
consider checking for it. However, standards are not a panacea -- not
all systems are POSIX compliant, and sometimes there are bugs in systems
functions which you must work around.
One final class of problems you might encounter is that it is also easy
to check for too much. This is bad because it adds unnecessary
maintenance burden to your program. For instance, sometimes you'll see
code that checks for <sys/types.h> . However, there's no point in
doing that -- using this header is mostly portable. Again, this can
only be addressed by having a practical knowledge, which is only really
possible by examining your target systems.
|