Conditional Compilation in Autoconf and Automake

While working on my audio based random password generator (you view the source in github), I wanted to do some conditional compilation: Compiling certain parts of the program only in case some option is passed to the configure script. As it usually happens with GNU’s autotools, it kind of hell to do it. Documentation is spread across dozens of sources, each provides only a specific part of what to do. I’m writing it here in the blog, in hope I’ll never have to search 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 --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 the --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 here a simple message, but if you want it to be formatted nicely when some one 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 is 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, is will still execute the action-if-given (which is bad). So you should ignore is 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 could 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 last one is 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 to long to do it, and I had to deal with documentation which is spread in too many places and obscure error messages. If someone can recommend a more developer-friendly build system, which is flexible and robust, I would gladly try it out.

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.