]> git.lizzy.rs Git - rust.git/blob - src/build_helper/lib.rs
Rollup merge of #68072 - JohnTitor:fix-macro-ice, r=petrochenkov
[rust.git] / src / build_helper / lib.rs
1 use std::path::{Path, PathBuf};
2 use std::process::{Command, Stdio};
3 use std::time::{SystemTime, UNIX_EPOCH};
4 use std::{env, fs};
5
6 /// A helper macro to `unwrap` a result except also print out details like:
7 ///
8 /// * The file/line of the panic
9 /// * The expression that failed
10 /// * The error itself
11 ///
12 /// This is currently used judiciously throughout the build system rather than
13 /// using a `Result` with `try!`, but this may change one day...
14 #[macro_export]
15 macro_rules! t {
16     ($e:expr) => {
17         match $e {
18             Ok(e) => e,
19             Err(e) => panic!("{} failed with {}", stringify!($e), e),
20         }
21     };
22     // it can show extra info in the second parameter
23     ($e:expr, $extra:expr) => {
24         match $e {
25             Ok(e) => e,
26             Err(e) => panic!("{} failed with {} ({:?})", stringify!($e), e, $extra),
27         }
28     };
29 }
30
31 // Because Cargo adds the compiler's dylib path to our library search path, llvm-config may
32 // break: the dylib path for the compiler, as of this writing, contains a copy of the LLVM
33 // shared library, which means that when our freshly built llvm-config goes to load it's
34 // associated LLVM, it actually loads the compiler's LLVM. In particular when building the first
35 // compiler (i.e., in stage 0) that's a problem, as the compiler's LLVM is likely different from
36 // the one we want to use. As such, we restore the environment to what bootstrap saw. This isn't
37 // perfect -- we might actually want to see something from Cargo's added library paths -- but
38 // for now it works.
39 pub fn restore_library_path() {
40     println!("cargo:rerun-if-env-changed=REAL_LIBRARY_PATH_VAR");
41     println!("cargo:rerun-if-env-changed=REAL_LIBRARY_PATH");
42     let key = env::var_os("REAL_LIBRARY_PATH_VAR").expect("REAL_LIBRARY_PATH_VAR");
43     if let Some(env) = env::var_os("REAL_LIBRARY_PATH") {
44         env::set_var(&key, &env);
45     } else {
46         env::remove_var(&key);
47     }
48 }
49
50 /// Run the command, printing what we are running.
51 pub fn run_verbose(cmd: &mut Command) {
52     println!("running: {:?}", cmd);
53     run(cmd);
54 }
55
56 pub fn run(cmd: &mut Command) {
57     if !try_run(cmd) {
58         std::process::exit(1);
59     }
60 }
61
62 pub fn try_run(cmd: &mut Command) -> bool {
63     let status = match cmd.status() {
64         Ok(status) => status,
65         Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
66     };
67     if !status.success() {
68         println!(
69             "\n\ncommand did not execute successfully: {:?}\n\
70              expected success, got: {}\n\n",
71             cmd, status
72         );
73     }
74     status.success()
75 }
76
77 pub fn run_suppressed(cmd: &mut Command) {
78     if !try_run_suppressed(cmd) {
79         std::process::exit(1);
80     }
81 }
82
83 pub fn try_run_suppressed(cmd: &mut Command) -> bool {
84     let output = match cmd.output() {
85         Ok(status) => status,
86         Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
87     };
88     if !output.status.success() {
89         println!(
90             "\n\ncommand did not execute successfully: {:?}\n\
91              expected success, got: {}\n\n\
92              stdout ----\n{}\n\
93              stderr ----\n{}\n\n",
94             cmd,
95             output.status,
96             String::from_utf8_lossy(&output.stdout),
97             String::from_utf8_lossy(&output.stderr)
98         );
99     }
100     output.status.success()
101 }
102
103 pub fn gnu_target(target: &str) -> &str {
104     match target {
105         "i686-pc-windows-msvc" => "i686-pc-win32",
106         "x86_64-pc-windows-msvc" => "x86_64-pc-win32",
107         "i686-pc-windows-gnu" => "i686-w64-mingw32",
108         "x86_64-pc-windows-gnu" => "x86_64-w64-mingw32",
109         s => s,
110     }
111 }
112
113 pub fn make(host: &str) -> PathBuf {
114     if host.contains("dragonfly")
115         || host.contains("freebsd")
116         || host.contains("netbsd")
117         || host.contains("openbsd")
118     {
119         PathBuf::from("gmake")
120     } else {
121         PathBuf::from("make")
122     }
123 }
124
125 pub fn output(cmd: &mut Command) -> String {
126     let output = match cmd.stderr(Stdio::inherit()).output() {
127         Ok(status) => status,
128         Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
129     };
130     if !output.status.success() {
131         panic!(
132             "command did not execute successfully: {:?}\n\
133              expected success, got: {}",
134             cmd, output.status
135         );
136     }
137     String::from_utf8(output.stdout).unwrap()
138 }
139
140 pub fn rerun_if_changed_anything_in_dir(dir: &Path) {
141     let mut stack = dir
142         .read_dir()
143         .unwrap()
144         .map(|e| e.unwrap())
145         .filter(|e| &*e.file_name() != ".git")
146         .collect::<Vec<_>>();
147     while let Some(entry) = stack.pop() {
148         let path = entry.path();
149         if entry.file_type().unwrap().is_dir() {
150             stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
151         } else {
152             println!("cargo:rerun-if-changed={}", path.display());
153         }
154     }
155 }
156
157 /// Returns the last-modified time for `path`, or zero if it doesn't exist.
158 pub fn mtime(path: &Path) -> SystemTime {
159     fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)
160 }
161
162 /// Returns `true` if `dst` is up to date given that the file or files in `src`
163 /// are used to generate it.
164 ///
165 /// Uses last-modified time checks to verify this.
166 pub fn up_to_date(src: &Path, dst: &Path) -> bool {
167     if !dst.exists() {
168         return false;
169     }
170     let threshold = mtime(dst);
171     let meta = match fs::metadata(src) {
172         Ok(meta) => meta,
173         Err(e) => panic!("source {:?} failed to get metadata: {}", src, e),
174     };
175     if meta.is_dir() {
176         dir_up_to_date(src, threshold)
177     } else {
178         meta.modified().unwrap_or(UNIX_EPOCH) <= threshold
179     }
180 }
181
182 fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
183     t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
184         let meta = t!(e.metadata());
185         if meta.is_dir() {
186             dir_up_to_date(&e.path(), threshold)
187         } else {
188             meta.modified().unwrap_or(UNIX_EPOCH) < threshold
189         }
190     })
191 }
192
193 fn fail(s: &str) -> ! {
194     println!("\n\n{}\n\n", s);
195     std::process::exit(1);
196 }