先日のhsbtさんの日記を読んでいて
https://www.hsbt.org/diary/20221227.html#p02
RubyGems 3.4 では C 拡張をインストール後にビルドディレクトリを make clean するようになった
タイトルが全部シリーズです。C 拡張な gem は ext ディレクトリの下に .c などを配置してその下でビルドしたのち lib にコピー(=インストール)という手続きを踏んでいます。今回 RubyGems 3.4 ではこの処理のうち、インストールが終わったあとは ext の下を make clean して不要なファイルを消すようになったんですが、この影響でいくつかの gem で動かないよ〜という報告が来たのであれこれ直していた。
https://github.com/rubygems/rubygems/issues/6205
要は ext の下を直接参照するようなコードがあって、そういう行儀が悪いコードは落ちますというものだった。最初はこれは revert した方がいいかなあとも思ったけど、数次第なら gem の方を直せばいいか、という気持ちになってきた。
ほ〜んと思っていたら、自分が携わっているプロジェクトでもこれに関係するエラーが出ているようだったので調べています。
対象のgemは https://github.com/etscrivner/rbsecp256k1 です。
とりあえず、Ruby 3.2を入れて irb で require 'rbsecp256k1'
したらエラーが出ることは確認しました。
% ruby -v ruby 3.2.0 (2022-12-25 revision a528908271) [arm64-darwin20] % brew install automake openssl libtool pkg-config gmp libffi # ref: https://github.com/etscrivner/rbsecp256k1#macos 🍺 ... # brew install のログは割愛 % gem install rbsecp256k1 Building native extensions. This could take a while... Successfully installed rbsecp256k1-5.1.0 Parsing documentation for rbsecp256k1-5.1.0 Installing ri documentation for rbsecp256k1-5.1.0 Done installing documentation for rbsecp256k1 after 3 seconds 1 gem installed % irb irb(main):001:0> require 'rbsecp256k1' Ignoring debug-1.7.1 because its extensions are not built. Try: gem pristine debug --version 1.7.1 Ignoring rbs-2.8.2 because its extensions are not built. Try: gem pristine rbs --version 2.8.2 <internal:/Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- rbsecp256k1/rbsecp256k1 (LoadError) from <internal:/Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require' from /Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/rbsecp256k1-5.1.0/lib/rbsecp256k1.rb:10:in `<top (required)>' from <internal:/Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:160:in `require' from <internal:/Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:160:in `rescue in require' from <internal:/Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:149:in `require' from (irb):1:in `<main>' from /Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/irb-1.6.2/exe/irb:11:in `<top (required)>' from /Users/kentarotanaka/.rbenv/versions/3.2.0/bin/irb:25:in `load' from /Users/kentarotanaka/.rbenv/versions/3.2.0/bin/irb:25:in `<main>' <internal:/Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require': cannot load such file -- rbsecp256k1 (LoadError) from <internal:/Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require' from (irb):1:in `<main>' from /Users/kentarotanaka/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/irb-1.6.2/exe/irb:11:in `<top (required)>' from /Users/kentarotanaka/.rbenv/versions/3.2.0/bin/irb:25:in `load' from /Users/kentarotanaka/.rbenv/versions/3.2.0/bin/irb:25:in `<main>'
cannot load such file -- rbsecp256k1/rbsecp256k1 (LoadError)
が出ていますね。
参考になる事例
同様の問題が確認されている ox
という gem がありまして、こちらのissue https://github.com/ohler55/ox/issues/300 で調査され、こちらのプルリクエスト https://github.com/ohler55/ox/pull/301 で解決に至っています。
このプルリクエストでは、このように lib/ox.rb
にて .so
ファイルを明示的に require しています。
begin require_relative 'ox.so' rescue LoadError require 'ox/ox' end
改めて今回のgemについて
/lib/rbsecp256k1.rb
にて require 'rbsecp256k1/rbsecp256k1'
で LoadError になっているので、
/lib/rbsecp256k1
ディレクトリ内になにがあるのか?を知りたくなりました。
とりあえず /lib/rbsecp256k1.rb
にこんなコードを書き加えて、雑にデバッグしてみます。
puts '*' * 300 Dir.foreach(File.join(__dir__,'rbsecp256k1')) do |item| puts item end puts '*' * 300
gemをbuildしてirbでrequireしてみると↓のようなログが出ました。
************************************************************************************************************************************************************************************************************************************************************************************************************ . util.rb context.rb version.rb .. ***************************************************************************************************************************************************************************
なんかしらの拡張ライブラリ(.so
や .bundle
など)があるかな?思ったけれどなかったです。
一応 /lib
ディレクトリも確認しておこうとおもって今度は↓でデバッグしてみました。
puts '*' * 300 Dir.foreach(File.join(__dir__)) do |item| puts item end puts '*' * 300
************************************************************************************************************************************************************************************************************************************************************************************************************ rbsecp256k1.rb . .. rbsecp256k1 ************************************************************************************************************************************************************************************************************************************************************************************************************
先ほどと同じく .so
や .bundle
などのファイルはないですね。
というか、手元の環境(Mac)で gem を build したときに /lib/rbsecp256k1/rbsecp256k1.bundle
ファイルが作られていることに気づきました。
じゃあなんでgemにはこのファイルが無いんじゃろ...?ともう少し調べると、gemspecで files
の指定が lib 配下 .rb
だけになっていたのでした。。
s.files = ( Dir['lib/**/**.rb'] + Dir['documentation/**.md'] + %w[ext/rbsecp256k1/rbsecp256k1.c ext/rbsecp256k1/extconf.rb Rakefile README.md] )
files
に指定されていないファイルはパッケージに含まれないので、gemのビルド時に生成された .so
や .bundle
などのファイルが lib 配下に含まれなくなっていたようです。
gemspec を書き換えるだけで解決できそう、ということがわかったので、これから改めて動作確認とプルリクエストを出そうと思います。