像是在 1997 年那样编译 Quake:一场探索遗产工具链的旅程
对于现代开发者来说,“编译”这一行为通常涉及单个命令——npm run build、cargo build,或者在处理依赖项自动化的复杂 IDE 中点击播放按钮。但在 90 年代中期,构建像 Quake 这样尖端的作品,是对特定硬件、脆弱的操作系统以及精确的安装盘序列的精细编排。
重建 1997 年时期的 Quake 构建环境不仅仅是一次怀旧之旅;它也是对那个时代约束条件和工程严谨性的研究。从 NeXTSTEP 到 Windows NT 的过渡,再到对手写优化汇编代码的处理,这一过程揭示了一个每一个字节和 CPU 周期都必须争取的时代。
Quake 技术栈的演进
Quake 的开发并非发生在单一平台上。最初的可执行文件(quake.exe 和 vquake.exe)是在运行 NeXT 的 HP 712-60 工作站上编写的,并使用 DEC Alpha 服务器 2100A 上的 DJGPP 进行交叉编译。然而,随着 NeXT 的停滞,id Software 转向了其他方向。
到 1996 年 6 月,John Carmack 指出,团队已转向运行 Windows NT 的 Intergraph 硬件。这一转变也为后续版本铺平了道路,包括 winquake.exe、glquake.exe 和 QuakeWorld,所有这些都是在 Windows NT 上使用 Visual C++ 4.X 开发和编译的。
重建 1997 年的环境
为了在今天以历史准确性构建 Quake,必须应对一系列遗产软件依赖项的“雷区”。虽然纯粹主义者可能会寻找 Intergraph RealizM 工作站或双 Pentium Pro 机器,但运行 Windows NT 4.0 或 Windows 98SE 的 VirtualBox VM 是最实际的途径。
操作系统与 IDE
安装 Windows NT 4.0 可以提供一种极简主义的体验,它显著支持 SMP(对称多处理),这使得 id 开发者能够利用双 CPU 设置——这在当时的消费级 PC 上是罕见的。
对于 IDE,目标是 Visual C++ 6 (VC++6)。虽然 Quake 最初是用 VC++ 4.X 编写的,但该项目在 1999 年迁移到了 VC++6。为了成功加载项目,开发者必须避免使用 GitHub 或 FTP 传输等现代方法,因为这些可能会损坏 .dsw(工作区)文件。使用来自 Quake Official Archive 的原始 q1source.zip 是确保工作区正确解析的关键。
汇编语言障碍
构建过程中最显著的障碍之一是 Michael Abrash 编写的手写优化汇编代码。由于 VC++6 无法在没有适当工具的情况下汇编 .s 文件,构建最初会失败。
解决这个问题需要一个特定的、非线性的安装序列,正如一位观察者所言,这感觉像是“遵循 Monkey Island 的解谜路径”:
- 安装 Visual Studio 6.0 Service Pack 5:这最初会失败,因为它需要 MDAC 2.5。
- 安装 MDAC 2.5:这可以在 service pack 的解压文件夹中找到(
mdac_typ.exe)。 - 完成 Service Pack 5 安装:现在安装程序将会继续。
- 安装 VC++6 Processor Pack:这提供了处理
.s文件的汇编器ml.exe。
一旦这些准备就绪,执行“Rebuild All”最终会生成二进制文件。
工程学见解与遗产
代码库的严谨性
尽管代码已经有了岁月的痕迹,但 Quake 的代码库仍然是高水平工程学的见证。一些尝试过构建该项目的开发者注意到,整个项目仅有两个警告。这反映了一种严谨的工程哲学——避免警告并使用大量的 debug builds 中的 assert,这在现代、快速迭代的游戏开发中并不常见。
VC++6 的力量
虽然它缺乏 IntelliSense 等现代功能,但 VC++6 在当时是一个强大的工具。它提供了“跳转到定义”、“断点”、“堆栈跟踪”和“变量检查”。它的速度和清晰度使其成为整整一代程序员的的宠儿;一些人,例如普及的 stb 库的作者 Sean Barrett,据闻在行业转向之后很长时间内仍在使用它。
向 Win32 的战略转向
John Carmack 对那个时代的历史回顾强调了一个战略性认识:Win32 是必然的赢家。虽然他个人更喜欢 NeXTSTEP 的优雅,但 Windows NT 4.0 的实际优势——TCP/IP 支持、动态链接和强大的虚拟内存——使其成为 id Software 未来发展的唯一可行选择。他曾有名地宣布过“Win32 统治世界”,标志着高端游戏开发向 DOS 时代的结束。
结论
在 1997 年风格的环境中编译 Quake 是一次提醒,软件开发中的“管道建设”发生了多大的变化。我们已经用现代构建系统的抽象化来换代了手动工具链配置的脆弱性。然而,在这样做的时候,我们也失去了一点与机器的亲密感,那正是定义了 Quake 时代——一个通过几条精心放置的汇编指令和特定的 service pack 就能定义交互式娱乐前沿的时代。