use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{Context, anyhow, bail};
use crate::check::dylib::prepend_dylib_path;
use crate::check::sysroot::sysroot;
use crate::config::{RUSTUP_TOOLCHAIN_ENV, SYSROOT_ENV, TOOL_VERSION};
use crate::toolchain::rustc_version;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DriverVersion {
pub tool_version: String,
pub pinned_toolchain: String,
pub built_against_rustc: String,
}
pub fn run(driver: &Path, toolchain: &str) -> anyhow::Result<PathBuf> {
let installed_rustc = rustc_version(toolchain).map_err(|e| {
anyhow!("{e}\n\nThe pinned toolchain is not installed. Run `cargo cgp setup`.")
})?;
let sysroot = sysroot("rustc", Some(toolchain))?;
let mut command = Command::new(driver);
command.env(RUSTUP_TOOLCHAIN_ENV, toolchain);
command.env(SYSROOT_ENV, &sysroot);
prepend_dylib_path(&mut command, &sysroot.join("lib"));
command.arg("--version");
let output = command.output().map_err(|e| {
anyhow!(
"failed to run the cargo-cgp-driver at {}: {e}\n\nRun `cargo cgp setup`.",
driver.display()
)
})?;
if !output.status.success() {
bail!(
"the cargo-cgp-driver could not run under toolchain `{toolchain}` \
(it was likely built against a different nightly). Run `cargo cgp setup`."
);
}
let stdout = String::from_utf8(output.stdout)
.context("`cargo-cgp-driver --version` produced non-UTF-8 output")?;
let reported = parse_driver_version(&stdout).ok_or_else(|| {
anyhow!("could not parse `cargo-cgp-driver --version` output. Run `cargo cgp setup`.")
})?;
evaluate(TOOL_VERSION, &reported, &installed_rustc)
.map_err(|reason| anyhow!("{reason}\n\nRun `cargo cgp setup`."))?;
Ok(sysroot)
}
pub fn parse_driver_version(output: &str) -> Option<DriverVersion> {
let mut lines = output.lines();
let tool_version = lines
.next()?
.strip_prefix("cargo-cgp-driver")?
.trim()
.to_owned();
if tool_version.is_empty() {
return None;
}
let mut pinned_toolchain = None;
let mut built_against_rustc = None;
for line in lines {
if let Some(value) = line.strip_prefix("pinned-toolchain:") {
pinned_toolchain = Some(value.trim().to_owned());
} else if let Some(value) = line.strip_prefix("built-against-rustc:") {
built_against_rustc = Some(value.trim().to_owned());
}
}
Some(DriverVersion {
tool_version,
pinned_toolchain: pinned_toolchain?,
built_against_rustc: built_against_rustc?,
})
}
pub fn evaluate(
frontend_version: &str,
driver: &DriverVersion,
installed_rustc: &str,
) -> Result<(), String> {
if driver.tool_version != frontend_version {
return Err(format!(
"the installed cargo-cgp-driver is version {}, but this cargo-cgp is {frontend_version} \
(the two are out of lockstep)",
driver.tool_version
));
}
if driver.built_against_rustc != installed_rustc {
return Err(format!(
"the cargo-cgp-driver was built against `{}`, but the pinned toolchain \
`{}` now provides `{installed_rustc}`",
driver.built_against_rustc, driver.pinned_toolchain
));
}
Ok(())
}