]> git.lizzy.rs Git - rust.git/blob - src/compiletest/runtest.rs
rustc: Fix for-range loops that can use iterators
[rust.git] / src / compiletest / runtest.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the
2 // COPYRIGHT 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::mode_run_pass;
12 use common::mode_run_fail;
13 use common::mode_compile_fail;
14 use common::mode_pretty;
15 use common::config;
16 use errors;
17 use header::load_props;
18 use header::TestProps;
19 use procsrv;
20 use util;
21 use util::logv;
22
23 use std::io;
24 use std::os;
25 use std::str;
26 use std::vec;
27
28 use extra::test::MetricMap;
29
30 pub fn run(config: config, testfile: ~str) {
31     let mut _mm = MetricMap::new();
32     run_metrics(config, testfile, &mut _mm);
33 }
34
35 pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) {
36     if config.verbose {
37         // We're going to be dumping a lot of info. Start on a new line.
38         io::stdout().write_str("\n\n");
39     }
40     let testfile = Path(testfile);
41     debug!("running %s", testfile.to_str());
42     let props = load_props(&testfile);
43     debug!("loaded props");
44     match config.mode {
45       mode_compile_fail => run_cfail_test(&config, &props, &testfile),
46       mode_run_fail => run_rfail_test(&config, &props, &testfile),
47       mode_run_pass => run_rpass_test(&config, &props, &testfile),
48       mode_pretty => run_pretty_test(&config, &props, &testfile),
49       mode_debug_info => run_debuginfo_test(&config, &props, &testfile),
50       mode_codegen => run_codegen_test(&config, &props, &testfile, mm)
51     }
52 }
53
54 fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) {
55     let ProcRes = compile_test(config, props, testfile);
56
57     if ProcRes.status == 0 {
58         fatal_ProcRes(~"compile-fail test compiled successfully!", &ProcRes);
59     }
60
61     check_correct_failure_status(&ProcRes);
62
63     let expected_errors = errors::load_errors(testfile);
64     if !expected_errors.is_empty() {
65         if !props.error_patterns.is_empty() {
66             fatal(~"both error pattern and expected errors specified");
67         }
68         check_expected_errors(expected_errors, testfile, &ProcRes);
69     } else {
70         check_error_patterns(props, testfile, &ProcRes);
71     }
72 }
73
74 fn run_rfail_test(config: &config, props: &TestProps, testfile: &Path) {
75     let ProcRes = if !config.jit {
76         let ProcRes = compile_test(config, props, testfile);
77
78         if ProcRes.status != 0 {
79             fatal_ProcRes(~"compilation failed!", &ProcRes);
80         }
81
82         exec_compiled_test(config, props, testfile)
83     } else {
84         jit_test(config, props, testfile)
85     };
86
87     // The value our Makefile configures valgrind to return on failure
88     static VALGRIND_ERR: int = 100;
89     if ProcRes.status == VALGRIND_ERR {
90         fatal_ProcRes(~"run-fail test isn't valgrind-clean!", &ProcRes);
91     }
92
93     match config.target {
94
95         ~"arm-linux-androideabi" => {
96             if (config.adb_device_status) {
97                 check_correct_failure_status(&ProcRes);
98                 check_error_patterns(props, testfile, &ProcRes);
99             }
100         }
101
102         _=> {
103             check_correct_failure_status(&ProcRes);
104             check_error_patterns(props, testfile, &ProcRes);
105         }
106     }
107 }
108
109 fn check_correct_failure_status(ProcRes: &ProcRes) {
110     // The value the rust runtime returns on failure
111     static RUST_ERR: int = 101;
112     if ProcRes.status != RUST_ERR {
113         fatal_ProcRes(
114             fmt!("failure produced the wrong error code: %d",
115                  ProcRes.status),
116             ProcRes);
117     }
118 }
119
120 fn run_rpass_test(config: &config, props: &TestProps, testfile: &Path) {
121     if !config.jit {
122         let mut ProcRes = compile_test(config, props, testfile);
123
124         if ProcRes.status != 0 {
125             fatal_ProcRes(~"compilation failed!", &ProcRes);
126         }
127
128         ProcRes = exec_compiled_test(config, props, testfile);
129
130         if ProcRes.status != 0 {
131             fatal_ProcRes(~"test run failed!", &ProcRes);
132         }
133     } else {
134         let ProcRes = jit_test(config, props, testfile);
135
136         if ProcRes.status != 0 { fatal_ProcRes(~"jit failed!", &ProcRes); }
137     }
138 }
139
140 fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) {
141     if props.pp_exact.is_some() {
142         logv(config, ~"testing for exact pretty-printing");
143     } else { logv(config, ~"testing for converging pretty-printing"); }
144
145     let rounds =
146         match props.pp_exact { Some(_) => 1, None => 2 };
147
148     let mut srcs = ~[io::read_whole_file_str(testfile).unwrap()];
149
150     let mut round = 0;
151     while round < rounds {
152         logv(config, fmt!("pretty-printing round %d", round));
153         let ProcRes = print_source(config, testfile, srcs[round].clone());
154
155         if ProcRes.status != 0 {
156             fatal_ProcRes(fmt!("pretty-printing failed in round %d", round),
157                           &ProcRes);
158         }
159
160         let ProcRes{ stdout, _ } = ProcRes;
161         srcs.push(stdout);
162         round += 1;
163     }
164
165     let mut expected = match props.pp_exact {
166         Some(ref file) => {
167             let filepath = testfile.dir_path().push_rel(file);
168             io::read_whole_file_str(&filepath).unwrap()
169           }
170           None => { srcs[srcs.len() - 2u].clone() }
171         };
172     let mut actual = srcs[srcs.len() - 1u].clone();
173
174     if props.pp_exact.is_some() {
175         // Now we have to care about line endings
176         let cr = ~"\r";
177         actual = actual.replace(cr, "");
178         expected = expected.replace(cr, "");
179     }
180
181     compare_source(expected, actual);
182
183     // Finally, let's make sure it actually appears to remain valid code
184     let ProcRes = typecheck_source(config, props, testfile, actual);
185
186     if ProcRes.status != 0 {
187         fatal_ProcRes(~"pretty-printed source does not typecheck", &ProcRes);
188     }
189
190     return;
191
192     fn print_source(config: &config, testfile: &Path, src: ~str) -> ProcRes {
193         compose_and_run(config, testfile, make_pp_args(config, testfile),
194                         ~[], config.compile_lib_path, Some(src))
195     }
196
197     fn make_pp_args(config: &config, _testfile: &Path) -> ProcArgs {
198         let args = ~[~"-", ~"--pretty", ~"normal"];
199         return ProcArgs {prog: config.rustc_path.to_str(), args: args};
200     }
201
202     fn compare_source(expected: &str, actual: &str) {
203         if expected != actual {
204             error(~"pretty-printed source does not match expected source");
205             let msg =
206                 fmt!("\n\
207 expected:\n\
208 ------------------------------------------\n\
209 %s\n\
210 ------------------------------------------\n\
211 actual:\n\
212 ------------------------------------------\n\
213 %s\n\
214 ------------------------------------------\n\
215 \n",
216                      expected, actual);
217             io::stdout().write_str(msg);
218             fail!();
219         }
220     }
221
222     fn typecheck_source(config: &config, props: &TestProps,
223                         testfile: &Path, src: ~str) -> ProcRes {
224         let args = make_typecheck_args(config, props, testfile);
225         compose_and_run_compiler(config, props, testfile, args, Some(src))
226     }
227
228     fn make_typecheck_args(config: &config, props: &TestProps, testfile: &Path) -> ProcArgs {
229         let mut args = ~[~"-",
230                          ~"--no-trans", ~"--lib",
231                          ~"-L", config.build_base.to_str(),
232                          ~"-L",
233                          aux_output_dir_name(config, testfile).to_str()];
234         args.push_all_move(split_maybe_args(&config.rustcflags));
235         args.push_all_move(split_maybe_args(&props.compile_flags));
236         return ProcArgs {prog: config.rustc_path.to_str(), args: args};
237     }
238 }
239
240 fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
241     // do not optimize debuginfo tests
242     let mut config = match config.rustcflags {
243         Some(ref flags) => config {
244             rustcflags: Some(flags.replace("-O", "")),
245             .. (*config).clone()
246         },
247         None => (*config).clone()
248     };
249     let config = &mut config;
250     let cmds = props.debugger_cmds.connect("\n");
251     let check_lines = props.check_lines.clone();
252
253     // compile test file (it shoud have 'compile-flags:-g' in the header)
254     let mut ProcRes = compile_test(config, props, testfile);
255     if ProcRes.status != 0 {
256         fatal_ProcRes(~"compilation failed!", &ProcRes);
257     }
258
259     // write debugger script
260     let script_str = cmds.append("\nquit\n");
261     debug!("script_str = %s", script_str);
262     dump_output_file(config, testfile, script_str, "debugger.script");
263
264     // run debugger script with gdb
265     #[cfg(windows)]
266     fn debugger() -> ~str { ~"gdb.exe" }
267     #[cfg(unix)]
268     fn debugger() -> ~str { ~"gdb" }
269     let debugger_script = make_out_name(config, testfile, "debugger.script");
270     let debugger_opts = ~[~"-quiet", ~"-batch", ~"-nx",
271                           ~"-command=" + debugger_script.to_str(),
272                           make_exe_name(config, testfile).to_str()];
273     let ProcArgs = ProcArgs {prog: debugger(), args: debugger_opts};
274     ProcRes = compose_and_run(config, testfile, ProcArgs, ~[], "", None);
275     if ProcRes.status != 0 {
276         fatal(~"gdb failed to execute");
277     }
278
279     let num_check_lines = check_lines.len();
280     if num_check_lines > 0 {
281         // check if each line in props.check_lines appears in the
282         // output (in order)
283         let mut i = 0u;
284         for line in ProcRes.stdout.line_iter() {
285             if check_lines[i].trim() == line.trim() {
286                 i += 1u;
287             }
288             if i == num_check_lines {
289                 // all lines checked
290                 break;
291             }
292         }
293         if i != num_check_lines {
294             fatal_ProcRes(fmt!("line not found in debugger output: %s",
295                                check_lines[i]), &ProcRes);
296         }
297     }
298 }
299
300 fn check_error_patterns(props: &TestProps,
301                         testfile: &Path,
302                         ProcRes: &ProcRes) {
303     if props.error_patterns.is_empty() {
304         fatal(~"no error pattern specified in " + testfile.to_str());
305     }
306
307     if ProcRes.status == 0 {
308         fatal(~"process did not return an error status");
309     }
310
311     let mut next_err_idx = 0u;
312     let mut next_err_pat = &props.error_patterns[next_err_idx];
313     let mut done = false;
314     for line in ProcRes.stderr.line_iter() {
315         if line.contains(*next_err_pat) {
316             debug!("found error pattern %s", *next_err_pat);
317             next_err_idx += 1u;
318             if next_err_idx == props.error_patterns.len() {
319                 debug!("found all error patterns");
320                 done = true;
321                 break;
322             }
323             next_err_pat = &props.error_patterns[next_err_idx];
324         }
325     }
326     if done { return; }
327
328     let missing_patterns =
329         props.error_patterns.slice(next_err_idx, props.error_patterns.len());
330     if missing_patterns.len() == 1u {
331         fatal_ProcRes(fmt!("error pattern '%s' not found!",
332                            missing_patterns[0]), ProcRes);
333     } else {
334         for pattern in missing_patterns.iter() {
335             error(fmt!("error pattern '%s' not found!", *pattern));
336         }
337         fatal_ProcRes(~"multiple error patterns not found", ProcRes);
338     }
339 }
340
341 fn check_expected_errors(expected_errors: ~[errors::ExpectedError],
342                          testfile: &Path,
343                          ProcRes: &ProcRes) {
344
345     // true if we found the error in question
346     let mut found_flags = vec::from_elem(
347         expected_errors.len(), false);
348
349     if ProcRes.status == 0 {
350         fatal(~"process did not return an error status");
351     }
352
353     let prefixes = expected_errors.iter().transform(|ee| {
354         fmt!("%s:%u:", testfile.to_str(), ee.line)
355     }).collect::<~[~str]>();
356
357     fn to_lower( s : &str ) -> ~str {
358         let i = s.iter();
359         let c : ~[char] = i.transform( |c| {
360             if c.is_ascii() {
361                 c.to_ascii().to_lower().to_char()
362             } else {
363                 c
364             }
365         } ).collect();
366         str::from_chars( c )
367     }
368
369     #[cfg(target_os = "win32")]
370     fn prefix_matches( line : &str, prefix : &str ) -> bool {
371         to_lower(line).starts_with( to_lower(prefix) )
372     }
373
374     #[cfg(target_os = "linux")]
375     #[cfg(target_os = "macos")]
376     #[cfg(target_os = "freebsd")]
377     fn prefix_matches( line : &str, prefix : &str ) -> bool {
378         line.starts_with( prefix )
379     }
380
381     // Scan and extract our error/warning messages,
382     // which look like:
383     //    filename:line1:col1: line2:col2: *error:* msg
384     //    filename:line1:col1: line2:col2: *warning:* msg
385     // where line1:col1: is the starting point, line2:col2:
386     // is the ending point, and * represents ANSI color codes.
387     for line in ProcRes.stderr.line_iter() {
388         let mut was_expected = false;
389         for (i, ee) in expected_errors.iter().enumerate() {
390             if !found_flags[i] {
391                 debug!("prefix=%s ee.kind=%s ee.msg=%s line=%s",
392                        prefixes[i], ee.kind, ee.msg, line);
393                 if (prefix_matches(line, prefixes[i]) &&
394                     line.contains(ee.kind) &&
395                     line.contains(ee.msg)) {
396                     found_flags[i] = true;
397                     was_expected = true;
398                     break;
399                 }
400             }
401         }
402
403         // ignore this msg which gets printed at the end
404         if line.contains("aborting due to") {
405             was_expected = true;
406         }
407
408         if !was_expected && is_compiler_error_or_warning(line) {
409             fatal_ProcRes(fmt!("unexpected compiler error or warning: '%s'",
410                                line),
411                           ProcRes);
412         }
413     }
414
415     for (i, &flag) in found_flags.iter().enumerate() {
416         if !flag {
417             let ee = &expected_errors[i];
418             fatal_ProcRes(fmt!("expected %s on line %u not found: %s",
419                                ee.kind, ee.line, ee.msg), ProcRes);
420         }
421     }
422 }
423
424 fn is_compiler_error_or_warning(line: &str) -> bool {
425     let mut i = 0u;
426     return
427         scan_until_char(line, ':', &mut i) &&
428         scan_char(line, ':', &mut i) &&
429         scan_integer(line, &mut i) &&
430         scan_char(line, ':', &mut i) &&
431         scan_integer(line, &mut i) &&
432         scan_char(line, ':', &mut i) &&
433         scan_char(line, ' ', &mut i) &&
434         scan_integer(line, &mut i) &&
435         scan_char(line, ':', &mut i) &&
436         scan_integer(line, &mut i) &&
437         scan_char(line, ' ', &mut i) &&
438         (scan_string(line, "error", &mut i) ||
439          scan_string(line, "warning", &mut i));
440 }
441
442 fn scan_until_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
443     if *idx >= haystack.len() {
444         return false;
445     }
446     let opt = haystack.slice_from(*idx).find(needle);
447     if opt.is_none() {
448         return false;
449     }
450     *idx = opt.unwrap();
451     return true;
452 }
453
454 fn scan_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
455     if *idx >= haystack.len() {
456         return false;
457     }
458     let range = haystack.char_range_at(*idx);
459     if range.ch != needle {
460         return false;
461     }
462     *idx = range.next;
463     return true;
464 }
465
466 fn scan_integer(haystack: &str, idx: &mut uint) -> bool {
467     let mut i = *idx;
468     while i < haystack.len() {
469         let range = haystack.char_range_at(i);
470         if range.ch < '0' || '9' < range.ch {
471             break;
472         }
473         i = range.next;
474     }
475     if i == *idx {
476         return false;
477     }
478     *idx = i;
479     return true;
480 }
481
482 fn scan_string(haystack: &str, needle: &str, idx: &mut uint) -> bool {
483     let mut haystack_i = *idx;
484     let mut needle_i = 0u;
485     while needle_i < needle.len() {
486         if haystack_i >= haystack.len() {
487             return false;
488         }
489         let range = haystack.char_range_at(haystack_i);
490         haystack_i = range.next;
491         if !scan_char(needle, range.ch, &mut needle_i) {
492             return false;
493         }
494     }
495     *idx = haystack_i;
496     return true;
497 }
498
499 struct ProcArgs {prog: ~str, args: ~[~str]}
500
501 struct ProcRes {status: int, stdout: ~str, stderr: ~str, cmdline: ~str}
502
503 fn compile_test(config: &config, props: &TestProps,
504                 testfile: &Path) -> ProcRes {
505     compile_test_(config, props, testfile, [])
506 }
507
508 fn jit_test(config: &config, props: &TestProps, testfile: &Path) -> ProcRes {
509     compile_test_(config, props, testfile, [~"--jit"])
510 }
511
512 fn compile_test_(config: &config, props: &TestProps,
513                  testfile: &Path, extra_args: &[~str]) -> ProcRes {
514     let link_args = ~[~"-L", aux_output_dir_name(config, testfile).to_str()];
515     let args = make_compile_args(config, props, link_args + extra_args,
516                                  make_exe_name, testfile);
517     compose_and_run_compiler(config, props, testfile, args, None)
518 }
519
520 fn exec_compiled_test(config: &config, props: &TestProps,
521                       testfile: &Path) -> ProcRes {
522
523     // If testing the new runtime then set the RUST_NEWRT env var
524     let env = props.exec_env.clone();
525     let env = if config.newrt { env + &[(~"RUST_NEWRT", ~"1")] } else { env };
526
527     match config.target {
528
529         ~"arm-linux-androideabi" => {
530             if (config.adb_device_status) {
531                 _arm_exec_compiled_test(config, props, testfile)
532             } else {
533                 _dummy_exec_compiled_test(config, props, testfile)
534             }
535         }
536
537         _=> {
538             compose_and_run(config, testfile,
539                             make_run_args(config, props, testfile),
540                             env,
541                             config.run_lib_path, None)
542         }
543     }
544 }
545
546 fn compose_and_run_compiler(
547     config: &config,
548     props: &TestProps,
549     testfile: &Path,
550     args: ProcArgs,
551     input: Option<~str>) -> ProcRes {
552
553     if !props.aux_builds.is_empty() {
554         ensure_dir(&aux_output_dir_name(config, testfile));
555     }
556
557     let extra_link_args = ~[~"-L",
558                             aux_output_dir_name(config, testfile).to_str()];
559
560     for rel_ab in props.aux_builds.iter() {
561         let abs_ab = config.aux_base.push_rel(&Path(*rel_ab));
562         let aux_args =
563             make_compile_args(config, props, ~[~"--lib"] + extra_link_args,
564                               |a,b| make_lib_name(a, b, testfile), &abs_ab);
565         let auxres = compose_and_run(config, &abs_ab, aux_args, ~[],
566                                      config.compile_lib_path, None);
567         if auxres.status != 0 {
568             fatal_ProcRes(
569                 fmt!("auxiliary build of %s failed to compile: ",
570                      abs_ab.to_str()),
571                 &auxres);
572         }
573
574         match config.target {
575
576             ~"arm-linux-androideabi" => {
577                 if (config.adb_device_status) {
578                     _arm_push_aux_shared_library(config, testfile);
579                 }
580             }
581
582             _=> { }
583         }
584     }
585
586     compose_and_run(config, testfile, args, ~[],
587                     config.compile_lib_path, input)
588 }
589
590 fn ensure_dir(path: &Path) {
591     if os::path_is_dir(path) { return; }
592     if !os::make_dir(path, 0x1c0i32) {
593         fail!("can't make dir %s", path.to_str());
594     }
595 }
596
597 fn compose_and_run(config: &config, testfile: &Path,
598                    ProcArgs{ args, prog }: ProcArgs,
599                    procenv: ~[(~str, ~str)],
600                    lib_path: &str,
601                    input: Option<~str>) -> ProcRes {
602     return program_output(config, testfile, lib_path,
603                           prog, args, procenv, input);
604 }
605
606 fn make_compile_args(config: &config, props: &TestProps, extras: ~[~str],
607                      xform: &fn(&config, (&Path)) -> Path,
608                      testfile: &Path) -> ProcArgs {
609     let mut args = ~[testfile.to_str(),
610                      ~"-o", xform(config, testfile).to_str(),
611                      ~"-L", config.build_base.to_str()]
612         + extras;
613     args.push_all_move(split_maybe_args(&config.rustcflags));
614     args.push_all_move(split_maybe_args(&props.compile_flags));
615     return ProcArgs {prog: config.rustc_path.to_str(), args: args};
616 }
617
618 fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path {
619     // what we return here is not particularly important, as it
620     // happens; rustc ignores everything except for the directory.
621     let auxname = output_testname(auxfile);
622     aux_output_dir_name(config, testfile).push_rel(&auxname)
623 }
624
625 fn make_exe_name(config: &config, testfile: &Path) -> Path {
626     Path(output_base_name(config, testfile).to_str() + os::EXE_SUFFIX)
627 }
628
629 fn make_run_args(config: &config, _props: &TestProps, testfile: &Path) ->
630    ProcArgs {
631     // If we've got another tool to run under (valgrind),
632     // then split apart its command
633     let mut args = split_maybe_args(&config.runtool);
634     args.push(make_exe_name(config, testfile).to_str());
635     let prog = args.shift();
636     return ProcArgs {prog: prog, args: args};
637 }
638
639 fn split_maybe_args(argstr: &Option<~str>) -> ~[~str] {
640     match *argstr {
641         Some(ref s) => {
642             s.split_iter(' ')
643                 .filter_map(|s| if s.is_whitespace() {None} else {Some(s.to_owned())})
644                 .collect()
645         }
646         None => ~[]
647     }
648 }
649
650 fn program_output(config: &config, testfile: &Path, lib_path: &str, prog: ~str,
651                   args: ~[~str], env: ~[(~str, ~str)],
652                   input: Option<~str>) -> ProcRes {
653     let cmdline =
654         {
655             let cmdline = make_cmdline(lib_path, prog, args);
656             logv(config, fmt!("executing %s", cmdline));
657             cmdline
658         };
659     let procsrv::Result{ out, err, status } =
660             procsrv::run(lib_path, prog, args, env, input);
661     dump_output(config, testfile, out, err);
662     return ProcRes {status: status,
663          stdout: out,
664          stderr: err,
665          cmdline: cmdline};
666 }
667
668 // Linux and mac don't require adjusting the library search path
669 #[cfg(target_os = "linux")]
670 #[cfg(target_os = "macos")]
671 #[cfg(target_os = "freebsd")]
672 fn make_cmdline(_libpath: &str, prog: &str, args: &[~str]) -> ~str {
673     fmt!("%s %s", prog, args.connect(" "))
674 }
675
676 #[cfg(target_os = "win32")]
677 fn make_cmdline(libpath: &str, prog: &str, args: &[~str]) -> ~str {
678     fmt!("%s %s %s", lib_path_cmd_prefix(libpath), prog,
679          args.connect(" "))
680 }
681
682 // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
683 // for diagnostic purposes
684 fn lib_path_cmd_prefix(path: &str) -> ~str {
685     fmt!("%s=\"%s\"", util::lib_path_env_var(), util::make_new_path(path))
686 }
687
688 fn dump_output(config: &config, testfile: &Path, out: &str, err: &str) {
689     dump_output_file(config, testfile, out, "out");
690     dump_output_file(config, testfile, err, "err");
691     maybe_dump_to_stdout(config, out, err);
692 }
693
694 fn dump_output_file(config: &config, testfile: &Path,
695                     out: &str, extension: &str) {
696     let outfile = make_out_name(config, testfile, extension);
697     let writer =
698         io::file_writer(&outfile, [io::Create, io::Truncate]).unwrap();
699     writer.write_str(out);
700 }
701
702 fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path {
703     output_base_name(config, testfile).with_filetype(extension)
704 }
705
706 fn aux_output_dir_name(config: &config, testfile: &Path) -> Path {
707     Path(output_base_name(config, testfile).to_str() + ".libaux")
708 }
709
710 fn output_testname(testfile: &Path) -> Path {
711     Path(testfile.filestem().unwrap())
712 }
713
714 fn output_base_name(config: &config, testfile: &Path) -> Path {
715     config.build_base
716         .push_rel(&output_testname(testfile))
717         .with_filetype(config.stage_id)
718 }
719
720 fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) {
721     if config.verbose {
722         let sep1 = fmt!("------%s------------------------------", "stdout");
723         let sep2 = fmt!("------%s------------------------------", "stderr");
724         let sep3 = ~"------------------------------------------";
725         io::stdout().write_line(sep1);
726         io::stdout().write_line(out);
727         io::stdout().write_line(sep2);
728         io::stdout().write_line(err);
729         io::stdout().write_line(sep3);
730     }
731 }
732
733 fn error(err: ~str) { io::stdout().write_line(fmt!("\nerror: %s", err)); }
734
735 fn fatal(err: ~str) -> ! { error(err); fail!(); }
736
737 fn fatal_ProcRes(err: ~str, ProcRes: &ProcRes) -> ! {
738     let msg =
739         fmt!("\n\
740 error: %s\n\
741 command: %s\n\
742 stdout:\n\
743 ------------------------------------------\n\
744 %s\n\
745 ------------------------------------------\n\
746 stderr:\n\
747 ------------------------------------------\n\
748 %s\n\
749 ------------------------------------------\n\
750 \n",
751              err, ProcRes.cmdline, ProcRes.stdout, ProcRes.stderr);
752     io::stdout().write_str(msg);
753     fail!();
754 }
755
756 fn _arm_exec_compiled_test(config: &config, props: &TestProps,
757                       testfile: &Path) -> ProcRes {
758
759     let args = make_run_args(config, props, testfile);
760     let cmdline = make_cmdline("", args.prog, args.args);
761
762     // get bare program string
763     let mut tvec: ~[~str] = args.prog.split_iter('/').transform(|ts| ts.to_owned()).collect();
764     let prog_short = tvec.pop();
765
766     // copy to target
767     let copy_result = procsrv::run("", config.adb_path,
768         [~"push", args.prog.clone(), config.adb_test_dir.clone()],
769         ~[(~"",~"")], Some(~""));
770
771     if config.verbose {
772         io::stdout().write_str(fmt!("push (%s) %s %s %s",
773             config.target, args.prog,
774             copy_result.out, copy_result.err));
775     }
776
777     logv(config, fmt!("executing (%s) %s", config.target, cmdline));
778
779     let mut runargs = ~[];
780
781     // run test via adb_run_wrapper
782     runargs.push(~"shell");
783     runargs.push(fmt!("%s/adb_run_wrapper.sh", config.adb_test_dir));
784     runargs.push(fmt!("%s", config.adb_test_dir));
785     runargs.push(fmt!("%s", prog_short));
786
787     for tv in args.args.iter() {
788         runargs.push(tv.to_owned());
789     }
790
791     procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""));
792
793     // get exitcode of result
794     runargs = ~[];
795     runargs.push(~"shell");
796     runargs.push(~"cat");
797     runargs.push(fmt!("%s/%s.exitcode", config.adb_test_dir, prog_short));
798
799     let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
800         procsrv::run("", config.adb_path, runargs, ~[(~"",~"")],
801                      Some(~""));
802
803     let mut exitcode : int = 0;
804     for c in exitcode_out.iter() {
805         if !c.is_digit() { break; }
806         exitcode = exitcode * 10 + match c {
807             '0' .. '9' => c as int - ('0' as int),
808             _ => 101,
809         }
810     }
811
812     // get stdout of result
813     runargs = ~[];
814     runargs.push(~"shell");
815     runargs.push(~"cat");
816     runargs.push(fmt!("%s/%s.stdout", config.adb_test_dir, prog_short));
817
818     let procsrv::Result{ out: stdout_out, err: _, status: _ } =
819         procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""));
820
821     // get stderr of result
822     runargs = ~[];
823     runargs.push(~"shell");
824     runargs.push(~"cat");
825     runargs.push(fmt!("%s/%s.stderr", config.adb_test_dir, prog_short));
826
827     let procsrv::Result{ out: stderr_out, err: _, status: _ } =
828         procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""));
829
830     dump_output(config, testfile, stdout_out, stderr_out);
831
832     ProcRes {status: exitcode, stdout: stdout_out, stderr: stderr_out, cmdline: cmdline }
833 }
834
835 fn _dummy_exec_compiled_test(config: &config, props: &TestProps,
836                       testfile: &Path) -> ProcRes {
837
838     let args = make_run_args(config, props, testfile);
839     let cmdline = make_cmdline("", args.prog, args.args);
840
841     match config.mode {
842         mode_run_fail => ProcRes {status: 101, stdout: ~"",
843                                  stderr: ~"", cmdline: cmdline},
844         _             => ProcRes {status: 0, stdout: ~"",
845                                  stderr: ~"", cmdline: cmdline}
846     }
847 }
848
849 fn _arm_push_aux_shared_library(config: &config, testfile: &Path) {
850     let tstr = aux_output_dir_name(config, testfile).to_str();
851
852     let dirs = os::list_dir_path(&Path(tstr));
853     for file in dirs.iter() {
854
855         if (file.filetype() == Some(~".so")) {
856
857             let copy_result = procsrv::run("", config.adb_path,
858                 [~"push", file.to_str(), config.adb_test_dir.clone()],
859                 ~[(~"",~"")], Some(~""));
860
861             if config.verbose {
862                 io::stdout().write_str(fmt!("push (%s) %s %s %s",
863                     config.target, file.to_str(),
864                     copy_result.out, copy_result.err));
865             }
866         }
867     }
868 }
869
870 // codegen tests (vs. clang)
871
872 fn make_o_name(config: &config, testfile: &Path) -> Path {
873     output_base_name(config, testfile).with_filetype("o")
874 }
875
876 fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path {
877     if suffix.len() == 0 {
878         (*p).clone()
879     } else {
880         let stem = p.filestem().unwrap();
881         p.with_filestem(stem + "-" + suffix)
882     }
883 }
884
885 fn compile_test_and_save_bitcode(config: &config, props: &TestProps,
886                                  testfile: &Path) -> ProcRes {
887     let link_args = ~[~"-L", aux_output_dir_name(config, testfile).to_str()];
888     let llvm_args = ~[~"-c", ~"--lib", ~"--save-temps"];
889     let args = make_compile_args(config, props,
890                                  link_args + llvm_args,
891                                  make_o_name, testfile);
892     compose_and_run_compiler(config, props, testfile, args, None)
893 }
894
895 fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps,
896                                           testfile: &Path) -> ProcRes {
897     let bitcodefile = output_base_name(config, testfile).with_filetype("bc");
898     let bitcodefile = append_suffix_to_stem(&bitcodefile, "clang");
899     let ProcArgs = ProcArgs {
900         prog: config.clang_path.get_ref().to_str(),
901         args: ~[~"-c",
902                 ~"-emit-llvm",
903                 ~"-o", bitcodefile.to_str(),
904                 testfile.with_filetype("cc").to_str() ]
905     };
906     compose_and_run(config, testfile, ProcArgs, ~[], "", None)
907 }
908
909 fn extract_function_from_bitcode(config: &config, _props: &TestProps,
910                                  fname: &str, testfile: &Path,
911                                  suffix: &str) -> ProcRes {
912     let bitcodefile = output_base_name(config, testfile).with_filetype("bc");
913     let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
914     let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
915     let ProcArgs = ProcArgs {
916         prog: config.llvm_bin_path.get_ref().push("llvm-extract").to_str(),
917         args: ~[~"-func=" + fname,
918                 ~"-o=" + extracted_bc.to_str(),
919                 bitcodefile.to_str() ]
920     };
921     compose_and_run(config, testfile, ProcArgs, ~[], "", None)
922 }
923
924 fn disassemble_extract(config: &config, _props: &TestProps,
925                        testfile: &Path, suffix: &str) -> ProcRes {
926     let bitcodefile = output_base_name(config, testfile).with_filetype("bc");
927     let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
928     let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
929     let extracted_ll = extracted_bc.with_filetype("ll");
930     let ProcArgs = ProcArgs {
931         prog: config.llvm_bin_path.get_ref().push("llvm-dis").to_str(),
932         args: ~[~"-o=" + extracted_ll.to_str(),
933                 extracted_bc.to_str() ]
934     };
935     compose_and_run(config, testfile, ProcArgs, ~[], "", None)
936 }
937
938
939 fn count_extracted_lines(p: &Path) -> uint {
940     let x = io::read_whole_file_str(&p.with_filetype("ll")).unwrap();
941     x.line_iter().len_()
942 }
943
944
945 fn run_codegen_test(config: &config, props: &TestProps,
946                     testfile: &Path, mm: &mut MetricMap) {
947
948     if config.llvm_bin_path.is_none() {
949         fatal(~"missing --llvm-bin-path");
950     }
951
952     if config.clang_path.is_none() {
953         fatal(~"missing --clang-path");
954     }
955
956     let mut ProcRes = compile_test_and_save_bitcode(config, props, testfile);
957     if ProcRes.status != 0 {
958         fatal_ProcRes(~"compilation failed!", &ProcRes);
959     }
960
961     ProcRes = extract_function_from_bitcode(config, props, "test", testfile, "");
962     if ProcRes.status != 0 {
963         fatal_ProcRes(~"extracting 'test' function failed", &ProcRes);
964     }
965
966     ProcRes = disassemble_extract(config, props, testfile, "");
967     if ProcRes.status != 0 {
968         fatal_ProcRes(~"disassembling extract failed", &ProcRes);
969     }
970
971
972     let mut ProcRes = compile_cc_with_clang_and_save_bitcode(config, props, testfile);
973     if ProcRes.status != 0 {
974         fatal_ProcRes(~"compilation failed!", &ProcRes);
975     }
976
977     ProcRes = extract_function_from_bitcode(config, props, "test", testfile, "clang");
978     if ProcRes.status != 0 {
979         fatal_ProcRes(~"extracting 'test' function failed", &ProcRes);
980     }
981
982     ProcRes = disassemble_extract(config, props, testfile, "clang");
983     if ProcRes.status != 0 {
984         fatal_ProcRes(~"disassembling extract failed", &ProcRes);
985     }
986
987     let base = output_base_name(config, testfile);
988     let base_extract = append_suffix_to_stem(&base, "extract");
989
990     let base_clang = append_suffix_to_stem(&base, "clang");
991     let base_clang_extract = append_suffix_to_stem(&base_clang, "extract");
992
993     let base_lines = count_extracted_lines(&base_extract);
994     let clang_lines = count_extracted_lines(&base_clang_extract);
995
996     mm.insert_metric("clang-codegen-ratio",
997                      (base_lines as f64) / (clang_lines as f64),
998                      0.001);
999 }
1000