我正在尝试编写一个可以在Linux / glibc和FreeBSD上使用自定义stdio
流的程序,为此,我正在尝试编写一个可以正确检测这些并转向的autoconf脚本在必要的编译器标志上支持它们。
在glibc上,我需要定义_GNU_SOURCE
才能访问fopencookie
和cookie_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
以及所有这些。)只需打开我可能使用或不使用的所有扩展,无条件地在文件的顶部?完全不同的东西?
答案 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
包含在内。这将阻止您的配置脚本变得非常混乱,并且很容易添加和测试(以及删除)。
上述代码行为的摘要:
正如您所看到的,除了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
(带模板等)。我会做前者。