| //! 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::compiler::CompileMode; |
| use crate::compiler::UnitIndex; |
| use crate::compiler::fingerprint::DirtyReason; |
| |
| /// A log message. |
| /// |
| /// Each variant represents a different type of event. |
| #[derive(Serialize, Deserialize, Clone)] |
| #[serde(tag = "reason", rename_all = "kebab-case")] |
| pub enum LogMessage { |
| /// Emitted when a build starts. |
| BuildStarted { |
| /// The command-line arguments Cargo was invoked with. |
| command: Vec<String>, |
| /// Current working directory. |
| cwd: PathBuf, |
| /// Host triple. |
| host: String, |
| /// Number of parallel jobs. |
| jobs: u32, |
| /// Available parallelism of the compilation environment. |
| num_cpus: Option<u64>, |
| /// Build profile name (e.g., "dev", "release"). |
| profile: String, |
| /// The rustc version (`1.23.4-beta.2`). |
| rustc_version: String, |
| /// The rustc verbose version information (the output of `rustc -vV`). |
| rustc_version_verbose: String, |
| /// Target directory for build artifacts. |
| target_dir: PathBuf, |
| /// Workspace root directory. |
| workspace_root: PathBuf, |
| }, |
| /// Emitted when resolving dependencies starts. |
| ResolutionStarted { |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| }, |
| /// Emitted when resolving dependencies finishes. |
| ResolutionFinished { |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| }, |
| /// Emitted when unit graph generation starts. |
| UnitGraphStarted { |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| }, |
| /// Emitted when unit graph generation finishes. |
| UnitGraphFinished { |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| }, |
| /// Emitted when a compilation unit is registered in the unit graph, |
| /// right before [`LogMessage::UnitGraphFinished`] that Cargo finalizes |
| /// the unit graph. |
| UnitRegistered { |
| /// Package ID specification. |
| package_id: PackageIdSpec, |
| /// Cargo target (lib, bin, example, etc.). |
| target: Target, |
| /// The compilation action this unit is for (check, build, test, etc.). |
| mode: CompileMode, |
| /// The target platform this unit builds for. |
| /// |
| /// It is either a [target triple] the compiler accepts, |
| /// or a file name with the `json` extension for a [custom target]. |
| /// |
| /// [target triple]: https://doc.rust-lang.org/nightly/rustc/platform-support.html |
| /// [custom target]: https://doc.rust-lang.org/nightly/rustc/targets/custom.html |
| platform: String, |
| /// Unit index for compact reference in subsequent events. |
| index: UnitIndex, |
| /// Enabled features. |
| #[serde(default, skip_serializing_if = "Vec::is_empty")] |
| features: Vec<String>, |
| /// Whether this is requested to build by user directly, |
| /// like via the `-p` flag or the default workspace members. |
| #[serde(default, skip_serializing_if = "std::ops::Not::not")] |
| requested: bool, |
| /// Unit indices that this unit depends on. |
| #[serde(default, skip_serializing_if = "Vec::is_empty")] |
| dependencies: Vec<UnitIndex>, |
| }, |
| /// Emitted when a compilation unit starts. |
| UnitStarted { |
| /// Unit index from the associated unit-registered event. |
| index: UnitIndex, |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| }, |
| /// Emitted when a section (e.g., rmeta, link) of the compilation unit finishes. |
| UnitRmetaFinished { |
| /// Unit index from the associated unit-registered event. |
| index: UnitIndex, |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| /// Unit indices that were unblocked by this rmeta completion. |
| #[serde(default, skip_serializing_if = "Vec::is_empty")] |
| unblocked: Vec<UnitIndex>, |
| }, |
| /// Emitted when a section (e.g., rmeta, link) of the compilation unit starts. |
| /// |
| /// Requires `-Zsection-timings` to be enabled. |
| UnitSectionStarted { |
| /// Unit index from the associated unit-registered event. |
| index: UnitIndex, |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| /// Section name from rustc's `-Zjson=timings` (e.g., "codegen", "link"). |
| section: String, |
| }, |
| /// Emitted when a section (e.g., rmeta, link) of the compilation unit finishes. |
| /// |
| /// Requires `-Zsection-timings` to be enabled. |
| UnitSectionFinished { |
| /// Unit index from the associated unit-registered event. |
| index: UnitIndex, |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| /// Section name from rustc's `-Zjson=timings` (e.g., "codegen", "link"). |
| section: String, |
| }, |
| /// Emitted when a compilation unit finishes. |
| UnitFinished { |
| /// Unit index from the associated unit-registered event. |
| index: UnitIndex, |
| /// Seconds elapsed from build start. |
| elapsed: f64, |
| /// Unit indices that were unblocked by this completion. |
| #[serde(default, skip_serializing_if = "Vec::is_empty")] |
| unblocked: Vec<UnitIndex>, |
| }, |
| /// Emitted when rebuild fingerprint information is determined for a unit. |
| UnitFingerprint { |
| /// Unit index from the associated unit-registered event. |
| index: UnitIndex, |
| /// Status of the rebuild detection fingerprint of this unit |
| status: FingerprintStatus, |
| /// Reason why the unit is dirty and needs rebuilding. |
| #[serde(default, skip_serializing_if = "Option::is_none")] |
| cause: Option<DirtyReason>, |
| }, |
| } |
| |
| /// Cargo target information. |
| #[derive(Serialize, Deserialize, Clone)] |
| pub struct Target { |
| /// Target name. |
| pub name: String, |
| /// Target kind (lib, bin, test, bench, example, build-script). |
| pub kind: Cow<'static, str>, |
| } |
| |
| /// Status of the rebuild detection fingerprint. |
| #[derive(Serialize, Deserialize, Clone)] |
| #[serde(rename_all = "kebab-case")] |
| pub enum FingerprintStatus { |
| /// There is no previous fingerprints for this unit. |
| /// Might be a brand-new build. |
| New, |
| /// The current fingerprint doesn't match the previous fingerprints. |
| /// Rebuild needed. |
| Dirty, |
| /// The current fingerprint matches the previous fingerprints. |
| /// No rebuild needed. |
| Fresh, |
| } |
| |
| impl From<&crate::workspace::Target> for Target { |
| fn from(target: &crate::workspace::Target) -> Self { |
| use crate::workspace::TargetKind; |
| Self { |
| name: target.name().to_string(), |
| kind: match target.kind() { |
| TargetKind::Lib(..) => "lib", |
| TargetKind::Bin => "bin", |
| TargetKind::Test => "test", |
| TargetKind::Bench => "bench", |
| TargetKind::ExampleLib(..) | TargetKind::ExampleBin => "example", |
| TargetKind::CustomBuild => "build-script", |
| } |
| .into(), |
| } |
| } |
| } |
| |
| impl LogMessage { |
| /// Serializes this message as a JSON log line directly to the writer. |
| pub fn write_json_log<W: Write>(&self, writer: &mut W, run_id: &str) -> std::io::Result<()> { |
| #[derive(Serialize)] |
| struct LogEntry<'a> { |
| run_id: &'a str, |
| timestamp: Timestamp, |
| #[serde(flatten)] |
| msg: &'a LogMessage, |
| } |
| |
| let entry = LogEntry { |
| run_id, |
| timestamp: Timestamp::now(), |
| msg: self, |
| }; |
| |
| serde_json::to_writer(&mut *writer, &entry)?; |
| writer.write_all(b"\n")?; |
| Ok(()) |
| } |
| } |