Slide 1

Slide 1 text

Keeping Ruby Running on Cygwin Daisuke Fujimura 2026-04-23 1

Slide 2

Slide 2 text

˒ Daisuke Fujimura ˒ Maintainer of the ruby binary package on cygwin (Not a Ruby commi tt er) ˒ Interested in software packaging, build systems, and platform engineering About Me 2

Slide 3

Slide 3 text

˒ Background ˒ Packaging Journey ˒ Upstream Contributions ˒ Looking Back / Looking Forward Outline 3

Slide 4

Slide 4 text

What is Cygwin? 4

Slide 5

Slide 5 text

˒ A large collection of GNU and Open Source tools which provide functionality similar to a Linux distribution on Windows ˒ A DLL (cygwin1.dll) which provides substantial POSIX API functionality Cygwin is... 5 h tt ps://cygwin.com/

Slide 6

Slide 6 text

˒ WSL has taken over many use cases ˒ But Cygwin still has demand ˒ Native Windows interop ˒ Legacy work fl ows Cygwin in the WSL Era 6

Slide 7

Slide 7 text

˒ O ff icial pre-compiled binary packages ˒ Distributed like rpm or deb ˒ Via the Cygwin installer; ruby.exe ready ˒ Maintained by me Ruby on Cygwin 7

Slide 8

Slide 8 text

Cygwin Installer 8

Slide 9

Slide 9 text

ruby.exe (x86_64-cygwin) ˒ x86_64-cygwin: identifying this platform 9

Slide 10

Slide 10 text

˒ Works like Linux ˒ Unix-style paths (e.g. /home, not C:\Users) ˒ Many POSIX APIs work (e.g. Process.fork) ˒ ...and more Di ff erent from Windows Ruby 10

Slide 11

Slide 11 text

˒ Each package has a volunteer maintainer ˒ Step away → package becomes orphaned ˒ Orphaned = no active maintainer ˒ Ruby went orphaned after 2.6 ˒ Ruby commi tt ers had no Cygwin maintainer either [1] The Orphaned Package 11 [1] h tt ps://github.com/ruby/ruby/blob/master/doc/maintainers.md

Slide 12

Slide 12 text

No one to ask for help 12

Slide 13

Slide 13 text

Wait — am I possibly the most active Ruby-on-Cygwin person in the world? 13

Slide 14

Slide 14 text

˒ My interests all converge here ˒ Long-time Cygwin user ˒ Into language runtimes and libraries ˒ Love Linux packaging (e.g. rpm, deb,...) Motivation: Why I started maintaining Ruby on Cygwin (1) 14

Slide 15

Slide 15 text

˒ The outdated Ruby was a real problem ˒ Security risk ˒ Missing new features Motivation: Why I started maintaining Ruby on Cygwin (2) 15

Slide 16

Slide 16 text

˒ KaigiE ff ect ˒ First a tt ended RubyKaigi 2018 ˒ Blew my mind; never missed one since Motivation: Why I started maintaining Ruby on Cygwin (3) 16

Slide 17

Slide 17 text

Release History 17 Version Original Release Cygwin Package Release EOL 2.6.4 2019-08-28 2019-09-16 2022-04-12 3.2.2 2023-03-30 2023-04-28 2026-03-31 3.4.7 2025-10-07 2025-10-17 TBD h tt ps://cygwin.com/packages/summary/ruby.html

Slide 18

Slide 18 text

Packaging Journey 18

Slide 19

Slide 19 text

˒ No handover docs: the repository was the only guide ˒ Let's look at what's in there Climbing from 2.6 to 3.2 19

Slide 20

Slide 20 text

˒ Cygport: Cygwin's build system ˒ Rules wri tt en in bash syntax in a .cygport fi le Repository Tree (1) 20 h tt ps://cygwin.com/cgit/cygwin-packages/ruby/

Slide 21

Slide 21 text

˒ Some parts of Ruby assume only major platforms; patches make them work correctly on Cygwin Repository Tree (2) 21 h tt ps://cygwin.com/cgit/cygwin-packages/ruby/

Slide 22

Slide 22 text

˒ Some patches are sourced from Fedora for reliability and license compatibility Repository Tree (3) 22 h tt ps://cygwin.com/cgit/cygwin-packages/ruby/

Slide 23

Slide 23 text

˒ Opt out of arch-speci fi c bindir (e.g. /usr/libexec/x86_64-linux/ bin) Patch Example 23 h tt ps://cygwin.com/cgit/cygwin-packages/ruby/tree/3.4.2-rbinstall-archbindir.patch

Slide 24

Slide 24 text

˒ $ cygport *.cygport Building with cygport 24 fetch prep compile install package

Slide 25

Slide 25 text

ruby.cygport — Metadata 25 ˒ Declared as bash variables: name, version, source URLs, patch locations, etc.

Slide 26

Slide 26 text

˒ Build steps (compile, install, etc.) are de fi ned by overriding bash functions ˒ Ruby uses GNU Autotools; con fi gure options are speci fi ed in src_compile() ruby.cygport — Build Functions 26

Slide 27

Slide 27 text

Package Layout (1) 📃 ruby.cygport (3.2.2) 📦 ruby (3.2.2) 📦 ruby-devel (3.2.2) 📦 rubygems (3.4.10) 📦 ruby-rdoc (6.5.0) $ cygport ruby.cygport package 27 X X (3.2.2) (3.2.2)

Slide 28

Slide 28 text

Package Layout (2) 📃 rubygems.cygport (3.4.10) 📦 rubygems (3.4.10) 📦 ruby-rdoc (6.5.0) 📃 ruby-rdoc.cygport (6.5.0) $ cygport rubygems.cygport package $ cygport ruby-rdoc.cygport package 28 📃 ruby.cygport (3.2.2) 📦 ruby (3.2.2) $ cygport ruby.cygport package

Slide 29

Slide 29 text

Package Layout (3) 📦 ruby (3.2.2) /usr/bin/ruby.exe /usr/bin/gem /usr/bin/rdoc : 📦 rubygems (3.4.10) /usr/bin/gem : 📦 ruby-rdoc (6.5.0) /usr/bin/rdoc : Con fl ict Con fl ict 29

Slide 30

Slide 30 text

ruby.cygport — install 📃 ruby.cygport (3.2.2) 📦 ruby (3.2.2) 30 Drop gem, rdoc, ...

Slide 31

Slide 31 text

ABI Info Missing (1) 📦 ruby-bcrypt (3.1.20) 📦 ruby (2.6.4) 📦 ruby (3.2.2) Wants to install Which ABI? ˒ New Ruby → ABI changes → gems must be rebuilt ˒ The installer had no way to know which Ruby ABI each gem was built for ˒ → Unable to resolve dependencies 31

Slide 32

Slide 32 text

˒ Hint fi les (generated per package build) → aggregated into setup.ini ABI Info Missing (2) 📃 ruby-bcrypt.hint 📃 setup.ini 📃 ruby-devel.hint : 📃 ruby.hint $ mksetupini 32

Slide 33

Slide 33 text

setup.ini 33 ˒ depends2 includes ruby_xy, the Ruby x.y ABI marker

Slide 34

Slide 34 text

˒ O ff icial CI/CD takes it from here ˒ GitHub Actions: build, upload, update setup.ini, announce ˒ Users get the latest Ruby. happy ending! Ge tt ing It Out the Door 34

Slide 35

Slide 35 text

˒ 3.3 never shipped: a SEGV with unknown cause blocked the release ˒ gdb investigation, but hard to use e ff ectively ˒ With AI's step-by-step guidance, narrowed down the cause Climbing Again: 3.2 to 3.4 — Debugging with AI 35

Slide 36

Slide 36 text

˒ Root cause: SEGV inside pthread, triggered by libssl 3 ˒ 3.2 was fi ne: linked against libssl 1.1 ˒ Shipped with 3.4.7, after libssl resolved the issue ˒Lesson: sometimes the bug is outside Ruby Climbing Again: 3.2 to 3.4 — Root Cause & Resolution 36

Slide 37

Slide 37 text

Upstream Contributions 37

Slide 38

Slide 38 text

˒ My own (uno ff icial) CI pipeline ˒ Tracks Ruby HEAD ˒ Fix issues upstream, reduce local patches ˒ Led to several upstream PRs ˒ #9269 (#9357), #11952, #14870 Running CI Myself — and What I Found 38

Slide 39

Slide 39 text

#9269: Use native_thread_init_stack 39

Slide 40

Slide 40 text

˒ test_ractor.rb, test_thread.rb → SEGV on Cygwin ˒ Grepping #if de fi ned(__CYGWIN__) ˒ native_thread_init_stack() was skipped ˒ Tried removing the exclusion → SEGV was gone ˒ But why? 🤔 ˒ Kept digging to fi nd the reason #9269: Use native_thread_init_stack — Problem 40

Slide 41

Slide 41 text

˒ Found commit in 2010 [1] ˒ Incomplete signal stack → exclusion was justi fi ed ˒ Cygwin fi xed signal stack in 2015 [2] ˒ Code outlived the reason #9269: Use native_thread_init_stack — Investigation 41 [1] h tt ps://github.com/ruby/ruby/commit/6957c6d9cf0b6a1e211c4b0175bc5474296f2031 [2] h tt ps://github.com/cygwin/cygwin/commit/c8432a01c8401c121940c806a9d868c4adc4cefd

Slide 42

Slide 42 text

˒ Removing the macro and branch #9269: Use native_thread_init_stack — Fix 42

Slide 43

Slide 43 text

˒ make test on Cygwin passing again after ~10 years ˒ That's win! #9357: Use ubf list 43

Slide 44

Slide 44 text

#11952: Avoid dangling pointer 44

Slide 45

Slide 45 text

˒ A dangling pointer is a pointer to memory that is no longer valid ˒ GCC's -Wdangling-pointer caught one in dln.c ˒ But only on Cygwin #11952: Avoid dangling pointer — Problem: Warning 45

Slide 46

Slide 46 text

˒ dladdr(): looks up which library a given address belongs to ˒ Fills a Dl_info struct with the result ˒ dli_fname member holds the library fi le path ˒ libname holds dli_fname... #11952: Avoid dangling pointer — Problem: The Code 46

Slide 47

Slide 47 text

#11952: Avoid dangling pointer — Investigation: Type Di ff erence 47 Linux [1] Cygwin [2] [1] h tt ps://sourceware.org/git/?p=glibc.git;a=blob;f=dlfcn/dlfcn.h [2] h tt ps://github.com/cygwin/cygwin/blob/main/winsup/cygwin/include/dlfcn.h

Slide 48

Slide 48 text

˒ Why the type di ff erence? ˒ dladdr() standardized in POSIX Issue 8 (2024) ˒ Cygwin: 2017 [1] ˒ Each platform de fi ned types independently #11952: Avoid dangling pointer — Investigation: Background 48 [1] h tt ps://github.com/cygwin/cygwin/commit/c8432a01c8401c121940c806a9d868c4adc4cefd

Slide 49

Slide 49 text

˒ libname is only used in error messages; skipping is su ff icient #11952: Avoid dangling pointer — Fix 49

Slide 50

Slide 50 text

#14870: Fix extension fi le permissions in namespace feature 50

Slide 51

Slide 51 text

˒ Tried namespace (box) locally ˒ Native extension .so → LoadError #14870: Fix extension fi le permissions in namespace feature — Problem 51

Slide 52

Slide 52 text

˒ fwrite copy → dlopen #14870: Fix extension fi le permissions in namespace feature — Investigation: Copy 52

Slide 53

Slide 53 text

˒ Cygwin: copied .so permissions come from Windows ACL ˒ The executable bit was not set on the copy #14870: Fix extension fi le permissions in namespace feature — Investigation: Permission 53 -rwxr-xr-x openssl.so -rw-r--r-- openssl.so Copy by fwrite

Slide 54

Slide 54 text

˒ dlopen internally calls Windows LoadLibraryW ˒ No Executable in ACL → ERROR_ACCESS_DENIED ˒ Not o ff icially documented, but widely known behavior #14870: Fix extension fi le permissions in namespace feature — Investigation: Implementation 54

Slide 55

Slide 55 text

˒ Preserve permissions after fwrite copy #14870: Fix extension fi le permissions in namespace feature — Fix 55

Slide 56

Slide 56 text

#12051: Try to build with Cygwin 56

Slide 57

Slide 57 text

˒ My CI caught a compile failure on Cygwin ˒ -municode (MinGW-w64-only) accidentally applied to Cygwin ˒ Fixed by #12015 Cygwin Joins the O ff icial CI Matrix (1) 57

Slide 58

Slide 58 text

˒ To prevent this class of breakage from landing again ˒ Now every commit is veri fi ed to build on Cygwin Cygwin Joins the O ff icial CI Matrix (2) 58

Slide 59

Slide 59 text

Looking Back / Looking Forward 59

Slide 60

Slide 60 text

˒ Upgraded Ruby on Cygwin from 2.6 to 3.4 ˒ Deepened understanding of the packaging system ˒ Encountered a new debugging experience ˒ A dependency-blocked release tackled with AI Looking Back — Packaging Journey 60

Slide 61

Slide 61 text

˒ Niche bugs that only surface on Cygwin ˒ Hard to see without downstream involvement ˒ Cygwin is now in the o ff icial CI matrix Looking Back — Upstream Contributions 61

Slide 62

Slide 62 text

˒ All of this contributes to Ruby's "Highly Portable" goal ˒ Covering underserved platforms helps grow the Ruby community Looking Forward 62

Slide 63

Slide 63 text

Ruby is still running on Cygwin 63

Slide 64

Slide 64 text

O ff icial Cygwin package for Ruby 4.0? 64

Slide 65

Slide 65 text

🙇 Sorry! 65