Intel MacでSDLを使ったプログラムをDMDでコンパイルしapp形式にする
Mac OS X 上でSDLを使ったプログラムをDMDでコンパイルしてもそのままでは起動できなくて苦労しました。
試しに以前作成したソフトウェアをMac OS X 10.4 Intel上で使えるようにしてみたのでその記録を残しておきたいと思います。
1. はじめに
ビルドに使用した環境は次の通りです。
2. SDL.frameworkを使ったビルド
の三つの方法があります。
ここでは作成したプログラムを配布する時のことを考えまして、三番目の「フレームワークを使う」方法をとることにします。
dmdでリンクするときには、リンカオプションを"-Lリンカオプション"のようにする必要があります。たとえばSDL.frameworkを使ってリンクするときには次のようになります。
$ dmd -L-framework -LSDL obj1.o obj2.o -ofprogram
otoolを使って使用しているライブラリを調べると、私のプログラムではSDL, SDL_image, SDL_mixerを使っていることが確認できます。
$ otool -L program program: @executable_path/../Frameworks/SDL.framework/Versions/A/SDL (compatibility version 1.0.0, current version 1.0.0) @executable_path/../Frameworks/SDL_mixer.framework/Versions/A/SDL_mixer (compatibility version 1.0.0, current version 1.0.0) @executable_path/../Frameworks/SDL_image.framework/Versions/A/SDL_image (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.11)
フレームワークはコンピュータ全体にインストールしてもいいですし、自分だけが使えるようにインストールしてもかまいません。
前者は"/Library/Frameworks/", 後者は"$(HOME)/Library/Frameworks/"にSDL.frameworkを配置すれば(自分の環境では)使用することができます。
3. sdlbootを使って起動する
さてビルドに成功したプログラムを実行してみると、次のようなエラーが出て起動できません。
$ ./program ****-**-** **:**:**.*** program[593] *** _NSAutoreleaseNoPool(): Object 0x******** of class NSLock autoreleased with no pool in place - just leaking (中略) ****-**-** **:**:**.*** program[***] *** Uncaught exception: <NSInternalInconsistencyException> Error (1002) creating CGSWindow Trace/BPT trap
SDLを使ったプログラムをMac OS X用にビルドするとCocoaの初期化フェーズを飛ばしてしまうのが原因のようです。詳しくはsdlbootのページを見てください。
main_hook.tgzを上記リンク先からダウンロードし展開します。
main_hook/sdlboot内のビルド済みファイルはFinkのSDLパッケージを使っているようなので、フレームワークを使ってビルドし直します。
私が修正したMakefileの内容を記述しておきます。
all: sdlboot.dylib sdlboot.dylib: sdlboot.o # $(CC) -dynamiclib -fPIC -o $@ $< `sdl-config --libs | sed s/-lSDLmain//` # $(CC) -flat_namespace -dynamiclib -init _sdlboot_init_ -single_module -fPIC -o $@ $< `sdl-config --libs | sed s/-lSDLmain//` $(CC) -dynamiclib -fPIC -o $@ $< -framework SDL -framework Cocoa sdlboot.o: sdlboot.m ../main_hook.c # $(CC) -c -fPIC $< `sdl-config --cflags` $(CC) -c -fPIC $< -framework SDL -I/Library/Frameworks/SDL.framework/Headers -framework Cocoa clean: rm -f *.o *.dylib
ビルドしたプログラムと同じ場所にsdlboot, sdlboot.dylibをコピーし、
$ ./sdlboot ./program
のように使うとやっと起動することができるようになりました。
(おまけ) gdcを使ってビルドする場合
Mac OS X用のgdcを使っている場合はSDLのDevelopment用のアーカイブの中にあるSDLMain.mを使うことで起動できない問題を回避できます。
D言語プログラムをgdcでビルドするときには、
// via http://gamehell2000.googlecode.com/svn/trunk/omega/koke/boot.d version (darwin) { extern (C) int _d_run_Dmain(int argc, char* argv[]); extern (C) int SDL_main(int argc, char* argv[]) { return _d_run_Dmain(argc, argv); } }
というコードを追加しておき、SDL-devel-1.2.X-extras.dmgの中のSDLMain.mと一緒にコンパイルすることで正しい順番でCocoaアプリケーションの初期化作業が行われるようになります。
この方法ですとsdlbootを使わずに起動できるようになるのでdmdと比べると簡単です。
4. .app形式の作成
最後にせっかくですのでパッケージ化してみます。MacでSDLアプリケーションの作り方を見ると、Info.plistを作らなくても実行できることがわかりました。簡単なのでその方法にすることにします。
4.1. 必要なファイル構造を作る
$ mkdir -p test.app/Contents/MacOS $ cp program sdlboot sdlboot.dylib test.app/Contents/MacOS
この状態でtest.appを開くとtest.app/Contents/MacOS/testを実行してくれるようです。
4.2. sdlbootでprogramを起動するスクリプトを作る
上述のようにtest.appを開くとtest.app/Contents/MacOS/testを実行してくれますが、今回は引数を渡す必要があるのでシェルスクリプトを使います。WindowsやLinuxなどで動くプログラムと同じソースコードが使えるようにするため、カレントディレクトリをプログラムと同じ場所に変更するスクリプトを用意します。
$ echo -e '#!/bin/sh\ncd (dirname $0)\n./sdlboot ./program' > test.app/Contents/MacOS/test $ chmod +x test.app/Contents/MacOS/test
4.3. アイコン画像
アイコン画像は、Finderからアプリケーションの「情報を見る」→アイコンのペースト、で貼り付けることができます。
パッケージの作成に必要な作業はこれだけですが、
- PROGRAM.app/Contents/MacOS/ にPROGRAMを置けばそのファイルが実行される
- sdlbootを使った場合は、メニューバーに表示されるプログラム名は実際に実行しているファイル名になる
- アプリケーションの情報を見ても著作権情報やバージョンが表示されない
- アイコンを付けるとPROGRAM.app/ContentsにIconという名前の不可視ファイルが作成される
という制限があります。ちゃんとInfo.plistを書いてPROGRAM.app/Contents/に置いた方がユーザフレンドリーなプログラムになるでしょう。
5. 配布するとき
開発環境のみで起動するにはこのままでいいですが、使用しているフレームワーク(今回はSDLフレームワーク)を
PROGRAM.app/Contents/Frameworks
におくことで、フレームワークがインストールされてない環境でもSDLを使うことができます。
またアイコンをFinderから貼り付けた場合にはアイコンファイルがパッケージ内に保存されているので、dmg(ディスクイメージ)やFinderからzip書庫にまとめた方がいいでしょう。
6. 参考にしたページ
- MacOSX版Ruby/SDLバイナリの作成方法
- Info.plistの書き方にも触れられています。
- MacOSX+SDLでの配布物作成法
- SDL.frameworkを使ってビルドする方法など。