Description
For design, see #14125 (comment)
For tracking the artifact-dir side of this, see #6790
Documentation: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-dir
Testing instructions: #14125 (comment)
Implementation
- unstable build-dir support (Implemented
build.build-dir
config option #15104) - build-dir templating (Added
build.build_dir
templating support #15236) -
cargo metadata
support (Addedbuild_directory
field to cargo metadata output #15377) - Document
build_directory
cargo metadata
field (docs(metadata): Added build_directory to cargo metadata documentation #15410) -
workspace-path-hash
should have symlinks resolved (Added symlink resolution forworkspace-path-hash
#15400) - error on unmatched
{
,}
(Added validation for unmatched brackets in build-dir template #15414) - append
closest_msg
to unknown variable error (and maybe list all variables if no match is found?) (Improved error message when build-dir template var is invalid #15418) - call for testing
- stabilize build-dir
Stabilizing this would also resolve
Decisions
cargo clean
will clean bothtarget-dir
andbuild-dir
- Skipping including a
CARGO_BUILD_DIR
shortcut forCARGO_BUILD_BUILD_DIR
(likeCARGO_TARGET_DIR
is a shortcut forCARGO_BUILD_TARGET_DIR
)
artifact-dir files:
- build
- binary executable for bin crates
- binary executable for examples
- depinfo files (
.d
files) for third party build-system integrations (see https://github.com/rust-lang/cargo/blob/master/src/cargo/core/compiler/fingerprint/mod.rs#L194) - Cargo's
--timings
HTML report
cargo package
's generated.crate
filescargo doc
output (html/css/js/etc)
build-dir files (intermediate artifacts, build state, caches):
- build
- "pre and non uplifted" binary executables. (ie. bins for
examples
that contain the hash in the name, bins forbenches
, proc macros, build scripts) - other depinfo files (generated by rustc, fingerprint, etc. See https://github.com/rust-lang/cargo/blob/master/src/cargo/core/compiler/fingerprint/mod.rs#L164)
- rlibs and debug info from dependencies
- build script
OUT_DIR
- output from proc macros (previously stored in
target/build
) - incremental build output from rustc
- fingerprint files used by Cargo for rebuild detection
- Cache of rustc invocations (
.rustc_info.json
)
- "pre and non uplifted" binary executables. (ie. bins for
cargo package
's scratchpad used for the verify stepCARGO_TARGET_TMPDIR
files (see rational for this here)- reports generated from
cargo report
like future-incompat-report
Open questions
- Are we good with the name
build-dir
?- Yes, discussed on 2025-03-18
- Should we offer
CARGO_BUILD_DIR
as a shortcut toCARGO_BUILD_BUILD_DIR
, likeCARGO_TARGET_DIR
is forCARGO_BUILD_TARGET_DIR
? see Implementedbuild.build-dir
config option #15104 (comment)- No, discussed on 2025-03-18
- End-users are most likely to set this globally in config
- Env variable is most likely in CI
- Length and "cleaner name" aren't priorities for the target audience, CI
- Should we stick with the name
{workspace-manifest-path-hash}
and what should it include? Should we shorten to{workspace-hash}
or even just{hash}
?- We should drop
manifest
, unsure about dropping eitherworkspace
orpath
. Users may be confused if they don't realize the path is a part of the hash - Renamed to
workspace-path-hash
in fix(build-dir): Renamed workspace-manifest-path-hash to workspace-path-hash #15334
- We should drop
-
Should we include the Cargo version indeferred{workspace-manifest-path-hash}
so we get unique whole-target directories for easier cleanup (Garbage collect wholetarget/
#13136) - Should we resolve symlinks in workspace manifest-path before generating
{workspace-manifest-path-hash}
? See Addedbuild.build_dir
templating support #15236 (comment)- The cargo team leans towards doing so right now
- As the hash is unspecified, we feel this is a two-way door
- Added symlink resolution for
workspace-path-hash
#15400
- Should we keep erroring on unknown template variables? Should we start erroring on unmatch
{
? See Addedbuild.build_dir
templating support #15236 (comment)- Yes, we should error on unknown template variables. It is the least surprising, even if annoying, and provides better error messages (229489e)
- Yes, we should error on unmatched
{
. This includes{{
; escaping has been deferred (Added validation for unmatched brackets in build-dir template #15414)
- Should we take the advantage to self-ignore
build.build-dir
? Make target dir self-ignoring #15061 - Include in
cargo metadata
? What about that people need to matchcargo metadata
version againstcargo
to get it?- Yes, it should be included in
cargo metadata
but only the current one. Changing the hash on every cargo version has been deferred to reduce the need to providebuild-dir
for all cargo versions - Added
build_directory
field to cargo metadata output #15377, docs(metadata): Added build_directory to cargo metadata documentation #15410
- Yes, it should be included in
- Should we offer
CARGO_BUILD_TMPDIR
, deprecatingCARGO_TARGET_TMPDIR
? - should we offer the workspace base name either as a prefix to the hash or as its own variable for making it easier for humans to find delete specific build dirs
Deferred
{workspace-manifest-hash}
also hashes the cargo version- Excludes release channel information, including which nightly
- Allows easier cleanup on
rustup update
(Garbage collect wholetarget/
#13136) - Makes very clear that the hash's meaning is unstable
- Blocked on Garbage collect whole
target/
#13136 (maybe more?) to reduce the need for third-party clean up tools which will need to know the path to everybuild-dir
for a workspace
- In templates,
{{
is an escaped{
- With us erroring on
{
, we have the room to allow escaping in the future - We'd like escaping support to be based on use cases and not speculatively developed as
{
in paths is rare and this isn't a general path
- With us erroring on
Original Issue:
Problem
There are a couple of issues with the CARGO_TARGET_DIR
that are seemingly in conflict with each other:
-
Multiple locations of
target
dirs complicate excluding them from backups and full-disk search, cleanup of the temp files, moving temp files to dedicated partitions, out of slow network drives or container mounts, etc. Users don't like that thetarget
dir is huge, and multiple instances of it add up to lot of disk space. Users would prefer a central location to ease management of the temp files, and also to dedupe/reuse dependencies across many projects. -
People (and tools) are relying on a relative
./target
directory being present to copy or run built files out of there. Additionally, users may not want to configure a sharedCARGO_TARGET_DIR
due to risk of file name conflicts between projects.
However, the dilemma between 1 and 2 exists only because Cargo uses CARGO_TARGET_DIR
for two different roles:
- A cache for all intermediate build products (a place where crates.io crates are built, where compiler-private temp files are) which aren't project-specific, and/or files that users don't need to access directly.
- A location for user-facing final build products (artifacts) that users expect to be there and need to access.
Proposed Solution
So to satisfy both uses, I suggest to change the thinking about what the role of CARGO_TARGET_DIR
should be. Instead of thinking where to put the same huge all-purpose mixed CARGO_TARGET_DIR
, think how to deduplicate and slim CARGO_TARGET_DIR
, and move everything non-user-facing out of it.
Instead of merging or sharding the CARGO_TARGET_DIR
as-is with all of its current content, and adding --artifact-dir
as a separate place where final products are being copied to — make CARGO_TARGET_DIR
to be the artifact dir (without copying).
As long as the CARGO_TARGET_DIR
dir is the place for all of the build files, of all crates including all the crates.io and local builds, with all the caches, all the temp junk, then this is going to be a problematic large directory that needs to be managed. But if the purpose of the ./target
dir was changed to be only for user-facing files (files that users can name, and would access via ./target
path themselves), then this directory would be relatively small, with a good reason to stay workspace-relative.
What isn't an intermediate build product? (and should stay in ./target
)
- linked (and stripped) binaries of the current workspace, including binaries for the examples,
- libraries of the current workspace as
.a
/.so
, wherelib.crate-type
calls for them. Possibly.rlib
/.rmeta
in the future if there's a stable ABI. - linked binaries for tests and benches of the current workspace (to make it easy to launch them under a debugger/profiler, and so they can use relative file paths to read workspace assets).
- debug symbols for all of the above.
.d
files for all of the above (so that IDEs and other build systems know when to rebuild the artifacts).- if Cargo adds some "staging" directory (a non-private
OUT_DIR
forbuild.rs
, see Allow build scripts to stage final artifacts #13663), then for build scripts belonging to the current workspace it would be inside./target
as well.
So generally files that users build intentionally, and may want to access directly (run themselves, or package up for distribution) and files that users may need configure their IDE and debugger to find inside the project.
Crates in [patch.crates-io]
with a path
are a gray area, an might also have their artifacts included in the ./target
dir (but in some way that avoids clobbering workspaces' files).
What isn't a final build product, and doesn't belong to ./target
:
- anything related to building crates from crates.io, or any other registry (packages with
source = "registry+…"
) - all
.fingerprint
andincremental
dir content of all crates. These are implementation details of the compiler, and nobody should be accessing these directly via./target/…
. .o
files. Users are not supposed to use them directly either (Rust has static libs for this).- proc macro libs. They're not useful without rustc present.
All of these should be built in some other shared build cache dir (one that is not inside CARGO_TARGET_DIR
), configurable by a new option/env var.
Registry dependencies would get unique paths derived from rustc version + package IDs + enabled features (so that different crates using different features don't invalidate each others' caches all the time). This would enable sharing built crates.io dependencies across all projects for the same local user, without also causing local workspaces to clobber each others' CARGO_TARGET_DIR/profile/product
paths. Temp directories for local projects would need some hashed paths in the shared build/temp dir too.
Advantages
- Such split removes about 90% of the weight from
./target
dirs (forcargo
itself, it makes./target/debug
with binaries and tests take 415MB, instead of 4.2GB). This makes cleanup of all the scatteredtarget
dirs less of a pressing problem. - The
./target
keeps relatively few files, and removes high-frequency-churning files out of it, which makes it less of a problem for real-time disk indexing (like search and backups on macOS). - I/O latency of
./target
stops being critical for build speeds, unlike I/O of the incremental cache and rewrites of thousands of.o
files. It becomes feasible to have project directory on a network drive without overridingCARGO_TARGET_DIR
(network filesystems are used by non-Linux systems where tools like Vagrant and Docker have to run full-fat VMs, and can't cheaply share the file system). - It makes
./target
contain only workspace-unique files, which makes it justified for every workspace to have one. - It enables moving registry deps to a shared build directory, without side effect of local projects overwriting each others' files. Sharing of dependencies matches users' expectation that the same dependencies shouldn't be redundantly rebuilt for each local project.
- It's almost entirely backwards compatible. Users can get the benefits without breaking their existing workflows, post-build scripts, and integrations. It doesn't invalidate documentation/books/tutorials that refer to
target/release/exe
etc. - It could be the default behavior, so it could benefit all users without friction of adding
--artifact-dir
or.cargo/config
.
Notes
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
Status