【UE5】Unityビルドシステムから除外されるcppファイルの条件について

# 概要

UnrealEngineのプロジェクトはデフォルトではUnityビルドシステムが有効になっております。

※Unityビルドシステムについては [[UE4] Unityビルドシステム｜株式会社ヒストリア](https://historia.co.jp/archives/4393/) が詳しいです。ご存じない方はご参照ください。

Untiyビルドシステムにより、様々な問題が発生する可能性があります。

問題の例
- cppファイルを新規追加して、ローカルPCではビルドが成功するが、pushしてJenkinsマシンでパッケージ作成するとビルドが失敗する
- 既存のcppファイルに一行空白を追加しただけでコンパイルエラーが発生するようになる

Unityビルドシステムでの問題が発生してしまう原因の一つに、**特定の条件でUnityビルドシステムからcppファイルが除外される**があるようです。
除外された場合はそのcpp単体でコンパイルが行われるため、他のcppの影響を受けなくなり、他のcppに影響を与えなくなります。
それにより、includeがされなくなったり等でコンパイルエラーが発生したり、逆にコンパイルエラーが発生しなくなったりします。

Unityビルドシステムから特定のcppファイルが除外された場合はビルド時に下記のようなログが出力されます。
```
12>[Adaptive Build] Excluded from TestUnityBuild unity file: HogeActor2.cpp
```

Unityビルドシステムから除外されるcppファイルの条件が良くわかっておりませんでしたので、調査＆検証いたしました。

■環境
- OS：Windows 11
- UnrealEngine：5.5.4
- ビルド構成：DebugGame Editor

# まとめ

Unityビルドシステムから除外されるcppファイルの条件は下記のようです。

|場合|どういうファイル？|備考|
|---|---|---|
|gitで管理されている場合|**git status コマンドを実行したときに差分があるファイル**|Untrack(まだgitで管理されていない)やModified(前のバージョンから変更がある)等|
|Perforce(HelixCore)で管理されている場合|**読み取り専用属性がOFF**のファイル|Perforceにおいてはファイルがチェックアウトされると読み取り専用属性がOFFになります|
|何もバージョン管理システムを使っていない場合|**読み取り専用属性がOFF**のファイル|デフォルトだとPerforceで管理されている場合と同じになるようです|

■補足
公式ドキュメントの [Unreal Engine のビルド コンフィギュレーション](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/build-configuration-for-unreal-engine?application_version=5.3) に下記のように記載されております(引用)。
 > $ bUseAdaptiveUnityBuild : ヒューリスティックな手法で、現在イテレート中のファイルを判別し、それらを unity BLOB から取り除くことで、インクリメンタル コンパイルの時間を短縮します。現在の実装では、ファイルが変更されるとソース コントロール システムによって書き込み可能になると仮定し、読み取り専用フラグを使用して作業セットが区別されています。この仮定は Perforce には当てはまりますが、Git には当てはまりません。

デフォルトではbUseAdaptiveUnityBuildがtrueになっており、↑の通りにUnityビルドシステムから除外するcppファイルを決めるようです。
Gitには当てはまらないとあるのは、**Gitの場合は常に各ファイルの読み取り専用属性がOFF**だからです。
では、Gitの場合どうしている？を具体的に書いてあるドキュメントを検索してみたのですが、見つけることができませんでした。ただ、ビルド時のログ出力を読むと、git status で判定していること↓がわかります。

```
12>Using 'git status' to determine working set for adaptive non-unity build (D:\work\TestUnityBuild).
```

# 検証
GitとPerforceで同じUnrealEngineのプロジェクトを用いて検証を行います。

検証用プロジェクトは↓にアップロードしております。何かしら疑問等ある方はダウンロードして確認していただければと思います。

https://github.com/ogamita777/TestUnityBuild

## 検証用プロジェクトについて

下記の2つのActorのcppを用意いたしました。
Unityビルドシステムを有効にしていない場合はHogeActor2.cppがKismetSystemLibrary.hをincludeしていないので、コンパイルエラーが発生します。

```HogeActor1.cpp
#include "HogeActor1.h"

// UKismetSystemLibraryを使っていないcppでincludeする
#include "Kismet/KismetSystemLibrary.h"

void AHogeActor1::BeginPlay()
{
	Super::BeginPlay();
	
}
```

```HogeActor2.cpp
#include "HogeActor2.h"

void AHogeActor2::BeginPlay()
{
	Super::BeginPlay();
	
	// KismetSystemLibrary.h は HogeActor2.cppではincludeされていない
	UKismetSystemLibrary::DrawDebugSphere(GetWorld(), GetActorLocation(), 100.0f, 12, FLinearColor::Red, 30.0f);

}
```

EditorのTarget.csファイルに↓のフラグを追加してあります。
```TestUnityBuildEditor.Target.cs
public class TestUnityBuildEditorTarget : TargetRules
{
	public TestUnityBuildEditorTarget( TargetInfo Target) : base(Target)
	{
		// 略

        // デフォルトでtrueのはずだが一応
		bUseUnityBuild = true;

        // cppのファイル数が少ないとbUseUnityBuildがtrueでもUnityビルドが行われないようなので強制的に行われるように
		bForceUnityBuild = true;

        // デフォルトでtrueのはずだが一応
		bUseAdaptiveUnityBuild = true;
	}
}

```

## gitで管理されている場合

まずビルドが成功することを確認します。

ソリューションエクスプローラーからTestUnityBuildを右クリック > リビルドを行います(※今後の手順でのリビルドは全てこの方法で行います)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29542/72ff329a-6df6-4654-8f79-6465cf277db4.png)

```ログ出力.log
// 略
12>Using 'git status' to determine working set for adaptive non-unity build (D:\work\TestUnityBuild).
// 略
12>[1/6] Resource Default.rc2
12>[2/6] Compile [x64] SharedPCH.UnrealEd.Project.NonOptimized.ValApi.Cpp20.cpp
12>[3/6] Compile [x64] Module.TestUnityBuild.cpp
12>[4/6] Link [x64] UnrealEditor-TestUnityBuild-Win64-DebugGame.lib
12>   ライブラリ D:\work\TestUnityBuild\Intermediate\Build\Win64\x64\UnrealEditor\DebugGame\TestUnityBuild\UnrealEditor-TestUnityBuild-Win64-DebugGame.lib とオブジェクト D:\work\TestUnityBuild\Intermediate\Build\Win64\x64\UnrealEditor\DebugGame\TestUnityBuild\UnrealEditor-TestUnityBuild-Win64-DebugGame.exp を作成中
12>[5/6] Link [x64] UnrealEditor-TestUnityBuild-Win64-DebugGame.dll
12>   オブジェクトを作成しています D:\work\TestUnityBuild\Binaries\Win64\UnrealEditor-TestUnityBuild-Win64-DebugGame.exp
12>[6/6] WriteMetadata TestUnityBuildEditor-Win64-DebugGame.target (UBA disabled)
// 略
12>プロジェクト "TestUnityBuild.vcxproj" のビルドが終了しました。
========== すべて再構築: 12 正常終了、0 失敗、0 スキップ ==========
```

無事？ビルドに成功します。
HogeActor1.cppとHogeActor2.cppは単体ではコンパイルされず、Module.TestUnityBuild.cpp というファイルにまとめられてコンパイルされているようです。

```Intermediate\Build\Win64\x64\UnrealEditor\DebugGame\TestUnityBuild\Module.TestUnityBuild.cpp
// This file is automatically generated at compile-time to include some subset of the user-created cpp files.
#include "D:/work/TestUnityBuild/Intermediate/Build/Win64/UnrealEditor/Inc/TestUnityBuild/UHT/HogeActor1.gen.cpp"
#include "D:/work/TestUnityBuild/Intermediate/Build/Win64/UnrealEditor/Inc/TestUnityBuild/UHT/HogeActor2.gen.cpp"
#include "D:/work/TestUnityBuild/Intermediate/Build/Win64/UnrealEditor/Inc/TestUnityBuild/UHT/TestUnityBuild.init.gen.cpp"
#include "D:/work/TestUnityBuild/Intermediate/Build/Win64/x64/UnrealEditor/DebugGame/TestUnityBuild/PerModuleInline.gen.cpp"
#include "D:/work/TestUnityBuild/Source/TestUnityBuild/Private/HogeActor1.cpp"
#include "D:/work/TestUnityBuild/Source/TestUnityBuild/Private/HogeActor2.cpp"
#include "D:/work/TestUnityBuild/Source/TestUnityBuild/TestUnityBuild.cpp"
```

次にHogeActor2.cppに差分を追加してビルドが失敗することを確認します。

空行を追加します。git statusでModifiedの差分が発生します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29542/97b924d6-d9bd-475d-b3a7-64e878284731.png)

リビルドを行います。
HogeActor2.cppがUnityビルドから除外され、ビルドが失敗することが確認できます。

```ログ出力.log
12>Using 'git status' to determine working set for adaptive non-unity build (D:\work\TestUnityBuild).
// 略
// HogeActor2.cppがUnityビルドから除外されている
12>[Adaptive Build] Excluded from TestUnityBuild unity file: HogeActor2.cpp
// 略
12>[1/7] Resource Default.rc2
12>[2/7] Compile [x64] SharedPCH.UnrealEd.Project.NonOptimized.ValApi.Cpp20.cpp
12>[3/7] Compile [x64] HogeActor2.cpp
12>D:\work\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(9): error C2653: 'UKismetSystemLibrary': 識別子がクラス名でも名前空間名でもありません。
12>D:\work\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(9): error C2664: 'void DrawDebugSphere(const UWorld *,const FVector &,float,int32,const FColor &,bool,float,uint8,float)': 引数 5 を 'const FLinearColor' から 'const FColor &' へ変換できません。
12>D:\work\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(9): note: 理由: 'const FLinearColor' から 'const FColor' へは変換できません。
12>D:\work\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(9): note: この変換を実行可能なユーザー定義変換演算子がないか、または演算子を呼び出せません。
12>C:\Program Files\Epic Games\UE_5.5\Engine\Source\Runtime\Engine\Public\DrawDebugHelpers.h(44): note: 'DrawDebugSphere' の宣言を確認してください
12>D:\work\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(9): note: 引数リスト '(UWorld *, FVector, float, int, const FLinearColor, float)' を一致させようとしているとき
12>[4/7] Compile [x64] Module.TestUnityBuild.cpp
// 略
12>プロジェクト "TestUnityBuild.vcxproj" のビルドが終了しました -- 失敗。
========== すべて再構築: 11 正常終了、1 失敗、0 スキップ ==========
```

Module.Test.cppからHogeActor2.cppが消えていることが確認できます。
```Intermediate\Build\Win64\x64\UnrealEditor\DebugGame\TestUnityBuild\Module.TestUnityBuild.cpp
// This file is automatically generated at compile-time to include some subset of the user-created cpp files.
#include "D:/work/TestUnityBuild/Intermediate/Build/Win64/UnrealEditor/Inc/TestUnityBuild/UHT/HogeActor1.gen.cpp"
#include "D:/work/TestUnityBuild/Intermediate/Build/Win64/UnrealEditor/Inc/TestUnityBuild/UHT/HogeActor2.gen.cpp"
#include "D:/work/TestUnityBuild/Intermediate/Build/Win64/UnrealEditor/Inc/TestUnityBuild/UHT/TestUnityBuild.init.gen.cpp"
#include "D:/work/TestUnityBuild/Intermediate/Build/Win64/x64/UnrealEditor/DebugGame/TestUnityBuild/PerModuleInline.gen.cpp"
#include "D:/work/TestUnityBuild/Source/TestUnityBuild/Private/HogeActor1.cpp"
#include "D:/work/TestUnityBuild/Source/TestUnityBuild/TestUnityBuild.cpp"
```

## Perforceで管理されている場合

まず最初は↓の画像のようにチェックアウトは取られておらず、読み取り専用属性がONなっております。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29542/016cc186-7583-4cf4-bac7-79ce26722202.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29542/0c90bbc5-99e4-4f82-bebe-02b29fd7aefe.png)

まずビルドが成功することを確認します。

TestUnityBuildのリビルドを行います。

```ログ出力.log
12>[1/6] Resource Default.rc2
12>[2/6] Compile [x64] SharedPCH.UnrealEd.Project.NonOptimized.ValApi.Cpp20.cpp
12>[3/6] Compile [x64] Module.TestUnityBuild.cpp
12>[4/6] Link [x64] UnrealEditor-TestUnityBuild-Win64-DebugGame.lib
12>   ライブラリ D:\work\perforce\TestUnityBuild\Intermediate\Build\Win64\x64\UnrealEditor\DebugGame\TestUnityBuild\UnrealEditor-TestUnityBuild-Win64-DebugGame.lib とオブジェクト D:\work\perforce\TestUnityBuild\Intermediate\Build\Win64\x64\UnrealEditor\DebugGame\TestUnityBuild\UnrealEditor-TestUnityBuild-Win64-DebugGame.exp を作成中
12>[5/6] Link [x64] UnrealEditor-TestUnityBuild-Win64-DebugGame.dll
12>   オブジェクトを作成しています D:\work\perforce\TestUnityBuild\Binaries\Win64\UnrealEditor-TestUnityBuild-Win64-DebugGame.exp
12>[6/6] WriteMetadata TestUnityBuildEditor-Win64-DebugGame.target (UBA disabled)
// 略
12>プロジェクト "TestUnityBuild.vcxproj" のビルドが終了しました。
========== すべて再構築: 12 正常終了、0 失敗、0 スキップ ==========
```

無事にビルドに成功します。

次に、HogeActor2.cppをチェックアウトして、ビルドが失敗することを確認します。

P4Vからチェックアウトを行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29542/3ab092b6-c92c-4d39-86c8-a094d6a1818c.png)

チェックアウトされ、アイコンの左上に赤チェックマークが付きました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29542/f9ebcf3e-37b0-4fdb-b9ac-ff10ec7a3466.png)

HogeActor2.cppのプロパティを確認すると、読み取り専用属性がOFFになっていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29542/f66b30f9-a706-4190-8eae-afbb50767004.png)

リビルドを行います。
HogeActor2.cppがUnityビルドから除外され、ビルドが失敗することが確認できます。

```ログ出力.log
12>[Adaptive Build] Excluded from TestUnityBuild unity file: HogeActor2.cpp
// 略
12>[1/7] Resource Default.rc2
12>[2/7] Compile [x64] SharedPCH.UnrealEd.Project.NonOptimized.ValApi.Cpp20.cpp
12>[3/7] Compile [x64] HogeActor2.cpp
12>D:\work\perforce\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(8): error C2653: 'UKismetSystemLibrary': 識別子がクラス名でも名前空間名でもありません。
12>D:\work\perforce\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(8): error C2664: 'void DrawDebugSphere(const UWorld *,const FVector &,float,int32,const FColor &,bool,float,uint8,float)': 引数 5 を 'const FLinearColor' から 'const FColor &' へ変換できません。
12>D:\work\perforce\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(8): note: 理由: 'const FLinearColor' から 'const FColor' へは変換できません。
12>D:\work\perforce\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(8): note: この変換を実行可能なユーザー定義変換演算子がないか、または演算子を呼び出せません。
12>C:\Program Files\Epic Games\UE_5.5\Engine\Source\Runtime\Engine\Public\DrawDebugHelpers.h(44): note: 'DrawDebugSphere' の宣言を確認してください
12>D:\work\perforce\TestUnityBuild\Source\TestUnityBuild\Private\HogeActor2.cpp(8): note: 引数リスト '(UWorld *, FVector, float, int, const FLinearColor, float)' を一致させようとしているとき
12>[4/7] Compile [x64] Module.TestUnityBuild.cpp
// 略
12>プロジェクト "TestUnityBuild.vcxproj" のビルドが終了しました -- 失敗。
========== すべて再構築: 11 正常終了、1 失敗、0 スキップ ==========
```

# 参考文献
- [[UE4] Unityビルドシステム｜株式会社ヒストリア](https://historia.co.jp/archives/4393/)
- [Unreal Engine のビルド コンフィギュレーション | Unreal Engine 5.3 ドキュメンテーション | Epic Developer Community](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/build-configuration-for-unreal-engine?application_version=5.3)
- [Git is breaking compile? - Development / Platform & Builds - Epic Developer Community Forums](https://forums.unrealengine.com/t/git-is-breaking-compile/421820)
    - gitがコンパイルを壊しているのではなく、特定のcppファイルがUnityビルドシステムから除外されたときにビルドが通らなくなってしまっているソースコード側に問題がある可能性高そうです