UE4编译模块系统
模块(Module)是构成Unreal Engine的基本元素,每个Module(build.cs)封装和实现了一组功能,并且可以供其他的Module使用。
整个Unreal Engine就是靠各个Module组合来构成一个插件(uplugin)、项目(uproject)和目标(Target.cs)。
build.cs
一个模块是UE4的一个编译块,在windows编辑器中对应一个dll模块,在mac编辑器中对一个dylib模块
每个模块有一个build.cs文件,控制的是Module编译过程,由它来控制所属Module的对其他Module的依赖、文件包含、链接、宏定义等相关的操作
开发人员通过编写该文件来定义不同平台下模块的编译选项(如:头文件查找目录,预编译宏,依赖模块列表等)
如Protobuf.Build.cs的配置内容如下:
using UnrealBuildTool; using System; using System.IO; public class Protobuf : ModuleRules // ModuleRules类型详见:ModuleRules.cs { public Protobuf(ReadOnlyTargetRules Target) : base(Target) {
// ModuleType.CPlusPlus 表示c++源码集成,会参与编译、链接、打包等
// ModuleType.External 表示第三方库形式集成,不参与编译,参加链接、打包等
Type = ModuleType.CPlusPlus;
// ModuleRules.PCHUsageMode.Default Engine modules use shared PCHs, game modules do not
// ModuleRules.PCHUsageMode.NoPCHs Never use any PCHs
// ModuleRules.PCHUsageMode.NoSharedPCHs Never use shared PCHs. Always generate a unique PCH for this module if appropriate
// ModuleRules.PCHUsageMode.UseSharedPCHs Shared PCHs are OK
// ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs Shared PCHs may be used if an explicit private PCH is not set through PrivatePCHHeaderFile. In either case, none of the source files manually include a module PCH, and should include a matching header instead.
PCHUsage = PCHUsageMode.NoSharedPCHs; string MFProtobufHeadDir = Path.Combine(ModuleDirectory, "Public"); PublicSystemIncludePaths.Add(MFProtobufHeadDir); bEnableUndefinedIdentifierWarnings = false; Definitions.AddRange( new string[] { "HAVE_PTHREAD", "PROTOBUF_INLINE_NOT_IN_HEADERS=1", "GOOGLE_PROTOBUF_NO_RTTI" }); if (Target.Platform == UnrealTargetPlatform.Win32 || Target.Platform == UnrealTargetPlatform.Win64) { Definitions.Add("PROTOBUF_USE_DLLS"); Definitions.Add("_CRT_SECURE_NO_WARNINGS"); } PublicDependencyModuleNames.AddRange(new string[] { "Core", }); } }
Definitions:存放所有定义的宏。这些宏会被写入Intermediate\Build\Win64\UE4Editor\Debug\<Module>\Definitions.<Module>.h中
编译器在编译每个c/cpp文件时,会带上/FI "Intermediate\Build\Win64\UE4Editor\Debug\<Module>\Definitions.<Module>.h"参数,将在c/cpp文件的第一行上#include该文件
每个模块中必须定义一个从IModuleInterface派生的类:
/*** UnrealEngine\Engine\Source\Runtime\Core\Public\Modules\ModuleInterface.h ***/ class IModuleInterface { public: // 析构函数 virtual ~IModuleInterface() { } // 模块加载创建时被调用 virtual void StartupModule() { } // 模块卸载前被调用 virtual void PreUnloadCallback() { } // 模块加载后被调用 virtual void PostLoadCallback() { } // 模块销毁前被嗲用 virtual void ShutdownModule() { } // 模块在运行时是否支持动态加载和卸载 virtual bool SupportsDynamicReloading() { return true; } // 进程退出时,模块是否也自动Shutdown virtual bool SupportsAutomaticShutdown() { return true; } // 是否为Gameplay的模块 virtual bool IsGameModule() const { return false; } };
并通过IMPLEMENT_MODULE宏(或IMPLEMENT_GAME_MODULE宏,注:两个宏为同一个东西)来创建该类的实例
/*** UnrealEngine\Engine\Source\Runtime\Core\Public\Modules\ModuleManager.h ***/
#if IS_MONOLITHIC // Monolithic,宏IS_MONOLITHIC为1,即:代码会编译到一个模块中 如:cook后的pc和手机版本 // If we're linking monolithically we assume all modules are linked in with the main binary. #define IMPLEMENT_MODULE( ModuleImplClass, ModuleName ) \ /** Global registrant object for this module when linked statically */ \ static FStaticallyLinkedModuleRegistrant< ModuleImplClass > ModuleRegistrant##ModuleName( TEXT(#ModuleName) ); \ /* Forced reference to this function is added by the linker to check that each module uses IMPLEMENT_MODULE */ \ extern "C" void IMPLEMENT_MODULE_##ModuleName() { } \ PER_MODULE_BOILERPLATE_ANYLINK(ModuleImplClass, ModuleName) #else // 非Monolithic,宏IS_MONOLITHIC为0,即:代码会编译到多个dll中 如:在编辑器或standalone下 #define IMPLEMENT_MODULE( ModuleImplClass, ModuleName ) \ \ /**/ \ /* InitializeModule function, called by module manager after this module's DLL has been loaded */ \ /**/ \ /* @return Returns an instance of this module */ \ /**/ \ extern "C" DLLEXPORT IModuleInterface* InitializeModule() \ { \ return new ModuleImplClass(); \ // 创建模块对象的实例 } \ /* Forced reference to this function is added by the linker to check that each module uses IMPLEMENT_MODULE */ \ extern "C" void IMPLEMENT_MODULE_##ModuleName() { } \ PER_MODULE_BOILERPLATE \ PER_MODULE_BOILERPLATE_ANYLINK(ModuleImplClass, ModuleName) #endif //IS_MONOLITHIC
如:Protobuf\Protobuf.cpp
#include "Protobuf.h" #include "Modules/ModuleManager.h" IMPLEMENT_GAME_MODULE(FDefaultGameModuleImpl, Protobuf);
模块的加载逻辑在FModuleManager::LoadModule函数中。FModuleManager类详见:
/*** UnrealEngine\Engine\Source\Runtime\Core\Public\Modules\ModuleManager.h ***/ class CORE_API FModuleManager : private FSelfRegisteringExec // 自动注册Exec函数来支持控制台命令 { // 析构函数 ~FModuleManager(); public: // 获取FModuleManager单例 static FModuleManager& Get( ); // 销毁FModuleManager单例 static void TearDown(); // 抛弃模块 void AbandonModule( const FName InModuleName ); // 添加模块 void AddModule( const FName InModuleName ); #if !IS_MONOLITHIC void RefreshModuleFilenameFromManifest(const FName InModuleName); #endif // !IS_MONOLITHIC // 得到指定模块指针 IModuleInterface* GetModule( const FName InModuleName ); // 指定模块是否加载 bool IsModuleLoaded( const FName InModuleName ) const; // 加载指定模块 IModuleInterface* LoadModule( const FName InModuleName ); // 加载指定模块,并check检查它是否加载成功 IModuleInterface& LoadModuleChecked( const FName InModuleName ); // 加载指定模块,并调用该模块的PostLoadCallback函数 bool LoadModuleWithCallback( const FName InModuleName, FOutputDevice &Ar ); // 加载指定模块,如果失败则返回原因 IModuleInterface* LoadModuleWithFailureReason( const FName InModuleName, EModuleLoadResult& OutFailureReason ); // 查询指定模块的信息 bool QueryModule( const FName InModuleName, FModuleStatus& OutModuleStatus ) const; // 查询指定多个模块的信息 void QueryModules( TArray<FModuleStatus>& OutModuleStatuses ) const; // 卸载指定模块 bool UnloadModule( const FName InModuleName, bool bIsShutdown = false ); // 卸载或抛弃指定模块,在此之前调用PreUnloadCallback函数 void UnloadOrAbandonModuleWithCallback( const FName InModuleName, FOutputDevice &Ar); // 抛弃指定模块,在此之前调用PreUnloadCallback函数 void AbandonModuleWithCallback( const FName InModuleName ); // 增加额外的搜索路径 void AddExtraBinarySearchPaths(); // 获取指定模块,并对它进行check检查 template<typename TModuleInterface> static TModuleInterface& GetModuleChecked( const FName ModuleName ) { FModuleManager& ModuleManager = FModuleManager::Get(); checkf(ModuleManager.IsModuleLoaded(ModuleName), TEXT("Tried to get module interface for unloaded module: '%s'"), *(ModuleName.ToString())); return static_cast<TModuleInterface&>(*ModuleManager.GetModule(ModuleName)); } private: static IModuleInterface* GetModulePtr_Internal(FName ModuleName); public: // 获取指定模块 template<typename TModuleInterface> static FORCEINLINE TModuleInterface* GetModulePtr( const FName ModuleName ) { return static_cast<TModuleInterface*>(GetModulePtr_Internal(ModuleName)); } // 加载指定模块,并check检查它是否加载成功 template<typename TModuleInterface> static TModuleInterface& LoadModuleChecked( const FName InModuleName) { IModuleInterface& ModuleInterface = FModuleManager::Get().LoadModuleChecked(InModuleName); return static_cast<TModuleInterface&>(ModuleInterface); } // 加载指定模块 template<typename TModuleInterface> static TModuleInterface* LoadModulePtr( const FName InModuleName) { return static_cast<TModuleInterface*>(FModuleManager::Get().LoadModule(InModuleName)); } // 使用通配符来查找模块 void FindModules( const TCHAR* WildcardWithoutExtension, TArray<FName>& OutModules ) const; // 模块名的模块是否存在 bool ModuleExists(const TCHAR* ModuleName) const; // 当前加载的模块数 int32 GetModuleCount( ) const; // 关闭进程时卸载模块 void UnloadModulesAtShutdown( ); // ... ...// FSelfRegisteringExec interface. 控制台命令执行函数 virtual bool Exec( UWorld* Inworld, const TCHAR* Cmd, FOutputDevice& Ar ) override; // ... ... /** * Information about a single module (may or may not be loaded.) */ class FModuleInfo { public: /** The original file name of the module, without any suffixes added */ FString OriginalFilename; /** File name of this module (.dll file name) */ FString Filename; /** Handle to this module (DLL handle), if it's currently loaded */ void* Handle; /** The module object for this module. We actually *own* this module, so it's lifetime is controlled by the scope of this shared pointer. */ TUniquePtr<IModuleInterface> Module; /** True if this module was unloaded at shutdown time, and we never want it to be loaded again */ bool bWasUnloadedAtShutdown; /** True if this module is full loaded and ready to be used */ TAtomic<bool> bIsReady; /** Arbitrary number that encodes the load order of this module, so we can shut them down in reverse order. */ int32 LoadOrder; /** static that tracks the current load number. Incremented whenever we add a new module*/ static int32 CurrentLoadOrder; public: /** Constructor */ FModuleInfo() : Handle(nullptr) , bWasUnloadedAtShutdown(false) , bIsReady(false) , LoadOrder(CurrentLoadOrder++) { } ~FModuleInfo() { } }; // ... ... };
每个模块的c++代码中都可以使用 {全大写的模块名}_API的宏来在本模块中导出类和函数给其他模块使用
进行动态链接库类型(dll, dylib)编译时,UBT会将当前模块的宏定义为DLLEXPORT来导出模块的类或函数,将非当前模块的宏定义为DLLIMPORT来导入其他模块的类或函数
DLLEXPORT和DLLIMPORT在不同的平台会被定义为如下内容:
系统平台 #include"HAL/Platform.h" |
宏定义 |
windows 见“WIndowsPlatform.h” |
#define DLLEXPORT __declspec(dllexport) |
Unix/Linux 见“UnixPlatform.h” |
#define DLLEXPORT __attribute__((visibility("default"))) |
Mac 见“MacPlatform.h” |
#define DLLEXPORT |
Android 见“AndroidPlatform.h” |
#define DLLEXPORT __attribute__((visibility("default"))) |
iOS 见“IOSPlatform.h” |
#define DLLEXPORT |
uproject、plugin
游戏可以有多个模块(需配置在MyTest1.uproject),插件也可以有多个模块(需配置在plugin文件中)。
游戏和插件中定义了其内部的模块及依赖的第三方plugin,并能拥有自己独立的资源(放在Content目录中)和配置文件(放在Config目录中)。
另外,plugin还能拥有自己独立的着色器文件(放在Shaders目录中)和编辑器资源(放在Resources目录中)。
插件可在plugin文件中设置自己缺省是否启用。 注1:设置EnabledByDefault为false,表示缺省不启用插件 注2:如果没有显示设置EnabledByDefault的值,为引擎插件则不启用,为项目插件则启用
也可以在项目的MyTest1.uproject和项目的其他plugin文件中将某个Plugin关闭。
"Plugins": [ { "Name": "MyPlugin", "Enabled": false } ]
注1:即使将plugin文件EnabledByDefault配置为false,当该plugin中module被其他的module引用到,插件会自动被启用
注2:项目的MyTest1.uproject或plugin依赖某个Plugin时,可以不用显示地配置在uproject或Plugin文件中,但在对应Module的build.cs中必须配置好依赖的外部Module
MyTest1.uproject对应ProjectDescriptor类型(见UnrealBuildTool中的ProjectDescriptor.cs文件),其配置内容如下:
{ "FileVersion": 3, // Descriptor version number. "EngineAssociation": "{CDEC017E-4B01-7570-44D6-02937BB1A0A4}", // The engine to open this project with. "Category": "", // Category to show under the project browser "Description": "", // Description to show in the project browser "Modules": [ // List of all modules associated with this project { "Name": "MyTest1", "Type": "Runtime", "LoadingPhase": "Default", "AdditionalDependencies": [ "Engine", "CoreUObject" ] } ], "Plugins": [ // List of plugins for this project (may be enabled/disabled) { "Name": "MobilePatchingUtils", "Enabled": true }, { "Name": "Wwise", "Enabled": true }, { "Name": "GooglePAD", "Enabled": false } ], // A hash that is used to determine if the project was forked from a sample "EpicSampleNameHash": 125662, // Indicates if this project is an Enterprise project "IsEnterpriseProject": false, // Indicates that enabled by default engine plugins should not be enabled unless explicitly enabled by the project or target files. "DisableEnginePluginsByDefault": false, // Steps to execute before building targets in this project "PreBuildSteps": { "Win64": [ "\"$(PluginDir)\\Source\\AkAudio\\WwisePostBuildSteps.bat\" \"$(EngineDir)\\Binaries\\Win64\\UE4Editor-cmd.exe\" \"$(ProjectFile)\" $(TargetType) -run=AkPluginActivator -platform=$(TargetPlatform) -configuration=Profile -targetconfig=$(TargetConfiguration)" ], "Mac": [ "\"$(PluginDir)/Source/AkAudio/WwisePostBuildSteps.sh\" \"$(EngineDir)/Binaries/Mac/UE4Editor.app/Contents/MacOS/UE4Editor\" \"$(ProjectFile)\" $(TargetType) -run=AkPluginActivator -platform=$(TargetPlatform) -configuration=Profile -targetconfig=$(TargetConfiguration)" ] }, // Steps to execute after building targets in this project "PostBuildSteps": { "Win64": [ "\"$(PluginDir)\\Source\\AkAudio\\WwisePostBuildSteps.bat\" \"$(EngineDir)\\Binaries\\Win64\\UE4Editor-cmd.exe\" \"$(ProjectFile)\" $(TargetType) -run=AkPluginActivator -platform=$(TargetPlatform) -configuration=Profile -targetconfig=$(TargetConfiguration)" ], "Mac": [ "\"$(PluginDir)/Source/AkAudio/WwisePostBuildSteps.sh\" \"$(EngineDir)/Binaries/Mac/UE4Editor.app/Contents/MacOS/UE4Editor\" \"$(ProjectFile)\" $(TargetType) -run=AkPluginActivator -platform=$(TargetPlatform) -configuration=Profile -targetconfig=$(TargetConfiguration)" ] }, "AdditionalRootDirectories": [ // Array of additional root directories "MyRoot/Dir1", // -->D:\svn\MyTest1\MyRoot\Dir1 "MyRoot/Dir2" // -->D:\svn\MyTest1\MyRoot\Dir2 ], "AdditionalPluginDirectories": [ // List of additional plugin directories to scan for available plugins "MyPluginRoot/Dir1", // -->D:\svn\MyTest1\MyPluginRoot\Dir1 "MyPluginRoot/Dir2" // -->D:\svn\MyTest1\MyPluginRoot\Dir1 ], "TargetPlatforms": [ //Array of platforms that this project is targeting "Win64", "Android", "IOS", "Mac", "Linux" ] }
MyTest1.uplugin对应PluginDescriptor类型(见UnrealBuildTool中的PluginDescriptor.cs文件)。如:引擎的WebMMoviePlayer.uplugin插件:
{ "FileVersion": 3, "Version": 1, "VersionName": "1.0", "FriendlyName": "Movie Player for WebM files", "Description": "Movie Player for WebM files", "Category": "Movie Players", "CreatedBy": "Epic Games, Inc.", "CreatedByURL": "http://epicgames.com", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", "EnabledByDefault": true, "CanContainContent": false, "IsBetaVersion": false, "Installed": false, "Modules": [ { "Name": "WebMMoviePlayer", "Type": "RuntimeNoCommandlet", "LoadingPhase": "PreLoadingScreen", "WhitelistPlatforms": [ "Win64", "Linux", "Mac" ], "BlacklistTargets": [ "Server" ] } ], "Plugins": [ { "Name": "WebMMedia", "Enabled": true } ] }
MyTest1.uproject和MyTest1.uplugin中Modules配置的Module,见UnrealBuildTool中的ModuleDescriptor.cs文件:
/// <summary> /// The type of host that can load a module /// </summary> public enum ModuleHostType { /// <summary> /// /// </summary> Default, /// <summary> /// Any target using the UE4 runtime /// </summary> Runtime, /// <summary> /// Any target except for commandlet /// </summary> RuntimeNoCommandlet, /// <summary> /// Any target or program /// </summary> RuntimeAndProgram, /// <summary> /// Loaded only in cooked builds /// </summary> CookedOnly, /// <summary> /// Loaded only in uncooked builds /// </summary> UncookedOnly, /// <summary> /// Loaded only when the engine has support for developer tools enabled /// </summary> Developer, /// <summary> /// Loads on any targets where bBuildDeveloperTools is enabled /// </summary> DeveloperTool, /// <summary> /// Loaded only by the editor /// </summary> Editor, /// <summary> /// Loaded only by the editor, except when running commandlets /// </summary> EditorNoCommandlet, /// <summary> /// Loaded by the editor or program targets /// </summary> EditorAndProgram, /// <summary> /// Loaded only by programs /// </summary> Program, /// <summary> /// Loaded only by servers /// </summary> ServerOnly, /// <summary> /// Loaded only by clients, and commandlets, and editor.... /// </summary> ClientOnly, /// <summary> /// Loaded only by clients and editor (editor can run PIE which is kinda a commandlet) /// </summary> ClientOnlyNoCommandlet, } /// <summary> /// Indicates when the engine should attempt to load this module /// </summary> public enum ModuleLoadingPhase { /// <summary> /// Loaded at the default loading point during startup (during engine init, after game modules are loaded.) /// </summary> Default, /// <summary> /// Right after the default phase /// </summary> PostDefault, /// <summary> /// Right before the default phase /// </summary> PreDefault, /// <summary> /// Loaded as soon as plugins can possibly be loaded (need GConfig) /// </summary> EarliestPossible, /// <summary> /// Loaded before the engine is fully initialized, immediately after the config system has been initialized. Necessary only for very low-level hooks /// </summary> PostConfigInit, /// <summary> /// The first screen to be rendered after system splash screen /// </summary> PostSplashScreen, /// <summary> /// After PostConfigInit and before coreUobject initialized. used for early boot loading screens before the uobjects are initialized /// </summary> PreEarlyLoadingScreen, /// <summary> /// Loaded before the engine is fully initialized for modules that need to hook into the loading screen before it triggers /// </summary> PreLoadingScreen, /// <summary> /// After the engine has been initialized /// </summary> PostEngineInit, /// <summary> /// Do not automatically load this module /// </summary> None, } /// <summary> /// Class containing information about a code module /// </summary> [DebuggerDisplay("Name={Name}")] public class ModuleDescriptor { /// <summary> /// Name of this module /// </summary> public readonly string Name; /// <summary> /// Usage type of module /// </summary> public ModuleHostType Type; /// <summary> /// When should the module be loaded during the startup sequence? This is sort of an advanced setting. /// </summary> public ModuleLoadingPhase LoadingPhase = ModuleLoadingPhase.Default; /// <summary> /// List of allowed platforms /// </summary> public List<UnrealTargetPlatform> WhitelistPlatforms; /// <summary> /// List of disallowed platforms /// </summary> public List<UnrealTargetPlatform> BlacklistPlatforms; /// <summary> /// List of allowed targets /// </summary> public TargetType[] WhitelistTargets; /// <summary> /// List of disallowed targets /// </summary> public TargetType[] BlacklistTargets; /// <summary> /// List of allowed target configurations /// </summary> public UnrealTargetConfiguration[] WhitelistTargetConfigurations; /// <summary> /// List of disallowed target configurations /// </summary> public UnrealTargetConfiguration[] BlacklistTargetConfigurations; /// <summary> /// List of allowed programs /// </summary> public string[] WhitelistPrograms; /// <summary> /// List of disallowed programs /// </summary> public string[] BlacklistPrograms; /// <summary> /// List of additional dependencies for building this module. /// </summary> public string[] AdditionalDependencies; }
Target.cs
Target.cs控制的是生成的可执行程序。如:生成的是什么Type(Game/Client/Server/Editor/Program),开不开启 RTTI (bForceEnableRTTI),CRT 使用什么方式链接 (bUseStaticCRT)等等。
不同Target(MyTest1.Target.cs、MyTest1Client.Target.cs、MyTest1Editor.Target.cs、MyTest1Server.Target.cs)中配置当前项目下属于自己Target的模块列表。
MyTest1Target.cs
using UnrealBuildTool; using System.Collections.Generic; using Tools.DotNETCommon; using System; using System.IO; public class MyTest1Target : TargetRules { public MyTest1Target(TargetInfo Target) : base(Target) { // Enable PSO Collection miss report if(Target.Platform == UnrealTargetPlatform.Android || Target.Platform == UnrealTargetPlatform.IOS) { System.Environment.SetEnvironmentVariable("ENABLE_PSO_REPORT", "FALSE"); } Type = TargetType.Game; ExtraModuleNames.AddRange( new string[] { "MyCommon", "MyGame", } ); if (Configuration == UnrealTargetConfiguration.Shipping || Configuration == UnrealTargetConfiguration.Test) { bUseLoggingInShipping = true; } } }
MyTest1ClientTarget.cs
using UnrealBuildTool; using System.Collections.Generic; using Tools.DotNETCommon; using System; using System.IO; public class MyTest1ClientTarget : TargetRules { public MyTest1ClientTarget(TargetInfo Target) : base(Target) { // Enable PSO Collection miss report if(Target.Platform == UnrealTargetPlatform.Android || Target.Platform == UnrealTargetPlatform.IOS) { System.Environment.SetEnvironmentVariable("ENABLE_PSO_REPORT", "FALSE"); }
// TargetLinkType.Default Use the default link type based on the current target type
// TargetLinkType.Monolithic Link all modules into a single binary
// TargetLinkType.Modular Link modules into individual dynamic libraries
LinkType = TargetLinkType.Default; Type = TargetType.Client; ExtraModuleNames.AddRange( new string[] { "MyCommon", "MyGame", } ); if (Configuration == UnrealTargetConfiguration.Shipping || Configuration == UnrealTargetConfiguration.Test) { bUseLoggingInShipping = true; } } }
MyTest1EditorTarget.cs
using UnrealBuildTool; using System.Collections.Generic; public class MyTest1EditorTarget : TargetRules { public MyTest1EditorTarget(TargetInfo Target) : base(Target) { Type = TargetType.Editor; ExtraModuleNames.AddRange( new string[] { "MyCommon", "MyGame", "MyGameEditor", } ); bUseUnityBuild = true; bUsePCHFiles = true; //for debugging on a different machine bUseFastPDBLinking = false; bCompileForSize = false; bAllowLTCG = false; //Win64开启变量初始化顺序约束,会引发编译引擎和编译项目的参数不同,导致重新编译,放到引擎里面导致引擎通用化减弱,先关闭 if (Target.Platform == UnrealTargetPlatform.Win64) { //bOverrideBuildEnvironment = true; //AdditionalLinkerArguments += " /release"; //AdditionalCompilerArguments += " /we5038"; } } }
MyTest1ServerTarget.cs
using UnrealBuildTool; using System.Collections.Generic; public class MyTest1ServerTarget : TargetRules { public MyTest1ServerTarget(TargetInfo Target) : base(Target) { Type = TargetType.Server; ExtraModuleNames.AddRange( new string[] { "MyCommon", "MyGame", });//for debugging on a different machine bUseFastPDBLinking = false; if (Configuration == UnrealTargetConfiguration.Shipping || Configuration == UnrealTargetConfiguration.Test) { bUseLoggingInShipping = true; } // enable push model bWithPushModel = true; LinuxPlatform.bEnableThinLTO = false; } }
参考
UE Build System:Target and Module
Build flow of the Unreal Engine4 project
UE4 Modules:Find the DLL and load it