03 单文件构建脚本与平台分包 本节摘要:第 3 章我们讲了「启动脚本挑平台包」,这一节讲反面——单文件构建脚本怎么造出那些平台包。OpenCode 用构建工具的「编译(compile)模式」把 TypeScript 源码产成一个单文件原生二进制(内嵌 Web UI 和 workers),按一个目标矩阵(共 12 个变体)产出所有平台包。本节讲清这套构建机制,把「造包」和「挑包」连成完整闭环。 一、目标:单文件原生二进制 先明确构建目标。OpenCode 想要的是单文件原生二进制——一个独立可执行文件,不需要用户装运行时,拷贝到机器上就能跑。 为什么是单文件? 零依赖:用户不用装 Bun/Node,降低使用门槛。 启动快:原生二进制比解释执行快。 分发简单:一个文件,好分发、好版本管理。
本节摘要:第 3 章我们讲了「启动脚本挑平台包」,这一节讲反面——单文件构建脚本怎么造出那些平台包。OpenCode 用构建工具的「编译(compile)模式」把 TypeScript 源码产成一个单文件原生二进制(内嵌 Web UI 和 workers),按一个目标矩阵(共 12 个变体)产出所有平台包。本节讲清这套构建机制,把「造包」和「挑包」连成完整闭环。
先明确构建目标。OpenCode 想要的是单文件原生二进制——一个独立可执行文件,不需要用户装运行时,拷贝到机器上就能跑。
为什么是单文件?
但 OpenCode 源码是 TypeScript,还依赖一堆包,怎么变成单文件原生二进制?靠构建工具的「编译模式」。
构建工具(这里是 Bun)提供「编译(compile)模式」——它把源码 + 依赖 + 运行时全部编译进一个原生二进制:
TypeScript 源码 + 依赖包 + 运行时(Bun) ▼ compile 单文件原生二进制(可执行)
这个二进制里内嵌了运行时,所以用户机器上不需要装任何东西。这就是「单文件、零依赖」的实现方式。
OpenCode 的单文件二进制不止含内核代码,还内嵌了 Web UI 和 workers:
内嵌的做法大致是:构建时先把 Web UI 构建成静态文件,再以「文件导入」形式生成一个虚拟入口,最终编译进二进制。这样二进制跑起来时,Web UI 直接从内部读取,不依赖外部文件。
💡 内嵌的价值:一个二进制就是完整产品——内核 + UI + workers 全在里面。用户下载一个文件,什么都有了。这是极致的分发体验。
一个二进制不够——因为第 3 章说的平台差异(OS×架构×libc×AVX2),要发多个变体。OpenCode 的目标矩阵有 12 个变体:
| 平台 | 变体 |
|---|---|
| Linux | arm64 / x64 / x64-baseline / arm64-musl / x64-musl / x64-musl-baseline |
| macOS | arm64 / x64 / x64-baseline |
| Windows | arm64 / x64 / x64-baseline |
每个变体对应一个平台包。构建脚本会按这个矩阵,为每个变体编译一个二进制:
build --single(当前平台) 或 build(全矩阵,产出 12 个变体)
--single 标志只构建当前平台(开发者本地用);全量构建产出所有 12 个变体(发布用)。
不同变体有些细微差异(如 musl vs glibc),构建时通过变量 define 区分:
这些变量在编译时注入,让同一份源码能编译出适应不同平台特性的二进制。
每个变体编译完后,写一份包描述(package.json),含 os/cpu/libc 字段:
{ "name": "opencode-linux-x64-baseline-musl", "os": ["linux"], "cpu": ["x64"], "libc": ["musl"] }
这些字段让 npm 能按平台选包——用户在 Linux x64 musl 机器上装时,npm 自动选这个包。这和第 3 章的「启动脚本挑包」连成闭环:
构建(本节):产出 12 个带平台描述的变体包 │ ▼ 发布到 npm │ 安装:用户机器上 npm 按平台描述选包 │ 启动(第 3 章):启动脚本探测平台,挑对包执行
构建完不是直接发布,先做冒烟测试——跑一下 --version,确认二进制能正常启动。通过后才发布:
这种「构建 → 冒烟 → 发布」的流程,保证发布的二进制至少能跑起来。
--version→发布,保证能跑。构建讲清了,最后一节讲可观测性——运行起来后怎么看它。