-
Notifications
You must be signed in to change notification settings - Fork 1k
fix!: stop deleting $CARGO_HOME during uninstall #4861
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
Draft
Cloud0310
wants to merge
10
commits into
rust-lang:main
Choose a base branch
from
Cloud0310:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+233
−114
Draft
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
93fd25a
chore(deps): lock file maintenance
renovate[bot] ef6d201
chore: re-organize imports
Cloud0310 f9f7269
refactor: rename delete_rustup_and_cargo_home to clean_cargo_bin
Cloud0310 c573da8
refactor: move PATH cleanup into clean_cargo_bin
Cloud0310 bd3af44
refactor: share cargo bin cleanup helper
Cloud0310 18a43de
fix: remove rustup proxies during uninstall
Cloud0310 9978bdb
fix: preserve CARGO_HOME during uninstall
Cloud0310 0adbc03
test: cover cargo bin removal during uninstall
Cloud0310 5f57ff1
test: cover cargo bin PATH cleanup during uninstall
Cloud0310 800d957
test: cover Windows cargo bin cleanup during uninstall
Cloud0310 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,8 +24,8 @@ | |
| //! During uninstall (`rustup self uninstall`): | ||
| //! | ||
| //! * Delete `$RUSTUP_HOME`. | ||
| //! * Delete everything in `$CARGO_HOME`, including | ||
| //! the rustup binary and its hardlinks | ||
| //! * Delete rustup tool proxy binaries from `$CARGO_HOME`/bin. | ||
| //! * Delete `$CARGO_HOME`/bin if it is empty after uninstall. | ||
| //! | ||
| //! Deleting the running binary during uninstall is tricky | ||
| //! and racy on Windows. | ||
|
|
@@ -47,7 +47,7 @@ use clap::ValueEnum; | |
| use clap::builder::PossibleValue; | ||
| use clap_cargo::style::{GOOD, WARN}; | ||
| use itertools::Itertools; | ||
| use same_file::Handle; | ||
| use same_file::{Handle, is_same_file}; | ||
| use serde::{Deserialize, Serialize}; | ||
| use tracing::{error, info, trace, warn}; | ||
|
|
||
|
|
@@ -80,7 +80,7 @@ mod shell; | |
| #[cfg(unix)] | ||
| mod unix; | ||
| #[cfg(unix)] | ||
| use unix::{delete_rustup_and_cargo_home, do_add_to_path, do_remove_from_path}; | ||
| use unix::{do_add_to_path, do_remove_from_path}; | ||
| #[cfg(unix)] | ||
| pub(crate) use unix::{run_update, self_replace}; | ||
|
|
||
|
|
@@ -91,7 +91,7 @@ pub use windows::complete_windows_uninstall; | |
| #[cfg(all(windows, feature = "test"))] | ||
| pub use windows::{RegistryGuard, RegistryValueId, USER_PATH, get_path}; | ||
| #[cfg(windows)] | ||
| use windows::{delete_rustup_and_cargo_home, do_add_to_path, do_remove_from_path}; | ||
| use windows::{do_add_to_path, do_remove_from_path}; | ||
| #[cfg(windows)] | ||
| pub(crate) use windows::{run_update, self_replace}; | ||
|
|
||
|
|
@@ -1048,6 +1048,12 @@ async fn maybe_install_rust(opts: InstallOpts<'_>, cfg: &mut Cfg<'_>) -> Result< | |
| Ok(()) | ||
| } | ||
|
|
||
| /// Uninstall process: | ||
| /// 1. Remove rustup home. | ||
| /// 2. Clean up rustup tool proxies. | ||
| /// 3. Remove rustup binary file. | ||
| /// 4. Try to clean up $CARGO_HOME/bin if it's empty. | ||
| /// 5. Upon successfully removing $CARGO_HOME/bin, clean up $PATH. | ||
| pub(crate) fn uninstall( | ||
| no_prompt: bool, | ||
| no_modify_path: bool, | ||
|
|
@@ -1061,7 +1067,8 @@ pub(crate) fn uninstall( | |
|
|
||
| let cargo_home = process.cargo_home()?; | ||
|
|
||
| if !cargo_home.join(format!("bin/rustup{EXE_SUFFIX}")).exists() { | ||
| let rustup_path = cargo_home.join(format!("bin/rustup{EXE_SUFFIX}")); | ||
| if !rustup_path.exists() { | ||
| return Err(CliError::NotSelfInstalled { p: cargo_home }.into()); | ||
| } | ||
|
|
||
|
|
@@ -1090,72 +1097,64 @@ pub(crate) fn uninstall( | |
| utils::remove_dir("rustup_home", &rustup_dir)?; | ||
| } | ||
|
|
||
| info!("removing cargo home"); | ||
| // Clean up rustup tool links | ||
| let cargo_bin_path = cargo_home.join("bin"); | ||
| let proxy_paths = TOOLS | ||
| .iter() | ||
| .chain(DUP_TOOLS.iter()) | ||
| .map(|tool| cargo_bin_path.join(format!("{tool}{EXE_SUFFIX}"))); | ||
|
Cloud0310 marked this conversation as resolved.
|
||
|
|
||
| // Remove CARGO_HOME/bin from PATH | ||
| if !no_modify_path { | ||
| do_remove_from_path(process)?; | ||
| for proxy_path in proxy_paths { | ||
| if is_same_file(&proxy_path, &rustup_path).unwrap_or(false) { | ||
| utils::remove_file("rustup tool proxy", &proxy_path)?; | ||
| } | ||
| } | ||
|
|
||
| // Delete everything in CARGO_HOME *except* the rustup bin | ||
| // Remove rustup executable and then clean up `$CARGO_HOME/bin` if empty. | ||
| // Optionally remove it from $PATH if successfully removed. | ||
| #[cfg(unix)] | ||
| clean_cargo_bin(process, no_modify_path)?; | ||
| // NOTE: On windows, this is *tricky*, | ||
| // the running executable and on Windows can't be unlinked until the process exits. | ||
| // see: windows::{complete_windows_uninstall,clean_cargo_bin} | ||
| #[cfg(windows)] | ||
| windows::clean_cargo_bin(process, no_modify_path)?; | ||
|
|
||
| // First everything except the bin directory | ||
| let diriter = fs::read_dir(&cargo_home).map_err(|e| CliError::ReadDirError { | ||
| p: cargo_home.clone(), | ||
| source: e, | ||
| })?; | ||
| for dirent in diriter { | ||
| let dirent = dirent.map_err(|e| CliError::ReadDirError { | ||
| p: cargo_home.clone(), | ||
| source: e, | ||
| })?; | ||
| if dirent.file_name().to_str() != Some("bin") { | ||
| if dirent.path().is_dir() { | ||
| utils::remove_dir("cargo_home", &dirent.path())?; | ||
| } else { | ||
| utils::remove_file("cargo_home", &dirent.path())?; | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
-1103
to
-1119
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not clear from reviewing this (presumably Unix-only) commit why this (presumably platform-independent) logic could be removed. |
||
| info!("rustup is uninstalled"); | ||
| Ok(ExitCode::SUCCESS) | ||
| } | ||
|
|
||
| // Then everything in bin except rustup and tools. These can't be unlinked | ||
| // until this process exits (on windows). | ||
| let tools = TOOLS | ||
| .iter() | ||
| .chain(DUP_TOOLS.iter()) | ||
| .map(|t| format!("{t}{EXE_SUFFIX}")); | ||
| let tools: Vec<_> = tools.chain(vec![format!("rustup{EXE_SUFFIX}")]).collect(); | ||
| let bin_dir = cargo_home.join("bin"); | ||
| let diriter = fs::read_dir(&bin_dir).map_err(|e| CliError::ReadDirError { | ||
| p: bin_dir.clone(), | ||
| source: e, | ||
| })?; | ||
| for dirent in diriter { | ||
| let dirent = dirent.map_err(|e| CliError::ReadDirError { | ||
| p: bin_dir.clone(), | ||
| source: e, | ||
| })?; | ||
| let name = dirent.file_name(); | ||
| let file_is_tool = name.to_str().map(|n| tools.iter().any(|t| *t == n)); | ||
| if file_is_tool == Some(false) { | ||
| if dirent.path().is_dir() { | ||
| utils::remove_dir("cargo_home", &dirent.path())?; | ||
| } else { | ||
| utils::remove_file("cargo_home", &dirent.path())?; | ||
| } | ||
| /// Remove the `$CARGO_HOME/bin` directory if it's empty. | ||
| /// On success, remove it from `$PATH` unless `no_modify_path` is set. | ||
| /// If the directory is not empty, emit a warning and return success. | ||
| fn clean_cargo_bin(process: &Process, no_modify_path: bool) -> Result<()> { | ||
| // Remove rustup binary | ||
| let cargo_bin_path = process.cargo_home()?.join("bin"); | ||
| let rustup_path = cargo_bin_path.join(format!("rustup{EXE_SUFFIX}")); | ||
|
|
||
| utils::remove_file("rustup_bin", &rustup_path)?; | ||
|
|
||
| // Remove $CARGO_HOME/bin | ||
| let cargo_bin_path_display = cargo_bin_path.display(); | ||
| info!("removing empty cargo bin directory `{cargo_bin_path_display}`"); | ||
|
|
||
| let Err(e) = fs::remove_dir(&cargo_bin_path) else { | ||
| if !no_modify_path { | ||
| info!("removing cargo bin directory `{cargo_bin_path_display}` from $PATH"); | ||
| do_remove_from_path(process)?; | ||
| } | ||
| } | ||
|
|
||
| info!("removing rustup binaries"); | ||
| return Ok(()); | ||
| }; | ||
|
|
||
| // Delete rustup. This is tricky because this is *probably* | ||
| // the running executable and on Windows can't be unlinked until | ||
| // the process exits. | ||
| delete_rustup_and_cargo_home(process)?; | ||
| if e.kind() == io::ErrorKind::DirectoryNotEmpty { | ||
| warn!("keeping non-empty cargo bin directory `{cargo_bin_path_display}`"); | ||
|
|
||
| info!("rustup is uninstalled"); | ||
| return Ok(()); | ||
| } | ||
|
|
||
| Ok(ExitCode::SUCCESS) | ||
| Err(e) | ||
| .with_context(|| format!("failed to remove cargo bin directory `{cargo_bin_path_display}`")) | ||
| } | ||
|
|
||
| #[derive(Clone, Copy, Debug)] | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest introducing this in a separate first commit, and then updating it in each commit (where necessary) as a way of documenting what is changed.
View changes since the review