Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: track version in fingerprint dep-info files
Browse files Browse the repository at this point in the history
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`.
weihanglo committed Oct 30, 2024

Verified

This commit was signed with the committer’s verified signature. The key has expired.
weihanglo Weihang Lo
1 parent ce550ca commit a2a2ad7
Showing 1 changed file with 46 additions and 3 deletions.
49 changes: 46 additions & 3 deletions src/cargo/core/compiler/fingerprint/dep_info.rs
Original file line number Diff line number Diff line change
@@ -22,6 +22,9 @@ use cargo_util::Sha256;
use crate::CargoResult;
use crate::CARGO_ENV;

/// The current format version of [`EncodedDepInfo`].
const CURRENT_ENCODED_DEP_INFO_VERSION: u8 = 1;

/// The representation of the `.d` dep-info file generated by rustc
#[derive(Default)]
pub struct RustcDepInfo {
@@ -61,13 +64,26 @@ pub enum DepInfoPathType {
/// Currently the format looks like:
///
/// ```text
/// +------------+------------+---------------+---------------+
/// | # of files | file paths | # of env vars | env var pairs |
/// +------------+------------+---------------+---------------+
/// +----------------+------------+------------+---------------+---------------+
/// | format version | # of files | file paths | # of env vars | env var pairs |
/// +----------------+------------+------------+---------------+---------------+
/// ```
///
/// Each field represents
///
/// * _Format version_ --- The version of the current encoding.
/// Since [`EncodedDepInfo`] is just an optimization, to avoid adding any complexity,
/// Cargo recognizes only one version of [`CURRENT_ENCODED_DEP_INFO_VERSION`].
/// To support v0 (prior to checksum support in [`f4ca7390`]),
/// which doesn't have this field, we do a hacky workaround. The current layout looks like
/// ```text
/// +----------------------------+---------+
/// | [0x01 0x00 0x00 0x00 0xff] | version |
/// +----------------------------+---------+
/// ```
/// The first 5 bytes will be interpreted as "one file trackced and invalid
/// [`DepInfoPathType`] variant with 255" by older Cargoes, so they will just
/// stop. This could possibly prevent some bad parsing in rust-lang/cargo#14712
/// * _Number of files/envs_ --- A `u32` representing the number of things.
/// * _File paths_ --- Zero or more paths of files the dep-info file depends on.
/// Each path is encoded as the following:
@@ -84,6 +100,8 @@ pub enum DepInfoPathType {
/// | len of key | key bytes | value exists? | len of value | value bytes |
/// +------------+-----------+---------------+--------------+-------------+
/// ```
///
/// [`f4ca7390`]: https://github.com/rust-lang/cargo/commit/f4ca739073185ea5e1148ff100bb4a06d3bf721d
#[derive(Default, Debug, PartialEq, Eq)]
pub struct EncodedDepInfo {
pub files: Vec<(DepInfoPathType, PathBuf, Option<(u64, String)>)>,
@@ -93,6 +111,11 @@ pub struct EncodedDepInfo {
impl EncodedDepInfo {
pub fn parse(mut bytes: &[u8]) -> Option<EncodedDepInfo> {
let bytes = &mut bytes;
let version = read_version(bytes)?;
if version != CURRENT_ENCODED_DEP_INFO_VERSION {
return None;
}

let nfiles = read_usize(bytes)?;
let mut files = Vec::with_capacity(nfiles);
for _ in 0..nfiles {
@@ -129,6 +152,16 @@ impl EncodedDepInfo {
}
return Some(EncodedDepInfo { files, env });

fn read_version(bytes: &mut &[u8]) -> Option<u8> {
let _size = read_usize(bytes)?;
let path_type = read_u8(bytes)?;
if path_type != u8::MAX {
// Old depinfo. Give up parsing it.
return None;
}
read_u8(bytes)
}

fn read_usize(bytes: &mut &[u8]) -> Option<usize> {
let ret = bytes.get(..4)?;
*bytes = &bytes[4..];
@@ -162,6 +195,9 @@ impl EncodedDepInfo {
pub fn serialize(&self) -> CargoResult<Vec<u8>> {
let mut ret = Vec::new();
let dst = &mut ret;

write_version(dst);

write_usize(dst, self.files.len());
for (ty, file, checksum_info) in self.files.iter() {
match ty {
@@ -189,6 +225,13 @@ impl EncodedDepInfo {
}
return Ok(ret);

// There is an assumption that there is always at least a file.
fn write_version(dst: &mut Vec<u8>) {
write_usize(dst, 1);
dst.push(u8::MAX);
dst.push(CURRENT_ENCODED_DEP_INFO_VERSION);
}

fn write_bytes(dst: &mut Vec<u8>, val: impl AsRef<[u8]>) {
let val = val.as_ref();
write_usize(dst, val.len());

0 comments on commit a2a2ad7

Please sign in to comment.