autoconf:在 `configure.ac` 中包含 `libftdi` 的最佳实践

autoconf: best practice for including `libftdi` in `configure.ac`

大家好使用 GNU autoconf,

查找 libftdi 并将其包含在 autoconf 中以使用它编译 C 程序的最佳做法是什么?
configure.ac 文件中的以下片段有效,但我不确定这是否是最佳实践:

PKG_CHECK_MODULES([LIBFTDI], [libftdi])
#AC_CHECK_LIB([ftdi],[ftdi],[ftdi])  # Why doesn't this work?
#AC_SEARCH_LIBS([ftdi],[ftdi],[ftdi])  # Why doesn't this work?
#AC_CHECK_HEADERS([ftdi.h],[],[echo "error: missing libftdi header files" && exit 1])
LIBS="-lftdi $LIBS $LDFLAGS" # works, but is this the best way?

我正在用 autoconf (GNU Autoconf) 2.69 构建程序并在 Ubuntu 18.04 上用 gcc version 7.5.0 编译它。

您可以这样查看:

AC_CHECK_LIB([ftdi],[ftdi_init],[],[echo "error: missing libftdi library" && exit 1],[])
    LDFLAGS="-lftdi $LDFLAGS"

AC_CHECK_LIB 的第二个参数是库导出的函数,在这种情况下,init 调用运行良好。

如果 libftdi 使用 pkg-config(并且看起来如此,鉴于您说该代码段有效),PKG_CHECK_MODULES 就是您想要的。 action-if-not-found 的默认值是错误输出,因此如果这是必需的依赖项,它正是您想要的。

但是你不应该那样使用LIBS。首先是因为 LDFLAGSLIBS 的语义不同,其次是因为 pkg-config 文件可能为您提供了所需的进一步搜索路径。

相反,您应该根据需要向 Makefile.am 添加标志:

mytarget_CFLAGS = $(LIBFTDI_CFLAGS)
mytarget_LDADD = $(LIBFTDI_LIBS)

你可以参考我的Autotools Mythbuster — Dependency Discovery了解更多关于如何使用pkg-config依赖的细节。你可以在那里看到你通常如何使用 AC_CHECK_LIBAC_SEARCH_LIB,但如果 pkg-config 有效,请坚持使用,因为它更可靠和一致。

为什么你的其他尝试失败了

库测试

您的 commented-out AC_CHECK_LIBAC_SEARCH_LIBS 示例没有展示正确的用法。提供了使用详情 in the manual,但总而言之:

  • AC_CHECK_LIB 的参数是

    1. 库的简单名称, ftdi
    2. 库提供的特性函数的名称
    3. (可选)configure 在找到库时执行的代码。默认是将 link 选项添加到 $LIBS 并定义 HAVE_LIB* 预处理器宏。
    4. (可选)configure 在找不到库时执行的代码
    5. (可选)额外的库 link 选项(尚未在 $LIBS 中)需要 link 使用正在检查的库的程序
  • AC_SEARCH_LIBS 的参数是

    1. 要搜索的函数的名称
    2. 要搜索的一个或多个库名称的列表
    3. (可选)configure 在找到库的情况下执行的代码, 除了 将 link 选项添加到 $LIBS(但未定义任何预处理器宏)
    4. (可选)configure 在找不到库时执行的代码
    5. (可选)额外的库 link 选项(尚未在 $LIBS 中 link 使用正在检查的库的程序

您的 AC_CHECK_LIB 示例和 AC_SEARCH_LIBS 示例都没有正确指定要检查的现有 libftdi 函数。此外,每种情况下的第三个参数不太可能是有效的 shell / Autoconf 代码,因此如果找到库 configure 可能会崩溃.更好的可能是:

AC_CHECK_LIB([ftdi], [ftdi_init])

AC_SEARCH_LIBS([ftdi_init], [ftdi])

根据您确切想要执行的操作、libftdi 的详细信息以及 configure.ac 上下文,您可能需要为部分或所有可选参数提供适当的值。

尽管实际上已经安装了库,但库检查失败的主要原因是

  • 正在安装的库不在默认搜索路径中
  • 该库对其他库有 link 依赖性,并且在检查时尚未考虑这些依赖性

前者类似于下一节中讨论的 header 安装位置注意事项。后者可以通过 AC_CHECK_LIBAC_SEARCH_LIBS 的第五个参数添加明确的额外 link 标志来解决,但更常见的是 semi-automatically 通过执行 AC_CHECK_LIBAC_SEARCH_LIBS 以相反的先决条件顺序进行测试,因此 LIBS 的值是用 appropriately-ordered 列表 link 标志构建的,在每个点都准备好​​支持下一次检查,并最终适合支持整体编译。

另请注意,libftdi 同时提供 C 和 C++ 接口。在 ftdi_init 中,我一直小心地选择了一个具有 C linkage 的函数,以避免 C++ name-mangling 问题(请参阅 Autoconf 手册中的 How to test a C++ library usability in configure.in?). You may also need to ensure that the tests are run with the C compiler (see Language Choice)。

Header 测试

另一方面,您的 AC_CHECK_HEADERS 用法似乎并没有本质上的错误。如果生成的 configure 脚本未检测到 ftdi.h,则意味着 header 不在编译器的默认 header 搜索路径中。例如,如果它安装在 /usr/include/ftdi 等子目录中,则可能会发生这种情况。这将是 ftdi 和系统安装约定的问题。

如果 ftdi 约定将 header 安装在子目录中,那么您的源文件应在其 #include 指令中指定:

#include <ftdi/ftdi.h>

如果您的源文件确实如此,那么这也应该是您告诉 Autoconf 寻找的内容:

AC_CHECK_HEADERS([ftdi/ftdi.h])

无论是否需要或使用子目录前缀,最好考虑将 header 和/或库安装在 non-standard 位置的可能性。尽管总是可以通过在 configure 环境中的 CPPFLAGS 变量中指定适当的标志来做到这一点,但我更喜欢并推荐使用 AC_ARG_WITH 来指定 --with 参数或 AC_ARG_VAR 指定一个环境变量,configure 将为此目的查询。例如,

AC_ARG_WITH([ftdi-includedir],
  [AS_HELP_STRING([--with-ftdiincludedir=dir],
    [specifies a custom directory for the libftdi header files])],
  [CPPFLAGS="$CPPFLAGS -I$withval"]
)

为特定目的公开参数或环境变量突出显示(在 ./configure --help 的输出中)这是用户可能需要调整的旋钮这一事实。此外,通过 for-purpose 向量接收包含目录有时有助于限制指定包含目录在哪些编译中可用。

PKG_CHECK_MODULES

Autotools objective 和哲学是通过最小化外部依赖和 wri 来支持尽可能广泛的构建机器和环境使用最便携的配置和构建代码。为此,Autotools 被设计成不需要它们自己在支持的系统上构建项目。相反,Autoconf 将 configure 作为 stand-alone、高度可移植的 shell 脚本生成,而 Automake 为高度可移植的 makefile 生成可配置模板。这些旨在包含在源包中,以在每个构建系统上使用 as-is。让你的 configure 脚本依赖于 pkg-config 安装在你的项目要构建的每个系统上,就像使用 PKG_CHECK_MODULES 一样,与那些 objectives 冲突。

一个问题的重要性可能是一些争议的主题。如果可用,pkg-config 会非常有用,特别是对于需要复杂构建标志的组件。因此,PKG_CHECK_MODULES 对于那些提供 pkg-config 元数据的组件来说, 在那些存在或随时可用的系统上 非常方便。 =84=]

但是 pkg-config 不一定适用于您的软件所针对的每个系统。即使在名义上可用的系统上,也不能合理地假设它存在或可获得。即使在拥有它的系统上,pkg-config 感兴趣的库的元数据也不一定与库一起安装。

因此,我强烈建议您避免在您的 Autoconf 项目中使用 PKG_CHECK_MODULES。在任何情况下,您都需要知道如何在没有它的情况下进行操作,因为它不是某些库的选项。在适当的地方,提供构建器可以提供适当标志的挂钩,并让 他们 选择是否将 pkg-config 与这些结合使用。以这种方式将 configurepkg-config 分离会使您的工作量增加一些,在某些情况下对构建者来说也是如此,但它更灵活。

您的 PKG_CHECK_MODULES 示例

假设“libftdi”是适当的 pkg-config 模块名称(您必须知道适当的名称):

您的示例调用本身似乎没问题
PKG_CHECK_MODULES([LIBFTDI], [libftdi])

但是,尽管这可能会产生一个成功运行的 configure 脚本,但它本身并不能为您做很多事情。特别是,它验证命名模块的 pkg-config 元数据是否存在,但是

  • 它不验证库的存在或测试库的使用或 header
  • 虽然它确实设置了一些包含编译和 link 标志的输出变量,但您似乎没有使用那些
    • 具体来说,如果您要依赖 pkg-config,那么您应该使用它向您报告的 link 标志,而不是硬编码 -lftdi,并且仅此而已。

此外,在您的 makefile 中使用 PKG_CHECK_MODULES 创建的输出变量比使用它们更新 $LIBSconfigure 中的其他通用变量更为典型。但是,如果您确实在 configure 中使用它们,则必须了解 LIBSLDFLAGS 具有不同的作用,几乎没有重叠。在 LIBS 中包含 LDFLAGS 通常是不合适的,更不用说不必要了。如果你想在 configure 内更新 LIBS,那么可以这样做:

LIBS="$LIBFTDI_LIBS $LIBS"

如果你打算这样做,那么你可能应该对 pkg-config 报告的编译器标志做同样的事情,如果有的话:

CFLAGS="$CFLAGS $LIBFTDI_CFLAGS"