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
。首先是因为 LDFLAGS
与 LIBS
的语义不同,其次是因为 pkg-config
文件可能为您提供了所需的进一步搜索路径。
相反,您应该根据需要向 Makefile.am
添加标志:
mytarget_CFLAGS = $(LIBFTDI_CFLAGS)
mytarget_LDADD = $(LIBFTDI_LIBS)
你可以参考我的Autotools Mythbuster — Dependency Discovery了解更多关于如何使用pkg-config
依赖的细节。你可以在那里看到你通常如何使用 AC_CHECK_LIB
或 AC_SEARCH_LIB
,但如果 pkg-config
有效,请坚持使用,因为它更可靠和一致。
为什么你的其他尝试失败了
库测试
您的 commented-out AC_CHECK_LIB
和 AC_SEARCH_LIBS
示例没有展示正确的用法。提供了使用详情 in the manual,但总而言之:
AC_CHECK_LIB
的参数是
- 库的简单名称,即
ftdi
- 库提供的特性函数的名称
- (可选)
configure
在找到库时执行的代码。默认是将 link 选项添加到 $LIBS
并定义 HAVE_LIB*
预处理器宏。
- (可选)
configure
在找不到库时执行的代码
- (可选)额外的库 link 选项(尚未在
$LIBS
中)需要 link 使用正在检查的库的程序
AC_SEARCH_LIBS
的参数是
- 要搜索的函数的名称
- 要搜索的一个或多个库名称的列表
- (可选)
configure
在找到库的情况下执行的代码, 除了 将 link 选项添加到 $LIBS
(但未定义任何预处理器宏)
- (可选)
configure
在找不到库时执行的代码
- (可选)额外的库 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_LIB
或 AC_SEARCH_LIBS
的第五个参数添加明确的额外 link 标志来解决,但更常见的是 semi-automatically 通过执行 AC_CHECK_LIB
或AC_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
与这些结合使用。以这种方式将 configure
与 pkg-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
创建的输出变量比使用它们更新 $LIBS
或 configure
中的其他通用变量更为典型。但是,如果您确实在 configure
中使用它们,则必须了解 LIBS
和 LDFLAGS
具有不同的作用,几乎没有重叠。在 LIBS
中包含 LDFLAGS
通常是不合适的,更不用说不必要了。如果你想在 configure
内更新 LIBS
,那么可以这样做:
LIBS="$LIBFTDI_LIBS $LIBS"
如果你打算这样做,那么你可能应该对 pkg-config 报告的编译器标志做同样的事情,如果有的话:
CFLAGS="$CFLAGS $LIBFTDI_CFLAGS"
大家好使用 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
。首先是因为 LDFLAGS
与 LIBS
的语义不同,其次是因为 pkg-config
文件可能为您提供了所需的进一步搜索路径。
相反,您应该根据需要向 Makefile.am
添加标志:
mytarget_CFLAGS = $(LIBFTDI_CFLAGS)
mytarget_LDADD = $(LIBFTDI_LIBS)
你可以参考我的Autotools Mythbuster — Dependency Discovery了解更多关于如何使用pkg-config
依赖的细节。你可以在那里看到你通常如何使用 AC_CHECK_LIB
或 AC_SEARCH_LIB
,但如果 pkg-config
有效,请坚持使用,因为它更可靠和一致。
为什么你的其他尝试失败了
库测试
您的 commented-out AC_CHECK_LIB
和 AC_SEARCH_LIBS
示例没有展示正确的用法。提供了使用详情 in the manual,但总而言之:
AC_CHECK_LIB
的参数是- 库的简单名称,即
ftdi
- 库提供的特性函数的名称
- (可选)
configure
在找到库时执行的代码。默认是将 link 选项添加到$LIBS
并定义HAVE_LIB*
预处理器宏。 - (可选)
configure
在找不到库时执行的代码 - (可选)额外的库 link 选项(尚未在
$LIBS
中)需要 link 使用正在检查的库的程序
- 库的简单名称,即
AC_SEARCH_LIBS
的参数是- 要搜索的函数的名称
- 要搜索的一个或多个库名称的列表
- (可选)
configure
在找到库的情况下执行的代码, 除了 将 link 选项添加到$LIBS
(但未定义任何预处理器宏) - (可选)
configure
在找不到库时执行的代码 - (可选)额外的库 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_LIB
或 AC_SEARCH_LIBS
的第五个参数添加明确的额外 link 标志来解决,但更常见的是 semi-automatically 通过执行 AC_CHECK_LIB
或AC_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
与这些结合使用。以这种方式将 configure
与 pkg-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
创建的输出变量比使用它们更新 $LIBS
或 configure
中的其他通用变量更为典型。但是,如果您确实在 configure
中使用它们,则必须了解 LIBS
和 LDFLAGS
具有不同的作用,几乎没有重叠。在 LIBS
中包含 LDFLAGS
通常是不合适的,更不用说不必要了。如果你想在 configure
内更新 LIBS
,那么可以这样做:
LIBS="$LIBFTDI_LIBS $LIBS"
如果你打算这样做,那么你可能应该对 pkg-config 报告的编译器标志做同样的事情,如果有的话:
CFLAGS="$CFLAGS $LIBFTDI_CFLAGS"