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; }