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