ユニバーサルバイナリを作ってみる

例えばruby野良ビルド。32ビットであるi386バイナリは何も考えずにビルドすればできる。64ビットであるx86_64バイナリもちょっと工夫すれば作れる。
両方できたなら、ユニバーサルバイナリとして1つにまとめたくなるもの。
動機はそれだけ。

i386バイナリは通常にconfigure、makeして作成する。インストールされるファイル以外は無視していいので調べてみると

MyMac:ruby-1.8.6-p287 chcoopu$ find . -type f | xargs file | grep "Mach-O" | grep -v "\.o"
./.ext/i686-darwin9.5.0/bigdecimal.bundle:                             Mach-O bundle i386
./.ext/i686-darwin9.5.0/curses.bundle:                                 Mach-O bundle i386
./.ext/i686-darwin9.5.0/dbm.bundle:                                    Mach-O bundle i386
./.ext/i686-darwin9.5.0/digest/bubblebabble.bundle:                    Mach-O bundle i386
./.ext/i686-darwin9.5.0/digest/md5.bundle:                             Mach-O bundle i386
./.ext/i686-darwin9.5.0/digest/rmd160.bundle:                          Mach-O bundle i386
./.ext/i686-darwin9.5.0/digest/sha1.bundle:                            Mach-O bundle i386
./.ext/i686-darwin9.5.0/digest/sha2.bundle:                            Mach-O bundle i386
./.ext/i686-darwin9.5.0/digest.bundle:                                 Mach-O bundle i386
./.ext/i686-darwin9.5.0/dl.bundle:                                     Mach-O bundle i386
./.ext/i686-darwin9.5.0/enumerator.bundle:                             Mach-O bundle i386
./.ext/i686-darwin9.5.0/etc.bundle:                                    Mach-O bundle i386
./.ext/i686-darwin9.5.0/fcntl.bundle:                                  Mach-O bundle i386
./.ext/i686-darwin9.5.0/iconv.bundle:                                  Mach-O bundle i386
./.ext/i686-darwin9.5.0/io/wait.bundle:                                Mach-O bundle i386
./.ext/i686-darwin9.5.0/nkf.bundle:                                    Mach-O bundle i386
./.ext/i686-darwin9.5.0/openssl.bundle:                                Mach-O bundle i386
./.ext/i686-darwin9.5.0/pty.bundle:                                    Mach-O bundle i386
./.ext/i686-darwin9.5.0/racc/cparse.bundle:                            Mach-O bundle i386
./.ext/i686-darwin9.5.0/readline.bundle:                               Mach-O bundle i386
./.ext/i686-darwin9.5.0/sdbm.bundle:                                   Mach-O bundle i386
./.ext/i686-darwin9.5.0/socket.bundle:                                 Mach-O bundle i386
./.ext/i686-darwin9.5.0/stringio.bundle:                               Mach-O bundle i386
./.ext/i686-darwin9.5.0/strscan.bundle:                                Mach-O bundle i386
./.ext/i686-darwin9.5.0/syck.bundle:                                   Mach-O bundle i386
./.ext/i686-darwin9.5.0/syslog.bundle:                                 Mach-O bundle i386
./.ext/i686-darwin9.5.0/thread.bundle:                                 Mach-O bundle i386
./.ext/i686-darwin9.5.0/zlib.bundle:                                   Mach-O bundle i386
./miniruby:                                                            Mach-O executable i386
./ruby:                                                                Mach-O executable i386
MyMac:ruby-1.8.6-p287 chcoopu$ 

miniruby、ruby、.ext/i686-darwin9.5.0以下のファイルだけ。
なので、これらを一旦リネームした上で、CFLAGS、configureスクリプトを修正してx86_64用バイナリをビルドし直し。
できたx86_64用のminiruby、ruby、.ext/i686-darwin9.5.0以下もi386用とは別の名前にリネーム。


ここでユニバーサルバイナリの作成方法を確認。

lipo -create 元ファイル1 元ファイル2 -output ユニバーサルバイナリ

といった感じで作るみたい。詳細はman lipoで。


ユニバーサルバイナリの作り方がわかったところで、これらの対象ファイルをlipoでユニバーサルバイナリ化。結構数あって面倒なのでスクリプトを組んで一括でやるのがいいね。
できたところで、make installでインストール。
するとユニバーサルバイナリ化されている。

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

しかしどっちが動作しているかわからない。多分i386の方だろうけど。

MyMac:~ chcoopu$ /usr/local/ruby/bin/ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-darwin9.5.0]
MyMac:~ chcoopu$

指定して動かすことも可能。

MyMac:~ chcoopu$ arch -arch i386 /usr/local/ruby/bin/ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-darwin9.5.0]
MyMac:~ chcoopu$ arch -arch x86_64 /usr/local/ruby/bin/ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [i686-darwin9.5.0]
MyMac:~ chcoopu$ 

しかし、作ってはみたものの使い勝手が悪いし、自分の環境ではx86_64のバイナリが動作するのでユニバーサルバイナリ化してi386バイナリを入れる必要性がないよ。

こんな遊びくらいにしか使い道ないかも

MyMac:~ chcoopu$ arch -arch i386 /usr/local/ruby/bin/ruby -v
ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0]
MyMac:~ chcoopu$ arch -arch x86_64 /usr/local/ruby/bin/ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9.5.0]
MyMac:~ chcoopu$ 

1つのユニバーサルバイナリに違うバージョンのバイナリを入れてみた。何も考えずに実行するとx86_64の方が優先されるみたいだ。ふむふむ。

MyMac:~ chcoopu$ /usr/local/ruby/bin/ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9.5.0]
MyMac:~ chcoopu$