5. Custom Autoconf Tests

While autoconf provides a number of pre-written tests to identify the presence of headers, symbols and libraries, they obviously don't cover the whole range of possible situations that software developers might be facing to recognise the environment that their software is being built on.

For this reason, autoconf also provides interfaces to write custom testing routines, which can be divided in two main groups: “build tests” and “run tests”, depending on how the test is performed. The former group can then be further split into “pre-processing tests”, “compile tests” and “link tests”, to denote the different finishing step in the tests' process.

5.1. “Build Tests”

Most of the tests implemented within configure scripts are designed to identify whether the compiler supports a particular syntax, or a given symbol, constant or header file is available at build time. These tests, the generic version of which is available with predefined macros such as AC_CHECK_HEADERS and AC_CHECK_LIB are the so-called “build tests”.

Since the predefined tests don't cover the whole range of tests that could be needed (e.g., they don't provide a way to check for a reported minimum version of an API library), autoconf exports macros that allows to check whether some given source code preprocesses, compiles or links properly: AC_PREPROC_IFELSE (formerly AC_TRY_CPP), AC_COMPILE_IFELSE and AC_LINK_IFELSE.

The three macros share the same interface, which itself follows the usual actions-based behaviour of other predefined macros:

AC_PREPROC_IFELSE(input, [action-if-true], [action-if-false])
AC_COMPILE_IFELSE(input, [action-if-true], [action-if-false])
AC_LINK_IFELSE(input, [action-if-true], [action-if-false])

The three macros have a progressively more strict requirement for the sources they are provided, given that each brings its input a step further in the usual build chain. This means that you don't require proper C code to be passed to the preprocessor, but you have to do so when compiling, whereas then you don't need a main() entrypoint, the link test will require one (as it can only link an executable, not a shared object).

Compared to the “run tests” discussed later on, the “build tests” are safe for cross-compilation, as long as the proper compiler and linker are present in the system.

Even though autoconf makes it available, the use of AC_PREPROC_IFELSE is actively discouraged. When invoked directly, the preprocessor lacks some of the definitions set up by the compiler frontend, and some features might behave inconsistently between the two. For this reason, it is suggested that tests for macro definitions and header presence to be performed using AC_COMPILE_IFELSE instead.

5.2. “Run Tests”

Sometimes, the mere presence of a function, or of a constant's definition, is not enough for a test to be considered successful. For instance an interface's optional function might be present as a stub returning a “not implemented” error condition, or a constant might be declared but ignored by the functions that are supposed to make use of it. For these reasons, it is sometimes a necessity to execute the code after building it, and wait for its results.

Important

Executing test code in a build scenario can be tricky: the system used to build a package might very well not be the same where the code would be executed (this is the case for most Linux – and not – distributions' build farms) which could lead to erroneous results or, in case the architectures of the two systems are not compatible, an unusable test, which will interrupt the course of the ./configure execution.

For those reasons it is important to make sure that the results of all the tests executed on the build host can be overridden. To do so, the best solution is to cache the results, so that a simple environment variable can be used to skip over the test execution, providing the correct, precalculated value.

The basic macro to support “run tests” is AC_RUN_IFELSE (formerly AC_TRY_RUN and AC_TEST_PROGRAM), which extends the AC_LINK_IFELSE flow by executing the just-linked program, and follows the usual actions paradigm, adding one third case for cross-compilation (when the test code cannot run because of architecture incompatibility).

AC_RUN_IFELSE(input, [action-if-true], [action-if-false], [action-if-cross-compiling])
input

The test program's source code; just like the “build test” macros it has to be provided through AC_LANG_SOURCE or variation thereof. Since this macro can be considered the next step after AC_LINK_IFELSE, the same requirements apply. Additionally, the main() function should return a zero status for success, and any non-zero status for failure, as any other shell program.

action-if-true

This block of M4sh code is executed if the test executable was cleanly executed and returned a zero status. If more verbose results are required out of the test case, the code can execute ./conftest$EXEEXT (literally).

action-if-false

This block of M4sh code is executed if either the test couldn't be compiled, linked, executed or if the executed test returns a non-zero status. The status of the latest command executed is available in $?, but there is no way to discern whether that is the compiler's, linker's or the test's status.

action-if-cross-compiling

Finally, this block of M4sh code is executed if the ./configure script is executed for cross-compilation. The default content of this section causes the script to abort (through AC_MSG_FAILURE), which is the main reason why “run tests” are frowned upon on cross-compiling prone environments.

5.3. Tests' Input Sources

All the test macros discussed in the previous sections require properly formatted and saved input sources; to help proper generation of these, autoconf provides the developer with a set of macros, starting with AC_LANG_SOURCE. Starting from autoconf version 2.68, it is no longer possible to provide sources that are not generated by this family of macros without it reporting a warning. It is possible that future versions will disallow such behaviour altogether.

The AC_LANG_SOURCE macro is the basis for providing sources to the input parameter of the above-described If-Else macros. it only has one parameter, which is the raw sources for the test program in the currently selected language. The generated source file will not only contain the provided sources, but will also include the list of macro definition emitted by calls to AC_DEFINE.

Important

The sources provided in this macro are expanded twice. This means that you have to quote them twice as well when providing them. So for instance, a test with a simple main() function would be declared this way:

AC_LINK_IFELSE([
  AC_LANG_SOURCE(
    [[int main() { return 0; }]]
  )
]) 

For a matter of pure convenience, autoconf provides a AC_LANG_PROGRAM macro that takes two distinct arguments: a prologue parameter that is used to emit code outside of the main function's body, and a body parameter that is emitted within the main function's body (main() for C and C++, but might differ for other languages).

This macro is especially helpful if your test is designed to only check for compiler or linker flags, as the entry point will be generated by autoconf and will return a non-error condition by default. Otherwise, it is simply a wrapper around the already defined AC_LANG_SOURCE macro.

Note

As of autoconf 2.68, there are a few more wrapper macros around AC_LANG_SOURCE, which are documented for completeness's sake in the official documentation. They will be not named or documented further from here, as their design makes them incompatible with the Erlang language, and their limitation make them unsuitable for the use in modern build systems.