一次惨绝人寰的编译(上)

为了让一个老游戏兼容 Mac 和 Steam Deck,我前阵子动手做了个新版本的构建。游戏是用 JavaScript 写的,跑在 Electron 上。既然用了“跨平台”的技术栈,那它至少应该在这种时候派上点用场吧?

任务目标很明确:编译 Electron 1.8.0 的 macOS 和 Linux 版本。

一开始我以为构建 macOS 版本需要用 Apple Silicon 的设备,于是入手了一台最低配的 Mac Mini M2,8GB 内存、256GB SSD,能省就省。但上手之后才发现,macOS 提供了一个叫 Rosetta 的 Intel 兼容层,大多数老程序在 M2 上运行完全没问题。换句话说,我这台新电脑根本没派上用场。

于是我翻出了多年前的老设备——Mac Mini 2014,发现当年我确实在这台机器上编译过 Electron 1.8.0。稍微调整下配置,理论上是可以继续用的。信心满满地开工。

回顾旧文档、准备构建环境、跑脚本,一切按部就班。但第一轮构建很快就失败了,脚本一运行就报错。问题出在 Python:Electron v1.8.0当年依赖的是 Python 2.7,而我在 2023 年升级了 macOS,系统默认已经切换到了 Python 3。很多老脚本因此不再兼容。

于是退回 Python 2.7,再次运行 build.py,这次终于顺利进入构建流程。前期编译过程很快,毕竟历史中间文件还在。可没想到,等到了 link 阶段,机器突然开始变卡,几分钟后系统直接重启了。

我试了几次,都是在 link 阶段死机。最初还怀疑是老机器硬件出问题了。但联想到死机总发生在同一阶段,我猜可能是资源耗尽。打开任务管理器观察,果然在崩溃前有异常磁盘写入,某个系统进程(kernel什么的)在短时间内写了三十多个 G数据。但编译目录并没有生成相应的大文件,看起来不是 link 阶段输出的问题。

我接着观察内存使用情况,发现一个叫 dsymutil 的进程在这个阶段疯狂占用内存,远超物理内存限制,很可能触发了大量 swap 操作,最终导致系统不堪重负直接重启。macOS 在内存耗尽时没有进行优雅处理,反而选择硬重启,这倒是出乎意料。

dsymutil 是什么我并不清楚,于是请教了 ChatGPT。它告诉我,这个工具是 LLVM 工具链的一部分,用于生成调试符号(debug symbols)。考虑到 Electron 本身体积庞大,符号信息数量可能非常可观。显然,dsymutil 没有能力处理这种规模的符号,最终把系统拖垮。

既然问题出在生成调试信息,而 link 阶段其实并不依赖它,我想到一个简单粗暴但有效的办法:制作一个同名空脚本,替代系统中的 dsymutil,让它直接跳过这一步。

具体做法很简单:写一个只包含 exit 0 的 shell 脚本,命名为 dsymutil,赋予可执行权限,放进 $PATH 的前列。这一改动之后,构建流程终于顺利完成,link 阶段不再崩溃。

剩下的收尾工作就相对平淡:为了发布到 Steam,还需要处理 macOS 应用包中的 symbolic links,把它们转换成实际文件,避免打包时出现兼容性问题。另外也踩了 macOS 的路径处理一个坑:JavaScript 获取到的是真实路径而非 symbolic link,对路径逻辑略作调整后问题解决。

Mac版本处理完之后,我打算着手构建 Linux 版本,以支持 Steam Deck。听起来似乎没什么难度?嗯嗯,我得先写到这儿了,牵扯到electron这坨***肯定简单不了,Linux的事情且听下回分解吧。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.