]> git.proxmox.com Git - rustc.git/blame - src/tools/compiletest/src/runtest.rs
New upstream version 1.58.1+dfsg1
[rustc.git] / src / tools / compiletest / src / runtest.rs
CommitLineData
48663c56
XL
1// ignore-tidy-filelength
2
9fa01778 3use crate::common::{expected_output_path, UI_EXTENSIONS, UI_FIXED, UI_STDERR, UI_STDOUT};
c295e0f8 4use crate::common::{incremental_dir, output_base_dir, output_base_name, output_testname_unique};
fc512014 5use crate::common::{Assembly, Incremental, JsDocTest, MirOpt, RunMake, RustdocJson, Ui};
dfeec247
XL
6use crate::common::{Codegen, CodegenUnits, DebugInfo, Debugger, Rustdoc};
7use crate::common::{CompareMode, FailMode, PassMode};
9fa01778 8use crate::common::{Config, TestPaths};
5869c6ff 9use crate::common::{Pretty, RunPassValgrind};
dfeec247 10use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT};
c295e0f8 11use crate::compute_diff::{write_diff, write_filtered_diff};
9fa01778 12use crate::errors::{self, Error, ErrorKind};
9fa01778
XL
13use crate::header::TestProps;
14use crate::json;
c295e0f8 15use crate::read2::read2_abbreviated;
ba9703b0 16use crate::util::get_pointer_width;
dfeec247 17use crate::util::{logv, PathBufExt};
6a06907d 18use crate::ColorConfig;
532ac7d7 19use regex::{Captures, Regex};
8faf50e0 20use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
a7813a04 21
94b46f34 22use std::collections::hash_map::DefaultHasher;
c295e0f8 23use std::collections::{HashMap, HashSet};
32a655c1 24use std::env;
0731742a 25use std::ffi::{OsStr, OsString};
532ac7d7 26use std::fs::{self, create_dir_all, File, OpenOptions};
94b46f34 27use std::hash::{Hash, Hasher};
a7813a04 28use std::io::prelude::*;
32a655c1 29use std::io::{self, BufReader};
a7813a04 30use std::path::{Path, PathBuf};
c295e0f8 31use std::process::{Command, ExitStatus, Output, Stdio};
a7813a04
XL
32use std::str;
33
3dfed10e 34use glob::glob;
48663c56 35use lazy_static::lazy_static;
3dfed10e 36use tracing::*;
48663c56 37
9fa01778
XL
38use crate::extract_gdb_version;
39use crate::is_android_gdb_target;
c30ab7b3 40
c295e0f8
XL
41mod debugger;
42use debugger::{check_debugger_output, DebuggerCommands};
43
416331ca
XL
44#[cfg(test)]
45mod tests;
46
83c7162d
XL
47#[cfg(windows)]
48fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
49 use std::sync::Mutex;
dfeec247
XL
50 use winapi::um::errhandlingapi::SetErrorMode;
51 use winapi::um::winbase::SEM_NOGPFAULTERRORBOX;
83c7162d
XL
52
53 lazy_static! {
f9f354fc 54 static ref LOCK: Mutex<()> = Mutex::new(());
83c7162d
XL
55 }
56 // Error mode is a global variable, so lock it so only one thread will change it
57 let _lock = LOCK.lock().unwrap();
58
59 // Tell Windows to not show any UI on errors (such as terminating abnormally).
60 // This is important for running tests, since some of them use abnormal
61 // termination by design. This mode is inherited by all child processes.
62 unsafe {
63 let old_mode = SetErrorMode(SEM_NOGPFAULTERRORBOX); // read inherited flags
64 SetErrorMode(old_mode | SEM_NOGPFAULTERRORBOX);
65 let r = f();
66 SetErrorMode(old_mode);
67 r
68 }
69}
70
71#[cfg(not(windows))]
72fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
73 f()
74}
75
3b2f2976
XL
76/// The name of the environment variable that holds dynamic library locations.
77pub fn dylib_env_var() -> &'static str {
78 if cfg!(windows) {
79 "PATH"
80 } else if cfg!(target_os = "macos") {
81 "DYLD_LIBRARY_PATH"
82 } else if cfg!(target_os = "haiku") {
83 "LIBRARY_PATH"
84 } else {
85 "LD_LIBRARY_PATH"
86 }
87}
88
48663c56
XL
89/// The platform-specific library name
90pub fn get_lib_name(lib: &str, dylib: bool) -> String {
91 // In some casess (e.g. MUSL), we build a static
92 // library, rather than a dynamic library.
93 // In this case, the only path we can pass
94 // with '--extern-meta' is the '.lib' file
95 if !dylib {
96 return format!("lib{}.rlib", lib);
97 }
98
99 if cfg!(windows) {
100 format!("{}.dll", lib)
101 } else if cfg!(target_os = "macos") {
102 format!("lib{}.dylib", lib)
103 } else {
104 format!("lib{}.so", lib)
105 }
106}
107
94b46f34 108pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
a7813a04 109 match &*config.target {
0731742a
XL
110 "arm-linux-androideabi"
111 | "armv7-linux-androideabi"
112 | "thumbv7neon-linux-androideabi"
113 | "aarch64-linux-android" => {
a7813a04
XL
114 if !config.adb_device_status {
115 panic!("android device not available");
116 }
117 }
118
c30ab7b3 119 _ => {
8bb4bdeb 120 // android has its own gdb handling
dfeec247 121 if config.debugger == Some(Debugger::Gdb) && config.gdb.is_none() {
c30ab7b3
SL
122 panic!("gdb not available but debuginfo gdb debuginfo test requested");
123 }
124 }
a7813a04
XL
125 }
126
127 if config.verbose {
128 // We're going to be dumping a lot of info. Start on a new line.
129 print!("\n\n");
130 }
131 debug!("running {:?}", testpaths.file.display());
c295e0f8
XL
132 let mut props = TestProps::from_file(&testpaths.file, revision, &config);
133 if props.incremental {
134 props.incremental_dir = Some(incremental_dir(&config, testpaths));
135 }
a7813a04 136
3dfed10e 137 let cx = TestCx { config: &config, props: &props, testpaths, revision };
94b46f34 138 create_dir_all(&cx.output_base_dir()).unwrap();
c295e0f8
XL
139 if props.incremental {
140 cx.init_incremental_test();
141 }
a7813a04 142
94b46f34
XL
143 if config.mode == Incremental {
144 // Incremental tests are special because they cannot be run in
145 // parallel.
dfeec247 146 assert!(!props.revisions.is_empty(), "Incremental tests require revisions.");
94b46f34 147 for revision in &props.revisions {
c295e0f8
XL
148 let mut revision_props = TestProps::from_file(&testpaths.file, Some(revision), &config);
149 revision_props.incremental_dir = props.incremental_dir.clone();
a7813a04
XL
150 let rev_cx = TestCx {
151 config: &config,
152 props: &revision_props,
3b2f2976 153 testpaths,
ff7c6d11 154 revision: Some(revision),
a7813a04
XL
155 };
156 rev_cx.run_revision();
157 }
94b46f34
XL
158 } else {
159 cx.run_revision();
a7813a04
XL
160 }
161
94b46f34
XL
162 cx.create_stamp();
163}
8bb4bdeb 164
94b46f34
XL
165pub fn compute_stamp_hash(config: &Config) -> String {
166 let mut hash = DefaultHasher::new();
167 config.stage_id.hash(&mut hash);
17df50a5 168 config.run.hash(&mut hash);
0bf4aa26 169
dfeec247
XL
170 match config.debugger {
171 Some(Debugger::Cdb) => {
172 config.cdb.hash(&mut hash);
173 }
dc9dc135 174
dfeec247
XL
175 Some(Debugger::Gdb) => {
176 config.gdb.hash(&mut hash);
177 env::var_os("PATH").hash(&mut hash);
178 env::var_os("PYTHONPATH").hash(&mut hash);
179 }
0bf4aa26 180
dfeec247
XL
181 Some(Debugger::Lldb) => {
182 config.lldb_python.hash(&mut hash);
183 config.lldb_python_dir.hash(&mut hash);
184 env::var_os("PATH").hash(&mut hash);
185 env::var_os("PYTHONPATH").hash(&mut hash);
186 }
187
188 None => {}
0bf4aa26
XL
189 }
190
dfeec247 191 if let Ui = config.mode {
dc9dc135
XL
192 config.force_pass_mode.hash(&mut hash);
193 }
194
94b46f34 195 format!("{:x}", hash.finish())
a7813a04
XL
196}
197
fc512014 198#[derive(Copy, Clone)]
a7813a04
XL
199struct TestCx<'test> {
200 config: &'test Config,
201 props: &'test TestProps,
202 testpaths: &'test TestPaths,
ff7c6d11 203 revision: Option<&'test str>,
a7813a04
XL
204}
205
0bf4aa26
XL
206enum ReadFrom {
207 Path,
208 Stdin(String),
209}
210
e1599b0c
XL
211enum TestOutput {
212 Compile,
213 Run,
214}
215
dfeec247
XL
216/// Will this test be executed? Should we use `make_exe_name`?
217#[derive(Copy, Clone, PartialEq)]
218enum WillExecute {
219 Yes,
220 No,
17df50a5 221 Disabled,
dfeec247
XL
222}
223
224/// Should `--emit metadata` be used?
225#[derive(Copy, Clone)]
226enum EmitMetadata {
227 Yes,
228 No,
229}
230
a7813a04 231impl<'test> TestCx<'test> {
a7813a04
XL
232 /// Code executed for each revision in turn (or, if there are no
233 /// revisions, exactly once, with revision == None).
234 fn run_revision(&self) {
c295e0f8
XL
235 if self.props.should_ice && self.config.mode != Incremental {
236 self.fatal("cannot use should-ice in a test that is not cfail");
60c5eb7d 237 }
a7813a04 238 match self.config.mode {
a7813a04
XL
239 RunPassValgrind => self.run_valgrind_test(),
240 Pretty => self.run_pretty_test(),
dfeec247 241 DebugInfo => self.run_debuginfo_test(),
a7813a04
XL
242 Codegen => self.run_codegen_test(),
243 Rustdoc => self.run_rustdoc_test(),
fc512014 244 RustdocJson => self.run_rustdoc_json_test(),
a7813a04
XL
245 CodegenUnits => self.run_codegen_units_test(),
246 Incremental => self.run_incremental_test(),
247 RunMake => self.run_rmake_test(),
416331ca 248 Ui => self.run_ui_test(),
5bcae85e 249 MirOpt => self.run_mir_opt_test(),
532ac7d7 250 Assembly => self.run_assembly_test(),
9fa01778 251 JsDocTest => self.run_js_doc_test(),
a7813a04
XL
252 }
253 }
254
dc9dc135
XL
255 fn pass_mode(&self) -> Option<PassMode> {
256 self.props.pass_mode(self.config)
257 }
258
dfeec247 259 fn should_run(&self, pm: Option<PassMode>) -> WillExecute {
17df50a5
XL
260 let test_should_run = match self.config.mode {
261 Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => true,
262 MirOpt if pm == Some(PassMode::Run) => true,
263 Ui | MirOpt => false,
e74abb32 264 mode => panic!("unimplemented for mode {:?}", mode),
17df50a5
XL
265 };
266 if test_should_run { self.run_if_enabled() } else { WillExecute::No }
267 }
268
269 fn run_if_enabled(&self) -> WillExecute {
270 if self.config.run_enabled() { WillExecute::Yes } else { WillExecute::Disabled }
e74abb32
XL
271 }
272
dfeec247 273 fn should_run_successfully(&self, pm: Option<PassMode>) -> bool {
dc9dc135 274 match self.config.mode {
ba9703b0 275 Ui | MirOpt => pm == Some(PassMode::Run),
dc9dc135
XL
276 mode => panic!("unimplemented for mode {:?}", mode),
277 }
0bf4aa26
XL
278 }
279
dfeec247 280 fn should_compile_successfully(&self, pm: Option<PassMode>) -> bool {
0bf4aa26 281 match self.config.mode {
9fa01778 282 JsDocTest => true,
dfeec247 283 Ui => pm.is_some() || self.props.fail_mode > Some(FailMode::Build),
0bf4aa26 284 Incremental => {
dfeec247
XL
285 let revision =
286 self.revision.expect("incremental tests require a list of revisions");
0bf4aa26
XL
287 if revision.starts_with("rpass") || revision.starts_with("rfail") {
288 true
289 } else if revision.starts_with("cfail") {
290 // FIXME: would be nice if incremental revs could start with "cpass"
dfeec247 291 pm.is_some()
0bf4aa26
XL
292 } else {
293 panic!("revision name must begin with rpass, rfail, or cfail");
294 }
295 }
296 mode => panic!("unimplemented for mode {:?}", mode),
297 }
298 }
299
dfeec247
XL
300 fn check_if_test_should_compile(&self, proc_res: &ProcRes, pm: Option<PassMode>) {
301 if self.should_compile_successfully(pm) {
9e0c209e 302 if !proc_res.status.success() {
ff7c6d11 303 self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res);
9e0c209e
SL
304 }
305 } else {
306 if proc_res.status.success() {
307 self.fatal_proc_rec(
308 &format!("{} test compiled successfully!", self.config.mode)[..],
ff7c6d11
XL
309 proc_res,
310 );
9e0c209e 311 }
a7813a04 312
ff7c6d11 313 self.check_correct_failure_status(proc_res);
9e0c209e 314 }
ff7c6d11
XL
315 }
316
317 fn run_cfail_test(&self) {
dfeec247
XL
318 let pm = self.pass_mode();
319 let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
320 self.check_if_test_should_compile(&proc_res, pm);
60c5eb7d 321 self.check_no_compiler_crash(&proc_res, self.props.should_ice);
a7813a04 322
a7813a04
XL
323 let output_to_check = self.get_output(&proc_res);
324 let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
325 if !expected_errors.is_empty() {
326 if !self.props.error_patterns.is_empty() {
327 self.fatal("both error pattern and expected errors specified");
328 }
329 self.check_expected_errors(expected_errors, &proc_res);
330 } else {
dfeec247 331 self.check_error_patterns(&output_to_check, &proc_res, pm);
a7813a04 332 }
60c5eb7d
XL
333 if self.props.should_ice {
334 match proc_res.status.code() {
335 Some(101) => (),
336 _ => self.fatal("expected ICE"),
337 }
338 }
9e0c209e 339
a7813a04
XL
340 self.check_forbid_output(&output_to_check, &proc_res);
341 }
342
343 fn run_rfail_test(&self) {
dfeec247 344 let pm = self.pass_mode();
17df50a5
XL
345 let should_run = self.run_if_enabled();
346 let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm));
a7813a04
XL
347
348 if !proc_res.status.success() {
349 self.fatal_proc_rec("compilation failed!", &proc_res);
350 }
351
17df50a5
XL
352 if let WillExecute::Disabled = should_run {
353 return;
354 }
355
a7813a04
XL
356 let proc_res = self.exec_compiled_test();
357
358 // The value our Makefile configures valgrind to return on failure
359 const VALGRIND_ERR: i32 = 100;
360 if proc_res.status.code() == Some(VALGRIND_ERR) {
361 self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
362 }
363
364 let output_to_check = self.get_output(&proc_res);
365 self.check_correct_failure_status(&proc_res);
dfeec247 366 self.check_error_patterns(&output_to_check, &proc_res, pm);
a7813a04
XL
367 }
368
369 fn get_output(&self, proc_res: &ProcRes) -> String {
370 if self.props.check_stdout {
371 format!("{}{}", proc_res.stdout, proc_res.stderr)
372 } else {
373 proc_res.stderr.clone()
374 }
375 }
376
377 fn check_correct_failure_status(&self, proc_res: &ProcRes) {
0531ce1d
XL
378 let expected_status = Some(self.props.failure_status);
379 let received_status = proc_res.status.code();
380
381 if expected_status != received_status {
a7813a04 382 self.fatal_proc_rec(
94b46f34
XL
383 &format!(
384 "Error: expected failure status ({:?}) but received status {:?}.",
385 expected_status, received_status
386 ),
ff7c6d11
XL
387 proc_res,
388 );
a7813a04
XL
389 }
390 }
391
392 fn run_rpass_test(&self) {
dfeec247 393 let emit_metadata = self.should_emit_metadata(self.pass_mode());
17df50a5
XL
394 let should_run = self.run_if_enabled();
395 let proc_res = self.compile_test(should_run, emit_metadata);
a7813a04
XL
396
397 if !proc_res.status.success() {
398 self.fatal_proc_rec("compilation failed!", &proc_res);
399 }
400
17df50a5
XL
401 if let WillExecute::Disabled = should_run {
402 return;
403 }
404
7cac9316 405 // FIXME(#41968): Move this check to tidy?
9e0c209e 406 let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
ff7c6d11
XL
407 assert!(
408 expected_errors.is_empty(),
409 "run-pass tests with expected warnings should be moved to ui/"
410 );
9e0c209e 411
dc9dc135
XL
412 let proc_res = self.exec_compiled_test();
413 if !proc_res.status.success() {
414 self.fatal_proc_rec("test run failed!", &proc_res);
a7813a04
XL
415 }
416 }
417
418 fn run_valgrind_test(&self) {
419 assert!(self.revision.is_none(), "revisions not relevant here");
420
421 if self.config.valgrind_path.is_none() {
422 assert!(!self.config.force_valgrind);
423 return self.run_rpass_test();
424 }
425
17df50a5
XL
426 let should_run = self.run_if_enabled();
427 let mut proc_res = self.compile_test(should_run, EmitMetadata::No);
a7813a04
XL
428
429 if !proc_res.status.success() {
430 self.fatal_proc_rec("compilation failed!", &proc_res);
431 }
432
17df50a5
XL
433 if let WillExecute::Disabled = should_run {
434 return;
435 }
436
a7813a04
XL
437 let mut new_config = self.config.clone();
438 new_config.runtool = new_config.valgrind_path.clone();
dfeec247 439 let new_cx = TestCx { config: &new_config, ..*self };
a7813a04
XL
440 proc_res = new_cx.exec_compiled_test();
441
442 if !proc_res.status.success() {
443 self.fatal_proc_rec("test run failed!", &proc_res);
444 }
445 }
446
447 fn run_pretty_test(&self) {
448 if self.props.pp_exact.is_some() {
449 logv(self.config, "testing for exact pretty-printing".to_owned());
450 } else {
dfeec247 451 logv(self.config, "testing for converging pretty-printing".to_owned());
a7813a04
XL
452 }
453
ff7c6d11
XL
454 let rounds = match self.props.pp_exact {
455 Some(_) => 1,
456 None => 2,
457 };
a7813a04 458
0731742a 459 let src = fs::read_to_string(&self.testpaths.file).unwrap();
c30ab7b3 460 let mut srcs = vec![src];
a7813a04
XL
461
462 let mut round = 0;
463 while round < rounds {
ff7c6d11
XL
464 logv(
465 self.config,
dfeec247 466 format!("pretty-printing round {} revision {:?}", round, self.revision),
ff7c6d11 467 );
dfeec247
XL
468 let read_from =
469 if round == 0 { ReadFrom::Path } else { ReadFrom::Stdin(srcs[round].to_owned()) };
a7813a04 470
dfeec247 471 let proc_res = self.print_source(read_from, &self.props.pretty_mode);
a7813a04 472 if !proc_res.status.success() {
ff7c6d11
XL
473 self.fatal_proc_rec(
474 &format!(
475 "pretty-printing failed in round {} revision {:?}",
94b46f34 476 round, self.revision
ff7c6d11
XL
477 ),
478 &proc_res,
479 );
a7813a04
XL
480 }
481
ff7c6d11 482 let ProcRes { stdout, .. } = proc_res;
a7813a04
XL
483 srcs.push(stdout);
484 round += 1;
485 }
486
487 let mut expected = match self.props.pp_exact {
488 Some(ref file) => {
489 let filepath = self.testpaths.file.parent().unwrap().join(file);
0731742a 490 fs::read_to_string(&filepath).unwrap()
a7813a04 491 }
ff7c6d11 492 None => srcs[srcs.len() - 2].clone(),
a7813a04
XL
493 };
494 let mut actual = srcs[srcs.len() - 1].clone();
495
496 if self.props.pp_exact.is_some() {
497 // Now we have to care about line endings
498 let cr = "\r".to_owned();
3dfed10e
XL
499 actual = actual.replace(&cr, "");
500 expected = expected.replace(&cr, "");
a7813a04
XL
501 }
502
503 self.compare_source(&expected, &actual);
504
505 // If we're only making sure that the output matches then just stop here
ff7c6d11
XL
506 if self.props.pretty_compare_only {
507 return;
508 }
a7813a04
XL
509
510 // Finally, let's make sure it actually appears to remain valid code
511 let proc_res = self.typecheck_source(actual);
512 if !proc_res.status.success() {
513 self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
514 }
515
ff7c6d11
XL
516 if !self.props.pretty_expanded {
517 return;
518 }
a7813a04 519
94222f64 520 // additionally, run `-Zunpretty=expanded` and try to build it.
0bf4aa26 521 let proc_res = self.print_source(ReadFrom::Path, "expanded");
a7813a04
XL
522 if !proc_res.status.success() {
523 self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
524 }
525
dfeec247 526 let ProcRes { stdout: expanded_src, .. } = proc_res;
a7813a04
XL
527 let proc_res = self.typecheck_source(expanded_src);
528 if !proc_res.status.success() {
dfeec247 529 self.fatal_proc_rec("pretty-printed source (expanded) does not typecheck", &proc_res);
a7813a04
XL
530 }
531 }
532
0bf4aa26 533 fn print_source(&self, read_from: ReadFrom, pretty_type: &str) -> ProcRes {
a7813a04 534 let aux_dir = self.aux_output_dir_name();
0bf4aa26
XL
535 let input: &str = match read_from {
536 ReadFrom::Stdin(_) => "-",
537 ReadFrom::Path => self.testpaths.file.to_str().unwrap(),
538 };
3b2f2976
XL
539
540 let mut rustc = Command::new(&self.config.rustc_path);
ff7c6d11 541 rustc
0bf4aa26 542 .arg(input)
2c00a5a8 543 .args(&["-Z", &format!("unpretty={}", pretty_type)])
3b2f2976 544 .args(&["--target", &self.config.target])
ff7c6d11
XL
545 .arg("-L")
546 .arg(&aux_dir)
3b2f2976 547 .args(&self.props.compile_flags)
3dfed10e 548 .envs(self.props.rustc_env.clone());
dfeec247
XL
549 self.maybe_add_external_args(
550 &mut rustc,
551 self.split_maybe_args(&self.config.target_rustcflags),
552 );
3b2f2976 553
0bf4aa26
XL
554 let src = match read_from {
555 ReadFrom::Stdin(src) => Some(src),
dfeec247 556 ReadFrom::Path => None,
0bf4aa26
XL
557 };
558
ff7c6d11
XL
559 self.compose_and_run(
560 rustc,
561 self.config.compile_lib_path.to_str().unwrap(),
562 Some(aux_dir.to_str().unwrap()),
0bf4aa26 563 src,
ff7c6d11 564 )
a7813a04
XL
565 }
566
ff7c6d11 567 fn compare_source(&self, expected: &str, actual: &str) {
a7813a04 568 if expected != actual {
8faf50e0
XL
569 self.fatal(&format!(
570 "pretty-printed source does not match expected source\n\
ff7c6d11
XL
571 expected:\n\
572 ------------------------------------------\n\
573 {}\n\
574 ------------------------------------------\n\
575 actual:\n\
576 ------------------------------------------\n\
577 {}\n\
578 ------------------------------------------\n\
3dfed10e
XL
579 diff:\n\
580 ------------------------------------------\n\
581 {}\n",
582 expected,
583 actual,
584 write_diff(expected, actual, 3),
dfeec247 585 ));
a7813a04
XL
586 }
587 }
588
9fa01778
XL
589 fn set_revision_flags(&self, cmd: &mut Command) {
590 if let Some(revision) = self.revision {
591 // Normalize revisions to be lowercase and replace `-`s with `_`s.
592 // Otherwise the `--cfg` flag is not valid.
593 let normalized_revision = revision.to_lowercase().replace("-", "_");
594 cmd.args(&["--cfg", &normalized_revision]);
595 }
596 }
597
a7813a04 598 fn typecheck_source(&self, src: String) -> ProcRes {
3b2f2976
XL
599 let mut rustc = Command::new(&self.config.rustc_path);
600
601 let out_dir = self.output_base_name().with_extension("pretty-out");
602 let _ = fs::remove_dir_all(&out_dir);
603 create_dir_all(&out_dir).unwrap();
a7813a04 604
dfeec247 605 let target = if self.props.force_host { &*self.config.host } else { &*self.config.target };
3157f602 606
3b2f2976
XL
607 let aux_dir = self.aux_output_dir_name();
608
ff7c6d11
XL
609 rustc
610 .arg("-")
94b46f34 611 .arg("-Zno-codegen")
ff7c6d11
XL
612 .arg("--out-dir")
613 .arg(&out_dir)
3b2f2976 614 .arg(&format!("--target={}", target))
ff7c6d11
XL
615 .arg("-L")
616 .arg(&self.config.build_base)
617 .arg("-L")
618 .arg(aux_dir);
9fa01778 619 self.set_revision_flags(&mut rustc);
dfeec247
XL
620 self.maybe_add_external_args(
621 &mut rustc,
622 self.split_maybe_args(&self.config.target_rustcflags),
623 );
3b2f2976
XL
624 rustc.args(&self.props.compile_flags);
625
626 self.compose_and_run_compiler(rustc, Some(src))
a7813a04
XL
627 }
628
dfeec247
XL
629 fn run_debuginfo_test(&self) {
630 match self.config.debugger.unwrap() {
631 Debugger::Cdb => self.run_debuginfo_cdb_test(),
632 Debugger::Gdb => self.run_debuginfo_gdb_test(),
633 Debugger::Lldb => self.run_debuginfo_lldb_test(),
634 }
635 }
636
dc9dc135
XL
637 fn run_debuginfo_cdb_test(&self) {
638 assert!(self.revision.is_none(), "revisions not relevant here");
639
640 let config = Config {
641 target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
642 host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
dc9dc135
XL
643 ..self.config.clone()
644 };
645
dfeec247 646 let test_cx = TestCx { config: &config, ..*self };
dc9dc135
XL
647
648 test_cx.run_debuginfo_cdb_test_no_opt();
649 }
650
651 fn run_debuginfo_cdb_test_no_opt(&self) {
652 // compile test file (it should have 'compile-flags:-g' in the header)
17df50a5
XL
653 let should_run = self.run_if_enabled();
654 let compile_result = self.compile_test(should_run, EmitMetadata::No);
dc9dc135
XL
655 if !compile_result.status.success() {
656 self.fatal_proc_rec("compilation failed!", &compile_result);
657 }
17df50a5
XL
658 if let WillExecute::Disabled = should_run {
659 return;
660 }
dc9dc135
XL
661
662 let exe_file = self.make_exe_name();
663
664 let prefixes = {
3dfed10e 665 static PREFIXES: &[&str] = &["cdb", "cdbg"];
dc9dc135
XL
666 // No "native rust support" variation for CDB yet.
667 PREFIXES
668 };
669
670 // Parse debugger commands etc from test files
dfeec247 671 let DebuggerCommands { commands, check_lines, breakpoint_lines, .. } =
c295e0f8
XL
672 match DebuggerCommands::parse_from(&self.testpaths.file, self.config, prefixes) {
673 Ok(cmds) => cmds,
674 Err(e) => self.fatal(&e),
675 };
dc9dc135
XL
676
677 // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
678 let mut script_str = String::with_capacity(2048);
679 script_str.push_str("version\n"); // List CDB (and more) version info in test output
680 script_str.push_str(".nvlist\n"); // List loaded `*.natvis` files, bulk of custom MSVC debug
681
136023e0
XL
682 // If a .js file exists next to the source file being tested, then this is a JavaScript
683 // debugging extension that needs to be loaded.
684 let mut js_extension = self.testpaths.file.clone();
685 js_extension.set_extension("cdb.js");
686 if js_extension.exists() {
687 script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension.to_string_lossy()));
688 }
689
dc9dc135
XL
690 // Set breakpoints on every line that contains the string "#break"
691 let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
692 for line in &breakpoint_lines {
dfeec247 693 script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line));
dc9dc135
XL
694 }
695
696 // Append the other `cdb-command:`s
697 for line in &commands {
698 script_str.push_str(line);
699 script_str.push_str("\n");
700 }
701
702 script_str.push_str("\nqq\n"); // Quit the debugger (including remote debugger, if any)
703
704 // Write the script into a file
705 debug!("script_str = {}", script_str);
706 self.dump_output_file(&script_str, "debugger.script");
707 let debugger_script = self.make_out_name("debugger.script");
708
709 let cdb_path = &self.config.cdb.as_ref().unwrap();
710 let mut cdb = Command::new(cdb_path);
dfeec247
XL
711 cdb.arg("-lines") // Enable source line debugging.
712 .arg("-cf")
713 .arg(&debugger_script)
dc9dc135
XL
714 .arg(&exe_file);
715
716 let debugger_run_result = self.compose_and_run(
717 cdb,
718 self.config.run_lib_path.to_str().unwrap(),
719 None, // aux_path
dfeec247 720 None, // input
dc9dc135
XL
721 );
722
723 if !debugger_run_result.status.success() {
724 self.fatal_proc_rec("Error while running CDB", &debugger_run_result);
725 }
726
c295e0f8
XL
727 if let Err(e) = check_debugger_output(&debugger_run_result, &check_lines) {
728 self.fatal_proc_rec(&e, &debugger_run_result);
729 }
dc9dc135
XL
730 }
731
a7813a04
XL
732 fn run_debuginfo_gdb_test(&self) {
733 assert!(self.revision.is_none(), "revisions not relevant here");
734
735 let config = Config {
736 target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
737 host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
ff7c6d11 738 ..self.config.clone()
a7813a04
XL
739 };
740
dfeec247 741 let test_cx = TestCx { config: &config, ..*self };
a7813a04
XL
742
743 test_cx.run_debuginfo_gdb_test_no_opt();
744 }
745
746 fn run_debuginfo_gdb_test_no_opt(&self) {
c30ab7b3
SL
747 let prefixes = if self.config.gdb_native_rust {
748 // GDB with Rust
3dfed10e 749 static PREFIXES: &[&str] = &["gdb", "gdbr"];
c30ab7b3
SL
750 println!("NOTE: compiletest thinks it is using GDB with native rust support");
751 PREFIXES
752 } else {
753 // Generic GDB
3dfed10e 754 static PREFIXES: &[&str] = &["gdb", "gdbg"];
c30ab7b3
SL
755 println!("NOTE: compiletest thinks it is using GDB without native rust support");
756 PREFIXES
757 };
758
dfeec247 759 let DebuggerCommands { commands, check_lines, breakpoint_lines } =
c295e0f8
XL
760 match DebuggerCommands::parse_from(&self.testpaths.file, self.config, prefixes) {
761 Ok(cmds) => cmds,
762 Err(e) => self.fatal(&e),
763 };
a7813a04
XL
764 let mut cmds = commands.join("\n");
765
766 // compile test file (it should have 'compile-flags:-g' in the header)
17df50a5
XL
767 let should_run = self.run_if_enabled();
768 let compiler_run_result = self.compile_test(should_run, EmitMetadata::No);
a7813a04
XL
769 if !compiler_run_result.status.success() {
770 self.fatal_proc_rec("compilation failed!", &compiler_run_result);
771 }
17df50a5
XL
772 if let WillExecute::Disabled = should_run {
773 return;
774 }
a7813a04
XL
775
776 let exe_file = self.make_exe_name();
777
778 let debugger_run_result;
0bf4aa26
XL
779 if is_android_gdb_target(&self.config.target) {
780 cmds = cmds.replace("run", "continue");
a7813a04 781
0bf4aa26
XL
782 let tool_path = match self.config.android_cross_path.to_str() {
783 Some(x) => x.to_owned(),
784 None => self.fatal("cannot find android cross path"),
785 };
a7813a04 786
0bf4aa26
XL
787 // write debugger script
788 let mut script_str = String::with_capacity(2048);
789 script_str.push_str(&format!("set charset {}\n", Self::charset()));
790 script_str.push_str(&format!("set sysroot {}\n", tool_path));
791 script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
792 script_str.push_str("target remote :5039\n");
793 script_str.push_str(&format!(
794 "set solib-search-path \
795 ./{}/stage2/lib/rustlib/{}/lib/\n",
796 self.config.host, self.config.target
797 ));
798 for line in &breakpoint_lines {
799 script_str.push_str(
800 &format!(
801 "break {:?}:{}\n",
802 self.testpaths.file.file_name().unwrap().to_string_lossy(),
803 *line
804 )[..],
ff7c6d11 805 );
0bf4aa26
XL
806 }
807 script_str.push_str(&cmds);
808 script_str.push_str("\nquit\n");
809
810 debug!("script_str = {}", script_str);
811 self.dump_output_file(&script_str, "debugger.script");
812
813 let adb_path = &self.config.adb_path;
814
815 Command::new(adb_path)
816 .arg("push")
817 .arg(&exe_file)
818 .arg(&self.config.adb_test_dir)
819 .status()
3dfed10e 820 .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path));
0bf4aa26
XL
821
822 Command::new(adb_path)
823 .args(&["forward", "tcp:5039", "tcp:5039"])
824 .status()
3dfed10e 825 .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path));
0bf4aa26
XL
826
827 let adb_arg = format!(
828 "export LD_LIBRARY_PATH={}; \
829 gdbserver{} :5039 {}/{}",
830 self.config.adb_test_dir.clone(),
dfeec247 831 if self.config.target.contains("aarch64") { "64" } else { "" },
0bf4aa26
XL
832 self.config.adb_test_dir.clone(),
833 exe_file.file_name().unwrap().to_str().unwrap()
834 );
a7813a04 835
0bf4aa26
XL
836 debug!("adb arg: {}", adb_arg);
837 let mut adb = Command::new(adb_path)
838 .args(&["shell", &adb_arg])
839 .stdout(Stdio::piped())
840 .stderr(Stdio::inherit())
841 .spawn()
3dfed10e 842 .unwrap_or_else(|_| panic!("failed to exec `{:?}`", adb_path));
0bf4aa26
XL
843
844 // Wait for the gdbserver to print out "Listening on port ..."
845 // at which point we know that it's started and then we can
846 // execute the debugger below.
847 let mut stdout = BufReader::new(adb.stdout.take().unwrap());
848 let mut line = String::new();
849 loop {
850 line.truncate(0);
851 stdout.read_line(&mut line).unwrap();
852 if line.starts_with("Listening on port 5039") {
853 break;
a7813a04 854 }
0bf4aa26
XL
855 }
856 drop(stdout);
857
0731742a
XL
858 let mut debugger_script = OsString::from("-command=");
859 debugger_script.push(self.make_out_name("debugger.script"));
dfeec247
XL
860 let debugger_opts: &[&OsStr] =
861 &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
0bf4aa26
XL
862
863 let gdb_path = self.config.gdb.as_ref().unwrap();
dfeec247 864 let Output { status, stdout, stderr } = Command::new(&gdb_path)
0731742a 865 .args(debugger_opts)
0bf4aa26 866 .output()
3dfed10e 867 .unwrap_or_else(|_| panic!("failed to exec `{:?}`", gdb_path));
0bf4aa26
XL
868 let cmdline = {
869 let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
0731742a 870 gdb.args(debugger_opts);
0bf4aa26
XL
871 let cmdline = self.make_cmdline(&gdb, "");
872 logv(self.config, format!("executing {}", cmdline));
873 cmdline
874 };
a7813a04 875
0bf4aa26
XL
876 debugger_run_result = ProcRes {
877 status,
878 stdout: String::from_utf8(stdout).unwrap(),
879 stderr: String::from_utf8(stderr).unwrap(),
880 cmdline,
881 };
882 if adb.kill().is_err() {
883 println!("Adb process is already finished.");
a7813a04 884 }
0bf4aa26 885 } else {
dfeec247
XL
886 let rust_src_root =
887 self.config.find_rust_src_root().expect("Could not find Rust source root");
0bf4aa26 888 let rust_pp_module_rel_path = Path::new("./src/etc");
dfeec247
XL
889 let rust_pp_module_abs_path =
890 rust_src_root.join(rust_pp_module_rel_path).to_str().unwrap().to_owned();
0bf4aa26
XL
891 // write debugger script
892 let mut script_str = String::with_capacity(2048);
893 script_str.push_str(&format!("set charset {}\n", Self::charset()));
894 script_str.push_str("show version\n");
895
896 match self.config.gdb_version {
897 Some(version) => {
dfeec247 898 println!("NOTE: compiletest thinks it is using GDB version {}", version);
a7813a04 899
0bf4aa26
XL
900 if version > extract_gdb_version("7.4").unwrap() {
901 // Add the directory containing the pretty printers to
902 // GDB's script auto loading safe path
903 script_str.push_str(&format!(
904 "add-auto-load-safe-path {}\n",
905 rust_pp_module_abs_path.replace(r"\", r"\\")
906 ));
a7813a04
XL
907 }
908 }
0bf4aa26
XL
909 _ => {
910 println!(
911 "NOTE: compiletest does not know which version of \
912 GDB it is using"
913 );
914 }
915 }
a7813a04 916
0bf4aa26
XL
917 // The following line actually doesn't have to do anything with
918 // pretty printing, it just tells GDB to print values on one line:
919 script_str.push_str("set print pretty off\n");
a7813a04 920
0bf4aa26 921 // Add the pretty printer directory to GDB's source-file search path
fc512014
XL
922 script_str
923 .push_str(&format!("directory {}\n", rust_pp_module_abs_path.replace(r"\", r"\\")));
a7813a04 924
0bf4aa26 925 // Load the target executable
dfeec247
XL
926 script_str
927 .push_str(&format!("file {}\n", exe_file.to_str().unwrap().replace(r"\", r"\\")));
a7813a04 928
0bf4aa26
XL
929 // Force GDB to print values in the Rust format.
930 if self.config.gdb_native_rust {
931 script_str.push_str("set language rust\n");
932 }
7cac9316 933
0bf4aa26
XL
934 // Add line breakpoints
935 for line in &breakpoint_lines {
936 script_str.push_str(&format!(
937 "break '{}':{}\n",
938 self.testpaths.file.file_name().unwrap().to_string_lossy(),
939 *line
940 ));
941 }
a7813a04 942
0bf4aa26
XL
943 script_str.push_str(&cmds);
944 script_str.push_str("\nquit\n");
a7813a04 945
0bf4aa26
XL
946 debug!("script_str = {}", script_str);
947 self.dump_output_file(&script_str, "debugger.script");
a7813a04 948
0731742a
XL
949 let mut debugger_script = OsString::from("-command=");
950 debugger_script.push(self.make_out_name("debugger.script"));
a7813a04 951
dfeec247
XL
952 let debugger_opts: &[&OsStr] =
953 &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script];
a7813a04 954
0bf4aa26 955 let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
dfeec247
XL
956 gdb.args(debugger_opts).env("PYTHONPATH", rust_pp_module_abs_path);
957
958 debugger_run_result =
959 self.compose_and_run(gdb, self.config.run_lib_path.to_str().unwrap(), None, None);
a7813a04
XL
960 }
961
962 if !debugger_run_result.status.success() {
041b39d2 963 self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
a7813a04
XL
964 }
965
c295e0f8
XL
966 if let Err(e) = check_debugger_output(&debugger_run_result, &check_lines) {
967 self.fatal_proc_rec(&e, &debugger_run_result);
968 }
a7813a04
XL
969 }
970
a7813a04
XL
971 fn run_debuginfo_lldb_test(&self) {
972 assert!(self.revision.is_none(), "revisions not relevant here");
973
974 if self.config.lldb_python_dir.is_none() {
975 self.fatal("Can't run LLDB test because LLDB's python path is not set.");
976 }
977
978 let config = Config {
979 target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
980 host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
ff7c6d11 981 ..self.config.clone()
a7813a04
XL
982 };
983
dfeec247 984 let test_cx = TestCx { config: &config, ..*self };
a7813a04
XL
985
986 test_cx.run_debuginfo_lldb_test_no_opt();
987 }
988
989 fn run_debuginfo_lldb_test_no_opt(&self) {
990 // compile test file (it should have 'compile-flags:-g' in the header)
17df50a5
XL
991 let should_run = self.run_if_enabled();
992 let compile_result = self.compile_test(should_run, EmitMetadata::No);
a7813a04
XL
993 if !compile_result.status.success() {
994 self.fatal_proc_rec("compilation failed!", &compile_result);
995 }
17df50a5
XL
996 if let WillExecute::Disabled = should_run {
997 return;
998 }
a7813a04
XL
999
1000 let exe_file = self.make_exe_name();
1001
1002 match self.config.lldb_version {
1003 Some(ref version) => {
dfeec247 1004 println!("NOTE: compiletest thinks it is using LLDB version {}", version);
a7813a04
XL
1005 }
1006 _ => {
ff7c6d11
XL
1007 println!(
1008 "NOTE: compiletest does not know which version of \
1009 LLDB it is using"
1010 );
a7813a04
XL
1011 }
1012 }
1013
0bf4aa26 1014 let prefixes = if self.config.lldb_native_rust {
3dfed10e 1015 static PREFIXES: &[&str] = &["lldb", "lldbr"];
0bf4aa26
XL
1016 println!("NOTE: compiletest thinks it is using LLDB with native rust support");
1017 PREFIXES
1018 } else {
3dfed10e 1019 static PREFIXES: &[&str] = &["lldb", "lldbg"];
0bf4aa26
XL
1020 println!("NOTE: compiletest thinks it is using LLDB without native rust support");
1021 PREFIXES
1022 };
1023
a7813a04 1024 // Parse debugger commands etc from test files
dfeec247 1025 let DebuggerCommands { commands, check_lines, breakpoint_lines, .. } =
c295e0f8
XL
1026 match DebuggerCommands::parse_from(&self.testpaths.file, self.config, prefixes) {
1027 Ok(cmds) => cmds,
1028 Err(e) => self.fatal(&e),
1029 };
a7813a04
XL
1030
1031 // Write debugger script:
1032 // We don't want to hang when calling `quit` while the process is still running
1033 let mut script_str = String::from("settings set auto-confirm true\n");
1034
1035 // Make LLDB emit its version, so we have it documented in the test output
1036 script_str.push_str("version\n");
1037
1038 // Switch LLDB into "Rust mode"
dfeec247
XL
1039 let rust_src_root =
1040 self.config.find_rust_src_root().expect("Could not find Rust source root");
f035d41b 1041 let rust_pp_module_rel_path = Path::new("./src/etc/lldb_lookup.py");
dfeec247
XL
1042 let rust_pp_module_abs_path =
1043 rust_src_root.join(rust_pp_module_rel_path).to_str().unwrap().to_owned();
ff7c6d11 1044
f035d41b
XL
1045 let rust_type_regexes = vec![
1046 "^(alloc::([a-z_]+::)+)String$",
136023e0
XL
1047 "^&(mut )?str$",
1048 "^&(mut )?\\[.+\\]$",
f035d41b
XL
1049 "^(std::ffi::([a-z_]+::)+)OsString$",
1050 "^(alloc::([a-z_]+::)+)Vec<.+>$",
1051 "^(alloc::([a-z_]+::)+)VecDeque<.+>$",
1052 "^(alloc::([a-z_]+::)+)BTreeSet<.+>$",
1053 "^(alloc::([a-z_]+::)+)BTreeMap<.+>$",
1054 "^(std::collections::([a-z_]+::)+)HashMap<.+>$",
1055 "^(std::collections::([a-z_]+::)+)HashSet<.+>$",
1056 "^(alloc::([a-z_]+::)+)Rc<.+>$",
1057 "^(alloc::([a-z_]+::)+)Arc<.+>$",
1058 "^(core::([a-z_]+::)+)Cell<.+>$",
1059 "^(core::([a-z_]+::)+)Ref<.+>$",
1060 "^(core::([a-z_]+::)+)RefMut<.+>$",
1061 "^(core::([a-z_]+::)+)RefCell<.+>$",
1062 ];
1063
ff7c6d11
XL
1064 script_str
1065 .push_str(&format!("command script import {}\n", &rust_pp_module_abs_path[..])[..]);
f035d41b
XL
1066 script_str.push_str("type synthetic add -l lldb_lookup.synthetic_lookup -x '.*' ");
1067 script_str.push_str("--category Rust\n");
1068 for type_regex in rust_type_regexes {
1069 script_str.push_str("type summary add -F lldb_lookup.summary_lookup -e -x -h ");
1070 script_str.push_str(&format!("'{}' ", type_regex));
1071 script_str.push_str("--category Rust\n");
1072 }
a7813a04
XL
1073 script_str.push_str("type category enable Rust\n");
1074
1075 // Set breakpoints on every line that contains the string "#break"
1076 let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
1077 for line in &breakpoint_lines {
ff7c6d11
XL
1078 script_str.push_str(&format!(
1079 "breakpoint set --file '{}' --line {}\n",
94b46f34 1080 source_file_name, line
ff7c6d11 1081 ));
a7813a04
XL
1082 }
1083
1084 // Append the other commands
1085 for line in &commands {
1086 script_str.push_str(line);
1087 script_str.push_str("\n");
1088 }
1089
1090 // Finally, quit the debugger
1091 script_str.push_str("\nquit\n");
1092
1093 // Write the script into a file
1094 debug!("script_str = {}", script_str);
1095 self.dump_output_file(&script_str, "debugger.script");
1096 let debugger_script = self.make_out_name("debugger.script");
1097
1098 // Let LLDB execute the script via lldb_batchmode.py
ff7c6d11 1099 let debugger_run_result = self.run_lldb(&exe_file, &debugger_script, &rust_src_root);
a7813a04
XL
1100
1101 if !debugger_run_result.status.success() {
1102 self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
1103 }
1104
c295e0f8
XL
1105 if let Err(e) = check_debugger_output(&debugger_run_result, &check_lines) {
1106 self.fatal_proc_rec(&e, &debugger_run_result);
1107 }
a7813a04
XL
1108 }
1109
ff7c6d11
XL
1110 fn run_lldb(
1111 &self,
1112 test_executable: &Path,
1113 debugger_script: &Path,
1114 rust_src_root: &Path,
1115 ) -> ProcRes {
a7813a04
XL
1116 // Prepare the lldb_batchmode which executes the debugger script
1117 let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
ff7c6d11
XL
1118 self.cmd2procres(
1119 Command::new(&self.config.lldb_python)
1120 .arg(&lldb_script_path)
1121 .arg(test_executable)
1122 .arg(debugger_script)
29967ef6 1123 .env("PYTHONUNBUFFERED", "1") // Help debugging #78665
ff7c6d11
XL
1124 .env("PYTHONPATH", self.config.lldb_python_dir.as_ref().unwrap()),
1125 )
a7813a04
XL
1126 }
1127
1128 fn cmd2procres(&self, cmd: &mut Command) -> ProcRes {
1129 let (status, out, err) = match cmd.output() {
dfeec247
XL
1130 Ok(Output { status, stdout, stderr }) => {
1131 (status, String::from_utf8(stdout).unwrap(), String::from_utf8(stderr).unwrap())
1132 }
ff7c6d11
XL
1133 Err(e) => self.fatal(&format!(
1134 "Failed to setup Python process for \
1135 LLDB script: {}",
1136 e
1137 )),
a7813a04
XL
1138 };
1139
1140 self.dump_output(&out, &err);
dfeec247 1141 ProcRes { status, stdout: out, stderr: err, cmdline: format!("{:?}", cmd) }
a7813a04
XL
1142 }
1143
a7813a04
XL
1144 fn cleanup_debug_info_options(&self, options: &Option<String>) -> Option<String> {
1145 if options.is_none() {
1146 return None;
1147 }
1148
1149 // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
ff7c6d11 1150 let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()];
94b46f34
XL
1151 let new_options = self
1152 .split_maybe_args(options)
ff7c6d11
XL
1153 .into_iter()
1154 .filter(|x| !options_to_remove.contains(x))
1155 .collect::<Vec<String>>();
a7813a04
XL
1156
1157 Some(new_options.join(" "))
1158 }
1159
9fa01778
XL
1160 fn maybe_add_external_args(&self, cmd: &mut Command, args: Vec<String>) {
1161 // Filter out the arguments that should not be added by runtest here.
1162 //
1163 // Notable use-cases are: do not add our optimisation flag if
1164 // `compile-flags: -Copt-level=x` and similar for debug-info level as well.
dfeec247
XL
1165 const OPT_FLAGS: &[&str] = &["-O", "-Copt-level=", /*-C<space>*/ "opt-level="];
1166 const DEBUG_FLAGS: &[&str] = &["-g", "-Cdebuginfo=", /*-C<space>*/ "debuginfo="];
9fa01778
XL
1167
1168 // FIXME: ideally we would "just" check the `cmd` itself, but it does not allow inspecting
1169 // its arguments. They need to be collected separately. For now I cannot be bothered to
1170 // implement this the "right" way.
dfeec247
XL
1171 let have_opt_flag =
1172 self.props.compile_flags.iter().any(|arg| OPT_FLAGS.iter().any(|f| arg.starts_with(f)));
1173 let have_debug_flag = self
1174 .props
1175 .compile_flags
1176 .iter()
1177 .any(|arg| DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)));
9fa01778
XL
1178
1179 for arg in args {
1180 if OPT_FLAGS.iter().any(|f| arg.starts_with(f)) && have_opt_flag {
1181 continue;
1182 }
1183 if DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)) && have_debug_flag {
1184 continue;
1185 }
1186 cmd.arg(arg);
1187 }
1188 }
1189
dfeec247
XL
1190 fn check_error_patterns(
1191 &self,
1192 output_to_check: &str,
1193 proc_res: &ProcRes,
1194 pm: Option<PassMode>,
1195 ) {
b7449926 1196 debug!("check_error_patterns");
a7813a04 1197 if self.props.error_patterns.is_empty() {
dfeec247
XL
1198 if pm.is_some() {
1199 // FIXME(#65865)
ff7c6d11 1200 return;
9e0c209e 1201 } else {
ff7c6d11
XL
1202 self.fatal(&format!(
1203 "no error pattern specified in {:?}",
1204 self.testpaths.file.display()
1205 ));
9e0c209e 1206 }
a7813a04 1207 }
b7449926
XL
1208
1209 let mut missing_patterns: Vec<String> = Vec::new();
1210
1211 for pattern in &self.props.error_patterns {
1212 if output_to_check.contains(pattern.trim()) {
1213 debug!("found error pattern {}", pattern);
1214 } else {
1215 missing_patterns.push(pattern.to_string());
a7813a04
XL
1216 }
1217 }
b7449926
XL
1218
1219 if missing_patterns.is_empty() {
ff7c6d11
XL
1220 return;
1221 }
a7813a04 1222
a7813a04
XL
1223 if missing_patterns.len() == 1 {
1224 self.fatal_proc_rec(
1225 &format!("error pattern '{}' not found!", missing_patterns[0]),
ff7c6d11
XL
1226 proc_res,
1227 );
a7813a04
XL
1228 } else {
1229 for pattern in missing_patterns {
b7449926 1230 self.error(&format!("error pattern '{}' not found!", pattern));
a7813a04
XL
1231 }
1232 self.fatal_proc_rec("multiple error patterns not found", proc_res);
1233 }
1234 }
1235
60c5eb7d 1236 fn check_no_compiler_crash(&self, proc_res: &ProcRes, should_ice: bool) {
8faf50e0 1237 match proc_res.status.code() {
60c5eb7d
XL
1238 Some(101) if !should_ice => {
1239 self.fatal_proc_rec("compiler encountered internal error", proc_res)
1240 }
8faf50e0
XL
1241 None => self.fatal_proc_rec("compiler terminated by signal", proc_res),
1242 _ => (),
a7813a04
XL
1243 }
1244 }
1245
ff7c6d11 1246 fn check_forbid_output(&self, output_to_check: &str, proc_res: &ProcRes) {
a7813a04
XL
1247 for pat in &self.props.forbid_output {
1248 if output_to_check.contains(pat) {
1249 self.fatal_proc_rec("forbidden pattern found in compiler output", proc_res);
1250 }
1251 }
1252 }
1253
ff7c6d11 1254 fn check_expected_errors(&self, expected_errors: Vec<errors::Error>, proc_res: &ProcRes) {
dfeec247
XL
1255 debug!(
1256 "check_expected_errors: expected_errors={:?} proc_res.status={:?}",
1257 expected_errors, proc_res.status
1258 );
ff7c6d11 1259 if proc_res.status.success()
dfeec247 1260 && expected_errors.iter().any(|x| x.kind == Some(ErrorKind::Error))
ff7c6d11 1261 {
a7813a04
XL
1262 self.fatal_proc_rec("process did not return an error status", proc_res);
1263 }
1264
8faf50e0
XL
1265 // On Windows, keep all '\' path separators to match the paths reported in the JSON output
1266 // from the compiler
1267 let os_file_name = self.testpaths.file.display().to_string();
1268
ff7c6d11
XL
1269 // on windows, translate all '\' path separators to '/'
1270 let file_name = format!("{}", self.testpaths.file.display()).replace(r"\", "/");
a7813a04
XL
1271
1272 // If the testcase being checked contains at least one expected "help"
1273 // message, then we'll ensure that all "help" messages are expected.
1274 // Otherwise, all "help" messages reported by the compiler will be ignored.
1275 // This logic also applies to "note" messages.
dfeec247
XL
1276 let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help));
1277 let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
a7813a04
XL
1278
1279 // Parse the JSON output from the compiler and extract out the messages.
8faf50e0 1280 let actual_errors = json::parse_output(&os_file_name, &proc_res.stderr, proc_res);
3157f602 1281 let mut unexpected = Vec::new();
a7813a04
XL
1282 let mut found = vec![false; expected_errors.len()];
1283 for actual_error in &actual_errors {
dfeec247
XL
1284 let opt_index =
1285 expected_errors.iter().enumerate().position(|(index, expected_error)| {
1286 !found[index]
1287 && actual_error.line_num == expected_error.line_num
ff7c6d11
XL
1288 && (expected_error.kind.is_none()
1289 || actual_error.kind == expected_error.kind)
1290 && actual_error.msg.contains(&expected_error.msg)
dfeec247 1291 });
a7813a04
XL
1292
1293 match opt_index {
1294 Some(index) => {
1295 // found a match, everybody is happy
1296 assert!(!found[index]);
1297 found[index] = true;
1298 }
1299
1300 None => {
1301 if self.is_unexpected_compiler_message(actual_error, expect_help, expect_note) {
ff7c6d11
XL
1302 self.error(&format!(
1303 "{}:{}: unexpected {}: '{}'",
1304 file_name,
1305 actual_error.line_num,
1306 actual_error
1307 .kind
1308 .as_ref()
1309 .map_or(String::from("message"), |k| k.to_string()),
1310 actual_error.msg
1311 ));
041b39d2 1312 unexpected.push(actual_error);
a7813a04
XL
1313 }
1314 }
1315 }
1316 }
1317
3157f602 1318 let mut not_found = Vec::new();
a7813a04
XL
1319 // anything not yet found is a problem
1320 for (index, expected_error) in expected_errors.iter().enumerate() {
1321 if !found[index] {
ff7c6d11
XL
1322 self.error(&format!(
1323 "{}:{}: expected {} not found: {}",
1324 file_name,
1325 expected_error.line_num,
dfeec247 1326 expected_error.kind.as_ref().map_or("message".into(), |k| k.to_string()),
ff7c6d11
XL
1327 expected_error.msg
1328 ));
041b39d2 1329 not_found.push(expected_error);
a7813a04
XL
1330 }
1331 }
1332
041b39d2 1333 if !unexpected.is_empty() || !not_found.is_empty() {
ff7c6d11
XL
1334 self.error(&format!(
1335 "{} unexpected errors found, {} expected errors not found",
1336 unexpected.len(),
1337 not_found.len()
1338 ));
1339 println!("status: {}\ncommand: {}", proc_res.status, proc_res.cmdline);
041b39d2 1340 if !unexpected.is_empty() {
3157f602
XL
1341 println!("unexpected errors (from JSON output): {:#?}\n", unexpected);
1342 }
041b39d2 1343 if !not_found.is_empty() {
3157f602
XL
1344 println!("not found errors (from test file): {:#?}\n", not_found);
1345 }
a7813a04
XL
1346 panic!();
1347 }
1348 }
1349
9fa01778 1350 /// Returns `true` if we should report an error about `actual_error`,
a7813a04
XL
1351 /// which did not match any of the expected error. We always require
1352 /// errors/warnings to be explicitly listed, but only require
1353 /// helps/notes if there are explicit helps/notes given.
ff7c6d11
XL
1354 fn is_unexpected_compiler_message(
1355 &self,
1356 actual_error: &Error,
1357 expect_help: bool,
1358 expect_note: bool,
1359 ) -> bool {
a7813a04
XL
1360 match actual_error.kind {
1361 Some(ErrorKind::Help) => expect_help,
1362 Some(ErrorKind::Note) => expect_note,
ff7c6d11
XL
1363 Some(ErrorKind::Error) | Some(ErrorKind::Warning) => true,
1364 Some(ErrorKind::Suggestion) | None => false,
a7813a04
XL
1365 }
1366 }
1367
dfeec247
XL
1368 fn should_emit_metadata(&self, pm: Option<PassMode>) -> EmitMetadata {
1369 match (pm, self.props.fail_mode, self.config.mode) {
1370 (Some(PassMode::Check), ..) | (_, Some(FailMode::Check), Ui) => EmitMetadata::Yes,
1371 _ => EmitMetadata::No,
1372 }
1373 }
1374
1375 fn compile_test(&self, will_execute: WillExecute, emit_metadata: EmitMetadata) -> ProcRes {
1376 self.compile_test_general(will_execute, emit_metadata, self.props.local_pass_mode())
1377 }
1378
1379 fn compile_test_general(
1380 &self,
1381 will_execute: WillExecute,
1382 emit_metadata: EmitMetadata,
1383 local_pm: Option<PassMode>,
1384 ) -> ProcRes {
48663c56 1385 // Only use `make_exe_name` when the test ends up being executed.
dfeec247
XL
1386 let output_file = match will_execute {
1387 WillExecute::Yes => TargetLocation::ThisFile(self.make_exe_name()),
17df50a5
XL
1388 WillExecute::No | WillExecute::Disabled => {
1389 TargetLocation::ThisDirectory(self.output_base_dir())
1390 }
48663c56
XL
1391 };
1392
74b04a01 1393 let allow_unused = match self.config.mode {
5869c6ff
XL
1394 Ui => {
1395 // UI tests tend to have tons of unused code as
3b2f2976
XL
1396 // it's just testing various pieces of the compile, but we don't
1397 // want to actually assert warnings about all this code. Instead
1398 // let's just ignore unused code warnings by defaults and tests
1399 // can turn it back on if needed.
dfeec247
XL
1400 if !self.is_rustdoc()
1401 // Note that we use the local pass mode here as we don't want
74b04a01 1402 // to set unused to allow if we've overridden the pass mode
e1599b0c 1403 // via command line flags.
dfeec247
XL
1404 && local_pm != Some(PassMode::Run)
1405 {
74b04a01
XL
1406 AllowUnused::Yes
1407 } else {
1408 AllowUnused::No
83c7162d 1409 }
3b2f2976 1410 }
74b04a01
XL
1411 _ => AllowUnused::No,
1412 };
1413
1414 let mut rustc =
1415 self.make_compile_args(&self.testpaths.file, output_file, emit_metadata, allow_unused);
1416
1417 rustc.arg("-L").arg(&self.aux_output_dir_name());
3b2f2976
XL
1418
1419 self.compose_and_run_compiler(rustc, None)
a7813a04
XL
1420 }
1421
1422 fn document(&self, out_dir: &Path) -> ProcRes {
1423 if self.props.build_aux_docs {
1424 for rel_ab in &self.props.aux_builds {
1425 let aux_testpaths = self.compute_aux_test_paths(rel_ab);
ff7c6d11 1426 let aux_props =
dfeec247 1427 self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
a7813a04
XL
1428 let aux_cx = TestCx {
1429 config: self.config,
1430 props: &aux_props,
1431 testpaths: &aux_testpaths,
ff7c6d11 1432 revision: self.revision,
a7813a04 1433 };
94b46f34
XL
1434 // Create the directory for the stdout/stderr files.
1435 create_dir_all(aux_cx.output_base_dir()).unwrap();
a7813a04
XL
1436 let auxres = aux_cx.document(out_dir);
1437 if !auxres.status.success() {
1438 return auxres;
1439 }
1440 }
1441 }
1442
1443 let aux_dir = self.aux_output_dir_name();
3b2f2976 1444
6a06907d 1445 let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed");
3b2f2976
XL
1446 let mut rustdoc = Command::new(rustdoc_path);
1447
ff7c6d11 1448 rustdoc
0531ce1d
XL
1449 .arg("-L")
1450 .arg(self.config.run_lib_path.to_str().unwrap())
ff7c6d11
XL
1451 .arg("-L")
1452 .arg(aux_dir)
1453 .arg("-o")
1454 .arg(out_dir)
3b2f2976
XL
1455 .arg(&self.testpaths.file)
1456 .args(&self.props.compile_flags);
8faf50e0 1457
fc512014 1458 if self.config.mode == RustdocJson {
cdc7bbd5 1459 rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options");
fc512014
XL
1460 }
1461
abe05a73 1462 if let Some(ref linker) = self.config.linker {
e1599b0c 1463 rustdoc.arg(format!("-Clinker={}", linker));
abe05a73 1464 }
3b2f2976
XL
1465
1466 self.compose_and_run_compiler(rustdoc, None)
a7813a04
XL
1467 }
1468
1469 fn exec_compiled_test(&self) -> ProcRes {
3b2f2976 1470 let env = &self.props.exec_env;
a7813a04 1471
ff7c6d11 1472 let proc_res = match &*self.config.target {
8bb4bdeb
XL
1473 // This is pretty similar to below, we're transforming:
1474 //
1475 // program arg1 arg2
1476 //
1477 // into
1478 //
f9f354fc 1479 // remote-test-client run program 2 support-lib.so support-lib2.so arg1 arg2
8bb4bdeb
XL
1480 //
1481 // The test-client program will upload `program` to the emulator
1482 // along with all other support libraries listed (in this case
f9f354fc
XL
1483 // `support-lib.so` and `support-lib2.so`. It will then execute
1484 // the program on the emulator with the arguments specified
1485 // (in the environment we give the process) and then report back
1486 // the same result.
7cac9316 1487 _ if self.config.remote_test_client.is_some() => {
8bb4bdeb 1488 let aux_dir = self.aux_output_dir_name();
f9f354fc
XL
1489 let ProcArgs { prog, args } = self.make_run_args();
1490 let mut support_libs = Vec::new();
8bb4bdeb
XL
1491 if let Ok(entries) = aux_dir.read_dir() {
1492 for entry in entries {
1493 let entry = entry.unwrap();
1494 if !entry.path().is_file() {
ff7c6d11 1495 continue;
8bb4bdeb 1496 }
f9f354fc 1497 support_libs.push(entry.path());
8bb4bdeb
XL
1498 }
1499 }
ff7c6d11
XL
1500 let mut test_client =
1501 Command::new(self.config.remote_test_client.as_ref().unwrap());
f9f354fc
XL
1502 test_client
1503 .args(&["run", &support_libs.len().to_string(), &prog])
1504 .args(support_libs)
1505 .args(args)
1506 .envs(env.clone());
ff7c6d11
XL
1507 self.compose_and_run(
1508 test_client,
1509 self.config.run_lib_path.to_str().unwrap(),
1510 Some(aux_dir.to_str().unwrap()),
1511 None,
1512 )
8bb4bdeb 1513 }
416331ca
XL
1514 _ if self.config.target.contains("vxworks") => {
1515 let aux_dir = self.aux_output_dir_name();
1516 let ProcArgs { prog, args } = self.make_run_args();
e1599b0c
XL
1517 let mut wr_run = Command::new("wr-run");
1518 wr_run.args(&[&prog]).args(args).envs(env.clone());
416331ca 1519 self.compose_and_run(
e1599b0c 1520 wr_run,
416331ca
XL
1521 self.config.run_lib_path.to_str().unwrap(),
1522 Some(aux_dir.to_str().unwrap()),
1523 None,
1524 )
1525 }
8bb4bdeb 1526 _ => {
a7813a04 1527 let aux_dir = self.aux_output_dir_name();
3b2f2976
XL
1528 let ProcArgs { prog, args } = self.make_run_args();
1529 let mut program = Command::new(&prog);
dfeec247 1530 program.args(args).current_dir(&self.output_base_dir()).envs(env.clone());
ff7c6d11
XL
1531 self.compose_and_run(
1532 program,
1533 self.config.run_lib_path.to_str().unwrap(),
1534 Some(aux_dir.to_str().unwrap()),
1535 None,
1536 )
a7813a04 1537 }
ff7c6d11
XL
1538 };
1539
1540 if proc_res.status.success() {
1541 // delete the executable after running it to save space.
1542 // it is ok if the deletion failed.
1543 let _ = fs::remove_file(self.make_exe_name());
a7813a04 1544 }
ff7c6d11
XL
1545
1546 proc_res
a7813a04
XL
1547 }
1548
1549 /// For each `aux-build: foo/bar` annotation, we check to find the
94222f64 1550 /// file in an `auxiliary` directory relative to the test itself.
a7813a04 1551 fn compute_aux_test_paths(&self, rel_ab: &str) -> TestPaths {
94b46f34
XL
1552 let test_ab = self
1553 .testpaths
ff7c6d11
XL
1554 .file
1555 .parent()
1556 .expect("test file path has no parent")
1557 .join("auxiliary")
1558 .join(rel_ab);
a7813a04 1559 if !test_ab.exists() {
dfeec247 1560 self.fatal(&format!("aux-build `{}` source not found", test_ab.display()))
a7813a04
XL
1561 }
1562
1563 TestPaths {
1564 file: test_ab,
94b46f34
XL
1565 relative_dir: self
1566 .testpaths
ff7c6d11 1567 .relative_dir
94b46f34 1568 .join(self.output_testname_unique())
ff7c6d11
XL
1569 .join("auxiliary")
1570 .join(rel_ab)
1571 .parent()
1572 .expect("aux-build path has no parent")
1573 .to_path_buf(),
a7813a04
XL
1574 }
1575 }
1576
e1599b0c
XL
1577 fn is_vxworks_pure_static(&self) -> bool {
1578 if self.config.target.contains("vxworks") {
1579 match env::var("RUST_VXWORKS_TEST_DYLINK") {
1580 Ok(s) => s != "1",
dfeec247 1581 _ => true,
e1599b0c
XL
1582 }
1583 } else {
1584 false
1585 }
1586 }
1587
1588 fn is_vxworks_pure_dynamic(&self) -> bool {
1589 self.config.target.contains("vxworks") && !self.is_vxworks_pure_static()
1590 }
1591
fc512014 1592 fn build_all_auxiliary(&self, rustc: &mut Command) -> PathBuf {
94b46f34
XL
1593 let aux_dir = self.aux_output_dir_name();
1594
a7813a04 1595 if !self.props.aux_builds.is_empty() {
94b46f34
XL
1596 let _ = fs::remove_dir_all(&aux_dir);
1597 create_dir_all(&aux_dir).unwrap();
a7813a04
XL
1598 }
1599
a7813a04 1600 for rel_ab in &self.props.aux_builds {
60c5eb7d 1601 self.build_auxiliary(rel_ab, &aux_dir);
a7813a04
XL
1602 }
1603
60c5eb7d
XL
1604 for (aux_name, aux_path) in &self.props.aux_crates {
1605 let is_dylib = self.build_auxiliary(&aux_path, &aux_dir);
dfeec247
XL
1606 let lib_name =
1607 get_lib_name(&aux_path.trim_end_matches(".rs").replace('-', "_"), is_dylib);
1608 rustc.arg("--extern").arg(format!("{}={}/{}", aux_name, aux_dir.display(), lib_name));
48663c56
XL
1609 }
1610
fc512014
XL
1611 aux_dir
1612 }
1613
1614 fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
1615 let aux_dir = self.build_all_auxiliary(&mut rustc);
dfeec247 1616 self.props.unset_rustc_env.clone().iter().fold(&mut rustc, |rustc, v| rustc.env_remove(v));
3b2f2976 1617 rustc.envs(self.props.rustc_env.clone());
ff7c6d11
XL
1618 self.compose_and_run(
1619 rustc,
1620 self.config.compile_lib_path.to_str().unwrap(),
1621 Some(aux_dir.to_str().unwrap()),
1622 input,
1623 )
1624 }
1625
60c5eb7d
XL
1626 /// Builds an aux dependency.
1627 ///
1628 /// Returns whether or not it is a dylib.
1629 fn build_auxiliary(&self, source_path: &str, aux_dir: &Path) -> bool {
1630 let aux_testpaths = self.compute_aux_test_paths(source_path);
17df50a5 1631 let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
60c5eb7d
XL
1632 let aux_output = TargetLocation::ThisDirectory(self.aux_output_dir_name());
1633 let aux_cx = TestCx {
1634 config: self.config,
1635 props: &aux_props,
1636 testpaths: &aux_testpaths,
1637 revision: self.revision,
1638 };
1639 // Create the directory for the stdout/stderr files.
1640 create_dir_all(aux_cx.output_base_dir()).unwrap();
dfeec247 1641 let input_file = &aux_testpaths.file;
74b04a01
XL
1642 let mut aux_rustc =
1643 aux_cx.make_compile_args(input_file, aux_output, EmitMetadata::No, AllowUnused::No);
60c5eb7d 1644
29967ef6
XL
1645 for key in &aux_props.unset_rustc_env {
1646 aux_rustc.env_remove(key);
1647 }
1648 aux_rustc.envs(aux_props.rustc_env.clone());
1649
60c5eb7d
XL
1650 let (dylib, crate_type) = if aux_props.no_prefer_dynamic {
1651 (true, None)
fc512014 1652 } else if self.config.target.contains("emscripten")
60c5eb7d
XL
1653 || (self.config.target.contains("musl")
1654 && !aux_props.force_host
1655 && !self.config.host.contains("musl"))
1656 || self.config.target.contains("wasm32")
1657 || self.config.target.contains("nvptx")
1658 || self.is_vxworks_pure_static()
f9f354fc 1659 || self.config.target.contains("sgx")
17df50a5 1660 || self.config.target.contains("bpf")
60c5eb7d
XL
1661 {
1662 // We primarily compile all auxiliary libraries as dynamic libraries
1663 // to avoid code size bloat and large binaries as much as possible
1664 // for the test suite (otherwise including libstd statically in all
1665 // executables takes up quite a bit of space).
1666 //
1667 // For targets like MUSL or Emscripten, however, there is no support for
1668 // dynamic libraries so we just go back to building a normal library. Note,
1669 // however, that for MUSL if the library is built with `force_host` then
1670 // it's ok to be a dylib as the host should always support dylibs.
1671 (false, Some("lib"))
1672 } else {
1673 (true, Some("dylib"))
1674 };
1675
1676 if let Some(crate_type) = crate_type {
1677 aux_rustc.args(&["--crate-type", crate_type]);
1678 }
1679
1680 aux_rustc.arg("-L").arg(&aux_dir);
1681
1682 let auxres = aux_cx.compose_and_run(
1683 aux_rustc,
1684 aux_cx.config.compile_lib_path.to_str().unwrap(),
1685 Some(aux_dir.to_str().unwrap()),
1686 None,
1687 );
1688 if !auxres.status.success() {
1689 self.fatal_proc_rec(
1690 &format!(
1691 "auxiliary build of {:?} failed to compile: ",
1692 aux_testpaths.file.display()
1693 ),
1694 &auxres,
1695 );
1696 }
1697 dylib
1698 }
1699
ff7c6d11
XL
1700 fn compose_and_run(
1701 &self,
1702 mut command: Command,
1703 lib_path: &str,
1704 aux_path: Option<&str>,
1705 input: Option<String>,
1706 ) -> ProcRes {
1707 let cmdline = {
3b2f2976
XL
1708 let cmdline = self.make_cmdline(&command, lib_path);
1709 logv(self.config, format!("executing {}", cmdline));
1710 cmdline
1711 };
a7813a04 1712
dfeec247 1713 command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::piped());
3b2f2976
XL
1714
1715 // Need to be sure to put both the lib_path and the aux path in the dylib
1716 // search path for the child.
3dfed10e
XL
1717 let mut path =
1718 env::split_paths(&env::var_os(dylib_env_var()).unwrap_or_default()).collect::<Vec<_>>();
3b2f2976
XL
1719 if let Some(p) = aux_path {
1720 path.insert(0, PathBuf::from(p))
1721 }
1722 path.insert(0, PathBuf::from(lib_path));
1723
1724 // Add the new dylib search path var
1725 let newpath = env::join_paths(&path).unwrap();
1726 command.env(dylib_env_var(), newpath);
1727
83c7162d 1728 let mut child = disable_error_reporting(|| command.spawn())
3dfed10e 1729 .unwrap_or_else(|_| panic!("failed to exec `{:?}`", &command));
3b2f2976 1730 if let Some(input) = input {
dfeec247 1731 child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
3b2f2976 1732 }
abe05a73 1733
dfeec247
XL
1734 let Output { status, stdout, stderr } =
1735 read2_abbreviated(child).expect("failed to read output");
3b2f2976
XL
1736
1737 let result = ProcRes {
1738 status,
abe05a73
XL
1739 stdout: String::from_utf8_lossy(&stdout).into_owned(),
1740 stderr: String::from_utf8_lossy(&stderr).into_owned(),
3b2f2976 1741 cmdline,
a7813a04
XL
1742 };
1743
3b2f2976
XL
1744 self.dump_output(&result.stdout, &result.stderr);
1745
1746 result
1747 }
1748
dfeec247 1749 fn is_rustdoc(&self) -> bool {
fc512014
XL
1750 self.config.src_base.ends_with("rustdoc-ui")
1751 || self.config.src_base.ends_with("rustdoc-js")
1752 || self.config.src_base.ends_with("rustdoc-json")
dfeec247
XL
1753 }
1754
dc9dc135
XL
1755 fn make_compile_args(
1756 &self,
1757 input_file: &Path,
1758 output_file: TargetLocation,
dfeec247 1759 emit_metadata: EmitMetadata,
74b04a01 1760 allow_unused: AllowUnused,
dc9dc135 1761 ) -> Command {
3dfed10e
XL
1762 let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary");
1763 let is_rustdoc = self.is_rustdoc() && !is_aux;
83c7162d
XL
1764 let mut rustc = if !is_rustdoc {
1765 Command::new(&self.config.rustc_path)
1766 } else {
dfeec247 1767 Command::new(&self.config.rustdoc_path.clone().expect("no rustdoc built yet"))
83c7162d 1768 };
cdc7bbd5 1769 rustc.arg(input_file);
476ff2be 1770
9fa01778
XL
1771 // Use a single thread for efficiency and a deterministic error message order
1772 rustc.arg("-Zthreads=1");
1773
476ff2be 1774 // Optionally prevent default --target if specified in test compile-flags.
dfeec247 1775 let custom_target = self.props.compile_flags.iter().any(|x| x.starts_with("--target"));
476ff2be
SL
1776
1777 if !custom_target {
dfeec247
XL
1778 let target =
1779 if self.props.force_host { &*self.config.host } else { &*self.config.target };
3b2f2976
XL
1780
1781 rustc.arg(&format!("--target={}", target));
476ff2be 1782 }
9fa01778 1783 self.set_revision_flags(&mut rustc);
a7813a04 1784
83c7162d
XL
1785 if !is_rustdoc {
1786 if let Some(ref incremental_dir) = self.props.incremental_dir {
94b46f34 1787 rustc.args(&["-C", &format!("incremental={}", incremental_dir.display())]);
83c7162d 1788 rustc.args(&["-Z", "incremental-verify-ich"]);
83c7162d 1789 }
a7813a04 1790
83c7162d
XL
1791 if self.config.mode == CodegenUnits {
1792 rustc.args(&["-Z", "human_readable_cgu_names"]);
1793 }
ff7c6d11
XL
1794 }
1795
a7813a04 1796 match self.config.mode {
5869c6ff 1797 Incremental => {
a7813a04
XL
1798 // If we are extracting and matching errors in the new
1799 // fashion, then you want JSON mode. Old-skool error
1800 // patterns still match the raw compiler output.
1801 if self.props.error_patterns.is_empty() {
3b2f2976 1802 rustc.args(&["--error-format", "json"]);
a7813a04 1803 }
dfeec247
XL
1804 rustc.arg("-Zui-testing");
1805 rustc.arg("-Zdeduplicate-diagnostics=no");
0531ce1d 1806 }
416331ca 1807 Ui => {
dfeec247 1808 if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
0531ce1d
XL
1809 rustc.args(&["--error-format", "json"]);
1810 }
cdc7bbd5 1811 rustc.arg("-Ccodegen-units=1");
dfeec247
XL
1812 rustc.arg("-Zui-testing");
1813 rustc.arg("-Zdeduplicate-diagnostics=no");
29967ef6 1814 rustc.arg("-Zemit-future-incompat-report");
a7813a04 1815 }
5bcae85e 1816 MirOpt => {
3b2f2976 1817 rustc.args(&[
6a06907d 1818 "-Copt-level=1",
3b2f2976 1819 "-Zdump-mir=all",
6a06907d 1820 "-Zmir-opt-level=4",
3dfed10e 1821 "-Zvalidate-mir",
ff7c6d11
XL
1822 "-Zdump-mir-exclude-pass-number",
1823 ]);
5bcae85e
SL
1824
1825 let mir_dump_dir = self.get_mir_dump_dir();
abe05a73 1826 let _ = fs::remove_dir_all(&mir_dump_dir);
cc61c64b 1827 create_dir_all(mir_dump_dir.as_path()).unwrap();
7cac9316 1828 let mut dir_opt = "-Zdump-mir-dir=".to_string();
5bcae85e
SL
1829 dir_opt.push_str(mir_dump_dir.to_str().unwrap());
1830 debug!("dir_opt: {:?}", dir_opt);
1831
3b2f2976 1832 rustc.arg(dir_opt);
5bcae85e 1833 }
5869c6ff
XL
1834 RunPassValgrind | Pretty | DebugInfo | Codegen | Rustdoc | RustdocJson | RunMake
1835 | CodegenUnits | JsDocTest | Assembly => {
a7813a04
XL
1836 // do not use JSON output
1837 }
1838 }
1839
dfeec247 1840 if let (false, EmitMetadata::Yes) = (is_rustdoc, emit_metadata) {
83c7162d
XL
1841 rustc.args(&["--emit", "metadata"]);
1842 }
abe05a73 1843
83c7162d 1844 if !is_rustdoc {
dfeec247 1845 if self.config.target == "wasm32-unknown-unknown" || self.is_vxworks_pure_static() {
83c7162d
XL
1846 // rustc.arg("-g"); // get any backtrace at all on errors
1847 } else if !self.props.no_prefer_dynamic {
1848 rustc.args(&["-C", "prefer-dynamic"]);
1849 }
a7813a04 1850 }
3b2f2976
XL
1851
1852 match output_file {
a7813a04 1853 TargetLocation::ThisFile(path) => {
3b2f2976 1854 rustc.arg("-o").arg(path);
a7813a04
XL
1855 }
1856 TargetLocation::ThisDirectory(path) => {
48663c56
XL
1857 if is_rustdoc {
1858 // `rustdoc` uses `-o` for the output directory.
1859 rustc.arg("-o").arg(path);
1860 } else {
1861 rustc.arg("--out-dir").arg(path);
1862 }
a7813a04 1863 }
3b2f2976
XL
1864 }
1865
83c7162d
XL
1866 match self.config.compare_mode {
1867 Some(CompareMode::Nll) => {
48663c56 1868 rustc.args(&["-Zborrowck=mir"]);
94b46f34
XL
1869 }
1870 Some(CompareMode::Polonius) => {
48663c56 1871 rustc.args(&["-Zpolonius", "-Zborrowck=mir"]);
94b46f34 1872 }
f035d41b
XL
1873 Some(CompareMode::Chalk) => {
1874 rustc.args(&["-Zchalk"]);
1875 }
fc512014 1876 Some(CompareMode::SplitDwarf) => {
5869c6ff 1877 rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
fc512014
XL
1878 }
1879 Some(CompareMode::SplitDwarfSingle) => {
5869c6ff 1880 rustc.args(&["-Csplit-debuginfo=packed", "-Zunstable-options"]);
fc512014 1881 }
94b46f34 1882 None => {}
83c7162d
XL
1883 }
1884
ba9703b0
XL
1885 // Add `-A unused` before `config` flags and in-test (`props`) flags, so that they can
1886 // overwrite this.
1887 if let AllowUnused::Yes = allow_unused {
1888 rustc.args(&["-A", "unused"]);
1889 }
1890
a7813a04 1891 if self.props.force_host {
dfeec247
XL
1892 self.maybe_add_external_args(
1893 &mut rustc,
1894 self.split_maybe_args(&self.config.host_rustcflags),
1895 );
a7813a04 1896 } else {
dfeec247
XL
1897 self.maybe_add_external_args(
1898 &mut rustc,
1899 self.split_maybe_args(&self.config.target_rustcflags),
1900 );
a1dfa0c6
XL
1901 if !is_rustdoc {
1902 if let Some(ref linker) = self.config.linker {
1903 rustc.arg(format!("-Clinker={}", linker));
1904 }
83c7162d 1905 }
abe05a73 1906 }
3b2f2976 1907
48663c56 1908 // Use dynamic musl for tests because static doesn't allow creating dylibs
dfeec247 1909 if self.config.host.contains("musl") || self.is_vxworks_pure_dynamic() {
48663c56
XL
1910 rustc.arg("-Ctarget-feature=-crt-static");
1911 }
1912
3b2f2976
XL
1913 rustc.args(&self.props.compile_flags);
1914
1915 rustc
a7813a04
XL
1916 }
1917
a7813a04 1918 fn make_exe_name(&self) -> PathBuf {
94b46f34
XL
1919 // Using a single letter here to keep the path length down for
1920 // Windows. Some test names get very long. rustc creates `rcgu`
1921 // files with the module name appended to it which can more than
1922 // double the length.
1923 let mut f = self.output_base_dir().join("a");
a7813a04 1924 // FIXME: This is using the host architecture exe suffix, not target!
c30ab7b3 1925 if self.config.target.contains("emscripten") {
83c7162d 1926 f = f.with_extra_extension("js");
abe05a73 1927 } else if self.config.target.contains("wasm32") {
83c7162d 1928 f = f.with_extra_extension("wasm");
6a06907d
XL
1929 } else if self.config.target.contains("spirv") {
1930 f = f.with_extra_extension("spv");
a7813a04 1931 } else if !env::consts::EXE_SUFFIX.is_empty() {
83c7162d 1932 f = f.with_extra_extension(env::consts::EXE_SUFFIX);
a7813a04
XL
1933 }
1934 f
1935 }
1936
1937 fn make_run_args(&self) -> ProcArgs {
1938 // If we've got another tool to run under (valgrind),
1939 // then split apart its command
1940 let mut args = self.split_maybe_args(&self.config.runtool);
1941
1942 // If this is emscripten, then run tests under nodejs
c30ab7b3 1943 if self.config.target.contains("emscripten") {
476ff2be
SL
1944 if let Some(ref p) = self.config.nodejs {
1945 args.push(p.clone());
1946 } else {
1947 self.fatal("no NodeJS binary found (--nodejs)");
1948 }
a1dfa0c6 1949 // If this is otherwise wasm, then run tests under nodejs with our
abe05a73 1950 // shim
a1dfa0c6 1951 } else if self.config.target.contains("wasm32") {
abe05a73
XL
1952 if let Some(ref p) = self.config.nodejs {
1953 args.push(p.clone());
1954 } else {
1955 self.fatal("no NodeJS binary found (--nodejs)");
1956 }
1957
dfeec247
XL
1958 let src = self
1959 .config
1960 .src_base
1961 .parent()
1962 .unwrap() // chop off `ui`
1963 .parent()
1964 .unwrap() // chop off `test`
1965 .parent()
1966 .unwrap(); // chop off `src`
abe05a73
XL
1967 args.push(src.join("src/etc/wasm32-shim.js").display().to_string());
1968 }
1969
a7813a04
XL
1970 let exe_file = self.make_exe_name();
1971
1972 // FIXME (#9639): This needs to handle non-utf8 paths
1973 args.push(exe_file.to_str().unwrap().to_owned());
1974
1975 // Add the arguments in the run_flags directive
1976 args.extend(self.split_maybe_args(&self.props.run_flags));
1977
1978 let prog = args.remove(0);
ff7c6d11 1979 ProcArgs { prog, args }
a7813a04
XL
1980 }
1981
1982 fn split_maybe_args(&self, argstr: &Option<String>) -> Vec<String> {
1983 match *argstr {
94b46f34
XL
1984 Some(ref s) => s
1985 .split(' ')
ff7c6d11 1986 .filter_map(|s| {
dfeec247 1987 if s.chars().all(|c| c.is_whitespace()) { None } else { Some(s.to_owned()) }
ff7c6d11
XL
1988 })
1989 .collect(),
1990 None => Vec::new(),
a7813a04
XL
1991 }
1992 }
1993
3b2f2976 1994 fn make_cmdline(&self, command: &Command, libpath: &str) -> String {
9fa01778 1995 use crate::util;
a7813a04
XL
1996
1997 // Linux and mac don't require adjusting the library search path
1998 if cfg!(unix) {
3b2f2976 1999 format!("{:?}", command)
a7813a04
XL
2000 } else {
2001 // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
2002 // for diagnostic purposes
2003 fn lib_path_cmd_prefix(path: &str) -> String {
dfeec247 2004 format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
a7813a04
XL
2005 }
2006
3b2f2976 2007 format!("{} {:?}", lib_path_cmd_prefix(libpath), command)
a7813a04
XL
2008 }
2009 }
2010
2011 fn dump_output(&self, out: &str, err: &str) {
dfeec247 2012 let revision = if let Some(r) = self.revision { format!("{}.", r) } else { String::new() };
cc61c64b
XL
2013
2014 self.dump_output_file(out, &format!("{}out", revision));
2015 self.dump_output_file(err, &format!("{}err", revision));
a7813a04
XL
2016 self.maybe_dump_to_stdout(out, err);
2017 }
2018
ff7c6d11 2019 fn dump_output_file(&self, out: &str, extension: &str) {
a7813a04 2020 let outfile = self.make_out_name(extension);
0731742a 2021 fs::write(&outfile, out).unwrap();
a7813a04
XL
2022 }
2023
9fa01778
XL
2024 /// Creates a filename for output with the given extension.
2025 /// E.g., `/.../testname.revision.mode/testname.extension`.
a7813a04
XL
2026 fn make_out_name(&self, extension: &str) -> PathBuf {
2027 self.output_base_name().with_extension(extension)
2028 }
2029
9fa01778
XL
2030 /// Gets the directory where auxiliary files are written.
2031 /// E.g., `/.../testname.revision.mode/auxiliary/`.
a7813a04 2032 fn aux_output_dir_name(&self) -> PathBuf {
94b46f34
XL
2033 self.output_base_dir()
2034 .join("auxiliary")
83c7162d 2035 .with_extra_extension(self.config.mode.disambiguator())
a7813a04
XL
2036 }
2037
94b46f34
XL
2038 /// Generates a unique name for the test, such as `testname.revision.mode`.
2039 fn output_testname_unique(&self) -> PathBuf {
2040 output_testname_unique(self.config, self.testpaths, self.safe_revision())
a7813a04
XL
2041 }
2042
9fa01778 2043 /// The revision, ignored for incremental compilation since it wants all revisions in
94b46f34
XL
2044 /// the same directory.
2045 fn safe_revision(&self) -> Option<&str> {
dfeec247 2046 if self.config.mode == Incremental { None } else { self.revision }
94b46f34 2047 }
a7813a04 2048
9fa01778
XL
2049 /// Gets the absolute path to the directory where all output for the given
2050 /// test/revision should reside.
2051 /// E.g., `/path/to/build/host-triple/test/ui/relative/testname.revision.mode/`.
94b46f34
XL
2052 fn output_base_dir(&self) -> PathBuf {
2053 output_base_dir(self.config, self.testpaths, self.safe_revision())
83c7162d
XL
2054 }
2055
9fa01778
XL
2056 /// Gets the absolute path to the base filename used as output for the given
2057 /// test/revision.
2058 /// E.g., `/.../relative/testname.revision.mode/testname`.
94b46f34
XL
2059 fn output_base_name(&self) -> PathBuf {
2060 output_base_name(self.config, self.testpaths, self.safe_revision())
a7813a04
XL
2061 }
2062
2063 fn maybe_dump_to_stdout(&self, out: &str, err: &str) {
2064 if self.config.verbose {
c295e0f8 2065 println!("------stdout------------------------------");
a7813a04 2066 println!("{}", out);
c295e0f8 2067 println!("------stderr------------------------------");
a7813a04
XL
2068 println!("{}", err);
2069 println!("------------------------------------------");
2070 }
2071 }
2072
2073 fn error(&self, err: &str) {
2074 match self.revision {
2075 Some(rev) => println!("\nerror in revision `{}`: {}", rev, err),
ff7c6d11 2076 None => println!("\nerror: {}", err),
a7813a04
XL
2077 }
2078 }
2079
2080 fn fatal(&self, err: &str) -> ! {
ff7c6d11 2081 self.error(err);
9fa01778
XL
2082 error!("fatal error, panic: {:?}", err);
2083 panic!("fatal error");
a7813a04
XL
2084 }
2085
2086 fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
2087 self.error(err);
fc512014
XL
2088 proc_res.fatal(None, || ());
2089 }
2090
2091 fn fatal_proc_rec_with_ctx(
2092 &self,
2093 err: &str,
2094 proc_res: &ProcRes,
2095 on_failure: impl FnOnce(Self),
2096 ) -> ! {
2097 self.error(err);
2098 proc_res.fatal(None, || on_failure(*self));
a7813a04
XL
2099 }
2100
a7813a04
XL
2101 // codegen tests (using FileCheck)
2102
2103 fn compile_test_and_save_ir(&self) -> ProcRes {
2104 let aux_dir = self.aux_output_dir_name();
3b2f2976 2105
94b46f34 2106 let output_file = TargetLocation::ThisDirectory(self.output_base_dir());
dfeec247 2107 let input_file = &self.testpaths.file;
74b04a01
XL
2108 let mut rustc =
2109 self.make_compile_args(input_file, output_file, EmitMetadata::No, AllowUnused::No);
ff7c6d11 2110 rustc.arg("-L").arg(aux_dir).arg("--emit=llvm-ir");
3b2f2976
XL
2111
2112 self.compose_and_run_compiler(rustc, None)
a7813a04
XL
2113 }
2114
532ac7d7
XL
2115 fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
2116 // This works with both `--emit asm` (as default output name for the assembly)
2117 // and `ptx-linker` because the latter can write output at requested location.
2118 let output_path = self.output_base_name().with_extension("s");
2119
2120 let output_file = TargetLocation::ThisFile(output_path.clone());
dfeec247 2121 let input_file = &self.testpaths.file;
74b04a01
XL
2122 let mut rustc =
2123 self.make_compile_args(input_file, output_file, EmitMetadata::No, AllowUnused::No);
532ac7d7
XL
2124
2125 rustc.arg("-L").arg(self.aux_output_dir_name());
2126
2127 match self.props.assembly_output.as_ref().map(AsRef::as_ref) {
2128 Some("emit-asm") => {
2129 rustc.arg("--emit=asm");
2130 }
2131
2132 Some("ptx-linker") => {
2133 // No extra flags needed.
2134 }
2135
2136 Some(_) => self.fatal("unknown 'assembly-output' header"),
2137 None => self.fatal("missing 'assembly-output' header"),
2138 }
2139
2140 (self.compose_and_run_compiler(rustc, None), output_path)
2141 }
2142
2143 fn verify_with_filecheck(&self, output: &Path) -> ProcRes {
3b2f2976 2144 let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
dfeec247 2145 filecheck.arg("--input-file").arg(output).arg(&self.testpaths.file);
9fa01778
XL
2146 // It would be more appropriate to make most of the arguments configurable through
2147 // a comment-attribute similar to `compile-flags`. For example, --check-prefixes is a very
2148 // useful flag.
2149 //
2150 // For now, though…
136023e0
XL
2151 let prefix_for_target =
2152 if self.config.target.contains("msvc") { "MSVC" } else { "NONMSVC" };
2153 let prefixes = if let Some(rev) = self.revision {
2154 format!("CHECK,{},{}", prefix_for_target, rev)
2155 } else {
2156 format!("CHECK,{}", prefix_for_target)
2157 };
2158 if self.config.llvm_version.unwrap_or(0) >= 130000 {
2159 filecheck.args(&["--allow-unused-prefixes", "--check-prefixes", &prefixes]);
2160 } else {
2161 filecheck.args(&["--check-prefixes", &prefixes]);
9fa01778 2162 }
3b2f2976 2163 self.compose_and_run(filecheck, "", None, None)
a7813a04
XL
2164 }
2165
2166 fn run_codegen_test(&self) {
a7813a04
XL
2167 if self.config.llvm_filecheck.is_none() {
2168 self.fatal("missing --llvm-filecheck");
2169 }
2170
532ac7d7 2171 let proc_res = self.compile_test_and_save_ir();
a7813a04
XL
2172 if !proc_res.status.success() {
2173 self.fatal_proc_rec("compilation failed!", &proc_res);
2174 }
2175
532ac7d7
XL
2176 let output_path = self.output_base_name().with_extension("ll");
2177 let proc_res = self.verify_with_filecheck(&output_path);
2178 if !proc_res.status.success() {
2179 self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
2180 }
2181 }
2182
2183 fn run_assembly_test(&self) {
2184 if self.config.llvm_filecheck.is_none() {
2185 self.fatal("missing --llvm-filecheck");
2186 }
2187
2188 let (proc_res, output_path) = self.compile_test_and_save_assembly();
2189 if !proc_res.status.success() {
2190 self.fatal_proc_rec("compilation failed!", &proc_res);
2191 }
2192
2193 let proc_res = self.verify_with_filecheck(&output_path);
a7813a04
XL
2194 if !proc_res.status.success() {
2195 self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
2196 }
2197 }
2198
2199 fn charset() -> &'static str {
2200 // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
dfeec247 2201 if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" }
a7813a04
XL
2202 }
2203
2204 fn run_rustdoc_test(&self) {
2205 assert!(self.revision.is_none(), "revisions not relevant here");
2206
94b46f34 2207 let out_dir = self.output_base_dir();
a7813a04 2208 let _ = fs::remove_dir_all(&out_dir);
cc61c64b 2209 create_dir_all(&out_dir).unwrap();
a7813a04
XL
2210
2211 let proc_res = self.document(&out_dir);
2212 if !proc_res.status.success() {
2213 self.fatal_proc_rec("rustdoc failed!", &proc_res);
2214 }
a7813a04 2215
041b39d2 2216 if self.props.check_test_line_numbers_match {
8bb4bdeb
XL
2217 self.check_rustdoc_test_option(proc_res);
2218 } else {
abe05a73
XL
2219 let root = self.config.find_rust_src_root().unwrap();
2220 let res = self.cmd2procres(
2221 Command::new(&self.config.docck_python)
2222 .arg(root.join("src/etc/htmldocck.py"))
fc512014 2223 .arg(&out_dir)
abe05a73
XL
2224 .arg(&self.testpaths.file),
2225 );
8bb4bdeb 2226 if !res.status.success() {
fc512014
XL
2227 self.fatal_proc_rec_with_ctx("htmldocck failed!", &res, |mut this| {
2228 this.compare_to_default_rustdoc(&out_dir)
2229 });
2230 }
2231 }
2232 }
2233
2234 fn compare_to_default_rustdoc(&mut self, out_dir: &Path) {
6a06907d
XL
2235 if !self.config.has_tidy {
2236 return;
2237 }
fc512014
XL
2238 println!("info: generating a diff against nightly rustdoc");
2239
2240 let suffix =
2241 self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly");
2242 let compare_dir = output_base_dir(self.config, self.testpaths, Some(&suffix));
2243 // Don't give an error if the directory didn't already exist
2244 let _ = fs::remove_dir_all(&compare_dir);
2245 create_dir_all(&compare_dir).unwrap();
2246
2247 // We need to create a new struct for the lifetimes on `config` to work.
2248 let new_rustdoc = TestCx {
2249 config: &Config {
2250 // FIXME: use beta or a user-specified rustdoc instead of
2251 // hardcoding the default toolchain
2252 rustdoc_path: Some("rustdoc".into()),
2253 // Needed for building auxiliary docs below
2254 rustc_path: "rustc".into(),
2255 ..self.config.clone()
2256 },
2257 ..*self
2258 };
2259
2260 let output_file = TargetLocation::ThisDirectory(new_rustdoc.aux_output_dir_name());
2261 let mut rustc = new_rustdoc.make_compile_args(
2262 &new_rustdoc.testpaths.file,
2263 output_file,
2264 EmitMetadata::No,
2265 AllowUnused::Yes,
2266 );
2267 rustc.arg("-L").arg(&new_rustdoc.aux_output_dir_name());
2268 new_rustdoc.build_all_auxiliary(&mut rustc);
2269
2270 let proc_res = new_rustdoc.document(&compare_dir);
2271 if !proc_res.status.success() {
2272 eprintln!("failed to run nightly rustdoc");
2273 return;
2274 }
2275
2276 #[rustfmt::skip]
2277 let tidy_args = [
2278 "--indent", "yes",
2279 "--indent-spaces", "2",
2280 "--wrap", "0",
2281 "--show-warnings", "no",
2282 "--markup", "yes",
2283 "--quiet", "yes",
2284 "-modify",
2285 ];
2286 let tidy_dir = |dir| {
2287 for entry in walkdir::WalkDir::new(dir) {
2288 let entry = entry.expect("failed to read file");
2289 if entry.file_type().is_file()
2290 && entry.path().extension().and_then(|p| p.to_str()) == Some("html".into())
2291 {
2292 let status =
2293 Command::new("tidy").args(&tidy_args).arg(entry.path()).status().unwrap();
2294 // `tidy` returns 1 if it modified the file.
2295 assert!(status.success() || status.code() == Some(1));
2296 }
8bb4bdeb 2297 }
fc512014 2298 };
6a06907d
XL
2299 tidy_dir(out_dir);
2300 tidy_dir(&compare_dir);
fc512014
XL
2301
2302 let pager = {
2303 let output = Command::new("git").args(&["config", "--get", "core.pager"]).output().ok();
2304 output.and_then(|out| {
2305 if out.status.success() {
2306 Some(String::from_utf8(out.stdout).expect("invalid UTF8 in git pager"))
2307 } else {
2308 None
2309 }
2310 })
2311 };
fc512014 2312
6a06907d
XL
2313 let diff_filename = format!("build/tmp/rustdoc-compare-{}.diff", std::process::id());
2314
c295e0f8
XL
2315 if !write_filtered_diff(
2316 &diff_filename,
2317 out_dir,
2318 &compare_dir,
2319 self.config.verbose,
2320 |file_type, extension| {
2321 file_type.is_file()
6a06907d 2322 && (extension == Some("html".into()) || extension == Some("js".into()))
c295e0f8
XL
2323 },
2324 ) {
2325 return;
6a06907d
XL
2326 }
2327
2328 match self.config.color {
2329 ColorConfig::AlwaysColor => colored::control::set_override(true),
2330 ColorConfig::NeverColor => colored::control::set_override(false),
2331 _ => {}
2332 }
2333
2334 if let Some(pager) = pager {
fc512014
XL
2335 let pager = pager.trim();
2336 if self.config.verbose {
2337 eprintln!("using pager {}", pager);
2338 }
2339 let output = Command::new(pager)
2340 // disable paging; we want this to be non-interactive
2341 .env("PAGER", "")
6a06907d 2342 .stdin(File::open(&diff_filename).unwrap())
fc512014
XL
2343 // Capture output and print it explicitly so it will in turn be
2344 // captured by libtest.
2345 .output()
2346 .unwrap();
2347 assert!(output.status.success());
6a06907d
XL
2348 println!("{}", String::from_utf8_lossy(&output.stdout));
2349 eprintln!("{}", String::from_utf8_lossy(&output.stderr));
fc512014 2350 } else {
6a06907d
XL
2351 use colored::Colorize;
2352 eprintln!("warning: no pager configured, falling back to unified diff");
fc512014
XL
2353 eprintln!(
2354 "help: try configuring a git pager (e.g. `delta`) with `git config --global core.pager delta`"
2355 );
6a06907d
XL
2356 let mut out = io::stdout();
2357 let mut diff = BufReader::new(File::open(&diff_filename).unwrap());
2358 let mut line = Vec::new();
2359 loop {
2360 line.truncate(0);
2361 match diff.read_until(b'\n', &mut line) {
2362 Ok(0) => break,
2363 Ok(_) => {}
2364 Err(e) => eprintln!("ERROR: {:?}", e),
2365 }
2366 match String::from_utf8(line.clone()) {
2367 Ok(line) => {
2368 if line.starts_with("+") {
2369 write!(&mut out, "{}", line.green()).unwrap();
2370 } else if line.starts_with("-") {
2371 write!(&mut out, "{}", line.red()).unwrap();
2372 } else if line.starts_with("@") {
2373 write!(&mut out, "{}", line.blue()).unwrap();
2374 } else {
2375 out.write_all(line.as_bytes()).unwrap();
2376 }
2377 }
2378 Err(_) => {
2379 write!(&mut out, "{}", String::from_utf8_lossy(&line).reversed()).unwrap();
2380 }
2381 }
2382 }
fc512014 2383 };
fc512014
XL
2384 }
2385
2386 fn run_rustdoc_json_test(&self) {
2387 //FIXME: Add bless option.
2388
2389 assert!(self.revision.is_none(), "revisions not relevant here");
2390
2391 let out_dir = self.output_base_dir();
2392 let _ = fs::remove_dir_all(&out_dir);
2393 create_dir_all(&out_dir).unwrap();
2394
2395 let proc_res = self.document(&out_dir);
2396 if !proc_res.status.success() {
2397 self.fatal_proc_rec("rustdoc failed!", &proc_res);
2398 }
2399
2400 let root = self.config.find_rust_src_root().unwrap();
2401 let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
2402 json_out.set_extension("json");
2403 let res = self.cmd2procres(
5869c6ff
XL
2404 Command::new(self.config.jsondocck_path.as_ref().unwrap())
2405 .arg("--doc-dir")
2406 .arg(root.join(&out_dir))
2407 .arg("--template")
2408 .arg(&self.testpaths.file),
fc512014
XL
2409 );
2410
2411 if !res.status.success() {
5869c6ff 2412 self.fatal_proc_rec("jsondocck failed!", &res)
fc512014
XL
2413 }
2414
5869c6ff
XL
2415 let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
2416 json_out.set_extension("json");
fc512014
XL
2417 let res = self.cmd2procres(
2418 Command::new(&self.config.docck_python)
5869c6ff
XL
2419 .arg(root.join("src/etc/check_missing_items.py"))
2420 .arg(&json_out),
fc512014
XL
2421 );
2422
2423 if !res.status.success() {
5869c6ff 2424 self.fatal_proc_rec("check_missing_items failed!", &res);
8bb4bdeb
XL
2425 }
2426 }
2427
ff7c6d11
XL
2428 fn get_lines<P: AsRef<Path>>(
2429 &self,
2430 path: &P,
2431 mut other_files: Option<&mut Vec<String>>,
2432 ) -> Vec<usize> {
0731742a 2433 let content = fs::read_to_string(&path).unwrap();
8bb4bdeb 2434 let mut ignore = false;
ff7c6d11
XL
2435 content
2436 .lines()
2437 .enumerate()
2438 .filter_map(|(line_nb, line)| {
0731742a
XL
2439 if (line.trim_start().starts_with("pub mod ")
2440 || line.trim_start().starts_with("mod "))
ff7c6d11
XL
2441 && line.ends_with(';')
2442 {
2443 if let Some(ref mut other_files) = other_files {
2444 other_files.push(line.rsplit("mod ").next().unwrap().replace(";", ""));
2445 }
2446 None
2447 } else {
2448 let sline = line.split("///").last().unwrap_or("");
0731742a 2449 let line = sline.trim_start();
ff7c6d11
XL
2450 if line.starts_with("```") {
2451 if ignore {
2452 ignore = false;
2453 None
2454 } else {
2455 ignore = true;
2456 Some(line_nb + 1)
2457 }
2458 } else {
2459 None
2460 }
2461 }
2462 })
2463 .collect()
8bb4bdeb
XL
2464 }
2465
2466 fn check_rustdoc_test_option(&self, res: ProcRes) {
2467 let mut other_files = Vec::new();
2468 let mut files: HashMap<String, Vec<usize>> = HashMap::new();
2469 let cwd = env::current_dir().unwrap();
ff7c6d11
XL
2470 files.insert(
2471 self.testpaths
2472 .file
2473 .strip_prefix(&cwd)
2474 .unwrap_or(&self.testpaths.file)
2475 .to_str()
2476 .unwrap()
2477 .replace('\\', "/"),
2478 self.get_lines(&self.testpaths.file, Some(&mut other_files)),
2479 );
8bb4bdeb
XL
2480 for other_file in other_files {
2481 let mut path = self.testpaths.file.clone();
2482 path.set_file_name(&format!("{}.rs", other_file));
ff7c6d11 2483 files.insert(
dfeec247 2484 path.strip_prefix(&cwd).unwrap_or(&path).to_str().unwrap().replace('\\', "/"),
ff7c6d11
XL
2485 self.get_lines(&path, None),
2486 );
8bb4bdeb
XL
2487 }
2488
2489 let mut tested = 0;
dfeec247 2490 for _ in res.stdout.split('\n').filter(|s| s.starts_with("test ")).inspect(|s| {
17df50a5
XL
2491 if let Some((left, right)) = s.split_once(" - ") {
2492 let path = left.rsplit("test ").next().unwrap();
dfeec247
XL
2493 if let Some(ref mut v) = files.get_mut(&path.replace('\\', "/")) {
2494 tested += 1;
17df50a5 2495 let mut iter = right.split("(line ");
dfeec247
XL
2496 iter.next();
2497 let line = iter
2498 .next()
2499 .unwrap_or(")")
2500 .split(')')
2501 .next()
2502 .unwrap_or("0")
2503 .parse()
2504 .unwrap_or(0);
2505 if let Ok(pos) = v.binary_search(&line) {
2506 v.remove(pos);
2507 } else {
2508 self.fatal_proc_rec(
2509 &format!("Not found doc test: \"{}\" in \"{}\":{:?}", s, path, v),
2510 &res,
2511 );
ff7c6d11
XL
2512 }
2513 }
dfeec247
XL
2514 }
2515 }) {}
8bb4bdeb
XL
2516 if tested == 0 {
2517 self.fatal_proc_rec(&format!("No test has been found... {:?}", files), &res);
2518 } else {
2519 for (entry, v) in &files {
041b39d2 2520 if !v.is_empty() {
ff7c6d11
XL
2521 self.fatal_proc_rec(
2522 &format!(
2523 "Not found test at line{} \"{}\":{:?}",
2524 if v.len() > 1 { "s" } else { "" },
2525 entry,
2526 v
2527 ),
2528 &res,
2529 );
8bb4bdeb
XL
2530 }
2531 }
a7813a04
XL
2532 }
2533 }
2534
2535 fn run_codegen_units_test(&self) {
2536 assert!(self.revision.is_none(), "revisions not relevant here");
2537
dfeec247 2538 let proc_res = self.compile_test(WillExecute::No, EmitMetadata::No);
a7813a04
XL
2539
2540 if !proc_res.status.success() {
2541 self.fatal_proc_rec("compilation failed!", &proc_res);
2542 }
2543
60c5eb7d 2544 self.check_no_compiler_crash(&proc_res, self.props.should_ice);
a7813a04 2545
3dfed10e
XL
2546 const PREFIX: &str = "MONO_ITEM ";
2547 const CGU_MARKER: &str = "@@";
a7813a04 2548
94b46f34 2549 let actual: Vec<MonoItem> = proc_res
a7813a04
XL
2550 .stdout
2551 .lines()
2552 .filter(|line| line.starts_with(PREFIX))
b7449926 2553 .map(|line| str_to_mono_item(line, true))
a7813a04
XL
2554 .collect();
2555
94b46f34 2556 let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None)
a7813a04 2557 .iter()
b7449926 2558 .map(|e| str_to_mono_item(&e.msg[..], false))
a7813a04
XL
2559 .collect();
2560
2561 let mut missing = Vec::new();
2562 let mut wrong_cgus = Vec::new();
2563
2564 for expected_item in &expected {
ff7c6d11 2565 let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name);
a7813a04
XL
2566
2567 if let Some(actual_item) = actual_item_with_same_name {
041b39d2
XL
2568 if !expected_item.codegen_units.is_empty() &&
2569 // Also check for codegen units
ff7c6d11
XL
2570 expected_item.codegen_units != actual_item.codegen_units
2571 {
041b39d2 2572 wrong_cgus.push((expected_item.clone(), actual_item.clone()));
a7813a04
XL
2573 }
2574 } else {
2575 missing.push(expected_item.string.clone());
2576 }
2577 }
2578
ff7c6d11
XL
2579 let unexpected: Vec<_> = actual
2580 .iter()
2581 .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
2582 .map(|acgu| acgu.string.clone())
2583 .collect();
a7813a04
XL
2584
2585 if !missing.is_empty() {
2586 missing.sort();
2587
2588 println!("\nThese items should have been contained but were not:\n");
2589
2590 for item in &missing {
2591 println!("{}", item);
2592 }
2593
2594 println!("\n");
2595 }
2596
2597 if !unexpected.is_empty() {
2598 let sorted = {
2599 let mut sorted = unexpected.clone();
2600 sorted.sort();
2601 sorted
2602 };
2603
2604 println!("\nThese items were contained but should not have been:\n");
2605
2606 for item in sorted {
2607 println!("{}", item);
2608 }
2609
2610 println!("\n");
2611 }
2612
2613 if !wrong_cgus.is_empty() {
2614 wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
2615 println!("\nThe following items were assigned to wrong codegen units:\n");
2616
2617 for &(ref expected_item, ref actual_item) in &wrong_cgus {
2618 println!("{}", expected_item.name);
dfeec247
XL
2619 println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units));
2620 println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units));
e1599b0c 2621 println!();
a7813a04
XL
2622 }
2623 }
2624
ff7c6d11 2625 if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) {
a7813a04
XL
2626 panic!();
2627 }
2628
2629 #[derive(Clone, Eq, PartialEq)]
94b46f34 2630 struct MonoItem {
a7813a04
XL
2631 name: String,
2632 codegen_units: HashSet<String>,
2633 string: String,
2634 }
2635
94b46f34 2636 // [MONO_ITEM] name [@@ (cgu)+]
b7449926 2637 fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem {
dfeec247 2638 let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() };
a7813a04 2639
b7449926 2640 let full_string = format!("{}{}", PREFIX, s);
a7813a04 2641
dfeec247
XL
2642 let parts: Vec<&str> =
2643 s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect();
a7813a04
XL
2644
2645 let name = parts[0].trim();
2646
2647 let cgus = if parts.len() > 1 {
2648 let cgus_str = parts[1];
2649
ff7c6d11
XL
2650 cgus_str
2651 .split(' ')
2652 .map(str::trim)
2653 .filter(|s| !s.is_empty())
b7449926
XL
2654 .map(|s| {
2655 if cgu_has_crate_disambiguator {
ba9703b0 2656 remove_crate_disambiguators_from_set_of_cgu_names(s)
b7449926
XL
2657 } else {
2658 s.to_string()
2659 }
2660 })
ff7c6d11
XL
2661 .collect()
2662 } else {
a7813a04
XL
2663 HashSet::new()
2664 };
2665
dfeec247 2666 MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string }
a7813a04
XL
2667 }
2668
ff7c6d11 2669 fn codegen_units_to_str(cgus: &HashSet<String>) -> String {
a7813a04
XL
2670 let mut cgus: Vec<_> = cgus.iter().collect();
2671 cgus.sort();
2672
2673 let mut string = String::new();
2674 for cgu in cgus {
2675 string.push_str(&cgu[..]);
2676 string.push_str(" ");
2677 }
2678
2679 string
2680 }
b7449926
XL
2681
2682 // Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or
2683 // the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>,
2684 // remove all crate-disambiguators.
2685 fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String {
2686 lazy_static! {
dfeec247
XL
2687 static ref RE: Regex =
2688 Regex::new(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?")
2689 .unwrap();
b7449926
XL
2690 }
2691
dfeec247
XL
2692 let captures =
2693 RE.captures(cgu).unwrap_or_else(|| panic!("invalid cgu name encountered: {}", cgu));
b7449926
XL
2694
2695 let mut new_name = cgu.to_owned();
2696
2697 if let Some(d2) = captures.name("d2") {
dfeec247 2698 new_name.replace_range(d2.start()..d2.end(), "");
b7449926
XL
2699 }
2700
2701 let d1 = captures.name("d1").unwrap();
dfeec247 2702 new_name.replace_range(d1.start()..d1.end(), "");
b7449926
XL
2703
2704 new_name
2705 }
ba9703b0
XL
2706
2707 // The name of merged CGUs is constructed as the names of the original
2708 // CGUs joined with "--". This function splits such composite CGU names
2709 // and handles each component individually.
2710 fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String {
2711 cgus.split("--")
2712 .map(|cgu| remove_crate_disambiguator_from_cgu(cgu))
2713 .collect::<Vec<_>>()
2714 .join("--")
2715 }
a7813a04
XL
2716 }
2717
2718 fn init_incremental_test(&self) {
2719 // (See `run_incremental_test` for an overview of how incremental tests work.)
2720
2721 // Before any of the revisions have executed, create the
2722 // incremental workproduct directory. Delete any old
2723 // incremental work products that may be there from prior
2724 // runs.
c295e0f8 2725 let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
a7813a04 2726 if incremental_dir.exists() {
9e0c209e
SL
2727 // Canonicalizing the path will convert it to the //?/ format
2728 // on Windows, which enables paths longer than 260 character
2729 let canonicalized = incremental_dir.canonicalize().unwrap();
2730 fs::remove_dir_all(canonicalized).unwrap();
a7813a04
XL
2731 }
2732 fs::create_dir_all(&incremental_dir).unwrap();
2733
2734 if self.config.verbose {
c295e0f8 2735 println!("init_incremental_test: incremental_dir={}", incremental_dir.display());
a7813a04
XL
2736 }
2737 }
2738
2739 fn run_incremental_test(&self) {
2740 // Basic plan for a test incremental/foo/bar.rs:
2741 // - load list of revisions rpass1, cfail2, rpass3
94b46f34 2742 // - each should begin with `rpass`, `cfail`, or `rfail`
a7813a04
XL
2743 // - if `rpass`, expect compile and execution to succeed
2744 // - if `cfail`, expect compilation to fail
2745 // - if `rfail`, expect execution to fail
2746 // - create a directory build/foo/bar.incremental
ba9703b0 2747 // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1
a7813a04 2748 // - because name of revision starts with "rpass", expect success
ba9703b0 2749 // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C cfail2
a7813a04
XL
2750 // - because name of revision starts with "cfail", expect an error
2751 // - load expected errors as usual, but filter for those that end in `[rfail2]`
ba9703b0 2752 // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass3
a7813a04
XL
2753 // - because name of revision starts with "rpass", expect success
2754 // - execute build/foo/bar.exe and save output
2755 //
2756 // FIXME -- use non-incremental mode as an oracle? That doesn't apply
2757 // to #[rustc_dirty] and clean tests I guess
2758
dfeec247 2759 let revision = self.revision.expect("incremental tests require a list of revisions");
a7813a04
XL
2760
2761 // Incremental workproduct directory should have already been created.
c295e0f8 2762 let incremental_dir = self.props.incremental_dir.as_ref().unwrap();
dfeec247 2763 assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
a7813a04 2764
a7813a04 2765 if self.config.verbose {
c295e0f8 2766 print!("revision={:?} props={:#?}", revision, self.props);
a7813a04
XL
2767 }
2768
2769 if revision.starts_with("rpass") {
c295e0f8
XL
2770 if self.props.should_ice {
2771 self.fatal("can only use should-ice in cfail tests");
60c5eb7d 2772 }
c295e0f8 2773 self.run_rpass_test();
a7813a04 2774 } else if revision.starts_with("rfail") {
c295e0f8
XL
2775 if self.props.should_ice {
2776 self.fatal("can only use should-ice in cfail tests");
60c5eb7d 2777 }
c295e0f8 2778 self.run_rfail_test();
a7813a04 2779 } else if revision.starts_with("cfail") {
c295e0f8 2780 self.run_cfail_test();
a7813a04 2781 } else {
c295e0f8 2782 self.fatal("revision name must begin with rpass, rfail, or cfail");
a7813a04
XL
2783 }
2784 }
2785
a7813a04 2786 fn run_rmake_test(&self) {
a7813a04 2787 let cwd = env::current_dir().unwrap();
dfeec247 2788 let src_root = self.config.src_base.parent().unwrap().parent().unwrap().parent().unwrap();
a7813a04
XL
2789 let src_root = cwd.join(&src_root);
2790
94b46f34 2791 let tmpdir = cwd.join(self.output_base_name());
a7813a04
XL
2792 if tmpdir.exists() {
2793 self.aggressive_rm_rf(&tmpdir).unwrap();
2794 }
cc61c64b 2795 create_dir_all(&tmpdir).unwrap();
a7813a04 2796
476ff2be 2797 let host = &self.config.host;
48663c56 2798 let make = if host.contains("dragonfly")
94b46f34
XL
2799 || host.contains("freebsd")
2800 || host.contains("netbsd")
ff7c6d11
XL
2801 || host.contains("openbsd")
2802 {
476ff2be
SL
2803 "gmake"
2804 } else {
2805 "make"
2806 };
2807
2808 let mut cmd = Command::new(make);
a7813a04 2809 cmd.current_dir(&self.testpaths.file)
ff7c6d11
XL
2810 .stdout(Stdio::piped())
2811 .stderr(Stdio::piped())
2812 .env("TARGET", &self.config.target)
2813 .env("PYTHON", &self.config.docck_python)
2814 .env("S", src_root)
2815 .env("RUST_BUILD_STAGE", &self.config.stage_id)
2816 .env("RUSTC", cwd.join(&self.config.rustc_path))
ff7c6d11
XL
2817 .env("TMPDIR", &tmpdir)
2818 .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
2819 .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
2820 .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
2821 .env("LLVM_COMPONENTS", &self.config.llvm_components)
0531ce1d 2822 // We for sure don't want these tests to run in parallel, so make
0731742a 2823 // sure they don't have access to these vars if we run via `make`
0531ce1d
XL
2824 // at the top level
2825 .env_remove("MAKEFLAGS")
2826 .env_remove("MFLAGS")
2827 .env_remove("CARGO_MAKEFLAGS");
2828
2829 if let Some(ref rustdoc) = self.config.rustdoc_path {
2830 cmd.env("RUSTDOC", cwd.join(rustdoc));
2831 }
2832
3dfed10e
XL
2833 if let Some(ref rust_demangler) = self.config.rust_demangler_path {
2834 cmd.env("RUST_DEMANGLER", cwd.join(rust_demangler));
2835 }
2836
0531ce1d
XL
2837 if let Some(ref node) = self.config.nodejs {
2838 cmd.env("NODE", node);
2839 }
a7813a04 2840
abe05a73
XL
2841 if let Some(ref linker) = self.config.linker {
2842 cmd.env("RUSTC_LINKER", linker);
2843 }
2844
9fa01778
XL
2845 if let Some(ref clang) = self.config.run_clang_based_tests_with {
2846 cmd.env("CLANG", clang);
2847 }
2848
48663c56
XL
2849 if let Some(ref filecheck) = self.config.llvm_filecheck {
2850 cmd.env("LLVM_FILECHECK", filecheck);
2851 }
2852
2853 if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir {
2854 cmd.env("LLVM_BIN_DIR", llvm_bin_dir);
2855 }
2856
32a655c1
SL
2857 // We don't want RUSTFLAGS set from the outside to interfere with
2858 // compiler flags set in the test cases:
2859 cmd.env_remove("RUSTFLAGS");
2860
48663c56
XL
2861 // Use dynamic musl for tests because static doesn't allow creating dylibs
2862 if self.config.host.contains("musl") {
dfeec247 2863 cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1");
48663c56
XL
2864 }
2865
1b1a35ee
XL
2866 if self.config.bless {
2867 cmd.env("RUSTC_BLESS_TEST", "--bless");
2868 // Assume this option is active if the environment variable is "defined", with _any_ value.
2869 // As an example, a `Makefile` can use this option by:
2870 //
2871 // ifdef RUSTC_BLESS_TEST
2872 // cp "$(TMPDIR)"/actual_something.ext expected_something.ext
2873 // else
2874 // $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext
2875 // endif
2876 }
2877
0531ce1d 2878 if self.config.target.contains("msvc") && self.config.cc != "" {
a7813a04
XL
2879 // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
2880 // and that `lib.exe` lives next to it.
2881 let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
2882
2883 // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
2884 // a path and instead passes `C:\msys64\foo`, so convert all
2885 // `/`-arguments to MSVC here to `-` arguments.
94b46f34
XL
2886 let cflags = self
2887 .config
ff7c6d11
XL
2888 .cflags
2889 .split(' ')
2890 .map(|s| s.replace("/", "-"))
2891 .collect::<Vec<_>>()
2892 .join(" ");
a7813a04
XL
2893
2894 cmd.env("IS_MSVC", "1")
ff7c6d11
XL
2895 .env("IS_WINDOWS", "1")
2896 .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
2897 .env("CC", format!("'{}' {}", self.config.cc, cflags))
94b46f34 2898 .env("CXX", format!("'{}'", &self.config.cxx));
a7813a04
XL
2899 } else {
2900 cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
ff7c6d11
XL
2901 .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags))
2902 .env("AR", &self.config.ar);
c30ab7b3
SL
2903
2904 if self.config.target.contains("windows") {
2905 cmd.env("IS_WINDOWS", "1");
2906 }
a7813a04
XL
2907 }
2908
dfeec247 2909 let output = cmd.spawn().and_then(read2_abbreviated).expect("failed to spawn `make`");
a7813a04
XL
2910 if !output.status.success() {
2911 let res = ProcRes {
7cac9316 2912 status: output.status,
a7813a04
XL
2913 stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
2914 stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
2915 cmdline: format!("{:?}", cmd),
2916 };
2917 self.fatal_proc_rec("make failed", &res);
2918 }
2919 }
2920
2921 fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
9e0c209e
SL
2922 for e in path.read_dir()? {
2923 let entry = e?;
a7813a04 2924 let path = entry.path();
9e0c209e
SL
2925 if entry.file_type()?.is_dir() {
2926 self.aggressive_rm_rf(&path)?;
a7813a04
XL
2927 } else {
2928 // Remove readonly files as well on windows (by default we can't)
9e0c209e 2929 fs::remove_file(&path).or_else(|e| {
a7813a04 2930 if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied {
9e0c209e 2931 let mut meta = entry.metadata()?.permissions();
a7813a04 2932 meta.set_readonly(false);
9e0c209e 2933 fs::set_permissions(&path, meta)?;
a7813a04
XL
2934 fs::remove_file(&path)
2935 } else {
2936 Err(e)
2937 }
9e0c209e 2938 })?;
a7813a04
XL
2939 }
2940 }
2941 fs::remove_dir(path)
2942 }
2943
9fa01778
XL
2944 fn run_js_doc_test(&self) {
2945 if let Some(nodejs) = &self.config.nodejs {
2946 let out_dir = self.output_base_dir();
2947
2948 self.document(&out_dir);
2949
2950 let root = self.config.find_rust_src_root().unwrap();
ba9703b0
XL
2951 let file_stem =
2952 self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem");
9fa01778
XL
2953 let res = self.cmd2procres(
2954 Command::new(&nodejs)
2955 .arg(root.join("src/tools/rustdoc-js/tester.js"))
ba9703b0
XL
2956 .arg("--doc-folder")
2957 .arg(out_dir)
2958 .arg("--crate-name")
2959 .arg(file_stem.replace("-", "_"))
2960 .arg("--test-file")
2961 .arg(self.testpaths.file.with_extension("js")),
9fa01778
XL
2962 );
2963 if !res.status.success() {
2964 self.fatal_proc_rec("rustdoc-js test failed!", &res);
2965 }
2966 } else {
2967 self.fatal("no nodeJS");
2968 }
2969 }
2970
dfeec247
XL
2971 fn load_compare_outputs(
2972 &self,
2973 proc_res: &ProcRes,
2974 output_kind: TestOutput,
2975 explicit_format: bool,
2976 ) -> usize {
cdc7bbd5 2977 let stderr_bits = format!("{}.stderr", get_pointer_width(&self.config.target));
e1599b0c 2978 let (stderr_kind, stdout_kind) = match output_kind {
cdc7bbd5
XL
2979 TestOutput::Compile => (
2980 {
2981 if self.props.stderr_per_bitwidth { &stderr_bits } else { UI_STDERR }
2982 },
2983 UI_STDOUT,
2984 ),
dfeec247 2985 TestOutput::Run => (UI_RUN_STDERR, UI_RUN_STDOUT),
e1599b0c
XL
2986 };
2987
2988 let expected_stderr = self.load_expected_output(stderr_kind);
2989 let expected_stdout = self.load_expected_output(stdout_kind);
2990
2991 let normalized_stdout = match output_kind {
2992 TestOutput::Run if self.config.remote_test_client.is_some() => {
2993 // When tests are run using the remote-test-client, the string
2994 // 'uploaded "$TEST_BUILD_DIR/<test_executable>, waiting for result"'
2995 // is printed to stdout by the client and then captured in the ProcRes,
2996 // so it needs to be removed when comparing the run-pass test execution output
2997 lazy_static! {
2998 static ref REMOTE_TEST_RE: Regex = Regex::new(
94222f64 2999 "^uploaded \"\\$TEST_BUILD_DIR(/[[:alnum:]_\\-.]+)+\", waiting for result\n"
dfeec247
XL
3000 )
3001 .unwrap();
e1599b0c 3002 }
dfeec247
XL
3003 REMOTE_TEST_RE
3004 .replace(
3005 &self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout),
3006 "",
3007 )
3008 .to_string()
3009 }
3010 _ => self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout),
e1599b0c
XL
3011 };
3012
3013 let stderr = if explicit_format {
3014 proc_res.stderr.clone()
3015 } else {
3016 json::extract_rendered(&proc_res.stderr)
3017 };
3018
3019 let normalized_stderr = self.normalize_output(&stderr, &self.props.normalize_stderr);
3020 let mut errors = 0;
3021 match output_kind {
3022 TestOutput::Compile => {
3023 if !self.props.dont_check_compiler_stdout {
cdc7bbd5
XL
3024 errors +=
3025 self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
e1599b0c
XL
3026 }
3027 if !self.props.dont_check_compiler_stderr {
cdc7bbd5
XL
3028 errors +=
3029 self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
e1599b0c
XL
3030 }
3031 }
3032 TestOutput::Run => {
3033 errors += self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
3034 errors += self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
3035 }
3036 }
3037 errors
3038 }
3039
a7813a04 3040 fn run_ui_test(&self) {
dfeec247
XL
3041 if let Some(FailMode::Build) = self.props.fail_mode {
3042 // Make sure a build-fail test cannot fail due to failing analysis (e.g. typeck).
3043 let pm = Some(PassMode::Check);
3044 let proc_res = self.compile_test_general(WillExecute::No, EmitMetadata::Yes, pm);
3045 self.check_if_test_should_compile(&proc_res, pm);
3046 }
3047
3048 let pm = self.pass_mode();
3049 let should_run = self.should_run(pm);
3050 let emit_metadata = self.should_emit_metadata(pm);
3051 let proc_res = self.compile_test(should_run, emit_metadata);
3052 self.check_if_test_should_compile(&proc_res, pm);
3053
ff7c6d11
XL
3054 // if the user specified a format in the ui test
3055 // print the output to the stderr file, otherwise extract
3056 // the rendered error messages from json and print them
dfeec247 3057 let explicit = self.props.compile_flags.iter().any(|s| s.contains("--error-format"));
a7813a04 3058
83c7162d 3059 let expected_fixed = self.load_expected_output(UI_FIXED);
a7813a04 3060
94b46f34
XL
3061 let modes_to_prune = vec![CompareMode::Nll];
3062 self.prune_duplicate_outputs(&modes_to_prune);
3063
e1599b0c 3064 let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit);
29967ef6 3065 let rustfix_input = json::rustfix_diagnostics_only(&proc_res.stderr);
e1599b0c 3066
83c7162d
XL
3067 if self.config.compare_mode.is_some() {
3068 // don't test rustfix with nll right now
532ac7d7
XL
3069 } else if self.config.rustfix_coverage {
3070 // Find out which tests have `MachineApplicable` suggestions but are missing
3071 // `run-rustfix` or `run-rustfix-only-machine-applicable` headers.
3072 //
3073 // This will return an empty `Vec` in case the executed test file has a
3074 // `compile-flags: --error-format=xxxx` header with a value other than `json`.
3075 let suggestions = get_suggestions_from_json(
29967ef6 3076 &rustfix_input,
532ac7d7 3077 &HashSet::new(),
dfeec247
XL
3078 Filter::MachineApplicableOnly,
3079 )
3080 .unwrap_or_default();
3dfed10e 3081 if !suggestions.is_empty()
532ac7d7 3082 && !self.props.run_rustfix
dfeec247
XL
3083 && !self.props.rustfix_only_machine_applicable
3084 {
3085 let mut coverage_file_path = self.config.build_base.clone();
3086 coverage_file_path.push("rustfix_missing_coverage.txt");
3087 debug!("coverage_file_path: {}", coverage_file_path.display());
3088
3089 let mut file = OpenOptions::new()
3090 .create(true)
3091 .append(true)
3092 .open(coverage_file_path.as_path())
3093 .expect("could not create or open file");
3094
3dfed10e 3095 if writeln!(file, "{}", self.testpaths.file.display()).is_err() {
dfeec247
XL
3096 panic!("couldn't write to {}", coverage_file_path.display());
3097 }
532ac7d7 3098 }
83c7162d
XL
3099 } else if self.props.run_rustfix {
3100 // Apply suggestions from rustc to the code itself
dfeec247 3101 let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file).unwrap();
8faf50e0 3102 let suggestions = get_suggestions_from_json(
29967ef6 3103 &rustfix_input,
8faf50e0 3104 &HashSet::new(),
b7449926
XL
3105 if self.props.rustfix_only_machine_applicable {
3106 Filter::MachineApplicableOnly
3107 } else {
3108 Filter::Everything
3109 },
dfeec247
XL
3110 )
3111 .unwrap();
136023e0
XL
3112 let fixed_code = apply_suggestions(&unfixed_code, &suggestions).unwrap_or_else(|e| {
3113 panic!(
3114 "failed to apply suggestions for {:?} with rustfix: {}",
3115 self.testpaths.file, e
3116 )
3dfed10e 3117 });
83c7162d
XL
3118
3119 errors += self.compare_output("fixed", &fixed_code, &expected_fixed);
3120 } else if !expected_fixed.is_empty() {
94b46f34
XL
3121 panic!(
3122 "the `// run-rustfix` directive wasn't found but a `*.fixed` \
3123 file was found"
3124 );
83c7162d
XL
3125 }
3126
a7813a04 3127 if errors > 0 {
94b46f34 3128 println!("To update references, rerun the tests and pass the `--bless` flag");
dfeec247
XL
3129 let relative_path_to_file =
3130 self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
ff7c6d11 3131 println!(
94b46f34
XL
3132 "To only update this specific test, also pass `--test-args {}`",
3133 relative_path_to_file.display(),
ff7c6d11
XL
3134 );
3135 self.fatal_proc_rec(
3136 &format!("{} errors occurred comparing output.", errors),
3137 &proc_res,
3138 );
a7813a04 3139 }
7cac9316 3140
ff7c6d11
XL
3141 let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
3142
dfeec247 3143 if let WillExecute::Yes = should_run {
7cac9316 3144 let proc_res = self.exec_compiled_test();
e1599b0c
XL
3145 let run_output_errors = if self.props.check_run_results {
3146 self.load_compare_outputs(&proc_res, TestOutput::Run, explicit)
3147 } else {
3148 0
3149 };
3150 if run_output_errors > 0 {
3151 self.fatal_proc_rec(
dfeec247 3152 &format!("{} errors occurred comparing run output.", run_output_errors),
e1599b0c
XL
3153 &proc_res,
3154 );
3155 }
dfeec247 3156 if self.should_run_successfully(pm) {
e74abb32
XL
3157 if !proc_res.status.success() {
3158 self.fatal_proc_rec("test run failed!", &proc_res);
3159 }
c295e0f8
XL
3160 } else if proc_res.status.success() {
3161 self.fatal_proc_rec("test run succeeded!", &proc_res);
e74abb32 3162 }
c295e0f8 3163
e74abb32
XL
3164 if !self.props.error_patterns.is_empty() {
3165 // "// error-pattern" comments
dfeec247 3166 self.check_error_patterns(&proc_res.stderr, &proc_res, pm);
7cac9316
XL
3167 }
3168 }
b7449926 3169
dfeec247
XL
3170 debug!(
3171 "run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \
b7449926 3172 proc_res.status={:?} props.error_patterns={:?}",
dfeec247
XL
3173 explicit,
3174 self.config.compare_mode,
3175 expected_errors,
3176 proc_res.status,
3177 self.props.error_patterns
3178 );
83c7162d 3179 if !explicit && self.config.compare_mode.is_none() {
60c5eb7d 3180 let check_patterns =
dfeec247 3181 should_run == WillExecute::No && !self.props.error_patterns.is_empty();
60c5eb7d 3182
dfeec247 3183 let check_annotations = !check_patterns || !expected_errors.is_empty();
60c5eb7d
XL
3184
3185 if check_patterns {
e74abb32 3186 // "// error-pattern" comments
dfeec247 3187 self.check_error_patterns(&proc_res.stderr, &proc_res, pm);
e74abb32 3188 }
60c5eb7d
XL
3189
3190 if check_annotations {
e74abb32
XL
3191 // "//~ERROR comments"
3192 self.check_expected_errors(expected_errors, &proc_res);
ff7c6d11
XL
3193 }
3194 }
83c7162d
XL
3195
3196 if self.props.run_rustfix && self.config.compare_mode.is_none() {
3197 // And finally, compile the fixed code and make sure it both
3198 // succeeds and has no diagnostics.
3199 let mut rustc = self.make_compile_args(
3200 &self.testpaths.file.with_extension(UI_FIXED),
3201 TargetLocation::ThisFile(self.make_exe_name()),
dfeec247 3202 emit_metadata,
74b04a01 3203 AllowUnused::No,
83c7162d
XL
3204 );
3205 rustc.arg("-L").arg(&self.aux_output_dir_name());
3206 let res = self.compose_and_run_compiler(rustc, None);
3207 if !res.status.success() {
3208 self.fatal_proc_rec("failed to compile fixed code", &res);
3209 }
c295e0f8
XL
3210 if !res.stderr.is_empty()
3211 && !self.props.rustfix_only_machine_applicable
3212 && !json::rustfix_diagnostics_only(&res.stderr).is_empty()
3213 {
3214 self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
83c7162d
XL
3215 }
3216 }
a7813a04
XL
3217 }
3218
5bcae85e 3219 fn run_mir_opt_test(&self) {
ba9703b0
XL
3220 let pm = self.pass_mode();
3221 let should_run = self.should_run(pm);
3222 let emit_metadata = self.should_emit_metadata(pm);
3223 let proc_res = self.compile_test(should_run, emit_metadata);
5bcae85e
SL
3224
3225 if !proc_res.status.success() {
3226 self.fatal_proc_rec("compilation failed!", &proc_res);
3227 }
3228
ba9703b0 3229 self.check_mir_dump();
5bcae85e 3230
ba9703b0
XL
3231 if let WillExecute::Yes = should_run {
3232 let proc_res = self.exec_compiled_test();
3233
3234 if !proc_res.status.success() {
3235 self.fatal_proc_rec("test run failed!", &proc_res);
3236 }
5bcae85e 3237 }
5bcae85e
SL
3238 }
3239
3240 fn check_mir_dump(&self) {
0731742a 3241 let test_file_contents = fs::read_to_string(&self.testpaths.file).unwrap();
ba9703b0 3242
3dfed10e
XL
3243 let test_dir = self.testpaths.file.parent().unwrap();
3244 let test_crate =
3245 self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace("-", "_");
ba9703b0 3246
3dfed10e 3247 let mut bit_width = String::new();
ba9703b0 3248 if test_file_contents.lines().any(|l| l == "// EMIT_MIR_FOR_EACH_BIT_WIDTH") {
3dfed10e 3249 bit_width = format!(".{}", get_pointer_width(&self.config.target));
ba9703b0
XL
3250 }
3251
3252 if self.config.bless {
3dfed10e 3253 for e in
1b1a35ee 3254 glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, bit_width)).unwrap()
3dfed10e
XL
3255 {
3256 std::fs::remove_file(e.unwrap()).unwrap();
3257 }
3258 for e in
1b1a35ee 3259 glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, bit_width)).unwrap()
3dfed10e
XL
3260 {
3261 std::fs::remove_file(e.unwrap()).unwrap();
3262 }
ba9703b0 3263 }
3dfed10e 3264
ba9703b0
XL
3265 for l in test_file_contents.lines() {
3266 if l.starts_with("// EMIT_MIR ") {
3dfed10e
XL
3267 let test_name = l.trim_start_matches("// EMIT_MIR ").trim();
3268 let mut test_names = test_name.split(' ');
3269 // sometimes we specify two files so that we get a diff between the two files
3270 let test_name = test_names.next().unwrap();
3271 let mut expected_file;
3272 let from_file;
3273 let to_file;
3274
3275 if test_name.ends_with(".diff") {
3276 let trimmed = test_name.trim_end_matches(".diff");
3277 let test_against = format!("{}.after.mir", trimmed);
3278 from_file = format!("{}.before.mir", trimmed);
1b1a35ee 3279 expected_file = format!("{}{}.diff", trimmed, bit_width);
3dfed10e
XL
3280 assert!(
3281 test_names.next().is_none(),
3282 "two mir pass names specified for MIR diff"
ba9703b0 3283 );
3dfed10e
XL
3284 to_file = Some(test_against);
3285 } else if let Some(first_pass) = test_names.next() {
3286 let second_pass = test_names.next().unwrap();
3287 assert!(
3288 test_names.next().is_none(),
3289 "three mir pass names specified for MIR diff"
3290 );
3291 expected_file =
3292 format!("{}{}.{}-{}.diff", test_name, bit_width, first_pass, second_pass);
3293 let second_file = format!("{}.{}.mir", test_name, second_pass);
3294 from_file = format!("{}.{}.mir", test_name, first_pass);
3295 to_file = Some(second_file);
3296 } else {
29967ef6
XL
3297 let ext_re = Regex::new(r#"(\.(mir|dot|html))$"#).unwrap();
3298 let cap = ext_re
3299 .captures_iter(test_name)
3300 .next()
3301 .expect("test_name has an invalid extension");
3302 let extension = cap.get(1).unwrap().as_str();
3303 expected_file = format!(
3304 "{}{}{}",
3305 test_name.trim_end_matches(extension),
3306 bit_width,
3307 extension,
3308 );
3dfed10e
XL
3309 from_file = test_name.to_string();
3310 assert!(
3311 test_names.next().is_none(),
3312 "two mir pass names specified for MIR dump"
3313 );
3314 to_file = None;
3315 };
3316 if !expected_file.starts_with(&test_crate) {
3317 expected_file = format!("{}.{}", test_crate, expected_file);
3318 }
3319 let expected_file = test_dir.join(expected_file);
3320
3321 let dumped_string = if let Some(after) = to_file {
3322 self.diff_mir_files(from_file.into(), after.into())
ba9703b0
XL
3323 } else {
3324 let mut output_file = PathBuf::new();
3325 output_file.push(self.get_mir_dump_dir());
3dfed10e 3326 output_file.push(&from_file);
ba9703b0
XL
3327 debug!(
3328 "comparing the contents of: {} with {}",
3329 output_file.display(),
3330 expected_file.display()
3331 );
3332 if !output_file.exists() {
3333 panic!(
3334 "Output file `{}` from test does not exist, available files are in `{}`",
3335 output_file.display(),
3336 output_file.parent().unwrap().display()
3337 );
3338 }
3dfed10e 3339 self.check_mir_test_timestamp(&from_file, &output_file);
ba9703b0
XL
3340 let dumped_string = fs::read_to_string(&output_file).unwrap();
3341 self.normalize_output(&dumped_string, &[])
3342 };
3dfed10e 3343
ba9703b0 3344 if self.config.bless {
ba9703b0
XL
3345 let _ = std::fs::remove_file(&expected_file);
3346 std::fs::write(expected_file, dumped_string.as_bytes()).unwrap();
3347 } else {
3348 if !expected_file.exists() {
3349 panic!(
3350 "Output file `{}` from test does not exist",
3351 expected_file.display()
3352 );
3353 }
3354 let expected_string = fs::read_to_string(&expected_file).unwrap();
3355 if dumped_string != expected_string {
3dfed10e 3356 print!("{}", write_diff(&expected_string, &dumped_string, 3));
ba9703b0
XL
3357 panic!(
3358 "Actual MIR output differs from expected MIR output {}",
3359 expected_file.display()
3360 );
5bcae85e 3361 }
5bcae85e
SL
3362 }
3363 }
3364 }
3365 }
3366
3dfed10e
XL
3367 fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String {
3368 let to_full_path = |path: PathBuf| {
3369 let full = self.get_mir_dump_dir().join(&path);
3370 if !full.exists() {
3371 panic!(
3372 "the mir dump file for {} does not exist (requested in {})",
3373 path.display(),
3374 self.testpaths.file.display(),
3375 );
3376 }
3377 full
3378 };
3379 let before = to_full_path(before);
3380 let after = to_full_path(after);
3381 debug!("comparing the contents of: {} with {}", before.display(), after.display());
3382 let before = fs::read_to_string(before).unwrap();
3383 let after = fs::read_to_string(after).unwrap();
3384 let before = self.normalize_output(&before, &[]);
3385 let after = self.normalize_output(&after, &[]);
3386 let mut dumped_string = String::new();
3387 for result in diff::lines(&before, &after) {
3388 use std::fmt::Write;
3389 match result {
3390 diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(),
3391 diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(),
3392 diff::Result::Both(s, _) => writeln!(dumped_string, " {}", s).unwrap(),
3393 }
3394 }
3395 dumped_string
3396 }
3397
8bb4bdeb 3398 fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) {
48663c56 3399 let t = |file| fs::metadata(file).unwrap().modified().unwrap();
8bb4bdeb
XL
3400 let source_file = &self.testpaths.file;
3401 let output_time = t(output_file);
3402 let source_time = t(source_file);
3403 if source_time > output_time {
dfeec247 3404 debug!("source file time: {:?} output file time: {:?}", source_time, output_time);
ff7c6d11
XL
3405 panic!(
3406 "test source file `{}` is newer than potentially stale output file `{}`.",
3407 source_file.display(),
3408 test_name
3409 );
8bb4bdeb
XL
3410 }
3411 }
3412
5bcae85e 3413 fn get_mir_dump_dir(&self) -> PathBuf {
abe05a73 3414 let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path());
5bcae85e 3415 debug!("input_file: {:?}", self.testpaths.file);
abe05a73
XL
3416 mir_dump_dir.push(&self.testpaths.relative_dir);
3417 mir_dump_dir.push(self.testpaths.file.file_stem().unwrap());
5bcae85e
SL
3418 mir_dump_dir
3419 }
3420
041b39d2 3421 fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
abe05a73 3422 let cflags = self.props.compile_flags.join(" ");
ff7c6d11
XL
3423 let json = cflags.contains("--error-format json")
3424 || cflags.contains("--error-format pretty-json")
3425 || cflags.contains("--error-format=json")
ba9703b0
XL
3426 || cflags.contains("--error-format=pretty-json")
3427 || cflags.contains("--output-format json")
3428 || cflags.contains("--output-format=json");
48663c56
XL
3429
3430 let mut normalized = output.to_string();
3431
3432 let mut normalize_path = |from: &Path, to: &str| {
3433 let mut from = from.display().to_string();
3434 if json {
3435 from = from.replace("\\", "\\\\");
3436 }
3437 normalized = normalized.replace(&from, to);
abe05a73
XL
3438 };
3439
48663c56
XL
3440 let parent_dir = self.testpaths.file.parent().unwrap();
3441 normalize_path(parent_dir, "$DIR");
ff7c6d11 3442
94b46f34 3443 // Paths into the libstd/libcore
3dfed10e
XL
3444 let src_dir = self
3445 .config
3446 .src_base
3447 .parent()
3448 .unwrap()
3449 .parent()
3450 .unwrap()
3451 .parent()
3452 .unwrap()
3453 .join("library");
3454 normalize_path(&src_dir, "$SRC_DIR");
94b46f34 3455
17df50a5
XL
3456 if let Some(virtual_rust_source_base_dir) =
3457 option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from)
3458 {
3459 normalize_path(&virtual_rust_source_base_dir.join("library"), "$SRC_DIR");
3460 }
3461
b7449926
XL
3462 // Paths into the build directory
3463 let test_build_dir = &self.config.build_base;
3464 let parent_build_dir = test_build_dir.parent().unwrap().parent().unwrap().parent().unwrap();
3465
3466 // eg. /home/user/rust/build/x86_64-unknown-linux-gnu/test/ui
48663c56 3467 normalize_path(test_build_dir, "$TEST_BUILD_DIR");
b7449926 3468 // eg. /home/user/rust/build
48663c56 3469 normalize_path(parent_build_dir, "$BUILD_DIR");
b7449926
XL
3470
3471 // Paths into lib directory.
48663c56 3472 normalize_path(&parent_build_dir.parent().unwrap().join("lib"), "$LIB_DIR");
b7449926 3473
ff7c6d11
XL
3474 if json {
3475 // escaped newlines in json strings should be readable
3476 // in the stderr files. There's no point int being correct,
3477 // since only humans process the stderr files.
3478 // Thus we just turn escaped newlines back into newlines.
3479 normalized = normalized.replace("\\n", "\n");
3480 }
3481
b7449926
XL
3482 // If there are `$SRC_DIR` normalizations with line and column numbers, then replace them
3483 // with placeholders as we do not want tests needing updated when compiler source code
3484 // changes.
3485 // eg. $SRC_DIR/libcore/mem.rs:323:14 becomes $SRC_DIR/libcore/mem.rs:LL:COL
ba9703b0 3486 normalized = Regex::new("SRC_DIR(.+):\\d+:\\d+(: \\d+:\\d+)?")
dfeec247
XL
3487 .unwrap()
3488 .replace_all(&normalized, "SRC_DIR$1:LL:COL")
3489 .into_owned();
b7449926 3490
532ac7d7
XL
3491 normalized = Self::normalize_platform_differences(&normalized);
3492 normalized = normalized.replace("\t", "\\t"); // makes tabs visible
3493
3494 // Remove test annotations like `//~ ERROR text` from the output,
3495 // since they duplicate actual errors and make the output hard to read.
cdc7bbd5
XL
3496 // This mirrors the regex in src/tools/tidy/src/style.rs, please update
3497 // both if either are changed.
dfeec247
XL
3498 normalized =
3499 Regex::new("\\s*//(\\[.*\\])?~.*").unwrap().replace_all(&normalized, "").into_owned();
532ac7d7 3500
041b39d2 3501 for rule in custom_rules {
ff7c6d11
XL
3502 let re = Regex::new(&rule.0).expect("bad regex in custom normalization rule");
3503 normalized = re.replace_all(&normalized, &rule.1[..]).into_owned();
041b39d2
XL
3504 }
3505 normalized
a7813a04
XL
3506 }
3507
532ac7d7
XL
3508 /// Normalize output differences across platforms. Generally changes Windows output to be more
3509 /// Unix-like.
3510 ///
3511 /// Replaces backslashes in paths with forward slashes, and replaces CRLF line endings
3512 /// with LF.
3513 fn normalize_platform_differences(output: &str) -> String {
3514 lazy_static! {
3515 /// Used to find Windows paths.
3516 ///
3517 /// It's not possible to detect paths in the error messages generally, but this is a
3518 /// decent enough heuristic.
3519 static ref PATH_BACKSLASH_RE: Regex = Regex::new(r#"(?x)
3520 (?:
3521 # Match paths that don't include spaces.
3522 (?:\\[\pL\pN\.\-_']+)+\.\pL+
3523 |
3524 # If the path starts with a well-known root, then allow spaces.
3525 \$(?:DIR|SRC_DIR|TEST_BUILD_DIR|BUILD_DIR|LIB_DIR)(?:\\[\pL\pN\.\-_' ]+)+
3526 )"#
3527 ).unwrap();
3528 }
3529
3530 let output = output.replace(r"\\", r"\");
3531
dfeec247
XL
3532 PATH_BACKSLASH_RE
3533 .replace_all(&output, |caps: &Captures<'_>| {
3534 println!("{}", &caps[0]);
3535 caps[0].replace(r"\", "/")
3536 })
3537 .replace("\r\n", "\n")
532ac7d7
XL
3538 }
3539
a7813a04 3540 fn expected_output_path(&self, kind: &str) -> PathBuf {
dfeec247
XL
3541 let mut path =
3542 expected_output_path(&self.testpaths, self.revision, &self.config.compare_mode, kind);
94b46f34
XL
3543
3544 if !path.exists() {
3545 if let Some(CompareMode::Polonius) = self.config.compare_mode {
3546 path = expected_output_path(
3547 &self.testpaths,
3548 self.revision,
3549 &Some(CompareMode::Nll),
3550 kind,
3551 );
3552 }
3553 }
3554
3555 if !path.exists() {
83c7162d
XL
3556 path = expected_output_path(&self.testpaths, self.revision, &None, kind);
3557 }
3558
3559 path
a7813a04
XL
3560 }
3561
83c7162d
XL
3562 fn load_expected_output(&self, kind: &str) -> String {
3563 let path = self.expected_output_path(kind);
3564 if path.exists() {
3565 match self.load_expected_output_from_path(&path) {
3566 Ok(x) => x,
3567 Err(x) => self.fatal(&x),
3568 }
3569 } else {
3570 String::new()
a7813a04 3571 }
83c7162d 3572 }
a7813a04 3573
83c7162d 3574 fn load_expected_output_from_path(&self, path: &Path) -> Result<String, String> {
0731742a
XL
3575 fs::read_to_string(path).map_err(|err| {
3576 format!("failed to load expected output from `{}`: {}", path.display(), err)
3577 })
a7813a04
XL
3578 }
3579
94b46f34 3580 fn delete_file(&self, file: &PathBuf) {
f9f354fc
XL
3581 if !file.exists() {
3582 // Deleting a nonexistant file would error.
3583 return;
3584 }
0731742a 3585 if let Err(e) = fs::remove_file(file) {
dfeec247 3586 self.fatal(&format!("failed to delete `{}`: {}", file.display(), e,));
94b46f34
XL
3587 }
3588 }
3589
a7813a04
XL
3590 fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
3591 if actual == expected {
3592 return 0;
3593 }
3594
94b46f34
XL
3595 if !self.config.bless {
3596 if expected.is_empty() {
3597 println!("normalized {}:\n{}\n", kind, actual);
3598 } else {
3599 println!("diff of {}:\n", kind);
3dfed10e 3600 print!("{}", write_diff(expected, actual, 3));
7cac9316 3601 }
a7813a04
XL
3602 }
3603
83c7162d 3604 let mode = self.config.compare_mode.as_ref().map_or("", |m| m.to_str());
94b46f34
XL
3605 let output_file = self
3606 .output_base_name()
83c7162d
XL
3607 .with_extra_extension(self.revision.unwrap_or(""))
3608 .with_extra_extension(mode)
3609 .with_extra_extension(kind);
3610
94b46f34
XL
3611 let mut files = vec![output_file];
3612 if self.config.bless {
6a06907d
XL
3613 // Delete non-revision .stderr/.stdout file if revisions are used.
3614 // Without this, we'd just generate the new files and leave the old files around.
3615 if self.revision.is_some() {
3616 let old =
3617 expected_output_path(self.testpaths, None, &self.config.compare_mode, kind);
3618 self.delete_file(&old);
3619 }
94b46f34
XL
3620 files.push(expected_output_path(
3621 self.testpaths,
3622 self.revision,
3623 &self.config.compare_mode,
ff7c6d11 3624 kind,
94b46f34
XL
3625 ));
3626 }
3627
3628 for output_file in &files {
3629 if actual.is_empty() {
3630 self.delete_file(output_file);
0731742a
XL
3631 } else if let Err(err) = fs::write(&output_file, &actual) {
3632 self.fatal(&format!(
3633 "failed to write {} to `{}`: {}",
3634 kind,
3635 output_file.display(),
3636 err,
3637 ));
94b46f34 3638 }
a7813a04
XL
3639 }
3640
3641 println!("\nThe actual {0} differed from the expected {0}.", kind);
94b46f34
XL
3642 for output_file in files {
3643 println!("Actual {} saved to {}", kind, output_file.display());
3644 }
dfeec247 3645 if self.config.bless { 0 } else { 1 }
94b46f34
XL
3646 }
3647
3648 fn prune_duplicate_output(&self, mode: CompareMode, kind: &str, canon_content: &str) {
dfeec247 3649 let examined_path = expected_output_path(&self.testpaths, self.revision, &Some(mode), kind);
94b46f34 3650
dfeec247
XL
3651 let examined_content =
3652 self.load_expected_output_from_path(&examined_path).unwrap_or_else(|_| String::new());
94b46f34 3653
3dfed10e 3654 if canon_content == examined_content {
94b46f34
XL
3655 self.delete_file(&examined_path);
3656 }
3657 }
3658
3659 fn prune_duplicate_outputs(&self, modes: &[CompareMode]) {
3660 if self.config.bless {
3661 for kind in UI_EXTENSIONS {
dfeec247
XL
3662 let canon_comparison_path =
3663 expected_output_path(&self.testpaths, self.revision, &None, kind);
94b46f34
XL
3664
3665 if let Ok(canon) = self.load_expected_output_from_path(&canon_comparison_path) {
3666 for mode in modes {
3667 self.prune_duplicate_output(mode.clone(), kind, &canon);
3668 }
3669 }
3670 }
3671 }
3672 }
3673
3674 fn create_stamp(&self) {
9fa01778 3675 let stamp = crate::stamp(&self.config, self.testpaths, self.revision);
0731742a 3676 fs::write(&stamp, compute_stamp_hash(&self.config)).unwrap();
a7813a04
XL
3677 }
3678}
3679
3680struct ProcArgs {
3681 prog: String,
3682 args: Vec<String>,
3683}
3684
3685pub struct ProcRes {
7cac9316 3686 status: ExitStatus,
a7813a04
XL
3687 stdout: String,
3688 stderr: String,
3689 cmdline: String,
3690}
3691
a7813a04 3692impl ProcRes {
fc512014 3693 pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! {
a7813a04
XL
3694 if let Some(e) = err {
3695 println!("\nerror: {}", e);
3696 }
ff7c6d11
XL
3697 print!(
3698 "\
3699 status: {}\n\
3700 command: {}\n\
3701 stdout:\n\
3702 ------------------------------------------\n\
3703 {}\n\
3704 ------------------------------------------\n\
3705 stderr:\n\
3706 ------------------------------------------\n\
3707 {}\n\
3708 ------------------------------------------\n\
3709 \n",
dfeec247
XL
3710 self.status,
3711 self.cmdline,
48663c56
XL
3712 json::extract_rendered(&self.stdout),
3713 json::extract_rendered(&self.stderr),
ff7c6d11 3714 );
fc512014 3715 on_failure();
48663c56
XL
3716 // Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
3717 // compiletest, which is unnecessary noise.
3718 std::panic::resume_unwind(Box::new(()));
a7813a04
XL
3719 }
3720}
3721
3dfed10e 3722#[derive(Debug)]
a7813a04
XL
3723enum TargetLocation {
3724 ThisFile(PathBuf),
3725 ThisDirectory(PathBuf),
3726}
3727
74b04a01
XL
3728enum AllowUnused {
3729 Yes,
3730 No,
3731}