02 平台二进制分发


02 平台二进制分发

本节摘要:第 1 章我们说过,OpenCode 的安装会按你的机器挑一份精确匹配的平台二进制。本节讲清这套「挑选」背后的工程:启动脚本(一个纯 Node 脚本)如何探测操作系统、CPU 架构、C 库(libc)、CPU 指令集(AVX2),然后从一个有序的候选包列表里找到第一个存在的包来执行。这套机制解释了「为什么安装后能直接跑」——它不是魔法,而是精确的平台匹配。

一、启动脚本:纯 Node,不依赖 Bun

先破除一个误解:OpenCode 的启动脚本(命令入口)是纯 Node 脚本,不依赖 Bun。这是个有意的设计——启动脚本要尽可能轻、尽可能兼容,所以用 Node 能跑的最小脚本。它只负责一件事:挑对平台二进制,然后把命令行参数转发给它。

用户敲 opencode 命令 │ ▼ 启动脚本(纯 Node) │ ├─ 探测平台(OS / 架构 / libc / AVX2) ├─ 生成候选包名列表(有序) ├─ 找到第一个存在的包 └─ spawn 那个二进制,转发参数 ​

启动脚本本身跑完后,真正的活儿交给被挑中的平台二进制(那才是 Bun 编译的产物)。

二、四维探测

启动脚本探测四个维度,每个都有具体方法:

1. 操作系统与架构

用 Node 内置的 os.platform() 和 os.arch() 直接拿到。组合有:darwin/linux/windows × x64/arm64/arm。包名前缀就是 opencode-<平台>-<架构>。

2. AVX2 指令集(仅 x64)

AVX2 是一种 SIMD 指令集,OpenCode 在某些本地推理路径上会用它加速。但不是所有 CPU 都支持,所以只对 x64 架构探测:

平台 探测方法
Linux 读 /proc/cpuinfo,正则匹配 avx2 标志
macOS 调 sysctl -n hw.optional.avx2_0
Windows 调 IsProcessorFeaturePresent(40)(40 = AVX2)

💡 为什么只对 x64:arm64 架构的 SIMD 是另一套(NEON),且通常都支持,不需要这种「支持/不支持」的探测。x64 因为历史原因,老 CPU 可能不支持 AVX2,所以必须探测。

3. C 库 libc(仅 Linux)

Linux 有两种主流 C 库:glibc(大多数发行版)和 musl(Alpine 等)。两者编译的二进制不兼容,所以必须区分。探测方法:

  • 检查 /etc/alpine-release 文件是否存在(Alpine 用 musl)。
  • 调 ldd --version 看输出是否含 musl。

4. 候选包名生成

探测完四个维度,生成一个有序的候选包名列表。顺序很关键——最具体的(如 baseline-musl)排前面,最通用的排后面。以「musl + x64 + baseline」为例,候选列表大致是:

1. opencode-linux-x64-baseline-musl ← 最具体,优先 2. opencode-linux-x64-musl 3. opencode-linux-x64-baseline 4. opencode-linux-x64 ← 最通用,兜底 ​

启动脚本从前往后找,用第一个实际存在的包。

三、找包:向上遍历 node_modules

有了候选列表,启动脚本怎么找「哪个包存在」?方法是从当前目录向上遍历 node_modules:

当前目录 └─ node_modules/opencode-linux-x64-baseline-musl ? ← 找 (没有)向上 └─ ../node_modules/... ← 继续找 ... └─ 找到第一个存在的,用它 ​

这种「向上找」保证不管你在项目的哪一层目录启动,都能找到装在顶层的平台包。

⚠️ 环境变量覆盖:有个逃生口——环境变量(如 OPENCODE_BIN_PATH)可以直接指定二进制路径,跳过整个探测。这在调试或特殊部署时有用。

四、为什么搞这么复杂

你可能会问:为什么不直接发一个「通用」二进制?三个理由:

  1. AVX2 不能假设:老 CPU 不支持,硬跑会崩。必须分 AVX2 版和 baseline 版。
  2. libc 不兼容:glibc 和 musl 二进制互不兼容,必须分开。
  3. 架构差异:x64/arm64/arm 指令集不同,必须分。

这些差异是真实的硬件/系统层面差异,没法用一个二进制覆盖。所以 OpenCode 的做法是:发多个平台包,启动脚本精确挑。这是跨平台二进制工具的常见做法,只是 OpenCode 把维度分得更细(尤其加了 AVX2)。

五、这套机制和第 12 章的关系

本节讲的是「挑包」,而「造包」是第 12 章的主题——单文件构建脚本会按一个目标矩阵(共 12 个变体)产出所有平台二进制,每个变体写一份带 os/cpu/libc 字段的包描述,供 npm 按平台分发。挑(本节)和造(第 12 章)是一对:造出 12 个包,挑出 1 个对的。

六、踩坑排查

记住这张表,踩坑时能快速定位:

症状 最可能原因 排查
非法指令(启动即崩) AVX2 版装到不支持 AVX2 的 CPU 让脚本重新探测,或强制用 baseline 版
找不到符号 musl 版装到 glibc 系统或反之 检查 libc 探测,选对 musl/gnu 版
找不到二进制 候选包都没装上 重新装,或用环境变量直接指定路径
架构不匹配 装错架构包(如 x64 装到 arm) 检查架构探测

本节要点回顾

  1. 启动脚本是纯 Node,不依赖 Bun,只负责挑对平台二进制并转发参数。
  2. 四维探测:OS×架构(直接拿)、AVX2(仅 x64,各平台方法不同)、libc(仅 Linux,Alpine/musl 检测)、候选包名(有序,具体在前)。
  3. 找包靠向上遍历 node_modules,用第一个存在的候选;有环境变量逃生口。
  4. 必须分细:AVX2/libc/架构是真实硬件差异,一个通用二进制覆盖不了。
  5. 造包(第 12 章)12 个变体,挑包(本节)1 个对的——挑和造是一对。
  6. 踩坑表:非法指令→AVX2、找不到符号→libc、找不到二进制→重装。

挑包讲完了,下一节讲最常用的「连接枢纽」——serve 模式。


作者与出处
原作者: 灏天文库
来源:anomalyco
许可证:MIT
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: 灏天文库 转发
评论区 (0)
U