Skip to content

Commit 041c7a5

Browse files
GankracliebBS
andauthored
Use suffix on uvx binary when searching for uv binary (#12923)
This is a rebased and updated version of #11925 based on my review (I didn't have permission to push to their branch). For posterity I've preserved their commits but my final commit essentially rewrites the whole thing anyway. Fixes #11637 --------- Co-authored-by: Chris Lieb <[email protected]>
1 parent fc3dacf commit 041c7a5

File tree

1 file changed

+54
-9
lines changed

1 file changed

+54
-9
lines changed

crates/uv/src/bin/uvx.rs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::convert::Infallible;
2+
use std::path::{Path, PathBuf};
23
use std::{
34
ffi::OsString,
45
process::{Command, ExitCode, ExitStatus},
@@ -22,6 +23,57 @@ fn exec_spawn(cmd: &mut Command) -> std::io::Result<Infallible> {
2223
}
2324
}
2425

26+
/// Assuming the binary is called something like `[email protected](.exe)`, compute the `@1.2.3(.exe)` part
27+
/// so that we can preferentially find `[email protected](.exe)`, for folks who like managing multiple
28+
/// installs in this way.
29+
fn get_uvx_suffix(current_exe: &Path) -> Option<&str> {
30+
let os_file_name = current_exe.file_name()?;
31+
let file_name_str = os_file_name.to_str()?;
32+
file_name_str.strip_prefix("uvx")
33+
}
34+
35+
/// Gets the path to `uv`, given info about `uvx`
36+
fn get_uv_path(current_exe_parent: &Path, uvx_suffix: Option<&str>) -> std::io::Result<PathBuf> {
37+
// First try to find a matching suffixed `uv`, e.g. `[email protected](.exe)`
38+
let uv_with_suffix = uvx_suffix.map(|suffix| current_exe_parent.join(format!("uv{suffix}")));
39+
if let Some(uv_with_suffix) = &uv_with_suffix {
40+
#[allow(clippy::print_stderr, reason = "printing a very rare warning")]
41+
match uv_with_suffix.try_exists() {
42+
Ok(true) => return Ok(uv_with_suffix.to_owned()),
43+
Ok(false) => { /* definitely not there, proceed to fallback */ }
44+
Err(err) => {
45+
// We don't know if `[email protected]` exists, something errored when checking.
46+
// We *could* blindly use `[email protected]` in this case, as the code below does, however
47+
// in this extremely narrow corner case it's *probably* better to default to `uv`,
48+
// since we don't want to mess up existing users who weren't using suffixes?
49+
eprintln!(
50+
"warning: failed to determine if `{}` exists, trying `uv` instead: {err}",
51+
uv_with_suffix.display()
52+
);
53+
}
54+
}
55+
}
56+
57+
// Then just look for good ol' `uv`
58+
let uv = current_exe_parent.join(format!("uv{}", std::env::consts::EXE_SUFFIX));
59+
// If we are sure the `uv` binary does not exist, display a clearer error message.
60+
// If we're not certain if uv exists (try_exists == Err), keep going and hope it works.
61+
if matches!(uv.try_exists(), Ok(false)) {
62+
let message = if let Some(uv_with_suffix) = uv_with_suffix {
63+
format!(
64+
"Could not find the `uv` binary at either of:\n {}\n {}",
65+
uv_with_suffix.display(),
66+
uv.display(),
67+
)
68+
} else {
69+
format!("Could not find the `uv` binary at: {}", uv.display())
70+
};
71+
Err(std::io::Error::new(std::io::ErrorKind::NotFound, message))
72+
} else {
73+
Ok(uv)
74+
}
75+
}
76+
2577
fn run() -> std::io::Result<ExitStatus> {
2678
let current_exe = std::env::current_exe()?;
2779
let Some(bin) = current_exe.parent() else {
@@ -30,22 +82,15 @@ fn run() -> std::io::Result<ExitStatus> {
3082
"Could not determine the location of the `uvx` binary",
3183
));
3284
};
33-
let uv = bin.join(format!("uv{}", std::env::consts::EXE_SUFFIX));
85+
let uvx_suffix = get_uvx_suffix(&current_exe);
86+
let uv = get_uv_path(bin, uvx_suffix)?;
3487
let args = ["tool", "uvx"]
3588
.iter()
3689
.map(OsString::from)
3790
// Skip the `uvx` name
3891
.chain(std::env::args_os().skip(1))
3992
.collect::<Vec<_>>();
4093

41-
// If we are sure the uv binary does not exist, display a clearer error message
42-
if matches!(uv.try_exists(), Ok(false)) {
43-
return Err(std::io::Error::new(
44-
std::io::ErrorKind::NotFound,
45-
format!("Could not find the `uv` binary at: {}", uv.display()),
46-
));
47-
}
48-
4994
let mut cmd = Command::new(uv);
5095
cmd.args(&args);
5196
match exec_spawn(&mut cmd)? {}

0 commit comments

Comments
 (0)