I have a C++ project using autotools as the build system.
My program involves an abstract interface, of which there are several implementations, with each implementation in a separate source file. These implementations depend on libraries which may or may not be installed, depending on user preference and platform.
I would like to configure autotools to skip building the implementations that are lacking the underlying dependencies. I'd like to do so in whatever way will be conventionally expected by users and package maintainers.
As an example, let's say my program outputs audio, and it can do so with OSS, ALSA, or JACK. These are implemented in oss_output.cc
, alsa_output.cc
, and jack_output.cc
, respectively.
Currently my Makefile.am
contains (among a few other things):
myprog_SOURCES = \
src/oss_output.cc \
src/alsa_output.cc \
src/jack_output.cc
myprog_LDFLAGS = [...some other stuff...] -ljack
Unfortunately attempting to build this program on a system without JACK installed fails because the linker can't find libjack and/or the compiler fails because JACK's header files are missing. In the case JACK isn't installed, I'd instead like to drop src/jack_output.cc
from myprog_SOURCES
, and remove -ljack
from myprog_LDFLAGS
. And similar, for ALSA and OSS.
Of these dependencies (OSS, ALSA, and JACK), some of these libraries use pkg-config
. Some do not, but I can still detect their presence by the existence of a header file and a library.
How is this accomplished with autotools, taking care to follow best practice and convention?
How is this accomplished with autotools, taking care to follow best practice and convention?
There are two pieces:
The first piece is Autoconf's bread & butter. Typically, one would check for the presence of each subsytem's library(-ies) via AC_CHECK_LIB()
or AC_SEARCH_LIBS()
to identify whether it is available, and also check for the main header file(s) via AC_CHECK_HEADER()
or AC_CHECK_HEADERS()
.
It would be a convenience to also set up a mechanism for opting out of building support for some of the subsystems that are available. That would take the form of setting up --without
or --disable
arguments (which of those to choose is often ambiguous). This is not obligatory, but I would consider it good form. Note, however, that it does require some care and attention to avoid including all found libraries in the link.
The Cadillac version would furthermore provide support for the user specifying where to find the wanted libraries and headers, in case they are not in the default search path. I often do see this sort of thing in well-maintained projects, but it takes the complexity up a level, and I would not consider it in any way obligatory.
The implementation details could vary quite widely, but typically, the end result would be, for each subsystem,
build_oss
, for example), andAC_SUBST()
) conveying preprocessor / compiler / and or linker flags to be used to build against the the appropriate back-end library. For instance, some or all of OSS_CPPFLAGS
, OSS_CXXFLAGS
, OSS_LIBS
for the OSS piece.Automake and Autoconf work together on this. The key tool is the Automake conditional. Automake provides an Autoconf macro, AM_CONDITIONAL
, for defining these and assigning their values, and it supports an if
/ then
/ else
construct in Makefile.am
files based on them. (Note well that these are entirely separate from GNU make
conditionals, though they have similar syntax.)
The section of the Automake manual I have linked refers to several others that discuss how conditionals should be used in various scenarios, and it's not entirely clear which of those apply to you. My best guess, however, is that all you really need is conditional sources, so that's what I'll focus on.
The Autoconf part would follow on from the bits I already discussed. It might be as simple as
AM_CONDITIONAL([oss], [test "$build_oss" = "1"])
AM_CONDITIONAL([alsa], [test "$build_alsa" = "1"])
AM_CONDITIONAL([jack], [test "$build_jack" = "1"])
The Automake manual gives other examples.
On the Makefile.am
side, that might match up with something like this:
myprog_SOURCES = \
src/main.cc \
src/another.cc \
...
if oss
myprog_SOURCES += src/oss_output.cc
endif
if alsa
myprog_SOURCES += src/alsa_output.cc
endif
if jack
myprog_SOURCES += src/jack_output.cc
endif
You would need to take care of applying the per-subsystem flags and libraries, too. You can take the same approach (and even use the same if
blocks) as for the sources, and that would be just fine, or you could let configure
take control through how (whether) it defines any flags for each subsystem in the first place.
You will have noticed that I glossed over many of the details. This is a fairly high-level answer to what I have to take as a high-level question. If I take the question any other way then it is too broad for this venue.