Conditional Compilation in Autoconf and Automake

While working on my audio-based random password generator (you can view the source in GitHub), I wanted to do some conditional compilation: compiling certain parts of the program only if some option is passed to the configure script. As it usually happens with GNU’s autotools, it’s kind of hell to do it. Documentation is spread across dozens of sources, and each provides only a specific part of what to do. I’m writing it here on the blog, in the hope I’ll never have to search for how to do so again.

The solution has to address the following things:

  1. Making autoconf present the compilation options to the user.
  2. Making automake aware of the option, and conditionally compile code accordingly.
  3. Making the program itself aware via some #define symbol.

Adding an option to autoconf is done using the AC_ARG_WITH macro. This adds a --with-something-style flag to configure (adding a --enable-style flag is done with AC_ARG_ENABLE). For example:

AC_ARG_WITH([portaudio],
    [AS_HELP_STRING([--with-portaudio], [use PortAudio as the audio backend
    instead of ALSA] @@)],
    [],
    [with_portaudio=no])

The first argument is the string that will follow --with- (in our case, portaudio); it will also create a variable named with_portaudio, which will get whatever the user passes. The second argument is the string of the help message. You could possibly write a simple message here, but if you want it to be formatted nicely when someone does ./configure --help, you would want to use the AS_HELP_STRING macro. It expects the name of the flag (--with-portaudio in our case) and the actual help string. The ugly part with @<:@ and @:>@ is just autoconf’s ugly way to escape the [ and ] characters. This result looks something like this:

  --with-portaudio        use PortAudio as the audio backend instead of ALSA
                          [default=no]

The next two arguments are the action to do if the flag is passed, and the last one is what to do if the flag isn’t passed. This is a bit tricky. If someone gives --without-portaudio, or --with-portaudio=no, it will still execute the action-if-given (which is bad). So you should ignore it and use an if-statement later to check the value and act upon it. The only useful thing here is the action-if-not-given, which you can use to implement a default value.

Now that we have the code that adds the relevant command-line option to ./configure, we use the AS_IF macro to check the variable for the command-line option.

AS_IF([test "x$with_portaudio" != xno],
    [
    PKG_CHECK_MODULES([PORTAUDIO], [portaudio-2.0])
    AM_CONDITIONAL(WITH_PORTAUDIO, true)
    AC_DEFINE(WITH_PORTAUDIO,[],[Use PortAudio])
    ], [
    AM_CONDITIONAL(WITH_PORTAUDIO, false)
    ])

The second argument is the action-if-true, and the last is the action-if-false. The AM_CONDITIONAL passes the WITH_PORTAUDIO variable to automake, so we can use it later there for the actual conditional compilation. The AC_DEFINE macro enables us to define a symbol in config.h. The last two arguments are the value for the defined symbol (leave empty if you just want it to be defined) and the help string that will appear in config.h (both arguments are required if you want to use autoheader). The PKG_CHECK_MODULES is unrelated, but it uses pkg-config to get the required CFLAGS and libraries for the given library.

This ends the code in autoconf itself. In automake you’ll have to add something like this:

if WITH_PORTAUDIO
spass_SOURCES += audio_random_portaudio.h 
    audio_random_portaudio.cpp
CFLAGS += $(PORTAUDIO_CFLAGS)
LDADD += $(PORTAUDIO_LIBS)
endif

Which is pretty self-explanatory, and sums everything up. It took me way too long to do it, and I had to deal with documentation that is spread across too many places and obscure error messages. If someone can recommend a more developer-friendly build system that is flexible and robust, I would gladly try it out.

Leave a Reply

Your email address will not be published. Required fields are marked *