-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Source::{fuzzy_}query{_vec} can say "try again" #8985
Closed
Closed
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
466e719
Source::{fuzzy_}query{_vec} can say "try again"
Eh2406 c354199
RegistryData::load can say "try again"
Eh2406 ea37a74
pull the loop up out of summary_for_patch
Eh2406 fdf08eb
reuse the resolvers cache
Eh2406 240cb5f
don't retry just for an error message
Eh2406 08f96cd
move Poll one layer up in is_yanked
Eh2406 33190fa
just assert when downloading
Eh2406 1d9e675
add a PollExt so we can use expect instead of match
Eh2406 44012fd
I missed one unneeded loop.
Eh2406 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
use std::collections::{HashMap, HashSet}; | ||
use std::task::Poll; | ||
|
||
use crate::core::PackageSet; | ||
use crate::core::{Dependency, PackageId, Source, SourceId, SourceMap, Summary}; | ||
use crate::sources::config::SourceConfigMap; | ||
use crate::util::errors::{CargoResult, CargoResultExt}; | ||
use crate::util::interning::InternedString; | ||
use crate::util::network::PollExt; | ||
use crate::util::{profile, CanonicalUrl, Config}; | ||
use anyhow::bail; | ||
use log::{debug, trace}; | ||
|
@@ -21,12 +23,11 @@ pub trait Registry { | |
dep: &Dependency, | ||
f: &mut dyn FnMut(Summary), | ||
fuzzy: bool, | ||
) -> CargoResult<()>; | ||
) -> CargoResult<Poll<()>>; | ||
|
||
fn query_vec(&mut self, dep: &Dependency, fuzzy: bool) -> CargoResult<Vec<Summary>> { | ||
fn query_vec(&mut self, dep: &Dependency, fuzzy: bool) -> CargoResult<Poll<Vec<Summary>>> { | ||
let mut ret = Vec::new(); | ||
self.query(dep, &mut |s| ret.push(s), fuzzy)?; | ||
Ok(ret) | ||
Ok(self.query(dep, &mut |s| ret.push(s), fuzzy)?.map(|_| ret)) | ||
} | ||
|
||
fn describe_source(&self, source: SourceId) -> String; | ||
|
@@ -260,67 +261,86 @@ impl<'cfg> PackageRegistry<'cfg> { | |
// Remember that each dependency listed in `[patch]` has to resolve to | ||
// precisely one package, so that's why we're just creating a flat list | ||
// of summaries which should be the same length as `deps` above. | ||
let unlocked_summaries = deps | ||
.iter() | ||
.map(|(orig_patch, locked)| { | ||
// Remove double reference in orig_patch. Is there maybe a | ||
// magic pattern that could avoid this? | ||
let orig_patch = *orig_patch; | ||
// Use the locked patch if it exists, otherwise use the original. | ||
let dep = match locked { | ||
Some((locked_patch, _locked_id)) => locked_patch, | ||
None => orig_patch, | ||
}; | ||
debug!( | ||
"registering a patch for `{}` with `{}`", | ||
url, | ||
dep.package_name() | ||
); | ||
|
||
// Go straight to the source for resolving `dep`. Load it as we | ||
// normally would and then ask it directly for the list of summaries | ||
// corresponding to this `dep`. | ||
self.ensure_loaded(dep.source_id(), Kind::Normal) | ||
.chain_err(|| { | ||
anyhow::format_err!( | ||
"failed to load source for dependency `{}`", | ||
dep.package_name() | ||
) | ||
})?; | ||
|
||
let source = self | ||
.sources | ||
.get_mut(dep.source_id()) | ||
.expect("loaded source not present"); | ||
let summaries = source.query_vec(dep)?; | ||
let (summary, should_unlock) = | ||
summary_for_patch(orig_patch, locked, summaries, source).chain_err(|| { | ||
format!( | ||
"patch for `{}` in `{}` failed to resolve", | ||
orig_patch.package_name(), | ||
let unlocked_summaries = loop { | ||
let try_summaries = | ||
deps.iter() | ||
.map(|(orig_patch, locked)| { | ||
// Remove double reference in orig_patch. Is there maybe a | ||
// magic pattern that could avoid this? | ||
let orig_patch = *orig_patch; | ||
// Use the locked patch if it exists, otherwise use the original. | ||
let dep = match locked { | ||
Some((locked_patch, _locked_id)) => locked_patch, | ||
None => orig_patch, | ||
}; | ||
debug!( | ||
"registering a patch for `{}` with `{}`", | ||
url, | ||
) | ||
})?; | ||
debug!( | ||
"patch summary is {:?} should_unlock={:?}", | ||
summary, should_unlock | ||
); | ||
if let Some(unlock_id) = should_unlock { | ||
unlock_patches.push((orig_patch.clone(), unlock_id)); | ||
} | ||
dep.package_name() | ||
); | ||
|
||
// Go straight to the source for resolving `dep`. Load it as we | ||
// normally would and then ask it directly for the list of summaries | ||
// corresponding to this `dep`. | ||
self.ensure_loaded(dep.source_id(), Kind::Normal) | ||
.chain_err(|| { | ||
anyhow::format_err!( | ||
"failed to load source for dependency `{}`", | ||
dep.package_name() | ||
) | ||
})?; | ||
|
||
let source = self | ||
.sources | ||
.get_mut(dep.source_id()) | ||
.expect("loaded source not present"); | ||
let summaries = match source.query_vec(dep)? { | ||
Poll::Ready(deps) => deps, | ||
Poll::Pending => return Ok(Poll::Pending), | ||
}; | ||
let (summary, should_unlock) = | ||
match summary_for_patch(orig_patch, locked, summaries, source) | ||
.chain_err(|| { | ||
format!( | ||
"patch for `{}` in `{}` failed to resolve", | ||
orig_patch.package_name(), | ||
url, | ||
) | ||
})? { | ||
Poll::Ready(x) => x, | ||
Poll::Pending => { | ||
return Ok(Poll::Pending); | ||
} | ||
}; | ||
debug!( | ||
"patch summary is {:?} should_unlock={:?}", | ||
summary, should_unlock | ||
); | ||
if let Some(unlock_id) = should_unlock { | ||
unlock_patches.push((orig_patch.clone(), unlock_id)); | ||
} | ||
|
||
if *summary.package_id().source_id().canonical_url() == canonical { | ||
anyhow::bail!( | ||
"patch for `{}` in `{}` points to the same source, but \ | ||
if *summary.package_id().source_id().canonical_url() == canonical { | ||
anyhow::bail!( | ||
"patch for `{}` in `{}` points to the same source, but \ | ||
patches must point to different sources", | ||
dep.package_name(), | ||
url | ||
); | ||
} | ||
Ok(summary) | ||
}) | ||
.collect::<CargoResult<Vec<_>>>() | ||
.chain_err(|| anyhow::format_err!("failed to resolve patches for `{}`", url))?; | ||
dep.package_name(), | ||
url | ||
); | ||
} | ||
Ok(Poll::Ready(summary)) | ||
}) | ||
.collect::<CargoResult<Vec<_>>>() | ||
.chain_err(|| anyhow::format_err!("failed to resolve patches for `{}`", url))?; | ||
if try_summaries.iter().all(|p| p.is_ready()) { | ||
break try_summaries | ||
.into_iter() | ||
.map(|p| p.expect("we just checked for this!")) | ||
.collect::<Vec<_>>(); | ||
} else { | ||
// TODO: dont hot loop for it to be Ready | ||
} | ||
}; | ||
|
||
let mut name_and_version = HashSet::new(); | ||
for summary in unlocked_summaries.iter() { | ||
|
@@ -396,9 +416,13 @@ impl<'cfg> PackageRegistry<'cfg> { | |
for &s in self.overrides.iter() { | ||
let src = self.sources.get_mut(s).unwrap(); | ||
let dep = Dependency::new_override(dep.package_name(), s); | ||
let mut results = src.query_vec(&dep)?; | ||
if !results.is_empty() { | ||
return Ok(Some(results.remove(0))); | ||
match src.query_vec(&dep)? { | ||
Poll::Ready(mut results) => { | ||
if !results.is_empty() { | ||
return Ok(Some(results.remove(0))); | ||
} | ||
} | ||
Poll::Pending => bail!("overrides have to be on path deps, how did we get here?"), | ||
} | ||
} | ||
Ok(None) | ||
|
@@ -487,7 +511,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> { | |
dep: &Dependency, | ||
f: &mut dyn FnMut(Summary), | ||
fuzzy: bool, | ||
) -> CargoResult<()> { | ||
) -> CargoResult<Poll<()>> { | ||
assert!(self.patches_locked); | ||
let (override_summary, n, to_warn) = { | ||
// Look for an override and get ready to query the real source. | ||
|
@@ -521,7 +545,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> { | |
Some(summary) => (summary, 1, Some(patch)), | ||
None => { | ||
f(patch); | ||
return Ok(()); | ||
return Ok(Poll::Ready(())); | ||
} | ||
} | ||
} else { | ||
|
@@ -549,7 +573,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> { | |
let source = self.sources.get_mut(dep.source_id()); | ||
match (override_summary, source) { | ||
(Some(_), None) => anyhow::bail!("override found but no real ones"), | ||
(None, None) => return Ok(()), | ||
(None, None) => return Ok(Poll::Ready(())), | ||
|
||
// If we don't have an override then we just ship | ||
// everything upstairs after locking the summary | ||
|
@@ -597,10 +621,13 @@ impl<'cfg> Registry for PackageRegistry<'cfg> { | |
n += 1; | ||
to_warn = Some(summary); | ||
}; | ||
if fuzzy { | ||
source.fuzzy_query(dep, callback)?; | ||
let pend = if fuzzy { | ||
source.fuzzy_query(dep, callback)? | ||
} else { | ||
source.query(dep, callback)?; | ||
source.query(dep, callback)? | ||
}; | ||
if pend.is_pending() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where I'd imagine that a record of this dependency's source id is recorded in an internal map for us to later iterate over and ask the source to block on things. |
||
return Ok(Poll::Pending); | ||
} | ||
} | ||
(override_summary, n, to_warn) | ||
|
@@ -615,7 +642,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> { | |
self.warn_bad_override(&override_summary, &summary)?; | ||
} | ||
f(self.lock(override_summary)); | ||
Ok(()) | ||
Ok(Poll::Ready(())) | ||
} | ||
|
||
fn describe_source(&self, id: SourceId) -> String { | ||
|
@@ -748,9 +775,9 @@ fn summary_for_patch( | |
locked: &Option<(Dependency, PackageId)>, | ||
mut summaries: Vec<Summary>, | ||
source: &mut dyn Source, | ||
) -> CargoResult<(Summary, Option<PackageId>)> { | ||
) -> CargoResult<Poll<(Summary, Option<PackageId>)>> { | ||
if summaries.len() == 1 { | ||
return Ok((summaries.pop().unwrap(), None)); | ||
return Ok(Poll::Ready((summaries.pop().unwrap(), None))); | ||
} | ||
if summaries.len() > 1 { | ||
// TODO: In the future, it might be nice to add all of these | ||
|
@@ -779,22 +806,43 @@ fn summary_for_patch( | |
// No summaries found, try to help the user figure out what is wrong. | ||
if let Some((_locked_patch, locked_id)) = locked { | ||
// Since the locked patch did not match anything, try the unlocked one. | ||
let orig_matches = source.query_vec(orig_patch).unwrap_or_else(|e| { | ||
let orig_matches = match source.query_vec(orig_patch) { | ||
Ok(Poll::Ready(deps)) => Ok(deps), | ||
Ok(Poll::Pending) => { | ||
return Ok(Poll::Pending); | ||
} | ||
Err(x) => Err(x), | ||
} | ||
.unwrap_or_else(|e| { | ||
log::warn!( | ||
"could not determine unlocked summaries for dep {:?}: {:?}", | ||
orig_patch, | ||
e | ||
); | ||
Vec::new() | ||
}); | ||
let (summary, _) = summary_for_patch(orig_patch, &None, orig_matches, source)?; | ||
// The unlocked version found a match. This returns a value to | ||
// indicate that this entry should be unlocked. | ||
return Ok((summary, Some(*locked_id))); | ||
return Ok( | ||
match summary_for_patch(orig_patch, &None, orig_matches, source)? { | ||
Poll::Ready((summary, _)) => { | ||
// The unlocked version found a match. This returns a value to | ||
// indicate that this entry should be unlocked. | ||
Poll::Ready((summary, Some(*locked_id))) | ||
} | ||
Poll::Pending => Poll::Pending, | ||
}, | ||
); | ||
} | ||
// Try checking if there are *any* packages that match this by name. | ||
let name_only_dep = Dependency::new_override(orig_patch.package_name(), orig_patch.source_id()); | ||
let name_summaries = source.query_vec(&name_only_dep).unwrap_or_else(|e| { | ||
|
||
let name_summaries = match source.query_vec(&name_only_dep) { | ||
Ok(Poll::Ready(deps)) => Ok(deps), | ||
Ok(Poll::Pending) => { | ||
return Ok(Poll::Pending); | ||
} | ||
Err(x) => Err(x), | ||
} | ||
.unwrap_or_else(|e| { | ||
log::warn!( | ||
"failed to do name-only summary query for {:?}: {:?}", | ||
name_only_dep, | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW I imagine this bottoms out in something like
self.wait_for_sources_to_be_ready()
. Each source would already be registered in some internal map ofPackageRegistry
if we're blocked on it, and then we'd ask each source, in sequence, "do your blocking thing now".