< 返回我的博客

爱国的张浩予 发表于 2022-05-25 08:09

Tags:offline,compile,cache,vendor,fetch,cargo

Cargo Package离线编译策略

简单地讲,cargoPackage提供了两套【离线编译】解决方案:

  • 方案一:cargo fetch + cargo build --offline
  • 方案二:cargo vendor + 修改.cargo/config.toml文件 + cargo build

两套方案的共同点

  • cargo fetchcargo vendor命令
    • 【执行-位置】都需要在Cargo Package工程根目录被执行。
    • 【执行-时间】都需要在工程构建程序(无论是build.rs还是shell脚本)被启动前,被预先执行。
  • 无论依赖项的来源是crates.io还是github,它们都可被预拉取和缓存至本地。

两套方案的差别

  • 依赖项crate源码的储存位置
    • cargo fetch缓存依赖项源码于$CARGO_HOME目录下。更具体地进,
      • crate.io依赖项 -> $CARGO_HOME/registry/src
      • github依赖项 -> $CARGO_HOME/git/checkouts
    • cargo vendor缓存依赖项源码于Cargo Package工程根目录下的vendor子文件夹里。
  • 依赖项crate共享范围
    • cargo fetch缓存的依赖项源码可被同一台机器上的所有Cargo Package工程所共享。
    • cargo vendor缓存的依赖项源码仅服务于当前Cargo Package工程。
      • vendor文件夹馁馁地就是rust版的node_modules
      • 若将vendor依赖目录在多个Cargo Package工程之间共享,需要额外地配置.cargo/config.toml文件和对多个工程的文件系统位置做一些约定。这要比npm - node_modules麻烦不少 — 回头单独写篇文章分享之。
  • CI系统集成方式(比如,Jenkins
    • cargo fetch方案·集成步骤:

      • 首先,cargo fetch命令需要由 @CI管理员,在Jenkins机器上,在启动打包jobs以前,连网执行一次。
      • 然后,在打包过程中,cargo build --offline命令才能在$CARGO_HOME目录下找到预缓存的依赖项源码。其中,命令行参数--offline会“短路”一切网络请求,包括:
        • 确认Cargo.lock是否已经反映了每个依赖项的最新语义兼容版本。
        • 下载依赖项crate源码

      对程序员来说,这可能不那么爽。毕竟,每次依赖升级,咱们都不得不走流程,编故事,写报告,请 @CI管理员 给执行一下cargo fetch命令。

    • cargo vendor方案·集成步骤:

      • 首先,cargo vendor命令需要由 @程序员,在个人电脑上,在代码提交前,执行一次。于是,vendor文件夹出现。

      • 然后,将·cargo vendor命令打印至【标准输出】的配置代码(如下)·复制到.cargo/config.toml配置文件内。

        [source.crates-io]
        replace-with = "vendored-sources"
        [source."https://github.com/shadowsocks/crypto"]
        git = "https://github.com/shadowsocks/crypto"
        branch = "master"
        replace-with = "vendored-sources"
        [source.vendored-sources]
        directory = "vendor"
        

        若你的Cargo Package工程曾经配置过【源码替换】规则(真高级),那么请合并(而不仅是追加)[source.***]配置代码。

      • 接着,将工程根目录下的vendor文件夹和.cargo/config.toml配置文件,随业务程序,一起提交至git仓库。

      • 在打包编译过程中,

        • Jenkinsgit clone到上述所有文件,包括:

          • 业务代码
          • 依赖源码
          • Cargo.toml
          • .cargo/config.toml
        • cargo build命令也不需要--offline命令行参数了,因为【目录·源码替换】配置规则会生效和告诉cargo从本地硬盘的何处寻找依赖项crate源码。

          题外话,没有想到吧!在这影响依赖项“寻址”指向的机制不是【依赖重载[patch.***]】,而是【源码替换[source.***]】。我也挺意外的。

cargo vendor性能最佳·参数组合

cargo vendor --no-delete --versioned-dirs --respect-source-config

  • --no-delete不删除上一次执行cargo vendor时留下的vendor文件夹。这样下载过的crate源码就不会再重新下载了。
  • --versioned-dirs给每个依赖项目录名追加以-开头的版本号后缀(例如,base64-0.5.2)。这样,不用刻意地浏览每个依赖项的Cargo.toml文件,便可知晓它们的版本信息。
  • --respect-source-config若你的工程早先就已经配置过【源码替换】[source.***]配置块,cargo将对旧配置做兼容处理。否则,旧配置就会被无视了。

番外篇

虽然上面讲了那么多,但其实仅只绕过了【依赖项源码下载】的耗时环节。但是,cargo打包编译慢,又何止于网络慢,编译本身也是“重灾区”。所以,若你的CI打包策略是:

  1. 抹除整个工程目录 — 毛都不剩
  2. git捡出业务代码 — 内网应该很快
  3. 下载依赖项 — 走cargo fetch / vendor的缓存,应该也很快
  4. 编译依赖项 — 我若说“不慢”,你敢信?

的话,那么上述的长篇大论仅只节省了#3的延时。而我要在此推荐的【究级】“补刀”配置却能够缩短#4的耽搁。即,给.cargo/config.toml文件添加(或合并)一段配置(如下)

[build]
target-dir = "../<工程名>-target"

将整个编译输出目录“请”出工程文件夹。于是,

  • 【编译输出文件夹】将和【Cargo Package工程】处于平级目录且名曰“<工程名>-target”
  • Jenkins不会再删除【编译输出目录】及其缓存的编译中间文件,因为它们都不在【工程目录】中。
  • 下次再对该Cargo Package工程编译,将启用【增量编译】处理流程。这馁馁地要比【全量编译】快速得多!

最后,cargo打包编译的最后两步都有了缓存做支撑,工程的编译速度进一步被提高。完美!!!

结束语

这次就分享这些。总的来说,我对rust编程的实践机会还是非常有限的。所以,文章中总结的内容有疏漏之处,还请路过的神仙哥哥与神仙妹妹指正,和多发评论呀!

评论区

写评论
ManonLoki 2022-05-25 16:40

这个是相当于把全局缓存改成放在项目本体里了? 这个对Chromium和FF这些项目来说确实方便。

1 共 1 条评论, 1 页