diff --git a/Cargo.lock b/Cargo.lock index 29c258197..d846cc936 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5037,6 +5037,7 @@ dependencies = [ "fs-err", "install-wheel-rs", "path-slash", + "pathdiff", "pep440_rs", "pep508_rs", "pypi-types", diff --git a/crates/install-wheel-rs/src/lib.rs b/crates/install-wheel-rs/src/lib.rs index 0202ddf18..0873d4bc5 100644 --- a/crates/install-wheel-rs/src/lib.rs +++ b/crates/install-wheel-rs/src/lib.rs @@ -11,11 +11,10 @@ use zip::result::ZipError; use pep440_rs::Version; use platform_tags::{Arch, Os}; use pypi_types::Scheme; -pub use script::{scripts_from_ini, Script}; pub use uninstall::{uninstall_egg, uninstall_legacy_editable, uninstall_wheel, Uninstall}; use uv_fs::Simplified; use uv_normalize::PackageName; -pub use wheel::{parse_wheel_file, LibKind}; +pub use wheel::{parse_wheel_file, read_record_file, LibKind}; pub mod linker; pub mod metadata; diff --git a/crates/install-wheel-rs/src/linker.rs b/crates/install-wheel-rs/src/linker.rs index c2be59f32..a71b8a32d 100644 --- a/crates/install-wheel-rs/src/linker.rs +++ b/crates/install-wheel-rs/src/linker.rs @@ -1,7 +1,7 @@ //! Like `wheel.rs`, but for installing wheels that have already been unzipped, rather than //! reading from a zip file. -use std::path::{Path, PathBuf}; +use std::path::Path; use std::str::FromStr; use std::time::SystemTime; @@ -143,24 +143,6 @@ pub fn install_wheel( Ok(()) } -/// Determine the absolute path to an entrypoint script. -pub fn entrypoint_path(entrypoint: &Script, layout: &Layout) -> PathBuf { - if cfg!(windows) { - // On windows we actually build an .exe wrapper - let script_name = entrypoint - .name - // FIXME: What are the in-reality rules here for names? - .strip_suffix(".py") - .unwrap_or(&entrypoint.name) - .to_string() - + ".exe"; - - layout.scheme.scripts.join(script_name) - } else { - layout.scheme.scripts.join(&entrypoint.name) - } -} - /// Find the `dist-info` directory in an unzipped wheel. /// /// See: diff --git a/crates/install-wheel-rs/src/record.rs b/crates/install-wheel-rs/src/record.rs index 4d0ade002..404cee531 100644 --- a/crates/install-wheel-rs/src/record.rs +++ b/crates/install-wheel-rs/src/record.rs @@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize}; /// tqdm-4.62.3.dist-info/RECORD,, /// ``` #[derive(Deserialize, Serialize, PartialOrd, PartialEq, Ord, Eq)] -pub(crate) struct RecordEntry { - pub(crate) path: String, - pub(crate) hash: Option, +pub struct RecordEntry { + pub path: String, + pub hash: Option, #[allow(dead_code)] - pub(crate) size: Option, + pub size: Option, } diff --git a/crates/install-wheel-rs/src/script.rs b/crates/install-wheel-rs/src/script.rs index 5a29e0d1f..eabe49363 100644 --- a/crates/install-wheel-rs/src/script.rs +++ b/crates/install-wheel-rs/src/script.rs @@ -9,10 +9,10 @@ use crate::{wheel, Error}; /// A script defining the name of the runnable entrypoint and the module and function that should be /// run. #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub struct Script { - pub name: String, - pub module: String, - pub function: String, +pub(crate) struct Script { + pub(crate) name: String, + pub(crate) module: String, + pub(crate) function: String, } impl Script { @@ -64,7 +64,7 @@ impl Script { } } -pub fn scripts_from_ini( +pub(crate) fn scripts_from_ini( extras: Option<&[String]>, python_minor: u8, ini: String, diff --git a/crates/install-wheel-rs/src/wheel.rs b/crates/install-wheel-rs/src/wheel.rs index 6170c6bb0..d77087f8f 100644 --- a/crates/install-wheel-rs/src/wheel.rs +++ b/crates/install-wheel-rs/src/wheel.rs @@ -17,7 +17,6 @@ use zip::ZipWriter; use pypi_types::DirectUrl; use uv_fs::{relative_to, Simplified}; -use crate::linker::entrypoint_path; use crate::record::RecordEntry; use crate::script::Script; use crate::{Error, Layout}; @@ -247,6 +246,24 @@ fn get_script_executable(python_executable: &Path, is_gui: bool) -> PathBuf { } } +/// Determine the absolute path to an entrypoint script. +fn entrypoint_path(entrypoint: &Script, layout: &Layout) -> PathBuf { + if cfg!(windows) { + // On windows we actually build an .exe wrapper + let script_name = entrypoint + .name + // FIXME: What are the in-reality rules here for names? + .strip_suffix(".py") + .unwrap_or(&entrypoint.name) + .to_string() + + ".exe"; + + layout.scheme.scripts.join(script_name) + } else { + layout.scheme.scripts.join(&entrypoint.name) + } +} + /// Create the wrapper scripts in the bin folder of the venv for launching console scripts. pub(crate) fn write_script_entrypoints( layout: &Layout, @@ -632,7 +649,7 @@ pub(crate) fn extra_dist_info( /// Reads the record file /// -pub(crate) fn read_record_file(record: &mut impl Read) -> Result, Error> { +pub fn read_record_file(record: &mut impl Read) -> Result, Error> { csv::ReaderBuilder::new() .has_headers(false) .escape(Some(b'"')) diff --git a/crates/uv-tool/Cargo.toml b/crates/uv-tool/Cargo.toml index c828c9fac..33426caba 100644 --- a/crates/uv-tool/Cargo.toml +++ b/crates/uv-tool/Cargo.toml @@ -27,8 +27,9 @@ uv-warnings = { workspace = true } dirs-sys = { workspace = true } fs-err = { workspace = true } path-slash = { workspace = true } +pathdiff = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } -toml = { workspace = true } -toml_edit = { workspace = true } +toml = { workspace = true } +toml_edit = { workspace = true } tracing = { workspace = true } diff --git a/crates/uv-tool/src/lib.rs b/crates/uv-tool/src/lib.rs index 0c3270e44..ecab0edfe 100644 --- a/crates/uv-tool/src/lib.rs +++ b/crates/uv-tool/src/lib.rs @@ -1,22 +1,23 @@ use core::fmt; -use fs_err as fs; -use install_wheel_rs::linker::entrypoint_path; -use install_wheel_rs::{scripts_from_ini, Script}; -use pep440_rs::Version; -use pep508_rs::PackageName; use std::io::{self, Write}; use std::path::{Path, PathBuf}; + +use fs_err as fs; +use fs_err::File; use thiserror::Error; use tracing::debug; + +use install_wheel_rs::read_record_file; +use pep440_rs::Version; +use pep508_rs::PackageName; +pub use receipt::ToolReceipt; +pub use tool::{Tool, ToolEntrypoint}; use uv_cache::Cache; use uv_fs::{LockedFile, Simplified}; +use uv_state::{StateBucket, StateStore}; use uv_toolchain::{Interpreter, PythonEnvironment}; use uv_warnings::warn_user_once; -pub use receipt::ToolReceipt; -pub use tool::{Tool, ToolEntrypoint}; - -use uv_state::{StateBucket, StateStore}; mod receipt; mod tool; @@ -291,7 +292,7 @@ pub fn find_executable_directory() -> Result { .ok_or(Error::NoExecutableDirectory) } -/// Find the dist-info directory for a package in an environment. +/// Find the `.dist-info` directory for a package in an environment. fn find_dist_info( environment: &PythonEnvironment, package_name: &PackageName, @@ -306,53 +307,61 @@ fn find_dist_info( .interpreter() .site_packages() .map(|path| path.join(&dist_info_prefix)) - .find(|path| path.exists()) + .find(|path| path.is_dir()) .ok_or_else(|| Error::DistInfoMissing(dist_info_prefix, environment.root().to_path_buf())) } -/// Parses the `entry_points.txt` entry for console scripts -/// -/// Returns (`script_name`, module, function) -fn parse_scripts( - dist_info_path: &Path, - python_minor: u8, -) -> Result<(Vec