Skip to content

Commit

Permalink
Enable uv to replace and delete itself on Windows (#8914)
Browse files Browse the repository at this point in the history
## Summary

On Windows, we can't delete the currently-running executable -- at
least, not trivially. But the
[`self_replace`](https://docs.rs/self-replace/latest/self_replace/)
crate can help us here.

Closes #1368.

Closes #4980.

## Test Plan

On my Windows machine:

- `maturin build`
- `python -m venv .venv`
- `.venv/Scripts/activate`
- `pip install /path/to/uv.whl`
- `uv pip install /path/to/uv.whl`
- `uv pip uninstall uv`
  • Loading branch information
charliermarsh authored Nov 8, 2024
1 parent 9cd51c8 commit 0db3884
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ rustix = { version = "0.38.37", default-features = false, features = ["fs", "std
same-file = { version = "1.0.6" }
schemars = { version = "0.8.21", features = ["url"] }
seahash = { version = "4.1.0" }
self-replace = { version = "1.5.0" }
serde = { version = "1.0.210", features = ["derive"] }
serde-untagged = { version = "0.1.6" }
serde_json = { version = "1.0.128" }
Expand Down
4 changes: 4 additions & 0 deletions crates/uv-install-wheel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ tracing = { workspace = true }
walkdir = { workspace = true }
zip = { workspace = true }

[target.'cfg(target_os = "windows")'.dependencies]
same-file = { workspace = true }
self-replace = { workspace = true }

[dev-dependencies]
anyhow = { version = "1.0.89" }
assert_fs = { version = "1.1.2" }
Expand Down
29 changes: 29 additions & 0 deletions crates/uv-install-wheel/src/uninstall.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,39 @@ pub fn uninstall_wheel(dist_info: &Path) -> Result<Uninstall, Error> {
let mut file_count = 0usize;
let mut dir_count = 0usize;

#[cfg(windows)]
let itself = std::env::current_exe().ok();

// Uninstall the files, keeping track of any directories that are left empty.
let mut visited = BTreeSet::new();
for entry in &record {
let path = site_packages.join(&entry.path);

// On Windows, deleting the current executable is a special case.
#[cfg(windows)]
if let Some(itself) = itself.as_ref() {
if itself
.file_name()
.is_some_and(|itself| path.file_name().is_some_and(|path| itself == path))
{
if same_file::is_same_file(itself, &path).unwrap_or(false) {
tracing::debug!("Detected self-delete of executable: {}", path.display());
match self_replace::self_delete_outside_path(site_packages) {
Ok(()) => {
trace!("Removed file: {}", path.display());
file_count += 1;
if let Some(parent) = path.parent() {
visited.insert(normalize_path(parent));
}
}
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
Err(err) => return Err(err.into()),
}
continue;
}
}
}

match fs::remove_file(&path) {
Ok(()) => {
trace!("Removed file: {}", path.display());
Expand Down

0 comments on commit 0db3884

Please sign in to comment.