-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Redefine CARGO_TARGET_DIR
to be only an artifacts directory
#14125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
In terms of incompatibilities, only a few come to my mind:
|
@poliorcetics from rust-lang/rfcs#3664 (comment)
|
Overall, I see this as a solution alternative to #6790 and had recommended we have that conversation there (or on internals).
This is likely going to be the most difficult topic to work through and we'll need to make sure we get wide input on this from #6790 users and others.
imo this is out of scope for this proposal (see #5931) and we should keep this focused so as not to get distracted. |
I see this as a re-framing of the problem, addressing #6790 and rust-lang/rfcs#3371 Instead of us defining a new
Potential names
Cargo script would default its This would need input from
|
This would need an audit of ways we publicly treat the target dir as a working dir, like exposing |
Sharing sources over NFS adds another incompatibility when the ability to move Every modern buildsystem allows to keep sources and build results separated (and users and tools do not have problems with it). I do not think that cargo should go the way back and enforce a fixed |
I'm not suggesting to force it to always be |
We talked about this in today's Cargo team meeting. Our care abouts include
While we acknowledged the potential for user confusion with The general shape of what we proposed in the meeting is... Shiny future
Legacy
Other
Path to Shiny Future
|
Something we overlooked in the above analysis is other "artifacts". In particular, I'm thinking of Ways of solving this
|
Just commenting here as I'm dealing with my own issues regarding the target dir, but personally, while it's nice for For some context, I use ZFS snapshots as a form of fast local backups; not long-term backups in case of hardware or extreme software failure, but decent short-term backups in case I accidentally delete a file or mess up an update. However, I explicitly go out of my way to exclude as many things as possible from auto-snapshotting that qualify as "cache" because they can very quickly clog up my disk if I'm not careful. (Also: since snapshotting is a filesystem-level feature, I can't just say "don't save files of this type in snapshots" since snapshotting works by instantly freezing the state of the FS into a snapshot, and doesn't copy files over like a long-term backup would.) For example, today I just deleted 200 GiB of snapshots of target directories. Not the current target directories, but past versions of them from previous snapshots. Snapshots are good for incremental stuff like code because they're copy-on-write, but the contents of a binary are effectively random to any snapshotting tool and they'll end up being fully duplicated every time they're snapshotted, and that means you can end up with several times that amount of data in snapshots until everything eventually gets old enough to be deleted. The "effectively random" part also applies especially to the final products, since while crates that don't change won't change in their compiled artifacts, the final linked products definitely will. So, as far as I'm concerned, moving the final build products back into the workspace without also having the option to keep them out effectively un-solves the problem that moving the target directory was meant to solve. After all, the final build products, modulo LTO (which isn't really going to happen for debug builds) will effectively be the same size as all the intermediate products, so, that means that about half the disk usage will not be saved. (I'm extremely approximating here; the point is that it's a considerable amount of the disk usage, even if it's not half. Even 10% of the size is still a lot when you consider that these are being multiplied across several snapshots.) And note that yes, other languages like Node and Python also have this exact same problem, but I don't think that other languages' inability to solve this problem forgives Rust not solving it. Also, even though I love the idea of keeping intermediate products deduplicated and in one place. I just don't want that to obscure the goal of having the final products also somewhere else too. |
No, the intermediate products are usually many many times larger. Not just double, they can be 1000× larger! On the project I'm currently working, a clean debug build of a 20MB executable creates 2300MB of junk in There are often many duplicate copies of libstd and other dependencies in each |
@clarfonthey the plan calls for both |
FWIW, I've had decent results with |
I'm in the same boat as @clarfonthey, but with BTRFS snapshots, which I create every 15 minutes and then stream to longer-term storage. My debug builds are easily 2.5G and I clean up I created Proposed separation (especially templating for both new options) should work nicely for such use case, Excited! |
Personally I also don't care about having the artifacts copied anywhere, I access them via |
My understanding is the the One question I have is about the handling of building for the host platform. Is this correct? |
One thing I do care about the artifact locations is that they are not in a sub-dir of the repository. Having large temporary artifacts mixed in with the source code complicates backups and filesystem snapshots. This is why I would prefer to disable the target-artifact-dir and just leave all intermediate and final artifacts somewhere in |
Call for testing
This allows users to move intermediate build artifacts into a location separate from final artifacts. Use cases include
This can be set in user config (e.g. e.g. [build]
build-dir = "{cargo-cache-home}/build/{workspace-path-hash}" Future possibilities
Special thanks to @ranger-ross for implementing this! Basic steps:Requirements:
See the unstable docs for what is supported Please test your workflows with
When testing, please consider
Notes:
Known issues:
Please leave testing reports on this issue; feel free to open dedicated issues for bugs |
Test reportI tested this feature with the mdconfig, libnv-rs, and fsx crates on FreeBSD 15 amd64, as well as with a complicated unpublished crate. It's a big step forward! What worked:
What did not work
Another feature request |
Thanks for giving it a spin!
the html files were considered a "final artifact" so we kept them in
We plan to add an
While you can look it up with |
Ahh, that sounds like it would work for me.
Is it possible to use |
Test reportI tested this feature by using tarpaulin with build dir on a few projects. Using the llvm and ptrace engine (as both have slightly different requirements in terms of artefacts). It all worked as expected, likely due to tarpaulin mostly relying on cargo_metadata to get paths to artefacts instead of relying on any structure of the target directory.
How I messed up (what didn't work)
|
FYI Note that snapbox already had This just leaves the use case when a dependency wants to do the test binary lookup (e.g. like |
I'm having a hard time understanding the new feature. The docs, your comments, etc. use the term intermediate but I can't find what the final state is. Are these intermediate files copied somewhere after the crate is compiled? After the entire build is complete? Other? Your last comment ("This just leaves the use case...") seems to imply you covered the "I want to deploy files to the target dir" scenario but I don't understand how to use this feature to do so. Edit: I came from #9661 (comment) and I think I understand now. This feature is orthogonal to the efforts there and you were just providing a heads up of possible impact to workarounds. |
|
‘-Z` flags can be set through the config system, see https://doc.rust-lang.org/cargo/reference/unstable.html
Maybe eventually
|
Test reportThis feature appears to work great for my use case detailed here: #6100 (comment)! Using the following commands: cargo build \
-Zbuild-dir \
--config 'build.build-dir = ".build/cargo"' \
--config 'build.target-dir = ".build/ush"' \
--package ush
cargo build \
-Zbuild-dir \
--config 'build.build-dir = ".build/cargo"' \
--config 'build.target-dir = ".build/ush-lsp-server"' \
--package ush-lsp-server where ush-lsp-server depends on ush with additional features; feature unification is correctly inhibited by building separately; overwriting of artifacts is correctly inhibited by specifying different
|
Having I'm not sure if there's really anything nice that can be done about this, just thought it'd be worthwhile to note as I don't recall it being mentioned before. (I've long wondered what would happen if cargo integrated native audit support and wanted to use |
Don't expose |
I'm not seeing how this is a new problem. Someone can already construct a path like this without variables. Granted, this makes it easier but I could see offering a As for future use, I'm not quite sure what you mean but when we make |
I think it's more obvious that using a path like
I mean that a setting for The only thought I have to actually doing something about this is either documentation about what cargo expects from subdirectories of |
The discussion above got me thinking about how I did some testing and discovered that setting I also checked This feels like a potential footgun. Given the name Having |
miri (13e8790949 2025-05-04) fails to run with
when
is set in After disabling these lines, miri can create the sysroot and works when these lines are reenabled. |
I've opened an issue at RalfJung/rustc-build-sysroot#25 |
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
build.build-dir
config option #15104)build.build_dir
templating support #15236)cargo metadata
support (Addedbuild_directory
field to cargo metadata output #15377)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){
,}
(Added validation for unmatched brackets in build-dir template #15414)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)Stabilizing this would also resolve
Known issues:
build_from_source
makes assumptions on internal Cargo details, breakingcargo-miri
with newbuild-dir
features RalfJung/rustc-build-sysroot#25Decisions
cargo clean
will clean bothtarget-dir
andbuild-dir
CARGO_BUILD_DIR
shortcut forCARGO_BUILD_BUILD_DIR
(likeCARGO_TARGET_DIR
is a shortcut forCARGO_BUILD_TARGET_DIR
)artifact-dir 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)--timings
HTML reportcargo package
's generated.crate
filescargo doc
output (html/css/js/etc)build-dir files (intermediate artifacts, build state, caches):
examples
that contain the hash in the name, bins forbenches
, proc macros, build scripts)OUT_DIR
target/build
).rustc_info.json
)cargo package
's scratchpad used for the verify stepCARGO_TARGET_TMPDIR
files (see rational for this here)cargo report
like future-incompat-reportOpen questions
build-dir
?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){workspace-manifest-path-hash}
and what should it include? Should we shorten to{workspace-hash}
or even just{hash}
?manifest
, unsure about dropping eitherworkspace
orpath
. Users may be confused if they don't realize the path is a part of the hashworkspace-path-hash
in fix(build-dir): Renamed workspace-manifest-path-hash to workspace-path-hash #15334Should we include the Cargo version indeferred{workspace-manifest-path-hash}
so we get unique whole-target directories for easier cleanup (Garbage collect wholetarget/
#13136){workspace-manifest-path-hash}
? See Addedbuild.build_dir
templating support #15236 (comment)workspace-path-hash
#15400{
? See Addedbuild.build_dir
templating support #15236 (comment){
. This includes{{
; escaping has been deferred (Added validation for unmatched brackets in build-dir template #15414)build.build-dir
? Make target dir self-ignoring #15061cargo metadata
? What about that people need to matchcargo metadata
version againstcargo
to get it?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 versionsbuild_directory
field to cargo metadata output #15377, docs(metadata): Added build_directory to cargo metadata documentation #15410CARGO_BUILD_TMPDIR
, deprecatingCARGO_TARGET_TMPDIR
?Deferred
{workspace-manifest-hash}
also hashes the cargo versionrustup update
(Garbage collect wholetarget/
#13136)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{{
is an escaped{
{
, we have the room to allow escaping in the future{
in paths is rare and this isn't a general pathOriginal 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: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 mixedCARGO_TARGET_DIR
, think how to deduplicate and slimCARGO_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 — makeCARGO_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
).a
/.so
, wherelib.crate-type
calls for them. Possibly.rlib
/.rmeta
in the future if there's a stable ABI..d
files for all of the above (so that IDEs and other build systems know when to rebuild the artifacts).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 apath
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
:source = "registry+…"
).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).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
./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../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)../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)../target
contain only workspace-unique files, which makes it justified for every workspace to have one.target/release/exe
etc.--artifact-dir
or.cargo/config
.Notes
No response
The text was updated successfully, but these errors were encountered: