When using autoconf for portability,
it's necessary to consider that some functions, even standard
system ones, are often in different libraries on different
operating systems. For instance, the dlopen()
function is in libdl
on GNU/Linux, and in the
standard C library on FreeBSD and other BSD-derived systems.
This is one of the most common things to test, but the
commonly-documented solution to this, which involves the use of
the AC_CHECK_LIB
macro, leads to either
wrong solutions, or over-engineered ones. This macro is used to
check for the presence of a known (usually, third-party) library
but it does not work that well when you have a list of
alternatives to check.
The correct macro to use for this task is
AC_SEARCH_LIBS
, which is designed keeping
into consideration at least two important points:
There might be no need for further libraries to be added for the
function to be available. This may either be because the
function is in the C library or because the library that it's
found in is already present in the LIBS
variable's list. This does not mean the function is present in a
library called libc
;
Only one library carrying the function is needed, so testing should stop at the first hit. Testing further libraries might very well lead to false positives and will certainly slow down the configuration step.
Other than these configurations, the interface of the macro is nothing special for autoconf:
AC_SEARCH_LIBS(function, libraries-list, action-if-found, action-if-not-found, extra-libraries)
function
The name of the symbol to look for in the libraries.
libraries-list
A whitespace-separated list of libraries, in library-name
format, that
have to be searched for the function listed before. This list
does not require the C library to be specified, as the first
test will be done with just the libraries present in the
LIBS
variable.
action-if-found
, action-if-not-found
As usual, the macro supports expanding code for success and
failure. In this instance, each will be called at most once,
and the default action-if-found
code,
adding the library to the LIBS
variables,
is always executed, even if a parameter
is passed.
extra-libraries
Technically, on some if not most operating systems, it is possible for libraries to have undefined symbols that require other libraries to be linked in to satisfy. This is the case for most static libraries, but it can also happen for some shared libraries.
To make it possible to search in the libraries, the macro
provides this parameter. There is an implicit value for
the parameter: the LIBS
variable, which
is always passed at link-time after the value of this
parameter. This list is not added to
the variable even on success.
It is important to note that if you were to find the symbol
in one of these libraries, you'd be hitting the same case as
if the symbol is already available in the libraries listed
in LIBS
.
Using this macro it's possible to ignore in the
Makefile
the different libraries that are
used to provide the functions on different operating systems. The
LIBS
variable is set up to list all the
libraries, hiding the need for anything besides the standard
library.
Example 1.3. Looking for two common system libraries with
AC_SEARCH_LIBS
dnl The dlopen() function is in the C library for *BSD and in dnl libdl on GLIBC-based systems AC_SEARCH_LIBS([dlopen], [dl dld], [], [ AC_MSG_ERROR([unable to find the dlopen() function]) ]) dnl Haiku does not use libm for the math functions, they are part dnl of the C library AC_SEARCH_LIBS([cos], [m], [], [ AC_MSG_ERROR([unable to find the cos() function]) ])
The header files describe the public interface of a C (or C++) library. To use a library you need its public headers, so to make sure a library is available, you have to ensure that its headers are available.
Especially in the Unix world, where a shared library already defines its public binary interface or ABI, the presence of headers can tell you whether the development packages needed to build against that library are present.
To make sure that headers can be found properly,
autoconf provides two macros:
AC_CHECK_HEADER
to find a single header or
AC_CHECK_HEADERS
to look for more than one
header at a time (either replacement headers or independent
headers).
It's not unlikely that we have to look for one out of a series of possible replacement headers in our software. That's the case when we've got to support libraries that might be installed at top level, in subdirectories, or when older non-standard headers are replaced with new equivalent ones.
A very common case is looking for either one of
stdint.h
, inttypes.h
or sys/types.h
headers to declare the
proper standard integer types (such as uint32_t)
for backward compatibility with older non-standard C libraries.
In this situation, the order used above is also a priority order; since the first is the preferred header and the third is the least favourite one. It doesn't make sense to test for any other headers once the first is found. There also has to be an error message if none at all of those is available.
The macro AC_CHECK_HEADERS
provides all the
needed parameters to implement just that. The first parameter of
the macro is a sh-compatible list, rather than an M4 list, and
it's argument to a for
call in the
configure
file and there is an action
executed once the header is found (actually, there is one also
when the header is not found, but we're not
going to use that one.
AC_CHECK_HEADERS([stdint.h inttypes.h sys/types.h], [mypj_found_int_headers=yes; break;]) AS_IF([test "x$mypj_found_int_headers" != "xyes"], [AC_MSG_ERROR([Unable to find the standard integers headers])])
In this case, the configure
script will
check for the headers in sequence, stopping at the first it
finds (thanks to the break
instruction). This reduces the amount of work needed for the
best case scenario (a modern operating system providing the
standard header stdint.h
).
Since exactly one of the actions (found or not found) is
executed per each header tested, we cannot use the 'not found'
action to error out of the script, otherwise any system lacking
stdint.h
(the first header tested) will be
unable to complete the step. To solve this, we just set a
convenience variable once a header is found and test that it has
been set.
Since August 2001, autoconf has been warning users about headers that are present in the system but couldn't be compiled, when testing for them with either of the two macros explained previously.
Before that time, the macros only checked if the header was present by asking the preprocessor for it. While this did find the whereabouts of headers, it included no information regarding their usability. Without checking if the headers compiled, it was impossible to say if the header was valid for the language variety chosen (like C99, or C++), or if it required unknown dependencies.
To solve the problem, it was decided to use the actual compiler for the currently selected language to look for the headers, but for compatibility with the previous version at the moment of writing, both tests are performed for each header checked, and the “header present but cannot be compiled” warning is reported if the preprocessor accepts a header while the compiler refuses it.
There are multiple causes for this problem, and thus to solve this warning, you have to identify the actual cause of it. For reference, you can find some examples here.
Example 1.4. header present but cannot be compiled: wrong language dialect
This example is based on an actual mistake in the KDE 3.5.10 build system for the kdepim package.
One situation where the header files are present but cannot be compiled is when they are not designed to work with a particular language variety. For instance, a header might not be compatible with the C99 dialect, or with C++, or on the other hand it might require C99 or C++.
If that particular language is selected, though, and the header is tested for, the behaviour of rejecting the header is indeed what the developers are expecting. On the other hand, it's possible that the test is being done with a different language than the one where the header is going to be used.
For instance, take the following snippet, that tries to look
for the bluetooth/bluetooth.h
header from
bluez-libs, using a strict C
compiler:
dnl SPDX-FileCopyrightText: 2009- Diego Elio Pettenò dnl dnl SPDX-License-Identifier: MIT AC_INIT CFLAGS="-std=iso9899:1990" AC_CHECK_HEADERS([bluetooth/bluetooth.h]) AC_OUTPUT
This will issue the warning discussed above when running
configure
:
checking bluetooth/bluetooth.h usability... no checking bluetooth/bluetooth.h presence... yes configure: WARNING: bluetooth/bluetooth.h: present but cannot be compiled configure: WARNING: bluetooth/bluetooth.h: check for missing prerequisite headers? configure: WARNING: bluetooth/bluetooth.h: see the Autoconf documentation configure: WARNING: bluetooth/bluetooth.h: section "Present But Cannot Be Compiled" configure: WARNING: bluetooth/bluetooth.h: proceeding with the preprocessor's result configure: WARNING: bluetooth/bluetooth.h: in the future, the compiler will take precedence checking for bluetooth/bluetooth.h... yes
The reason for the above warnings can be found by looking at
the config.log
file that the
configure
script writes:
configure:3338: checking bluetooth/bluetooth.h usability configure:3355: gcc -c -std=iso9899:1990 conftest.c >&5 In file included from conftest.c:51: /usr/include/bluetooth/bluetooth.h:117: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'int' /usr/include/bluetooth/bluetooth.h:121: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'void' configure:3362: $? = 1
Looking at those lines in the header file shows that it is using the inline keyword, which is not supported by the C90 language (without extensions). If the header is checked for, though, it means it's going to be used, and even if it does not work with the selected C90 dialect, it might work with the C++ language used by the actual program.
The configure.ac
file can then be fixed
by changing it to the following code:
dnl SPDX-FileCopyrightText: 2009- Diego Elio Pettenò dnl dnl SPDX-License-Identifier: MIT AC_INIT CFLAGS="-std=iso9899:1990" AC_LANG_PUSH([C++]) AC_CHECK_HEADERS([bluetooth/bluetooth.h]) AC_LANG_POP AC_OUTPUT