64ビットなrubyを野良ビルドする

何も考えずに本家からソースをダウンロードしてコンパイル&インストールすると

MyMac:~ chcoopu$ file /usr/local/ruby/bin/ruby 
/usr/local/ruby/bin/ruby: Mach-O executable i386
MyMac:~ chcoopu$ 

といった感じでi386アーキテクチャ用バイナリ、つまり32ビット環境でも動作するバイナリが作成される。PowerPCSPARCEM64T/AMD64なら64ビットモードで動作している時は32/64ビットのバイナリ両方を同時に実行できるので特に問題ないし不思議でもない。
Max OS X付属のrubyなら

MyMac:~ chcoopu$ file /usr/bin/ruby 
/usr/bin/ruby: Mach-O universal binary with 2 architectures
/usr/bin/ruby (for architecture ppc7400):	Mach-O executable ppc
/usr/bin/ruby (for architecture i386):	Mach-O executable i386
MyMac:~ chcoopu$ 

PowerPCIntel両方で動作するユニバーサルバイナリだとわかる。
ppc7400ということは、おそらくPowerPC 7400シリーズのこと。ということはG3かG4以降のバイナリということか。まぁ自分で使わないからどっちでもいいけど。
でも、実行バイナリではなくライブラリを調べてみると

MyMac:~ chcoopu$ file /System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib 
/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib: Mach-O universal binary with 4 architectures
/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib (for architecture ppc7400):	Mach-O dynamically linked shared library ppc
/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib (for architecture ppc64):	Mach-O 64-bit dynamically linked shared library ppc64
/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib (for architecture i386):	Mach-O dynamically linked shared library i386
/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/lib/libruby.1.dylib (for architecture x86_64):	Mach-O 64-bit dynamically linked shared library x86_64
MyMac:~ chcoopu$ 

PowerPCも32ビット(ppc7400)、64ビット(ppc64)バイナリの2種類入っており、Intelの方も32ビット(i386)、64ビット(x86_64)バイナリの両方入った、合計4種類のバイナリの入ったユニバーサルバイナリだとわかる。実行バイナリに両アーキテクチャの64ビット版が入っていないのは何か理由があるのかな?

とゆーわけで、普通に使える64ビットなrubyバイナリはない訳だ。
なので、自分で作ってみようという話。


x86_64向けにバイナリを作れば64ビットバイナリになるので、そのようにする。特に-m64とかは必要ではないみたい。
gccへのオプションとして-arch x86_64を付けるだけ。x86_64の代わりにi386を指定すれば今までと同じバイナリになる……が、指定する意味はないな。ppcやppc64も選択できるが、まぁ必要な人だけ、ドゾー。

で、大抵のソフトはMakefileの中でCC、CXXでCコンパイラC++コンパイラの指定をすることが多い。同時にCFLAGS、CXXFLAGSでCコンパイラC++コンパイラに対するオプションを指定することになる。

rubyコンパイルする時にもCFLAGSに-arch x86_64を指定すればOKかなと。
指定してconfigure、makeすると見事64ビット対応のrubyができる。

MyMac:~ chcoopu$ file /usr/local/ruby/bin/ruby 
/usr/local/ruby/bin/ruby: Mach-O 64-bit executable x86_64
MyMac:~ chcoopu$ 
MyMac:~ chcoopu$ /usr/local/ruby/bin/ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-darwin9.5.0]
MyMac:~ chcoopu$ 

できるのだが、作っている途中で部分的に文句を言われる。

ld warning: in bytecode.o, file is not of required architecture

その結果何も拡張していないruby本体は動作するものの、添付ライブラリ辺りでこけてしまう。

MyMac:rubygems-1.3.1 chcoopu$ /usr/local/ruby/bin/ruby setup.rb 
/usr/local/ruby/lib/ruby/1.8/i686-darwin9.5.0/thread.bundle: Failed to load /usr/local/ruby/ruby/lib/ruby/1.8/i686-darwin9.5.0/thread.bundle (LoadError)
	from /usr/local/ruby/lib/ruby/1.8/thread.rb:5
	from ./lib/rubygems.rb:10:in `require'
	from ./lib/rubygems.rb:10
	from setup.rb:22:in `require'
	from setup.rb:22
MyMac:rubygems-1.3.1 chcoopu$ 

というわけで、どこが問題なのかを確認してみる。
ソースの中の$RUBY_SOURCE/extディレクトリ以下にある拡張ライブラリのソースをコンパイルしてできたオブジェクトファイル( .o )は64ビットバイナリ。

MyMac:thread chcoopu$ file thread.o 
thread.o: Mach-O 64-bit object x86_64
MyMac:thread chcoopu$ 

それに対して$RUBY_SOURCE/.ext/i686-darwin9.5.0ディレクトリ以下にあるのは何故かi386な32ビットバイナリ。

MyMac:i686-darwin9.5.0 chcoopu$ file thread.bundle 
thread.bundle: Mach-O bundle i386
MyMac:i686-darwin9.5.0 chcoopu$ 

つまりオブジェクトファイルをリンクする時に64ビットバイナリではなくなっているみたい。
ここでどうやってリンクしているかMakefileを確認してみると

LDSHARED = cc -dynamic -bundle -undefined suppress -flat_namespace

となっている。この呼び出されているccというのは/usr/bin/ccであり実体はgccである。

MyMac:~ chcoopu$ which cc
/usr/bin/cc
MyMac:~ chcoopu$ ls -l /usr/bin/cc 
lrwxr-xr-x  1 root  wheel  7 10 22 00:14 /usr/bin/cc@ -> gcc-4.0
MyMac:~ chcoopu$ 

gccなら、コンパイル時と同じにオプションを渡してやればいいわけ。
今回はMakefileを変更するのではなく、Makefileを生成するためのconfigureを変更。しかも手抜き変更。
ccをgcc-4.0に固定しないように変更+x86_64用の設定。
※そのままMakefileに展開されるので、変数の書き方はMakefile形式で。

MyMac:ruby-1.8.6-p287 chcoopu$ diff configure configure.ORG
16520c16520
< 	darwin*)	: ${LDSHARED='$(CC) -arch x86_64 -dynamic -bundle -undefined suppress -flat_namespace'}
---
> 	darwin*)	: ${LDSHARED='cc -dynamic -bundle -undefined suppress -flat_namespace'}
MyMac:ruby-1.8.6-p287 chcoopu$ 

この状態でconfigure、makeをし直し。一度makeした後ならmake distcleanしてからconfigure、makeをすると。
そうすると、今度はちゃんと64ビットバイナリになった。

MyMac:i686-darwin9.5.0 chcoopu$ file thread.bundle 
thread.bundle: Mach-O 64-bit bundle x86_64
MyMac:i686-darwin9.5.0 chcoopu$ 

そして、拡張ライブラリでもこけなくなった。めでたしめでたし。