mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-01 12:24:15 +00:00
Refactor os, arch, and libc information into a shared Platform type (#15027)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
Addresses this outstanding item from a previous review https://github.com/astral-sh/uv/pull/13724#discussion_r2114867288 I'm interested in this now for consolidating some logic in #12731
This commit is contained in:
parent
323aa8f332
commit
78c8c711fa
8 changed files with 698 additions and 172 deletions
|
|
@ -1,7 +1,5 @@
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{cmp, fmt};
|
|
||||||
|
|
||||||
/// Architecture variants, e.g., with support for different instruction sets
|
/// Architecture variants, e.g., with support for different instruction sets
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Ord, PartialOrd)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Ord, PartialOrd)]
|
||||||
|
|
@ -24,7 +22,7 @@ pub struct Arch {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Arch {
|
impl Ord for Arch {
|
||||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
if self.family == other.family {
|
if self.family == other.family {
|
||||||
return self.variant.cmp(&other.variant);
|
return self.variant.cmp(&other.variant);
|
||||||
}
|
}
|
||||||
|
|
@ -55,8 +53,8 @@ impl Ord for Arch {
|
||||||
other.family == preferred.family,
|
other.family == preferred.family,
|
||||||
) {
|
) {
|
||||||
(true, true) => unreachable!(),
|
(true, true) => unreachable!(),
|
||||||
(true, false) => cmp::Ordering::Less,
|
(true, false) => std::cmp::Ordering::Less,
|
||||||
(false, true) => cmp::Ordering::Greater,
|
(false, true) => std::cmp::Ordering::Greater,
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
// Both non-preferred, fallback to lexicographic order
|
// Both non-preferred, fallback to lexicographic order
|
||||||
self.family.to_string().cmp(&other.family.to_string())
|
self.family.to_string().cmp(&other.family.to_string())
|
||||||
|
|
@ -66,48 +64,29 @@ impl Ord for Arch {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Arch {
|
impl PartialOrd for Arch {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arch {
|
impl Arch {
|
||||||
pub fn new(family: target_lexicon::Architecture, variant: Option<ArchVariant>) -> Self {
|
pub fn new(family: target_lexicon::Architecture, variant: Option<ArchVariant>) -> Self {
|
||||||
Self { family, variant }
|
Self { family, variant }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_env() -> Self {
|
pub fn from_env() -> Self {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
if let Some(arch) = test_support::get_mock_arch() {
|
||||||
|
return arch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
family: target_lexicon::HOST.architecture,
|
family: target_lexicon::HOST.architecture,
|
||||||
variant: None,
|
variant: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does the current architecture support running the other?
|
|
||||||
///
|
|
||||||
/// When the architecture is equal, this is always true. Otherwise, this is true if the
|
|
||||||
/// architecture is transparently emulated or is a microarchitecture with worse performance
|
|
||||||
/// characteristics.
|
|
||||||
pub fn supports(self, other: Self) -> bool {
|
|
||||||
if self == other {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement `variant` support checks
|
|
||||||
|
|
||||||
// Windows ARM64 runs emulated x86_64 binaries transparently
|
|
||||||
// Similarly, macOS aarch64 runs emulated x86_64 binaries transparently if you have Rosetta
|
|
||||||
// installed. We don't try to be clever and check if that's the case here, we just assume
|
|
||||||
// that if x86_64 distributions are available, they're usable.
|
|
||||||
if (cfg!(windows) || cfg!(target_os = "macos"))
|
|
||||||
&& matches!(self.family, target_lexicon::Architecture::Aarch64(_))
|
|
||||||
{
|
|
||||||
return other.family == target_lexicon::Architecture::X86_64;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn family(&self) -> target_lexicon::Architecture {
|
pub fn family(&self) -> target_lexicon::Architecture {
|
||||||
self.family
|
self.family
|
||||||
}
|
}
|
||||||
|
|
@ -117,8 +96,8 @@ impl Arch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Arch {
|
impl std::fmt::Display for Arch {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self.family {
|
match self.family {
|
||||||
target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686) => {
|
target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686) => {
|
||||||
write!(f, "x86")?;
|
write!(f, "x86")?;
|
||||||
|
|
@ -192,8 +171,8 @@ impl FromStr for ArchVariant {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ArchVariant {
|
impl std::fmt::Display for ArchVariant {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::V2 => write!(f, "v2"),
|
Self::V2 => write!(f, "v2"),
|
||||||
Self::V3 => write!(f, "v3"),
|
Self::V3 => write!(f, "v3"),
|
||||||
|
|
@ -247,3 +226,60 @@ impl From<&uv_platform_tags::Arch> for Arch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod test_support {
|
||||||
|
use super::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static MOCK_ARCH: RefCell<Option<Arch>> = const { RefCell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_mock_arch() -> Option<Arch> {
|
||||||
|
MOCK_ARCH.with(|arch| *arch.borrow())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mock_arch(arch: Option<Arch>) {
|
||||||
|
MOCK_ARCH.with(|mock| *mock.borrow_mut() = arch);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct MockArchGuard {
|
||||||
|
previous: Option<Arch>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockArchGuard {
|
||||||
|
pub(crate) fn new(arch: Arch) -> Self {
|
||||||
|
let previous = get_mock_arch();
|
||||||
|
set_mock_arch(Some(arch));
|
||||||
|
Self { previous }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MockArchGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
set_mock_arch(self.previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a function with a mocked architecture.
|
||||||
|
/// The mock is automatically cleaned up after the function returns.
|
||||||
|
pub(crate) fn run_with_arch<F, R>(arch: Arch, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce() -> R,
|
||||||
|
{
|
||||||
|
let _guard = MockArchGuard::new(arch);
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn x86_64() -> Arch {
|
||||||
|
Arch::new(target_lexicon::Architecture::X86_64, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn aarch64() -> Arch {
|
||||||
|
Arch::new(
|
||||||
|
target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
//! Platform detection for operating system, architecture, and libc.
|
//! Platform detection for operating system, architecture, and libc.
|
||||||
|
|
||||||
|
use std::cmp;
|
||||||
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub use crate::arch::{Arch, ArchVariant};
|
pub use crate::arch::{Arch, ArchVariant};
|
||||||
|
|
@ -23,4 +26,409 @@ pub enum Error {
|
||||||
UnsupportedVariant(String, String),
|
UnsupportedVariant(String, String),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
LibcDetectionError(#[from] crate::libc::LibcDetectionError),
|
LibcDetectionError(#[from] crate::libc::LibcDetectionError),
|
||||||
|
#[error("Invalid platform format: {0}")]
|
||||||
|
InvalidPlatformFormat(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A platform identifier that combines operating system, architecture, and libc.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Platform {
|
||||||
|
pub os: Os,
|
||||||
|
pub arch: Arch,
|
||||||
|
pub libc: Libc,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Platform {
|
||||||
|
/// Create a new platform with the given components.
|
||||||
|
pub fn new(os: Os, arch: Arch, libc: Libc) -> Self {
|
||||||
|
Self { os, arch, libc }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a platform from string parts (os, arch, libc).
|
||||||
|
pub fn from_parts(os: &str, arch: &str, libc: &str) -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
os: Os::from_str(os)?,
|
||||||
|
arch: Arch::from_str(arch)?,
|
||||||
|
libc: Libc::from_str(libc)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Detect the platform from the current environment.
|
||||||
|
pub fn from_env() -> Result<Self, Error> {
|
||||||
|
let os = Os::from_env();
|
||||||
|
let arch = Arch::from_env();
|
||||||
|
let libc = Libc::from_env()?;
|
||||||
|
Ok(Self { os, arch, libc })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if this platform supports running another platform.
|
||||||
|
pub fn supports(&self, other: &Self) -> bool {
|
||||||
|
// If platforms are exactly equal, they're compatible
|
||||||
|
if self == other {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OS must match exactly
|
||||||
|
if self.os != other.os {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Libc must match exactly
|
||||||
|
if self.libc != other.libc {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check architecture compatibility
|
||||||
|
if self.arch == other.arch {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows ARM64 runs emulated x86_64 binaries transparently
|
||||||
|
// Similarly, macOS aarch64 runs emulated x86_64 binaries transparently if you have Rosetta
|
||||||
|
// installed. We don't try to be clever and check if that's the case here, we just assume
|
||||||
|
// that if x86_64 distributions are available, they're usable.
|
||||||
|
if (self.os.is_windows() || self.os.is_macos())
|
||||||
|
&& matches!(self.arch.family(), target_lexicon::Architecture::Aarch64(_))
|
||||||
|
&& matches!(other.arch.family(), target_lexicon::Architecture::X86_64)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Allow inequal variants, as we don't implement variant support checks yet.
|
||||||
|
// See https://github.com/astral-sh/uv/pull/9788
|
||||||
|
// For now, allow same architecture family as a fallback
|
||||||
|
self.arch.family() == other.arch.family()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Platform {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}-{}-{}", self.os, self.arch, self.libc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Platform {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let parts: Vec<&str> = s.split('-').collect();
|
||||||
|
|
||||||
|
if parts.len() != 3 {
|
||||||
|
return Err(Error::InvalidPlatformFormat(format!(
|
||||||
|
"expected exactly 3 parts separated by '-', got {}",
|
||||||
|
parts.len()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::from_parts(parts[0], parts[1], parts[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Platform {
|
||||||
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||||
|
self.os
|
||||||
|
.to_string()
|
||||||
|
.cmp(&other.os.to_string())
|
||||||
|
// Then architecture
|
||||||
|
.then_with(|| {
|
||||||
|
if self.arch.family == other.arch.family {
|
||||||
|
return self.arch.variant.cmp(&other.arch.variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the time being, manually make aarch64 windows disfavored on its own host
|
||||||
|
// platform, because most packages don't have wheels for aarch64 windows, making
|
||||||
|
// emulation more useful than native execution!
|
||||||
|
//
|
||||||
|
// The reason we do this in "sorting" and not "supports" is so that we don't
|
||||||
|
// *refuse* to use an aarch64 windows pythons if they happen to be installed and
|
||||||
|
// nothing else is available.
|
||||||
|
//
|
||||||
|
// Similarly if someone manually requests an aarch64 windows install, we should
|
||||||
|
// respect that request (this is the way users should "override" this behaviour).
|
||||||
|
let preferred = if self.os.is_windows() {
|
||||||
|
Arch {
|
||||||
|
family: target_lexicon::Architecture::X86_64,
|
||||||
|
variant: None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Prefer native architectures
|
||||||
|
Arch::from_env()
|
||||||
|
};
|
||||||
|
|
||||||
|
match (
|
||||||
|
self.arch.family == preferred.family,
|
||||||
|
other.arch.family == preferred.family,
|
||||||
|
) {
|
||||||
|
(true, true) => unreachable!(),
|
||||||
|
(true, false) => cmp::Ordering::Less,
|
||||||
|
(false, true) => cmp::Ordering::Greater,
|
||||||
|
(false, false) => {
|
||||||
|
// Both non-preferred, fallback to lexicographic order
|
||||||
|
self.arch
|
||||||
|
.family
|
||||||
|
.to_string()
|
||||||
|
.cmp(&other.arch.family.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Finally compare libc
|
||||||
|
.then_with(|| self.libc.to_string().cmp(&other.libc.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Platform {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&uv_platform_tags::Platform> for Platform {
|
||||||
|
fn from(value: &uv_platform_tags::Platform) -> Self {
|
||||||
|
Self {
|
||||||
|
os: Os::from(value.os()),
|
||||||
|
arch: Arch::from(&value.arch()),
|
||||||
|
libc: Libc::from(value.os()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_display() {
|
||||||
|
let platform = Platform {
|
||||||
|
os: Os::from_str("linux").unwrap(),
|
||||||
|
arch: Arch::from_str("x86_64").unwrap(),
|
||||||
|
libc: Libc::from_str("gnu").unwrap(),
|
||||||
|
};
|
||||||
|
assert_eq!(platform.to_string(), "linux-x86_64-gnu");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_from_str() {
|
||||||
|
let platform = Platform::from_str("macos-aarch64-none").unwrap();
|
||||||
|
assert_eq!(platform.os.to_string(), "macos");
|
||||||
|
assert_eq!(platform.arch.to_string(), "aarch64");
|
||||||
|
assert_eq!(platform.libc.to_string(), "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_from_parts() {
|
||||||
|
let platform = Platform::from_parts("linux", "x86_64", "gnu").unwrap();
|
||||||
|
assert_eq!(platform.os.to_string(), "linux");
|
||||||
|
assert_eq!(platform.arch.to_string(), "x86_64");
|
||||||
|
assert_eq!(platform.libc.to_string(), "gnu");
|
||||||
|
|
||||||
|
// Test with arch variant
|
||||||
|
let platform = Platform::from_parts("linux", "x86_64_v3", "musl").unwrap();
|
||||||
|
assert_eq!(platform.os.to_string(), "linux");
|
||||||
|
assert_eq!(platform.arch.to_string(), "x86_64_v3");
|
||||||
|
assert_eq!(platform.libc.to_string(), "musl");
|
||||||
|
|
||||||
|
// Test error cases
|
||||||
|
assert!(Platform::from_parts("invalid_os", "x86_64", "gnu").is_err());
|
||||||
|
assert!(Platform::from_parts("linux", "invalid_arch", "gnu").is_err());
|
||||||
|
assert!(Platform::from_parts("linux", "x86_64", "invalid_libc").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_from_str_with_arch_variant() {
|
||||||
|
let platform = Platform::from_str("linux-x86_64_v3-gnu").unwrap();
|
||||||
|
assert_eq!(platform.os.to_string(), "linux");
|
||||||
|
assert_eq!(platform.arch.to_string(), "x86_64_v3");
|
||||||
|
assert_eq!(platform.libc.to_string(), "gnu");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_from_str_error() {
|
||||||
|
// Too few parts
|
||||||
|
assert!(Platform::from_str("linux-x86_64").is_err());
|
||||||
|
assert!(Platform::from_str("invalid").is_err());
|
||||||
|
|
||||||
|
// Too many parts (would have been accepted by the old code)
|
||||||
|
assert!(Platform::from_str("linux-x86-64-gnu").is_err());
|
||||||
|
assert!(Platform::from_str("linux-x86_64-gnu-extra").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_sorting_os_precedence() {
|
||||||
|
let linux = Platform::from_str("linux-x86_64-gnu").unwrap();
|
||||||
|
let macos = Platform::from_str("macos-x86_64-none").unwrap();
|
||||||
|
let windows = Platform::from_str("windows-x86_64-none").unwrap();
|
||||||
|
|
||||||
|
// OS sorting takes precedence (alphabetical)
|
||||||
|
assert!(linux < macos);
|
||||||
|
assert!(macos < windows);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_sorting_libc() {
|
||||||
|
let gnu = Platform::from_str("linux-x86_64-gnu").unwrap();
|
||||||
|
let musl = Platform::from_str("linux-x86_64-musl").unwrap();
|
||||||
|
|
||||||
|
// Same OS and arch, libc comparison (alphabetical)
|
||||||
|
assert!(gnu < musl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_sorting_arch_linux() {
|
||||||
|
// Test that Linux prefers the native architecture
|
||||||
|
use crate::arch::test_support::{aarch64, run_with_arch, x86_64};
|
||||||
|
|
||||||
|
let linux_x86_64 = Platform::from_str("linux-x86_64-gnu").unwrap();
|
||||||
|
let linux_aarch64 = Platform::from_str("linux-aarch64-gnu").unwrap();
|
||||||
|
|
||||||
|
// On x86_64 Linux, x86_64 should be preferred over aarch64
|
||||||
|
run_with_arch(x86_64(), || {
|
||||||
|
assert!(linux_x86_64 < linux_aarch64);
|
||||||
|
});
|
||||||
|
|
||||||
|
// On aarch64 Linux, aarch64 should be preferred over x86_64
|
||||||
|
run_with_arch(aarch64(), || {
|
||||||
|
assert!(linux_aarch64 < linux_x86_64);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_sorting_arch_macos() {
|
||||||
|
use crate::arch::test_support::{aarch64, run_with_arch, x86_64};
|
||||||
|
|
||||||
|
let macos_x86_64 = Platform::from_str("macos-x86_64-none").unwrap();
|
||||||
|
let macos_aarch64 = Platform::from_str("macos-aarch64-none").unwrap();
|
||||||
|
|
||||||
|
// On x86_64 macOS, x86_64 should be preferred over aarch64
|
||||||
|
run_with_arch(x86_64(), || {
|
||||||
|
assert!(macos_x86_64 < macos_aarch64);
|
||||||
|
});
|
||||||
|
|
||||||
|
// On aarch64 macOS, aarch64 should be preferred over x86_64
|
||||||
|
run_with_arch(aarch64(), || {
|
||||||
|
assert!(macos_aarch64 < macos_x86_64);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_platform_supports() {
|
||||||
|
let native = Platform::from_str("linux-x86_64-gnu").unwrap();
|
||||||
|
let same = Platform::from_str("linux-x86_64-gnu").unwrap();
|
||||||
|
let different_arch = Platform::from_str("linux-aarch64-gnu").unwrap();
|
||||||
|
let different_os = Platform::from_str("macos-x86_64-none").unwrap();
|
||||||
|
let different_libc = Platform::from_str("linux-x86_64-musl").unwrap();
|
||||||
|
|
||||||
|
// Exact match
|
||||||
|
assert!(native.supports(&same));
|
||||||
|
|
||||||
|
// Different OS - not supported
|
||||||
|
assert!(!native.supports(&different_os));
|
||||||
|
|
||||||
|
// Different libc - not supported
|
||||||
|
assert!(!native.supports(&different_libc));
|
||||||
|
|
||||||
|
// Different architecture but same family
|
||||||
|
// x86_64 doesn't support aarch64 on Linux
|
||||||
|
assert!(!native.supports(&different_arch));
|
||||||
|
|
||||||
|
// Test architecture family support
|
||||||
|
let x86_64_v2 = Platform::from_str("linux-x86_64_v2-gnu").unwrap();
|
||||||
|
let x86_64_v3 = Platform::from_str("linux-x86_64_v3-gnu").unwrap();
|
||||||
|
|
||||||
|
// These have the same architecture family (both x86_64)
|
||||||
|
assert_eq!(native.arch.family(), x86_64_v2.arch.family());
|
||||||
|
assert_eq!(native.arch.family(), x86_64_v3.arch.family());
|
||||||
|
|
||||||
|
// Due to the family check, these should support each other
|
||||||
|
assert!(native.supports(&x86_64_v2));
|
||||||
|
assert!(native.supports(&x86_64_v3));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_windows_aarch64_platform_sorting() {
|
||||||
|
// Test that on Windows, x86_64 is preferred over aarch64
|
||||||
|
let windows_x86_64 = Platform::from_str("windows-x86_64-none").unwrap();
|
||||||
|
let windows_aarch64 = Platform::from_str("windows-aarch64-none").unwrap();
|
||||||
|
|
||||||
|
// x86_64 should sort before aarch64 on Windows (preferred)
|
||||||
|
assert!(windows_x86_64 < windows_aarch64);
|
||||||
|
|
||||||
|
// Test with multiple Windows platforms
|
||||||
|
let mut platforms = [
|
||||||
|
Platform::from_str("windows-aarch64-none").unwrap(),
|
||||||
|
Platform::from_str("windows-x86_64-none").unwrap(),
|
||||||
|
Platform::from_str("windows-x86-none").unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
platforms.sort();
|
||||||
|
|
||||||
|
// After sorting on Windows, the order should be: x86_64 (preferred), aarch64, x86
|
||||||
|
// x86_64 is preferred on Windows regardless of native architecture
|
||||||
|
assert_eq!(platforms[0].arch.to_string(), "x86_64");
|
||||||
|
assert_eq!(platforms[1].arch.to_string(), "aarch64");
|
||||||
|
assert_eq!(platforms[2].arch.to_string(), "x86");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_windows_sorting_always_prefers_x86_64() {
|
||||||
|
// Test that Windows always prefers x86_64 regardless of host architecture
|
||||||
|
use crate::arch::test_support::{aarch64, run_with_arch, x86_64};
|
||||||
|
|
||||||
|
let windows_x86_64 = Platform::from_str("windows-x86_64-none").unwrap();
|
||||||
|
let windows_aarch64 = Platform::from_str("windows-aarch64-none").unwrap();
|
||||||
|
|
||||||
|
// Even with aarch64 as host, Windows should still prefer x86_64
|
||||||
|
run_with_arch(aarch64(), || {
|
||||||
|
assert!(windows_x86_64 < windows_aarch64);
|
||||||
|
});
|
||||||
|
|
||||||
|
// With x86_64 as host, Windows should still prefer x86_64
|
||||||
|
run_with_arch(x86_64(), || {
|
||||||
|
assert!(windows_x86_64 < windows_aarch64);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_windows_aarch64_supports() {
|
||||||
|
// Test that Windows aarch64 can run x86_64 binaries through emulation
|
||||||
|
let windows_aarch64 = Platform::from_str("windows-aarch64-none").unwrap();
|
||||||
|
let windows_x86_64 = Platform::from_str("windows-x86_64-none").unwrap();
|
||||||
|
|
||||||
|
// aarch64 Windows supports x86_64 through transparent emulation
|
||||||
|
assert!(windows_aarch64.supports(&windows_x86_64));
|
||||||
|
|
||||||
|
// But x86_64 doesn't support aarch64
|
||||||
|
assert!(!windows_x86_64.supports(&windows_aarch64));
|
||||||
|
|
||||||
|
// Self-support should always work
|
||||||
|
assert!(windows_aarch64.supports(&windows_aarch64));
|
||||||
|
assert!(windows_x86_64.supports(&windows_x86_64));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_platform_tags_platform() {
|
||||||
|
// Test conversion from uv_platform_tags::Platform to uv_platform::Platform
|
||||||
|
let tags_platform = uv_platform_tags::Platform::new(
|
||||||
|
uv_platform_tags::Os::Windows,
|
||||||
|
uv_platform_tags::Arch::X86_64,
|
||||||
|
);
|
||||||
|
let platform = Platform::from(&tags_platform);
|
||||||
|
|
||||||
|
assert_eq!(platform.os.to_string(), "windows");
|
||||||
|
assert_eq!(platform.arch.to_string(), "x86_64");
|
||||||
|
assert_eq!(platform.libc.to_string(), "none");
|
||||||
|
|
||||||
|
// Test with manylinux
|
||||||
|
let tags_platform_linux = uv_platform_tags::Platform::new(
|
||||||
|
uv_platform_tags::Os::Manylinux {
|
||||||
|
major: 2,
|
||||||
|
minor: 17,
|
||||||
|
},
|
||||||
|
uv_platform_tags::Arch::Aarch64,
|
||||||
|
);
|
||||||
|
let platform_linux = Platform::from(&tags_platform_linux);
|
||||||
|
|
||||||
|
assert_eq!(platform_linux.os.to_string(), "linux");
|
||||||
|
assert_eq!(platform_linux.arch.to_string(), "aarch64");
|
||||||
|
assert_eq!(platform_linux.libc.to_string(), "gnu");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ impl Os {
|
||||||
pub fn is_windows(&self) -> bool {
|
pub fn is_windows(&self) -> bool {
|
||||||
matches!(self.0, target_lexicon::OperatingSystem::Windows)
|
matches!(self.0, target_lexicon::OperatingSystem::Windows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_macos(&self) -> bool {
|
||||||
|
matches!(self.0, target_lexicon::OperatingSystem::Darwin(_))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Os {
|
impl Display for Os {
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,7 @@ fn python_executables_from_installed<'a>(
|
||||||
debug!("Skipping managed installation `{installation}`: does not satisfy `{version}`");
|
debug!("Skipping managed installation `{installation}`: does not satisfy `{version}`");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !platform.matches(installation.key()) {
|
if !platform.matches(installation.platform()) {
|
||||||
debug!("Skipping managed installation `{installation}`: does not satisfy `{platform}`");
|
debug!("Skipping managed installation `{installation}`: does not satisfy `{platform}`");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ use uv_client::{BaseClient, WrappedReqwestError, is_extended_transient_error};
|
||||||
use uv_distribution_filename::{ExtensionError, SourceDistExtension};
|
use uv_distribution_filename::{ExtensionError, SourceDistExtension};
|
||||||
use uv_extract::hash::Hasher;
|
use uv_extract::hash::Hasher;
|
||||||
use uv_fs::{Simplified, rename_with_retry};
|
use uv_fs::{Simplified, rename_with_retry};
|
||||||
use uv_platform::{self as platform, Arch, Libc, Os};
|
use uv_platform::{self as platform, Arch, Libc, Os, Platform};
|
||||||
use uv_pypi_types::{HashAlgorithm, HashDigest};
|
use uv_pypi_types::{HashAlgorithm, HashDigest};
|
||||||
use uv_redacted::DisplaySafeUrl;
|
use uv_redacted::DisplaySafeUrl;
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
@ -171,22 +171,22 @@ pub struct PlatformRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformRequest {
|
impl PlatformRequest {
|
||||||
/// Check if this platform request is satisfied by an installation key.
|
/// Check if this platform request is satisfied by a platform.
|
||||||
pub fn matches(&self, key: &PythonInstallationKey) -> bool {
|
pub fn matches(&self, platform: &Platform) -> bool {
|
||||||
if let Some(os) = self.os {
|
if let Some(os) = self.os {
|
||||||
if key.os != os {
|
if platform.os != os {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(arch) = self.arch {
|
if let Some(arch) = self.arch {
|
||||||
if !arch.satisfied_by(key.arch) {
|
if !arch.satisfied_by(platform) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(libc) = self.libc {
|
if let Some(libc) = self.libc {
|
||||||
if key.libc != libc {
|
if platform.libc != libc {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,10 +220,14 @@ impl Display for ArchRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArchRequest {
|
impl ArchRequest {
|
||||||
pub(crate) fn satisfied_by(self, arch: Arch) -> bool {
|
pub(crate) fn satisfied_by(self, platform: &Platform) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Explicit(request) => request == arch,
|
Self::Explicit(request) => request == platform.arch,
|
||||||
Self::Environment(env) => env.supports(arch),
|
Self::Environment(env) => {
|
||||||
|
// Check if the environment's platform can run the target platform
|
||||||
|
let env_platform = Platform::new(platform.os, env, platform.libc);
|
||||||
|
env_platform.supports(platform)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,14 +331,15 @@ impl PythonDownloadRequest {
|
||||||
///
|
///
|
||||||
/// Platform information is pulled from the environment.
|
/// Platform information is pulled from the environment.
|
||||||
pub fn fill_platform(mut self) -> Result<Self, Error> {
|
pub fn fill_platform(mut self) -> Result<Self, Error> {
|
||||||
|
let platform = Platform::from_env()?;
|
||||||
if self.arch.is_none() {
|
if self.arch.is_none() {
|
||||||
self.arch = Some(ArchRequest::Environment(Arch::from_env()));
|
self.arch = Some(ArchRequest::Environment(platform.arch));
|
||||||
}
|
}
|
||||||
if self.os.is_none() {
|
if self.os.is_none() {
|
||||||
self.os = Some(Os::from_env());
|
self.os = Some(platform.os);
|
||||||
}
|
}
|
||||||
if self.libc.is_none() {
|
if self.libc.is_none() {
|
||||||
self.libc = Some(Libc::from_env()?);
|
self.libc = Some(platform.libc);
|
||||||
}
|
}
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
@ -378,23 +383,16 @@ impl PythonDownloadRequest {
|
||||||
|
|
||||||
/// Whether this request is satisfied by an installation key.
|
/// Whether this request is satisfied by an installation key.
|
||||||
pub fn satisfied_by_key(&self, key: &PythonInstallationKey) -> bool {
|
pub fn satisfied_by_key(&self, key: &PythonInstallationKey) -> bool {
|
||||||
if let Some(os) = &self.os {
|
// Check platform requirements
|
||||||
if key.os != *os {
|
let request = PlatformRequest {
|
||||||
return false;
|
os: self.os,
|
||||||
}
|
arch: self.arch,
|
||||||
|
libc: self.libc,
|
||||||
|
};
|
||||||
|
if !request.matches(key.platform()) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(arch) = &self.arch {
|
|
||||||
if !arch.satisfied_by(key.arch) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(libc) = &self.libc {
|
|
||||||
if key.libc != *libc {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(implementation) = &self.implementation {
|
if let Some(implementation) = &self.implementation {
|
||||||
if key.implementation != LenientImplementationName::from(*implementation) {
|
if key.implementation != LenientImplementationName::from(*implementation) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -453,19 +451,20 @@ impl PythonDownloadRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(os) = self.os() {
|
if let Some(os) = self.os() {
|
||||||
let interpreter_os = Os::from(interpreter.platform().os());
|
if &interpreter.os() != os {
|
||||||
if &interpreter_os != os {
|
|
||||||
debug!(
|
debug!(
|
||||||
"Skipping interpreter at `{executable}`: operating system `{interpreter_os}` does not match request `{os}`"
|
"Skipping interpreter at `{executable}`: operating system `{}` does not match request `{os}`",
|
||||||
|
interpreter.os()
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(arch) = self.arch() {
|
if let Some(arch) = self.arch() {
|
||||||
let interpreter_arch = Arch::from(&interpreter.platform().arch());
|
let interpreter_platform = Platform::from(interpreter.platform());
|
||||||
if !arch.satisfied_by(interpreter_arch) {
|
if !arch.satisfied_by(&interpreter_platform) {
|
||||||
debug!(
|
debug!(
|
||||||
"Skipping interpreter at `{executable}`: architecture `{interpreter_arch}` does not match request `{arch}`"
|
"Skipping interpreter at `{executable}`: architecture `{}` does not match request `{arch}`",
|
||||||
|
interpreter.arch()
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -482,10 +481,10 @@ impl PythonDownloadRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(libc) = self.libc() {
|
if let Some(libc) = self.libc() {
|
||||||
let interpreter_libc = Libc::from(interpreter.platform().os());
|
if &interpreter.libc() != libc {
|
||||||
if &interpreter_libc != libc {
|
|
||||||
debug!(
|
debug!(
|
||||||
"Skipping interpreter at `{executable}`: libc `{interpreter_libc}` does not match request `{libc}`"
|
"Skipping interpreter at `{executable}`: libc `{}` does not match request `{libc}`",
|
||||||
|
interpreter.libc()
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -514,9 +513,9 @@ impl From<&ManagedPythonInstallation> for PythonDownloadRequest {
|
||||||
"Managed Python installations are expected to always have known implementation names, found {name}"
|
"Managed Python installations are expected to always have known implementation names, found {name}"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
Some(ArchRequest::Explicit(key.arch)),
|
Some(ArchRequest::Explicit(*key.arch())),
|
||||||
Some(key.os),
|
Some(*key.os()),
|
||||||
Some(key.libc),
|
Some(*key.libc()),
|
||||||
Some(key.prerelease.is_some()),
|
Some(key.prerelease.is_some()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1184,9 +1183,7 @@ fn parse_json_downloads(
|
||||||
key: PythonInstallationKey::new_from_version(
|
key: PythonInstallationKey::new_from_version(
|
||||||
implementation,
|
implementation,
|
||||||
&version,
|
&version,
|
||||||
os,
|
Platform::new(os, arch, libc),
|
||||||
arch,
|
|
||||||
libc,
|
|
||||||
variant,
|
variant,
|
||||||
),
|
),
|
||||||
url,
|
url,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use uv_cache::Cache;
|
||||||
use uv_client::BaseClientBuilder;
|
use uv_client::BaseClientBuilder;
|
||||||
use uv_configuration::Preview;
|
use uv_configuration::Preview;
|
||||||
use uv_pep440::{Prerelease, Version};
|
use uv_pep440::{Prerelease, Version};
|
||||||
use uv_platform::{Arch, Libc, Os};
|
use uv_platform::{Arch, Libc, Os, Platform};
|
||||||
|
|
||||||
use crate::discovery::{
|
use crate::discovery::{
|
||||||
EnvironmentPreference, PythonRequest, find_best_python_installation, find_python_installation,
|
EnvironmentPreference, PythonRequest, find_best_python_installation, find_python_installation,
|
||||||
|
|
@ -352,9 +352,7 @@ pub struct PythonInstallationKey {
|
||||||
pub(crate) minor: u8,
|
pub(crate) minor: u8,
|
||||||
pub(crate) patch: u8,
|
pub(crate) patch: u8,
|
||||||
pub(crate) prerelease: Option<Prerelease>,
|
pub(crate) prerelease: Option<Prerelease>,
|
||||||
pub(crate) os: Os,
|
pub(crate) platform: Platform,
|
||||||
pub(crate) arch: Arch,
|
|
||||||
pub(crate) libc: Libc,
|
|
||||||
pub(crate) variant: PythonVariant,
|
pub(crate) variant: PythonVariant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -365,9 +363,7 @@ impl PythonInstallationKey {
|
||||||
minor: u8,
|
minor: u8,
|
||||||
patch: u8,
|
patch: u8,
|
||||||
prerelease: Option<Prerelease>,
|
prerelease: Option<Prerelease>,
|
||||||
os: Os,
|
platform: Platform,
|
||||||
arch: Arch,
|
|
||||||
libc: Libc,
|
|
||||||
variant: PythonVariant,
|
variant: PythonVariant,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -376,9 +372,7 @@ impl PythonInstallationKey {
|
||||||
minor,
|
minor,
|
||||||
patch,
|
patch,
|
||||||
prerelease,
|
prerelease,
|
||||||
os,
|
platform,
|
||||||
arch,
|
|
||||||
libc,
|
|
||||||
variant,
|
variant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -386,9 +380,7 @@ impl PythonInstallationKey {
|
||||||
pub fn new_from_version(
|
pub fn new_from_version(
|
||||||
implementation: LenientImplementationName,
|
implementation: LenientImplementationName,
|
||||||
version: &PythonVersion,
|
version: &PythonVersion,
|
||||||
os: Os,
|
platform: Platform,
|
||||||
arch: Arch,
|
|
||||||
libc: Libc,
|
|
||||||
variant: PythonVariant,
|
variant: PythonVariant,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -397,9 +389,7 @@ impl PythonInstallationKey {
|
||||||
minor: version.minor(),
|
minor: version.minor(),
|
||||||
patch: version.patch().unwrap_or_default(),
|
patch: version.patch().unwrap_or_default(),
|
||||||
prerelease: version.pre(),
|
prerelease: version.pre(),
|
||||||
os,
|
platform,
|
||||||
arch,
|
|
||||||
libc,
|
|
||||||
variant,
|
variant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -434,16 +424,20 @@ impl PythonInstallationKey {
|
||||||
self.minor
|
self.minor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn platform(&self) -> &Platform {
|
||||||
|
&self.platform
|
||||||
|
}
|
||||||
|
|
||||||
pub fn arch(&self) -> &Arch {
|
pub fn arch(&self) -> &Arch {
|
||||||
&self.arch
|
&self.platform.arch
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn os(&self) -> &Os {
|
pub fn os(&self) -> &Os {
|
||||||
&self.os
|
&self.platform.os
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn libc(&self) -> &Libc {
|
pub fn libc(&self) -> &Libc {
|
||||||
&self.libc
|
&self.platform.libc
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variant(&self) -> &PythonVariant {
|
pub fn variant(&self) -> &PythonVariant {
|
||||||
|
|
@ -489,7 +483,7 @@ impl fmt::Display for PythonInstallationKey {
|
||||||
};
|
};
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}-{}.{}.{}{}{}-{}-{}-{}",
|
"{}-{}.{}.{}{}{}-{}",
|
||||||
self.implementation,
|
self.implementation,
|
||||||
self.major,
|
self.major,
|
||||||
self.minor,
|
self.minor,
|
||||||
|
|
@ -498,9 +492,7 @@ impl fmt::Display for PythonInstallationKey {
|
||||||
.map(|pre| pre.to_string())
|
.map(|pre| pre.to_string())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
variant,
|
variant,
|
||||||
self.os,
|
self.platform
|
||||||
self.arch,
|
|
||||||
self.libc
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -510,31 +502,25 @@ impl FromStr for PythonInstallationKey {
|
||||||
|
|
||||||
fn from_str(key: &str) -> Result<Self, Self::Err> {
|
fn from_str(key: &str) -> Result<Self, Self::Err> {
|
||||||
let parts = key.split('-').collect::<Vec<_>>();
|
let parts = key.split('-').collect::<Vec<_>>();
|
||||||
let [implementation, version, os, arch, libc] = parts.as_slice() else {
|
|
||||||
|
// We need exactly implementation-version-os-arch-libc
|
||||||
|
if parts.len() != 5 {
|
||||||
return Err(PythonInstallationKeyError::ParseError(
|
return Err(PythonInstallationKeyError::ParseError(
|
||||||
key.to_string(),
|
key.to_string(),
|
||||||
"not enough `-`-separated values".to_string(),
|
format!(
|
||||||
|
"expected exactly 5 `-`-separated values, got {}",
|
||||||
|
parts.len()
|
||||||
|
),
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let [implementation_str, version_str, os, arch, libc] = parts.as_slice() else {
|
||||||
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
let implementation = LenientImplementationName::from(*implementation);
|
let implementation = LenientImplementationName::from(*implementation_str);
|
||||||
|
|
||||||
let os = Os::from_str(os).map_err(|err| {
|
let (version, variant) = match version_str.split_once('+') {
|
||||||
PythonInstallationKeyError::ParseError(key.to_string(), format!("invalid OS: {err}"))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let arch = Arch::from_str(arch).map_err(|err| {
|
|
||||||
PythonInstallationKeyError::ParseError(
|
|
||||||
key.to_string(),
|
|
||||||
format!("invalid architecture: {err}"),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let libc = Libc::from_str(libc).map_err(|err| {
|
|
||||||
PythonInstallationKeyError::ParseError(key.to_string(), format!("invalid libc: {err}"))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let (version, variant) = match version.split_once('+') {
|
|
||||||
Some((version, variant)) => {
|
Some((version, variant)) => {
|
||||||
let variant = PythonVariant::from_str(variant).map_err(|()| {
|
let variant = PythonVariant::from_str(variant).map_err(|()| {
|
||||||
PythonInstallationKeyError::ParseError(
|
PythonInstallationKeyError::ParseError(
|
||||||
|
|
@ -544,7 +530,7 @@ impl FromStr for PythonInstallationKey {
|
||||||
})?;
|
})?;
|
||||||
(version, variant)
|
(version, variant)
|
||||||
}
|
}
|
||||||
None => (*version, PythonVariant::Default),
|
None => (*version_str, PythonVariant::Default),
|
||||||
};
|
};
|
||||||
|
|
||||||
let version = PythonVersion::from_str(version).map_err(|err| {
|
let version = PythonVersion::from_str(version).map_err(|err| {
|
||||||
|
|
@ -554,14 +540,22 @@ impl FromStr for PythonInstallationKey {
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Self::new_from_version(
|
let platform = Platform::from_parts(os, arch, libc).map_err(|err| {
|
||||||
|
PythonInstallationKeyError::ParseError(
|
||||||
|
key.to_string(),
|
||||||
|
format!("invalid platform: {err}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
implementation,
|
implementation,
|
||||||
&version,
|
major: version.major(),
|
||||||
os,
|
minor: version.minor(),
|
||||||
arch,
|
patch: version.patch().unwrap_or_default(),
|
||||||
libc,
|
prerelease: version.pre(),
|
||||||
|
platform,
|
||||||
variant,
|
variant,
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -576,10 +570,8 @@ impl Ord for PythonInstallationKey {
|
||||||
self.implementation
|
self.implementation
|
||||||
.cmp(&other.implementation)
|
.cmp(&other.implementation)
|
||||||
.then_with(|| self.version().cmp(&other.version()))
|
.then_with(|| self.version().cmp(&other.version()))
|
||||||
.then_with(|| self.os.to_string().cmp(&other.os.to_string()))
|
// Platforms are sorted in preferred order for the target
|
||||||
// Architectures are sorted in preferred order, with native architectures first
|
.then_with(|| self.platform.cmp(&other.platform).reverse())
|
||||||
.then_with(|| self.arch.cmp(&other.arch).reverse())
|
|
||||||
.then_with(|| self.libc.to_string().cmp(&other.libc.to_string()))
|
|
||||||
// Python variants are sorted in preferred order, with `Default` first
|
// Python variants are sorted in preferred order, with `Default` first
|
||||||
.then_with(|| self.variant.cmp(&other.variant).reverse())
|
.then_with(|| self.variant.cmp(&other.variant).reverse())
|
||||||
}
|
}
|
||||||
|
|
@ -632,14 +624,8 @@ impl fmt::Display for PythonInstallationMinorVersionKey {
|
||||||
};
|
};
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}-{}.{}{}-{}-{}-{}",
|
"{}-{}.{}{}-{}",
|
||||||
self.0.implementation,
|
self.0.implementation, self.0.major, self.0.minor, variant, self.0.platform,
|
||||||
self.0.major,
|
|
||||||
self.0.minor,
|
|
||||||
variant,
|
|
||||||
self.0.os,
|
|
||||||
self.0.arch,
|
|
||||||
self.0.libc,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -653,9 +639,9 @@ impl fmt::Debug for PythonInstallationMinorVersionKey {
|
||||||
.field("major", &self.0.major)
|
.field("major", &self.0.major)
|
||||||
.field("minor", &self.0.minor)
|
.field("minor", &self.0.minor)
|
||||||
.field("variant", &self.0.variant)
|
.field("variant", &self.0.variant)
|
||||||
.field("os", &self.0.os)
|
.field("os", &self.0.platform.os)
|
||||||
.field("arch", &self.0.arch)
|
.field("arch", &self.0.platform.arch)
|
||||||
.field("libc", &self.0.libc)
|
.field("libc", &self.0.platform.libc)
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -667,9 +653,7 @@ impl PartialEq for PythonInstallationMinorVersionKey {
|
||||||
self.0.implementation == other.0.implementation
|
self.0.implementation == other.0.implementation
|
||||||
&& self.0.major == other.0.major
|
&& self.0.major == other.0.major
|
||||||
&& self.0.minor == other.0.minor
|
&& self.0.minor == other.0.minor
|
||||||
&& self.0.os == other.0.os
|
&& self.0.platform == other.0.platform
|
||||||
&& self.0.arch == other.0.arch
|
|
||||||
&& self.0.libc == other.0.libc
|
|
||||||
&& self.0.variant == other.0.variant
|
&& self.0.variant == other.0.variant
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -681,9 +665,7 @@ impl Hash for PythonInstallationMinorVersionKey {
|
||||||
self.0.implementation.hash(state);
|
self.0.implementation.hash(state);
|
||||||
self.0.major.hash(state);
|
self.0.major.hash(state);
|
||||||
self.0.minor.hash(state);
|
self.0.minor.hash(state);
|
||||||
self.0.os.hash(state);
|
self.0.platform.hash(state);
|
||||||
self.0.arch.hash(state);
|
|
||||||
self.0.libc.hash(state);
|
|
||||||
self.0.variant.hash(state);
|
self.0.variant.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -693,3 +675,113 @@ impl From<PythonInstallationKey> for PythonInstallationMinorVersionKey {
|
||||||
Self(key)
|
Self(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use uv_platform::ArchVariant;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_python_installation_key_from_str() {
|
||||||
|
// Test basic parsing
|
||||||
|
let key = PythonInstallationKey::from_str("cpython-3.12.0-linux-x86_64-gnu").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
key.implementation,
|
||||||
|
LenientImplementationName::Known(ImplementationName::CPython)
|
||||||
|
);
|
||||||
|
assert_eq!(key.major, 3);
|
||||||
|
assert_eq!(key.minor, 12);
|
||||||
|
assert_eq!(key.patch, 0);
|
||||||
|
assert_eq!(
|
||||||
|
key.platform.os,
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Linux)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
key.platform.arch,
|
||||||
|
Arch::new(target_lexicon::Architecture::X86_64, None)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
key.platform.libc,
|
||||||
|
Libc::Some(target_lexicon::Environment::Gnu)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with architecture variant
|
||||||
|
let key = PythonInstallationKey::from_str("cpython-3.11.2-linux-x86_64_v3-musl").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
key.implementation,
|
||||||
|
LenientImplementationName::Known(ImplementationName::CPython)
|
||||||
|
);
|
||||||
|
assert_eq!(key.major, 3);
|
||||||
|
assert_eq!(key.minor, 11);
|
||||||
|
assert_eq!(key.patch, 2);
|
||||||
|
assert_eq!(
|
||||||
|
key.platform.os,
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Linux)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
key.platform.arch,
|
||||||
|
Arch::new(target_lexicon::Architecture::X86_64, Some(ArchVariant::V3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
key.platform.libc,
|
||||||
|
Libc::Some(target_lexicon::Environment::Musl)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test with Python variant (freethreaded)
|
||||||
|
let key = PythonInstallationKey::from_str("cpython-3.13.0+freethreaded-macos-aarch64-none")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
key.implementation,
|
||||||
|
LenientImplementationName::Known(ImplementationName::CPython)
|
||||||
|
);
|
||||||
|
assert_eq!(key.major, 3);
|
||||||
|
assert_eq!(key.minor, 13);
|
||||||
|
assert_eq!(key.patch, 0);
|
||||||
|
assert_eq!(key.variant, PythonVariant::Freethreaded);
|
||||||
|
assert_eq!(
|
||||||
|
key.platform.os,
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Darwin(None))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
key.platform.arch,
|
||||||
|
Arch::new(
|
||||||
|
target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_eq!(key.platform.libc, Libc::None);
|
||||||
|
|
||||||
|
// Test error cases
|
||||||
|
assert!(PythonInstallationKey::from_str("cpython-3.12.0-linux-x86_64").is_err());
|
||||||
|
assert!(PythonInstallationKey::from_str("cpython-3.12.0").is_err());
|
||||||
|
assert!(PythonInstallationKey::from_str("cpython").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_python_installation_key_display() {
|
||||||
|
let key = PythonInstallationKey {
|
||||||
|
implementation: LenientImplementationName::from("cpython"),
|
||||||
|
major: 3,
|
||||||
|
minor: 12,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: None,
|
||||||
|
platform: Platform::from_str("linux-x86_64-gnu").unwrap(),
|
||||||
|
variant: PythonVariant::Default,
|
||||||
|
};
|
||||||
|
assert_eq!(key.to_string(), "cpython-3.12.0-linux-x86_64-gnu");
|
||||||
|
|
||||||
|
let key_with_variant = PythonInstallationKey {
|
||||||
|
implementation: LenientImplementationName::from("cpython"),
|
||||||
|
major: 3,
|
||||||
|
minor: 13,
|
||||||
|
patch: 0,
|
||||||
|
prerelease: None,
|
||||||
|
platform: Platform::from_str("macos-aarch64-none").unwrap(),
|
||||||
|
variant: PythonVariant::Freethreaded,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
key_with_variant.to_string(),
|
||||||
|
"cpython-3.13.0+freethreaded-macos-aarch64-none"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ use uv_install_wheel::Layout;
|
||||||
use uv_pep440::Version;
|
use uv_pep440::Version;
|
||||||
use uv_pep508::{MarkerEnvironment, StringVersion};
|
use uv_pep508::{MarkerEnvironment, StringVersion};
|
||||||
use uv_platform::{Arch, Libc, Os};
|
use uv_platform::{Arch, Libc, Os};
|
||||||
use uv_platform_tags::Platform;
|
use uv_platform_tags::{Platform, Tags, TagsError};
|
||||||
use uv_platform_tags::{Tags, TagsError};
|
|
||||||
use uv_pypi_types::{ResolverMarkerEnvironment, Scheme};
|
use uv_pypi_types::{ResolverMarkerEnvironment, Scheme};
|
||||||
|
|
||||||
use crate::implementation::LenientImplementationName;
|
use crate::implementation::LenientImplementationName;
|
||||||
|
|
@ -205,9 +204,7 @@ impl Interpreter {
|
||||||
self.python_minor(),
|
self.python_minor(),
|
||||||
self.python_patch(),
|
self.python_patch(),
|
||||||
self.python_version().pre(),
|
self.python_version().pre(),
|
||||||
self.os(),
|
uv_platform::Platform::new(self.os(), self.arch(), self.libc()),
|
||||||
self.arch(),
|
|
||||||
self.libc(),
|
|
||||||
self.variant(),
|
self.variant(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
|
||||||
|
|
||||||
use uv_fs::{LockedFile, Simplified, replace_symlink, symlink_or_copy_file};
|
use uv_fs::{LockedFile, Simplified, replace_symlink, symlink_or_copy_file};
|
||||||
use uv_platform::Error as PlatformError;
|
use uv_platform::Error as PlatformError;
|
||||||
use uv_platform::{Arch, Libc, LibcDetectionError, Os};
|
use uv_platform::{LibcDetectionError, Platform};
|
||||||
use uv_state::{StateBucket, StateStore};
|
use uv_state::{StateBucket, StateStore};
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
use uv_trampoline_builder::{Launcher, windows_python_launcher};
|
use uv_trampoline_builder::{Launcher, windows_python_launcher};
|
||||||
|
|
@ -259,20 +259,11 @@ impl ManagedPythonInstallations {
|
||||||
pub fn find_matching_current_platform(
|
pub fn find_matching_current_platform(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation> + use<>, Error> {
|
) -> Result<impl DoubleEndedIterator<Item = ManagedPythonInstallation> + use<>, Error> {
|
||||||
let os = Os::from_env();
|
let platform = Platform::from_env()?;
|
||||||
let arch = Arch::from_env();
|
|
||||||
let libc = Libc::from_env()?;
|
|
||||||
|
|
||||||
let iter = Self::from_settings(None)?
|
let iter = Self::from_settings(None)?
|
||||||
.find_all()?
|
.find_all()?
|
||||||
.filter(move |installation| {
|
.filter(move |installation| platform.supports(installation.platform()));
|
||||||
installation.key.os == os
|
|
||||||
&& (arch.supports(installation.key.arch)
|
|
||||||
// TODO(zanieb): Allow inequal variants, as `Arch::supports` does not
|
|
||||||
// implement this yet. See https://github.com/astral-sh/uv/pull/9788
|
|
||||||
|| arch.family() == installation.key.arch.family())
|
|
||||||
&& installation.key.libc == libc
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(iter)
|
Ok(iter)
|
||||||
}
|
}
|
||||||
|
|
@ -451,6 +442,10 @@ impl ManagedPythonInstallation {
|
||||||
&self.key
|
&self.key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn platform(&self) -> &Platform {
|
||||||
|
self.key.platform()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn minor_version_key(&self) -> &PythonInstallationMinorVersionKey {
|
pub fn minor_version_key(&self) -> &PythonInstallationMinorVersionKey {
|
||||||
PythonInstallationMinorVersionKey::ref_cast(&self.key)
|
PythonInstallationMinorVersionKey::ref_cast(&self.key)
|
||||||
}
|
}
|
||||||
|
|
@ -544,7 +539,7 @@ impl ManagedPythonInstallation {
|
||||||
/// standard `EXTERNALLY-MANAGED` file.
|
/// standard `EXTERNALLY-MANAGED` file.
|
||||||
pub fn ensure_externally_managed(&self) -> Result<(), Error> {
|
pub fn ensure_externally_managed(&self) -> Result<(), Error> {
|
||||||
// Construct the path to the `stdlib` directory.
|
// Construct the path to the `stdlib` directory.
|
||||||
let stdlib = if self.key.os.is_windows() {
|
let stdlib = if self.key.os().is_windows() {
|
||||||
self.python_dir().join("Lib")
|
self.python_dir().join("Lib")
|
||||||
} else {
|
} else {
|
||||||
let lib_suffix = self.key.variant.suffix();
|
let lib_suffix = self.key.variant.suffix();
|
||||||
|
|
@ -588,7 +583,7 @@ impl ManagedPythonInstallation {
|
||||||
/// See <https://github.com/astral-sh/uv/issues/10598> for more information.
|
/// See <https://github.com/astral-sh/uv/issues/10598> for more information.
|
||||||
pub fn ensure_dylib_patched(&self) -> Result<(), macos_dylib::Error> {
|
pub fn ensure_dylib_patched(&self) -> Result<(), macos_dylib::Error> {
|
||||||
if cfg!(target_os = "macos") {
|
if cfg!(target_os = "macos") {
|
||||||
if self.key().os.is_like_darwin() {
|
if self.key().os().is_like_darwin() {
|
||||||
if *self.implementation() == ImplementationName::CPython {
|
if *self.implementation() == ImplementationName::CPython {
|
||||||
let dylib_path = self.python_dir().join("lib").join(format!(
|
let dylib_path = self.python_dir().join("lib").join(format!(
|
||||||
"{}python{}{}{}",
|
"{}python{}{}{}",
|
||||||
|
|
@ -890,10 +885,7 @@ pub fn create_link_to_executable(link: &Path, executable: &Path) -> Result<(), E
|
||||||
// TODO(zanieb): Only used in tests now.
|
// TODO(zanieb): Only used in tests now.
|
||||||
/// Generate a platform portion of a key from the environment.
|
/// Generate a platform portion of a key from the environment.
|
||||||
pub fn platform_key_from_env() -> Result<String, Error> {
|
pub fn platform_key_from_env() -> Result<String, Error> {
|
||||||
let os = Os::from_env();
|
Ok(Platform::from_env()?.to_string().to_lowercase())
|
||||||
let arch = Arch::from_env();
|
|
||||||
let libc = Libc::from_env()?;
|
|
||||||
Ok(format!("{os}-{arch}-{libc}").to_lowercase())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ManagedPythonInstallation {
|
impl fmt::Display for ManagedPythonInstallation {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue