18.3. Autoconf Example

This example demonstrates how to use autoconf and also shows one use of the after-build statement within Abuild.interface. In this example, we create a stub library that replaces functionality from an external library if that library is not available. Our example is somewhat contrived, but it demonstrates the core functionality and patterns required to do this. Our example resides in doc/example/general/user/derived/world-peace.

Notice that the Abuild.conf in the world-peace directory itself defines a pass-through build item (see Section 4.5, “Special Types of Build Items”) that depends on the world-peace.stub build item:

general/user/derived/world-peace/Abuild.conf

name: world-peace
child-dirs: autoconf stub
deps: world-peace.stub

The world-peace.stub build item, in turn, depends on the world-peace.autoconf build item:

general/user/derived/world-peace/stub/Abuild.conf

name: world-peace.stub
platform-types: native
deps: world-peace.autoconf

The world-peace.autoconf build item's Abuild.interface file adds its output directory to the INCLUDES variable since this where the autoconf-generated header file will go. Then it declares autoconf.interface in its output directory as an after-build file using the after-build statement:

general/user/derived/world-peace/autoconf/Abuild.interface

# $(ABUILD_OUTPUT_DIR) contains the autoconf-generated header.
INCLUDES = $(ABUILD_OUTPUT_DIR)

after-build $(ABUILD_OUTPUT_DIR)/autoconf.interface

This means that the autoconf.interface file won't be included when this build item is built but will be included when other build items that depend on this one are built. This is important since the file won't actually exist yet when this build item is being built from a clean state.

Next, look at the autoconf/Abuild.mk file:

general/user/derived/world-peace/autoconf/Abuild.mk

AUTOFILES := autoconf.interface
AUTOCONFIGH := world-peace-config.h
RULES := autoconf

Here, we set the variables that the autoconf rules require. The AUTOFILES variable is set to the value autoconf.interface, which is the same as the file name used as the after-build file in the Abuild.interface file. Additionally, we set the variable AUTOCONFIGH to the name of the header file that we will be generating.

Here is the autoconf/configure.ac file:

general/user/derived/world-peace/autoconf/configure.ac

AC_PREREQ(2.59)
AC_INIT(world-peace,1.0)
AC_CONFIG_HEADERS([world-peace-config.h])
AC_CONFIG_FILES([autoconf.interface])

AC_PROG_CXX
AC_LANG(C++)
AC_SUBST(HAVE_PRINTF)
AC_CHECK_FUNCS(printf, [HAVE_PRINTF=1], [HAVE_PRINTF=0])
AC_SUBST(HAVE_CREATE_WORLD_PEACE)
AC_CHECK_FUNCS(create_world_peace, [HAVE_CREATE_WORLD_PEACE=1],
                                   [HAVE_CREATE_WORLD_PEACE=0])

AC_OUTPUT

This contains normal autoconf macros. There are two important things to notice here. The first is the AC_CONFIG_FILES macro, which tells autoconf to generate the autoconf.interface file from autoconf.interface.in. The second is the AC_CONFIG_HEADERS call, which takes name of the file set as the value of the AUTOCONFIGH variable in Abuild.mk. The header file template is generated automatically using autoheader. The need to duplicate this information is unfortunate, and this may be improved in a future version of abuild. Note that the autoconf macros don't have any knowledge of the abuild output directory. This works because we actually run autoconf inside the output directory with copies of the input files.

Use of AC_CONFIG_HEADERS and AUTOCONFIGH are optional. If you omit one, you should omit both. If you decide to use an autoconf-generated header, you should be aware of the possibility that you may have duplicated preprocessor symbols defined by different autoconf-based build items. There are several ways to avoid this. One way would be to create your own header file template and generate it using AC_CONFIG_FILES rather than AC_CONFIG_HEADER. Another way would be to structure your build so that you combine functionality that requires use of preprocessor symbols into a single build item, using separate build items only for cases that can be handled through interface variables. It may also be possible to set XCPPFLAGS in an after-build file based on interface variables initialized by a file generated with autoconf. The most important thing is that you pick a way to do it and use it consistently.

Next, we examine the autoconf.interface.in file:

general/user/derived/world-peace/autoconf/autoconf.interface.in

declare HAVE_PRINTF boolean
HAVE_PRINTF=@HAVE_PRINTF@

declare HAVE_CREATE_WORLD_PEACE boolean
HAVE_CREATE_WORLD_PEACE=@HAVE_CREATE_WORLD_PEACE@

if ($(HAVE_CREATE_WORLD_PEACE))
  LIBS = world_peace
endif

This is just like any other file generated by autoconf: it contains substitution tokens surrounded by @ signs. Since it is an abuild interface file, it has abuild interface syntax.

In our example, our configure.ac file checks to see whether we have two functions: printf and create_world_peace. Unfortunately, only the first of these two functions is defined on most systems. Our autoconf.interface.in file will set abuild boolean variables to the values determined by autoconf. Then, if the create_world_peace function is available, we will add its library (which, in a real case, you would know or test for explicitly in configure.ac) to the library path. If the library were not installed in the default library and include paths, it probably would also have add something to the LIBDIRS and INCLUDES variables.

Now we turn our attention to the stub directory. This directory contains our stub implementation of create_world_peace. It is a poor substitute for the real thing, but it will at least allow our software to compile. The implementation protects the definition of the function with the HAVE_CREATE_WORLD_PEACE preprocessor symbol as generated by autoconf. It also makes use of printf and checks to make sure it's there, just to demonstrate how you might do such a thing:

general/user/derived/world-peace/stub/stub.cc

#include <world_peace.hh>
#include <stdio.h>

// Provide a stub version of create_world_peace if we don't have one.

#ifndef HAVE_CREATE_WORLD_PEACE
void create_world_peace()
{
    // Silly example: make this conditional upon whether we have
    // printf.  This is just to illustrate a case that's true as well
    // as a case that's false.
#ifdef HAVE_PRINTF
    printf("I don't know how to create world peace.\n");
    printf("How about visualizing whirled peas?\n");
#else
# error "Can't do this without printf."
#endif
}
#endif

The stub implementation provides a header file called world_peace.hh, which is presumably the same as the name of the header provided by the real implementation and which would have been made available by the world-peace.autoconf build item if the library were found:

general/user/derived/world-peace/stub/world_peace.hh

#ifndef __WORLD_PEACE_HH
#define __WORLD_PEACE_HH

#include <world-peace-config.h>

#ifndef HAVE_CREATE_WORLD_PEACE
extern void create_world_peace();
#endif

#endif // __WORLD_PEACE_HH

The Abuild.interface file in the stub directory actually adds world-peace to the list of libraries only if the HAVE_CREATE_WORLD_PEACE variable, as provided by world-peace.autoconf's autoconf.interface file, is false. That way, if we had a real create_world_peace function (whose library would have presumably also been made available to us in world-piece.autoconf's autoconf.interface file), we wouldn't provide information about our stub library:

general/user/derived/world-peace/stub/Abuild.interface

INCLUDES = .
if (not($(HAVE_CREATE_WORLD_PEACE)))
   LIBS = world-peace-stub
   LIBDIRS = $(ABUILD_OUTPUT_DIR)
endif

Note that users of the world-peace build item actually don't even have to know whether they are using the stub library or the real library—those details are all completely hidden inside of its private build items. Declaring a dependency on world-peace will make sure that you have the appropriate interfaces available. You can see an example of this by looking at main.cpp in user/derived/main/src:

general/user/derived/main/src/main.cpp

#include <ProjectLib.hpp>
#include <CommonLib2.hpp>
#include <iostream>
#include <world_peace.hh>
#include "auto.h"

int main(int argc, char* argv[])
{
    std::cout << "This is derived-main." << std::endl;
    ProjectLib l;
    l.hello();
    CommonLib2 cl2(6);
    cl2.talkAbout();
    cl2.count();
    std::cout << "Number is " << getNumber() << "." << std::endl;
    // We don't have to know or care whether this is the stub
    // implementation or the real implementation.
    create_world_peace();
    return 0;
}