]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/runtest.rs
Modify MIR testing to require continuous lines
[rust.git] / src / tools / compiletest / src / runtest.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use common::Config;
12 use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
13 use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
14 use common::{Incremental, RunMake, Ui, MirOpt};
15 use diff;
16 use errors::{self, ErrorKind, Error};
17 use filetime::FileTime;
18 use json;
19 use header::TestProps;
20 use test::TestPaths;
21 use util::logv;
22
23 use std::collections::HashMap;
24 use std::collections::HashSet;
25 use std::env;
26 use std::ffi::OsString;
27 use std::fs::{self, File, create_dir_all};
28 use std::fmt;
29 use std::io::prelude::*;
30 use std::io::{self, BufReader};
31 use std::path::{Path, PathBuf};
32 use std::process::{Command, Output, ExitStatus, Stdio};
33 use std::str;
34
35 use extract_gdb_version;
36
37 /// The name of the environment variable that holds dynamic library locations.
38 pub fn dylib_env_var() -> &'static str {
39     if cfg!(windows) {
40         "PATH"
41     } else if cfg!(target_os = "macos") {
42         "DYLD_LIBRARY_PATH"
43     } else if cfg!(target_os = "haiku") {
44         "LIBRARY_PATH"
45     } else {
46         "LD_LIBRARY_PATH"
47     }
48 }
49
50 pub fn run(config: Config, testpaths: &TestPaths) {
51     match &*config.target {
52
53         "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
54             if !config.adb_device_status {
55                 panic!("android device not available");
56             }
57         }
58
59         _ => {
60             // android has its own gdb handling
61             if config.mode == DebugInfoGdb && config.gdb.is_none() {
62                 panic!("gdb not available but debuginfo gdb debuginfo test requested");
63             }
64         }
65     }
66
67     if config.verbose {
68         // We're going to be dumping a lot of info. Start on a new line.
69         print!("\n\n");
70     }
71     debug!("running {:?}", testpaths.file.display());
72     let base_props = TestProps::from_file(&testpaths.file, &config);
73
74     let base_cx = TestCx { config: &config,
75                            props: &base_props,
76                            testpaths,
77                            revision: None };
78     base_cx.init_all();
79
80     if base_props.revisions.is_empty() {
81         base_cx.run_revision()
82     } else {
83         for revision in &base_props.revisions {
84             let mut revision_props = base_props.clone();
85             revision_props.load_from(&testpaths.file, Some(revision), &config);
86             let rev_cx = TestCx {
87                 config: &config,
88                 props: &revision_props,
89                 testpaths,
90                 revision: Some(revision)
91             };
92             rev_cx.run_revision();
93         }
94     }
95
96     base_cx.complete_all();
97
98     File::create(::stamp(&config, testpaths)).unwrap();
99 }
100
101 struct TestCx<'test> {
102     config: &'test Config,
103     props: &'test TestProps,
104     testpaths: &'test TestPaths,
105     revision: Option<&'test str>
106 }
107
108 struct DebuggerCommands {
109     commands: Vec<String>,
110     check_lines: Vec<String>,
111     breakpoint_lines: Vec<usize>,
112 }
113
114 impl<'test> TestCx<'test> {
115     /// invoked once before any revisions have been processed
116     fn init_all(&self) {
117         assert!(self.revision.is_none(), "init_all invoked for a revision");
118         if let Incremental = self.config.mode {
119             self.init_incremental_test()
120         }
121     }
122
123     /// Code executed for each revision in turn (or, if there are no
124     /// revisions, exactly once, with revision == None).
125     fn run_revision(&self) {
126         match self.config.mode {
127             CompileFail |
128             ParseFail => self.run_cfail_test(),
129             RunFail => self.run_rfail_test(),
130             RunPass => self.run_rpass_test(),
131             RunPassValgrind => self.run_valgrind_test(),
132             Pretty => self.run_pretty_test(),
133             DebugInfoGdb => self.run_debuginfo_gdb_test(),
134             DebugInfoLldb => self.run_debuginfo_lldb_test(),
135             Codegen => self.run_codegen_test(),
136             Rustdoc => self.run_rustdoc_test(),
137             CodegenUnits => self.run_codegen_units_test(),
138             Incremental => self.run_incremental_test(),
139             RunMake => self.run_rmake_test(),
140             Ui => self.run_ui_test(),
141             MirOpt => self.run_mir_opt_test(),
142         }
143     }
144
145     /// Invoked after all revisions have executed.
146     fn complete_all(&self) {
147         assert!(self.revision.is_none(), "init_all invoked for a revision");
148     }
149
150     fn run_cfail_test(&self) {
151         let proc_res = self.compile_test();
152
153         if self.props.must_compile_successfully {
154             if !proc_res.status.success() {
155                 self.fatal_proc_rec(
156                     "test compilation failed although it shouldn't!",
157                     &proc_res);
158             }
159         } else {
160             if proc_res.status.success() {
161                 self.fatal_proc_rec(
162                     &format!("{} test compiled successfully!", self.config.mode)[..],
163                     &proc_res);
164             }
165
166             self.check_correct_failure_status(&proc_res);
167         }
168
169         let output_to_check = self.get_output(&proc_res);
170         let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
171         if !expected_errors.is_empty() {
172             if !self.props.error_patterns.is_empty() {
173                 self.fatal("both error pattern and expected errors specified");
174             }
175             self.check_expected_errors(expected_errors, &proc_res);
176         } else {
177             self.check_error_patterns(&output_to_check, &proc_res);
178         }
179
180         self.check_no_compiler_crash(&proc_res);
181         self.check_forbid_output(&output_to_check, &proc_res);
182     }
183
184     fn run_rfail_test(&self) {
185         let proc_res = self.compile_test();
186
187         if !proc_res.status.success() {
188             self.fatal_proc_rec("compilation failed!", &proc_res);
189         }
190
191         let proc_res = self.exec_compiled_test();
192
193         // The value our Makefile configures valgrind to return on failure
194         const VALGRIND_ERR: i32 = 100;
195         if proc_res.status.code() == Some(VALGRIND_ERR) {
196             self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
197         }
198
199         let output_to_check = self.get_output(&proc_res);
200         self.check_correct_failure_status(&proc_res);
201         self.check_error_patterns(&output_to_check, &proc_res);
202     }
203
204     fn get_output(&self, proc_res: &ProcRes) -> String {
205         if self.props.check_stdout {
206             format!("{}{}", proc_res.stdout, proc_res.stderr)
207         } else {
208             proc_res.stderr.clone()
209         }
210     }
211
212     fn check_correct_failure_status(&self, proc_res: &ProcRes) {
213         // The value the rust runtime returns on failure
214         const RUST_ERR: i32 = 101;
215         if proc_res.status.code() != Some(RUST_ERR) {
216             self.fatal_proc_rec(
217                 &format!("failure produced the wrong error: {}",
218                          proc_res.status),
219                 proc_res);
220         }
221     }
222
223     fn run_rpass_test(&self) {
224         let proc_res = self.compile_test();
225
226         if !proc_res.status.success() {
227             self.fatal_proc_rec("compilation failed!", &proc_res);
228         }
229
230         // FIXME(#41968): Move this check to tidy?
231         let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
232         assert!(expected_errors.is_empty(),
233                 "run-pass tests with expected warnings should be moved to ui/");
234
235         let proc_res = self.exec_compiled_test();
236
237         if !proc_res.status.success() {
238             self.fatal_proc_rec("test run failed!", &proc_res);
239         }
240     }
241
242     fn run_valgrind_test(&self) {
243         assert!(self.revision.is_none(), "revisions not relevant here");
244
245         if self.config.valgrind_path.is_none() {
246             assert!(!self.config.force_valgrind);
247             return self.run_rpass_test();
248         }
249
250         let mut proc_res = self.compile_test();
251
252         if !proc_res.status.success() {
253             self.fatal_proc_rec("compilation failed!", &proc_res);
254         }
255
256         let mut new_config = self.config.clone();
257         new_config.runtool = new_config.valgrind_path.clone();
258         let new_cx = TestCx { config: &new_config, ..*self };
259         proc_res = new_cx.exec_compiled_test();
260
261         if !proc_res.status.success() {
262             self.fatal_proc_rec("test run failed!", &proc_res);
263         }
264     }
265
266     fn run_pretty_test(&self) {
267         if self.props.pp_exact.is_some() {
268             logv(self.config, "testing for exact pretty-printing".to_owned());
269         } else {
270             logv(self.config, "testing for converging pretty-printing".to_owned());
271         }
272
273         let rounds = match self.props.pp_exact { Some(_) => 1, None => 2 };
274
275         let mut src = String::new();
276         File::open(&self.testpaths.file).unwrap().read_to_string(&mut src).unwrap();
277         let mut srcs = vec![src];
278
279         let mut round = 0;
280         while round < rounds {
281             logv(self.config, format!("pretty-printing round {} revision {:?}",
282                                       round, self.revision));
283             let proc_res = self.print_source(srcs[round].to_owned(), &self.props.pretty_mode);
284
285             if !proc_res.status.success() {
286                 self.fatal_proc_rec(&format!("pretty-printing failed in round {} revision {:?}",
287                                              round, self.revision),
288                                     &proc_res);
289             }
290
291             let ProcRes{ stdout, .. } = proc_res;
292             srcs.push(stdout);
293             round += 1;
294         }
295
296         let mut expected = match self.props.pp_exact {
297             Some(ref file) => {
298                 let filepath = self.testpaths.file.parent().unwrap().join(file);
299                 let mut s = String::new();
300                 File::open(&filepath).unwrap().read_to_string(&mut s).unwrap();
301                 s
302             }
303             None => { srcs[srcs.len() - 2].clone() }
304         };
305         let mut actual = srcs[srcs.len() - 1].clone();
306
307         if self.props.pp_exact.is_some() {
308             // Now we have to care about line endings
309             let cr = "\r".to_owned();
310             actual = actual.replace(&cr, "").to_owned();
311             expected = expected.replace(&cr, "").to_owned();
312         }
313
314         self.compare_source(&expected, &actual);
315
316         // If we're only making sure that the output matches then just stop here
317         if self.props.pretty_compare_only { return; }
318
319         // Finally, let's make sure it actually appears to remain valid code
320         let proc_res = self.typecheck_source(actual);
321         if !proc_res.status.success() {
322             self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
323         }
324
325         if !self.props.pretty_expanded { return }
326
327         // additionally, run `--pretty expanded` and try to build it.
328         let proc_res = self.print_source(srcs[round].clone(), "expanded");
329         if !proc_res.status.success() {
330             self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
331         }
332
333         let ProcRes{ stdout: expanded_src, .. } = proc_res;
334         let proc_res = self.typecheck_source(expanded_src);
335         if !proc_res.status.success() {
336             self.fatal_proc_rec(
337                 "pretty-printed source (expanded) does not typecheck",
338                 &proc_res);
339         }
340     }
341
342     fn print_source(&self, src: String, pretty_type: &str) -> ProcRes {
343         let aux_dir = self.aux_output_dir_name();
344
345         let mut rustc = Command::new(&self.config.rustc_path);
346         rustc.arg("-")
347             .arg("-Zunstable-options")
348             .args(&["--unpretty", &pretty_type])
349             .args(&["--target", &self.config.target])
350             .arg("-L").arg(&aux_dir)
351             .args(self.split_maybe_args(&self.config.target_rustcflags))
352             .args(&self.props.compile_flags)
353             .envs(self.props.exec_env.clone());
354
355         self.compose_and_run(rustc,
356                              self.config.compile_lib_path.to_str().unwrap(),
357                              Some(aux_dir.to_str().unwrap()),
358                              Some(src))
359     }
360
361     fn compare_source(&self,
362                       expected: &str,
363                       actual: &str) {
364         if expected != actual {
365             self.error("pretty-printed source does not match expected source");
366             println!("\n\
367 expected:\n\
368 ------------------------------------------\n\
369 {}\n\
370 ------------------------------------------\n\
371 actual:\n\
372 ------------------------------------------\n\
373 {}\n\
374 ------------------------------------------\n\
375 \n",
376                      expected, actual);
377             panic!();
378         }
379     }
380
381     fn typecheck_source(&self, src: String) -> ProcRes {
382         let mut rustc = Command::new(&self.config.rustc_path);
383
384         let out_dir = self.output_base_name().with_extension("pretty-out");
385         let _ = fs::remove_dir_all(&out_dir);
386         create_dir_all(&out_dir).unwrap();
387
388         let target = if self.props.force_host {
389             &*self.config.host
390         } else {
391             &*self.config.target
392         };
393
394         let aux_dir = self.aux_output_dir_name();
395
396         rustc.arg("-")
397             .arg("-Zno-trans")
398             .arg("--out-dir").arg(&out_dir)
399             .arg(&format!("--target={}", target))
400             .arg("-L").arg(&self.config.build_base)
401             .arg("-L").arg(aux_dir);
402
403         if let Some(revision) = self.revision {
404             rustc.args(&["--cfg", revision]);
405         }
406
407         rustc.args(self.split_maybe_args(&self.config.target_rustcflags));
408         rustc.args(&self.props.compile_flags);
409
410         self.compose_and_run_compiler(rustc, Some(src))
411     }
412
413     fn run_debuginfo_gdb_test(&self) {
414         assert!(self.revision.is_none(), "revisions not relevant here");
415
416         let config = Config {
417             target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
418             host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
419             .. self.config.clone()
420         };
421
422         let test_cx = TestCx {
423             config: &config,
424             ..*self
425         };
426
427         test_cx.run_debuginfo_gdb_test_no_opt();
428     }
429
430     fn run_debuginfo_gdb_test_no_opt(&self) {
431         let prefixes = if self.config.gdb_native_rust {
432             // GDB with Rust
433             static PREFIXES: &'static [&'static str] = &["gdb", "gdbr"];
434             println!("NOTE: compiletest thinks it is using GDB with native rust support");
435             PREFIXES
436         } else {
437             // Generic GDB
438             static PREFIXES: &'static [&'static str] = &["gdb", "gdbg"];
439             println!("NOTE: compiletest thinks it is using GDB without native rust support");
440             PREFIXES
441         };
442
443         let DebuggerCommands {
444             commands,
445             check_lines,
446             breakpoint_lines
447         } = self.parse_debugger_commands(prefixes);
448         let mut cmds = commands.join("\n");
449
450         // compile test file (it should have 'compile-flags:-g' in the header)
451         let compiler_run_result = self.compile_test();
452         if !compiler_run_result.status.success() {
453             self.fatal_proc_rec("compilation failed!", &compiler_run_result);
454         }
455
456         let exe_file = self.make_exe_name();
457
458         let debugger_run_result;
459         match &*self.config.target {
460             "arm-linux-androideabi" |
461             "armv7-linux-androideabi" |
462             "aarch64-linux-android" => {
463
464                 cmds = cmds.replace("run", "continue");
465
466                 let tool_path = match self.config.android_cross_path.to_str() {
467                     Some(x) => x.to_owned(),
468                     None => self.fatal("cannot find android cross path")
469                 };
470
471                 // write debugger script
472                 let mut script_str = String::with_capacity(2048);
473                 script_str.push_str(&format!("set charset {}\n", Self::charset()));
474                 script_str.push_str(&format!("set sysroot {}\n", tool_path));
475                 script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
476                 script_str.push_str("target remote :5039\n");
477                 script_str.push_str(&format!("set solib-search-path \
478                                               ./{}/stage2/lib/rustlib/{}/lib/\n",
479                                              self.config.host, self.config.target));
480                 for line in &breakpoint_lines {
481                     script_str.push_str(&format!("break {:?}:{}\n",
482                                                  self.testpaths.file.file_name()
483                                                  .unwrap()
484                                                  .to_string_lossy(),
485                                                  *line)[..]);
486                 }
487                 script_str.push_str(&cmds);
488                 script_str.push_str("\nquit\n");
489
490                 debug!("script_str = {}", script_str);
491                 self.dump_output_file(&script_str, "debugger.script");
492
493                 let adb_path = &self.config.adb_path;
494
495                 Command::new(adb_path)
496                     .arg("push")
497                     .arg(&exe_file)
498                     .arg(&self.config.adb_test_dir)
499                     .status()
500                     .expect(&format!("failed to exec `{:?}`", adb_path));
501
502                 Command::new(adb_path)
503                     .args(&["forward", "tcp:5039", "tcp:5039"])
504                     .status()
505                     .expect(&format!("failed to exec `{:?}`", adb_path));
506
507                 let adb_arg = format!("export LD_LIBRARY_PATH={}; \
508                                        gdbserver{} :5039 {}/{}",
509                                       self.config.adb_test_dir.clone(),
510                                       if self.config.target.contains("aarch64")
511                                       {"64"} else {""},
512                                       self.config.adb_test_dir.clone(),
513                                       exe_file.file_name().unwrap().to_str()
514                                       .unwrap());
515
516                 debug!("adb arg: {}", adb_arg);
517                 let mut adb = Command::new(adb_path)
518                     .args(&["shell", &adb_arg])
519                     .stdout(Stdio::piped())
520                     .stderr(Stdio::inherit())
521                     .spawn()
522                     .expect(&format!("failed to exec `{:?}`", adb_path));
523
524                 // Wait for the gdbserver to print out "Listening on port ..."
525                 // at which point we know that it's started and then we can
526                 // execute the debugger below.
527                 let mut stdout = BufReader::new(adb.stdout.take().unwrap());
528                 let mut line = String::new();
529                 loop {
530                     line.truncate(0);
531                     stdout.read_line(&mut line).unwrap();
532                     if line.starts_with("Listening on port 5039") {
533                         break
534                     }
535                 }
536                 drop(stdout);
537
538                 let debugger_script = self.make_out_name("debugger.script");
539                 // FIXME (#9639): This needs to handle non-utf8 paths
540                 let debugger_opts =
541                     vec!["-quiet".to_owned(),
542                          "-batch".to_owned(),
543                          "-nx".to_owned(),
544                          format!("-command={}", debugger_script.to_str().unwrap())];
545
546                 let mut gdb_path = tool_path;
547                 gdb_path.push_str("/bin/gdb");
548                 let Output {
549                     status,
550                     stdout,
551                     stderr
552                 } = Command::new(&gdb_path)
553                     .args(&debugger_opts)
554                     .output()
555                     .expect(&format!("failed to exec `{:?}`", gdb_path));
556                 let cmdline = {
557                     let mut gdb = Command::new(&format!("{}-gdb", self.config.target));
558                     gdb.args(&debugger_opts);
559                     let cmdline = self.make_cmdline(&gdb, "");
560                     logv(self.config, format!("executing {}", cmdline));
561                     cmdline
562                 };
563
564                 debugger_run_result = ProcRes {
565                     status,
566                     stdout: String::from_utf8(stdout).unwrap(),
567                     stderr: String::from_utf8(stderr).unwrap(),
568                     cmdline,
569                 };
570                 if adb.kill().is_err() {
571                     println!("Adb process is already finished.");
572                 }
573             }
574
575             _=> {
576                 let rust_src_root = self.find_rust_src_root()
577                                         .expect("Could not find Rust source root");
578                 let rust_pp_module_rel_path = Path::new("./src/etc");
579                 let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
580                                                            .to_str()
581                                                            .unwrap()
582                                                            .to_owned();
583                 // write debugger script
584                 let mut script_str = String::with_capacity(2048);
585                 script_str.push_str(&format!("set charset {}\n", Self::charset()));
586                 script_str.push_str("show version\n");
587
588                 match self.config.gdb_version {
589                     Some(version) => {
590                         println!("NOTE: compiletest thinks it is using GDB version {}",
591                                  version);
592
593                         if version > extract_gdb_version("7.4").unwrap() {
594                             // Add the directory containing the pretty printers to
595                             // GDB's script auto loading safe path
596                             script_str.push_str(
597                                 &format!("add-auto-load-safe-path {}\n",
598                                          rust_pp_module_abs_path.replace(r"\", r"\\"))
599                             );
600                         }
601                     }
602                     _ => {
603                         println!("NOTE: compiletest does not know which version of \
604                                   GDB it is using");
605                     }
606                 }
607
608                 // The following line actually doesn't have to do anything with
609                 // pretty printing, it just tells GDB to print values on one line:
610                 script_str.push_str("set print pretty off\n");
611
612                 // Add the pretty printer directory to GDB's source-file search path
613                 script_str.push_str(&format!("directory {}\n",
614                                              rust_pp_module_abs_path));
615
616                 // Load the target executable
617                 script_str.push_str(&format!("file {}\n",
618                                              exe_file.to_str().unwrap()
619                                              .replace(r"\", r"\\")));
620
621                 // Force GDB to print values in the Rust format.
622                 if self.config.gdb_native_rust {
623                     script_str.push_str("set language rust\n");
624                 }
625
626                 // Add line breakpoints
627                 for line in &breakpoint_lines {
628                     script_str.push_str(&format!("break '{}':{}\n",
629                                                  self.testpaths.file.file_name().unwrap()
630                                                  .to_string_lossy(),
631                                                  *line));
632                 }
633
634                 script_str.push_str(&cmds);
635                 script_str.push_str("\nquit\n");
636
637                 debug!("script_str = {}", script_str);
638                 self.dump_output_file(&script_str, "debugger.script");
639
640                 let debugger_script = self.make_out_name("debugger.script");
641
642                 // FIXME (#9639): This needs to handle non-utf8 paths
643                 let debugger_opts =
644                     vec!["-quiet".to_owned(),
645                          "-batch".to_owned(),
646                          "-nx".to_owned(),
647                          format!("-command={}", debugger_script.to_str().unwrap())];
648
649                 let mut gdb = Command::new(self.config.gdb.as_ref().unwrap());
650                 gdb.args(&debugger_opts)
651                     .env("PYTHONPATH", rust_pp_module_abs_path);
652
653                 debugger_run_result =
654                     self.compose_and_run(gdb,
655                                          self.config.run_lib_path.to_str().unwrap(),
656                                          None,
657                                          None);
658             }
659         }
660
661         if !debugger_run_result.status.success() {
662             self.fatal_proc_rec("gdb failed to execute", &debugger_run_result);
663         }
664
665         self.check_debugger_output(&debugger_run_result, &check_lines);
666     }
667
668     fn find_rust_src_root(&self) -> Option<PathBuf> {
669         let mut path = self.config.src_base.clone();
670         let path_postfix = Path::new("src/etc/lldb_batchmode.py");
671
672         while path.pop() {
673             if path.join(&path_postfix).is_file() {
674                 return Some(path);
675             }
676         }
677
678         None
679     }
680
681     fn run_debuginfo_lldb_test(&self) {
682         assert!(self.revision.is_none(), "revisions not relevant here");
683
684         if self.config.lldb_python_dir.is_none() {
685             self.fatal("Can't run LLDB test because LLDB's python path is not set.");
686         }
687
688         let config = Config {
689             target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags),
690             host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags),
691             .. self.config.clone()
692         };
693
694
695         let test_cx = TestCx {
696             config: &config,
697             ..*self
698         };
699
700         test_cx.run_debuginfo_lldb_test_no_opt();
701     }
702
703     fn run_debuginfo_lldb_test_no_opt(&self) {
704         // compile test file (it should have 'compile-flags:-g' in the header)
705         let compile_result = self.compile_test();
706         if !compile_result.status.success() {
707             self.fatal_proc_rec("compilation failed!", &compile_result);
708         }
709
710         let exe_file = self.make_exe_name();
711
712         match self.config.lldb_version {
713             Some(ref version) => {
714                 println!("NOTE: compiletest thinks it is using LLDB version {}",
715                          version);
716             }
717             _ => {
718                 println!("NOTE: compiletest does not know which version of \
719                           LLDB it is using");
720             }
721         }
722
723         // Parse debugger commands etc from test files
724         let DebuggerCommands {
725             commands,
726             check_lines,
727             breakpoint_lines,
728             ..
729         } = self.parse_debugger_commands(&["lldb"]);
730
731         // Write debugger script:
732         // We don't want to hang when calling `quit` while the process is still running
733         let mut script_str = String::from("settings set auto-confirm true\n");
734
735         // Make LLDB emit its version, so we have it documented in the test output
736         script_str.push_str("version\n");
737
738         // Switch LLDB into "Rust mode"
739         let rust_src_root = self.find_rust_src_root().expect("Could not find Rust source root");
740         let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py");
741         let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
742                                                    .to_str()
743                                                    .unwrap()
744                                                    .to_owned();
745
746         script_str.push_str(&format!("command script import {}\n",
747                                      &rust_pp_module_abs_path[..])[..]);
748         script_str.push_str("type summary add --no-value ");
749         script_str.push_str("--python-function lldb_rust_formatters.print_val ");
750         script_str.push_str("-x \".*\" --category Rust\n");
751         script_str.push_str("type category enable Rust\n");
752
753         // Set breakpoints on every line that contains the string "#break"
754         let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy();
755         for line in &breakpoint_lines {
756             script_str.push_str(&format!("breakpoint set --file '{}' --line {}\n",
757                                          source_file_name,
758                                          line));
759         }
760
761         // Append the other commands
762         for line in &commands {
763             script_str.push_str(line);
764             script_str.push_str("\n");
765         }
766
767         // Finally, quit the debugger
768         script_str.push_str("\nquit\n");
769
770         // Write the script into a file
771         debug!("script_str = {}", script_str);
772         self.dump_output_file(&script_str, "debugger.script");
773         let debugger_script = self.make_out_name("debugger.script");
774
775         // Let LLDB execute the script via lldb_batchmode.py
776         let debugger_run_result = self.run_lldb(&exe_file,
777                                                 &debugger_script,
778                                                 &rust_src_root);
779
780         if !debugger_run_result.status.success() {
781             self.fatal_proc_rec("Error while running LLDB", &debugger_run_result);
782         }
783
784         self.check_debugger_output(&debugger_run_result, &check_lines);
785     }
786
787     fn run_lldb(&self,
788                 test_executable: &Path,
789                 debugger_script: &Path,
790                 rust_src_root: &Path)
791                 -> ProcRes {
792         // Prepare the lldb_batchmode which executes the debugger script
793         let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
794         self.cmd2procres(Command::new(&self.config.lldb_python)
795                          .arg(&lldb_script_path)
796                          .arg(test_executable)
797                          .arg(debugger_script)
798                          .env("PYTHONPATH",
799                               self.config.lldb_python_dir.as_ref().unwrap()))
800     }
801
802     fn cmd2procres(&self, cmd: &mut Command) -> ProcRes {
803         let (status, out, err) = match cmd.output() {
804             Ok(Output { status, stdout, stderr }) => {
805                 (status,
806                  String::from_utf8(stdout).unwrap(),
807                  String::from_utf8(stderr).unwrap())
808             },
809             Err(e) => {
810                 self.fatal(&format!("Failed to setup Python process for \
811                                       LLDB script: {}", e))
812             }
813         };
814
815         self.dump_output(&out, &err);
816         ProcRes {
817             status,
818             stdout: out,
819             stderr: err,
820             cmdline: format!("{:?}", cmd)
821         }
822     }
823
824     fn parse_debugger_commands(&self, debugger_prefixes: &[&str]) -> DebuggerCommands {
825         let directives = debugger_prefixes.iter().map(|prefix| (
826             format!("{}-command", prefix),
827             format!("{}-check", prefix),
828         )).collect::<Vec<_>>();
829
830         let mut breakpoint_lines = vec![];
831         let mut commands = vec![];
832         let mut check_lines = vec![];
833         let mut counter = 1;
834         let reader = BufReader::new(File::open(&self.testpaths.file).unwrap());
835         for line in reader.lines() {
836             match line {
837                 Ok(line) => {
838                     if line.contains("#break") {
839                         breakpoint_lines.push(counter);
840                     }
841
842                     for &(ref command_directive, ref check_directive) in &directives {
843                         self.config.parse_name_value_directive(
844                             &line,
845                             command_directive).map(|cmd| {
846                                 commands.push(cmd)
847                             });
848
849                         self.config.parse_name_value_directive(
850                             &line,
851                             check_directive).map(|cmd| {
852                                 check_lines.push(cmd)
853                             });
854                     }
855                 }
856                 Err(e) => {
857                     self.fatal(&format!("Error while parsing debugger commands: {}", e))
858                 }
859             }
860             counter += 1;
861         }
862
863         DebuggerCommands {
864             commands,
865             check_lines,
866             breakpoint_lines,
867         }
868     }
869
870     fn cleanup_debug_info_options(&self, options: &Option<String>) -> Option<String> {
871         if options.is_none() {
872             return None;
873         }
874
875         // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
876         let options_to_remove = [
877             "-O".to_owned(),
878             "-g".to_owned(),
879             "--debuginfo".to_owned()
880         ];
881         let new_options =
882             self.split_maybe_args(options).into_iter()
883                                           .filter(|x| !options_to_remove.contains(x))
884                                           .collect::<Vec<String>>();
885
886         Some(new_options.join(" "))
887     }
888
889     fn check_debugger_output(&self, debugger_run_result: &ProcRes, check_lines: &[String]) {
890         let num_check_lines = check_lines.len();
891
892         let mut check_line_index = 0;
893         for line in debugger_run_result.stdout.lines() {
894             if check_line_index >= num_check_lines {
895                 break;
896             }
897
898             if check_single_line(line, &(check_lines[check_line_index])[..]) {
899                 check_line_index += 1;
900             }
901         }
902         if check_line_index != num_check_lines && num_check_lines > 0 {
903             self.fatal_proc_rec(&format!("line not found in debugger output: {}",
904                                          check_lines[check_line_index]),
905                                 debugger_run_result);
906         }
907
908         fn check_single_line(line: &str, check_line: &str) -> bool {
909             // Allow check lines to leave parts unspecified (e.g., uninitialized
910             // bits in the  wrong case of an enum) with the notation "[...]".
911             let line = line.trim();
912             let check_line = check_line.trim();
913             let can_start_anywhere = check_line.starts_with("[...]");
914             let can_end_anywhere = check_line.ends_with("[...]");
915
916             let check_fragments: Vec<&str> = check_line.split("[...]")
917                                                        .filter(|frag| !frag.is_empty())
918                                                        .collect();
919             if check_fragments.is_empty() {
920                 return true;
921             }
922
923             let (mut rest, first_fragment) = if can_start_anywhere {
924                 match line.find(check_fragments[0]) {
925                     Some(pos) => (&line[pos + check_fragments[0].len() ..], 1),
926                     None => return false
927                 }
928             } else {
929                 (line, 0)
930             };
931
932             for current_fragment in &check_fragments[first_fragment..] {
933                 match rest.find(current_fragment) {
934                     Some(pos) => {
935                         rest = &rest[pos + current_fragment.len() .. ];
936                     }
937                     None => return false
938                 }
939             }
940
941             if !can_end_anywhere && !rest.is_empty() {
942                 return false;
943             }
944
945             true
946         }
947     }
948
949     fn check_error_patterns(&self,
950                             output_to_check: &str,
951                             proc_res: &ProcRes) {
952         if self.props.error_patterns.is_empty() {
953             if self.props.must_compile_successfully {
954                 return
955             } else {
956                 self.fatal(&format!("no error pattern specified in {:?}",
957                                     self.testpaths.file.display()));
958             }
959         }
960         let mut next_err_idx = 0;
961         let mut next_err_pat = self.props.error_patterns[next_err_idx].trim();
962         let mut done = false;
963         for line in output_to_check.lines() {
964             if line.contains(next_err_pat) {
965                 debug!("found error pattern {}", next_err_pat);
966                 next_err_idx += 1;
967                 if next_err_idx == self.props.error_patterns.len() {
968                     debug!("found all error patterns");
969                     done = true;
970                     break;
971                 }
972                 next_err_pat = self.props.error_patterns[next_err_idx].trim();
973             }
974         }
975         if done { return; }
976
977         let missing_patterns = &self.props.error_patterns[next_err_idx..];
978         if missing_patterns.len() == 1 {
979             self.fatal_proc_rec(
980                 &format!("error pattern '{}' not found!", missing_patterns[0]),
981                 proc_res);
982         } else {
983             for pattern in missing_patterns {
984                 self.error(&format!("error pattern '{}' not found!", *pattern));
985             }
986             self.fatal_proc_rec("multiple error patterns not found", proc_res);
987         }
988     }
989
990     fn check_no_compiler_crash(&self, proc_res: &ProcRes) {
991         for line in proc_res.stderr.lines() {
992             if line.contains("error: internal compiler error") {
993                 self.fatal_proc_rec("compiler encountered internal error", proc_res);
994             }
995         }
996     }
997
998     fn check_forbid_output(&self,
999                            output_to_check: &str,
1000                            proc_res: &ProcRes) {
1001         for pat in &self.props.forbid_output {
1002             if output_to_check.contains(pat) {
1003                 self.fatal_proc_rec("forbidden pattern found in compiler output", proc_res);
1004             }
1005         }
1006     }
1007
1008     fn check_expected_errors(&self,
1009                              expected_errors: Vec<errors::Error>,
1010                              proc_res: &ProcRes) {
1011         if proc_res.status.success() &&
1012             expected_errors.iter().any(|x| x.kind == Some(ErrorKind::Error)) {
1013             self.fatal_proc_rec("process did not return an error status", proc_res);
1014         }
1015
1016         let file_name =
1017             format!("{}", self.testpaths.file.display())
1018             .replace(r"\", "/"); // on windows, translate all '\' path separators to '/'
1019
1020         // If the testcase being checked contains at least one expected "help"
1021         // message, then we'll ensure that all "help" messages are expected.
1022         // Otherwise, all "help" messages reported by the compiler will be ignored.
1023         // This logic also applies to "note" messages.
1024         let expect_help = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Help));
1025         let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
1026
1027         // Parse the JSON output from the compiler and extract out the messages.
1028         let actual_errors = json::parse_output(&file_name, &proc_res.stderr, proc_res);
1029         let mut unexpected = Vec::new();
1030         let mut found = vec![false; expected_errors.len()];
1031         for actual_error in &actual_errors {
1032             let opt_index =
1033                 expected_errors
1034                 .iter()
1035                 .enumerate()
1036                 .position(|(index, expected_error)| {
1037                     !found[index] &&
1038                         actual_error.line_num == expected_error.line_num &&
1039                         (expected_error.kind.is_none() ||
1040                          actual_error.kind == expected_error.kind) &&
1041                         actual_error.msg.contains(&expected_error.msg)
1042                 });
1043
1044             match opt_index {
1045                 Some(index) => {
1046                     // found a match, everybody is happy
1047                     assert!(!found[index]);
1048                     found[index] = true;
1049                 }
1050
1051                 None => {
1052                     if self.is_unexpected_compiler_message(actual_error, expect_help, expect_note) {
1053                         self.error(
1054                             &format!("{}:{}: unexpected {:?}: '{}'",
1055                                      file_name,
1056                                      actual_error.line_num,
1057                                      actual_error.kind.as_ref()
1058                                      .map_or(String::from("message"),
1059                                              |k| k.to_string()),
1060                                      actual_error.msg));
1061                         unexpected.push(actual_error);
1062                     }
1063                 }
1064             }
1065         }
1066
1067         let mut not_found = Vec::new();
1068         // anything not yet found is a problem
1069         for (index, expected_error) in expected_errors.iter().enumerate() {
1070             if !found[index] {
1071                 self.error(
1072                     &format!("{}:{}: expected {} not found: {}",
1073                              file_name,
1074                              expected_error.line_num,
1075                              expected_error.kind.as_ref()
1076                              .map_or("message".into(),
1077                                      |k| k.to_string()),
1078                              expected_error.msg));
1079                 not_found.push(expected_error);
1080             }
1081         }
1082
1083         if !unexpected.is_empty() || !not_found.is_empty() {
1084             self.error(
1085                 &format!("{} unexpected errors found, {} expected errors not found",
1086                          unexpected.len(), not_found.len()));
1087             println!("status: {}\ncommand: {}",
1088                    proc_res.status, proc_res.cmdline);
1089             if !unexpected.is_empty() {
1090                 println!("unexpected errors (from JSON output): {:#?}\n", unexpected);
1091             }
1092             if !not_found.is_empty() {
1093                 println!("not found errors (from test file): {:#?}\n", not_found);
1094             }
1095             panic!();
1096         }
1097     }
1098
1099     /// Returns true if we should report an error about `actual_error`,
1100     /// which did not match any of the expected error. We always require
1101     /// errors/warnings to be explicitly listed, but only require
1102     /// helps/notes if there are explicit helps/notes given.
1103     fn is_unexpected_compiler_message(&self,
1104                                       actual_error: &Error,
1105                                       expect_help: bool,
1106                                       expect_note: bool)
1107                                       -> bool {
1108         match actual_error.kind {
1109             Some(ErrorKind::Help) => expect_help,
1110             Some(ErrorKind::Note) => expect_note,
1111             Some(ErrorKind::Error) |
1112             Some(ErrorKind::Warning) => true,
1113             Some(ErrorKind::Suggestion) |
1114             None => false
1115         }
1116     }
1117
1118     fn compile_test(&self) -> ProcRes {
1119         let mut rustc = self.make_compile_args(
1120             &self.testpaths.file, TargetLocation::ThisFile(self.make_exe_name()));
1121
1122         rustc.arg("-L").arg(&self.aux_output_dir_name());
1123
1124         match self.config.mode {
1125             CompileFail | Ui => {
1126                 // compile-fail and ui tests tend to have tons of unused code as
1127                 // it's just testing various pieces of the compile, but we don't
1128                 // want to actually assert warnings about all this code. Instead
1129                 // let's just ignore unused code warnings by defaults and tests
1130                 // can turn it back on if needed.
1131                 rustc.args(&["-A", "unused"]);
1132             }
1133             _ => {}
1134         }
1135
1136         self.compose_and_run_compiler(rustc, None)
1137     }
1138
1139     fn document(&self, out_dir: &Path) -> ProcRes {
1140         if self.props.build_aux_docs {
1141             for rel_ab in &self.props.aux_builds {
1142                 let aux_testpaths = self.compute_aux_test_paths(rel_ab);
1143                 let aux_props = self.props.from_aux_file(&aux_testpaths.file,
1144                                                          self.revision,
1145                                                          self.config);
1146                 let aux_cx = TestCx {
1147                     config: self.config,
1148                     props: &aux_props,
1149                     testpaths: &aux_testpaths,
1150                     revision: self.revision
1151                 };
1152                 let auxres = aux_cx.document(out_dir);
1153                 if !auxres.status.success() {
1154                     return auxres;
1155                 }
1156             }
1157         }
1158
1159         let aux_dir = self.aux_output_dir_name();
1160
1161         let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path passed");
1162         let mut rustdoc = Command::new(rustdoc_path);
1163
1164         rustdoc.arg("-L").arg(aux_dir)
1165             .arg("-o").arg(out_dir)
1166             .arg(&self.testpaths.file)
1167             .args(&self.props.compile_flags);
1168
1169         self.compose_and_run_compiler(rustdoc, None)
1170     }
1171
1172     fn exec_compiled_test(&self) -> ProcRes {
1173         let env = &self.props.exec_env;
1174
1175         match &*self.config.target {
1176             // This is pretty similar to below, we're transforming:
1177             //
1178             //      program arg1 arg2
1179             //
1180             // into
1181             //
1182             //      remote-test-client run program:support-lib.so arg1 arg2
1183             //
1184             // The test-client program will upload `program` to the emulator
1185             // along with all other support libraries listed (in this case
1186             // `support-lib.so`. It will then execute the program on the
1187             // emulator with the arguments specified (in the environment we give
1188             // the process) and then report back the same result.
1189             _ if self.config.remote_test_client.is_some() => {
1190                 let aux_dir = self.aux_output_dir_name();
1191                 let ProcArgs { mut prog, args } = self.make_run_args();
1192                 if let Ok(entries) = aux_dir.read_dir() {
1193                     for entry in entries {
1194                         let entry = entry.unwrap();
1195                         if !entry.path().is_file() {
1196                             continue
1197                         }
1198                         prog.push_str(":");
1199                         prog.push_str(entry.path().to_str().unwrap());
1200                     }
1201                 }
1202                 let mut test_client = Command::new(
1203                     self.config.remote_test_client.as_ref().unwrap());
1204                 test_client
1205                     .args(&["run", &prog])
1206                     .args(args)
1207                     .envs(env.clone());
1208                 self.compose_and_run(test_client,
1209                                      self.config.run_lib_path.to_str().unwrap(),
1210                                      Some(aux_dir.to_str().unwrap()),
1211                                      None)
1212             }
1213             _ => {
1214                 let aux_dir = self.aux_output_dir_name();
1215                 let ProcArgs { prog, args } = self.make_run_args();
1216                 let mut program = Command::new(&prog);
1217                 program.args(args)
1218                     .current_dir(&self.output_base_name().parent().unwrap())
1219                     .envs(env.clone());
1220                 self.compose_and_run(program,
1221                                      self.config.run_lib_path.to_str().unwrap(),
1222                                      Some(aux_dir.to_str().unwrap()),
1223                                      None)
1224             }
1225         }
1226     }
1227
1228     /// For each `aux-build: foo/bar` annotation, we check to find the
1229     /// file in a `aux` directory relative to the test itself.
1230     fn compute_aux_test_paths(&self, rel_ab: &str) -> TestPaths {
1231         let test_ab = self.testpaths.file
1232                                     .parent()
1233                                     .expect("test file path has no parent")
1234                                     .join("auxiliary")
1235                                     .join(rel_ab);
1236         if !test_ab.exists() {
1237             self.fatal(&format!("aux-build `{}` source not found", test_ab.display()))
1238         }
1239
1240         TestPaths {
1241             file: test_ab,
1242             base: self.testpaths.base.clone(),
1243             relative_dir: self.testpaths.relative_dir
1244                                         .join("auxiliary")
1245                                         .join(rel_ab)
1246                                         .parent()
1247                                         .expect("aux-build path has no parent")
1248                                         .to_path_buf()
1249         }
1250     }
1251
1252     fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
1253         if !self.props.aux_builds.is_empty() {
1254             create_dir_all(&self.aux_output_dir_name()).unwrap();
1255         }
1256
1257         let aux_dir = self.aux_output_dir_name();
1258
1259         for rel_ab in &self.props.aux_builds {
1260             let aux_testpaths = self.compute_aux_test_paths(rel_ab);
1261             let aux_props = self.props.from_aux_file(&aux_testpaths.file,
1262                                                      self.revision,
1263                                                      self.config);
1264             let aux_output = {
1265                 let f = self.make_lib_name(&self.testpaths.file);
1266                 let parent = f.parent().unwrap();
1267                 TargetLocation::ThisDirectory(parent.to_path_buf())
1268             };
1269             let aux_cx = TestCx {
1270                 config: self.config,
1271                 props: &aux_props,
1272                 testpaths: &aux_testpaths,
1273                 revision: self.revision
1274             };
1275             let mut aux_rustc = aux_cx.make_compile_args(&aux_testpaths.file, aux_output);
1276
1277             let crate_type = if aux_props.no_prefer_dynamic {
1278                 None
1279             } else if (self.config.target.contains("musl") && !aux_props.force_host) ||
1280                       self.config.target.contains("emscripten") {
1281                 // We primarily compile all auxiliary libraries as dynamic libraries
1282                 // to avoid code size bloat and large binaries as much as possible
1283                 // for the test suite (otherwise including libstd statically in all
1284                 // executables takes up quite a bit of space).
1285                 //
1286                 // For targets like MUSL or Emscripten, however, there is no support for
1287                 // dynamic libraries so we just go back to building a normal library. Note,
1288                 // however, that for MUSL if the library is built with `force_host` then
1289                 // it's ok to be a dylib as the host should always support dylibs.
1290                 Some("lib")
1291             } else {
1292                 Some("dylib")
1293             };
1294
1295             if let Some(crate_type) = crate_type {
1296                 aux_rustc.args(&["--crate-type", crate_type]);
1297             }
1298
1299             aux_rustc.arg("-L").arg(&aux_dir);
1300
1301             let auxres = aux_cx.compose_and_run(aux_rustc,
1302                                                 aux_cx.config.compile_lib_path.to_str().unwrap(),
1303                                                 Some(aux_dir.to_str().unwrap()),
1304                                                 None);
1305             if !auxres.status.success() {
1306                 self.fatal_proc_rec(
1307                     &format!("auxiliary build of {:?} failed to compile: ",
1308                              aux_testpaths.file.display()),
1309                     &auxres);
1310             }
1311         }
1312
1313         rustc.envs(self.props.rustc_env.clone());
1314         self.compose_and_run(rustc,
1315                              self.config.compile_lib_path.to_str().unwrap(),
1316                              Some(aux_dir.to_str().unwrap()),
1317                              input)
1318     }
1319
1320     fn compose_and_run(&self,
1321                        mut command: Command,
1322                        lib_path: &str,
1323                        aux_path: Option<&str>,
1324                        input: Option<String>) -> ProcRes {
1325         let cmdline =
1326         {
1327             let cmdline = self.make_cmdline(&command, lib_path);
1328             logv(self.config, format!("executing {}", cmdline));
1329             cmdline
1330         };
1331
1332         command
1333             .stdout(Stdio::piped())
1334             .stderr(Stdio::piped())
1335             .stdin(Stdio::piped());
1336
1337         // Need to be sure to put both the lib_path and the aux path in the dylib
1338         // search path for the child.
1339         let mut path = env::split_paths(&env::var_os(dylib_env_var()).unwrap_or(OsString::new()))
1340             .collect::<Vec<_>>();
1341         if let Some(p) = aux_path {
1342             path.insert(0, PathBuf::from(p))
1343         }
1344         path.insert(0, PathBuf::from(lib_path));
1345
1346         // Add the new dylib search path var
1347         let newpath = env::join_paths(&path).unwrap();
1348         command.env(dylib_env_var(), newpath);
1349
1350         let mut child = command.spawn().expect(&format!("failed to exec `{:?}`", &command));
1351         if let Some(input) = input {
1352             child.stdin.as_mut().unwrap().write_all(input.as_bytes()).unwrap();
1353         }
1354         let Output { status, stdout, stderr } = child.wait_with_output().unwrap();
1355
1356         let result = ProcRes {
1357             status,
1358             stdout: String::from_utf8(stdout).unwrap(),
1359             stderr: String::from_utf8(stderr).unwrap(),
1360             cmdline,
1361         };
1362
1363         self.dump_output(&result.stdout, &result.stderr);
1364
1365         result
1366     }
1367
1368     fn make_compile_args(&self, input_file: &Path, output_file: TargetLocation) -> Command {
1369         let mut rustc = Command::new(&self.config.rustc_path);
1370         rustc.arg(input_file)
1371             .arg("-L").arg(&self.config.build_base);
1372
1373         // Optionally prevent default --target if specified in test compile-flags.
1374         let custom_target = self.props.compile_flags
1375             .iter()
1376             .fold(false, |acc, x| acc || x.starts_with("--target"));
1377
1378         if !custom_target {
1379             let target = if self.props.force_host {
1380                 &*self.config.host
1381             } else {
1382                 &*self.config.target
1383             };
1384
1385             rustc.arg(&format!("--target={}", target));
1386         }
1387
1388         if let Some(revision) = self.revision {
1389             rustc.args(&["--cfg", revision]);
1390         }
1391
1392         if let Some(ref incremental_dir) = self.props.incremental_dir {
1393             rustc.args(&["-Z", &format!("incremental={}", incremental_dir.display())]);
1394         }
1395
1396         match self.config.mode {
1397             CompileFail |
1398             ParseFail |
1399             Incremental => {
1400                 // If we are extracting and matching errors in the new
1401                 // fashion, then you want JSON mode. Old-skool error
1402                 // patterns still match the raw compiler output.
1403                 if self.props.error_patterns.is_empty() {
1404                     rustc.args(&["--error-format", "json"]);
1405                 }
1406             }
1407             MirOpt => {
1408                 rustc.args(&[
1409                     "-Zdump-mir=all",
1410                     "-Zmir-opt-level=3",
1411                     "-Zdump-mir-exclude-pass-number"]);
1412
1413                 let mir_dump_dir = self.get_mir_dump_dir();
1414                 create_dir_all(mir_dump_dir.as_path()).unwrap();
1415                 let mut dir_opt = "-Zdump-mir-dir=".to_string();
1416                 dir_opt.push_str(mir_dump_dir.to_str().unwrap());
1417                 debug!("dir_opt: {:?}", dir_opt);
1418
1419                 rustc.arg(dir_opt);
1420             }
1421             RunPass |
1422             RunFail |
1423             RunPassValgrind |
1424             Pretty |
1425             DebugInfoGdb |
1426             DebugInfoLldb |
1427             Codegen |
1428             Rustdoc |
1429             RunMake |
1430             Ui |
1431             CodegenUnits => {
1432                 // do not use JSON output
1433             }
1434         }
1435
1436         if !self.props.no_prefer_dynamic {
1437             rustc.args(&["-C", "prefer-dynamic"]);
1438         }
1439
1440         match output_file {
1441             TargetLocation::ThisFile(path) => {
1442                 rustc.arg("-o").arg(path);
1443             }
1444             TargetLocation::ThisDirectory(path) => {
1445                 rustc.arg("--out-dir").arg(path);
1446             }
1447         }
1448
1449         if self.props.force_host {
1450             rustc.args(self.split_maybe_args(&self.config.host_rustcflags));
1451         } else {
1452             rustc.args(self.split_maybe_args(&self.config.target_rustcflags));
1453         }
1454
1455         rustc.args(&self.props.compile_flags);
1456
1457         rustc
1458     }
1459
1460     fn make_lib_name(&self, auxfile: &Path) -> PathBuf {
1461         // what we return here is not particularly important, as it
1462         // happens; rustc ignores everything except for the directory.
1463         let auxname = self.output_testname(auxfile);
1464         self.aux_output_dir_name().join(&auxname)
1465     }
1466
1467     fn make_exe_name(&self) -> PathBuf {
1468         let mut f = self.output_base_name();
1469         // FIXME: This is using the host architecture exe suffix, not target!
1470         if self.config.target.contains("emscripten") {
1471             let mut fname = f.file_name().unwrap().to_os_string();
1472             fname.push(".js");
1473             f.set_file_name(&fname);
1474         } else if !env::consts::EXE_SUFFIX.is_empty() {
1475             let mut fname = f.file_name().unwrap().to_os_string();
1476             fname.push(env::consts::EXE_SUFFIX);
1477             f.set_file_name(&fname);
1478         }
1479         f
1480     }
1481
1482     fn make_run_args(&self) -> ProcArgs {
1483         // If we've got another tool to run under (valgrind),
1484         // then split apart its command
1485         let mut args = self.split_maybe_args(&self.config.runtool);
1486
1487         // If this is emscripten, then run tests under nodejs
1488         if self.config.target.contains("emscripten") {
1489             if let Some(ref p) = self.config.nodejs {
1490                 args.push(p.clone());
1491             } else {
1492                 self.fatal("no NodeJS binary found (--nodejs)");
1493             }
1494         }
1495
1496         let exe_file = self.make_exe_name();
1497
1498         // FIXME (#9639): This needs to handle non-utf8 paths
1499         args.push(exe_file.to_str().unwrap().to_owned());
1500
1501         // Add the arguments in the run_flags directive
1502         args.extend(self.split_maybe_args(&self.props.run_flags));
1503
1504         let prog = args.remove(0);
1505          ProcArgs {
1506             prog,
1507             args,
1508         }
1509     }
1510
1511     fn split_maybe_args(&self, argstr: &Option<String>) -> Vec<String> {
1512         match *argstr {
1513             Some(ref s) => {
1514                 s
1515                     .split(' ')
1516                     .filter_map(|s| {
1517                         if s.chars().all(|c| c.is_whitespace()) {
1518                             None
1519                         } else {
1520                             Some(s.to_owned())
1521                         }
1522                     }).collect()
1523             }
1524             None => Vec::new()
1525         }
1526     }
1527
1528     fn make_cmdline(&self, command: &Command, libpath: &str) -> String {
1529         use util;
1530
1531         // Linux and mac don't require adjusting the library search path
1532         if cfg!(unix) {
1533             format!("{:?}", command)
1534         } else {
1535             // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
1536             // for diagnostic purposes
1537             fn lib_path_cmd_prefix(path: &str) -> String {
1538                 format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
1539             }
1540
1541             format!("{} {:?}", lib_path_cmd_prefix(libpath), command)
1542         }
1543     }
1544
1545     fn dump_output(&self, out: &str, err: &str) {
1546         let revision = if let Some(r) = self.revision {
1547             format!("{}.", r)
1548         } else {
1549             String::new()
1550         };
1551
1552         self.dump_output_file(out, &format!("{}out", revision));
1553         self.dump_output_file(err, &format!("{}err", revision));
1554         self.maybe_dump_to_stdout(out, err);
1555     }
1556
1557     fn dump_output_file(&self,
1558                         out: &str,
1559                         extension: &str) {
1560         let outfile = self.make_out_name(extension);
1561         File::create(&outfile).unwrap().write_all(out.as_bytes()).unwrap();
1562     }
1563
1564     fn make_out_name(&self, extension: &str) -> PathBuf {
1565         self.output_base_name().with_extension(extension)
1566     }
1567
1568     fn aux_output_dir_name(&self) -> PathBuf {
1569         let f = self.output_base_name();
1570         let mut fname = f.file_name().unwrap().to_os_string();
1571         fname.push(&format!(".{}.libaux", self.config.mode));
1572         f.with_file_name(&fname)
1573     }
1574
1575     fn output_testname(&self, filepath: &Path) -> PathBuf {
1576         PathBuf::from(filepath.file_stem().unwrap())
1577     }
1578
1579     /// Given a test path like `compile-fail/foo/bar.rs` Returns a name like
1580     ///
1581     ///     <output>/foo/bar-stage1
1582     fn output_base_name(&self) -> PathBuf {
1583         let dir = self.config.build_base.join(&self.testpaths.relative_dir);
1584
1585         // Note: The directory `dir` is created during `collect_tests_from_dir`
1586         dir
1587             .join(&self.output_testname(&self.testpaths.file))
1588             .with_extension(&self.config.stage_id)
1589     }
1590
1591     fn maybe_dump_to_stdout(&self, out: &str, err: &str) {
1592         if self.config.verbose {
1593             println!("------{}------------------------------", "stdout");
1594             println!("{}", out);
1595             println!("------{}------------------------------", "stderr");
1596             println!("{}", err);
1597             println!("------------------------------------------");
1598         }
1599     }
1600
1601     fn error(&self, err: &str) {
1602         match self.revision {
1603             Some(rev) => println!("\nerror in revision `{}`: {}", rev, err),
1604             None => println!("\nerror: {}", err)
1605         }
1606     }
1607
1608     fn fatal(&self, err: &str) -> ! {
1609         self.error(err); panic!();
1610     }
1611
1612     fn fatal_proc_rec(&self, err: &str, proc_res: &ProcRes) -> ! {
1613         self.try_print_open_handles();
1614         self.error(err);
1615         proc_res.fatal(None);
1616     }
1617
1618     // This function is a poor man's attempt to debug rust-lang/rust#38620, if
1619     // that's closed then this should be deleted
1620     //
1621     // This is a very "opportunistic" debugging attempt, so we ignore all
1622     // errors here.
1623     fn try_print_open_handles(&self) {
1624         if !cfg!(windows) {
1625             return
1626         }
1627         if self.config.mode != Incremental {
1628             return
1629         }
1630
1631         let filename = match self.testpaths.file.file_stem() {
1632             Some(path) => path,
1633             None => return,
1634         };
1635
1636         let mut cmd = Command::new("handle.exe");
1637         cmd.arg("-a").arg("-u");
1638         cmd.arg(filename);
1639         cmd.arg("-nobanner");
1640         let output = match cmd.output() {
1641             Ok(output) => output,
1642             Err(_) => return,
1643         };
1644         println!("---------------------------------------------------");
1645         println!("ran extra command to debug rust-lang/rust#38620: ");
1646         println!("{:?}", cmd);
1647         println!("result: {}", output.status);
1648         println!("--- stdout ----------------------------------------");
1649         println!("{}", String::from_utf8_lossy(&output.stdout));
1650         println!("--- stderr ----------------------------------------");
1651         println!("{}", String::from_utf8_lossy(&output.stderr));
1652         println!("---------------------------------------------------");
1653     }
1654
1655     // codegen tests (using FileCheck)
1656
1657     fn compile_test_and_save_ir(&self) -> ProcRes {
1658         let aux_dir = self.aux_output_dir_name();
1659
1660         let output_file = TargetLocation::ThisDirectory(
1661             self.output_base_name().parent().unwrap().to_path_buf());
1662         let mut rustc = self.make_compile_args(&self.testpaths.file, output_file);
1663         rustc.arg("-L").arg(aux_dir)
1664             .arg("--emit=llvm-ir");
1665
1666         self.compose_and_run_compiler(rustc, None)
1667     }
1668
1669     fn check_ir_with_filecheck(&self) -> ProcRes {
1670         let irfile = self.output_base_name().with_extension("ll");
1671         let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
1672         filecheck.arg("--input-file").arg(irfile)
1673             .arg(&self.testpaths.file);
1674         self.compose_and_run(filecheck, "", None, None)
1675     }
1676
1677     fn run_codegen_test(&self) {
1678         assert!(self.revision.is_none(), "revisions not relevant here");
1679
1680         if self.config.llvm_filecheck.is_none() {
1681             self.fatal("missing --llvm-filecheck");
1682         }
1683
1684         let mut proc_res = self.compile_test_and_save_ir();
1685         if !proc_res.status.success() {
1686             self.fatal_proc_rec("compilation failed!", &proc_res);
1687         }
1688
1689         proc_res = self.check_ir_with_filecheck();
1690         if !proc_res.status.success() {
1691             self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res);
1692         }
1693     }
1694
1695     fn charset() -> &'static str {
1696         // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
1697         if cfg!(target_os = "bitrig") {
1698             "auto"
1699         } else if cfg!(target_os = "freebsd") {
1700             "ISO-8859-1"
1701         } else {
1702             "UTF-8"
1703         }
1704     }
1705
1706     fn run_rustdoc_test(&self) {
1707         assert!(self.revision.is_none(), "revisions not relevant here");
1708
1709         let out_dir = self.output_base_name();
1710         let _ = fs::remove_dir_all(&out_dir);
1711         create_dir_all(&out_dir).unwrap();
1712
1713         let proc_res = self.document(&out_dir);
1714         if !proc_res.status.success() {
1715             self.fatal_proc_rec("rustdoc failed!", &proc_res);
1716         }
1717
1718         if self.props.check_test_line_numbers_match {
1719             self.check_rustdoc_test_option(proc_res);
1720         } else {
1721             let root = self.find_rust_src_root().unwrap();
1722             let res = self.cmd2procres(Command::new(&self.config.docck_python)
1723                                        .arg(root.join("src/etc/htmldocck.py"))
1724                                        .arg(out_dir)
1725                                        .arg(&self.testpaths.file));
1726             if !res.status.success() {
1727                 self.fatal_proc_rec("htmldocck failed!", &res);
1728             }
1729         }
1730     }
1731
1732     fn get_lines<P: AsRef<Path>>(&self, path: &P,
1733                                  mut other_files: Option<&mut Vec<String>>) -> Vec<usize> {
1734         let mut file = fs::File::open(path)
1735                                 .expect("markdown_test_output_check_entry File::open failed");
1736         let mut content = String::new();
1737         file.read_to_string(&mut content)
1738             .expect("markdown_test_output_check_entry read_to_string failed");
1739         let mut ignore = false;
1740         content.lines()
1741                .enumerate()
1742                .filter_map(|(line_nb, line)| {
1743                    if (line.trim_left().starts_with("pub mod ") ||
1744                        line.trim_left().starts_with("mod ")) &&
1745                       line.ends_with(';') {
1746                        if let Some(ref mut other_files) = other_files {
1747                            other_files.push(line.rsplit("mod ")
1748                                       .next()
1749                                       .unwrap()
1750                                       .replace(";", ""));
1751                        }
1752                        None
1753                    } else {
1754                        let sline = line.split("///").last().unwrap_or("");
1755                        let line = sline.trim_left();
1756                        if line.starts_with("```") {
1757                            if ignore {
1758                                ignore = false;
1759                                None
1760                            } else {
1761                                ignore = true;
1762                                Some(line_nb + 1)
1763                            }
1764                        } else {
1765                            None
1766                        }
1767                    }
1768                })
1769                .collect()
1770     }
1771
1772     fn check_rustdoc_test_option(&self, res: ProcRes) {
1773         let mut other_files = Vec::new();
1774         let mut files: HashMap<String, Vec<usize>> = HashMap::new();
1775         let cwd = env::current_dir().unwrap();
1776         files.insert(self.testpaths.file.strip_prefix(&cwd)
1777                                         .unwrap_or(&self.testpaths.file)
1778                                         .to_str()
1779                                         .unwrap()
1780                                         .replace('\\', "/"),
1781                      self.get_lines(&self.testpaths.file, Some(&mut other_files)));
1782         for other_file in other_files {
1783             let mut path = self.testpaths.file.clone();
1784             path.set_file_name(&format!("{}.rs", other_file));
1785             files.insert(path.strip_prefix(&cwd)
1786                              .unwrap_or(&path)
1787                              .to_str()
1788                              .unwrap()
1789                              .replace('\\', "/"),
1790                          self.get_lines(&path, None));
1791         }
1792
1793         let mut tested = 0;
1794         for _ in res.stdout.split('\n')
1795                            .filter(|s| s.starts_with("test "))
1796                            .inspect(|s| {
1797                                let tmp: Vec<&str> = s.split(" - ").collect();
1798                                if tmp.len() == 2 {
1799                                    let path = tmp[0].rsplit("test ").next().unwrap();
1800                                    if let Some(ref mut v) = files.get_mut(
1801                                                                 &path.replace('\\', "/")) {
1802                                        tested += 1;
1803                                        let mut iter = tmp[1].split("(line ");
1804                                        iter.next();
1805                                        let line = iter.next()
1806                                                       .unwrap_or(")")
1807                                                       .split(')')
1808                                                       .next()
1809                                                       .unwrap_or("0")
1810                                                       .parse()
1811                                                       .unwrap_or(0);
1812                                        if let Ok(pos) = v.binary_search(&line) {
1813                                            v.remove(pos);
1814                                        } else {
1815                                            self.fatal_proc_rec(
1816                                                &format!("Not found doc test: \"{}\" in \"{}\":{:?}",
1817                                                         s, path, v),
1818                                                &res);
1819                                        }
1820                                    }
1821                                }
1822                            }) {}
1823         if tested == 0 {
1824             self.fatal_proc_rec(&format!("No test has been found... {:?}", files), &res);
1825         } else {
1826             for (entry, v) in &files {
1827                 if !v.is_empty() {
1828                     self.fatal_proc_rec(&format!("Not found test at line{} \"{}\":{:?}",
1829                                                  if v.len() > 1 { "s" } else { "" }, entry, v),
1830                                         &res);
1831                 }
1832             }
1833         }
1834     }
1835
1836     fn run_codegen_units_test(&self) {
1837         assert!(self.revision.is_none(), "revisions not relevant here");
1838
1839         let proc_res = self.compile_test();
1840
1841         if !proc_res.status.success() {
1842             self.fatal_proc_rec("compilation failed!", &proc_res);
1843         }
1844
1845         self.check_no_compiler_crash(&proc_res);
1846
1847         const PREFIX: &'static str = "TRANS_ITEM ";
1848         const CGU_MARKER: &'static str = "@@";
1849
1850         let actual: Vec<TransItem> = proc_res
1851             .stdout
1852             .lines()
1853             .filter(|line| line.starts_with(PREFIX))
1854             .map(str_to_trans_item)
1855             .collect();
1856
1857         let expected: Vec<TransItem> = errors::load_errors(&self.testpaths.file, None)
1858             .iter()
1859             .map(|e| str_to_trans_item(&e.msg[..]))
1860             .collect();
1861
1862         let mut missing = Vec::new();
1863         let mut wrong_cgus = Vec::new();
1864
1865         for expected_item in &expected {
1866             let actual_item_with_same_name = actual.iter()
1867                                                    .find(|ti| ti.name == expected_item.name);
1868
1869             if let Some(actual_item) = actual_item_with_same_name {
1870                 if !expected_item.codegen_units.is_empty() &&
1871                    // Also check for codegen units
1872                    expected_item.codegen_units != actual_item.codegen_units {
1873                     wrong_cgus.push((expected_item.clone(), actual_item.clone()));
1874                 }
1875             } else {
1876                 missing.push(expected_item.string.clone());
1877             }
1878         }
1879
1880         let unexpected: Vec<_> =
1881             actual.iter()
1882                   .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name))
1883                   .map(|acgu| acgu.string.clone())
1884                   .collect();
1885
1886         if !missing.is_empty() {
1887             missing.sort();
1888
1889             println!("\nThese items should have been contained but were not:\n");
1890
1891             for item in &missing {
1892                 println!("{}", item);
1893             }
1894
1895             println!("\n");
1896         }
1897
1898         if !unexpected.is_empty() {
1899             let sorted = {
1900                 let mut sorted = unexpected.clone();
1901                 sorted.sort();
1902                 sorted
1903             };
1904
1905             println!("\nThese items were contained but should not have been:\n");
1906
1907             for item in sorted {
1908                 println!("{}", item);
1909             }
1910
1911             println!("\n");
1912         }
1913
1914         if !wrong_cgus.is_empty() {
1915             wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
1916             println!("\nThe following items were assigned to wrong codegen units:\n");
1917
1918             for &(ref expected_item, ref actual_item) in &wrong_cgus {
1919                 println!("{}", expected_item.name);
1920                 println!("  expected: {}", codegen_units_to_str(&expected_item.codegen_units));
1921                 println!("  actual:   {}", codegen_units_to_str(&actual_item.codegen_units));
1922                 println!("");
1923             }
1924         }
1925
1926         if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty())
1927         {
1928             panic!();
1929         }
1930
1931         #[derive(Clone, Eq, PartialEq)]
1932         struct TransItem {
1933             name: String,
1934             codegen_units: HashSet<String>,
1935             string: String,
1936         }
1937
1938         // [TRANS_ITEM] name [@@ (cgu)+]
1939         fn str_to_trans_item(s: &str) -> TransItem {
1940             let s = if s.starts_with(PREFIX) {
1941                 (&s[PREFIX.len()..]).trim()
1942             } else {
1943                 s.trim()
1944             };
1945
1946             let full_string = format!("{}{}", PREFIX, s.trim().to_owned());
1947
1948             let parts: Vec<&str> = s.split(CGU_MARKER)
1949                                     .map(str::trim)
1950                                     .filter(|s| !s.is_empty())
1951                                     .collect();
1952
1953             let name = parts[0].trim();
1954
1955             let cgus = if parts.len() > 1 {
1956                 let cgus_str = parts[1];
1957
1958                 cgus_str.split(' ')
1959                         .map(str::trim)
1960                         .filter(|s| !s.is_empty())
1961                         .map(str::to_owned)
1962                         .collect()
1963             }
1964             else {
1965                 HashSet::new()
1966             };
1967
1968             TransItem {
1969                 name: name.to_owned(),
1970                 codegen_units: cgus,
1971                 string: full_string,
1972             }
1973         }
1974
1975         fn codegen_units_to_str(cgus: &HashSet<String>) -> String
1976         {
1977             let mut cgus: Vec<_> = cgus.iter().collect();
1978             cgus.sort();
1979
1980             let mut string = String::new();
1981             for cgu in cgus {
1982                 string.push_str(&cgu[..]);
1983                 string.push_str(" ");
1984             }
1985
1986             string
1987         }
1988     }
1989
1990     fn init_incremental_test(&self) {
1991         // (See `run_incremental_test` for an overview of how incremental tests work.)
1992
1993         // Before any of the revisions have executed, create the
1994         // incremental workproduct directory.  Delete any old
1995         // incremental work products that may be there from prior
1996         // runs.
1997         let incremental_dir = self.incremental_dir();
1998         if incremental_dir.exists() {
1999             // Canonicalizing the path will convert it to the //?/ format
2000             // on Windows, which enables paths longer than 260 character
2001             let canonicalized = incremental_dir.canonicalize().unwrap();
2002             fs::remove_dir_all(canonicalized).unwrap();
2003         }
2004         fs::create_dir_all(&incremental_dir).unwrap();
2005
2006         if self.config.verbose {
2007             print!("init_incremental_test: incremental_dir={}", incremental_dir.display());
2008         }
2009     }
2010
2011     fn run_incremental_test(&self) {
2012         // Basic plan for a test incremental/foo/bar.rs:
2013         // - load list of revisions rpass1, cfail2, rpass3
2014         //   - each should begin with `rpass`, `cfail`, or `cfail`
2015         //   - if `rpass`, expect compile and execution to succeed
2016         //   - if `cfail`, expect compilation to fail
2017         //   - if `rfail`, expect execution to fail
2018         // - create a directory build/foo/bar.incremental
2019         // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass1
2020         //   - because name of revision starts with "rpass", expect success
2021         // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C cfail2
2022         //   - because name of revision starts with "cfail", expect an error
2023         //   - load expected errors as usual, but filter for those that end in `[rfail2]`
2024         // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass3
2025         //   - because name of revision starts with "rpass", expect success
2026         // - execute build/foo/bar.exe and save output
2027         //
2028         // FIXME -- use non-incremental mode as an oracle? That doesn't apply
2029         // to #[rustc_dirty] and clean tests I guess
2030
2031         let revision = self.revision.expect("incremental tests require a list of revisions");
2032
2033         // Incremental workproduct directory should have already been created.
2034         let incremental_dir = self.incremental_dir();
2035         assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
2036
2037         // Add an extra flag pointing at the incremental directory.
2038         let mut revision_props = self.props.clone();
2039         revision_props.incremental_dir = Some(incremental_dir);
2040         revision_props.compile_flags.push(String::from("-Zincremental-info"));
2041
2042         let revision_cx = TestCx {
2043             config: self.config,
2044             props: &revision_props,
2045             testpaths: self.testpaths,
2046             revision: self.revision,
2047         };
2048
2049         if self.config.verbose {
2050             print!("revision={:?} revision_props={:#?}", revision, revision_props);
2051         }
2052
2053         if revision.starts_with("rpass") {
2054             revision_cx.run_rpass_test();
2055         } else if revision.starts_with("rfail") {
2056             revision_cx.run_rfail_test();
2057         } else if revision.starts_with("cfail") {
2058             revision_cx.run_cfail_test();
2059         } else {
2060             revision_cx.fatal(
2061                 "revision name must begin with rpass, rfail, or cfail");
2062         }
2063     }
2064
2065     /// Directory where incremental work products are stored.
2066     fn incremental_dir(&self) -> PathBuf {
2067         self.output_base_name().with_extension("inc")
2068     }
2069
2070     fn run_rmake_test(&self) {
2071         // FIXME(#11094): we should fix these tests
2072         if self.config.host != self.config.target {
2073             return
2074         }
2075
2076         let cwd = env::current_dir().unwrap();
2077         let src_root = self.config.src_base.parent().unwrap()
2078                                            .parent().unwrap()
2079                                            .parent().unwrap();
2080         let src_root = cwd.join(&src_root);
2081
2082         let tmpdir = cwd.join(self.output_base_name());
2083         if tmpdir.exists() {
2084             self.aggressive_rm_rf(&tmpdir).unwrap();
2085         }
2086         create_dir_all(&tmpdir).unwrap();
2087
2088         let host = &self.config.host;
2089         let make = if host.contains("bitrig") || host.contains("dragonfly") ||
2090             host.contains("freebsd") || host.contains("netbsd") ||
2091             host.contains("openbsd") {
2092             "gmake"
2093         } else {
2094             "make"
2095         };
2096
2097         let mut cmd = Command::new(make);
2098         cmd.current_dir(&self.testpaths.file)
2099            .env("TARGET", &self.config.target)
2100            .env("PYTHON", &self.config.docck_python)
2101            .env("S", src_root)
2102            .env("RUST_BUILD_STAGE", &self.config.stage_id)
2103            .env("RUSTC", cwd.join(&self.config.rustc_path))
2104            .env("RUSTDOC",
2105                cwd.join(&self.config.rustdoc_path.as_ref().expect("--rustdoc-path passed")))
2106            .env("TMPDIR", &tmpdir)
2107            .env("LD_LIB_PATH_ENVVAR", dylib_env_var())
2108            .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path))
2109            .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path))
2110            .env("LLVM_COMPONENTS", &self.config.llvm_components)
2111            .env("LLVM_CXXFLAGS", &self.config.llvm_cxxflags);
2112
2113         // We don't want RUSTFLAGS set from the outside to interfere with
2114         // compiler flags set in the test cases:
2115         cmd.env_remove("RUSTFLAGS");
2116
2117         if self.config.target.contains("msvc") {
2118             // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
2119             // and that `lib.exe` lives next to it.
2120             let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe");
2121
2122             // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
2123             // a path and instead passes `C:\msys64\foo`, so convert all
2124             // `/`-arguments to MSVC here to `-` arguments.
2125             let cflags = self.config.cflags.split(' ').map(|s| s.replace("/", "-"))
2126                                                  .collect::<Vec<_>>().join(" ");
2127
2128             cmd.env("IS_MSVC", "1")
2129                .env("IS_WINDOWS", "1")
2130                .env("MSVC_LIB", format!("'{}' -nologo", lib.display()))
2131                .env("CC", format!("'{}' {}", self.config.cc, cflags))
2132                .env("CXX", &self.config.cxx);
2133         } else {
2134             cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags))
2135                .env("CXX", format!("{} {}", self.config.cxx, self.config.cflags));
2136
2137             if self.config.target.contains("windows") {
2138                 cmd.env("IS_WINDOWS", "1");
2139             }
2140         }
2141
2142         let output = cmd.output().expect("failed to spawn `make`");
2143         if !output.status.success() {
2144             let res = ProcRes {
2145                 status: output.status,
2146                 stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
2147                 stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
2148                 cmdline: format!("{:?}", cmd),
2149             };
2150             self.fatal_proc_rec("make failed", &res);
2151         }
2152     }
2153
2154     fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
2155         for e in path.read_dir()? {
2156             let entry = e?;
2157             let path = entry.path();
2158             if entry.file_type()?.is_dir() {
2159                 self.aggressive_rm_rf(&path)?;
2160             } else {
2161                 // Remove readonly files as well on windows (by default we can't)
2162                 fs::remove_file(&path).or_else(|e| {
2163                     if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied {
2164                         let mut meta = entry.metadata()?.permissions();
2165                         meta.set_readonly(false);
2166                         fs::set_permissions(&path, meta)?;
2167                         fs::remove_file(&path)
2168                     } else {
2169                         Err(e)
2170                     }
2171                 })?;
2172             }
2173         }
2174         fs::remove_dir(path)
2175     }
2176
2177     fn run_ui_test(&self) {
2178         let proc_res = self.compile_test();
2179
2180         let expected_stderr_path = self.expected_output_path("stderr");
2181         let expected_stderr = self.load_expected_output(&expected_stderr_path);
2182
2183         let expected_stdout_path = self.expected_output_path("stdout");
2184         let expected_stdout = self.load_expected_output(&expected_stdout_path);
2185
2186         let normalized_stdout =
2187             self.normalize_output(&proc_res.stdout, &self.props.normalize_stdout);
2188         let normalized_stderr =
2189             self.normalize_output(&proc_res.stderr, &self.props.normalize_stderr);
2190
2191         let mut errors = 0;
2192         errors += self.compare_output("stdout", &normalized_stdout, &expected_stdout);
2193         errors += self.compare_output("stderr", &normalized_stderr, &expected_stderr);
2194
2195         if errors > 0 {
2196             println!("To update references, run this command from build directory:");
2197             let relative_path_to_file =
2198                 self.testpaths.relative_dir
2199                               .join(self.testpaths.file.file_name().unwrap());
2200             println!("{}/update-references.sh '{}' '{}'",
2201                      self.config.src_base.display(),
2202                      self.config.build_base.display(),
2203                      relative_path_to_file.display());
2204             self.fatal_proc_rec(&format!("{} errors occurred comparing output.", errors),
2205                                 &proc_res);
2206         }
2207
2208         if self.props.run_pass {
2209             let proc_res = self.exec_compiled_test();
2210
2211             if !proc_res.status.success() {
2212                 self.fatal_proc_rec("test run failed!", &proc_res);
2213             }
2214         }
2215     }
2216
2217     fn run_mir_opt_test(&self) {
2218         let proc_res = self.compile_test();
2219
2220         if !proc_res.status.success() {
2221             self.fatal_proc_rec("compilation failed!", &proc_res);
2222         }
2223
2224         let proc_res = self.exec_compiled_test();
2225
2226         if !proc_res.status.success() {
2227             self.fatal_proc_rec("test run failed!", &proc_res);
2228         }
2229         self.check_mir_dump();
2230     }
2231
2232     fn check_mir_dump(&self) {
2233         let mut test_file_contents = String::new();
2234         fs::File::open(self.testpaths.file.clone()).unwrap()
2235                                                    .read_to_string(&mut test_file_contents)
2236                                                    .unwrap();
2237         if let Some(idx) =  test_file_contents.find("// END RUST SOURCE") {
2238             let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len());
2239             let tests_text_str = String::from(tests_text);
2240             let mut curr_test : Option<&str> = None;
2241             let mut curr_test_contents = vec![ExpectedLine::Elision];
2242             for l in tests_text_str.lines() {
2243                 debug!("line: {:?}", l);
2244                 if l.starts_with("// START ") {
2245                     let (_, t) = l.split_at("// START ".len());
2246                     curr_test = Some(t);
2247                 } else if l.starts_with("// END") {
2248                     let (_, t) = l.split_at("// END ".len());
2249                     if Some(t) != curr_test {
2250                         panic!("mismatched START END test name");
2251                     }
2252                     self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents);
2253                     curr_test = None;
2254                     curr_test_contents.clear();
2255                     curr_test_contents.push(ExpectedLine::Elision);
2256                 } else if l.is_empty() {
2257                     // ignore
2258                 } else if l.starts_with("//") && l.split_at("//".len()).1.trim() == "..." {
2259                     curr_test_contents.push(ExpectedLine::Elision)
2260                 } else if l.starts_with("// ") {
2261                     let (_, test_content) = l.split_at("// ".len());
2262                     curr_test_contents.push(ExpectedLine::Text(test_content));
2263                 }
2264             }
2265         }
2266     }
2267
2268     fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) {
2269         let t = |file| FileTime::from_last_modification_time(&fs::metadata(file).unwrap());
2270         let source_file = &self.testpaths.file;
2271         let output_time = t(output_file);
2272         let source_time = t(source_file);
2273         if source_time > output_time {
2274             debug!("source file time: {:?} output file time: {:?}", source_time, output_time);
2275             panic!("test source file `{}` is newer than potentially stale output file `{}`.",
2276                    source_file.display(), test_name);
2277         }
2278     }
2279
2280     fn compare_mir_test_output(&self, test_name: &str, expected_content: &[ExpectedLine<&str>]) {
2281         let mut output_file = PathBuf::new();
2282         output_file.push(self.get_mir_dump_dir());
2283         output_file.push(test_name);
2284         debug!("comparing the contests of: {:?}", output_file);
2285         debug!("with: {:?}", expected_content);
2286         self.check_mir_test_timestamp(test_name, &output_file);
2287
2288         let mut dumped_file = fs::File::open(output_file.clone()).unwrap();
2289         let mut dumped_string = String::new();
2290         dumped_file.read_to_string(&mut dumped_string).unwrap();
2291         let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty());
2292         let mut expected_lines = expected_content.iter().filter(|&l| {
2293             if let &ExpectedLine::Text(l) = l {
2294                 !l.is_empty()
2295             } else {
2296                 true
2297             }
2298         }).peekable();
2299
2300         let compare = |expected_line, dumped_line| {
2301             let e_norm = normalize_mir_line(expected_line);
2302             let d_norm = normalize_mir_line(dumped_line);
2303             debug!("found: {:?}", d_norm);
2304             debug!("expected: {:?}", e_norm);
2305             e_norm == d_norm
2306         };
2307
2308         let error = |expected_line, extra_msg| {
2309             let normalize_all = dumped_string.lines()
2310                                              .map(nocomment_mir_line)
2311                                              .filter(|l| !l.is_empty())
2312                                              .collect::<Vec<_>>()
2313                                              .join("\n");
2314             let f = |l: &ExpectedLine<_>| match l {
2315                 &ExpectedLine::Elision => "... (elided)".into(),
2316                 &ExpectedLine::Text(t) => t
2317             };
2318             let expected_content = expected_content.iter()
2319                                                    .map(|l| f(l))
2320                                                    .collect::<Vec<_>>()
2321                                                    .join("\n");
2322             panic!("Did not find expected line, error: {}\n\
2323                    Actual Line: {:?}\n\
2324                    Expected:\n{}\n\
2325                    Actual:\n{}",
2326                    extra_msg,
2327                    expected_line,
2328                    expected_content,
2329                    normalize_all);
2330         };
2331
2332         // We expect each non-empty line to appear consecutively, non-consecutive lines
2333         // must be separated by at least one Elision
2334         while let Some(dumped_line) = dumped_lines.next() {
2335             match expected_lines.next() {
2336                 Some(&ExpectedLine::Text(expected_line)) =>
2337                     if !compare(expected_line, dumped_line) {
2338                         error(expected_line,
2339                               format!("Mismatch in lines\nExpected Line: {:?}", dumped_line));
2340                     },
2341                 Some(&ExpectedLine::Elision) => {
2342                     // skip any number of elisions in a row.
2343                     while let Some(&&ExpectedLine::Elision) = expected_lines.peek() {
2344                         expected_lines.next();
2345                     }
2346                     if let Some(&ExpectedLine::Text(expected_line)) = expected_lines.next() {
2347                         let mut found = compare(expected_line, dumped_line);
2348                         if found {
2349                             continue;
2350                         }
2351                         while let Some(dumped_line) = dumped_lines.next() {
2352                             found = compare(expected_line, dumped_line);
2353                             if found {
2354                                 break;
2355                             }
2356                         }
2357                         if !found {
2358                             error(expected_line, "ran out of mir dump to match against".into());
2359                         }
2360                     }
2361                 },
2362                 None => {},
2363             }
2364         }
2365     }
2366
2367     fn get_mir_dump_dir(&self) -> PathBuf {
2368         let mut mir_dump_dir = PathBuf::from(self.config.build_base
2369                                                     .as_path()
2370                                                     .to_str()
2371                                                     .unwrap());
2372         debug!("input_file: {:?}", self.testpaths.file);
2373         mir_dump_dir.push(self.testpaths.file.file_stem().unwrap().to_str().unwrap());
2374         mir_dump_dir
2375     }
2376
2377     fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String {
2378         let parent_dir = self.testpaths.file.parent().unwrap();
2379         let parent_dir_str = parent_dir.display().to_string();
2380         let mut normalized = output.replace(&parent_dir_str, "$DIR")
2381               .replace("\\", "/") // normalize for paths on windows
2382               .replace("\r\n", "\n") // normalize for linebreaks on windows
2383               .replace("\t", "\\t"); // makes tabs visible
2384         for rule in custom_rules {
2385             normalized = normalized.replace(&rule.0, &rule.1);
2386         }
2387         normalized
2388     }
2389
2390     fn expected_output_path(&self, kind: &str) -> PathBuf {
2391         let extension = match self.revision {
2392             Some(r) => format!("{}.{}", r, kind),
2393             None => kind.to_string(),
2394         };
2395         self.testpaths.file.with_extension(extension)
2396     }
2397
2398     fn load_expected_output(&self, path: &Path) -> String {
2399         if !path.exists() {
2400             return String::new();
2401         }
2402
2403         let mut result = String::new();
2404         match File::open(path).and_then(|mut f| f.read_to_string(&mut result)) {
2405             Ok(_) => result,
2406             Err(e) => {
2407                 self.fatal(&format!("failed to load expected output from `{}`: {}",
2408                                     path.display(), e))
2409             }
2410         }
2411     }
2412
2413     fn compare_output(&self, kind: &str, actual: &str, expected: &str) -> usize {
2414         if actual == expected {
2415             return 0;
2416         }
2417
2418         println!("normalized {}:\n{}\n", kind, actual);
2419         println!("expected {}:\n{}\n", kind, expected);
2420         println!("diff of {}:\n", kind);
2421
2422         for diff in diff::lines(expected, actual) {
2423             match diff {
2424                 diff::Result::Left(l)    => println!("-{}", l),
2425                 diff::Result::Both(l, _) => println!(" {}", l),
2426                 diff::Result::Right(r)   => println!("+{}", r),
2427             }
2428         }
2429
2430         let output_file = self.output_base_name().with_extension(kind);
2431         match File::create(&output_file).and_then(|mut f| f.write_all(actual.as_bytes())) {
2432             Ok(()) => { }
2433             Err(e) => {
2434                 self.fatal(&format!("failed to write {} to `{}`: {}",
2435                                     kind, output_file.display(), e))
2436             }
2437         }
2438
2439         println!("\nThe actual {0} differed from the expected {0}.", kind);
2440         println!("Actual {} saved to {}", kind, output_file.display());
2441         1
2442     }
2443 }
2444
2445 struct ProcArgs {
2446     prog: String,
2447     args: Vec<String>,
2448 }
2449
2450 pub struct ProcRes {
2451     status: ExitStatus,
2452     stdout: String,
2453     stderr: String,
2454     cmdline: String,
2455 }
2456
2457 impl ProcRes {
2458     pub fn fatal(&self, err: Option<&str>) -> ! {
2459         if let Some(e) = err {
2460             println!("\nerror: {}", e);
2461         }
2462         print!("\
2463             status: {}\n\
2464             command: {}\n\
2465             stdout:\n\
2466             ------------------------------------------\n\
2467             {}\n\
2468             ------------------------------------------\n\
2469             stderr:\n\
2470             ------------------------------------------\n\
2471             {}\n\
2472             ------------------------------------------\n\
2473             \n",
2474                self.status, self.cmdline, self.stdout,
2475                self.stderr);
2476         panic!();
2477     }
2478 }
2479
2480 enum TargetLocation {
2481     ThisFile(PathBuf),
2482     ThisDirectory(PathBuf),
2483 }
2484
2485 #[derive(Clone, PartialEq, Eq)]
2486 enum ExpectedLine<T: AsRef<str>> {
2487     Elision,
2488     Text(T)
2489 }
2490
2491 impl<T> fmt::Debug for ExpectedLine<T>
2492 where
2493     T: AsRef<str> + fmt::Debug
2494 {
2495     fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2496         if let &ExpectedLine::Text(ref t) = self {
2497             write!(formatter, "{:?}", t)
2498         } else {
2499             write!(formatter, "\"...\" (Elision)")
2500         }
2501     }
2502 }
2503
2504 fn normalize_mir_line(line: &str) -> String {
2505     nocomment_mir_line(line).replace(char::is_whitespace, "")
2506 }
2507
2508 fn nocomment_mir_line(line: &str) -> &str {
2509     if let Some(idx) = line.find("//") {
2510         let (l, _) = line.split_at(idx);
2511         l.trim_right()
2512     } else {
2513         line
2514     }
2515 }