@@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet};
2
2
3
3
use anyhow:: bail;
4
4
use log:: { debug, trace} ;
5
- use semver:: VersionReq ;
5
+ use semver:: { Version , VersionReq } ;
6
6
use url:: Url ;
7
7
8
8
use crate :: core:: PackageSet ;
@@ -222,14 +222,22 @@ 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
+ pub fn patch (
237
+ & mut self ,
238
+ url : & Url ,
239
+ deps : & [ ( & Dependency , Option < Dependency > ) ] ,
240
+ ) -> CargoResult < ( ) > {
233
241
let canonical = CanonicalUrl :: new ( url) ?;
234
242
235
243
// First up we need to actually resolve each `deps` specification to
@@ -243,7 +251,8 @@ impl<'cfg> PackageRegistry<'cfg> {
243
251
// of summaries which should be the same length as `deps` above.
244
252
let unlocked_summaries = deps
245
253
. iter ( )
246
- . map ( |dep| {
254
+ . map ( |( orig_dep, locked_dep) | {
255
+ let dep = locked_dep. as_ref ( ) . unwrap_or ( orig_dep) ;
247
256
debug ! (
248
257
"registering a patch for `{}` with `{}`" ,
249
258
url,
@@ -261,30 +270,13 @@ impl<'cfg> PackageRegistry<'cfg> {
261
270
)
262
271
} ) ?;
263
272
264
- let mut summaries = self
273
+ let source = self
265
274
. sources
266
275
. 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
- )
287
- }
276
+ . expect ( "loaded source not present" ) ;
277
+ let summaries = source. query_vec ( dep) ?;
278
+ let summary = summary_for_patch ( orig_dep, locked_dep, url, summaries, source) ?;
279
+
288
280
if * summary. package_id ( ) . source_id ( ) . canonical_url ( ) == canonical {
289
281
anyhow:: bail!(
290
282
"patch for `{}` in `{}` points to the same source, but \
@@ -718,3 +710,108 @@ fn lock(
718
710
dep
719
711
} )
720
712
}
713
+
714
+ /// This is a helper for generating a user-friendly error message for a bad patch.
715
+ fn summary_for_patch (
716
+ orig_patch : & Dependency ,
717
+ locked_patch : & Option < Dependency > ,
718
+ url : & Url ,
719
+ mut summaries : Vec < Summary > ,
720
+ source : & mut dyn Source ,
721
+ ) -> CargoResult < Summary > {
722
+ if summaries. len ( ) == 1 {
723
+ return Ok ( summaries. pop ( ) . unwrap ( ) ) ;
724
+ }
725
+ // Helpers to create a comma-separated string of versions.
726
+ let versions = |versions : & mut [ & Version ] | -> String {
727
+ versions. sort ( ) ;
728
+ let versions: Vec < _ > = versions. into_iter ( ) . map ( |v| v. to_string ( ) ) . collect ( ) ;
729
+ versions. join ( ", " )
730
+ } ;
731
+ let summary_versions = |summaries : & [ Summary ] | -> String {
732
+ let mut vers: Vec < _ > = summaries. iter ( ) . map ( |summary| summary. version ( ) ) . collect ( ) ;
733
+ versions ( & mut vers)
734
+ } ;
735
+ if summaries. len ( ) > 1 {
736
+ anyhow:: bail!(
737
+ "patch for `{}` in `{}` resolved to more than one candidate\n \
738
+ Found versions: {}\n \
739
+ Update the patch definition to select only one package, \
740
+ or remove the extras from the patch location.",
741
+ orig_patch. package_name( ) ,
742
+ url,
743
+ summary_versions( & summaries)
744
+ ) ;
745
+ }
746
+ // No summaries found, try to help the user figure out what is wrong.
747
+ let extra = if let Some ( locked_patch) = locked_patch {
748
+ let found = match source. query_vec ( orig_patch) {
749
+ Ok ( unlocked_summaries) => format ! ( " (found {})" , summary_versions( & unlocked_summaries) ) ,
750
+ Err ( e) => {
751
+ log:: warn!(
752
+ "could not determine unlocked summaries for dep {:?}: {:?}" ,
753
+ orig_patch,
754
+ e
755
+ ) ;
756
+ "" . to_string ( )
757
+ }
758
+ } ;
759
+ format ! (
760
+ "The patch is locked to {} in Cargo.lock, \
761
+ but the version in the patch location does not match{}.\n \
762
+ Make sure the patch points to the correct version.\n \
763
+ If it does, run `cargo update -p {}` to update Cargo.lock.",
764
+ locked_patch. version_req( ) ,
765
+ found,
766
+ locked_patch. package_name( ) ,
767
+ )
768
+ } else {
769
+ // Try checking if there are *any* packages that match this by name.
770
+ let name_only_dep =
771
+ Dependency :: new_override ( orig_patch. package_name ( ) , orig_patch. source_id ( ) ) ;
772
+ let found = match source. query_vec ( & name_only_dep) {
773
+ Ok ( name_summaries) => {
774
+ let mut vers = name_summaries
775
+ . iter ( )
776
+ . map ( |summary| summary. version ( ) )
777
+ . collect :: < Vec < _ > > ( ) ;
778
+ match vers. len ( ) {
779
+ 0 => format ! ( "" ) ,
780
+ 1 => format ! ( "version `{}`" , versions( & mut vers) ) ,
781
+ _ => format ! ( "versions `{}`" , versions( & mut vers) ) ,
782
+ }
783
+ }
784
+ Err ( e) => {
785
+ log:: warn!(
786
+ "failed to do name-only summary query for {:?}: {:?}" ,
787
+ name_only_dep,
788
+ e
789
+ ) ;
790
+ "" . to_string ( )
791
+ }
792
+ } ;
793
+ if found. is_empty ( ) {
794
+ format ! (
795
+ "The patch location does not appear to contain any packages \
796
+ matching the name `{}`.",
797
+ orig_patch. package_name( )
798
+ )
799
+ } else {
800
+ format ! (
801
+ "The patch location contains a `{}` package with {}, but the patch \
802
+ definition requires `{}`.\n \
803
+ Check that the version in the patch location is what you expect, \
804
+ and update the patch definition to match.",
805
+ orig_patch. package_name( ) ,
806
+ found,
807
+ orig_patch. version_req( )
808
+ )
809
+ }
810
+ } ;
811
+ anyhow:: bail!(
812
+ "patch for `{}` in `{}` did not resolve to any crates.\n {}" ,
813
+ orig_patch. package_name( ) ,
814
+ url,
815
+ extra
816
+ ) ;
817
+ }
0 commit comments