64-bit CentOS 上の 32-bit Ruby に gem で拡張ライブラリをインストール
タイトルがごちゃごちゃしてますが、32-bit の Ruby がインストールされている 64-bit OS 上で gem install ruby-debug してもうまくインストールできなかったので、原因&解決策についてです。
前置き
Linux 環境では自分で Ruby をビルドする or 環境に合わせたパッケージを入れることが多いので「なんで 64-bit 環境で 32-bit の Ruby を入れてるの?」という疑問がありそうですが、今の案件の Redmine が 64-bit CentOS(RHEL かも?)上に BitNami Redmine Stack を使って構築されているからです。
検証用に私も同じ構成で Redmine を立ててみたんですが、確かにバイナリをダウンロード→実行して、Redmine の管理者情報、SMTP サーバの情報くらいを入力すればすぐに使えるので導入はすごく楽でした。
発生した問題
この環境でちょっと動作が怪しいプラグインがあって ruby-debug でステップ実行したかったのですが、
yum install gcc gem install ruby-debug ruby script/server -e production -u
とやっても、なぜか
You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'
と言われてしまいます。gem list -d ruby-debug とか gem which ruby-debug で確認しても問題なくインストールはされているようなのですが・・・。
原因&解決策
長くなるので先に原因&解決策を書いておくと、BitNami Redmine Stack でインストールされる Ruby が 32-bit なのに、gem install 時に gcc が 64-bit のバイナリを作っており、うまくライブラリをロードできていないのが原因です。
もし ruby-debug をもうインストールしてしまったのであれば、一度
gem uninstall linecache ruby-debug ruby-debug-base
でアンインストール後、gcc で 32-bit のバイナリを生成できるよう
yum install glibc-devel.i386
を実行した後で、
env CONFIGURE_ARGS="--with-cflags='-m32' --with-ldflags='-m32'" gem install ruby-debug --no-ri --no-rdoc
のように CONFIGURE_ARGS を指定してやるとよいです。
原因特定までのいろいろ
エラーの詳細を出力してみる。
どうもソースを見ると require 以外の失敗でもこのメッセージを出しているみたいなので、vendor/rails/railties/lib/rails/rack/debugger.rb を書き換えて詳細なメッセージを出力してみます。
--- - 2011-05-12 07:38:00.502458000 +0900 +++ ./vendor/rails/railties/lib/rails/rack/debugger.rb 2011-05-12 07:35:42.000000000 +0900 @@ -10,8 +10,9 @@ ::Debugger.start ::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings) puts "=> Debugger enabled" - rescue Exception + rescue Exception => e puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'" + puts e.message exit end
まあこんなことしなくても ruby -rrubygems -e'require "ruby-debug"' とかでも同じメッセージは拾えますけど・・・エラーメッセージはこんなでした。
/opt/redmine-1.1.3-0/ruby/lib/ruby/gems/1.8/gems/ruby-debug-base-0.10.4/lib/ruby_debug.so: wrong ELF class: ELFCLASS64 - /opt/redmine-1.1.3-0/ruby/lib/ruby/gems/1.8/gems/ruby-debug-base-0.10.4/lib/ruby_debug.so
調べてみると、32-bit のプログラムが 64-bit のライブラリを呼ぼうとすると出るエラーなんだとか。
バイナリの情報を見てみると・・・
# file /opt/redmine-1.1.3-0/ruby/bin/.ruby.bin /opt/redmine-1.1.3-0/ruby/bin/.ruby.bin: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped # file /opt/redmine-1.1.3-0/ruby/lib/ruby/gems/1.8/gems/ruby-debug-base-0.10.4/lib/ruby_debug.so /opt/redmine-1.1.3-0/ruby/lib/ruby/gems/1.8/gems/ruby-debug-base-0.10.4/lib/ruby_debug.so: ELF 64-bit LSB shared object, AMD x86-64, version 1 (SYSV), not stripped
確かにそのようで。
gem install のオプションでなんとかならない?→効果なし
gem install に --platform オプションがあるのでこれを使えばいいのかなと思ったんですが、うまくいかず。
よくよく考えるとこれはどの gem を使うかのオプションでしょうし、gem environment で確認したらもともと x86-linux になっていましたから、設定しても意味はなさそうですね。
gem install の詳細を追って見る
gem install に -V オプションを付加すると詳細なログが出力されるそうで。ビルドの箇所を抜き出すと、
/opt/redmine-1.1.3-0/ruby/bin/ruby extconf.rb gcc -I. -I/opt/redmine-1.1.3-0/ruby/lib/ruby/1.8/i686-linux -I/opt/redmine-1.1.3-0/ruby/lib/ruby/1.8/i686-linux -I. -DAI_ADDRCONFIG=0 -I/opt/redmine-1.1.3-0/common/include -D_FILE_OFFSET_BITS=64 -fPIC -I/opt/redmine-1.1.3-0/common/include -c ruby_debug.c gcc -I. -I/opt/redmine-1.1.3-0/ruby/lib/ruby/1.8/i686-linux -I/opt/redmine-1.1.3-0/ruby/lib/ruby/1.8/i686-linux -I. -DAI_ADDRCONFIG=0 -I/opt/redmine-1.1.3-0/common/include -D_FILE_OFFSET_BITS=64 -fPIC -I/opt/redmine-1.1.3-0/common/include -c breakpoint.c gcc -shared -o ruby_debug.so ruby_debug.o breakpoint.o -L. -L/opt/redmine-1.1.3-0/ruby/lib -Wl,-R/opt/redmine-1.1.3-0/ruby/lib -L. -L/opt/redmine-1.1.3-0/common/lib -rdynamic -Wl,-export-dynamic -lpthread -lrt -ldl -lcrypt -lm -lc
gcc で 32-bit バイナリを生成するには?
- m32 オプションを付加してコンパイルすればいいそうです。
適当なファイルで試してみましたが、適切なヘッダファイルがないと
# gcc -m32 hello.c In file included from /usr/include/features.h:352, from /usr/include/stdio.h:28, from hello.c:1: /usr/include/gnu/stubs.h:7:27: error: gnu/stubs-32.h: No such file or directory
のようなエラーになるので、
yum install glibc-devel.i386
の実行が必要でした。
extconf.rb 経由で gcc オプションを渡すには?
を見てもちゃんと書いてませんが、extconf.rb 実行時 --with-cflags='〜' のようにオプションを渡すと Makefile 中で CFLAGS が設定されるようです。
によると
export CONFIGURE_ARGS="--with-cflags='〜'"
のように CONFIGURE_ARGS を設定しておけば、実行時にオプションを渡さなくても extconf.rb(mkmf)で使われるんだとか。
今回は CFLAGS に -m32 を設定したいので、
CONFIGURE_ARGS="--with-cflags='-m32'" gem install ruby-debug --no-ri --no-rdoc -V
を試してみると・・・コンパイルは通りましたが、リンクのタイミングで
/usr/bin/ld: warning: i386 architecture of input file `ruby_debug.o' is incompatible with i386:x86-64 output
のエラーになってしまいました。
リンクに使われるオプションは ldflags、dldflags、archflag の 3 種類があってどれが適切かわからないんですが、とりあえず
env CONFIGURE_ARGS="--with-cflags='-m32' --with-ldflags='-m32'" gem install linecache --no-ri --no-rdoc -V
のようにすると、無事インストールが完了しました。ふぅ。
動作確認
動作させる前にバイナリをいちおう確認しておくと・・・
# file /opt/redmine-1.1.3-0/ruby/lib/ruby/gems/1.8/gems/ruby-debug-base-0.10.4/lib/ruby_debug.so /opt/redmine-1.1.3-0/ruby/lib/ruby/gems/1.8/gems/ruby-debug-base-0.10.4/lib/ruby_debug.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped
ちゃんと 32-bit になってますね。
ruby-debugger を実際に動作させてみると・・・
ruby script/server -e production -u
でも
ruby script/console production --debugger
でも、ちゃんと
=> Debugger enabled
と表示され、breakpoint 実行時にちゃんと rdb が有効になりました。めでたしめでたし。