1 use std::ffi::{OsStr, OsString};
3 use std::path::{Path, PathBuf};
4 use std::process::{Command, Stdio};
5 use std::time::{SystemTime, UNIX_EPOCH};
8 /// A helper macro to `unwrap` a result except also print out details like:
10 /// * The file/line of the panic
11 /// * The expression that failed
12 /// * The error itself
14 /// This is currently used judiciously throughout the build system rather than
15 /// using a `Result` with `try!`, but this may change one day...
21 Err(e) => panic!("{} failed with {}", stringify!($e), e),
24 // it can show extra info in the second parameter
25 ($e:expr, $extra:expr) => {
28 Err(e) => panic!("{} failed with {} ({:?})", stringify!($e), e, $extra),
33 /// Reads an environment variable and adds it to dependencies.
34 /// Supposed to be used for all variables except those set for build scripts by cargo
35 /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts>
36 pub fn tracked_env_var_os<K: AsRef<OsStr> + Display>(key: K) -> Option<OsString> {
37 println!("cargo:rerun-if-env-changed={}", key);
41 // Because Cargo adds the compiler's dylib path to our library search path, llvm-config may
42 // break: the dylib path for the compiler, as of this writing, contains a copy of the LLVM
43 // shared library, which means that when our freshly built llvm-config goes to load it's
44 // associated LLVM, it actually loads the compiler's LLVM. In particular when building the first
45 // compiler (i.e., in stage 0) that's a problem, as the compiler's LLVM is likely different from
46 // the one we want to use. As such, we restore the environment to what bootstrap saw. This isn't
47 // perfect -- we might actually want to see something from Cargo's added library paths -- but
49 pub fn restore_library_path() {
50 let key = tracked_env_var_os("REAL_LIBRARY_PATH_VAR").expect("REAL_LIBRARY_PATH_VAR");
51 if let Some(env) = tracked_env_var_os("REAL_LIBRARY_PATH") {
52 env::set_var(&key, &env);
54 env::remove_var(&key);
58 pub fn run(cmd: &mut Command) {
60 std::process::exit(1);
64 pub fn try_run(cmd: &mut Command) -> bool {
65 let status = match cmd.status() {
67 Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
69 if !status.success() {
71 "\n\ncommand did not execute successfully: {:?}\n\
72 expected success, got: {}\n\n",
79 pub fn run_suppressed(cmd: &mut Command) {
80 if !try_run_suppressed(cmd) {
81 std::process::exit(1);
85 pub fn try_run_suppressed(cmd: &mut Command) -> bool {
86 let output = match cmd.output() {
88 Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
90 if !output.status.success() {
92 "\n\ncommand did not execute successfully: {:?}\n\
93 expected success, got: {}\n\n\
98 String::from_utf8_lossy(&output.stdout),
99 String::from_utf8_lossy(&output.stderr)
102 output.status.success()
105 pub fn make(host: &str) -> PathBuf {
106 if host.contains("dragonfly")
107 || host.contains("freebsd")
108 || host.contains("netbsd")
109 || host.contains("openbsd")
111 PathBuf::from("gmake")
113 PathBuf::from("make")
118 pub fn output(cmd: &mut Command) -> String {
119 let output = match cmd.stderr(Stdio::inherit()).output() {
120 Ok(status) => status,
121 Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
123 if !output.status.success() {
125 "command did not execute successfully: {:?}\n\
126 expected success, got: {}",
130 String::from_utf8(output.stdout).unwrap()
133 pub fn rerun_if_changed_anything_in_dir(dir: &Path) {
138 .filter(|e| &*e.file_name() != ".git")
139 .collect::<Vec<_>>();
140 while let Some(entry) = stack.pop() {
141 let path = entry.path();
142 if entry.file_type().unwrap().is_dir() {
143 stack.extend(path.read_dir().unwrap().map(|e| e.unwrap()));
145 println!("cargo:rerun-if-changed={}", path.display());
150 /// Returns the last-modified time for `path`, or zero if it doesn't exist.
151 pub fn mtime(path: &Path) -> SystemTime {
152 fs::metadata(path).and_then(|f| f.modified()).unwrap_or(UNIX_EPOCH)
155 /// Returns `true` if `dst` is up to date given that the file or files in `src`
156 /// are used to generate it.
158 /// Uses last-modified time checks to verify this.
159 pub fn up_to_date(src: &Path, dst: &Path) -> bool {
163 let threshold = mtime(dst);
164 let meta = match fs::metadata(src) {
166 Err(e) => panic!("source {:?} failed to get metadata: {}", src, e),
169 dir_up_to_date(src, threshold)
171 meta.modified().unwrap_or(UNIX_EPOCH) <= threshold
175 fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
176 t!(fs::read_dir(src)).map(|e| t!(e)).all(|e| {
177 let meta = t!(e.metadata());
179 dir_up_to_date(&e.path(), threshold)
181 meta.modified().unwrap_or(UNIX_EPOCH) < threshold
186 fn fail(s: &str) -> ! {
187 println!("\n\n{}\n\n", s);
188 std::process::exit(1);