@@ -22,6 +22,9 @@ use cargo_util::Sha256;
22
22
use crate :: CargoResult ;
23
23
use crate :: CARGO_ENV ;
24
24
25
+ /// The current format version of [`EncodedDepInfo`].
26
+ const CURRENT_ENCODED_DEP_INFO_VERSION : u8 = 1 ;
27
+
25
28
/// The representation of the `.d` dep-info file generated by rustc
26
29
#[ derive( Default ) ]
27
30
pub struct RustcDepInfo {
@@ -61,20 +64,36 @@ pub enum DepInfoPathType {
61
64
/// Currently the format looks like:
62
65
///
63
66
/// ```text
64
- /// +------------+------------+---------------+---------------+
65
- /// | # of files | file paths | # of env vars | env var pairs |
66
- /// +------------+------------+---------------+---------------+
67
+ /// +--------+---------+-------- ----+------------+---------------+---------------+
68
+ /// | marker | version | # of files | file paths | # of env vars | env var pairs |
69
+ /// +--------+---------+-------- ----+------------+---------------+---------------+
67
70
/// ```
68
71
///
69
72
/// Each field represents
70
73
///
74
+ /// * _Marker_ --- A magic marker to ensure that older Cargoes, which only
75
+ /// recognize format v0 (prior to checksum support in [`f4ca7390`]), do not
76
+ /// proceed with parsing newer formats. Since [`EncodedDepInfo`] is merely
77
+ /// an optimization, and to avoid adding complexity, Cargo recognizes only
78
+ /// one version of [`CURRENT_ENCODED_DEP_INFO_VERSION`].
79
+ /// The current layout looks like this
80
+ /// ```text
81
+ /// +----------------------------+
82
+ /// | [0x01 0x00 0x00 0x00 0xff] |
83
+ /// +----------------------------+
84
+ /// ```
85
+ /// These bytes will be interpreted as "one file tracked and an invalid
86
+ /// [`DepInfoPathType`] variant with 255" by older Cargoes, causing them to
87
+ /// stop parsing. This could prevent problematic parsing as noted in
88
+ /// rust-lang/cargo#14712.
89
+ /// * _Version_ --- The current format version.
71
90
/// * _Number of files/envs_ --- A `u32` representing the number of things.
72
91
/// * _File paths_ --- Zero or more paths of files the dep-info file depends on.
73
92
/// Each path is encoded as the following:
74
93
///
75
94
/// ```text
76
95
/// +-----------+-------------+------------+---------------+-----------+-------+
77
- /// | Path type | len of path | path bytes | cksum exists? | file size | cksum |
96
+ /// | path type | len of path | path bytes | cksum exists? | file size | cksum |
78
97
/// +-----------+-------------+------------+---------------+-----------+-------+
79
98
/// ```
80
99
/// * _Env var pairs_ --- Zero or more env vars the dep-info file depends on.
@@ -84,7 +103,9 @@ pub enum DepInfoPathType {
84
103
/// | len of key | key bytes | value exists? | len of value | value bytes |
85
104
/// +------------+-----------+---------------+--------------+-------------+
86
105
/// ```
87
- #[ derive( Default ) ]
106
+ ///
107
+ /// [`f4ca7390`]: https://github.com/rust-lang/cargo/commit/f4ca739073185ea5e1148ff100bb4a06d3bf721d
108
+ #[ derive( Default , Debug , PartialEq , Eq ) ]
88
109
pub struct EncodedDepInfo {
89
110
pub files : Vec < ( DepInfoPathType , PathBuf , Option < ( u64 , String ) > ) > ,
90
111
pub env : Vec < ( String , Option < String > ) > ,
@@ -93,6 +114,12 @@ pub struct EncodedDepInfo {
93
114
impl EncodedDepInfo {
94
115
pub fn parse ( mut bytes : & [ u8 ] ) -> Option < EncodedDepInfo > {
95
116
let bytes = & mut bytes;
117
+ read_magic_marker ( bytes) ?;
118
+ let version = read_u8 ( bytes) ?;
119
+ if version != CURRENT_ENCODED_DEP_INFO_VERSION {
120
+ return None ;
121
+ }
122
+
96
123
let nfiles = read_usize ( bytes) ?;
97
124
let mut files = Vec :: with_capacity ( nfiles) ;
98
125
for _ in 0 ..nfiles {
@@ -129,6 +156,18 @@ impl EncodedDepInfo {
129
156
}
130
157
return Some ( EncodedDepInfo { files, env } ) ;
131
158
159
+ /// See [`EncodedDepInfo`] for why a magic marker exists.
160
+ fn read_magic_marker ( bytes : & mut & [ u8 ] ) -> Option < ( ) > {
161
+ let _size = read_usize ( bytes) ?;
162
+ let path_type = read_u8 ( bytes) ?;
163
+ if path_type != u8:: MAX {
164
+ // Old depinfo. Give up parsing it.
165
+ None
166
+ } else {
167
+ Some ( ( ) )
168
+ }
169
+ }
170
+
132
171
fn read_usize ( bytes : & mut & [ u8 ] ) -> Option < usize > {
133
172
let ret = bytes. get ( ..4 ) ?;
134
173
* bytes = & bytes[ 4 ..] ;
@@ -162,6 +201,10 @@ impl EncodedDepInfo {
162
201
pub fn serialize ( & self ) -> CargoResult < Vec < u8 > > {
163
202
let mut ret = Vec :: new ( ) ;
164
203
let dst = & mut ret;
204
+
205
+ write_magic_marker ( dst) ;
206
+ dst. push ( CURRENT_ENCODED_DEP_INFO_VERSION ) ;
207
+
165
208
write_usize ( dst, self . files . len ( ) ) ;
166
209
for ( ty, file, checksum_info) in self . files . iter ( ) {
167
210
match ty {
@@ -189,6 +232,14 @@ impl EncodedDepInfo {
189
232
}
190
233
return Ok ( ret) ;
191
234
235
+ /// See [`EncodedDepInfo`] for why a magic marker exists.
236
+ ///
237
+ /// There is an assumption that there is always at least a file.
238
+ fn write_magic_marker ( dst : & mut Vec < u8 > ) {
239
+ write_usize ( dst, 1 ) ;
240
+ dst. push ( u8:: MAX ) ;
241
+ }
242
+
192
243
fn write_bytes ( dst : & mut Vec < u8 > , val : impl AsRef < [ u8 ] > ) {
193
244
let val = val. as_ref ( ) ;
194
245
write_usize ( dst, val. len ( ) ) ;
@@ -614,3 +665,94 @@ pub enum InvalidChecksum {
614
665
#[ error( "expected a string with format \" algorithm=hex_checksum\" " ) ]
615
666
InvalidFormat ,
616
667
}
668
+
669
+ #[ cfg( test) ]
670
+ mod encoded_dep_info {
671
+ use super :: * ;
672
+
673
+ #[ track_caller]
674
+ fn gen_test ( checksum : bool ) {
675
+ let checksum = checksum. then_some ( ( 768 , "c01efc669f09508b55eced32d3c88702578a7c3e" . into ( ) ) ) ;
676
+ let lib_rs = (
677
+ DepInfoPathType :: TargetRootRelative ,
678
+ PathBuf :: from ( "src/lib.rs" ) ,
679
+ checksum. clone ( ) ,
680
+ ) ;
681
+
682
+ let depinfo = EncodedDepInfo {
683
+ files : vec ! [ lib_rs. clone( ) ] ,
684
+ env : Vec :: new ( ) ,
685
+ } ;
686
+ let data = depinfo. serialize ( ) . unwrap ( ) ;
687
+ assert_eq ! ( EncodedDepInfo :: parse( & data) . unwrap( ) , depinfo) ;
688
+
689
+ let mod_rs = (
690
+ DepInfoPathType :: TargetRootRelative ,
691
+ PathBuf :: from ( "src/mod.rs" ) ,
692
+ checksum. clone ( ) ,
693
+ ) ;
694
+ let depinfo = EncodedDepInfo {
695
+ files : vec ! [ lib_rs. clone( ) , mod_rs. clone( ) ] ,
696
+ env : Vec :: new ( ) ,
697
+ } ;
698
+ let data = depinfo. serialize ( ) . unwrap ( ) ;
699
+ assert_eq ! ( EncodedDepInfo :: parse( & data) . unwrap( ) , depinfo) ;
700
+
701
+ let depinfo = EncodedDepInfo {
702
+ files : vec ! [ lib_rs, mod_rs] ,
703
+ env : vec ! [
704
+ ( "Gimli" . into( ) , Some ( "Legolas" . into( ) ) ) ,
705
+ ( "Beren" . into( ) , Some ( "Lúthien" . into( ) ) ) ,
706
+ ] ,
707
+ } ;
708
+ let data = depinfo. serialize ( ) . unwrap ( ) ;
709
+ assert_eq ! ( EncodedDepInfo :: parse( & data) . unwrap( ) , depinfo) ;
710
+ }
711
+
712
+ #[ test]
713
+ fn round_trip ( ) {
714
+ gen_test ( false ) ;
715
+ }
716
+
717
+ #[ test]
718
+ fn round_trip_with_checksums ( ) {
719
+ gen_test ( true ) ;
720
+ }
721
+
722
+ #[ test]
723
+ fn path_type_is_u8_max ( ) {
724
+ #[ rustfmt:: skip]
725
+ let data = [
726
+ 0x01 , 0x00 , 0x00 , 0x00 , 0xff , // magic marker
727
+ CURRENT_ENCODED_DEP_INFO_VERSION , // version
728
+ 0x01 , 0x00 , 0x00 , 0x00 , // # of files
729
+ 0x00 , // path type
730
+ 0x04 , 0x00 , 0x00 , 0x00 , // len of path
731
+ 0x72 , 0x75 , 0x73 , 0x74 , // path bytes ("rust")
732
+ 0x00 , // cksum exists?
733
+ 0x00 , 0x00 , 0x00 , 0x00 , // # of env vars
734
+ ] ;
735
+ // The current cargo doesn't recognize the magic marker.
736
+ assert_eq ! (
737
+ EncodedDepInfo :: parse( & data) . unwrap( ) ,
738
+ EncodedDepInfo {
739
+ files: vec![ ( DepInfoPathType :: PackageRootRelative , "rust" . into( ) , None ) ] ,
740
+ env: Vec :: new( ) ,
741
+ }
742
+ ) ;
743
+ }
744
+
745
+ #[ test]
746
+ fn parse_v0_fingerprint_dep_info ( ) {
747
+ #[ rustfmt:: skip]
748
+ let data = [
749
+ 0x01 , 0x00 , 0x00 , 0x00 , // # of files
750
+ 0x00 , // path type
751
+ 0x04 , 0x00 , 0x00 , 0x00 , // len of path
752
+ 0x72 , 0x75 , 0x73 , 0x74 , // path bytes: "rust"
753
+ 0x00 , 0x00 , 0x00 , 0x00 , // # of env vars
754
+ ] ;
755
+ // Cargo can't recognize v0 after `-Zchecksum-freshess` added.
756
+ assert ! ( EncodedDepInfo :: parse( & data) . is_none( ) ) ;
757
+ }
758
+ }
0 commit comments