diff --git a/CMakeLists.txt b/CMakeLists.txt index c7219a071d3..476cc030470 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -394,7 +394,7 @@ endif () # Enable POSIX features up to POSIX.1-2008 plus the XSI extension and BSD-derived definitions. # Both _BSD_SOURCE and _DEFAULT_SOURCE are defined for backwards-compatibility with glibc 2.19 and earlier. # _BSD_SOURCE and _DEFAULT_SOURCE are required by `getpagesize`, `h_errno`, etc. -# _XOPEN_SOURCE=700 is required by `strnlen`, etc. +# _XOPEN_SOURCE=700 is required by `strnlen`, `strerror_r`, etc. add_definitions (-D_XOPEN_SOURCE=700 -D_BSD_SOURCE -D_DEFAULT_SOURCE) list (APPEND CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE=700 -D_BSD_SOURCE -D_DEFAULT_SOURCE) diff --git a/src/libbson/CMakeLists.txt b/src/libbson/CMakeLists.txt index 73f6e80f7da..cc8da23dcc5 100644 --- a/src/libbson/CMakeLists.txt +++ b/src/libbson/CMakeLists.txt @@ -14,12 +14,14 @@ include (MongoC-Warnings) set (BSON_OUTPUT_BASENAME "bson" CACHE STRING "Output bson library base name") +include (CheckCSourceCompiles) include (CheckFunctionExists) include (CheckIncludeFile) +include (CheckIncludeFiles) include (CheckStructHasMember) include (CheckSymbolExists) -include (TestBigEndian) include (InstallRequiredSystemLibraries) +include (TestBigEndian) set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/build/cmake) @@ -83,8 +85,6 @@ else () set (BSON_OS 1) endif () -include (CheckIncludeFiles) - CHECK_INCLUDE_FILE (strings.h BSON_HAVE_STRINGS_H) if (NOT BSON_HAVE_STRINGS_H) set (BSON_HAVE_STRINGS_H 0) @@ -119,6 +119,38 @@ else () set (BSON_BYTE_ORDER 1234) endif () +# 1. Normally, `defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600` should be enough +# to detect if an XSI-compliant `strerror_r` is available. +# 2. However, GNU provides an incompatible `strerror_r` as an extension, +# requiring an additional test for `!defined(_GNU_SOURCE)`. +# 3. However, musl does not support the GNU extension even when `_GNU_SOURCE` is +# defined, preventing `!defined(_GNU_SOURCE)` from being a portable +# XSI-compliance test. +# 4. However, musl does not provide a feature test macro to detect compilation +# with musl, and workarounds that use internal glibc feature test macros such +# as `defined(__USE_GNU)` are explicitly forbidden by glibc documentation. +# 5. Therefore, give up on using feature test macros: use `CheckCSourceCompiles` +# to test if the current `strerror_r` is XSI-compliant if present. +if (NOT WIN32) + CHECK_C_SOURCE_COMPILES( + [[ + #include + int test(int errnum, char *buf, size_t buflen) { + return strerror_r (errnum, buf, buflen); + } + int main(void) {} + ]] + BSON_HAVE_XSI_STRERROR_R + FAIL_REGEX "int-conversion" + ) +endif () + +if (BSON_HAVE_XSI_STRERROR_R) + set(BSON_HAVE_XSI_STRERROR_R 1) +else() + set(BSON_HAVE_XSI_STRERROR_R 0) +endif () + configure_file ( "${PROJECT_SOURCE_DIR}/src/bson/bson-config.h.in" "${PROJECT_BINARY_DIR}/src/bson/bson-config.h" diff --git a/src/libbson/src/bson/bson-config.h.in b/src/libbson/src/bson/bson-config.h.in index 2849d0206a2..8c55eadfdb7 100644 --- a/src/libbson/src/bson/bson-config.h.in +++ b/src/libbson/src/bson/bson-config.h.in @@ -122,4 +122,13 @@ # undef BSON_HAVE_STRLCPY #endif + +/* + * Define to 1 if you have an XSI-compliant strerror_r on your platform. + */ +#define BSON_HAVE_XSI_STRERROR_R @BSON_HAVE_XSI_STRERROR_R@ +#if BSON_HAVE_XSI_STRERROR_R != 1 +# undef BSON_HAVE_XSI_STRERROR_R +#endif + #endif /* BSON_CONFIG_H */ diff --git a/src/libbson/src/bson/bson-error.c b/src/libbson/src/bson/bson-error.c index d16b5faefcf..7d2352805d0 100644 --- a/src/libbson/src/bson/bson-error.c +++ b/src/libbson/src/bson/bson-error.c @@ -109,12 +109,14 @@ bson_strerror_r (int err_code, /* IN */ if (strerror_s (buf, buflen, err_code) != 0) { ret = buf; } -#elif defined(__GNUC__) && defined(_GNU_SOURCE) - ret = strerror_r (err_code, buf, buflen); -#else /* XSI strerror_r */ +#elif defined(BSON_HAVE_XSI_STRERROR_R) if (strerror_r (err_code, buf, buflen) == 0) { ret = buf; } +#elif defined(_GNU_SOURCE) + ret = strerror_r (err_code, buf, buflen); +#else + #error "Unable to find a supported strerror_r candidate" #endif if (!ret) {