Browse Source

Specially recognize ccache/distcc

These are commonly at the front of `CC`, so let's try to recognize them and work
appropriately.

Closes #75
add-rc-path
Alex Crichton 9 years ago
parent
commit
c934cb3b36
  1. 4
      src/bin/gcc-shim.rs
  2. 23
      src/lib.rs
  3. 49
      tests/cc_env.rs
  4. 111
      tests/support/mod.rs
  5. 109
      tests/test.rs

4
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();

23
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<String>)> {
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<String> {

49
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");
}

111
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<String>,
}
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::<Vec<_>>();
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<P: AsRef<OsStr>>(&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<P: AsRef<OsStr>>(&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
})
}
}

109
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<String>,
}
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::<Vec<_>>();
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<P: AsRef<OsStr>>(&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<P: AsRef<OsStr>>(&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() {

Loading…
Cancel
Save