mirror of
https://github.com/astral-sh/uv.git
synced 2025-10-26 18:06:45 +00:00
Split platform detection code into a dedicated uv-platform crate (#14918)
Some checks are pending
CI / check system | alpine (push) Blocked by required conditions
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 test | macos (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / cargo test | ubuntu (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 | x86_64 (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 / 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 / smoke test | linux aarch64 (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 | 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 | 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 | pypy on windows (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
Some checks are pending
CI / check system | alpine (push) Blocked by required conditions
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 test | macos (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / cargo test | ubuntu (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 | x86_64 (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 / 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 / smoke test | linux aarch64 (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 | 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 | 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 | pypy on windows (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
In service of some subsequent work...
This commit is contained in:
parent
5686771464
commit
00efde06b6
22 changed files with 530 additions and 469 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
|
@ -4732,6 +4732,7 @@ dependencies = [
|
||||||
"uv-pep440",
|
"uv-pep440",
|
||||||
"uv-pep508",
|
"uv-pep508",
|
||||||
"uv-performance-memory-allocator",
|
"uv-performance-memory-allocator",
|
||||||
|
"uv-platform",
|
||||||
"uv-platform-tags",
|
"uv-platform-tags",
|
||||||
"uv-publish",
|
"uv-publish",
|
||||||
"uv-pypi-types",
|
"uv-pypi-types",
|
||||||
|
|
@ -5553,6 +5554,23 @@ dependencies = [
|
||||||
"tikv-jemallocator",
|
"tikv-jemallocator",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uv-platform"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"fs-err",
|
||||||
|
"goblin",
|
||||||
|
"indoc",
|
||||||
|
"procfs",
|
||||||
|
"regex",
|
||||||
|
"target-lexicon",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"tracing",
|
||||||
|
"uv-fs",
|
||||||
|
"uv-platform-tags",
|
||||||
|
"uv-static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uv-platform-tags"
|
name = "uv-platform-tags"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
@ -5646,14 +5664,12 @@ dependencies = [
|
||||||
"dunce",
|
"dunce",
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"futures",
|
"futures",
|
||||||
"goblin",
|
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"indoc",
|
"indoc",
|
||||||
"insta",
|
"insta",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"procfs",
|
|
||||||
"ref-cast",
|
"ref-cast",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
|
@ -5687,6 +5703,7 @@ dependencies = [
|
||||||
"uv-install-wheel",
|
"uv-install-wheel",
|
||||||
"uv-pep440",
|
"uv-pep440",
|
||||||
"uv-pep508",
|
"uv-pep508",
|
||||||
|
"uv-platform",
|
||||||
"uv-platform-tags",
|
"uv-platform-tags",
|
||||||
"uv-pypi-types",
|
"uv-pypi-types",
|
||||||
"uv-redacted",
|
"uv-redacted",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ uv-once-map = { path = "crates/uv-once-map" }
|
||||||
uv-options-metadata = { path = "crates/uv-options-metadata" }
|
uv-options-metadata = { path = "crates/uv-options-metadata" }
|
||||||
uv-pep440 = { path = "crates/uv-pep440", features = ["tracing", "rkyv", "version-ranges"] }
|
uv-pep440 = { path = "crates/uv-pep440", features = ["tracing", "rkyv", "version-ranges"] }
|
||||||
uv-pep508 = { path = "crates/uv-pep508", features = ["non-pep508-extensions"] }
|
uv-pep508 = { path = "crates/uv-pep508", features = ["non-pep508-extensions"] }
|
||||||
|
uv-platform = { path = "crates/uv-platform" }
|
||||||
uv-platform-tags = { path = "crates/uv-platform-tags" }
|
uv-platform-tags = { path = "crates/uv-platform-tags" }
|
||||||
uv-publish = { path = "crates/uv-publish" }
|
uv-publish = { path = "crates/uv-publish" }
|
||||||
uv-pypi-types = { path = "crates/uv-pypi-types" }
|
uv-pypi-types = { path = "crates/uv-pypi-types" }
|
||||||
|
|
|
||||||
34
crates/uv-platform/Cargo.toml
Normal file
34
crates/uv-platform/Cargo.toml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
[package]
|
||||||
|
name = "uv-platform"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = { workspace = true }
|
||||||
|
rust-version = { workspace = true }
|
||||||
|
homepage = { workspace = true }
|
||||||
|
documentation = { workspace = true }
|
||||||
|
repository = { workspace = true }
|
||||||
|
authors = { workspace = true }
|
||||||
|
license = { workspace = true }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
uv-static = { workspace = true }
|
||||||
|
uv-fs = { workspace = true }
|
||||||
|
uv-platform-tags = { workspace = true }
|
||||||
|
|
||||||
|
fs-err = { workspace = true }
|
||||||
|
goblin = { workspace = true }
|
||||||
|
regex = { workspace = true }
|
||||||
|
target-lexicon = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
procfs = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
indoc = { workspace = true }
|
||||||
249
crates/uv-platform/src/arch.rs
Normal file
249
crates/uv-platform/src/arch.rs
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
use crate::Error;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::{cmp, fmt};
|
||||||
|
|
||||||
|
/// Architecture variants, e.g., with support for different instruction sets
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Ord, PartialOrd)]
|
||||||
|
pub enum ArchVariant {
|
||||||
|
/// Targets 64-bit Intel/AMD CPUs newer than Nehalem (2008).
|
||||||
|
/// Includes SSE3, SSE4 and other post-2003 CPU instructions.
|
||||||
|
V2,
|
||||||
|
/// Targets 64-bit Intel/AMD CPUs newer than Haswell (2013) and Excavator (2015).
|
||||||
|
/// Includes AVX, AVX2, MOVBE and other newer CPU instructions.
|
||||||
|
V3,
|
||||||
|
/// Targets 64-bit Intel/AMD CPUs with AVX-512 instructions (post-2017 Intel CPUs).
|
||||||
|
/// Many post-2017 Intel CPUs do not support AVX-512.
|
||||||
|
V4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||||
|
pub struct Arch {
|
||||||
|
pub(crate) family: target_lexicon::Architecture,
|
||||||
|
pub(crate) variant: Option<ArchVariant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Arch {
|
||||||
|
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||||
|
if self.family == other.family {
|
||||||
|
return self.variant.cmp(&other.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 cfg!(all(windows, target_arch = "aarch64")) {
|
||||||
|
Arch {
|
||||||
|
family: target_lexicon::Architecture::X86_64,
|
||||||
|
variant: None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Prefer native architectures
|
||||||
|
Arch::from_env()
|
||||||
|
};
|
||||||
|
|
||||||
|
match (
|
||||||
|
self.family == preferred.family,
|
||||||
|
other.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.family.to_string().cmp(&other.family.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Arch {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arch {
|
||||||
|
pub fn new(family: target_lexicon::Architecture, variant: Option<ArchVariant>) -> Self {
|
||||||
|
Self { family, variant }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_env() -> Self {
|
||||||
|
Self {
|
||||||
|
family: target_lexicon::HOST.architecture,
|
||||||
|
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 {
|
||||||
|
self.family
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_arm(&self) -> bool {
|
||||||
|
matches!(self.family, target_lexicon::Architecture::Arm(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Arch {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self.family {
|
||||||
|
target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686) => {
|
||||||
|
write!(f, "x86")?;
|
||||||
|
}
|
||||||
|
inner => write!(f, "{inner}")?,
|
||||||
|
}
|
||||||
|
if let Some(variant) = self.variant {
|
||||||
|
write!(f, "_{variant}")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Arch {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
fn parse_family(s: &str) -> Result<target_lexicon::Architecture, Error> {
|
||||||
|
let inner = match s {
|
||||||
|
// Allow users to specify "x86" as a shorthand for the "i686" variant, they should not need
|
||||||
|
// to specify the exact architecture and this variant is what we have downloads for.
|
||||||
|
"x86" => {
|
||||||
|
target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686)
|
||||||
|
}
|
||||||
|
_ => target_lexicon::Architecture::from_str(s)
|
||||||
|
.map_err(|()| Error::UnknownArch(s.to_string()))?,
|
||||||
|
};
|
||||||
|
if matches!(inner, target_lexicon::Architecture::Unknown) {
|
||||||
|
return Err(Error::UnknownArch(s.to_string()));
|
||||||
|
}
|
||||||
|
Ok(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First check for a variant
|
||||||
|
if let Some((Ok(family), Ok(variant))) = s
|
||||||
|
.rsplit_once('_')
|
||||||
|
.map(|(family, variant)| (parse_family(family), ArchVariant::from_str(variant)))
|
||||||
|
{
|
||||||
|
// We only support variants for `x86_64` right now
|
||||||
|
if !matches!(family, target_lexicon::Architecture::X86_64) {
|
||||||
|
return Err(Error::UnsupportedVariant(
|
||||||
|
variant.to_string(),
|
||||||
|
family.to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return Ok(Self {
|
||||||
|
family,
|
||||||
|
variant: Some(variant),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let family = parse_family(s)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
family,
|
||||||
|
variant: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ArchVariant {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"v2" => Ok(Self::V2),
|
||||||
|
"v3" => Ok(Self::V3),
|
||||||
|
"v4" => Ok(Self::V4),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ArchVariant {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::V2 => write!(f, "v2"),
|
||||||
|
Self::V3 => write!(f, "v3"),
|
||||||
|
Self::V4 => write!(f, "v4"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&uv_platform_tags::Arch> for Arch {
|
||||||
|
fn from(value: &uv_platform_tags::Arch) -> Self {
|
||||||
|
match value {
|
||||||
|
uv_platform_tags::Arch::Aarch64 => Arch::new(
|
||||||
|
target_lexicon::Architecture::Aarch64(target_lexicon::Aarch64Architecture::Aarch64),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
uv_platform_tags::Arch::Armv5TEL => Arch::new(
|
||||||
|
target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv5te),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
uv_platform_tags::Arch::Armv6L => Arch::new(
|
||||||
|
target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv6),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
uv_platform_tags::Arch::Armv7L => Arch::new(
|
||||||
|
target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv7),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
uv_platform_tags::Arch::S390X => Arch::new(target_lexicon::Architecture::S390x, None),
|
||||||
|
uv_platform_tags::Arch::Powerpc => {
|
||||||
|
Arch::new(target_lexicon::Architecture::Powerpc, None)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Arch::Powerpc64 => {
|
||||||
|
Arch::new(target_lexicon::Architecture::Powerpc64, None)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Arch::Powerpc64Le => {
|
||||||
|
Arch::new(target_lexicon::Architecture::Powerpc64le, None)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Arch::X86 => Arch::new(
|
||||||
|
target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
uv_platform_tags::Arch::X86_64 => Arch::new(target_lexicon::Architecture::X86_64, None),
|
||||||
|
uv_platform_tags::Arch::LoongArch64 => {
|
||||||
|
Arch::new(target_lexicon::Architecture::LoongArch64, None)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Arch::Riscv64 => Arch::new(
|
||||||
|
target_lexicon::Architecture::Riscv64(target_lexicon::Riscv64Architecture::Riscv64),
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
uv_platform_tags::Arch::Wasm32 => Arch::new(target_lexicon::Architecture::Wasm32, None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! Fetches CPU information.
|
//! Fetches CPU information.
|
||||||
|
|
||||||
use anyhow::Error;
|
use std::io::Error;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use procfs::{CpuInfo, Current};
|
use procfs::{CpuInfo, Current};
|
||||||
|
|
@ -14,7 +14,7 @@ use procfs::{CpuInfo, Current};
|
||||||
/// More information on this can be found in the [Debian ARM Hard Float Port documentation](https://wiki.debian.org/ArmHardFloatPort#VFP).
|
/// More information on this can be found in the [Debian ARM Hard Float Port documentation](https://wiki.debian.org/ArmHardFloatPort#VFP).
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub(crate) fn detect_hardware_floating_point_support() -> Result<bool, Error> {
|
pub(crate) fn detect_hardware_floating_point_support() -> Result<bool, Error> {
|
||||||
let cpu_info = CpuInfo::current()?;
|
let cpu_info = CpuInfo::current().map_err(Error::other)?;
|
||||||
if let Some(features) = cpu_info.fields.get("Features") {
|
if let Some(features) = cpu_info.fields.get("Features") {
|
||||||
if features.contains("vfp") {
|
if features.contains("vfp") {
|
||||||
return Ok(true); // "vfp" found: hard-float (gnueabihf) detected
|
return Ok(true); // "vfp" found: hard-float (gnueabihf) detected
|
||||||
26
crates/uv-platform/src/lib.rs
Normal file
26
crates/uv-platform/src/lib.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
//! Platform detection for operating system, architecture, and libc.
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub use crate::arch::{Arch, ArchVariant};
|
||||||
|
pub use crate::libc::{Libc, LibcDetectionError, LibcVersion};
|
||||||
|
pub use crate::os::Os;
|
||||||
|
|
||||||
|
mod arch;
|
||||||
|
mod cpuinfo;
|
||||||
|
mod libc;
|
||||||
|
mod os;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Unknown operating system: {0}")]
|
||||||
|
UnknownOs(String),
|
||||||
|
#[error("Unknown architecture: {0}")]
|
||||||
|
UnknownArch(String),
|
||||||
|
#[error("Unknown libc environment: {0}")]
|
||||||
|
UnknownLibc(String),
|
||||||
|
#[error("Unsupported variant `{0}` for architecture `{1}`")]
|
||||||
|
UnsupportedVariant(String, String),
|
||||||
|
#[error(transparent)]
|
||||||
|
LibcDetectionError(#[from] crate::libc::LibcDetectionError),
|
||||||
|
}
|
||||||
|
|
@ -3,18 +3,22 @@
|
||||||
//! Taken from `glibc_version` (<https://github.com/delta-incubator/glibc-version-rs>),
|
//! Taken from `glibc_version` (<https://github.com/delta-incubator/glibc-version-rs>),
|
||||||
//! which used the Apache 2.0 license (but not the MIT license)
|
//! which used the Apache 2.0 license (but not the MIT license)
|
||||||
|
|
||||||
|
use crate::cpuinfo::detect_hardware_floating_point_support;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use goblin::elf::Elf;
|
use goblin::elf::Elf;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use thiserror::Error;
|
use std::{env, fmt};
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum LibcDetectionError {
|
pub enum LibcDetectionError {
|
||||||
#[error(
|
#[error(
|
||||||
"Could not detect either glibc version nor musl libc version, at least one of which is required"
|
"Could not detect either glibc version nor musl libc version, at least one of which is required"
|
||||||
|
|
@ -45,11 +49,89 @@ pub enum LibcDetectionError {
|
||||||
|
|
||||||
/// We support glibc (manylinux) and musl (musllinux) on linux.
|
/// We support glibc (manylinux) and musl (musllinux) on linux.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum LibcVersion {
|
pub enum LibcVersion {
|
||||||
Manylinux { major: u32, minor: u32 },
|
Manylinux { major: u32, minor: u32 },
|
||||||
Musllinux { major: u32, minor: u32 },
|
Musllinux { major: u32, minor: u32 },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||||
|
pub enum Libc {
|
||||||
|
Some(target_lexicon::Environment),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Libc {
|
||||||
|
pub fn from_env() -> Result<Self, crate::Error> {
|
||||||
|
match env::consts::OS {
|
||||||
|
"linux" => {
|
||||||
|
if let Ok(libc) = env::var(EnvVars::UV_LIBC) {
|
||||||
|
if !libc.is_empty() {
|
||||||
|
return Self::from_str(&libc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self::Some(match detect_linux_libc()? {
|
||||||
|
LibcVersion::Manylinux { .. } => match env::consts::ARCH {
|
||||||
|
// Checks if the CPU supports hardware floating-point operations.
|
||||||
|
// Depending on the result, it selects either the `gnueabihf` (hard-float) or `gnueabi` (soft-float) environment.
|
||||||
|
// download-metadata.json only includes armv7.
|
||||||
|
"arm" | "armv5te" | "armv7" => {
|
||||||
|
match detect_hardware_floating_point_support() {
|
||||||
|
Ok(true) => target_lexicon::Environment::Gnueabihf,
|
||||||
|
Ok(false) => target_lexicon::Environment::Gnueabi,
|
||||||
|
Err(_) => target_lexicon::Environment::Gnu,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => target_lexicon::Environment::Gnu,
|
||||||
|
},
|
||||||
|
LibcVersion::Musllinux { .. } => target_lexicon::Environment::Musl,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
"windows" | "macos" => Ok(Self::None),
|
||||||
|
// Use `None` on platforms without explicit support.
|
||||||
|
_ => Ok(Self::None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_musl(&self) -> bool {
|
||||||
|
matches!(self, Self::Some(target_lexicon::Environment::Musl))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Libc {
|
||||||
|
type Err = crate::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"gnu" => Ok(Self::Some(target_lexicon::Environment::Gnu)),
|
||||||
|
"gnueabi" => Ok(Self::Some(target_lexicon::Environment::Gnueabi)),
|
||||||
|
"gnueabihf" => Ok(Self::Some(target_lexicon::Environment::Gnueabihf)),
|
||||||
|
"musl" => Ok(Self::Some(target_lexicon::Environment::Musl)),
|
||||||
|
"none" => Ok(Self::None),
|
||||||
|
_ => Err(crate::Error::UnknownLibc(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Libc {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Some(env) => write!(f, "{env}"),
|
||||||
|
Self::None => write!(f, "none"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&uv_platform_tags::Os> for Libc {
|
||||||
|
fn from(value: &uv_platform_tags::Os) -> Self {
|
||||||
|
match value {
|
||||||
|
uv_platform_tags::Os::Manylinux { .. } => Libc::Some(target_lexicon::Environment::Gnu),
|
||||||
|
uv_platform_tags::Os::Musllinux { .. } => Libc::Some(target_lexicon::Environment::Musl),
|
||||||
|
_ => Libc::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine whether we're running glibc or musl and in which version, given we are on linux.
|
/// Determine whether we're running glibc or musl and in which version, given we are on linux.
|
||||||
///
|
///
|
||||||
/// Normally, we determine this from the python interpreter, which is more accurate, but when
|
/// Normally, we determine this from the python interpreter, which is more accurate, but when
|
||||||
88
crates/uv-platform/src/os.rs
Normal file
88
crates/uv-platform/src/os.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
use crate::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
||||||
|
pub struct Os(pub(crate) target_lexicon::OperatingSystem);
|
||||||
|
|
||||||
|
impl Os {
|
||||||
|
pub fn new(os: target_lexicon::OperatingSystem) -> Self {
|
||||||
|
Self(os)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_env() -> Self {
|
||||||
|
Self(target_lexicon::HOST.operating_system)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_windows(&self) -> bool {
|
||||||
|
matches!(self.0, target_lexicon::OperatingSystem::Windows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Os {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match &**self {
|
||||||
|
target_lexicon::OperatingSystem::Darwin(_) => write!(f, "macos"),
|
||||||
|
inner => write!(f, "{inner}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Os {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let inner = match s {
|
||||||
|
"macos" => target_lexicon::OperatingSystem::Darwin(None),
|
||||||
|
_ => target_lexicon::OperatingSystem::from_str(s)
|
||||||
|
.map_err(|()| Error::UnknownOs(s.to_string()))?,
|
||||||
|
};
|
||||||
|
if matches!(inner, target_lexicon::OperatingSystem::Unknown) {
|
||||||
|
return Err(Error::UnknownOs(s.to_string()));
|
||||||
|
}
|
||||||
|
Ok(Self(inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Os {
|
||||||
|
type Target = target_lexicon::OperatingSystem;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&uv_platform_tags::Os> for Os {
|
||||||
|
fn from(value: &uv_platform_tags::Os) -> Self {
|
||||||
|
match value {
|
||||||
|
uv_platform_tags::Os::Dragonfly { .. } => {
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Dragonfly)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Os::FreeBsd { .. } => {
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Freebsd)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Os::Haiku { .. } => Os::new(target_lexicon::OperatingSystem::Haiku),
|
||||||
|
uv_platform_tags::Os::Illumos { .. } => {
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Illumos)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Os::Macos { .. } => {
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Darwin(None))
|
||||||
|
}
|
||||||
|
uv_platform_tags::Os::Manylinux { .. }
|
||||||
|
| uv_platform_tags::Os::Musllinux { .. }
|
||||||
|
| uv_platform_tags::Os::Android { .. } => {
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Linux)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Os::NetBsd { .. } => Os::new(target_lexicon::OperatingSystem::Netbsd),
|
||||||
|
uv_platform_tags::Os::OpenBsd { .. } => {
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Openbsd)
|
||||||
|
}
|
||||||
|
uv_platform_tags::Os::Windows => Os::new(target_lexicon::OperatingSystem::Windows),
|
||||||
|
uv_platform_tags::Os::Pyodide { .. } => {
|
||||||
|
Os::new(target_lexicon::OperatingSystem::Emscripten)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ uv-fs = { workspace = true }
|
||||||
uv-install-wheel = { workspace = true }
|
uv-install-wheel = { workspace = true }
|
||||||
uv-pep440 = { workspace = true }
|
uv-pep440 = { workspace = true }
|
||||||
uv-pep508 = { workspace = true }
|
uv-pep508 = { workspace = true }
|
||||||
|
uv-platform = { workspace = true }
|
||||||
uv-platform-tags = { workspace = true }
|
uv-platform-tags = { workspace = true }
|
||||||
uv-pypi-types = { workspace = true }
|
uv-pypi-types = { workspace = true }
|
||||||
uv-redacted = { workspace = true }
|
uv-redacted = { workspace = true }
|
||||||
|
|
@ -42,7 +43,6 @@ configparser = { workspace = true }
|
||||||
dunce = { workspace = true }
|
dunce = { workspace = true }
|
||||||
fs-err = { workspace = true, features = ["tokio"] }
|
fs-err = { workspace = true, features = ["tokio"] }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
goblin = { workspace = true, default-features = false }
|
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
owo-colors = { workspace = true }
|
owo-colors = { workspace = true }
|
||||||
|
|
@ -68,9 +68,6 @@ url = { workspace = true }
|
||||||
which = { workspace = true }
|
which = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
procfs = { workspace = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
windows-registry = { workspace = true }
|
windows-registry = { workspace = true }
|
||||||
windows-result = { workspace = true }
|
windows-result = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -3066,8 +3066,8 @@ mod tests {
|
||||||
discovery::{PythonRequest, VersionRequest},
|
discovery::{PythonRequest, VersionRequest},
|
||||||
downloads::{ArchRequest, PythonDownloadRequest},
|
downloads::{ArchRequest, PythonDownloadRequest},
|
||||||
implementation::ImplementationName,
|
implementation::ImplementationName,
|
||||||
platform::{Arch, Libc, Os},
|
|
||||||
};
|
};
|
||||||
|
use uv_platform::{Arch, Libc, Os};
|
||||||
|
|
||||||
use super::{Error, PythonVariant};
|
use super::{Error, PythonVariant};
|
||||||
|
|
||||||
|
|
@ -3154,11 +3154,11 @@ mod tests {
|
||||||
PythonVariant::Default
|
PythonVariant::Default
|
||||||
)),
|
)),
|
||||||
implementation: Some(ImplementationName::CPython),
|
implementation: Some(ImplementationName::CPython),
|
||||||
arch: Some(ArchRequest::Explicit(Arch {
|
arch: Some(ArchRequest::Explicit(Arch::new(
|
||||||
family: Architecture::Aarch64(Aarch64Architecture::Aarch64),
|
Architecture::Aarch64(Aarch64Architecture::Aarch64),
|
||||||
variant: None
|
None
|
||||||
})),
|
))),
|
||||||
os: Some(Os(target_lexicon::OperatingSystem::Darwin(None))),
|
os: Some(Os::new(target_lexicon::OperatingSystem::Darwin(None))),
|
||||||
libc: Some(Libc::None),
|
libc: Some(Libc::None),
|
||||||
prereleases: None
|
prereleases: None
|
||||||
})
|
})
|
||||||
|
|
@ -3189,10 +3189,10 @@ mod tests {
|
||||||
PythonVariant::Default
|
PythonVariant::Default
|
||||||
)),
|
)),
|
||||||
implementation: None,
|
implementation: None,
|
||||||
arch: Some(ArchRequest::Explicit(Arch {
|
arch: Some(ArchRequest::Explicit(Arch::new(
|
||||||
family: Architecture::Aarch64(Aarch64Architecture::Aarch64),
|
Architecture::Aarch64(Aarch64Architecture::Aarch64),
|
||||||
variant: None
|
None
|
||||||
})),
|
))),
|
||||||
os: None,
|
os: None,
|
||||||
libc: None,
|
libc: None,
|
||||||
prereleases: None
|
prereleases: None
|
||||||
|
|
|
||||||
|
|
@ -25,6 +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_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;
|
||||||
|
|
@ -34,9 +35,7 @@ use crate::implementation::{
|
||||||
Error as ImplementationError, ImplementationName, LenientImplementationName,
|
Error as ImplementationError, ImplementationName, LenientImplementationName,
|
||||||
};
|
};
|
||||||
use crate::installation::PythonInstallationKey;
|
use crate::installation::PythonInstallationKey;
|
||||||
use crate::libc::LibcDetectionError;
|
|
||||||
use crate::managed::ManagedPythonInstallation;
|
use crate::managed::ManagedPythonInstallation;
|
||||||
use crate::platform::{self, Arch, Libc, Os};
|
|
||||||
use crate::{Interpreter, PythonRequest, PythonVersion, VersionRequest};
|
use crate::{Interpreter, PythonRequest, PythonVersion, VersionRequest};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
@ -98,7 +97,7 @@ pub enum Error {
|
||||||
#[error("A mirror was provided via `{0}`, but the URL does not match the expected format: {0}")]
|
#[error("A mirror was provided via `{0}`, but the URL does not match the expected format: {0}")]
|
||||||
Mirror(&'static str, &'static str),
|
Mirror(&'static str, &'static str),
|
||||||
#[error("Failed to determine the libc used on the current platform")]
|
#[error("Failed to determine the libc used on the current platform")]
|
||||||
LibcDetection(#[from] LibcDetectionError),
|
LibcDetection(#[from] platform::LibcDetectionError),
|
||||||
#[error("Remote Python downloads JSON is not yet supported, please use a local path")]
|
#[error("Remote Python downloads JSON is not yet supported, please use a local path")]
|
||||||
RemoteJSONNotSupported,
|
RemoteJSONNotSupported,
|
||||||
#[error("The JSON of the python downloads is invalid: {0}")]
|
#[error("The JSON of the python downloads is invalid: {0}")]
|
||||||
|
|
|
||||||
|
|
@ -10,6 +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 crate::discovery::{
|
use crate::discovery::{
|
||||||
EnvironmentPreference, PythonRequest, find_best_python_installation, find_python_installation,
|
EnvironmentPreference, PythonRequest, find_best_python_installation, find_python_installation,
|
||||||
|
|
@ -17,7 +18,6 @@ use crate::discovery::{
|
||||||
use crate::downloads::{DownloadResult, ManagedPythonDownload, PythonDownloadRequest, Reporter};
|
use crate::downloads::{DownloadResult, ManagedPythonDownload, PythonDownloadRequest, Reporter};
|
||||||
use crate::implementation::LenientImplementationName;
|
use crate::implementation::LenientImplementationName;
|
||||||
use crate::managed::{ManagedPythonInstallation, ManagedPythonInstallations};
|
use crate::managed::{ManagedPythonInstallation, ManagedPythonInstallations};
|
||||||
use crate::platform::{Arch, Libc, Os};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Error, ImplementationName, Interpreter, PythonDownloads, PythonPreference, PythonSource,
|
Error, ImplementationName, Interpreter, PythonDownloads, PythonPreference, PythonSource,
|
||||||
PythonVariant, PythonVersion, downloads,
|
PythonVariant, PythonVersion, downloads,
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,13 @@ use uv_fs::{LockedFile, PythonExt, Simplified, write_atomic_sync};
|
||||||
use uv_install_wheel::Layout;
|
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_tags::Platform;
|
use uv_platform_tags::Platform;
|
||||||
use uv_platform_tags::{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;
|
||||||
use crate::managed::ManagedPythonInstallations;
|
use crate::managed::ManagedPythonInstallations;
|
||||||
use crate::platform::{Arch, Libc, Os};
|
|
||||||
use crate::pointer_size::PointerSize;
|
use crate::pointer_size::PointerSize;
|
||||||
use crate::{
|
use crate::{
|
||||||
Prefix, PythonInstallationKey, PythonVariant, PythonVersion, Target, VersionRequest,
|
Prefix, PythonInstallationKey, PythonVariant, PythonVersion, Target, VersionRequest,
|
||||||
|
|
|
||||||
|
|
@ -29,19 +29,16 @@ pub use crate::version_files::{
|
||||||
};
|
};
|
||||||
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
|
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
|
||||||
|
|
||||||
mod cpuinfo;
|
|
||||||
mod discovery;
|
mod discovery;
|
||||||
pub mod downloads;
|
pub mod downloads;
|
||||||
mod environment;
|
mod environment;
|
||||||
mod implementation;
|
mod implementation;
|
||||||
mod installation;
|
mod installation;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
mod libc;
|
|
||||||
pub mod macos_dylib;
|
pub mod macos_dylib;
|
||||||
pub mod managed;
|
pub mod managed;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
mod microsoft_store;
|
mod microsoft_store;
|
||||||
pub mod platform;
|
|
||||||
mod pointer_size;
|
mod pointer_size;
|
||||||
mod prefix;
|
mod prefix;
|
||||||
mod python_version;
|
mod python_version;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ use uv_configuration::{Preview, PreviewFeatures};
|
||||||
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
|
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::{Arch, Libc, LibcDetectionError, Os};
|
||||||
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};
|
||||||
|
|
@ -26,9 +28,6 @@ use crate::implementation::{
|
||||||
Error as ImplementationError, ImplementationName, LenientImplementationName,
|
Error as ImplementationError, ImplementationName, LenientImplementationName,
|
||||||
};
|
};
|
||||||
use crate::installation::{self, PythonInstallationKey};
|
use crate::installation::{self, PythonInstallationKey};
|
||||||
use crate::libc::LibcDetectionError;
|
|
||||||
use crate::platform::Error as PlatformError;
|
|
||||||
use crate::platform::{Arch, Libc, Os};
|
|
||||||
use crate::python_version::PythonVersion;
|
use crate::python_version::PythonVersion;
|
||||||
use crate::{
|
use crate::{
|
||||||
PythonInstallationMinorVersionKey, PythonRequest, PythonVariant, macos_dylib, sysconfig,
|
PythonInstallationMinorVersionKey, PythonRequest, PythonVariant, macos_dylib, sysconfig,
|
||||||
|
|
@ -271,7 +270,7 @@ impl ManagedPythonInstallations {
|
||||||
&& (arch.supports(installation.key.arch)
|
&& (arch.supports(installation.key.arch)
|
||||||
// TODO(zanieb): Allow inequal variants, as `Arch::supports` does not
|
// TODO(zanieb): Allow inequal variants, as `Arch::supports` does not
|
||||||
// implement this yet. See https://github.com/astral-sh/uv/pull/9788
|
// implement this yet. See https://github.com/astral-sh/uv/pull/9788
|
||||||
|| arch.family == installation.key.arch.family)
|
|| arch.family() == installation.key.arch.family())
|
||||||
&& installation.key.libc == libc
|
&& installation.key.libc == libc
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -545,7 +544,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 matches!(self.key.os, Os(target_lexicon::OperatingSystem::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();
|
||||||
|
|
|
||||||
|
|
@ -1,427 +0,0 @@
|
||||||
use crate::cpuinfo::detect_hardware_floating_point_support;
|
|
||||||
use crate::libc::{LibcDetectionError, LibcVersion, detect_linux_libc};
|
|
||||||
use std::fmt::Display;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::{fmt, str::FromStr};
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use uv_static::EnvVars;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("Unknown operating system: {0}")]
|
|
||||||
UnknownOs(String),
|
|
||||||
#[error("Unknown architecture: {0}")]
|
|
||||||
UnknownArch(String),
|
|
||||||
#[error("Unknown libc environment: {0}")]
|
|
||||||
UnknownLibc(String),
|
|
||||||
#[error("Unsupported variant `{0}` for architecture `{1}`")]
|
|
||||||
UnsupportedVariant(String, String),
|
|
||||||
#[error(transparent)]
|
|
||||||
LibcDetectionError(#[from] LibcDetectionError),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Architecture variants, e.g., with support for different instruction sets
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash, Ord, PartialOrd)]
|
|
||||||
pub enum ArchVariant {
|
|
||||||
/// Targets 64-bit Intel/AMD CPUs newer than Nehalem (2008).
|
|
||||||
/// Includes SSE3, SSE4 and other post-2003 CPU instructions.
|
|
||||||
V2,
|
|
||||||
/// Targets 64-bit Intel/AMD CPUs newer than Haswell (2013) and Excavator (2015).
|
|
||||||
/// Includes AVX, AVX2, MOVBE and other newer CPU instructions.
|
|
||||||
V3,
|
|
||||||
/// Targets 64-bit Intel/AMD CPUs with AVX-512 instructions (post-2017 Intel CPUs).
|
|
||||||
/// Many post-2017 Intel CPUs do not support AVX-512.
|
|
||||||
V4,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
|
||||||
pub struct Arch {
|
|
||||||
pub(crate) family: target_lexicon::Architecture,
|
|
||||||
pub(crate) variant: Option<ArchVariant>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Arch {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
if self.family == other.family {
|
|
||||||
return self.variant.cmp(&other.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 cfg!(all(windows, target_arch = "aarch64")) {
|
|
||||||
Arch {
|
|
||||||
family: target_lexicon::Architecture::X86_64,
|
|
||||||
variant: None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Prefer native architectures
|
|
||||||
Arch::from_env()
|
|
||||||
};
|
|
||||||
|
|
||||||
match (
|
|
||||||
self.family == preferred.family,
|
|
||||||
other.family == preferred.family,
|
|
||||||
) {
|
|
||||||
(true, true) => unreachable!(),
|
|
||||||
(true, false) => std::cmp::Ordering::Less,
|
|
||||||
(false, true) => std::cmp::Ordering::Greater,
|
|
||||||
(false, false) => {
|
|
||||||
// Both non-preferred, fallback to lexicographic order
|
|
||||||
self.family.to_string().cmp(&other.family.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Arch {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
|
||||||
pub struct Os(pub(crate) target_lexicon::OperatingSystem);
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
|
|
||||||
pub enum Libc {
|
|
||||||
Some(target_lexicon::Environment),
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Libc {
|
|
||||||
pub(crate) fn from_env() -> Result<Self, Error> {
|
|
||||||
match std::env::consts::OS {
|
|
||||||
"linux" => {
|
|
||||||
if let Ok(libc) = std::env::var(EnvVars::UV_LIBC) {
|
|
||||||
if !libc.is_empty() {
|
|
||||||
return Self::from_str(&libc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self::Some(match detect_linux_libc()? {
|
|
||||||
LibcVersion::Manylinux { .. } => match std::env::consts::ARCH {
|
|
||||||
// Checks if the CPU supports hardware floating-point operations.
|
|
||||||
// Depending on the result, it selects either the `gnueabihf` (hard-float) or `gnueabi` (soft-float) environment.
|
|
||||||
// download-metadata.json only includes armv7.
|
|
||||||
"arm" | "armv5te" | "armv7" => {
|
|
||||||
match detect_hardware_floating_point_support() {
|
|
||||||
Ok(true) => target_lexicon::Environment::Gnueabihf,
|
|
||||||
Ok(false) => target_lexicon::Environment::Gnueabi,
|
|
||||||
Err(_) => target_lexicon::Environment::Gnu,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => target_lexicon::Environment::Gnu,
|
|
||||||
},
|
|
||||||
LibcVersion::Musllinux { .. } => target_lexicon::Environment::Musl,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
"windows" | "macos" => Ok(Self::None),
|
|
||||||
// Use `None` on platforms without explicit support.
|
|
||||||
_ => Ok(Self::None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_musl(&self) -> bool {
|
|
||||||
matches!(self, Self::Some(target_lexicon::Environment::Musl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Libc {
|
|
||||||
type Err = Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s {
|
|
||||||
"gnu" => Ok(Self::Some(target_lexicon::Environment::Gnu)),
|
|
||||||
"gnueabi" => Ok(Self::Some(target_lexicon::Environment::Gnueabi)),
|
|
||||||
"gnueabihf" => Ok(Self::Some(target_lexicon::Environment::Gnueabihf)),
|
|
||||||
"musl" => Ok(Self::Some(target_lexicon::Environment::Musl)),
|
|
||||||
"none" => Ok(Self::None),
|
|
||||||
_ => Err(Error::UnknownLibc(s.to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Os {
|
|
||||||
pub fn from_env() -> Self {
|
|
||||||
Self(target_lexicon::HOST.operating_system)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Arch {
|
|
||||||
pub fn from_env() -> Self {
|
|
||||||
Self {
|
|
||||||
family: target_lexicon::HOST.architecture,
|
|
||||||
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(crate) 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 {
|
|
||||||
self.family
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_arm(&self) -> bool {
|
|
||||||
matches!(self.family, target_lexicon::Architecture::Arm(_))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Libc {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Some(env) => write!(f, "{env}"),
|
|
||||||
Self::None => write!(f, "none"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Os {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match &**self {
|
|
||||||
target_lexicon::OperatingSystem::Darwin(_) => write!(f, "macos"),
|
|
||||||
inner => write!(f, "{inner}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Arch {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self.family {
|
|
||||||
target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686) => {
|
|
||||||
write!(f, "x86")?;
|
|
||||||
}
|
|
||||||
inner => write!(f, "{inner}")?,
|
|
||||||
}
|
|
||||||
if let Some(variant) = self.variant {
|
|
||||||
write!(f, "_{variant}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Os {
|
|
||||||
type Err = Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let inner = match s {
|
|
||||||
"macos" => target_lexicon::OperatingSystem::Darwin(None),
|
|
||||||
_ => target_lexicon::OperatingSystem::from_str(s)
|
|
||||||
.map_err(|()| Error::UnknownOs(s.to_string()))?,
|
|
||||||
};
|
|
||||||
if matches!(inner, target_lexicon::OperatingSystem::Unknown) {
|
|
||||||
return Err(Error::UnknownOs(s.to_string()));
|
|
||||||
}
|
|
||||||
Ok(Self(inner))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Arch {
|
|
||||||
type Err = Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
fn parse_family(s: &str) -> Result<target_lexicon::Architecture, Error> {
|
|
||||||
let inner = match s {
|
|
||||||
// Allow users to specify "x86" as a shorthand for the "i686" variant, they should not need
|
|
||||||
// to specify the exact architecture and this variant is what we have downloads for.
|
|
||||||
"x86" => {
|
|
||||||
target_lexicon::Architecture::X86_32(target_lexicon::X86_32Architecture::I686)
|
|
||||||
}
|
|
||||||
_ => target_lexicon::Architecture::from_str(s)
|
|
||||||
.map_err(|()| Error::UnknownArch(s.to_string()))?,
|
|
||||||
};
|
|
||||||
if matches!(inner, target_lexicon::Architecture::Unknown) {
|
|
||||||
return Err(Error::UnknownArch(s.to_string()));
|
|
||||||
}
|
|
||||||
Ok(inner)
|
|
||||||
}
|
|
||||||
|
|
||||||
// First check for a variant
|
|
||||||
if let Some((Ok(family), Ok(variant))) = s
|
|
||||||
.rsplit_once('_')
|
|
||||||
.map(|(family, variant)| (parse_family(family), ArchVariant::from_str(variant)))
|
|
||||||
{
|
|
||||||
// We only support variants for `x86_64` right now
|
|
||||||
if !matches!(family, target_lexicon::Architecture::X86_64) {
|
|
||||||
return Err(Error::UnsupportedVariant(
|
|
||||||
variant.to_string(),
|
|
||||||
family.to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return Ok(Self {
|
|
||||||
family,
|
|
||||||
variant: Some(variant),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let family = parse_family(s)?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
family,
|
|
||||||
variant: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for ArchVariant {
|
|
||||||
type Err = ();
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s {
|
|
||||||
"v2" => Ok(Self::V2),
|
|
||||||
"v3" => Ok(Self::V3),
|
|
||||||
"v4" => Ok(Self::V4),
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ArchVariant {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::V2 => write!(f, "v2"),
|
|
||||||
Self::V3 => write!(f, "v3"),
|
|
||||||
Self::V4 => write!(f, "v4"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Os {
|
|
||||||
type Target = target_lexicon::OperatingSystem;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&uv_platform_tags::Arch> for Arch {
|
|
||||||
fn from(value: &uv_platform_tags::Arch) -> Self {
|
|
||||||
match value {
|
|
||||||
uv_platform_tags::Arch::Aarch64 => Self {
|
|
||||||
family: target_lexicon::Architecture::Aarch64(
|
|
||||||
target_lexicon::Aarch64Architecture::Aarch64,
|
|
||||||
),
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::Armv5TEL => Self {
|
|
||||||
family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv5te),
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::Armv6L => Self {
|
|
||||||
family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv6),
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::Armv7L => Self {
|
|
||||||
family: target_lexicon::Architecture::Arm(target_lexicon::ArmArchitecture::Armv7),
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::S390X => Self {
|
|
||||||
family: target_lexicon::Architecture::S390x,
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::Powerpc => Self {
|
|
||||||
family: target_lexicon::Architecture::Powerpc,
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::Powerpc64 => Self {
|
|
||||||
family: target_lexicon::Architecture::Powerpc64,
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::Powerpc64Le => Self {
|
|
||||||
family: target_lexicon::Architecture::Powerpc64le,
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::X86 => Self {
|
|
||||||
family: target_lexicon::Architecture::X86_32(
|
|
||||||
target_lexicon::X86_32Architecture::I686,
|
|
||||||
),
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::X86_64 => Self {
|
|
||||||
family: target_lexicon::Architecture::X86_64,
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::LoongArch64 => Self {
|
|
||||||
family: target_lexicon::Architecture::LoongArch64,
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::Riscv64 => Self {
|
|
||||||
family: target_lexicon::Architecture::Riscv64(
|
|
||||||
target_lexicon::Riscv64Architecture::Riscv64,
|
|
||||||
),
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
uv_platform_tags::Arch::Wasm32 => Self {
|
|
||||||
family: target_lexicon::Architecture::Wasm32,
|
|
||||||
variant: None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&uv_platform_tags::Os> for Libc {
|
|
||||||
fn from(value: &uv_platform_tags::Os) -> Self {
|
|
||||||
match value {
|
|
||||||
uv_platform_tags::Os::Manylinux { .. } => Self::Some(target_lexicon::Environment::Gnu),
|
|
||||||
uv_platform_tags::Os::Musllinux { .. } => Self::Some(target_lexicon::Environment::Musl),
|
|
||||||
_ => Self::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&uv_platform_tags::Os> for Os {
|
|
||||||
fn from(value: &uv_platform_tags::Os) -> Self {
|
|
||||||
match value {
|
|
||||||
uv_platform_tags::Os::Dragonfly { .. } => {
|
|
||||||
Self(target_lexicon::OperatingSystem::Dragonfly)
|
|
||||||
}
|
|
||||||
uv_platform_tags::Os::FreeBsd { .. } => Self(target_lexicon::OperatingSystem::Freebsd),
|
|
||||||
uv_platform_tags::Os::Haiku { .. } => Self(target_lexicon::OperatingSystem::Haiku),
|
|
||||||
uv_platform_tags::Os::Illumos { .. } => Self(target_lexicon::OperatingSystem::Illumos),
|
|
||||||
uv_platform_tags::Os::Macos { .. } => {
|
|
||||||
Self(target_lexicon::OperatingSystem::Darwin(None))
|
|
||||||
}
|
|
||||||
uv_platform_tags::Os::Manylinux { .. }
|
|
||||||
| uv_platform_tags::Os::Musllinux { .. }
|
|
||||||
| uv_platform_tags::Os::Android { .. } => Self(target_lexicon::OperatingSystem::Linux),
|
|
||||||
uv_platform_tags::Os::NetBsd { .. } => Self(target_lexicon::OperatingSystem::Netbsd),
|
|
||||||
uv_platform_tags::Os::OpenBsd { .. } => Self(target_lexicon::OperatingSystem::Openbsd),
|
|
||||||
uv_platform_tags::Os::Windows => Self(target_lexicon::OperatingSystem::Windows),
|
|
||||||
uv_platform_tags::Os::Pyodide { .. } => {
|
|
||||||
Self(target_lexicon::OperatingSystem::Emscripten)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
//! PEP 514 interactions with the Windows registry.
|
//! PEP 514 interactions with the Windows registry.
|
||||||
|
|
||||||
use crate::managed::ManagedPythonInstallation;
|
use crate::managed::ManagedPythonInstallation;
|
||||||
use crate::platform::Arch;
|
|
||||||
use crate::{COMPANY_DISPLAY_NAME, COMPANY_KEY, PythonInstallationKey, PythonVersion};
|
use crate::{COMPANY_DISPLAY_NAME, COMPANY_KEY, PythonInstallationKey, PythonVersion};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
@ -11,6 +10,7 @@ use std::str::FromStr;
|
||||||
use target_lexicon::PointerWidth;
|
use target_lexicon::PointerWidth;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
use uv_platform::Arch;
|
||||||
use uv_warnings::{warn_user, warn_user_once};
|
use uv_warnings::{warn_user, warn_user_once};
|
||||||
use windows_registry::{CURRENT_USER, HSTRING, Key, LOCAL_MACHINE, Value};
|
use windows_registry::{CURRENT_USER, HSTRING, Key, LOCAL_MACHINE, Value};
|
||||||
use windows_result::HRESULT;
|
use windows_result::HRESULT;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ uv-normalize = { workspace = true }
|
||||||
uv-pep440 = { workspace = true }
|
uv-pep440 = { workspace = true }
|
||||||
uv-pep508 = { workspace = true }
|
uv-pep508 = { workspace = true }
|
||||||
uv-performance-memory-allocator = { path = "../uv-performance-memory-allocator", optional = true }
|
uv-performance-memory-allocator = { path = "../uv-performance-memory-allocator", optional = true }
|
||||||
|
uv-platform = { workspace = true }
|
||||||
uv-platform-tags = { workspace = true }
|
uv-platform-tags = { workspace = true }
|
||||||
uv-publish = { workspace = true }
|
uv-publish = { workspace = true }
|
||||||
uv-pypi-types = { workspace = true }
|
uv-pypi-types = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use tracing::{debug, trace};
|
||||||
|
|
||||||
use uv_configuration::{Preview, PreviewFeatures};
|
use uv_configuration::{Preview, PreviewFeatures};
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
|
use uv_platform::{Arch, Libc};
|
||||||
use uv_python::downloads::{
|
use uv_python::downloads::{
|
||||||
self, ArchRequest, DownloadResult, ManagedPythonDownload, PythonDownloadRequest,
|
self, ArchRequest, DownloadResult, ManagedPythonDownload, PythonDownloadRequest,
|
||||||
};
|
};
|
||||||
|
|
@ -23,7 +24,6 @@ use uv_python::managed::{
|
||||||
ManagedPythonInstallation, ManagedPythonInstallations, PythonMinorVersionLink,
|
ManagedPythonInstallation, ManagedPythonInstallations, PythonMinorVersionLink,
|
||||||
create_link_to_executable, python_executable_dir,
|
create_link_to_executable, python_executable_dir,
|
||||||
};
|
};
|
||||||
use uv_python::platform::{Arch, Libc};
|
|
||||||
use uv_python::{
|
use uv_python::{
|
||||||
PythonDownloads, PythonInstallationKey, PythonInstallationMinorVersionKey, PythonRequest,
|
PythonDownloads, PythonInstallationKey, PythonInstallationMinorVersionKey, PythonRequest,
|
||||||
PythonVersionFile, VersionFileDiscoveryOptions, VersionFilePreference, VersionRequest,
|
PythonVersionFile, VersionFileDiscoveryOptions, VersionFilePreference, VersionRequest,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use assert_fs::prelude::{FileTouch, PathChild};
|
||||||
use assert_fs::{fixture::FileWriteStr, prelude::PathCreateDir};
|
use assert_fs::{fixture::FileWriteStr, prelude::PathCreateDir};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
|
||||||
use uv_python::platform::{Arch, Os};
|
use uv_platform::{Arch, Os};
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
use crate::common::{TestContext, uv_snapshot, venv_bin_path};
|
use crate::common::{TestContext, uv_snapshot, venv_bin_path};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use uv_python::platform::{Arch, Os};
|
use uv_platform::{Arch, Os};
|
||||||
use uv_static::EnvVars;
|
use uv_static::EnvVars;
|
||||||
|
|
||||||
use crate::common::{TestContext, uv_snapshot};
|
use crate::common::{TestContext, uv_snapshot};
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,8 @@ use anyhow::Result;
|
||||||
use assert_cmd::assert::OutputAssertExt;
|
use assert_cmd::assert::OutputAssertExt;
|
||||||
use assert_fs::fixture::{FileWriteStr, PathChild, PathCreateDir};
|
use assert_fs::fixture::{FileWriteStr, PathChild, PathCreateDir};
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use uv_python::{
|
use uv_platform::{Arch, Os};
|
||||||
PYTHON_VERSION_FILENAME, PYTHON_VERSIONS_FILENAME,
|
use uv_python::{PYTHON_VERSION_FILENAME, PYTHON_VERSIONS_FILENAME};
|
||||||
platform::{Arch, Os},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn python_pin() {
|
fn python_pin() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue