]> git.lizzy.rs Git - rust.git/blob - src/compiletest/runtest.rs
test: Make manual changes to deal with the fallout from removal of
[rust.git] / src / compiletest / runtest.rs
1 // Copyright 2012-2013 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::mode_compile_fail;
13 use common::mode_pretty;
14 use common::mode_run_fail;
15 use common::mode_run_pass;
16 use errors;
17 use header::TestProps;
18 use header::load_props;
19 use procsrv;
20 use util::logv;
21 #[cfg(target_os = "win32")]
22 use util;
23
24 use std::io::File;
25 use std::io::fs;
26 use std::io::net::ip::{Ipv4Addr, SocketAddr};
27 use std::io::net::tcp;
28 use std::io::process::ProcessExit;
29 use std::io::process;
30 use std::io::timer;
31 use std::io;
32 use std::os;
33 use std::str;
34 use std::task;
35 use std::slice;
36 use std::vec_ng;
37
38 use test::MetricMap;
39
40 pub fn run(config: config, testfile: ~str) {
41
42     match config.target.as_slice() {
43
44         "arm-linux-androideabi" => {
45             if !config.adb_device_status {
46                 fail!("android device not available");
47             }
48         }
49
50         _=> { }
51     }
52
53     let mut _mm = MetricMap::new();
54     run_metrics(config, testfile, &mut _mm);
55 }
56
57 pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) {
58     if config.verbose {
59         // We're going to be dumping a lot of info. Start on a new line.
60         print!("\n\n");
61     }
62     let testfile = Path::new(testfile);
63     debug!("running {}", testfile.display());
64     let props = load_props(&testfile);
65     debug!("loaded props");
66     match config.mode {
67       mode_compile_fail => run_cfail_test(&config, &props, &testfile),
68       mode_run_fail => run_rfail_test(&config, &props, &testfile),
69       mode_run_pass => run_rpass_test(&config, &props, &testfile),
70       mode_pretty => run_pretty_test(&config, &props, &testfile),
71       mode_debug_info => run_debuginfo_test(&config, &props, &testfile),
72       mode_codegen => run_codegen_test(&config, &props, &testfile, mm)
73     }
74 }
75
76 fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) {
77     let proc_res = compile_test(config, props, testfile);
78
79     if proc_res.status.success() {
80         fatal_ProcRes(~"compile-fail test compiled successfully!", &proc_res);
81     }
82
83     check_correct_failure_status(&proc_res);
84
85     let expected_errors = errors::load_errors(testfile);
86     if !expected_errors.is_empty() {
87         if !props.error_patterns.is_empty() {
88             fatal(~"both error pattern and expected errors specified");
89         }
90         check_expected_errors(expected_errors, testfile, &proc_res);
91     } else {
92         check_error_patterns(props, testfile, &proc_res);
93     }
94 }
95
96 fn run_rfail_test(config: &config, props: &TestProps, testfile: &Path) {
97     let proc_res = if !config.jit {
98         let proc_res = compile_test(config, props, testfile);
99
100         if !proc_res.status.success() {
101             fatal_ProcRes(~"compilation failed!", &proc_res);
102         }
103
104         exec_compiled_test(config, props, testfile)
105     } else {
106         jit_test(config, props, testfile)
107     };
108
109     // The value our Makefile configures valgrind to return on failure
110     static VALGRIND_ERR: int = 100;
111     if proc_res.status.matches_exit_status(VALGRIND_ERR) {
112         fatal_ProcRes(~"run-fail test isn't valgrind-clean!", &proc_res);
113     }
114
115     check_correct_failure_status(&proc_res);
116     check_error_patterns(props, testfile, &proc_res);
117 }
118
119 fn check_correct_failure_status(proc_res: &ProcRes) {
120     // The value the rust runtime returns on failure
121     static RUST_ERR: int = 101;
122     if !proc_res.status.matches_exit_status(RUST_ERR) {
123         fatal_ProcRes(
124             format!("failure produced the wrong error: {}", proc_res.status),
125             proc_res);
126     }
127 }
128
129 fn run_rpass_test(config: &config, props: &TestProps, testfile: &Path) {
130     if !config.jit {
131         let mut proc_res = compile_test(config, props, testfile);
132
133         if !proc_res.status.success() {
134             fatal_ProcRes(~"compilation failed!", &proc_res);
135         }
136
137         proc_res = exec_compiled_test(config, props, testfile);
138
139         if !proc_res.status.success() {
140             fatal_ProcRes(~"test run failed!", &proc_res);
141         }
142     } else {
143         let proc_res = jit_test(config, props, testfile);
144
145         if !proc_res.status.success() { fatal_ProcRes(~"jit failed!", &proc_res); }
146     }
147 }
148
149 fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) {
150     if props.pp_exact.is_some() {
151         logv(config, ~"testing for exact pretty-printing");
152     } else { logv(config, ~"testing for converging pretty-printing"); }
153
154     let rounds =
155         match props.pp_exact { Some(_) => 1, None => 2 };
156
157     let src = File::open(testfile).read_to_end().unwrap();
158     let src = str::from_utf8_owned(src).unwrap();
159     let mut srcs = vec!(src);
160
161     let mut round = 0;
162     while round < rounds {
163         logv(config, format!("pretty-printing round {}", round));
164         let proc_res = print_source(config,
165                                     testfile,
166                                     (*srcs.get(round)).clone());
167
168         if !proc_res.status.success() {
169             fatal_ProcRes(format!("pretty-printing failed in round {}", round),
170                           &proc_res);
171         }
172
173         let ProcRes{ stdout, .. } = proc_res;
174         srcs.push(stdout);
175         round += 1;
176     }
177
178     let mut expected = match props.pp_exact {
179         Some(ref file) => {
180             let filepath = testfile.dir_path().join(file);
181             let s = File::open(&filepath).read_to_end().unwrap();
182             str::from_utf8_owned(s).unwrap()
183           }
184           None => { (*srcs.get(srcs.len() - 2u)).clone() }
185         };
186     let mut actual = (*srcs.get(srcs.len() - 1u)).clone();
187
188     if props.pp_exact.is_some() {
189         // Now we have to care about line endings
190         let cr = ~"\r";
191         actual = actual.replace(cr, "");
192         expected = expected.replace(cr, "");
193     }
194
195     compare_source(expected, actual);
196
197     // Finally, let's make sure it actually appears to remain valid code
198     let proc_res = typecheck_source(config, props, testfile, actual);
199
200     if !proc_res.status.success() {
201         fatal_ProcRes(~"pretty-printed source does not typecheck", &proc_res);
202     }
203
204     return;
205
206     fn print_source(config: &config, testfile: &Path, src: ~str) -> ProcRes {
207         compose_and_run(config, testfile, make_pp_args(config, testfile),
208                         Vec::new(), config.compile_lib_path, Some(src))
209     }
210
211     fn make_pp_args(config: &config, _testfile: &Path) -> ProcArgs {
212         let args = vec!(~"-", ~"--pretty", ~"normal",
213                      ~"--target=" + config.target);
214         // FIXME (#9639): This needs to handle non-utf8 paths
215         return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
216     }
217
218     fn compare_source(expected: &str, actual: &str) {
219         if expected != actual {
220             error(~"pretty-printed source does not match expected source");
221             println!("\n\
222 expected:\n\
223 ------------------------------------------\n\
224 {}\n\
225 ------------------------------------------\n\
226 actual:\n\
227 ------------------------------------------\n\
228 {}\n\
229 ------------------------------------------\n\
230 \n",
231                      expected, actual);
232             fail!();
233         }
234     }
235
236     fn typecheck_source(config: &config, props: &TestProps,
237                         testfile: &Path, src: ~str) -> ProcRes {
238         let args = make_typecheck_args(config, props, testfile);
239         compose_and_run_compiler(config, props, testfile, args, Some(src))
240     }
241
242     fn make_typecheck_args(config: &config, props: &TestProps, testfile: &Path) -> ProcArgs {
243         let aux_dir = aux_output_dir_name(config, testfile);
244         let target = if props.force_host {
245             config.host.as_slice()
246         } else {
247             config.target.as_slice()
248         };
249         // FIXME (#9639): This needs to handle non-utf8 paths
250         let mut args = vec!(~"-",
251                          ~"--no-trans", ~"--crate-type=lib",
252                          ~"--target=" + target,
253                          ~"-L", config.build_base.as_str().unwrap().to_owned(),
254                          ~"-L",
255                          aux_dir.as_str().unwrap().to_owned());
256         args.push_all_move(split_maybe_args(&config.target_rustcflags));
257         args.push_all_move(split_maybe_args(&props.compile_flags));
258         // FIXME (#9639): This needs to handle non-utf8 paths
259         return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
260     }
261 }
262
263 fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
264     let mut config = config {
265         target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
266         host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
267         .. config.clone()
268     };
269
270     let config = &mut config;
271     let check_lines = &props.check_lines;
272     let mut cmds = props.debugger_cmds.connect("\n");
273
274     // compile test file (it shoud have 'compile-flags:-g' in the header)
275     let mut proc_res = compile_test(config, props, testfile);
276     if !proc_res.status.success() {
277         fatal_ProcRes(~"compilation failed!", &proc_res);
278     }
279
280     let exe_file = make_exe_name(config, testfile);
281
282     let mut proc_args;
283     match config.target.as_slice() {
284         "arm-linux-androideabi" => {
285
286             cmds = cmds.replace("run","continue");
287
288             // write debugger script
289             let script_str = [~"set charset UTF-8",
290                               format!("file {}",exe_file.as_str().unwrap().to_owned()),
291                               ~"target remote :5039",
292                               cmds,
293                               ~"quit"].connect("\n");
294             debug!("script_str = {}", script_str);
295             dump_output_file(config, testfile, script_str, "debugger.script");
296
297
298             procsrv::run("", config.adb_path,
299                          [~"push", exe_file.as_str().unwrap().to_owned(),
300                           config.adb_test_dir.clone()],
301                          vec!((~"",~"")), Some(~""))
302                 .expect(format!("failed to exec `{}`", config.adb_path));
303
304             procsrv::run("", config.adb_path,
305                          [~"forward", ~"tcp:5039", ~"tcp:5039"],
306                          vec!((~"",~"")), Some(~""))
307                 .expect(format!("failed to exec `{}`", config.adb_path));
308
309             let adb_arg = format!("export LD_LIBRARY_PATH={}; gdbserver :5039 {}/{}",
310                                   config.adb_test_dir.clone(), config.adb_test_dir.clone(),
311                                   str::from_utf8(exe_file.filename().unwrap()).unwrap());
312
313             let mut process = procsrv::run_background("", config.adb_path,
314                                                       [~"shell",adb_arg.clone()],
315                                                       vec!((~"",~"")), Some(~""))
316                 .expect(format!("failed to exec `{}`", config.adb_path));
317             loop {
318                 //waiting 1 second for gdbserver start
319                 timer::sleep(1000);
320                 let result = task::try(proc() {
321                     tcp::TcpStream::connect(SocketAddr {
322                         ip: Ipv4Addr(127, 0, 0, 1),
323                         port: 5039,
324                     }).unwrap();
325                 });
326                 if result.is_err() {
327                     continue;
328                 }
329                 break;
330             }
331
332             let args = split_maybe_args(&config.target_rustcflags);
333             let mut tool_path:~str = ~"";
334             for arg in args.iter() {
335                 if arg.contains("android-cross-path=") {
336                     tool_path = arg.replace("android-cross-path=","");
337                     break;
338                 }
339             }
340
341             if tool_path.equals(&~"") {
342                 fatal(~"cannot found android cross path");
343             }
344
345             let debugger_script = make_out_name(config, testfile, "debugger.script");
346             // FIXME (#9639): This needs to handle non-utf8 paths
347             let debugger_opts = vec!(~"-quiet", ~"-batch", ~"-nx",
348                                   "-command=" + debugger_script.as_str().unwrap().to_owned());
349
350             let gdb_path = tool_path.append("/bin/arm-linux-androideabi-gdb");
351             let procsrv::Result{ out, err, status }=
352                 procsrv::run("",
353                              gdb_path,
354                              debugger_opts.as_slice(),
355                              vec!((~"",~"")),
356                              None)
357                 .expect(format!("failed to exec `{}`", gdb_path));
358             let cmdline = {
359                 let cmdline = make_cmdline("",
360                                            "arm-linux-androideabi-gdb",
361                                            debugger_opts.as_slice());
362                 logv(config, format!("executing {}", cmdline));
363                 cmdline
364             };
365
366             proc_res = ProcRes {status: status,
367                                stdout: out,
368                                stderr: err,
369                                cmdline: cmdline};
370             process.signal_kill().unwrap();
371         }
372
373         _=> {
374             // write debugger script
375             let script_str = [~"set charset UTF-8",
376                 cmds,
377                 ~"quit\n"].connect("\n");
378             debug!("script_str = {}", script_str);
379             dump_output_file(config, testfile, script_str, "debugger.script");
380
381             // run debugger script with gdb
382             #[cfg(windows)]
383             fn debugger() -> ~str { ~"gdb.exe" }
384             #[cfg(unix)]
385             fn debugger() -> ~str { ~"gdb" }
386
387             let debugger_script = make_out_name(config, testfile, "debugger.script");
388
389             // FIXME (#9639): This needs to handle non-utf8 paths
390             let debugger_opts = vec!(~"-quiet", ~"-batch", ~"-nx",
391                 "-command=" + debugger_script.as_str().unwrap().to_owned(),
392                 exe_file.as_str().unwrap().to_owned());
393             proc_args = ProcArgs {prog: debugger(), args: debugger_opts};
394             proc_res = compose_and_run(config, testfile, proc_args, Vec::new(), "", None);
395         }
396     }
397
398     if !proc_res.status.success() {
399         fatal(~"gdb failed to execute");
400     }
401     let num_check_lines = check_lines.len();
402     if num_check_lines > 0 {
403         // Allow check lines to leave parts unspecified (e.g., uninitialized
404         // bits in the wrong case of an enum) with the notation "[...]".
405         let check_fragments: Vec<Vec<~str>> =
406             check_lines.iter().map(|s| {
407                 s.split_str("[...]").map(|x| x.to_str()).collect()
408             }).collect();
409         // check if each line in props.check_lines appears in the
410         // output (in order)
411         let mut i = 0u;
412         for line in proc_res.stdout.lines() {
413             let mut rest = line.trim();
414             let mut first = true;
415             let mut failed = false;
416             for frag in check_fragments.get(i).iter() {
417                 let found = if first {
418                     if rest.starts_with(*frag) { Some(0) } else { None }
419                 } else {
420                     rest.find_str(*frag)
421                 };
422                 match found {
423                     None => {
424                         failed = true;
425                         break;
426                     }
427                     Some(i) => {
428                         rest = rest.slice_from(i + frag.len());
429                     }
430                 }
431                 first = false;
432             }
433             if !failed && rest.len() == 0 {
434                 i += 1u;
435             }
436             if i == num_check_lines {
437                 // all lines checked
438                 break;
439             }
440         }
441         if i != num_check_lines {
442             fatal_ProcRes(format!("line not found in debugger output: {}",
443                                   *check_lines.get(i)), &proc_res);
444         }
445     }
446
447     fn cleanup_debug_info_options(options: &Option<~str>) -> Option<~str> {
448         if options.is_none() {
449             return None;
450         }
451
452         // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
453         let options_to_remove = [~"-O", ~"-g", ~"--debuginfo"];
454         let new_options = split_maybe_args(options).move_iter()
455                                                    .filter(|x| !options_to_remove.contains(x))
456                                                    .to_owned_vec()
457                                                    .connect(" ");
458         Some(new_options)
459     }
460 }
461
462 fn check_error_patterns(props: &TestProps,
463                         testfile: &Path,
464                         proc_res: &ProcRes) {
465     if props.error_patterns.is_empty() {
466         fatal(~"no error pattern specified in " + testfile.display().as_maybe_owned().as_slice());
467     }
468
469     if proc_res.status.success() {
470         fatal(~"process did not return an error status");
471     }
472
473     let mut next_err_idx = 0u;
474     let mut next_err_pat = props.error_patterns.get(next_err_idx);
475     let mut done = false;
476     let output_to_check = if props.check_stdout {
477         proc_res.stdout + proc_res.stderr
478     } else {
479         proc_res.stderr.clone()
480     };
481     for line in output_to_check.lines() {
482         if line.contains(*next_err_pat) {
483             debug!("found error pattern {}", *next_err_pat);
484             next_err_idx += 1u;
485             if next_err_idx == props.error_patterns.len() {
486                 debug!("found all error patterns");
487                 done = true;
488                 break;
489             }
490             next_err_pat = props.error_patterns.get(next_err_idx);
491         }
492     }
493     if done { return; }
494
495     let missing_patterns =
496         props.error_patterns.slice(next_err_idx, props.error_patterns.len());
497     if missing_patterns.len() == 1u {
498         fatal_ProcRes(format!("error pattern '{}' not found!",
499                               missing_patterns[0]), proc_res);
500     } else {
501         for pattern in missing_patterns.iter() {
502             error(format!("error pattern '{}' not found!", *pattern));
503         }
504         fatal_ProcRes(~"multiple error patterns not found", proc_res);
505     }
506 }
507
508 fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
509                          testfile: &Path,
510                          proc_res: &ProcRes) {
511
512     // true if we found the error in question
513     let mut found_flags = slice::from_elem(
514         expected_errors.len(), false);
515
516     if proc_res.status.success() {
517         fatal(~"process did not return an error status");
518     }
519
520     let prefixes = expected_errors.iter().map(|ee| {
521         format!("{}:{}:", testfile.display(), ee.line)
522     }).collect::<Vec<~str> >();
523
524     #[cfg(target_os = "win32")]
525     fn to_lower( s : &str ) -> ~str {
526         let i = s.chars();
527         let c : Vec<char> = i.map( |c| {
528             if c.is_ascii() {
529                 c.to_ascii().to_lower().to_char()
530             } else {
531                 c
532             }
533         } ).collect();
534         str::from_chars( c )
535     }
536
537     #[cfg(target_os = "win32")]
538     fn prefix_matches( line : &str, prefix : &str ) -> bool {
539         to_lower(line).starts_with( to_lower(prefix) )
540     }
541
542     #[cfg(target_os = "linux")]
543     #[cfg(target_os = "macos")]
544     #[cfg(target_os = "freebsd")]
545     fn prefix_matches( line : &str, prefix : &str ) -> bool {
546         line.starts_with( prefix )
547     }
548
549     // Scan and extract our error/warning messages,
550     // which look like:
551     //    filename:line1:col1: line2:col2: *error:* msg
552     //    filename:line1:col1: line2:col2: *warning:* msg
553     // where line1:col1: is the starting point, line2:col2:
554     // is the ending point, and * represents ANSI color codes.
555     for line in proc_res.stderr.lines() {
556         let mut was_expected = false;
557         for (i, ee) in expected_errors.iter().enumerate() {
558             if !found_flags[i] {
559                 debug!("prefix={} ee.kind={} ee.msg={} line={}",
560                        *prefixes.get(i), ee.kind, ee.msg, line);
561                 if prefix_matches(line, *prefixes.get(i)) &&
562                     line.contains(ee.kind) &&
563                     line.contains(ee.msg) {
564                     found_flags[i] = true;
565                     was_expected = true;
566                     break;
567                 }
568             }
569         }
570
571         // ignore this msg which gets printed at the end
572         if line.contains("aborting due to") {
573             was_expected = true;
574         }
575
576         if !was_expected && is_compiler_error_or_warning(line) {
577             fatal_ProcRes(format!("unexpected compiler error or warning: '{}'",
578                                line),
579                           proc_res);
580         }
581     }
582
583     for (i, &flag) in found_flags.iter().enumerate() {
584         if !flag {
585             let ee = expected_errors.get(i);
586             fatal_ProcRes(format!("expected {} on line {} not found: {}",
587                                ee.kind, ee.line, ee.msg), proc_res);
588         }
589     }
590 }
591
592 fn is_compiler_error_or_warning(line: &str) -> bool {
593     let mut i = 0u;
594     return
595         scan_until_char(line, ':', &mut i) &&
596         scan_char(line, ':', &mut i) &&
597         scan_integer(line, &mut i) &&
598         scan_char(line, ':', &mut i) &&
599         scan_integer(line, &mut i) &&
600         scan_char(line, ':', &mut i) &&
601         scan_char(line, ' ', &mut i) &&
602         scan_integer(line, &mut i) &&
603         scan_char(line, ':', &mut i) &&
604         scan_integer(line, &mut i) &&
605         scan_char(line, ' ', &mut i) &&
606         (scan_string(line, "error", &mut i) ||
607          scan_string(line, "warning", &mut i));
608 }
609
610 fn scan_until_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
611     if *idx >= haystack.len() {
612         return false;
613     }
614     let opt = haystack.slice_from(*idx).find(needle);
615     if opt.is_none() {
616         return false;
617     }
618     *idx = opt.unwrap();
619     return true;
620 }
621
622 fn scan_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
623     if *idx >= haystack.len() {
624         return false;
625     }
626     let range = haystack.char_range_at(*idx);
627     if range.ch != needle {
628         return false;
629     }
630     *idx = range.next;
631     return true;
632 }
633
634 fn scan_integer(haystack: &str, idx: &mut uint) -> bool {
635     let mut i = *idx;
636     while i < haystack.len() {
637         let range = haystack.char_range_at(i);
638         if range.ch < '0' || '9' < range.ch {
639             break;
640         }
641         i = range.next;
642     }
643     if i == *idx {
644         return false;
645     }
646     *idx = i;
647     return true;
648 }
649
650 fn scan_string(haystack: &str, needle: &str, idx: &mut uint) -> bool {
651     let mut haystack_i = *idx;
652     let mut needle_i = 0u;
653     while needle_i < needle.len() {
654         if haystack_i >= haystack.len() {
655             return false;
656         }
657         let range = haystack.char_range_at(haystack_i);
658         haystack_i = range.next;
659         if !scan_char(needle, range.ch, &mut needle_i) {
660             return false;
661         }
662     }
663     *idx = haystack_i;
664     return true;
665 }
666
667 struct ProcArgs {prog: ~str, args: Vec<~str> }
668
669 struct ProcRes {status: ProcessExit, stdout: ~str, stderr: ~str, cmdline: ~str}
670
671 fn compile_test(config: &config, props: &TestProps,
672                 testfile: &Path) -> ProcRes {
673     compile_test_(config, props, testfile, [])
674 }
675
676 fn jit_test(config: &config, props: &TestProps, testfile: &Path) -> ProcRes {
677     compile_test_(config, props, testfile, [~"--jit"])
678 }
679
680 fn compile_test_(config: &config, props: &TestProps,
681                  testfile: &Path, extra_args: &[~str]) -> ProcRes {
682     let aux_dir = aux_output_dir_name(config, testfile);
683     // FIXME (#9639): This needs to handle non-utf8 paths
684     let link_args = vec!(~"-L", aux_dir.as_str().unwrap().to_owned());
685     let args = make_compile_args(config,
686                                  props,
687                                  vec::append(link_args, extra_args),
688                                  |a, b| ThisFile(make_exe_name(a, b)), testfile);
689     compose_and_run_compiler(config, props, testfile, args, None)
690 }
691
692 fn exec_compiled_test(config: &config, props: &TestProps,
693                       testfile: &Path) -> ProcRes {
694
695     let env = props.exec_env.clone();
696
697     match config.target.as_slice() {
698
699         "arm-linux-androideabi" => {
700             _arm_exec_compiled_test(config, props, testfile, env)
701         }
702
703         _=> {
704             compose_and_run(config, testfile,
705                             make_run_args(config, props, testfile),
706                             env,
707                             config.run_lib_path, None)
708         }
709     }
710 }
711
712 fn compose_and_run_compiler(
713     config: &config,
714     props: &TestProps,
715     testfile: &Path,
716     args: ProcArgs,
717     input: Option<~str>) -> ProcRes {
718
719     if !props.aux_builds.is_empty() {
720         ensure_dir(&aux_output_dir_name(config, testfile));
721     }
722
723     let aux_dir = aux_output_dir_name(config, testfile);
724     // FIXME (#9639): This needs to handle non-utf8 paths
725     let extra_link_args = vec!(~"-L", aux_dir.as_str().unwrap().to_owned());
726
727     for rel_ab in props.aux_builds.iter() {
728         let abs_ab = config.aux_base.join(rel_ab.as_slice());
729         let aux_props = load_props(&abs_ab);
730         let crate_type = if aux_props.no_prefer_dynamic {
731             Vec::new()
732         } else {
733             vec!(~"--crate-type=dylib")
734         };
735         let aux_args =
736             make_compile_args(config,
737                               &aux_props,
738                               vec::append(crate_type,
739                                              extra_link_args.as_slice()),
740                               |a,b| {
741                                   let f = make_lib_name(a, b, testfile);
742                                   ThisDirectory(f.dir_path())
743                               }, &abs_ab);
744         let auxres = compose_and_run(config, &abs_ab, aux_args, Vec::new(),
745                                      config.compile_lib_path, None);
746         if !auxres.status.success() {
747             fatal_ProcRes(
748                 format!("auxiliary build of {} failed to compile: ",
749                      abs_ab.display()),
750                 &auxres);
751         }
752
753         match config.target.as_slice() {
754
755             "arm-linux-androideabi" => {
756                 _arm_push_aux_shared_library(config, testfile);
757             }
758
759             _=> { }
760         }
761     }
762
763     compose_and_run(config, testfile, args, Vec::new(),
764                     config.compile_lib_path, input)
765 }
766
767 fn ensure_dir(path: &Path) {
768     if path.is_dir() { return; }
769     fs::mkdir(path, io::UserRWX).unwrap();
770 }
771
772 fn compose_and_run(config: &config, testfile: &Path,
773                    ProcArgs{ args, prog }: ProcArgs,
774                    procenv: Vec<(~str, ~str)> ,
775                    lib_path: &str,
776                    input: Option<~str>) -> ProcRes {
777     return program_output(config, testfile, lib_path,
778                           prog, args, procenv, input);
779 }
780
781 enum TargetLocation {
782     ThisFile(Path),
783     ThisDirectory(Path),
784 }
785
786 fn make_compile_args(config: &config,
787                      props: &TestProps,
788                      extras: Vec<~str> ,
789                      xform: |&config, &Path| -> TargetLocation,
790                      testfile: &Path)
791                      -> ProcArgs {
792     let xform_file = xform(config, testfile);
793     let target = if props.force_host {
794         config.host.as_slice()
795     } else {
796         config.target.as_slice()
797     };
798     // FIXME (#9639): This needs to handle non-utf8 paths
799     let mut args = vec!(testfile.as_str().unwrap().to_owned(),
800                      ~"-L", config.build_base.as_str().unwrap().to_owned(),
801                      ~"--target=" + target);
802     args.push_all(extras.as_slice());
803     if !props.no_prefer_dynamic {
804         args.push(~"-C");
805         args.push(~"prefer-dynamic");
806     }
807     let path = match xform_file {
808         ThisFile(path) => { args.push(~"-o"); path }
809         ThisDirectory(path) => { args.push(~"--out-dir"); path }
810     };
811     args.push(path.as_str().unwrap().to_owned());
812     if props.force_host {
813         args.push_all_move(split_maybe_args(&config.host_rustcflags));
814     } else {
815         args.push_all_move(split_maybe_args(&config.target_rustcflags));
816     }
817     args.push_all_move(split_maybe_args(&props.compile_flags));
818     return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
819 }
820
821 fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path {
822     // what we return here is not particularly important, as it
823     // happens; rustc ignores everything except for the directory.
824     let auxname = output_testname(auxfile);
825     aux_output_dir_name(config, testfile).join(&auxname)
826 }
827
828 fn make_exe_name(config: &config, testfile: &Path) -> Path {
829     let mut f = output_base_name(config, testfile);
830     if !os::consts::EXE_SUFFIX.is_empty() {
831         match f.filename().map(|s| s + os::consts::EXE_SUFFIX.as_bytes()) {
832             Some(v) => f.set_filename(v),
833             None => ()
834         }
835     }
836     f
837 }
838
839 fn make_run_args(config: &config, _props: &TestProps, testfile: &Path) ->
840    ProcArgs {
841     // If we've got another tool to run under (valgrind),
842     // then split apart its command
843     let mut args = split_maybe_args(&config.runtool);
844     let exe_file = make_exe_name(config, testfile);
845     // FIXME (#9639): This needs to handle non-utf8 paths
846     args.push(exe_file.as_str().unwrap().to_owned());
847     let prog = args.shift().unwrap();
848     return ProcArgs {prog: prog, args: args};
849 }
850
851 fn split_maybe_args(argstr: &Option<~str>) -> Vec<~str> {
852     match *argstr {
853         Some(ref s) => {
854             s.split(' ')
855                 .filter_map(|s| if s.is_whitespace() {None} else {Some(s.to_owned())})
856                 .collect()
857         }
858         None => Vec::new()
859     }
860 }
861
862 fn program_output(config: &config, testfile: &Path, lib_path: &str, prog: ~str,
863                   args: Vec<~str> , env: Vec<(~str, ~str)> ,
864                   input: Option<~str>) -> ProcRes {
865     let cmdline =
866         {
867             let cmdline = make_cmdline(lib_path, prog, args.as_slice());
868             logv(config, format!("executing {}", cmdline));
869             cmdline
870         };
871     let procsrv::Result{ out, err, status } =
872             procsrv::run(lib_path, prog, args.as_slice(), env, input)
873             .expect(format!("failed to exec `{}`", prog));
874     dump_output(config, testfile, out, err);
875     return ProcRes {status: status,
876          stdout: out,
877          stderr: err,
878          cmdline: cmdline};
879 }
880
881 // Linux and mac don't require adjusting the library search path
882 #[cfg(target_os = "linux")]
883 #[cfg(target_os = "macos")]
884 #[cfg(target_os = "freebsd")]
885 fn make_cmdline(_libpath: &str, prog: &str, args: &[~str]) -> ~str {
886     format!("{} {}", prog, args.connect(" "))
887 }
888
889 #[cfg(target_os = "win32")]
890 fn make_cmdline(libpath: &str, prog: &str, args: &[~str]) -> ~str {
891     format!("{} {} {}", lib_path_cmd_prefix(libpath), prog,
892          args.connect(" "))
893 }
894
895 // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
896 // for diagnostic purposes
897 #[cfg(target_os = "win32")]
898 fn lib_path_cmd_prefix(path: &str) -> ~str {
899     format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
900 }
901
902 fn dump_output(config: &config, testfile: &Path, out: &str, err: &str) {
903     dump_output_file(config, testfile, out, "out");
904     dump_output_file(config, testfile, err, "err");
905     maybe_dump_to_stdout(config, out, err);
906 }
907
908 fn dump_output_file(config: &config, testfile: &Path,
909                     out: &str, extension: &str) {
910     let outfile = make_out_name(config, testfile, extension);
911     File::create(&outfile).write(out.as_bytes()).unwrap();
912 }
913
914 fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path {
915     output_base_name(config, testfile).with_extension(extension)
916 }
917
918 fn aux_output_dir_name(config: &config, testfile: &Path) -> Path {
919     let mut f = output_base_name(config, testfile);
920     match f.filename().map(|s| s + bytes!(".libaux")) {
921         Some(v) => f.set_filename(v),
922         None => ()
923     }
924     f
925 }
926
927 fn output_testname(testfile: &Path) -> Path {
928     Path::new(testfile.filestem().unwrap())
929 }
930
931 fn output_base_name(config: &config, testfile: &Path) -> Path {
932     config.build_base
933         .join(&output_testname(testfile))
934         .with_extension(config.stage_id.as_slice())
935 }
936
937 fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) {
938     if config.verbose {
939         println!("------{}------------------------------", "stdout");
940         println!("{}", out);
941         println!("------{}------------------------------", "stderr");
942         println!("{}", err);
943         println!("------------------------------------------");
944     }
945 }
946
947 fn error(err: ~str) { println!("\nerror: {}", err); }
948
949 fn fatal(err: ~str) -> ! { error(err); fail!(); }
950
951 fn fatal_ProcRes(err: ~str, proc_res: &ProcRes) -> ! {
952     print!("\n\
953 error: {}\n\
954 command: {}\n\
955 stdout:\n\
956 ------------------------------------------\n\
957 {}\n\
958 ------------------------------------------\n\
959 stderr:\n\
960 ------------------------------------------\n\
961 {}\n\
962 ------------------------------------------\n\
963 \n",
964              err, proc_res.cmdline, proc_res.stdout, proc_res.stderr);
965     fail!();
966 }
967
968 fn _arm_exec_compiled_test(config: &config, props: &TestProps,
969                       testfile: &Path, env: Vec<(~str, ~str)> ) -> ProcRes {
970
971     let args = make_run_args(config, props, testfile);
972     let cmdline = make_cmdline("", args.prog, args.args.as_slice());
973
974     // get bare program string
975     let mut tvec: Vec<~str> = args.prog.split('/').map(|ts| ts.to_owned()).collect();
976     let prog_short = tvec.pop().unwrap();
977
978     // copy to target
979     let copy_result = procsrv::run("", config.adb_path,
980         [~"push", args.prog.clone(), config.adb_test_dir.clone()],
981         vec!((~"",~"")), Some(~""))
982         .expect(format!("failed to exec `{}`", config.adb_path));
983
984     if config.verbose {
985         println!("push ({}) {} {} {}",
986             config.target, args.prog,
987             copy_result.out, copy_result.err);
988     }
989
990     logv(config, format!("executing ({}) {}", config.target, cmdline));
991
992     let mut runargs = Vec::new();
993
994     // run test via adb_run_wrapper
995     runargs.push(~"shell");
996     for (key, val) in env.move_iter() {
997         runargs.push(format!("{}={}", key, val));
998     }
999     runargs.push(format!("{}/adb_run_wrapper.sh", config.adb_test_dir));
1000     runargs.push(format!("{}", config.adb_test_dir));
1001     runargs.push(format!("{}", prog_short));
1002
1003     for tv in args.args.iter() {
1004         runargs.push(tv.to_owned());
1005     }
1006     procsrv::run("",
1007                  config.adb_path,
1008                  runargs.as_slice(),
1009                  vec!((~"",~"")), Some(~""))
1010         .expect(format!("failed to exec `{}`", config.adb_path));
1011
1012     // get exitcode of result
1013     runargs = Vec::new();
1014     runargs.push(~"shell");
1015     runargs.push(~"cat");
1016     runargs.push(format!("{}/{}.exitcode", config.adb_test_dir, prog_short));
1017
1018     let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
1019         procsrv::run("", config.adb_path, runargs.as_slice(), vec!((~"",~"")),
1020                      Some(~""))
1021         .expect(format!("failed to exec `{}`", config.adb_path));
1022
1023     let mut exitcode : int = 0;
1024     for c in exitcode_out.chars() {
1025         if !c.is_digit() { break; }
1026         exitcode = exitcode * 10 + match c {
1027             '0' .. '9' => c as int - ('0' as int),
1028             _ => 101,
1029         }
1030     }
1031
1032     // get stdout of result
1033     runargs = Vec::new();
1034     runargs.push(~"shell");
1035     runargs.push(~"cat");
1036     runargs.push(format!("{}/{}.stdout", config.adb_test_dir, prog_short));
1037
1038     let procsrv::Result{ out: stdout_out, err: _, status: _ } =
1039         procsrv::run("",
1040                      config.adb_path,
1041                      runargs.as_slice(),
1042                      vec!((~"",~"")), Some(~""))
1043         .expect(format!("failed to exec `{}`", config.adb_path));
1044
1045     // get stderr of result
1046     runargs = Vec::new();
1047     runargs.push(~"shell");
1048     runargs.push(~"cat");
1049     runargs.push(format!("{}/{}.stderr", config.adb_test_dir, prog_short));
1050
1051     let procsrv::Result{ out: stderr_out, err: _, status: _ } =
1052         procsrv::run("",
1053                      config.adb_path,
1054                      runargs.as_slice(),
1055                      vec!((~"",~"")), Some(~""))
1056         .expect(format!("failed to exec `{}`", config.adb_path));
1057
1058     dump_output(config, testfile, stdout_out, stderr_out);
1059
1060     ProcRes {
1061         status: process::ExitStatus(exitcode),
1062         stdout: stdout_out,
1063         stderr: stderr_out,
1064         cmdline: cmdline
1065     }
1066 }
1067
1068 fn _arm_push_aux_shared_library(config: &config, testfile: &Path) {
1069     let tdir = aux_output_dir_name(config, testfile);
1070
1071     let dirs = fs::readdir(&tdir).unwrap();
1072     for file in dirs.iter() {
1073         if file.extension_str() == Some("so") {
1074             // FIXME (#9639): This needs to handle non-utf8 paths
1075             let copy_result = procsrv::run("", config.adb_path,
1076                 [~"push", file.as_str().unwrap().to_owned(), config.adb_test_dir.clone()],
1077                 vec!((~"",~"")), Some(~""))
1078                 .expect(format!("failed to exec `{}`", config.adb_path));
1079
1080             if config.verbose {
1081                 println!("push ({}) {} {} {}",
1082                     config.target, file.display(),
1083                     copy_result.out, copy_result.err);
1084             }
1085         }
1086     }
1087 }
1088
1089 // codegen tests (vs. clang)
1090
1091 fn make_o_name(config: &config, testfile: &Path) -> Path {
1092     output_base_name(config, testfile).with_extension("o")
1093 }
1094
1095 fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path {
1096     if suffix.len() == 0 {
1097         (*p).clone()
1098     } else {
1099         let stem = p.filestem().unwrap();
1100         p.with_filename(stem + bytes!("-") + suffix.as_bytes())
1101     }
1102 }
1103
1104 fn compile_test_and_save_bitcode(config: &config, props: &TestProps,
1105                                  testfile: &Path) -> ProcRes {
1106     let aux_dir = aux_output_dir_name(config, testfile);
1107     // FIXME (#9639): This needs to handle non-utf8 paths
1108     let link_args = vec!(~"-L", aux_dir.as_str().unwrap().to_owned());
1109     let llvm_args = vec!(~"--emit=obj", ~"--crate-type=lib", ~"-C", ~"save-temps");
1110     let args = make_compile_args(config,
1111                                  props,
1112                                  vec::append(link_args,
1113                                                 llvm_args.as_slice()),
1114                                  |a, b| ThisFile(make_o_name(a, b)), testfile);
1115     compose_and_run_compiler(config, props, testfile, args, None)
1116 }
1117
1118 fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps,
1119                                           testfile: &Path) -> ProcRes {
1120     let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1121     let bitcodefile = append_suffix_to_stem(&bitcodefile, "clang");
1122     let testcc = testfile.with_extension("cc");
1123     let proc_args = ProcArgs {
1124         // FIXME (#9639): This needs to handle non-utf8 paths
1125         prog: config.clang_path.get_ref().as_str().unwrap().to_owned(),
1126         args: vec!(~"-c",
1127                 ~"-emit-llvm",
1128                 ~"-o", bitcodefile.as_str().unwrap().to_owned(),
1129                 testcc.as_str().unwrap().to_owned() )
1130     };
1131     compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
1132 }
1133
1134 fn extract_function_from_bitcode(config: &config, _props: &TestProps,
1135                                  fname: &str, testfile: &Path,
1136                                  suffix: &str) -> ProcRes {
1137     let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1138     let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
1139     let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
1140     let prog = config.llvm_bin_path.get_ref().join("llvm-extract");
1141     let proc_args = ProcArgs {
1142         // FIXME (#9639): This needs to handle non-utf8 paths
1143         prog: prog.as_str().unwrap().to_owned(),
1144         args: vec!("-func=" + fname,
1145                 "-o=" + extracted_bc.as_str().unwrap(),
1146                 bitcodefile.as_str().unwrap().to_owned() )
1147     };
1148     compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
1149 }
1150
1151 fn disassemble_extract(config: &config, _props: &TestProps,
1152                        testfile: &Path, suffix: &str) -> ProcRes {
1153     let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1154     let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
1155     let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
1156     let extracted_ll = extracted_bc.with_extension("ll");
1157     let prog = config.llvm_bin_path.get_ref().join("llvm-dis");
1158     let proc_args = ProcArgs {
1159         // FIXME (#9639): This needs to handle non-utf8 paths
1160         prog: prog.as_str().unwrap().to_owned(),
1161         args: vec!("-o=" + extracted_ll.as_str().unwrap(),
1162                 extracted_bc.as_str().unwrap().to_owned() )
1163     };
1164     compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
1165 }
1166
1167
1168 fn count_extracted_lines(p: &Path) -> uint {
1169     let x = File::open(&p.with_extension("ll")).read_to_end().unwrap();
1170     let x = str::from_utf8_owned(x).unwrap();
1171     x.lines().len()
1172 }
1173
1174
1175 fn run_codegen_test(config: &config, props: &TestProps,
1176                     testfile: &Path, mm: &mut MetricMap) {
1177
1178     if config.llvm_bin_path.is_none() {
1179         fatal(~"missing --llvm-bin-path");
1180     }
1181
1182     if config.clang_path.is_none() {
1183         fatal(~"missing --clang-path");
1184     }
1185
1186     let mut proc_res = compile_test_and_save_bitcode(config, props, testfile);
1187     if !proc_res.status.success() {
1188         fatal_ProcRes(~"compilation failed!", &proc_res);
1189     }
1190
1191     proc_res = extract_function_from_bitcode(config, props, "test", testfile, "");
1192     if !proc_res.status.success() {
1193         fatal_ProcRes(~"extracting 'test' function failed", &proc_res);
1194     }
1195
1196     proc_res = disassemble_extract(config, props, testfile, "");
1197     if !proc_res.status.success() {
1198         fatal_ProcRes(~"disassembling extract failed", &proc_res);
1199     }
1200
1201
1202     let mut proc_res = compile_cc_with_clang_and_save_bitcode(config, props, testfile);
1203     if !proc_res.status.success() {
1204         fatal_ProcRes(~"compilation failed!", &proc_res);
1205     }
1206
1207     proc_res = extract_function_from_bitcode(config, props, "test", testfile, "clang");
1208     if !proc_res.status.success() {
1209         fatal_ProcRes(~"extracting 'test' function failed", &proc_res);
1210     }
1211
1212     proc_res = disassemble_extract(config, props, testfile, "clang");
1213     if !proc_res.status.success() {
1214         fatal_ProcRes(~"disassembling extract failed", &proc_res);
1215     }
1216
1217     let base = output_base_name(config, testfile);
1218     let base_extract = append_suffix_to_stem(&base, "extract");
1219
1220     let base_clang = append_suffix_to_stem(&base, "clang");
1221     let base_clang_extract = append_suffix_to_stem(&base_clang, "extract");
1222
1223     let base_lines = count_extracted_lines(&base_extract);
1224     let clang_lines = count_extracted_lines(&base_clang_extract);
1225
1226     mm.insert_metric("clang-codegen-ratio",
1227                      (base_lines as f64) / (clang_lines as f64),
1228                      0.001);
1229 }