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;
40 pub fn run(config: config, testfile: ~str) {
42 match config.target.as_slice() {
44 "arm-linux-androideabi" => {
45 if !config.adb_device_status {
46 fail!("android device not available");
53 let mut _mm = MetricMap::new();
54 run_metrics(config, testfile, &mut _mm);
57 pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) {
59 // We're going to be dumping a lot of info. Start on a new line.
62 let testfile = Path::new(testfile);
63 debug!("running {}", testfile.display());
64 let props = load_props(&testfile);
65 debug!("loaded props");
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)
76 fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) {
77 let proc_res = compile_test(config, props, testfile);
79 if proc_res.status.success() {
80 fatal_ProcRes(~"compile-fail test compiled successfully!", &proc_res);
83 check_correct_failure_status(&proc_res);
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");
90 check_expected_errors(expected_errors, testfile, &proc_res);
92 check_error_patterns(props, testfile, &proc_res);
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);
100 if !proc_res.status.success() {
101 fatal_ProcRes(~"compilation failed!", &proc_res);
104 exec_compiled_test(config, props, testfile)
106 jit_test(config, props, testfile)
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);
115 check_correct_failure_status(&proc_res);
116 check_error_patterns(props, testfile, &proc_res);
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) {
124 format!("failure produced the wrong error: {}", proc_res.status),
129 fn run_rpass_test(config: &config, props: &TestProps, testfile: &Path) {
131 let mut proc_res = compile_test(config, props, testfile);
133 if !proc_res.status.success() {
134 fatal_ProcRes(~"compilation failed!", &proc_res);
137 proc_res = exec_compiled_test(config, props, testfile);
139 if !proc_res.status.success() {
140 fatal_ProcRes(~"test run failed!", &proc_res);
143 let proc_res = jit_test(config, props, testfile);
145 if !proc_res.status.success() { fatal_ProcRes(~"jit failed!", &proc_res); }
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"); }
155 match props.pp_exact { Some(_) => 1, None => 2 };
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);
162 while round < rounds {
163 logv(config, format!("pretty-printing round {}", round));
164 let proc_res = print_source(config,
166 (*srcs.get(round)).clone());
168 if !proc_res.status.success() {
169 fatal_ProcRes(format!("pretty-printing failed in round {}", round),
173 let ProcRes{ stdout, .. } = proc_res;
178 let mut expected = match props.pp_exact {
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()
184 None => { (*srcs.get(srcs.len() - 2u)).clone() }
186 let mut actual = (*srcs.get(srcs.len() - 1u)).clone();
188 if props.pp_exact.is_some() {
189 // Now we have to care about line endings
191 actual = actual.replace(cr, "");
192 expected = expected.replace(cr, "");
195 compare_source(expected, actual);
197 // Finally, let's make sure it actually appears to remain valid code
198 let proc_res = typecheck_source(config, props, testfile, actual);
200 if !proc_res.status.success() {
201 fatal_ProcRes(~"pretty-printed source does not typecheck", &proc_res);
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))
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};
218 fn compare_source(expected: &str, actual: &str) {
219 if expected != actual {
220 error(~"pretty-printed source does not match expected source");
223 ------------------------------------------\n\
225 ------------------------------------------\n\
227 ------------------------------------------\n\
229 ------------------------------------------\n\
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))
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()
247 config.target.as_slice()
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(),
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};
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),
270 let config = &mut config;
271 let check_lines = &props.check_lines;
272 let mut cmds = props.debugger_cmds.connect("\n");
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);
280 let exe_file = make_exe_name(config, testfile);
283 match config.target.as_slice() {
284 "arm-linux-androideabi" => {
286 cmds = cmds.replace("run","continue");
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",
293 ~"quit"].connect("\n");
294 debug!("script_str = {}", script_str);
295 dump_output_file(config, testfile, script_str, "debugger.script");
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));
304 procsrv::run("", config.adb_path,
305 [~"forward", ~"tcp:5039", ~"tcp:5039"],
306 vec!((~"",~"")), Some(~""))
307 .expect(format!("failed to exec `{}`", config.adb_path));
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());
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));
318 //waiting 1 second for gdbserver start
320 let result = task::try(proc() {
321 tcp::TcpStream::connect(SocketAddr {
322 ip: Ipv4Addr(127, 0, 0, 1),
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=","");
341 if tool_path.equals(&~"") {
342 fatal(~"cannot found android cross path");
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());
350 let gdb_path = tool_path.append("/bin/arm-linux-androideabi-gdb");
351 let procsrv::Result{ out, err, status }=
354 debugger_opts.as_slice(),
357 .expect(format!("failed to exec `{}`", gdb_path));
359 let cmdline = make_cmdline("",
360 "arm-linux-androideabi-gdb",
361 debugger_opts.as_slice());
362 logv(config, format!("executing {}", cmdline));
366 proc_res = ProcRes {status: status,
370 process.signal_kill().unwrap();
374 // write debugger script
375 let script_str = [~"set charset UTF-8",
377 ~"quit\n"].connect("\n");
378 debug!("script_str = {}", script_str);
379 dump_output_file(config, testfile, script_str, "debugger.script");
381 // run debugger script with gdb
383 fn debugger() -> ~str { ~"gdb.exe" }
385 fn debugger() -> ~str { ~"gdb" }
387 let debugger_script = make_out_name(config, testfile, "debugger.script");
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);
398 if !proc_res.status.success() {
399 fatal(~"gdb failed to execute");
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()
409 // check if each line in props.check_lines appears in the
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 }
428 rest = rest.slice_from(i + frag.len());
433 if !failed && rest.len() == 0 {
436 if i == num_check_lines {
441 if i != num_check_lines {
442 fatal_ProcRes(format!("line not found in debugger output: {}",
443 *check_lines.get(i)), &proc_res);
447 fn cleanup_debug_info_options(options: &Option<~str>) -> Option<~str> {
448 if options.is_none() {
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))
462 fn check_error_patterns(props: &TestProps,
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());
469 if proc_res.status.success() {
470 fatal(~"process did not return an error status");
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", 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 = slice::from_elem(
514 expected_errors.len(), false);
516 if proc_res.status.success() {
517 fatal(~"process did not return an error status");
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()
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() {
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;
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"])
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,
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)
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", 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")
736 make_compile_args(config,
738 vec::append(crate_type,
739 extra_link_args.as_slice()),
741 let f = make_lib_name(a, b, testfile);
742 ThisDirectory(f.dir_path())
744 let auxres = compose_and_run(config, &abs_ab, aux_args, Vec::new(),
745 config.compile_lib_path, None);
746 if !auxres.status.success() {
748 format!("auxiliary build of {} failed to compile: ",
753 match config.target.as_slice() {
755 "arm-linux-androideabi" => {
756 _arm_push_aux_shared_library(config, testfile);
763 compose_and_run(config, testfile, args, Vec::new(),
764 config.compile_lib_path, input)
767 fn ensure_dir(path: &Path) {
768 if path.is_dir() { return; }
769 fs::mkdir(path, io::UserRWX).unwrap();
772 fn compose_and_run(config: &config, testfile: &Path,
773 ProcArgs{ args, prog }: ProcArgs,
774 procenv: Vec<(~str, ~str)> ,
776 input: Option<~str>) -> ProcRes {
777 return program_output(config, testfile, lib_path,
778 prog, args, procenv, input);
781 enum TargetLocation {
786 fn make_compile_args(config: &config,
789 xform: |&config, &Path| -> TargetLocation,
792 let xform_file = xform(config, testfile);
793 let target = if props.force_host {
794 config.host.as_slice()
796 config.target.as_slice()
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 {
805 args.push(~"prefer-dynamic");
807 let path = match xform_file {
808 ThisFile(path) => { args.push(~"-o"); path }
809 ThisDirectory(path) => { args.push(~"--out-dir"); path }
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));
815 args.push_all_move(split_maybe_args(&config.target_rustcflags));
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};
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)
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),
839 fn make_run_args(config: &config, _props: &TestProps, testfile: &Path) ->
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};
851 fn split_maybe_args(argstr: &Option<~str>) -> Vec<~str> {
855 .filter_map(|s| if s.is_whitespace() {None} else {Some(s.to_owned())})
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 {
867 let cmdline = make_cmdline(lib_path, prog, args.as_slice());
868 logv(config, format!("executing {}", cmdline));
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,
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(" "))
889 #[cfg(target_os = "win32")]
890 fn make_cmdline(libpath: &str, prog: &str, args: &[~str]) -> ~str {
891 format!("{} {} {}", lib_path_cmd_prefix(libpath), prog,
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))
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);
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();
914 fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path {
915 output_base_name(config, testfile).with_extension(extension)
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),
927 fn output_testname(testfile: &Path) -> Path {
928 Path::new(testfile.filestem().unwrap())
931 fn output_base_name(config: &config, testfile: &Path) -> Path {
933 .join(&output_testname(testfile))
934 .with_extension(config.stage_id.as_slice())
937 fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) {
939 println!("------{}------------------------------", "stdout");
941 println!("------{}------------------------------", "stderr");
943 println!("------------------------------------------");
947 fn error(err: ~str) { println!("\nerror: {}", err); }
949 fn fatal(err: ~str) -> ! { error(err); fail!(); }
951 fn fatal_ProcRes(err: ~str, proc_res: &ProcRes) -> ! {
956 ------------------------------------------\n\
958 ------------------------------------------\n\
960 ------------------------------------------\n\
962 ------------------------------------------\n\
964 err, proc_res.cmdline, proc_res.stdout, proc_res.stderr);
968 fn _arm_exec_compiled_test(config: &config, props: &TestProps,
969 testfile: &Path, env: Vec<(~str, ~str)> ) -> ProcRes {
971 let args = make_run_args(config, props, testfile);
972 let cmdline = make_cmdline("", args.prog, args.args.as_slice());
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();
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));
985 println!("push ({}) {} {} {}",
986 config.target, args.prog,
987 copy_result.out, copy_result.err);
990 logv(config, format!("executing ({}) {}", config.target, cmdline));
992 let mut runargs = Vec::new();
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));
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));
1003 for tv in args.args.iter() {
1004 runargs.push(tv.to_owned());
1009 vec!((~"",~"")), Some(~""))
1010 .expect(format!("failed to exec `{}`", config.adb_path));
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));
1018 let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
1019 procsrv::run("", config.adb_path, runargs.as_slice(), vec!((~"",~"")),
1021 .expect(format!("failed to exec `{}`", config.adb_path));
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),
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));
1038 let procsrv::Result{ out: stdout_out, err: _, status: _ } =
1042 vec!((~"",~"")), Some(~""))
1043 .expect(format!("failed to exec `{}`", config.adb_path));
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));
1051 let procsrv::Result{ out: stderr_out, err: _, status: _ } =
1055 vec!((~"",~"")), Some(~""))
1056 .expect(format!("failed to exec `{}`", config.adb_path));
1058 dump_output(config, testfile, stdout_out, stderr_out);
1061 status: process::ExitStatus(exitcode),
1068 fn _arm_push_aux_shared_library(config: &config, testfile: &Path) {
1069 let tdir = aux_output_dir_name(config, testfile);
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));
1081 println!("push ({}) {} {} {}",
1082 config.target, file.display(),
1083 copy_result.out, copy_result.err);
1089 // codegen tests (vs. clang)
1091 fn make_o_name(config: &config, testfile: &Path) -> Path {
1092 output_base_name(config, testfile).with_extension("o")
1095 fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path {
1096 if suffix.len() == 0 {
1099 let stem = p.filestem().unwrap();
1100 p.with_filename(stem + bytes!("-") + suffix.as_bytes())
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,
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)
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(),
1128 ~"-o", bitcodefile.as_str().unwrap().to_owned(),
1129 testcc.as_str().unwrap().to_owned() )
1131 compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
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() )
1148 compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
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() )
1164 compose_and_run(config, testfile, proc_args, Vec::new(), "", None)
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();
1175 fn run_codegen_test(config: &config, props: &TestProps,
1176 testfile: &Path, mm: &mut MetricMap) {
1178 if config.llvm_bin_path.is_none() {
1179 fatal(~"missing --llvm-bin-path");
1182 if config.clang_path.is_none() {
1183 fatal(~"missing --clang-path");
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);
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);
1196 proc_res = disassemble_extract(config, props, testfile, "");
1197 if !proc_res.status.success() {
1198 fatal_ProcRes(~"disassembling extract failed", &proc_res);
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);
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);
1212 proc_res = disassemble_extract(config, props, testfile, "clang");
1213 if !proc_res.status.success() {
1214 fatal_ProcRes(~"disassembling extract failed", &proc_res);
1217 let base = output_base_name(config, testfile);
1218 let base_extract = append_suffix_to_stem(&base, "extract");
1220 let base_clang = append_suffix_to_stem(&base, "clang");
1221 let base_clang_extract = append_suffix_to_stem(&base_clang, "extract");
1223 let base_lines = count_extracted_lines(&base_extract);
1224 let clang_lines = count_extracted_lines(&base_clang_extract);
1226 mm.insert_metric("clang-codegen-ratio",
1227 (base_lines as f64) / (clang_lines as f64),