diff --git a/crates/install-wheel-rs/src/wheel.rs b/crates/install-wheel-rs/src/wheel.rs index 2ebca601b1ba8..b196bc3594ff7 100644 --- a/crates/install-wheel-rs/src/wheel.rs +++ b/crates/install-wheel-rs/src/wheel.rs @@ -1,13 +1,13 @@ +use std::{env, io}; +use std::collections::HashMap; +use std::io::{BufRead, BufReader, Cursor, Read, Seek, Write}; +use std::path::{Path, PathBuf}; + use data_encoding::BASE64URL_NOPAD; use fs_err as fs; use fs_err::{DirEntry, File}; use rustc_hash::FxHashMap; use sha2::{Digest, Sha256}; -use std::borrow::Cow; -use std::collections::HashMap; -use std::io::{BufRead, BufReader, Cursor, Read, Seek, Write}; -use std::path::{Path, PathBuf}; -use std::{env, io}; use tracing::{instrument, warn}; use walkdir::WalkDir; use zip::write::FileOptions; @@ -17,9 +17,9 @@ use pypi_types::DirectUrl; use uv_fs::{relative_to, Simplified}; use uv_normalize::PackageName; +use crate::{Error, Layout}; use crate::record::RecordEntry; use crate::script::Script; -use crate::{Error, Layout}; const LAUNCHER_MAGIC_NUMBER: [u8; 4] = [b'U', b'V', b'U', b'V']; @@ -300,8 +300,8 @@ pub(crate) fn write_script_entrypoints( })?; // Generate the launcher script. - let python_executable = get_python_executable(layout, relocatable)?; - let launcher_executable = get_script_executable(&python_executable, is_gui); + let launcher_executable = get_script_executable(&layout.sys_executable, is_gui); + let launcher_executable = get_relocatable_executable(launcher_executable, layout, relocatable)?; let launcher_python_script = get_script_launcher( entrypoint, &format_shebang(&launcher_executable, &layout.os_name, relocatable), @@ -514,8 +514,8 @@ fn install_script( false } }; - let python_executable = get_python_executable(layout, relocatable)?; - let executable = get_script_executable(&python_executable, is_gui); + let executable = get_script_executable(&layout.sys_executable, is_gui); + let executable = get_relocatable_executable(executable, layout, relocatable)?; let start = format_shebang(&executable, &layout.os_name, relocatable) .as_bytes() .to_vec(); @@ -710,26 +710,27 @@ pub(crate) fn extra_dist_info( /// /// Returns `sys.executable` if the wheel is not relocatable; otherwise, returns a path relative /// to the scripts directory. -pub(crate) fn get_python_executable( +pub(crate) fn get_relocatable_executable( + executable: PathBuf, layout: &Layout, relocatable: bool, -) -> Result, Error> { +) -> Result { Ok(if relocatable { - Cow::Owned( - pathdiff::diff_paths(&layout.sys_executable, &layout.scheme.scripts).ok_or_else( + + pathdiff::diff_paths(&executable, &layout.scheme.scripts).ok_or_else( || { Error::Io(io::Error::new( io::ErrorKind::Other, format!( "Could not find relative path for: {}", - layout.sys_executable.simplified_display() + executable.simplified_display() ), )) }, - )?, - ) + )? + } else { - Cow::Borrowed(&layout.sys_executable) + executable }) } @@ -787,8 +788,8 @@ mod test { use assert_fs::prelude::*; use indoc::{formatdoc, indoc}; - use crate::wheel::format_shebang; use crate::Error; + use crate::wheel::format_shebang; use super::{ get_script_executable, parse_key_value_file, parse_wheel_file, read_record_file, Script, @@ -1067,25 +1068,6 @@ mod test { let script_path = get_script_executable(&dot_python_exe, false); assert_eq!(script_path, dot_python_exe.to_path_buf()); - // Test with relocatable executable. - let temp_dir = assert_fs::TempDir::new()?; - let python_exe = temp_dir.child("python.exe"); - let pythonw_exe = temp_dir.child("pythonw.exe"); - python_exe.write_str("")?; - pythonw_exe.write_str("")?; - - // Truncate to a relative path. - let python_exe = python_exe.path().strip_prefix(temp_dir.path()).unwrap(); - - let script_path = get_script_executable(python_exe, true); - #[cfg(windows)] - assert_eq!(script_path, Path::new("pythonw.exe").to_path_buf()); - #[cfg(not(windows))] - assert_eq!(script_path, Path::new("python.exe").to_path_buf()); - - let script_path = get_script_executable(python_exe, false); - assert_eq!(script_path, Path::new("python.exe").to_path_buf()); - Ok(()) } } diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 95c11d3596eb7..2aae67a06aeaf 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -1745,12 +1745,17 @@ pub struct VenvArgs { /// Make the virtual environment relocatable. /// - /// A relocatable virtual environment can be moved around and redistributed with its - /// associated entrypoint and activation scripts functioning as usual. + /// A relocatable virtual environment can be moved around and redistributed without + /// invalidating its associated entrypoint and activation scripts. /// /// Note that this can only be guaranteed for standard `console_scripts` and `gui_scripts`. /// Other scripts may be adjusted if they ship with a generic `#!python[w]` shebang, /// and binaries are left as-is. + /// + /// As a result of making the environment relocatable (by way of writing relative, rather than + /// absolute paths), the entrypoints and scripts themselves will _not_ be relocatable. In other + /// words, copying those entrypoints and scripts to a location outside the environment will not + /// work, as they reference paths relative to the environment itself. #[arg(long)] pub relocatable: bool,