-
Notifications
You must be signed in to change notification settings - Fork 2.9k
universal-lock: propagate full set of artifacts for each distribution #3351
Description
At present, our resolver does not deal with cross platform concerns. Instead, it assumes there is a single fixed set of marker values for the current environment, and makes decisions based on those. Even more, the structure of the code itself is oriented around this assumption.
In this ticket, we'll need to relax one manifestation of that assumption: the collapsing of distribution "simple" metadata down into a single wheel/sdist. Right now, this happens at a fairly low level in our VersionMap:
uv/crates/uv-resolver/src/version_map.rs
Lines 344 to 431 in e33ff95
| /// Given an optional starting point, return the final form of the | |
| /// given simple distribution. If it wasn't initialized yet, then this | |
| /// initializes it. If the distribution would otherwise be empty, this | |
| /// returns `None`. | |
| fn get_simple<'p>( | |
| &'p self, | |
| init: Option<&'p PrioritizedDist>, | |
| simple: &'p SimplePrioritizedDist, | |
| ) -> Option<&'p PrioritizedDist> { | |
| let get_or_init = || { | |
| let files: VersionFiles = self | |
| .simple_metadata | |
| .datum(simple.datum_index) | |
| .expect("index to lazy dist is correct") | |
| .files | |
| .deserialize(&mut SharedDeserializeMap::new()) | |
| .expect("archived version files should deserialize"); | |
| let mut priority_dist = init.cloned().unwrap_or_default(); | |
| for (filename, file) in files.all() { | |
| // Support resolving as if it were an earlier timestamp, at least as long files have | |
| // upload time information. | |
| let (excluded, upload_time) = if let Some(exclude_newer) = self.exclude_newer { | |
| match file.upload_time_utc_ms.as_ref() { | |
| Some(&upload_time) if upload_time >= exclude_newer.timestamp_millis() => { | |
| (true, Some(upload_time)) | |
| } | |
| None => { | |
| warn_user_once!( | |
| "{} is missing an upload date, but user provided: {exclude_newer}", | |
| file.filename, | |
| ); | |
| (true, None) | |
| } | |
| _ => (false, None), | |
| } | |
| } else { | |
| (false, None) | |
| }; | |
| // Prioritize amongst all available files. | |
| let version = filename.version().clone(); | |
| let requires_python = file.requires_python.clone(); | |
| let yanked = file.yanked.clone(); | |
| let hashes = file.hashes.clone(); | |
| match filename { | |
| DistFilename::WheelFilename(filename) => { | |
| let compatibility = self.wheel_compatibility( | |
| &filename, | |
| &version, | |
| requires_python, | |
| &hashes, | |
| yanked, | |
| excluded, | |
| upload_time, | |
| ); | |
| let dist = Dist::from_registry( | |
| DistFilename::WheelFilename(filename), | |
| file, | |
| self.index.clone(), | |
| ); | |
| priority_dist.insert_built(dist, hashes, compatibility); | |
| } | |
| DistFilename::SourceDistFilename(filename) => { | |
| let compatibility = self.source_dist_compatibility( | |
| &version, | |
| requires_python, | |
| &hashes, | |
| yanked, | |
| excluded, | |
| upload_time, | |
| ); | |
| let dist = Dist::from_registry( | |
| DistFilename::SourceDistFilename(filename), | |
| file, | |
| self.index.clone(), | |
| ); | |
| priority_dist.insert_source(dist, hashes, compatibility); | |
| } | |
| } | |
| } | |
| if priority_dist.is_empty() { | |
| None | |
| } else { | |
| Some(priority_dist) | |
| } | |
| }; | |
| simple.dist.get_or_init(get_or_init).as_ref() | |
| } |
That is, when retrieving metadata for a distribution, we almost immediately collapse the metadata down into the single "best" wheel based on the current marker environment. But in a cross platform context, all of the wheels need to make it into the lock file somehow, and thus we can't discard this metadata.
I think the simplest change we can make here is to continue selecting the "best" wheel at this point, but instead of throwing away everything else, a PrioritizedDist hangs on to it. And this information probably needs to be threaded through to other downstream types, such as ResolvedDist (and of its "built" member types).