ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

UE Windows 交叉编译 Linux 项目打包实战指南

UE Windows 交叉编译 Linux 项目打包实战指南 做UE项目这些年真正让人半夜爬起来改配置的往往不是渲染管线调参也不是网络同步那套东西而是我这台Windows开发机怎么才能一键出一个能在Linux上跑起来的包。UE、Windows、Linux、交叉编译、项目打包这五个词单独拎出来都不难凑在一起就变成了一堆版本号、环境变量和找不到的依赖库。我所在的团队做的是一套要在国产化终端和云渲染容器里跑的项目开发全在Windows上交付却要求Linux最早那阵子我们专门养了一台Linux编译机每次出包都要远程连过去拉代码、编译、拷回来一天下来光等编译就耗尽了大半精力。后来把UE的Linux交叉编译彻底吃透Windows上一台机器就能出Linux包出一版从两小时压到二十分钟以内。这篇就把这套流程从头到尾拆开讲包括工具链怎么选、环境变量怎么配、打包命令每个参数在干什么、以及那些只会在真机上爆出来的坑。不管你是刚接触UE多平台发布的新手还是已经在做Linux发行版适配的老手应该都能从里面捞到点能直接抄的东西。1. 先弄明白UE在Windows上是怎么骗出Linux包的1.1 真机编译和交叉编译到底该选哪条路先说清楚两条路的区别因为很多人一上来就走错了方向。真机编译就是在Linux系统上装一份完整的UE引擎把工程拷过去用Linux上的UnrealBuildTool和clang编译。这条路的好处是环境纯粹原生编译出来的东西不会有奇怪的兼容问题调试也直观gdb、perf这些工具随手就能用。坏处也很明显你得有一台性能过得去的Linux机器得在Linux上重新装一遍引擎和依赖团队每个人的开发习惯还得改美术同学改个材质想本地验证一下都得往Linux上同步。交叉编译的思路完全不同——引擎和工具链都跑在Windows上但工具链本身是一套为Linux目标打造的clang它能在Windows主机上生成Linux的ELF可执行文件和.so动态库。整个编译过程里Windows只是宿主产出的二进制从头到尾都是Linux的。这样一来开发同学在Windows上照常写代码、点编译出包的时候选Linux平台ailian的东西直接就出来了。我个人的判断标准是这样的如果你只是偶尔出一次Linux包项目规模也不大真机编译其实更省心省掉一堆工具链配置的麻烦。但如果是持续交付、每天都要出包、或者团队里没人愿意维护一台Linux机器那交叉编译是唯一合理的选项。我们最终选交叉编译核心原因就是它把出Linux包这件事的门槛降到了和出Windows包差不多谁都能在本地出不用排编译机的队。1.2 Epic官方工具链的版本对应关系装错一步全盘皆输这是最容易翻车的地方。Epic维护了一套专门给Linux交叉编译用的工具链发布在EpicGames的UnrealToolchains仓库里命名格式大概是vXX_clang-YY.Y.Y-发行版。这里的XX是工具链自己的版本号和后缀里的clang版本、底层发行版是绑死的不能随便混用。关键在于每个UE大版本对工具链版本有硬性要求。UBT在编译Linux目标时会去读引擎里配置好的期望版本版本对不上会直接报错甚至更糟——报一堆莫名其妙的编译错误让你怀疑人生。我整理了一份我们实测验证过的对应关系仅供参考具体还是以你那个引擎版本目录下的官方说明为准引擎版本推荐工具链底层系统编译产物最低glibc要求UE 4.27v19_clang-11.0.1-centos7CentOS 72.17UE 5.0 - 5.2v20_clang-13.0.1-centos7CentOS 72.17UE 5.3v22_clang-16.0.6-centos7CentOS 72.17UE 5.4v23_clang-16.0.6-rockylinux8Rocky Linux 82.28UE 5.5v25_clang-18.1.0-rockylinux8Rocky Linux 82.28这张表里最后一列是很多人忽略的重点。你用什么底层系统编译产出的二进制就继承那个系统的glibc版本要求。用CentOS 7那套工具链编出来的包只要目标机器glibc不低于2.17就能跑覆盖面极广换成Rocky Linux 8那套最低要求直接抬到2.28意味着Ubuntu 18.04这类老系统直接就点不着了。我们有一次从UE 5.3升到5.4客户现场那批老终端全部起不来排查了半天才发现是glibc版本这道坎。提示引擎升级的时候一定要把工具链一起升级别偷懒复用旧版本。旧工具链在新引擎上大概率编不过新工具链在旧引擎上也可能因为ABI差异出问题。1.3 工具链装了UE却不认九成是环境变量的问题工具链下载下来了解压了重启引擎结果打包的时候还是提示找不到Linux工具链这个问题我遇到太多次了。根源在于UBT并不知道你把工具链扔哪了它只认几个固定的位置和一个环境变量。最稳的做法是设置一个系统环境变量指向工具链的根目录。这个变量的名字是LINUX_MULTIARCH_ROOT值就是那个解压出来的文件夹路径注意是根目录不是里面的bin目录。设完之后一定要重新打开命令行和编辑器让新环境变量生效很多人设完不重启进程白白浪费半小时。另一个备选方案是把工具链放到引擎目录下的特定位置让UBT通过相对路径找到它。这个方案的好处是不依赖环境变量团队里每个人只要拿到同一份引擎目录就能用坏处是引擎目录会变得非常臃肿而且引擎一升级就得重新挪一次。我们现在的做法是环境变量加脚本自动设置双保险机器多了以后手动配真的会漏。还有一点如果你们团队用CI跑打包环境变量必须写进构建脚本里因为CI的构建账户和你本地登录账户完全是两套环境本地能跑不代表CI能跑。2. 工具链落地从下载到工程能够识别2.1 拿到对应版本的工具链压缩包下载渠道就是EpicGames在代码托管平台上开的那几个仓库里面有Windows版本和Linux版本的压缩包我们只需要Windows那个名字里通常带-windows后缀。文件不大几百MB到1GB多解压完可能有三四个GB所以提前找个空间充裕的盘。解压路径上有个经验路径里千万别有中文、空格和特殊字符。我们最早图省事解压到新建文件夹2这种默认名字里UBT直接报路径解析失败改成纯英文短路径之后就好了。另外路径也别太深Windows的路径长度限制在打包过程中很容易被撑爆尤其是Cook阶段会生成大量临时文件路径叠起来很容易超260个字符。我们现在的规范是统一放在盘符根目录下的一个短英文目录里比如C:\UnrealToolchains\简单粗暴。解压完之后你会看到一个目录结构里面有几个子目录分别对应不同的架构和工具。这个时候不用去动里面的任何东西也别想着精简掉哪个文件夹交叉编译用到的clang、lld、链接器、头文件、sysroot都在里面删一个就编不过。2.2 环境变量怎么设才算设对了设置LINUX_MULTIARCH_ROOT这个变量操作本身很简单在系统属性的高级设置里加一条就行但有几个细节必须注意。第一值必须指向包含各工具链版本的父目录还是具体版本目录这两者的区别经常把人绕晕。按我们的实测指向你解压出来的那个具体工具链根目录是可行的里面有clang、bin、x86_64-unknown-linux-gnu这样的结构。稳妥起见设完之后可以在命令行里echo一下确认然后再去引擎的打包日志里找一行关于toolchain的输出来验证它真的被识别到了。第二如果你机器上同时装了多个版本的引擎需要多个工具链那就得在打包脚本里按需覆盖这个变量。做法是写一个批处理先set对应的路径再调用打包命令。这样比改系统级环境变量灵活得多也不会互相打架。第三杀毒软件要加白名单。交叉编译过程中会生成和调用大量小的临时可执行文件某些杀软会把它们当成可疑行为拦截掉表现就是编译到一半突然失败报一个exec相关的错误重试一次又能过。这种随机失败最折磨人加白名单之后立刻就稳了。注意设置环境变量之后务必完全退出并重启命令行窗口和编辑器。Windows下已启动的进程不会自动继承新的环境变量这是最容易忽略的一步。2.3 工程侧要确认的几个关键开关工具链配好了工程本身也有几处要动。首先是目标平台列表如果你用的是源码版引擎生成工程文件的时候要把Linux目标带上这样UBT才知道这个工程支持Linux平台。如果是安装版引擎Linux支持一般是内置的但打包选项里能不能看到Linux平台取决于引擎是否带了对应该平台的编译支持。然后是工程设置里的目标平台相关配置。项目设置里Linux平台下面有一系列选项包括是否启用Vulkan渲染、目标架构、是否生成Server版本等等。渲染这块特别要说一句UE5的Linux默认走Vulkan如果目标机器显卡驱动对Vulkan支持不好可能需要回退到其他方案但UE5里OpenGL那条路基本已经废弃了别指望它。目标架构默认是x86_64如果你要跑在ARM架构的板子或者某些国产化设备上得看引擎版本是否支持aarch64这个在UE 5.3之后才逐渐完善老版本做不了。还有一个坑是项目里所有引用的资源路径大小写必须规范。Windows文件系统不区分大小写Linux是严格区分的。一个贴图在Windows上叫Rock_01.png代码里写成rock_01.png照样能跑到了Linux上就直接丢资源。这个错误在编辑器里完全看不出来只有出包之后在Linux上跑才会暴露。我们的做法是过一遍打包日志里的warning那些大小写不一致的提示全清掉宁可现在多花点时间也别等交付之后被打回来。3. 打包命令逐条拆解从Cook到Archive3.1 一条能直接跑起来的最小命令图形界面当然可以点但真正做交付必须走命令行因为命令行可复现、可脚本化、可进CI。下面这条是我们日常在用的基础命令路径换成你自己的工程就能跑Engine\Build\BatchFiles\RunUAT.bat BuildCookRun ^ -projectD:\Work\MyProject\MyProject.uproject ^ -noP4 ^ -platformLinux ^ -clientconfigShipping ^ -serverconfigShipping ^ -cook ^ -allmaps ^ -build ^ -stage ^ -pak ^ -compressed ^ -archive ^ -archivedirectoryD:\Builds\MyProject\Linux这条命令里有十几个参数看着吓人其实每个都在干一件明确的事。-noP4是告诉它别去连Perforce本地打包必须加不然会卡在版本控制检查上。-platformLinux是整个命令的核心它决定了后面所有操作都针对Linux目标。-clientconfigShipping指定以发行配置编译客户端这个配置会开优化、去掉调试符号是正式交付必须的。-serverconfig是给专服用的不出专服可以不加。3.2 Cook、Build、Stage、Pak、Archive到底各干了什么这五个动词是打包流程的骨架理解了它们才能定位问题出在哪一步。Cook是把资源从编辑器的格式转换成运行时能读的格式。你在编辑器里看到的那些uasset在Cook阶段会被翻译成平台相关的二进制贴图会按目标平台支持的格式重新编码Shader会针对目标平台编译。这一步最耗时也最容易出问题跨平台的资源兼容性问题基本都是在这一步暴露的。Linux的Shader必须在Windows上针对Linux目标编译这靠的是引擎自带的Shader编译工作进程它会调用交叉工具链所以如果工具链没配好Cook阶段就会报错。Build是编译代码。这一步针对Linux目标调用交叉编译工具链把C代码编成Linux的.so和可执行文件。如果你改了代码但没加-build打包出来的还是旧的二进制这个坑我们踩过改了半天逻辑发现包里的行为没变最后才发现参数漏了。Stage是把所有需要的东西按运行时的目录结构摆到一个临时目录里。这一步决定了最终包里的文件组织形式缺文件、多文件基本都是这一步的问题。Pak是把Stage好的内容打包成.pak文件。好处是文件数量少、加载快、方便整包分发坏处是调试的时候不方便改单个资源。配合-compressed会给pak做压缩包体明显变小代价是首次加载稍微慢一点点。Archive是把Stage好的结果复制到-archivedirectory指定的目录。不加这个参数打包产物会留在工程目录里加了这个参数就是你要的最终交付物。3.3 打包产物长什么样哪些文件必须一起发第一次拿到Linux包的人经常会懵因为目录结构是这样的一个带LinuxNoEditor后缀的总目录里面又有工程名目录、Engine目录然后才是Binaries、Content、Plugins这些。真正要运行的时候入口是Binaries/Linux下面那个没有扩展名的可执行文件以及同目录下的一堆.so。工程根目录下还会有一个同名但带.sh后缀的脚本直接执行这个脚本是最省事的它会自动处理一些环境变量。但是在Linux上有个细节从Windows拷过去的文件默认没有可执行权限直接运行会报权限拒绝需要手动加执行权限或者打包分发的时候用tar之类的格式保留权限位。我们最早用压缩软件打包发出去客户解压完全是没有执行权限的文件一堆人问怎么启动。后来改成打包脚本里直接用Linux原生压缩权限问题就没了。Content目录下的Paks是核心资源Engine目录下是引擎的运行时依赖Plugins目录下是插件。如果用了第三方插件带原生库一定要检查它有没有出Linux版本的.so很多插件在Windows上好好的一到Linux就缺库。提示交付前在一台干净的Linux机器上解压运行一次别在开发机上跑通了就直接发。开发机上装了各种运行库很容易掩盖依赖缺失的问题。4. 实测最容易翻车的环节与排查手法4.1 编译期报错怎么定位编译期的问题绝大多数和工具链相关。最容易遇到的报错是找不到工具链或者工具链版本不匹配错误信息里一般会出现toolchain、LINUX_MULTIARCH_ROOT、clang这些关键词。看到这类报错第一反应就是去检查环境变量是否正确、设完之后进程有没有重启、工具链版本和引擎版本是否匹配。第二类常见报错是链接阶段的符号找不到。这种情况通常是第三方库没有提供Linux版本或者提供了但架构不对。排查方法是看报错里的.so名字去工具的库目录里确认这个文件是不是真的存在、是不是对应的架构。我们遇到过一次某个音频中间件只有Windows和macOS的库Linux的库要单独申请结果编译到最后链接阶段才炸前面全白跑。这种问题越早发现越好所以接入新插件的第一时间就应该跑一次Linux打包验证别等到交付前才发现。第三类是内存不足。交叉编译对内存的要求比原生编译高一些尤其是Unity Build开启之后一个编译单元可能吃好几个GB内存。内存不够的表现是随机的编译失败、进程被系统杀掉。解决办法要么加内存要么关掉Unity Build分散编译压力代价是编译时间变长。4.2 运行期报错怎么排查包能编出来不代表能跑起来运行期的问题更隐蔽。第一类常见的是缺动态库报错信息里会明确写出缺哪个.so。这种情况下要分清楚是系统库还是工程自带的库系统库要靠目标机器装工程库要检查打包是否漏了。第二类是图形相关的问题。表现是启动就崩或者黑屏日志里通常有Vulkan、驱动、设备相关的字样。这个多半是目标机器的显卡驱动太老或者不支持Vulkan。UE5的Linux渲染对驱动版本是有要求的特别是那些老旧的集成显卡和国产化平台上的显卡驱动跟不上的时候只能换机器或者找厂商要新驱动。第三类是资源加载失败表现是某些模型、贴图不显示或者界面上一堆问号。这个问题八九成是前面提到的大小写问题也有一小部分是字体问题。中文字体是个大坑Windows上系统字体是现成的Linux上未必有对应的字体文件如果项目里UI用的是系统字体到了Linux上就可能显示成方块。解决办法是把字体资源打包进工程别依赖系统字体。4.3 一张速查表出问题先照着对现象大概率原因处理方向提示找不到Linux工具链环境变量未设或未生效检查变量并重启进程编译到一半随机失败杀毒软件拦截临时文件加白名单大量clang报错工具链版本和引擎不匹配按引擎版本换工具链链接阶段缺符号第三方库没有Linux版本联系厂商或换库启动即崩、日志有Vulkan字样显卡驱动不支持升级驱动或换硬件界面中文显示方块缺中文字体文件字体资源打包进工程部分资源不显示路径大小写不一致清理Cook日志里的warning拷到Linux后无法执行缺可执行权限加权限或用tar分发启动报缺.so依赖库未随包发出补齐库文件或装系统库这张表是我们团队踩了两年坑攒出来的遇到问题先照着对一遍能省掉大半排查时间。5. 把打包做成可重复的自动化流程5.1 打包脚本化与参数外置单机偶尔出一次包手敲命令没关系。但一旦进入持续交付参数就必须外置。我们的做法是准备一个配置文件把工程路径、输出目录、包配置、是否出专服这些变量集中管理打包脚本读配置再拼命令。这样换项目、换版本号的时候只改一个地方不会出现某个脚本忘了改导致包版本号对不上的情况。脚本里还要做几件事打包前清理上一次的输出目录避免旧文件混进新包打包失败的时候返回非零退出码让CI能感知把打包日志完整落盘出问题时能翻。日志这个东西平时没人看真出问题的时候是唯一的线索尤其是Cook阶段的日志资源类问题的答案全在里面。出包自动化还有一个收益是版本号一致性。Windows包和Linux包如果分别手工出版本号很容易对不上测试同学拿到两边包一对比就发现问题。把版本号作为参数注入两个平台的包一起出这个问题从根上就没了。5.2 目标架构和发行版兼容性怎么定这是做Linux交付绕不开的决策。架构上x86_64是绝对主流服务器、PC、大部分云环境都是它。ARM64这两年在国产化设备和一些嵌入式场景上越来越常见UE从5.3之后对aarch64的支持才逐步可用如果你的目标平台是ARM务必先用一个最小的空工程把打包链路跑通别直接上正式工程不然一堆引擎自身的问题会把你埋掉。发行版兼容性上核心就是前面反复提的glibc版本。工具链决定了最低版本你只能在工具链给的范围内选目标系统。如果客户现场有一批老系统就得用老底包的工具链宁可放弃一些新特性也要保证能跑。反过来如果目标环境全是新系统那用新的工具链能少踩很多老工具的坑。还有一个容易被忽略的点是包的大小和启动速度。Linux交付经常会走到容器或者云环境里包越小、启动越快成本越低。可以关掉的调试符号、可以裁剪的引擎模块、可以延迟加载的资源都值得花时间优化。我们做过一轮优化把包体压下来三成启动时间也明显缩短云上跑的时候成本差异很直观。5.3 多平台配置的维护经验最后说几句工程维护。多平台项目最怕的是某一平台能跑另一平台跑不了而问题往往藏得很深。我们的规范是每次主干合入之后Windows和Linux两个平台的打包都必须过一遍宁可多花点机器时间也别让问题攒着。攒着的问题不会自己消失只会在交付前一天集中爆发。插件管理上引入任何带原生代码的插件之前先确认它有没有Linux支持。没有的话能不能自己编译能不能用纯蓝图方案替代都要提前想清楚。我们在项目中期换过一次音频方案就是因为原来的插件死活没有Linux版本越晚换成本越高。配置管理上Linux相关的设置尽量放在单独的平台配置文件里别和Windows混在一起改。引擎里针对不同平台有覆盖机制用好这套机制能让平台差异集中在一个地方排查问题的时候一目了然。我个人在实际操作中的体会是UE的Linux交叉编译这件事难点从来不在技术本身而在那些散落在文档角落里、只有踩过才知道的细节。工具链版本、环境变量、路径大小写、glibc版本、执行权限、中文字体这六样东西基本覆盖了我遇到过的八成问题。把这几处提前处理干净Windows上出Linux包就是一条命令的事。另外分享一个小技巧第一次接触某个新引擎版本要做Linux打包时先建一个空白的第三人称模板工程把整条链路跑通确认工具链、打包、Linux上启动这三步都没问题再往正式工程上套。这个习惯帮我省掉了无数次到底是引擎的问题还是我工程的问题的纠结。
返回列表