• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::env;
2 use std::ffi::OsString;
3 use std::path::PathBuf;
4 use std::process::Command;
5 
6 use super::error::Error;
7 use super::version::Version;
8 
9 #[derive(Clone, Debug)]
10 pub struct Rustc {
11     rustc: PathBuf,
12     rustc_wrapper: Option<PathBuf>,
13     rustc_workspace_wrapper: Option<PathBuf>,
14 }
15 
16 impl Rustc {
new() -> Self17     pub fn new() -> Self {
18         Rustc {
19             rustc: env::var_os("RUSTC")
20                 .unwrap_or_else(|| "rustc".into())
21                 .into(),
22             rustc_wrapper: get_rustc_wrapper(false),
23             rustc_workspace_wrapper: get_rustc_wrapper(true),
24         }
25     }
26 
27     /// Build the command with possible wrappers.
command(&self) -> Command28     pub fn command(&self) -> Command {
29         let mut rustc = self
30             .rustc_wrapper
31             .iter()
32             .chain(self.rustc_workspace_wrapper.iter())
33             .chain(Some(&self.rustc));
34         let mut command = Command::new(rustc.next().unwrap());
35         for arg in rustc {
36             command.arg(arg);
37         }
38         command
39     }
40 
41     /// Try to get the `rustc` version.
version(&self) -> Result<Version, Error>42     pub fn version(&self) -> Result<Version, Error> {
43         // Some wrappers like clippy-driver don't pass through version commands,
44         // so we try to fall back to combinations without each wrapper.
45         macro_rules! try_version {
46             ($command:expr) => {
47                 if let Ok(value) = Version::from_command($command) {
48                     return Ok(value);
49                 }
50             };
51         }
52 
53         let rustc = &self.rustc;
54         if let Some(ref rw) = self.rustc_wrapper {
55             if let Some(ref rww) = self.rustc_workspace_wrapper {
56                 try_version!(Command::new(rw).args(&[rww, rustc]));
57             }
58             try_version!(Command::new(rw).arg(rustc));
59         }
60         if let Some(ref rww) = self.rustc_workspace_wrapper {
61             try_version!(Command::new(rww).arg(rustc));
62         }
63         Version::from_command(&mut Command::new(rustc))
64     }
65 }
66 
get_rustc_wrapper(workspace: bool) -> Option<PathBuf>67 fn get_rustc_wrapper(workspace: bool) -> Option<PathBuf> {
68     // We didn't really know whether the workspace wrapper is applicable until Cargo started
69     // deliberately setting or unsetting it in rust-lang/cargo#9601. We'll use the encoded
70     // rustflags as a proxy for that change for now, but we could instead check version 1.55.
71     if workspace && env::var_os("CARGO_ENCODED_RUSTFLAGS").is_none() {
72         return None;
73     }
74 
75     let name = if workspace {
76         "RUSTC_WORKSPACE_WRAPPER"
77     } else {
78         "RUSTC_WRAPPER"
79     };
80 
81     if let Some(wrapper) = env::var_os(name) {
82         // NB: `OsStr` didn't get `len` or `is_empty` until 1.9.
83         if wrapper != OsString::new() {
84             return Some(wrapper.into());
85         }
86     }
87 
88     None
89 }
90