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
#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.