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