news 2026/9/19 23:49:33

pnpm 对 Cargo `[patch]` / `[replace]` 源覆盖的支持:为 Rust 工作区生成并保留 Cargo.lock

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
pnpm 对 Cargo `[patch]` / `[replace]` 源覆盖的支持:为 Rust 工作区生成并保留 Cargo.lock

pnpm 对 Cargo[patch]/[replace]源覆盖的支持:为 Rust 工作区生成并保留 Cargo.lock

【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm

pnpm 的 Cargo 依赖支持(crate:依赖、cargo.enabled工作区)此前无法正确处理带有路径(path)或 Git 来源的[patch][replace]源覆盖的 Rust 工作区。本次功能更新(见 变更记录)让pnpm install可以为这类工作区正确生成Cargo.lock,并在添加、删除、更新 crate 时保留覆盖配置;同时,Cargo lockfile 解析会提前阻断由传递依赖声明的、pnpm 不支持的 Git 传输助手(transport helper),避免解析过程执行不可信的可执行文件。读完本文,你将掌握 pnpm 在何种条件下接管 Cargo 解析、源覆盖如何被检测与校验、以及这一特性背后的实现与测试证据。

背景:pnpm 如何参与 Cargo 依赖解析

pnpm 在启用cargo.enabled: true的 workspace(见 pnpm-workspace.yaml 配置)中,可以用crate:extra@1这类说明符为 Cargo 清单添加 Rust crate 依赖(见 add.rs)。当缺少Cargo.lock时,pnpm 需要负责解析依赖并生成锁文件。其解析策略集中在 lockfile.rs 的read_or_resolve_lockfile

  1. 若锁文件已存在且策略为复用(UseExisting),直接读取;
  2. 若配置了--frozen-lockfile而锁文件缺失,直接报错,绝不自行生成;
  3. 否则调用cargo metadata并检测工作区是否含有Git 依赖source overrides[patch]/[replace]);
  4. 若命中,则放弃 pnpm 自研的稀疏索引解析,转交真实的cargo generate-lockfile来完成解析;
  5. 否则优先走 pnpr 服务端解析(resolve_via_pnpr),最后回退到 pnpm 内置的 cargo-resolver。

也就是说,source overrides 与 git 依赖是触发「委托 Cargo 本体解析」的两类关键信号,这正是本次变更的核心逻辑。

理解 Cargo 的[patch][replace]源覆盖

Rust 生态中有两种在清单层面替换依赖来源的机制:

  • [patch.crates-io](推荐):把某个来自 crates.io 的 crate 覆盖为本地路径或 Git 仓库,例如:
    [package] name = "app" version = "0.1.0" [dependencies] demo = "0" [patch.crates-io] demo = { git = "https://example.com/demo", rev = "abc123" } # 或 demo = { path = "dep" }
  • [replace](遗留机制):以精确的"名称:版本"键指定被替换的 crate:
    [replace] "demo:0.0.0" = { path = "dep" } # 或 "demo:1.0.0" = { git = "https://example.com/demo" }

两者都要求解析器具备与常规 registry 依赖不同的来源处理能力,因此此前 pnpm 无法为这类工作区生成锁文件。

检测逻辑:has_source_overrides如何识别覆盖

在 resolution.rs 的has_source_overrides中,pnpm 读取工作区根目录的Cargo.toml并将其解析为 TOML 表:

  • 遍历[patch]下的每一个子表(如[patch.crates-io]);
  • 检查顶层[replace]表;
  • 只要任一覆盖表非空,即判定工作区存在 source overrides(无论覆盖来源是 path 还是 git)。

这一判定与解析解耦:判定只需要清单文本,不触发网络请求;只有判定命中后,后续cargo generate-lockfile才会真正联网解析。

覆盖来源的 Git 传输校验

对于每个声明了git = "..."的覆盖项,has_override_sources会构造git+<url>形式的SourceId并调用git::validate_transport(见 git.rs):仅当 URL scheme 属于SUPPORTED_GIT_PROTOCOLS(来自 pnpm-git-fetcher crate)时才放行,否则报错:

Cargo source <url> asks for the <scheme> transport, which pnpm does not fetch a git dependency over

这保证了在 Cargo 运行之前ext://ssh://等 pnpm 不支持的传输协议就会被拦截,而不是等到解析中途失败或触发意外执行。

委托解析:resolve_with_cargo的隔离与安全

当检测到 source overrides 或 git 依赖时,resolve_with_cargo会:

  1. 强制默认 registry:若配置了自定义cargo.indexUrl(非 crates.io),直接拒绝解析并提示「Resolving Cargo git dependencies or source overrides requires the default cargo.indexUrl. Use an existing Cargo.lock with a custom Cargo registry.」——自定义 registry 场景只能配合既有锁文件使用;
  2. 使用工具链 sysroot 中的cargo二进制(而非 PATH 上任意 cargo),以--manifest-path指向工作区Cargo.toml执行cargo generate-lockfile(见 resolution_command);
  3. 通过GIT_ALLOW_PROTOCOL环境变量把 Git 传输白名单(来自pnpm_git_fetcher::read_allowed_git_protocols)传递给 Cargo,使子进程的 git 抓取同样受控;
  4. .cargo/config/.cargo/config.toml中的unstable.bindepsresolver.incompatible-rust-versions两项经校验后以--config key=value显式传入(见 resolution_settings),其余配置(含[env]git-fetch-with-cli、凭据提供器)一律不进入解析进程,防止 checkout 目录中不可信的可执行助手被执行;
  5. 支持--offline透传。

解析完成后,pnpm 读取工作区新生成的Cargo.lock作为结果。生成流程结束后,覆盖配置仍原样保留在Cargo.toml中。

覆盖在 add / remove / update 场景下的保留

变更记录承诺「Adding, removing, and updating crates also preserve these overrides」。在 cargo_git_install.rs 集成测试 中可验证该行为:

  • adding_a_registry_crate_preserves_git_dependencies_and_source_overrides(第 213 行):对 git 依赖、git patch、path patch 三种工作区执行pnpm add crate:extra@1 --lockfile-only后,新锁文件既包含新增的extra(含正确 checksum),又保留原有的demo(以及sibling)git 依赖,且.cargo/config.toml不被改写;
  • lockfile_generation_preserves_legacy_path_replacements(第 357 行):针对遗留[replace]语法,解析后锁文件同时保留被替换包(带 source)与替换包(无 source、带replace标记)的独立身份,cargo check --locked --offline可成功通过;
  • failed_resolution_restores_manifest_lockfile_and_sources(第 280 行):当解析失败(如新增的 crate 在 registry 中 404)时,Cargo.tomlCargo.lock.cargo/config.toml三者全部回滚到原状,保证失败不污染工作区。

此外,pnpm removepnpm update复用同一套cargo generate-lockfile管线,因此覆盖信息天然随Cargo.toml保留,解析结果只反映清单当前的覆盖声明。

阻止传递依赖声明的 Git 传输助手

变更记录的后半句针对的是安全边界:传递依赖(transitive dependencies)通过Cargo.toml声明的 Git 传输协议必须受控。实现上做了两道防线:

  1. 解析入口校验has_git_dependencies(resolution.rs 第 7-13 行)提取元数据中所有 git 依赖来源并对每个来源执行validate_transport
  2. 执行期环境收紧:解析子进程工作目录设在工具链 sysroot,避免读取 checkout 目录中的配置;同时仅放行GIT_ALLOW_PROTOCOL白名单中的协议。

对应测试path_patched_dependencies_cannot_execute_git_transport_helpers(第 424 行):一个 path 覆盖的本地依赖里声明helper = { git = "pnpm-test://invalid.example/repository" },并在 PATH 上放置伪造的git-remote-pnpm-test助手;执行pnpm install --lockfile-only后,安装失败且助手从未被执行,报错包含transport 'pnpm-test' not allowed

锁文件复用场景下的 Git 包落地

若锁文件已存在(--frozen-lockfile),pnpm 不再解析,而是直接解析锁文件中的 git 包并落地到 store:git 包以git-submodules-v1-<commit>作为 slot 标识(见 git.rs 的store_slot),通过生成[source."git+<url>..."]替换块与.cargo-checksum.json(package 字段为 null)的方式,让 Cargo 以目录源形式直接使用被钉住的 commit,无需再次抓取(见 git.rs 模块注释)。frozen_install_vendors_versionless_git_members_and_path_dependencies(第 100 行)验证了冻结安装下版本号缺失的 git 成员与 path 依赖都能被正确落地且配置不被篡改。

单元测试佐证:覆盖检测的判定矩阵

resolution/tests.rs 的root_source_overrides_are_detected_and_git_transports_are_validated给出了精确的判定矩阵:

Cargo.toml 片段has_source_overrides结果
[workspace]false
[patch]/ 空[patch.crates-io]/ 空[replace]false
[patch.crates-io]+ 无 git 的其它注册表false
[patch.crates-io] demo = { path = "dep" }true
[patch."https://example.test/index"] demo = { git = ..., rev = ... }true
[replace] "demo:1.0.0" = { path = "dep" }true
覆盖使用git = "ext://..."报错(transport 校验失败)

空表判定为 false 是刻意设计:无实际覆盖项时无需委托 Cargo,可继续走 pnpm 自身更快的解析路径。

使用前提与限制小结

  • 需要可用的 Cargo 工具链(cargo/rustc可从rustc --print sysroot定位);
  • Git 覆盖要求配置cargo.indexUrl为默认的 crates.io;自定义 registry 场景请使用既有Cargo.lock
  • 传递依赖声明的传输协议必须落在 pnpm 支持的白名单内(如httpsfilegit等),ext://等自定义传输会被拒绝;
  • --frozen-lockfile下锁文件缺失时不会生成,而是直接报错「forbids generating」;
  • 解析失败时工作区文件(Cargo.tomlCargo.lock.cargo/config.toml)会自动回滚。

综上,本次更新让 pnpm 在 Rust 工作区中真正补齐了「覆盖来源」场景的锁文件生成、保留与安全校验能力,使其与常规 npm 依赖安装一样具备可重复、可冻结、可回滚的安装语义。

【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm

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

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

台式机耳机没声音?从物理层到系统层的精准排查指南

1. 问题本质与真实场景还原&#xff1a;这不是“插上就响”的简单事“台式机插耳机听不到”——这七个字背后&#xff0c;藏着至少六种完全不同的故障层级&#xff0c;而绝大多数人一上来就猛点音量图标、狂按F键、反复拔插耳机&#xff0c;结果折腾半小时&#xff0c;问题还在…

作者头像 李华
网站建设 2026/9/19 23:44:02

别再“硬写”课程论文了:书匠策AI教你换个姿势过这关

官网&#xff1a;www.shujiangce.com | 微信 公众号 &#xff1a;书匠策AI 课程论文这东西&#xff0c;说难不难&#xff0c;说简单也绝对不简单。 它不像毕业论文那样要你憋出几万字的大工程&#xff0c;但它烦人的地方在于&#xff1a;你明明知道它不是学术生涯的巅峰之…

作者头像 李华