Skip to content

Commit e11a624

Browse files
committed
feat(report): cargo report timings log discovery logic
Currently look at the newest log file for the workspace. If not in a workspace, pick the newest log file in the log directory.
1 parent 4198b66 commit e11a624

File tree

5 files changed

+128
-5
lines changed

5 files changed

+128
-5
lines changed

src/bin/cargo/commands/report.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use crate::command_prelude::*;
22

3+
use cargo::CargoResult;
34
use cargo::core::compiler::future_incompat::OnDiskReports;
45
use cargo::core::compiler::future_incompat::REPORT_PREAMBLE;
56
use cargo::drop_println;
7+
use cargo::ops;
68

79
pub fn cli() -> Command {
810
subcommand("report")
@@ -43,7 +45,9 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
4345
"build-analysis",
4446
gctx.cli_unstable().build_analysis,
4547
)?;
46-
report_timings(gctx, args)
48+
let opts = timings_opts(gctx, args)?;
49+
ops::report_timings(gctx, opts)?;
50+
Ok(())
4751
}
4852
Some((cmd, _)) => {
4953
unreachable!("unexpected command {}", cmd)
@@ -67,6 +71,11 @@ fn report_future_incompatibilities(gctx: &GlobalContext, args: &ArgMatches) -> C
6771
Ok(())
6872
}
6973

70-
fn report_timings(_gctx: &GlobalContext, _args: &ArgMatches) -> CliResult {
71-
Ok(())
74+
fn timings_opts<'a>(
75+
gctx: &'a GlobalContext,
76+
args: &ArgMatches,
77+
) -> CargoResult<ops::ReportTimingsOptions<'a>> {
78+
let open_result = args.get_flag("open");
79+
80+
Ok(ops::ReportTimingsOptions { open_result, gctx })
7281
}

src/cargo/ops/cargo_report/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod timings;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! The `cargo report timings` command.
2+
3+
use std::ffi::OsStr;
4+
use std::fs::File;
5+
use std::path::PathBuf;
6+
7+
use annotate_snippets::Level;
8+
use anyhow::Context as _;
9+
10+
use crate::AlreadyPrintedError;
11+
use crate::CargoResult;
12+
use crate::GlobalContext;
13+
use crate::core::Workspace;
14+
use crate::util::BuildLogger;
15+
use crate::util::important_paths::find_root_manifest_for_wd;
16+
use crate::util::logger::RunId;
17+
18+
pub struct ReportTimingsOptions<'gctx> {
19+
/// Whether to attempt to open the browser after the report is generated
20+
pub open_result: bool,
21+
pub gctx: &'gctx GlobalContext,
22+
}
23+
24+
pub fn report_timings(gctx: &GlobalContext, _opts: ReportTimingsOptions<'_>) -> CargoResult<()> {
25+
let ws = find_root_manifest_for_wd(gctx.cwd())
26+
.ok()
27+
.and_then(|manifest_path| Workspace::new(&manifest_path, gctx).ok());
28+
let Some((log, _run_id)) = select_log_file(gctx, ws.as_ref())? else {
29+
let title_extra = if let Some(ws) = ws {
30+
format!(" for workspace at `{}`", ws.root().display())
31+
} else {
32+
String::new()
33+
};
34+
let title = format!("no build log files found{title_extra}");
35+
let note = "run command with `-Z build-analysis` to generate log files";
36+
let report = [Level::ERROR
37+
.primary_title(title)
38+
.element(Level::NOTE.message(note))];
39+
gctx.shell().print_report(&report, false)?;
40+
return Err(AlreadyPrintedError::new(anyhow::anyhow!("")).into());
41+
};
42+
43+
let _f = File::open(&log)
44+
.with_context(|| format!("failed to analyze log at `{}`", log.display()))?;
45+
46+
Ok(())
47+
}
48+
49+
/// Selects the appropriate log file.
50+
///
51+
/// Currently look at the newest log file for the workspace.
52+
/// If not in a workspace, pick the newest log file in the log directory.
53+
fn select_log_file(
54+
gctx: &GlobalContext,
55+
ws: Option<&Workspace<'_>>,
56+
) -> CargoResult<Option<(PathBuf, RunId)>> {
57+
let log_dir = gctx.home().join("log");
58+
let log_dir = log_dir.as_path_unlocked();
59+
60+
if !log_dir.exists() {
61+
return Ok(None);
62+
}
63+
64+
// Gets the latest log files in the log directory
65+
let mut walk = walkdir::WalkDir::new(log_dir)
66+
.follow_links(true)
67+
.sort_by(|a, b| a.file_name().cmp(b.file_name()).reverse())
68+
.min_depth(1)
69+
.max_depth(1)
70+
.into_iter()
71+
.filter_map(|entry| {
72+
let entry = entry.ok()?;
73+
let path = entry.path();
74+
75+
// We only accept JSONL/NDJSON files.
76+
if !entry.file_type().is_file() {
77+
return None;
78+
}
79+
if entry.path().extension() != Some(OsStr::new("jsonl")) {
80+
return None;
81+
}
82+
83+
// ...and the file name must follow RunId format
84+
let run_id = path.file_stem()?.to_str()?.parse::<RunId>().ok()?;
85+
Some((entry, run_id))
86+
});
87+
88+
let item = if let Some(ws) = ws {
89+
// If we are under a workspace, find only that workspace's log files.
90+
let ws_run_id = BuildLogger::generate_run_id(ws);
91+
walk.skip_while(|(_, run_id)| !run_id.same_workspace(&ws_run_id))
92+
.next()
93+
} else {
94+
walk.next()
95+
};
96+
97+
Ok(item.map(|(entry, run_id)| (entry.into_path(), run_id)))
98+
}

src/cargo/ops/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub use self::cargo_package::check_yanked;
1717
pub use self::cargo_package::package;
1818
pub use self::cargo_pkgid::pkgid;
1919
pub use self::cargo_read_manifest::read_package;
20+
pub use self::cargo_report::timings::ReportTimingsOptions;
21+
pub use self::cargo_report::timings::report_timings;
2022
pub use self::cargo_run::run;
2123
pub use self::cargo_test::{TestOptions, run_benches, run_tests};
2224
pub use self::cargo_uninstall::uninstall;
@@ -61,6 +63,7 @@ mod cargo_package;
6163
mod cargo_pkgid;
6264
mod cargo_read_manifest;
6365
pub mod cargo_remove;
66+
mod cargo_report;
6467
mod cargo_run;
6568
mod cargo_test;
6669
mod cargo_uninstall;

tests/testsuite/cargo_report_timings/mod.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,13 @@ See https://github.com/rust-lang/cargo/issues/15844 for more information about t
5050
fn no_log() {
5151
cargo_process("report timings -Zbuild-analysis")
5252
.masquerade_as_nightly_cargo(&["build-analysis"])
53-
.with_stderr_data(str![""])
53+
.with_status(101)
54+
.with_stderr_data(str![[r#"
55+
[ERROR] no build log files found
56+
|
57+
= [NOTE] run command with `-Z build-analysis` to generate log files
58+
59+
"#]])
5460
.run();
5561
}
5662

@@ -78,7 +84,13 @@ fn no_log_for_the_current_workspace() {
7884

7985
bar.cargo("report timings -Zbuild-analysis")
8086
.masquerade_as_nightly_cargo(&["build-analysis"])
81-
.with_stderr_data(str![""])
87+
.with_status(101)
88+
.with_stderr_data(str![[r#"
89+
[ERROR] no build log files found for workspace at `[ROOT]/bar`
90+
|
91+
= [NOTE] run command with `-Z build-analysis` to generate log files
92+
93+
"#]])
8294
.run();
8395
}
8496

0 commit comments

Comments
 (0)