UHT和UBT分析
前言
本文大致梳理一下UBT和UHT的作用以及涉及到的其他几个概念的使用
UHT
UHT是C++代码解析生成工具,我们在C++里面写的UClass
和#include
和.generate.h
都是为UHT提供了对应的信息来生成对应的C++反射代码的
简单说, 由于UE4代码不是标准的C++代码, 而UHT主要作用就是把UE4的C++代码翻译成标准C++代码
UBT
主要责任是UE4的各个模块的编译并处理各模块之间的依赖关系的。
build.cs和Target.cs都是为这个工具来服务的。
UBT调用标准C++代码的编译器(VS)来将UHT转化后的标准C++代码完全编译成二进制文件
Target.cs
UBT编译的目标类型,
每个项目都包含一个名称为项目名.target.cs
的文件, 这个C#
文件主要描述描述生成的目标类型, 默认为Game
, 还可以为其他类型
- Game - 默认模式, 一般的打包形式
- Client - 类似Game, 但是不包含服务端代码
- Server - 类似Game, 但是不包含客户端代码, 一般作为独立服务器打包
- Editor - 包含编辑器模块
- Program -独立程序
Build.cs
每个模块都包含的一个文件, 每个.build.cs
文件都声明一个从ModuleRules
基类派生的类,并设置控制应如何从其构造函数构造它的属性。这些.build.cs
文件由UBT
编译,并构造为确定总体编译环境。
类似的如下
1 | using UnrealBuildTool; |
Public和Private
常见的如PublicDependencyModuleNames
和PrivateDependencyModuleNames
, 或者PublicIncludePaths
和PrivateIncludePaths
有些文章有误读, 解释与目录有关系, 其实两者主要描述的是三个模块及以上的关系, 如A包含B,B包含C这种情况, 对于两个模块的情况可以随意使用
对于三个模块的情况,无法获得Private中模块的任何信息,可以获得Public中模块的头文件信息.
下图来自知乎大佬, 另外还有国外大佬的PPT
结论: 建议只使用PrivateDependencyModuleNames.
Include和Dependency
二者都能使本模块可以包含其它模块Public目录下的头文件,从而调用函数.
但IncludePathModuleNames
只能调用定义全部在头文件里的函数.
结论: 使用Dependency
常见属性
详情见官方文档
PublicIncludePaths
常用于包含第三方库的头文件如
1 | private string ThirdPartyPath |
PublicLibraryPaths/PublicAdditionalLibaries
系统/库路径列表(.lib文件的目录) - 通常用于外部(第三方)模块
如
1 | PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "Lib", "ThirdPartyDLL.lib")); |
RuntimeDependencies
该模块在运行时依赖的文件列表。这些文件将与目标一同暂存.
如果使用动态链接库可以用此添加, 不然打包以后无法正确获取
1 | RuntimeDependencies.Add(new RuntimeDependency(Path.Combine(ThirdPartyPath, "DLL", "ThirdPartyDLL.dll"))); |
BuildConfiguration
UBT将从以下位置中的XML配置文件读取设置:
- Engine/Saved/UnrealBuildTool/BuildConfiguration.xml
- User Folder/AppData/Roaming/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml
- My Documents/Unreal Engine/UnrealBuildTool/BuildConfiguration.xml
具体可以看官方文档
IWYU
全称nclude what you use
,即只包含你需要的头文件。
在旧版本的虚幻引擎4(UE4)中,引擎的大部分功能通过大型、以模块为中心的头文件(如 Engine.h
和 UnrealEd.h
)进行包含。通过预编译头文件(PCH)快速编译这些文件即可达成较快的编译时间。但随着引擎的更新,这成为了一个瓶颈。
通过IWYU,每个文件只包括其需要的内容。我们选择使用的所有PCH文件纯粹只是作为基础源文件之上的优化层。可对它们进行修改,将编译时间缩至最短。其独立于源文件本身的修改,不会影响代码是否成功编译。
编写IWYU代码时,我们需要遵循4个特定规则:
- 所有头文件包含其所需的依赖性。
- .cpp文件首先包含其匹配的*.h文件。
- PCH文件已不再是显式包含。
- 不再包含单块头文件。
验证已启用
在build,cs
文件中的PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
其他描述符
MinimalAPI
暴露该类的类型信息让其它模块可以
- Cast到该类型
- 继承该类型 (该类型的所有定义在.cpp文件的虚函数都需要导出)
- 使用内联函数
[YourModuleName]_API宏
- 放函数声明前用于暴露该函数
- 放类声明前用于暴露该类的所有内容
结论: 不加这个宏的话其他模块就无法使用该函数/类