Skip to content

Support for rustup category home mode - #5056

Draft
Cloud0310 wants to merge 24 commits into
rust-lang:mainfrom
Cloud0310:xdg-final
Draft

Support for rustup category home mode#5056
Cloud0310 wants to merge 24 commits into
rust-lang:mainfrom
Cloud0310:xdg-final

Conversation

@Cloud0310

@Cloud0310 Cloud0310 commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Related to #247.

Summary

Rustup historically stores configuration, state, data, and caches under a single RUSTUP_HOME (defaulting to $HOME/.rustup).
This prevents rustup from adhering to platform-standard directories (XDG on Unix, Known Folders on Windows).

This PR introduces an opt-in category-home layout guarded by RUSTUP_USE_CATEGORY_HOME, while preserving the legacy single-directory layout by default.

When category mode is enabled (RUSTUP_USE_CATEGORY_HOME=1 or any non-empty value other than "0"):

Category Contents Platform Default
Cache downloads/, tmp/, update-hashes/ $XDG_CACHE_HOME/rustup or ~/.cache/rustup
Config settings.toml $XDG_CONFIG_HOME/rustup or ~/.config/rustup
Data toolchains/, fallback/ $XDG_DATA_HOME/rustup or ~/.local/share/rustup
State state.toml $XDG_STATE_HOME/rustup or ~/.local/state/rustup
Bin Proxies (rustc, cargo, etc.) Unix: ~/.local/bin
Windows: %USERPROFILE%/.local/bin (tentative)

Important

Why gated?
Gating both category overrides and platform defaults avoids split-brain installations where external tools (e.g. older rust-analyzer) still look exclusively in RUSTUP_HOME for toolchains.


Resolution Precedence

Note

This order still needs discussion, upon whether we should consider RUSTUP_HOME and CARGO_HOME. See open questions.

Design Guidance

We consider this as a breaking change, and RUSTUP_HOME as a purely legacy env
var, so in new mode, we don't use it anymore, so, on category mode available,
the fallback paths are ignored.

Categories (Cache / Config / Data / State)

  1. RUSTUP_<CATEGORY>_HOME (if category mode is enabled)
  2. Platform category default (if category mode is enabled)
  3. RUSTUP_HOME (legacy shared override)
  4. $HOME/.rustup (legacy fallback)

Note

On Unix, explicit absolute XDG_variables take precedence over $HOME-derived paths. Empty or relative XDG values are ignored.
Windows does not consult XDG variables.

Bin Directory

  1. RUSTUP_BIN_HOME (if category mode is enabled)
  2. Platform default bin directory (if category mode is enabled: ~/.local/bin)
  3. $CARGO_HOME/bin
  4. $HOME/.cargo/bin

Important Commits

feat(home): resolve category homes

Implements the core path resolution logic structured for future home crate compatibility across three layers:

  1. Underlying platform resolution: Handles Unix XDG environment variables and Windows native directories.
  2. home crate facade (mod home): Re-exports standard home APIs, implements RUSTUP_<CATEGORY>_HOME resolution and RUSTUP_HOME fallback without rollout logic.
  3. Rollout strategy: Implements the gating logic driven by RUSTUP_USE_CATEGORY_HOME.

feat(uninstall): remove legacy and category rustup homes

Removes rustup home directories (config, cache, data, state) for both legacy and category layouts. (Excludes bin directory).

feat(uninstall): clean legacy and category cargo bin homes

Cleans up rustup-managed proxy binaries and symlinks across both legacy and category bin directories, leaving unrelated user binaries untouched.

feat(installer): migrate bin and env paths to split homes

Completes the installer-side migration from Cargo-owned paths to Rustup's resolved homes. In category mode, rustup binaries, proxy links, self-update artifacts, executable checks, and child-process PATH setup now use rustup_bin_home, while generated shell environment scripts use the config home through rustup_env_home. Legacy mode preserves the existing $CARGO_HOME layout.

The commit also updates shell profile integration, Windows PATH handling, installer messages, and end-to-end coverage for the split layout.

Open Questions

  1. Windows Bin Location:

    • Currently set to %USERPROFILE%/.local/bin. Needs community consensus on whether this is the appropriate platform default for Windows.
    • Requires validating PATH precedence and migration UX from %USERPROFILE%\.cargo\bin.
  2. home Crate Compatibility:

    • Should category resolution eventually be upstreamed into the home crate, or should rustup maintain its own internal implementation permanently?
  3. RUSTUP_HOME is deprecated or not

    • Should we consider RUSTUP_HOME as an overall category override var?
    • Another option: consider RUSTUP_HOME as an overall category override variable,
      then resolution order is:
    1. RUSTUP_<CATEGORY>_HOME
    2. RUSTUP_HOME
    3. Platform category default

    overall variable. Then the the resolution order would be:

Following tasks

  • Create a cli interface for migration, rustup migration category-mode, for
    creating symlinks in legacy dir, pointing to coorresponding category dirs.
  • Add warning message for asking user for migrating to new category mode.
  • Update user-guide for new mode

@rami3l rami3l self-assigned this Sep 5, 2026
@Cloud0310

Copy link
Copy Markdown
Contributor Author

Fixed a minor CI/CD problem on Windows.

This is an alternative design commit and will be rewritten later.

In category mode, resolve RUSTUP_<CATEGORY>_HOME first, then platform
defaults, ignoring RUSTUP_HOME. Keep legacy resolution when category
mode is disabled.

Include the corresponding documentation, installer messages,
test-environment isolation, and focused regression tests.
@Cloud0310

Cloud0310 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

About Windows' bin directory location problem, here's more context:

Directory Pros Cons Precedent
%USERPROFILE%\.local\bin Consistent across platforms; shared PATH entry Not a Windows Known Folder; shared filenames complicate collision handling and cleanup uv and uv tool install use this directory
%LOCALAPPDATA%\rustup\bin Clear ownership; relatively short path Separate PATH entry; application-defined layout pnpm 11+ uses %LOCALAPPDATA%\pnpm\bin
%LOCALAPPDATA%\Programs\Rustup\bin Standard per-user programs parent directory; clear ownership Longer path; separate PATH entry VS Code user setup installs under %LOCALAPPDATA%\Programs\Microsoft VS Code

@ChrisDenton @rami3l , Current PR's defaulting to use %USERPROFILE%\.local\bin, but I think we need to discuss upon this.

@ChrisDenton

Copy link
Copy Markdown
Member

Hm, since I feel one of the points of this feature is to use more platform native paths, I don't think %USERPROFILE%\.local\bin is the best choice. That seems quite alien to Windows. I suppose a more Windows-like equivalent would be %LOCALAPPDATA%\Programs\bin but I don't think there's much (if any) precedent for that.

I think either of the other two options are justifiable. Putting it under "Programs" is the most technically correct I guess but if anything there's a weaker precedent for that, albeit applications that used to support XP wouldn't have used it because "Programs" wasn't a thing back then.

@djc

djc commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

What are we doing for macOS here? The XDG stuff definitely doesn't feel native for it.

@ChrisDenton

Copy link
Copy Markdown
Member

There appears to be a fairly strong use of XDG for macOS cli utilities. I cannot find any officially documented conventions that aren't intended for app bundles but unofficially almost everything in the shell seems to treat it as a Unix (which it is).

@Cloud0310

Copy link
Copy Markdown
Contributor Author

There appears to be a fairly strong use of XDG for macOS cli utilities. I cannot find any officially documented conventions that aren't intended for app bundles but unofficially almost everything in the shell seems to treat it as a Unix (which it is).

That's what @rami3l and I wants as well.
I personally agree with this idea to consider macOS as Unix, which also agrees with current repo's state: we only maintain two major platforms: Unix/Windows.
Currently introducing another macOS specific code would not fit in.

@rami3l

rami3l commented Sep 6, 2026

Copy link
Copy Markdown
Member

In terms of precedence of different configuration options, as I have discussed with @Cloud0310, the only remaining concern is what to do when RUSTUP_USE_CATEGORY_HOME=1 meets explicit RUSTUP_HOME or CARGO_HOME overrides.

As per the previous summary in https://blog.rust-lang.org/inside-rust/2025/10/01/this-development-cycle-in-cargo-1.90/#all-hands-xdg-paths:

The most immediate problem is Rustup setting CARGO_HOME. To not break compatibility, Cargo should always respect it if CARGO_HOME is set.

This means CARGO_HOME must have higher precedence than RUSTUP_USE_CATEGORY_HOME=1. To ensure consistency across both rustup and cargo, I propose that we treat RUSTUP_HOME similarly, hence the following fallback order:

  1. RUSTUP_XXX_HOME. Setting this means that the user has explicitly deviated from the old paths. RUSTUP_USE_CATEGORY_HOME=1 only.
  2. The old override path, whether that is CARGO_HOME/bin or RUSTUP_HOME/xxx.
  3. The platformdirs-derived path that is derived from XDG on Unix, or the registry on Windows. RUSTUP_USE_CATEGORY_HOME=1 only.
  4. The old default path, whether that is ~/.cargo/bin or ~/.rustup/xxx.

@rami3l

rami3l commented Sep 6, 2026

Copy link
Copy Markdown
Member

What are we doing for macOS here? The XDG stuff definitely doesn't feel native for it.

AFAIK the standard library currently uses the same logic for macOS and Linux, so I think that is a very good default to start with: https://doc.rust-lang.org/stable/std/os/unix/xdg/fn.cache_home_dir.html

Also, this is the convention followed by other existing tools such as neovim and uv.

I am aware of the frequently-quoted macOS documentation page which to some may suggest paths like ~/Library/ApplicationSupport/rustup/toolchains etc.

I would again point out (as @ChrisDenton has mentioned above) that these guidelines are specific to app bundles (that page is full of app bundle-specific paths, with Library being the only out-of-bundle directory), for which using this convention may facilitate migration to iOS apps, sandboxed bundles or iCloud-synced data. However, none of the above would be interesting to rustup usage on macOS (also note that uv has explicitly migrated from macOS bundle-specific paths to general XDG paths).

If the user really prefers to place stuff in ApplicationSupport, we have explicit overrides for that on both rustup and XDG levels. Oftentimes they would only need to set an $XDG_CONFIG_HOME override and that will work for all their CLI apps, not just rustup.

@Cloud0310

Copy link
Copy Markdown
Contributor Author

In terms of precedence of different configuration options, as I have discussed with @Cloud0310, the only remaining concern is what to do when RUSTUP_USE_CATEGORY_HOME=1 meets explicit RUSTUP_HOME or CARGO_HOME overrides.

As per the previous summary in https://blog.rust-lang.org/inside-rust/2025/10/01/this-development-cycle-in-cargo-1.90/#all-hands-xdg-paths:

The most immediate problem is Rustup setting CARGO_HOME. To not break compatibility, Cargo should always respect it if CARGO_HOME is set.

This means CARGO_HOME must have higher precedence than RUSTUP_USE_CATEGORY_HOME=1. To ensure consistency across both rustup and cargo, I propose that we treat RUSTUP_HOME similarly, hence the following fallback order:

  1. RUSTUP_XXX_HOME. Setting this means that the user has explicitly deviated from the old paths. RUSTUP_USE_CATEGORY_HOME=1 only.
  2. The old override path, whether that is CARGO_HOME/bin or RUSTUP_HOME/xxx.
  3. The platformdirs-derived path that is derived from XDG on Unix, or the registry on Windows. RUSTUP_USE_CATEGORY_HOME=1 only.
  4. The old default path, whether that is ~/.cargo/bin or ~/.rustup/xxx.

This also has an effect on the env var recursive forwarding system, I guess once we're in new mode, I need to stop forwarding cargo_home and rustup_home in case of causing underlaying program having confusing about if this var is from manually setting or from rustup resolution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants