在GitHub Actions中优化Rust项目CI编译缓存方案
解决Rust项目GitHub Actions分层缓存优化问题
核心思路
针对你的嵌套依赖结构和无Cargo.lock的场景,要实现分层缓存复用+精准触发缓存失效,核心是:
- 给每个子模块(core、lib_A、lib_A1等)生成独立缓存键,包含「依赖变更哈希」+「自有代码哈希」+「目标平台」+「Rust版本」
- 共享全局Cargo registry缓存,避免第三方依赖重复编译
- 子模块缓存键继承父模块标识,实现父模块编译结果的复用
具体实现步骤
1. 生成依赖变更哈希(替代Cargo.lock哈希)
因为未存储Cargo.lock,直接基于各模块的Cargo.toml和Cargo.toml.orig(如果存在)生成哈希,同时加入Rust版本和目标平台,确保依赖更新或环境变化时缓存失效:
- name: Generate dependency hash id: dep-hash run: | # 生成core模块的依赖哈希 core_dep_hash=$(find core -name "Cargo.toml" -o -name "Cargo.toml.orig" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) # 生成lib_A模块的依赖哈希(包含core的依赖文件) lib_a_dep_hash=$(find core lib_A -name "Cargo.toml" -o -name "Cargo.toml.orig" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) # 生成lib_A1模块的依赖哈希 lib_a1_dep_hash=$(find core lib_A lib_A1 -name "Cargo.toml" -o -name "Cargo.toml.orig" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) # 输出到环境变量供后续步骤使用 echo "core_dep_hash=$core_dep_hash" >> $GITHUB_OUTPUT echo "lib_a_dep_hash=$lib_a_dep_hash" >> $GITHUB_OUTPUT echo "lib_a1_dep_hash=$lib_a1_dep_hash" >> $GITHUB_OUTPUT
2. 生成自有代码哈希(控制代码变更时的缓存失效)
针对每个模块的Rust源码生成哈希,只有代码变动时才触发该模块重编译:
- name: Generate source code hash id: src-hash run: | core_src_hash=$(find core/src -type f -name "*.rs" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) lib_a_src_hash=$(find lib_A/src -type f -name "*.rs" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) lib_a1_src_hash=$(find lib_A1/src -type f -name "*.rs" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1) echo "core_src_hash=$core_src_hash" >> $GITHUB_OUTPUT echo "lib_a_src_hash=$lib_a_src_hash" >> $GITHUB_OUTPUT echo "lib_a1_src_hash=$lib_a1_src_hash" >> $GITHUB_OUTPUT
3. 分层配置缓存
每个模块的缓存键由「目标平台」+「依赖哈希」+「代码哈希」组成,同时通过restore-keys实现缓存回退,复用父模块或依赖相同的缓存:
# 缓存core模块 - uses: actions/cache@v4 with: path: | core/target ~/.cargo/registry/index/ ~/.cargo/registry/cache/ key: ${{ env.TARGET }}-core-${{ steps.dep-hash.outputs.core_dep_hash }}-${{ steps.src-hash.outputs.core_src_hash }} restore-keys: | ${{ env.TARGET }}-core-${{ steps.dep-hash.outputs.core_dep_hash }}- # 缓存lib_A模块,继承core的缓存标识 - uses: actions/cache@v4 with: path: | lib_A/target ~/.cargo/registry/index/ ~/.cargo/registry/cache/ key: ${{ env.TARGET }}-lib_A-${{ steps.dep-hash.outputs.lib_a_dep_hash }}-${{ steps.src-hash.outputs.lib_a_src_hash }} restore-keys: | ${{ env.TARGET }}-lib_A-${{ steps.dep-hash.outputs.lib_a_dep_hash }}- ${{ env.TARGET }}-core-${{ steps.dep-hash.outputs.core_dep_hash }}- # 缓存lib_A1模块,继承lib_A和core的缓存标识 - uses: actions/cache@v4 with: path: | lib_A1/target ~/.cargo/registry/index/ ~/.cargo/registry/cache/ key: ${{ env.TARGET }}-lib_A1-${{ steps.dep-hash.outputs.lib_a1_dep_hash }}-${{ steps.src-hash.outputs.lib_a1_src_hash }} restore-keys: | ${{ env.TARGET }}-lib_A1-${{ steps.dep-hash.outputs.lib_a1_dep_hash }}- ${{ env.TARGET }}-lib_A-${{ steps.dep-hash.outputs.lib_a_dep_hash }}- ${{ env.TARGET }}-core-${{ steps.dep-hash.outputs.core_dep_hash }}-
关键说明
- 依赖哈希的层级性:lib_A的依赖哈希包含core的Cargo文件,确保core依赖更新时lib_A缓存自动失效,符合依赖链逻辑
- 回退规则:
restore-keys让缓存找不到精确匹配时,优先复用依赖相同但代码不同的缓存,代码变动时只需重编译当前模块,无需重新编译依赖和父模块 - 全局Cargo缓存:每个缓存步骤都包含
~/.cargo/registry路径,确保第三方依赖在所有模块间共享,避免重复下载编译 - 灵活性:依赖哈希仅基于Cargo文件,宽松版本约束下只要依赖版本更新,哈希就会变动,不会错过依赖更新,同时不会像
rust-cache那样因锁文件全量哈希导致缓存频繁失效
额外优化建议
- 若矩阵构建包含多个Rust版本,可将Rust版本加入缓存键(如
${{ matrix.rust-version }}-${{ env.TARGET }}-...) - 不要忽略
target目录下的incremental文件夹,增量编译文件能大幅减少后续编译时间 - lib_B和lib_B1模块可按照相同逻辑复制配置
内容的提问来源于stack exchange,提问作者Jury
相关产品推荐
相关产品推荐

