tidy/
x_version.rs

1use std::path::Path;
2use std::process::{Command, Stdio};
3
4use semver::Version;
5
6use crate::diagnostics::{CheckId, DiagCtx};
7
8pub fn check(root: &Path, cargo: &Path, diag_ctx: DiagCtx) {
9    let mut check = diag_ctx.start_check(CheckId::new("x_version").path(root));
10    let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn();
11
12    let child = match cargo_list {
13        Ok(child) => child,
14        Err(e) => {
15            check.error(format!("failed to run `cargo`: {e}"));
16            return;
17        }
18    };
19
20    let cargo_list = child.wait_with_output().unwrap();
21
22    if cargo_list.status.success() {
23        let exe_list = String::from_utf8_lossy(&cargo_list.stdout);
24        let exe_list = exe_list.lines();
25
26        let mut installed: Option<Version> = None;
27
28        for line in exe_list {
29            let mut iter = line.split_whitespace();
30            if iter.next() == Some("x") {
31                if let Some(version) = iter.next() {
32                    // Check this is the rust-lang/rust x tool installation since it should be
33                    // installed at a path containing `src/tools/x`.
34                    if let Some(path) = iter.next()
35                        && path.contains("src/tools/x")
36                    {
37                        let version = version.strip_prefix("v").unwrap();
38                        installed = Some(Version::parse(version).unwrap());
39                        break;
40                    };
41                }
42            } else {
43                continue;
44            }
45        }
46        // Unwrap the some if x is installed, otherwise return because it's fine if x isn't installed.
47        let installed = if let Some(i) = installed { i } else { return };
48
49        if let Some(expected) = get_x_wrapper_version(root, cargo) {
50            if installed < expected {
51                println!(
52                    "Current version of x is {installed}, but the latest version is {expected}\nConsider updating to the newer version of x by running `cargo install --path src/tools/x`"
53                )
54            }
55        } else {
56            check.error("Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`")
57        }
58    } else {
59        check.error(format!("failed to check version of `x`: {}", cargo_list.status))
60    }
61}
62
63// Parse latest version out of `x` Cargo.toml
64fn get_x_wrapper_version(root: &Path, cargo: &Path) -> Option<Version> {
65    let mut cmd = cargo_metadata::MetadataCommand::new();
66    cmd.cargo_path(cargo)
67        .manifest_path(root.join("src/tools/x/Cargo.toml"))
68        .no_deps()
69        .features(cargo_metadata::CargoOpt::AllFeatures);
70    let mut metadata = t!(cmd.exec());
71    metadata.packages.pop().map(|x| x.version)
72}