Skip to content

Conversation

@eramongodb
Copy link
Contributor

@eramongodb eramongodb commented Nov 20, 2025

Related to CXX-3180. This PR applies various mingw-w64 GCC toolchain ("mingw") compatibility fixes to enable pkg-config tasks coverage on Windows (CXX-2973).

This PR is a followup to various prior work across the libmongocrypt and mongo-c-driver repositories concerning mingw compatibility. It is not sufficient to build the C++ Driver libraries with mingw alone: all dependent libraries must also be built with the same toolchain in order to satisfy linker requirements (a la libstdc++ vs. libc++). Therefore, upstream compatibility issues in the C Driver and libmongocrypt libraries needed to be addressed together or in advance of the C++ Driver libraries.

This PR description enumerates the various compatibility issues addressed by this PR (ordered by commit).


mingw-w64 GCC refuses to link weak variable symbols:

multiple definition of `.weak._ZN7bsoncxx6detail15strong_ordering4lessE._ZNSt11char_traitsIcE6lengthEPKc'

Per GCC docs:

The selectany attribute causes an initialized global variable to have link-once semantics. When multiple definitions of the variable are encountered by the linker, the first is selected and the remainder are discarded. Following usage by the Microsoft compiler, the linker is told not to warn about size or content differences of the multiple definitions. [...] The selectany attribute is only available on Microsoft Windows targets.

Therefore, this PR proposes using [[gnu::selectany]] instead of [[gnu::weak]] on Windows, for mingw-w64 GCC only, via the BSONCXX_PRIVATE_INLINE_CXX17 macro added in #1468.


When linking with mingw, ABI symbols do not seem to have a unique address as typically assumed:

failed: *deleter_ptr == &bson_free for: 0x00007ff998fdd3d8 == 0x00007ff6bfb09d08

This scoped_bson test assumes the globally-defined bson_free function has a unique address; however, this (and related) test assertions fail, suggesting the mongocxx_mocked library "sees" a different definition of bson_free than the test_driver executable. It is unclear to me if this behavior also affects other unique identity assumptions within the codebase, such as in bsoncxx::v1::document::value concerning the identity of the preallocated empty BSON document storage. For now, this PR implements a workaround that tests the inverse of the regular "deleter changed as expected" ("changed to Y" -> "is no longer X").


Possibly related to the bson_free issue, the behavior of bson_concat() allocation via scoped_bson differs when built with mingw:

failed: lhs += rhs, std::logic_error, Catch::Matchers::Message("mongocxx::scoped_bson::operator+=: bson_concat failed") for: mongocxx::scoped_bson::operator+=: bson_new_from_data failed exception message matches "mongocxx::scoped_bson::operator+=: bson_concat failed"

I do not think there is a compelling motivation for the error message here to differ from the one here given both depend on the result of bson_concat (not bson_new_from_data). Therefore, this PR makes the error messages the same in both cases.


Possibly related to the bson_free issue, linking with mingw detected an ODR violation of the Catch::StringMaker<T>::convert() specialization for bsoncxx::v1::document::view (output edited for clarity):

ld.exe: src/mongocxx/test/private/scoped_bson.cpp.obj: in function `Catch::StringMaker<bsoncxx::v1::document::view, void>::convert[abi:cxx11](bsoncxx::v1::document::view const&)':
catch2/catch_tostring.hpp:527: multiple definition of `Catch::StringMaker<bsoncxx::v1::document::view, void>::convert[abi:cxx11](bsoncxx::v1::document::view const&)'; bsoncxx/test/v1/document/view_string_maker.cpp.obj:src/bsoncxx/test/v1/document/view_string_maker.cpp:28: first defined here

This seems to be caused by the implicit instantiation of Catch::StringMaker<T> in the bsoncxx/test/v_noabi/array.cpp component in bsoncxx_testing, which later conflicts with the explicit definition(s) defined by the mongocxx_mocked library. It is unclear why this ODR violation is not detected during linking of the bsoncxx_testing library itself.


Building with mingw-w64 revealed the presence of overlooked export macros applied to inline function definitions, e.g.:

error: function 'mongocxx::v_noabi::pipeline& mongocxx::v_noabi::pipeline::operator=(
const mongocxx::v_noabi::pipeline&)' definition is marked dllimport
   65 |     MONGOCXX_ABI_EXPORT_CDECL(pipeline&) operator=(pipeline const&) = delete;
      |                                          ^~~~~~~~

All such stray export macros are removed by this PR.


mingw does not provide <sys/wait.h>, per docs:

This header file is missing on some platforms: mingw, MSVC 14.

Preprocessor conditions for fork()-related test features are extended from excluding MSVC only (defined(_MSC_VER)) to excluding Windows entirely (defined(_WIN32)).


Following mongodb/mongo-c-driver#2171 (MSVCRT -> UCRT), in theory the compatibility issues with C99 format specifiers should have all been addressed. However, despite the UCRT library documenting support for C99 format specifiers, including %F and %T for std::strftime, mingw does not seem to correctly expose these features in its own front-end:

In function 'void {anonymous}::compare_string(const time_t&, std::string)':
error: unknown conversion type character 'e' in format [-Werror=format=]
      |     REQUIRE(0 != (strftime(time_str, sizeof(time_str), "%b %e, %Y %H:%M:%S UTC", std::gmtime(&t))));
      |                                                        ^~~~~~~~~~~~~~~~~~~~~~~~
note: format string is defined here
      |     REQUIRE(0 != (strftime(time_str, sizeof(time_str), "%b %e, %Y %H:%M:%S UTC", std::gmtime(&t))));
      |                                                             ^

This is not merely a compiler warning problem: the runtime behavior is also unsupported:

std::array<char, sizeof("YYYY-MM-DD")> str = {};
std::tm time = {0}; // 1900-01-00
CHECK(std::strftime(str.data(), str.size(), "%F", &time) == (str.size() - 1u));
CHECK_THAT(str.data(), Catch::Matchers::Equals("1900-01-00"));
FAILED:
  CHECK( std::strftime(str.data(), str.size(), "%F", &time) == (str.size() - 1u) )
with expansion:
  0 == 10

FAILED:
  CHECK_THAT( str.data(), Catch::Matchers::Equals("1900-01-00") )
with expansion:
  "" equals: "1900-01-00"

This unresolved GCC issue may be related, summarized:

it seems like the ["gnu_printf"] attribute is ignored for the "printf" symbol; regardless what the function declaration says, GCC treats it as "ms_printf". This has become an issue now that mingw-w64 supports using the UCRT instead of msvcrt.dll, [...] strftime is another case that's affected. UCRT supports C99 formats (e.g. %F, %T) but GCC warns because of the hardcoded built-in ms_strftime format attribute, even though mingw-w64 has specified gnu_strftime.

Per this comment:

We do use gnu_printf for the UCRT printf functions (which is what MSVC ships since 2015) though, see e.g. mingw-w64/mingw-w64@8565cdb. But since the change in c51f1e7427e6a5ae2a6d82b5a790df77a3adc99a (released in GCC 12 already), we probably don't need this any longer. So I think it might be more correct to revert to ms_printf for UCRT, at least for GCC >= 12 - what do you think?

GCC 12 and newer may be required for correct behavior; however, the currently available mingw-w64 GCC toolchain on windows-2022-latest following DEVPROD-20813 is GCC 10.2.0. Unless the choco winlibs package is updated or DevProd manually installs a newer version (GCC 15.2 is currently available) and we document/enforce minimum mingw-w64 GCC compiler requirements, we will likely need to continue supporting these compatibility issues. Therefore, this PR rewrites the format specifiers as (near-)equivalents (e.g. %F -> %Y-%m-%d).

@eramongodb eramongodb self-assigned this Nov 20, 2025
@eramongodb eramongodb requested a review from a team as a code owner November 20, 2025 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants