Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/bin/cargo/commands/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ pub fn cli() -> Command {
.default_value("10"),
),
)
.subcommand(
subcommand("rebuilds")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not thrilled with this command name though we could defer it to the tracking issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any specific one in mind?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like ehuss made a one off comment for cargo report rebuild (#2904 (comment))

I feel like what is missing is that we are showing the "reason" or "cause" of the build. Not thrilled with any of the names though (build-cause, build-reason, what-changed).

Depending on how we render it, this could instead be a buck what-ran or build though that would likely move too far away from the intended purpose here.

.about("Reports rebuild reasons from previous sessions (unstable)")
.arg_manifest_path(),
)
}

pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
Expand Down Expand Up @@ -75,6 +80,19 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
ops::report_sessions(gctx, ws.as_ref(), opts)?;
Ok(())
}
Some(("rebuilds", args)) => {
gctx.cli_unstable().fail_if_stable_command(
gctx,
"report rebuilds",
15844,
"build-analysis",
gctx.cli_unstable().build_analysis,
)?;
let ws = args.workspace(gctx).ok();
let opts = rebuilds_opts(args)?;
ops::report_rebuilds(gctx, ws.as_ref(), opts)?;
Ok(())
}
Some((cmd, _)) => {
unreachable!("unexpected command {}", cmd)
}
Expand Down Expand Up @@ -112,3 +130,7 @@ fn sessions_opts(args: &ArgMatches) -> CargoResult<ops::ReportSessionsOptions> {

Ok(ops::ReportSessionsOptions { limit })
}

fn rebuilds_opts(_args: &ArgMatches) -> CargoResult<ops::ReportRebuildsOptions> {
Ok(ops::ReportRebuildsOptions {})
}
10 changes: 10 additions & 0 deletions src/cargo/core/compiler/fingerprint/dep_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,16 @@ impl Serialize for Checksum {
}
}

impl<'de> serde::Deserialize<'de> for Checksum {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}

#[derive(Debug, thiserror::Error)]
pub enum InvalidChecksum {
#[error("algorithm portion incorrect, expected `sha256`, or `blake3`")]
Expand Down
73 changes: 37 additions & 36 deletions src/cargo/core/compiler/fingerprint/dirty_reason.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::HashMap;
use std::fmt;
use std::fmt::Debug;

Expand All @@ -10,7 +11,7 @@ use crate::core::Shell;
/// to a recompile. Usually constructed via [`Fingerprint::compare`].
///
/// [`Fingerprint::compare`]: super::Fingerprint::compare
#[derive(Clone, Debug, Serialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "dirty_reason", rename_all = "kebab-case")]
pub enum DirtyReason {
RustcChanged,
Expand Down Expand Up @@ -61,8 +62,8 @@ pub enum DirtyReason {
new_value: Option<String>,
},
LocalFingerprintTypeChanged {
old: &'static str,
new: &'static str,
old: String,
new: String,
},
NumberOfDependenciesChanged {
old: usize,
Expand All @@ -73,11 +74,7 @@ pub enum DirtyReason {
new: InternedString,
},
UnitDependencyInfoChanged {
old_name: InternedString,
old_fingerprint: u64,

new_name: InternedString,
new_fingerprint: u64,
unit: u64,
},
FsStatusOutdated(FsStatus),
NothingObvious,
Expand Down Expand Up @@ -148,7 +145,13 @@ impl DirtyReason {
}
}

pub fn present_to(&self, s: &mut Shell, unit: &Unit, root: &Path) -> CargoResult<()> {
pub fn present_to(
&self,
s: &mut Shell,
unit: &Unit,
root: &Path,
index_to_unit: &HashMap<u64, Unit>,
) -> CargoResult<()> {
match self {
DirtyReason::RustcChanged => s.dirty_because(unit, "the toolchain changed"),
DirtyReason::FeaturesChanged { .. } => {
Expand Down Expand Up @@ -220,8 +223,12 @@ impl DirtyReason {
unit,
format_args!("name of dependency changed ({old} => {new})"),
),
DirtyReason::UnitDependencyInfoChanged { .. } => {
s.dirty_because(unit, "dependency info changed")
DirtyReason::UnitDependencyInfoChanged { unit: dep_unit } => {
let dep_name = index_to_unit.get(dep_unit).map(|u| u.pkg.name()).unwrap();
s.dirty_because(
unit,
format_args!("info of dependency `{dep_name}` changed"),
)
}
DirtyReason::FsStatusOutdated(status) => match status {
FsStatus::Stale => s.dirty_because(unit, "stale, unknown reason"),
Expand Down Expand Up @@ -301,19 +308,23 @@ impl DirtyReason {
),
},
FsStatus::StaleDependency {
name,
unit: dep_unit,
dep_mtime,
max_mtime,
..
} => {
let dep_name = index_to_unit.get(dep_unit).map(|u| u.pkg.name()).unwrap();
let after = Self::after(*max_mtime, *dep_mtime, "last build");
s.dirty_because(
unit,
format_args!("the dependency {name} was rebuilt ({after})"),
format_args!("the dependency `{dep_name}` was rebuilt ({after})"),
)
}
FsStatus::StaleDepFingerprint { name } => {
s.dirty_because(unit, format_args!("the dependency {name} was rebuilt"))
FsStatus::StaleDepFingerprint { unit: dep_unit } => {
let dep_name = index_to_unit.get(dep_unit).map(|u| u.pkg.name()).unwrap();
s.dirty_because(
unit,
format_args!("the dependency `{dep_name}` was rebuilt"),
)
}
FsStatus::UpToDate { .. } => {
unreachable!()
Expand Down Expand Up @@ -529,8 +540,8 @@ mod json_schema {
str![[r#"
{
"dirty_reason": "unit-dependency-name-changed",
"old": "old_dep",
"new": "new_dep"
"new": "new_dep",
"old": "old_dep"
}
"#]]
.is_json()
Expand All @@ -539,21 +550,13 @@ mod json_schema {

#[test]
fn unit_dependency_info_changed() {
let reason = DirtyReason::UnitDependencyInfoChanged {
old_name: "serde".into(),
old_fingerprint: 0x1234567890abcdef,
new_name: "serde".into(),
new_fingerprint: 0xfedcba0987654321,
};
let reason = DirtyReason::UnitDependencyInfoChanged { unit: 15 };
assert_data_eq!(
to_json(&reason),
str![[r#"
{
"dirty_reason": "unit-dependency-info-changed",
"new_fingerprint": 18364757930599072545,
"new_name": "serde",
"old_fingerprint": 1311768467294899695,
"old_name": "serde"
"unit": 15
}
"#]]
.is_json()
Expand Down Expand Up @@ -647,7 +650,7 @@ mod json_schema {
#[test]
fn fs_status_stale_dependency() {
let reason = DirtyReason::FsStatusOutdated(FsStatus::StaleDependency {
name: "serde".into(),
unit: 42,
dep_mtime: FileTime::from_unix_time(1730567892, 789000000),
max_mtime: FileTime::from_unix_time(1730567890, 123000000),
});
Expand All @@ -659,7 +662,7 @@ mod json_schema {
"dirty_reason": "fs-status-outdated",
"fs_status": "stale-dependency",
"max_mtime": 1730567890123.0,
"name": "serde"
"unit": 42
}
"#]]
.is_json()
Expand All @@ -668,16 +671,14 @@ mod json_schema {

#[test]
fn fs_status_stale_dep_fingerprint() {
let reason = DirtyReason::FsStatusOutdated(FsStatus::StaleDepFingerprint {
name: "tokio".into(),
});
let reason = DirtyReason::FsStatusOutdated(FsStatus::StaleDepFingerprint { unit: 42 });
assert_data_eq!(
to_json(&reason),
str![[r#"
{
"dirty_reason": "fs-status-outdated",
"fs_status": "stale-dep-fingerprint",
"name": "tokio"
"unit": 42
}
"#]]
.is_json()
Expand Down Expand Up @@ -830,8 +831,8 @@ mod json_schema {
#[test]
fn local_fingerprint_type_changed() {
let reason = DirtyReason::LocalFingerprintTypeChanged {
old: "precalculated",
new: "rerun-if-changed",
old: "precalculated".to_owned(),
new: "rerun-if-changed".to_owned(),
};
assert_data_eq!(
to_json(&reason),
Expand Down
Loading