diff --git a/src/lib.rs b/src/lib.rs index 4147fe2..ee89a24 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,7 +65,7 @@ use std::fmt::Arguments; use std::fs; use std::io::{stderr, Write}; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::Stdio; #[allow(deprecated)] use std::sync::atomic::ATOMIC_USIZE_INIT; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -73,6 +73,9 @@ use std::sync::atomic::{AtomicUsize, Ordering}; mod error; pub use error::Error; +mod rustc; +use rustc::Rustc; + mod version; use version::Version; @@ -83,9 +86,7 @@ mod tests; #[derive(Clone, Debug)] pub struct AutoCfg { out_dir: PathBuf, - rustc: PathBuf, - rustc_wrapper: Option, - rustc_workspace_wrapper: Option, + rustc: Rustc, rustc_version: Version, target: Option, no_std: bool, @@ -156,9 +157,8 @@ impl AutoCfg { /// - `dir` is not a writable directory. /// pub fn with_dir>(dir: T) -> Result { - let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); - let rustc: PathBuf = rustc.into(); - let rustc_version = try!(Version::from_rustc(&rustc)); + let rustc = Rustc::new(); + let rustc_version = try!(rustc.version()); let target = env::var_os("TARGET"); @@ -171,8 +171,6 @@ impl AutoCfg { let mut ac = AutoCfg { rustflags: rustflags(&target, &dir), - rustc_wrapper: get_rustc_wrapper(false), - rustc_workspace_wrapper: get_rustc_wrapper(true), out_dir: dir, rustc: rustc, rustc_version: rustc_version, @@ -240,17 +238,7 @@ impl AutoCfg { let id = ID.fetch_add(1, Ordering::Relaxed); - // Build the command with possible wrappers. - let mut rustc = self - .rustc_wrapper - .iter() - .chain(self.rustc_workspace_wrapper.iter()) - .chain(Some(&self.rustc)); - let mut command = Command::new(rustc.next().unwrap()); - for arg in rustc { - command.arg(arg); - } - + let mut command = self.rustc.command(); command .arg("--crate-name") .arg(format!("probe{}", id)) @@ -545,27 +533,3 @@ fn rustflags(target: &Option, dir: &Path) -> Vec { Vec::new() } - -fn get_rustc_wrapper(workspace: bool) -> Option { - // We didn't really know whether the workspace wrapper is applicable until Cargo started - // deliberately setting or unsetting it in rust-lang/cargo#9601. We'll use the encoded - // rustflags as a proxy for that change for now, but we could instead check version 1.55. - if workspace && env::var_os("CARGO_ENCODED_RUSTFLAGS").is_none() { - return None; - } - - let name = if workspace { - "RUSTC_WORKSPACE_WRAPPER" - } else { - "RUSTC_WRAPPER" - }; - - if let Some(wrapper) = env::var_os(name) { - // NB: `OsStr` didn't get `len` or `is_empty` until 1.9. - if wrapper != OsString::new() { - return Some(wrapper.into()); - } - } - - None -} diff --git a/src/rustc.rs b/src/rustc.rs new file mode 100644 index 0000000..e324ed1 --- /dev/null +++ b/src/rustc.rs @@ -0,0 +1,89 @@ +use std::env; +use std::ffi::OsString; +use std::path::PathBuf; +use std::process::Command; + +use super::error::Error; +use super::version::Version; + +#[derive(Clone, Debug)] +pub struct Rustc { + rustc: PathBuf, + rustc_wrapper: Option, + rustc_workspace_wrapper: Option, +} + +impl Rustc { + pub fn new() -> Self { + Rustc { + rustc: env::var_os("RUSTC") + .unwrap_or_else(|| "rustc".into()) + .into(), + rustc_wrapper: get_rustc_wrapper(false), + rustc_workspace_wrapper: get_rustc_wrapper(true), + } + } + + /// Build the command with possible wrappers. + pub fn command(&self) -> Command { + let mut rustc = self + .rustc_wrapper + .iter() + .chain(self.rustc_workspace_wrapper.iter()) + .chain(Some(&self.rustc)); + let mut command = Command::new(rustc.next().unwrap()); + for arg in rustc { + command.arg(arg); + } + command + } + + /// Try to get the `rustc` version. + pub fn version(&self) -> Result { + // Some wrappers like clippy-driver don't pass through version commands, + // so we try to fall back to combinations without each wrapper. + macro_rules! try_version { + ($command:expr) => { + if let Ok(value) = Version::from_command($command) { + return Ok(value); + } + }; + } + + let rustc = &self.rustc; + if let Some(ref rw) = self.rustc_wrapper { + if let Some(ref rww) = self.rustc_workspace_wrapper { + try_version!(Command::new(rw).args(&[rww, rustc])); + } + try_version!(Command::new(rw).arg(rustc)); + } + if let Some(ref rww) = self.rustc_workspace_wrapper { + try_version!(Command::new(rww).arg(rustc)); + } + Version::from_command(&mut Command::new(rustc)) + } +} + +fn get_rustc_wrapper(workspace: bool) -> Option { + // We didn't really know whether the workspace wrapper is applicable until Cargo started + // deliberately setting or unsetting it in rust-lang/cargo#9601. We'll use the encoded + // rustflags as a proxy for that change for now, but we could instead check version 1.55. + if workspace && env::var_os("CARGO_ENCODED_RUSTFLAGS").is_none() { + return None; + } + + let name = if workspace { + "RUSTC_WORKSPACE_WRAPPER" + } else { + "RUSTC_WRAPPER" + }; + + if let Some(wrapper) = env::var_os(name) { + // NB: `OsStr` didn't get `len` or `is_empty` until 1.9. + if wrapper != OsString::new() { + return Some(wrapper.into()); + } + } + + None +} diff --git a/src/version.rs b/src/version.rs index 4302e09..9cf5b12 100644 --- a/src/version.rs +++ b/src/version.rs @@ -1,4 +1,3 @@ -use std::path::Path; use std::process::Command; use std::str; @@ -22,9 +21,9 @@ impl Version { } } - pub fn from_rustc(rustc: &Path) -> Result { + pub fn from_command(command: &mut Command) -> Result { // Get rustc's verbose version - let output = try!(Command::new(rustc) + let output = try!(command .args(&["--version", "--verbose"]) .output() .map_err(error::from_io)); diff --git a/tests/wrap_ignored b/tests/wrap_ignored new file mode 100755 index 0000000..5e577d0 --- /dev/null +++ b/tests/wrap_ignored @@ -0,0 +1,12 @@ +#!/bin/bash + +for arg in "$@"; do + case "$arg" in + # Add our own version so we can check that the wrapper is used for that. + "--version") echo "release: 12345.6789.0" ;; + # Read all input so the writer doesn't get EPIPE when we exit. + "-") read -d "" PROBE ;; + esac +done + +exit 0 diff --git a/tests/wrappers.rs b/tests/wrappers.rs index 560cecd..7ede12e 100644 --- a/tests/wrappers.rs +++ b/tests/wrappers.rs @@ -39,13 +39,18 @@ fn test_wrappers() { assert!(ac.probe_type("usize")); assert!(!ac.probe_type("mesize")); } + // Either way, we should have found the inner rustc version. + assert!(ac.probe_rustc_version(1, 0)); } } // Finally, make sure that `RUSTC_WRAPPER` is applied outermost // by using something that doesn't pass through at all. - env::set_var("RUSTC_WRAPPER", "/bin/true"); + env::set_var("RUSTC_WRAPPER", "./tests/wrap_ignored"); env::set_var("RUSTC_WORKSPACE_WRAPPER", "/bin/false"); let ac = autocfg::AutoCfg::new().unwrap(); assert!(ac.probe_type("mesize")); // anything goes! + + // Make sure we also got the version from that wrapper. + assert!(ac.probe_rustc_version(12345, 6789)); }