From dce0d24702dff4c9b82cab315757eb0efb7a87b0 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Tue, 11 Aug 2026 20:01:27 -0700 Subject: [PATCH] Fix google-protobuf gem source build on the mswin platform (#28433) `gem install google-protobuf --platform ruby` fails on Windows with the native mswin toolchain (`x64-mswin64_140`, `cl.exe` and `nmake`). `Init_protobuf_c` in `protobuf.c` is annotated with the GCC-style `__attribute__((visibility("default")))`, which `cl.exe` cannot parse and rejects with a cascade of C2143/C2091 syntax errors. In addition, `extconf.rb` passes GCC-only flags (`-std=gnu99`, `-O3`, `-fvisibility=hidden`) that `cl.exe` does not understand. This change replaces the attribute with Ruby's portable `RUBY_FUNC_EXPORTED` macro, which expands to `extern __declspec(dllexport)` under MSVC and to `__attribute__((__visibility__("default"))) extern` under GCC/Clang, so the symbol stays exported when the extension is built with `-fvisibility=hidden`. It also gives mswin its own `$CFLAGS` branch. `-std:c11` is required there because the MSVC atomics fallback in upb only uses its `_Generic` based implementation when `__STDC_VERSION__` is at least 201112L. The pre-C11 fallback returns `void*` from `upb_Atomic_Load`, and mswin Ruby compiles extensions with `-we4047` by default, which turns those conversion warnings into hard errors. Verified on Windows 11 with Ruby 4.0.5 (`x64-mswin64_140`) and Visual Studio 2022 Build Tools (MSVC 14.51). Building the extension in-tree succeeds, and a gem built from the released 4.35.1 sources with this patch applied installs successfully and passes a `Struct` and `Timestamp` encode/decode round-trip. Precompiled `x64-mingw-ucrt` gems are unaffected because MinGW keeps using the existing GCC branch. Generated with [Claude Code](https://claude.com/claude-code) Closes #28433 COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/28433 from hsbt:claude/elated-goldwasser-3862b4 7d533bab6c059e8818e18bb62fa2a839c2458bc2 PiperOrigin-RevId: 963158243 --- ruby/ext/google/protobuf_c/extconf.rb | 7 ++++++- ruby/ext/google/protobuf_c/protobuf.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ruby/ext/google/protobuf_c/extconf.rb b/ruby/ext/google/protobuf_c/extconf.rb index f97b9b5b25..5a63119e02 100755 --- a/ruby/ext/google/protobuf_c/extconf.rb +++ b/ruby/ext/google/protobuf_c/extconf.rb @@ -22,7 +22,12 @@ debug_enabled = ENV["PROTOBUF_CONFIG"] == "dbg" additional_c_flags = debug_enabled ? "-O0 -fno-omit-frame-pointer -fvisibility=default -g" : "-O3 -DNDEBUG -fvisibility=hidden" -if RUBY_PLATFORM =~ /darwin/ || RUBY_PLATFORM =~ /linux/ || RUBY_PLATFORM =~ /freebsd/ +if RUBY_PLATFORM =~ /mswin/ + # cl.exe does not understand GCC-style flags. C11 or later is required + # for _Generic, which the upb MSVC atomics fall back on. c17 is the + # highest C standard mode cl.exe supports. + $CFLAGS += debug_enabled ? " -std:c17 -Od" : " -std:c17 -DNDEBUG" +elsif RUBY_PLATFORM =~ /darwin/ || RUBY_PLATFORM =~ /linux/ || RUBY_PLATFORM =~ /freebsd/ $CFLAGS += " -std=gnu99 -Wall -Wsign-compare -Wno-declaration-after-statement #{additional_c_flags}" else $CFLAGS += " -std=gnu99 #{additional_c_flags}" diff --git a/ruby/ext/google/protobuf_c/protobuf.c b/ruby/ext/google/protobuf_c/protobuf.c index aff8fab38a..5c8f046d3c 100644 --- a/ruby/ext/google/protobuf_c/protobuf.c +++ b/ruby/ext/google/protobuf_c/protobuf.c @@ -379,7 +379,7 @@ static VALUE Google_Protobuf_Internal_allocation_count_fail_on(VALUE self, // This must be named "Init_protobuf_c" because the Ruby module is named // "protobuf_c" -- the VM looks for this symbol in our .so. -__attribute__((visibility("default"))) void Init_protobuf_c() { +RUBY_FUNC_EXPORTED void Init_protobuf_c() { VALUE google = rb_define_module("Google"); VALUE protobuf = rb_define_module_under(google, "Protobuf");