构建支持多平台打包的二进制引擎

通常,UE4 开发者获取 UE4 引擎的方式有两种:

  1. 从 Epic Games Launcher 安装
  2. 从 Github 上 Clone 代码本地编译

从 EpicGamesLauncher 安装的是公版引擎,不能修改代码重新编译,可以在根据选择安装支持的平台、调试符号等。
自己从 Github 上 Clone 代码进行编译的则是源码版引擎,有些功能只能在源码版中使用(比如 Standalone Application),但是如果在项目中修改了引擎的代码,导致每个人都需要 Clone 一遍源码编译一遍引擎,这个过程十分耗时,而且源码版引擎的占用的磁盘空间十分巨大,达到上百 G。在当需要把引擎部署到多台构建机时,编译引擎的时间和空间是冗余的,所以需要通过一台机器编译引擎,然后其他的机器只需要拉取编译后的引擎即可,实现与安装版引擎一样的行为。

本篇文章主要介绍 BuildGraph 的工作流程,以及对引擎默认构建脚本 InstalledEngineBuild.xml 的分析;如何使用 BuildGraph 从源码配置、编译并导出支持 Android/IOS 打包的二进制引擎、以及如何裁剪和加速整个的构建流程。

UE 提供了 BuildGraph 功能使我们可以从源码中构建出和 Epic Game Launcher 相同的安装版引擎。

使用参数如下:

1
RunUAT.bat BuildGraph -target="Make Installed Build Win64" -script=Engine/Build/InstalledEngineBuild.xml -set:WithMac=false -set:WithAndroid=false -set:WithIOS=false -set:WithTVOS=false -set:WithLinux=false -set:WithHTML5=false -set:WithSwitch=false -WithDDC=false -set:WithWin32=false -set:WithLumin=false -set:WithPS4=false -set:WithXboxOne=false -set:WithHoloLens=false -set:GameConfigurations=Development

通过 BuildGraph 来执行 InstalledEngineBuild.xml 脚本中的 Make Installed Build Win64 实现,引擎编译、导出二进制的一条龙流程。

但是这样有几个问题:默认这样构建出来的只能够打包 Windows,不能打包 Android/IOS。

如何构建 Android/IOS 的二进制引擎,暂时先按下不表,首先先来分析一下 UE 默认的 InstalledEngineBuild.xml 脚本。

优化 BuildGraph 的构建流程

InstalledEngineBuild.xml 是位于引擎的 Engine/Build/ 目录下的 BuildGraph 的构建脚本,主要实现了通过代码导出二进制引擎的功能,支持了很多的平台。BuildGraph 的官方介绍和语法:BuildGraph

我们主要使用Make Installed Build Win64,是它的一个 Node 实现。

主要流程如下:

  1. 编译 UBT 等构建工具
  2. 编译 NotForLicence 工具
  3. 编译 Editor
  4. 编译支持的平台(默认 Win64)
  5. Make Feature Packs
  6. 拷贝构建结果

而且,经过分析Make Installed Build Win64,发现它其中有很多重复执行的编译流程。

以编译 UE4Game Win64 为例:

1
2
3
4
5
6
7
8
<Node Name="Compile UE4Game Win64" Requires="Compile UnrealHeaderTool Win64" Produces="#UE4Game Win64;#UE4Game Win64 Unstripped;#UE4Game Win64 Stripped;#UE4Game Win64 Unsigned;#UE4Game Win64 Signed">
<ForEach Name="Target" Values="UE4Game;$(OptionalClientTarget);$(OptionalServerTarget)">
<ForEach Name="Configuration" Values="$(GameConfigurations)">
<Compile Target="$(Target)" Platform="Win64" Configuration="$(Configuration)" Tag="#UE4Game Win64" Arguments="-precompile -allmodules -nolink $(VSCompilerArg) $(TargetDebugInfoArg)"/>
<Compile Target="$(Target)" Platform="Win64" Configuration="$(Configuration)" Tag="#UE4Game Win64" Arguments="-precompile $(VSCompilerArg) $(TargetDebugInfoArg)" Clean="false"/>
</ForEach>
</ForEach>
</Node>

可以看到,针对同一个编译的 Target,使用不同的编译参数执行了两次:

  1. 具有 -allmodules-nolink 等参数
  2. 不具有上述参数

而开启了 -allmodules,则意味着引擎中所有的模块都要重新编译,是完整地编译引擎。当下次执行,还是要完整地编译整个引擎,UBT 中具有对-allmodules 的介绍:

-allmodules参数是定义在 UBT 的 TargetRules.cs 中的:

UnrealBuildTool/Configuration/TargetRules.cs
1
2
3
4
5
/// <summary>
/// Build all the modules that are valid for this target type. Used for CIS and making installed engine builds.
/// </summary>
[CommandLine("-AllModules")]
public bool bBuildAllModules = false;

专门用来构建安装版引擎的,我觉得可以关掉,使用增量编译的方式执行,不然在 ci 上每次执行都太慢了。

而且这个脚本中对每个平台都执行了两遍,一遍是不含 -nolink 参数的,一遍是包含 -nolink 参数的,可以根据自己的需求决定是否关闭。
UE 的文档中是这么介绍的(Unreal Engine 4.14 Released!):

New: Added a -nolink command line option for Unreal Build Tool, which enables compiling a target without linking it. This is useful for automated build tests, such as non-unity compiles.

当使用 BuildGraph 来构建引擎的时候,默认情况下对引擎的编译次数计算公式:

1
2
UE4Editor DebugGame/Development 2 次
UE4Game Win64/Android/IOS/... 每个平台 2*Configuration 次(-allmodules -nolink)

如果我们使用 BuildGraph 通过 Make Installed Build Win64 来构建出安装版引擎(支持 Win/Android/IOS 打包),至少需要编译2+3*2=8,对引擎的代码要编译八次!而且每一次执行都要完整的编译所有的模块,耗时非常之长。

所以裁剪是非常有必要的,上面也已经提到了:

  1. 去掉 -allmodules 参数,增量编译
  2. 去掉 -nolink 的构建(根据需求决定)
  3. 减少需要构建的平台

裁剪之后需要需要编译的次数:

  1. UE4Editor Development 1 次
  2. UE4Game Win64/Android/IOS/… 每个平台 1*Configuration

则需要编译 4 次,与默认减少一倍,并且可以增量编译,时间会快很多了。

构建引擎支持 Android 打包

想要使用 Make Installed Build Win64 构建出支持 Android 打包的引擎,需要在执行 BuildGraph 脚本时添加 -set:WithAndroid=true,因为WithAndroidInstalledEngineBuild.xml中定义的参数,可以通过命令行传递。

不过,仅仅是命令行指定开启是不够的,在执行 BuildGraph 之前,需要安装好 Android 的开发环境,在 BuildGraph 在执行编译时会去系统 PATH 里查找 JDK/Android NDK/Android SDK 以及 gradle 的路径,需要自己进行预先配置:

注意:虽然在执行编译时会检测环境自动下载 gradle,但是由于在墙内的原因,不一定会下载成功。

需要把 Android 的环境按照添加至系统的环境变量中:

注意:当使用一些 ci 工具的时候,ci 只能查到服务启动的用户的环境变量,为避免额外的问题,最好添加至系统的环境变量。

默认情况下会编译出支持打包 Android 架构为 armv7arm64的引擎,如果不需要其中的某个架构,可以在 InstalledEngineBuild.xml 里选择注释掉指定的平台。

Android 比较简单,配置完 Android 的开发环境就可以直接导出可以打包 Android 的引擎:

构建引擎支持 IOS 打包

前面已经提到了,构建支持 Android 打包的二进制引擎,支持 IOS 平台要更复杂一些。

首先构建支持 IOS 打包的引擎必须要有一台 Mac 执行远程构建,因为 BuildGraph 需要编译 IOS 平台的 UE4Game,在 Win 上无法编译 IOS 的库,所以必须要一台 Mac。

  1. 局域网中有一台可访问的 Mac
  2. 具有 mobileprovision
  3. 生成 SSH Key

至于“为什么有了 Mac 还需要在 Win 上支持 IOS?”这个问题主要是因为:

  1. Mac 机能受限,直接在 Mac 上打包是编译 +Cook 的操作都在 Mac 上,如果进行远程构建,则 Mac 只需要处理代码的编译,而不需要执行 Cook 的流程,降低对 Mac 机能的依赖,可以提高构建的效率。
  2. 统一地使用 Win 来构建出全平台包(Win/Android/IOS)

首先需要在 Win 上生成 Mac 的 SSH Keys,BuildGraph 编译引擎支持 IOS 也需要设置 ssh key,生成的方法在我之前的博客中有记录,不再赘述:UE4 开发笔记:Mac/iOS 篇 #配置远程构建

当生成了 SSHkey 之后,需要在引擎的 Engine/Config/BaseEngine.ini 中修改以下配置:

1
2
3
4
5
6
[/Script/IOSRuntimeSettings.IOSRuntimeSettings]
RemoteServerName=
RSyncUsername=
SSHPrivateKeyOverridePath=
mobileprovision=
SigningCertificate=

注意:这里的 mobileprovision 和 SigningCertificate 指定名字就可以,如:

1
2
MobileProvision=com.XXXX.XXXX.fmgame_Development_SignProvision.mobileprovision
SigningCertificate=iPhone Developer: Created via API (JDPXHYVWYZ)

并且,可以不指定 SSHPrivateKeyOverridePath 的路径,在 RemoteMac.cs 中,针对引擎目录有三个自动查找的路径:

1
2
3
4
5
6
if (ProjectFile != null)
{
Locations.Add(DirectoryReference.Combine(ProjectFile.Directory, "Build", "NotForLicensees"));
Locations.Add(DirectoryReference.Combine(ProjectFile.Directory, "Build", "NoRedist"));
Locations.Add(DirectoryReference.Combine(ProjectFile.Directory, "Build"));
}

把 SSH Key 按照这样的命名格式放到这三个目录下即可:

1
SSHKeys/IP_ADDR/USER_NAME/RemoteToolChainPrivate.key

如:

1
SSHKeys/192.168.1.123/buildmachine/RemoteToolChainPrivate.key

远程构建的 UBT 路径异常

在执行时 RemoteMac.cs 中报错的问题:

// RemoteMac.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
/// <summary>
/// Attempts to get the SSH private key from the standard locations
/// </summary>
/// <param name="OutPrivateKey">If successful, receives the location of the private key that was found</param>
/// <returns>True if a private key was found, false otherwise</returns>
private bool TryGetSshPrivateKey(out FileReference OutPrivateKey)
{
// Build a list of all the places to look for a private key
List<DirectoryReference> Locations = new List<DirectoryReference>();
Locations.Add(DirectoryReference.Combine(DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.ApplicationData), "Unreal Engine", "UnrealBuildTool"));
Locations.Add(DirectoryReference.Combine(DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.Personal), "Unreal Engine", "UnrealBuildTool"));
// ...
}

上面的代码在 Combine 时没有做检测,Environment.SpecialFolder.ApplicationData这个路径是不一定能得到的,所以要修改 UBT 的 RemoteMac.cs 中的这部分代码,进行检测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private bool TryGetSshPrivateKey(out FileReference OutPrivateKey)
{
// Build a list of all the places to look for a private key
List<DirectoryReference> Locations = new List<DirectoryReference>();
DirectoryReference ApplicationData = DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.ApplicationData);

DirectoryReference Personal = DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.Personal);
if(ApplicationData != null)
Locations.Add(DirectoryReference.Combine(ApplicationData, "Unreal Engine", "UnrealBuildTool"));
if(Personal != null)
Locations.Add(DirectoryReference.Combine(Personal, "Unreal Engine", "UnrealBuildTool"));

// ...
}

上一步的操作,只能解决找 ssh key 时的路径报错问题。

远程构建时 SSH 连接错误

解决了这个问题还有另外一个问题:

1
2
3
4
5
6
7
8
9
10
11
****** [4/11] Compile UnrealHeaderTool Mac

Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Update Version Files\Tag-Update Version Files.xml
Running: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\UnrealBuildTool.exe UnrealHeaderTool Mac Development -NoUBTMakefiles -nobuilduht -precompile -allmodules -Manifest=C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Build\Manifest.xml -NoHotReload -log="C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Programs\AutomationTool\Saved\Logs\UBT-UnrealHeaderTool-Mac-Development.txt"
[Remote] Using remote server 'xx.xx.xx.xx' on port 2222 (user 'buildmachine')
[Remote] Using private key at C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Build\NotForLicensees\SSHKeys\xx.xx.xx.xx\buildmachine\RemoteToolChainPrivate.key
ERROR: Unable to determine home directory for remote user. SSH output:
Host key verification failed.
Took 0.6103776s to run UnrealBuildTool.exe, ExitCode=6
UnrealBuildTool failed. See log for more details. (C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Programs\AutomationTool\Saved\Logs\UBT-UnrealHeaderTool-Mac-Development.txt)

看到是 ssh key 的验证失败了,其实这个错误并不是 Key 的问题(这个问题非常坑)。
而是因为 RemoteMac.cs 中对 ssh 连接命令的代码里是这么写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class RemoteMac
{
// ...
/// <summary>
/// The authentication used for SSH (probably similar to RsyncAuthentication).
/// </summary>
[XmlConfigFile]
private string SshAuthentication = "-i '${CYGWIN_SSH_PRIVATE_KEY}'";
// ...
};

public RemoteMac(FileReference ProjectFile)
{
// ...
SshAuthentication = ExpandVariables(SshAuthentication);

// Build a list of arguments for SSH
CommonSshArguments = new List<string>();
CommonSshArguments.Add("-o BatchMode=yes");
CommonSshArguments.Add(SshAuthentication);
CommonSshArguments.Add(String.Format("-p {0}", ServerPort));
CommonSshArguments.Add(String.Format("\"{0}@{1}\"", UserName, ServerName));
// ...
}

这个代码生成的拼接的 ssh 命令:

1
\Engine\Extras\ThirdPartyNotUE\DeltaCopy\Binaries\ssh.exe -o BatchMode=yes -i \Engine\Build\NotForLicensees\SSHKeys\xx.xx.xx.xx\buildmachine\RemoteToolChainPrivate.key -p 22 [email protected]

问题的关键就是处在 BatchMode=yes 上,当我们第一次通过 ssh 连接一台主机,会下列提示:

1
2
3
4
5
6
$ Engine\Extras\ThirdPartyNotUE\DeltaCopy\Binaries\ssh.exe -p 22 buildmachine@ 192.168.1.123 
The authenticity of host '[192.168.1.123]:22 ([192.168.1.123]:22)' can't be established.
RSA key fingerprint is e0:8d:b9:7c:65:c7:9e:18:94:12:ed:ef:40:1a:15:47.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.1.123]:22' (RSA) to the list of known hosts.
Password:

会弹出提示让你验证这台主机(需要手动输入 yes),但是开启了 BatchMode=yes 之后,会禁止所有的交互式提示,出现交互直接连接失败!这就是我们使用正确的 Key,但是会提示 Host key verification failed. 的原因。
那么,知道了原因,解决这个问题的办法有两种:

  1. 关闭 ssh 的连接时验证
  2. 在进行构建之前,手动使用 ssh 命令连接主机,通过交互式的验证(只需要初始化验证一次)

未导入 provision 的错误

编译时的其他错误:

1
2
3
4
5
6
7
8
9
10
11
****** [6/11] Compile UE4Game IOS

Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Compile UnrealHeaderTool Mac\Tag-Compile UnrealHeaderTool Mac.xml
Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Update Version Files\Tag-Update Version Files.xml
Reading shared manifest from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Compile UnrealHeaderTool Mac\Manifest.xml
Running: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\UnrealBuildTool.exe UE4Game IOS Development -NoUBTMakefiles -nobuilduht -precompile -allmodules -nolink -nodebuginfo -Manifest=C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Build\Manifest.xml -NoHotReload -log="C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Programs\AutomationTool\Saved\Logs\UBT-UE4Game-IOS-Development.txt"
[Remote] Using remote server 'xx.xx.xx.xxx' on port 22 (user 'buildmachine')
[Remote] Using private key at C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Build\NotForLicensees\SSHKeys\10.75.27.129\buildmachine\RemoteToolChainPrivate.key
[Remote] Using base directory '/Users/buildmachine/UE4/Builds/lipengzha-PC2'
ERROR: Unable to find mobile provision for UE4Game. See log for more information.
Took 4.0300959s to run UnrealBuildTool.exe, ExitCode=6

这是因为编译引擎时没有配置 provision,需要在Engine/Config/BaseEngine.ini 中设置。

在这里可以只指定 provision 文件的名字,在 UEBuildIOS.cs 中的代码会从 C:\Users\lipengzha\AppData\Local\Apple Computer\MobileDevice\Provisioning Profiles 路径下去查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected IOSProvisioningData(IOSProjectSettings ProjectSettings, bool bIsTVOS, bool bForDistribtion)
{
// ...
if(!string.IsNullOrEmpty(MobileProvision))
{
DirectoryReference MobileProvisionDir;
if(BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac)
{
MobileProvisionDir = DirectoryReference.Combine(new DirectoryReference(Environment.GetEnvironmentVariable("HOME")), "Library", "MobileDevice", "Provisioning Profiles");
}
else
{
MobileProvisionDir = DirectoryReference.Combine(DirectoryReference.GetSpecialFolder(Environment.SpecialFolder.LocalApplicationData), "Apple Computer", "MobileDevice", "Provisioning Profiles");
}

FileReference PossibleMobileProvisionFile = FileReference.Combine(MobileProvisionDir, MobileProvision);
if(FileReference.Exists(PossibleMobileProvisionFile))
{
MobileProvisionFile = PossibleMobileProvisionFile;
}
}
// ...
}

这部分操作也可以通过引擎来导入,不仅仅只需要导入 provision 还需要导入证书(也可以通过 iPhonePackager 来导入):

如果不导入会有下列错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
****** [7/12] Compile UE4Game IOS

Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Compile UnrealHeaderTool Mac\Tag-Compile UnrealHeaderTool Mac.xml
Reading local file list from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Update Version Files\Tag-Update Version Files.xml
Reading shared manifest from C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Saved\BuildGraph\Compile UnrealHeaderTool Mac\Manifest.xml
Running: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\UnrealBuildTool.exe UE4Game IOS Development -NoUBTMakefiles -nobuilduht -precompile -allmodules -nolink -nodebuginfo -Manifest=C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Build\Manifest.xml -NoHotReload -log="C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Programs\AutomationTool\Saved\Logs\UBT-UE4Game-IOS-Development.txt"
[Remote] Using remote server '192.168.1.123' on port 22 (user 'buildmachine')
[Remote] Using private key at C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Build\NotForLicensees\SSHKeys\192.168.1.123\buildmachine\RemoteToolChainPrivate.key
[Remote] Using base directory '/Users/buildmachine/UE4/Builds/lipengzha-PC3'
[Remote] Uploading C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Remote\UE4Game\IOS\Development\com.xxxxx.xxxx.xx_Development_SignProvision.mobileprovision
[Remote] Exporting certificate for C:\Users\lipengzha\AppData\Local\Apple Computer\MobileDevice\Provisioning Profiles\com.xxxxx.xxxx.xx_Development_SignProvision.mobileprovision...
Executing iPhonePackager ExportCertificate C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Source -provisionfile C:\Users\lipengzha\AppData\Local\Apple Computer\MobileDevice\Provisioning Profiles\com.xxxxx.xxxx.xx_Development_SignProvision.mobileprovision -outputcertificate C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Remote\UE4Game\IOS\Development\Certificate.p12
CWD: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\IOS
Initial Dir: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Source
Env CWD: C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Binaries\DotNET\IOS
BranchPath = lipengzha-PC3/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Binaries --- GameBranchPath = lipengzha-PC3/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Binaries

----------
Executing command 'ExportCertificate' '' ...
Looking for a certificate that matches the application identifier '9TV4ZYSS4J.com.xxxxx.xxxx.xx'
.. Provision entry SN '61B440405D86B84D' matched 0 installed certificate(s)
.. Failed to find a valid certificate that was in date
IPP ERROR: Failed to find a valid certificate

ERROR: IphonePackager failed.
Took 2.9281688s to run UnrealBuildTool.exe, ExitCode=6

上述的问题解决完毕之后就能够正常地把 BuildGraph 的流程执行完毕,导出了根据引擎代码编译出的二进制引擎,启动之后就可以看到能够打包 Windows/Android/IOS 了。

错误排查

No certificate for team xxxx matching

如果有以下错误提示:

1
Code Signing Error: No certificate for team '9TV4ZYSS4J' matching 'iPhone Developer: Created via API (JDPXHYVWYZ)' found: Select a different signing certificate for CODE_SIGN_IDENTITY, a team that matches your selected certificate, or switch to autom atic provisioning.

解决办法:

  1. 在 Mac 上的 ~/Library/MobileDevice/Provisioning\ Profiles 清理掉多余的 mobileprovision 文件。
  2. 在 Mac 钥匙串中清理掉过期的开发者证书
  3. 重新导入 mobileprovision 与证书

注意:导入的 mobileprovision 的文件命名要与在 BaseEngine.ini 中指定的 MobileProvision 相同。

errSecInternalComponent 错误

方法一

是因为通过 ssh 去调用 /usr/bin/codesign 访问钥匙串没有权限,可以使用以下命令在 ssh 中执行解锁:

1
security unlock-keychain -p password login.keychain

在 UE 远程构建时,可以先执行这条命令在当前的 ssh 环境下解锁 keychain,使后面的签名可以正常执行。
修改 UE 中的 Engine\Build\BatchFiles\Mac\Build.sh 文件,在调用 UBT 编译之前,写入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh

cd "`dirname "$0"`/../../../.."

# Setup Mono
source Engine/Build/BatchFiles/Mac/SetupMono.sh Engine/Build/BatchFiles/Mac

if ["$4" == "-buildscw" ] || ["$5" == "-buildscw" ]; then
echo Building ShaderCompileWorker...
mono Engine/Binaries/DotNET/UnrealBuildTool.exe ShaderCompileWorker Mac Development
fi
echo unlock mac keychain...
security unlock-keychain -p password login.keychain
echo Running command : Engine/Binaries/DotNET/UnrealBuildTool.exe "$@"
mono Engine/Binaries/DotNET/UnrealBuildTool.exe "$@"

ExitCode=$?
if [$ExitCode -eq 254 ] || [$ExitCode -eq 255 ] || [$ExitCode -eq 2 ]; then
exit 0
else
exit $ExitCode
fi

因为编译时会把 Build.sh 通过 RSync 传递到 Mac 上,所以可以看到以下 log:

1
2
3
4
5
6
7
8
9
10
11
[Remote] Executing build
Running bundled mono, ue_version: Mono JIT compiler version 5.16.0.220 (2018-06/bb3ae37d71a Fri Nov 16 17:12:11 EST 2018)
unlock mac keychain...
Running command : Engine/Binaries/DotNET/UnrealBuildTool.exe UnrealHeaderTool Mac Development -SkipRulesCompile -XmlConfigCache=/Users/buildmachine/UE4/Builds/lipengzha-PC2/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Intermediate/Build/XmlConfigCache.bin -precompile -allmodules -Log=/Users/buildmachine/UE4/Builds/lipengzha-PC2/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Programs/AutomationTool/Saved/Logs/UBT-UnrealHeaderTool-Mac-Development_Remote.txt -Manifest=/Users/buildmachine/UE4/Builds/lipengzha-PC2/C/BuildAgent/workspace/FGameEngine/Engine/Engine/Intermediate/Remote/UnrealHeaderTool/Mac/Development/Manifest.xml
Target is up to date
Deploying UnrealHeaderTool Mac Development...
Deploying now!
Total execution time: 1.01 seconds
[Remote] Downloading C:\BuildAgent\workspace\FGameEngine\Engine\Engine\Intermediate\Remote\UnrealHeaderTool\Mac\Development\Manifest.xml
[Remote] Downloading build products
receiving file list ... done

这样每次编译都会解锁 keychain,从而避免 ssh 连接时没有访问 codesign 导致的签名错误。

注意:也需要排查 BaseEngine.ini 中 SigningCertificate 的值是否被指定。

方法二

导入新的苹果开发者根证书:

钥匙串 - 系统 -Apple Worldwise Developer Relations Certification Authority 删除,然后从上面的连接中下载新的,重新导入即可。

Invalid trust settings

如果 Log 中出现以下错误:

1
2
Code Signing Error: Invalid trust settings. Restore system default trust settings for certificate "iPhone Developer: Created via API (JDPXHYVWYZ)" in order to sign code with it.
Code Signing Error: Code signing is required for product type 'Application' in SDK 'iOS 13.6'

这是因为在 Mac 上的钥匙串中对证书的设置被修改为了 始终信任 ,修改回 使用系统默认 即可。

结语

本篇文章主要介绍了 BuildGraph 的工作流程、优化构建速度,以及支持 Android/IOS 的打包。

需要注意的是,文章开头使用的命令:

1
RunUAT.bat BuildGraph -target="Make Installed Build Win64" -script=Engine/Build/InstalledEngineBuild.xml -set:WithMac=false -set:WithAndroid=false -set:WithIOS=false -set:WithTVOS=false -set:WithLinux=false -set:WithHTML5=false -set:WithSwitch=false -WithDDC=false -set:WithWin32=false -set:WithLumin=false -set:WithPS4=false -set:WithXboxOne=false -set:WithHoloLens=false -set:GameConfigurations=Development

它的 GameConfigurations 只设置了 Dvelopment,所以编译出来的引擎也只能打包Development,如果想要支持 Shipping 打包,就需要在BuildGraph 中指定:-set:GameConfigurations=Development;Shipping,但是这样会增加构建的时间,因为 Development 和 Shipping 都是需要分别编译的,对执行支持的所有平台都增加了一次引擎编译的时间开销,当然是必要的,不过在日常的开发中,可以有选择性地开启,节省构建的时间。