岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

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点です。

  • Ruby 1.8および1.9を「--enable-shared」フラグ付きでインストールする必要がある
  • Ruby 1.8の共有ライブラリを「libruby1.8.so」にリネームする必要がある

まっさらなGHC環境がお勧め

HubrisのHaskellパッケージをインストールすると、下記の依存パッケージもインストールされます。

  • MonadCatchIO-mtl
  • ghc-mtl
  • ghc-paths
  • haskell-src
  • hint
  • mtl
  • utf8-string

いずれかが「--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」とは異なるため、注意が必要です。

ディレクトリのアクセス権確認

また、Hubrisを実行すると、「/var/hubris/cache」「/var/hubris/source」ディレクトリが作成され、キャッシュファイルなどが配置されていきます。必要に応じて、ディレクトリへのアクセス権を設定しておかなければなりません。僕は下記のように「/var/hubris」を作りました。

$ sudo mkdir /var/hubris
$ sudo chown iwamot /var/hubris

サンプルの実行

インストールメモは以上で終わりですが、せっかくなのでサンプルを動かしてみましょう。

/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/」が参考になりそうです。

*1:RVMでRuby 1.9.1をインストールしています

*2:インストール手順は「Haskell初心者の僕が試したことをまとめてみた」に書いたとおりです