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
30 changes: 30 additions & 0 deletions src/cargo/core/compiler/build_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,36 @@ impl ser::Serialize for CompileMode {
}
}

impl<'de> serde::Deserialize<'de> for CompileMode {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
match s.as_str() {
"test" => Ok(CompileMode::Test),
"build" => Ok(CompileMode::Build),
"check" => Ok(CompileMode::Check { test: false }),
"doc" => Ok(CompileMode::Doc),
"doctest" => Ok(CompileMode::Doctest),
"docscrape" => Ok(CompileMode::Docscrape),
"run-custom-build" => Ok(CompileMode::RunCustomBuild),
other => Err(serde::de::Error::unknown_variant(
other,
&[
"test",
"build",
"check",
"doc",
"doctest",
"docscrape",
"run-custom-build",
],
)),
}
}
}

impl CompileMode {
/// Returns `true` if the unit is being checked.
pub fn is_check(self) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/core/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ mod output_depinfo;
mod output_sbom;
pub mod rustdoc;
pub mod standard_lib;
mod timings;
pub mod timings;
mod unit;
pub mod unit_dependencies;
pub mod unit_graph;
Expand Down
38 changes: 19 additions & 19 deletions src/cargo/core/compiler/timings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! This module implements some simple tracking information for timing of how
//! long it takes for different units to compile.

mod report;
pub mod report;

use super::{CompileMode, Unit};
use crate::core::PackageId;
Expand Down Expand Up @@ -75,9 +75,9 @@ pub struct Timings<'gctx> {
#[derive(Copy, Clone, serde::Serialize)]
pub struct CompilationSection {
/// Start of the section, as an offset in seconds from `UnitTime::start`.
start: f64,
pub start: f64,
/// End of the section, as an offset in seconds from `UnitTime::start`.
end: Option<f64>,
pub end: Option<f64>,
}

/// Tracking information for an individual unit.
Expand Down Expand Up @@ -107,18 +107,18 @@ struct UnitTime {
///
/// This is used by the HTML report's JavaScript to render the pipeline graph.
#[derive(serde::Serialize)]
struct UnitData {
i: u64,
name: String,
version: String,
mode: String,
target: String,
features: Vec<String>,
start: f64,
duration: f64,
unblocked_units: Vec<u64>,
unblocked_rmeta_units: Vec<u64>,
sections: Option<Vec<(report::SectionName, report::SectionData)>>,
pub struct UnitData {
pub i: u64,
pub name: String,
pub version: String,
pub mode: String,
pub target: String,
pub features: Vec<String>,
pub start: f64,
pub duration: f64,
pub unblocked_units: Vec<u64>,
pub unblocked_rmeta_units: Vec<u64>,
pub sections: Option<Vec<(report::SectionName, report::SectionData)>>,
}

impl<'gctx> Timings<'gctx> {
Expand Down Expand Up @@ -445,16 +445,16 @@ impl<'gctx> Timings<'gctx> {
let concurrency = report::compute_concurrency(&unit_data);

let ctx = report::RenderContext {
start_str: &self.start_str,
start_str: self.start_str.clone(),
root_units: &self.root_targets,
profile: &self.profile,
profile: self.profile.clone(),
total_fresh: self.total_fresh,
total_dirty: self.total_dirty,
unit_data,
concurrency,
cpu_usage: &self.cpu_usage,
rustc_version,
host: &build_runner.bcx.rustc().host,
rustc_version: rustc_version.into(),
host: build_runner.bcx.rustc().host.to_string(),
requested_targets,
jobs: build_runner.bcx.jobs(),
num_cpus,
Expand Down
61 changes: 26 additions & 35 deletions src/cargo/core/compiler/timings/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,19 @@ use std::collections::HashMap;
use std::collections::HashSet;
use std::io::Write;

use indexmap::IndexMap;
use itertools::Itertools as _;

use crate::CargoResult;
use crate::core::compiler::Unit;

use super::CompilationSection;
use super::UnitData;
use super::UnitTime;

/// Contains post-processed data of individual compilation sections.
enum AggregatedSections {
/// We know the names and durations of individual compilation sections
Sections(Vec<(SectionName, SectionData)>),
/// We know only the total duration
OnlyTotalDuration,
}

/// Name of an individual compilation section.
#[derive(Clone, Hash, Eq, PartialEq)]
pub(super) enum SectionName {
pub enum SectionName {
Frontend,
Codegen,
Named(String),
Expand Down Expand Up @@ -67,11 +61,11 @@ impl serde::ser::Serialize for SectionName {

/// Postprocessed section data that has both start and an end.
#[derive(Copy, Clone, serde::Serialize)]
pub(super) struct SectionData {
pub struct SectionData {
/// Start (relative to the start of the unit)
start: f64,
pub start: f64,
/// End (relative to the start of the unit)
end: f64,
pub end: f64,
}

impl SectionData {
Expand All @@ -96,13 +90,13 @@ pub struct Concurrency {

pub struct RenderContext<'a> {
/// A rendered string of when compilation started.
pub start_str: &'a str,
pub start_str: String,
/// A summary of the root units.
///
/// Tuples of `(package_description, target_descriptions)`.
pub root_units: &'a [(String, Vec<String>)],
/// The build profile.
pub profile: &'a str,
pub profile: String,
/// Total number of fresh units.
pub total_fresh: u32,
/// Total number of dirty units.
Expand All @@ -117,9 +111,9 @@ pub struct RenderContext<'a> {
/// system.
pub cpu_usage: &'a [(f64, f64)],
/// Compiler version info, i.e., `rustc 1.92.0-beta.2 (0a411606e 2025-10-31)`.
pub rustc_version: &'a str,
pub rustc_version: String,
/// The host triple (arch-platform-OS).
pub host: &'a str,
pub host: String,
/// The requested target platforms of compilation for this build.
pub requested_targets: &'a [&'a str],
/// The number of jobs specified for this build.
Expand All @@ -131,7 +125,7 @@ pub struct RenderContext<'a> {
}

/// Writes an HTML report.
pub(super) fn write_html(ctx: RenderContext<'_>, f: &mut impl Write) -> CargoResult<()> {
pub fn write_html(ctx: RenderContext<'_>, f: &mut impl Write) -> CargoResult<()> {
// The last concurrency record should equal to the last unit finished time.
let duration = ctx.concurrency.last().map(|c| c.t).unwrap_or(0.0);
let roots: Vec<&str> = ctx
Expand Down Expand Up @@ -384,10 +378,7 @@ pub(super) fn to_unit_data(
.iter()
.filter_map(|unit| unit_map.get(unit).copied())
.collect();
let sections = match aggregate_sections(ut) {
AggregatedSections::Sections(sections) => Some(sections),
AggregatedSections::OnlyTotalDuration => None,
};
let sections = aggregate_sections(ut.sections.clone(), ut.duration, ut.rmeta_time);

UnitData {
i,
Expand All @@ -407,7 +398,7 @@ pub(super) fn to_unit_data(
}

/// Derives concurrency information from unit timing data.
pub(super) fn compute_concurrency(unit_data: &[UnitData]) -> Vec<Concurrency> {
pub fn compute_concurrency(unit_data: &[UnitData]) -> Vec<Concurrency> {
if unit_data.is_empty() {
return Vec::new();
}
Expand Down Expand Up @@ -539,16 +530,17 @@ pub(super) fn compute_concurrency(unit_data: &[UnitData]) -> Vec<Concurrency> {
/// in which case we use them to determine the headers.
/// - We have at least one rmeta time, so we hard-code Frontend and Codegen headers.
/// - We only have total durations, so we don't add any additional headers.
fn aggregate_sections(unit_time: &UnitTime) -> AggregatedSections {
let end = unit_time.duration;

if !unit_time.sections.is_empty() {
pub fn aggregate_sections(
sections: IndexMap<String, CompilationSection>,
end: f64,
rmeta_time: Option<f64>,
) -> Option<Vec<(SectionName, SectionData)>> {
if !sections.is_empty() {
// We have some detailed compilation section timings, so we postprocess them
// Since it is possible that we do not have an end timestamp for a given compilation
// section, we need to iterate them and if an end is missing, we assign the end of
// the section to the start of the following section.

let mut sections = unit_time.sections.clone().into_iter().fold(
let mut sections = sections.into_iter().fold(
// The frontend section is currently implicit in rustc.
// It is assumed to start at compilation start and end when codegen starts,
// So we hard-code it here.
Expand Down Expand Up @@ -593,11 +585,10 @@ fn aggregate_sections(unit_time: &UnitTime) -> AggregatedSections {
},
));
}

AggregatedSections::Sections(sections)
} else if let Some(rmeta) = unit_time.rmeta_time {
Some(sections)
} else if let Some(rmeta) = rmeta_time {
// We only know when the rmeta time was generated
AggregatedSections::Sections(vec![
Some(vec![
(
SectionName::Frontend,
SectionData {
Expand All @@ -614,13 +605,13 @@ fn aggregate_sections(unit_time: &UnitTime) -> AggregatedSections {
),
])
} else {
// We only know the total duration
AggregatedSections::OnlyTotalDuration
// No section data provided. We only know the total duration.
None
}
}

/// Rounds seconds to 0.01s precision.
fn round_to_centisecond(x: f64) -> f64 {
pub fn round_to_centisecond(x: f64) -> f64 {
(x * 100.0).round() / 100.0
}

Expand Down
22 changes: 15 additions & 7 deletions src/cargo/util/log_message.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
//! Messages for logging.

use std::borrow::Cow;
use std::io::Write;
use std::path::PathBuf;

use cargo_util_schemas::core::PackageIdSpec;
use jiff::Timestamp;
use serde::Deserialize;
use serde::Serialize;

use crate::core::compiler::CompileMode;
Expand All @@ -13,7 +15,7 @@ use crate::core::compiler::fingerprint::DirtyReason;
/// A log message.
///
/// Each variant represents a different type of event.
#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
#[serde(tag = "reason", rename_all = "kebab-case")]
pub enum LogMessage {
/// Emitted when a build starts.
Expand Down Expand Up @@ -57,7 +59,7 @@ pub enum LogMessage {
/// Seconds elapsed from build start.
elapsed: f64,
/// Unit indices that were unblocked by this rmeta completion.
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
unblocked: Vec<u64>,
},
/// Emitted when a section (e.g., rmeta, link) of the compilation unit starts.
Expand Down Expand Up @@ -89,7 +91,7 @@ pub enum LogMessage {
/// Seconds elapsed from build start.
elapsed: f64,
/// Unit indices that were unblocked by this completion.
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
unblocked: Vec<u64>,
},
/// Emitted when a unit needs to be rebuilt.
Expand All @@ -101,17 +103,18 @@ pub enum LogMessage {
/// The compilation action this unit is for (check, build, test, etc.).
mode: CompileMode,
/// Reason why the unit is dirty and needs rebuilding.
#[serde(skip_deserializing, default = "default_reason")]
cause: DirtyReason,
},
}

/// Cargo target information.
#[derive(Serialize)]
#[derive(Serialize, Deserialize)]
pub struct Target {
/// Target name.
name: String,
pub name: String,
/// Target kind (lib, bin, test, bench, example, build-script).
kind: &'static str,
pub kind: Cow<'static, str>,
}

impl From<&crate::core::Target> for Target {
Expand All @@ -126,7 +129,8 @@ impl From<&crate::core::Target> for Target {
TargetKind::Bench => "bench",
TargetKind::ExampleLib(..) | TargetKind::ExampleBin => "example",
TargetKind::CustomBuild => "build-script",
},
}
.into(),
}
}
}
Expand All @@ -153,3 +157,7 @@ impl LogMessage {
Ok(())
}
}

fn default_reason() -> DirtyReason {
DirtyReason::NothingObvious
}
Loading