The Cargo team invites developers to test the nightly-only cargo -Zbuild-dir-new-layout flag and report any issues. While the build directory layout is officially internal, many projects depend on its unspecified structure due to gaps in Cargo's feature set. Although a crater run has been performed, it can't catch everything. Community help is needed to identify affected tools and workflows, report issues to maintainers, and ensure compatibility with both old and new layouts.
How to test this?
Using nightly 2026-03-10 or later, run your tests, release processes, and any workflow that touches the build or target directory with the -Zbuild-dir-new-layout flag.
For example:
$ cargo test -Zbuild-dir-new-layout
If you encounter failures, they may not be specific to -Zbuild-dir-new-layout. Since Cargo 1.91, users can separate intermediate build artifacts (build-dir) from final artifacts (target-dir). Verify this by testing with only CARGO_BUILD_BUILD_DIR=build set. The Cargo team is considering changing the default build-dir location in #16147.
Possible outcomes include:
- Fixing local issues in your own projects
- Reporting upstream tool problems with a note on the tracking issue
- Providing general feedback on the tracking issue
Known failure modes:
- Inferring a
[[bin]]path from a[[test]]path:- Use
std::env::var_os("CARGO_BIN_EXE_*")for Cargo 1.94+, with the old inference as a fallback for earlier versions - Use
env!("CARGO_BIN_EXE_*")
- Use
- Build scripts deriving target-dir from their binary or
OUT_DIR: see Issue #13663- Update existing workarounds to handle the new layout
- Locating user-requested artifacts from rustc: see Issue #13672
- Update existing workarounds to handle the new layout
Library compatibility status at publication:
- assert_cmd: fixed
- cli_test_dir: Issue #65
- compiletest_rs: Issue #309
- executable-path: fixed
- snapbox: fixed
- term-transcript: Issue #269
- test_bin: Issue #13
- trycmd: fixed
What is not changing?
The layout of final artifacts within the target directory remains unchanged.
Build artifacts will continue to be nested under the profile and target tuple when specified.
What is changing?
The organization shifts from grouping by content type to scoping content by package name and a hash of the build unit and its inputs.
Current layout example with packages lib and bin, both having build scripts:
build-dir/
├── CACHEDIR.TAG
└── debug/
├── .cargo-lock # file lock protecting access to this location
├── .fingerprint/ # build cache tracking
│ ├── bin-[BUILD_SCRIPT_RUN_HASH]/*
│ ├── bin-[BUILD_SCRIPT_BIN_HASH]/*
│ ├── bin-[HASH]/*
│ ├── lib-[BUILD_SCRIPT_RUN_HASH]/*
│ ├── lib-[BUILD_SCRIPT_BIN_HASH]/*
│ └── lib-[HASH]/*
├── build/
│ ├── bin-[BIN_HASH]/* # build script binary
│ ├── bin-[RUN_HASH]/out/ # build script run OUT_DIR
│ ├── bin-[RUN_HASH]/* # build script run cache
│ ├── lib-[BIN_HASH]/* # build script binary
│ ├── lib-[RUN_HASH]/out/ # build script run OUT_DIR
│ └── lib-[RUN_HASH]/* # build script run cache
├── deps/
│ ├── bin-[HASH]* # binary and debug information
│ ├── lib-[HASH]* # library and debug information
│ └── liblib-[HASH]* # library and debug information
├── examples/ # unused in this case
└── incremental/... # managed by rustc
Proposed layout:
build-dir/
├── CACHEDIR.TAG
└── debug/
├── .cargo-lock # file lock protecting access to this location
├── build/
│ ├── bin/ # package name
│ │ ├── [BUILD_SCRIPT_BIN_HASH]/
│ │ │ ├── fingerprint/* # build cache tracking
│ │ │ └── out/* # build script binary
│ │ ├── [BUILD_SCRIPT_RUN_HASH]/
│ │ │ ├── fingerprint/* # build cache tracking
│ │ │ ├── out/* # build script run OUT_DIR
│ │ │ └── run/* # build script run cache
│ │ └── [HASH]/
│ │ ├── fingerprint/* # build cache tracking
│ │ └── out/* # binary and debug information
│ └── lib/ # package name
│ ├── [BUILD_SCRIPT_BIN_HASH]/
│ │ ├── fingerprint/* # build cache tracking
│ │ └── out/* # build script binary
│ ├── [BUILD_SCRIPT_RUN_HASH]/
│ │ ├── fingerprint/* # build cache tracking
│ │ ├── out/* # build script run OUT_DIR
│ │ └── run/* # build script run cache
│ └── [HASH]/
│ ├── fingerprint/* # build cache tracking
│ └── out/* # library and debug information
└── incremental/... # managed by rustc
For deeper technical details, see the mod layout documentation.
Why is this being done?
ranger-ross has driven this effort as a foundation for cross-workspace caching, which becomes more feasible when each cacheable unit lives in a self-contained directory.
This change also enables future work on:
- Automatic cleanup of stale build units to maintain consistent disk usage over time
- More granular locking so
cargo testand rust-analyzer don't block each other
Additional benefits discovered during development:
- Improved build performance as intermediate artifacts accumulate in
deps/ - Eliminating
deps/pollution ofPATHduring Windows builds - Preventing file collisions among intermediate artifacts
While the Cargo team doesn't officially support sharing a build-dir across workspaces, that last improvement should reduce issues for developers who do.
Future work
Lessons from this layout change will inform how and when to implement future modifications, including:
- Reducing path lengths to minimize Windows-specific errors
- Experimenting with moving artifacts out of
--profileand--targetdirectories to enable broader artifact sharing
Some potential changes weren't included now because they depend on the lock improvements, which in turn depend on this layout change.
The team also aims to reduce project coupling to the unspecified details of build-dir structure.