ラベル Xcode の投稿を表示しています。 すべての投稿を表示
ラベル Xcode の投稿を表示しています。 すべての投稿を表示

2014年1月7日火曜日

LLDB のカスタムコマンドを Python で書いてみようとして大失敗した話

なんかうまくいかないんです(´・_・`)

Xcode 5のデバッガとして用意されているLLDBですが、実は設定ファイルを書くことで自由にカスタマイズすることが可能になっています。またPythonを使ってより深いLLDB自体の挙動をカスタマイズすることも出来るらしいと最近教えてもらいました。

参考: http://qiita.com/dealforest/items/e3a5284badd17733ccc1

さてこちらの参考記事に、
例えば動的に生成した UIImage をファイルに出力するコマンドとかは便利そうですね。
というなかなか夢のある発言があるのですが、残念ながらこちらの記事の中では実際のコードがありません。ということでLLDBの設定の練習がてら、私の方で早速デバッガからUIImageをファイルに書き出すコマンドの作成にチャレンジしてみました。

■実装方針

LLDBを操作するために使用できるPythonモジュールについては、LLDBの公式ページに詳しいドキュメントが揃っています。
http://lldb.llvm.org/python-reference.html (簡単な解説とチュートリアル)
http://lldb.llvm.org/python_reference/ (APIドキュメント)

また既に何人かの方が似たようなことをされた形跡があります。
http://stackoverflow.com/questions/12668815/lldb-python-access-of-ios-variables
http://stackoverflow.com/questions/18468126/pointer-arithmetic-in-lldb-python-scripts
http://lists.cs.uiuc.edu/pipermail/lldb-dev/2011-January/000321.html

これを元にして、以下の様な戦略でいってみます。

  1. 引数のUIImageをUIImagePNGRepresentation関数を通してNSDataにする。
    • ただのUIImageをそのままファイルに書き出しても使えませんので、まず一旦JPEGなりPNGなりに変換する必要があります。ここではObjective-C側に変換してもらうことにします。
  2. NSData bytesを取得する。
    • expr (const void *)[(NSData *)UIImagePNGRepresentation(image) bytes]とか叩けば一発なはずなので、その方針で行きます。
  3. ファイルに書き出す。
    • exprの結果をraw dataで取り出せれば、あとは普通にファイルに書き出しておしまいです。簡単そうですね。
ということで出来たコードはこちら。


■しかし

やはりというか落とし穴がorz
冒頭の画像にもあるのですが、Python経由でexprした実行結果を取得する手段がどうにも見つかりません。SBFrame.EvaluateExpression(expr)の結果がSBValueなんだからそっからデータが引けるだろうと思っていたのですが、何を実行しても壊れたSBValueしか返却されないためどうにもうまくいきません。SBDebuggerやSBCommandInterpreter経由でHandleCommand(expr, result)する手も考えたのですが、今度はresultから値を取る手段がない(resultをコンソールではなくファイルにリダイレクトする手段しか無い)ためやはり断念です・・・

うーん。もう少し頑張ったらうまくいきそうな気もするのですが。残念です><


2014年1月6日月曜日

Spark Inspectorを一ヶ月ほど使ってみた感想


ちょっと仕事で複雑なiOSの画面を作りたいということになり、デバッグに難儀していた所、Spark InspectorというiOS向けのランタイムインスペクタツールが発売されていましたので、やや乗り遅れた感がありますが私も早速試してみました。

Spark Inspectorを使うと、

  • リアルタイムにViewの構造を視覚的に確認し、直接プロパティの値を編集してアプリに反映させることができる
    • Webエンジニアの人にわかりやすくたとえるならば、ChromeやFirefoxなどに付いているインスペクタをiOS上で使えるようなものです
  • その他、アプリ内を飛び交っているNSNotificationの状態を監視することができる(らしい)

既に試された方のブログ記事で導入方法など詳しく公開されていますので、まずはそちらを見ていただくのが良いかと思います。
Spark Inspectorを導入してみた
http://inon29.hateblo.jp/entry/2013/12/06/002948
[Xcode][tool] ランタイムデバッガーSpark Inspectorが便利!
http://blog.natsuapps.com/2013/05/spark-inspector.html
ということで、私は一ヶ月ほど実際に仕事で使ってみて良かった点と悪かった点をまとめてみようかと思います。興味ある方の参考になればと思います。

■良かった点

導入が非常に楽です。ブログ記事で既に紹介されている方法は自分のプロジェクトに必要なフレームワークを組み込んで設定する方法ですが、最近のSpark Inspectorはこの方法を用いなくてもXcodeから一発で必要なフレームワークを動的に読み込んでインスペクタを起動してくれます。

Viewの階層構造をリアルタイムに監視できるのはやはり非常に有効です。これまではUIView- (void)recursiveDescriptionプライベートメソッドを呼び出したりして状態を確認したりしていたのですが、実行中の画面に対して動的に状態を見て、動的にプロパティの値を書き換えて見た目を調整することができるのは劇的に作業効率を改善してくれます。これってWebプログラミングだと実に当たり前のことなんですが、iOSプログラミングだと余りまだ普及してない考えだなと思います。

動作はやや重いですが、2012年モデルのMacBook Airでも問題なく動作させることが出来ました。もちろんiPadのアプリでもちゃんと動作します。

■イマイチだった点

楽に導入できる、自分のプロジェクトに必要なフレームワークを組み込まないやり方では、いくつか制限があります。

  • 自分のプロジェクトに必要なフレームワークを組み込まないと、実機でSpark Inspectorを動作させることが出来ない。
  • 動作が非常に安定しない。プロジェクトによってはかなりの確率で起動に失敗したりする。今回私が仕事で使っていたプロジェクトでは8割ぐらいの確率で起動に失敗していた。

それから地味に痛いのが、表示と編集が可能なのが現状CALayerとUIViewのプロパティのみに制限されているところです。Auto Layoutには対応していませんし、例えばUIScrollViewのcontentInsetを動的に調整してみたりですとかUIButtonの横幅とタイトルを動的に調整してみたりですとかそういう小技が出来ないため、なんというかかなり中途半端感を受けます。結局私の場合は表示されているはずなのに表示されていないViewの構造を調べる程度の用途にとどまりました。

2013年12月9日月曜日

Xcode 5 で、発生した NSException の詳細が表示されない時の対処法

原因がよくわからないのですが、iOSアプリをデバッグ中にNSExceptionが発生してアプリがクラッシュしてしまった時、その詳細がXcodeのコンソール上に表示されなくなってしまいました。普通はデフォルトのexception handlerがうまい具合にやってくれるのですが、何らかの理由でそれがうまくいかない場合があるようです。自分でスレッド立ててるとかでしょうか・・・


上図のように、例外が発生している箇所にブレークポイントをおいてどこで発生したのかを知ることはできるのですが、実際には発生箇所がわかっても発生原因がさっぱりわからないというケースもあります。例えばiOSのシステムが例外を発生させたときや、コードが公開されていないライブラリが例外を発生させたときなどです。

さて、このようなときは発生しているNSExceptionのdescriptionを直接読めれば便利そうです。というわけで調べてみました。

http://stackoverflow.com/questions/384775/how-do-i-find-out-what-exception-was-thrown-in-the-xcode-debugger-for-iphone
After you stop in the debugger, you can enter po $eax (simulator) or po $r0 (device) to see the exception. This is because the exception object is passed as the first argument to objc_exception_throw, which is kept in register r0 or EAX.
こちらの投稿のコメントにちょっとだけ書かれている方法がどうやら一番良さそうです。というわけで試してみました。
  1. まずはプロジェクトのAll Exception Breakpointを有効にします。
  2. アプリをデバッグして、例外が発生する箇所を実行します。
  3. 例外発生箇所でブレークポイントが発動したら、左のスタックトレースナビゲーションから、スタックトレースの一番上(objc_exception_throw)を選択します。
  4. コンソール上で以下のコマンドを実行します。
    • シミュレータなら、po $eax
    • 実機上なら、po $r0
うまくいきました!これで万一発生した例外の原因がよくわからない場合でも安心です。

余談ですが、eaxレジスタ/r0レジスタは関数の引数だかなんだかに相当するレジスタみたいなので、よくわからないコードで詰まっている場合はここを見れば他のケースでも解決できるかもしれません。

2013年10月30日水曜日

【誰得】ncursesをiOS 7向けにビルドしてみた



久々のブログ投稿がこんな内容でほんとすみません。

ややタイトル出落ち感がありますが、ncursesをiOS向けにビルドしてみました。環境は以下のとおりです。

  • OS X 10.8.5 (Mountain Lion)
  • Xcode 5.0 & iOS SDK 7.0
  • ncurses-5.9
  • TerminalにはOS X付属のTerminal.appを使用

※注意: 本当に使えるかどうかまでは試してません。ちょっと根性が足りませんでした。あくまでiOS向けにC/C++で書かれたライブラリをビルドして使えるようにするときの参考になったりならなかったり程度でお考えください。

というわけで、まずはncursesのソースコードをhttp://ftp.gnu.org/gnu/ncurses/あたりからサクッと拾ってきます。今回はncurses-5.9を使用しましたがどのバージョンでも大して変わらないと思います。

あとはconfigureしてmakeすればOKですが、configureが少々厄介です。そのままだとerror: Cross-build requires two compilers.とか言われてしまいますので、./configureの当該箇所をコメントアウトしてからオプションを付けて実行してください。それから./configureを実行した端末エミュレータ向けにビルドされてしまうようで、その挙動を変更するオプションが見つからなかったのでこちらも注意してください。私は今回xterm向けにビルドしてしまいましたがxtermエミュレータが用意できない場合はVT100などに変更した上でビルドしたほうが良いかと思います。



うまくいけば無事ビルドが通ってタイトルの画像のようになります。
CFLAGSでarchとisysrootを指定してビルドする手法は他のautoconfを使っているオープンソースのライブラリでも応用できそうですね。

さてlibncurses.aはビルドできたのですが、このままでは端末エミュレータがなくてどうにもならないので、iOS用の端末エミュレータを用意しなければなりません。幸いにしてオープンソースのmobileterminalというプロジェクトを見つけましたので、こちらをお借りしましょう。ライセンスがGPLですがどうせこんな酔狂なものを人様の前に晒すことは決してないはずなので問題ありません。

あとはhttp://www.kis-lab.com/serikashiki/man/ncurses.htmlでも参考にncursesでのCUIプログラミングをお楽しみください。多分、きっと、運が良ければ、動くと思います。


参考文献:
http://d.hatena.ne.jp/maminus/20100129/1264781242 - configureのbuild、host、targetの違い
https://twitter.com/moriyoshit - @moriyoshi

2012年7月25日水曜日

LLDB を使って CUI で動作させているテストケースをデバッグする

GHUnit のテストケースを Jenkins で自動的に走らせるために CUI のテストを作っているのですが、 GUI を使ったテストでは問題なく通るのに CUI を使ったテストのときだけテストが落ちるという問題が発生してしまいました。 Xcode 経由の実行であれば Instruments だろうが内臓のデバッガだろうが使い放題なのですが、 CUI となるとそうもいきません。そこで LLDB を直接使って CUI で動作させているテストケースを直接テストしてみることにしました。

参考にしたのはこちら。
http://lldb.llvm.org/tutorial.html

さっそくやってみましょう。lldbコマンドで LLDB の対話環境を起動した後、
process attach --name MyApp --waitfor
でMyApp.appという名前のプロセスが立ち上がるのを監視します。



この状態でMyApp.appというアプリを起動すると、上の画像のようにばっちり LLDB がプロセスを検知して捕まえてくれます。今、MyApp.appはポーズ中になっているので、適当な箇所に
breakpoint -f MyTestModel.m -l 123
とかやって適当にブレークポイントを設置した後、
thread continue
で続きを実行開始します。



ご覧のとおりバッチリブレークポイントで捕まえることに成功です。こちらの図はEXC_BAD_ACCESSが発生した時になんか自動的に止めてくれたときのものです。Xcode経由で起動するときに比べれば不便ですが、何もないよりはだいぶはかどりますよ。

2012年1月24日火曜日

Jenkins を iOS アプリ開発に導入してみた (gcov編)


前回 はSenTestKitに続いてGHUnitを導入して、Jenkins上でGHUnitによるテストの自動実行を行いました。今回はさらにステップアップして、gcovを使用してコードカバレッジを取るようにしてみようかと思います。


■gcovを使ったコードカバレッジの取得(Xcode 4.3版)

Xcode 4.3の場合は以下の参考ページで紹介されている方法がオススメです。実機ででもシミュレータでも動作してお得です。
http://www.cocoabuilder.com/archive/xcode/314794-xcode-4-3-moved-libprofile-rt-how-to-reference-it-now.html
http://www.infinite-loop.dk/blog/2012/02/code-coverage-and-fopen-unix2003-problems/
https://github.com/InfiniteLoopDK/ILTesting
  1. Generate Test Coverrage FilesとInstrument Program FlowにYESを指定する
  2. http://www.infinite-loop.dk/blog/2012/02/code-coverage-and-fopen-unix2003-problems/で紹介されているILProfilerCompat.cを組み込む(これによってfopen$UNIX2003によるエラーを防ぐ)
これだけで大丈夫です。


■gcovを使ったコードカバレッジの取得(Xcode 4.2版)

【2012/03/15追記】こちらの方法はXcode 4.2が前提となっており、Xcode 4.3以降は動作しません!

gcovとはgccに付属されているC言語用のカバレッジ測定ツールです。これを使うことで、C言語と、その拡張であるObjective-Cのコードカバレッジをバッチリ取得することができます。ということで早速プロジェクトに組み込んでみましょう。幸いにしてすでに先駆者の方がいらっしゃいました。
http://tech.naver.jp/blog/?p=706
基本的に上記に記載されている通りの手順で導入すれば簡単にgcovによるコードカバレッジの取得ができますが、実際にやってみたところ幾つか補足があるので追記します。

先ほど紹介した記事では/Developer/usr/lib/libprofile_rt.dylibの追加と-lgcovの指定両方を行なっているのですが、私が試したところどちらか片方だけで成功するということがわかりました。具体的には以下のとおりになります。

1. /Developer/usr/lib/libprofile_rt.dylibを使うパターン
2. -lgcovを使うパターン

それぞれ見ていきます。

1. /Developer/usr/lib/libprofile_rt.dylibを使うパターン

/Developer/usr/lib/libprofile_rt.dylibをプロジェクトに追加し、Other C Flagsの-fprofile-arcs -ftest-coverageを設定すればOKです。それ以外の手順は必要ありません。
このパターンですとスムーズにgcovの結果を出力することができましたが、残念ながらこの方法はSimulator上でしか使用できません。というのも、/Developer/usr/lib/libprofile_rt.dylibがそもそもarmv6, armv7用のバイナリを含んでいないという問題があるのと、ライブラリ探索パス"/Developer"以下がDeviceビルド時に勝手にiphoneos.sdk以下の/Developerに置換されてしまうため、ビルド時にlibprofile_rt.dylibをリンカが発見できずエラーになる問題があるためです。基本的にSenTestingKitのようなSimulatorでしか実行しないビルドターゲットでやるのが良いと思われます。

2. -lgcovを使うパターン

参考: http://stackoverflow.com/questions/5101014/code-coverage-not-showing-results-using-xcode-gcov/5140459#5140459
-lgcovをOther Linker Flagsに追加し、Generate Test Coverrage FilesとInstrument Program FlowにYESを指定すればOKです。それ以外の手順は必要ありません。
このパターンですとlibgcovはarmv6, armv7でも動作するので実機でのカバレッジ取得ができるのですが、問題はそのままの設定でビルドして実行すると実行時にクラッシュします。これはlibgcovが実行時にカバレッジデータを書きだす際に、デフォルトの設定ではiOSのサンドボックスの外にアクセスしようとするため書き出しに失敗してしまうのが原因です。したがって上記参考URLに従ってGCOV_PREFIX環境変数を使いサンドボックス内部にカバレッジデータを書きだすように指定してやる必要があります。
この方法は環境変数を実行時に設定する必要があって面倒なのと、カバレッジデータが書き出される箇所がJenkinsのビルドディレクトリの中ではなくiOSシミュレータまたは実機のサンドボックスの中になってしまうため、Jenkinsからカバレッジの取得を行うのが困難になってしまう問題があります。

今回は1. のパターンを採用し、SenTestingKit上でSimulatorビルド時のカバレッジ取得だけを行うことにしました。GHUnitでも実機上での動作を諦めれば問題ないのですが・・・まだ課題が多い感じですね。

ビルド設定ができたらビルドして、きちんとgcovコマンド経由でカバレッジが取得できていることを確認しましょう。


■gcovrを使ってCoberturaのXML形式に変換する

gcovのカバレッジデータの取得ができるようになったら、今度はJenkinsと連携させるために、gcovの出力をCoberturaというJava環境のカバレッジ計測ツールの出力するXML形式に変換する必要があります。これはJenkinsがgcovのカバレッジデータ出力の集計に対応していない為です。っていうかgcovの出力は単なるタブで区切られた文字列で人間以外にはとても優しくないので集計ができないのです。
ここで、Python製のgcovrというツールをJenkinsサーバマシンに導入します。 gcovrをgcovの代わりに使うことで、CoberturaのXML形式で結果を出力できるようになるほか、カバレッジ計測対象となるファイルを簡単にフィルタリングできる(たとえばテストケースクラスのカバレッジなんか集計してもしょうがないので除外するなどできる)のが大きな魅力です。
http://wiki.hudson-ci.org/pages/viewpage.action?pageId=45482230
https://software.sandia.gov/trac/fast/wiki/gcovr

インストールはMacマシンであれば非常に簡単で、以下のコマンドを打つだけです。
sudo easy_install gcovr
インストールが完了したら、あとは
gcovr --xml --output=cobertura-report.xml build/
とかやれば一発でbuildディレクトリ配下のすべてのカバレッジを計測結果を集計してXML形式で書きだしてくれます。
ここでもし、テストケースのカバレッジは除外したいなどという要件がある場合は、
gcovr --xml --exclude=".*/.*Test(s)?\.[(c)(cpp)(m)(mm)]" --output="cobertura-report.xml" build/
などとすればOKです。


■JenkinsのCoberturaプラグインを使って結果を集約する

CoberturaのXML形式で結果が取れるようになってしまえばあとは楽勝です。JenkinsにCoberturaプラグインをサクっとインストールして、
https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin
あとは以下のようにJenkinsのジョブ設定を書いてしまうだけです。先述の通り、SenTestingKit上でSimulatorビルド時のカバレッジ取得だけを行いたいので、既存のSenTestingKit用単体テストジョブのXcodeビルドの後でgcovrを実行するシェルスクリプトを実行してやれば一発です。


カバレッジが取れるようになると断然見栄えがしますね。

2012年1月23日月曜日

Jenkins を iOS アプリ開発に導入してみた (GHUnit編)


前回 はSenTestKitを用いてJenkins上で単体テストの自動実行を行いました。今回はGHUnitを使った単体テストの自動実行にチャレンジしてみたいと思います。またついでといっては何ですが、単体テスト時に必要になってくるモックを作成するためのライブラリOCMockも同時に導入してみようと思います。


■なぜGHUnitを使うのか

GHUnitを使うことで、SenTestingKitと比べて以下のようなメリットが得られます。
  • 非同期処理のテストを行うための仕組みが用意されている(GHAsyncTestCase)これをSenTestingKitないし他のテスティングフレームワークでやろうとすると大変骨が折れます。
  • .app形式(要するに実際のiOSアプリケーション)でテストを実行するため、UIApplicationやUIWindowといったUIコンポーネントを使うクラスのテストが可能になる。UIテストの支援をするための仕組みも最近のGHUnitには追加されている。
    • SenTestingKitの"Logic Test"では想定通りに動作しないUIKitやFoundationのクラスが幾つかあり、中には純粋なロジックで使いそうなクラスも含まれている。そのためSenTestingKitではテストできないロジッククラスが発生する場合があるので、そういう時はGHUnitを使うと良い。
    • 参考: https://gist.github.com/1662887
  • 実機上でテストを実行できる。そのため実機でのみ発生するバグをつかまえることが可能。
  • SenTestingKitのテストケースをそのまま実行できるため上位互換として使用できる。
逆にGHUnitにはSenTestingKitと比べて以下のようなデメリットがあります。
  • Xcode 4で統合されたUnitTestingの恩恵に預かれない。すなわちCmd+U一発でテストケースを実行することができない。Xcode経由だとCmd+Rでアプリケーションとして起動して、その後起動したアプリのテスト実行ボタンを押す手間が必要になるため、開発のテンポが乱されてしまう。特にTDDを採用している場合には深刻。
  • 上記問題を回避するためCUI経由でテストを実行することもできるのだが、Xcodeのエディタと連携しないため、どこがエラーになっているのかが一目でわかりづらい。
  • カバレッジの取得が楽。SenTestingKitをシミュレータ上で実行している時が一番やりやすい。これについては別の記事で解説します。
これを踏まえると、以下のように使い分けができるようになります。
  • SenTestingKitは純粋なロジックの単体テストを実行したいときに良い。Xcodeからすぐに実行できて一瞬で結果がコード上に出るので、開発のテンポを乱さない。TDDに向いている。
  • GHUnitは非同期なAPIを持ったクラスの単体テストを実行したいときに良い。またUIApplication, UIWindowといったUIコンポーネントと結合したり、通信やファイルアクセスなどの外部リソースとの結合をおこなった状態でのテストを作る際にも向いている(純粋な単体テストともユーザー受け入れテストとも異なるため、仮に結合テストと呼ぶことにする)ので、そういったものが必要な場合には最高の選択肢となる。
プロジェクトの開発方針や開発対象によってどのようなテストが必要になるのかが異なってくると思いますが、どれか1つだけ必要であればGHUnitを選択し、きちんとしたXPでやりたいプロジェクトはSenTestingKitに単体テストをやらせ、ユーザー受け入れテストに KIF などを採用し、その中間を埋める必要があればGHUnitも導入する、というのが良いのではないかと考えています。ちょっと面倒ですけど。


■GHUnitを導入する

以下のリポジトリからコードを取得して、XcodeからビルドすればOKです。
https://github.com/gabriel/gh-unit
注意点として、GHUnitは.framework版がそのままgithubからダウンロードできるのですが、
https://github.com/gabriel/gh-unit/issues/69
このような問題が報告されており使えないので、githubからソースコードをクローンしてきて、
cd Project-iOS
make
でビルドしてできたGHUnitIOS.frameworkを使うようにしてください。

frameworkをビルドしたら自分のプロジェクトに追加して、GHUnit用のビルドターゲットを作ります。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_install_ios_4.html

ビルドターゲットができたら、次は試しにテストケースを追加してみて、問題なくテストが走るか確認。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_testing.html

最後にCUI(要するにxcodebuildコマンド経由)でビルドができるようにします。これをやらないとJenkinsと連携できません。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_command_line.html


■OCMockを導入する

以下のリポジトリからコードを取得して、XcodeからビルドすればOKです。
https://github.com/erikdoe/ocmock
iOS向けビルドはスタティックライブラリ用のビルドしか用意されていませんので、どうしてもフレームワークが良いとこだわる人は頑張ってください>< そうでなければ非常に簡単です。ビルドしたら成果物をそのままプロジェクトに突っ込めばすぐ使えるようになります。
OCMock自体の使い方にはここでは触れませんが、例えば以下のようなテストケースが作れたりします。

- (void)testOCMock
{
    // Creating a new stub object from class
    {
        id mockedObject = [OCMockObject mockForClass:[NSString class]];
        STAssertThrows([mockedObject length], @"Mocked object raises exception because the fake method is not ready yet.");
        STAssertThrows([mockedObject isKindOfClass:[NSString class]] , @"Mocked object can't even call isKindOfClass: because it's not ready.");
        STAssertFalse([mockedObject class] == [NSString class], @"Mocked object is not a kind of the target class.");

        [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length];
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value.");

        [[[mockedObject stub] andReturn:@"AllYourBaseAreBelongToUs"] lowercaseString];
        STAssertEqualObjects([mockedObject lowercaseString], @"AllYourBaseAreBelongToUs", @"Returns mocked value.");
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Previously mocked methods are still valid.");
    }

    // Method Stubbing for existing object
    {
        NSString *stubTargetObject = @"I am a stub target.";
        STAssertEquals([stubTargetObject length], (NSUInteger)19, @"The original implementation of the length method.");

        id mockedObject = [OCMockObject partialMockForObject:stubTargetObject];
        STAssertTrue([mockedObject isKindOfClass:[NSString class]] , @"Mocked object is a kind of the target class.");
        STAssertEquals([mockedObject length], (NSUInteger)19, @"Mocked object returns the original value.");

        [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length];
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value.");
        STAssertEquals([stubTargetObject length], (NSUInteger)100, @"Stubbed target object also returns the fake value.");
    }
}
- (NSUInteger)mockedLength
{
    return 100;
}


■Jenkinsと連携させる

最後にJenkins側でビルドターゲットを作りましょう。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_ci.html

こんな感じの設定になると思います。

あとはいつもどおりジョブを回してみて問題がないか確認すれば完了です。前回ご紹介したようなSenTestKitの単体テストジョブがうまく回っていれば、それをコピーしてきてちょっと設定を変えれば簡単にできるとおもいます。

2012年1月20日金曜日

Jenkins を iOS アプリ開発に導入してみた (SenTestKit編)


最近、iOSアプリの開発でも継続的インテグレーション(CI)を取り入れていくプロジェクトが増加傾向にあるようで、各種ツールやライブラリ、ノウハウが出回ってきているように感じられます。そこで私も早速iOSアプリ開発でのCI導入を試してみることにしました。今回の導入試験では、以下のような環境を想定して行いました。
  • iOSアプリの開発を、Xcode 4.X系のプロジェクトとして行う。
  • VCSにはgitを採用し、githubの公開リポジトリをリポジトリサーバーとして使用する。
  • CIサーバにはMacを採用し、プロジェクトをビルドするためにXcode 4.Xをインストールしておく。


■必要なツールを準備する

CIといったら、まずは何はなくともJenkinsです。
http://jenkins-ci.org/
ここでは導入について詳しくは挙げませんが、私は以下の本を参考にしました。
https://gihyo.jp/dp/ebook/2011/978-4-7741-4952-3

続いてJenkinsのプラグインを導入します。
これでJenkinsの準備はだいたい完了です。
あとはVCSのgitですが、こちらは準備が出来ているという想定で進めます。


■余談:xcodebuildの使い方

JenkinsのXcodeプラグインを使えば、自分で面倒なantのbuild.xmlを書いたり、ビルドスクリプトを用意しなくても、JenkinsのGUI上でビルド設定を行うことが出来ますが、念のため基本を理解しておくべく、xcodebuildというツールを使ってみます。xcodebuildはXcodeプロジェクトをCUIからビルドするためのツールで、Xcodeに付属しています。JenkinsのXcodeプラグインも内部的にはこいつを使用しています。

基本的な使い方の例は以下の通り。
xcodebuild -project MyApp.xcodeproj -configuration Release -target MyApp clean build
xcodebuild -project MyApp.xcodeproj -configuration Debug -target MyAppTests -sdk iphonesimulator5.0 clean build
xcodebuild -project MyApp.xcodeproj -configuration Debug -target MyAppTests -sdk iphonesimulator clean build
上の例ではデフォルト設定のiOS SDKを用いてビルドターゲットMyAppがReleaseビルドされます。次の例ではiPhone Simulator 5.0 SDKを用いてビルドターゲットMyAppTestsがDebugビルドされます。一番下の例では、手元にある最新のiPhone Simulator SDKが使用されるようです。また -target の代わりに -scheme を使うことも出来ます。ビルドに使うことができるSDKの一覧を得るためには、
xcodebuild -showsdks
を使えばよいです。

ビルドアクションにはclean, build以外にもarchiveとかあるのですが試していないので詳細は不明です。しかし残念ながら、少なくともtestアクションがないことは確認しました。そのため、Xcode上で Cmd+U を押して実行できる単体テストが、xcodebuild経由では実行できません。これについては後ほどまた触れます。


■Xcodeプロジェクト側の準備

Jenkinsによる自動化を行うためには、まずはXcodeプロジェクト側を修正して、xcodebuildツールにより単体テストが実行できるような状態にして置かなければなりません。そのため先にXcodeプロジェクト側の設定を修正します。Xcode 4でテストケース付きの新規プロジェクトを作るとテストケース実行用のターゲットが自動的に用意されると思いますが、先述の通りこのターゲットはxcodebuild経由では実行できないため、以下の画像の様にUnit Testing -> Test Hostを削除して対応します。


このように設定を変更することで、UIWindowやUIApplicationを使わなければならないテストが実行できなくなりますが、それ以外のテストはXcodeからもxcodebuildからも実行できるようになります。試しにxcodebuildを使ってテストターゲットを実行し、テストが走っていることを確認してください。


■Jenkins側の準備(ジョブの設計)

続いていよいよJenkinsの設定に移ります。プラグインは導入してあるので、あとはジョブを作るだけです。Xcode連携のプラグインが入っていれば楽勝です。今回はシミュレータビルドで単体テストを行うので、ビルド後の処理でテスト結果の集約を行い、test-reports/*.xmlを指定しましょう。


デバイスビルドでリリース用のビルドを作る場合には、ビルド後の処理でビルド成果物の保存を行い、build/Debug-iphoneos/*.ipa(必要ならdSYMも)などを指定しておけば良いかと思います。デバイスビルドをするにはビルドをするマシンのKeychainにcodesign用の証明書と鍵が格納されている必要があるので注意してください。Technical VersionやMarketing Versionの値を指定すれば、自動的にInfo.plistに指定されているCFBundleVersionやCFBundleVersionShortStringを置換してくれるので非常に便利です。


ジョブを作ったら早速実行してみましょう。おそらくビルドだけならあまり苦労せずに通ると思います。私は10回目のビルドで完全に問題なくビルドが回りだすようになりました。

2011年5月29日日曜日

-ObjC とか -all_load って何をやってるのか調べてみた

よく外部のライブラリやFrameworkをiOSのプロジェクトに取り込むときに、つけないと動かないからつけてねと言われる、 Other Linker Flag = -ObjC -all_load のフラグ。これって何をやっているのか今までずっと気になっていたので、ちょっと調べてみました。

こういうことらしいです。
http://developer.apple.com/library/mac/#qa/qa1490/_index.html
This flag causes the linker to load every object file in the library that defines an Objective-C class or category. While this option will typically result in a larger executable (due to additional object code loaded into the application), it will allow the successful creation of effective Objective-C static libraries that contain categories on existing classes.
このフラグは、Objective-Cのクラスまたはカテゴリを定義しているライブラリに含まれる、全てのオブジェクトファイルをロードするようリンカに促します。これによってたいていの場合実行ファイルのサイズが大きくなってしまいますが(アプリに追加のオブジェクトコードが読み込まれるので)、既存のクラスに対するカテゴリを含むObjective-C静的ライブラリを正しく使えるようになります。
へぇぇ!全く知らなかったです。ちなみに -all_load はというと、
-all_load forces the linker to load all object files from every archive it sees, even those without Objective-C code. -force_load is available in Xcode 3.2 and later. It allows finer grain control of archive loading. Each -force_load option must be followed by a path to an archive, and every object file in that archive will be loaded.
-all_load は全てのオブジェクトファイルを全ての認識できる静的ライブラリ(アーカイブ)から全部ロードしてくる(意訳)
とかそういうことらしいです。なるほどねー。

ということは -all_load はより強力な -ObjC に見えますが、実際には -ObjC は -all_load と違って上記に記載されているオブジェクトファイルのロードの挙動変化以外にも何かやっているようで、実際 -all_load -ObjC 両方指定してあるとリンクできるのに、 -all_load だけではリンクが通らない場合がありました。なのでやっぱり必要なときはまず -ObjC だけを試してみて、それでも駄目なら両方設定するのがいいみたいですね。


余談ですが、上記の内容を調べていたとき、リンカとかローダについて詳しく学ぶには以下の本がよいとオススメしていただきました。

Linkers and Loaders (日本語訳版もありますが、訳がひどいので英語版の方がよいとのことです。ここでは日本語版のリンクはあえて紹介しません)
http://linker.iecc.com/

4789838072リンカ・ローダ実践開発テクニック―実行ファイルを作成するために必須の技術 (COMPUTER TECHNOLOGY)
坂井 弘亮
CQ出版 2010-08

by G-Tools


KindleかiBooksで欲しかったのですが、残念ながら紙の本しかないですね・・・><

2011年5月5日木曜日

atos を使ってアプリのクラッシュログを symbolicate する方法

iOSアプリがクラッシュするとクラッシュログがデバイスに残され、 Xcode のオーガナイザーからログを取得してバグの原因を解析できるのは皆さんご存知の通りだと思います。このとき、基本的には Xcode がクラッシュログがの中のシンボルを自動的に読める状態にしてくれる (symbolicate) のですが、どうも Xcode 4 になってからこの symbolicate がいまいちよく動いてくれないので、手動で symbolicate をする方法を調べてみました。

参考にしたページは以下の通り。
http://stackoverflow.com/questions/1460892/symbolicating-iphone-app-crash-reports


■atosの使い方

symbolicate をするには Xcode に付属している atos というコマンドラインツールと、ビルドの際に生成される dSYM と呼ばれるファイルを使います。

まずは dSYM ファイルを探します。 Xcode 4 の場合は、デフォルトで *~/Library/Developer/Xcode/DerivedData/プロジェクト名-ランダムな文字列/Build/Products/ビルド設定名/アプリ名.app.dSYM* に生成されるようになっているはずです。

dSYM ファイルが見つかったら、コンソールから atos を実行します。 atos の使い方は以下のとおりです。
atos -arch アプリがクラッシュしたデバイスのアーキテクチャ -o アプリ名.app.dSYM/Contents/Resources/DWARF/アプリ名 クラッシュした関数またはメソッドのアドレス
実際にやってみるとこんな感じです。
cd ~/Library/Developer/Xcode/DerivedData/MyProject-xxxxxxxxxxxxxxxxxxxxxxxxxxxx/Build/Products/Debug-iphoneos/
atos -arch armv7 -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp 0x00072ce6
これで指定されたアドレスが symbolicate され、シンボル名および元のソースコード上の行数まで表示してくれます。超便利です。


■注意点

何回か試してみましたが、ときどき実際にクラッシュしたシンボルと違うシンボルが表示されることがあるみたいです。上記で紹介したやり方が間違っているのか、それともそういうものなのかわからないのですが・・・すんません>< しかしその場合でもだいたい表示されたシンボルの近くが問題の原因だったため、いまのところ参考ぐらいには使えています。

2011年5月3日火曜日

Xcode のビルド設定で $(inherited) を使って設定を継承する



AppleのXcode 2のころのドキュメントによるとXcodeのビルド設定は以下の順に設定内容が解決されていきます。
  1. ユーザーのデフォルトの環境変数設定(たいていは ~/.MacOSX/environment.plist で指定されている)
  2. Xcodeのビルトインデフォルト設定
  3. Xcodeのアプリケーション設定(これはXcode 4では廃止されている気がします)
  4. ベースとなるビルド設定ファイル(元のドキュメントには書いてありませんが、.xcconfigファイルを使ってビルド設定を指定可能です)
  5. プロジェクトのビルド設定
  6. ターゲットのビルド設定
  7. コマンドラインから起動した場合は、コマンドラインのビルド設定フラグの値
このとき、下位の設定は上位の設定を上書きして使用するようになっているのですが、ここで上位の設定をそのまま受け継いで使いたい場合には、設定項目に$(inherited)という特殊な値を指定することが可能です。

2011年4月17日日曜日

Xcode 4 で scheme が My Mac 64bit になって iPhone 向けのビルドが出来なくなった時の対処法



最近会社のプロジェクトで使う Xcode を Xcode 4 に乗り換えたのですが、全く新しく作り直された バグだらけでまともにビルドすら出来ない上に無料じゃなくなったどうしようもない出来映えの IDE ということで、何かとトラブルが多いようです。今回はその中でももっとも頻発したものをご紹介します。

たまに iOS 向けのプロジェクトを Xcode 4 で開いたときに、左上の scheme 選択欄に My Mac 64bit と表示されてしまって iPhone シミュレータや実機でビルドが出来なくなることがあるようです。

対処法はこちら。
http://stackoverflow.com/questions/5319251/xcode-4-the-selected-run-destination-is-not-valid-for-this-action

Build Settings を開き、 Base SDK の設定をいったん Mac OS X SDK に変更してから、もとの iOS SDK に戻すと問題が解決されるようです。これで一安心ですね。

Xcode 4.0 と Xcode 4.0.1 で再現することを確認しました。 Xcode 4.0.2 では未確認です。

Xcode 4 で build target 名にスペースが含まれているとビルド時に 100% エラーになる



タイトルからしてひどい出落ちですが、 Xcode 4 で build target 名にスペースが含まれているとビルドを行った際に 100% ビルドに失敗します。

原因がまたお粗末で、 Xcode 4 は build target 名をビルド成果物の中間配置ディレクトリの名前に使っているのですが、スペースをエスケープしていないためコンパイラが正しくパスを解釈できずに落ちているようです。がっくし。



Xcode 4.0 での調査結果です。 4.0.1 または 4.0.2 では修正されている可能性がありますが、いずれにせよスペースはさけたほうが良さそうです。

2011年3月17日木曜日

Xcode 4 で昔の Three20 フレームワークを使っているプロジェクトをビルドする方法

主に自分用のメモ。
Xcode 3 時代に作った Three20 フレームワークを使っているプロジェクトが動かない場合の対処法です。


動かない原因は簡単で、Xcode 4からはビルドした生成物が用意される場所が、各プロジェクトのbuildディレクトリ以下から、
~/Library/Developer/Xcode/DerivedData/プロジェクト名/Build/Products
以下に移動しているからです。なので、たいていの場合ヘッダ検索パスが間違っているので動かなくなります。

対処法はThree20公式ブログに載っているので転載します。
http://three20.info/article/2011-03-10-Xcode4-Support

以下のようにヘッダ検索パスを追記すると良いようです。
"$(BUILT_PRODUCTS_DIR)/../three20"
"$(BUILT_PRODUCTS_DIR)/../../three20"


2011/04/03追記: はじめに載せていた対処法は間違いでしたorz 大変申し訳ありません!
以下、はじめに掲載していた間違っている対処法です。この方法では通常のビルドはうまくいきますが、アーカイブ時のビルドに失敗します!どうもアーカイブ時と通常のビルド時でもビルド先のディレクトリが指し示す先が異なるようです、

対処法は、以下のようにヘッダ検索パスを書き直します。
$SYMROOT/three20


$SYMROOTという環境変数がキモです。



こいつはドキュメントには書いてませんが、Xcode 4のビルド設定ですと、
~/Library/Developer/Xcode/DerivedData/プロジェクト名/Build/Products
に対応するパスを返してくれるようです。

2011/04/03追記: 上にも書きましたが、アーカイブ時には上記に対応するパスではなく、異なるパスが使われます。そのためこの方法ではうまくいきません。Three20.infoに掲載されている方法を使うのが一番良いようです。

2011年3月16日水曜日

Python で Xcode のビルドスクリプトを書く方法

以前こんな記事を書きましたが、今回はもっと実践的なお話。PythonでXcodeのビルドスクリプトを書いてハッピーになろうというお話です。


■なぜXcodeのビルドスクリプトを書くのか

Xcodeのビルド機能だけでは出来ないことをやりたいからです。たとえば、
  • 特定のディレクトリの中に入っているリソースを、ビルド時にアプリにパッケージングしたい。
  • ビルドする前に、特定のリソースを暗号化して、アプリにパッケージングしたい。
といった要望が結構ありますが、これらはビルドスクリプトを使えば簡単に可能になります。
手でいちいちやるより楽で安全ですね。


■なぜPythonか

理由はいくつかあります。
  1. Windows, Mac, Linux, 全ての環境で動く。したがって、万が一のときにはビルドスクリプトだけを移植できる。
  2. sh とか csh とか非力すぎてやってらんない。 zsh もつかえるけど Python よりはやはり弱いと思う。
  3. スクリプトを Xcode 内部のエディタで書いて、そこに閉じ込められてしまうため、可搬性が無くなってしまう。
  4. 外部スクリプトにしておくと、引数としてオプションを渡せるので、ビルド設定に応じてオプションを切り替えたり、テストと本番でオプションを切り替えて動作を変更する、とかできる
たとえばクライアント・サーバーアプリで、サーバー側がPythonで出来ていたりする場合、
サーバー側の処理を一部ビルド時にやりたいとかあったりするわけですよ。・・・たまに。・・・ごくまれに。
そういうときに便利です。


■例:

長々と語るより例を示した方がよいと思うので、さっそくビルドスクリプトの例を示します。
ここでご紹介するのは、プロジェクトの/Resources/MyResourceディレクトリ以下にあるファイルを全てアプリバンドル内の/MyResourcesディレクトリ以下にコピーするだけの簡単なビルドスクリプトです。オプションとして平文/暗号化の有無を選択できるようにしてみました(実装はしてないです><)

Xcodeがビルドスクリプトを実行する際に、環境変数にたくさんの情報をセットしてくれます。なので、Pythonの os.environ を使ってそれらの情報を拾っていきます。Xcodeがセットしてくれる環境変数についてはhttp://developer.apple.com/library/ios/#documentation/DeveloperTools/Reference/XcodeBuildSettingRef/0-Introduction/introduction.htmlにまとめがあります。

#! /usr/bin/env python
# coding: utf-8

import os
import shutil
from optparse import OptionParser

def main():
# Option parser settings
#
description = """Sample build script"""
package_type_choices = (
'plain',
'crypted',
)
parser = OptionParser(description=description)
parser.add_option('-t', '--type',
action='store',
type='choice',
choices=package_type_choices,
default=package_type_choices[0],
dest='package_type',
help='Type of the destination package',
metavar='TYPE')
(options, args) = parser.parse_args()
print "*** Begin packaging resources. Package type is %s. ***" % options.package_type
# Env var settings
#
src_resources_path = "%s/Resources/MyResources" % (
os.environ['SRCROOT'],
)
destination_resources_path = "%s/%s/MyResources" % (
os.environ['TARGET_BUILD_DIR'],
os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']
)
# Create target dir
#
print "*** Creating the destination MyResources directory at %s ***" % destination_resources_path
if os.path.isdir(destination_resources_path):
shutil.rmtree(destination_resources_path)
os.mkdir(destination_resources_path)
# Copy each resources in src_resources_path to the destination
#
for root, dirs, files in os.walk(src_resources_path):
for dir in dirs:
# TODO: This implementation is only for 'plain' packaging. Implement the 'crypted' packaging later
print "*** Copying the resource %s to the target build location ***" % dir
fromdir_path = os.path.join(root, dir)
todir_path = os.path.join(destination_resources_path, dir)
shutil.copytree(fromdir_path, todir_path)
print "*** Removing garbage files from the copied resource ***"
for r, ds, fs in os.walk(todir_path):
for f in fs:
# .から始まるファイルをゴミファイルと見なしてパッケージに加えないようにします
if f.startswith('.'):
os.remove(os.path.join(r,f))
print os.path.join(r,f)
# Make sure not to traverse into the subdirectories
del dirs[0:len(dirs)]
# Completed
#
print "*** Packaging resources is successfully completed! ***"

if __name__ == "__main__":
main()
はい、できました。Pythonを使ったメリットとして、 optparse モジュールのおかげでオプションを扱うのがすごく楽にできるとか、リストを扱うのが強力とかが見て取れます。ファイル名の文字列加工も shやcsh の中で sed を使うより安全でらくちんです。


■Xcodeから呼び出す

あとはこれをXcodeのビルド時に呼び出すようにしてやれば良いだけです。

Xcode 3の場合には、画面左のナビゲーションバーからターゲットを選択して、「新規ビルドフェーズを追加」→「スクリプトの実行」とかで出来たと思います。
Xcode 4の場合には、以下の画像が示すとおりにすればOKです。



はい、出来ました。あとはビルドするたびに毎回このスクリプトが実行されてくれるわけです。

画像では紹介してないですが、もちろん呼び出し時にオプションをつけたりできますよ。
/usr/bin/env python $SRCROOT/bin/mybuildscript.py --myoption=1 -v --enable_my_secret

LLVM compiler 2.0 (clang 2.9) で linker にパスを出力させる方法

Xcode 4 から使えるようになった LLVM compiler 2.0 (clang 2.9) の覚え書きです。リンカにパスを出力させる方法を調べてみました。

※ clang 2.8 じゃなくて 2.9 がベースになってるみたいです。ごめんなさい><



方法は簡単。ビルド設定中のOther Linker Flags (OTHER_LDFLAGS) に、
-v -Xlinker -v
または
-v -Wl,-v
を与えればOKです。


ほいごらんのとおり。

gcc 4.2 または LLVM gcc 4.2 を使っているときは、 gcc がコンパイルして ld でリンクするようになっていましたが、 clang を使うときは clang が一人でコンパイルして一人でリンクまでやってしまいます。そのため、これまで通り普通に-vを渡しただけではうまくいきません。これだと、単に clang が verbose モードになるだけですからね。そこで -Xlinker とか -Wl を使ってリンカを verbose モードにすればOKということです。詳細はman clangすべし。

2011年3月8日火曜日

lipo を使って簡単に Universal Binary を作成する方法

iOS 向けのライブラリやフレームワークは、よく static library (.aファイル) の形式で配布されています。これは iOS がユーザーが作成した dynamic library (.dylibファイル) や framework バンドルをサポートしていないからなのですが、ときどきこの static library がシミュレーターとデバイス両方で使える形式、いわゆる Universal Binary になっていない場合があります。

たとえばこんな感じですね。



この状態でビルドを行うと、シミュレーター向けビルドを行えばデバイス用のバイナリが、デバイス向けビルドを行えばシミュレーター用のバイナリが、それぞれ対応していないアーキテクチャであると警告を出してしまいます。警告ですからコンパイルは通るのですが、私は几帳面で気になってしまうので、これを解消したいと考えます。


■lipoの出番

そこで lipo を使います。 lipo は Xcode に付属されているコマンドラインツールですので、 iOS SDK をインストールしている人でしたら誰でも使えます。詳しい使い方は man を見ていただければわかりますが、普通使うのは以下のパターンだけです:
lipo -create ライブラリ1.a ライブラリ2.a -output 出力するライブラリ.a
たったのこれだけで二つのライブラリを組み合わせて Universal Binary を生成してくれます。さっきの画像の例ですと、
lipo -create libAbesi_dev.a libAbesi_sim.a -output libAbesi.a
これでOKです。


■本当に Universal なの?

それでは本当に Universal Build になったのか見てみましょう。 static library の中身を見るには、 nm コマンドを使います。



どうやらうまくいったみたいですね。一つのライブラリファイルの中に、3種類のアーキテクチャに対応したオブジェクトが格納されています。

2010年12月29日水曜日

アプリのビルド時に CSSMERR_TP_NOT_TRUSTED エラーが発生したときの対処法

http://d.hatena.ne.jp/drill256/20090820/1250752178
http://discussions.apple.com/thread.jspa?threadID=1630090

このエラーは、以下の証明書がすべて存在しないか、または Keychain Access 内でのステータスが、「この証明書は信頼されています。」ではないときに発生するようです。
  • Apple Worldwide Developer Relations Certification Authority
  • iPhone Developer または iPhone Distribution


上記の画像のように、「この証明書は信頼されています。」と緑色のチェックマーク付きで表示されている必要があります。そうでない場合は何らかの問題があります。

対処法は、
  1. まず何はなくともこれらの証明書がすべて存在するか確認する。 Apple Worldwide Developer Relations Certification Authority を忘れているケースが良くあります。
  2. 「この証明書は信頼されています。」になっていない場合には、証明書を選択して、「情報を見る」 -> 「信頼」 -> 「システムデフォルトを使う」 を選択する。「常に信頼する」ではダメです、エラーになります。
証明書をどのキーチェイン項目に入れていても問題はなさそうです。私の場合は WWDR をシステムに、 iPhone Developer をログインに入れていますが正常に動作しています。

2010年5月30日日曜日

iPad 3G を Xcode につないだら対応していない OS と言われてしまった件



昨日の勉強会でさっそく買ったばかりのiPad 3GをXcodeにつないでみたら、ご覧のように対応していないOSであるとエラーが出てしまいました。

再度iPhone Developer CenterからSDKをダウンロードしてインストールし直したら問題なく動作するようになりました。おそらく単にSDKが古かっただけなのだとは思いますが、一応。

2009年8月7日金曜日

cocos2d for iPhone Project Template v0.8 真似して作ってみました



cocos2d導入キット(http://d.hatena.ne.jp/Seasons/20090511/1241990196)としてid:Seasonsさんが公開されているキットを元に、私もcocos2d用のプロジェクトテンプレートを作ってみることにしました。id:Seasonsさん、すばらしいキットをありがとうございます!

今回の作業にあたり参考にしたページはこちら。
http://d.hatena.ne.jp/griffin-stewie/20090315/p1

作成したテンプレートはgitに公開いたしましたので、よろしければ使ってみてください。
http://github.com/akisute/cocos2d-xcode-template/tree/master


■インストール方法
downloadタブから0.8をダウンロードしてきて、解凍したファイルを以下のパスに配置してください。
~/Library/Application Support/Developer/Shared/Xcode/Project Templates
配置後、.gitと.gitignoreファイルを削除してください。削除しないと後からテンプレを使って新しいプロジェクトを作ったときに、これらのファイルがコピーされてしまいgitを使おうとしたときに問題が発生する可能性があります。


■テンプレートの内容
id:Seasonsさんが公開されている0.8beta用のテンプレートを元に、一部自分の気になった箇所を自分好みに修正しました。また、使用するcocos2dのバージョンを0.8betaから0.8リリース版に変更しました。一応ビルドして画面が出るところまでは確認しています。ただし、全機能をテストしたわけではないので、一部不具合があるかもしれません!
また注意点として、簡素化のため元のcocos2dや外部ライブラリに付随していたREADMEやドキュメント、LICENSEファイルなどをすべて削除してしまっています。おそらくは大丈夫だと思いますが、ライセンス的に問題が発生するかもしれません。大変申し訳ありませんが、このテンプレートを利用した際に生じる一切の不具合について当方では責任を負いきれませんのでご了承ください。


■id:Seasonsさんのテンプレートから変更したところ一覧
  1. 使用するcocos2dのバージョンを0.8リリース版にした。
  2. プロジェクトのグループ構成およびディレクトリ構成を変更した。この変更により、本来システムが利用する/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application以下ではなく、ユーザーのライブラリである~/Library/Application Support/Developer/Shared/Xcode/Project Templates以下にテンプレートを配置しても動作するようになった。
  3. MenuSceneとGameSceneが最初のテンプレとして用意されていたのを統廃合し、MainSceneひとつだけにした。
  4. ビルドの設定を必要に応じて変更し、またGCC_PREPROCESSOR_DEFINITIONSにDEBUGを追加した。これにより最初からデバッグビルド時にCCLOGマクロが正しく機能する。
  5. ソースコードのインデントがまちまちになっていたのを、すべてApple式のインデントに修正。
  6. ソースコード中から日本語コメントをすべて除去し英語コメントに置換。
  7. 一部使用されていたdepreciatedメソッドを除去。[[Director sharedDirector ] setDeviceOrientation:CCDeviceOrientationLandscapeRight];を使うようにした。


■思わぬ収穫
この作業によってXcodeの仕組みとテンプレートの作り方、ビルドの仕方などにかなり詳しくなることができました。たとえば・・・
  • Xcodeのテンプレートファイルは単なる普通のXcodeプロジェクトと何ら変わらない。そのままビルドして実行することもできる。
    唯一異なる点が、xcodeprojバンドルの中に、TemplateIcon.icnsとTemplateInfo.plistというファイルが存在し、これらを変更することでテンプレートとしての設定を変更することが出来る点。
  • libhogehoge.a(静的ライブラリ?)の作り方。ターゲットに新規ターゲットを追加して、必要なソースをぽいぽい投げ込むだけ。antでbuild.xmlを書くより簡単。
  • ___PROJECTNAMEASIDENTIFIER___.pchの配置パスを変更したときは、ターゲットのビルド設定のPrefix Headerの値を書き換える必要がある
  • 同様に、___PROJECTNAMEASIDENTIFIER___-Info.plistのパスを変更したときも、ターゲットのビルド設定のInfo.plistファイルの値を書き換える必要がある
  • main.mは何処に置いておいても大丈夫らしい

ということで、Xcodeプロジェクトのテンプレートを作成するのは凄く勉強になります。皆さんも一度自分好みのテンプレートを作成してみてはいかがでしょうか?