【问题标题】:How to make autoconf conditionally use system extensions?如何使 autoconf 有条件地使用系统扩展?
【发布时间】:2014-02-27 01:05:14
【问题描述】:

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

在 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 以及所有这些。)只需在文件顶部无条件地打开我可能使用或可能不使用的所有扩展名?完全不同的东西?

【问题讨论】:

    标签: c autoconf


    【解决方案1】:

    我与 Autoconf 邮件列表中的好人讨论了这个问题。他们的观点似乎是,由于我不想严格兼容 POSIX 或 ANSI C 之类的东西,所以无论如何我都应该使用AC_USE_SYSTEM_EXTENSIONS;并且,这样做应该没有害处,因为我实际上正在使用系统扩展,无论该扩展是否实际上是默认编译环境的一部分。

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

    【讨论】:

      【解决方案2】:

      (提前为冗长的答案道歉,但我不想冒险遗漏太多细节。)

      您应该有条件地定义_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 是否都是 yes。如果两者都是肯定的,请将_GNU_SOURCE 定义为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])
      

      我将它包装在一个名为 ck_FUNC_FOPENCOOKIE 的宏中,因此您可以在调用宏之前将它放在一个独立的 m4 文件中,该文件通过 m4_include 包含在内。这将防止您的配置脚本变得非常混乱,并且很容易添加和测试(以及删除)。

      以上代码行为总结:

      • 没有可编译/可链接的 fopencookie 函数或类型不是预期的:
        • ck_cv_libc_fopencookie = 否
        • ck_cv_libc_fopencookie_gnu = 否
        • _GNU_SOURCE (AC_DEFINE) = 未定义
        • HAVE_FOPENCOOKIE = 未定义
      • fopencookie 有效,不需要 _GNU_SOURCE:
        • ck_cv_libc_fopencookie = 是
        • ck_cv_libc_fopencookie_gnu = 未设置
        • _GNU_SOURCE (AC_DEFINE) = 未定义
        • HAVE_FOPENCOOKIE = 1
      • fopencookie 有效,需要 _GNU_SOURCE:
        • ck_cv_libc_fopencookie = 是
        • ck_cv_libc_fopencookie_gnu = 是
        • _GNU_SOURCE (AC_DEFINE) = 1
        • HAVE_FOPENCOOKIE = 1

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

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

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

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

      【讨论】:

      • 我想给这个答案不止一个赞成票,因为它在这种情况下包含很多关于 Autoconf 的有用信息,但我觉得我从 Autoconf 邮件列表中收到的答案(我张贴在单独的答案中,看看哪个)应该被视为权威答案。
      • 话虽如此,但我认为在没有定义_GNU_SOURCE 的情况下fopencookie 是可用的。
      【解决方案3】:

      要么无条件开启系统扩展,要么自己写AC_DEFINE(带模板等)。我会做前者。

      【讨论】:

        猜你喜欢
        • 2012-07-20
        • 1970-01-01
        • 1970-01-01
        • 2012-08-02
        • 2010-12-15
        • 2013-10-18
        • 2020-04-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多