Summary
Today, cargo
stores all user-global files in CARGO_HOME
(~/.cargo
), including
- Caches
- Config
- Installed binaries
Similarly, rustup
stores all user-global files in ~/.rustup
.
This RFC would provide a way for users to emulate platform-specific paths by providing environment variables to control each of these types of paths as an incremental, transitional step towards eventually supporting platform-specific paths.
Motivation
Benefits include:
- Using a
.cargo
directory is violating the recommendations/rules on most operating systems. (Linux, Windows, macOS. Especially painful on Windows, where dotfiles are not hidden) - Putting caches in designated cache directories allows backup tools to ignore them. (Linux, Windows, macOS. Example: Time Machine ignores the cache directory on macOS)
- It makes it easier for users to manage, share and version-control their configuration files, as configuration files from different applications end up in the same place, instead of being intermingled with cache files. (Linux and macOS)
- Cargo contributes to the slow cleanup of the
$HOME
directory by stopping to add its application-private clutter to it. (Linux) - Using a standard directory for binary outputs can allow the user to execute
Cargo-installed binaries without modifying their
PATH
variable. (Linux)
See arch's wiki for a non-exhaustive list of software that has native support for XDG paths and software that allows emulating it, like in this proposal.
While providing full support for XDG paths, and the Windows equivalent, would be ideal, we are going for this scaled back solution for now because
- We'll likely want a transition window anyways and this just splits each phase into its own RFC, rather than deciding on every phase's details up front
- We can get users benefits now without getting bogged down in the details like whether macOS users should use XDG or OS native paths
- This allows us to collect feedback and iterate, rather than predict how important each use case is (e.g. how to lookup the paths)
Guide-level explanation
Users who want to split up ~/.cargo
or ~/rustup
into platform-specific paths can set
CARGO_CONFIG_HOME
CARGO_DATA_HOME
CARGO_BIN_HOME
CARGO_CONFIG_HOME
RUSTUP_CONFIG_HOME
RUSTUP_CACHE_HOME
Previously, we had CARGO_HOME
and RUSTUP_HOME
which allowed moving the directories as a monolith.
A Linux user could run the following migration script
CARGO_HOME=$(realpath ~/cargo)
CARGO_CONFIG_HOME=$(realpath ~/.config/cargo)
mkdir -p $CARGO_CONFIG_HOME
echo CARGO_CONFIG_HOME=$CARGO_CONFIG_HOME >> ~/.bashrc
CARGO_DATA_HOME=$(realpath ~/.local/share/cargo)
mkdir -p $CARGO_DATA_HOME
echo CARGO_DATA_HOME=$CARGO_DATA_HOME >> ~/.bashrc
CARGO_BIN_HOME=$(realpath ~/.local/share/cargo/bin)
echo CARGO_BIN_HOME=$CARGO_BIN_HOME >> ~/.bashrc
CARGO_CACHE_HOME=$(realpath ~/.cache/cargo)
mkdir -p $CARGO_CACHE_HOME
echo CARGO_CACHE_HOME=$CARGO_CACHE_HOME >> ~/.bashrc
RUSTUP_HOME=$(realpath ~/rustup)
RUSTUP_CONFIG_HOME=$(realpath ~/.config/rustup)
mkdir -p $RUSTUP_CONFIG_HOME
echo RUSTUP_CONFIG_HOME=$RUSTUP_CONFIG_HOME >> ~/.bashrc
RUSTUP_CACHE_HOME=$(realpath ~/.cache/rustup)
mkdir -p $RUSTUP_CACHE_HOME
echo RUSTUP_CACHE_HOME=$RUSTUP_CACHE_HOME >> ~/.bashrc
function migrate_cargo_config {
local item=$1
mv $CARGO_HOME/$item $CARGO_CONFIG_HOME/$item
ln -s $CARGO_CONFIG_HOME/$item $CARGO_HOME/$item
}
function migrate_cargo_data {
local item=$1
mv $CARGO_HOME/$item $CARGO_DATA_HOME/$item
ln -s $CARGO_DATA_HOME/$item $CARGO_HOME/$item
}
function migrate_cargo_bin {
mv $CARGO_HOME/bin $CARGO_BIN_HOME
ln -s $CARGO_BIN_HOME/$item $CARGO_HOME/$item
}
function migrate_cargo_cache {
local item=$1
mv $CARGO_HOME/$item $CARGO_CACHE_HOME/$item
ln -s $CARGO_CACHE_HOME/$item $CARGO_HOME/$item
}
function migrate_rustup_config {
local item=$1
mv $RUSTUP_HOME/$item $RUSTUP_CONFIG_HOME/$item
ln -s $RUSTUP_CONFIG_HOME/$item $RUSTUP_HOME/$item
}
function migrate_rustup_cache {
local item=$1
mv $RUSTUP_HOME/$item $RUSTUP_CACHE_HOME/$item
ln -s $RUSTUP_CACHE_HOME/$item $RUSTUP_HOME/$item
}
migrate_cargo_config config.toml
migrate_cargo_data env
migrate_cargo_data .crates.toml
migrate_cargo_data .crates2.json
migrate_cargo_data credentials.toml # avoid backing up secrets
migrate_cargo_bin
migrate_cargo_cache registry
migrate_cargo_cache git
migrate_cargo_cache target # used by "cargo script"
migrate_cargo_cache .package-cache
migrate_cargo_cache .package-cache-mutate
migrate_rustup_config settings.toml
migrate_rustup_cache downloads
migrate_rustup_cache tmp
migrate_rustup_cache toolchains
migrate_rustup_cache update-hashes
Reference-level explanation
We'll add to the confusingly named home
package, the following
cargo_config_home
: Returns the first matchCARGO_CONFIG_HOME
, if sethome::cargo_home()
cargo_data_home
: Returns the first matchCARGO_DATA_HOME
, if sethome::cargo_home()
cargo_bin_home
: Returns the first matchCARGO_BIN_HOME
, if sethome::cargo_home().join("bin")
cargo_cache_home
: Returns the first matchCARGO_CACHE_HOME
, if sethome::cargo_home()
rustup_config_home
: Returns the first matchRUSTUP_CONFIG_HOME
, if sethome::rustup_home()
rustup_cache_home
: Returns the first matchRUSTUP_CACHE_HOME
, if set- Return
home::rustup_home()
Cargo will be modified to call these more specific home directories, based on the above migration script.
Each of these new environment variables will be blocked from being set in config.toml
s [env]
table.
Drawbacks
Why should we not do this?
~/.cargo/bin
and related files requires both updated rustup and cargo
rustup assumes complete control of ~/.cargo/bin
and people might map it to ~/.local/bin
which would cause unexpected behavior
Rationale and alternatives
credentials.toml
was put under CARGO_DATA_HOME
as its program-managed data
CARGO_CONFIG_HOME
might cause it to get backed up to public git repos, exposing secrets- Ideally people will start migrating to OS-native credential stores
Existing issues:
Previous proposals:
An alternative approach is to rely on caches being throw-away and to instead do
- Read both configs (like git)
- Hard split for cache
Reasons we didn't go with this:
- This doesn't work for
CARGO_BIN_HOME
- Rustup doesn't support a layered config for reading from two locations at once
Prior art
git
- Layers both the old
~/.git/config
and the new~/config/git/config
on top of each other - No user cache
ansible
ANSIBLE_HOME
ANSIGLE_HOME_CONFIG
(config file path)ANSIBLE_GALAXY_CACHE_DIR
asdf
ASDF_CONFIG_FILE
ASDF_DATA_DIR
For more, see arch's wiki entry for XDG Base Directory.
Unresolved questions
- Does config fallback work for rustup?
- What should be done for env variables that are empty (treat is unset?) or relative (unset? error?)?
- Is rustup
update-hashes
cache, state, data, or config? - Is rustup
toolchains
cache, state, data, or config? - What all paths should
rustup uninstall
delete?
Future possibilities
Platform-specific paths
Note: The information here is for illustrative purposes and discussing the details is only relevant so far as it might affect a decision being made within this RFC.
Update home
package with the following
cargo_config_home
: Returns the first matchCARGO_CONFIG_HOME
, if setCARGO_HOME
, if setcargo_home()
, if present- linux or macOS:
$XDG_CONFIG_HOME/cargo
, if set~/.config/cargo
- windows:
AppData\Roaming\Cargo
cargo_data_home
: Returns the first matchCARGO_DATA_HOME
, if setCARGO_HOME
, if setcargo_home()
, if present- linux or macOS:
$XDG_DATA_HOME/cargo
, if set~/.local/share/cargo
- windows:
- TBD
cargo_bin_home
: Returns the first matchCARGO_BIN_HOME
, if setCARGO_HOME/bin
, if setcargo_home().join("bin")
, if present- linux or macOS:
- TBD
- windows:
- TBD
cargo_cache_home
: Returns the first matchCARGO_CACHE_HOME
, if setCARGO_HOME
, if setcargo_home()
, if present- linux or macOS:
$XDG_CACHE_HOME/cargo
, if set~/.cache/cargo
- windows:
AppData\Local\Temp\Cargo
rustup_config_home
: ReturnsRUSTUP_CONFIG_HOME
, if setRUSTUP_HOME
, if setcargo_home()
, if present- linux or macOS:
$XDG_CONFIG_HOME/rustup
, if set~/.config/rustup
- windows:
AppData\Roaming\Rustup
rustup_cache_home
: Returns the first matchRUSTUP_CACHE_HOME
, if setRUSTUP_HOME
, if setcargo_home()
, if present- linux or macOS:
$XDG_CACHE_HOME/rustup
, if set~/.cache/rustup
- windows:
AppData\Local\Temp\Cargo
Windows: Roaming app data is no longer supported on Windows 11. We'll still use these paths to communicate intent to Windows to be future compatible.
macOS: This currently favors XDG over the platform-specific application directories to be more consistent with other CLI developer tooling. The platform-specific application directories can always be emulated by setting the appropriate environment variables. The final decision will be left to the relevant RFC
Open questions
- The paths for macOS
- Local vs roaming on Windows
- Should the higher-precedence-with-presence check be used for
cargo_home
or platform-specific paths? - How do we want to handle the fact that rustup proxies set
CARGO_HOME
? When we get to the state when users aren't explicitly setting more specific variables, rustup'sCARGO_HOME
will win out
Automated migration
Add a rustup
subcommand to migrate people to this
Compat symlinks
Have rustup manage symlinks when installing older toolchains
Cargo CLI for reading the values
While applications can call into home
to get the values, sometimes a user will want it interactively.
Potential ideas include:
- A
cargo dirs
built-in command - Reuse
cargo config
cargo --print <target>
much likerustc --print <target>
Allow setting CARGO_CONFIG_HOME
from .cargo/config.toml
For sharing of caches between host and docker,
rust-lang/cargo#6452
requested config-control over the global cache location.
Being a cache,
all results should be safe to re-calculate,
unless some of the other CARGO_*_HOME
variables.