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 { |
17 | 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. |
28 | 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. |
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 | |
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(key:"CARGO_ENCODED_RUSTFLAGS" ).is_none() { |
72 | return None; |
73 | } |
74 | |
75 | let name: &'static str = if workspace { |
76 | "RUSTC_WORKSPACE_WRAPPER" |
77 | } else { |
78 | "RUSTC_WRAPPER" |
79 | }; |
80 | |
81 | if let Some(wrapper: OsString) = env::var_os(key: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 | |