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