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:
- Making autoconf present the compilation options to the user.
- Making automake aware of the option, and conditionally compile code accordingly.
- Making the program itself aware via some
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
@:>@ is just autoconf’s ugly way to escape the
] 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
--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.