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

通常,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 -set: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. 拷贝构建结果

如果想要修改构建的二进制引擎的导出路径,可以修改InstalledEngineBuild.xml中的BuiltDirectory值。

1
2
3
4
5
6
7
<!-- Ouput directory for the build -->
<Option Name="BuiltDirectory" DefaultValue="$(RootDir)/LocalBuilds/Engine" Description="Directory for outputting the built engine"/>

<!-- The local output directory -->
<Property Name="LocalInstalledDir" Value="$(BuiltDirectory)/Windows"/>
<Property Name="LocalInstalledDirMac" Value="$(BuiltDirectory)/Mac"/>
<Property Name="LocalInstalledDirLinux" Value="$(BuiltDirectory)/Linux"/>

而且,经过分析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

在4.26及后续引擎中,SSH Key的查找路径有变化。详见:ToolChain/RemoteMac.cs

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

如:

1
[PROJECT_DIR]]\Restricted\NotForLicensees\Build\SSHKeys\192.168.31.55\lipengzha\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 buildmachine@xx.xx.xx.xx

问题的关键就是处在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命令连接主机,通过交互式的验证(只需要初始化验证一次)
  3. 也有可能是Client上的key文件权限过于开放导致的,需要修改权限。

关闭SSH的连接时验证,需要编辑Mac上的/etc/ssh/ssh_config文件,修改或添加以下项:

1
StrictHostKeyChecking no

然后重启sshd服务:

1
2
sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist

或者修改ssh连接的命令,添加以下参数:

1
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null user@192.168.1.123

具体位置为:

RemoteMac.cs
1
2
3
4
5
6
CommonSshArguments = new List<string>();
//++[lipengzha] skip ssh new host auth
CommonSshArguments.Add("-o StrictHostKeyChecking=no");
CommonSshArguments.Add("-o UserKnownHostsFile=/dev/null");
CommonSshArguments.Add("-q"); // dont print shh warnning log,for rsync GetRemotePath
//--[lipengzha]

因为远程构建时,rsync也是通过配置的ssh信息进行文件传输的,所以也要对rsync的命令进行修改:

RemoteMac.cs
1
2
3
//++[RSTDIO][lipengzha] skip ssh new host auth
BasicRsyncArguments.Add(String.Format("--rsh=\"{0} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -q -p {1}\"", RsyncAuthentication, ServerPort));
//--[RSTUDIO]

原始rsync的命令:

1
rsync --compress --verbose --rsh="./ssh -i '/cygdrive/D/Projects/Client/Build/NotForLicensees/SSHKeys/xx.xx.xx.xx/buildmachine/RemoteToolChainPrivate.key'-p 2222" --chmod=ugo=rwx --recursive --delete --delete-excluded --times --omit-dir-times --prune-empty-dirs --copy-links --rsync-path="mkdir -p /Users/buildmachine/UE4/Builds/lipengzha-PCb/D/Projects/Client && rsync" --filter="merge /cygdrive/D/Projects/Client/Build/Rsync/RsyncProject.txt" --filter="merge /cygdrive/D/Projects/Engine/Engine/Build/Rsync/RsyncProject.txt" --exclude='*' "/cygdrive/D/Projects/Client/" "buildmachine@xx.xx.xx.xx":'/Users/buildmachine/UE4/Builds/lipengzha-PCb/D/Projects/Client/'

修改后的命令:

1
rsync --compress --verbose --rsh="./ssh -i '/cygdrive/D/Projects/Client/Build/NotForLicensees/SSHKeys/xx.xx.xx.xx/buildmachine/RemoteToolChainPrivate.key' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -q -p 2222" --chmod=ugo=rwx --recursive --delete --delete-excluded --times --omit-dir-times --prune-empty-dirs --copy-links --rsync-path="mkdir -p /Users/buildmachine/UE4/Builds/lipengzha-PCb/D/Projects/Client && rsync" --filter="merge /cygdrive/D/Projects/Client/Build/Rsync/RsyncProject.txt" --filter="merge /cygdrive/D/Projects/Engine/Engine/Build/Rsync/RsyncProject.txt" --exclude='*' "/cygdrive/D/Projects/Client/" "buildmachine@xx.xx.xx.xx":'/Users/buildmachine/UE4/Builds/lipengzha-PCb/D/Projects/Client/'

未导入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

这个问题应该是项目里配置的证书和系统中的匹配不上导致的(或者同一个mobileProvision对应了多个证书),按照以下步骤操作后需重启机器生效。

如果有以下错误提示:

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, 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删除,然后从上面的连接中下载新的,重新导入即可。

方法三

在钥匙串中检查证书是否被导入了登录(Login)下,删除后在系统中导入。

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上的钥匙串中对证书的设置被修改为了始终信任,修改回使用系统默认即可。

unable to build chain to self-signed root for signer

检查证书在钥匙串中,是被导入到了登录还是系统分类下。
如果在登录中,则删除后重新导入到系统中。

拷贝自定义目录

在构建二进制引擎时,默认情况下就和从EpicLauncher中安装的一致,但如果修改了引擎,想要把一些新的目录打包进去。

修改InstalledEngineFilters.xml文件,在CopyEditorFilter下添加目录即可:

InstalledEngineFilters.xml
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<!-- Define Editor Filters -->
<Property Name="CopyEditorFilter">
<!-- This assembly is normally embedded into the UBT executable, but it can technically be rebuilt from an installed build -->
Engine/Binaries/ThirdParty/Newtonsoft/...
Engine/Binaries/ThirdParty/VisualStudio/...

<!-- In-editor documentation -->
Engine/Documentation/Source/Shared/...
Engine/Documentation/Extras/...

<!-- Content folders -->
Engine/Content/...
<!-- Shaders folders -->
Engine/Shaders/...
<!-- Source code -->
Engine/Source/UE4Game.Target.cs
Engine/Source/UE4Editor.Target.cs

<!-- Starter content -->
Samples/StarterContent/Content/...
Samples/MobileStarterContent/Content/...

<!-- Templates -->
Templates/TemplateCategories.ini
Templates/FP_FirstPerson/...
Templates/FP_FirstPersonBP/...
Templates/Media/...
Templates/TP_Blank/...
Templates/TP_BlankBP/...
Templates/TP_FirstPerson/...
Templates/TP_FirstPersonBP/...
Templates/TP_Flying/...
Templates/TP_FlyingBP/...
Templates/TP_HandheldARBP/...
Templates/TP_ProductConfigBP/...
Templates/TP_Rolling/...
Templates/TP_RollingBP/...
Templates/TP_SideScroller/...
Templates/TP_SideScrollerBP/...
Templates/TP_ThirdPerson/...
Templates/TP_ThirdPersonBP/...
Templates/TP_TopDown/...
Templates/TP_TopDownBP/...
Templates/TP_TwinStick/...
Templates/TP_TwinStickBP/...
Templates/TP_Vehicle/...
Templates/TP_VehicleBP/...
Templates/TP_Puzzle/...
Templates/TP_PuzzleBP/...
Templates/TP_2DSideScroller/...
Templates/TP_2DSideScrollerBP/...
Templates/TP_VehicleAdv/...
Templates/TP_VehicleAdvBP/...
Templates/TP_VirtualRealityBP/...

<!-- Enterprise Templates -->
Templates/TP_AEC_BlankBP/...
Templates/TP_AEC_ArchvisBP/...
Templates/TP_PhotoStudioBP/...

Templates/TP_ME_BlankBP/...
Templates/TP_ME_VProdBP/...

Templates/TP_CollaborativeBP/...

<!-- Shared template resources -->
Templates/TemplateResources/...

<!-- Build files -->
Engine/Build/Build.version
Engine/Build/Target.cs.template
</Property>

根据自己的需要,修改即可。

增量构建二进制引擎

避免每次都重新编译Engine模块,导致引擎中大量依赖它的模块被重新编译。修改引擎中的Engine.Build.cs

1
2
3
4
5
6
PrivateIncludePathModuleNames.AddRange(
new string[] {
"MessagingRpc",
"PortalRpc",
"PortalServices",
}

改为:

1
2
3
4
5
6
PublicDependencyModuleNames.AddRange(
new string[] {
"MessagingRpc",
"PortalRpc",
"PortalServices",
}

UDN上的相关讨论:Make Installed Build Win64中 Compile UE4Game Win64 特别慢的问题

结语

本篇文章主要介绍了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都是需要分别编译的,对执行支持的所有平台都增加了一次引擎编译的时间开销,当然是必要的,不过在日常的开发中,可以有选择性地开启,节省构建的时间。

全文完,若有不足之处请评论指正。

微信扫描二维码,关注我的公众号。

本文标题:BuildGraph:构建支持多平台打包的二进制引擎
文章作者:查利鹏
发布时间:2020年11月08日 15时40分
更新时间:2023年10月22日 12时35分
本文字数:本文一共有6.3k字
原始链接:https://imzlp.com/posts/11956/
许可协议: CC BY-NC-SA 4.0
文章禁止全文转载,摘要转发请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!