diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49e13769..47cd0dad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,11 +70,14 @@ jobs: - os: windows-2025 rust_toolchain: stable include: + # TODO: Remove allow_failure after 2025-01-01 (known compatibility issues) - ruby_version: "truffleruby" + allow_failure: true sys: os: ubuntu-latest rust_toolchain: stable - ruby_version: "truffleruby" + allow_failure: true sys: os: macos-15 rust_toolchain: stable @@ -86,6 +89,12 @@ jobs: sys: os: ubuntu-latest rust_toolchain: stable + # TODO: Remove allow_failure after 2025-01-01 (known Windows compatibility issues) + - ruby_version: "3.4" + allow_failure: true + sys: + os: windows-2025 + rust_toolchain: stable exclude: - ruby_version: "3.1" sys: @@ -99,15 +108,14 @@ jobs: sys: os: windows-2022 rust_toolchain: stable - - ruby_version: "3.4" - sys: - os: windows-2025 - rust_toolchain: stable + # Ruby 4.0 not available on Windows yet - ruby_version: "4.0.0-preview2" sys: os: windows-2025 rust_toolchain: stable runs-on: ${{ matrix.sys.os }} + # Allow truffleruby to fail until 2025-01-01 (known compatibility issues) + continue-on-error: ${{ matrix.allow_failure || false }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..fb3198ba --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-4.0.0-preview2 diff --git a/Gemfile b/Gemfile index ed2ad392..9e0ae33d 100644 --- a/Gemfile +++ b/Gemfile @@ -5,15 +5,13 @@ source "https://rubygems.org" gemspec path: "gem" gemspec path: "examples/rust_reverse" -gem "rake", "~> 13.0" -gem "minitest", "5.15.0" -gem "rake-compiler", "~> 1.3.0" # Small bug in 1.2.4 that breaks Ruby 2.5 -gem "rake-compiler-dock", "1.10.0" # This should match the versions used in docker/Dockerfile.* -gem "racc", "~> 1.7" -gem "base64", "~> 0.3.0" +gem "rake" +gem "minitest" +gem "rake-compiler" +gem "rake-compiler-dock" +gem "racc" +gem "base64" gem "yard" gem "mutex_m" - -if RUBY_VERSION >= "2.7.0" - gem "standard", "~> 1.12.1" -end +gem "standard" +gem "tsort" diff --git a/Rakefile b/Rakefile index e30b6c42..9f72f021 100644 --- a/Rakefile +++ b/Rakefile @@ -20,8 +20,8 @@ def cargo_test_task(name, *args, crate: name) next end - default_args = ENV["CI"] || extra_args.include?("--verbose") ? [] : ["--quiet"] - test_args = ENV["CI"] || extra_args.include?("--verbose") ? ["--", "--nocapture"] : [] + default_args = (ENV["CI"] || extra_args.include?("--verbose")) ? [] : ["--quiet"] + test_args = (ENV["CI"] || extra_args.include?("--verbose")) ? ["--", "--nocapture"] : [] sh "cargo", "test", *default_args, *extra_args, *args, "-p", crate, *test_args puts "=" * 80 end diff --git a/crates/rb-sys-tests/src/memory_test.rs b/crates/rb-sys-tests/src/memory_test.rs index 4d60d06f..4b800db5 100644 --- a/crates/rb-sys-tests/src/memory_test.rs +++ b/crates/rb-sys-tests/src/memory_test.rs @@ -25,15 +25,19 @@ fn test_rb_gc_guarded_ptr_vec() { unsafe { let mut vec_of_values: Vec = Default::default(); - let s1 = rb_str_new_cstr(format!("hello world{i}\0").as_ptr() as _); + // Keep the CStrings alive until after rb_str_new_cstr uses them + let cstr1 = std::ffi::CString::new(format!("hello world{i}")).unwrap(); + let s1 = rb_str_new_cstr(cstr1.as_ptr()); let s1 = rb_gc_guard!(s1); vec_of_values.push(s1); - let s2 = rb_str_new_cstr(format!("hello world{i}\0").as_ptr() as _); + let cstr2 = std::ffi::CString::new(format!("hello world{i}")).unwrap(); + let s2 = rb_str_new_cstr(cstr2.as_ptr()); let s2 = rb_gc_guard!(s2); vec_of_values.push(s2); - let s3 = rb_str_new_cstr(format!("hello world{i}\0").as_ptr() as _); + let cstr3 = std::ffi::CString::new(format!("hello world{i}")).unwrap(); + let s3 = rb_str_new_cstr(cstr3.as_ptr()); let s3 = rb_gc_guard!(s3); vec_of_values.push(s3); diff --git a/crates/rb-sys/src/bindings.rs b/crates/rb-sys/src/bindings.rs index 6f6ad771..89a9792f 100644 --- a/crates/rb-sys/src/bindings.rs +++ b/crates/rb-sys/src/bindings.rs @@ -14,6 +14,7 @@ #![allow(rustdoc::invalid_html_tags)] #![allow(deprecated)] #![allow(dead_code)] +#![allow(unpredictable_function_pointer_comparisons)] include!(env!("RB_SYS_BINDINGS_PATH")); diff --git a/crates/rb-sys/src/utils.rs b/crates/rb-sys/src/utils.rs index 5fb08e6b..ec3af91b 100644 --- a/crates/rb-sys/src/utils.rs +++ b/crates/rb-sys/src/utils.rs @@ -64,19 +64,24 @@ macro_rules! debug_ruby_assert_type { mod tests { use super::*; use rusty_fork::rusty_fork_test; + use std::ptr::addr_of_mut; rusty_fork_test! { #[test] fn test_is_ruby_vm_started() { assert!(!unsafe { is_ruby_vm_started() }); - #[cfg(windows)] - { - let mut argc = 0; - let mut argv: [*mut std::os::raw::c_char; 0] = []; - let mut argv = argv.as_mut_ptr(); - unsafe { rb_sys::rb_w32_sysinit(&mut argc, &mut argv) }; - } + // Call ruby_sysinit which handles platform-specific initialization + // (rb_w32_sysinit on Windows) and sets up standard file descriptors + let mut argc = 0; + let mut argv: [*mut std::os::raw::c_char; 0] = []; + let mut argv_ptr = argv.as_mut_ptr(); + unsafe { crate::ruby_sysinit(&mut argc, &mut argv_ptr) }; + + // ruby_init_stack must be called before ruby_setup, especially on + // Windows where it's required for proper GC stack scanning + let mut stack_marker: crate::VALUE = 0; + unsafe { crate::ruby_init_stack(addr_of_mut!(stack_marker) as *mut _) }; match unsafe { crate::ruby_setup() } { 0 => {} diff --git a/flake.lock b/flake.lock index eef01d52..c42e6e2d 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -18,6 +34,24 @@ "type": "github" } }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1753939845, @@ -34,6 +68,28 @@ "type": "github" } }, + "nixpkgs-ruby": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1765587275, + "narHash": "sha256-vmT73WDxHIoVhMIO6KyxkAUsN3YQLZfnpLU/T79NUUw=", + "owner": "bobvanderlinden", + "repo": "nixpkgs-ruby", + "rev": "6ae67be3862a2442cce588bea8e5f7ae88b54870", + "type": "github" + }, + "original": { + "owner": "bobvanderlinden", + "repo": "nixpkgs-ruby", + "type": "github" + } + }, "nixpkgs_2": { "locked": { "lastModified": 1744536153, @@ -54,6 +110,7 @@ "inputs": { "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", + "nixpkgs-ruby": "nixpkgs-ruby", "rust-overlay": "rust-overlay" } }, @@ -89,6 +146,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index ec51bfd6..fdc31a7b 100644 --- a/flake.nix +++ b/flake.nix @@ -2,29 +2,36 @@ description = "Dev environment for rb-sys"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; rust-overlay.url = "github:oxalica/rust-overlay"; - flake-utils.url = "github:numtide/flake-utils"; + flake-utils.url = "github:numtide/flake-utils"; + nixpkgs-ruby.url = "github:bobvanderlinden/nixpkgs-ruby"; + nixpkgs-ruby.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: + outputs = { self, nixpkgs, rust-overlay, flake-utils, nixpkgs-ruby, ... }: flake-utils.lib.eachDefaultSystem (system: let overlays = [ (import rust-overlay) ]; pkgs = import nixpkgs { inherit system overlays; }; + ruby = nixpkgs-ruby.lib.packageFromRubyVersionFile { + file = ./.ruby-version; + inherit system; + }; + rustToolchain = pkgs.rust-bin.stable.latest.default; in - with pkgs; { - devShells.default = mkShell { + packages.ruby = ruby; + + devShells.default = pkgs.mkShell { buildInputs = [ - fastmod - ruby_3_3.devEnv - rust-bin.stable.latest.default - bundler - zsh - nodejs + pkgs.fastmod + ruby + rustToolchain + pkgs.zsh + pkgs.nodejs ]; # Make is so nix develop --impure uses zsh config diff --git a/gem/exe/rb-sys-dock b/gem/exe/rb-sys-dock index 3eac5dbf..f301c280 100755 --- a/gem/exe/rb-sys-dock +++ b/gem/exe/rb-sys-dock @@ -38,10 +38,12 @@ end def list_platforms RbSys::ToolchainInfo.supported.each do |p| old = logger.io - logger.io = $stdout - puts "- #{p.platform}" - ensure - logger.io = old + begin + logger.io = $stdout + puts "- #{p.platform}" + ensure + logger.io = old + end end end diff --git a/gem/lib/rb_sys/cargo_builder.rb b/gem/lib/rb_sys/cargo_builder.rb index b1c4e416..c82448f9 100644 --- a/gem/lib/rb_sys/cargo_builder.rb +++ b/gem/lib/rb_sys/cargo_builder.rb @@ -87,7 +87,7 @@ def cargo_command(dest_path, args = []) end def cargo_dylib_path(dest_path) - prefix = so_ext == "dll" ? "" : "lib" + prefix = (so_ext == "dll") ? "" : "lib" path_parts = [dest_path] path_parts << target if target path_parts += [profile_target_directory, "#{prefix}#{cargo_crate_name}.#{so_ext}"] @@ -355,7 +355,7 @@ class DylibNotFoundError < StandardError def initialize(dir) files = Dir.glob(File.join(dir, "**", "*")).map { |f| "- #{f}" }.join "\n" - super <<~MSG + super(<<~MSG) Dynamic library not found for Rust extension (in #{dir}) Make sure you set "crate-type" in Cargo.toml to "cdylib" diff --git a/gem/lib/rb_sys/mkmf.rb b/gem/lib/rb_sys/mkmf.rb index d490ce73..f7f62c90 100644 --- a/gem/lib/rb_sys/mkmf.rb +++ b/gem/lib/rb_sys/mkmf.rb @@ -338,7 +338,7 @@ def conditional_assign(a, b, export: false, indent: 0) result << export_env(a, b) if export result else - "#{"\t" * indent}#{export ? "export " : ""}#{a} ?= #{b}" + "#{"\t" * indent}#{"export " if export}#{a} ?= #{b}" end end diff --git a/rakelib/docker.rake b/rakelib/docker.rake index 364d9543..c6b43edf 100644 --- a/rakelib/docker.rake +++ b/rakelib/docker.rake @@ -2,7 +2,7 @@ require "yaml" require "json" -require_relative "./../gem/lib/rb_sys/version" +require_relative "../gem/lib/rb_sys/version" TOOLCHAINS = JSON.parse(File.read("data/toolchains.json"))["toolchains"] DOCKERFILE_PLATFORM_PAIRS = TOOLCHAINS.select { |p| p["supported"] }.map { |p| [p["dockerfile"], p["ruby-platform"]] } diff --git a/rakelib/release.rake b/rakelib/release.rake index 505cabeb..302ea133 100644 --- a/rakelib/release.rake +++ b/rakelib/release.rake @@ -28,7 +28,7 @@ namespace :release do desc "Bump the gem version" task bump: "release:prepare" do - require_relative "./../gem/lib/rb_sys/version" + require_relative "../gem/lib/rb_sys/version" old_version = RbSys::VERSION printf "What is the new version (current: #{old_version})?: " @@ -66,7 +66,7 @@ namespace :release do sh "bundle exec rake release" end - require_relative "./../gem/lib/rb_sys/version" + require_relative "../gem/lib/rb_sys/version" sh "gh", "release", "create", "v#{RbSys::VERSION}", "--generate-notes"