UE4 は C# で書かれた独自のビルドシステム UnrealBuildTool を使用しています。Makefile に相当するのが Module 毎に用意する *.Build.cs ファイルです。
例えば GameProject/Source/GameModule をビルドする場合下記のようなファイルを作ります。
// GameProject/Source/GameModule/GameModule.Build.cs
using UnrealBuildTool;
namespace UnrealBuildTool.Rules
{
public class GameModule : ModuleRules {
public GameModule( ReadOnlyTargetRules Target ) : base( Target )
{
}
}
}
この中で依存モジュールの宣言や、外部ライブラリの path 追加などさまざまな設定を行うことができます。
*.Build.cs の他に Project 全体の設定を記述する *.Target.cs もあります。
*.Build.cs |
Module 毎のビルド設定 |
*.Target.cs |
Project 全体のビルド設定 |
Target.cs はあくまで Project 単位に用意するため Plugin にはありません。また Build 対象に応じて Target.cs が変わるため複数存在します。
~.Target.cs |
Game |
~Editor.Target.cs |
Editor |
~Server.Target.cs |
Server |
~Client.Target.cs |
Client |
●他の Module を参照する場合
UE4 内部の Module 同士の依存関係は Module 名で管理されています。そのため Module については個別に Include path を与えたり Link するライブラリを指定する必要がありません。
例えば依存ファイルとして下記のような宣言を行えば、それぞれの Module への include も Link する Lib も自動で管理してくれます。
PublicDependencyModuleNames.AddRange( new string[] {
"Core",
"CoreUObject",
"Engine",
"Projects",
});
Public と Private の違いはわかりにくいですが、依存している Module の Include path を追加するかどうかの違いとなります。
例えば GameModule を参照している他の MainModule があったとします。
// GameModule.Build.cs
using UnrealBuildTool;
namespace UnrealBuildTool.Rules
{
public class GameModule : ModuleRules {
public GameModule( ReadOnlyTargetRules Target ) : base( Target )
{
PublicDependencyModuleNames.AddRange( new string[] {
"Core",
});
}
}
}
// MainModule.Build.cs
using UnrealBuildTool;
namespace UnrealBuildTool.Rules
{
public class MainModule : ModuleRules {
public MainModule( ReadOnlyTargetRules Target ) : base( Target )
{
PrivateDependencyModuleNames.AddRange( new string[] {
"GameModule",
});
}
}
}
このとき GameModule の Core は PublicDependency なので、MainModule の include path に Core が含まれます。もし GameModule の Core が Private なら MainModule の include path に Core は含まれません。
つまり、他の Module に公開したいヘッダファイルがある場合、ヘッダ側で include している Module は Public にしておく必要があります。他の Module から参照されることがない GameProject のような Module (PRIMARY_GAME_MODULE) では、すべて Private 宣言にして構いません。
●外部ライブラリを参照する場合
UnrealBuildTool は “ThirdParty” というフォルダを特別扱いします。このフォルダは検索対象から外れており、ソースコードがあっても勝手にビルドを行いません。外部ライブラリは一般的に ThirdParty フォルダに入れておきます。Build.cs では下記のように相対的に ThirdParty のパスを求めます。
using UnrealBuildTool;
namespace UnrealBuildTool.Rules
{
public class GamePlugin : ModuleRules {
private string ThirdPartyPath
{
get { return Path.GetFullPath( Path.Combine( ModuleDirectory, "../../../ThirdParty/" ) ); }
}
~
Include path, Lib path, Lib file 等は個別に指定が必要で、さらに Platform も複数あるので外部ライブラリの参照は複雑になりがちです。
using UnrealBuildTool;
using System.IO;
using System.Collections.Generic;
namespace UnrealBuildTool.Rules
{
public class GamePlugin : ModuleRules {
private string ThirdPartyPath
{
get { return Path.GetFullPath( Path.Combine( ModuleDirectory, "../../../ThirdParty/" ) ); }
}
private Dictionary PlatformTable= new Dictionary(){
{ UnrealTargetPlatform.Win64, "Windows/x64", },
{ UnrealTargetPlatform.Win32, "Windows/x86", },
{ UnrealTargetPlatform.Linux, "Linux/x64", },
{ UnrealTargetPlatform.Android, "Android28", },
{ UnrealTargetPlatform.Mac, "macOS/x64", },
{ UnrealTargetPlatform.IOS, "iOS/arm64", },
};
public GamePlugin( ReadOnlyTargetRules Target ) : base( Target )
{
PublicDependencyModuleNames.AddRange( new string[] {
"Core",
"CoreUObject",
"Engine",
"Projects",
}
if( !PlatformTable.ContainsKey( Target.Platform ) ){
return;
}
bool debug= Target.Configuration == UnrealTargetConfiguration.Debug && Target.bDebugBuildsActuallyUseDebugCRT;
string config= debug ? "Debug" : "Release";
string PlatformName= PlatformTable[ Target.Platform ];
PublicIncludePaths.AddRange( new string[] {
Path.Combine( ThirdPartyPath, "flatlib" ),
});
if( Target.Platform == UnrealTargetConfiguration.Android ){
PublicLibraryPaths.AddRange( new string[] {
Path.Combine( ThirdPartyPath, "flatlib/lib", PlatformName, "arm7", config ),
Path.Combine( ThirdPartyPath, "flatlib/lib", PlatformName, "arm64", config ),
Path.Combine( ThirdPartyPath, "flatlib/lib", PlatformName, "x86", config ),
Path.Combine( ThirdPartyPath, "flatlib/lib", PlatformName, "x64", config ),
});
}else{
PublicLibraryPaths.AddRange( new string[] {
Path.Combine( ThirdPartyPath, "flatlib/lib", PlatformName, config ),
});
}
PublicAdditionalLibraries.Add( "flatCore" );
}
}
}
外部ライブラリを必要としている Module や Plugin があれば、それぞれの Build.cs に記述しておく必要があります。Makefile の include のように共通の設定を動的に読み込む仕組みがあれば良いのですが、特に用意されていないようです。
●UnrealBuildTool による Build.cs の読み込み方
*.Build.cs は UnrealBuildTool によって dll に変換されます。
(1) Intermediate/Build/BuildRules/~.dll を読み込む
(2) もし *.Build.cs が更新されている場合は動的にコンパイルを行い dll を Reload する
この dll の生成は Project 単位で行います。例えば GameProject の場合、下記のファイルをまとめてコンパイルし単一の GameProjectModuleRules.dll が作られます。
GameProjectEditor.Target.cs
GameProject.Build.cs
GameModule.Build.cs
MainModule.Build.cs
→ GameProject/Intermediate/Build/BuildRules/GameProjectModuleRules.dll
Engine も同様で、下記の cs ファイルから単一の dll にコンパイルします。
Engine/Source/UE4Editor.Target.cs
Engine/Plugins/~/*.Build.cs
Engine/Source/~/*.Build.cs
→ Engine/Intermediate/Build/BuildRules/UE4Rules.dll
よって動的な include はできないものの、他の Build.cs や Target.cs で宣言したコードを呼び出すことが可能です。例えば代表 Module を 1つ決めて、Build.cs に共通で使いたいコードを書いておきます。
// GameProject.Build.cs
using UnrealBuildTool;
using System.IO;
using System.Collections.Generic;
namespace UnrealBuildTool.Rules
{
public class ThirdPartyCommonLibrary {
~
public static ThirdPartyCommonLibrary Create( ModuleRules rules, ReadOnlyTargetRules target, string path= null )
{
~
}
public void SetupFlatlib()
{
~
}
}
public class GameProject : ModuleRules {
public GameProject( ReadOnlyTargetRules Target ) : base( Target )
{
PublicDependencyModuleNames.AddRange( new string[] {
"Core",
"CoreUObject",
"Engine",
}
var lib= ThirdPartyCommonLibrary.Create( this, Target );
lib.SetupFlatlib();
}
}
}
他の Module の *.Build.cs からも参照できます。これでメンテしやすくなりすっきりしました。
// GameModule.Build.cs
using UnrealBuildTool;
namespace UnrealBuildTool.Rules
{
public class GameModule : ModuleRules {
public GameModule( ReadOnlyTargetRules Target ) : base( Target )
{
PublicDependencyModuleNames.AddRange( new string[] {
"Core",
"CoreUObject",
"Engine",
}
var lib= ThirdPartyCommonLibrary.Create( this, Target );
lib.SetupFlatlib();
}
}
}