Cygwin Support - ratatui_ruby does not build/run on Cygwin w/o manual workarounds

ratatui_ruby does not build/run on Cygwin without manual workarounds

Description

ratatui_ruby cannot be installed from source on Cygwin (x86_64-cygwin) without several manual workarounds — three separate, independent bugs each mask the next until fixed. No precompiled binary gem exists for Cygwin, so every Cygwin user hits the source build path and all three issues. Root causes are in rb_sys (two bugs) and crossterm’s Cygwin input handling (one bug), not ratatui_ruby itself, but they’re only reachable/visible through installing this gem.

Environment: Cygwin (Ruby 4.0.6), rb_sys 0.9.128, rb-sys 0.9.123, crossterm 0.29.0, magnus 0.8.2.

Steps to Reproduce

On a clean Cygwin install with ruby-devel, clang, libclang-devel, cargo/rustc installed:

gem install ratatui_ruby

Expected Results

Gem installs successfully, require 'ratatui_ruby' loads, and RatatuiRuby.run’s interactive event loop (arrow keys, q/Esc to quit) responds normally.

Actual Results

Fails in stages, each only visible after fixing the last:

1. bindgen/clang parse failure

rb-sys’s build script panics generating FFI bindings:

error: 'short __wchar_t' is invalid
thread 'main' panicked at rb-sys-build-0.9.123/build/main.rs:51: generate bindings: ClangDiagnostic(...)

Cygwin’s __wchar_t typedef conflicts with clang’s builtin under default flags.

2. Wrong build-artifact filename (rb_sys bug)

Once (1) is worked around, the build fails with:

cp: cannot stat 'target/release/libratatui_ruby.so'

rb_sys’s CargoBuilder#so_ext (lib/rb_sys/cargo_builder.rb) predicts Cargo’s raw cdylib output filename from RbConfig::CONFIG["SOEXT"], which is "so" on Cygwin — that’s Ruby’s own require-facing convention (true on every platform, including native Windows), not evidence of the underlying binary format. Cargo’s actual output for x86_64-pc-cygwin is ratatui_ruby.dll (no lib prefix, genuine Windows PE format) — verified directly with a throwaway cargo build --release cdylib crate. The win_target? fallback (Gem::WIN_PATTERNS) also excludes Cygwin, since RubyGems treats it as its own platform distinct from mingw/mswin, so there’s no branch that gets this right for Cygwin specifically.

3. Missing Ruby link flag (rb_sys bug)

Once (2) is worked around, the final Rust link step fails:

undefined reference to `rb_protect'
undefined reference to `rb_errinfo'
undefined reference to `rb_cBasicObject'

CargoBuilder#platform_specific_rustc_args only adds Ruby’s linker flags (LIBRUBYARG_SHARED, i.e. -lruby400) when mingw_target? is true — which, like (2), never matches Cygwin.

4. Interactive TUI hangs completely (crossterm bug)

Once the gem actually builds and installs, RatatuiRuby.run’s event loop draws its first frame then never responds to any key (q, arrows, Esc) or Ctrl-C, requiring the process to be killed externally. Reproduces identically in both mintty and Windows Terminal; unrelated to terminal choice. crossterm defaults to reading input via mio’s poll()-based selector on Cygwin (mio groups Cygwin with far-less-exercised targets like Solaris/QNX/Vita, not Linux’s epoll path), and that selector doesn’t reliably notice available input on a Cygwin tty.

Workaround

Full working recipe (env vars + a RUBYOPT-injected monkeypatch for the rb_sys bugs, plus a post-install Cargo.toml edit for the crossterm bug): RatatuiRuby Cygwin Install/Build Instructions

Summary of the pieces:

  • BINDGEN_EXTRA_CLANG_ARGS="-D__wchar_t=__cygwin_wchar_t" works around (1)
  • A RbSys::CargoBuilder#so_ext override returning "dll" on Cygwin works around (2)
  • RUSTFLAGS="-C link-arg=-lruby400" works around (3)
  • Adding a direct crossterm dependency with features = ["use-dev-tty"] to the installed gem’s own Cargo.toml and rebuilding works around (4)