-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a trampoline variant that just executes python
#8637
Changes from 1 commit
9f7fd2e
7909dcc
357431b
c7fdb95
f446582
c29b9d9
b420b40
cbb90ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,9 +151,8 @@ pub fn windows_script_launcher( | |
/// Sort of equivalent to a `python` symlink on Unix. | ||
#[allow(unused_variables)] | ||
pub fn windows_python_launcher( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is new |
||
launcher_python_script: &str, | ||
is_gui: bool, | ||
python_executable: impl AsRef<Path>, | ||
is_gui: bool, | ||
) -> Result<Vec<u8>, Error> { | ||
// This method should only be called on Windows, but we avoid `#[cfg(windows)]` to retain | ||
// compilation on all platforms. | ||
|
@@ -178,3 +177,230 @@ pub fn windows_python_launcher( | |
|
||
Ok(launcher) | ||
} | ||
|
||
#[cfg(all(test, windows))] | ||
#[allow(clippy::print_stdout)] | ||
mod test { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These tests pulled from |
||
use std::io::Write; | ||
use std::path::Path; | ||
use std::process::Command; | ||
|
||
use anyhow::Result; | ||
use assert_cmd::prelude::OutputAssertExt; | ||
use assert_fs::prelude::PathChild; | ||
use fs_err::File; | ||
|
||
use which::which; | ||
|
||
use super::{windows_python_launcher, windows_script_launcher}; | ||
|
||
#[test] | ||
#[cfg(all(windows, target_arch = "x86"))] | ||
fn test_launchers_are_small() { | ||
// At time of writing, they are 45kb~ bytes. | ||
assert!( | ||
super::LAUNCHER_I686_GUI.len() < 45 * 1024, | ||
"GUI launcher: {}", | ||
super::LAUNCHER_I686_GUI.len() | ||
); | ||
assert!( | ||
super::LAUNCHER_I686_CONSOLE.len() < 45 * 1024, | ||
"CLI launcher: {}", | ||
super::LAUNCHER_I686_CONSOLE.len() | ||
); | ||
} | ||
|
||
#[test] | ||
#[cfg(all(windows, target_arch = "x86_64"))] | ||
fn test_launchers_are_small() { | ||
// At time of writing, they are 45kb~ bytes. | ||
assert!( | ||
super::LAUNCHER_X86_64_GUI.len() < 45 * 1024, | ||
"GUI launcher: {}", | ||
super::LAUNCHER_X86_64_GUI.len() | ||
); | ||
assert!( | ||
super::LAUNCHER_X86_64_CONSOLE.len() < 45 * 1024, | ||
"CLI launcher: {}", | ||
super::LAUNCHER_X86_64_CONSOLE.len() | ||
); | ||
} | ||
|
||
#[test] | ||
#[cfg(all(windows, target_arch = "aarch64"))] | ||
fn test_launchers_are_small() { | ||
// At time of writing, they are 45kb~ bytes. | ||
assert!( | ||
super::LAUNCHER_AARCH64_GUI.len() < 45 * 1024, | ||
"GUI launcher: {}", | ||
super::LAUNCHER_AARCH64_GUI.len() | ||
); | ||
assert!( | ||
super::LAUNCHER_AARCH64_CONSOLE.len() < 45 * 1024, | ||
"CLI launcher: {}", | ||
super::LAUNCHER_AARCH64_CONSOLE.len() | ||
); | ||
} | ||
|
||
/// Utility script for the test. | ||
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) | ||
for arg in sys.argv[1:]: | ||
print(arg, file=sys.stderr) | ||
|
||
if __name__ == "__main__": | ||
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) | ||
sys.exit(main_console()) | ||
"## | ||
) | ||
} | ||
} | ||
|
||
/// See [`uv-install-wheel::wheel::format_shebang`]. | ||
fn format_shebang(executable: impl AsRef<Path>) -> String { | ||
// Convert the executable to a simplified path. | ||
let executable = executable.as_ref().display().to_string(); | ||
format!("#!{executable}") | ||
} | ||
|
||
#[test] | ||
fn console_script_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().display() | ||
); | ||
|
||
let stdout_predicate = "Hello from uv-trampoline-console.exe\r\n"; | ||
let stderr_predicate = "Hello from uv-trampoline-console.exe\r\n"; | ||
|
||
// Test Console Launcher | ||
#[cfg(windows)] | ||
Command::new(console_bin_path.path()) | ||
.assert() | ||
.success() | ||
.stdout(stdout_predicate) | ||
.stderr(stderr_predicate); | ||
|
||
let args_to_test = vec!["foo", "bar", "foo bar", "foo \"bar\"", "foo 'bar'"]; | ||
let stderr_predicate = format!("{}{}\r\n", stderr_predicate, args_to_test.join("\r\n")); | ||
|
||
// Test Console Launcher (with args) | ||
Command::new(console_bin_path.path()) | ||
.args(args_to_test) | ||
.assert() | ||
.success() | ||
.stdout(stdout_predicate) | ||
.stderr(stderr_predicate); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[test] | ||
fn console_python_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 Payload | ||
let console_launcher = windows_python_launcher(&python_executable_path, false)?; | ||
|
||
// Create Launcher | ||
File::create(console_bin_path.path())?.write_all(console_launcher.as_ref())?; | ||
|
||
println!( | ||
"Wrote Python Launcher in {}", | ||
console_bin_path.path().display() | ||
); | ||
|
||
// Test Console Launcher | ||
Command::new(console_bin_path.path()) | ||
.arg("-c") | ||
.arg("print('Hello from Python Launcher')") | ||
.assert() | ||
.success() | ||
.stdout("Hello from Python Launcher\r\n"); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[test] | ||
#[ignore] | ||
fn 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().display()); | ||
|
||
// Test GUI Launcher | ||
// NOTICE: This will spawn a GUI and will wait until you close the window. | ||
Command::new(gui_bin_path.path()).assert().success(); | ||
|
||
Ok(()) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,8 +47,8 @@ enum TrampolineKind { | |
impl TrampolineKind { | ||
const fn magic_number(&self) -> &'static [u8; 4] { | ||
match self { | ||
Self::Script => &[b'U', b'V', b'S', b'C'], | ||
Self::Python => &[b'U', b'V', b'P', b'Y'], | ||
Self::Script => b"UVSC", | ||
Self::Python => b"UVPY", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah whoops, you already did this. :P There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry; this is one of those things that wasn't worth moving in the rebase since it was a mess |
||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This moved into
uv-trampoline-builder
, size limit bumped