mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Add uvx
alias for uv tool run
(#4632)
Closes https://github.com/astral-sh/uv/issues/4476
Originally, this used the changes in #4642 to invoke `main()` from a
`uvx` binary. This had the benefit of `uvx` being entirely standalone at
the cost of doubling our artifact size. We think that's the incorrect
trade-off.
Instead, we assume `uvx` is always next to `uv` and create a tiny binary
(<1MB) that invokes `uv` in a child process. This seems preferable to a
`cargo-dist` alias because we have more control over it. This binary
should "just work" for all of our cargo-dist distributions and
installers, but we'll need to add a new entry point for our PyPI
distribution. I'll probably tackle support there separately?
```
❯ ls -lah target/release/uv target/release/uvx
-rwxr-xr-x 1 zb staff 31M Jun 28 23:23 target/release/uv
-rwxr-xr-x 1 zb staff 452K Jun 28 23:22 target/release/uvx
```
This includes some small overhead:
```
❯ hyperfine --shell=none --warmup=100 './target/release/uv tool run --help' './target/release/uvx --help' --min-runs 2000
Benchmark 1: ./target/release/uv tool run --help
Time (mean ± σ): 2.2 ms ± 0.1 ms [User: 1.3 ms, System: 0.5 ms]
Range (min … max): 2.0 ms … 4.0 ms 2000 runs
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Benchmark 2: ./target/release/uvx --help
Time (mean ± σ): 2.9 ms ± 0.1 ms [User: 1.7 ms, System: 0.9 ms]
Range (min … max): 2.8 ms … 4.2 ms 2000 runs
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Summary
./target/release/uv tool run --help ran
1.35 ± 0.09 times faster than ./target/release/uvx --help
```
I presume there may be some other downsides to a child process? The
wrapper is a little awkward. We could consider `execv` but this is
complicated across platforms. An example implementation of that over in
[monotrail](433af5aed9/crates/monotrail/src/monotrail.rs (L764-L799)
).
This commit is contained in:
parent
8e935e2c17
commit
ec2723a9f5
1 changed files with 39 additions and 0 deletions
39
crates/uv/src/bin/uvx.rs
Normal file
39
crates/uv/src/bin/uvx.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use std::{
|
||||
ffi::OsString,
|
||||
process::{Command, ExitCode, ExitStatus},
|
||||
};
|
||||
|
||||
use anyhow::bail;
|
||||
|
||||
fn run() -> Result<ExitStatus, anyhow::Error> {
|
||||
let current_exe = std::env::current_exe()?;
|
||||
let Some(bin) = current_exe.parent() else {
|
||||
bail!("Could not determine the location of the `uvx` binary")
|
||||
};
|
||||
let uv = bin.join("uv");
|
||||
let args = ["tool", "run"]
|
||||
.iter()
|
||||
.map(OsString::from)
|
||||
// Skip the `uvx` name
|
||||
.chain(std::env::args_os().skip(1))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(Command::new(uv).args(&args).status()?)
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
fn main() -> ExitCode {
|
||||
let result = run();
|
||||
match result {
|
||||
// Fail with 2 if the status cannot be cast to an exit code
|
||||
Ok(status) => u8::try_from(status.code().unwrap_or(2)).unwrap_or(2).into(),
|
||||
Err(err) => {
|
||||
let mut causes = err.chain();
|
||||
eprintln!("error: {}", causes.next().unwrap());
|
||||
for err in causes {
|
||||
eprintln!(" Caused by: {err}");
|
||||
}
|
||||
ExitCode::from(2)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue