|
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | activate nushell venv (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | windows python install manager (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | pyenv on wsl x86-64 (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 10 (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | x86-64 python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
`.rsrc` is the idiomatic way of storing metadata and non-code resources in PE binaries. This should make the resulting binaries more robust as they are no longer dependent on the exact location of a certain magic number. Addresses: #15022 ## Test Plan Existing integration test for `uv-trampoline-builder` + addition to ensure robustness to code signing. --------- Co-authored-by: samypr100 <3933065+samypr100@users.noreply.github.com> Co-authored-by: Zanie Blue <contact@zanie.dev> |
||
|---|---|---|
| .. | ||
| .cargo | ||
| src | ||
| trampolines | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
| rust-toolchain.toml | ||
Windows trampolines
This is a fork of posy trampolines.
Building
Cross-compiling from Linux
Install cargo xwin. Use your package manager to install
LLD and add the rustup targets:
sudo apt install llvm clang lld
cargo install cargo-xwin
rustup toolchain install nightly-2025-06-23
rustup component add rust-src --toolchain nightly-2025-06-23-x86_64-unknown-linux-gnu
rustup target add --toolchain nightly-2025-06-23 i686-pc-windows-msvc
rustup target add --toolchain nightly-2025-06-23 x86_64-pc-windows-msvc
rustup target add --toolchain nightly-2025-06-23 aarch64-pc-windows-msvc
Then, build the trampolines for all supported architectures:
cargo +nightly-2025-06-23 xwin build --xwin-arch x86 --release --target i686-pc-windows-msvc
cargo +nightly-2025-06-23 xwin build --release --target x86_64-pc-windows-msvc
cargo +nightly-2025-06-23 xwin build --release --target aarch64-pc-windows-msvc
Cross-compiling from macOS
Install cargo xwin. Use your package manager to install
LLVM and add the rustup targets:
brew install llvm
cargo install cargo-xwin
rustup toolchain install nightly-2025-06-23
rustup component add rust-src --toolchain nightly-2025-06-23-aarch64-apple-darwin
rustup target add --toolchain nightly-2025-06-23 i686-pc-windows-msvc
rustup target add --toolchain nightly-2025-06-23 x86_64-pc-windows-msvc
rustup target add --toolchain nightly-2025-06-23 aarch64-pc-windows-msvc
Then, build the trampolines for all supported architectures:
cargo +nightly-2025-06-23 xwin build --xwin-arch x86 --release --target i686-pc-windows-msvc
cargo +nightly-2025-06-23 xwin build --release --target x86_64-pc-windows-msvc
cargo +nightly-2025-06-23 xwin build --release --target aarch64-pc-windows-msvc
Updating the prebuilt executables
After building the trampolines for all supported architectures:
cp target/aarch64-pc-windows-msvc/release/uv-trampoline-console.exe trampolines/uv-trampoline-aarch64-console.exe
cp target/aarch64-pc-windows-msvc/release/uv-trampoline-gui.exe trampolines/uv-trampoline-aarch64-gui.exe
cp target/x86_64-pc-windows-msvc/release/uv-trampoline-console.exe trampolines/uv-trampoline-x86_64-console.exe
cp target/x86_64-pc-windows-msvc/release/uv-trampoline-gui.exe trampolines/uv-trampoline-x86_64-gui.exe
cp target/i686-pc-windows-msvc/release/uv-trampoline-console.exe trampolines/uv-trampoline-i686-console.exe
cp target/i686-pc-windows-msvc/release/uv-trampoline-gui.exe trampolines/uv-trampoline-i686-gui.exe
Testing the trampolines
To perform a basic smoke test of the trampolines, run the following commands on a Windows machine, from the root of the repository:
cargo clean
cargo run venv
cargo run pip install black
.venv\Scripts\black --version
Background
What is this?
Sometimes you want to run a tool on Windows that's written in Python, like black or mypy or
jupyter or whatever. But, Windows does not know how to run Python files! It knows how to run
.exe files. So we need to somehow convert our Python file a .exe file.
That's what this does: it's a generic "trampoline" that lets us generate custom .exes for
arbitrary Python scripts, and when invoked it bounces to invoking python <the script> instead.
How do you use it?
Basically, this looks up python.exe (for console programs) and invokes
python.exe path\to\the\<the .exe>.
It uses PE resources to store/load the information required to do this:
| Resource name | Contains |
|---|---|
RESOURCE_TRAMPOLINE_KIND |
1 (script) or 2 (Python launcher) |
RESOURCE_PYTHON_PATH |
Path to python.exe |
RESOURCE_SCRIPT_DATA |
Zip file, containing a Python script called __main__.py |
This works because when you run python on the .exe, the zipimport mechanism will see the
embedded .zip file, and automagically look inside to find and execute __main__.py. Easy-peasy.
Why does this exist?
I probably could have used Vinay's C++ implementation from distlib, but what's the fun in that? In
particular, optimizing for binary size was entertaining (these are ~7x smaller than the distlib,
which doesn't matter much, but does a little bit, considering that it gets added to every Python
script). There are also some minor advantages, like I think the Rust code is easier to understand
(multiple files!) and it's convenient to be able to straightforwardly code the Python-finding logic
we want. But mostly it was just an interesting challenge.
This does owe a lot to the distlib implementation though. The overall logic is copied
more-or-less directly.
Anything I should know for hacking on this?
In order to minimize binary size, this uses, panic="abort", and carefully avoids using
core::fmt. This removes a bunch of runtime overhead: by default, Rust "hello world" on Windows is
~150 KB! So these binaries are ~10x smaller.
Of course the tradeoff is that this is an awkward super-limited environment. No C runtime and limited platform APIs... you don't even panicking support by default. To work around this:
-
We use
windowsto access Win32 APIs directly. Who needs a C runtime? Though uh, this does mean that literally all of our code isunsafe. Sorry! -
diagnostics.rsusesufmtand some cute Windows tricks to get a convenient version ofeprintln!that works withoutcore::fmt, and automatically prints to either the console if available or pops up a message box if not. -
All the meat is in
bounce.rs.
Miscellaneous tips:
-
cargo-bloatis a useful tool for checking what code is ending up in the final binary and how much space it's taking. (It makes it very obvious whether you've pulled incore::fmt!) -
Lots of Rust built-in panicking checks will pull in
core::fmt, e.g., if you ever use.unwrap()then suddenly our binaries double in size, because theif foo.is_none() { panic!(...) }that's hidden inside.unwrap()will invokecore::fmt, even if the unwrap will actually never fail..unwrap_unchecked()avoids this. Similar forslice[idx]vsslice.get_unchecked(idx).
How do you build this stupid thing?
Building this can be frustrating, because the low-level compiler/runtime machinery have a bunch of
implicit assumptions about the environment they'll run in, and the facilities it provides for things
like memcpy, unwinding, etc. So we need to replace the bits that we actually need, and which bits
we need can change depending on stuff like optimization options. For example: we use
panic="abort", so we don't actually need unwinding support, but at lower optimization levels the
compiler might not realize that, and still emit references to the unwinding
helper__CxxFrameHandler3. And then the linker blows up because that symbol doesn't exist.
cargo build --release --target i686-pc-windows-msvc
cargo build --release --target x86_64-pc-windows-msvc
cargo build --release --target aarch64-pc-windows-msvc