先日までRustでRubyのネイティブ拡張をビルドする方法を調べていたのだけど、やっと ruby gems として公開するところまでもっていけた。
調子にのってかなり大胆な名前を付けてしまった。
hello_rust_gem
RubyGems: https://rubygems.org/gems/hello_rust
GitHub: https://github.com/irxground/hello_rust_gem
(コードレビューしていただけると大変助かります!!)
このgem自体は意味がなく、RustでRubyのgemを書きたいときの参考にできることを目的として作成した。
hello_rust_gemは他のライブラリに一切依存していない。(ただし、rspec
のような開発時に利用するライブラリは除く)
使い方
require "hello_rust"
p HelloRust::VERSION
各ファイルの説明
src/lib.rs
このファイルはRubyのネイティブ拡張の本体部分になる。
中身は HelloRust
モジュールおよび HelloRust::VERSION
定数の定義のみなので、大したことはしていない。
本物のネイティブ拡張を書くときはここを拡張していけばよい。
Cargo.toml
Rustプロジェクトの定義。重要なのは crate-type = ["cdylib"]
の部分のみ
build.rs
RustのコードからRuby APIをリンクするための設定を生成している。 build.rs
は cargo build
するときに自動的に呼ばれる。
lib/hello_rust.rb
これは外から require "hello_rust"
したときに読み込まれるファイルになる。
いきなりネイティブライブラリを読み込むようにすると、いざRubyコードが書きたくなった時に困るかもしれないので最初の入り口はRubyファイルにしておくとよい。というより、 bundle gem --ext
がそうなのでそれに則った。
中で require "hello_rust/hello_rust"
しているが、この部分でネイティブライブラリを読み込んでいる。
hello_rust.gemspec
RubyGems の設定を書くファイル。今回は例として、Cargo.toml
に書かれたデータを再利用してみたが、そうする必要はない。(が、バージョン番号の更新はミスしやすいのでこの例のように一か所にまとめた方が良いかもしれない)
重要なのは spec.extensions = ["ext/Rakefile"]
の部分で、
gem install
するとソースコードを配置した後、ネイティブ拡張をビルドするために ext/Rakefile
が実行される。
Rakefile
開発者が使う rake
コマンドのための Rakefile
rake
で成果物を消して、ビルドして、テストを実行する。
rake fmt
でRustとRubyのフォーマッターが走る。
その他については rake -T
を参照。
ext/Rakefile
gemspecに書いた通り、 gem install
の際に呼ばれる。
RUBYLIBDIR
という環境変数が渡されるのでそこにネイティブライブラリを配置する。
今回は require "hello_rust/hello_rust"
で読み込めるように、ファイルパスは $RUBYLIBDIR/hello_rust/hello_rust.so
に(Linuxの場合)なる。
ext/build_task.rb
ローカル開発するときとgemをインストールするときは、どちらもコンパイルして適切なパスにライブラリを配置することになるので、共通する部分をここにまとめた。
Windowsでビルドするときには、RUSTUP_TOOLCHAIN
環境変数を設定している。
RubyInstaller で入るRubyはmingwだが、WindowsでのRustはmingwとmsvcの二種類から選べるので、mingwを強制するため。
それ以外
あとはテストやライセンスファイル等。
spec/hello_rust_spec.rb
は HelloRust::VERSION
をテストしており、最小限だがネイティブライブラリのコンパイルに失敗すると気づけるテストにはなっている。
今後
だいたいのプラットフォームで動くことは確認できたので、CIを充実させていきたい。