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