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