nextest_runner/
rustc_cli.rsuse crate::cargo_config::TargetTriple;
use camino::Utf8PathBuf;
use std::{borrow::Cow, path::PathBuf};
use tracing::{debug, trace};
#[derive(Clone, Debug)]
pub struct RustcCli<'a> {
rustc_path: Utf8PathBuf,
args: Vec<Cow<'a, str>>,
}
impl<'a> RustcCli<'a> {
pub fn print_host_libdir() -> Self {
let mut cli = Self::default();
cli.add_arg("--print").add_arg("target-libdir");
cli
}
pub fn print_target_libdir(triple: &'a TargetTriple) -> Self {
let mut cli = Self::default();
cli.add_arg("--print")
.add_arg("target-libdir")
.add_arg("--target")
.add_arg(triple.platform.triple_str());
cli
}
fn add_arg(&mut self, arg: impl Into<Cow<'a, str>>) -> &mut Self {
self.args.push(arg.into());
self
}
fn to_expression(&self) -> duct::Expression {
duct::cmd(
self.rustc_path.as_str(),
self.args.iter().map(|arg| arg.as_ref()),
)
}
pub fn read(&self) -> Option<Vec<u8>> {
let expression = self.to_expression();
trace!("Executing command: {:?}", expression);
let output = match expression
.stdout_capture()
.stderr_capture()
.unchecked()
.run()
{
Ok(output) => output,
Err(e) => {
debug!("Failed to spawn the child process: {}", e);
return None;
}
};
if !output.status.success() {
debug!("execution failed with {}", output.status);
debug!("stdout:");
debug!("{}", String::from_utf8_lossy(&output.stdout));
debug!("stderr:");
debug!("{}", String::from_utf8_lossy(&output.stderr));
return None;
}
Some(output.stdout)
}
}
impl Default for RustcCli<'_> {
fn default() -> Self {
Self {
rustc_path: rustc_path(),
args: vec![],
}
}
}
fn rustc_path() -> Utf8PathBuf {
match std::env::var_os("RUSTC") {
Some(rustc_path) => PathBuf::from(rustc_path)
.try_into()
.expect("RUSTC env var is not valid UTF-8"),
None => Utf8PathBuf::from("rustc"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use camino_tempfile::Utf8TempDir;
use std::env;
#[test]
fn test_should_run_rustc_version() {
let mut cli = RustcCli::default();
cli.add_arg("--version");
let output = cli.read().expect("rustc --version should run successfully");
let output = String::from_utf8(output).expect("the output should be valid utf-8");
assert!(
output.starts_with("rustc"),
"The output should start with rustc, but the actual output is: {output}"
);
}
#[test]
fn test_should_respect_rustc_env() {
env::set_var("RUSTC", "cargo");
let mut cli = RustcCli::default();
cli.add_arg("--version");
let output = cli.read().expect("cargo --version should run successfully");
let output = String::from_utf8(output).expect("the output should be valid utf-8");
assert!(
output.starts_with("cargo"),
"The output should start with cargo, but the actual output is: {output}"
);
}
#[test]
fn test_fail_to_spawn() {
let fake_dir = Utf8TempDir::new().expect("should create the temp dir successfully");
env::set_var("RUSTC", fake_dir.path());
let mut cli = RustcCli::default();
cli.add_arg("--version");
let output = cli.read();
assert_eq!(output, None);
}
#[test]
fn test_execute_with_failure() {
let mut cli = RustcCli::default();
cli.add_arg("--print");
cli.add_arg("Y7uDG1HrrY");
let output = cli.read();
assert_eq!(output, None);
}
}