如何使autoconf有条件地使用系统扩展?

时间:2014-02-27 01:05:14

标签: c autoconf

我正在尝试编写一个可以在Linux / glibc和FreeBSD上使用自定义stdio流的程序,为此,我正在尝试编写一个可以正确检测这些并转向的autoconf脚本在必要的编译器标志上支持它们。

在glibc上,我需要定义_GNU_SOURCE才能访问fopencookiecookie_io_functions_t。 Autoconf巧妙地支持使用AC_USE_SYSTEM_EXTENSIONS打开它,但我似乎无法弄清楚如何在必要时才这样做。目前,我正试图这样做:

HAS_FOPENCOOKIE=yes
AC_CHECK_FUNC(fopencookie, [AC_USE_SYSTEM_EXTENSIONS], [HAS_FOPENCOOKIE=no])
AC_CHECK_MEMBER([cookie_io_functions_t.read], [], [HAS_FOPENCOOKIE=no])

这个本身就可以运行,但是当我尝试生成配置脚本时,autoconf会大声抱怨AC_COMPILE_IFELSE was called before AC_USE_SYSTEM_EXTENSIONS。显然,它认为在开启系统扩展之前实际进行任何测试确实是不好的做法。

我该怎么办?只是忽略警告?而是手动执行必要的AC_DEFINE? (后者看起来很难看,因为我还需要定义一个AH_TEMPLATE以及所有这些。)只需打开我可能使用或不使用的所有扩展,无条件地在文件的顶部?完全不同的东西?

3 个答案:

答案 0 :(得分:3)

(对于长篇答案提前道歉,但我不想冒险留下太多细节。)

您应该使用_GNU_SOURCE来有条件地定义AH_VERBATIM以创建模板,并AC_DEFINE来定义其值,同时如果您希望仅为Glibc执行此操作,则会禁用其他系统的扩展。否则,最好默认使用系统扩展。请参阅下面代码的前几行,了解我对AH_VERBATIM的想法。

您可以省略AC_CHECK_ *调用的if / else部分,并使用shell代码来测试$ac_cv_func_fopencookie$ac_cv_member_cookie_io_functions_t_read是否都是。如果它们都是,则将_GNU_SOURCE定义为1.请注意,这可能会影响编译器的其他测试,因此,除非您有充分的理由不这样做,否则您应该执行此检查并定义{ {1}}在运行任何其他编译测试之前(例如_GNU_SOURCE)。

请注意,在这种情况下,如果您绝对拒绝使用系统扩展,那么编写自己的测试可能会更好。除非我使用AC_CHECK_FUNC或先前定义AC_USE_SYSTEM_EXTENSIONS,否则_GNU_SOURCE是未定义的类型。检查该类型还需要cookie_io_functions_t,并且一旦定义,您显然无法取消定义。

这是我如何做的一个例子。它说明为什么你应该使用_GNU_SOURCE,因为它只处理AC_USE_SYSTEM_EXTENSIONS

_GNU_SOURCE

我将它包装在一个名为AC_DEFUN([ck_FUNC_FOPENCOOKIE], [AH_VERBATIM( [_GNU_SOURCE], [/* Enable GNU extensions for fopencookie functionality to work where required. */ #ifndef _GNU_SOURCE #undef _GNU_SOURCE #endif]) AC_CACHE_CHECK( [whether fopencookie works without _GNU_SOURCE being defined], [ck_cv_libc_fopencookie], [AC_LINK_IFELSE( [AC_LANG_SOURCE( [#undef _GNU_SOURCE #include <stdio.h> cookie_read_function_t *ck_read = (cookie_read_function_t *)foo_read; cookie_write_function_t *ck_write = (cookie_write_function_t *)ck_read; cookie_seek_function_t *ck_seek = (cookie_seek_function_t *)ck_read; cookie_close_function_t *ck_close = (cookie_close_function_t *)fclose; size_t foo_read(void *cookie, char *buf, size_t size) { cookie = buf; buf = cookie; return (ssize_t)size; } int main(void) { cookie_io_functions_t x; x.read = ck_read; x.write = ck_write; x.seek = ck_seek; x.close = ck_close; fopencookie(NULL, NULL, x); return 0; } ])], [ck_cv_libc_fopencookie=yes], [ck_cv_libc_fopencookie=no])]) if test "x${ck_cv_libc_fopencookie}" = xno ; then AC_CACHE_CHECK( [whether fopencookie works at all], [ck_cv_libc_fopencookie_gnu], [AC_LINK_IFELSE( [AC_LANG_SOURCE( [#ifndef _GNU_SOURCE #define _GNU_SOURCE 1 #endif #include <stdio.h> cookie_read_function_t *ck_read = (cookie_read_function_t *)foo_read; cookie_write_function_t *ck_write = (cookie_write_function_t *)ck_read; cookie_seek_function_t *ck_seek = (cookie_seek_function_t *)ck_read; cookie_close_function_t *ck_close = (cookie_close_function_t *)fclose; size_t foo_read(void *cookie, char *buf, size_t size) { cookie = buf; buf = cookie; return (ssize_t)size; } int main(void) { cookie_io_functions_t x; x.read = ck_read; x.write = ck_write; x.seek = ck_seek; x.close = ck_close; fopencookie(NULL, NULL, x); return 0; } ])], [ck_cv_libc_fopencookie_gnu=yes], [ck_cv_libc_fopencookie_gnu=no])]) if test "x${ck_cv_libc_fopencookie_gnu}" = xyes ; then AC_DEFINE([_GNU_SOURCE], [1]) ck_cv_libc_fopencookie=yes fi # test with _GNU_SOURCE succeeded fi # test without _GNU_SOURCE failed if test "x${ck_cv_libc_fopencookie}" = xyes ; then AC_DEFINE( [HAVE_FOPENCOOKIE], [1], [Define to 1 if fopencookie and related functionality is fully working.]) fi]) 的宏中,因此您可以将其放在一个独立的m4文件中,该文件在调用宏之前通过ck_FUNC_FOPENCOOKIE包含在内。这将阻止您的配置脚本变得非常混乱,并且很容易添加和测试(以及删除)。

上述代码行为的摘要:

  • 没有可编辑/可链接的fopencookie功能或类型不符合预期:
    • ck_cv_libc_fopencookie = no
    • ck_cv_libc_fopencookie_gnu = no
    • _GNU_SOURCE(AC_DEFINE)= undefined
    • HAVE_FOPENCOOKIE = undefined
  • fopencookie有效,_GNU_SOURCE不需要:
    • ck_cv_libc_fopencookie = yes
    • ck_cv_libc_fopencookie_gnu = unset
    • _GNU_SOURCE(AC_DEFINE)= undefined
    • HAVE_FOPENCOOKIE = 1
  • fopencookie有效,需要_GNU_SOURCE:
    • ck_cv_libc_fopencookie = yes
    • ck_cv_libc_fopencookie_gnu = yes
    • _GNU_SOURCE(AC_DEFINE)= 1
    • HAVE_FOPENCOOKIE = 1

正如您所看到的,除了m4_include定义或缺乏定义之外,还有更多内容。但是,不需要_GNU_SOURCE的情况是跨平台兼容性。

基本上_GNU_SOURCE仅在需要时启用(Glibc),如果_GNU_SOURCE内容可用,您仍然可以获得定义HAVE_FOPENCOOKIE。我使用了非常通用的测试,但您可以轻松编辑代码以使其更合适。

但是,fopencookie宏与上述代码之间的主要区别在于我使用链接测试来确保C库包含AC_CHECK_FUNC函数的符号。如果没有,即使在另一个驻留在不同库中的系统上存在这样的功能,也会被认为是不可用的。由于我对项目的目标一无所知,所以我只能评论你需要保存fopencookie变量,根据目标将其设置为包含特定库的事实,调用{ {1}}宏,并在两个测试完成后恢复LIBS的值。在调用上面的宏之前,甚至可以在主代码中完成。

我认为最简单的方法是“吃蛋糕并吃掉它”,这将是确定是否使用AC_LINK_IFELSE调用检测到glibc,并且将检查Glibc独有的任何宏以确定它是定义。如果是,则进行必要的检查以确保存在的版本包括您需要的功能和成员。如果检查成功,您将自动定义LIBS,一切都会很棒。但是你牺牲了跨平台兼容性,这是GNU Autotools的重点,并且可以在不依赖Glibc存在的情况下完成相同的检查。据我所知,没有研究,uClibc可能有相同的东西,尽管不是Glibc而且没有任何Glibc特定的预处理器符号。

答案 1 :(得分:3)

我和Autoconf邮件列表上的好人谈到了这一点。他们的意见似乎是因为我并没有试图与POSIX或ANSI C严格兼容,所以我几乎应该使用AC_USE_SYSTEM_EXTENSIONS;因为我实际上正在使用系统扩展,所以不应该有任何伤害,无论该扩展是否实际上是默认编译环境的一部分。

我承认我不确定我是否完全理解默认编译环境和“扩展”编辑环境之间的区别,但由于这是他们对自己工具的看法,我会购买它并将让它成为权威的答案。

答案 2 :(得分:1)

无条件打开系统扩展,或编写自己的AC_DEFINE(带模板等)。我会做前者。