Rust项目公开征测:Cargo 构建目录新布局方案
Rust 官方团队正在邀请社区测试一项酝酿已久的 Cargo 变更:全新的构建目录布局(Build Dir Layout v2)。 这件事看上去只是目录结构的调整,但它是 Cargo 未来几个重要特性的地基,也可能悄悄影响你现有的构建脚本、CI 流程,乃至你依赖的某些测试库。了解它,比等到正式上线后踩坑要好得多。 在谈新布局之前,先搞清楚现在的布局有什么问题。 打开一个 Rust 项目的 这是一种"按内容类型分类"的组织方式——把所有包的 fingerprint 文件堆在一个 这种方式带来了几个长期未能解决的问题: 问题一:无法做跨工作区缓存。 共享缓存需要知道哪些产物属于哪个构建单元,而现在一个 问题二:过期产物无法自动清理。 你换了一个依赖版本,旧版本的中间产物就这样永远堆在 问题三:文件锁粒度太粗。 现在 问题四:Windows 上 问题五:中间产物文件名冲突。 两个包恰好产生同名的中间文件,就会互相覆盖,这是一个隐性的正确性问题。 新布局的核心思想只有一句话:按包名分层,每个构建单元自成一体。 同样是两个包 与旧布局的对比,用一张表来说明: 旧布局中, 这次目录重构是一块基石,它让下面这些长期搁置的功能变得可行: 跨工作区缓存共享(cross-workspace caching) 这是最重要的动机。当每个构建单元都有自己独立的目录时,Cargo 才能把这些目录当作可移植的缓存单元,在不同工作区之间共享。对于大型 monorepo 或多项目组合来说,这意味着编译时间的大幅缩短。 过期构建产物的自动清理 旧布局下,Cargo 很难判断一个构建单元的产物是否还有用,因为相关文件散落在多个目录。新布局下,一个构建单元的全部产物就在一个目录里,过期了直接删掉那个目录即可,实现常数级的磁盘占用。 更细粒度的文件锁 顺带修复的问题 在推进这个方案的过程中,团队还发现它意外地改善了一些历史问题: final artifacts(最终产物,即 target-dir 下的内容)不会变化,也就是说你用 受影响的是那些依赖了 build-dir 内部布局的工具或脚本,例如: 目前已知受影响的库状态(截至博客发布时): 需要 nightly 2026-03-10 及以上版本,在命令行加上 把你平时跑的测试、发布流程、CI 脚本都过一遍。如果遇到失败,也可以先用以下方式单独验证是否与构建目录分离本身(而非新布局)相关: 发现问题后,可以: 这次布局变更之后,Cargo 团队还有几个方向在路上: 部分变更目前被文件锁的改进工作所阻塞,而文件锁的改进又依赖本次布局变更先落地,所以这是一个需要按顺序推进的工程。 Cargo 的构建目录布局是个典型的"用久了就不敢动"的地方。大量工具在文档未声明支持的情况下,依赖了 target-dir 内部的路径约定,导致每一次改动都牵一发动全身。 这次 Rust 团队选择在正式发布前广泛征集测试反馈,正是希望在改动落地之前,把受影响的工具逐一梳理清楚,给上游项目足够的时间适配。 如果你有使用 CI 系统缓存 target 目录、或者依赖测试辅助库来定位编译产物,建议现在就用 nightly 跑一遍,早发现早适配,比等到稳定版本上线后再处理要从容得多。 参考资料:为什么要改目录结构
target/debug/(或 build-dir/debug/),你会看到这样的结构:debug/
├── .fingerprint/ # 构建缓存追踪,所有包混在一起
├── build/ # 所有包的 build script 产物混在一起
├── deps/ # 所有包的中间编译产物混在一起
├── examples/
└── incremental/.fingerprint/ 目录里,把所有包的编译产物堆在 deps/ 里。deps/ 里几十个包的产物全部混在一起,根本无法做到独立追踪。deps/ 里,target/ 目录越来越大,只能靠 cargo clean 一刀切。cargo build 和 rust-analyzer 会互相等待同一把锁,因为它们操作的是同一个目录下的混合产物,锁不能更细。deps/ 污染 PATH。 构建过程中 deps/ 目录会被加入 PATH,里面混杂着所有包的中间产物,会造成意外的路径污染。新布局长什么样
lib 和 bin,新布局变成这样:build-dir/
└── debug/
├── .cargo-lock
├── build/
│ ├── bin/ # 按包名分目录
│ │ ├── [BUILD_SCRIPT_BIN_HASH]/
│ │ │ ├── fingerprint/*
│ │ │ └── out/*
│ │ ├── [BUILD_SCRIPT_RUN_HASH]/
│ │ │ ├── fingerprint/*
│ │ │ ├── out/*
│ │ │ └── run/*
│ │ └── [HASH]/
│ │ ├── fingerprint/*
│ │ └── out/*
│ └── lib/ # 按包名分目录
│ ├── [BUILD_SCRIPT_BIN_HASH]/
│ │ ├── fingerprint/*
│ │ └── out/*
│ ├── [BUILD_SCRIPT_RUN_HASH]/
│ │ ├── fingerprint/*
│ │ ├── out/*
│ │ └── run/*
│ └── [HASH]/
│ ├── fingerprint/*
│ └── out/*
└── incremental/对比维度 旧布局 新布局 组织方式 按内容类型(fingerprint/build/deps) 按包名 + 构建单元哈希 产物隔离 所有包混在同一目录 每个包独立子目录 fingerprint 位置 统一在 .fingerprint/随各构建单元就近存放 清理粒度 只能整体 clean 可以按包、按单元精确清理 文件锁粒度 整个 target 目录 可细化到每个构建单元 .fingerprint/bin-[HASH] 和 deps/bin-[HASH] 是分散在两个地方的,属于同一个构建单元的信息被物理上拆开了。新布局把它们全部收拢在 build/bin/[HASH]/ 这一棵子树下,一个构建单元的所有信息都在自己的目录里,完全自包含。这个改动解锁了什么
cargo test、cargo build 和 rust-analyzer 现在会争抢同一把粗粒度的锁。新布局可以将锁的粒度细化到每个构建单元目录,让它们真正并行工作,互不阻塞。deps/ 里中间产物的积累导致构建性能下降的问题、Windows 上 PATH 污染的问题、中间产物文件名冲突的问题,都随之得到缓解。谁可能受到影响
target/release/my-binary 这样的路径访问最终可执行文件,不受影响。CARGO_BIN_EXE_* 环境变量;OUT_DIR 推导 target-dir 位置:需要适配新的目录结构;库名 状态 assert_cmd 已修复 snapbox 已修复 executable-path 已修复 trycmd 已修复 cli_test_dir 待修复(Issue #65) compiletest_rs 待修复(Issue #309) term-transcript 待修复(Issue #269) test_bin 待修复(Issue #13) 如何参与测试
-Zbuild-dir-new-layout 标志:cargo test -Zbuild-dir-new-layout
cargo build -Zbuild-dir-new-layoutCARGO_BUILD_BUILD_DIR=build cargo test后续规划
--profile 和 --target 子目录中移出,让更多产物可以跨配置共享;写在最后