Skip to content

Commit e4fac7a

Browse files
committed
fix: track version in fingerprint dep-info files
Encodes the version information into Cargo's fingerprint dep-info files, so that when the format encoding changes in the future, Cargo understands a dep-info file was outdated and doesn't bother parsing it. Since there was no version info encoded in the old format (call it v0), to be compatible with older cargoes, this PR works around it with a horrible hack. It is explained in the doc comment of `EncodedDepInfo`.
1 parent 512561f commit e4fac7a

File tree

2 files changed

+68
-6
lines changed

2 files changed

+68
-6
lines changed

crates/cargo-test-support/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,12 @@ pub fn assert_deps(project: &Project, fingerprint: &str, test_cb: impl Fn(&Path,
15971597
assert!(files.next().is_none(), "expected only 1 dep-info file");
15981598
let dep_info = fs::read(&info_path).unwrap();
15991599
let dep_info = &mut &dep_info[..];
1600+
1601+
// Consume the magic marker and version. Here they don't really matter.
1602+
read_usize(dep_info);
1603+
read_u8(dep_info);
1604+
read_u8(dep_info);
1605+
16001606
let deps = (0..read_usize(dep_info))
16011607
.map(|_| {
16021608
let ty = read_u8(dep_info);

src/cargo/core/compiler/fingerprint/dep_info.rs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ use cargo_util::Sha256;
2222
use crate::CargoResult;
2323
use crate::CARGO_ENV;
2424

25+
/// The current format version of [`EncodedDepInfo`].
26+
const CURRENT_ENCODED_DEP_INFO_VERSION: u8 = 1;
27+
2528
/// The representation of the `.d` dep-info file generated by rustc
2629
#[derive(Default)]
2730
pub struct RustcDepInfo {
@@ -61,20 +64,35 @@ pub enum DepInfoPathType {
6164
/// Currently the format looks like:
6265
///
6366
/// ```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+
/// +--------+---------+------------+------------+---------------+---------------+
6770
/// ```
6871
///
6972
/// Each field represents
7073
///
74+
/// * _Marker_ --- A magic marker to ensure older Cargoes that only recognize
75+
/// format v0 (prior to checksum support in [`f4ca7390`]) not go further
76+
/// parsing newer formats. Since [`EncodedDepInfo`] is just an optimization,
77+
/// to avoid adding any complexity, Cargo recognizes only one version of
78+
/// [`CURRENT_ENCODED_DEP_INFO_VERSION`].
79+
/// The current layout looks like
80+
/// ```text
81+
/// +----------------------------+
82+
/// | [0x01 0x00 0x00 0x00 0xff] |
83+
/// +----------------------------+
84+
/// ```
85+
/// These bytes will be interpreted as "one file tracked and invalid
86+
/// [`DepInfoPathType`] variant with 255" by older Cargoes, so they will just
87+
/// stop parsing. This could prevent some bad parsing in rust-lang/cargo#14712.
88+
/// * _Version_ --- The current format version.
7189
/// * _Number of files/envs_ --- A `u32` representing the number of things.
7290
/// * _File paths_ --- Zero or more paths of files the dep-info file depends on.
7391
/// Each path is encoded as the following:
7492
///
7593
/// ```text
7694
/// +-----------+-------------+------------+---------------+-----------+-------+
77-
/// | Path type | len of path | path bytes | cksum exists? | file size | cksum |
95+
/// | path type | len of path | path bytes | cksum exists? | file size | cksum |
7896
/// +-----------+-------------+------------+---------------+-----------+-------+
7997
/// ```
8098
/// * _Env var pairs_ --- Zero or more env vars the dep-info file depends on.
@@ -84,6 +102,8 @@ pub enum DepInfoPathType {
84102
/// | len of key | key bytes | value exists? | len of value | value bytes |
85103
/// +------------+-----------+---------------+--------------+-------------+
86104
/// ```
105+
///
106+
/// [`f4ca7390`]: https://github.com/rust-lang/cargo/commit/f4ca739073185ea5e1148ff100bb4a06d3bf721d
87107
#[derive(Default, Debug, PartialEq, Eq)]
88108
pub struct EncodedDepInfo {
89109
pub files: Vec<(DepInfoPathType, PathBuf, Option<(u64, String)>)>,
@@ -93,6 +113,12 @@ pub struct EncodedDepInfo {
93113
impl EncodedDepInfo {
94114
pub fn parse(mut bytes: &[u8]) -> Option<EncodedDepInfo> {
95115
let bytes = &mut bytes;
116+
read_magic_marker(bytes)?;
117+
let version = read_u8(bytes)?;
118+
if version != CURRENT_ENCODED_DEP_INFO_VERSION {
119+
return None;
120+
}
121+
96122
let nfiles = read_usize(bytes)?;
97123
let mut files = Vec::with_capacity(nfiles);
98124
for _ in 0..nfiles {
@@ -129,6 +155,18 @@ impl EncodedDepInfo {
129155
}
130156
return Some(EncodedDepInfo { files, env });
131157

158+
/// See [`EncodedDepInfo`] for why a magic marker exists.
159+
fn read_magic_marker(bytes: &mut &[u8]) -> Option<()> {
160+
let _size = read_usize(bytes)?;
161+
let path_type = read_u8(bytes)?;
162+
if path_type != u8::MAX {
163+
// Old depinfo. Give up parsing it.
164+
return None;
165+
} else {
166+
Some(())
167+
}
168+
}
169+
132170
fn read_usize(bytes: &mut &[u8]) -> Option<usize> {
133171
let ret = bytes.get(..4)?;
134172
*bytes = &bytes[4..];
@@ -162,6 +200,10 @@ impl EncodedDepInfo {
162200
pub fn serialize(&self) -> CargoResult<Vec<u8>> {
163201
let mut ret = Vec::new();
164202
let dst = &mut ret;
203+
204+
write_magic_marker(dst);
205+
dst.push(CURRENT_ENCODED_DEP_INFO_VERSION);
206+
165207
write_usize(dst, self.files.len());
166208
for (ty, file, checksum_info) in self.files.iter() {
167209
match ty {
@@ -189,6 +231,14 @@ impl EncodedDepInfo {
189231
}
190232
return Ok(ret);
191233

234+
/// See [`EncodedDepInfo`] for why a magic marker exists.
235+
///
236+
/// There is an assumption that there is always at least a file.
237+
fn write_magic_marker(dst: &mut Vec<u8>) {
238+
write_usize(dst, 1);
239+
dst.push(u8::MAX);
240+
}
241+
192242
fn write_bytes(dst: &mut Vec<u8>, val: impl AsRef<[u8]>) {
193243
let val = val.as_ref();
194244
write_usize(dst, val.len());
@@ -673,7 +723,7 @@ mod encoded_dep_info {
673723
#[rustfmt::skip]
674724
let data = [
675725
0x01, 0x00, 0x00, 0x00, 0xff, // magic marker
676-
0x01, // version
726+
CURRENT_ENCODED_DEP_INFO_VERSION, // version
677727
0x01, 0x00, 0x00, 0x00, // # of files
678728
0x00, // path type
679729
0x04, 0x00, 0x00, 0x00, // len of path
@@ -682,7 +732,13 @@ mod encoded_dep_info {
682732
0x00, 0x00, 0x00, 0x00, // # of env vars
683733
];
684734
// The current cargo doesn't recognize the magic marker.
685-
assert!(EncodedDepInfo::parse(&data).is_none());
735+
assert_eq!(
736+
EncodedDepInfo::parse(&data).unwrap(),
737+
EncodedDepInfo {
738+
files: vec![(DepInfoPathType::PackageRootRelative, "rust".into(), None)],
739+
env: Vec::new(),
740+
}
741+
);
686742
}
687743

688744
#[test]

0 commit comments

Comments
 (0)