]> git.proxmox.com Git - rustc.git/blame - src/build_helper/lib.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / build_helper / lib.rs
CommitLineData
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
13extern crate filetime;
14
15use std::fs::File;
7453a54e 16use std::path::{Path, PathBuf};
8bb4bdeb
XL
17use std::process::{Command, Stdio};
18use std::{fs, env};
19
20use 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]
31macro_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)]
39pub enum BuildExpectation {
40 Succeeding,
41 Failing,
42 None,
43}
44
45pub fn run(cmd: &mut Command, expect: BuildExpectation) {
7453a54e 46 println!("running: {:?}", cmd);
ea8adc8c 47 run_silent(cmd, expect);
7453a54e
SL
48}
49
ea8adc8c
XL
50pub 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 56pub 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
72fn 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
105pub 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 111pub 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
131pub 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
141pub 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
151pub 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
166pub 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.
182pub 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.
192pub 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]
206pub struct NativeLibBoilerplate {
207 pub src_dir: PathBuf,
208 pub out_dir: PathBuf,
209}
210
211impl 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.
223pub 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"), &timestamp) || !up_to_date(&src_dir, &timestamp) {
244 Ok(NativeLibBoilerplate { src_dir: src_dir, out_dir: out_dir })
245 } else {
246 Err(())
247 }
248}
249
7cac9316
XL
250pub 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
268fn 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
279fn fail(s: &str) -> ! {
280 println!("\n\n{}\n\n", s);
281 std::process::exit(1);
282}