ククログ

株式会社クリアコード > ククログ > Ruby 1.8.7/1.9.1どちらでも使えるWindows用バイナリ入りgemをDebian GNU/Linux上で作る方法

Ruby 1.8.7/1.9.1どちらでも使えるWindows用バイナリ入りgemをDebian GNU/Linux上で作る方法

groongaのRubyバインディングrroonga 0.9.3がリリースされました。rroonga 0.9.3に関することはメーリングリストでのアナウンスを見てください。

rroonga 0.9.3ではWindows用のgemも提供するようにしました。このgemにはgroonga/rroongaのビルド済みのバイナリが含まれているのでビルド環境がないことが多いWindowsでも簡単に使えるようになっています。

さて、このgemですが、1つのgemでRuby 1.8.7にもRuby 1.9.1にも対応しています。そもそも、gemにはWindowsや32bit環境などのプラットフォームを指定することはできますが、Rubyのバージョンは指定することができません。そのため、Rubyのバージョン毎にgemを用意することはできません。用意する場合はgemのパッケージ名を"rroonga187"や"rroonga191"などと変えなければいけません。これはカッコワルイですね。

解決法は、1つのgemの中に1.8用のバイナリと1.9用のバイナリを両方いれ、実行時にどちらを使うかを切り替える、です。

まず、以下のようにバイナリを配置します1

lib/1.8/groonga.so # <- Ruby 1.8.7のWindows用バイナリ
lib/1.9/groonga.so # <- Ruby 1.9.1のWindows用バイナリ

そして、groonga.soを読み込む部分を以下のようにします。

major, minor, micro, = RUBY_VERSION.split(/\./)
require "#{major}.#{minor}/groonga.so"

これで、適切なバイナリを読み込むことができます。

この他に、rroongaのように依存しているDLL(libgroonga.dll)がある場合はそのDLLがあるフォルダをPATHに入れなければいけない、などといった注意点がありますが、それはまたいつか機会があったら触れるかもしれません。

それでは、1.8.7でも1.9.1でも使えるWindows用バイナリが入ったgem2をDebian GNU/Linux上のMinGWで作る方法を紹介します。

rake-compiler: Ruby 1.8.7と1.9.1をクロスコンパイル

まず、Windows用のRuby 1.8.7と1.9.1をMinGWでクロスコンパイルします。これにはrake-compilerが便利です。

% sudo gem install rake-compiler

まず、MinGWをインストールします。

% sudo aptitude install -y mingw32

それでは、Ruby 1.8.7-p249をビルドします。

% rake-compiler cross-ruby VERSION=1.8.7-p249 EXTS=--without-extensions

クロスコンパイルしたrubyはextconf.rbを実行してMakefileを作れればいいだけなので、拡張ライブラリなどはいりません。環境変数として「EXTS=--without-extensions」を指定すると拡張ライブラリはビルドされないのですが、もっとカッコイイ方法がありそうな気がします。

同様にRuby 1.9.1-p378もビルドします。

% rake-compiler cross-ruby VERSION=1.9.1-p378 EXTS=--without-extensions

ただ、これは失敗します。失敗したら以下のパッチを当てます3

diff -ru ruby-1.9.1-p378.orig/win32/win32.c ruby-1.9.1-p378/win32/win32.c
--- ruby-1.9.1-p378.orig/win32/win32.c	2009-12-05 18:40:53.000000000 +0900
+++ ruby-1.9.1-p378/win32/win32.c	2010-04-20 23:10:13.000000000 +0900
@@ -4604,7 +4604,7 @@

     ret += written;
     if (written == len) {
-	(const char *)buf += len;
+	buf = (const char *)buf + len;
 	if (size > 0)
 	    goto retry;
     }

このパッチは以下のように適用できます。

% cd ~/.rake-compiler/sources
% patch -p0 < /tmp/ruby-1.9.1-build-fix.diff

もう一度、同じコマンドでビルドすると成功します。

% rake-compiler cross-ruby VERSION=1.9.1-p378 EXTS=--without-extensions

Rake::ExtensionTask: gem用バイナリをクロスコンパイル

Windows用のRuby 1.8.7とRuby 1.9.1ができたので、これを利用してgem用のバイナリをクロスコンパイルします。これには、rake-compilerが提供するRake::ExtensionTaskが便利です。

Rake::ExtensionTaskの使い方を紹介しますが、ここでは、もうすでにRakefileがあり、その中でGem::Specificationを作っているものとします。

specがGem::Specificationだとすると、Rakefileに以下を追加することでcrossタスクが定義されます。

require 'rake/extensiontask'
Rake::ExtensionTask.new("groonga", spec) do |ext|
  ext.cross_compile = true
  ext.cross_platform = 'x86-mingw32'
end

Rake::ExtensionTask.newに"groonga"を指定していますが、このようなRakefileを使うときは、以下のようなファイル構成になっている必要があります。

./
+-- ext/
|    +-- groonga/
|        +-- extconf.rb
|        +-- rb-grn.c
|        +-- ...
+ Rakefile
...

ext/の下にRake::ExtensionTask.newで指定した名前と同じディレクトリを作り、その下にextconf.rbを置きます。

crossタスクを使って1.8.7用のバイナリと1.9.1用のバイナリをクロスコンパイルするには以下のようにします。

% rake cross compile RUBY_CC_VERSION=1.8.7:1.9.1

うまくいくとlib/1.8/groonga.soとlib/1.9/groonga.soができます。

これらを両方含んだgemを作るには以下のようにします。

% rake cross native gem RUBY_CC_VERSION=1.8.7:1.9.1

これでpkg/rroonga-0.9.3-x86-mingw32.gemができます。あとは、このgemをrubygems.orgにアップロードすれば完了です。

% gem push pkg/rroonga-0.9.3-x86-mingw32.gem

まとめ

Ruby 1.8.7/1.9.1のどちらでも使えるWindows用のgemをDebian GNU/Linux上で作成する方法を紹介しました。もし、拡張ライブラリをWindows上でも簡単に使えるようにしたいのであれば、Ruby 1.8.xと1.9.xの両方をサポートしてみてはいかがでしょうか。

この話とは関係ありませんが、Ruby Summer of Codeの学生の応募の締切りは今週の土曜日だそうです。(参考: [ruby-list:47029] [ANN] Ruby Summer of Code

Rubyとオープンソースに興味のある学生の方は応募してみてはいかがでしょうか。本家のSummer of CodeやRuby Summer of Codeはフリーソフトウェアの開発に関わるよい機会といえます。Rubyベストプラクティスの著者が開発しているPDF生成ライブラリのPrawnなど、いくつものRuby関連のフリーソフトウェアがSummer of Codeのおかげで開発が進んできました。(参考: Summer of Codeと須藤さんとSubversionのRuby bindings - 角谷HTML化計画(2005-10-26)

ぜひ、このような機会を活かして、フリーソフトウェアの開発に積極的に参加してみてください。

https://amazon.co.jp/dp/4873114454

  1. Windowsでも拡張子は.soでいいのです。

  2. 複数のバージョン向けのバイナリが入ったgemをfat gemというらしいです。

  3. この問題はtrunkではすでに修正されています。