bootstrap/core/build_steps/suggest.rs
1//! Attempt to magically identify good tests to run
2
3use std::path::PathBuf;
4use std::str::FromStr;
5
6use clap::Parser;
7
8use crate::core::build_steps::tool::Tool;
9use crate::core::builder::Builder;
10
11/// Suggests a list of possible `x.py` commands to run based on modified files in branch.
12pub fn suggest(builder: &Builder<'_>, run: bool) {
13 let git_config = builder.config.git_config();
14 let suggestions = builder
15 .tool_cmd(Tool::SuggestTests)
16 .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch)
17 .env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL", git_config.git_merge_commit_email)
18 .run_capture_stdout(builder)
19 .stdout();
20
21 let suggestions = suggestions
22 .lines()
23 .map(|line| {
24 let mut sections = line.split_ascii_whitespace();
25
26 // this code expects one suggestion per line in the following format:
27 // <x_subcommand> {some number of flags} [optional stage number]
28 let cmd = sections.next().unwrap();
29 let stage = sections.next_back().and_then(|s| str::parse(s).ok());
30 let paths: Vec<PathBuf> = sections.map(|p| PathBuf::from_str(p).unwrap()).collect();
31
32 (cmd, stage, paths)
33 })
34 .collect::<Vec<_>>();
35
36 if !suggestions.is_empty() {
37 println!("==== SUGGESTIONS ====");
38 for sug in &suggestions {
39 print!("x {} ", sug.0);
40 if let Some(stage) = sug.1 {
41 print!("--stage {stage} ");
42 }
43
44 for path in &sug.2 {
45 print!("{} ", path.display());
46 }
47 println!();
48 }
49 println!("=====================");
50 } else {
51 println!("No suggestions found!");
52 return;
53 }
54
55 if run {
56 for sug in suggestions {
57 let mut build: crate::Build = builder.build.clone();
58 build.config.paths = sug.2;
59 build.config.cmd = crate::core::config::flags::Flags::parse_from(["x.py", sug.0]).cmd;
60 if let Some(stage) = sug.1 {
61 build.config.stage = stage;
62 }
63 build.build();
64 }
65 } else {
66 println!("HELP: consider using the `--run` flag to automatically run suggested tests");
67 }
68}