6868
6969use crate :: core:: dependency:: Dependency ;
7070use crate :: core:: { PackageId , SourceId , Summary } ;
71- use crate :: sources:: registry:: { RegistryData , RegistryPackage } ;
71+ use crate :: sources:: registry:: { RegistryData , RegistryPackage , INDEX_V_MAX } ;
7272use crate :: util:: interning:: InternedString ;
7373use crate :: util:: paths;
7474use crate :: util:: { internal, CargoResult , Config , Filesystem , ToSemver } ;
75- use log:: info;
75+ use anyhow:: bail;
76+ use log:: { debug, info} ;
7677use semver:: { Version , VersionReq } ;
7778use std:: collections:: { HashMap , HashSet } ;
79+ use std:: convert:: TryInto ;
7880use std:: fs;
7981use std:: path:: Path ;
8082use std:: str;
@@ -233,6 +235,8 @@ enum MaybeIndexSummary {
233235pub struct IndexSummary {
234236 pub summary : Summary ,
235237 pub yanked : bool ,
238+ /// Schema version, see [`RegistryPackage`].
239+ v : u32 ,
236240}
237241
238242/// A representation of the cache on disk that Cargo maintains of summaries.
@@ -305,6 +309,11 @@ impl<'cfg> RegistryIndex<'cfg> {
305309 // minimize the amount of work being done here and parse as little as
306310 // necessary.
307311 let raw_data = & summaries. raw_data ;
312+ let max_version = if namespaced_features || weak_dep_features {
313+ INDEX_V_MAX
314+ } else {
315+ 1
316+ } ;
308317 Ok ( summaries
309318 . versions
310319 . iter_mut ( )
@@ -318,6 +327,19 @@ impl<'cfg> RegistryIndex<'cfg> {
318327 }
319328 } ,
320329 )
330+ . filter ( move |is| {
331+ if is. v > max_version {
332+ debug ! (
333+ "unsupported schema version {} ({} {})" ,
334+ is. v,
335+ is. summary. name( ) ,
336+ is. summary. version( )
337+ ) ;
338+ false
339+ } else {
340+ true
341+ }
342+ } )
321343 . filter ( move |is| {
322344 is. summary
323345 . unstable_gate ( namespaced_features, weak_dep_features)
@@ -550,6 +572,14 @@ impl Summaries {
550572 let summary = match IndexSummary :: parse ( config, line, source_id) {
551573 Ok ( summary) => summary,
552574 Err ( e) => {
575+ // This should only happen when there is an index
576+ // entry from a future version of cargo that this
577+ // version doesn't understand. Hopefully, those future
578+ // versions of cargo correctly set INDEX_V_MAX and
579+ // CURRENT_CACHE_VERSION, otherwise this will skip
580+ // entries in the cache preventing those newer
581+ // versions from reading them (that is, until the
582+ // cache is rebuilt).
553583 log:: info!( "failed to parse {:?} registry package: {}" , relative, e) ;
554584 continue ;
555585 }
@@ -578,7 +608,14 @@ impl Summaries {
578608 // actually happens to verify that our cache is indeed fresh and
579609 // computes exactly the same value as before.
580610 if cfg ! ( debug_assertions) && cache_contents. is_some ( ) {
581- assert_eq ! ( cache_bytes, cache_contents) ;
611+ if cache_bytes != cache_contents {
612+ panic ! (
613+ "original cache contents:\n {:?}\n \
614+ does not equal new cache contents:\n {:?}\n ",
615+ cache_contents. as_ref( ) . map( |s| String :: from_utf8_lossy( s) ) ,
616+ cache_bytes. as_ref( ) . map( |s| String :: from_utf8_lossy( s) ) ,
617+ ) ;
618+ }
582619 }
583620
584621 // Once we have our `cache_bytes` which represents the `Summaries` we're
@@ -630,9 +667,9 @@ impl Summaries {
630667// Implementation of serializing/deserializing the cache of summaries on disk.
631668// Currently the format looks like:
632669//
633- // +--------------+-------------+---+
634- // | version byte | git sha rev | 0 |
635- // +--------------+-------------+---+
670+ // +--------------------+---------------------- +-------------+---+
671+ // | cache version byte | index format version | git sha rev | 0 |
672+ // +--------------------+---------------------- +-------------+---+
636673//
637674// followed by...
638675//
@@ -649,8 +686,14 @@ impl Summaries {
649686// versions of Cargo share the same cache they don't get too confused. The git
650687// sha lets us know when the file needs to be regenerated (it needs regeneration
651688// whenever the index itself updates).
689+ //
690+ // Cache versions:
691+ // * `1`: The original version.
692+ // * `2`: Added the "index format version" field so that if the index format
693+ // changes, different versions of cargo won't get confused reading each
694+ // other's caches.
652695
653- const CURRENT_CACHE_VERSION : u8 = 1 ;
696+ const CURRENT_CACHE_VERSION : u8 = 2 ;
654697
655698impl < ' a > SummariesCache < ' a > {
656699 fn parse ( data : & ' a [ u8 ] , last_index_update : & str ) -> CargoResult < SummariesCache < ' a > > {
@@ -659,19 +702,32 @@ impl<'a> SummariesCache<'a> {
659702 . split_first ( )
660703 . ok_or_else ( || anyhow:: format_err!( "malformed cache" ) ) ?;
661704 if * first_byte != CURRENT_CACHE_VERSION {
662- anyhow:: bail!( "looks like a different Cargo's cache, bailing out" ) ;
705+ bail ! ( "looks like a different Cargo's cache, bailing out" ) ;
706+ }
707+ let index_v_bytes = rest
708+ . get ( ..4 )
709+ . ok_or_else ( || anyhow:: anyhow!( "cache expected 4 bytes for index version" ) ) ?;
710+ let index_v = u32:: from_le_bytes ( index_v_bytes. try_into ( ) . unwrap ( ) ) ;
711+ if index_v != INDEX_V_MAX {
712+ bail ! (
713+ "index format version {} doesn't match the version I know ({})" ,
714+ index_v,
715+ INDEX_V_MAX
716+ ) ;
663717 }
718+ let rest = & rest[ 4 ..] ;
719+
664720 let mut iter = split ( rest, 0 ) ;
665721 if let Some ( update) = iter. next ( ) {
666722 if update != last_index_update. as_bytes ( ) {
667- anyhow :: bail!(
723+ bail ! (
668724 "cache out of date: current index ({}) != cache ({})" ,
669725 last_index_update,
670726 str :: from_utf8( update) ?,
671727 )
672728 }
673729 } else {
674- anyhow :: bail!( "malformed file" ) ;
730+ bail ! ( "malformed file" ) ;
675731 }
676732 let mut ret = SummariesCache :: default ( ) ;
677733 while let Some ( version) = iter. next ( ) {
@@ -692,6 +748,7 @@ impl<'a> SummariesCache<'a> {
692748 . sum ( ) ;
693749 let mut contents = Vec :: with_capacity ( size) ;
694750 contents. push ( CURRENT_CACHE_VERSION ) ;
751+ contents. extend ( & u32:: to_le_bytes ( INDEX_V_MAX ) ) ;
695752 contents. extend_from_slice ( index_version. as_bytes ( ) ) ;
696753 contents. push ( 0 ) ;
697754 for ( version, data) in self . versions . iter ( ) {
@@ -741,26 +798,41 @@ impl IndexSummary {
741798 ///
742799 /// The `line` provided is expected to be valid JSON.
743800 fn parse ( config : & Config , line : & [ u8 ] , source_id : SourceId ) -> CargoResult < IndexSummary > {
801+ // ****CAUTION**** Please be extremely careful with returning errors
802+ // from this function. Entries that error are not included in the
803+ // index cache, and can cause cargo to get confused when switching
804+ // between different versions that understand the index differently.
805+ // Make sure to consider the INDEX_V_MAX and CURRENT_CACHE_VERSION
806+ // values carefully when making changes here.
744807 let RegistryPackage {
745808 name,
746809 vers,
747810 cksum,
748811 deps,
749- features,
812+ mut features,
813+ features2,
750814 yanked,
751815 links,
816+ v,
752817 } = serde_json:: from_slice ( line) ?;
818+ let v = v. unwrap_or ( 1 ) ;
753819 log:: trace!( "json parsed registry {}/{}" , name, vers) ;
754820 let pkgid = PackageId :: new ( name, & vers, source_id) ?;
755821 let deps = deps
756822 . into_iter ( )
757823 . map ( |dep| dep. into_dep ( source_id) )
758824 . collect :: < CargoResult < Vec < _ > > > ( ) ?;
825+ if let Some ( features2) = features2 {
826+ for ( name, values) in features2 {
827+ features. entry ( name) . or_default ( ) . extend ( values) ;
828+ }
829+ }
759830 let mut summary = Summary :: new ( config, pkgid, deps, & features, links) ?;
760831 summary. set_checksum ( cksum) ;
761832 Ok ( IndexSummary {
762833 summary,
763834 yanked : yanked. unwrap_or ( false ) ,
835+ v,
764836 } )
765837 }
766838}
0 commit comments