]>
Commit | Line | Data |
---|---|---|
7453a54e SL |
1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | #![deny(warnings)] | |
12 | ||
8bb4bdeb XL |
13 | extern crate filetime; |
14 | ||
15 | use std::fs::File; | |
7453a54e | 16 | use std::path::{Path, PathBuf}; |
8bb4bdeb XL |
17 | use std::process::{Command, Stdio}; |
18 | use std::{fs, env}; | |
19 | ||
20 | use filetime::FileTime; | |
21 | ||
22 | /// A helper macro to `unwrap` a result except also print out details like: | |
23 | /// | |
24 | /// * The file/line of the panic | |
25 | /// * The expression that failed | |
26 | /// * The error itself | |
27 | /// | |
28 | /// This is currently used judiciously throughout the build system rather than | |
29 | /// using a `Result` with `try!`, but this may change one day... | |
30 | #[macro_export] | |
31 | macro_rules! t { | |
32 | ($e:expr) => (match $e { | |
33 | Ok(e) => e, | |
34 | Err(e) => panic!("{} failed with {}", stringify!($e), e), | |
35 | }) | |
36 | } | |
7453a54e | 37 | |
ea8adc8c XL |
38 | #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] |
39 | pub enum BuildExpectation { | |
40 | Succeeding, | |
41 | Failing, | |
42 | None, | |
43 | } | |
44 | ||
45 | pub fn run(cmd: &mut Command, expect: BuildExpectation) { | |
7453a54e | 46 | println!("running: {:?}", cmd); |
ea8adc8c | 47 | run_silent(cmd, expect); |
7453a54e SL |
48 | } |
49 | ||
ea8adc8c XL |
50 | pub fn run_silent(cmd: &mut Command, expect: BuildExpectation) { |
51 | if !try_run_silent(cmd, expect) { | |
7cac9316 XL |
52 | std::process::exit(1); |
53 | } | |
54 | } | |
55 | ||
ea8adc8c | 56 | pub fn try_run_silent(cmd: &mut Command, expect: BuildExpectation) -> bool { |
7453a54e SL |
57 | let status = match cmd.status() { |
58 | Ok(status) => status, | |
476ff2be SL |
59 | Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", |
60 | cmd, e)), | |
7453a54e | 61 | }; |
ea8adc8c XL |
62 | process_status( |
63 | cmd, | |
64 | status.success(), | |
65 | expect, | |
66 | || println!("\n\ncommand did not execute successfully: {:?}\n\ | |
67 | expected success, got: {}\n\n", | |
68 | cmd, | |
69 | status)) | |
70 | } | |
71 | ||
72 | fn process_status<F: FnOnce()>( | |
73 | cmd: &Command, | |
74 | success: bool, | |
75 | expect: BuildExpectation, | |
76 | f: F, | |
77 | ) -> bool { | |
78 | use BuildExpectation::*; | |
79 | match (expect, success) { | |
80 | (None, false) => { f(); false }, | |
81 | // Non-tool build succeeds, everything is good | |
82 | (None, true) => true, | |
83 | // Tool expected to work and is working | |
84 | (Succeeding, true) => true, | |
85 | // Tool expected to fail and is failing | |
86 | (Failing, false) => { | |
87 | println!("This failure is expected (see `src/tools/toolstate.toml`)"); | |
88 | true | |
89 | }, | |
90 | // Tool expected to work, but is failing | |
91 | (Succeeding, false) => { | |
92 | f(); | |
93 | println!("You can disable the tool in `src/tools/toolstate.toml`"); | |
94 | false | |
95 | }, | |
96 | // Tool expected to fail, but is working | |
97 | (Failing, true) => { | |
98 | println!("Expected `{:?}` to fail, but it succeeded.\n\ | |
99 | Please adjust `src/tools/toolstate.toml` accordingly", cmd); | |
100 | false | |
101 | } | |
7453a54e SL |
102 | } |
103 | } | |
104 | ||
ea8adc8c XL |
105 | pub fn run_suppressed(cmd: &mut Command, expect: BuildExpectation) { |
106 | if !try_run_suppressed(cmd, expect) { | |
7cac9316 XL |
107 | std::process::exit(1); |
108 | } | |
109 | } | |
110 | ||
ea8adc8c | 111 | pub fn try_run_suppressed(cmd: &mut Command, expect: BuildExpectation) -> bool { |
8bb4bdeb XL |
112 | let output = match cmd.output() { |
113 | Ok(status) => status, | |
114 | Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", | |
115 | cmd, e)), | |
116 | }; | |
ea8adc8c XL |
117 | process_status( |
118 | cmd, | |
119 | output.status.success(), | |
120 | expect, | |
121 | || println!("\n\ncommand did not execute successfully: {:?}\n\ | |
7cac9316 XL |
122 | expected success, got: {}\n\n\ |
123 | stdout ----\n{}\n\ | |
124 | stderr ----\n{}\n\n", | |
125 | cmd, | |
126 | output.status, | |
127 | String::from_utf8_lossy(&output.stdout), | |
ea8adc8c | 128 | String::from_utf8_lossy(&output.stderr))) |
8bb4bdeb XL |
129 | } |
130 | ||
7453a54e SL |
131 | pub fn gnu_target(target: &str) -> String { |
132 | match target { | |
133 | "i686-pc-windows-msvc" => "i686-pc-win32".to_string(), | |
134 | "x86_64-pc-windows-msvc" => "x86_64-pc-win32".to_string(), | |
135 | "i686-pc-windows-gnu" => "i686-w64-mingw32".to_string(), | |
136 | "x86_64-pc-windows-gnu" => "x86_64-w64-mingw32".to_string(), | |
137 | s => s.to_string(), | |
138 | } | |
139 | } | |
140 | ||
476ff2be SL |
141 | pub fn make(host: &str) -> PathBuf { |
142 | if host.contains("bitrig") || host.contains("dragonfly") || | |
143 | host.contains("freebsd") || host.contains("netbsd") || | |
144 | host.contains("openbsd") { | |
145 | PathBuf::from("gmake") | |
146 | } else { | |
147 | PathBuf::from("make") | |
148 | } | |
149 | } | |
150 | ||
7453a54e SL |
151 | pub fn output(cmd: &mut Command) -> String { |
152 | let output = match cmd.stderr(Stdio::inherit()).output() { | |
153 | Ok(status) => status, | |
476ff2be SL |
154 | Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", |
155 | cmd, e)), | |
7453a54e SL |
156 | }; |
157 | if !output.status.success() { | |
158 | panic!("command did not execute successfully: {:?}\n\ | |
c30ab7b3 SL |
159 | expected success, got: {}", |
160 | cmd, | |
161 | output.status); | |
7453a54e SL |
162 | } |
163 | String::from_utf8(output.stdout).unwrap() | |
164 | } | |
165 | ||
8bb4bdeb XL |
166 | pub fn rerun_if_changed_anything_in_dir(dir: &Path) { |
167 | let mut stack = dir.read_dir().unwrap() | |
168 | .map(|e| e.unwrap()) | |
169 | .filter(|e| &*e.file_name() != ".git") | |
170 | .collect::<Vec<_>>(); | |
171 | while let Some(entry) = stack.pop() { | |
172 | let path = entry.path(); | |
173 | if entry.file_type().unwrap().is_dir() { | |
174 | stack.extend(path.read_dir().unwrap().map(|e| e.unwrap())); | |
175 | } else { | |
176 | println!("cargo:rerun-if-changed={}", path.display()); | |
177 | } | |
178 | } | |
179 | } | |
180 | ||
181 | /// Returns the last-modified time for `path`, or zero if it doesn't exist. | |
182 | pub fn mtime(path: &Path) -> FileTime { | |
183 | fs::metadata(path).map(|f| { | |
184 | FileTime::from_last_modification_time(&f) | |
185 | }).unwrap_or(FileTime::zero()) | |
186 | } | |
187 | ||
188 | /// Returns whether `dst` is up to date given that the file or files in `src` | |
189 | /// are used to generate it. | |
190 | /// | |
191 | /// Uses last-modified time checks to verify this. | |
192 | pub fn up_to_date(src: &Path, dst: &Path) -> bool { | |
193 | let threshold = mtime(dst); | |
194 | let meta = match fs::metadata(src) { | |
195 | Ok(meta) => meta, | |
196 | Err(e) => panic!("source {:?} failed to get metadata: {}", src, e), | |
197 | }; | |
198 | if meta.is_dir() { | |
199 | dir_up_to_date(src, &threshold) | |
200 | } else { | |
201 | FileTime::from_last_modification_time(&meta) <= threshold | |
202 | } | |
203 | } | |
204 | ||
205 | #[must_use] | |
206 | pub struct NativeLibBoilerplate { | |
207 | pub src_dir: PathBuf, | |
208 | pub out_dir: PathBuf, | |
209 | } | |
210 | ||
211 | impl Drop for NativeLibBoilerplate { | |
212 | fn drop(&mut self) { | |
213 | t!(File::create(self.out_dir.join("rustbuild.timestamp"))); | |
214 | } | |
215 | } | |
216 | ||
217 | // Perform standard preparations for native libraries that are build only once for all stages. | |
218 | // Emit rerun-if-changed and linking attributes for Cargo, check if any source files are | |
219 | // updated, calculate paths used later in actual build with CMake/make or C/C++ compiler. | |
220 | // If Err is returned, then everything is up-to-date and further build actions can be skipped. | |
221 | // Timestamps are created automatically when the result of `native_lib_boilerplate` goes out | |
222 | // of scope, so all the build actions should be completed until then. | |
223 | pub fn native_lib_boilerplate(src_name: &str, | |
224 | out_name: &str, | |
225 | link_name: &str, | |
226 | search_subdir: &str) | |
227 | -> Result<NativeLibBoilerplate, ()> { | |
228 | let current_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); | |
229 | let src_dir = current_dir.join("..").join(src_name); | |
230 | rerun_if_changed_anything_in_dir(&src_dir); | |
231 | ||
232 | let out_dir = env::var_os("RUSTBUILD_NATIVE_DIR").unwrap_or(env::var_os("OUT_DIR").unwrap()); | |
233 | let out_dir = PathBuf::from(out_dir).join(out_name); | |
3b2f2976 | 234 | t!(fs::create_dir_all(&out_dir)); |
7cac9316 XL |
235 | if link_name.contains('=') { |
236 | println!("cargo:rustc-link-lib={}", link_name); | |
237 | } else { | |
238 | println!("cargo:rustc-link-lib=static={}", link_name); | |
239 | } | |
8bb4bdeb XL |
240 | println!("cargo:rustc-link-search=native={}", out_dir.join(search_subdir).display()); |
241 | ||
242 | let timestamp = out_dir.join("rustbuild.timestamp"); | |
243 | if !up_to_date(Path::new("build.rs"), ×tamp) || !up_to_date(&src_dir, ×tamp) { | |
244 | Ok(NativeLibBoilerplate { src_dir: src_dir, out_dir: out_dir }) | |
245 | } else { | |
246 | Err(()) | |
247 | } | |
248 | } | |
249 | ||
7cac9316 XL |
250 | pub fn sanitizer_lib_boilerplate(sanitizer_name: &str) -> Result<NativeLibBoilerplate, ()> { |
251 | let (link_name, search_path) = match &*env::var("TARGET").unwrap() { | |
252 | "x86_64-unknown-linux-gnu" => ( | |
253 | format!("clang_rt.{}-x86_64", sanitizer_name), | |
254 | "build/lib/linux", | |
255 | ), | |
256 | "x86_64-apple-darwin" => ( | |
257 | format!("dylib=clang_rt.{}_osx_dynamic", sanitizer_name), | |
258 | "build/lib/darwin", | |
259 | ), | |
260 | _ => return Err(()), | |
261 | }; | |
041b39d2 XL |
262 | native_lib_boilerplate("libcompiler_builtins/compiler-rt", |
263 | sanitizer_name, | |
264 | &link_name, | |
265 | search_path) | |
7cac9316 XL |
266 | } |
267 | ||
8bb4bdeb XL |
268 | fn dir_up_to_date(src: &Path, threshold: &FileTime) -> bool { |
269 | t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| { | |
270 | let meta = t!(e.metadata()); | |
271 | if meta.is_dir() { | |
272 | dir_up_to_date(&e.path(), threshold) | |
273 | } else { | |
274 | FileTime::from_last_modification_time(&meta) < *threshold | |
275 | } | |
276 | }) | |
277 | } | |
278 | ||
7453a54e SL |
279 | fn fail(s: &str) -> ! { |
280 | println!("\n\n{}\n\n", s); | |
281 | std::process::exit(1); | |
282 | } |