autotools如何配置为仅在安装依赖项时构建可选组件?

How is autotools configured to build optional components only when dependencies are installed?

我有一个使用 autotools 作为构建系统的 C++ 项目。

我的程序涉及一个抽象接口,其中有几个实现,每个实现在一个单独的源文件中。这些实现取决于可能安装或未安装的库,具体取决于用户偏好和平台。

我想配置 autotools 以跳过构建缺少底层依赖项的实现。我想以用户和包维护者通常期望的任何方式这样做。

举个例子,假设我的程序输出音频,它可以用 OSS, ALSA, or JACK 来输出。这些分别在 oss_output.ccalsa_output.ccjack_output.cc 中实现。

目前我的 Makefile.am 包含(除其他外):

myprog_SOURCES = \
    src/oss_output.cc \
    src/alsa_output.cc \
    src/jack_output.cc

myprog_LDFLAGS = [...some other stuff...] -ljack

不幸的是,尝试在未安装 JACK 的系统上构建此程序失败,因为链接器找不到 libjack and/or编译器失败,因为缺少 JACK 的头文件。在没有安装 JACK 的情况下,我想从 myprog_SOURCES 中删除 src/jack_output.cc,并从 myprog_LDFLAGS 中删除 -ljack。和类似的,对于 ALSA 和 OSS。

在这些依赖项(OSS、ALSA 和 JACK)中,其中一些库使用 pkg-config。有些没有,但我仍然可以通过头文件和库的存在来检测它们的存在。

这是如何使用 autotools 完成的,同时注意遵循最佳实践和惯例?

How is this accomplished with autotools, taking care to follow best practice and convention?

有两块:

  1. 检测需要/支持哪些子系统,以及
  2. 将构建限制为仅选定的子系统。

确定要支持的子系统

第一部分是 Autoconf 的面包和黄油。通常,人们会通过 AC_CHECK_HEADER()AC_CHECK_HEADERS().

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)

同时设置一种机制以选择退出对某些可用子系统的构建支持会很方便。这将采取设置 --without or --disable 参数的形式(选择哪些参数通常是模棱两可的)。这不是强制性的,但我认为这是一种很好的形式。但是请注意,它确实需要一些小心和注意,以避免在 link.

中包含所有找到的库。

Cadillac 版本还将进一步支持用户指定在哪里可以找到所需的库和 headers,以防它们不在默认搜索路径中。我经常在 well-maintained 项目中看到这种事情,但它使复杂性更上一层楼,我不认为它是强制性的。

实施细节可能差异很大,但通常,对于每个子系统,最终结果将是,

  • 一个 shell 变量指示是否在构建中包含子系统(例如 build_oss)和
  • (可选)输出变量(通过 AC_SUBST() 创建)传递预处理器/编译器/和/或 linker 标志,用于构建适当的 back-end 库。例如,OSS 部分 OSS_CPPFLAGSOSS_CXXFLAGSOSS_LIBS 的部分或全部。

将构建限制为选定的子系统

Automake 和 Autoconf 在这方面合作。关键工具是Automake conditional。 Automake 提供了一个 Autoconf 宏 AM_CONDITIONAL,用于定义这些宏并为其赋值,它支持 Makefile.am 文件中的 if / then / else 构造在他们。 (请注意,这些完全独立于 GNU make 条件,尽管它们具有相似的语法。)

我 linked 的 Automake 手册部分提到了其他几个讨论如何在各种情况下使用条件的部分,但并不完全清楚哪些适用于您。然而,我最好的猜测是,您真正需要的只是 conditional sources,所以这就是我要关注的内容。

Autoconf 部分将继续我已经讨论过的内容。可能就这么简单

AM_CONDITIONAL([oss], [test "$build_oss" = "1"])
AM_CONDITIONAL([alsa], [test "$build_alsa" = "1"])
AM_CONDITIONAL([jack], [test "$build_jack" = "1"])

Automake 手册给出了其他示例。

Makefile.am 方面,这可能与这样的事情相匹配:

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

您还需要注意应用 per-subsystem 标志和库。您可以采用与源相同的方法(甚至使用相同的 if 块),这很好,或者您可以让 configure 控制它如何(是否)定义任何首先是每个子系统的标志。

您会注意到我忽略了许多细节。这是对我必须作为 high-level 问题的相当 high-level 的回答。如果我以其他方式回答这个问题,那么这个问题对于这个场地来说太宽泛了。

使用 autotools 进行选择性编译的方法是在 configure.ac 中检测并在 Makefile.am 中定义。使用此方法,在执行初始配置命令时完成可用库的检测,这有条件地创建一个 Makefile,如果安装了其依赖项,则仅编译源代码。

例如来自gtkIOStream software's configure.ac detects the presence of jack。然后定义变量 HAVE_JACK(如果存在),如下所示:

# jack
PKG_CHECK_MODULES([JACK], [jack], HAVE_JACK="yes", HAVE_JACK="no")
AC_SUBST(JACK_CFLAGS)
AC_SUBST(JACK_LIBS)
AM_CONDITIONAL(HAVE_JACK, test x$HAVE_JACK = xyes)
if test "x$HAVE_JACK" == xyes ; then
    AC_DEFINE(HAVE_JACK, [], [whether to build in Jack support])
fi

Makefile.am checks for the HAVE_JACK variable 如果它存在,它会改变编译。在您的情况下,这应该有效:

if HAVE_JACK
    myprog_SOURCES += src/jack_output.cc
endif

现在,如果配置检测到 jack,则您的 Makefile 会将其添加到要构建的源中。

如果你想限制正在构建的 ammplications,那么你将需要 ammed 一个不同的变量(对于前面提到的 gtkIOStream 示例),如下所示:

if HAVE_JACK
bin_PROGRAMS += I2SEndianTest CrossoverTester
endif

gtkIOStream 也像这样检测 ALSA :

# alsa
PKG_CHECK_MODULES(ALSA, alsa, HAVE_ALSA="yes", HAVE_ALSA="no")
AC_SUBST(ALSA_CFLAGS)
AC_SUBST(ALSA_LIBS)
AM_CONDITIONAL(HAVE_ALSA, test x$HAVE_ALSA = xyes)
if test "x$HAVE_ALSA" == xyes ; then
    AC_DEFINE(HAVE_ALSA, [], [whether to build in Alsa support])
fi