RubyからHaskellを呼び出したくてHubrisをインストールした
Hubrisという、RubyからHaskellのコードを呼ぶためのブリッジがあります。HaskellパッケージとRubyライブラリで構成されており、現在のバージョンは0.0.3です。
とある理由から、このHubrisで遊んでみようと思ったのですが、Haskellパッケージのインストールにかなり手こずったため、ポイントをまとめておきます。ちなみにRubyライブラリのほうは「gem install hubris」で簡単に入りました。
今回まとめる内容は、あくまで僕の環境(CentOS 5)でうまくいったもので、他の環境ではどうだか分かりません。誤った記述があるかもしれませんので、お気づきの際はぜひお知らせください。
Hubrisで遊びたくなった理由については、後日書くつもりです。
ざっくりした流れ
$ wget http://hackage.haskell.org/packages/archive/hubris/0.0.3/hubris-0.0.3.tar.gz $ tar xvzf hubris-0.0.3.tar.gz $ cd hubris-0.0.3 $ runhaskell Setup.hs configure (ここで dist/build/autogen/Includes.hs を編集) $ cabal install --enable-shared \ > --extra-include-dirs=/home/iwamot/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux \ > --extra-lib-dirs=/home/iwamot/.rvm/rubies/ruby-1.9.1-p378/lib \ > --extra-lib-dirs=/home/iwamot/.rvm/rubies/ruby-1.8.7-p302/lib
「runhaskell Setup.hs configure」すると、ビルドに必要な dist/build/autogen/Includes.hs が生成されます。ただし、生成された内容のままでビルドすると、Hubrisが正常動作しないため、手を加える必要があります。
cabal installのオプションでは、Ruby 1.8のヘッダファイル格納先と、Ruby 1.8および1.9の共有ライブラリの格納先を渡しています。
dist/build/autogen/Includes.hs の編集
僕の環境*1では、生成された dist/build/autogen/Includes.hs の内容は下記の通りでした。
module Includes where extraIncludeDirs=["/home/iwamot/.rvm/rubies/ruby-1.9.1-p378/lib/ruby/1.9.1/i686-linux"]
このままでもビルドはできるのですが、extraIncludeDirsで指定されたディレクトリにヘッダファイルが格納されていないため、Hubrisの実行時にエラーが発生してしまいます。
エラー内容を参考に、下記のように編集すると、うまく動くようになりました。
module Includes where extraIncludeDirs=["/home/iwamot/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1", "/home/iwamot/.rvm/rubies/ruby-1.9.1-p378/include/ruby-1.9.1/i686-linux"]
cabal installのオプション
cabal install部分を再掲します。
$ cabal install --enable-shared \ > --extra-include-dirs=/home/iwamot/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/i686-linux \ > --extra-lib-dirs=/home/iwamot/.rvm/rubies/ruby-1.9.1-p378/lib \ > --extra-lib-dirs=/home/iwamot/.rvm/rubies/ruby-1.8.7-p302/lib
「--extra-include-dirs」は問題ないでしょう。Ruby 1.8のヘッダファイル格納先を渡します。
問題は「--extra-lib-dirs」で、上記のように指定すると、下記ライブラリファイルの参照をインストーラに指示することになります。
- /home/iwamot/.rvm/rubies/ruby-1.9.1-p378/lib/libruby.so
- /home/iwamot/.rvm/rubies/ruby-1.8.7-p302/lib/libruby1.8.so
注意すべきポイントは2点です。
まっさらなGHC環境がお勧め
HubrisのHaskellパッケージをインストールすると、下記の依存パッケージもインストールされます。
いずれかが「--enable-shared」なしでインストールされている場合、HubrisのHaskellパッケージのインストールに失敗します。回避するには、事前に削除(cabal unregister)しておくか、「--enable-shared」付きで再インストール(cabal install --reinstall --enable-shared)しておかなければなりません。
僕は当初、Haskell Platformを使っていたのですが、依存関係の縛りで、削除も再インストールもできませんでした。そこでHaskell Platformをアンインストールし、GHC 6.12.3を入れ直し*2、インストール済みのパッケージを全削除しました。
Hubrisを実行する前に
インストールが終わればHubrisを実行できるのですが、事前に確認しておかなければならないことがあります。
共有ライブラリのロード
まず、Ruby 1.8の共有ライブラリをロードしておかなければなりません。
$ echo /home/iwamot/.rvm/rubies/ruby-1.8.7-p302/lib > /etc/ld.so.conf.d/hubris.conf $ sudo /sbin/ldconfig
Hubris実行時に検索されるライブラリファイル名は「libruby.so.1.8」です。cabal install時の「libruby1.8.so」とは異なるため、注意が必要です。
サンプルの実行
インストールメモは以上で終わりですが、せっかくなのでサンプルを動かしてみましょう。
/home/iwamot/.rvm/gems/ruby-1.9.1-p378/gems/hubris-0.0.3/sample にサンプルスクリプトが配置されています。実は、このサンプルも一部修正が必要でした。
config.ru(一部修正)
use Rack::Reloader, 0 use Rack::ContentLength require 'pp' require 'hubris' class Fibonacci hubris :source => 'Fibonacci.hs' end def arg_from env # 岩本修正ここから #env['REQUEST_URI'] ? env['REQUEST_URI'].to_s.sub(/^\//, '').to_i : 0 env['PATH_INFO'] ? env['PATH_INFO'].to_s.sub(/^\//, '').to_i : 0 # ここまで end app = proc do |env| value = Fibonacci.new.fibonacci( arg_from env ) [ 200, {'Content-Type' => 'text/plain'}, "The fib number is #{value }" ] end run app
Fibonacci.hs(修正なし、コメント行は省略)
module Fibonacci where import Foreign.C.Types import Maybe fibonacci :: Int -> Int fibonacci n = fibs !! n where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
実行
$ cd /home/iwamot/.rvm/gems/ruby-1.9.1-p378/gems/hubris-0.0.3/sample $ rackup config.ru
http://localhost:8765/10 にアクセスすると、無事「The fib number is 55」と表示されました。
今後の予定
冒頭で書いたとおり、Hubrisで遊びたくなった理由を次回以降の日記に書く予定です。
また、当然ながら、自作のHaskellコードをRubyから呼び出してみようと思っています。「http://www.engineyard.com/blog/2010/a-hint-of-hubris/」が参考になりそうです。
*2:インストール手順は「Haskell初心者の僕が試したことをまとめてみた」に書いたとおりです