@@ -222,16 +222,35 @@ impl<'cfg> PackageRegistry<'cfg> {
222
222
/// the manifest.
223
223
///
224
224
/// 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`.
228
232
///
229
233
/// Note that the patch list specified here *will not* be available to
230
234
/// `query` until `lock_patches` is called below, which should be called
231
235
/// 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.
233
249
let canonical = CanonicalUrl :: new ( url) ?;
234
250
251
+ // Return value of patches that shouldn't be locked.
252
+ let mut unlock_patches = Vec :: new ( ) ;
253
+
235
254
// First up we need to actually resolve each `deps` specification to
236
255
// precisely one summary. We're not using the `query` method below as it
237
256
// internally uses maps we're building up as part of this method
@@ -243,7 +262,15 @@ impl<'cfg> PackageRegistry<'cfg> {
243
262
// of summaries which should be the same length as `deps` above.
244
263
let unlocked_summaries = deps
245
264
. 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
+ } ;
247
274
debug ! (
248
275
"registering a patch for `{}` with `{}`" ,
249
276
url,
@@ -261,30 +288,27 @@ impl<'cfg> PackageRegistry<'cfg> {
261
288
)
262
289
} ) ?;
263
290
264
- let mut summaries = self
291
+ let source = self
265
292
. sources
266
293
. 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) ) ;
287
310
}
311
+
288
312
if * summary. package_id ( ) . source_id ( ) . canonical_url ( ) == canonical {
289
313
anyhow:: bail!(
290
314
"patch for `{}` in `{}` points to the same source, but \
@@ -321,7 +345,7 @@ impl<'cfg> PackageRegistry<'cfg> {
321
345
self . patches_available . insert ( canonical. clone ( ) , ids) ;
322
346
self . patches . insert ( canonical, unlocked_summaries) ;
323
347
324
- Ok ( ( ) )
348
+ Ok ( unlock_patches )
325
349
}
326
350
327
351
/// Lock all patch summaries added via `patch`, making them available to
@@ -335,6 +359,7 @@ impl<'cfg> PackageRegistry<'cfg> {
335
359
assert ! ( !self . patches_locked) ;
336
360
for summaries in self . patches . values_mut ( ) {
337
361
for summary in summaries {
362
+ debug ! ( "locking patch {:?}" , summary) ;
338
363
* summary = lock ( & self . locked , & self . patches_available , summary. clone ( ) ) ;
339
364
}
340
365
}
@@ -718,3 +743,97 @@ fn lock(
718
743
dep
719
744
} )
720
745
}
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