news 2026/9/19 20:20:44

pnpm 原生支持 Rust Cargo Git 依赖:省略包版本与 Cargo.lock 自动生成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
pnpm 原生支持 Rust Cargo Git 依赖:省略包版本与 Cargo.lock 自动生成
  • 包管理器
  • 开发工具
  • CLI

【免费下载链接】pnpm

Fast, disk space efficient package manager

项目地址:https://gitcode.com/gh_mirrors/pn/pnpm
点击查看免费下载

导读

pnpm 的 Rust 实现(pnpm/目录下的 Cargo workspace)为 Cargo 生态引入了一套与cargo行为对齐的依赖解析能力。本文基于 fix-cargo-git-workspaces.md 记录的两项变更展开:pnpm install现在可以接受省略包版本号的 Rust git 依赖,并为含有 git 依赖的 Cargo workspace 自动生成 Cargo.lock 锁文件。读完本文,你将掌握这两项能力的适用场景、底层解析流程(基于 PubGrub 求解器与 cargo-lock 序列化),以及仓库中对应的源码与测试验证位置。


一、背景:pnpm 的 Cargo 兼容依赖解析器

pnpm 主仓库同时维护着 TypeScript 版实现(pnpm11/)与 Rust 原生实现(pnpm/)。在 Rust 侧,cargo-resolvercrate(见 pnpm/crates/cargo-resolver/Cargo.toml)承担了"Cargo 兼容依赖解析"职责,其依赖选型直接体现了实现路径:

依赖作用
cargo-lock解析与序列化Cargo.lock文件(v4 格式)
cargo-util-schemas复用 Cargo 官方对稀疏索引条目(sparse index entry)的 schema
pubgrub通用依赖求解器,负责版本选择与回溯
semver版本号与版本区间(VersionReq)的解析匹配
serde/serde_json解析cargo metadata输出的 JSON

crate 的公开 API 集中在 src/lib.rs:

pub use metadata::{git_dependency_sources, resolve_inputs}; pub use registry::{CRATES_IO_SOURCE, ...}; pub use resolution::{missing_index_names, resolve_lockfile};

其中git_dependency_sources正是本次 changeset 所涉功能的核心入口之一。


二、核心变更一:接受省略包版本的 Rust Git 依赖

2.1 变更含义

changeset 原文:

pnpm installnow accepts Rust git dependencies that omit a package version.

在 Cargo 语法中,git 依赖通常写作:

[dependencies] some-crate = { git = "https://github.com/example/some-crate", rev = "abc123" }

此时依赖不携带版本号。过去 pnpm 的解析路径可能因此失败;本次变更后,这类依赖可以被正确识别并纳入解析流程,无需用户手动补充version字段。

2.2 源码实现:git_dependency_sources

在 pnpm/crates/cargo-resolver/src/metadata.rs#L12-L27 中,函数从cargo metadata文档提取所有 git 依赖源:

/// Git sources declared by the packages in a Cargo metadata document. /// The metadata need not contain a resolved dependency graph. pub fn git_dependency_sources(metadata: &str) -> Result<Vec<cargo_lock::SourceId>> { parse_metadata(metadata)?.packages .into_iter() .flat_map(|package| package.dependencies) .filter_map(|dependency| dependency.source) .filter(|source| source.starts_with("git+")) .map(|source| { source .parse() .into_diagnostic() .wrap_err("parse Cargo git dependency") }) .collect() }

关键点:

  • 遍历cargo metadata中所有包的依赖,过滤出sourcegit+开头的条目;
  • 将字符串解析为cargo_lock::SourceId,从而保留原始 URL 及查询参数(如?rev=release?branch=...?tag=...);
  • 该函数不要求元数据包含已解析的依赖图,因此可用于安装早期阶段(此时只有声明,尚无解析结果)。

2.3 测试验证

pnpm/crates/cargo-resolver/src/tests.rs#L23-L32 中的测试git_dependency_sources_preserve_the_requested_revision验证了带?rev=release的 git 依赖能够被原样提取:

fn git_dependency_sources_preserve_the_requested_revision() { let source = "git+https://example.test/repository?rev=release"; ... let sources = git_dependency_sources(&metadata).unwrap(); ... }

同一文件中的registry_and_path_dependencies_do_not_require_git_resolution(tests.rs#L34-L40)则反向验证:纯 registry 与 path 依赖不会误入 git 解析分支。


三、核心变更二:为含 Git 依赖的 Workspace 生成 Cargo.lock

3.1 变更含义

changeset 原文:

pnpm also generates Cargo lockfiles for workspaces with git dependencies.

对于包含 git 依赖的 Cargo workspace,pnpm 现在会在安装后生成Cargo.lock,从而固化 git 依赖解析结果(指向的提交、传递依赖版本等),保证后续安装可复现。

3.2 整体流程:resolve_lockfile

生成锁文件的入口是 pnpm/crates/cargo-resolver/src/resolution.rs#L115-L147 的resolve_lockfile

pub fn resolve_lockfile( metadata: &str, index_files: &BTreeMap<String, String>, source: &str, ) -> Result<String> { let metadata = parse_metadata(metadata)?; let registry = Registry::new(index_files, source)?; let root_dependencies = root_dependencies(&metadata)?; let mut feature_selections = root_feature_selections(&registry, &root_dependencies)?; ... loop { // 迭代求解:解析 → 计算 feature 选择 → 校验 → 收敛则输出 let solution = resolve_with_features(&registry, &root_dependencies, &feature_selections)?; ... if let Some(validated_solution) = validate_selected_graph(...)? { return lockfile_from_solution(&metadata, &registry, &validated_solution, &selected_features, source); } feature_selections = selected_features; } }

流程要点:

  1. 解析元数据:将cargo metadata的 JSON 解析为内部模型;
  2. 构建注册表:把稀疏索引文件(sparse index files)加载为可查询的版本列表;
  3. PubGrub 求解:用pubgrubOfflineDependencyProvider进行版本求解(见 resolution.rs#L224-L254),支持跨 semver 兼容线(compatibility line)选择版本并回溯;
  4. Feature 收敛:由于 feature 激活不具备单调性,求解会迭代多轮直至收敛,不收敛时抛出Cargo feature resolution did not converge
  5. 序列化输出:最终交给lockfile_from_solution生成 v4 格式的Cargo.lock

3.3 锁文件序列化:workspace 成员与 git 依赖的处理

pnpm/crates/cargo-resolver/src/lockfile.rs 负责把求解结果序列化为Cargo.lock(版本固定为ResolveVersion::V4,见 lockfile.rs#L60-L66)。

其中 workspace_packages 专门处理 workspace 自身成员:

  • 仅收录metadata.workspace_members中的包;
  • 对每个成员,将其依赖分为两类分别锁定:
    • registry 依赖:通过locked_dependency记录所选版本与其 source;
    • workspace 内部依赖:通过 locked_workspace_dependency 在 workspace 成员中按名字与版本区间匹配,source记为None(与 Cargo 行为一致:本地 workspace 成员不写 source)。

最终所有包按名称排序后写入锁文件,workspace 成员包与 registry 包在同一个packages列表中。

3.4 source 归属规则:git 依赖如何被记录

pnpm/crates/cargo-resolver/src/lockfile.rs#L160-L202 的locked_sources实现了 Cargo 的 source 归属语义:

  • 依赖声明指向 crates.io(或未声明 registry 且父包来自 crates.io)→ 记录为registry+https://github.com/rust-lang/crates.io-index(常量见 registry.rs#L11);
  • 依赖声明指向当前解析的 registry → 记录为该 registry 的稀疏源(sparse+...);
  • 同一 crate 被两个不同 registry 同时要求时,直接报错crate {} is required from two Cargo registries
  • git 依赖的git+...source 通过cargo_lock::SourceId::from_url写入锁文件,保留?rev?branch?tag等片段。

git_dependency_sourceslocked_sources分工明确:前者在解析前扫描声明,后者在解析后固化归属。


四、工作流视角:pnpm install如何利用这些能力

结合 resolution.rs 与 metadata.rs 可以推断出安装时的大致链路:

  1. 对包含 Cargo 项目的 workspace 运行等价于cargo metadata的采集,得到 JSON 文档;
  2. 调用git_dependency_sources识别所有git+依赖(不要求版本号),并保留其修订请求(rev/branch/tag);
  3. 通过resolve_inputs(metadata.rs#L48-L85)对元数据做"瘦身":只保留解析所需的nameversionfeatures与依赖键,并把包 id 替换为序号——这样元数据离开本机(例如交给 pnpr 服务端解析)时不再携带机器相关路径;
  4. missing_index_names(resolution.rs#L35-L66)找出稀疏索引中尚未获取的 crate 名,按需拉取索引文件;
  5. 调用resolve_lockfile完成 PubGrub 求解并序列化Cargo.lock(v4)。

该链路同样服务于 pnpr(见 pnpr/),即服务端代理解析场景:客户端只上传精简后的依赖图,解析与锁文件生成在远端完成。


五、使用前提与限制

  • 本文所述能力位于Rust 原生实现(pnpm/),对应 pnpm/crates/cargo-resolver/ 与 pnpm/crates/cli/Cargo.toml 中对pnpm-cargo-resolver的引用,而非 pnpm11/ 的 TypeScript 实现;
  • 求解依赖的版本元数据来自 crates.io 稀疏索引(默认https://index.crates.io,见 registry.rs#L17),因此网络访问与索引缓存在解析 registry 部分时必不可少;
  • git 依赖在cargo metadata输出中以其声明为准(git+前缀),锁文件生成时对 git 包的 source 原样记录;workspace 成员的本地依赖则一律不写 source;
  • 解析与 feature 选择采用迭代收敛策略,若 feature 组合无法收敛会报错终止,而非静默产出错误锁文件;
  • 使用方式以pnpm install触发,无需额外命令;如需观察具体行为可运行pnpm/crates/cargo-resolver的单元测试(如cargo test -p pnpm-cargo-resolver,测试代码见 tests.rs)。

六、小结

本次 changeset 让 pnpm 的 Rust 实现补齐了两块 Cargo 兼容能力:一是容忍省略版本号的 git 依赖声明(metadata.rs#L14-L27),二是为含 git 依赖的 workspace 生成 v4 格式Cargo.lock(lockfile.rs#L20-L68)。两者共同打通了"声明 git 依赖 → 识别与保留修订 → PubGrub 求解 → 写入锁文件"的完整链路,让 pnpm 管理同时包含 npm 与 Cargo 生态的项目时更加一致、可复现。

  • 包管理器
  • 开发工具
  • CLI

【免费下载链接】pnpm

Fast, disk space efficient package manager

项目地址:https://gitcode.com/gh_mirrors/pn/pnpm
点击查看免费下载

相关推荐

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

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

ZenCart多地区多运费配置:Zone定义与Zone Rates实战

简介&#xff1a;ZenCart作为开源电商平台&#xff0c;多地区多运费配置是跨境运营的关键环节。这份Word文档面向店铺管理员和开发人员&#xff0c;系统讲解基于分区&#xff08;shp1、shp2&#xff09;的国家指定、阶梯重量运费规则&#xff08;如0.5:20,1:30,2:40&#xff09…

作者头像 李华
网站建设 2026/9/19 20:19:34

出租车计费计课程设计:从脉冲计数到Verilog仿真与TTL实现

简介&#xff1a;出租车计费计数字电路课程设计文档是一份面向电子信息工程及相关专业学生的完整课设参考&#xff0c;围绕行车里程计费、等候时间计费和起步费三部分&#xff0c;给出总额不超过99.99元的计费器设计方案&#xff0c;内容涵盖设计目的、总体框图、各单元电路详解…

作者头像 李华
网站建设 2026/9/19 20:19:03

互联网家装公司如何分类?基于商业模式的六类划分法

简介&#xff1a;面向家装行业观察者、产品运营人员及创业研究者的一份PPT文档&#xff0c;聚焦互联网家装公司的模式拆解。内容按中介平台、自建电商、大型购物平台、B2C家装平台、建材O2O、3D设计软件、定制家具O2O、一站式O2S等类别展开&#xff0c;逐一说明代表企业、商业模…

作者头像 李华
网站建设 2026/9/19 20:14:00

两条命令把论文翻成中英双语:免费 PDF 翻译完整保留排版

两条命令把论文翻成中英双语&#xff1a;免费 PDF 翻译完整保留排版 【免费下载链接】PDFMathTranslate [EMNLP 2025 Demo] PDF scientific paper translation with preserved formats - 基于 AI 完整保留排版的 PDF 文档全文双语翻译&#xff0c;支持 Google/DeepL/Ollama/Ope…

作者头像 李华