| //! 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 tuple. | |
| 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 tuple] the compiler accepts, | |
| /// or a file name with the `json` extension for a [custom target]. | |
| /// | |
| /// [target tuple]: 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 the peak memory usage of a compilation unit is captured. | |
| UnitPeakMemory { | |
| /// Unit index from the associated unit-registered event. | |
| index: UnitIndex, | |
| /// Peak resident set size of the unit's process, in bytes. | |
| bytes: u64, | |
| }, | |
| /// 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(()) | |
| } | |
| } |