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
˒ 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
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