在 Chromium 中引入第三方 Rust crate:以 uwuify 为例的完整实战指南
在 Chromium 中引入第三方 Rust crate:以 uwuify 为例的完整实战指南
本篇指南基于 comprehensive-rust 课程中 Chromium 方向的第三道练习(将 uwuify crate 引入 Chromium 并关闭其默认特性),帮助读者掌握在 Chromium 中引入、配置并依赖一个第三方 Rust crate 的完整流程。读完后,你将能够:正确执行 gnrt vendor 拉取 crate 及其传递依赖、理解为何必须关闭默认特性、配置 gnrt_config.toml 的分组与安全约束,并在 BUILD.gn 中以 rust_static_library 正确依赖 :lib 目标。
练习任务:引入 uwuify 并关闭默认特性
课程给出的核心练习是:把 [uwuify][crates.io/crates/uwuify] 加入 Chromium,并关闭该 crate 的默认特性(default features)。题目还有一个关键前提:假设该 crate 会被用于正式发布的(shipping)Chromium,但不会用来处理不可信输入。
# 练习目标(概念性伪代码)
# 在 Cargo.toml / BUILD.gn 中引入 uwuify,
# 并且显式关闭其 default features。
这里有两个必须理解的技术要点:
- 为什么必须关闭默认特性:Rust crate 经常通过
defaultfeature 开启额外的可选功能,而这些功能往往又会引入更多的传递依赖。若不加关闭,你实际拉进 Chromium 的 crate 集合会比预期大得多。题目明确要求"关闭默认特性",正是为了保证引入的依赖集合是受控、最小化的。 - 为什么强调"不处理不可信输入":这关系到后续在
gnrt_config.toml中给 crate 分组时的安全定级(safe/sandbox/test),详见下文"安全分组"小节。
补充:练习中还提示,可以顺手新建一个
rust_executable目标来使用uwuify,或跳到下一道"集成使用"练习。下一道练习(见 bringing-it-together.md)会真正让 Chromium 的 UI 字符串经过uwuify转换,从而构成 "Chrome for Pixies"。
依赖清单:一次正确引入应恰好拉取哪些 crate
根据练习文档给出的标准答案,当你正确关闭默认特性后,uwuify 及其传递依赖的完整集合应当恰好是以下 8 个 crate:
instantlock_apiparking_lotparking_lot_coreredox_syscallscopeguardsmallvecuwuify
这是一个非常有用的自检信号:如果你在 vendor 后发现实际下载的 crate 数量明显超过这 8 个,几乎可以肯定是你忘记关闭默认特性了——默认特性会连带引入一批额外的依赖。把"依赖数量是否符合预期"作为一个可验证的检查项,是引入任何第三方 crate 时都适用的好习惯。
完整引入流程:从下载到依赖
下面把练习中隐含的操作,补全为可落地的完整流程。这些步骤与 adding-third-party-crates 章节中的说明一致。
第 1 步:用 gnrt vendor 下载 crate 及依赖
负责下载 crate 并生成 BUILD.gn 规则的工具有 gnrt:
cd chromium/src
vpython3 tools/crates/run_gnrt.py -- vendor
这个 vendor 命令可能下载三类内容(见 downloading-crates.md):
- 你要引入的 crate(本例是
uwuify); - 它的直接依赖与传递依赖(上节的 8 个 crate);
- 因
cargo需要解析完整依赖集合而带来的其他 crate 的新版本。
安全提示:虽然
gnrt工具本身是 Chromium 源码的一部分,但运行vendor命令时,你实际上是在从 crates.io 下载并运行它的依赖。这是 Chromium 做出的一个明确安全决策,理解这一点有助于评估引入新 crate 的攻击面。
此外,Chromium 会为部分 crate 维护补丁,保存在 //third_party/rust/chromium_crates_io/patches。这些补丁会被自动重新应用;若补丁应用失败,可能需要人工介入。
第 2 步:在集中式 Cargo.toml 中声明依赖并关闭默认特性
Chromium 通过单个集中管理的 Cargo.toml 维护所有直接 crate 依赖,而不是每个 crate 各自为政:
[dependencies]
bitflags = "1"
cfg-if = "1"
cxx = "1"
# lots more...
作为任何 Cargo.toml,你都可以为依赖指定更多细节(见 configuring-cargo-toml.md)。对本题而言,关键就是指定 features——具体来说是关闭 uwuify 的默认特性。用 cargo 的写法表达即:
# 关闭默认特性(示意)
uwuify = { version = "0_2", default-features = false }
注意:上面的
version = "0_2"取值来自课程对 Chromium 目录结构的约定(crate 目录名用v0_2表示 major/minor semver 版本),实际版本号请以你在gnrt_config.toml/Cargo.toml中 vendor 得到的真实版本为准。核心要点始终是default-features = false。
第 3 步:在 gnrt_config.toml 中声明 Chromium 特有的配置
与 Cargo.toml 并列的 gnrt_config.toml 承载 Chromium 专属的 crate 处理扩展。引入新 crate 时,至少需要指定 group,其取值有明确的语义:
# 'safe': The library satisfies the rule-of-2 and can be used in any process.
# 'sandbox': The library does not satisfy the rule-of-2 and must be used in
# a sandboxed process such as the renderer or a utility process.
# 'test': The library is only used in tests.
例如:
[crate.my-new-crate]
group = 'test' # only used in test code
把这三组语义与练习题的前提条件对应起来看,就理解了题目为何反复强调"shipping Chromium,但不处理不可信输入":
safe:满足 rule-of-2,可用于任意进程;sandbox:不满足 rule-of-2,只能用于 renderer / utility 等沙箱进程;test:仅用于测试代码。
对 uwuify 这类"用于正式产品但绝不接触不可信输入"的 crate,需要在审阅其源码后,依据它是否满足 rule-of-2 来判定应归入 safe 还是 sandbox。此外,若 crate 的源码布局特殊,你还可能需要在这个文件里额外指定其 LICENSE 文件的位置。
第 4 步:用 BUILD.gn 依赖该 crate 的 :lib 目标
vendor 完成、构建规则生成后,"依赖某个 crate"就变得很简单:找到你的 rust_static_library 目标,添加一个指向该 crate :lib 目标的 dep。
路径命名约定(见 depending-on-a-crate.md)为:
//third_party/rust/<crate 名>/v<major semver 版本>:lib
示例:
rust_static_library("my_rust_lib") {
crate_root = "lib.rs"
sources = [ "lib.rs" ]
deps = [ "//third_party/rust/example_rust_crate/v1:lib" ]
}
对本题,uwuify 的依赖路径即为 //third_party/rust/uwuify/v0_2:lib(这一写法在下一道"集成"练习的提示中也被明确使用)。
第 5 步:把 crate 检入 Chromium 源码树
git status 应当显示两类变更(见 checking-in.md):
- crate 本体代码,位于
//third_party/rust/chromium_crates_io; - 元数据(
BUILD.gn与README.chromium),位于//third_party/rust/<crate>/<version>。
还需要在后者目录下补一个 OWNERS 文件。把这些变更连同 Cargo.toml 与 gnrt_config.toml 的改动一起提交进 Chromium 仓库。
重点:必须使用 git add -f。否则 .gitignore 可能导致部分文件被跳过。另外,你可能会发现 presubmit 检查因"非包容性语言"而失败——因为 crate 数据常包含 git 分支名,而不少项目仍在使用非包容性术语。必要时可运行:
infra/update_inclusive_language_presubmit_exempt_dirs.sh > infra/inclusive_language_presubmit_exempt_dirs.txt
git add -p infra/inclusive_language_presubmit_exempt_dirs.txt # add whatever changes are yours
为什么"默认特性"是这类练习的关键考点
回到练习的本质:它表面是"加一个 crate",真正考查的是对 Rust 生态依赖特性的理解。Rust crate 之间依赖彼此非常容易,因此传递依赖常常很多(见 adding-third-party-crates.md 中的对比表:C++ 库通常传递依赖少、体积偏大;Rust crate 则构建系统统一为 Cargo.toml、体积小、但传递依赖多)。
在 Chromium 这种对依赖集合有严格审计与供应链管控的项目里,"关闭默认特性"不只是一个构建技巧,而是控制攻击面与依赖膨胀的第一道闸门。配合 gnrt_config.toml 的 group 安全分组、OWNERS 责任归属、以及对 crate 源码的安全性审阅(safety audit),共同构成了一套可审计、可追责的第三方引入规范。
小结与可验证检查清单
引入 uwuify(或任意第三方 crate)到 Chromium,可落地为以下清单,并附可验证依据:
- 执行
vendor:vpython3 tools/crates/run_gnrt.py -- vendor,拉取 crate、传递依赖及必要的新版本(downloading-crates.md)。 - 关闭默认特性:在集中式
Cargo.toml中以default-features = false声明,确保最终依赖集合恰好为instant、lock_api、parking_lot、parking_lot_core、redox_syscall、scopeguard、smallvec、uwuify共 8 个 crate。若数量超过,说明默认特性未关闭。 - 声明分组:在
gnrt_config.toml中按 rule-of-2 与使用场景指定group(safe/sandbox/test)。 - 声明依赖:在
rust_static_library的deps中加入//third_party/rust/uwuify/v0_2:lib。 - 检入仓库:
git add -f提交 crate 代码与元数据,补齐OWNERS,按需处理包容性语言的 presubmit 豁免。
完成本练习后,即可衔接下一道"集成使用"练习(bringing-it-together.md),把 uwuify 真正接进 Chromium 的本地化字符串处理,完成 "Chrome for Pixies" 的端到端实现。