通常,UE4开发者获取UE4引擎的方式有两种:
- 从Epic Games Launcher安装
- 从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实现。
主要流程如下:
- 编译UBT等构建工具
- 编译NotForLicence工具
- 编译Editor
- 编译支持的平台(默认Win64)
- Make Feature Packs
- 拷贝构建结果
如果想要修改构建的二进制引擎的导出路径,可以修改
InstalledEngineBuild.xml
中的BuiltDirectory
值。
1 | <!-- Ouput directory for the build --> |
而且,经过分析Make Installed Build Win64
,发现它其中有很多重复执行的编译流程。
以编译UE4Game Win64
为例:
1 | <Node Name="Compile UE4Game Win64" Requires="Compile UnrealHeaderTool Win64" Produces="#UE4Game Win64;#UE4Game Win64 Unstripped;#UE4Game Win64 Stripped;#UE4Game Win64 Unsigned;#UE4Game Win64 Signed"> |
可以看到,针对同一个编译的Target,使用不同的编译参数执行了两次:
- 具有
-allmodules
、-nolink
等参数 - 不具有上述参数
而开启了-allmodules
,则意味着引擎中所有的模块都要重新编译,是完整地编译引擎。当下次执行,还是要完整地编译整个引擎,UBT中具有对-allmodules
的介绍:
-allmodules
参数是定义在UBT的TargetRules.cs
中的:
1 | /// <summary> |
专门用来构建安装版引擎的,我觉得可以关掉,使用增量编译的方式执行,不然在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 | UE4Editor DebugGame/Development 2次 |
如果我们使用BuildGraph通过Make Installed Build Win64
来构建出安装版引擎(支持Win/Android/IOS打包),至少需要编译2+3*2=8
,对引擎的代码要编译八次!而且每一次执行都要完整的编译所有的模块,耗时非常之长。
所以裁剪是非常有必要的,上面也已经提到了:
- 去掉
-allmodules
参数,增量编译 - 去掉
-nolink
的构建(根据需求决定) - 减少需要构建的平台
裁剪之后需要需要编译的次数:
- UE4Editor Development 1次
- UE4Game Win64/Android/IOS/… 每个平台
1*Configuration
次
则需要编译4次,与默认减少一倍,并且可以增量编译,时间会快很多了。
构建引擎支持Android打包
想要使用Make Installed Build Win64
构建出支持Android打包的引擎,需要在执行BuildGraph脚本时添加-set:WithAndroid=true
,因为WithAndroid
是InstalledEngineBuild.xml
中定义的参数,可以通过命令行传递。
不过,仅仅是命令行指定开启是不够的,在执行BuildGraph之前,需要安装好Android的开发环境,在BuildGraph在执行编译时会去系统PATH里查找JDK/Android NDK/Android SDK以及gradle
的路径,需要自己进行预先配置:
注意:虽然在执行编译时会检测环境自动下载gradle,但是由于在墙内的原因,不一定会下载成功。
需要把Android的环境按照添加至系统的环境变量中:
注意:当使用一些ci工具的时候,ci只能查到服务启动的用户的环境变量,为避免额外的问题,最好添加至系统的环境变量。
默认情况下会编译出支持打包Android架构为armv7
和arm64
的引擎,如果不需要其中的某个架构,可以在InstalledEngineBuild.xml
里选择注释掉指定的平台。
Android比较简单,配置完Android的开发环境就可以直接导出可以打包Android的引擎:
构建引擎支持IOS打包
前面已经提到了,构建支持Android打包的二进制引擎,支持IOS平台要更复杂一些。
首先构建支持IOS打包的引擎必须要有一台Mac执行远程构建,因为BuildGraph需要编译IOS平台的UE4Game,在Win上无法编译IOS的库,所以必须要一台Mac。
- 局域网中有一台可访问的Mac
- 具有mobileprovision
- 生成SSH Key
至于“为什么有了Mac还需要在Win上支持IOS?”这个问题主要是因为:
- Mac机能受限,直接在Mac上打包是编译+Cook的操作都在Mac上,如果进行远程构建,则Mac只需要处理代码的编译,而不需要执行Cook的流程,降低对Mac机能的依赖,可以提高构建的效率。
- 统一地使用Win来构建出全平台包(Win/Android/IOS)
首先需要在Win上生成Mac的SSH Keys,BuildGraph编译引擎支持IOS也需要设置ssh key,生成的方法在我之前的博客中有记录,不再赘述:UE4 开发笔记:Mac/iOS 篇#配置远程构建
当生成了SSHkey之后,需要在引擎的Engine/Config/BaseEngine.ini
中修改以下配置:
1 | [/Script/IOSRuntimeSettings.IOSRuntimeSettings] |
注意:这里的mobileprovision和SigningCertificate指定名字就可以,如:
1 | MobileProvision=com.XXXX.XXXX.fmgame_Development_SignProvision.mobileprovision |
并且,可以不指定SSHPrivateKeyOverridePath
的路径,在RemoteMac.cs
中,针对引擎目录有三个自动查找的路径:
1 | if (ProjectFile != null) |
把SSH Key按照这样的命名格式放到这三个目录下即可:
1 | SSHKeys/IP_ADDR/USER_NAME/RemoteToolChainPrivate.key |
如:
1 | SSHKeys/192.168.1.123/buildmachine/RemoteToolChainPrivate.key |
远程构建的UBT路径异常
在执行时RemoteMac.cs
中报错的问题:
1 | /// <summary> |
上面的代码在Combine时没有做检测,Environment.SpecialFolder.ApplicationData
这个路径是不一定能得到的,所以要修改UBT的RemoteMac.cs
中的这部分代码,进行检测:
1 | private bool TryGetSshPrivateKey(out FileReference OutPrivateKey) |
上一步的操作,只能解决找ssh key时的路径报错问题。
远程构建时SSH连接错误
解决了这个问题还有另外一个问题:
1 | ****** [4/11] Compile UnrealHeaderTool Mac |
看到是ssh key的验证失败了,其实这个错误并不是Key的问题(这个问题非常坑)。
而是因为RemoteMac.cs
中对ssh连接命令的代码里是这么写的:
1 | class RemoteMac |
这个代码生成的拼接的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 | $ Engine\Extras\ThirdPartyNotUE\DeltaCopy\Binaries\ssh.exe -p 22 buildmachine@ 192.168.1.123 |
会弹出提示让你验证这台主机(需要手动输入yes),但是开启了BatchMode=yes
之后,会禁止所有的交互式提示,出现交互直接连接失败!这就是我们使用正确的Key,但是会提示Host key verification failed.
的原因。
那么,知道了原因,解决这个问题的办法有两种:
- 关闭ssh的连接时验证
- 在进行构建之前,手动使用ssh命令连接主机,通过交互式的验证(只需要初始化验证一次)
未导入provision的错误
编译时的其他错误:
1 | ****** [6/11] Compile UE4Game IOS |
这是因为编译引擎时没有配置provision
,需要在Engine/Config/BaseEngine.ini
中设置。
在这里可以只指定provision
文件的名字,在UEBuildIOS.cs
中的代码会从C:\Users\lipengzha\AppData\Local\Apple Computer\MobileDevice\Provisioning Profiles
路径下去查找。
1 | protected IOSProvisioningData(IOSProjectSettings ProjectSettings, bool bIsTVOS, bool bForDistribtion) |
这部分操作也可以通过引擎来导入,不仅仅只需要导入provision
还需要导入证书(也可以通过iPhonePackager来导入):
如果不导入会有下列错误:
1 | ****** [7/12] Compile UE4Game IOS |
上述的问题解决完毕之后就能够正常地把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. |
解决办法:
- 在Mac上的
~/Library/MobileDevice/Provisioning\ Profiles
清理掉多余的mobileprovision文件。 - 在Mac钥匙串中清理掉过期的开发者证书
- 重新导入mobileprovision与证书
注意:导入的mobileprovision的文件命名要与在BaseEngine.ini
中指定的MobileProvision
相同。
errSecInternalComponent错误
- Xcode Command /usr/bin/codesign failed with exit code 1 : errSecInternalComponent
- iOS远程自动打包问题
- How to Fix iOS Application Code Signing Error?
- “Warning: unable to build chain to self-signed root for signer” warning in Xcode 9.2
方法一
是因为通过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 |
|
因为编译时会把Build.sh通过RSync传递到Mac上,所以可以看到以下log:
1 | [Remote] Executing build |
这样每次编译都会解锁keychain,从而避免ssh连接时没有访问codesign导致的签名错误。
注意:也需要排查BaseEngine.ini中
SigningCertificate
的值是否被指定。
方法二
如果系统中苹果的开发者中级证书过期,同样会导致这个问题。解决办法是导入新的苹果开发者根证书:
把钥匙串
-系统
-Apple Worldwise Developer Relations Certification Authority
删除,然后从上面的连接中下载新的,重新导入即可。
Invalid trust settings
如果Log中出现以下错误:
1 | 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. |
这是因为在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都是需要分别编译的,对执行支持的所有平台都增加了一次引擎编译的时间开销,当然是必要的,不过在日常的开发中,可以有选择性地开启,节省构建的时间。