Unity リンカー
Understand how the Unity linker analyzes and marks elements of your code before stripping.
読み終わるまでの所要時間 21 分最終更新 1ヶ月前
Unity のビルドプロセスでは、未使用のマネージコードを削除するために Unity リンカーというツールを使用します。Unity リンカーは IL Linker のバージョンの 1 つで、Unity で動作するようにカスタマイズされています。Unity リンカーのためにカスタマイズされた Unity エンジン固有の部分は一般には使用できません。
Unity リンカーは、マネージコードストリッピングと一部のエンジンコードストリッピングの両方を担当します。エンジンコードストリッピングは IL2CPP スクリプティングバックエンドを通じて利用できる別の処理であり、使用されていないエンジンコードを削除します。詳しくは、PlayerSettings.StripEngineCode を参照してください。
Unity リンカーのしくみ
Unity リンカーは、プロジェクト内のすべてのアセンブリを解析します。最初に、ルート型、メソッド、プロパティ、フィールドをマークします。例えば、シーンのゲームオブジェクトに追加する MonoBehaviour 派生クラスはルート型です。
次に、Unity リンカーはマークしたルートを解析して識別し、これらのルートが依存するマネージコードをマークします。この静的解析が完了すると、マークされていない残りのコードはアプリケーションコードを通るどの実行パスからも到達できなくなり、Unity リンカーによってアセンブリから削除されます。
Unity リンカーがアセンブリを除去する方法
Unity エディターは、Unity プロジェクトのシーンで使用する型を含むアセンブリのリストを作成し、Unity リンカーに渡します。Unity リンカーはそれらのアセンブリ、それらのアセンブリの参照、link.xml ファイルで宣言されたアセンブリ、AlwaysLinkAssembly 属性を持つアセンブリを処理します。通常、これらのカテゴリに該当しないプロジェクト内のアセンブリは Unity リンカーで処理されず、プレイヤービルドから除外されます。
Unity リンカーが処理する各アセンブリは、アセンブリの分類、アセンブリにシーンで使用する型が含まれているかどうか、およびビルド用に選択した Managed Stripping Level に基づく一連のルールに従います。
これらの規則の目的のために、アセンブリは以下のように分類されます。
-
.NET Class Library アセンブリ — mscorlib.dll や System.dll などの Mono クラスライブラリと、netstandard.dll などの .NET クラスライブラリファサード (facade) アセンブリが含まれます。
-
Platform SDK アセンブリ — プラットフォーム SDK 固有のマネージアセンブリが含まれます。例えば、ユニバーサル Windows プラットフォーム (UWP) SDK の一部である windows.winmd アセンブリなどです。
-
Unity Engine Module アセンブリ — UnityEngine.Core.dll など、Unity エンジンを構成するマネージアセンブリを含みます。
-
Project assemblies — 以下のようなプロジェクト固有のアセンブリを含みます。
- Assembly-CSharp.dll などのスクリプトアセンブリ
- 事前コンパイルされたアセンブリ
- アセンブリ定義アセンブリ
- パッケージアセンブリ
マーキングの規則
Unity でプロジェクトをビルドすると、ビルドプロセスは C# コードを共通中間言語 (Common Intermediate Language、CIL) と呼ばれる .NET バイトコード形式にコンパイルします。Unity は、この CIL バイトコードをアセンブリと呼ばれるファイルにパッケージ化します。プロジェクトで使用するプラグインの .NET Framework ライブラリと C# ライブラリも、CIL バイトコードのアセンブリとして事前にパッケージ化されます。
Unity リンカーが静的解析を行うとき、CIL バイトコードのどの部分をビルドに必要なものとしてマークするかは一連のルールに従って決定します。ルートのマーキングルールは、Unity リンカーがビルドで最高位のアセンブリをどのように識別して保存するかを決定します。依存関係のマーキングルールは、ルートアセンブリが依存するコードを Unity リンカーが識別して保存する方法を決定します。
Managed Stripping Level プロパティは、Unity リンカーが使用する一連のルールを変更します。以下のセクションでは、Managed Stripping Level プロパティで行える各設定のマークのルールについて説明します。
ルートマーキングの規則
次の表は、Unity リンカーがアセンブリの最高位のタイプを識別する方法を説明したものです。
アセンブリタイプ | マーキングの規則 | |||
|---|---|---|---|---|
| 最低限 | 低 | 中 | 高 | |
| .NET クラス & Platform SDK と UnityEngine アセンブリ | 予防的保存と任意の link.xml ファイルで定義された保存を適用します。 | 予防的保存と任意の link.xml ファイルで定義された保存を適用します。 | link.xml ファイルで定義された保存を適用します。 | link.xml ファイルで定義された保存を適用します。 |
| シーンで参照される型を持つアセンブリ | アセンブリ内のすべての型とメンバーをマークします。 | アセンブリ内のすべての型とメンバーをマークします。 | 以下をマークします。 • • 保存は任意の link.xml で定義されます。 •プリコンパイル、パッケージ、Unity スクリプト、またはアセンブリ定義アセンブリの MonoBehaviour と ScriptableObject から派生したすべての型。 | 以下をマークします。 • • 保存は任意の link.xml で定義されます。 •プリコンパイル、パッケージ、Unity スクリプト、またはアセンブリ定義アセンブリの MonoBehaviour と ScriptableObject から派生したすべての型。 |
| 他のすべて | アセンブリ内のすべての型とメンバーをマークします。 | 以下をマークします。 • すべての public 型とそれらの型の public メンバー。 • • 保存は任意の link.xml で定義されます。 • プリコンパイル、パッケージ、Unity スクリプト、またはアセンブリ定義アセンブリの MonoBehaviour と ScriptableObject から派生したすべての型。 | 以下をマークします。 • すべての public 型とそれらの型の public メンバー。 • • 保存は任意の link.xml で定義されます。 • プリコンパイル、パッケージ、Unity スクリプト、またはアセンブリ定義アセンブリの MonoBehaviour と ScriptableObject から派生したすべての型。 | 以下をマークします。 • • 保存は任意の link.xml で定義されます。 • プリコンパイル、パッケージ、Unity スクリプト、またはアセンブリ定義アセンブリの MonoBehaviour と ScriptableObject から派生したすべての型。 |
| テスト | |
依存性のマーキング規則
Unity リンカーは、アセンブリのルートを特定した後、それらのルートが依存するコードを特定する必要があります。以下の表は、Unity リンカーがアセンブリ内のルート型の依存関係を識別する方法を説明します。
規則のターゲット | 各ストリッピングレベルでの動作 | |||
|---|---|---|---|---|
| 最低限 | 低 | 中 | 高 | |
| MonoBehaviour | Unity リンカーは、型をマークするときに MonoBehavior 型のすべてのメンバをマークします。 | |||
| ScriptableObject | Unity リンカーは、型をマークするときに ScriptableObject 型のすべてのメンバをマークします。 | |||
| 属性 | Unity リンカーがアセンブリ、型、またはその他のコードの構造体をマークするときに、それらの構造体のすべての属性もマークします。 | Unity リンカーがアセンブリ、型、またはその他のコードの構造体をマークするときに、それらの構造体のすべての属性もマークします。 | Unity リンカーがアセンブリ、型、またはその他のコード構造をマークするとき、その属性の型もマークされている場合にのみこれらの構造体の属性もマークします。 | Unity リンカーがアセンブリ、型、またはその他のコード構造をマークするとき、その属性の型もマークされている場合にのみこれらの構造体の属性もマークします。 |
| デバッグの属性 | スクリプトのデバッグが有効な場合、Unity リンカーは、メンバーを使用するコードパスがない場合でも、 | スクリプトのデバッグが有効な場合、Unity リンカーは、メンバーを使用するコードパスがない場合でも、 | Unity リンカーは常に、DebuggerDisplayAttribute や DebuggerTypeProxyAttribute などのデバッグ属性を削除します。 | Unity リンカーは常に、DebuggerDisplayAttribute や DebuggerTypeProxyAttribute などのデバッグ属性を削除します。 |
| .NET ファサードクラスライブラリ | ランタイムに必要ないため、ファサードアセンブリは削除されます。 |
Link XML 機能タグの除去
Link.xmlmscorlib.dllmscorlib.xmllink.xmlストリッピングレベルを High に設定すると、Unity リンカーは、現在のビルドの設定に基づいてサポートされていない機能の保持を除外します。
- remoting – IL2CPP スクリプティングバックエンドをターゲットにする場合に除外されます。
- sre – IL2CPP スクリプティングバックエンドをターゲットにする場合に除外されます。
- com – COM をサポートしないプラットフォームをターゲットにする場合は除外されます。
例えば、以下の link.xml ファイルは、COM をサポートするプラットフォーム上で FeatureOne メソッドを保持し、すべてのプラットフォームで FeatureTwo メソッドを保持します。
<linker> <assembly fullname="Foo"> <type fullname="Type1"> <!--Preserve FeatureOne on platforms that support COM--> <method signature="System.Void FeatureOne()" feature="com"/> <!--Preserve FeatureTwo on all platforms--> <method signature="System.Void FeatureTwo()"/> </type> </assembly></linker>
メソッド本体の編集
ストリッピングレベルを High に設定すると、Unity リンカーはコードサイズをさらに削減するためにメソッドのボディを編集します。このセクションでは、Unity リンカーがメソッドのボディに対して行う編集の注意すべき点をいくつかまとめています。
Unity リンカーは、.NET クラスライブラリアセンブリのメソッドのボディのみを編集します。メソッドのボディの編集後は、アセンブリのソースコードとアセンブリのコンパイル済みコードが一致しなくなり、デバッグが困難になる場合があります。
以下のリストは、Unity リンカーがメソッド本体を編集するために実行するアクションを説明したものです。
- アクセスできないブランチを削除 - Unity リンカーは、System.Environment.OSVersion.Platform をチェックして、現在適応するプラットフォーム用のアクセスできない If ステートメントのブロックを削除します。
- フィールドにのみアクセスするインラインメソッド - Unity リンカーはフィールドの値を取得または設定するメソッドへの呼び出しを、フィールドへの直接アクセスに置き換えます。これにより、メソッドを完全に取り除くことができ、サイズの削減に役立ちます。Mono バックエンドを使用する場合、Unity リンカーによるこの変更は、フィールドの可視性に基づいて、メソッドの呼び出し元がフィールドへの直接アクセスを許可されている場合にのみ行われます。IL2CPP では可視性ルールは適用されないため、Unity リンカーは必要に応じてこの変更を行います。
- const 値を返すメソッドをインラインにする - Unity リンカーは const 値のみを返すメソッドの呼び出しをインラインにします。
- 空の戻り値のない呼び出しを削除 - Unity リンカーは、空で戻り値が void の型を持つメソッドへの呼び出しを削除します。
- 空のスコープを削除 - Unity リンカーは、Finally ブロックが空の場合、Try/Finally ブロックを削除します。空の呼び出し を削除すると、空の Finally ブロックが作成されます。メソッド編集中にこのようなことが起こると、Unity リンカーは Try/Finally ブロック全体を削除します。これが発生する可能性のあるシナリオの 1 つは、コンパイラーが Dispose() を呼び出すために foreach ループの一部として Try/Finally ブロックを生成する場合です。