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;
39 pub fn run(config: config, testfile: ~str) {
41 match config.target.as_slice() {
43 "arm-linux-androideabi" => {
44 if !config.adb_device_status {
45 fail!("android device not available");
52 let mut _mm = MetricMap::new();
53 run_metrics(config, testfile, &mut _mm);
56 pub fn run_metrics(config: config, testfile: ~str, mm: &mut MetricMap) {
58 // We're going to be dumping a lot of info. Start on a new line.
61 let testfile = Path::new(testfile);
62 debug!("running {}", testfile.display());
63 let props = load_props(&testfile);
64 debug!("loaded props");
66 mode_compile_fail => run_cfail_test(&config, &props, &testfile),
67 mode_run_fail => run_rfail_test(&config, &props, &testfile),
68 mode_run_pass => run_rpass_test(&config, &props, &testfile),
69 mode_pretty => run_pretty_test(&config, &props, &testfile),
70 mode_debug_info => run_debuginfo_test(&config, &props, &testfile),
71 mode_codegen => run_codegen_test(&config, &props, &testfile, mm)
75 fn run_cfail_test(config: &config, props: &TestProps, testfile: &Path) {
76 let proc_res = compile_test(config, props, testfile);
78 if proc_res.status.success() {
79 fatal_ProcRes(~"compile-fail test compiled successfully!", &proc_res);
82 check_correct_failure_status(&proc_res);
84 let expected_errors = errors::load_errors(testfile);
85 if !expected_errors.is_empty() {
86 if !props.error_patterns.is_empty() {
87 fatal(~"both error pattern and expected errors specified");
89 check_expected_errors(expected_errors, testfile, &proc_res);
91 check_error_patterns(props, testfile, &proc_res);
95 fn run_rfail_test(config: &config, props: &TestProps, testfile: &Path) {
96 let proc_res = if !config.jit {
97 let proc_res = compile_test(config, props, testfile);
99 if !proc_res.status.success() {
100 fatal_ProcRes(~"compilation failed!", &proc_res);
103 exec_compiled_test(config, props, testfile)
105 jit_test(config, props, testfile)
108 // The value our Makefile configures valgrind to return on failure
109 static VALGRIND_ERR: int = 100;
110 if proc_res.status.matches_exit_status(VALGRIND_ERR) {
111 fatal_ProcRes(~"run-fail test isn't valgrind-clean!", &proc_res);
114 check_correct_failure_status(&proc_res);
115 check_error_patterns(props, testfile, &proc_res);
118 fn check_correct_failure_status(proc_res: &ProcRes) {
119 // The value the rust runtime returns on failure
120 static RUST_ERR: int = 101;
121 if !proc_res.status.matches_exit_status(RUST_ERR) {
123 format!("failure produced the wrong error: {}", proc_res.status),
128 fn run_rpass_test(config: &config, props: &TestProps, testfile: &Path) {
130 let mut proc_res = compile_test(config, props, testfile);
132 if !proc_res.status.success() {
133 fatal_ProcRes(~"compilation failed!", &proc_res);
136 proc_res = exec_compiled_test(config, props, testfile);
138 if !proc_res.status.success() {
139 fatal_ProcRes(~"test run failed!", &proc_res);
142 let proc_res = jit_test(config, props, testfile);
144 if !proc_res.status.success() { fatal_ProcRes(~"jit failed!", &proc_res); }
148 fn run_pretty_test(config: &config, props: &TestProps, testfile: &Path) {
149 if props.pp_exact.is_some() {
150 logv(config, ~"testing for exact pretty-printing");
151 } else { logv(config, ~"testing for converging pretty-printing"); }
154 match props.pp_exact { Some(_) => 1, None => 2 };
156 let src = File::open(testfile).read_to_end().unwrap();
157 let src = str::from_utf8_owned(src).unwrap();
158 let mut srcs = ~[src];
161 while round < rounds {
162 logv(config, format!("pretty-printing round {}", round));
163 let proc_res = print_source(config, testfile, srcs[round].clone());
165 if !proc_res.status.success() {
166 fatal_ProcRes(format!("pretty-printing failed in round {}", round),
170 let ProcRes{ stdout, .. } = proc_res;
175 let mut expected = match props.pp_exact {
177 let filepath = testfile.dir_path().join(file);
178 let s = File::open(&filepath).read_to_end().unwrap();
179 str::from_utf8_owned(s).unwrap()
181 None => { srcs[srcs.len() - 2u].clone() }
183 let mut actual = srcs[srcs.len() - 1u].clone();
185 if props.pp_exact.is_some() {
186 // Now we have to care about line endings
188 actual = actual.replace(cr, "");
189 expected = expected.replace(cr, "");
192 compare_source(expected, actual);
194 // Finally, let's make sure it actually appears to remain valid code
195 let proc_res = typecheck_source(config, props, testfile, actual);
197 if !proc_res.status.success() {
198 fatal_ProcRes(~"pretty-printed source does not typecheck", &proc_res);
203 fn print_source(config: &config, testfile: &Path, src: ~str) -> ProcRes {
204 compose_and_run(config, testfile, make_pp_args(config, testfile),
205 ~[], config.compile_lib_path, Some(src))
208 fn make_pp_args(config: &config, _testfile: &Path) -> ProcArgs {
209 let args = ~[~"-", ~"--pretty", ~"normal",
210 ~"--target=" + config.target];
211 // FIXME (#9639): This needs to handle non-utf8 paths
212 return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
215 fn compare_source(expected: &str, actual: &str) {
216 if expected != actual {
217 error(~"pretty-printed source does not match expected source");
220 ------------------------------------------\n\
222 ------------------------------------------\n\
224 ------------------------------------------\n\
226 ------------------------------------------\n\
233 fn typecheck_source(config: &config, props: &TestProps,
234 testfile: &Path, src: ~str) -> ProcRes {
235 let args = make_typecheck_args(config, props, testfile);
236 compose_and_run_compiler(config, props, testfile, args, Some(src))
239 fn make_typecheck_args(config: &config, props: &TestProps, testfile: &Path) -> ProcArgs {
240 let aux_dir = aux_output_dir_name(config, testfile);
241 let target = if props.force_host {
242 config.host.as_slice()
244 config.target.as_slice()
246 // FIXME (#9639): This needs to handle non-utf8 paths
247 let mut args = ~[~"-",
248 ~"--no-trans", ~"--crate-type=lib",
249 ~"--target=" + target,
250 ~"-L", config.build_base.as_str().unwrap().to_owned(),
252 aux_dir.as_str().unwrap().to_owned()];
253 args.push_all_move(split_maybe_args(&config.target_rustcflags));
254 args.push_all_move(split_maybe_args(&props.compile_flags));
255 // FIXME (#9639): This needs to handle non-utf8 paths
256 return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
260 fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
261 let mut config = config {
262 target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
263 host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
267 let config = &mut config;
268 let check_lines = &props.check_lines;
269 let mut cmds = props.debugger_cmds.connect("\n");
271 // compile test file (it shoud have 'compile-flags:-g' in the header)
272 let mut proc_res = compile_test(config, props, testfile);
273 if !proc_res.status.success() {
274 fatal_ProcRes(~"compilation failed!", &proc_res);
277 let exe_file = make_exe_name(config, testfile);
280 match config.target.as_slice() {
281 "arm-linux-androideabi" => {
283 cmds = cmds.replace("run","continue");
285 // write debugger script
286 let script_str = [~"set charset UTF-8",
287 format!("file {}",exe_file.as_str().unwrap().to_owned()),
288 ~"target remote :5039",
290 ~"quit"].connect("\n");
291 debug!("script_str = {}", script_str);
292 dump_output_file(config, testfile, script_str, "debugger.script");
295 procsrv::run("", config.adb_path,
296 [~"push", exe_file.as_str().unwrap().to_owned(),
297 config.adb_test_dir.clone()],
298 ~[(~"",~"")], Some(~""))
299 .expect(format!("failed to exec `{}`", config.adb_path));
301 procsrv::run("", config.adb_path,
302 [~"forward", ~"tcp:5039", ~"tcp:5039"],
303 ~[(~"",~"")], Some(~""))
304 .expect(format!("failed to exec `{}`", config.adb_path));
306 let adb_arg = format!("export LD_LIBRARY_PATH={}; gdbserver :5039 {}/{}",
307 config.adb_test_dir.clone(), config.adb_test_dir.clone(),
308 str::from_utf8(exe_file.filename().unwrap()).unwrap());
310 let mut process = procsrv::run_background("", config.adb_path,
311 [~"shell",adb_arg.clone()],
312 ~[(~"",~"")], Some(~""))
313 .expect(format!("failed to exec `{}`", config.adb_path));
315 //waiting 1 second for gdbserver start
317 let result = task::try(proc() {
318 tcp::TcpStream::connect(SocketAddr {
319 ip: Ipv4Addr(127, 0, 0, 1),
329 let args = split_maybe_args(&config.target_rustcflags);
330 let mut tool_path:~str = ~"";
331 for arg in args.iter() {
332 if arg.contains("android-cross-path=") {
333 tool_path = arg.replace("android-cross-path=","");
338 if tool_path.equals(&~"") {
339 fatal(~"cannot found android cross path");
342 let debugger_script = make_out_name(config, testfile, "debugger.script");
343 // FIXME (#9639): This needs to handle non-utf8 paths
344 let debugger_opts = ~[~"-quiet", ~"-batch", ~"-nx",
345 "-command=" + debugger_script.as_str().unwrap().to_owned()];
347 let gdb_path = tool_path.append("/bin/arm-linux-androideabi-gdb");
348 let procsrv::Result{ out, err, status }=
351 debugger_opts, ~[(~"",~"")], None)
352 .expect(format!("failed to exec `{}`", gdb_path));
354 let cmdline = make_cmdline("", "arm-linux-androideabi-gdb", debugger_opts);
355 logv(config, format!("executing {}", cmdline));
359 proc_res = ProcRes {status: status,
363 process.signal_kill().unwrap();
367 // write debugger script
368 let script_str = [~"set charset UTF-8",
370 ~"quit\n"].connect("\n");
371 debug!("script_str = {}", script_str);
372 dump_output_file(config, testfile, script_str, "debugger.script");
374 // run debugger script with gdb
376 fn debugger() -> ~str { ~"gdb.exe" }
378 fn debugger() -> ~str { ~"gdb" }
380 let debugger_script = make_out_name(config, testfile, "debugger.script");
382 // FIXME (#9639): This needs to handle non-utf8 paths
383 let debugger_opts = ~[~"-quiet", ~"-batch", ~"-nx",
384 "-command=" + debugger_script.as_str().unwrap().to_owned(),
385 exe_file.as_str().unwrap().to_owned()];
386 proc_args = ProcArgs {prog: debugger(), args: debugger_opts};
387 proc_res = compose_and_run(config, testfile, proc_args, ~[], "", None);
391 if !proc_res.status.success() {
392 fatal(~"gdb failed to execute");
394 let num_check_lines = check_lines.len();
395 if num_check_lines > 0 {
396 // Allow check lines to leave parts unspecified (e.g., uninitialized
397 // bits in the wrong case of an enum) with the notation "[...]".
398 let check_fragments: ~[~[&str]] = check_lines.map(|s| s.split_str("[...]").collect());
399 // check if each line in props.check_lines appears in the
402 for line in proc_res.stdout.lines() {
403 let mut rest = line.trim();
404 let mut first = true;
405 let mut failed = false;
406 for &frag in check_fragments[i].iter() {
407 let found = if first {
408 if rest.starts_with(frag) { Some(0) } else { None }
418 rest = rest.slice_from(i + frag.len());
423 if !failed && rest.len() == 0 {
426 if i == num_check_lines {
431 if i != num_check_lines {
432 fatal_ProcRes(format!("line not found in debugger output: {}",
433 check_lines[i]), &proc_res);
437 fn cleanup_debug_info_options(options: &Option<~str>) -> Option<~str> {
438 if options.is_none() {
442 // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
443 let options_to_remove = [~"-O", ~"-g", ~"--debuginfo"];
444 let new_options = split_maybe_args(options).move_iter()
445 .filter(|x| !options_to_remove.contains(x))
452 fn check_error_patterns(props: &TestProps,
454 proc_res: &ProcRes) {
455 if props.error_patterns.is_empty() {
456 fatal(~"no error pattern specified in " + testfile.display().as_maybe_owned().as_slice());
459 if proc_res.status.success() {
460 fatal(~"process did not return an error status");
463 let mut next_err_idx = 0u;
464 let mut next_err_pat = &props.error_patterns[next_err_idx];
465 let mut done = false;
466 let output_to_check = if props.check_stdout {
467 proc_res.stdout + proc_res.stderr
469 proc_res.stderr.clone()
471 for line in output_to_check.lines() {
472 if line.contains(*next_err_pat) {
473 debug!("found error pattern {}", *next_err_pat);
475 if next_err_idx == props.error_patterns.len() {
476 debug!("found all error patterns");
480 next_err_pat = &props.error_patterns[next_err_idx];
485 let missing_patterns =
486 props.error_patterns.slice(next_err_idx, props.error_patterns.len());
487 if missing_patterns.len() == 1u {
488 fatal_ProcRes(format!("error pattern '{}' not found!",
489 missing_patterns[0]), proc_res);
491 for pattern in missing_patterns.iter() {
492 error(format!("error pattern '{}' not found!", *pattern));
494 fatal_ProcRes(~"multiple error patterns not found", proc_res);
498 fn check_expected_errors(expected_errors: ~[errors::ExpectedError],
500 proc_res: &ProcRes) {
502 // true if we found the error in question
503 let mut found_flags = slice::from_elem(
504 expected_errors.len(), false);
506 if proc_res.status.success() {
507 fatal(~"process did not return an error status");
510 let prefixes = expected_errors.iter().map(|ee| {
511 format!("{}:{}:", testfile.display(), ee.line)
512 }).collect::<~[~str]>();
514 #[cfg(target_os = "win32")]
515 fn to_lower( s : &str ) -> ~str {
517 let c : ~[char] = i.map( |c| {
519 c.to_ascii().to_lower().to_char()
527 #[cfg(target_os = "win32")]
528 fn prefix_matches( line : &str, prefix : &str ) -> bool {
529 to_lower(line).starts_with( to_lower(prefix) )
532 #[cfg(target_os = "linux")]
533 #[cfg(target_os = "macos")]
534 #[cfg(target_os = "freebsd")]
535 fn prefix_matches( line : &str, prefix : &str ) -> bool {
536 line.starts_with( prefix )
539 // Scan and extract our error/warning messages,
541 // filename:line1:col1: line2:col2: *error:* msg
542 // filename:line1:col1: line2:col2: *warning:* msg
543 // where line1:col1: is the starting point, line2:col2:
544 // is the ending point, and * represents ANSI color codes.
545 for line in proc_res.stderr.lines() {
546 let mut was_expected = false;
547 for (i, ee) in expected_errors.iter().enumerate() {
549 debug!("prefix={} ee.kind={} ee.msg={} line={}",
550 prefixes[i], ee.kind, ee.msg, line);
551 if prefix_matches(line, prefixes[i]) &&
552 line.contains(ee.kind) &&
553 line.contains(ee.msg) {
554 found_flags[i] = true;
561 // ignore this msg which gets printed at the end
562 if line.contains("aborting due to") {
566 if !was_expected && is_compiler_error_or_warning(line) {
567 fatal_ProcRes(format!("unexpected compiler error or warning: '{}'",
573 for (i, &flag) in found_flags.iter().enumerate() {
575 let ee = &expected_errors[i];
576 fatal_ProcRes(format!("expected {} on line {} not found: {}",
577 ee.kind, ee.line, ee.msg), proc_res);
582 fn is_compiler_error_or_warning(line: &str) -> bool {
585 scan_until_char(line, ':', &mut i) &&
586 scan_char(line, ':', &mut i) &&
587 scan_integer(line, &mut i) &&
588 scan_char(line, ':', &mut i) &&
589 scan_integer(line, &mut i) &&
590 scan_char(line, ':', &mut i) &&
591 scan_char(line, ' ', &mut i) &&
592 scan_integer(line, &mut i) &&
593 scan_char(line, ':', &mut i) &&
594 scan_integer(line, &mut i) &&
595 scan_char(line, ' ', &mut i) &&
596 (scan_string(line, "error", &mut i) ||
597 scan_string(line, "warning", &mut i));
600 fn scan_until_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
601 if *idx >= haystack.len() {
604 let opt = haystack.slice_from(*idx).find(needle);
612 fn scan_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
613 if *idx >= haystack.len() {
616 let range = haystack.char_range_at(*idx);
617 if range.ch != needle {
624 fn scan_integer(haystack: &str, idx: &mut uint) -> bool {
626 while i < haystack.len() {
627 let range = haystack.char_range_at(i);
628 if range.ch < '0' || '9' < range.ch {
640 fn scan_string(haystack: &str, needle: &str, idx: &mut uint) -> bool {
641 let mut haystack_i = *idx;
642 let mut needle_i = 0u;
643 while needle_i < needle.len() {
644 if haystack_i >= haystack.len() {
647 let range = haystack.char_range_at(haystack_i);
648 haystack_i = range.next;
649 if !scan_char(needle, range.ch, &mut needle_i) {
657 struct ProcArgs {prog: ~str, args: ~[~str]}
659 struct ProcRes {status: ProcessExit, stdout: ~str, stderr: ~str, cmdline: ~str}
661 fn compile_test(config: &config, props: &TestProps,
662 testfile: &Path) -> ProcRes {
663 compile_test_(config, props, testfile, [])
666 fn jit_test(config: &config, props: &TestProps, testfile: &Path) -> ProcRes {
667 compile_test_(config, props, testfile, [~"--jit"])
670 fn compile_test_(config: &config, props: &TestProps,
671 testfile: &Path, extra_args: &[~str]) -> ProcRes {
672 let aux_dir = aux_output_dir_name(config, testfile);
673 // FIXME (#9639): This needs to handle non-utf8 paths
674 let link_args = ~[~"-L", aux_dir.as_str().unwrap().to_owned()];
675 let args = make_compile_args(config, props, link_args + extra_args,
676 |a, b| ThisFile(make_exe_name(a, b)), testfile);
677 compose_and_run_compiler(config, props, testfile, args, None)
680 fn exec_compiled_test(config: &config, props: &TestProps,
681 testfile: &Path) -> ProcRes {
683 let env = props.exec_env.clone();
685 match config.target.as_slice() {
687 "arm-linux-androideabi" => {
688 _arm_exec_compiled_test(config, props, testfile, env)
692 compose_and_run(config, testfile,
693 make_run_args(config, props, testfile),
695 config.run_lib_path, None)
700 fn compose_and_run_compiler(
705 input: Option<~str>) -> ProcRes {
707 if !props.aux_builds.is_empty() {
708 ensure_dir(&aux_output_dir_name(config, testfile));
711 let aux_dir = aux_output_dir_name(config, testfile);
712 // FIXME (#9639): This needs to handle non-utf8 paths
713 let extra_link_args = ~[~"-L", aux_dir.as_str().unwrap().to_owned()];
715 for rel_ab in props.aux_builds.iter() {
716 let abs_ab = config.aux_base.join(rel_ab.as_slice());
717 let aux_props = load_props(&abs_ab);
718 let crate_type = if aux_props.no_prefer_dynamic {
721 ~[~"--crate-type=dylib"]
724 make_compile_args(config, &aux_props, crate_type + extra_link_args,
726 let f = make_lib_name(a, b, testfile);
727 ThisDirectory(f.dir_path())
729 let auxres = compose_and_run(config, &abs_ab, aux_args, ~[],
730 config.compile_lib_path, None);
731 if !auxres.status.success() {
733 format!("auxiliary build of {} failed to compile: ",
738 match config.target.as_slice() {
740 "arm-linux-androideabi" => {
741 _arm_push_aux_shared_library(config, testfile);
748 compose_and_run(config, testfile, args, ~[],
749 config.compile_lib_path, input)
752 fn ensure_dir(path: &Path) {
753 if path.is_dir() { return; }
754 fs::mkdir(path, io::UserRWX).unwrap();
757 fn compose_and_run(config: &config, testfile: &Path,
758 ProcArgs{ args, prog }: ProcArgs,
759 procenv: ~[(~str, ~str)],
761 input: Option<~str>) -> ProcRes {
762 return program_output(config, testfile, lib_path,
763 prog, args, procenv, input);
766 enum TargetLocation {
771 fn make_compile_args(config: &config,
774 xform: |&config, &Path| -> TargetLocation,
777 let xform_file = xform(config, testfile);
778 let target = if props.force_host {
779 config.host.as_slice()
781 config.target.as_slice()
783 // FIXME (#9639): This needs to handle non-utf8 paths
784 let mut args = ~[testfile.as_str().unwrap().to_owned(),
785 ~"-L", config.build_base.as_str().unwrap().to_owned(),
786 ~"--target=" + target]
788 if !props.no_prefer_dynamic {
790 args.push(~"prefer-dynamic");
792 let path = match xform_file {
793 ThisFile(path) => { args.push(~"-o"); path }
794 ThisDirectory(path) => { args.push(~"--out-dir"); path }
796 args.push(path.as_str().unwrap().to_owned());
797 if props.force_host {
798 args.push_all_move(split_maybe_args(&config.host_rustcflags));
800 args.push_all_move(split_maybe_args(&config.target_rustcflags));
802 args.push_all_move(split_maybe_args(&props.compile_flags));
803 return ProcArgs {prog: config.rustc_path.as_str().unwrap().to_owned(), args: args};
806 fn make_lib_name(config: &config, auxfile: &Path, testfile: &Path) -> Path {
807 // what we return here is not particularly important, as it
808 // happens; rustc ignores everything except for the directory.
809 let auxname = output_testname(auxfile);
810 aux_output_dir_name(config, testfile).join(&auxname)
813 fn make_exe_name(config: &config, testfile: &Path) -> Path {
814 let mut f = output_base_name(config, testfile);
815 if !os::consts::EXE_SUFFIX.is_empty() {
816 match f.filename().map(|s| s + os::consts::EXE_SUFFIX.as_bytes()) {
817 Some(v) => f.set_filename(v),
824 fn make_run_args(config: &config, _props: &TestProps, testfile: &Path) ->
826 // If we've got another tool to run under (valgrind),
827 // then split apart its command
828 let mut args = split_maybe_args(&config.runtool);
829 let exe_file = make_exe_name(config, testfile);
830 // FIXME (#9639): This needs to handle non-utf8 paths
831 args.push(exe_file.as_str().unwrap().to_owned());
832 let prog = args.shift().unwrap();
833 return ProcArgs {prog: prog, args: args};
836 fn split_maybe_args(argstr: &Option<~str>) -> ~[~str] {
840 .filter_map(|s| if s.is_whitespace() {None} else {Some(s.to_owned())})
847 fn program_output(config: &config, testfile: &Path, lib_path: &str, prog: ~str,
848 args: ~[~str], env: ~[(~str, ~str)],
849 input: Option<~str>) -> ProcRes {
852 let cmdline = make_cmdline(lib_path, prog, args);
853 logv(config, format!("executing {}", cmdline));
856 let procsrv::Result{ out, err, status } =
857 procsrv::run(lib_path, prog, args, env, input)
858 .expect(format!("failed to exec `{}`", prog));
859 dump_output(config, testfile, out, err);
860 return ProcRes {status: status,
866 // Linux and mac don't require adjusting the library search path
867 #[cfg(target_os = "linux")]
868 #[cfg(target_os = "macos")]
869 #[cfg(target_os = "freebsd")]
870 fn make_cmdline(_libpath: &str, prog: &str, args: &[~str]) -> ~str {
871 format!("{} {}", prog, args.connect(" "))
874 #[cfg(target_os = "win32")]
875 fn make_cmdline(libpath: &str, prog: &str, args: &[~str]) -> ~str {
876 format!("{} {} {}", lib_path_cmd_prefix(libpath), prog,
880 // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
881 // for diagnostic purposes
882 #[cfg(target_os = "win32")]
883 fn lib_path_cmd_prefix(path: &str) -> ~str {
884 format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
887 fn dump_output(config: &config, testfile: &Path, out: &str, err: &str) {
888 dump_output_file(config, testfile, out, "out");
889 dump_output_file(config, testfile, err, "err");
890 maybe_dump_to_stdout(config, out, err);
893 fn dump_output_file(config: &config, testfile: &Path,
894 out: &str, extension: &str) {
895 let outfile = make_out_name(config, testfile, extension);
896 File::create(&outfile).write(out.as_bytes()).unwrap();
899 fn make_out_name(config: &config, testfile: &Path, extension: &str) -> Path {
900 output_base_name(config, testfile).with_extension(extension)
903 fn aux_output_dir_name(config: &config, testfile: &Path) -> Path {
904 let mut f = output_base_name(config, testfile);
905 match f.filename().map(|s| s + bytes!(".libaux")) {
906 Some(v) => f.set_filename(v),
912 fn output_testname(testfile: &Path) -> Path {
913 Path::new(testfile.filestem().unwrap())
916 fn output_base_name(config: &config, testfile: &Path) -> Path {
918 .join(&output_testname(testfile))
919 .with_extension(config.stage_id.as_slice())
922 fn maybe_dump_to_stdout(config: &config, out: &str, err: &str) {
924 println!("------{}------------------------------", "stdout");
926 println!("------{}------------------------------", "stderr");
928 println!("------------------------------------------");
932 fn error(err: ~str) { println!("\nerror: {}", err); }
934 fn fatal(err: ~str) -> ! { error(err); fail!(); }
936 fn fatal_ProcRes(err: ~str, proc_res: &ProcRes) -> ! {
941 ------------------------------------------\n\
943 ------------------------------------------\n\
945 ------------------------------------------\n\
947 ------------------------------------------\n\
949 err, proc_res.cmdline, proc_res.stdout, proc_res.stderr);
953 fn _arm_exec_compiled_test(config: &config, props: &TestProps,
954 testfile: &Path, env: ~[(~str, ~str)]) -> ProcRes {
956 let args = make_run_args(config, props, testfile);
957 let cmdline = make_cmdline("", args.prog, args.args);
959 // get bare program string
960 let mut tvec: ~[~str] = args.prog.split('/').map(|ts| ts.to_owned()).collect();
961 let prog_short = tvec.pop().unwrap();
964 let copy_result = procsrv::run("", config.adb_path,
965 [~"push", args.prog.clone(), config.adb_test_dir.clone()],
966 ~[(~"",~"")], Some(~""))
967 .expect(format!("failed to exec `{}`", config.adb_path));
970 println!("push ({}) {} {} {}",
971 config.target, args.prog,
972 copy_result.out, copy_result.err);
975 logv(config, format!("executing ({}) {}", config.target, cmdline));
977 let mut runargs = ~[];
979 // run test via adb_run_wrapper
980 runargs.push(~"shell");
981 for (key, val) in env.move_iter() {
982 runargs.push(format!("{}={}", key, val));
984 runargs.push(format!("{}/adb_run_wrapper.sh", config.adb_test_dir));
985 runargs.push(format!("{}", config.adb_test_dir));
986 runargs.push(format!("{}", prog_short));
988 for tv in args.args.iter() {
989 runargs.push(tv.to_owned());
991 procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""))
992 .expect(format!("failed to exec `{}`", config.adb_path));
994 // get exitcode of result
996 runargs.push(~"shell");
997 runargs.push(~"cat");
998 runargs.push(format!("{}/{}.exitcode", config.adb_test_dir, prog_short));
1000 let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
1001 procsrv::run("", config.adb_path, runargs, ~[(~"",~"")],
1003 .expect(format!("failed to exec `{}`", config.adb_path));
1005 let mut exitcode : int = 0;
1006 for c in exitcode_out.chars() {
1007 if !c.is_digit() { break; }
1008 exitcode = exitcode * 10 + match c {
1009 '0' .. '9' => c as int - ('0' as int),
1014 // get stdout of result
1016 runargs.push(~"shell");
1017 runargs.push(~"cat");
1018 runargs.push(format!("{}/{}.stdout", config.adb_test_dir, prog_short));
1020 let procsrv::Result{ out: stdout_out, err: _, status: _ } =
1021 procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""))
1022 .expect(format!("failed to exec `{}`", config.adb_path));
1024 // get stderr of result
1026 runargs.push(~"shell");
1027 runargs.push(~"cat");
1028 runargs.push(format!("{}/{}.stderr", config.adb_test_dir, prog_short));
1030 let procsrv::Result{ out: stderr_out, err: _, status: _ } =
1031 procsrv::run("", config.adb_path, runargs, ~[(~"",~"")], Some(~""))
1032 .expect(format!("failed to exec `{}`", config.adb_path));
1034 dump_output(config, testfile, stdout_out, stderr_out);
1037 status: process::ExitStatus(exitcode),
1044 fn _arm_push_aux_shared_library(config: &config, testfile: &Path) {
1045 let tdir = aux_output_dir_name(config, testfile);
1047 let dirs = fs::readdir(&tdir).unwrap();
1048 for file in dirs.iter() {
1049 if file.extension_str() == Some("so") {
1050 // FIXME (#9639): This needs to handle non-utf8 paths
1051 let copy_result = procsrv::run("", config.adb_path,
1052 [~"push", file.as_str().unwrap().to_owned(), config.adb_test_dir.clone()],
1053 ~[(~"",~"")], Some(~""))
1054 .expect(format!("failed to exec `{}`", config.adb_path));
1057 println!("push ({}) {} {} {}",
1058 config.target, file.display(),
1059 copy_result.out, copy_result.err);
1065 // codegen tests (vs. clang)
1067 fn make_o_name(config: &config, testfile: &Path) -> Path {
1068 output_base_name(config, testfile).with_extension("o")
1071 fn append_suffix_to_stem(p: &Path, suffix: &str) -> Path {
1072 if suffix.len() == 0 {
1075 let stem = p.filestem().unwrap();
1076 p.with_filename(stem + bytes!("-") + suffix.as_bytes())
1080 fn compile_test_and_save_bitcode(config: &config, props: &TestProps,
1081 testfile: &Path) -> ProcRes {
1082 let aux_dir = aux_output_dir_name(config, testfile);
1083 // FIXME (#9639): This needs to handle non-utf8 paths
1084 let link_args = ~[~"-L", aux_dir.as_str().unwrap().to_owned()];
1085 let llvm_args = ~[~"--emit=obj", ~"--crate-type=lib", ~"-C", ~"save-temps"];
1086 let args = make_compile_args(config, props,
1087 link_args + llvm_args,
1088 |a, b| ThisFile(make_o_name(a, b)), testfile);
1089 compose_and_run_compiler(config, props, testfile, args, None)
1092 fn compile_cc_with_clang_and_save_bitcode(config: &config, _props: &TestProps,
1093 testfile: &Path) -> ProcRes {
1094 let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1095 let bitcodefile = append_suffix_to_stem(&bitcodefile, "clang");
1096 let testcc = testfile.with_extension("cc");
1097 let proc_args = ProcArgs {
1098 // FIXME (#9639): This needs to handle non-utf8 paths
1099 prog: config.clang_path.get_ref().as_str().unwrap().to_owned(),
1102 ~"-o", bitcodefile.as_str().unwrap().to_owned(),
1103 testcc.as_str().unwrap().to_owned() ]
1105 compose_and_run(config, testfile, proc_args, ~[], "", None)
1108 fn extract_function_from_bitcode(config: &config, _props: &TestProps,
1109 fname: &str, testfile: &Path,
1110 suffix: &str) -> ProcRes {
1111 let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1112 let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
1113 let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
1114 let prog = config.llvm_bin_path.get_ref().join("llvm-extract");
1115 let proc_args = ProcArgs {
1116 // FIXME (#9639): This needs to handle non-utf8 paths
1117 prog: prog.as_str().unwrap().to_owned(),
1118 args: ~["-func=" + fname,
1119 "-o=" + extracted_bc.as_str().unwrap(),
1120 bitcodefile.as_str().unwrap().to_owned() ]
1122 compose_and_run(config, testfile, proc_args, ~[], "", None)
1125 fn disassemble_extract(config: &config, _props: &TestProps,
1126 testfile: &Path, suffix: &str) -> ProcRes {
1127 let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1128 let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
1129 let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
1130 let extracted_ll = extracted_bc.with_extension("ll");
1131 let prog = config.llvm_bin_path.get_ref().join("llvm-dis");
1132 let proc_args = ProcArgs {
1133 // FIXME (#9639): This needs to handle non-utf8 paths
1134 prog: prog.as_str().unwrap().to_owned(),
1135 args: ~["-o=" + extracted_ll.as_str().unwrap(),
1136 extracted_bc.as_str().unwrap().to_owned() ]
1138 compose_and_run(config, testfile, proc_args, ~[], "", None)
1142 fn count_extracted_lines(p: &Path) -> uint {
1143 let x = File::open(&p.with_extension("ll")).read_to_end().unwrap();
1144 let x = str::from_utf8_owned(x).unwrap();
1149 fn run_codegen_test(config: &config, props: &TestProps,
1150 testfile: &Path, mm: &mut MetricMap) {
1152 if config.llvm_bin_path.is_none() {
1153 fatal(~"missing --llvm-bin-path");
1156 if config.clang_path.is_none() {
1157 fatal(~"missing --clang-path");
1160 let mut proc_res = compile_test_and_save_bitcode(config, props, testfile);
1161 if !proc_res.status.success() {
1162 fatal_ProcRes(~"compilation failed!", &proc_res);
1165 proc_res = extract_function_from_bitcode(config, props, "test", testfile, "");
1166 if !proc_res.status.success() {
1167 fatal_ProcRes(~"extracting 'test' function failed", &proc_res);
1170 proc_res = disassemble_extract(config, props, testfile, "");
1171 if !proc_res.status.success() {
1172 fatal_ProcRes(~"disassembling extract failed", &proc_res);
1176 let mut proc_res = compile_cc_with_clang_and_save_bitcode(config, props, testfile);
1177 if !proc_res.status.success() {
1178 fatal_ProcRes(~"compilation failed!", &proc_res);
1181 proc_res = extract_function_from_bitcode(config, props, "test", testfile, "clang");
1182 if !proc_res.status.success() {
1183 fatal_ProcRes(~"extracting 'test' function failed", &proc_res);
1186 proc_res = disassemble_extract(config, props, testfile, "clang");
1187 if !proc_res.status.success() {
1188 fatal_ProcRes(~"disassembling extract failed", &proc_res);
1191 let base = output_base_name(config, testfile);
1192 let base_extract = append_suffix_to_stem(&base, "extract");
1194 let base_clang = append_suffix_to_stem(&base, "clang");
1195 let base_clang_extract = append_suffix_to_stem(&base_clang, "extract");
1197 let base_lines = count_extracted_lines(&base_extract);
1198 let clang_lines = count_extracted_lines(&base_clang_extract);
1200 mm.insert_metric("clang-codegen-ratio",
1201 (base_lines as f64) / (clang_lines as f64),