2010年12月8日水曜日

Universal Binaryの作り方

Universal Binary(ユニバーサルバイナリ)といえば、Macで”複数のCPUアーキテクチャやOSに対応している"ことを示すもの。ようはUniversalの印がついてれば同じアプリケーションが32bit CPUだろうが64bit CPUだろうが問題なく動く。

正確にはクロスアーキテクチャを実現するための技術の名前なんだけど、まぁ開発者としては作り方さえわかればいいんじゃね?ってことで、仕組みの解説は他のサイトに任せて作り方の説明。
ただし、XCodeでのやり方はたくさん出回ってるのでコマンドラインからの作り方。




MacのCPUは大きく分けてPower PCとIntel x86がある。そしてppcはたくさんのVERSIONがあるけれど、まぁ基本的に使うのはppc (32 bitのPower PC汎用) ,ppc64 (64 bitのPower PC), i386 (x86の32 bit, i686,pentiumなど別名が幾つかある), x86_64 (x86 の64 bit)くらい。
(他のCPU Typeについては「/usr/include/mach/machine.h」参照)

ちなみに、自分のMacのアーキテクチャを調べたいときは「arch」コマンドを使う
% arch
i386

作成したバイナリが対応しているアーキテクチャを調べる方法は幾つかあるけど、ここではlipoコマンドを使った調べ方を紹介。
lipo -info <ファイル名>
lsコマンドにためしてみるとこんな感じ。
% lipo -info /bin/ls
Architectures in the fat file: /bin/ls are: x86_64 i386

さて本題
ユニバーサルバイナリで異なるCPUに対応させる作り方は2通りある。「予めクロスアーキテクチャで作る」か「別々のアーキテクチャで作っといて合体させる」か。

最初から複数のアーキテクチャに対応させるにはコンパイラに「-arch」オプションをつけてこんな感じ
%gcc -arch i386 -arch x86_64 -o hello hello.c
別々に作っておいて合体させるには「lipo -create」を使う。
まずは異なるアーキテクチャでコンパイル
%gcc -arch i386 -o hello_i386 hello.c
%gcc -arch x86_64 -o hello_x86_64 hello.c
でもって合体!
%lipo -create -arch i386 hello_i386 -arch x86_64 hello_x86_64 -output hello


ではでは、複数のOSに対応させるにはどうするか?それにはどうやら”スタブ”というのを使うらしい。

MacではターゲットSDKの指定がこれに当たるらしい。
たとえば「/Developer/SDKs/MacOSX10.6.sdk」にはMacOSX 10.6向けにコンパイルされたライブラリ群が入っている。コンパイル時に"logical root directory"としてこのSDKを指定すると、そこにあるライブラリを使用してコンパイルするので、、、ということらしい。
なのでコンパイル時に「sysroot」オプションをつけると異なるOS向けにコンパイルできるとのこと。「--sysroot=/Developer/SDKs/MacOSX10.6.sdk」とかつける。

ちなみにこれ、ppcなMacでIntelなMac向けのバイナリをコンパイルする(またはその逆)のにも使えるらしい。
指定するSDKをppcに対応してた時代のOSに指定すれば、ppc向けにコンパイルされたライブラリの情報が入っているのでIntel Macでもppc向けにコンパイルできるんだとか。


さて、このUniversal Binary、たまに厄介な問題を引き起こすことがある。
というのも、「i386でコンパイルするには使用するライブラリすべてがi386に対応していなければならない」という考えてみれば至極当然な問題。
でもこれのおかげで「たしかにライブラリインストールしてあるのにリンカ(ld)でエラーが?!」ってことになることがある。

なのでライブラリなんかを作るときは、Universal BinaryでコンパイルされるようにMakefileとかを作成しておかないと問い合わせが殺到することになるかもしれないので注意。


おまけ

+ ウニバーサルロゴ
ちなみにUniversal Binaryなアプリケーションにつけるユニバーサルロゴは正式にはAppleの審査が必要らしい(?)
そこで、審査とかめんどくさかったり、厳密なテストとかしてないけど大抵の場合動くだろうなというときは「ウニバーサルロゴ」を付けるのが一部ではやってるらしい。

+ cmakeでUniversal
CMakeを使ってコンパイルするライブラリ、普通にコンパイラフラグ(CXX_FLAGとか)に「-arch i386」とかしてもエラーで止まることがある。これはcmakeが「プリコンパイル」というのをして".h"なヘッダから".gch"なヘッダを作っていることが原因らしい。
このプリコンパイルの時にコンパイラフラグが使われないことがあり、そうするとユニバーサルで作成されないので、実際のmake時に「そのアーキテクチャには対応してません」と言われるんだとか。

というわけで、cmakeでユニバーサルなバイナリをコンパイルしたいときは「CMAKE_OSX_ARCHITECTURES='i386;x86_64'」とかしましょう。

0 comments:

コメントを投稿