From 166fe85bb7442f5a3b4b1979f9d3396bcf7e48cd Mon Sep 17 00:00:00 2001 From: samypr100 <3933065+samypr100@users.noreply.github.com> Date: Tue, 23 Jul 2024 04:12:28 -0400 Subject: [PATCH] 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 --- crates/uv-trampoline/.cargo/config.toml | 1 + crates/uv-trampoline/Cargo.lock | 872 +++++++++++++++++- crates/uv-trampoline/Cargo.toml | 19 +- crates/uv-trampoline/build.rs | 2 - .../src/bin/uv-trampoline-console.rs | 9 +- .../src/bin/uv-trampoline-gui.rs | 9 +- crates/uv-trampoline/src/bounce.rs | 73 +- crates/uv-trampoline/src/helpers.rs | 11 - crates/uv-trampoline/src/lib.rs | 1 - crates/uv-trampoline/tests/harness.rs | 257 ++++++ 10 files changed, 1155 insertions(+), 99 deletions(-) delete mode 100644 crates/uv-trampoline/src/helpers.rs create mode 100644 crates/uv-trampoline/tests/harness.rs diff --git a/crates/uv-trampoline/.cargo/config.toml b/crates/uv-trampoline/.cargo/config.toml index 39413188a..8e1f395df 100644 --- a/crates/uv-trampoline/.cargo/config.toml +++ b/crates/uv-trampoline/.cargo/config.toml @@ -1,3 +1,4 @@ [unstable] build-std = ["std", "panic_abort"] build-std-features = ["compiler-builtins-mem", "panic_immediate_abort"] +panic-abort-tests = true diff --git a/crates/uv-trampoline/Cargo.lock b/crates/uv-trampoline/Cargo.lock index 1182ff5b5..4dffd0d6f 100644 --- a/crates/uv-trampoline/Cargo.lock +++ b/crates/uv-trampoline/Cargo.lock @@ -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", +] diff --git a/crates/uv-trampoline/Cargo.toml b/crates/uv-trampoline/Cargo.toml index 3a8f898c1..2210bbe97 100644 --- a/crates/uv-trampoline/Cargo.toml +++ b/crates/uv-trampoline/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" authors = ["Nathaniel J. Smith "] 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"] } diff --git a/crates/uv-trampoline/build.rs b/crates/uv-trampoline/build.rs index e89b972bf..468b458fe 100644 --- a/crates/uv-trampoline/build.rs +++ b/crates/uv-trampoline/build.rs @@ -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"); } } diff --git a/crates/uv-trampoline/src/bin/uv-trampoline-console.rs b/crates/uv-trampoline/src/bin/uv-trampoline-console.rs index 62ccef436..09af4a728 100644 --- a/crates/uv-trampoline/src/bin/uv-trampoline-console.rs +++ b/crates/uv-trampoline/src/bin/uv-trampoline-console.rs @@ -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) } diff --git a/crates/uv-trampoline/src/bin/uv-trampoline-gui.rs b/crates/uv-trampoline/src/bin/uv-trampoline-gui.rs index 63d2eab51..28ac04265 100644 --- a/crates/uv-trampoline/src/bin/uv-trampoline-gui.rs +++ b/crates/uv-trampoline/src/bin/uv-trampoline-gui.rs @@ -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) } diff --git a/crates/uv-trampoline/src/bounce.rs b/crates/uv-trampoline/src/bounce.rs index cf9eefb45..cc0748884 100644 --- a/crates/uv-trampoline/src/bounce.rs +++ b/crates/uv-trampoline/src/bounce.rs @@ -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::::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::::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::::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::::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"); diff --git a/crates/uv-trampoline/src/helpers.rs b/crates/uv-trampoline/src/helpers.rs deleted file mode 100644 index 6a7e2b49f..000000000 --- a/crates/uv-trampoline/src/helpers.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::mem::size_of; - -pub trait SizeOf { - fn size_of(&self) -> u32; -} - -impl SizeOf for T { - fn size_of(&self) -> u32 { - size_of::() as u32 - } -} diff --git a/crates/uv-trampoline/src/lib.rs b/crates/uv-trampoline/src/lib.rs index a887b24c8..caacb92f1 100644 --- a/crates/uv-trampoline/src/lib.rs +++ b/crates/uv-trampoline/src/lib.rs @@ -1,3 +1,2 @@ pub mod bounce; mod diagnostics; -mod helpers; diff --git a/crates/uv-trampoline/tests/harness.rs b/crates/uv-trampoline/tests/harness.rs new file mode 100644 index 000000000..d37e99948 --- /dev/null +++ b/crates/uv-trampoline/tests/harness.rs @@ -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 +/// +/// +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: +fn format_shebang(executable: impl AsRef) -> 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. +/// +/// +#[allow(unused_variables)] +fn windows_script_launcher( + launcher_python_script: &str, + is_gui: bool, + python_executable: impl AsRef, +) -> Result, 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 = 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 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 = 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(()) +}