]>
Commit | Line | Data |
---|---|---|
54a0048b SL |
1 | //! Shim which is passed to Cargo as "rustc" when running the bootstrap. |
2 | //! | |
3 | //! This shim will take care of some various tasks that our build process | |
4 | //! requires that Cargo can't quite do through normal configuration: | |
5 | //! | |
6 | //! 1. When compiling build scripts and build dependencies, we need a guaranteed | |
7 | //! full standard library available. The only compiler which actually has | |
8 | //! this is the snapshot, so we detect this situation and always compile with | |
9 | //! the snapshot compiler. | |
10 | //! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling | |
11 | //! (and this slightly differs based on a whether we're using a snapshot or | |
12 | //! not), so we do that all here. | |
13 | //! | |
14 | //! This may one day be replaced by RUSTFLAGS, but the dynamic nature of | |
15 | //! switching compilers for the bootstrap and for build scripts will probably | |
16 | //! never get replaced. | |
7453a54e | 17 | |
7453a54e | 18 | use std::env; |
7453a54e | 19 | use std::path::PathBuf; |
6a06907d | 20 | use std::process::{Child, Command}; |
0531ce1d XL |
21 | use std::str::FromStr; |
22 | use std::time::Instant; | |
7453a54e SL |
23 | |
24 | fn main() { | |
e1599b0c | 25 | let args = env::args_os().skip(1).collect::<Vec<_>>(); |
0531ce1d | 26 | |
7453a54e SL |
27 | // Detect whether or not we're a build script depending on whether --target |
28 | // is passed (a bit janky...) | |
dfeec247 | 29 | let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str()); |
9e0c209e | 30 | let version = args.iter().find(|w| &**w == "-vV"); |
7453a54e | 31 | |
32a655c1 SL |
32 | let verbose = match env::var("RUSTC_VERBOSE") { |
33 | Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"), | |
34 | Err(_) => 0, | |
35 | }; | |
36 | ||
041b39d2 XL |
37 | // Use a different compiler for build scripts, since there may not yet be a |
38 | // libstd for the real compiler to use. However, if Cargo is attempting to | |
39 | // determine the version of the compiler, the real compiler needs to be | |
40 | // used. Currently, these two states are differentiated based on whether | |
41 | // --target and -vV is/isn't passed. | |
9e0c209e | 42 | let (rustc, libdir) = if target.is_none() && version.is_none() { |
a7813a04 | 43 | ("RUSTC_SNAPSHOT", "RUSTC_SNAPSHOT_LIBDIR") |
7453a54e | 44 | } else { |
a7813a04 | 45 | ("RUSTC_REAL", "RUSTC_LIBDIR") |
7453a54e | 46 | }; |
9e0c209e | 47 | let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set"); |
32a655c1 | 48 | let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set"); |
74b04a01 | 49 | let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new); |
a7813a04 | 50 | |
9e0c209e SL |
51 | let rustc = env::var_os(rustc).unwrap_or_else(|| panic!("{:?} was not set", rustc)); |
52 | let libdir = env::var_os(libdir).unwrap_or_else(|| panic!("{:?} was not set", libdir)); | |
5bcae85e | 53 | let mut dylib_path = bootstrap::util::dylib_path(); |
0531ce1d | 54 | dylib_path.insert(0, PathBuf::from(&libdir)); |
7453a54e SL |
55 | |
56 | let mut cmd = Command::new(rustc); | |
dfeec247 | 57 | cmd.args(&args).env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap()); |
7453a54e | 58 | |
dc9dc135 | 59 | // Get the name of the crate we're compiling, if any. |
dfeec247 XL |
60 | let crate_name = |
61 | args.windows(2).find(|args| args[0] == "--crate-name").and_then(|args| args[1].to_str()); | |
dc9dc135 | 62 | |
416331ca | 63 | if let Some(crate_name) = crate_name { |
dc9dc135 | 64 | if let Some(target) = env::var_os("RUSTC_TIME") { |
dfeec247 | 65 | if target == "all" |
74b04a01 | 66 | || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name) |
dc9dc135 XL |
67 | { |
68 | cmd.arg("-Ztime"); | |
69 | } | |
70 | } | |
71 | } | |
72 | ||
83c7162d XL |
73 | // Print backtrace in case of ICE |
74 | if env::var("RUSTC_BACKTRACE_ON_ICE").is_ok() && env::var("RUST_BACKTRACE").is_err() { | |
75 | cmd.env("RUST_BACKTRACE", "1"); | |
76 | } | |
77 | ||
f035d41b XL |
78 | if let Ok(lint_flags) = env::var("RUSTC_LINT_FLAGS") { |
79 | cmd.args(lint_flags.split_whitespace()); | |
80 | } | |
81 | ||
e1599b0c | 82 | if target.is_some() { |
a7813a04 | 83 | // The stage0 compiler has a special sysroot distinct from what we |
416331ca XL |
84 | // actually downloaded, so we just always pass the `--sysroot` option, |
85 | // unless one is already set. | |
86 | if !args.iter().any(|arg| arg == "--sysroot") { | |
87 | cmd.arg("--sysroot").arg(&sysroot); | |
88 | } | |
7453a54e | 89 | |
a7813a04 XL |
90 | // If we're compiling specifically the `panic_abort` crate then we pass |
91 | // the `-C panic=abort` option. Note that we do not do this for any | |
92 | // other crate intentionally as this is the only crate for now that we | |
93 | // ship with panic=abort. | |
94 | // | |
95 | // This... is a bit of a hack how we detect this. Ideally this | |
96 | // information should be encoded in the crate I guess? Would likely | |
97 | // require an RFC amendment to RFC 1513, however. | |
041b39d2 XL |
98 | // |
99 | // `compiler_builtins` are unconditionally compiled with panic=abort to | |
100 | // workaround undefined references to `rust_eh_unwind_resume` generated | |
101 | // otherwise, see issue https://github.com/rust-lang/rust/issues/43095. | |
dfeec247 XL |
102 | if crate_name == Some("panic_abort") |
103 | || crate_name == Some("compiler_builtins") && stage != "0" | |
104 | { | |
a7813a04 XL |
105 | cmd.arg("-C").arg("panic=abort"); |
106 | } | |
abe05a73 | 107 | } else { |
e1599b0c XL |
108 | // FIXME(rust-lang/cargo#5754) we shouldn't be using special env vars |
109 | // here, but rather Cargo should know what flags to pass rustc itself. | |
110 | ||
abe05a73 XL |
111 | // Override linker if necessary. |
112 | if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") { | |
113 | cmd.arg(format!("-Clinker={}", host_linker)); | |
114 | } | |
1b1a35ee XL |
115 | if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() { |
116 | cmd.arg("-Clink-args=-fuse-ld=lld"); | |
117 | } | |
94b46f34 XL |
118 | |
119 | if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") { | |
120 | if s == "true" { | |
121 | cmd.arg("-C").arg("target-feature=+crt-static"); | |
122 | } | |
123 | if s == "false" { | |
124 | cmd.arg("-C").arg("target-feature=-crt-static"); | |
125 | } | |
126 | } | |
7453a54e SL |
127 | } |
128 | ||
e1599b0c XL |
129 | if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") { |
130 | cmd.arg("--remap-path-prefix").arg(&map); | |
131 | } | |
132 | ||
532ac7d7 XL |
133 | // Force all crates compiled by this compiler to (a) be unstable and (b) |
134 | // allow the `rustc_private` feature to link to other unstable crates | |
135 | // also in the sysroot. We also do this for host crates, since those | |
136 | // may be proc macros, in which case we might ship them. | |
137 | if env::var_os("RUSTC_FORCE_UNSTABLE").is_some() && (stage != "0" || target.is_some()) { | |
138 | cmd.arg("-Z").arg("force-unstable-if-unmarked"); | |
139 | } | |
140 | ||
6a06907d | 141 | let is_test = args.iter().any(|a| a == "--test"); |
32a655c1 | 142 | if verbose > 1 { |
6a06907d XL |
143 | let rust_env_vars = |
144 | env::vars().filter(|(k, _)| k.starts_with("RUST") || k.starts_with("CARGO")); | |
145 | let prefix = if is_test { "[RUSTC-SHIM] rustc --test" } else { "[RUSTC-SHIM] rustc" }; | |
146 | let prefix = match crate_name { | |
147 | Some(crate_name) => format!("{} {}", prefix, crate_name), | |
148 | None => prefix.to_string(), | |
149 | }; | |
150 | for (i, (k, v)) in rust_env_vars.enumerate() { | |
151 | eprintln!("{} env[{}]: {:?}={:?}", prefix, i, k, v); | |
152 | } | |
153 | eprintln!("{} working directory: {}", prefix, env::current_dir().unwrap().display()); | |
94b46f34 | 154 | eprintln!( |
6a06907d XL |
155 | "{} command: {:?}={:?} {:?}", |
156 | prefix, | |
94b46f34 XL |
157 | bootstrap::util::dylib_path_var(), |
158 | env::join_paths(&dylib_path).unwrap(), | |
159 | cmd, | |
160 | ); | |
6a06907d XL |
161 | eprintln!("{} sysroot: {:?}", prefix, sysroot); |
162 | eprintln!("{} libdir: {:?}", prefix, libdir); | |
0531ce1d XL |
163 | } |
164 | ||
3dfed10e | 165 | let start = Instant::now(); |
6a06907d | 166 | let (child, status) = { |
3dfed10e | 167 | let errmsg = format!("\nFailed to run:\n{:?}\n-------------", cmd); |
6a06907d XL |
168 | let mut child = cmd.spawn().expect(&errmsg); |
169 | let status = child.wait().expect(&errmsg); | |
170 | (child, status) | |
3dfed10e | 171 | }; |
32a655c1 | 172 | |
6a06907d XL |
173 | if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() |
174 | || env::var_os("RUSTC_PRINT_STEP_RUSAGE").is_some() | |
175 | { | |
416331ca | 176 | if let Some(crate_name) = crate_name { |
0531ce1d | 177 | let dur = start.elapsed(); |
6a06907d XL |
178 | // If the user requested resource usage data, then |
179 | // include that in addition to the timing output. | |
180 | let rusage_data = | |
181 | env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| format_rusage_data(child)); | |
dfeec247 | 182 | eprintln!( |
6a06907d | 183 | "[RUSTC-TIMING] {} test:{} {}.{:03}{}{}", |
dfeec247 XL |
184 | crate_name, |
185 | is_test, | |
186 | dur.as_secs(), | |
6a06907d XL |
187 | dur.subsec_millis(), |
188 | if rusage_data.is_some() { " " } else { "" }, | |
189 | rusage_data.unwrap_or(String::new()), | |
dfeec247 | 190 | ); |
8bb4bdeb | 191 | } |
0531ce1d XL |
192 | } |
193 | ||
3dfed10e XL |
194 | if status.success() { |
195 | std::process::exit(0); | |
196 | // note: everything below here is unreachable. do not put code that | |
197 | // should run on success, after this block. | |
198 | } | |
199 | if verbose > 0 { | |
200 | println!("\nDid not run successfully: {}\n{:?}\n-------------", status, cmd); | |
201 | } | |
476ff2be | 202 | |
3dfed10e XL |
203 | if let Some(mut on_fail) = on_fail { |
204 | on_fail.status().expect("Could not run the on_fail command"); | |
205 | } | |
476ff2be | 206 | |
3dfed10e XL |
207 | // Preserve the exit code. In case of signal, exit with 0xfe since it's |
208 | // awkward to preserve this status in a cross-platform way. | |
209 | match status.code() { | |
210 | Some(i) => std::process::exit(i), | |
211 | None => { | |
212 | eprintln!("rustc exited with {}", status); | |
213 | std::process::exit(0xfe); | |
214 | } | |
215 | } | |
476ff2be | 216 | } |
6a06907d XL |
217 | |
218 | #[cfg(all(not(unix), not(windows)))] | |
219 | // In the future we can add this for more platforms | |
220 | fn format_rusage_data(_child: Child) -> Option<String> { | |
221 | None | |
222 | } | |
223 | ||
224 | #[cfg(windows)] | |
225 | fn format_rusage_data(child: Child) -> Option<String> { | |
226 | use std::os::windows::io::AsRawHandle; | |
227 | use winapi::um::{processthreadsapi, psapi, timezoneapi}; | |
228 | let handle = child.as_raw_handle(); | |
229 | macro_rules! try_bool { | |
230 | ($e:expr) => { | |
231 | if $e != 1 { | |
232 | return None; | |
233 | } | |
234 | }; | |
235 | } | |
236 | ||
237 | let mut user_filetime = Default::default(); | |
238 | let mut user_time = Default::default(); | |
239 | let mut kernel_filetime = Default::default(); | |
240 | let mut kernel_time = Default::default(); | |
241 | let mut memory_counters = psapi::PROCESS_MEMORY_COUNTERS::default(); | |
242 | ||
243 | unsafe { | |
244 | try_bool!(processthreadsapi::GetProcessTimes( | |
245 | handle, | |
246 | &mut Default::default(), | |
247 | &mut Default::default(), | |
248 | &mut kernel_filetime, | |
249 | &mut user_filetime, | |
250 | )); | |
251 | try_bool!(timezoneapi::FileTimeToSystemTime(&user_filetime, &mut user_time)); | |
252 | try_bool!(timezoneapi::FileTimeToSystemTime(&kernel_filetime, &mut kernel_time)); | |
253 | ||
254 | // Unlike on Linux with RUSAGE_CHILDREN, this will only return memory information for the process | |
255 | // with the given handle and none of that process's children. | |
256 | try_bool!(psapi::GetProcessMemoryInfo( | |
257 | handle as _, | |
258 | &mut memory_counters as *mut _ as _, | |
259 | std::mem::size_of::<psapi::PROCESS_MEMORY_COUNTERS_EX>() as u32, | |
260 | )); | |
261 | } | |
262 | ||
263 | // Guide on interpreting these numbers: | |
264 | // https://docs.microsoft.com/en-us/windows/win32/psapi/process-memory-usage-information | |
265 | let peak_working_set = memory_counters.PeakWorkingSetSize / 1024; | |
266 | let peak_page_file = memory_counters.PeakPagefileUsage / 1024; | |
267 | let peak_paged_pool = memory_counters.QuotaPeakPagedPoolUsage / 1024; | |
268 | let peak_nonpaged_pool = memory_counters.QuotaPeakNonPagedPoolUsage / 1024; | |
269 | Some(format!( | |
270 | "user: {USER_SEC}.{USER_USEC:03} \ | |
271 | sys: {SYS_SEC}.{SYS_USEC:03} \ | |
272 | peak working set (kb): {PEAK_WORKING_SET} \ | |
273 | peak page file usage (kb): {PEAK_PAGE_FILE} \ | |
274 | peak paged pool usage (kb): {PEAK_PAGED_POOL} \ | |
275 | peak non-paged pool usage (kb): {PEAK_NONPAGED_POOL} \ | |
276 | page faults: {PAGE_FAULTS}", | |
277 | USER_SEC = user_time.wSecond + (user_time.wMinute * 60), | |
278 | USER_USEC = user_time.wMilliseconds, | |
279 | SYS_SEC = kernel_time.wSecond + (kernel_time.wMinute * 60), | |
280 | SYS_USEC = kernel_time.wMilliseconds, | |
281 | PEAK_WORKING_SET = peak_working_set, | |
282 | PEAK_PAGE_FILE = peak_page_file, | |
283 | PEAK_PAGED_POOL = peak_paged_pool, | |
284 | PEAK_NONPAGED_POOL = peak_nonpaged_pool, | |
285 | PAGE_FAULTS = memory_counters.PageFaultCount, | |
286 | )) | |
287 | } | |
288 | ||
289 | #[cfg(unix)] | |
290 | /// Tries to build a string with human readable data for several of the rusage | |
291 | /// fields. Note that we are focusing mainly on data that we believe to be | |
292 | /// supplied on Linux (the `rusage` struct has other fields in it but they are | |
293 | /// currently unsupported by Linux). | |
294 | fn format_rusage_data(_child: Child) -> Option<String> { | |
295 | let rusage: libc::rusage = unsafe { | |
296 | let mut recv = std::mem::zeroed(); | |
297 | // -1 is RUSAGE_CHILDREN, which means to get the rusage for all children | |
298 | // (and grandchildren, etc) processes that have respectively terminated | |
299 | // and been waited for. | |
300 | let retval = libc::getrusage(-1, &mut recv); | |
301 | if retval != 0 { | |
302 | return None; | |
303 | } | |
304 | recv | |
305 | }; | |
306 | // Mac OS X reports the maxrss in bytes, not kb. | |
307 | let divisor = if env::consts::OS == "macos" { 1024 } else { 1 }; | |
308 | let maxrss = rusage.ru_maxrss + (divisor - 1) / divisor; | |
309 | ||
310 | let mut init_str = format!( | |
311 | "user: {USER_SEC}.{USER_USEC:03} \ | |
312 | sys: {SYS_SEC}.{SYS_USEC:03} \ | |
313 | max rss (kb): {MAXRSS}", | |
314 | USER_SEC = rusage.ru_utime.tv_sec, | |
315 | USER_USEC = rusage.ru_utime.tv_usec, | |
316 | SYS_SEC = rusage.ru_stime.tv_sec, | |
317 | SYS_USEC = rusage.ru_stime.tv_usec, | |
318 | MAXRSS = maxrss | |
319 | ); | |
320 | ||
321 | // The remaining rusage stats vary in platform support. So we treat | |
322 | // uniformly zero values in each category as "not worth printing", since it | |
323 | // either means no events of that type occurred, or that the platform | |
324 | // does not support it. | |
325 | ||
326 | let minflt = rusage.ru_minflt; | |
327 | let majflt = rusage.ru_majflt; | |
328 | if minflt != 0 || majflt != 0 { | |
329 | init_str.push_str(&format!(" page reclaims: {} page faults: {}", minflt, majflt)); | |
330 | } | |
331 | ||
332 | let inblock = rusage.ru_inblock; | |
333 | let oublock = rusage.ru_oublock; | |
334 | if inblock != 0 || oublock != 0 { | |
335 | init_str.push_str(&format!(" fs block inputs: {} fs block outputs: {}", inblock, oublock)); | |
336 | } | |
337 | ||
338 | let nvcsw = rusage.ru_nvcsw; | |
339 | let nivcsw = rusage.ru_nivcsw; | |
340 | if nvcsw != 0 || nivcsw != 0 { | |
341 | init_str.push_str(&format!( | |
342 | " voluntary ctxt switches: {} involuntary ctxt switches: {}", | |
343 | nvcsw, nivcsw | |
344 | )); | |
345 | } | |
346 | ||
347 | return Some(init_str); | |
348 | } |