From c934cb3b36f0c610871773b5c67eeb7381467664 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 13 Apr 2016 10:38:06 -0700 Subject: [PATCH] Specially recognize ccache/distcc These are commonly at the front of `CC`, so let's try to recognize them and work appropriately. Closes #75 --- src/bin/gcc-shim.rs | 4 +- src/lib.rs | 23 +++++++-- tests/cc_env.rs | 49 +++++++++++++++++++ tests/support/mod.rs | 111 +++++++++++++++++++++++++++++++++++++++++++ tests/test.rs | 109 +----------------------------------------- 5 files changed, 184 insertions(+), 112 deletions(-) create mode 100644 tests/cc_env.rs create mode 100644 tests/support/mod.rs diff --git a/src/bin/gcc-shim.rs b/src/bin/gcc-shim.rs index 9aa1d7e..43fd811 100644 --- a/src/bin/gcc-shim.rs +++ b/src/bin/gcc-shim.rs @@ -1,7 +1,7 @@ #![cfg_attr(test, allow(dead_code))] use std::env; -use std::fs::{self, File}; +use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; @@ -9,7 +9,7 @@ fn main() { let out_dir = PathBuf::from(env::var_os("GCCTEST_OUT_DIR").unwrap()); for i in 0.. { let candidate = out_dir.join(format!("out{}", i)); - if fs::metadata(&candidate).is_ok() { + if candidate.exists() { continue } let mut f = File::create(candidate).unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 9c94700..bfce7c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -605,8 +605,12 @@ impl Config { } else { ("CC", "cl.exe", "gcc", "cc") }; - self.get_var(env).ok().map(|env| { - Tool::new(PathBuf::from(env)) + self.env_tool(env).map(|(tool, args)| { + let mut t = Tool::new(PathBuf::from(tool)); + for arg in args { + t.args.push(arg.into()); + } + return t }).or_else(|| { windows_registry::find_tool(&target, "cl.exe") }).unwrap_or_else(|| { @@ -662,7 +666,7 @@ impl Config { match res { Some(res) => Ok(res), - None => Err("Could not get environment variable".to_string()), + None => Err("could not get environment variable".to_string()), } } @@ -673,6 +677,19 @@ impl Config { .collect() } + fn env_tool(&self, name: &str) -> Option<(String, Vec)> { + self.get_var(name).ok().map(|tool| { + let whitelist = ["ccache", "distcc"]; + for t in whitelist.iter() { + if tool.starts_with(t) && tool[t.len()..].starts_with(" ") { + return (t.to_string(), + vec![tool[t.len()..].trim_left().to_string()]) + } + } + (tool, Vec::new()) + }) + } + /// Returns the default C++ standard library for the current target: `libc++` /// for OS X and `libstdc++` for anything else. fn get_cpp_link_stdlib(&self) -> Option { diff --git a/tests/cc_env.rs b/tests/cc_env.rs new file mode 100644 index 0000000..559dbe8 --- /dev/null +++ b/tests/cc_env.rs @@ -0,0 +1,49 @@ +extern crate tempdir; +extern crate gcc; + +use std::env; + +mod support; +use support::Test; + +#[test] +fn main() { + ccache(); + distcc(); + ccache_spaces(); +} + +fn ccache() { + let test = Test::gnu(); + test.shim("ccache"); + + env::set_var("CC", "ccache lol-this-is-not-a-compiler foo"); + test.gcc().file("foo.c").compile("libfoo.a"); + + test.cmd(0) + .must_have("lol-this-is-not-a-compiler foo") + .must_have("foo.c") + .must_not_have("ccache"); +} + +fn ccache_spaces() { + let test = Test::gnu(); + test.shim("ccache"); + + env::set_var("CC", "ccache lol-this-is-not-a-compiler foo"); + test.gcc().file("foo.c").compile("libfoo.a"); + test.cmd(0).must_have("lol-this-is-not-a-compiler foo"); +} + +fn distcc() { + let test = Test::gnu(); + test.shim("distcc"); + + env::set_var("CC", "distcc lol-this-is-not-a-compiler foo"); + test.gcc().file("foo.c").compile("libfoo.a"); + + test.cmd(0) + .must_have("lol-this-is-not-a-compiler foo") + .must_have("foo.c") + .must_not_have("distcc"); +} diff --git a/tests/support/mod.rs b/tests/support/mod.rs new file mode 100644 index 0000000..b5703d2 --- /dev/null +++ b/tests/support/mod.rs @@ -0,0 +1,111 @@ +#![allow(dead_code)] + +use std::env; +use std::ffi::OsStr; +use std::fs::{self, File}; +use std::io::prelude::*; +use std::path::PathBuf; + +use gcc; +use tempdir::TempDir; + +pub struct Test { + pub td: TempDir, + pub gcc: PathBuf, + pub msvc: bool, +} + +pub struct Execution { + args: Vec, +} + +impl Test { + pub fn new() -> Test { + let mut gcc = PathBuf::from(env::current_exe().unwrap()); + gcc.pop(); + gcc.push(format!("gcc-shim{}", env::consts::EXE_SUFFIX)); + Test { + td: TempDir::new("gcc-test").unwrap(), + gcc: gcc, + msvc: false, + } + } + + pub fn gnu() -> Test { + let t = Test::new(); + t.shim("cc").shim("ar"); + return t + } + + pub fn msvc() -> Test { + let mut t = Test::new(); + t.shim("cl").shim("lib.exe"); + t.msvc = true; + return t + } + + pub fn shim(&self, name: &str) -> &Test { + let fname = format!("{}{}", name, env::consts::EXE_SUFFIX); + fs::hard_link(&self.gcc, self.td.path().join(&fname)).or_else(|_| { + fs::copy(&self.gcc, self.td.path().join(&fname)).map(|_| ()) + }).unwrap(); + self + } + + pub fn gcc(&self) -> gcc::Config { + let mut cfg = gcc::Config::new(); + let mut path = env::split_paths(&env::var_os("PATH").unwrap()) + .collect::>(); + path.insert(0, self.td.path().to_owned()); + let target = if self.msvc { + "x86_64-pc-windows-msvc" + } else { + "x86_64-unknown-linux-gnu" + }; + + cfg.target(target).host(target) + .opt_level(2) + .debug(false) + .out_dir(self.td.path()) + .__set_env("PATH", env::join_paths(path).unwrap()) + .__set_env("GCCTEST_OUT_DIR", self.td.path()); + if self.msvc { + cfg.compiler(self.td.path().join("cl")); + cfg.archiver(self.td.path().join("lib.exe")); + } + return cfg + } + + pub fn cmd(&self, i: u32) -> Execution { + let mut s = String::new(); + File::open(self.td.path().join(format!("out{}", i))).unwrap() + .read_to_string(&mut s).unwrap(); + Execution { + args: s.lines().map(|s| s.to_string()).collect(), + } + } +} + +impl Execution { + pub fn must_have>(&self, p: P) -> &Execution { + if !self.has(p.as_ref()) { + panic!("didn't find {:?} in {:?}", p.as_ref(), self.args); + } else { + self + } + } + + pub fn must_not_have>(&self, p: P) -> &Execution { + if self.has(p.as_ref()) { + panic!("found {:?}", p.as_ref()); + } else { + self + } + } + + pub fn has(&self, p: &OsStr) -> bool { + self.args.iter().any(|arg| { + OsStr::new(arg) == p + }) + } +} diff --git a/tests/test.rs b/tests/test.rs index 9683ba3..b1d6c8d 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,114 +1,9 @@ extern crate gcc; extern crate tempdir; -use std::env; -use std::ffi::OsStr; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::path::PathBuf; +use support::Test; -use tempdir::TempDir; - -struct Test { - td: TempDir, - gcc: PathBuf, - msvc: bool, -} - -struct Execution { - args: Vec, -} - -impl Test { - fn new() -> Test { - let mut gcc = PathBuf::from(env::current_exe().unwrap()); - gcc.pop(); - gcc.push(format!("gcc-shim{}", env::consts::EXE_SUFFIX)); - Test { - td: TempDir::new("gcc-test").unwrap(), - gcc: gcc, - msvc: false, - } - } - - fn gnu() -> Test { - let t = Test::new(); - t.shim("cc").shim("ar"); - return t - } - - fn msvc() -> Test { - let mut t = Test::new(); - t.shim("cl").shim("lib.exe"); - t.msvc = true; - return t - } - - fn shim(&self, name: &str) -> &Test { - let fname = format!("{}{}", name, env::consts::EXE_SUFFIX); - fs::hard_link(&self.gcc, self.td.path().join(&fname)).or_else(|_| { - fs::copy(&self.gcc, self.td.path().join(&fname)).map(|_| ()) - }).unwrap(); - self - } - - fn gcc(&self) -> gcc::Config { - let mut cfg = gcc::Config::new(); - let mut path = env::split_paths(&env::var_os("PATH").unwrap()) - .collect::>(); - path.insert(0, self.td.path().to_owned()); - let target = if self.msvc { - "x86_64-pc-windows-msvc" - } else { - "x86_64-unknown-linux-gnu" - }; - - cfg.target(target).host(target) - .opt_level(2) - .debug(false) - .out_dir(self.td.path()) - .__set_env("PATH", env::join_paths(path).unwrap()) - .__set_env("GCCTEST_OUT_DIR", self.td.path()); - if self.msvc { - cfg.compiler(self.td.path().join("cl")); - cfg.archiver(self.td.path().join("lib.exe")); - } - return cfg - } - - fn cmd(&self, i: u32) -> Execution { - let mut s = String::new(); - File::open(self.td.path().join(format!("out{}", i))).unwrap() - .read_to_string(&mut s).unwrap(); - Execution { - args: s.lines().map(|s| s.to_string()).collect(), - } - } -} - -impl Execution { - fn must_have>(&self, p: P) -> &Execution { - if !self.has(p.as_ref()) { - panic!("didn't find {:?} in {:?}", p.as_ref(), self.args); - } else { - self - } - } - - fn must_not_have>(&self, p: P) -> &Execution { - if self.has(p.as_ref()) { - panic!("found {:?}", p.as_ref()); - } else { - self - } - } - - fn has(&self, p: &OsStr) -> bool { - self.args.iter().any(|arg| { - OsStr::new(arg) == p - }) - } -} +mod support; #[test] fn gnu_smoke() {