diff --git a/src/lib.rs b/src/lib.rs index f519a1a..69e8b6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,8 @@ mod tests; pub struct AutoCfg { out_dir: PathBuf, rustc: PathBuf, + rustc_wrapper: Option, + rustc_workspace_wrapper: Option, rustc_version: Version, target: Option, no_std: bool, @@ -168,6 +170,8 @@ 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, @@ -234,7 +238,18 @@ impl AutoCfg { static ID: AtomicUsize = ATOMIC_USIZE_INIT; let id = ID.fetch_add(1, Ordering::Relaxed); - let mut command = Command::new(&self.rustc); + + // 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); + } + command .arg("--crate-name") .arg(format!("probe{}", id)) @@ -478,3 +493,27 @@ 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/tests/wrappers.rs b/tests/wrappers.rs new file mode 100644 index 0000000..560cecd --- /dev/null +++ b/tests/wrappers.rs @@ -0,0 +1,51 @@ +extern crate autocfg; + +use std::env; + +/// Tests that autocfg uses the RUSTC_WRAPPER and/or RUSTC_WORKSPACE_WRAPPER +/// environment variables when running rustc. +#[test] +#[cfg(unix)] // we're using system binaries as wrappers +fn test_wrappers() { + fn set(name: &str, value: Option) { + match value { + Some(true) => env::set_var(name, "/usr/bin/env"), + Some(false) => env::set_var(name, "/bin/false"), + None => env::remove_var(name), + } + } + + // Use the same path as this test binary. + let dir = env::current_exe().unwrap().parent().unwrap().to_path_buf(); + env::set_var("OUT_DIR", &format!("{}", dir.display())); + + // This is used as a heuristic to detect rust-lang/cargo#9601. + env::set_var("CARGO_ENCODED_RUSTFLAGS", ""); + + // No wrapper, a good pass-through wrapper, and a bad wrapper. + let variants = [None, Some(true), Some(false)]; + + for &workspace in &variants { + for &rustc in &variants { + set("RUSTC_WRAPPER", rustc); + set("RUSTC_WORKSPACE_WRAPPER", workspace); + + let ac = autocfg::AutoCfg::new().unwrap(); + if rustc == Some(false) || workspace == Some(false) { + // Everything should fail with bad wrappers. + assert!(!ac.probe_type("usize")); + } else { + // Try known good and bad types for the wrapped rustc. + assert!(ac.probe_type("usize")); + assert!(!ac.probe_type("mesize")); + } + } + } + + // 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_WORKSPACE_WRAPPER", "/bin/false"); + let ac = autocfg::AutoCfg::new().unwrap(); + assert!(ac.probe_type("mesize")); // anything goes! +}