mirror of https://github.com/astral-sh/uv
Enable `uv tool uninstall uv` on Windows (#8963)
## Summary Extending self-delete and self-replace functionality to uv itself on Windows. Closes https://github.com/astral-sh/uv/issues/6400.
This commit is contained in:
parent
389a26ef9e
commit
3ee2b10738
|
|
@ -4432,6 +4432,7 @@ dependencies = [
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rkyv",
|
"rkyv",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
"self-replace",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"similar",
|
"similar",
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,8 @@ impl InstalledTools {
|
||||||
environment_path.user_display()
|
environment_path.user_display()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO(charlie): On Windows, if the current executable is in the directory,
|
||||||
|
// we need to use `safe_delete`.
|
||||||
fs_err::remove_dir_all(environment_path)?;
|
fs_err::remove_dir_all(environment_path)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,8 @@ pub(crate) fn create(
|
||||||
if allow_existing {
|
if allow_existing {
|
||||||
debug!("Allowing existing directory");
|
debug!("Allowing existing directory");
|
||||||
} else if location.join("pyvenv.cfg").is_file() {
|
} else if location.join("pyvenv.cfg").is_file() {
|
||||||
|
// TODO(charlie): On Windows, if the current executable is in the directory,
|
||||||
|
// we need to use `safe_delete`.
|
||||||
debug!("Removing existing directory");
|
debug!("Removing existing directory");
|
||||||
fs::remove_dir_all(location)?;
|
fs::remove_dir_all(location)?;
|
||||||
fs::create_dir_all(location)?;
|
fs::create_dir_all(location)?;
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,9 @@ walkdir = { workspace = true }
|
||||||
which = { workspace = true }
|
which = { workspace = true }
|
||||||
zip = { workspace = true }
|
zip = { workspace = true }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
self-replace = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = { version = "2.0.16" }
|
assert_cmd = { version = "2.0.16" }
|
||||||
assert_fs = { version = "1.1.2" }
|
assert_fs = { version = "1.1.2" }
|
||||||
|
|
|
||||||
|
|
@ -125,20 +125,13 @@ pub(crate) fn install_executables(
|
||||||
return Ok(ExitStatus::Failure);
|
return Ok(ExitStatus::Failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if they exist, before installing
|
// Error if we're overwriting an existing entrypoint, unless the user passed `--force`.
|
||||||
|
if !force {
|
||||||
let mut existing_entry_points = target_entry_points
|
let mut existing_entry_points = target_entry_points
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, _, target_path)| target_path.exists())
|
.filter(|(_, _, target_path)| target_path.exists())
|
||||||
.peekable();
|
.peekable();
|
||||||
|
if existing_entry_points.peek().is_some() {
|
||||||
// Ignore any existing entrypoints if the user passed `--force`, or the existing recept was
|
|
||||||
// broken.
|
|
||||||
if force {
|
|
||||||
for (name, _, target) in existing_entry_points {
|
|
||||||
debug!("Removing existing executable: `{name}`");
|
|
||||||
fs_err::remove_file(target)?;
|
|
||||||
}
|
|
||||||
} else if existing_entry_points.peek().is_some() {
|
|
||||||
// Clean up the environment we just created
|
// Clean up the environment we just created
|
||||||
installed_tools.remove_environment(name)?;
|
installed_tools.remove_environment(name)?;
|
||||||
|
|
||||||
|
|
@ -159,14 +152,26 @@ pub(crate) fn install_executables(
|
||||||
.join(", ")
|
.join(", ")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let itself = std::env::current_exe().ok();
|
||||||
|
|
||||||
for (name, source_path, target_path) in &target_entry_points {
|
for (name, source_path, target_path) in &target_entry_points {
|
||||||
debug!("Installing executable: `{name}`");
|
debug!("Installing executable: `{name}`");
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
replace_symlink(source_path, target_path).context("Failed to install executable")?;
|
replace_symlink(source_path, target_path).context("Failed to install executable")?;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
if itself.as_ref().is_some_and(|itself| {
|
||||||
|
std::path::absolute(target_path).is_ok_and(|target| *itself == target)
|
||||||
|
}) {
|
||||||
|
self_replace::self_replace(source_path).context("Failed to install entrypoint")?;
|
||||||
|
} else {
|
||||||
fs_err::copy(source_path, target_path).context("Failed to install entrypoint")?;
|
fs_err::copy(source_path, target_path).context("Failed to install entrypoint")?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let s = if target_entry_points.len() == 1 {
|
let s = if target_entry_points.len() == 1 {
|
||||||
""
|
""
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,9 @@ async fn uninstall_tool(
|
||||||
// Remove the tool itself.
|
// Remove the tool itself.
|
||||||
tools.remove_environment(name)?;
|
tools.remove_environment(name)?;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let itself = std::env::current_exe().ok();
|
||||||
|
|
||||||
// Remove the tool's entrypoints.
|
// Remove the tool's entrypoints.
|
||||||
let entrypoints = receipt.entrypoints();
|
let entrypoints = receipt.entrypoints();
|
||||||
for entrypoint in entrypoints {
|
for entrypoint in entrypoints {
|
||||||
|
|
@ -187,6 +190,15 @@ async fn uninstall_tool(
|
||||||
"Removing executable: {}",
|
"Removing executable: {}",
|
||||||
entrypoint.install_path.user_display()
|
entrypoint.install_path.user_display()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
if itself.as_ref().is_some_and(|itself| {
|
||||||
|
std::path::absolute(&entrypoint.install_path).is_ok_and(|target| *itself == target)
|
||||||
|
}) {
|
||||||
|
self_replace::self_delete()?;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
match fs_err::tokio::remove_file(&entrypoint.install_path).await {
|
match fs_err::tokio::remove_file(&entrypoint.install_path).await {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue