feat: fix uv-trampoline renovate issues (#5204)

## Summary

1. Fixes errors from https://github.com/astral-sh/uv/pull/4878
2. More cleanup. Removed the need for `MaybeUninit` and `SizeOf`
helpers. Renamed main entrypoint to expected default in windows
`mainCRTStartup` to avoid re-declaring /ENTRY in build.rs.
3. Adds a small basic test harness that >>on windows<< will generate
both types of launchers and run them. I've had been using this locally
to test changes and edge cases, but it might be useful for others. It's
based on core parts of install-wheel-rs.

## Test Plan

Tested locally on a couple of script/gui apps.

---------

Co-authored-by: konsti <konstin@mailbox.org>
This commit is contained in:
samypr100 2024-07-23 04:12:28 -04:00 committed by GitHub
parent e61fcbd7af
commit 166fe85bb7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 1155 additions and 99 deletions

View file

@ -1,3 +1,4 @@
[unstable]
build-std = ["std", "panic_abort"]
build-std-features = ["compiler-builtins-mem", "panic_immediate_abort"]
panic-abort-tests = true

View file

@ -2,12 +2,487 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
[[package]]
name = "anstyle-parse"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
[[package]]
name = "assert_cmd"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8"
dependencies = [
"anstyle",
"bstr",
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "assert_fs"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd762e110c8ed629b11b6cde59458cc1c71de78ebbcc30099fc8e0403a2a2ec"
dependencies = [
"anstyle",
"doc-comment",
"globwalk",
"predicates",
"predicates-core",
"predicates-tree",
"tempfile",
]
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "backoff"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1"
dependencies = [
"getrandom",
"instant",
"rand",
]
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bstr"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
dependencies = [
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cachedir"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4703f3937077db8fa35bee3c8789343c1aec2585f0146f09d658d4ccc0e8d873"
dependencies = [
"tempfile",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "colorchoice"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "dunce"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "embed-manifest"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cd446c890d6bed1d8b53acef5f240069ebef91d6fae7c5f52efe61fe8b5eae"
[[package]]
name = "encoding_rs"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
dependencies = [
"cfg-if",
]
[[package]]
name = "encoding_rs_io"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83"
dependencies = [
"encoding_rs",
]
[[package]]
name = "errno"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]]
name = "flate2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fs-err"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
dependencies = [
"autocfg",
]
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "globset"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "globwalk"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
dependencies = [
"bitflags",
"ignore",
"walkdir",
]
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys",
]
[[package]]
name = "ignore"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata",
"same-file",
"walkdir",
"winapi-util",
]
[[package]]
name = "instant"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [
"cfg-if",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
[[package]]
name = "junction"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c9c415a9b7b1e86cd5738f39d34c9e78c765da7fb1756dbd7d31b3b0d2e7afa"
dependencies = [
"scopeguard",
"windows-sys",
]
[[package]]
name = "libc"
version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
dependencies = [
"adler",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "owo-colors"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
[[package]]
name = "path-absolutize"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4af381fe79fa195b4909485d99f73a80792331df0625188e707854f0b3383f5"
dependencies = [
"path-dedot",
]
[[package]]
name = "path-dedot"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397"
dependencies = [
"once_cell",
]
[[package]]
name = "path-slash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "predicates"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
dependencies = [
"anstyle",
"difflib",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
[[package]]
name = "predicates-tree"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"
@ -26,6 +501,107 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "regex-automata"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "rustc-hash"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustix"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.204"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
]
[[package]]
name = "syn"
version = "1.0.109"
@ -48,6 +624,75 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
dependencies = [
"cfg-if",
"fastrand",
"rustix",
"windows-sys",
]
[[package]]
name = "termtree"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "thiserror"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
]
[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
]
[[package]]
name = "ufmt"
version = "0.2.0"
@ -82,20 +727,139 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "uv-trampoline"
version = "0.1.0"
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uv-fs"
version = "0.0.1"
dependencies = [
"embed-manifest",
"ufmt",
"ufmt-write",
"windows",
"backoff",
"cachedir",
"dunce",
"either",
"encoding_rs_io",
"fs-err",
"fs2",
"junction",
"once_cell",
"path-absolutize",
"path-slash",
"tempfile",
"tracing",
"urlencoding",
"uv-warnings",
]
[[package]]
name = "windows"
version = "0.57.0"
name = "uv-trampoline"
version = "0.1.0"
dependencies = [
"anyhow",
"assert_cmd",
"assert_fs",
"embed-manifest",
"fs-err",
"thiserror",
"ufmt",
"ufmt-write",
"uv-fs",
"which",
"windows",
"zip",
]
[[package]]
name = "uv-warnings"
version = "0.0.1"
dependencies = [
"anstream",
"once_cell",
"owo-colors",
"rustc-hash",
]
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "which"
version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7"
dependencies = [
"either",
"home",
"rustix",
"winsafe",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
dependencies = [
"windows-sys",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core",
"windows-targets",
@ -103,21 +867,22 @@ dependencies = [
[[package]]
name = "windows-core"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-strings",
"windows-targets",
]
[[package]]
name = "windows-implement"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
@ -126,9 +891,9 @@ dependencies = [
[[package]]
name = "windows-interface"
version = "0.57.0"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
@ -137,18 +902,37 @@ dependencies = [
[[package]]
name = "windows-result"
version = "0.1.2"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@ -162,48 +946,66 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.5"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "zip"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
"flate2",
]

View file

@ -4,7 +4,6 @@ version = "0.1.0"
authors = ["Nathaniel J. Smith <njs@pobox.com>"]
license = "MIT OR Apache-2.0"
edition = "2021"
autotests = false
# Need to optimize etc. or else build fails
[profile.dev]
@ -13,6 +12,10 @@ opt-level = 1
panic = "abort"
debug = true
[profile.test]
inherits = "dev"
codegen-units = 1
[profile.release]
# Enable Link Time Optimization.
lto = true
@ -29,7 +32,8 @@ debug = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
windows = { version = "0.57.0", features = [
windows = { version = "0.58.0", features = [
"std",
"Win32_Foundation",
"Win32_Security",
"Win32_Storage_FileSystem",
@ -45,9 +49,18 @@ windows = { version = "0.57.0", features = [
"Win32_System_WindowsProgramming",
"Win32_UI_WindowsAndMessaging",
] }
ufmt-write = "0.1.0"
ufmt = { version = "0.2.0", features = ["std"] }
[build-dependencies]
embed-manifest = "1.4.0"
[dev-dependencies]
anyhow = { version = "1.0.80" }
assert_cmd = { version = "2.0.14" }
assert_fs = { version = "1.1.1" }
fs-err = { version = "2.11.0" }
thiserror = { version = "1.0.56" }
uv-fs = { path = "../uv-fs" }
which = { version = "6.0.0" }
zip = { version = "0.6.6", default-features = false, features = ["deflate"] }

View file

@ -9,8 +9,6 @@ fn main() {
let manifest =
new_manifest("uv.Trampoline").remove_dependency("Microsoft.Windows.Common-Controls");
embed_manifest(manifest).expect("unable to embed manifest");
println!("cargo:rustc-link-arg=/ENTRY:entry");
println!("cargo:rustc-link-arg=/LTCG");
println!("cargo:rerun-if-changed=build.rs");
}
}

View file

@ -1,8 +1,9 @@
#![no_main]
#![windows_subsystem = "console"]
#![no_main] // disable all rust entry points, requires enabling compiler-builtins-mem
#![windows_subsystem = "console"] // configures /SUBSYSTEM:CONSOLE
// build.rs passes a custom linker flag to make this the entrypoint to the executable
// Named according to https://docs.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbol
// This avoids having to define a custom /ENTRY:entry_fn in build.rs
#[no_mangle]
pub extern "C" fn entry() -> ! {
pub extern "C" fn mainCRTStartup() -> ! {
uv_trampoline::bounce::bounce(false)
}

View file

@ -1,8 +1,9 @@
#![no_main]
#![windows_subsystem = "windows"]
#![no_main] // disable all rust entry points, requires enabling compiler-builtins-mem
#![windows_subsystem = "windows"] // configures /SUBSYSTEM:WINDOWS
// build.rs passes a custom linker flag to make this the entrypoint to the executable
// Named according to https://docs.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbol
// This avoids having to define a custom /ENTRY:entry_fn in build.rs
#[no_mangle]
pub extern "C" fn entry() -> ! {
pub extern "C" fn mainCRTStartup() -> ! {
uv_trampoline::bounce::bounce(true)
}

View file

@ -1,7 +1,5 @@
use std::ffi::{CStr, CString};
use std::mem::size_of;
use std::mem::MaybeUninit;
use std::ptr::addr_of;
use std::ffi::{c_void, CStr, CString};
use std::mem::{size_of, size_of_val};
use std::vec::Vec;
use windows::core::{s, PCSTR, PSTR};
@ -25,7 +23,7 @@ use windows::Win32::{
},
System::Environment::GetCommandLineA,
System::JobObjects::{
AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
AssignProcessToJobObject, CreateJobObjectA, JobObjectExtendedLimitInformation,
QueryInformationJobObject, SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK,
},
@ -41,7 +39,6 @@ use windows::Win32::{
},
};
use crate::helpers::SizeOf;
use crate::{eprintln, format};
const MAGIC_NUMBER: [u8; 4] = [b'U', b'V', b'U', b'V'];
@ -76,7 +73,7 @@ fn make_child_cmdline() -> CString {
CString::from_vec_with_nul(child_cmdline).unwrap_or_else(|_| {
eprintln!("Child command line is not correctly null terminated");
exit_with_status(1)
exit_with_status(1);
})
}
@ -129,7 +126,7 @@ fn executable_filename() -> CString {
CString::from_vec_with_nul(buffer).unwrap_or_else(|_| {
eprintln!("Executable name is not correctly null terminated");
exit_with_status(1)
exit_with_status(1);
})
}
@ -215,11 +212,11 @@ fn find_python_exe(executable_name: &CStr) -> CString {
Some(path_len) => {
let path_len = u32::from_le_bytes(path_len.try_into().unwrap_or_else(|_| {
eprintln!("Slice length is not equal to 4 bytes");
exit_with_status(1)
exit_with_status(1);
}));
if path_len > MAX_PATH_LEN {
eprintln!("Only paths with a length up to 32KBs are supported but the python path has a length of {}.", path_len);
eprintln!("Only paths with a length up to 32KBs are supported but the python path has a length of {}", path_len);
exit_with_status(1);
}
@ -241,7 +238,7 @@ fn find_python_exe(executable_name: &CStr) -> CString {
break CString::from_vec_with_nul(buffer).unwrap_or_else(|_| {
eprintln!("Python executable path is not correctly null terminated");
exit_with_status(1)
exit_with_status(1);
});
} else {
// SAFETY: Casting to u32 is safe because `path_len` is guaranteed to be less than 32KBs,
@ -270,13 +267,13 @@ fn find_python_exe(executable_name: &CStr) -> CString {
Some(parent_dir) => parent_dir,
None => {
eprintln!("Script path has unknown separator characters");
exit_with_status(1)
exit_with_status(1);
}
};
let final_path = [parent_dir, b"\\", path.as_bytes()].concat();
CString::new(final_path).unwrap_or_else(|_| {
eprintln!("Could not construct the absolute path to the Python executable");
exit_with_status(1)
exit_with_status(1);
})
}
}
@ -333,16 +330,16 @@ fn skip_one_argument(arguments: &[u8]) -> &[u8] {
}
fn make_job_object() -> HANDLE {
let job = unsafe { CreateJobObjectW(None, None) }
let job = unsafe { CreateJobObjectA(None, None) }
.unwrap_or_else(|_| print_last_error_and_exit("Job creation failed"));
let mut job_info = MaybeUninit::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>::uninit();
let mut job_info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
let mut retlen = 0u32;
if unsafe {
QueryInformationJobObject(
job,
JobObjectExtendedLimitInformation,
job_info.as_mut_ptr() as *mut _,
job_info.size_of(),
&mut job_info as *mut _ as *mut c_void,
size_of_val(&job_info) as u32,
Some(&mut retlen),
)
}
@ -350,15 +347,14 @@ fn make_job_object() -> HANDLE {
{
print_last_error_and_exit("Job information querying failed");
}
let mut job_info = unsafe { job_info.assume_init() };
job_info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
job_info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
if unsafe {
SetInformationJobObject(
job,
JobObjectExtendedLimitInformation,
addr_of!(job_info) as *const _,
job_info.size_of(),
&job_info as *const _ as *const c_void,
size_of_val(&job_info) as u32,
)
}
.is_err()
@ -379,7 +375,7 @@ fn spawn_child(si: &STARTUPINFOA, child_cmdline: CString) -> HANDLE {
unsafe { SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT.0, HANDLE_FLAG_INHERIT) }
.unwrap_or_else(|_| eprintln!("Making stderr inheritable failed"));
}
let mut child_process_info = MaybeUninit::<PROCESS_INFORMATION>::uninit();
let mut child_process_info = PROCESS_INFORMATION::default();
unsafe {
CreateProcessA(
None,
@ -393,13 +389,12 @@ fn spawn_child(si: &STARTUPINFOA, child_cmdline: CString) -> HANDLE {
None,
None,
si,
child_process_info.as_mut_ptr(),
&mut child_process_info,
)
}
.unwrap_or_else(|_| {
print_last_error_and_exit("Failed to spawn the python child process");
});
let child_process_info = unsafe { child_process_info.assume_init() };
unsafe { CloseHandle(child_process_info.hThread) }.unwrap_or_else(|_| {
print_last_error_and_exit("Failed to close handle to python child process main thread");
});
@ -416,10 +411,10 @@ fn close_handles(si: &STARTUPINFOA) {
for std_handle in [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE] {
if let Ok(handle) = unsafe { GetStdHandle(std_handle) } {
unsafe { CloseHandle(handle) }.unwrap_or_else(|_| {
eprintln!("Failed to close standard device handle {}.", handle.0);
eprintln!("Failed to close standard device handle {}", handle.0 as u32);
});
unsafe { SetStdHandle(std_handle, INVALID_HANDLE_VALUE) }.unwrap_or_else(|_| {
eprintln!("Failed to modify standard device handle {}.", std_handle.0);
eprintln!("Failed to modify standard device handle {}", std_handle.0);
});
}
}
@ -435,7 +430,7 @@ fn close_handles(si: &STARTUPINFOA) {
let handle_ptr = unsafe { handle_start.offset(i).read_unaligned() } as *const HANDLE;
// Close all fds inherited from the parent, except for the standard I/O fds.
unsafe { CloseHandle(*handle_ptr) }.unwrap_or_else(|_| {
eprintln!("Failed to close child file descriptors at {}.", i);
eprintln!("Failed to close child file descriptors at {}", i);
});
}
}
@ -460,13 +455,13 @@ fn close_handles(si: &STARTUPINFOA) {
Is creating a window and calling PeekMessage the best way to do this? idk.
*/
fn clear_app_starting_state(child_handle: HANDLE) {
let mut msg = MaybeUninit::<MSG>::uninit();
let mut msg = MSG::default();
unsafe {
// End the launcher's "app starting" cursor state.
PostMessageA(None, 0, None, None).unwrap_or_else(|_| {
eprintln!("Failed to post a message to specified window");
});
if GetMessageA(msg.as_mut_ptr(), None, 0, 0) != TRUE {
if GetMessageA(&mut msg, None, 0, 0) != TRUE {
eprintln!("Failed to retrieve posted window message");
}
// Proxy the child's input idle event.
@ -476,7 +471,7 @@ fn clear_app_starting_state(child_handle: HANDLE) {
// Signal the process input idle event by creating a window and pumping
// sent messages. The window class isn't important, so just use the
// system "STATIC" class.
let hwnd = CreateWindowExA(
if let Ok(hwnd) = CreateWindowExA(
WINDOW_EX_STYLE(0),
s!("STATIC"),
s!("uv Python Trampoline"),
@ -489,21 +484,21 @@ fn clear_app_starting_state(child_handle: HANDLE) {
None,
None,
None,
);
// Process all sent messages and signal input idle.
_ = PeekMessageA(msg.as_mut_ptr(), hwnd, 0, 0, PEEK_MESSAGE_REMOVE_TYPE(0));
DestroyWindow(hwnd).unwrap_or_else(|_| {
print_last_error_and_exit("Failed to destroy temporary window");
});
) {
// Process all sent messages and signal input idle.
let _ = PeekMessageA(&mut msg, hwnd, 0, 0, PEEK_MESSAGE_REMOVE_TYPE(0));
DestroyWindow(hwnd).unwrap_or_else(|_| {
print_last_error_and_exit("Failed to destroy temporary window");
});
}
}
}
pub fn bounce(is_gui: bool) -> ! {
let child_cmdline = make_child_cmdline();
let mut si = MaybeUninit::<STARTUPINFOA>::uninit();
unsafe { GetStartupInfoA(si.as_mut_ptr()) }
let si = unsafe { si.assume_init() };
let mut si = STARTUPINFOA::default();
unsafe { GetStartupInfoA(&mut si) }
let child_handle = spawn_child(&si, child_cmdline);
let job = make_job_object();
@ -535,7 +530,7 @@ pub fn bounce(is_gui: bool) -> ! {
clear_app_starting_state(child_handle);
}
_ = unsafe { WaitForSingleObject(child_handle, INFINITE) };
let _ = unsafe { WaitForSingleObject(child_handle, INFINITE) };
let mut exit_code = 0u32;
if unsafe { GetExitCodeProcess(child_handle, &mut exit_code) }.is_err() {
print_last_error_and_exit("Failed to get exit code of child process");

View file

@ -1,11 +0,0 @@
use std::mem::size_of;
pub trait SizeOf {
fn size_of(&self) -> u32;
}
impl<T: Sized> SizeOf for T {
fn size_of(&self) -> u32 {
size_of::<T>() as u32
}
}

View file

@ -1,3 +1,2 @@
pub mod bounce;
mod diagnostics;
mod helpers;

View file

@ -0,0 +1,257 @@
use std::io::{Cursor, Write};
use std::path::Path;
use std::process::Command;
use std::{env, io};
use anyhow::Result;
use assert_cmd::prelude::OutputAssertExt;
use assert_fs::prelude::PathChild;
use fs_err::File;
use thiserror::Error;
use which::which;
use zip::write::FileOptions;
use zip::ZipWriter;
use uv_fs::Simplified;
const LAUNCHER_MAGIC_NUMBER: [u8; 4] = [b'U', b'V', b'U', b'V'];
#[cfg(all(windows, target_arch = "x86"))]
const LAUNCHER_I686_GUI: &[u8] = include_bytes!("../trampolines/uv-trampoline-i686-gui.exe");
#[cfg(all(windows, target_arch = "x86"))]
const LAUNCHER_I686_CONSOLE: &[u8] =
include_bytes!("../trampolines/uv-trampoline-i686-console.exe");
#[cfg(all(windows, target_arch = "x86_64"))]
const LAUNCHER_X86_64_GUI: &[u8] = include_bytes!("../trampolines/uv-trampoline-x86_64-gui.exe");
#[cfg(all(windows, target_arch = "x86_64"))]
const LAUNCHER_X86_64_CONSOLE: &[u8] =
include_bytes!("../trampolines/uv-trampoline-x86_64-console.exe");
#[cfg(all(windows, target_arch = "aarch64"))]
const LAUNCHER_AARCH64_GUI: &[u8] = include_bytes!("../trampolines/uv-trampoline-aarch64-gui.exe");
#[cfg(all(windows, target_arch = "aarch64"))]
const LAUNCHER_AARCH64_CONSOLE: &[u8] =
include_bytes!("../trampolines/uv-trampoline-aarch64-console.exe");
/// Note: The caller is responsible for adding the path of the wheel we're installing.
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Io(#[from] io::Error),
#[error(
"Unable to create Windows launcher for: {0} (only x86_64, x86, and arm64 are supported)"
)]
UnsupportedWindowsArch(&'static str),
#[error("Unable to create Windows launcher on non-Windows platform")]
NotWindows,
}
/// Wrapper script template function
///
/// <https://github.com/pypa/pip/blob/7f8a6844037fb7255cfd0d34ff8e8cf44f2598d4/src/pip/_vendor/distlib/scripts.py#L41-L48>
fn get_script_launcher(shebang: &str, is_gui: bool) -> String {
if is_gui {
format!(
r##"{shebang}
# -*- coding: utf-8 -*-
import re
import sys
def make_gui() -> None:
from tkinter import Tk, ttk
root = Tk()
root.title("uv Test App")
frm = ttk.Frame(root, padding=10)
frm.grid()
ttk.Label(frm, text="Hello from uv-trampoline-gui.exe").grid(column=0, row=0)
root.mainloop()
if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(make_gui())
"##
)
} else {
format!(
r##"{shebang}
# -*- coding: utf-8 -*-
import re
import sys
def main_console() -> None:
print("Hello from uv-trampoline-console.exe", file=sys.stdout)
print("Hello from uv-trampoline-console.exe", file=sys.stderr)
if __name__ == "__main__":
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(main_console())
"##
)
}
}
/// Format the shebang for a given Python executable.
///
/// Like pip, if a shebang is non-simple (too long or contains spaces), we use `/bin/sh` as the
/// executable.
///
/// See: <https://github.com/pypa/pip/blob/0ad4c94be74cc24874c6feb5bb3c2152c398a18e/src/pip/_vendor/distlib/scripts.py#L136-L165>
fn format_shebang(executable: impl AsRef<Path>) -> String {
// Convert the executable to a simplified path.
let executable = executable.as_ref().simplified_display().to_string();
format!("#!{executable}")
}
/// A Windows script is a minimal .exe launcher binary with the python entrypoint script appended as
/// stored zip file.
///
/// <https://github.com/pypa/pip/blob/fd0ea6bc5e8cb95e518c23d901c26ca14db17f89/src/pip/_vendor/distlib/scripts.py#L248-L262>
#[allow(unused_variables)]
fn windows_script_launcher(
launcher_python_script: &str,
is_gui: bool,
python_executable: impl AsRef<Path>,
) -> Result<Vec<u8>, Error> {
// This method should only be called on Windows, but we avoid `#[cfg(windows)]` to retain
// compilation on all platforms.
if cfg!(not(windows)) {
return Err(Error::NotWindows);
}
let launcher_bin: &[u8] = match env::consts::ARCH {
#[cfg(all(windows, target_arch = "x86"))]
"x86" => {
if is_gui {
LAUNCHER_I686_GUI
} else {
LAUNCHER_I686_CONSOLE
}
}
#[cfg(all(windows, target_arch = "x86_64"))]
"x86_64" => {
if is_gui {
LAUNCHER_X86_64_GUI
} else {
LAUNCHER_X86_64_CONSOLE
}
}
#[cfg(all(windows, target_arch = "aarch64"))]
"aarch64" => {
if is_gui {
LAUNCHER_AARCH64_GUI
} else {
LAUNCHER_AARCH64_CONSOLE
}
}
#[cfg(windows)]
arch => {
return Err(Error::UnsupportedWindowsArch(arch));
}
#[cfg(not(windows))]
arch => &[],
};
let mut payload: Vec<u8> = Vec::new();
{
// We're using the zip writer, but with stored compression
// https://github.com/njsmith/posy/blob/04927e657ca97a5e35bb2252d168125de9a3a025/src/trampolines/mod.rs#L75-L82
// https://github.com/pypa/distlib/blob/8ed03aab48add854f377ce392efffb79bb4d6091/PC/launcher.c#L259-L271
let stored = FileOptions::default().compression_method(zip::CompressionMethod::Stored);
let mut archive = ZipWriter::new(Cursor::new(&mut payload));
let error_msg = "Writing to Vec<u8> should never fail";
archive.start_file("__main__.py", stored).expect(error_msg);
archive
.write_all(launcher_python_script.as_bytes())
.expect(error_msg);
archive.finish().expect(error_msg);
}
let python = python_executable.as_ref();
let python_path = python.simplified_display().to_string();
let mut launcher: Vec<u8> = Vec::with_capacity(launcher_bin.len() + payload.len());
launcher.extend_from_slice(launcher_bin);
launcher.extend_from_slice(&payload);
launcher.extend_from_slice(python_path.as_bytes());
launcher.extend_from_slice(
&u32::try_from(python_path.as_bytes().len())
.expect("File Path to be smaller than 4GB")
.to_le_bytes(),
);
launcher.extend_from_slice(&LAUNCHER_MAGIC_NUMBER);
Ok(launcher)
}
#[test]
fn generate_console_launcher() -> Result<()> {
// Create Temp Dirs
let temp_dir = assert_fs::TempDir::new()?;
let console_bin_path = temp_dir.child("launcher.console.exe");
// Locate an arbitrary python installation from PATH
let python_executable_path = which("python")?;
// Generate Launcher Script
let launcher_console_script =
get_script_launcher(&format_shebang(&python_executable_path), false);
// Generate Launcher Payload
let console_launcher =
windows_script_launcher(&launcher_console_script, false, &python_executable_path)?;
// Create Launcher
File::create(console_bin_path.path())?.write_all(console_launcher.as_ref())?;
println!(
"Wrote Console Launcher in {}",
console_bin_path.path().simplified_display()
);
// Test Console Launcher
#[cfg(windows)]
Command::new(console_bin_path.path())
.assert()
.success()
.stdout("Hello from uv-trampoline-console.exe\r\n")
.stderr("Hello from uv-trampoline-console.exe\r\n");
Ok(())
}
#[test]
#[ignore]
fn generate_gui_launcher() -> Result<()> {
// Create Temp Dirs
let temp_dir = assert_fs::TempDir::new()?;
let gui_bin_path = temp_dir.child("launcher.gui.exe");
// Locate an arbitrary pythonw installation from PATH
let pythonw_executable_path = which("pythonw")?;
// Generate Launcher Script
let launcher_gui_script = get_script_launcher(&format_shebang(&pythonw_executable_path), true);
// Generate Launcher Payload
let gui_launcher =
windows_script_launcher(&launcher_gui_script, true, &pythonw_executable_path)?;
// Create Launcher
File::create(gui_bin_path.path())?.write_all(gui_launcher.as_ref())?;
println!(
"Wrote GUI Launcher in {}",
gui_bin_path.path().simplified_display()
);
// Test GUI Launcher
// NOTICE: This will spawn a GUI and will wait until you close the window.
#[cfg(windows)]
Command::new(gui_bin_path.path()).assert().success();
Ok(())
}