(提前为冗长的答案道歉,但我不想冒险遗漏太多细节。)
您应该有条件地定义_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 特定的预处理器符号。