[英]How to make autoconf conditionally use system extensions?
我正在嘗試編寫一個可以在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
以及所有這些。)只需打開我可能使用或不使用的所有擴展,無條件地在文件的頂部? 完全不同的東西?
(對於長期答案,請提前道歉,但我不想冒險留下太多細節。)
您應該使用AH_VERBATIM
有條件地定義_GNU_SOURCE
以創建模板,並使用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.請注意,這有可能影響編譯器的其他測試,因此除非您有充分的理由不這樣做,否則您應該執行此檢查並在運行任何其他之前定義_GNU_SOURCE
編譯測試(例如AC_CHECK_FUNC
)。
請注意,在這種情況下,如果您絕對拒絕使用系統擴展,那么編寫自己的測試可能會更好。 除非我使用AC_USE_SYSTEM_EXTENSIONS
,或者之前定義_GNU_SOURCE
, cookie_io_functions_t
是未定義的類型。 檢查該類型還需要_GNU_SOURCE
,並且自然一旦定義,您顯然無法_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
包含在內。 這將阻止您的配置腳本變得非常混亂,並且很容易添加和測試(以及刪除)。
以上代碼的行為摘要:
正如您所看到的,除了_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郵件列表上的好人談到了這一點。 他們的意見似乎是因為我並沒有試圖與POSIX或ANSI C這樣的東西嚴格兼容, AC_USE_SYSTEM_EXTENSIONS
無論如何我應該使用AC_USE_SYSTEM_EXTENSIONS
; 因為我實際上正在使用系統擴展,所以這樣做應該沒有害處,無論該擴展是否實際上是默認編譯環境的一部分。
我承認我不確定我是否完全理解默認編譯環境和“擴展”編譯環境之間的區別,但由於這是他們對自己工具的看法,我會購買它並讓它成為權威的答案。
要么無條件地打開系統擴展,要么編寫自己的AC_DEFINE
(帶模板等)。 我會做前者。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.