如何为含工作区的Cargo Git依赖打补丁以实现离线托管
问题场景
我最近在搭建Cargo包的离线安装方案,已经搞定了crates.io源的情况——只要在.cargo/config.toml里配置replace-with = "vendored-sources"就行。单包的Git依赖也没问题,比如pubgrub这类,步骤就是:
- 把仓库下载成tar.gz包,解压到
/vendor目录 - 生成仅包含
"package"校验和的.cargo-checksum.json文件 - 在config里添加patch配置:
[patch."https://github.com/astral-sh/pubgrub"] pubgrub = { path = "/vendor/pubgrub" }
但碰到多包工作区的Git依赖就卡壳了,比如reqwest-middleware仓库,里面包含reqwest-middleware、reqwest-retry等多个子包:
- 直接patch工作区根目录,会报虚拟清单错误:
found a virtual manifest at
/vendor/reqwest-middleware/Cargo.tomlinstead of a package manifest - 把path指向子包目录,离线模式下又会触发Git拉取报错:
Unable to update https://github.com/astral-sh/reqwest-middleware?rev=21ceec9a5fd2e8d6f71c3ea2999078fecbd13cbe#21ceec9a
Caused by:
can't checkout from 'https://github.com/astral-sh/reqwest-middleware': you are in the offline mode (--offline)
我自己试出了一种方法,但太啰嗦,还不能和crates.io的vendored源放在同一目录,否则会报虚拟包和源更新失败的错误:
[source.reqwest-middleware] directory = "/vendor/reqwest-middleware" [source.doesntmatter] git = "https://github.com/astral-sh/reqwest-middleware" rev = "21ceec9a5fd2e8d6f71c3ea2999078fecbd13cbe" replace-with = "reqwest-middleware"
首选解决方案
对付这种工作区Git依赖,最靠谱的方法是给工作区里每个子包单独配置patch路径,同时保证工作区根目录有正确的.cargo-checksum.json文件。
具体步骤:
- 把整个工作区仓库下载解压到
/vendor/reqwest-middleware目录 - 在工作区根目录生成
.cargo-checksum.json——这个文件需要包含所有子包的校验和,最简单的方式是在线环境执行cargo vendor自动生成,也可以手动计算每个子包的校验和填入 - 在
.cargo/config.toml中为每个子包添加patch配置:[patch."https://github.com/astral-sh/reqwest-middleware"] reqwest-middleware = { path = "/vendor/reqwest-middleware/reqwest-middleware" } reqwest-retry = { path = "/vendor/reqwest-middleware/reqwest-retry" } # 有其他子包就继续添加对应配置
为什么这个方案可行?
- 每个子包单独指定路径,直接避开了虚拟清单的问题
- 工作区根目录的校验和文件会告知Cargo该Git仓库的内容合法,离线模式下不会再尝试拉取远程仓库
简化替代方案
如果子包太多不想逐个配置,也可以用自定义源+replace-with的方式,但需要把工作区依赖和crates.io的vendored源分开存放:
- 单独创建目录存放工作区依赖,比如
/vendor/git-repos/reqwest-middleware,crates.io的vendored源放在/vendor/crates-io - 在
.cargo/config.toml中配置:# crates.io的vendored源配置 [source.crates-io] replace-with = "vendored-crates" [source.vendored-crates] directory = "/vendor/crates-io" # 工作区依赖的自定义源 [source.reqwest-middleware-workspace] directory = "/vendor/git-repos/reqwest-middleware" [source.reqwest-middleware-git] git = "https://github.com/astral-sh/reqwest-middleware" rev = "21ceec9a5fd2e8d6f71c3ea2999078fecbd13cbe" replace-with = "reqwest-middleware-workspace"
这种方法不用逐个编写子包配置,但需要维护独立的目录结构,避免和crates.io源冲突。
内容的提问来源于stack exchange,提问作者Flamefire

