+ use std::io::{BufRead, BufReader};
+ use std::process::{Command, Stdio};
+
+ use cargo_metadata::{PackageId, Target};
+ use serde::{Deserialize, Serialize};
+
+ // Use an alternative profile so clean-building is "sandboxed" to this tool
+ const CARGO_CUSOTM_PROFILE: [&str; 2] = ["--config", r#"profile.depwiz.inherits="dev""#];
+
+ /// See https://docs.rs/cargo/latest/cargo/core/compiler/enum.CompileMode.html
+ /// These are the only two cases we are interested in
+ #[derive(Clone, Debug, Serialize, Deserialize)]
+ #[serde(rename_all = "kebab-case")]
+ enum Mode {
+ Build,
+ RunCustomBuild,
+ }
+
+ /// See https://docs.rs/cargo/latest/cargo/util/machine_message/struct.TimingInfo.html
+ #[derive(Clone, Debug, Serialize, Deserialize)]
+ pub struct Message {
+ reason: String,
+ package_id: PackageId,
+ target: Target,
+ mode: Mode,
+ duration: f64,
+ rmeta_time: Option<f64>,
+ }
+
+ pub fn generate() -> Vec<Message> {
+ // TODO: `cargo build --timings=json` seems to only work on a clean build
+ Command::new("cargo")
+ .arg("clean")
+ .args(CARGO_CUSOTM_PROFILE)
+ .args(&["--profile", "depwiz"])
+ .stdout(Stdio::inherit())
+ .stderr(Stdio::inherit())
+ .output()
+ .unwrap();
+
+ let timings_ouput = Command::new("cargo")
+ .arg("build")
+ .args(CARGO_CUSOTM_PROFILE)
+ .args(&[
+ "--profile",
+ "depwiz",
+ "-Zunstable-options",
+ "--timings=json",
+ ])
+ .stderr(Stdio::inherit())
+ .output()
+ .unwrap();
+
+ let mut timings = Vec::new();
+ let mut timings_buffer = BufReader::new(timings_ouput.stdout.as_slice());
+ let mut message_buffer = Vec::new();
+
+ loop {
+ timings_buffer
+ .read_until(b'\n', &mut message_buffer)
+ .unwrap();
+
+ let json_message: Message = match serde_json::from_slice(&message_buffer) {
+ Ok(message) => message,
+ Err(err) => {
+ use serde_json::error::Category;
+ match err.classify() {
+ Category::Eof => break,
+ Category::Data => todo!(
+ "JSON object not properly handled, got `{err:?}` for message: `{}`",
+ String::from_utf8(message_buffer.clone()).unwrap()
+ ),
+ _ => {
+ panic!("Unexpected error while parsing: {err:?}");
+ }
+ }
+ }
+ };
+
+ // Make sure it's actually a timings message
+ assert_eq!(json_message.reason, "timing-info");
+ timings.push(json_message);
+ message_buffer.clear();
+ }
+
+ timings
+ }