]> git.lizzy.rs Git - rust.git/blob - src/compiletest/runtest.rs
Auto merge of #30413 - pnkfelix:fsk-span_note, r=Manishearth
[rust.git] / src / compiletest / 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};
14 use errors;
15 use header::TestProps;
16 use header;
17 use procsrv;
18 use util::logv;
19
20 use std::env;
21 use std::fmt;
22 use std::fs::{self, File};
23 use std::io::BufReader;
24 use std::io::prelude::*;
25 use std::net::TcpStream;
26 use std::path::{Path, PathBuf, Component};
27 use std::process::{Command, Output, ExitStatus};
28
29 pub fn run(config: Config, testfile: &Path) {
30     match &*config.target {
31
32         "arm-linux-androideabi" | "aarch64-linux-android" => {
33             if !config.adb_device_status {
34                 panic!("android device not available");
35             }
36         }
37
38         _=> { }
39     }
40
41     if config.verbose {
42         // We're going to be dumping a lot of info. Start on a new line.
43         print!("\n\n");
44     }
45     debug!("running {:?}", testfile.display());
46     let props = header::load_props(&testfile);
47     debug!("loaded props");
48     match config.mode {
49         CompileFail => run_cfail_test(&config, &props, &testfile),
50         ParseFail => run_cfail_test(&config, &props, &testfile),
51         RunFail => run_rfail_test(&config, &props, &testfile),
52         RunPass => run_rpass_test(&config, &props, &testfile),
53         RunPassValgrind => run_valgrind_test(&config, &props, &testfile),
54         Pretty => run_pretty_test(&config, &props, &testfile),
55         DebugInfoGdb => run_debuginfo_gdb_test(&config, &props, &testfile),
56         DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testfile),
57         Codegen => run_codegen_test(&config, &props, &testfile),
58         Rustdoc => run_rustdoc_test(&config, &props, &testfile),
59     }
60 }
61
62 fn get_output(props: &TestProps, proc_res: &ProcRes) -> String {
63     if props.check_stdout {
64         format!("{}{}", proc_res.stdout, proc_res.stderr)
65     } else {
66         proc_res.stderr.clone()
67     }
68 }
69
70 fn run_cfail_test(config: &Config, props: &TestProps, testfile: &Path) {
71     let proc_res = compile_test(config, props, testfile);
72
73     if proc_res.status.success() {
74         fatal_proc_rec(&format!("{} test compiled successfully!", config.mode)[..],
75                       &proc_res);
76     }
77
78     check_correct_failure_status(&proc_res);
79
80     if proc_res.status.success() {
81         fatal("process did not return an error status");
82     }
83
84     let output_to_check = get_output(props, &proc_res);
85     let expected_errors = errors::load_errors(testfile);
86     if !expected_errors.is_empty() {
87         if !props.error_patterns.is_empty() {
88             fatal("both error pattern and expected errors specified");
89         }
90         check_expected_errors(expected_errors, testfile, &proc_res);
91     } else {
92         check_error_patterns(props, testfile, &output_to_check, &proc_res);
93     }
94     check_no_compiler_crash(&proc_res);
95     check_forbid_output(props, &output_to_check, &proc_res);
96 }
97
98 fn run_rfail_test(config: &Config, props: &TestProps, testfile: &Path) {
99     let proc_res = compile_test(config, props, testfile);
100
101     if !proc_res.status.success() {
102         fatal_proc_rec("compilation failed!", &proc_res);
103     }
104
105     let proc_res = exec_compiled_test(config, props, testfile);
106
107     // The value our Makefile configures valgrind to return on failure
108     const VALGRIND_ERR: i32 = 100;
109     if proc_res.status.code() == Some(VALGRIND_ERR) {
110         fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
111     }
112
113     let output_to_check = get_output(props, &proc_res);
114     check_correct_failure_status(&proc_res);
115     check_error_patterns(props, testfile, &output_to_check, &proc_res);
116 }
117
118 fn check_correct_failure_status(proc_res: &ProcRes) {
119     // The value the rust runtime returns on failure
120     const RUST_ERR: i32 = 101;
121     if proc_res.status.code() != Some(RUST_ERR) {
122         fatal_proc_rec(
123             &format!("failure produced the wrong error: {}",
124                      proc_res.status),
125             proc_res);
126     }
127 }
128
129 fn run_rpass_test(config: &Config, props: &TestProps, testfile: &Path) {
130     let proc_res = compile_test(config, props, testfile);
131
132     if !proc_res.status.success() {
133         fatal_proc_rec("compilation failed!", &proc_res);
134     }
135
136     let proc_res = exec_compiled_test(config, props, testfile);
137
138     if !proc_res.status.success() {
139         fatal_proc_rec("test run failed!", &proc_res);
140     }
141 }
142
143 fn run_valgrind_test(config: &Config, props: &TestProps, testfile: &Path) {
144     if config.valgrind_path.is_none() {
145         assert!(!config.force_valgrind);
146         return run_rpass_test(config, props, testfile);
147     }
148
149     let mut proc_res = compile_test(config, props, testfile);
150
151     if !proc_res.status.success() {
152         fatal_proc_rec("compilation failed!", &proc_res);
153     }
154
155     let mut new_config = config.clone();
156     new_config.runtool = new_config.valgrind_path.clone();
157     proc_res = exec_compiled_test(&new_config, props, testfile);
158
159     if !proc_res.status.success() {
160         fatal_proc_rec("test run failed!", &proc_res);
161     }
162 }
163
164 fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
165     if props.pp_exact.is_some() {
166         logv(config, "testing for exact pretty-printing".to_owned());
167     } else {
168         logv(config, "testing for converging pretty-printing".to_owned());
169     }
170
171     let rounds =
172         match props.pp_exact { Some(_) => 1, None => 2 };
173
174     let mut src = String::new();
175     File::open(testfile).unwrap().read_to_string(&mut src).unwrap();
176     let mut srcs = vec!(src);
177
178     let mut round = 0;
179     while round < rounds {
180         logv(config, format!("pretty-printing round {}", round));
181         let proc_res = print_source(config,
182                                     props,
183                                     testfile,
184                                     srcs[round].to_owned(),
185                                     &props.pretty_mode);
186
187         if !proc_res.status.success() {
188             fatal_proc_rec(&format!("pretty-printing failed in round {}", round),
189                           &proc_res);
190         }
191
192         let ProcRes{ stdout, .. } = proc_res;
193         srcs.push(stdout);
194         round += 1;
195     }
196
197     let mut expected = match props.pp_exact {
198         Some(ref file) => {
199             let filepath = testfile.parent().unwrap().join(file);
200             let mut s = String::new();
201             File::open(&filepath).unwrap().read_to_string(&mut s).unwrap();
202             s
203         }
204         None => { srcs[srcs.len() - 2].clone() }
205     };
206     let mut actual = srcs[srcs.len() - 1].clone();
207
208     if props.pp_exact.is_some() {
209         // Now we have to care about line endings
210         let cr = "\r".to_owned();
211         actual = actual.replace(&cr, "").to_owned();
212         expected = expected.replace(&cr, "").to_owned();
213     }
214
215     compare_source(&expected, &actual);
216
217     // If we're only making sure that the output matches then just stop here
218     if props.pretty_compare_only { return; }
219
220     // Finally, let's make sure it actually appears to remain valid code
221     let proc_res = typecheck_source(config, props, testfile, actual);
222
223     if !proc_res.status.success() {
224         fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
225     }
226     if !props.pretty_expanded { return }
227
228     // additionally, run `--pretty expanded` and try to build it.
229     let proc_res = print_source(config, props, testfile, srcs[round].clone(), "expanded");
230     if !proc_res.status.success() {
231         fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
232     }
233
234     let ProcRes{ stdout: expanded_src, .. } = proc_res;
235     let proc_res = typecheck_source(config, props, testfile, expanded_src);
236     if !proc_res.status.success() {
237         fatal_proc_rec("pretty-printed source (expanded) does not typecheck",
238                       &proc_res);
239     }
240
241     return;
242
243     fn print_source(config: &Config,
244                     props: &TestProps,
245                     testfile: &Path,
246                     src: String,
247                     pretty_type: &str) -> ProcRes {
248         let aux_dir = aux_output_dir_name(config, testfile);
249         compose_and_run(config,
250                         testfile,
251                         make_pp_args(config,
252                                      props,
253                                      testfile,
254                                      pretty_type.to_owned()),
255                         props.exec_env.clone(),
256                         &config.compile_lib_path,
257                         Some(aux_dir.to_str().unwrap()),
258                         Some(src))
259     }
260
261     fn make_pp_args(config: &Config,
262                     props: &TestProps,
263                     testfile: &Path,
264                     pretty_type: String) -> ProcArgs {
265         let aux_dir = aux_output_dir_name(config, testfile);
266         // FIXME (#9639): This needs to handle non-utf8 paths
267         let mut args = vec!("-".to_owned(),
268                             "-Zunstable-options".to_owned(),
269                             "--unpretty".to_owned(),
270                             pretty_type,
271                             format!("--target={}", config.target),
272                             "-L".to_owned(),
273                             aux_dir.to_str().unwrap().to_owned());
274         args.extend(split_maybe_args(&config.target_rustcflags));
275         args.extend(split_maybe_args(&props.compile_flags));
276         return ProcArgs {
277             prog: config.rustc_path.to_str().unwrap().to_owned(),
278             args: args,
279         };
280     }
281
282     fn compare_source(expected: &str, actual: &str) {
283         if expected != actual {
284             error("pretty-printed source does not match expected source");
285             println!("\n\
286 expected:\n\
287 ------------------------------------------\n\
288 {}\n\
289 ------------------------------------------\n\
290 actual:\n\
291 ------------------------------------------\n\
292 {}\n\
293 ------------------------------------------\n\
294 \n",
295                      expected, actual);
296             panic!();
297         }
298     }
299
300     fn typecheck_source(config: &Config, props: &TestProps,
301                         testfile: &Path, src: String) -> ProcRes {
302         let args = make_typecheck_args(config, props, testfile);
303         compose_and_run_compiler(config, props, testfile, args, Some(src))
304     }
305
306     fn make_typecheck_args(config: &Config, props: &TestProps, testfile: &Path) -> ProcArgs {
307         let aux_dir = aux_output_dir_name(config, testfile);
308         let target = if props.force_host {
309             &*config.host
310         } else {
311             &*config.target
312         };
313         // FIXME (#9639): This needs to handle non-utf8 paths
314         let mut args = vec!("-".to_owned(),
315                             "-Zno-trans".to_owned(),
316                             format!("--target={}", target),
317                             "-L".to_owned(),
318                             config.build_base.to_str().unwrap().to_owned(),
319                             "-L".to_owned(),
320                             aux_dir.to_str().unwrap().to_owned());
321         args.extend(split_maybe_args(&config.target_rustcflags));
322         args.extend(split_maybe_args(&props.compile_flags));
323         // FIXME (#9639): This needs to handle non-utf8 paths
324         return ProcArgs {
325             prog: config.rustc_path.to_str().unwrap().to_owned(),
326             args: args,
327         };
328     }
329 }
330
331 fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
332     let mut config = Config {
333         target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
334         host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
335         .. config.clone()
336     };
337
338     let config = &mut config;
339     let DebuggerCommands {
340         commands,
341         check_lines,
342         breakpoint_lines
343     } = parse_debugger_commands(testfile, "gdb");
344     let mut cmds = commands.join("\n");
345
346     // compile test file (it should have 'compile-flags:-g' in the header)
347     let compiler_run_result = compile_test(config, props, testfile);
348     if !compiler_run_result.status.success() {
349         fatal_proc_rec("compilation failed!", &compiler_run_result);
350     }
351
352     let exe_file = make_exe_name(config, testfile);
353
354     let debugger_run_result;
355     match &*config.target {
356         "arm-linux-androideabi" | "aarch64-linux-android" => {
357
358             cmds = cmds.replace("run", "continue");
359
360             // write debugger script
361             let mut script_str = String::with_capacity(2048);
362             script_str.push_str(&format!("set charset {}\n", charset()));
363             script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
364             script_str.push_str("target remote :5039\n");
365             script_str.push_str(&format!("set solib-search-path \
366                                          ./{}/stage2/lib/rustlib/{}/lib/\n",
367                                          config.host, config.target));
368             for line in &breakpoint_lines {
369                 script_str.push_str(&format!("break {:?}:{}\n",
370                                              testfile.file_name().unwrap()
371                                                      .to_string_lossy(),
372                                              *line)[..]);
373             }
374             script_str.push_str(&cmds);
375             script_str.push_str("\nquit\n");
376
377             debug!("script_str = {}", script_str);
378             dump_output_file(config,
379                              testfile,
380                              &script_str,
381                              "debugger.script");
382
383
384             procsrv::run("",
385                          &config.adb_path,
386                          None,
387                          &[
388                             "push".to_owned(),
389                             exe_file.to_str().unwrap().to_owned(),
390                             config.adb_test_dir.clone()
391                          ],
392                          vec!(("".to_owned(), "".to_owned())),
393                          Some("".to_owned()))
394                 .expect(&format!("failed to exec `{:?}`", config.adb_path));
395
396             procsrv::run("",
397                          &config.adb_path,
398                          None,
399                          &[
400                             "forward".to_owned(),
401                             "tcp:5039".to_owned(),
402                             "tcp:5039".to_owned()
403                          ],
404                          vec!(("".to_owned(), "".to_owned())),
405                          Some("".to_owned()))
406                 .expect(&format!("failed to exec `{:?}`", config.adb_path));
407
408             let adb_arg = format!("export LD_LIBRARY_PATH={}; \
409                                    gdbserver{} :5039 {}/{}",
410                                   config.adb_test_dir.clone(),
411                                   if config.target.contains("aarch64")
412                                   {"64"} else {""},
413                                   config.adb_test_dir.clone(),
414                                   exe_file.file_name().unwrap().to_str()
415                                           .unwrap());
416
417             let mut process = procsrv::run_background("",
418                                                       &config.adb_path
419                                                             ,
420                                                       None,
421                                                       &[
422                                                         "shell".to_owned(),
423                                                         adb_arg.clone()
424                                                       ],
425                                                       vec!(("".to_owned(),
426                                                             "".to_owned())),
427                                                       Some("".to_owned()))
428                 .expect(&format!("failed to exec `{:?}`", config.adb_path));
429             loop {
430                 //waiting 1 second for gdbserver start
431                 ::std::thread::sleep(::std::time::Duration::new(1,0));
432                 if TcpStream::connect("127.0.0.1:5039").is_ok() {
433                     break
434                 }
435             }
436
437             let tool_path = match config.android_cross_path.to_str() {
438                 Some(x) => x.to_owned(),
439                 None => fatal("cannot find android cross path")
440             };
441
442             let debugger_script = make_out_name(config, testfile, "debugger.script");
443             // FIXME (#9639): This needs to handle non-utf8 paths
444             let debugger_opts =
445                 vec!("-quiet".to_owned(),
446                      "-batch".to_owned(),
447                      "-nx".to_owned(),
448                      format!("-command={}", debugger_script.to_str().unwrap()));
449
450             let mut gdb_path = tool_path;
451             gdb_path.push_str(&format!("/bin/{}-gdb", config.target));
452             let procsrv::Result {
453                 out,
454                 err,
455                 status
456             } = procsrv::run("",
457                              &gdb_path,
458                              None,
459                              &debugger_opts,
460                              vec!(("".to_owned(), "".to_owned())),
461                              None)
462                 .expect(&format!("failed to exec `{:?}`", gdb_path));
463             let cmdline = {
464                 let cmdline = make_cmdline("",
465                                            &format!("{}-gdb", config.target),
466                                            &debugger_opts);
467                 logv(config, format!("executing {}", cmdline));
468                 cmdline
469             };
470
471             debugger_run_result = ProcRes {
472                 status: Status::Normal(status),
473                 stdout: out,
474                 stderr: err,
475                 cmdline: cmdline
476             };
477             if process.kill().is_err() {
478                 println!("Adb process is already finished.");
479             }
480         }
481
482         _=> {
483             let rust_src_root = find_rust_src_root(config)
484                 .expect("Could not find Rust source root");
485             let rust_pp_module_rel_path = Path::new("./src/etc");
486             let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
487                                                        .to_str()
488                                                        .unwrap()
489                                                        .to_owned();
490             // write debugger script
491             let mut script_str = String::with_capacity(2048);
492             script_str.push_str(&format!("set charset {}\n", charset()));
493             script_str.push_str("show version\n");
494
495             match config.gdb_version {
496                 Some(ref version) => {
497                     println!("NOTE: compiletest thinks it is using GDB version {}",
498                              version);
499
500                     if header::gdb_version_to_int(version) >
501                         header::gdb_version_to_int("7.4") {
502                         // Add the directory containing the pretty printers to
503                         // GDB's script auto loading safe path
504                         script_str.push_str(
505                             &format!("add-auto-load-safe-path {}\n",
506                                      rust_pp_module_abs_path.replace(r"\", r"\\"))
507                                 );
508                     }
509                 }
510                 _ => {
511                     println!("NOTE: compiletest does not know which version of \
512                               GDB it is using");
513                 }
514             }
515
516             // The following line actually doesn't have to do anything with
517             // pretty printing, it just tells GDB to print values on one line:
518             script_str.push_str("set print pretty off\n");
519
520             // Add the pretty printer directory to GDB's source-file search path
521             script_str.push_str(&format!("directory {}\n",
522                                          rust_pp_module_abs_path));
523
524             // Load the target executable
525             script_str.push_str(&format!("file {}\n",
526                                          exe_file.to_str().unwrap()
527                                                  .replace(r"\", r"\\")));
528
529             // Add line breakpoints
530             for line in &breakpoint_lines {
531                 script_str.push_str(&format!("break '{}':{}\n",
532                                              testfile.file_name().unwrap()
533                                                      .to_string_lossy(),
534                                              *line));
535             }
536
537             script_str.push_str(&cmds);
538             script_str.push_str("\nquit\n");
539
540             debug!("script_str = {}", script_str);
541             dump_output_file(config,
542                              testfile,
543                              &script_str,
544                              "debugger.script");
545
546             // run debugger script with gdb
547             fn debugger() -> &'static str {
548                 if cfg!(windows) {"gdb.exe"} else {"gdb"}
549             }
550
551             let debugger_script = make_out_name(config, testfile, "debugger.script");
552
553             // FIXME (#9639): This needs to handle non-utf8 paths
554             let debugger_opts =
555                 vec!("-quiet".to_owned(),
556                      "-batch".to_owned(),
557                      "-nx".to_owned(),
558                      format!("-command={}", debugger_script.to_str().unwrap()));
559
560             let proc_args = ProcArgs {
561                 prog: debugger().to_owned(),
562                 args: debugger_opts,
563             };
564
565             let environment = vec![("PYTHONPATH".to_owned(), rust_pp_module_abs_path)];
566
567             debugger_run_result = compose_and_run(config,
568                                                   testfile,
569                                                   proc_args,
570                                                   environment,
571                                                   &config.run_lib_path,
572                                                   None,
573                                                   None);
574         }
575     }
576
577     if !debugger_run_result.status.success() {
578         fatal("gdb failed to execute");
579     }
580
581     check_debugger_output(&debugger_run_result, &check_lines);
582 }
583
584 fn find_rust_src_root(config: &Config) -> Option<PathBuf> {
585     let mut path = config.src_base.clone();
586     let path_postfix = Path::new("src/etc/lldb_batchmode.py");
587
588     while path.pop() {
589         if path.join(&path_postfix).is_file() {
590             return Some(path);
591         }
592     }
593
594     return None;
595 }
596
597 fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) {
598     if config.lldb_python_dir.is_none() {
599         fatal("Can't run LLDB test because LLDB's python path is not set.");
600     }
601
602     let mut config = Config {
603         target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
604         host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
605         .. config.clone()
606     };
607
608     let config = &mut config;
609
610     // compile test file (it should have 'compile-flags:-g' in the header)
611     let compile_result = compile_test(config, props, testfile);
612     if !compile_result.status.success() {
613         fatal_proc_rec("compilation failed!", &compile_result);
614     }
615
616     let exe_file = make_exe_name(config, testfile);
617
618     match config.lldb_version {
619         Some(ref version) => {
620             println!("NOTE: compiletest thinks it is using LLDB version {}",
621                      version);
622         }
623         _ => {
624             println!("NOTE: compiletest does not know which version of \
625                       LLDB it is using");
626         }
627     }
628
629     // Parse debugger commands etc from test files
630     let DebuggerCommands {
631         commands,
632         check_lines,
633         breakpoint_lines,
634         ..
635     } = parse_debugger_commands(testfile, "lldb");
636
637     // Write debugger script:
638     // We don't want to hang when calling `quit` while the process is still running
639     let mut script_str = String::from("settings set auto-confirm true\n");
640
641     // Make LLDB emit its version, so we have it documented in the test output
642     script_str.push_str("version\n");
643
644     // Switch LLDB into "Rust mode"
645     let rust_src_root = find_rust_src_root(config)
646         .expect("Could not find Rust source root");
647     let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py");
648     let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
649                                                .to_str()
650                                                .unwrap()
651                                                .to_owned();
652
653     script_str.push_str(&format!("command script import {}\n",
654                                  &rust_pp_module_abs_path[..])[..]);
655     script_str.push_str("type summary add --no-value ");
656     script_str.push_str("--python-function lldb_rust_formatters.print_val ");
657     script_str.push_str("-x \".*\" --category Rust\n");
658     script_str.push_str("type category enable Rust\n");
659
660     // Set breakpoints on every line that contains the string "#break"
661     for line in &breakpoint_lines {
662         script_str.push_str(&format!("breakpoint set --line {}\n", line));
663     }
664
665     // Append the other commands
666     for line in &commands {
667         script_str.push_str(line);
668         script_str.push_str("\n");
669     }
670
671     // Finally, quit the debugger
672     script_str.push_str("\nquit\n");
673
674     // Write the script into a file
675     debug!("script_str = {}", script_str);
676     dump_output_file(config,
677                      testfile,
678                      &script_str,
679                      "debugger.script");
680     let debugger_script = make_out_name(config, testfile, "debugger.script");
681
682     // Let LLDB execute the script via lldb_batchmode.py
683     let debugger_run_result = run_lldb(config,
684                                        &exe_file,
685                                        &debugger_script,
686                                        &rust_src_root);
687
688     if !debugger_run_result.status.success() {
689         fatal_proc_rec("Error while running LLDB", &debugger_run_result);
690     }
691
692     check_debugger_output(&debugger_run_result, &check_lines);
693
694     fn run_lldb(config: &Config,
695                 test_executable: &Path,
696                 debugger_script: &Path,
697                 rust_src_root: &Path)
698                 -> ProcRes {
699         // Prepare the lldb_batchmode which executes the debugger script
700         let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
701         cmd2procres(config,
702                     test_executable,
703                     Command::new(&config.python)
704                             .arg(&lldb_script_path)
705                             .arg(test_executable)
706                             .arg(debugger_script)
707                             .env("PYTHONPATH",
708                                  config.lldb_python_dir.as_ref().unwrap()))
709     }
710 }
711
712 fn cmd2procres(config: &Config, test_executable: &Path, cmd: &mut Command)
713               -> ProcRes {
714     let (status, out, err) = match cmd.output() {
715         Ok(Output { status, stdout, stderr }) => {
716             (status,
717              String::from_utf8(stdout).unwrap(),
718              String::from_utf8(stderr).unwrap())
719         },
720         Err(e) => {
721             fatal(&format!("Failed to setup Python process for \
722                             LLDB script: {}", e))
723         }
724     };
725
726     dump_output(config, test_executable, &out, &err);
727     ProcRes {
728         status: Status::Normal(status),
729         stdout: out,
730         stderr: err,
731         cmdline: format!("{:?}", cmd)
732     }
733 }
734
735 struct DebuggerCommands {
736     commands: Vec<String>,
737     check_lines: Vec<String>,
738     breakpoint_lines: Vec<usize>,
739 }
740
741 fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
742                            -> DebuggerCommands {
743     let command_directive = format!("{}-command", debugger_prefix);
744     let check_directive = format!("{}-check", debugger_prefix);
745
746     let mut breakpoint_lines = vec!();
747     let mut commands = vec!();
748     let mut check_lines = vec!();
749     let mut counter = 1;
750     let reader = BufReader::new(File::open(file_path).unwrap());
751     for line in reader.lines() {
752         match line {
753             Ok(line) => {
754                 if line.contains("#break") {
755                     breakpoint_lines.push(counter);
756                 }
757
758                 header::parse_name_value_directive(
759                         &line,
760                         &command_directive).map(|cmd| {
761                     commands.push(cmd)
762                 });
763
764                 header::parse_name_value_directive(
765                         &line,
766                         &check_directive).map(|cmd| {
767                     check_lines.push(cmd)
768                 });
769             }
770             Err(e) => {
771                 fatal(&format!("Error while parsing debugger commands: {}", e))
772             }
773         }
774         counter += 1;
775     }
776
777     DebuggerCommands {
778         commands: commands,
779         check_lines: check_lines,
780         breakpoint_lines: breakpoint_lines,
781     }
782 }
783
784 fn cleanup_debug_info_options(options: &Option<String>) -> Option<String> {
785     if options.is_none() {
786         return None;
787     }
788
789     // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
790     let options_to_remove = [
791         "-O".to_owned(),
792         "-g".to_owned(),
793         "--debuginfo".to_owned()
794     ];
795     let new_options =
796         split_maybe_args(options).into_iter()
797                                  .filter(|x| !options_to_remove.contains(x))
798                                  .collect::<Vec<String>>()
799                                  .join(" ");
800     Some(new_options)
801 }
802
803 fn check_debugger_output(debugger_run_result: &ProcRes, check_lines: &[String]) {
804     let num_check_lines = check_lines.len();
805     if num_check_lines > 0 {
806         // Allow check lines to leave parts unspecified (e.g., uninitialized
807         // bits in the wrong case of an enum) with the notation "[...]".
808         let check_fragments: Vec<Vec<String>> =
809             check_lines.iter().map(|s| {
810                 s
811                  .trim()
812                  .split("[...]")
813                  .map(str::to_owned)
814                  .collect()
815             }).collect();
816         // check if each line in props.check_lines appears in the
817         // output (in order)
818         let mut i = 0;
819         for line in debugger_run_result.stdout.lines() {
820             let mut rest = line.trim();
821             let mut first = true;
822             let mut failed = false;
823             for frag in &check_fragments[i] {
824                 let found = if first {
825                     if rest.starts_with(frag) {
826                         Some(0)
827                     } else {
828                         None
829                     }
830                 } else {
831                     rest.find(frag)
832                 };
833                 match found {
834                     None => {
835                         failed = true;
836                         break;
837                     }
838                     Some(i) => {
839                         rest = &rest[(i + frag.len())..];
840                     }
841                 }
842                 first = false;
843             }
844             if !failed && rest.is_empty() {
845                 i += 1;
846             }
847             if i == num_check_lines {
848                 // all lines checked
849                 break;
850             }
851         }
852         if i != num_check_lines {
853             fatal_proc_rec(&format!("line not found in debugger output: {}",
854                                     check_lines.get(i).unwrap()),
855                           debugger_run_result);
856         }
857     }
858 }
859
860 fn check_error_patterns(props: &TestProps,
861                         testfile: &Path,
862                         output_to_check: &str,
863                         proc_res: &ProcRes) {
864     if props.error_patterns.is_empty() {
865         fatal(&format!("no error pattern specified in {:?}", testfile.display()));
866     }
867     let mut next_err_idx = 0;
868     let mut next_err_pat = &props.error_patterns[next_err_idx];
869     let mut done = false;
870     for line in output_to_check.lines() {
871         if line.contains(next_err_pat) {
872             debug!("found error pattern {}", next_err_pat);
873             next_err_idx += 1;
874             if next_err_idx == props.error_patterns.len() {
875                 debug!("found all error patterns");
876                 done = true;
877                 break;
878             }
879             next_err_pat = &props.error_patterns[next_err_idx];
880         }
881     }
882     if done { return; }
883
884     let missing_patterns = &props.error_patterns[next_err_idx..];
885     if missing_patterns.len() == 1 {
886         fatal_proc_rec(&format!("error pattern '{}' not found!", missing_patterns[0]),
887                       proc_res);
888     } else {
889         for pattern in missing_patterns {
890             error(&format!("error pattern '{}' not found!", *pattern));
891         }
892         fatal_proc_rec("multiple error patterns not found", proc_res);
893     }
894 }
895
896 fn check_no_compiler_crash(proc_res: &ProcRes) {
897     for line in proc_res.stderr.lines() {
898         if line.starts_with("error: internal compiler error:") {
899             fatal_proc_rec("compiler encountered internal error",
900                           proc_res);
901         }
902     }
903 }
904
905 fn check_forbid_output(props: &TestProps,
906                        output_to_check: &str,
907                        proc_res: &ProcRes) {
908     for pat in &props.forbid_output {
909         if output_to_check.contains(pat) {
910             fatal_proc_rec("forbidden pattern found in compiler output", proc_res);
911         }
912     }
913 }
914
915 fn check_expected_errors(expected_errors: Vec<errors::ExpectedError>,
916                          testfile: &Path,
917                          proc_res: &ProcRes) {
918
919     // true if we found the error in question
920     let mut found_flags = vec![false; expected_errors.len()];
921
922     if proc_res.status.success() {
923         fatal("process did not return an error status");
924     }
925
926     let prefixes = expected_errors.iter().map(|ee| {
927         format!("{}:{}:", testfile.display(), ee.line)
928     }).collect::<Vec<String>>();
929
930     fn prefix_matches(line: &str, prefix: &str) -> bool {
931         use std::ascii::AsciiExt;
932         // On windows just translate all '\' path separators to '/'
933         let line = line.replace(r"\", "/");
934         if cfg!(windows) {
935             line.to_ascii_lowercase().starts_with(&prefix.to_ascii_lowercase())
936         } else {
937             line.starts_with(prefix)
938         }
939     }
940
941     // A multi-line error will have followup lines which start with a space
942     // or open paren.
943     fn continuation( line: &str) -> bool {
944         line.starts_with(" ") || line.starts_with("(")
945     }
946
947     // Scan and extract our error/warning messages,
948     // which look like:
949     //    filename:line1:col1: line2:col2: *error:* msg
950     //    filename:line1:col1: line2:col2: *warning:* msg
951     // where line1:col1: is the starting point, line2:col2:
952     // is the ending point, and * represents ANSI color codes.
953     //
954     // This pattern is ambiguous on windows, because filename may contain
955     // a colon, so any path prefix must be detected and removed first.
956     for line in proc_res.stderr.lines() {
957         let mut was_expected = false;
958         let mut prev = 0;
959         for (i, ee) in expected_errors.iter().enumerate() {
960             if !found_flags[i] {
961                 debug!("prefix={} ee.kind={} ee.msg={} line={}",
962                        prefixes[i],
963                        ee.kind,
964                        ee.msg,
965                        line);
966                 // Suggestions have no line number in their output, so take on the line number of
967                 // the previous expected error
968                 if ee.kind == "suggestion" {
969                     assert!(expected_errors[prev].kind == "help",
970                             "SUGGESTIONs must be preceded by a HELP");
971                     if line.contains(&ee.msg) {
972                         found_flags[i] = true;
973                         was_expected = true;
974                         break;
975                     }
976                 }
977                 if (prefix_matches(line, &prefixes[i]) || continuation(line)) &&
978                     line.contains(&ee.kind) &&
979                     line.contains(&ee.msg) {
980                     found_flags[i] = true;
981                     was_expected = true;
982                     break;
983                 }
984             }
985             prev = i;
986         }
987
988         // ignore this msg which gets printed at the end
989         if line.contains("aborting due to") {
990             was_expected = true;
991         }
992
993         if !was_expected && is_compiler_error_or_warning(line) {
994             fatal_proc_rec(&format!("unexpected compiler error or warning: '{}'",
995                                     line),
996                           proc_res);
997         }
998     }
999
1000     for (i, &flag) in found_flags.iter().enumerate() {
1001         if !flag {
1002             let ee = &expected_errors[i];
1003             fatal_proc_rec(&format!("expected {} on line {} not found: {}",
1004                                     ee.kind, ee.line, ee.msg),
1005                           proc_res);
1006         }
1007     }
1008 }
1009
1010 fn is_compiler_error_or_warning(line: &str) -> bool {
1011     let mut c = Path::new(line).components();
1012     let line = match c.next() {
1013         Some(Component::Prefix(_)) => c.as_path().to_str().unwrap(),
1014         _ => line,
1015     };
1016
1017     let mut i = 0;
1018     return
1019         scan_until_char(line, ':', &mut i) &&
1020         scan_char(line, ':', &mut i) &&
1021         scan_integer(line, &mut i) &&
1022         scan_char(line, ':', &mut i) &&
1023         scan_integer(line, &mut i) &&
1024         scan_char(line, ':', &mut i) &&
1025         scan_char(line, ' ', &mut i) &&
1026         scan_integer(line, &mut i) &&
1027         scan_char(line, ':', &mut i) &&
1028         scan_integer(line, &mut i) &&
1029         scan_char(line, ' ', &mut i) &&
1030         (scan_string(line, "error", &mut i) ||
1031          scan_string(line, "warning", &mut i));
1032 }
1033
1034 fn scan_until_char(haystack: &str, needle: char, idx: &mut usize) -> bool {
1035     if *idx >= haystack.len() {
1036         return false;
1037     }
1038     let opt = haystack[(*idx)..].find(needle);
1039     if opt.is_none() {
1040         return false;
1041     }
1042     *idx = opt.unwrap();
1043     return true;
1044 }
1045
1046 fn scan_char(haystack: &str, needle: char, idx: &mut usize) -> bool {
1047     if *idx >= haystack.len() {
1048         return false;
1049     }
1050     let ch = haystack.char_at(*idx);
1051     if ch != needle {
1052         return false;
1053     }
1054     *idx += ch.len_utf8();
1055     return true;
1056 }
1057
1058 fn scan_integer(haystack: &str, idx: &mut usize) -> bool {
1059     let mut i = *idx;
1060     while i < haystack.len() {
1061         let ch = haystack.char_at(i);
1062         if ch < '0' || '9' < ch {
1063             break;
1064         }
1065         i += ch.len_utf8();
1066     }
1067     if i == *idx {
1068         return false;
1069     }
1070     *idx = i;
1071     return true;
1072 }
1073
1074 fn scan_string(haystack: &str, needle: &str, idx: &mut usize) -> bool {
1075     let mut haystack_i = *idx;
1076     let mut needle_i = 0;
1077     while needle_i < needle.len() {
1078         if haystack_i >= haystack.len() {
1079             return false;
1080         }
1081         let ch = haystack.char_at(haystack_i);
1082         haystack_i += ch.len_utf8();
1083         if !scan_char(needle, ch, &mut needle_i) {
1084             return false;
1085         }
1086     }
1087     *idx = haystack_i;
1088     return true;
1089 }
1090
1091 struct ProcArgs {
1092     prog: String,
1093     args: Vec<String>,
1094 }
1095
1096 struct ProcRes {
1097     status: Status,
1098     stdout: String,
1099     stderr: String,
1100     cmdline: String,
1101 }
1102
1103 enum Status {
1104     Parsed(i32),
1105     Normal(ExitStatus),
1106 }
1107
1108 impl Status {
1109     fn code(&self) -> Option<i32> {
1110         match *self {
1111             Status::Parsed(i) => Some(i),
1112             Status::Normal(ref e) => e.code(),
1113         }
1114     }
1115
1116     fn success(&self) -> bool {
1117         match *self {
1118             Status::Parsed(i) => i == 0,
1119             Status::Normal(ref e) => e.success(),
1120         }
1121     }
1122 }
1123
1124 impl fmt::Display for Status {
1125     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1126         match *self {
1127             Status::Parsed(i) => write!(f, "exit code: {}", i),
1128             Status::Normal(ref e) => e.fmt(f),
1129         }
1130     }
1131 }
1132
1133 fn compile_test(config: &Config, props: &TestProps,
1134                 testfile: &Path) -> ProcRes {
1135     let aux_dir = aux_output_dir_name(config, testfile);
1136     // FIXME (#9639): This needs to handle non-utf8 paths
1137     let link_args = vec!("-L".to_owned(),
1138                          aux_dir.to_str().unwrap().to_owned());
1139     let args = make_compile_args(config,
1140                                  props,
1141                                  link_args,
1142                                  |a, b| TargetLocation::ThisFile(make_exe_name(a, b)), testfile);
1143     compose_and_run_compiler(config, props, testfile, args, None)
1144 }
1145
1146 fn document(config: &Config, props: &TestProps,
1147             testfile: &Path, out_dir: &Path) -> ProcRes {
1148     if props.build_aux_docs {
1149         for rel_ab in &props.aux_builds {
1150             let abs_ab = config.aux_base.join(rel_ab);
1151             let aux_props = header::load_props(&abs_ab);
1152
1153             let auxres = document(config, &aux_props, &abs_ab, out_dir);
1154             if !auxres.status.success() {
1155                 return auxres;
1156             }
1157         }
1158     }
1159
1160     let aux_dir = aux_output_dir_name(config, testfile);
1161     let mut args = vec!["-L".to_owned(),
1162                         aux_dir.to_str().unwrap().to_owned(),
1163                         "-o".to_owned(),
1164                         out_dir.to_str().unwrap().to_owned(),
1165                         testfile.to_str().unwrap().to_owned()];
1166     args.extend(split_maybe_args(&props.compile_flags));
1167     let args = ProcArgs {
1168         prog: config.rustdoc_path.to_str().unwrap().to_owned(),
1169         args: args,
1170     };
1171     compose_and_run_compiler(config, props, testfile, args, None)
1172 }
1173
1174 fn exec_compiled_test(config: &Config, props: &TestProps,
1175                       testfile: &Path) -> ProcRes {
1176
1177     let env = props.exec_env.clone();
1178
1179     match &*config.target {
1180
1181         "arm-linux-androideabi" | "aarch64-linux-android" => {
1182             _arm_exec_compiled_test(config, props, testfile, env)
1183         }
1184
1185         _=> {
1186             let aux_dir = aux_output_dir_name(config, testfile);
1187             compose_and_run(config,
1188                             testfile,
1189                             make_run_args(config, props, testfile),
1190                             env,
1191                             &config.run_lib_path,
1192                             Some(aux_dir.to_str().unwrap()),
1193                             None)
1194         }
1195     }
1196 }
1197
1198 fn compose_and_run_compiler(config: &Config, props: &TestProps,
1199                             testfile: &Path, args: ProcArgs,
1200                             input: Option<String>) -> ProcRes {
1201     if !props.aux_builds.is_empty() {
1202         ensure_dir(&aux_output_dir_name(config, testfile));
1203     }
1204
1205     let aux_dir = aux_output_dir_name(config, testfile);
1206     // FIXME (#9639): This needs to handle non-utf8 paths
1207     let extra_link_args = vec!["-L".to_owned(),
1208                                aux_dir.to_str().unwrap().to_owned()];
1209
1210     for rel_ab in &props.aux_builds {
1211         let abs_ab = config.aux_base.join(rel_ab);
1212         let aux_props = header::load_props(&abs_ab);
1213         let mut crate_type = if aux_props.no_prefer_dynamic {
1214             Vec::new()
1215         } else {
1216             // We primarily compile all auxiliary libraries as dynamic libraries
1217             // to avoid code size bloat and large binaries as much as possible
1218             // for the test suite (otherwise including libstd statically in all
1219             // executables takes up quite a bit of space).
1220             //
1221             // For targets like MUSL, however, there is no support for dynamic
1222             // libraries so we just go back to building a normal library. Note,
1223             // however, that if the library is built with `force_host` then it's
1224             // ok to be a dylib as the host should always support dylibs.
1225             if config.target.contains("musl") && !aux_props.force_host {
1226                 vec!("--crate-type=lib".to_owned())
1227             } else {
1228                 vec!("--crate-type=dylib".to_owned())
1229             }
1230         };
1231         crate_type.extend(extra_link_args.clone());
1232         let aux_args =
1233             make_compile_args(config,
1234                               &aux_props,
1235                               crate_type,
1236                               |a,b| {
1237                                   let f = make_lib_name(a, b, testfile);
1238                                   let parent = f.parent().unwrap();
1239                                   TargetLocation::ThisDirectory(parent.to_path_buf())
1240                               },
1241                               &abs_ab);
1242         let auxres = compose_and_run(config,
1243                                      &abs_ab,
1244                                      aux_args,
1245                                      Vec::new(),
1246                                      &config.compile_lib_path,
1247                                      Some(aux_dir.to_str().unwrap()),
1248                                      None);
1249         if !auxres.status.success() {
1250             fatal_proc_rec(
1251                 &format!("auxiliary build of {:?} failed to compile: ",
1252                         abs_ab.display()),
1253                 &auxres);
1254         }
1255
1256         match &*config.target {
1257             "arm-linux-androideabi"  | "aarch64-linux-android" => {
1258                 _arm_push_aux_shared_library(config, testfile);
1259             }
1260             _ => {}
1261         }
1262     }
1263
1264     compose_and_run(config,
1265                     testfile,
1266                     args,
1267                     Vec::new(),
1268                     &config.compile_lib_path,
1269                     Some(aux_dir.to_str().unwrap()),
1270                     input)
1271 }
1272
1273 fn ensure_dir(path: &Path) {
1274     if path.is_dir() { return; }
1275     fs::create_dir(path).unwrap();
1276 }
1277
1278 fn compose_and_run(config: &Config, testfile: &Path,
1279                    ProcArgs{ args, prog }: ProcArgs,
1280                    procenv: Vec<(String, String)> ,
1281                    lib_path: &str,
1282                    aux_path: Option<&str>,
1283                    input: Option<String>) -> ProcRes {
1284     return program_output(config, testfile, lib_path,
1285                           prog, aux_path, args, procenv, input);
1286 }
1287
1288 enum TargetLocation {
1289     ThisFile(PathBuf),
1290     ThisDirectory(PathBuf),
1291 }
1292
1293 fn make_compile_args<F>(config: &Config,
1294                         props: &TestProps,
1295                         extras: Vec<String> ,
1296                         xform: F,
1297                         testfile: &Path)
1298                         -> ProcArgs where
1299     F: FnOnce(&Config, &Path) -> TargetLocation,
1300 {
1301     let xform_file = xform(config, testfile);
1302     let target = if props.force_host {
1303         &*config.host
1304     } else {
1305         &*config.target
1306     };
1307     // FIXME (#9639): This needs to handle non-utf8 paths
1308     let mut args = vec!(testfile.to_str().unwrap().to_owned(),
1309                         "-L".to_owned(),
1310                         config.build_base.to_str().unwrap().to_owned(),
1311                         format!("--target={}", target));
1312     args.extend_from_slice(&extras);
1313     if !props.no_prefer_dynamic {
1314         args.push("-C".to_owned());
1315         args.push("prefer-dynamic".to_owned());
1316     }
1317     let path = match xform_file {
1318         TargetLocation::ThisFile(path) => {
1319             args.push("-o".to_owned());
1320             path
1321         }
1322         TargetLocation::ThisDirectory(path) => {
1323             args.push("--out-dir".to_owned());
1324             path
1325         }
1326     };
1327     args.push(path.to_str().unwrap().to_owned());
1328     if props.force_host {
1329         args.extend(split_maybe_args(&config.host_rustcflags));
1330     } else {
1331         args.extend(split_maybe_args(&config.target_rustcflags));
1332     }
1333     args.extend(split_maybe_args(&props.compile_flags));
1334     return ProcArgs {
1335         prog: config.rustc_path.to_str().unwrap().to_owned(),
1336         args: args,
1337     };
1338 }
1339
1340 fn make_lib_name(config: &Config, auxfile: &Path, testfile: &Path) -> PathBuf {
1341     // what we return here is not particularly important, as it
1342     // happens; rustc ignores everything except for the directory.
1343     let auxname = output_testname(auxfile);
1344     aux_output_dir_name(config, testfile).join(&auxname)
1345 }
1346
1347 fn make_exe_name(config: &Config, testfile: &Path) -> PathBuf {
1348     let mut f = output_base_name(config, testfile);
1349     if !env::consts::EXE_SUFFIX.is_empty() {
1350         let mut fname = f.file_name().unwrap().to_os_string();
1351         fname.push(env::consts::EXE_SUFFIX);
1352         f.set_file_name(&fname);
1353     }
1354     f
1355 }
1356
1357 fn make_run_args(config: &Config, props: &TestProps, testfile: &Path)
1358                  -> ProcArgs {
1359     // If we've got another tool to run under (valgrind),
1360     // then split apart its command
1361     let mut args = split_maybe_args(&config.runtool);
1362     let exe_file = make_exe_name(config, testfile);
1363
1364     // FIXME (#9639): This needs to handle non-utf8 paths
1365     args.push(exe_file.to_str().unwrap().to_owned());
1366
1367     // Add the arguments in the run_flags directive
1368     args.extend(split_maybe_args(&props.run_flags));
1369
1370     let prog = args.remove(0);
1371     return ProcArgs {
1372         prog: prog,
1373         args: args,
1374     };
1375 }
1376
1377 fn split_maybe_args(argstr: &Option<String>) -> Vec<String> {
1378     match *argstr {
1379         Some(ref s) => {
1380             s
1381              .split(' ')
1382              .filter_map(|s| {
1383                  if s.chars().all(|c| c.is_whitespace()) {
1384                      None
1385                  } else {
1386                      Some(s.to_owned())
1387                  }
1388              }).collect()
1389         }
1390         None => Vec::new()
1391     }
1392 }
1393
1394 fn program_output(config: &Config, testfile: &Path, lib_path: &str, prog: String,
1395                   aux_path: Option<&str>, args: Vec<String>,
1396                   env: Vec<(String, String)>,
1397                   input: Option<String>) -> ProcRes {
1398     let cmdline =
1399         {
1400             let cmdline = make_cmdline(lib_path,
1401                                        &prog,
1402                                        &args);
1403             logv(config, format!("executing {}", cmdline));
1404             cmdline
1405         };
1406     let procsrv::Result {
1407         out,
1408         err,
1409         status
1410     } = procsrv::run(lib_path,
1411                      &prog,
1412                      aux_path,
1413                      &args,
1414                      env,
1415                      input).expect(&format!("failed to exec `{}`", prog));
1416     dump_output(config, testfile, &out, &err);
1417     return ProcRes {
1418         status: Status::Normal(status),
1419         stdout: out,
1420         stderr: err,
1421         cmdline: cmdline,
1422     };
1423 }
1424
1425 fn make_cmdline(libpath: &str, prog: &str, args: &[String]) -> String {
1426     use util;
1427
1428     // Linux and mac don't require adjusting the library search path
1429     if cfg!(unix) {
1430         format!("{} {}", prog, args.join(" "))
1431     } else {
1432         // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
1433         // for diagnostic purposes
1434         fn lib_path_cmd_prefix(path: &str) -> String {
1435             format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
1436         }
1437
1438         format!("{} {} {}", lib_path_cmd_prefix(libpath), prog, args.join(" "))
1439     }
1440 }
1441
1442 fn dump_output(config: &Config, testfile: &Path, out: &str, err: &str) {
1443     dump_output_file(config, testfile, out, "out");
1444     dump_output_file(config, testfile, err, "err");
1445     maybe_dump_to_stdout(config, out, err);
1446 }
1447
1448 fn dump_output_file(config: &Config, testfile: &Path,
1449                     out: &str, extension: &str) {
1450     let outfile = make_out_name(config, testfile, extension);
1451     File::create(&outfile).unwrap().write_all(out.as_bytes()).unwrap();
1452 }
1453
1454 fn make_out_name(config: &Config, testfile: &Path, extension: &str) -> PathBuf {
1455     output_base_name(config, testfile).with_extension(extension)
1456 }
1457
1458 fn aux_output_dir_name(config: &Config, testfile: &Path) -> PathBuf {
1459     let f = output_base_name(config, testfile);
1460     let mut fname = f.file_name().unwrap().to_os_string();
1461     fname.push(&format!(".{}.libaux", config.mode));
1462     f.with_file_name(&fname)
1463 }
1464
1465 fn output_testname(testfile: &Path) -> PathBuf {
1466     PathBuf::from(testfile.file_stem().unwrap())
1467 }
1468
1469 fn output_base_name(config: &Config, testfile: &Path) -> PathBuf {
1470     config.build_base
1471         .join(&output_testname(testfile))
1472         .with_extension(&config.stage_id)
1473 }
1474
1475 fn maybe_dump_to_stdout(config: &Config, out: &str, err: &str) {
1476     if config.verbose {
1477         println!("------{}------------------------------", "stdout");
1478         println!("{}", out);
1479         println!("------{}------------------------------", "stderr");
1480         println!("{}", err);
1481         println!("------------------------------------------");
1482     }
1483 }
1484
1485 fn error(err: &str) { println!("\nerror: {}", err); }
1486
1487 fn fatal(err: &str) -> ! { error(err); panic!(); }
1488
1489 fn fatal_proc_rec(err: &str, proc_res: &ProcRes) -> ! {
1490     print!("\n\
1491 error: {}\n\
1492 status: {}\n\
1493 command: {}\n\
1494 stdout:\n\
1495 ------------------------------------------\n\
1496 {}\n\
1497 ------------------------------------------\n\
1498 stderr:\n\
1499 ------------------------------------------\n\
1500 {}\n\
1501 ------------------------------------------\n\
1502 \n",
1503              err, proc_res.status, proc_res.cmdline, proc_res.stdout,
1504              proc_res.stderr);
1505     panic!();
1506 }
1507
1508 fn _arm_exec_compiled_test(config: &Config,
1509                            props: &TestProps,
1510                            testfile: &Path,
1511                            env: Vec<(String, String)>)
1512                            -> ProcRes {
1513     let args = make_run_args(config, props, testfile);
1514     let cmdline = make_cmdline("",
1515                                &args.prog,
1516                                &args.args);
1517
1518     // get bare program string
1519     let mut tvec: Vec<String> = args.prog
1520                                     .split('/')
1521                                     .map(str::to_owned)
1522                                     .collect();
1523     let prog_short = tvec.pop().unwrap();
1524
1525     // copy to target
1526     let copy_result = procsrv::run("",
1527                                    &config.adb_path,
1528                                    None,
1529                                    &[
1530                                     "push".to_owned(),
1531                                     args.prog.clone(),
1532                                     config.adb_test_dir.clone()
1533                                    ],
1534                                    vec!(("".to_owned(), "".to_owned())),
1535                                    Some("".to_owned()))
1536         .expect(&format!("failed to exec `{}`", config.adb_path));
1537
1538     if config.verbose {
1539         println!("push ({}) {} {} {}",
1540                  config.target,
1541                  args.prog,
1542                  copy_result.out,
1543                  copy_result.err);
1544     }
1545
1546     logv(config, format!("executing ({}) {}", config.target, cmdline));
1547
1548     let mut runargs = Vec::new();
1549
1550     // run test via adb_run_wrapper
1551     runargs.push("shell".to_owned());
1552     for (key, val) in env {
1553         runargs.push(format!("{}={}", key, val));
1554     }
1555     runargs.push(format!("{}/../adb_run_wrapper.sh", config.adb_test_dir));
1556     runargs.push(format!("{}", config.adb_test_dir));
1557     runargs.push(format!("{}", prog_short));
1558
1559     for tv in &args.args {
1560         runargs.push(tv.to_owned());
1561     }
1562     procsrv::run("",
1563                  &config.adb_path,
1564                  None,
1565                  &runargs,
1566                  vec!(("".to_owned(), "".to_owned())), Some("".to_owned()))
1567         .expect(&format!("failed to exec `{}`", config.adb_path));
1568
1569     // get exitcode of result
1570     runargs = Vec::new();
1571     runargs.push("shell".to_owned());
1572     runargs.push("cat".to_owned());
1573     runargs.push(format!("{}/{}.exitcode", config.adb_test_dir, prog_short));
1574
1575     let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
1576         procsrv::run("",
1577                      &config.adb_path,
1578                      None,
1579                      &runargs,
1580                      vec!(("".to_owned(), "".to_owned())),
1581                      Some("".to_owned()))
1582         .expect(&format!("failed to exec `{}`", config.adb_path));
1583
1584     let mut exitcode: i32 = 0;
1585     for c in exitcode_out.chars() {
1586         if !c.is_numeric() { break; }
1587         exitcode = exitcode * 10 + match c {
1588             '0' ... '9' => c as i32 - ('0' as i32),
1589             _ => 101,
1590         }
1591     }
1592
1593     // get stdout of result
1594     runargs = Vec::new();
1595     runargs.push("shell".to_owned());
1596     runargs.push("cat".to_owned());
1597     runargs.push(format!("{}/{}.stdout", config.adb_test_dir, prog_short));
1598
1599     let procsrv::Result{ out: stdout_out, err: _, status: _ } =
1600         procsrv::run("",
1601                      &config.adb_path,
1602                      None,
1603                      &runargs,
1604                      vec!(("".to_owned(), "".to_owned())),
1605                      Some("".to_owned()))
1606         .expect(&format!("failed to exec `{}`", config.adb_path));
1607
1608     // get stderr of result
1609     runargs = Vec::new();
1610     runargs.push("shell".to_owned());
1611     runargs.push("cat".to_owned());
1612     runargs.push(format!("{}/{}.stderr", config.adb_test_dir, prog_short));
1613
1614     let procsrv::Result{ out: stderr_out, err: _, status: _ } =
1615         procsrv::run("",
1616                      &config.adb_path,
1617                      None,
1618                      &runargs,
1619                      vec!(("".to_owned(), "".to_owned())),
1620                      Some("".to_owned()))
1621         .expect(&format!("failed to exec `{}`", config.adb_path));
1622
1623     dump_output(config,
1624                 testfile,
1625                 &stdout_out,
1626                 &stderr_out);
1627
1628     ProcRes {
1629         status: Status::Parsed(exitcode),
1630         stdout: stdout_out,
1631         stderr: stderr_out,
1632         cmdline: cmdline
1633     }
1634 }
1635
1636 fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) {
1637     let tdir = aux_output_dir_name(config, testfile);
1638
1639     let dirs = fs::read_dir(&tdir).unwrap();
1640     for file in dirs {
1641         let file = file.unwrap().path();
1642         if file.extension().and_then(|s| s.to_str()) == Some("so") {
1643             // FIXME (#9639): This needs to handle non-utf8 paths
1644             let copy_result = procsrv::run("",
1645                                            &config.adb_path,
1646                                            None,
1647                                            &[
1648                                             "push".to_owned(),
1649                                             file.to_str()
1650                                                 .unwrap()
1651                                                 .to_owned(),
1652                                             config.adb_test_dir.to_owned(),
1653                                            ],
1654                                            vec!(("".to_owned(),
1655                                                  "".to_owned())),
1656                                            Some("".to_owned()))
1657                 .expect(&format!("failed to exec `{}`", config.adb_path));
1658
1659             if config.verbose {
1660                 println!("push ({}) {:?} {} {}",
1661                     config.target, file.display(),
1662                     copy_result.out, copy_result.err);
1663             }
1664         }
1665     }
1666 }
1667
1668 // codegen tests (using FileCheck)
1669
1670 fn compile_test_and_save_ir(config: &Config, props: &TestProps,
1671                                  testfile: &Path) -> ProcRes {
1672     let aux_dir = aux_output_dir_name(config, testfile);
1673     // FIXME (#9639): This needs to handle non-utf8 paths
1674     let mut link_args = vec!("-L".to_owned(),
1675                              aux_dir.to_str().unwrap().to_owned());
1676     let llvm_args = vec!("--emit=llvm-ir".to_owned(),);
1677     link_args.extend(llvm_args);
1678     let args = make_compile_args(config,
1679                                  props,
1680                                  link_args,
1681                                  |a, b| TargetLocation::ThisDirectory(
1682                                      output_base_name(a, b).parent()
1683                                         .unwrap().to_path_buf()),
1684                                  testfile);
1685     compose_and_run_compiler(config, props, testfile, args, None)
1686 }
1687
1688 fn check_ir_with_filecheck(config: &Config, testfile: &Path) -> ProcRes {
1689     let irfile = output_base_name(config, testfile).with_extension("ll");
1690     let prog = config.llvm_bin_path.as_ref().unwrap().join("FileCheck");
1691     let proc_args = ProcArgs {
1692         // FIXME (#9639): This needs to handle non-utf8 paths
1693         prog: prog.to_str().unwrap().to_owned(),
1694         args: vec!(format!("-input-file={}", irfile.to_str().unwrap()),
1695                    testfile.to_str().unwrap().to_owned())
1696     };
1697     compose_and_run(config, testfile, proc_args, Vec::new(), "", None, None)
1698 }
1699
1700 fn run_codegen_test(config: &Config, props: &TestProps, testfile: &Path) {
1701
1702     if config.llvm_bin_path.is_none() {
1703         fatal("missing --llvm-bin-path");
1704     }
1705
1706     let mut proc_res = compile_test_and_save_ir(config, props, testfile);
1707     if !proc_res.status.success() {
1708         fatal_proc_rec("compilation failed!", &proc_res);
1709     }
1710
1711     proc_res = check_ir_with_filecheck(config, testfile);
1712     if !proc_res.status.success() {
1713         fatal_proc_rec("verification with 'FileCheck' failed",
1714                       &proc_res);
1715     }
1716 }
1717
1718 fn charset() -> &'static str {
1719     // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
1720     if cfg!(target_os = "bitrig") {
1721         "auto"
1722     } else if cfg!(target_os = "freebsd") {
1723         "ISO-8859-1"
1724     } else {
1725         "UTF-8"
1726     }
1727 }
1728
1729 fn run_rustdoc_test(config: &Config, props: &TestProps, testfile: &Path) {
1730     let out_dir = output_base_name(config, testfile);
1731     let _ = fs::remove_dir_all(&out_dir);
1732     ensure_dir(&out_dir);
1733
1734     let proc_res = document(config, props, testfile, &out_dir);
1735     if !proc_res.status.success() {
1736         fatal_proc_rec("rustdoc failed!", &proc_res);
1737     }
1738     let root = find_rust_src_root(config).unwrap();
1739
1740     let res = cmd2procres(config,
1741                           testfile,
1742                           Command::new(&config.python)
1743                                   .arg(root.join("src/etc/htmldocck.py"))
1744                                   .arg(out_dir)
1745                                   .arg(testfile));
1746     if !res.status.success() {
1747         fatal_proc_rec("htmldocck failed!", &res);
1748     }
1749 }