news 2026/9/7 18:56:49

codex-rs 的 Bazel 构建体系:codex_rust_crate 宏、Bzlmod 工具链与 BuildBuddy 远程执行实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
codex-rs 的 Bazel 构建体系:codex_rust_crate 宏、Bzlmod 工具链与 BuildBuddy 远程执行实战

codex-rs 的 Bazel 构建体系:codex_rust_crate 宏、Bzlmod 工具链与 BuildBuddy 远程执行实战

【免费下载链接】openinterpreterA coding agent for open models like Kimi K3 and GLM 5.3项目地址: https://gitcode.com/GitHub_Trending/op/openinterpreter

本篇基于仓库内的 Bazel 构建文档 展开,讲清楚 codex-rs Rust 工作区如何用 Bazel 实现 hermetic(可复现、自包含)构建:MODULE.bazel如何声明工具链、rules_rs如何从Cargo.toml/Cargo.lock导入第三方 crate、defs.bzl中的codex_rust_crate宏如何把 Bazel 目标与 Cargo 约定对齐,以及如何通过 BuildBuddy 配置启用远程缓存与远程执行。读完后,你应能独立完成本地 Bazel 构建与测试、理解各--config的作用边界,并知道新增依赖、新增 crate 时的完整操作流程。

需要先说明适用前提:文档中注明,截至 2026-06-01,该 Bazel 体系仍处于实验阶段,正在持续稳定化。日常开发仍可以 Cargo 为主,Bazel 主要用于 CI、跨平台产物和可复现构建。

核心定位:Cargo 是事实来源,Bazel 是构建执行层

原文明确了这一分工原则:

This repository uses Bazel to build the Rust workspace undercodex-rs. Cargo remains the source of truth for crates and features, while Bazel provides hermetic builds, toolchains, and cross-platform artifacts.

这意味着:

  • 依赖声明、feature、crate 划分全部以 codex-rs/Cargo.toml 和 codex-rs/Cargo.lock 为准;
  • Bazel 负责的是构建可复现性:版本钉死的 Rust 工具链、自包含(hermetic)的 LLVM、macOS SDK 等,以及跨平台交叉编译产物(包括 Windows gnullvm 这种 Cargo 原生不直接支持的 ABI);
  • 因此你几乎不需要为 Bazel 维护一套并行的依赖清单——这正是后文crate.from_cargo(...)要解决的问题。

高层结构:四个关键构件

原文给出了四段式结构,下面逐一结合仓库中的实际文件展开。

1.MODULE.bazel:Bzlmod 依赖与工具链声明

根目录 MODULE.bazel 声明了整个仓库的 Bazel 模块(模块名为codex),承担三类职责:

(1)外部模块依赖。关键依赖包括rules_rs 0.0.96(新一代 Rust 构建规则,见 MODULE.bazel#L93)、llvm 0.8.11(hermetic LLVM,见 MODULE.bazel#L5)、apple_supportaws-lc等。

(2)上游模块打补丁。由于 hermetic LLVM 需要一些尚未上游化的定制(例如 V8 所需的 custom libc++、Windows gnullvm 运行时),仓库通过single_version_override+patchesllvmabseil-cpprules_ccbzip2等模块打补丁,补丁文件集中在 patches/ 目录下(如 patches/llvm_rusty_v8_custom_libcxx.patch)。对rules_rust本身也打了三个补丁(build-script 工具 runfiles、Windows/MSVC 直接链接参数、Windows process wrapper),见 MODULE.bazel#L103-L117。

(3)工具链注册。包括:

  • macOS SDK 归档下载与 framework 白名单(@llvm//extensions:osx.bzl,见 MODULE.bazel#L31-L78);
  • 默认 Rust 工具链:edition = "2024"version = "1.95.0"(MODULE.bazel#L190-L197);
  • 一个独立的 nightly 工具链(nightly/2025-09-18dev_components = True),专门服务于需要rustc_private的 argument-comment-lint(MODULE.bazel#L123-L131);
  • Windows 双 ABI 支持:MSVC 与 gnullvm 两套repository_set(MODULE.bazel#L135-L188)。

本地 Bazel 版本由 .bazelversion 钉在9.0.0

2.rules_rscrate.from_cargo(...):从 Cargo 元数据导入第三方 crate

MODULE.bazel#L209-L228 展示了原文提到的核心机制:

crate = use_extension("@rules_rs//rs:extensions.bzl", "crate") crate.from_cargo( cargo_lock = "//codex-rs:Cargo.lock", cargo_toml = "//codex-rs:Cargo.toml", platform_triples = [ "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", "aarch64-apple-darwin", "aarch64-pc-windows-msvc", "aarch64-pc-windows-gnullvm", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "x86_64-pc-windows-gnullvm", ], )

crate.from_cargo直接解析 Cargo 工作区的Cargo.tomlCargo.lock,为每个第三方 crate 生成 Bazel 目标并统一暴露在@crates仓库下。platform_triples列表同时列出了 9 种目标 triple(含 Linux musl、macOS、Windows MSVC 与 gnullvm),这正是"跨平台产物"能力的来源——同一次依赖解析覆盖了所有交叉编译目标。注释中还解释了为何同时保留 MSVC 与 gnullvm 两个 Windows triple:V8 实验仍在消费仅以 MSVC 命名发布的 release 资产。

对于需要"特殊照顾"的上游 crate,仓库用crate.annotation做定点修正,例如:

  • blake3:在 Windows gnullvm 上强制启用purefeature,因为该工具链无法可靠地生成其 x86 原生汇编(MODULE.bazel#L249-L255);
  • zstd-sysring:打补丁修正 MSVC 头文件搜索路径(MODULE.bazel#L256-L270);
  • aws-lc-sys/aws-lc-rs:关闭 build script,改用预生成 bindgen 产物(MODULE.bazel#L271-L284)。

这就是原文"Evolving the setup"一节所说"上游 crate 可能需要 patch 或crate.annotation才能在 Bazel 沙箱中构建"的具体形态。

3.defs.bzlcodex_rust_crate:与 Cargo 约定对齐的宏

根目录 defs.bzl 提供codex_rust_crate宏(defs.bzl#L181),封装rust_libraryrust_binaryrust_test,让 Bazel 目标与 Cargo 的目录约定一一对应。原文说它"提供了对大多数一方 crate 合理的默认值,但某些情况下需要微调"。

从宏的参数列表(defs.bazel#L181-L206)可以看到它的覆盖面:

参数作用
crate_name/crate_features/crate_edition对应Cargo.toml中的名称、feature、edition。宏文档特别提示 feature 会在整个工作区以单一配置编译(列表内全部启用),应慎用
build_script_data暴露给build.rs运行时使用的数据文件
compile_data/lib_data_extra库目标的编译期数据 / 运行期数据
rustc_flags_extra/rustc_env追加 rustc 参数与环境变量(宏默认注入BAZEL_PACKAGE=<包名>,见 defs.bazel#L282-L284)
integration_test_args/unit_test_args/*_timeout集成/单元测试参数与超时
test_shard_counts按测试名映射的分片数,启用 Bazel 原生分片并标记 flaky(三次重试)
test_tags传给单元 + 集成测试目标,典型用途是no-sandbox
extra_binaries/extra_binaries_non_windows把别的 crate 的二进制暴露为测试数据与CARGO_BIN_EXE_*环境变量
run_tests_with_wine_exec为每个集成测试生成 Wine 执行变体(在 Linux 上跑交叉编译的 Windows exec-server)

宏内部的工作方式(均可在源码中验证):

  • 构建脚本:若存在build.rs,生成<name>-build-script目标(cargo_build_script规则,defs.bazel#L297-L305);
  • src/**/*.rs非空时生成rust_library(proc-macro crate 用rust_proc_macro),并同步生成单元测试目标(defs.bazel#L309-L371);
  • 二进制:按Cargo.toml解析出的binaries表逐一生成rust_binary,并导出CARGO_BIN_EXE_<name>环境变量供集成测试使用——完整复刻了 Cargo 的行为(defs.bazel#L376-L391);
  • 集成测试tests/*.rs每个文件生成一个测试目标;源码注释(defs.bazel#L497-L513)明确列出四种生成形态:非分片原生测试、分片原生测试(拆成 manual 的rust_test+ 外层workspace_root_test)、Windows 交叉测试、Wine 执行测试。

其中workspace_root_test(defs.bazel#L142-L179)是一个自研规则,通过 workspace_root_test_launcher.sh.tpl / workspace_root_test_launcher.bat.tpl 生成跨平台启动器:它在运行时解析出真实的仓库根目录、cd进去,并把 runfiles 路径改写为绝对路径。这是为了让insta快照测试看到 Cargo 风格的相对路径(配合INSTA_WORKSPACE_ROOT/INSTA_SNAPSHOT_PATH环境变量,defs.bazel#L260-L266),同时兼容仓库使用--noenable_runfiles(manifest-only runfiles)的策略。

另一个细节:测试目标的rustc_flags中统一加入了--remap-path-prefix=../codex-rs=--remap-path-prefix=codex-rs=(defs.bazel#L526-L532),因为 Bazel 曾对file!()宏产生两种不同的路径前缀,剥掉后 insta 快照元数据才与 Cargo 构建一致。

4. 每个 crate 的BUILD.bazel

各 crate 目录下的BUILD.bazel通常只是调用codex_rust_crate并做少量调整。最简单的形态见 codex-rs/code-mode-host/BUILD.bazel:

load("//:defs.bzl", "codex_rust_crate") codex_rust_crate( name = "code-mode-host", crate_name = "codex_code_mode_host", )

当 crate 需要额外的编译期/运行期数据、特殊环境变量或测试定制时,再按宏的参数列表补充。

本地运行 Bazel:justfile 入口

仓库根目录 justfile 暴露了常用入口(该文件的工作目录为codex-rs):

just bazel-test just bazel-clippy

这两个 recipe 的实际展开(justfile#L158-L164):

# bazel-test bazel test --test_tag_filters=-argument-comment-lint //... --keep_going # bazel-clippy bazel_targets="$(scripts/list-bazel-clippy-targets.sh)" && bazel build --config=clippy -- ${bazel_targets}

即:测试全仓库目标并排除 nightly-only 的 argument-comment-lint 标签;clippy 通过--config=clippy用 rules_rust 的 Clippy aspect 对目标做检查。此外还有几个直接可用的 recipe:

  • just bazel-codexbazel run //codex-rs/cli:codex,在 Bazel 构建产物上运行 CLI(justfile#L128-L135);
  • just bazel-lock-update/just bazel-lock-check:见下文"演进"一节。

一个重要的边界事实(原文也强调了):普通的本地bazeljust调用全部在本地执行;BuildBuddy 缓存、构建事件上报(BES)、远程下载与远程执行都是 opt-in 配置,不选--config=buildbuddy-*就不会接触任何远程服务。

BuildBuddy:远程缓存与远程执行

codex-rs 的 CI 与内部构建通过 BuildBuddy 做共享缓存和远程构建/测试。要提速,需要两件事:提供 API key,并选择一个配置。

API key 配置

按 BuildBuddy 的认证文档创建 key 后,加入~/.bazelrc

# Local machine only; this file contains a BuildBuddy credential. common --remote_header=x-buildbuddy-api-key=<your-buildbuddy-api-key>

把凭据放在工作区外可以降低误提交的概率。如果不同项目需要不同的 key,放到%workspace%/user.bazelrc——仓库的 .bazelrc 末尾以try-import %workspace%/user.bazelrc可选导入该文件(见 .bazelrc#L225),且 .gitignore 已将user.bazelrc排除在版本控制之外。切记不要提交或分享含凭据的文件。

选择远程构建配置

外部用户应使用buildbuddy-generic-rbebuildbuddy-generic;OpenAI 内部用户默认buildbuddy-openai-rbe。把配置写入%workspace%/user.bazelrc

common --config=buildbuddy-openai-rbe

这组配置在 .bazelrc 中有明确定义(文件注释说明:这些配置"只有在用户显式选择时才会接触 BuildBuddy"):

  • buildbuddy-generic:cache/BES/下载均指向remote.buildbuddy.io,无远程执行;
  • buildbuddy-generic-rbe:在上一项基础上叠加--config=remote,使用remote.buildbuddy.io远程执行器;
  • buildbuddy-openai/buildbuddy-openai-rbe:同样的两级结构,但指向openai.buildbuddy.io

--config=remote本体设置--strategy=remote--extra_execution_platforms=//:rbe并把并发提到--jobs=800。而//:rbe平台由 rbe.bzl 生成,其container-imageexec property 钉死了含 git/python3/dotslash 等测试依赖的 Ubuntu 镜像(按主机架构选 x86_64 或 aarch64 镜像及 sha256),保证远程执行环境一致。

各配置的完整对照表(原文表格)

调用/配置需要 keyCache/BES构建执行测试执行
bazel ...本地本地
bazel ... --config=buildbuddy-genericremote.buildbuddy.io本地本地
bazel ... --config=buildbuddy-generic-rberemote.buildbuddy.io远程远程
bazel ... --config=buildbuddy-openaiopenai.buildbuddy.io本地本地
bazel ... --config=buildbuddy-openai-rbeopenai.buildbuddy.io远程远程

Cache/BES主机同时用于远程下载(--experimental_remote_downloader)。

CI 侧:租户选择由统一 wrapper 完成

GitHub Actions 通过 .github/scripts/run_bazel_with_buildbuddy.py 路由所有 Bazel 构建与输出解析命令;更高层的辅助脚本(如 .github/scripts/run-bazel-ci.sh、.github/scripts/rusty_v8_bazel.py)都把远程配置选择委托给它。wrapper 的设计要点(原文描述,代码可印证):

  • 它读取 GitHub Actions 的仓库与事件载荷来决定租户,而不是让每个 workflow 文件复制租户选择逻辑;
  • 它归一化 Bazel 启动选项,让同一 job 内的所有 Bazel 调用复用同一个 server 和内存中的分析缓存(见脚本中startup_args函数,.github/scripts/run_bazel_with_buildbuddy.py#L23-L33);
  • 加载阶段的bazel query目标发现命令在本地运行,因为它只枚举 label,不需要远程缓存或执行;
  • 没有 API key 时,wrapper 会剥离远程 CI 配置、退回本地运行;pull request 事件载荷缺失或畸形时"fail closed"到 generic 主机;
  • 只有在 GitHub Actions 中、且是受信运行(openai/codex仓库内的 push/dispatch/同仓 PR)才选择 OpenAI 主机;fork 的 PR 一律本地运行。

CI 各配置与执行位置的对应关系(原文表格):

CI 配置远程配置构建执行测试执行
ci-linux*-rbe远程主机远程主机
ci-v8*-rbe远程主机远程主机
ci-macos*-rbe远程主机本地
ci-windows-cross*-rbe远程主机本地
ci-windows非 RBE本地本地
无 key 的 CI 回退本地本地

这些ci-*配置同样定义在 .bazelrc 中,并各有明确注释:ci-linux可全量远程构建/测试(覆盖 x86 与 arm runner);ci-macos构建远程化、测试留在本地(--strategy=TestRunner=darwin-sandbox,local);ci-windows-cross用 Linux 远程执行构建 Windows gnullvm 二进制、测试留在 Windows runner 以保留 Bazel 分片与 flaky 重试,并强制 V8 的mksnapshot在本地执行(因为 Windows 快照必须由 Windows 的 mksnapshot 二进制生成)。wrapper 源码中也维护了"哪些 CI 配置需要远程构建执行"的映射(ci-linuxci-macosci-v8ci-windows-cross,见 .github/scripts/run_bazel_with_buildbuddy.py#L13-L18)。

若想在本地验证 generic 远程配置:

BUILDBUDDY_API_KEY=... GITHUB_REPOSITORY=my-fork/codex \ ./.github/scripts/run_bazel_with_buildbuddy.py \ build --config=ci-linux //codex-rs/cli:codex

演进构建体系:改依赖、加 crate 的标准流程

更新依赖后刷新 Bzlmod 锁文件

改动Cargo.toml/Cargo.lock后,在仓库根目录运行:

just bazel-lock-update

它执行bazel mod deps --lockfile_mode=update(justfile#L146-L147),按需更新 MODULE.bazel.lock。要把锁文件变更和 Cargo 锁文件一起提交。

本地验证锁文件对齐(与 CI 相同检查):

just bazel-lock-check

该 recipe 指向 scripts/check-module-bazel-lock.sh,脚本内部调用同一个 BuildBuddy wrapper 执行bazel mod deps --lockfile_mode=error,失败时会明确提示运行just bazel-lock-update并提交锁文件。

如果某个上游 crate 无法在 Bazel 沙箱中构建、或需要做交叉编译适配,正确姿势是给它打 patch 或在MODULE.bazel中加crate.annotation(前文blake3/zstd-sys/ring即是现成范例),而不是改 crate 源码。

新增 crate / binary 的三步流程

  1. 像往常一样把 crate 加入 Cargo 工作区;
  2. 创建BUILD.bazel并调用codex_rust_crate(参考相邻 crate,如 codex-rs/code-mode-host/BUILD.bazel);
  3. 如果依赖需要特殊处理(编译期/运行期数据、集成测试附加二进制、环境变量等),调整codex_rust_crate参数。

原文特别提到一个常见定制:把test_tags = ["no-sandbox"]加给测试目标,让测试在无沙箱模式下运行。文档建议尽量避免,因为它绕过了沙箱隔离;典型必要场景是测试本身使用 Seatbelt——Bazel 沙箱在 macOS 上也是 Seatbelt 实现,而 Seatbelt 不能嵌套。为进一步限制影响面,可以把这类测试隔离到独立 crate。

顺带一提:Bazel 侧的 lint 与测试基础设施

  • just bazel-clippy使用的--config=clippy会启用rust_clippy_aspect,且因为 rules_rust 不读取 Cargo 的 lint 级别,.bazelrc 里手工维护了一份与codex-rs/Cargo.toml[workspace.lints.clippy]对齐的 deny 列表;
  • 测试环境默认RUST_MIN_STACK=8388608(8 MiB),因为 Rust libtest 在 Windows 上以 std-spawned 线程跑测试体,默认 2 MiB 栈对大型 async 测试 future 不够;
  • 仓库采用--noenable_runfiles(manifest-only runfiles)策略,这也是workspace_root_test启动器存在的直接原因之一。

参考与延伸阅读

原文给出的外部参考为 Bazel 官方文档(Bazel 概览与 Bzlmod 模块系统)、rules_rustrules_rs两个规则项目的仓库,此处不再重复外链,可分别在对应官方渠道检索。仓库内的深入阅读路径:

  • 文档本身:codex-rs/docs/bazel.md
  • 模块与工具链声明:MODULE.bazel、MODULE.bazel.lock
  • 宏实现:defs.bzl(codex_rust_crate于 L181 起,workspace_root_test规则于 L142 起)
  • 平台定义(含//:rbe、Windows gnullvm/MSVC 平台):BUILD.bazel、rbe.bzl
  • 全局 Bazel 配置与 BuildBuddy/CI 配置:.bazelrc
  • CI wrapper 及其租户选择逻辑:.github/scripts/run_bazel_with_buildbuddy.py
  • 上游补丁集:patches/

小结:codex-rs 的 Bazel 体系本质上是"Cargo 管声明、Bazel 管执行"的双轨制——crate.from_cargo消除依赖清单的双写,codex_rust_crate消除构建目标的重复描述,MODULE.bazel+patches/保证工具链与上游 crate 的可复现性,BuildBuddy 各--config则以 opt-in 方式提供从纯缓存到全远程执行的分级加速。本地开发用just bazel-test/just bazel-clippy即可起步,新增依赖后记住just bazel-lock-update并提交锁文件,就能跟上这套仍在演进中的构建体系。

【免费下载链接】openinterpreterA coding agent for open models like Kimi K3 and GLM 5.3项目地址: https://gitcode.com/GitHub_Trending/op/openinterpreter

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 18:56:41

小说人设总崩?从设定到剧情一致性的管理方法

"主角写着写着变了一个人"——这是长篇写作最常见的翻车现场。开篇冷静理智的主角&#xff0c;写到五十章突然无脑冲动&#xff1b;配角前期的重要性格&#xff0c;后文完全消失。人设崩塌的本质不是"你不会写人物"&#xff0c;而是"你没管好人物设定…

作者头像 李华
网站建设 2026/9/7 18:55:45

数据库方向考研调剂全攻略:从信息解读到技术准备与导师沟通

每年三四月份调剂系统开放前后&#xff0c;各种招生信息在考研群和导师朋友圈里刷屏。我自己当年也经历过那种感觉&#xff1a;一边盯着研招网&#xff0c;一边翻遍导师主页和课题组论文&#xff0c;连一条调剂通知里的措辞都要反复琢磨好几遍。回头再看&#xff0c;调剂这件事…

作者头像 李华
网站建设 2026/9/7 18:54:35

生成式 AI 应用生命周期:从 MLOps 到 LLMOps 的工程范式

生成式 AI 应用生命周期&#xff1a;从 MLOps 到 LLMOps 的工程范式 【免费下载链接】generative-ai-for-beginners 21 Lessons, Get Started Building with Generative AI 项目地址: https://gitcode.com/GitHub_Trending/ge/generative-ai-for-beginners 生成式 AI&a…

作者头像 李华
网站建设 2026/9/7 18:54:14

Spring Boot明星周边商城系统实战:从项目拆解到核心模块

最近总有同学拿着这个项目来找我答疑——Spring Boot 明星周边商城系统&#xff0c;项目编号是 au72407e。乍一看这就是个典型的 Java Web 课程设计或毕业设计题目&#xff0c;但真把它拆开看&#xff0c;里面藏的东西其实不少&#xff1a;商品管理、购物车、订单流转、库存扣减…

作者头像 李华