ビルドシステム、CIとCMSの統合

ゲームを作る上でCIシステムとCMSは不可欠になっている。teslawireは使用するコンパイル環境だけでも3種(Win32/Mac+iOS/Android)あり、GPU毎に数えると2桁になる。今回はシナリオ等アセットの制作を一切アウトソースしないので、他人の作業がビルドを壊すことは無い。しかし、α版のフィードバックをメールベースで受け取るのも限界があるはずなので、他人にCMSを見せることを最初から想定する。
というわけで、ビルドシステムやテストシステムも間接的にコンテンツを生成しているという考えに立って、CMSを統合したCIシステムを構築することにした。CMSソースコードやアセットに注釈を付けられるが、その注釈をCIシステムが生成できるように考える。
テストシステムのテスト結果は、リポジトリのある時点とある特定の環境に対しての結果であり、その結果をレビューするときには現状との差分が無いと意味が無い。ソースコードの差分は簡単に取ることができるが、モデルデータやステージデータ等の差分は簡単には取れない。PerforceやAlienbrain等は画像ファイルのdiff生成等にも対応しているが、率直にいってもっと真面目なソリューションを必要としている。
過去のプロジェクトではJIRAのようなCMSと、JenkinsのようなCIシステムを組み合わせて使用していたが、これをもっと密結合でone-stopなシステムにすることを目指す。

ゲームにおけるCIの必要性と特殊性

過去のプロジェクトでは、ソースコードコンパイラが生成するオブジェクトコードのみがCIの対象であり、テスト類は実際のゲーム記述ではなくエンジンのテストコードに限られていた。しかし、今回は責任境界が存在しないのでゲームアセットがビルドを壊すことができるという難易度の高い特性が加わった。
通常のソフトウェアプロジェクトと異なり、ゲームはそれをドライブするリソース類; シナリオデータやモデルデータ、ステージデータと不可分で、それら全てを組み合わせて初めてプロダクトとしての価値が出てくるという特性がある。つまり、CIシステムはリソースもハンドリングしなければならない:

  • 行指向以外の警告が出る

ロード時間の超過やカットシーンにおけるリソースの不足等は、ゲーム内時間に対して報告される。このため、ソースコードの行数と警告を関連づけるだけではなく、ゲーム内のシーンと関連づける機能も必要となる。

  • "Take me here"機能

テストによって生成されたエラーリポートや、テスターから寄せられたバグレポートをデバッグするために、当該シーンを一発で再現するための機能が要求されている。当該リビジョンのゲームもしくは最新リビジョンのゲームをビルドし、セーブデータをロードしてcheatで当該シーンにジャンプする - これを機械的に行える必要がある。
テスターから寄せられたエラーリポートがPC版で再現するならば、PC版を先にデバッグしてモバイル(iOS+Android)で検証するというフローを取ることができる。

  • 特殊なビルド経路

幾つかの環境は特定のプラットフォームでしか動作しない特殊なツールを必要とする。要するにWin32とiOSのビルドはそれぞれの環境でしか行えない。アセットの検証や最適化はGPUベンダのツールを使用するためWindowsでしか行えない。
これは通常のソフトウェアプロジェクトでも起こるが: 一つのソースは複数のツールチェインでビルドされる。このため、生成される警告もツールチェインの数だけ別々に発生する。これらの別々に発生した警告を統合し、1箇所で(適切なフィルタリングを施して)表示する必要がある。

  • 手動ビルドは(ほぼ)不要 / "hosted"ビルド環境

PC版ビルドだけはXcodeやVisualStudioのようなIDEで編集されその場でビルドされるが、他のビルドに手動のビルドは必要ない - というより、必要のない環境を構築する必要がある。通常の人間が2桁に及ぶ対応プラットフォームでのビルドを確認してからリポジトリにチェックインするのは単純に不可能と言える。このため、チェックイン前のチェックは要求しない。
この問題に対応するために、特定のgitブランチのビルドをリクエストできるようにする必要がある(この概念自体はJenkinsのような既存のCIにも存在する)。

生成されるアノテーション

ビルド時の解析/警告、ユーザからのバグレポート、コードレビューによるレビューコメント等は全てソースコード行に関連付けられるアノテーションとしてCMSに保存される。
この時、(bisect要求時を除いて)システムは全てのリビジョンをビルドするわけではないので、最新のリビジョンに対してremapする機能が必要になる。
解析ビルドはブラウズ情報(シンボル間のリンク関係)やインライン化の有無等をアノテーションとして生成する。つまり、アノテーションシステムは行だけでなくカラム位置と範囲も正確に記録する必要がある。(そもそもOS Xのclangは警告等もカラム位置で表記できる)
単純に列挙するだけでも:

  • ビルド時警告/エラー/Analyzer警告 - Clangのstatic-analyzer警告はcallstackを持つ
  • エラーレポートのcallstack - 複数スレッドを含む
  • レビューコメント
  • 関数/マクロ宣言、予約語のreference
  • ロードしたヘッダファイル
  • マクロの展開結果

予約語の参照をアノテーションにしているのは、単純なキーワードハイライトではマクロによるoverrideによって間違いが起こるため(!) ...マクロによって if を置換するのは常識外れという意見もあるが、#if 0のようなマクロブロックによってビルド時に除去したものはそもそもコンパイルされていないわけでハイライトしないのが正しい - ただし、MSのコンパイラのようにASTを取得できないコンパイラも世間には存在するため、これを徹底することは事実上不可能で、通常のキーワードハイライトと併用することになる。