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
2
3
4
5
6
7
8
9
using UnrealBuildTool;
using System.Collections.Generic;
public class MyModule : ModuleRules
{
public MyModule(ReadOnlyTargetRules Target) : base(Target)
{
// Settings go here
}
}

Public和Private

常见的如PublicDependencyModuleNamesPrivateDependencyModuleNames, 或者PublicIncludePathsPrivateIncludePaths

有些文章有误读, 解释与目录有关系, 其实两者主要描述的是三个模块及以上的关系, 如A包含B,B包含C这种情况, 对于两个模块的情况可以随意使用

对于三个模块的情况,无法获得Private中模块的任何信息,可以获得Public中模块的头文件信息.

下图来自知乎大佬, 另外还有国外大佬的PPT

chrome_u7pBEW5ju4

结论: 建议只使用PrivateDependencyModuleNames.

Include和Dependency

二者都能使本模块可以包含其它模块Public目录下的头文件,从而调用函数.

IncludePathModuleNames只能调用定义全部在头文件里的函数.

结论: 使用Dependency

常见属性

详情见官方文档

PublicIncludePaths

常用于包含第三方库的头文件如

1
2
3
4
5
6
private string ThirdPartyPath
{
get { return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/")); }
}

PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "Include"));

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.hUnrealEd.h)进行包含。通过预编译头文件(PCH)快速编译这些文件即可达成较快的编译时间。但随着引擎的更新,这成为了一个瓶颈。

通过IWYU,每个文件只包括其需要的内容。我们选择使用的所有PCH文件纯粹只是作为基础源文件之上的优化层。可对它们进行修改,将编译时间缩至最短。其独立于源文件本身的修改,不会影响代码是否成功编译。

编写IWYU代码时,我们需要遵循4个特定规则:

  1. 所有头文件包含其所需的依赖性。
  2. .cpp文件首先包含其匹配的*.h文件。
  3. PCH文件已不再是显式包含。
  4. 不再包含单块头文件。

官方文档

验证已启用

build,cs文件中的PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;

其他描述符

MinimalAPI

暴露该类的类型信息让其它模块可以

  • Cast到该类型
  • 继承该类型 (该类型的所有定义在.cpp文件的虚函数都需要导出)
  • 使用内联函数

[YourModuleName]_API宏

  • 放函数声明前用于暴露该函数
  • 放类声明前用于暴露该类的所有内容

结论: 不加这个宏的话其他模块就无法使用该函数/类