首页 > 其他分享 >UE Build System:Target and Module

UE Build System:Target and Module

时间:2023-05-23 19:12:09浏览次数:36  
标签:Target GWorld Module cs Add Build 模块 UE

Module 是构成 Unreal 的基本元素,每一个 Module 封装和实现了一组功能,并且可以供其他的 Module 使用,整个 Unreal Engine 就是靠各个 Module 组合驱动的,连我们创建的游戏项目本身,都是一个单独的 Module。

那么 UE 又是怎么创建和构建这这些 Module 的呢?这是写这篇文章的主要目的,研究一下 Unreal 的构建系统以及它们 (Target 和 Module) 支持的各种属性。

建议在看这篇文章之前先看一下我之前的这篇文章:Build flow of the Unreal Engine4 project,主要内容是大致过一遍 UE 的构建流程,本篇文章只是 UE 构建系统中的一环。

对于 UE 项目比较熟悉的都知道,当使用 UE 创建一个 C++ 游戏项目时,会在项目路径下创建 Source 文件夹,默认包含了下列文件:

 
Example\GWorld\Source>tree /a /f
| GWorld.Target.cs
| GWorldEditor.Target.cs
|
\---GWorld
GWorld.Build.cs
GWorld.cpp
GWorld.h
GWorldGameModeBase.cpp
GWorldGameModeBase.h
 

其中,*.Target.cs*.Build.cs 是 Unreal 构建系统的实际控制者,UBT 通过扫描这两个文件来确定整个编译环境,它们也是本篇文章研究的重点。
它们的职责各不相同:

  • *.Target.cs 控制的是生成的可执行程序的外部编译环境,就是所谓的 Target。比如,生成的是什么 Type(Game/Client/Server/Editor/Program),开不开启 RTTI (bForceEnableRTTI),CRT 使用什么方式链接 (bUseStaticCRT) 等等。
  • *.Build.cs 控制的是 Module 编译过程,由它来控制所属 Module 的对其他 Module 的依赖、文件包含、链接、宏定义等等相关的操作,*.Build.cs 告诉 UE 的构建系统,它是一个 Module,并且编译的时候要做哪些事情。

以一言以蔽之:与外部编译环境相关的都归 *.target.cs 管,与 Module 自身相关的都归 *.build.cs 管。

插个题外话,在 GWorld.hGWorld.cpp 中定义的是 Module 真正的执行逻辑,使用 IMPLEMENT_MODULE 定义。UE 中所有的 Module 都是继承自 IModuleInterface,具有以下接口:

 
class IModuleInterface
{
public:
virtual ~IModuleInterface();
virtual void StartupModule();
virtual void PreUnloadCallback();
virtual void PostLoadCallback();
virtual void ShutdownModule();
virtual bool SupportsDynamicReloading();
virtual bool SupportsAutomaticShutdown();
virtual bool IsGameModule(); const
};
 

通过 IModuleInterface 来驱动 Module 的启动与关闭,不过一般 Game Module 不使用这个控制游戏流程。
这部分的详细内容可以看我之前的文章:UE4 Modules:Load and Startup

Target

每一个基于 Unreal 的项目,都有一个 Tergat.cs,具有一个继承自 TargetRules 的类定义;并且默认需要关联着一个同名 (非必要,但建议) 的 Module 的定义,否则编译时会有 Module 未定义错误,它的含意时将指定的 Module 编译到 Target 中:

 
UnrealBuildTool : error : Could not find definition for module 'GWorld' (referenced via GWorld.Target.cs)
 

Target 关联的 Module 的名字可以通过 ExtraModuleNames 来指定:

 
public class GWorldTarget : TargetRules
{
public GWorldTarget(TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
ExtraModuleNames.AddRange( new string[] { "GWorld" } );
}
}
 

上面指定的是 GWorld,UBT 解析的时候就会去找 GWorld 这个 Module 的定义,也就是 GWorld.build.cs 这个文件中的 GWorld 类定义,如果没有就会产生上面的 Module 未定义错误。

注意,与 Target 关联的 Module 不仅仅只是一个指定的名字这么简单,所有代码中使用的 XXXX_API 都是与 Module 的名字相关的。

如果我进行以下改动:ExtraModuleNames.AddRange( new string[] { "GWorldAAA" } );,那么需要对项目中所有的源文件进行的改动有:

  1. 将原有的 GWorld.build.cs 文件改名为 GWorldAAA.build.cs,并将文件内容的所有 GWorld 替换为 GWorldAAA
  2. 将项目内所有头文件的 GWORLD_API 改名为 GWORLDAAA_API,因为 XXX_API 的导出符号是依赖于 ModuleName 的;

实在是个不小的工作量,所以还是建议将 ExtraModuleNames 中指定的名字与 Game Module 同名。
通过上面的内容,我们可以知道了 Target.cs 是如何与 Build.cs 关联的。那么,其实 Game/Server/Client/EditorTarget 可以共用同一个 Module,将他们的 ExtraModuleNames 都设置成同一个就可以了 (如果你想要针对每个 Target 类型单独写也可以)。

TargetRules 的代码在 UnrealBuildTools/Configuration/ModuleRulesReadOnlyTargetRules 也定义其中),可以看一下所支持参数的默认值;UE 对 Target 支持属性的描述文档:Targets

但是 UE 的官方文档里面也只是代码里的注释,有些描述看了之后摸不着头脑,后面我会分析一下 TargetRule 一些属性的含义,先埋个坑。

Type(TargetType)

TargetRules 中的属性 Type,其类型为 TargetType,定义为 TargetRules.cs 中,是指定项目要编译出来的是什么程序。

  • Game - A standalone game which requires cooked data to run.
  • Client - Same as Game, but does not include any server code. Useful for networked games.
  • Server - Same as Game, but does not include any client code. Useful for dedicated servers in networked games.
  • Editor - A target which extends the Unreal Editor.
  • Program - A standalone utility program built on top of the Unreal Engine.

LinkType(TargetLinkType)

TargetRules 中的 LinkType,其类型为 TargetLinkType,定义在 TargetRules.cs 中,是指定项目的链接类型。

TargetLinkType 具有三个枚举值:

 
/// <summary>
/// Specifies how to link all the modules in this target
/// </summary>
[Serializable]
public enum TargetLinkType
{
/// <summary>
/// Use the default link type based on the current target type
/// </summary>
Default,

/// <summary>
/// Link all modules into a single binary
/// </summary>
Monolithic,

/// <summary>
/// Link modules into individual dynamic libraries
/// </summary>
Modular,
}
  展开代码
  • TargetLinkType.DefaultLinkType 的默认值,在此种状态下,如果当前 TargetTypeEditor 则使用 Modular 类型,链接所有的模块的方式为动态链接库。
  • TargetLinkType.Modular:以动态链接库的方式链接 Module
  • TargetLinkType.Monolithic:将所有的模块链接到单个文件 (静态链接)

可以通过修改 LinkType 来修改。

 
/// <summary>
/// Backing storage for the LinkType property.
/// </summary>
[RequiresUniqueBuildEnvironment]
[CommandLine("-Monolithic", Value ="Monolithic")]
[CommandLine("-Modular", Value ="Modular")]
TargetLinkType LinkTypePrivate = TargetLinkType.Default;

/// <summary>
/// Specifies how to link modules in this target (monolithic or modular). This is currently protected for backwards compatibility. Call the GetLinkType() accessor
/// until support for the deprecated ShouldCompileMonolithic() override has been removed.
/// </summary>
public TargetLinkType LinkType
{
get
{
return (LinkTypePrivate != TargetLinkType.Default) ? LinkTypePrivate : ((Type == global::UnrealBuildTool.TargetType.Editor) ? TargetLinkType.Modular : TargetLinkType.Monolithic);
}
set
{
LinkTypePrivate = value;
}
}
  展开代码

Name(string)

Target 的名字,只读属性,传进来的项目名字。

Platform(UnrealTargetPlatform)

Platform 的类型为 UnrealTargetPlatform,它是一个枚举,定义在 UnrealBuildTool\Configuration\UEBuildTarget.cs

它记录着当前 Target 的平台信息,如 Win32/Win64 等等,目前 UE_4.22 的版本支持的平台为:

 
public enum UnrealTargetPlatform
{
/// <summary>
/// Unknown target platform
/// </summary>
Unknown,

/// <summary>
/// 32-bit Windows
/// </summary>
Win32,

/// <summary>
/// 64-bit Windows
/// </summary>
Win64,

/// <summary>
/// Mac
/// </summary>
Mac,

/// <summary>
/// XboxOne
/// </summary>
XboxOne,

/// <summary>
/// Playstation 4
/// </summary>
PS4,

/// <summary>
/// iOS
/// </summary>
IOS,

/// <summary>
/// Android
/// </summary>
Android,

/// <summary>
/// HTML5
/// </summary>
HTML5,

/// <summary>
/// Linux
/// </summary>
Linux,

/// <summary>
/// All desktop platforms
/// </summary>
AllDesktop,

/// <summary>
/// TVOS
/// </summary>
TVOS,

/// <summary>
/// Nintendo Switch
/// </summary>
Switch,

/// <summary>
/// NDA'd platform Quail
/// </summary>
Quail,

/// <summary>
/// Confidential platform
/// </summary>
Lumin,
}
  展开代码

我们可以在 build.cs 或者 target.cs 中通过判断 Platform 来做不同的事情。

如:

 
if(Target.Platform != UnrealTargetPlatform.Win32 && Target.Platform != UnrealTargetPlatform.Win64)
{
PublicDefinitions.Add("HAVE_PTHREAD");
}
 

IsInPlatformGroup

这是一个函数 bool IsInPlatformGroup(UnrealPlatformGroup Group),定义在 TargetRules.cs 中,它用来判断当前的 Platform 是否输入某一组。

需要传入的参数为 UnrealTargetformGroup 枚举类型,它定义在 UEBuildTarget.cs 中:

 
/// <summary>
/// Platform groups
/// </summary>
public enum UnrealPlatformGroup
{
/// <summary>
/// this group is just to lump Win32 and Win64 into Windows directories, removing the special Windows logic in MakeListOfUnsupportedPlatforms
/// </summary>
Windows,

/// <summary>
/// Microsoft platforms
/// </summary>
Microsoft,

/// <summary>
/// Apple platforms
/// </summary>
Apple,

/// <summary>
/// making IOS a group allows TVOS to compile IOS code
/// </summary>
IOS,

/// <summary>
/// Unix platforms
/// </summary>
Unix,

/// <summary>
/// Android platforms
/// </summary>
Android,

/// <summary>
/// Sony platforms
/// </summary>
Sony,

/// <summary>
/// Target all desktop platforms (Win64, Mac, Linux) simultaneously
/// </summary>
AllDesktop,
}
  展开代码

Configuration(UnrealTargetConfiguration)

当前编译的配置,类型为 UnrealTargetConfiguration 的枚举,定义在 UEBuildTarget.cs 中,由 VS 中的 Configuration 构造而来,如:

  • Development
  • Shipping
  • DebugGame
  • Debug
  • Test
  • Unknow

也就是通过这个设置,UBT 才在编译环境中添加了下列宏:

 
public override void SetUpConfigurationEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment GlobalCompileEnvironment, LinkEnvironment GlobalLinkEnvironment)
{
// other code
UnrealTargetConfiguration CheckConfig = Target.Configuration;
switch (CheckConfig)
{
default:
case UnrealTargetConfiguration.Debug:
GlobalCompileEnvironment.Definitions.Add("UE_BUILD_DEBUG=1");
break;
case UnrealTargetConfiguration.DebugGame:
// Default to Development; can be overridden by individual modules.
case UnrealTargetConfiguration.Development:
GlobalCompileEnvironment.Definitions.Add("UE_BUILD_DEVELOPMENT=1");
break;
case UnrealTargetConfiguration.Shipping:
GlobalCompileEnvironment.Definitions.Add("UE_BUILD_SHIPPING=1");
break;
case UnrealTargetConfiguration.Test:
GlobalCompileEnvironment.Definitions.Add("UE_BUILD_TEST=1");
break;bUseDebugCRT
}
// other code
}
  展开代码

Architecture(string)

所运行的平台的架构信息:x86/arm 等等。

CppStandard(CppStandardVersion)

用于指定编译项目时所用的 C++ 标准版本(在新版本引擎 (4.23) 中才有)。
CppStandardVersion

  • Latast
  • Cpp17
  • Cpp14

这个选项本质上就是将 /std:c++xxx 添加到 VS 的编译选项中。

 
void AppendCLArguments_CPP(CppCompileEnvironment CompileEnvironment, List<string> Arguments)
{
// other code...
if(CompileEnvironment.CppStandard >= CppStandardVersion.Latest)
{
Arguments.Add("/std:c++latest");
}
else if(CompileEnvironment.CppStandard >= CppStandardVersion.Cpp17)
{
Arguments.Add("/std:c++17");
}
else if(CompileEnvironment.CppStandard >= CppStandardVersion.Cpp14)
{
Arguments.Add("/std:c++14");
}
// other code...
}
  展开代码

bUseDebugCRT(bool)

用来控制输出的 Runtime Librart 类型是 MT 还是 MD
还用来控制添加_DEBUGNODEBUG 宏:

 
public override void SetUpConfigurationEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment GlobalCompileEnvironment, LinkEnvironment GlobalLinkEnvironment)
{
if (GlobalCompileEnvironment.bUseDebugCRT)
{
GlobalCompileEnvironment.Definitions.Add("_DEBUG=1"); // the engine doesn't use this, but lots of 3rd party stuff does
}
else
{
GlobalCompileEnvironment.Definitions.Add("NDEBUG=1"); // the engine doesn't use this, but lots of 3rd party stuff does
}
// other code
}
 

ProjectDefinitions(List<string>)

为当前项目添加的宏定义,在整个项目中可用。

GlobalDefinitions(List<string>)

添加在整个 Target 中都可以用的宏定义。

bShouldCompileAsDLL(bool)

将 Target 编译为 DLL,为 true 时要求 LinkTypeMonolithic

 
/// <summary>
/// Whether this target should be compiled as a DLL. Requires LinkType to be set to TargetLinkType.Monolithic.
/// </summary>
[RequiresUniqueBuildEnvironment]
public bool bShouldCompileAsDLL = false;
 

AdditionalCompilerArguments(String)

传递给编译器的参数。

AdditionalLinkerArguments(String)

传递给连接器的参数。

bUsesSlate(bool)

控制打包时时候把 Slate 相关的图片资源打包到 pak 中。

Module

Target 类似,每一个 Unreal 的 Module,都有一个专属的 ModuleName.Build.cs 里面定义着专属的 ModuleName 类,它由 ModuleRules 继承而来,我们对 Module 构建时进行的操作就是通过它来控制。

注意:不管是 Game Module 还是 Plugin Module,只要是项目依赖的 Module,编译时它们都会接收到当前使用的 Target 信息。

ModuleRules 的代码在 UnrealBuildTools/Configuration/ModuleRules,同样可以看一下支持的属性默认值;UE 对 Modules 描述的官方文档:Modules,这里也同样只有代码的注释内容,没有实际例子,我就先来分析一些在工程中常见的 Build.cs 中属性的含义。

*.Build.cs 中可以通过它构造接收的 ReadOnlyTargetRules Target 参数来获取 *.Target.cs 中的属性信息。

 
using UnrealBuildTool;
using System.IO;

public class GWorld : ModuleRules
{
public GWorld(ReadOnlyTargetRules ReadOnlyTargetRules) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
// something
}
}
 

通过 Target 对象,可以在 *.build.cs 中控制对不同的平台 (Platform),架构 (Architecture),以及其他的选项来对 Module 进行不同的操作 (比如定义不同的宏 / 包含不同的 ThridParty / 链接不同的 Lib 等等)。

ModuleDirectory

  • string ModuleDirectory:项目的源码路径 PROJECT_NAME/Source/PROJECT_NAME 的绝对路径。

EngineDirectory

  • string EngineDirectory:引擎目录 Engine/ 在当前环境的绝对路径。

PublicAdditionalLibraries

添加静态链接库文件 (注意与 PublicLibraryPaths 的区别),一般是用于第三方库的链接。

 
PublicAdditionalLibraries.AddRange(
new string[]
{
Path.Combine(ThridPartyPath,"protobuf/lib/Win64/MD/Release","libprotobuf.lib"),
Path.Combine(ThridPartyPath,"protobuf/lib/Win64/MD/Release","libprotobuf-lite.lib"),
Path.Combine(ThridPartyPath,"protobuf/lib/Win64/MD/Release","libprotoc.lib"),
}
);
 

详细的内容可以看:Linking Static Libraries Using The Build System

同样可以用在 DLL 的导入库,与 PublicDelayLoadDLLsRuntimeDependencies 配合使用。

PublicAdditionalShadowFiles

当执行远程编译的时候,指定当前模块需要复制到远程服务器上的文件,确保能够链接成功。

如远程打包 IOS 平台时,需要把当前模块依赖的静态链接库添加到里面(如 Game 模块依赖某个插件中的 External 模块)。

RuntimeDependencies

  • list<RuntimeDependency> RuntimeDependencies:Module 在运行时依赖的文件 (.so/.dll 等),打包时将会拷贝到存储目录。

在打包 Windows 时会直接把文件拷贝到打包的对应目录下,但是在 Android 上会把文件放到 Apk 包的 main.obb.webp 中。

PublicDelayLoadDLLs

  • List<string> PublicDelayLoadDLLs:延迟加载的 DLL 列表,通常用于第三方库。
 
// build.cs
PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "myExternalLib.lib"));
PublicDelayLoadDLLs.Add("myExternalLib.dll");
 

含义是不在程序启动时立即加载 DLL 的列表,等到首次需要使用他们的符号后再进行加载。这样可以在模块的 StartupModule 中自行指定位置并加载他们,从而实现可以不把 dll 放到 exe 的目录。

 
FString AbsPath = FileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*MyLibPath);
FPlatformProcess::AddDllDirectory(AbsPath)
FPlatformProcess::PushDllDirectory(*AbsPath);
// direct call dll function
// or load dll handle
// void* DLLHandle3 = FPlatformProcess::GetDllHandle( L"myExternalLib.dll" );
 

PS:配合 PublicAdditionalLibraries 可以用在使用 DLL 导入库的第三方库。

  1. 使用 PublicAdditionalLibraries 添加 lib
  2. DLL 的名字添加至 PublicDelayLoadDLLs
  3. 使用 RuntimeDependencies 打包时拷贝 dll
  4. 如果拷贝到的目录不是 exe 路径,需要 StartupModule 里执行 AddDllDirectoryPushDllDirectory 把 dll 的路径添加到里面

    PublicDelayLoadDLLs 只添加 xxxx.dll 就可以了,不需要路径。

我使用 GoogleInstantPreview 测试使用 DLL + 导入库并将 DLL 放在非 exe 目录的例子:ue4-plugin-GoogleInstanceIns.7z

PublicDefinitions

  • List<string> PublicDefinitions:为当前 Module 添加公开宏定义,等同于传统 VS 项目在项目设置中添加一个预处理宏。

它被 UBT 分析之后会在产生一个 Definitions.PROJECT_NAME.h 的头文件,里面定了各种宏。

 
Intermediate\Build\Win64\UE4Editor\Development\ReflectionExample\Definitions.ReflectionExample.h
 

PublicSystemIncludePaths

  • List<string> PublicSystemIncludePaths:文档介绍是用于添加系统的 Include 路径,与 PublicIncludePaths 的区别是会跳过头文件解析检查 (但是经我测试,使用这种方式包含的代码依然会检测下列错误 (UE_4.20)):
 
error : Expected mpack-platform.h to be first header included.
 

注意:如果不指定路径,则默认的 IncludePath 路径是 Engine/Source

比如:

 
PublicSystemIncludePaths.AddRange(
new string[] {
"TEST_LIB"
}
);
 

它表示的路径是:

 
D:\UnrealEngine\Epic\UE_4.21\Engine\Source\TEST_LIB
 

所有可以在 *.build.cs 中指定的 *IncludePaths,默认的路径都是 Engine/Source.

PrivateRuntimeLibraryPaths

  • List<string> PrivateRuntimeLibraryPaths:运行时库的搜索路径。例如.so 或者.dll

PublicRuntimeLibraryPaths

  • List<string> PublicRuntimeLibraryPaths:运行时库的搜索路径。例如.so 或者.dll

因为动态链接库的查找路径默认只有:

  1. 系统的 PATH 路径;
  2. 可执行程序的当前目录;

如果我们的动态链接库在其他的位置,运行时就会错误,可以通过 PublicRuntimeLibraryPaths 或者 PrivateRuntimeLibraryPaths 来添加。

PublicLibraryPaths

添加链接库文件的路径,如在源码中使用的:

 



 
 

可以通过 PublicLibraryPaths 来添加依赖的 Lib。

DynamicallyLoadedModuleNames

  • List<string> DynamicallyLoadedModuleNames:添加需要运行时动态加载的 Module,使用 FModuleManager::LoadModuleChecked<MODULE_TYPE>(TEXT("MODULE_NAME")) 等函数启动。
 
// e.g
FModuleManager::LoadModuleChecked< IAIModule >( "AIModule" );
 

PublicDependencyModuleNames

  • List<string> PublicDependencyModuleNames:添加对执行 Module 的源文件依赖,自动添加所依赖 Module 的 PublicPrivate 源文件包含。

PrivateDependencyModuleNames

  • List<string> PrivateDependencyModuleNames:与 PublicDependencyModuleNames 不同的是,意味着所依赖的 Module 中的源文件只可以在 Private 中使用。

假如现在有一个模块 A,还有一个模块 B,他们中都是 UE 的 Module/PublicModule/Private 的文件结构。

  • 如果 B 中依赖 A,如果使用的是 PrivateDependencyModuleNames 的方式添加的依赖,则 A 模块的源文件只可以在 B 的 Private 目录下的源文件中使用,在 Public 目录下的源文件使用时会报 No such file or directory 的错误。
  • 如果使用的是 PublicDependencyModuleNames 方式添加的依赖,则 A 的源文件在 B 的 PublicPrivate 中都可用。

除了上述的区别之外,还影响依赖于 B 模块的模块 ,当一个模块 C 依赖模块 B 的时候,只能访问到 B 模块的 PublicDependencyModule 中的模块暴露出来的类。
例如,C 依赖 B,B 依赖 A;那么,假如 C 想访问 A 中的类则有两种方式:

  1. 在 C 的依赖中添加上 A 模块
  2. 确保 B 在 PublicDependencyModuleNames 依赖中添加的 A 模块,这样 C 就可以间接的访问到 A。

经过测试发现,其实对于游戏模块 (PROJECT_NAME/Source/PROJECT_NAME.target.cs) 使用而言,所依赖的模块是使用 PublicDependencyModuleNames 还是 PrivateDependencyModuleNames 包含,没什么区别。
使用 Private 方式依赖的 Module 中的头文件依然可以在游戏模块的 Public 中用,这一点与插件等其他模块有所不同(但是这只有在所依赖的模块不是 bUsePrecompiled 的基础上的,如果所依赖的模块是 bUsePrecompiled 的,则与其他的模块一样,PrivateDependencyModuleNames 依赖的模块不可以在 Pulibc 目录下的源文件使用),这个行为比较奇怪:有时候出错有时又不出错。

注意:在游戏项目中使用依赖其他 Module 时尽量确定性需求地使用 PrivateDependencyModuleNames 或者 PublicDependencyModuleNames,在组合其他的选项时可能会有一些奇怪的行为。

相关的讨论:

  1. What is the difference between PublicDependencyModuleNames and PrivateDependencyModuleNames
  2. Explanation of Source Code folder structure?

bPrecompile 与 bUsePrecompiled

 
/// <summary>
/// Whether this module should be precompiled. Defaults to the bPrecompile flag from the target. Clear this flag to prevent a module being precompiled.
/// </summary>
public bool bPrecompile;

/// <summary>
/// Whether this module should use precompiled data. Always true for modules created from installed assemblies.
/// </summary>
public bool bUsePrecompiled;
 

这个两个属性需要组合来使用。

考虑下列需求:
如果我们写好的一个模块 A 希望拿给别人来用,但是又不想把所有代码开放出来,该怎么办?

在传统的 C++ 领域,我应该会说:把代码编译成 DLL,然后把头文件和 DLL 发放给用户就可以啦。
对!其实 bPrecompilebUsePrecompiled 就是做的类似的事情。

当我们对模块 A 进行编译之前,在它的 *.build.cs 中添加:

 
public class A : ModuleRules
{
public A(ReadOnlyTargetRules Target) : base(Target)
{
// ...
bPrecompile=true;
// ...
}
}
 

然后编译模块 A。编译完成之后,将模块 A 的 Source/Private 删除 (删除之前请确保你已经备份),然后删除模块目录下的 Intermediate,但是要保留 Binaries 目录。
最后,打开模块 A 的 A.build.cs,将 bPrecompile=true; 删掉,然后再添加:

 
public class A : ModuleRules
{
public A(ReadOnlyTargetRules Target) : base(Target)
{
// ...
bUsePrecompiled=true;
// ...
}
}
 

此时我们想要实现的目标都已经完成了:不发布实现代码 (Private),发布预先编译好的二进制,但是这样无法进行静态链接,如果只是暴露给蓝图使用可以,在其他的 Module 中使用它的符号会有符号未定义错误。

OptimizeCode(CodeOptimization)

这个属性是用来控制当前模块是否要开启优化代码,在我们用 VS 调试时,有时候会看到 “变量已被优化,因而不可用”,这就是因为被优化了。

可以使用它来关闭优化:

 
// build.cs
OptimizeCode = CodeOptimization.Never;
 

CodeOptimization 支持几种值,默认是 Default,开启优化:

  • Never
  • Default
  • InNonDebugBuilds
  • InShippingBuildsOnly

相关的代码:

 
// UnrealBuildTool/Configutation/UEBuildModuleCPP.cs
public static bool ShouldEnableOptimization(ModuleRules.CodeOptimization Setting, UnrealTargetConfiguration Configuration, bool bIsEngineModule)
{
switch(Setting)
{
case ModuleRules.CodeOptimization.Never:
return false;
case ModuleRules.CodeOptimization.Default:
case ModuleRules.CodeOptimization.InNonDebugBuilds:
return (Configuration == UnrealTargetConfiguration.Debug)? false : (Configuration != UnrealTargetConfiguration.DebugGame || bIsEngineModule);
case ModuleRules.CodeOptimization.InShippingBuildsOnly:
return (Configuration == UnrealTargetConfiguration.Shipping);
default:
return true;
}
}
 

这个函数在 UEBuildModuleCPP.csCreateModuleCompileEnvironment 中调用,将结果赋值给了 CppCompileEnvironment.bOptimizeCode,进而又在 VCToolChain.cs 中被使用:

 
UnrealBuildTool\Platform\Windows\VCToolChain.cs

void AppendCLArguments_Global(CppCompileEnvironment CompileEnvironment, List<string> Arguments)
{
// other code ...

//
// Debug
//
if (CompileEnvironment.Configuration == CppConfiguration.Debug)
{
// Disable compiler optimization.
Arguments.Add("/Od");

// Favor code size (especially useful for embedded platforms).
Arguments.Add("/Os");

// Allow inline method expansion unless E&C support is requested
if (!CompileEnvironment.bSupportEditAndContinue && CompileEnvironment.bUseInlining)
{
Arguments.Add("/Ob2");
}

if ((CompileEnvironment.Platform == CppPlatform.Win32) ||
(CompileEnvironment.Platform == CppPlatform.Win64))
{
Arguments.Add("/RTCs");
}
}
//
// Development and LTCG
//
else
{
if(!CompileEnvironment.bOptimizeCode)
{
// Disable compiler optimization.
Arguments.Add("/Od");
}
else
{
// Maximum optimizations.
Arguments.Add("/Ox");

// Favor code speed.
Arguments.Add("/Ot");

// Coalesce duplicate strings
Arguments.Add("/GF");

// Only omit frame pointers on the PC (which is implied by /Ox) if wanted.
if (CompileEnvironment.bOmitFramePointers == false
&& ((CompileEnvironment.Platform == CppPlatform.Win32) ||
(CompileEnvironment.Platform == CppPlatform.Win64)))
{
Arguments.Add("/Oy-");
}
}

// Allow inline method expansion
Arguments.Add("/Ob2");

//
// LTCG
//
if (CompileEnvironment.bAllowLTCG)
{
// Enable link-time code generation.
Arguments.Add("/GL");
}
}

// other code...
}
  展开代码

可以看到,在 Debug 的环境下,是默认关闭优化的。在非 Debug 时根据 CompileEnvironment.bOptimizeCode 的值来决定是否开启优化。
调试效果:
当使用默认时(OptimizeCode = CodeOptimization.Default;):

当关闭代码优化时 (OptimizeCode = CodeOptimization.Never;):

建议使用 OptimizeCode = CodeOptimization.InShippingBuildsOnly;

注意:这个选项和普通的 C++ 项目在 VS 中的 Properties-Configuration-C/C++-Optimization-Optimization 的设置时一样的。

bEnableUndefinedIdentifierWarnings (bool)

是否启用在预处理代码#if 中使用未定义标识符的警告。

 



 
 

如果这个宏未定义,在启用 bEnableUndefinedIdentifierWarnings 的情况下会产生 C4688 错误。

相关的代码时定义在 UBT 的代码中的:

 
// Source\Programs\UnrealBuildTool\Platform\Windows\VCToolChain.cs
if(WindowsPlatform.bUseVCCompilerArgs && CompileEnvironment.bEnableUndefinedIdentifierWarnings)
{
if (CompileEnvironment.bUndefinedIdentifierWarningsAsErrors)
{
Arguments.Add("/we4668");
}
else
{
Arguments.Add("/w44668");
}
}
 

bUseRTTI (bool)

UE4 默认关闭了 RTTI,所以在工程的代码中写了类似 typeid 的代码,会产生下列错误:

 
In file included from C:\UnrealProject_\Source\GWorld\Private\Modules\Flibs\FLibIniConfigHelper.cpp:10:
C:/UnrealProject_/Source/GWorld/Public/Modules/Flibs\FlibMateReflectionHelper.h(29,41): error: cannot use typeid with -fno-rtti
FString EnumTypeName = ANSI_TO_TCHAR(typeid(ENUM_TYPE).name());
 

解决办法只有两个:去掉 rtti 相关的代码,或者在当前 Modulebuild.cs 中把 bUseRTTI 设置为 true

标签:Target,GWorld,Module,cs,Add,Build,模块,UE
From: https://www.cnblogs.com/tomato-haha/p/17426145.html

相关文章

  • flask_SQLAlchemy 出现了 Lost connection to MySQL server during query Mysql主机连
    使用pythonflask框架 flask_sqlalchemy时出现了LostconnectiontoMySQLserverduringqueryMysql主机连接超时的问题由于Mysql会定时处理长时间未连接使用的连接池具体时长可通过查看showvariableslike'%timeout%' wait_timeout为超时时长,这里的时间时120秒......
  • request
    请求行请求方式请求的资源协议/版本获取请求的方式StringgetMethod():获取项目动态路径StringgetContextPath():StringgetRemoteAddr()StringgetQueryString():获取的get请求参数username=tom&password=123StringgetProtocol():获取协议和版本请求头key/value(value可......
  • 我的第一个项目(十三) :组件间传值的一些方案(vuex,eventbus,localStorage)
    好家伙, 先说一下我的需求,我要组件间传值 1.eventBus前端兄弟组件传值eventbus无法使用不报错也不触发,就很奇怪//eventBus.jsimportVuefrom"vue";exportdefaultnewVue();//Mylogin.vue<buttontype="button"class="btnbtn-primary"@click="login&quo......
  • 注解中动态获取nacos值【attribute value must be constant】
    nacos中配置环境参数env:es:dev注解中添加参数信息@Data@IndexName(value="#{@envEs}")publicclassEsInfo{privateLongid;}添加配置文件获取配置数据@ComponentpublicclassEnvEsConfig{@Value("${env.es}")privateStringenvEs;@Be......
  • vue3.0组件封装
    组件全局祖册1.建立公共文件夹my-ui2.index.js文件导出全局祖册组件的install方法3.main.js中impotindex.js导入install方法使用并useimportmyUifrom'./components/my-ui'createApp(App).use(myUi).mount('#app')......
  • Maven报错 解决方案。ERROR: No goals have been specified for this build. You must
    转:https://www.codeleading.com/article/61821466327/报错:[ERROR]Nogoalshavebeenspecifiedforthisbuild.Youmustspecifyavalidlifecyclephaseoragoalintheformat<plugin-prefix>:<goal>or<plugin-group-id>:<plugin-artifact-......
  • Query execution was interrupted, maximum statement execution time exceeded
    数据库版本:MySQL5.7.16报错信息:ERROR3024(HY000):Queryexecutionwasinterrupted,maximumstatementexecutiontimeexceeded检查bug库,发现同样问题:https://bugs.mysql.com/bug.php?id=83339原因是max_execution_time设置过小导致。复现:将max_execution_time设置成......
  • 修改querydict几种方法
    修改querydict几种方法简介在正常的请求/响应周期中访问时,request.POST和request.GET上的QueryDict将是不可变的.要获得可变版本,您需要使用QueryDict.copy()或者._mutable=True第一种方式 用request.POST调用_mutable并修改为Truereuqets.POST._mutable=Tr......
  • ele+vue简单操作
    绑定值和字符串拼接:1、:title="`字符串${xx}`"2、:title="'字符串'+xx"3.异步回调setTimeReflush:function(){var_this=this;returnnewPromise(function(resolve,reject){$.post(BaseUrl+'&#......
  • vue中使用scss公共变量的方法 :export
    vue中使用scss公共变量的方法:export:https://blog.csdn.net/weixin_44698285/article/details/124051066?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-124051066-blog-125074100.235%5Ev36%5Epc_relevant_def......