]>
Commit | Line | Data |
---|---|---|
476ff2be SL |
1 | #![allow(dead_code)] |
2 | ||
3 | use std::env; | |
60c5eb7d | 4 | use std::ffi::{OsStr, OsString}; |
476ff2be | 5 | use std::fs::{self, File}; |
60c5eb7d | 6 | use std::io; |
476ff2be | 7 | use std::io::prelude::*; |
60c5eb7d | 8 | use std::path::{Path, PathBuf}; |
476ff2be | 9 | |
ea8adc8c | 10 | use cc; |
dfeec247 | 11 | use tempfile::{Builder, TempDir}; |
476ff2be SL |
12 | |
13 | pub struct Test { | |
14 | pub td: TempDir, | |
15 | pub gcc: PathBuf, | |
16 | pub msvc: bool, | |
17 | } | |
18 | ||
19 | pub struct Execution { | |
20 | args: Vec<String>, | |
21 | } | |
22 | ||
23 | impl Test { | |
24 | pub fn new() -> Test { | |
ba9703b0 XL |
25 | // This is ugly: `sccache` needs to introspect the compiler it is |
26 | // executing, as it adjusts its behavior depending on the | |
27 | // language/compiler. This crate's test driver uses mock compilers that | |
28 | // are obviously not supported by sccache, so the tests fail if | |
29 | // RUSTC_WRAPPER is set. rust doesn't build test dependencies with | |
30 | // the `test` feature enabled, so we can't conditionally disable the | |
31 | // usage of `sccache` if running in a test environment, at least not | |
32 | // without setting an environment variable here and testing for it | |
33 | // there. Explicitly deasserting RUSTC_WRAPPER here seems to be the | |
34 | // lesser of the two evils. | |
35 | env::remove_var("RUSTC_WRAPPER"); | |
36 | ||
476ff2be SL |
37 | let mut gcc = PathBuf::from(env::current_exe().unwrap()); |
38 | gcc.pop(); | |
39 | if gcc.ends_with("deps") { | |
40 | gcc.pop(); | |
41 | } | |
dfeec247 | 42 | let td = Builder::new().prefix("gcc-test").tempdir_in(&gcc).unwrap(); |
476ff2be SL |
43 | gcc.push(format!("gcc-shim{}", env::consts::EXE_SUFFIX)); |
44 | Test { | |
60c5eb7d | 45 | td: td, |
476ff2be SL |
46 | gcc: gcc, |
47 | msvc: false, | |
48 | } | |
49 | } | |
50 | ||
51 | pub fn gnu() -> Test { | |
52 | let t = Test::new(); | |
ea8adc8c | 53 | t.shim("cc").shim("c++").shim("ar"); |
8bb4bdeb | 54 | t |
476ff2be SL |
55 | } |
56 | ||
57 | pub fn msvc() -> Test { | |
58 | let mut t = Test::new(); | |
59 | t.shim("cl").shim("lib.exe"); | |
60 | t.msvc = true; | |
8bb4bdeb | 61 | t |
476ff2be SL |
62 | } |
63 | ||
64 | pub fn shim(&self, name: &str) -> &Test { | |
ee023bcb FG |
65 | let name = if name.ends_with(env::consts::EXE_SUFFIX) { |
66 | name.to_string() | |
67 | } else { | |
68 | format!("{}{}", name, env::consts::EXE_SUFFIX) | |
69 | }; | |
70 | link_or_copy(&self.gcc, self.td.path().join(name)).unwrap(); | |
476ff2be SL |
71 | self |
72 | } | |
73 | ||
ea8adc8c XL |
74 | pub fn gcc(&self) -> cc::Build { |
75 | let mut cfg = cc::Build::new(); | |
476ff2be SL |
76 | let target = if self.msvc { |
77 | "x86_64-pc-windows-msvc" | |
78 | } else { | |
79 | "x86_64-unknown-linux-gnu" | |
80 | }; | |
81 | ||
8bb4bdeb XL |
82 | cfg.target(target) |
83 | .host(target) | |
84 | .opt_level(2) | |
85 | .debug(false) | |
86 | .out_dir(self.td.path()) | |
60c5eb7d | 87 | .__set_env("PATH", self.path()) |
8bb4bdeb | 88 | .__set_env("GCCTEST_OUT_DIR", self.td.path()); |
476ff2be SL |
89 | if self.msvc { |
90 | cfg.compiler(self.td.path().join("cl")); | |
91 | cfg.archiver(self.td.path().join("lib.exe")); | |
92 | } | |
8bb4bdeb | 93 | cfg |
476ff2be SL |
94 | } |
95 | ||
60c5eb7d XL |
96 | fn path(&self) -> OsString { |
97 | let mut path = env::split_paths(&env::var_os("PATH").unwrap()).collect::<Vec<_>>(); | |
98 | path.insert(0, self.td.path().to_owned()); | |
99 | env::join_paths(path).unwrap() | |
100 | } | |
101 | ||
476ff2be SL |
102 | pub fn cmd(&self, i: u32) -> Execution { |
103 | let mut s = String::new(); | |
8bb4bdeb XL |
104 | File::open(self.td.path().join(format!("out{}", i))) |
105 | .unwrap() | |
106 | .read_to_string(&mut s) | |
107 | .unwrap(); | |
0531ce1d XL |
108 | Execution { |
109 | args: s.lines().map(|s| s.to_string()).collect(), | |
110 | } | |
476ff2be SL |
111 | } |
112 | } | |
113 | ||
114 | impl Execution { | |
115 | pub fn must_have<P: AsRef<OsStr>>(&self, p: P) -> &Execution { | |
116 | if !self.has(p.as_ref()) { | |
117 | panic!("didn't find {:?} in {:?}", p.as_ref(), self.args); | |
118 | } else { | |
119 | self | |
120 | } | |
121 | } | |
122 | ||
123 | pub fn must_not_have<P: AsRef<OsStr>>(&self, p: P) -> &Execution { | |
124 | if self.has(p.as_ref()) { | |
125 | panic!("found {:?}", p.as_ref()); | |
126 | } else { | |
127 | self | |
128 | } | |
129 | } | |
130 | ||
131 | pub fn has(&self, p: &OsStr) -> bool { | |
8bb4bdeb | 132 | self.args.iter().any(|arg| OsStr::new(arg) == p) |
476ff2be | 133 | } |
abe05a73 XL |
134 | |
135 | pub fn must_have_in_order(&self, before: &str, after: &str) -> &Execution { | |
48663c56 XL |
136 | let before_position = self |
137 | .args | |
0531ce1d XL |
138 | .iter() |
139 | .rposition(|x| OsStr::new(x) == OsStr::new(before)); | |
48663c56 XL |
140 | let after_position = self |
141 | .args | |
0531ce1d XL |
142 | .iter() |
143 | .rposition(|x| OsStr::new(x) == OsStr::new(after)); | |
abe05a73 | 144 | match (before_position, after_position) { |
0531ce1d XL |
145 | (Some(b), Some(a)) if b < a => {} |
146 | (b, a) => panic!( | |
147 | "{:?} (last position: {:?}) did not appear before {:?} (last position: {:?})", | |
148 | before, b, after, a | |
149 | ), | |
abe05a73 XL |
150 | }; |
151 | self | |
152 | } | |
476ff2be | 153 | } |
60c5eb7d XL |
154 | |
155 | /// Hard link an executable or copy it if that fails. | |
156 | /// | |
157 | /// We first try to hard link an executable to save space. If that fails (as on Windows with | |
158 | /// different mount points, issue #60), we copy. | |
159 | #[cfg(not(target_os = "macos"))] | |
160 | fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { | |
161 | let from = from.as_ref(); | |
162 | let to = to.as_ref(); | |
163 | fs::hard_link(from, to).or_else(|_| fs::copy(from, to).map(|_| ())) | |
164 | } | |
165 | ||
166 | /// Copy an executable. | |
167 | /// | |
168 | /// On macOS, hard linking the executable leads to strange failures (issue #419), so we just copy. | |
169 | #[cfg(target_os = "macos")] | |
170 | fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { | |
171 | fs::copy(from, to).map(|_| ()) | |
172 | } |