@@ -222,16 +222,35 @@ impl<'cfg> PackageRegistry<'cfg> {
222222 /// the manifest.
223223 ///
224224 /// Here the `deps` will be resolved to a precise version and stored
225- /// internally for future calls to `query` below. It's expected that `deps`
226- /// have had `lock_to` call already, if applicable. (e.g., if a lock file was
227- /// already present).
225+ /// internally for future calls to `query` below. `deps` should be a tuple
226+ /// where the first element is the patch definition straight from the
227+ /// manifest, and the second element is an optional variant where the
228+ /// patch has been locked. This locked patch is the patch locked to
229+ /// a specific version found in Cargo.lock. This will be `None` if
230+ /// `Cargo.lock` doesn't exist, or the patch did not match any existing
231+ /// entries in `Cargo.lock`.
228232 ///
229233 /// Note that the patch list specified here *will not* be available to
230234 /// `query` until `lock_patches` is called below, which should be called
231235 /// once all patches have been added.
232- pub fn patch ( & mut self , url : & Url , deps : & [ Dependency ] ) -> CargoResult < ( ) > {
236+ ///
237+ /// The return value is a `Vec` of patches that should *not* be locked.
238+ /// This happens when the patch is locked, but the patch has been updated
239+ /// so the locked value is no longer correct.
240+ pub fn patch (
241+ & mut self ,
242+ url : & Url ,
243+ deps : & [ ( & Dependency , Option < ( Dependency , PackageId ) > ) ] ,
244+ ) -> CargoResult < Vec < ( Dependency , PackageId ) > > {
245+ // NOTE: None of this code is aware of required features. If a patch
246+ // is missing a required feature, you end up with an "unused patch"
247+ // warning, which is very hard to understand. Ideally the warning
248+ // would be tailored to indicate *why* it is unused.
233249 let canonical = CanonicalUrl :: new ( url) ?;
234250
251+ // Return value of patches that shouldn't be locked.
252+ let mut unlock_patches = Vec :: new ( ) ;
253+
235254 // First up we need to actually resolve each `deps` specification to
236255 // precisely one summary. We're not using the `query` method below as it
237256 // internally uses maps we're building up as part of this method
@@ -243,7 +262,15 @@ impl<'cfg> PackageRegistry<'cfg> {
243262 // of summaries which should be the same length as `deps` above.
244263 let unlocked_summaries = deps
245264 . iter ( )
246- . map ( |dep| {
265+ . map ( |( orig_patch, locked) | {
266+ // Remove double reference in orig_patch. Is there maybe a
267+ // magic pattern that could avoid this?
268+ let orig_patch = * orig_patch;
269+ // Use the locked patch if it exists, otherwise use the original.
270+ let dep = match locked {
271+ Some ( ( locked_patch, _locked_id) ) => locked_patch,
272+ None => orig_patch,
273+ } ;
247274 debug ! (
248275 "registering a patch for `{}` with `{}`" ,
249276 url,
@@ -261,30 +288,27 @@ impl<'cfg> PackageRegistry<'cfg> {
261288 )
262289 } ) ?;
263290
264- let mut summaries = self
291+ let source = self
265292 . sources
266293 . get_mut ( dep. source_id ( ) )
267- . expect ( "loaded source not present" )
268- . query_vec ( dep) ?
269- . into_iter ( ) ;
270-
271- let summary = match summaries. next ( ) {
272- Some ( summary) => summary,
273- None => anyhow:: bail!(
274- "patch for `{}` in `{}` did not resolve to any crates. If this is \
275- unexpected, you may wish to consult: \
276- https://github.com/rust-lang/cargo/issues/4678",
277- dep. package_name( ) ,
278- url
279- ) ,
280- } ;
281- if summaries. next ( ) . is_some ( ) {
282- anyhow:: bail!(
283- "patch for `{}` in `{}` resolved to more than one candidate" ,
284- dep. package_name( ) ,
285- url
286- )
294+ . expect ( "loaded source not present" ) ;
295+ let summaries = source. query_vec ( dep) ?;
296+ let ( summary, should_unlock) =
297+ summary_for_patch ( orig_patch, & locked, summaries, source) . chain_err ( || {
298+ format ! (
299+ "patch for `{}` in `{}` failed to resolve" ,
300+ orig_patch. package_name( ) ,
301+ url,
302+ )
303+ } ) ?;
304+ debug ! (
305+ "patch summary is {:?} should_unlock={:?}" ,
306+ summary, should_unlock
307+ ) ;
308+ if let Some ( unlock_id) = should_unlock {
309+ unlock_patches. push ( ( orig_patch. clone ( ) , unlock_id) ) ;
287310 }
311+
288312 if * summary. package_id ( ) . source_id ( ) . canonical_url ( ) == canonical {
289313 anyhow:: bail!(
290314 "patch for `{}` in `{}` points to the same source, but \
@@ -321,7 +345,7 @@ impl<'cfg> PackageRegistry<'cfg> {
321345 self . patches_available . insert ( canonical. clone ( ) , ids) ;
322346 self . patches . insert ( canonical, unlocked_summaries) ;
323347
324- Ok ( ( ) )
348+ Ok ( unlock_patches )
325349 }
326350
327351 /// Lock all patch summaries added via `patch`, making them available to
@@ -335,6 +359,7 @@ impl<'cfg> PackageRegistry<'cfg> {
335359 assert ! ( !self . patches_locked) ;
336360 for summaries in self . patches . values_mut ( ) {
337361 for summary in summaries {
362+ debug ! ( "locking patch {:?}" , summary) ;
338363 * summary = lock ( & self . locked , & self . patches_available , summary. clone ( ) ) ;
339364 }
340365 }
@@ -718,3 +743,97 @@ fn lock(
718743 dep
719744 } )
720745}
746+
747+ /// This is a helper for selecting the summary, or generating a helpful error message.
748+ fn summary_for_patch (
749+ orig_patch : & Dependency ,
750+ locked : & Option < ( Dependency , PackageId ) > ,
751+ mut summaries : Vec < Summary > ,
752+ source : & mut dyn Source ,
753+ ) -> CargoResult < ( Summary , Option < PackageId > ) > {
754+ if summaries. len ( ) == 1 {
755+ return Ok ( ( summaries. pop ( ) . unwrap ( ) , None ) ) ;
756+ }
757+ if summaries. len ( ) > 1 {
758+ // TODO: In the future, it might be nice to add all of these
759+ // candidates so that version selection would just pick the
760+ // appropriate one. However, as this is currently structured, if we
761+ // added these all as patches, the unselected versions would end up in
762+ // the "unused patch" listing, and trigger a warning. It might take a
763+ // fair bit of restructuring to make that work cleanly, and there
764+ // isn't any demand at this time to support that.
765+ let mut vers: Vec < _ > = summaries. iter ( ) . map ( |summary| summary. version ( ) ) . collect ( ) ;
766+ vers. sort ( ) ;
767+ let versions: Vec < _ > = vers. into_iter ( ) . map ( |v| v. to_string ( ) ) . collect ( ) ;
768+ anyhow:: bail!(
769+ "patch for `{}` in `{}` resolved to more than one candidate\n \
770+ Found versions: {}\n \
771+ Update the patch definition to select only one package.\n \
772+ For example, add an `=` version requirement to the patch definition, \
773+ such as `version = \" ={}\" `.",
774+ orig_patch. package_name( ) ,
775+ orig_patch. source_id( ) ,
776+ versions. join( ", " ) ,
777+ versions. last( ) . unwrap( )
778+ ) ;
779+ }
780+ assert ! ( summaries. is_empty( ) ) ;
781+ // No summaries found, try to help the user figure out what is wrong.
782+ if let Some ( ( _locked_patch, locked_id) ) = locked {
783+ // Since the locked patch did not match anything, try the unlocked one.
784+ let orig_matches = source. query_vec ( orig_patch) . unwrap_or_else ( |e| {
785+ log:: warn!(
786+ "could not determine unlocked summaries for dep {:?}: {:?}" ,
787+ orig_patch,
788+ e
789+ ) ;
790+ Vec :: new ( )
791+ } ) ;
792+ let ( summary, _) = summary_for_patch ( orig_patch, & None , orig_matches, source) ?;
793+ // The unlocked version found a match. This returns a value to
794+ // indicate that this entry should be unlocked.
795+ return Ok ( ( summary, Some ( * locked_id) ) ) ;
796+ }
797+ // Try checking if there are *any* packages that match this by name.
798+ let name_only_dep = Dependency :: new_override ( orig_patch. package_name ( ) , orig_patch. source_id ( ) ) ;
799+ let name_summaries = source. query_vec ( & name_only_dep) . unwrap_or_else ( |e| {
800+ log:: warn!(
801+ "failed to do name-only summary query for {:?}: {:?}" ,
802+ name_only_dep,
803+ e
804+ ) ;
805+ Vec :: new ( )
806+ } ) ;
807+ let mut vers = name_summaries
808+ . iter ( )
809+ . map ( |summary| summary. version ( ) )
810+ . collect :: < Vec < _ > > ( ) ;
811+ let found = match vers. len ( ) {
812+ 0 => format ! ( "" ) ,
813+ 1 => format ! ( "version `{}`" , vers[ 0 ] ) ,
814+ _ => {
815+ vers. sort ( ) ;
816+ let strs: Vec < _ > = vers. into_iter ( ) . map ( |v| v. to_string ( ) ) . collect ( ) ;
817+ format ! ( "versions `{}`" , strs. join( ", " ) )
818+ }
819+ } ;
820+ if found. is_empty ( ) {
821+ anyhow:: bail!(
822+ "The patch location `{}` does not appear to contain any packages \
823+ matching the name `{}`.",
824+ orig_patch. source_id( ) ,
825+ orig_patch. package_name( )
826+ ) ;
827+ } else {
828+ anyhow:: bail!(
829+ "The patch location `{}` contains a `{}` package with {}, but the patch \
830+ definition requires `{}`.\n \
831+ Check that the version in the patch location is what you expect, \
832+ and update the patch definition to match.",
833+ orig_patch. source_id( ) ,
834+ orig_patch. package_name( ) ,
835+ found,
836+ orig_patch. version_req( )
837+ ) ;
838+ }
839+ }
0 commit comments