Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ jobs:
ruby generate.rb ../../../prism/config.yml "$RUNNER_TEMP/prism-gen"
diff -r --strip-trailing-cr Generated "$RUNNER_TEMP/prism-gen"

# package-release.sh rebuilds libe_sqlite3.so for the Linux archive (gcc, unzip) and
# checks it with the system's libsqlite3 preloaded, the case SQLitePCL.raw#682 is about.
- name: SQLite for the Linux archive
if: runner.os == 'Linux'
run: sudo apt-get update -qq && sudo apt-get install -y -qq libsqlite3-0 unzip

- name: Package
working-directory: ironruby
run: |
Expand Down
2 changes: 1 addition & 1 deletion CurrentVersion.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<MajorVersion>4</MajorVersion>
<MinorVersion>0</MinorVersion>
<MicroVersion>0</MicroVersion>
<MicroVersion>1</MicroVersion>
<ReleaseLevel>final</ReleaseLevel>
<ReleaseSerial>0</ReleaseSerial>

Expand Down
54 changes: 54 additions & 0 deletions Docs/releases/v4.0.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
**IronRuby 4.0.1** fixes pipes and sockets on macOS, where Puma could not start,
`IO#write_nonblock` on sockets, which cut large responses short, and a SQLite crash on
Linux. All of them were found by [@rubys](https://github.com/rubys) while serving Rails
apps that [Roundhouse](https://github.com/rubys/roundhouse) transpiles to plain Ruby.

### Download

Self-contained: unpack one and run `ir.sh` / `ir.cmd`, `irb`, `igem`. No .NET to install.

- **Windows x64**: `ironruby-4.0.1-win-x64.zip`, with Windows Forms, WPF and
`Samples\Notepad\notepad.cmd`
- **Linux x64**: `ironruby-4.0.1-linux-x64.tar.gz`
- **macOS arm64**: `ironruby-4.0.1-osx-arm64.tar.gz`

`SHA256SUMS` lists the checksums.

### Fixes

- **Pipes to child processes work on macOS** (#12). Backticks, `%x()`, `IO.popen` and
`Open3` failed with `Errno::EINVAL`: IronRuby passed Linux's value of `O_CLOEXEC` to
`pipe2`, and macOS numbers the flag differently. On macOS, `core/io/pipe_spec`,
`core/kernel/backtick_spec` and `library/open3` now pass.
- **Socket addresses on macOS and the BSDs** (#13). Every address the kernel returned
(`getsockname`, `accept`, `recvfrom`) raised `unknown address family: 528`, because a BSD
`sockaddr` starts with a length byte; Puma could not bind. On macOS, ruby/spec
`library/socket` errors fall from 838 to 178.
- **`IO#write_nonblock` on a socket returns what the kernel took** (#14). It claimed the
whole string even when the kernel accepted only part of it, and the rest was lost: Puma
responses larger than the socket's send buffer arrived truncated, on Linux and macOS.
CI now checks that what `write_nonblock` claims is what the peer receives, on Linux and
Windows (#15); Winsock takes a non-blocking send whole or refuses it, so Windows did not
lose bytes.
- **SQLite no longer crashes on Linux once another library has loaded the system SQLite**
([SQLitePCL.raw#682](https://github.com/ericsink/SQLitePCL.raw/issues/682)), libvips
through ruby-vips for one. SQLitePCLRaw's `libe_sqlite3.so` is linked without
`-Bsymbolic`, so its calls into itself could bind to the other copy, and
`SQLite3::Database.new` segfaulted. The Linux archive now carries a `libe_sqlite3.so`
built from the same SQLite 3.53.3 with the same compile options, linked `-Bsymbolic`.
The release build checks that SQLite reports the same version and options from both, and
that the rebuilt one still answers with the system's `libsqlite3` preloaded. Windows and
macOS were not affected.

Linux and Windows behave as in 4.0.0 apart from #14. The full status is in the
[README](https://github.com/Largo/ironruby#status).

### Also new

- [Issues](https://github.com/Largo/ironruby/issues) are open.
- The [launch video](https://github.com/Largo/ironruby/releases/download/v4.0.0/ironruby-4-launch.mp4)
has music now, and plays inline in the README as a teaser.

*Music in the launch video: Edvard Grieg, In the Hall of the Mountain King, played by the
Czech National Symphony Orchestra for Musopen; public domain, from
[classicals.de](https://www.classicals.de/grieg-peer-gynt).*
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ brought back to life: it **builds and runs on .NET 8 and 10**, and it parses Rub
**[prism](https://github.com/ruby/prism), CRuby's own parser**, instead of the hand-ported
Ruby 1.9 grammar it shipped with in 2011.

**IronRuby 4.0.0 is the first stable release since 1.1.3 in 2011.** The downloads are
self-contained: unpack one and run it, there is no .NET to install.
**IronRuby 4.0 is the first stable release since 1.1.3 in 2011**, and 4.0.1 is its latest.
The downloads are self-contained: unpack one and run it, there is no .NET to install.

| | Download | |
|---|---|---|
| **Windows** x64 | [ironruby-4.0.0-win-x64.zip](https://github.com/Largo/ironruby/releases/download/v4.0.0/ironruby-4.0.0-win-x64.zip) | with Windows Forms, WPF and the Notepad sample |
| **Linux** x64 | [ironruby-4.0.0-linux-x64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.0/ironruby-4.0.0-linux-x64.tar.gz) | |
| **macOS** arm64 | [ironruby-4.0.0-osx-arm64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.0/ironruby-4.0.0-osx-arm64.tar.gz) | untested |
| **Windows** x64 | [ironruby-4.0.1-win-x64.zip](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-win-x64.zip) | with Windows Forms, WPF and the Notepad sample |
| **Linux** x64 | [ironruby-4.0.1-linux-x64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-linux-x64.tar.gz) | |
| **macOS** arm64 | [ironruby-4.0.1-osx-arm64.tar.gz](https://github.com/Largo/ironruby/releases/download/v4.0.1/ironruby-4.0.1-osx-arm64.tar.gz) | built in CI, not tested there |

[SHA256SUMS](https://github.com/Largo/ironruby/releases/download/v4.0.0/SHA256SUMS) ·
[release notes](https://github.com/Largo/ironruby/releases/tag/v4.0.0) ·
[SHA256SUMS](https://github.com/Largo/ironruby/releases/download/v4.0.1/SHA256SUMS) ·
[release notes](https://github.com/Largo/ironruby/releases/tag/v4.0.1) ·
[all releases](https://github.com/Largo/ironruby/releases) · [getting started](#binary-releases)

[![IronRuby 4.0 launch video: the title, an irb session, Windows Forms and the Notepad sample](media/launch/teaser.webp)](https://github.com/Largo/ironruby/releases/download/v4.0.0/ironruby-4-launch.mp4)
Expand Down Expand Up @@ -57,7 +57,7 @@ drives it in CI with nobody at the keyboard.*
```console
$ ./irb.sh
irb(main):001> RUBY_DESCRIPTION
=> "IronRuby 4.0.0 (4.0.0) on .NET 8.0.31 [x86_64-linux]"
=> "IronRuby 4.0.1 (4.0.0) on .NET 8.0.31 [x86_64-linux]"
irb(main):002> def fib(n) = n < 2 ? n : fib(n - 1) + fib(n - 2)
=> :fib
irb(main):003> (1..10).map { fib(_1) }
Expand Down Expand Up @@ -113,7 +113,7 @@ releases for that Ruby version (4.0.0, 4.0.1, ...) and is not CRuby's patch leve
release notes say which CRuby release the specs were checked against. The last release of
the original project was 1.1.3, for Ruby 1.9.2, in 2011.

`IRONRUBY_VERSION` is the full version (`4.0.0`, or `4.0.0-preview1` for a pre-release),
`IRONRUBY_VERSION` is the full version (`4.0.1`, or `4.0.0-preview1` for a pre-release),
`RUBY_ENGINE_VERSION` the same without the pre-release suffix, and `RUBY_VERSION` the Ruby
version (`4.0.0`).

Expand Down Expand Up @@ -424,17 +424,18 @@ come from the Windows Desktop runtime, which `ir` references whenever it is buil
(`-p:IronRubyWindowsDesktop=false` leaves it out); on Linux and macOS they do not exist.
[`Samples/Notepad`](Samples/Notepad/notepad.rb) is Windows Notepad written that way, in Ruby
(`Samples\Notepad\notepad.cmd`); its test drives it with nobody at the keyboard and runs in CI.
macOS is untested.
macOS (arm64) is built by the release workflow but not tested in CI; pipes to child
processes, socket addresses and Puma were fixed there in 4.0.1 (#12, #13).

## Binary releases

[Releases](https://github.com/Largo/ironruby/releases) carry a ready-to-run IronRuby for
Linux x64, Windows x64 and macOS arm64 (untested); the [table at the top](#ironruby-modernized)
links the ones for 4.0.0. They are self-contained, so no .NET has to be installed. Unpack
Linux x64, Windows x64 and macOS arm64 (not tested in CI); the [table at the top](#ironruby-modernized)
links the ones for 4.0.1. They are self-contained, so no .NET has to be installed. Unpack
one and run the launchers in it:

```console
$ tar xzf ironruby-4.0.0-linux-x64.tar.gz && cd ironruby-4.0.0-linux-x64
$ tar xzf ironruby-4.0.1-linux-x64.tar.gz && cd ironruby-4.0.1-linux-x64
$ ./ir.sh -e 'puts RUBY_DESCRIPTION'
$ ./irb.sh
$ ./igem.sh install rack
Expand Down Expand Up @@ -508,7 +509,7 @@ Windows machine, keeping the same relative layout (`ir.cmd` finds the binaries u
```
C:\ir> set IR_CONFIG=Release
C:\ir> ir.cmd -e "puts RUBY_DESCRIPTION"
IronRuby 4.0.0 (4.0.0) on .NET 8.0.31 [x64-mswin64]
IronRuby 4.0.1 (4.0.0) on .NET 8.0.31 [x64-mswin64]
C:\ir> irb.cmd
C:\ir> set RUBY_EXE=C:\ir\ir.cmd
C:\ir> ir.cmd -Imspec/lib mspec/bin/mspec-run spec/language
Expand Down
12 changes: 7 additions & 5 deletions Src/Ruby/CurrentVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@ namespace IronRuby {
public static class CurrentVersion {
public const int Major = 4;
public const int Minor = 0;
public const int Micro = 0;
public const int Micro = 1;
public const string ReleaseLevel = "final";
public const int ReleaseSerial = 0;

public const string ShortReleaseLevel = "final";

public const string Series = "4.0";
public const string DisplayVersion = "4.0.0";
public const string DisplayName = "IronRuby 4.0.0";
public const string DisplayVersion = "4.0.1";
public const string DisplayName = "IronRuby 4.0.1";

// Stays 4.0.0.0 across 4.0.x, so that what was built against one 4.0 release binds
// to the next; the file version and the display version say which release it is.
public const string AssemblyVersion = "4.0.0.0";

public const string AssemblyFileVersion = "4.0.0.0";
public const string AssemblyInformationalVersion = "IronRuby 4.0.0";
public const string AssemblyFileVersion = "4.0.1.0";
public const string AssemblyInformationalVersion = "IronRuby 4.0.1";

public static readonly Version Version = new Version(Major, Minor, Micro);
}
Expand Down
69 changes: 69 additions & 0 deletions Util/build-e_sqlite3.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
# Rebuilds the libe_sqlite3.so that SQLitePCLRaw ships for linux-x64, linked -Bsymbolic:
#
# Util/build-e_sqlite3.sh <dir holding libe_sqlite3.so>
#
# SQLitePCLRaw's libe_sqlite3.so exports SQLite's whole API and is linked without
# -Bsymbolic, so its calls to its own exported functions go through the PLT. Once another
# SQLite is in the global symbol scope - a plugin loaded RTLD_GLOBAL, libvips's
# vips-openslide for one, or LD_PRELOAD - those calls bind to that copy: it initializes
# itself, the bundled copy's allocator stays empty, and the first sqlite3_open_v2
# segfaults (https://github.com/ericsink/SQLitePCL.raw/issues/682).
#
# This builds the same SQLite (3.53.3, the one SQLitePCLRaw.lib.e_sqlite3 2.1.13 carries)
# with the same compile options, the way SQLitePCLRaw builds it (ericsink/cb, bld/cb.cs:
# gcc -m64 -msse4.2 -maes -shared -fPIC -O), plus -Wl,-Bsymbolic. package-release.sh
# checks that SQLite reports the same version, source id and compile options before and
# after the swap. Windows and macOS do not bind symbols this way and keep the original.
set -euo pipefail

BIN=${1:?usage: Util/build-e_sqlite3.sh <dir holding libe_sqlite3.so>}
VERSION=3530300 # 3.53.3
URL="https://sqlite.org/2026/sqlite-amalgamation-$VERSION.zip"
SHA256='646421e12aac110282ef8cc68f1a62d4bb15fc7b8f09da0b53e29ee690500431' # of the zip
SOURCE_ID='2026-06-26 20:14:12 d4c0e51e4aeb96955b99185ab9cde75c339e2c29c3f3f12428d364a10d782c62'

# SQLitePCLRaw's set (bld/cb.cs, add_basic_sqlite3_defines and add_linux_sqlite3_defines),
# and what PRAGMA compile_options shows the 2.1.13 build has beyond it.
DEFINES=(
-DSQLITE_DEFAULT_FOREIGN_KEYS=1
-DSQLITE_DQS=0
-DSQLITE_ENABLE_COLUMN_METADATA
-DSQLITE_ENABLE_FTS3_PARENTHESIS
-DSQLITE_ENABLE_FTS4
-DSQLITE_ENABLE_FTS5
-DSQLITE_ENABLE_GEOPOLY
-DSQLITE_ENABLE_JSON1
-DSQLITE_ENABLE_MATH_FUNCTIONS
-DSQLITE_ENABLE_PREUPDATE_HOOK
-DSQLITE_ENABLE_RTREE
-DSQLITE_ENABLE_SESSION
-DSQLITE_ENABLE_SNAPSHOT
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS
-DSQLITE_OS_UNIX
-DNDEBUG
)

[ -f "$BIN/libe_sqlite3.so" ] || { echo "build-e_sqlite3.sh: no libe_sqlite3.so in $BIN" >&2; exit 1; }
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT

curl -fsSL --retry 3 -o "$tmp/sqlite.zip" "$URL"
actual=$(sha256sum "$tmp/sqlite.zip" | cut -d' ' -f1)
if [ -z "$SHA256" ]; then
echo "build-e_sqlite3.sh: no checksum pinned yet; this download is sha256 $actual" >&2
elif [ "$actual" != "$SHA256" ]; then
echo "build-e_sqlite3.sh: the download is sha256 $actual, not the pinned $SHA256" >&2
exit 1
fi
unzip -q -j "$tmp/sqlite.zip" "sqlite-amalgamation-$VERSION/sqlite3.c" -d "$tmp"
if ! grep -qF "\"$SOURCE_ID\"" "$tmp/sqlite3.c"; then
echo "build-e_sqlite3.sh: sqlite3.c is not SQLite check-in $SOURCE_ID" >&2
exit 1
fi

gcc -m64 -msse4.2 -maes -shared -fPIC -O "${DEFINES[@]}" \
-o "$tmp/libe_sqlite3.so" "$tmp/sqlite3.c" -lm -Wl,-Bsymbolic
readelf -d "$tmp/libe_sqlite3.so" | grep -q SYMBOLIC
cp "$tmp/libe_sqlite3.so" "$BIN/libe_sqlite3.so"
echo "build-e_sqlite3.sh: $BIN/libe_sqlite3.so is SQLite 3.53.3, linked -Bsymbolic"
35 changes: 35 additions & 0 deletions Util/package-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,41 @@ grep -oE '"(name|version)": *"[^"]*"' "$STAGE/Src/Console/bin/Release/$IR_TFM/ir
fi
done

# Linux: SQLitePCLRaw's libe_sqlite3.so crashes once another SQLite is in the global
# symbol scope (SQLitePCL.raw#682), so the archive carries one rebuilt -Bsymbolic
# (Util/build-e_sqlite3.sh). SQLite must report the same version, source id and compile
# options from both, and the rebuilt one must survive the system's libsqlite3 preloaded,
# which is how the issue reproduces it.
case "$RID" in
linux-*)
bin="$STAGE/Src/Console/bin/Release/$IR_TFM"
check="$IR_ROOT/Util/sqlite-options.rb"
report=$(mktemp -d)
system_sqlite=$(ldconfig -p 2>/dev/null | awk '/libsqlite3\.so\.0 /{print $NF; exit}')
"$STAGE/ir.sh" "$check" > "$report/shipped.txt"
if [ -n "$system_sqlite" ]; then
if LD_PRELOAD="$system_sqlite" "$STAGE/ir.sh" "$check" > /dev/null 2>&1; then
echo "package-release.sh: the shipped libe_sqlite3.so survives $system_sqlite preloaded"
else
echo "package-release.sh: the shipped libe_sqlite3.so fails with $system_sqlite preloaded"
fi
fi
bash "$IR_ROOT/Util/build-e_sqlite3.sh" "$bin"
"$STAGE/ir.sh" "$check" > "$report/rebuilt.txt"
diff "$report/shipped.txt" "$report/rebuilt.txt"
if [ -n "$system_sqlite" ]; then
# Not only no crash: it must still be the bundled SQLite answering, not the system's.
LD_PRELOAD="$system_sqlite" "$STAGE/ir.sh" "$check" > "$report/preloaded.txt"
diff "$report/rebuilt.txt" "$report/preloaded.txt"
echo "package-release.sh: the rebuilt libe_sqlite3.so survives $system_sqlite preloaded," \
"and answers as $(head -1 "$report/rebuilt.txt")"
else
echo "package-release.sh: no system libsqlite3.so.0 here to preload against the rebuilt one" >&2
fi
rm -rf "$report"
;;
esac

# A quick check that the unpacked tree starts, before it is archived - and on Windows
# that Windows Forms and WPF load from it, which a missing framework reference breaks
# without breaking anything else.
Expand Down
10 changes: 10 additions & 0 deletions Util/sqlite-options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# The SQLite IronRuby's sqlite3 library loads: version, source id and compile options,
# one per line and sorted, so two builds can be compared with diff. The compiler is left
# out, as a rebuild names its own. package-release.sh runs this before and after it
# swaps in Util/build-e_sqlite3.sh's libe_sqlite3.so on Linux.
require "sqlite3"

db = SQLite3::Database.new(":memory:")
puts "sqlite_version=#{db.get_first_value('select sqlite_version()')}"
puts "source_id=#{db.get_first_value('select sqlite_source_id()')}"
puts db.execute("PRAGMA compile_options").flatten.reject { it.start_with?("COMPILER=") }.sort
Loading