Skip to content

Commit

Permalink
Respect all platforms
Browse files Browse the repository at this point in the history
Uhh trying

Fork on...

Add heuristic

Gate to local versions

Remove KnownMarkers

Add required platform support
  • Loading branch information
charliermarsh committed Dec 21, 2024
1 parent ddc290f commit eeb249d
Show file tree
Hide file tree
Showing 26 changed files with 1,181 additions and 375 deletions.
54 changes: 54 additions & 0 deletions crates/uv-distribution-types/src/known_platform.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::fmt::{Display, Formatter};

use uv_pep508::{MarkerExpression, MarkerOperator, MarkerTree, MarkerValueString};

/// A platform for which the resolver is solving.
#[derive(Debug, Clone, Copy)]
pub enum KnownPlatform {
Linux,
Windows,
MacOS,
}

impl KnownPlatform {
/// Return the platform's `sys.platform` value.
pub fn sys_platform(self) -> &'static str {
match self {
KnownPlatform::Linux => "linux",
KnownPlatform::Windows => "win32",
KnownPlatform::MacOS => "darwin",
}
}

/// Return a [`MarkerTree`] for the platform.
pub fn marker(self) -> MarkerTree {
MarkerTree::expression(MarkerExpression::String {
key: MarkerValueString::SysPlatform,
operator: MarkerOperator::Equal,
value: self.sys_platform().to_string(),
})
}

/// Determine the [`KnownPlatform`] from a marker tree.
pub fn from_marker(marker: MarkerTree) -> Option<KnownPlatform> {
if marker == KnownPlatform::Linux.marker() {
Some(KnownPlatform::Linux)
} else if marker == KnownPlatform::Windows.marker() {
Some(KnownPlatform::Windows)
} else if marker == KnownPlatform::MacOS.marker() {
Some(KnownPlatform::MacOS)
} else {
None
}
}
}

impl Display for KnownPlatform {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
KnownPlatform::Linux => write!(f, "Linux"),
KnownPlatform::Windows => write!(f, "Windows"),
KnownPlatform::MacOS => write!(f, "macOS"),
}
}
}
2 changes: 2 additions & 0 deletions crates/uv-distribution-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub use crate::index::*;
pub use crate::index_name::*;
pub use crate::index_url::*;
pub use crate::installed::*;
pub use crate::known_platform::*;
pub use crate::origin::*;
pub use crate::pip_index::*;
pub use crate::prioritized_distribution::*;
Expand All @@ -87,6 +88,7 @@ mod index;
mod index_name;
mod index_url;
mod installed;
mod known_platform;
mod origin;
mod pip_index;
mod prioritized_distribution;
Expand Down
43 changes: 35 additions & 8 deletions crates/uv-distribution-types/src/prioritized_distribution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use uv_platform_tags::{IncompatibleTag, TagPriority};
use uv_pypi_types::{HashDigest, Yanked};

use crate::{
InstalledDist, RegistryBuiltDist, RegistryBuiltWheel, RegistrySourceDist, ResolvedDistRef,
InstalledDist, KnownPlatform, RegistryBuiltDist, RegistryBuiltWheel, RegistrySourceDist,
ResolvedDistRef,
};

/// A collection of distributions that have been filtered by relevance.
Expand Down Expand Up @@ -119,6 +120,7 @@ impl IncompatibleDist {
None => format!("has {self}"),
},
IncompatibleWheel::RequiresPython(..) => format!("requires {self}"),
IncompatibleWheel::MissingPlatform(_) => format!("has {self}"),
},
Self::Source(incompatibility) => match incompatibility {
IncompatibleSource::NoBuild => format!("has {self}"),
Expand Down Expand Up @@ -146,6 +148,7 @@ impl IncompatibleDist {
None => format!("have {self}"),
},
IncompatibleWheel::RequiresPython(..) => format!("require {self}"),
IncompatibleWheel::MissingPlatform(_) => format!("have {self}"),
},
Self::Source(incompatibility) => match incompatibility {
IncompatibleSource::NoBuild => format!("have {self}"),
Expand Down Expand Up @@ -196,6 +199,15 @@ impl Display for IncompatibleDist {
IncompatibleWheel::RequiresPython(python, _) => {
write!(f, "Python {python}")
}
IncompatibleWheel::MissingPlatform(marker) => {
if let Some(platform) = KnownPlatform::from_marker(*marker) {
write!(f, "no {platform}-compatible wheels")
} else if let Some(marker) = marker.try_to_string() {
write!(f, "no `{marker}`-compatible wheels")
} else {
write!(f, "no compatible wheels")
}
}
},
Self::Source(incompatibility) => match incompatibility {
IncompatibleSource::NoBuild => f.write_str("no usable wheels"),
Expand Down Expand Up @@ -248,6 +260,8 @@ pub enum IncompatibleWheel {
Yanked(Yanked),
/// The use of binary wheels is disabled.
NoBinary,
/// Wheels are not available for the current platform.
MissingPlatform(MarkerTree),
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -607,28 +621,41 @@ impl IncompatibleWheel {
timestamp_other < timestamp_self
}
},
Self::NoBinary | Self::RequiresPython(_, _) | Self::Tag(_) | Self::Yanked(_) => {
true
}
Self::MissingPlatform(_)
| Self::NoBinary
| Self::RequiresPython(_, _)
| Self::Tag(_)
| Self::Yanked(_) => true,
},
Self::Tag(tag_self) => match other {
Self::ExcludeNewer(_) => false,
Self::Tag(tag_other) => tag_self > tag_other,
Self::NoBinary | Self::RequiresPython(_, _) | Self::Yanked(_) => true,
Self::MissingPlatform(_)
| Self::NoBinary
| Self::RequiresPython(_, _)
| Self::Yanked(_) => true,
},
Self::RequiresPython(_, _) => match other {
Self::ExcludeNewer(_) | Self::Tag(_) => false,
// Version specifiers cannot be reasonably compared
Self::RequiresPython(_, _) => false,
Self::NoBinary | Self::Yanked(_) => true,
Self::MissingPlatform(_) | Self::NoBinary | Self::Yanked(_) => true,
},
Self::Yanked(_) => match other {
Self::ExcludeNewer(_) | Self::Tag(_) | Self::RequiresPython(_, _) => false,
// Yanks with a reason are more helpful for errors
Self::Yanked(yanked_other) => matches!(yanked_other, Yanked::Reason(_)),
Self::NoBinary => true,
Self::MissingPlatform(_) | Self::NoBinary => true,
},
Self::NoBinary => match other {
Self::ExcludeNewer(_)
| Self::Tag(_)
| Self::RequiresPython(_, _)
| Self::Yanked(_) => false,
Self::NoBinary => false,
Self::MissingPlatform(_) => true,
},
Self::NoBinary => false,
Self::MissingPlatform(_) => false,
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions crates/uv-pypi-types/src/supported_environments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ use uv_pep508::MarkerTree;
pub struct SupportedEnvironments(Vec<MarkerTree>);

impl SupportedEnvironments {
/// Create a new [`SupportedEnvironments`] struct from a list of marker trees.
pub fn from_markers(markers: Vec<MarkerTree>) -> Self {
SupportedEnvironments(markers)
}

/// Return the list of marker trees.
pub fn as_markers(&self) -> &[MarkerTree] {
&self.0
Expand All @@ -18,6 +23,19 @@ impl SupportedEnvironments {
pub fn into_markers(self) -> Vec<MarkerTree> {
self.0
}

/// Returns an iterator over the marker trees.
pub fn iter(&self) -> std::slice::Iter<MarkerTree> {
self.0.iter()
}
}

impl<'a> IntoIterator for &'a SupportedEnvironments {
type IntoIter = std::slice::Iter<'a, MarkerTree>;
type Item = &'a MarkerTree;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

/// Serialize a [`SupportedEnvironments`] struct into a list of marker strings.
Expand Down
51 changes: 50 additions & 1 deletion crates/uv-resolver/src/lock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ pub struct Lock {
conflicts: Conflicts,
/// The list of supported environments specified by the user.
supported_environments: Vec<MarkerTree>,
/// The list of required platforms specified by the user.
required_platforms: Vec<MarkerTree>,
/// The range of supported Python versions.
requires_python: RequiresPython,
/// We discard the lockfile if these options don't match.
Expand Down Expand Up @@ -242,6 +244,7 @@ impl Lock {
ResolverManifest::default(),
Conflicts::empty(),
vec![],
vec![],
resolution.fork_markers.clone(),
)?;
Ok(lock)
Expand Down Expand Up @@ -314,6 +317,7 @@ impl Lock {
manifest: ResolverManifest,
conflicts: Conflicts,
supported_environments: Vec<MarkerTree>,
required_platforms: Vec<MarkerTree>,
fork_markers: Vec<UniversalMarker>,
) -> Result<Self, LockError> {
// Put all dependencies for each package in a canonical order and
Expand Down Expand Up @@ -464,6 +468,7 @@ impl Lock {
fork_markers,
conflicts,
supported_environments,
required_platforms,
requires_python,
options,
packages,
Expand Down Expand Up @@ -506,6 +511,16 @@ impl Lock {
self
}

/// Record the required platforms that were used to generate this lock.
#[must_use]
pub fn with_required_platforms(mut self, required_platforms: Vec<MarkerTree>) -> Self {
self.required_platforms = required_platforms
.into_iter()
.map(|marker| self.requires_python.complexify_markers(marker))
.collect();
self
}

/// Returns the lockfile version.
pub fn version(&self) -> u32 {
self.version
Expand Down Expand Up @@ -561,6 +576,11 @@ impl Lock {
&self.supported_environments
}

/// Returns the required platforms that were used to generate this lock.
pub fn required_platforms(&self) -> &[MarkerTree] {
&self.required_platforms
}

/// Returns the workspace members that were used to generate this lock.
pub fn members(&self) -> &BTreeSet<PackageName> {
&self.manifest.members
Expand Down Expand Up @@ -593,6 +613,16 @@ impl Lock {
.collect()
}

/// Returns the required platforms that were used to generate this
/// lock.
pub fn simplified_required_platforms(&self) -> Vec<MarkerTree> {
self.required_platforms()
.iter()
.copied()
.map(|marker| self.simplify_environment(marker))
.collect()
}

/// Simplify the given marker environment with respect to the lockfile's
/// `requires-python` setting.
pub fn simplify_environment(&self, marker: MarkerTree) -> MarkerTree {
Expand Down Expand Up @@ -630,11 +660,22 @@ impl Lock {
.iter()
.copied()
.map(|marker| SimplifiedMarkerTree::new(&self.requires_python, marker))
.filter_map(super::requires_python::SimplifiedMarkerTree::try_to_string),
.filter_map(SimplifiedMarkerTree::try_to_string),
);
doc.insert("supported-markers", value(supported_environments));
}

if !self.required_platforms.is_empty() {
let required_platforms = each_element_on_its_line_array(
self.required_platforms
.iter()
.copied()
.map(|marker| SimplifiedMarkerTree::new(&self.requires_python, marker))
.filter_map(SimplifiedMarkerTree::try_to_string),
);
doc.insert("required-markers", value(required_platforms));
}

if !self.conflicts.is_empty() {
let mut list = Array::new();
for set in self.conflicts.iter() {
Expand Down Expand Up @@ -1402,6 +1443,8 @@ struct LockWire {
fork_markers: Vec<SimplifiedMarkerTree>,
#[serde(rename = "supported-markers", default)]
supported_environments: Vec<SimplifiedMarkerTree>,
#[serde(rename = "required-markers", default)]
required_platforms: Vec<SimplifiedMarkerTree>,
#[serde(rename = "conflicts", default)]
conflicts: Option<Conflicts>,
/// We discard the lockfile if these options match.
Expand Down Expand Up @@ -1444,6 +1487,11 @@ impl TryFrom<LockWire> for Lock {
.into_iter()
.map(|simplified_marker| simplified_marker.into_marker(&wire.requires_python))
.collect();
let required_platforms = wire
.required_platforms
.into_iter()
.map(|simplified_marker| simplified_marker.into_marker(&wire.requires_python))
.collect();
let fork_markers = wire
.fork_markers
.into_iter()
Expand All @@ -1462,6 +1510,7 @@ impl TryFrom<LockWire> for Lock {
wire.manifest,
wire.conflicts.unwrap_or_else(Conflicts::empty),
supported_environments,
required_platforms,
fork_markers,
)?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ok(
[],
),
supported_environments: [],
required_platforms: [],
requires_python: RequiresPython {
specifiers: VersionSpecifiers(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ok(
[],
),
supported_environments: [],
required_platforms: [],
requires_python: RequiresPython {
specifiers: VersionSpecifiers(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ok(
[],
),
supported_environments: [],
required_platforms: [],
requires_python: RequiresPython {
specifiers: VersionSpecifiers(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ok(
[],
),
supported_environments: [],
required_platforms: [],
requires_python: RequiresPython {
specifiers: VersionSpecifiers(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ok(
[],
),
supported_environments: [],
required_platforms: [],
requires_python: RequiresPython {
specifiers: VersionSpecifiers(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ok(
[],
),
supported_environments: [],
required_platforms: [],
requires_python: RequiresPython {
specifiers: VersionSpecifiers(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ok(
[],
),
supported_environments: [],
required_platforms: [],
requires_python: RequiresPython {
specifiers: VersionSpecifiers(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Ok(
[],
),
supported_environments: [],
required_platforms: [],
requires_python: RequiresPython {
specifiers: VersionSpecifiers(
[
Expand Down
Loading

0 comments on commit eeb249d

Please sign in to comment.