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.
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.
12 use common::mode_compile_fail;
13 use common::mode_pretty;
14 use common::mode_run_fail;
15 use common::mode_run_pass;
17 use header::TestProps;
18 use header::load_props;
21 #[cfg(target_os = "win32")]
26 use std::io::net::ip::{Ipv4Addr, SocketAddr};
27 use std::io::net::tcp;
28 use std::io::process::ProcessExit;
34 use std::strbuf::StrBuf;
38 pub fn run(config: config, testfile: ~str) {
40 match config.target.as_slice() {
42 "arm-linux-androideabi" => {
43 if !config.adb_device_status {
44 fail!("android device not available");
51 let mut _mm = MetricMap::new();
52 run_metrics(config, testfile, &mut _mm);
55 pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) {
57 // We're going to be dumping a lot of info. Start on a new line.
60 let testfile = Path::new(testfile);
61 debug!("running {}", testfile.display());
62 let props = load_props(&testfile);
63 debug!("loaded props");
65 mode_compile_fail => run_cfail_test(&config, &props, &testfile),
66 mode_run_fail => run_rfail_test(&config, &props, &testfile),
67 mode_run_pass => run_rpass_test(&config, &props, &testfile),
68 mode_pretty => run_pretty_test(&config, &props, &testfile),
69 mode_debug_info => run_debuginfo_test(&config, &props, &testfile),
70 mode_codegen => run_codegen_test(&config, &props, &testfile, mm)
74 fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) {
75 let proc_res = compile_test(config, props, testfile);
77 if proc_res.status.success() {
78 fatal_ProcRes("compile-fail test compiled successfully!".to_owned(), &proc_res);
81 check_correct_failure_status(&proc_res);
83 let expected_errors = errors::load_errors(testfile);
84 if !expected_errors.is_empty() {
85 if !props.error_patterns.is_empty() {
86 fatal("both error pattern and expected errors specified".to_owned());
88 check_expected_errors(expected_errors, testfile, &proc_res);
90 check_error_patterns(props, testfile, &proc_res);
94 fn run_rfail_test(config: &config, props: &TestProps, testfile: &Path) {
95 let proc_res = if !config.jit {
96 let proc_res = compile_test(config, props, testfile);
98 if !proc_res.status.success() {
99 fatal_ProcRes("compilation failed!".to_owned(), &proc_res);
102 exec_compiled_test(config, props, testfile)
104 jit_test(config, props, testfile)
107 // The value our Makefile configures valgrind to return on failure
108 static VALGRIND_ERR: int = 100;
109 if proc_res.status.matches_exit_status(VALGRIND_ERR) {
110 fatal_ProcRes("run-fail test isn't valgrind-clean!".to_owned(), &proc_res);
113 check_correct_failure_status(&proc_res);
114 check_error_patterns(props, testfile, &proc_res);
117 fn check_correct_failure_status(proc_res: &ProcRes) {
118 // The value the rust runtime returns on failure
119 static RUST_ERR: int = 101;
120 if !proc_res.status.matches_exit_status(RUST_ERR) {
122 format!("failure produced the wrong error: {}", proc_res.status),
127 fn run_rpass_test(config: &config, props: &TestProps, testfile: &Path) {
129 let mut proc_res = compile_test(config, props, testfile);
131 if !proc_res.status.success() {
132 fatal_ProcRes("compilation failed!".to_owned(), &proc_res);
135 proc_res = exec_compiled_test(config, props, testfile);
137 if !proc_res.status.success() {
138 fatal_ProcRes("test run failed!".to_owned(), &proc_res);
141 let proc_res = jit_test(config, props, testfile);
143 if !proc_res.status.success() { fatal_ProcRes("jit failed!".to_owned(), &proc_res); }
147 fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) {
148 if props.pp_exact.is_some() {
149 logv(config, "testing for exact pretty-printing".to_owned());
150 } else { logv(config, "testing for converging pretty-printing".to_owned()); }
153 match props.pp_exact { Some(_) => 1, None => 2 };
155 let src = File::open(testfile).read_to_end().unwrap();
156 let src = str::from_utf8(src.as_slice()).unwrap().to_owned();
157 let mut srcs = vec!(src);
160 while round < rounds {
161 logv(config, format!("pretty-printing round {}", round));
162 let proc_res = print_source(config,
164 (*srcs.get(round)).clone());
166 if !proc_res.status.success() {
167 fatal_ProcRes(format!("pretty-printing failed in round {}", round),
171 let ProcRes{ stdout, .. } = proc_res;
176 let mut expected = match props.pp_exact {
178 let filepath = testfile.dir_path().join(file);
179 let s = File::open(&filepath).read_to_end().unwrap();
180 str::from_utf8(s.as_slice()).unwrap().to_owned()
182 None => { (*srcs.get(srcs.len() - 2u)).clone() }
184 let mut actual = (*srcs.get(srcs.len() - 1u)).clone();
186 if props.pp_exact.is_some() {
187 // Now we have to care about line endings
188 let cr = "\r".to_owned();
189 actual = actual.replace(cr, "");
190 expected = expected.replace(cr, "");
193 compare_source(expected, actual);
195 // Finally, let's make sure it actually appears to remain valid code
196 let proc_res = typecheck_source(config, props, testfile, actual);
198 if !proc_res.status.success() {
199 fatal_ProcRes("pretty-printed source does not typecheck".to_owned(), &proc_res);
204 fn print_source(config: &config, testfile: &Path, src: ~str) -> ProcRes {
205 compose_and_run(config, testfile, make_pp_args(config, testfile),
206 Vec::new(), config.compile_lib_path, Some(src))
209 fn make_pp_args(config: &config, _testfile: &Path) -> ProcArgs {
210 let args = vec!("-".to_owned(), "--pretty".to_owned(), "normal".to_owned(),
211 "--target=".to_owned() + config.target);
212 // FIXME (#9639): This needs to handle non-utf8 paths
213 return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
216 fn compare_source(expected: &str, actual: &str) {
217 if expected != actual {
218 error("pretty-printed source does not match expected source".to_owned());
221 ------------------------------------------\n\
223 ------------------------------------------\n\
225 ------------------------------------------\n\
227 ------------------------------------------\n\
234 fn typecheck_source(config: &config, props: &TestProps,
235 testfile: &Path, src: ~str) -> ProcRes {
236 let args = make_typecheck_args(config, props, testfile);
237 compose_and_run_compiler(config, props, testfile, args, Some(src))
240 fn make_typecheck_args(config: &config, props: &TestProps, testfile: &Path) -> ProcArgs {
241 let aux_dir = aux_output_dir_name(config, testfile);
242 let target = if props.force_host {
243 config.host.as_slice()
245 config.target.as_slice()
247 // FIXME (#9639): This needs to handle non-utf8 paths
248 let mut args = vec!("-".to_owned(),
249 "--no-trans".to_owned(), "--crate-type=lib".to_owned(),
250 "--target=".to_owned() + target,
251 "-L".to_owned(), config.build_base.as_str().unwrap().to_owned(),
253 aux_dir.as_str().unwrap().to_owned());
254 args.push_all_move(split_maybe_args(&config.target_rustcflags));
255 args.push_all_move(split_maybe_args(&props.compile_flags));
256 // FIXME (#9639): This needs to handle non-utf8 paths
257 return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
261 fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
262 let mut config = config {
263 target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
264 host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
268 let config = &mut config;
269 let check_lines = &props.check_lines;
270 let mut cmds = props.debugger_cmds.connect("\n");
272 // compile test file (it shoud have 'compile-flags:-g' in the header)
273 let mut proc_res = compile_test(config, props, testfile);
274 if !proc_res.status.success() {
275 fatal_ProcRes("compilation failed!".to_owned(), &proc_res);
278 let exe_file = make_exe_name(config, testfile);
281 match config.target.as_slice() {
282 "arm-linux-androideabi" => {
284 cmds = cmds.replace("run","continue");
286 // write debugger script
287 let script_str = ["set charset UTF-8".to_owned(),
288 format!("file {}",exe_file.as_str().unwrap().to_owned()),
289 "target remote :5039".to_owned(),
291 "quit".to_owned()].connect("\n");
292 debug!("script_str = {}", script_str);
293 dump_output_file(config, testfile, script_str, "debugger.script");
296 procsrv::run("", config.adb_path,
297 ["push".to_owned(), exe_file.as_str().unwrap().to_owned(),
298 config.adb_test_dir.clone()],
299 vec!(("".to_owned(),"".to_owned())), Some("".to_owned()))
300 .expect(format!("failed to exec `{}`", config.adb_path));
302 procsrv::run("", config.adb_path,
303 ["forward".to_owned(), "tcp:5039".to_owned(), "tcp:5039".to_owned()],
304 vec!(("".to_owned(),"".to_owned())), Some("".to_owned()))
305 .expect(format!("failed to exec `{}`", config.adb_path));
307 let adb_arg = format!("export LD_LIBRARY_PATH={}; gdbserver :5039 {}/{}",
308 config.adb_test_dir.clone(), config.adb_test_dir.clone(),
309 str::from_utf8(exe_file.filename().unwrap()).unwrap());
311 let mut process = procsrv::run_background("", config.adb_path,
312 ["shell".to_owned(),adb_arg.clone()],
313 vec!(("".to_owned(),"".to_owned())),
315 .expect(format!("failed to exec `{}`", config.adb_path));
317 //waiting 1 second for gdbserver start
319 let result = task::try(proc() {
320 tcp::TcpStream::connect(SocketAddr {
321 ip: Ipv4Addr(127, 0, 0, 1),
331 let args = split_maybe_args(&config.target_rustcflags);
332 let mut tool_path = StrBuf::new();
333 for arg in args.iter() {
334 if arg.contains("android-cross-path=") {
335 tool_path = StrBuf::from_str(arg.replace("android-cross-path=", ""));
340 if tool_path.is_empty() {
341 fatal("cannot found android cross path".to_owned());
344 let debugger_script = make_out_name(config, testfile, "debugger.script");
345 // FIXME (#9639): This needs to handle non-utf8 paths
346 let debugger_opts = vec!("-quiet".to_owned(), "-batch".to_owned(), "-nx".to_owned(),
347 "-command=" + debugger_script.as_str().unwrap().to_owned());
349 let gdb_path = tool_path.append("/bin/arm-linux-androideabi-gdb");
350 let procsrv::Result{ out, err, status }=
353 debugger_opts.as_slice(),
354 vec!(("".to_owned(),"".to_owned())),
356 .expect(format!("failed to exec `{}`", gdb_path));
358 let cmdline = make_cmdline("",
359 "arm-linux-androideabi-gdb",
360 debugger_opts.as_slice());
361 logv(config, format!("executing {}", cmdline));
365 proc_res = ProcRes {status: status,
369 process.signal_kill().unwrap();
373 // write debugger script
374 let script_str = ["set charset UTF-8".to_owned(),
376 "quit\n".to_owned()].connect("\n");
377 debug!("script_str = {}", script_str);
378 dump_output_file(config, testfile, script_str, "debugger.script");
380 // run debugger script with gdb
382 fn debugger() -> ~str { "gdb.exe".to_owned() }
384 fn debugger() -> ~str { "gdb".to_owned() }
386 let debugger_script = make_out_name(config, testfile, "debugger.script");
388 // FIXME (#9639): This needs to handle non-utf8 paths
389 let debugger_opts = vec!("-quiet".to_owned(), "-batch".to_owned(), "-nx".to_owned(),
390 "-command=" + debugger_script.as_str().unwrap().to_owned(),
391 exe_file.as_str().unwrap().to_owned());
392 proc_args = ProcArgs {prog: debugger(), args: debugger_opts};
393 proc_res = compose_and_run(config, testfile, proc_args, Vec::new(), "", None);
397 if !proc_res.status.success() {
398 fatal("gdb failed to execute".to_owned());
400 let num_check_lines = check_lines.len();
401 if num_check_lines > 0 {
402 // Allow check lines to leave parts unspecified (e.g., uninitialized
403 // bits in the wrong case of an enum) with the notation "[...]".
404 let check_fragments: Vec<Vec<~str>> =
405 check_lines.iter().map(|s| {
406 s.split_str("[...]").map(|x| x.to_str()).collect()
408 // check if each line in props.check_lines appears in the
411 for line in proc_res.stdout.lines() {
412 let mut rest = line.trim();
413 let mut first = true;
414 let mut failed = false;
415 for frag in check_fragments.get(i).iter() {
416 let found = if first {
417 if rest.starts_with(*frag) { Some(0) } else { None }
427 rest = rest.slice_from(i + frag.len());
432 if !failed && rest.len() == 0 {
435 if i == num_check_lines {
440 if i != num_check_lines {
441 fatal_ProcRes(format!("line not found in debugger output: {}",
442 *check_lines.get(i)), &proc_res);
446 fn cleanup_debug_info_options(options: &Option<~str>) -> Option<~str> {
447 if options.is_none() {
451 // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
452 let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()];
453 let new_options = split_maybe_args(options).move_iter()
454 .filter(|x| !options_to_remove.contains(x))
455 .collect::<Vec<~str>>()
461 fn check_error_patterns(props: &TestProps,
463 proc_res: &ProcRes) {
464 if props.error_patterns.is_empty() {
465 fatal("no error pattern specified in ".to_owned() +
466 testfile.display().as_maybe_owned().as_slice());
469 if proc_res.status.success() {
470 fatal("process did not return an error status".to_owned());
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
479 proc_res.stderr.clone()
481 for line in output_to_check.lines() {
482 if line.contains(*next_err_pat) {
483 debug!("found error pattern {}", *next_err_pat);
485 if next_err_idx == props.error_patterns.len() {
486 debug!("found all error patterns");
490 next_err_pat = props.error_patterns.get(next_err_idx);
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);
501 for pattern in missing_patterns.iter() {
502 error(format!("error pattern '{}' not found!", *pattern));
504 fatal_ProcRes("multiple error patterns not found".to_owned(), proc_res);
508 fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
510 proc_res: &ProcRes) {
512 // true if we found the error in question
513 let mut found_flags = Vec::from_elem(
514 expected_errors.len(), false);
516 if proc_res.status.success() {
517 fatal("process did not return an error status".to_owned());
520 let prefixes = expected_errors.iter().map(|ee| {
521 format!("{}:{}:", testfile.display(), ee.line)
522 }).collect::<Vec<~str> >();
524 #[cfg(target_os = "win32")]
525 fn to_lower( s : &str ) -> ~str {
527 let c : Vec<char> = i.map( |c| {
529 c.to_ascii().to_lower().to_char()
534 str::from_chars(c.as_slice())
537 #[cfg(target_os = "win32")]
538 fn prefix_matches( line : &str, prefix : &str ) -> bool {
539 to_lower(line).starts_with( to_lower(prefix) )
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 )
549 // Scan and extract our error/warning messages,
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.get(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.get_mut(i) = true;
571 // ignore this msg which gets printed at the end
572 if line.contains("aborting due to") {
576 if !was_expected && is_compiler_error_or_warning(line) {
577 fatal_ProcRes(format!("unexpected compiler error or warning: '{}'",
583 for (i, &flag) in found_flags.iter().enumerate() {
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);
592 fn is_compiler_error_or_warning(line: &str) -> bool {
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));
610 fn scan_until_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
611 if *idx >= haystack.len() {
614 let opt = haystack.slice_from(*idx).find(needle);
622 fn scan_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
623 if *idx >= haystack.len() {
626 let range = haystack.char_range_at(*idx);
627 if range.ch != needle {
634 fn scan_integer(haystack: &str, idx: &mut uint) -> bool {
636 while i < haystack.len() {
637 let range = haystack.char_range_at(i);
638 if range.ch < '0' || '9' < range.ch {
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() {
657 let range = haystack.char_range_at(haystack_i);
658 haystack_i = range.next;
659 if !scan_char(needle, range.ch, &mut needle_i) {
667 struct ProcArgs {prog: ~str, args: Vec<~str> }
669 struct ProcRes {status: ProcessExit, stdout: ~str, stderr: ~str, cmdline: ~str}
671 fn compile_test(config: &config, props: &TestProps,
672 testfile: &Path) -> ProcRes {
673 compile_test_(config, props, testfile, [])
676 fn jit_test(config: &config, props: &TestProps, testfile: &Path) -> ProcRes {
677 compile_test_(config, props, testfile, ["--jit".to_owned()])
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".to_owned(), aux_dir.as_str().unwrap().to_owned());
685 let args = make_compile_args(config,
687 link_args.append(extra_args),
688 |a, b| ThisFile(make_exe_name(a, b)), testfile);
689 compose_and_run_compiler(config, props, testfile, args, None)
692 fn exec_compiled_test(config: &config, props: &TestProps,
693 testfile: &Path) -> ProcRes {
695 let env = props.exec_env.clone();
697 match config.target.as_slice() {
699 "arm-linux-androideabi" => {
700 _arm_exec_compiled_test(config, props, testfile, env)
704 compose_and_run(config, testfile,
705 make_run_args(config, props, testfile),
707 config.run_lib_path, None)
712 fn compose_and_run_compiler(
717 input: Option<~str>) -> ProcRes {
719 if !props.aux_builds.is_empty() {
720 ensure_dir(&aux_output_dir_name(config, testfile));
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".to_owned(), aux_dir.as_str().unwrap().to_owned());
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 {
733 vec!("--crate-type=dylib".to_owned())
736 make_compile_args(config,
738 crate_type.append(extra_link_args.as_slice()),
740 let f = make_lib_name(a, b, testfile);
741 ThisDirectory(f.dir_path())
743 let auxres = compose_and_run(config, &abs_ab, aux_args, Vec::new(),
744 config.compile_lib_path, None);
745 if !auxres.status.success() {
747 format!("auxiliary build of {} failed to compile: ",
752 match config.target.as_slice() {
754 "arm-linux-androideabi" => {
755 _arm_push_aux_shared_library(config, testfile);
762 compose_and_run(config, testfile, args, Vec::new(),
763 config.compile_lib_path, input)
766 fn ensure_dir(path: &Path) {
767 if path.is_dir() { return; }
768 fs::mkdir(path, io::UserRWX).unwrap();
771 fn compose_and_run(config: &config, testfile: &Path,
772 ProcArgs{ args, prog }: ProcArgs,
773 procenv: Vec<(~str, ~str)> ,
775 input: Option<~str>) -> ProcRes {
776 return program_output(config, testfile, lib_path,
777 prog, args, procenv, input);
780 enum TargetLocation {
785 fn make_compile_args(config: &config,
788 xform: |&config, &Path| -> TargetLocation,
791 let xform_file = xform(config, testfile);
792 let target = if props.force_host {
793 config.host.as_slice()
795 config.target.as_slice()
797 // FIXME (#9639): This needs to handle non-utf8 paths
798 let mut args = vec!(testfile.as_str().unwrap().to_owned(),
799 "-L".to_owned(), config.build_base.as_str().unwrap().to_owned(),
800 "--target=".to_owned() + target);
801 args.push_all(extras.as_slice());
802 if !props.no_prefer_dynamic {
803 args.push("-C".to_owned());
804 args.push("prefer-dynamic".to_owned());
806 let path = match xform_file {
807 ThisFile(path) => { args.push("-o".to_owned()); path }
808 ThisDirectory(path) => { args.push("--out-dir".to_owned()); path }
810 args.push(path.as_str().unwrap().to_owned());
811 if props.force_host {
812 args.push_all_move(split_maybe_args(&config.host_rustcflags));
814 args.push_all_move(split_maybe_args(&config.target_rustcflags));
816 args.push_all_move(split_maybe_args(&props.compile_flags));
817 return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
820 fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path {
821 // what we return here is not particularly important, as it
822 // happens; rustc ignores everything except for the directory.
823 let auxname = output_testname(auxfile);
824 aux_output_dir_name(config, testfile).join(&auxname)
827 fn make_exe_name(config: &config, testfile: &Path) -> Path {
828 let mut f = output_base_name(config, testfile);
829 if !os::consts::EXE_SUFFIX.is_empty() {
830 match f.filename().map(|s| s + os::consts::EXE_SUFFIX.as_bytes()) {
831 Some(v) => f.set_filename(v),
838 fn make_run_args(config: &config, props: &TestProps, testfile: &Path) ->
840 // If we've got another tool to run under (valgrind),
841 // then split apart its command
842 let mut args = split_maybe_args(&config.runtool);
843 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());
848 // Add the arguments in the run_flags directive
849 args.push_all_move(split_maybe_args(&props.run_flags));
851 let prog = args.shift().unwrap();
852 return ProcArgs {prog: prog, args: args};
855 fn split_maybe_args(argstr: &Option<~str>) -> Vec<~str> {
859 .filter_map(|s| if s.is_whitespace() {None} else {Some(s.to_owned())})
866 fn program_output(config: &config, testfile: &Path, lib_path: &str, prog: ~str,
867 args: Vec<~str> , env: Vec<(~str, ~str)> ,
868 input: Option<~str>) -> ProcRes {
871 let cmdline = make_cmdline(lib_path, prog, args.as_slice());
872 logv(config, format!("executing {}", cmdline));
875 let procsrv::Result{ out, err, status } =
876 procsrv::run(lib_path, prog, args.as_slice(), env, input)
877 .expect(format!("failed to exec `{}`", prog));
878 dump_output(config, testfile, out, err);
879 return ProcRes {status: status,
885 // Linux and mac don't require adjusting the library search path
886 #[cfg(target_os = "linux")]
887 #[cfg(target_os = "macos")]
888 #[cfg(target_os = "freebsd")]
889 fn make_cmdline(_libpath: &str, prog: &str, args: &[~str]) -> ~str {
890 format!("{} {}", prog, args.connect(" "))
893 #[cfg(target_os = "win32")]
894 fn make_cmdline(libpath: &str, prog: &str, args: &[~str]) -> ~str {
895 format!("{} {} {}", lib_path_cmd_prefix(libpath), prog,
899 // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
900 // for diagnostic purposes
901 #[cfg(target_os = "win32")]
902 fn lib_path_cmd_prefix(path: &str) -> ~str {
903 format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
906 fn dump_output(config: &config, testfile: &Path, out: &str, err: &str) {
907 dump_output_file(config, testfile, out, "out");
908 dump_output_file(config, testfile, err, "err");
909 maybe_dump_to_stdout(config, out, err);
912 fn dump_output_file(config: &config, testfile: &Path,
913 out: &str, extension: &str) {
914 let outfile = make_out_name(config, testfile, extension);
915 File::create(&outfile).write(out.as_bytes()).unwrap();
918 fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path {
919 output_base_name(config, testfile).with_extension(extension)
922 fn aux_output_dir_name(config: &config, testfile: &Path) -> Path {
923 let mut f = output_base_name(config, testfile);
924 match f.filename().map(|s| s + bytes!(".libaux")) {
925 Some(v) => f.set_filename(v),
931 fn output_testname(testfile: &Path) -> Path {
932 Path::new(testfile.filestem().unwrap())
935 fn output_base_name(config: &config, testfile: &Path) -> Path {
937 .join(&output_testname(testfile))
938 .with_extension(config.stage_id.as_slice())
941 fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) {
943 println!("------{}------------------------------", "stdout");
945 println!("------{}------------------------------", "stderr");
947 println!("------------------------------------------");
951 fn error(err: ~str) { println!("\nerror: {}", err); }
953 fn fatal(err: ~str) -> ! { error(err); fail!(); }
955 fn fatal_ProcRes(err: ~str, proc_res: &ProcRes) -> ! {
961 ------------------------------------------\n\
963 ------------------------------------------\n\
965 ------------------------------------------\n\
967 ------------------------------------------\n\
969 err, proc_res.status, proc_res.cmdline, proc_res.stdout,
974 fn _arm_exec_compiled_test(config: &config, props: &TestProps,
975 testfile: &Path, env: Vec<(~str, ~str)> ) -> ProcRes {
977 let args = make_run_args(config, props, testfile);
978 let cmdline = make_cmdline("", args.prog, args.args.as_slice());
980 // get bare program string
981 let mut tvec: Vec<~str> = args.prog.split('/').map(|ts| ts.to_owned()).collect();
982 let prog_short = tvec.pop().unwrap();
985 let copy_result = procsrv::run("", config.adb_path,
986 ["push".to_owned(), args.prog.clone(), config.adb_test_dir.clone()],
987 vec!(("".to_owned(),"".to_owned())), Some("".to_owned()))
988 .expect(format!("failed to exec `{}`", config.adb_path));
991 println!("push ({}) {} {} {}",
992 config.target, args.prog,
993 copy_result.out, copy_result.err);
996 logv(config, format!("executing ({}) {}", config.target, cmdline));
998 let mut runargs = Vec::new();
1000 // run test via adb_run_wrapper
1001 runargs.push("shell".to_owned());
1002 for (key, val) in env.move_iter() {
1003 runargs.push(format!("{}={}", key, val));
1005 runargs.push(format!("{}/adb_run_wrapper.sh", config.adb_test_dir));
1006 runargs.push(format!("{}", config.adb_test_dir));
1007 runargs.push(format!("{}", prog_short));
1009 for tv in args.args.iter() {
1010 runargs.push(tv.to_owned());
1015 vec!(("".to_owned(),"".to_owned())), Some("".to_owned()))
1016 .expect(format!("failed to exec `{}`", config.adb_path));
1018 // get exitcode of result
1019 runargs = Vec::new();
1020 runargs.push("shell".to_owned());
1021 runargs.push("cat".to_owned());
1022 runargs.push(format!("{}/{}.exitcode", config.adb_test_dir, prog_short));
1024 let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
1025 procsrv::run("", config.adb_path, runargs.as_slice(), vec!(("".to_owned(),"".to_owned())),
1026 Some("".to_owned()))
1027 .expect(format!("failed to exec `{}`", config.adb_path));
1029 let mut exitcode : int = 0;
1030 for c in exitcode_out.chars() {
1031 if !c.is_digit() { break; }
1032 exitcode = exitcode * 10 + match c {
1033 '0' .. '9' => c as int - ('0' as int),
1038 // get stdout of result
1039 runargs = Vec::new();
1040 runargs.push("shell".to_owned());
1041 runargs.push("cat".to_owned());
1042 runargs.push(format!("{}/{}.stdout", config.adb_test_dir, prog_short));
1044 let procsrv::Result{ out: stdout_out, err: _, status: _ } =
1048 vec!(("".to_owned(),"".to_owned())), Some("".to_owned()))
1049 .expect(format!("failed to exec `{}`", config.adb_path));
1051 // get stderr of result
1052 runargs = Vec::new();
1053 runargs.push("shell".to_owned());
1054 runargs.push("cat".to_owned());
1055 runargs.push(format!("{}/{}.stderr", config.adb_test_dir, prog_short));
1057 let procsrv::Result{ out: stderr_out, err: _, status: _ } =
1061 vec!(("".to_owned(),"".to_owned())), Some("".to_owned()))
1062 .expect(format!("failed to exec `{}`", config.adb_path));
1064 dump_output(config, testfile, stdout_out, stderr_out);
1067 status: process::ExitStatus(exitcode),
1074 fn _arm_push_aux_shared_library(config: &config, testfile: &Path) {
1075 let tdir = aux_output_dir_name(config, testfile);
1077 let dirs = fs::readdir(&tdir).unwrap();
1078 for file in dirs.iter() {
1079 if file.extension_str() == Some("so") {
1080 // FIXME (#9639): This needs to handle non-utf8 paths
1081 let copy_result = procsrv::run("", config.adb_path,
1082 ["push".to_owned(), file.as_str().unwrap().to_owned(), config.adb_test_dir.clone()],
1083 vec!(("".to_owned(),"".to_owned())), Some("".to_owned()))
1084 .expect(format!("failed to exec `{}`", config.adb_path));
1087 println!("push ({}) {} {} {}",
1088 config.target, file.display(),
1089 copy_result.out, copy_result.err);
1095 // codegen tests (vs. clang)
1097 fn make_o_name(config: &config, testfile: &Path) -> Path {
1098 output_base_name(config, testfile).with_extension("o")
1101 fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path {
1102 if suffix.len() == 0 {
1105 let stem = p.filestem().unwrap();
1106 p.with_filename(stem + bytes!("-") + suffix.as_bytes())
1110 fn compile_test_and_save_bitcode(config: &config, props: &TestProps,
1111 testfile: &Path) -> ProcRes {
1112 let aux_dir = aux_output_dir_name(config, testfile);
1113 // FIXME (#9639): This needs to handle non-utf8 paths
1114 let link_args = vec!("-L".to_owned(), aux_dir.as_str().unwrap().to_owned());
1115 let llvm_args = vec!("--emit=obj".to_owned(), "--crate-type=lib".to_owned(),
1116 "-C".to_owned(), "save-temps".to_owned());
1117 let args = make_compile_args(config,
1119 link_args.append(llvm_args.as_slice()),
1120 |a, b| ThisFile(make_o_name(a, b)), testfile);
1121 compose_and_run_compiler(config, props, testfile, args, None)
1124 fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps,
1125 testfile: &Path) -> ProcRes {
1126 let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1127 let bitcodefile = append_suffix_to_stem(&bitcodefile, "clang");
1128 let testcc = testfile.with_extension("cc");
1129 let proc_args = ProcArgs {
1130 // FIXME (#9639): This needs to handle non-utf8 paths
1131 prog: config.clang_path.get_ref().as_str().unwrap().to_owned(),
1132 args: vec!("-c".to_owned(),
1133 "-emit-llvm".to_owned(),
1134 "-o".to_owned(), bitcodefile.as_str().unwrap().to_owned(),
1135 testcc.as_str().unwrap().to_owned() )
1137 compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
1140 fn extract_function_from_bitcode(config: &config, _props: &TestProps,
1141 fname: &str, testfile: &Path,
1142 suffix: &str) -> ProcRes {
1143 let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1144 let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
1145 let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
1146 let prog = config.llvm_bin_path.get_ref().join("llvm-extract");
1147 let proc_args = ProcArgs {
1148 // FIXME (#9639): This needs to handle non-utf8 paths
1149 prog: prog.as_str().unwrap().to_owned(),
1150 args: vec!("-func=" + fname,
1151 "-o=" + extracted_bc.as_str().unwrap(),
1152 bitcodefile.as_str().unwrap().to_owned() )
1154 compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
1157 fn disassemble_extract(config: &config, _props: &TestProps,
1158 testfile: &Path, suffix: &str) -> ProcRes {
1159 let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1160 let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
1161 let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
1162 let extracted_ll = extracted_bc.with_extension("ll");
1163 let prog = config.llvm_bin_path.get_ref().join("llvm-dis");
1164 let proc_args = ProcArgs {
1165 // FIXME (#9639): This needs to handle non-utf8 paths
1166 prog: prog.as_str().unwrap().to_owned(),
1167 args: vec!("-o=" + extracted_ll.as_str().unwrap(),
1168 extracted_bc.as_str().unwrap().to_owned() )
1170 compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
1174 fn count_extracted_lines(p: &Path) -> uint {
1175 let x = File::open(&p.with_extension("ll")).read_to_end().unwrap();
1176 let x = str::from_utf8(x.as_slice()).unwrap();
1181 fn run_codegen_test(config: &config, props: &TestProps,
1182 testfile: &Path, mm: &mut MetricMap) {
1184 if config.llvm_bin_path.is_none() {
1185 fatal("missing --llvm-bin-path".to_owned());
1188 if config.clang_path.is_none() {
1189 fatal("missing --clang-path".to_owned());
1192 let mut proc_res = compile_test_and_save_bitcode(config, props, testfile);
1193 if !proc_res.status.success() {
1194 fatal_ProcRes("compilation failed!".to_owned(), &proc_res);
1197 proc_res = extract_function_from_bitcode(config, props, "test", testfile, "");
1198 if !proc_res.status.success() {
1199 fatal_ProcRes("extracting 'test' function failed".to_owned(), &proc_res);
1202 proc_res = disassemble_extract(config, props, testfile, "");
1203 if !proc_res.status.success() {
1204 fatal_ProcRes("disassembling extract failed".to_owned(), &proc_res);
1208 let mut proc_res = compile_cc_with_clang_and_save_bitcode(config, props, testfile);
1209 if !proc_res.status.success() {
1210 fatal_ProcRes("compilation failed!".to_owned(), &proc_res);
1213 proc_res = extract_function_from_bitcode(config, props, "test", testfile, "clang");
1214 if !proc_res.status.success() {
1215 fatal_ProcRes("extracting 'test' function failed".to_owned(), &proc_res);
1218 proc_res = disassemble_extract(config, props, testfile, "clang");
1219 if !proc_res.status.success() {
1220 fatal_ProcRes("disassembling extract failed".to_owned(), &proc_res);
1223 let base = output_base_name(config, testfile);
1224 let base_extract = append_suffix_to_stem(&base, "extract");
1226 let base_clang = append_suffix_to_stem(&base, "clang");
1227 let base_clang_extract = append_suffix_to_stem(&base_clang, "extract");
1229 let base_lines = count_extracted_lines(&base_extract);
1230 let clang_lines = count_extracted_lines(&base_clang_extract);
1232 mm.insert_metric("clang-codegen-ratio",
1233 (base_lines as f64) / (clang_lines as f64),