Choose your operating system:
Windows
macOS
Linux
Unreal Engine 4 (UE4) を含む、多数の内部 Epic プロジェクトを変換するために IncludeTool が使用されてきました。 このツールは厚意で提供されているため、提供できるサポートのレベルは限られています。
IncludeTool ユーティリティを使用すると、既存の C++ プロジェクトを Include-What-You-Use (IWYU) スタイルに変換できます。 各ヘッダ ファイルから自己完結型の翻訳単位を形成しようとすることによって、このユーティリティが実行されます。
これにより多くの時間を節約できますが、ヒューリスティックに基づいており、完璧な出力を生成できることはほとんどありません。代わりに、変換を行うための出発点を作り、そこから手動で編集して完成させることができます。 しかしながら、このユーティリティは力任せ (brute force) な方法 (かなり遅く、大規模なプロジェクトでは完了までに数日かかります) で実行するため、イテレーションが面倒となります。そのため、プロジェクトごとに修正が必要となったり、デバッグが難しくなったりする可能性があります。
ツールを操作する
IncludeTool はいくつかのフェーズで動作します。これについては以下で説明します。
UnrealBuildTool は、ビルド ステップのリストを生成するためにターゲットと共に呼び出されます。 プロジェクトのエディタ ターゲットの使用をおすすめします。他のターゲットタイプよりも多くのコードパスを含むためです。 Clang ツールチェーンを使用するために、Windows からクロスコンパイラ ツールチェーンで Linux 用 にコンパイルすることもお勧めします。 Claig の使用は重要となります。Visual C++ は 2 フェーズの名前検索を行わないため、テンプレートがインスタンス化されるまでテンプレート クラスから依存クラスの依存関係を見つけることができず、出力が非常に読みにくくなります。
ソースファイルは、内部プリプロセッサを使用して部分的にプリプロセス (前処理) されています。 これはトークン化されたソース ファイル自体を変換するのではなく、現在のターゲットに対してどのコード ブロックがアクティブか非アクティブかを判断し、各ファイルを一連のフラグメントに分割します。 各フラグメントは、
#include
ディレクティブ間のソースファイル内の行の範囲を定義します。つまり、#include
ディレクティブを再帰的に追跡し、検出されたフラグメントのリストを連結することによって、任意のソースファイルの単一ファイルバージョンをいつでも組み立てることができます。IncludeTool が最適化しようとしているフラグメントのリストであるため、このステップは重要となります。 この時点で、IncludeTool が検証を行い警告を発する制限がいくつかあります。 細かく形式張った警告がいくつかありますが、これらは出力が有効であることを確認するために必要となります。 以下は特に注意しておくべきものです。
ヘッダ ファイル間に循環インクルードがないようにしてください。
インクルードされたソースファイルは、すべての翻訳単位によって同じ方法でプリプロセスされている必要があります (例えば、マクロはインクルードされるすべてのコンテキストで同じ方法で定義されている必要があります) 。
各フラグメントが作業フォルダに書き込まれます。 各ソースファイルは、他のフラグメントに属する行がコメントアウトされた状態で行番号を保持できるように、元のレイアウトで出力されます。
ソースファイルはトークン化され、前方宣言可能なシンボルのようなパターンを検索します。 (例: 「class Foo {...」)
各ソースファイルは、どのフラグメントに依存してコンパイルが成功するかを判断するために、力任せにコンパイルされます。 これは変換時の最も面倒な作業となりますが、この分析結果は作業ディレクトリに格納され、ソース ファイルが変更されない限りは再利用が可能です。 このツールは、複数の PC を使用して依存関係データを計算するための「共有」モードもサポートしています (下記参照) 。
検索は次のように構成されています。
一連のフラグメント (1...n) で表すため、入力変換単位が拡張されます。
必要なフラグメントのセット (r) は、 (入力ファイルに含まれるのではなく) 入力ファイルに含まれていたフラグメントのみに初期化されます。
バイナリ検索を実行して、ソース ファイルが正常にコンパイルされるのに必要なフラグメントの最も長いシーケンスを見つけます (1...m で、r と一緒にあるもの) 。
シーケンス (m) の最後のフラグメントは、必要なセット (r) に追加されます。 「m」がすでに最適化されている場合は、その依存関係も r に追加されます。
バイナリ検索は、フラグメントのシーケンス (1...m - 1) に対して繰り返されます。
各出力ファイルは、単独でコンパイルするための最小限のインクルード セットで書き込まれます。 ヒューリスティックは、名前によって明示的に参照されるシンボルを含む依存関係のヘッダを直接インクルードすることを試みます。そして、それらが再帰的に含まれていない場合にのみ、他の依存関係をインクルードします。
使用例
下記で、フラグメント解析を実行して診断情報を出力できます。
-Mode=Scan -Target=FooEditor -Platform=Linux -Configuration=Development -WorkingDir=D:\Working
下記で、コード ベースを最適化できます。
-Mode=Optimize -Target=FooEditor -Platform=Linux -Configuration=Development -WorkingDir=D:\Working -OutputDir=D:\Output -SourceFiles=-/Engine/... -OptimizeFiles=-/Engine/... -OutputFiles=-/Engine/...
ソース ファイルのイテレーションを制限するために、いくつかのフィルタ引数を渡すことができます (-SourceFiles
、-OptimizeFiles
、-OutputFiles
) 。 それぞれのフィルタ引数は、P4 スタイルのワイルドカードのセミコロン リストを取り込んで include (/Engine/...
) または exclude (-/Engine/Foo.cpp
) ファイルを作成したり、応答ファイルを 1 行ごとに書いて、規則的に指定することもできます (D:\Filter.txt
) 。 一般的には、多くの場合エンジン コードを再最適化しない方が良いので、フィルタ引数をすべて分析から除外する方が合理的です。
このプログラムの実行には非常に時間がかかることがあるので、エンジンには 「共有」モードで実行するための機能があります。 複数の PC がすべて同じソース ツリーに同期している場合は、-Shard=N
と -NumShards=M
を使用して、入力セットの一部のみを使用するようにマシンを構成できます。 作業ディレクトリは各マシンの同じ場所を指定してください。そうすると、結果として得られる作業ディレクトリをまとめてコピーして、単一のマシンで最終的に実行して出力ファイルを生成することができます。
他のモード (ToolMode
enum を参照) とコマンドライン オプション (CommandLineOptions
クラス) については Program.cs
のコメントを参照してください。