]> git.proxmox.com Git - rustc.git/blob - src/compiletest/runtest.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / compiletest / runtest.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use common::Config;
12 use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
13 use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
14 use common::{Incremental};
15 use errors::{self, ErrorKind};
16 use header::TestProps;
17 use header;
18 use procsrv;
19 use test::TestPaths;
20 use util::logv;
21
22 use std::env;
23 use std::collections::HashSet;
24 use std::fmt;
25 use std::fs::{self, File};
26 use std::io::BufReader;
27 use std::io::prelude::*;
28 use std::net::TcpStream;
29 use std::path::{Path, PathBuf, Component};
30 use std::process::{Command, Output, ExitStatus};
31
32 pub fn run(config: Config, testpaths: &TestPaths) {
33 match &*config.target {
34
35 "arm-linux-androideabi" | "aarch64-linux-android" => {
36 if !config.adb_device_status {
37 panic!("android device not available");
38 }
39 }
40
41 _=> { }
42 }
43
44 if config.verbose {
45 // We're going to be dumping a lot of info. Start on a new line.
46 print!("\n\n");
47 }
48 debug!("running {:?}", testpaths.file.display());
49 let props = header::load_props(&testpaths.file);
50 debug!("loaded props");
51 match config.mode {
52 CompileFail => run_cfail_test(&config, &props, &testpaths),
53 ParseFail => run_cfail_test(&config, &props, &testpaths),
54 RunFail => run_rfail_test(&config, &props, &testpaths),
55 RunPass => run_rpass_test(&config, &props, &testpaths),
56 RunPassValgrind => run_valgrind_test(&config, &props, &testpaths),
57 Pretty => run_pretty_test(&config, &props, &testpaths),
58 DebugInfoGdb => run_debuginfo_gdb_test(&config, &props, &testpaths),
59 DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testpaths),
60 Codegen => run_codegen_test(&config, &props, &testpaths),
61 Rustdoc => run_rustdoc_test(&config, &props, &testpaths),
62 CodegenUnits => run_codegen_units_test(&config, &props, &testpaths),
63 Incremental => run_incremental_test(&config, &props, &testpaths),
64 }
65 }
66
67 fn get_output(props: &TestProps, proc_res: &ProcRes) -> String {
68 if props.check_stdout {
69 format!("{}{}", proc_res.stdout, proc_res.stderr)
70 } else {
71 proc_res.stderr.clone()
72 }
73 }
74
75
76 fn for_each_revision<OP>(config: &Config, props: &TestProps, testpaths: &TestPaths,
77 mut op: OP)
78 where OP: FnMut(&Config, &TestProps, &TestPaths, Option<&str>)
79 {
80 if props.revisions.is_empty() {
81 op(config, props, testpaths, None)
82 } else {
83 for revision in &props.revisions {
84 let mut revision_props = props.clone();
85 header::load_props_into(&mut revision_props,
86 &testpaths.file,
87 Some(&revision));
88 revision_props.compile_flags.extend(vec![
89 format!("--cfg"),
90 format!("{}", revision),
91 ]);
92 op(config, &revision_props, testpaths, Some(revision));
93 }
94 }
95 }
96
97 fn run_cfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
98 for_each_revision(config, props, testpaths, run_cfail_test_revision);
99 }
100
101 fn run_cfail_test_revision(config: &Config,
102 props: &TestProps,
103 testpaths: &TestPaths,
104 revision: Option<&str>) {
105 let proc_res = compile_test(config, props, testpaths);
106
107 if proc_res.status.success() {
108 fatal_proc_rec(
109 revision,
110 &format!("{} test compiled successfully!", config.mode)[..],
111 &proc_res);
112 }
113
114 check_correct_failure_status(revision, &proc_res);
115
116 if proc_res.status.success() {
117 fatal(revision, "process did not return an error status");
118 }
119
120 let output_to_check = get_output(props, &proc_res);
121 let expected_errors = errors::load_errors(&testpaths.file, revision);
122 if !expected_errors.is_empty() {
123 if !props.error_patterns.is_empty() {
124 fatal(revision, "both error pattern and expected errors specified");
125 }
126 check_expected_errors(revision, expected_errors, testpaths, &proc_res);
127 } else {
128 check_error_patterns(revision, props, testpaths, &output_to_check, &proc_res);
129 }
130 check_no_compiler_crash(revision, &proc_res);
131 check_forbid_output(revision, props, &output_to_check, &proc_res);
132 }
133
134 fn run_rfail_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
135 for_each_revision(config, props, testpaths, run_rfail_test_revision);
136 }
137
138 fn run_rfail_test_revision(config: &Config,
139 props: &TestProps,
140 testpaths: &TestPaths,
141 revision: Option<&str>) {
142 let proc_res = compile_test(config, props, testpaths);
143
144 if !proc_res.status.success() {
145 fatal_proc_rec(revision, "compilation failed!", &proc_res);
146 }
147
148 let proc_res = exec_compiled_test(config, props, testpaths);
149
150 // The value our Makefile configures valgrind to return on failure
151 const VALGRIND_ERR: i32 = 100;
152 if proc_res.status.code() == Some(VALGRIND_ERR) {
153 fatal_proc_rec(revision, "run-fail test isn't valgrind-clean!", &proc_res);
154 }
155
156 let output_to_check = get_output(props, &proc_res);
157 check_correct_failure_status(revision, &proc_res);
158 check_error_patterns(revision, props, testpaths, &output_to_check, &proc_res);
159 }
160
161 fn check_correct_failure_status(revision: Option<&str>, proc_res: &ProcRes) {
162 // The value the rust runtime returns on failure
163 const RUST_ERR: i32 = 101;
164 if proc_res.status.code() != Some(RUST_ERR) {
165 fatal_proc_rec(
166 revision,
167 &format!("failure produced the wrong error: {}",
168 proc_res.status),
169 proc_res);
170 }
171 }
172
173 fn run_rpass_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
174 for_each_revision(config, props, testpaths, run_rpass_test_revision);
175 }
176
177 fn run_rpass_test_revision(config: &Config,
178 props: &TestProps,
179 testpaths: &TestPaths,
180 revision: Option<&str>) {
181 let proc_res = compile_test(config, props, testpaths);
182
183 if !proc_res.status.success() {
184 fatal_proc_rec(revision, "compilation failed!", &proc_res);
185 }
186
187 let proc_res = exec_compiled_test(config, props, testpaths);
188
189 if !proc_res.status.success() {
190 fatal_proc_rec(revision, "test run failed!", &proc_res);
191 }
192 }
193
194 fn run_valgrind_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
195 assert!(props.revisions.is_empty(), "revisions not relevant here");
196
197 if config.valgrind_path.is_none() {
198 assert!(!config.force_valgrind);
199 return run_rpass_test(config, props, testpaths);
200 }
201
202 let mut proc_res = compile_test(config, props, testpaths);
203
204 if !proc_res.status.success() {
205 fatal_proc_rec(None, "compilation failed!", &proc_res);
206 }
207
208 let mut new_config = config.clone();
209 new_config.runtool = new_config.valgrind_path.clone();
210 proc_res = exec_compiled_test(&new_config, props, testpaths);
211
212 if !proc_res.status.success() {
213 fatal_proc_rec(None, "test run failed!", &proc_res);
214 }
215 }
216
217 fn run_pretty_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
218 for_each_revision(config, props, testpaths, run_pretty_test_revision);
219 }
220
221 fn run_pretty_test_revision(config: &Config,
222 props: &TestProps,
223 testpaths: &TestPaths,
224 revision: Option<&str>) {
225 if props.pp_exact.is_some() {
226 logv(config, "testing for exact pretty-printing".to_owned());
227 } else {
228 logv(config, "testing for converging pretty-printing".to_owned());
229 }
230
231 let rounds =
232 match props.pp_exact { Some(_) => 1, None => 2 };
233
234 let mut src = String::new();
235 File::open(&testpaths.file).unwrap().read_to_string(&mut src).unwrap();
236 let mut srcs = vec!(src);
237
238 let mut round = 0;
239 while round < rounds {
240 logv(config, format!("pretty-printing round {} revision {:?}",
241 round, revision));
242 let proc_res = print_source(config,
243 props,
244 testpaths,
245 srcs[round].to_owned(),
246 &props.pretty_mode);
247
248 if !proc_res.status.success() {
249 fatal_proc_rec(revision,
250 &format!("pretty-printing failed in round {} revision {:?}",
251 round, revision),
252 &proc_res);
253 }
254
255 let ProcRes{ stdout, .. } = proc_res;
256 srcs.push(stdout);
257 round += 1;
258 }
259
260 let mut expected = match props.pp_exact {
261 Some(ref file) => {
262 let filepath = testpaths.file.parent().unwrap().join(file);
263 let mut s = String::new();
264 File::open(&filepath).unwrap().read_to_string(&mut s).unwrap();
265 s
266 }
267 None => { srcs[srcs.len() - 2].clone() }
268 };
269 let mut actual = srcs[srcs.len() - 1].clone();
270
271 if props.pp_exact.is_some() {
272 // Now we have to care about line endings
273 let cr = "\r".to_owned();
274 actual = actual.replace(&cr, "").to_owned();
275 expected = expected.replace(&cr, "").to_owned();
276 }
277
278 compare_source(revision, &expected, &actual);
279
280 // If we're only making sure that the output matches then just stop here
281 if props.pretty_compare_only { return; }
282
283 // Finally, let's make sure it actually appears to remain valid code
284 let proc_res = typecheck_source(config, props, testpaths, actual);
285 if !proc_res.status.success() {
286 fatal_proc_rec(revision, "pretty-printed source does not typecheck", &proc_res);
287 }
288
289 if !props.pretty_expanded { return }
290
291 // additionally, run `--pretty expanded` and try to build it.
292 let proc_res = print_source(config, props, testpaths, srcs[round].clone(), "expanded");
293 if !proc_res.status.success() {
294 fatal_proc_rec(revision, "pretty-printing (expanded) failed", &proc_res);
295 }
296
297 let ProcRes{ stdout: expanded_src, .. } = proc_res;
298 let proc_res = typecheck_source(config, props, testpaths, expanded_src);
299 if !proc_res.status.success() {
300 fatal_proc_rec(
301 revision,
302 "pretty-printed source (expanded) does not typecheck",
303 &proc_res);
304 }
305
306 return;
307
308 fn print_source(config: &Config,
309 props: &TestProps,
310 testpaths: &TestPaths,
311 src: String,
312 pretty_type: &str) -> ProcRes {
313 let aux_dir = aux_output_dir_name(config, testpaths);
314 compose_and_run(config,
315 testpaths,
316 make_pp_args(config,
317 props,
318 testpaths,
319 pretty_type.to_owned()),
320 props.exec_env.clone(),
321 config.compile_lib_path.to_str().unwrap(),
322 Some(aux_dir.to_str().unwrap()),
323 Some(src))
324 }
325
326 fn make_pp_args(config: &Config,
327 props: &TestProps,
328 testpaths: &TestPaths,
329 pretty_type: String) -> ProcArgs {
330 let aux_dir = aux_output_dir_name(config, testpaths);
331 // FIXME (#9639): This needs to handle non-utf8 paths
332 let mut args = vec!("-".to_owned(),
333 "-Zunstable-options".to_owned(),
334 "--unpretty".to_owned(),
335 pretty_type,
336 format!("--target={}", config.target),
337 "-L".to_owned(),
338 aux_dir.to_str().unwrap().to_owned());
339 args.extend(split_maybe_args(&config.target_rustcflags));
340 args.extend(props.compile_flags.iter().cloned());
341 return ProcArgs {
342 prog: config.rustc_path.to_str().unwrap().to_owned(),
343 args: args,
344 };
345 }
346
347 fn compare_source(revision: Option<&str>, expected: &str, actual: &str) {
348 if expected != actual {
349 error(revision, "pretty-printed source does not match expected source");
350 println!("\n\
351 expected:\n\
352 ------------------------------------------\n\
353 {}\n\
354 ------------------------------------------\n\
355 actual:\n\
356 ------------------------------------------\n\
357 {}\n\
358 ------------------------------------------\n\
359 \n",
360 expected, actual);
361 panic!();
362 }
363 }
364
365 fn typecheck_source(config: &Config, props: &TestProps,
366 testpaths: &TestPaths, src: String) -> ProcRes {
367 let args = make_typecheck_args(config, props, testpaths);
368 compose_and_run_compiler(config, props, testpaths, args, Some(src))
369 }
370
371 fn make_typecheck_args(config: &Config, props: &TestProps, testpaths: &TestPaths) -> ProcArgs {
372 let aux_dir = aux_output_dir_name(config, testpaths);
373 let target = if props.force_host {
374 &*config.host
375 } else {
376 &*config.target
377 };
378 // FIXME (#9639): This needs to handle non-utf8 paths
379 let mut args = vec!("-".to_owned(),
380 "-Zno-trans".to_owned(),
381 format!("--target={}", target),
382 "-L".to_owned(),
383 config.build_base.to_str().unwrap().to_owned(),
384 "-L".to_owned(),
385 aux_dir.to_str().unwrap().to_owned());
386 args.extend(split_maybe_args(&config.target_rustcflags));
387 args.extend(props.compile_flags.iter().cloned());
388 // FIXME (#9639): This needs to handle non-utf8 paths
389 return ProcArgs {
390 prog: config.rustc_path.to_str().unwrap().to_owned(),
391 args: args,
392 };
393 }
394 }
395
396 fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
397 assert!(props.revisions.is_empty(), "revisions not relevant here");
398
399 let mut config = Config {
400 target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
401 host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
402 .. config.clone()
403 };
404
405 let config = &mut config;
406 let DebuggerCommands {
407 commands,
408 check_lines,
409 breakpoint_lines
410 } = parse_debugger_commands(testpaths, "gdb");
411 let mut cmds = commands.join("\n");
412
413 // compile test file (it should have 'compile-flags:-g' in the header)
414 let compiler_run_result = compile_test(config, props, testpaths);
415 if !compiler_run_result.status.success() {
416 fatal_proc_rec(None, "compilation failed!", &compiler_run_result);
417 }
418
419 let exe_file = make_exe_name(config, testpaths);
420
421 let debugger_run_result;
422 match &*config.target {
423 "arm-linux-androideabi" | "aarch64-linux-android" => {
424
425 cmds = cmds.replace("run", "continue");
426
427 // write debugger script
428 let mut script_str = String::with_capacity(2048);
429 script_str.push_str(&format!("set charset {}\n", charset()));
430 script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
431 script_str.push_str("target remote :5039\n");
432 script_str.push_str(&format!("set solib-search-path \
433 ./{}/stage2/lib/rustlib/{}/lib/\n",
434 config.host, config.target));
435 for line in &breakpoint_lines {
436 script_str.push_str(&format!("break {:?}:{}\n",
437 testpaths.file
438 .file_name()
439 .unwrap()
440 .to_string_lossy(),
441 *line)[..]);
442 }
443 script_str.push_str(&cmds);
444 script_str.push_str("\nquit\n");
445
446 debug!("script_str = {}", script_str);
447 dump_output_file(config,
448 testpaths,
449 &script_str,
450 "debugger.script");
451
452
453 procsrv::run("",
454 &config.adb_path,
455 None,
456 &[
457 "push".to_owned(),
458 exe_file.to_str().unwrap().to_owned(),
459 config.adb_test_dir.clone()
460 ],
461 vec!(("".to_owned(), "".to_owned())),
462 Some("".to_owned()))
463 .expect(&format!("failed to exec `{:?}`", config.adb_path));
464
465 procsrv::run("",
466 &config.adb_path,
467 None,
468 &[
469 "forward".to_owned(),
470 "tcp:5039".to_owned(),
471 "tcp:5039".to_owned()
472 ],
473 vec!(("".to_owned(), "".to_owned())),
474 Some("".to_owned()))
475 .expect(&format!("failed to exec `{:?}`", config.adb_path));
476
477 let adb_arg = format!("export LD_LIBRARY_PATH={}; \
478 gdbserver{} :5039 {}/{}",
479 config.adb_test_dir.clone(),
480 if config.target.contains("aarch64")
481 {"64"} else {""},
482 config.adb_test_dir.clone(),
483 exe_file.file_name().unwrap().to_str()
484 .unwrap());
485
486 let mut process = procsrv::run_background("",
487 &config.adb_path
488 ,
489 None,
490 &[
491 "shell".to_owned(),
492 adb_arg.clone()
493 ],
494 vec!(("".to_owned(),
495 "".to_owned())),
496 Some("".to_owned()))
497 .expect(&format!("failed to exec `{:?}`", config.adb_path));
498 loop {
499 //waiting 1 second for gdbserver start
500 ::std::thread::sleep(::std::time::Duration::new(1,0));
501 if TcpStream::connect("127.0.0.1:5039").is_ok() {
502 break
503 }
504 }
505
506 let tool_path = match config.android_cross_path.to_str() {
507 Some(x) => x.to_owned(),
508 None => fatal(None, "cannot find android cross path")
509 };
510
511 let debugger_script = make_out_name(config, testpaths, "debugger.script");
512 // FIXME (#9639): This needs to handle non-utf8 paths
513 let debugger_opts =
514 vec!("-quiet".to_owned(),
515 "-batch".to_owned(),
516 "-nx".to_owned(),
517 format!("-command={}", debugger_script.to_str().unwrap()));
518
519 let mut gdb_path = tool_path;
520 gdb_path.push_str(&format!("/bin/{}-gdb", config.target));
521 let procsrv::Result {
522 out,
523 err,
524 status
525 } = procsrv::run("",
526 &gdb_path,
527 None,
528 &debugger_opts,
529 vec!(("".to_owned(), "".to_owned())),
530 None)
531 .expect(&format!("failed to exec `{:?}`", gdb_path));
532 let cmdline = {
533 let cmdline = make_cmdline("",
534 &format!("{}-gdb", config.target),
535 &debugger_opts);
536 logv(config, format!("executing {}", cmdline));
537 cmdline
538 };
539
540 debugger_run_result = ProcRes {
541 status: Status::Normal(status),
542 stdout: out,
543 stderr: err,
544 cmdline: cmdline
545 };
546 if process.kill().is_err() {
547 println!("Adb process is already finished.");
548 }
549 }
550
551 _=> {
552 let rust_src_root = find_rust_src_root(config)
553 .expect("Could not find Rust source root");
554 let rust_pp_module_rel_path = Path::new("./src/etc");
555 let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
556 .to_str()
557 .unwrap()
558 .to_owned();
559 // write debugger script
560 let mut script_str = String::with_capacity(2048);
561 script_str.push_str(&format!("set charset {}\n", charset()));
562 script_str.push_str("show version\n");
563
564 match config.gdb_version {
565 Some(ref version) => {
566 println!("NOTE: compiletest thinks it is using GDB version {}",
567 version);
568
569 if header::gdb_version_to_int(version) >
570 header::gdb_version_to_int("7.4") {
571 // Add the directory containing the pretty printers to
572 // GDB's script auto loading safe path
573 script_str.push_str(
574 &format!("add-auto-load-safe-path {}\n",
575 rust_pp_module_abs_path.replace(r"\", r"\\"))
576 );
577 }
578 }
579 _ => {
580 println!("NOTE: compiletest does not know which version of \
581 GDB it is using");
582 }
583 }
584
585 // The following line actually doesn't have to do anything with
586 // pretty printing, it just tells GDB to print values on one line:
587 script_str.push_str("set print pretty off\n");
588
589 // Add the pretty printer directory to GDB's source-file search path
590 script_str.push_str(&format!("directory {}\n",
591 rust_pp_module_abs_path));
592
593 // Load the target executable
594 script_str.push_str(&format!("file {}\n",
595 exe_file.to_str().unwrap()
596 .replace(r"\", r"\\")));
597
598 // Add line breakpoints
599 for line in &breakpoint_lines {
600 script_str.push_str(&format!("break '{}':{}\n",
601 testpaths.file.file_name().unwrap()
602 .to_string_lossy(),
603 *line));
604 }
605
606 script_str.push_str(&cmds);
607 script_str.push_str("\nquit\n");
608
609 debug!("script_str = {}", script_str);
610 dump_output_file(config,
611 testpaths,
612 &script_str,
613 "debugger.script");
614
615 // run debugger script with gdb
616 fn debugger() -> &'static str {
617 if cfg!(windows) {"gdb.exe"} else {"gdb"}
618 }
619
620 let debugger_script = make_out_name(config, testpaths, "debugger.script");
621
622 // FIXME (#9639): This needs to handle non-utf8 paths
623 let debugger_opts =
624 vec!("-quiet".to_owned(),
625 "-batch".to_owned(),
626 "-nx".to_owned(),
627 format!("-command={}", debugger_script.to_str().unwrap()));
628
629 let proc_args = ProcArgs {
630 prog: debugger().to_owned(),
631 args: debugger_opts,
632 };
633
634 let environment = vec![("PYTHONPATH".to_owned(), rust_pp_module_abs_path)];
635
636 debugger_run_result = compose_and_run(config,
637 testpaths,
638 proc_args,
639 environment,
640 config.run_lib_path.to_str().unwrap(),
641 None,
642 None);
643 }
644 }
645
646 if !debugger_run_result.status.success() {
647 fatal(None, "gdb failed to execute");
648 }
649
650 check_debugger_output(&debugger_run_result, &check_lines);
651 }
652
653 fn find_rust_src_root(config: &Config) -> Option<PathBuf> {
654 let mut path = config.src_base.clone();
655 let path_postfix = Path::new("src/etc/lldb_batchmode.py");
656
657 while path.pop() {
658 if path.join(&path_postfix).is_file() {
659 return Some(path);
660 }
661 }
662
663 return None;
664 }
665
666 fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
667 assert!(props.revisions.is_empty(), "revisions not relevant here");
668
669 if config.lldb_python_dir.is_none() {
670 fatal(None, "Can't run LLDB test because LLDB's python path is not set.");
671 }
672
673 let mut config = Config {
674 target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
675 host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
676 .. config.clone()
677 };
678
679 let config = &mut config;
680
681 // compile test file (it should have 'compile-flags:-g' in the header)
682 let compile_result = compile_test(config, props, testpaths);
683 if !compile_result.status.success() {
684 fatal_proc_rec(None, "compilation failed!", &compile_result);
685 }
686
687 let exe_file = make_exe_name(config, testpaths);
688
689 match config.lldb_version {
690 Some(ref version) => {
691 println!("NOTE: compiletest thinks it is using LLDB version {}",
692 version);
693 }
694 _ => {
695 println!("NOTE: compiletest does not know which version of \
696 LLDB it is using");
697 }
698 }
699
700 // Parse debugger commands etc from test files
701 let DebuggerCommands {
702 commands,
703 check_lines,
704 breakpoint_lines,
705 ..
706 } = parse_debugger_commands(testpaths, "lldb");
707
708 // Write debugger script:
709 // We don't want to hang when calling `quit` while the process is still running
710 let mut script_str = String::from("settings set auto-confirm true\n");
711
712 // Make LLDB emit its version, so we have it documented in the test output
713 script_str.push_str("version\n");
714
715 // Switch LLDB into "Rust mode"
716 let rust_src_root = find_rust_src_root(config)
717 .expect("Could not find Rust source root");
718 let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py");
719 let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
720 .to_str()
721 .unwrap()
722 .to_owned();
723
724 script_str.push_str(&format!("command script import {}\n",
725 &rust_pp_module_abs_path[..])[..]);
726 script_str.push_str("type summary add --no-value ");
727 script_str.push_str("--python-function lldb_rust_formatters.print_val ");
728 script_str.push_str("-x \".*\" --category Rust\n");
729 script_str.push_str("type category enable Rust\n");
730
731 // Set breakpoints on every line that contains the string "#break"
732 let source_file_name = testpaths.file.file_name().unwrap().to_string_lossy();
733 for line in &breakpoint_lines {
734 script_str.push_str(&format!("breakpoint set --file '{}' --line {}\n",
735 source_file_name,
736 line));
737 }
738
739 // Append the other commands
740 for line in &commands {
741 script_str.push_str(line);
742 script_str.push_str("\n");
743 }
744
745 // Finally, quit the debugger
746 script_str.push_str("\nquit\n");
747
748 // Write the script into a file
749 debug!("script_str = {}", script_str);
750 dump_output_file(config,
751 testpaths,
752 &script_str,
753 "debugger.script");
754 let debugger_script = make_out_name(config, testpaths, "debugger.script");
755
756 // Let LLDB execute the script via lldb_batchmode.py
757 let debugger_run_result = run_lldb(config,
758 testpaths,
759 &exe_file,
760 &debugger_script,
761 &rust_src_root);
762
763 if !debugger_run_result.status.success() {
764 fatal_proc_rec(None, "Error while running LLDB", &debugger_run_result);
765 }
766
767 check_debugger_output(&debugger_run_result, &check_lines);
768
769 fn run_lldb(config: &Config,
770 testpaths: &TestPaths,
771 test_executable: &Path,
772 debugger_script: &Path,
773 rust_src_root: &Path)
774 -> ProcRes {
775 // Prepare the lldb_batchmode which executes the debugger script
776 let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
777 cmd2procres(config,
778 testpaths,
779 Command::new(&config.python)
780 .arg(&lldb_script_path)
781 .arg(test_executable)
782 .arg(debugger_script)
783 .env("PYTHONPATH",
784 config.lldb_python_dir.as_ref().unwrap()))
785 }
786 }
787
788 fn cmd2procres(config: &Config, testpaths: &TestPaths, cmd: &mut Command)
789 -> ProcRes {
790 let (status, out, err) = match cmd.output() {
791 Ok(Output { status, stdout, stderr }) => {
792 (status,
793 String::from_utf8(stdout).unwrap(),
794 String::from_utf8(stderr).unwrap())
795 },
796 Err(e) => {
797 fatal(None, &format!("Failed to setup Python process for \
798 LLDB script: {}", e))
799 }
800 };
801
802 dump_output(config, testpaths, &out, &err);
803 ProcRes {
804 status: Status::Normal(status),
805 stdout: out,
806 stderr: err,
807 cmdline: format!("{:?}", cmd)
808 }
809 }
810
811 struct DebuggerCommands {
812 commands: Vec<String>,
813 check_lines: Vec<String>,
814 breakpoint_lines: Vec<usize>,
815 }
816
817 fn parse_debugger_commands(testpaths: &TestPaths, debugger_prefix: &str)
818 -> DebuggerCommands {
819 let command_directive = format!("{}-command", debugger_prefix);
820 let check_directive = format!("{}-check", debugger_prefix);
821
822 let mut breakpoint_lines = vec!();
823 let mut commands = vec!();
824 let mut check_lines = vec!();
825 let mut counter = 1;
826 let reader = BufReader::new(File::open(&testpaths.file).unwrap());
827 for line in reader.lines() {
828 match line {
829 Ok(line) => {
830 if line.contains("#break") {
831 breakpoint_lines.push(counter);
832 }
833
834 header::parse_name_value_directive(
835 &line,
836 &command_directive).map(|cmd| {
837 commands.push(cmd)
838 });
839
840 header::parse_name_value_directive(
841 &line,
842 &check_directive).map(|cmd| {
843 check_lines.push(cmd)
844 });
845 }
846 Err(e) => {
847 fatal(None, &format!("Error while parsing debugger commands: {}", e))
848 }
849 }
850 counter += 1;
851 }
852
853 DebuggerCommands {
854 commands: commands,
855 check_lines: check_lines,
856 breakpoint_lines: breakpoint_lines,
857 }
858 }
859
860 fn cleanup_debug_info_options(options: &Option<String>) -> Option<String> {
861 if options.is_none() {
862 return None;
863 }
864
865 // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
866 let options_to_remove = [
867 "-O".to_owned(),
868 "-g".to_owned(),
869 "--debuginfo".to_owned()
870 ];
871 let mut new_options =
872 split_maybe_args(options).into_iter()
873 .filter(|x| !options_to_remove.contains(x))
874 .collect::<Vec<String>>();
875
876 let mut i = 0;
877 while i + 1 < new_options.len() {
878 if new_options[i] == "-Z" {
879 // FIXME #31005 MIR missing debuginfo currently.
880 if new_options[i + 1] == "orbit" {
881 // Remove "-Z" and "orbit".
882 new_options.remove(i);
883 new_options.remove(i);
884 continue;
885 }
886 // Always skip over -Z's argument.
887 i += 1;
888 }
889 i += 1;
890 }
891
892 Some(new_options.join(" "))
893 }
894
895 fn check_debugger_output(debugger_run_result: &ProcRes, check_lines: &[String]) {
896 let num_check_lines = check_lines.len();
897 if num_check_lines > 0 {
898 // Allow check lines to leave parts unspecified (e.g., uninitialized
899 // bits in the wrong case of an enum) with the notation "[...]".
900 let check_fragments: Vec<Vec<String>> =
901 check_lines.iter().map(|s| {
902 s
903 .trim()
904 .split("[...]")
905 .map(str::to_owned)
906 .collect()
907 }).collect();
908 // check if each line in props.check_lines appears in the
909 // output (in order)
910 let mut i = 0;
911 for line in debugger_run_result.stdout.lines() {
912 let mut rest = line.trim();
913 let mut first = true;
914 let mut failed = false;
915 for frag in &check_fragments[i] {
916 let found = if first {
917 if rest.starts_with(frag) {
918 Some(0)
919 } else {
920 None
921 }
922 } else {
923 rest.find(frag)
924 };
925 match found {
926 None => {
927 failed = true;
928 break;
929 }
930 Some(i) => {
931 rest = &rest[(i + frag.len())..];
932 }
933 }
934 first = false;
935 }
936 if !failed && rest.is_empty() {
937 i += 1;
938 }
939 if i == num_check_lines {
940 // all lines checked
941 break;
942 }
943 }
944 if i != num_check_lines {
945 fatal_proc_rec(None, &format!("line not found in debugger output: {}",
946 check_lines.get(i).unwrap()),
947 debugger_run_result);
948 }
949 }
950 }
951
952 fn check_error_patterns(revision: Option<&str>,
953 props: &TestProps,
954 testpaths: &TestPaths,
955 output_to_check: &str,
956 proc_res: &ProcRes) {
957 if props.error_patterns.is_empty() {
958 fatal(revision,
959 &format!("no error pattern specified in {:?}",
960 testpaths.file.display()));
961 }
962 let mut next_err_idx = 0;
963 let mut next_err_pat = &props.error_patterns[next_err_idx];
964 let mut done = false;
965 for line in output_to_check.lines() {
966 if line.contains(next_err_pat) {
967 debug!("found error pattern {}", next_err_pat);
968 next_err_idx += 1;
969 if next_err_idx == props.error_patterns.len() {
970 debug!("found all error patterns");
971 done = true;
972 break;
973 }
974 next_err_pat = &props.error_patterns[next_err_idx];
975 }
976 }
977 if done { return; }
978
979 let missing_patterns = &props.error_patterns[next_err_idx..];
980 if missing_patterns.len() == 1 {
981 fatal_proc_rec(
982 revision,
983 &format!("error pattern '{}' not found!", missing_patterns[0]),
984 proc_res);
985 } else {
986 for pattern in missing_patterns {
987 error(revision, &format!("error pattern '{}' not found!", *pattern));
988 }
989 fatal_proc_rec(revision, "multiple error patterns not found", proc_res);
990 }
991 }
992
993 fn check_no_compiler_crash(revision: Option<&str>, proc_res: &ProcRes) {
994 for line in proc_res.stderr.lines() {
995 if line.starts_with("error: internal compiler error:") {
996 fatal_proc_rec(revision,
997 "compiler encountered internal error",
998 proc_res);
999 }
1000 }
1001 }
1002
1003 fn check_forbid_output(revision: Option<&str>,
1004 props: &TestProps,
1005 output_to_check: &str,
1006 proc_res: &ProcRes) {
1007 for pat in &props.forbid_output {
1008 if output_to_check.contains(pat) {
1009 fatal_proc_rec(revision,
1010 "forbidden pattern found in compiler output",
1011 proc_res);
1012 }
1013 }
1014 }
1015
1016 fn check_expected_errors(revision: Option<&str>,
1017 expected_errors: Vec<errors::ExpectedError>,
1018 testpaths: &TestPaths,
1019 proc_res: &ProcRes) {
1020 // true if we found the error in question
1021 let mut found_flags = vec![false; expected_errors.len()];
1022
1023 if proc_res.status.success() {
1024 fatal_proc_rec(revision, "process did not return an error status", proc_res);
1025 }
1026
1027 let prefixes = expected_errors.iter().map(|ee| {
1028 let expected = format!("{}:{}:", testpaths.file.display(), ee.line_num);
1029 // On windows just translate all '\' path separators to '/'
1030 expected.replace(r"\", "/")
1031 }).collect::<Vec<String>>();
1032
1033 // If the testcase being checked contains at least one expected "help"
1034 // message, then we'll ensure that all "help" messages are expected.
1035 // Otherwise, all "help" messages reported by the compiler will be ignored.
1036 // This logic also applies to "note" messages.
1037 let (expect_help, expect_note) =
1038 expected_errors.iter()
1039 .fold((false, false),
1040 |(acc_help, acc_note), ee|
1041 (acc_help || ee.kind == Some(ErrorKind::Help),
1042 acc_note || ee.kind == Some(ErrorKind::Note)));
1043
1044 // Scan and extract our error/warning messages,
1045 // which look like:
1046 // filename:line1:col1: line2:col2: *error:* msg
1047 // filename:line1:col1: line2:col2: *warning:* msg
1048 // where line1:col1: is the starting point, line2:col2:
1049 // is the ending point, and * represents ANSI color codes.
1050 //
1051 // This pattern is ambiguous on windows, because filename may contain
1052 // a colon, so any path prefix must be detected and removed first.
1053 let mut unexpected = 0;
1054 let mut not_found = 0;
1055 for line in proc_res.stderr.lines() {
1056 let mut was_expected = false;
1057 let mut prev = 0;
1058 for (i, ee) in expected_errors.iter().enumerate() {
1059 if !found_flags[i] {
1060 debug!("prefix={} ee.kind={:?} ee.msg={} line={}",
1061 prefixes[i],
1062 ee.kind,
1063 ee.msg,
1064 line);
1065 // Suggestions have no line number in their output, so take on the line number of
1066 // the previous expected error
1067 if ee.kind == Some(ErrorKind::Suggestion) {
1068 assert!(expected_errors[prev].kind == Some(ErrorKind::Help),
1069 "SUGGESTIONs must be preceded by a HELP");
1070 if line.contains(&ee.msg) {
1071 found_flags[i] = true;
1072 was_expected = true;
1073 break;
1074 }
1075 }
1076 if
1077 (prefix_matches(line, &prefixes[i]) || continuation(line)) &&
1078 (ee.kind.is_none() || line.contains(&ee.kind.as_ref().unwrap().to_string())) &&
1079 line.contains(&ee.msg)
1080 {
1081 found_flags[i] = true;
1082 was_expected = true;
1083 break;
1084 }
1085 }
1086 prev = i;
1087 }
1088
1089 // ignore this msg which gets printed at the end
1090 if line.contains("aborting due to") {
1091 was_expected = true;
1092 }
1093
1094 if !was_expected && is_unexpected_compiler_message(line, expect_help, expect_note) {
1095 error(revision, &format!("unexpected compiler message: '{}'", line));
1096 unexpected += 1;
1097 }
1098 }
1099
1100 for (i, &flag) in found_flags.iter().enumerate() {
1101 if !flag {
1102 let ee = &expected_errors[i];
1103 error(revision, &format!("expected {} on line {} not found: {}",
1104 ee.kind.as_ref()
1105 .map_or("message".into(),
1106 |k| k.to_string()),
1107 ee.line_num, ee.msg));
1108 not_found += 1;
1109 }
1110 }
1111
1112 if unexpected > 0 || not_found > 0 {
1113 fatal_proc_rec(
1114 revision,
1115 &format!("{} unexpected errors found, {} expected errors not found",
1116 unexpected, not_found),
1117 proc_res);
1118 }
1119
1120 fn prefix_matches(line: &str, prefix: &str) -> bool {
1121 use std::ascii::AsciiExt;
1122 // On windows just translate all '\' path separators to '/'
1123 let line = line.replace(r"\", "/");
1124 if cfg!(windows) {
1125 line.to_ascii_lowercase().starts_with(&prefix.to_ascii_lowercase())
1126 } else {
1127 line.starts_with(prefix)
1128 }
1129 }
1130
1131 // A multi-line error will have followup lines which start with a space
1132 // or open paren.
1133 fn continuation( line: &str) -> bool {
1134 line.starts_with(" ") || line.starts_with("(")
1135 }
1136 }
1137
1138 fn is_unexpected_compiler_message(line: &str, expect_help: bool, expect_note: bool) -> bool {
1139 let mut c = Path::new(line).components();
1140 let line = match c.next() {
1141 Some(Component::Prefix(_)) => c.as_path().to_str().unwrap(),
1142 _ => line,
1143 };
1144
1145 let mut i = 0;
1146 return scan_until_char(line, ':', &mut i) &&
1147 scan_char(line, ':', &mut i) &&
1148 scan_integer(line, &mut i) &&
1149 scan_char(line, ':', &mut i) &&
1150 scan_integer(line, &mut i) &&
1151 scan_char(line, ':', &mut i) &&
1152 scan_char(line, ' ', &mut i) &&
1153 scan_integer(line, &mut i) &&
1154 scan_char(line, ':', &mut i) &&
1155 scan_integer(line, &mut i) &&
1156 scan_char(line, ' ', &mut i) &&
1157 (scan_string(line, "error", &mut i) ||
1158 scan_string(line, "warning", &mut i) ||
1159 (expect_help && scan_string(line, "help", &mut i)) ||
1160 (expect_note && scan_string(line, "note", &mut i))
1161 );
1162 }
1163
1164 fn scan_until_char(haystack: &str, needle: char, idx: &mut usize) -> bool {
1165 if *idx >= haystack.len() {
1166 return false;
1167 }
1168 let opt = haystack[(*idx)..].find(needle);
1169 if opt.is_none() {
1170 return false;
1171 }
1172 *idx = opt.unwrap();
1173 return true;
1174 }
1175
1176 fn scan_char(haystack: &str, needle: char, idx: &mut usize) -> bool {
1177 if *idx >= haystack.len() {
1178 return false;
1179 }
1180 let ch = haystack[*idx..].chars().next().unwrap();
1181 if ch != needle {
1182 return false;
1183 }
1184 *idx += ch.len_utf8();
1185 return true;
1186 }
1187
1188 fn scan_integer(haystack: &str, idx: &mut usize) -> bool {
1189 let mut i = *idx;
1190 while i < haystack.len() {
1191 let ch = haystack[i..].chars().next().unwrap();
1192 if ch < '0' || '9' < ch {
1193 break;
1194 }
1195 i += ch.len_utf8();
1196 }
1197 if i == *idx {
1198 return false;
1199 }
1200 *idx = i;
1201 return true;
1202 }
1203
1204 fn scan_string(haystack: &str, needle: &str, idx: &mut usize) -> bool {
1205 let mut haystack_i = *idx;
1206 let mut needle_i = 0;
1207 while needle_i < needle.len() {
1208 if haystack_i >= haystack.len() {
1209 return false;
1210 }
1211 let ch = haystack[haystack_i..].chars().next().unwrap();
1212 haystack_i += ch.len_utf8();
1213 if !scan_char(needle, ch, &mut needle_i) {
1214 return false;
1215 }
1216 }
1217 *idx = haystack_i;
1218 return true;
1219 }
1220
1221 struct ProcArgs {
1222 prog: String,
1223 args: Vec<String>,
1224 }
1225
1226 struct ProcRes {
1227 status: Status,
1228 stdout: String,
1229 stderr: String,
1230 cmdline: String,
1231 }
1232
1233 enum Status {
1234 Parsed(i32),
1235 Normal(ExitStatus),
1236 }
1237
1238 impl Status {
1239 fn code(&self) -> Option<i32> {
1240 match *self {
1241 Status::Parsed(i) => Some(i),
1242 Status::Normal(ref e) => e.code(),
1243 }
1244 }
1245
1246 fn success(&self) -> bool {
1247 match *self {
1248 Status::Parsed(i) => i == 0,
1249 Status::Normal(ref e) => e.success(),
1250 }
1251 }
1252 }
1253
1254 impl fmt::Display for Status {
1255 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1256 match *self {
1257 Status::Parsed(i) => write!(f, "exit code: {}", i),
1258 Status::Normal(ref e) => e.fmt(f),
1259 }
1260 }
1261 }
1262
1263 fn compile_test(config: &Config, props: &TestProps,
1264 testpaths: &TestPaths) -> ProcRes {
1265 let aux_dir = aux_output_dir_name(config, testpaths);
1266 // FIXME (#9639): This needs to handle non-utf8 paths
1267 let link_args = vec!("-L".to_owned(),
1268 aux_dir.to_str().unwrap().to_owned());
1269 let args = make_compile_args(config,
1270 props,
1271 link_args,
1272 |a, b| TargetLocation::ThisFile(make_exe_name(a, b)), testpaths);
1273 compose_and_run_compiler(config, props, testpaths, args, None)
1274 }
1275
1276 fn document(config: &Config,
1277 props: &TestProps,
1278 testpaths: &TestPaths,
1279 out_dir: &Path)
1280 -> ProcRes {
1281 if props.build_aux_docs {
1282 for rel_ab in &props.aux_builds {
1283 let aux_testpaths = compute_aux_test_paths(config, testpaths, rel_ab);
1284 let aux_props = header::load_props(&aux_testpaths.file);
1285 let auxres = document(config, &aux_props, &aux_testpaths, out_dir);
1286 if !auxres.status.success() {
1287 return auxres;
1288 }
1289 }
1290 }
1291
1292 let aux_dir = aux_output_dir_name(config, testpaths);
1293 let mut args = vec!["-L".to_owned(),
1294 aux_dir.to_str().unwrap().to_owned(),
1295 "-o".to_owned(),
1296 out_dir.to_str().unwrap().to_owned(),
1297 testpaths.file.to_str().unwrap().to_owned()];
1298 args.extend(props.compile_flags.iter().cloned());
1299 let args = ProcArgs {
1300 prog: config.rustdoc_path.to_str().unwrap().to_owned(),
1301 args: args,
1302 };
1303 compose_and_run_compiler(config, props, testpaths, args, None)
1304 }
1305
1306 fn exec_compiled_test(config: &Config, props: &TestProps,
1307 testpaths: &TestPaths) -> ProcRes {
1308
1309 let env = props.exec_env.clone();
1310
1311 match &*config.target {
1312
1313 "arm-linux-androideabi" | "aarch64-linux-android" => {
1314 _arm_exec_compiled_test(config, props, testpaths, env)
1315 }
1316
1317 _=> {
1318 let aux_dir = aux_output_dir_name(config, testpaths);
1319 compose_and_run(config,
1320 testpaths,
1321 make_run_args(config, props, testpaths),
1322 env,
1323 config.run_lib_path.to_str().unwrap(),
1324 Some(aux_dir.to_str().unwrap()),
1325 None)
1326 }
1327 }
1328 }
1329
1330 fn compute_aux_test_paths(config: &Config,
1331 testpaths: &TestPaths,
1332 rel_ab: &str)
1333 -> TestPaths
1334 {
1335 let abs_ab = config.aux_base.join(rel_ab);
1336 TestPaths {
1337 file: abs_ab,
1338 base: testpaths.base.clone(),
1339 relative_dir: Path::new(rel_ab).parent()
1340 .map(|p| p.to_path_buf())
1341 .unwrap_or_else(|| PathBuf::new())
1342 }
1343 }
1344
1345 fn compose_and_run_compiler(config: &Config, props: &TestProps,
1346 testpaths: &TestPaths, args: ProcArgs,
1347 input: Option<String>) -> ProcRes {
1348 if !props.aux_builds.is_empty() {
1349 ensure_dir(&aux_output_dir_name(config, testpaths));
1350 }
1351
1352 let aux_dir = aux_output_dir_name(config, testpaths);
1353 // FIXME (#9639): This needs to handle non-utf8 paths
1354 let extra_link_args = vec!["-L".to_owned(),
1355 aux_dir.to_str().unwrap().to_owned()];
1356
1357 for rel_ab in &props.aux_builds {
1358 let aux_testpaths = compute_aux_test_paths(config, testpaths, rel_ab);
1359 let aux_props = header::load_props(&aux_testpaths.file);
1360 let mut crate_type = if aux_props.no_prefer_dynamic {
1361 Vec::new()
1362 } else {
1363 // We primarily compile all auxiliary libraries as dynamic libraries
1364 // to avoid code size bloat and large binaries as much as possible
1365 // for the test suite (otherwise including libstd statically in all
1366 // executables takes up quite a bit of space).
1367 //
1368 // For targets like MUSL or Emscripten, however, there is no support for
1369 // dynamic libraries so we just go back to building a normal library. Note,
1370 // however, that for MUSL if the library is built with `force_host` then
1371 // it's ok to be a dylib as the host should always support dylibs.
1372 if (config.target.contains("musl") && !aux_props.force_host) ||
1373 config.target.contains("emscripten")
1374 {
1375 vec!("--crate-type=lib".to_owned())
1376 } else {
1377 vec!("--crate-type=dylib".to_owned())
1378 }
1379 };
1380 crate_type.extend(extra_link_args.clone());
1381 let aux_args =
1382 make_compile_args(config,
1383 &aux_props,
1384 crate_type,
1385 |a,b| {
1386 let f = make_lib_name(a, &b.file, testpaths);
1387 let parent = f.parent().unwrap();
1388 TargetLocation::ThisDirectory(parent.to_path_buf())
1389 },
1390 &aux_testpaths);
1391 let auxres = compose_and_run(config,
1392 &aux_testpaths,
1393 aux_args,
1394 Vec::new(),
1395 config.compile_lib_path.to_str().unwrap(),
1396 Some(aux_dir.to_str().unwrap()),
1397 None);
1398 if !auxres.status.success() {
1399 fatal_proc_rec(
1400 None,
1401 &format!("auxiliary build of {:?} failed to compile: ",
1402 aux_testpaths.file.display()),
1403 &auxres);
1404 }
1405
1406 match &*config.target {
1407 "arm-linux-androideabi" | "aarch64-linux-android" => {
1408 _arm_push_aux_shared_library(config, testpaths);
1409 }
1410 _ => {}
1411 }
1412 }
1413
1414 compose_and_run(config,
1415 testpaths,
1416 args,
1417 props.rustc_env.clone(),
1418 config.compile_lib_path.to_str().unwrap(),
1419 Some(aux_dir.to_str().unwrap()),
1420 input)
1421 }
1422
1423 fn ensure_dir(path: &Path) {
1424 if path.is_dir() { return; }
1425 fs::create_dir_all(path).unwrap();
1426 }
1427
1428 fn compose_and_run(config: &Config,
1429 testpaths: &TestPaths,
1430 ProcArgs{ args, prog }: ProcArgs,
1431 procenv: Vec<(String, String)> ,
1432 lib_path: &str,
1433 aux_path: Option<&str>,
1434 input: Option<String>) -> ProcRes {
1435 return program_output(config, testpaths, lib_path,
1436 prog, aux_path, args, procenv, input);
1437 }
1438
1439 enum TargetLocation {
1440 ThisFile(PathBuf),
1441 ThisDirectory(PathBuf),
1442 }
1443
1444 fn make_compile_args<F>(config: &Config,
1445 props: &TestProps,
1446 extras: Vec<String> ,
1447 xform: F,
1448 testpaths: &TestPaths)
1449 -> ProcArgs where
1450 F: FnOnce(&Config, &TestPaths) -> TargetLocation,
1451 {
1452 let xform_file = xform(config, testpaths);
1453 let target = if props.force_host {
1454 &*config.host
1455 } else {
1456 &*config.target
1457 };
1458 // FIXME (#9639): This needs to handle non-utf8 paths
1459 let mut args = vec!(testpaths.file.to_str().unwrap().to_owned(),
1460 "-L".to_owned(),
1461 config.build_base.to_str().unwrap().to_owned(),
1462 format!("--target={}", target));
1463 args.extend_from_slice(&extras);
1464 if !props.no_prefer_dynamic {
1465 args.push("-C".to_owned());
1466 args.push("prefer-dynamic".to_owned());
1467 }
1468 let path = match xform_file {
1469 TargetLocation::ThisFile(path) => {
1470 args.push("-o".to_owned());
1471 path
1472 }
1473 TargetLocation::ThisDirectory(path) => {
1474 args.push("--out-dir".to_owned());
1475 path
1476 }
1477 };
1478 args.push(path.to_str().unwrap().to_owned());
1479 if props.force_host {
1480 args.extend(split_maybe_args(&config.host_rustcflags));
1481 } else {
1482 args.extend(split_maybe_args(&config.target_rustcflags));
1483 }
1484 args.extend(props.compile_flags.iter().cloned());
1485 return ProcArgs {
1486 prog: config.rustc_path.to_str().unwrap().to_owned(),
1487 args: args,
1488 };
1489 }
1490
1491 fn make_lib_name(config: &Config, auxfile: &Path, testpaths: &TestPaths) -> PathBuf {
1492 // what we return here is not particularly important, as it
1493 // happens; rustc ignores everything except for the directory.
1494 let auxname = output_testname(auxfile);
1495 aux_output_dir_name(config, testpaths).join(&auxname)
1496 }
1497
1498 fn make_exe_name(config: &Config, testpaths: &TestPaths) -> PathBuf {
1499 let mut f = output_base_name(config, testpaths);
1500 // FIXME: This is using the host architecture exe suffix, not target!
1501 if config.target == "asmjs-unknown-emscripten" {
1502 let mut fname = f.file_name().unwrap().to_os_string();
1503 fname.push(".js");
1504 f.set_file_name(&fname);
1505 } else if !env::consts::EXE_SUFFIX.is_empty() {
1506 let mut fname = f.file_name().unwrap().to_os_string();
1507 fname.push(env::consts::EXE_SUFFIX);
1508 f.set_file_name(&fname);
1509 }
1510 f
1511 }
1512
1513 fn make_run_args(config: &Config, props: &TestProps, testpaths: &TestPaths)
1514 -> ProcArgs {
1515 // If we've got another tool to run under (valgrind),
1516 // then split apart its command
1517 let mut args = split_maybe_args(&config.runtool);
1518
1519 // If this is emscripten, then run tests under nodejs
1520 if config.target == "asmjs-unknown-emscripten" {
1521 args.push("nodejs".to_owned());
1522 }
1523
1524 let exe_file = make_exe_name(config, testpaths);
1525
1526 // FIXME (#9639): This needs to handle non-utf8 paths
1527 args.push(exe_file.to_str().unwrap().to_owned());
1528
1529 // Add the arguments in the run_flags directive
1530 args.extend(split_maybe_args(&props.run_flags));
1531
1532 let prog = args.remove(0);
1533 return ProcArgs {
1534 prog: prog,
1535 args: args,
1536 };
1537 }
1538
1539 fn split_maybe_args(argstr: &Option<String>) -> Vec<String> {
1540 match *argstr {
1541 Some(ref s) => {
1542 s
1543 .split(' ')
1544 .filter_map(|s| {
1545 if s.chars().all(|c| c.is_whitespace()) {
1546 None
1547 } else {
1548 Some(s.to_owned())
1549 }
1550 }).collect()
1551 }
1552 None => Vec::new()
1553 }
1554 }
1555
1556 fn program_output(config: &Config, testpaths: &TestPaths, lib_path: &str, prog: String,
1557 aux_path: Option<&str>, args: Vec<String>,
1558 env: Vec<(String, String)>,
1559 input: Option<String>) -> ProcRes {
1560 let cmdline =
1561 {
1562 let cmdline = make_cmdline(lib_path,
1563 &prog,
1564 &args);
1565 logv(config, format!("executing {}", cmdline));
1566 cmdline
1567 };
1568 let procsrv::Result {
1569 out,
1570 err,
1571 status
1572 } = procsrv::run(lib_path,
1573 &prog,
1574 aux_path,
1575 &args,
1576 env,
1577 input).expect(&format!("failed to exec `{}`", prog));
1578 dump_output(config, testpaths, &out, &err);
1579 return ProcRes {
1580 status: Status::Normal(status),
1581 stdout: out,
1582 stderr: err,
1583 cmdline: cmdline,
1584 };
1585 }
1586
1587 fn make_cmdline(libpath: &str, prog: &str, args: &[String]) -> String {
1588 use util;
1589
1590 // Linux and mac don't require adjusting the library search path
1591 if cfg!(unix) {
1592 format!("{} {}", prog, args.join(" "))
1593 } else {
1594 // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
1595 // for diagnostic purposes
1596 fn lib_path_cmd_prefix(path: &str) -> String {
1597 format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
1598 }
1599
1600 format!("{} {} {}", lib_path_cmd_prefix(libpath), prog, args.join(" "))
1601 }
1602 }
1603
1604 fn dump_output(config: &Config, testpaths: &TestPaths, out: &str, err: &str) {
1605 dump_output_file(config, testpaths, out, "out");
1606 dump_output_file(config, testpaths, err, "err");
1607 maybe_dump_to_stdout(config, out, err);
1608 }
1609
1610 fn dump_output_file(config: &Config,
1611 testpaths: &TestPaths,
1612 out: &str,
1613 extension: &str) {
1614 let outfile = make_out_name(config, testpaths, extension);
1615 File::create(&outfile).unwrap().write_all(out.as_bytes()).unwrap();
1616 }
1617
1618 fn make_out_name(config: &Config, testpaths: &TestPaths, extension: &str) -> PathBuf {
1619 output_base_name(config, testpaths).with_extension(extension)
1620 }
1621
1622 fn aux_output_dir_name(config: &Config, testpaths: &TestPaths) -> PathBuf {
1623 let f = output_base_name(config, testpaths);
1624 let mut fname = f.file_name().unwrap().to_os_string();
1625 fname.push(&format!(".{}.libaux", config.mode));
1626 f.with_file_name(&fname)
1627 }
1628
1629 fn output_testname(filepath: &Path) -> PathBuf {
1630 PathBuf::from(filepath.file_stem().unwrap())
1631 }
1632
1633 fn output_base_name(config: &Config, testpaths: &TestPaths) -> PathBuf {
1634 let dir = config.build_base.join(&testpaths.relative_dir);
1635
1636 // Note: The directory `dir` is created during `collect_tests_from_dir`
1637 dir
1638 .join(&output_testname(&testpaths.file))
1639 .with_extension(&config.stage_id)
1640 }
1641
1642 fn maybe_dump_to_stdout(config: &Config, out: &str, err: &str) {
1643 if config.verbose {
1644 println!("------{}------------------------------", "stdout");
1645 println!("{}", out);
1646 println!("------{}------------------------------", "stderr");
1647 println!("{}", err);
1648 println!("------------------------------------------");
1649 }
1650 }
1651
1652 fn error(revision: Option<&str>, err: &str) {
1653 match revision {
1654 Some(rev) => println!("\nerror in revision `{}`: {}", rev, err),
1655 None => println!("\nerror: {}", err)
1656 }
1657 }
1658
1659 fn fatal(revision: Option<&str>, err: &str) -> ! {
1660 error(revision, err); panic!();
1661 }
1662
1663 fn fatal_proc_rec(revision: Option<&str>, err: &str, proc_res: &ProcRes) -> ! {
1664 error(revision, err);
1665 print!("\
1666 status: {}\n\
1667 command: {}\n\
1668 stdout:\n\
1669 ------------------------------------------\n\
1670 {}\n\
1671 ------------------------------------------\n\
1672 stderr:\n\
1673 ------------------------------------------\n\
1674 {}\n\
1675 ------------------------------------------\n\
1676 \n",
1677 proc_res.status, proc_res.cmdline, proc_res.stdout,
1678 proc_res.stderr);
1679 panic!();
1680 }
1681
1682 fn _arm_exec_compiled_test(config: &Config,
1683 props: &TestProps,
1684 testpaths: &TestPaths,
1685 env: Vec<(String, String)>)
1686 -> ProcRes {
1687 let args = make_run_args(config, props, testpaths);
1688 let cmdline = make_cmdline("",
1689 &args.prog,
1690 &args.args);
1691
1692 // get bare program string
1693 let mut tvec: Vec<String> = args.prog
1694 .split('/')
1695 .map(str::to_owned)
1696 .collect();
1697 let prog_short = tvec.pop().unwrap();
1698
1699 // copy to target
1700 let copy_result = procsrv::run("",
1701 &config.adb_path,
1702 None,
1703 &[
1704 "push".to_owned(),
1705 args.prog.clone(),
1706 config.adb_test_dir.clone()
1707 ],
1708 vec!(("".to_owned(), "".to_owned())),
1709 Some("".to_owned()))
1710 .expect(&format!("failed to exec `{}`", config.adb_path));
1711
1712 if config.verbose {
1713 println!("push ({}) {} {} {}",
1714 config.target,
1715 args.prog,
1716 copy_result.out,
1717 copy_result.err);
1718 }
1719
1720 logv(config, format!("executing ({}) {}", config.target, cmdline));
1721
1722 let mut runargs = Vec::new();
1723
1724 // run test via adb_run_wrapper
1725 runargs.push("shell".to_owned());
1726 for (key, val) in env {
1727 runargs.push(format!("{}={}", key, val));
1728 }
1729 runargs.push(format!("{}/../adb_run_wrapper.sh", config.adb_test_dir));
1730 runargs.push(format!("{}", config.adb_test_dir));
1731 runargs.push(format!("{}", prog_short));
1732
1733 for tv in &args.args {
1734 runargs.push(tv.to_owned());
1735 }
1736 procsrv::run("",
1737 &config.adb_path,
1738 None,
1739 &runargs,
1740 vec!(("".to_owned(), "".to_owned())), Some("".to_owned()))
1741 .expect(&format!("failed to exec `{}`", config.adb_path));
1742
1743 // get exitcode of result
1744 runargs = Vec::new();
1745 runargs.push("shell".to_owned());
1746 runargs.push("cat".to_owned());
1747 runargs.push(format!("{}/{}.exitcode", config.adb_test_dir, prog_short));
1748
1749 let procsrv::Result{ out: exitcode_out, err: _, status: _ } =
1750 procsrv::run("",
1751 &config.adb_path,
1752 None,
1753 &runargs,
1754 vec!(("".to_owned(), "".to_owned())),
1755 Some("".to_owned()))
1756 .expect(&format!("failed to exec `{}`", config.adb_path));
1757
1758 let mut exitcode: i32 = 0;
1759 for c in exitcode_out.chars() {
1760 if !c.is_numeric() { break; }
1761 exitcode = exitcode * 10 + match c {
1762 '0' ... '9' => c as i32 - ('0' as i32),
1763 _ => 101,
1764 }
1765 }
1766
1767 // get stdout of result
1768 runargs = Vec::new();
1769 runargs.push("shell".to_owned());
1770 runargs.push("cat".to_owned());
1771 runargs.push(format!("{}/{}.stdout", config.adb_test_dir, prog_short));
1772
1773 let procsrv::Result{ out: stdout_out, err: _, status: _ } =
1774 procsrv::run("",
1775 &config.adb_path,
1776 None,
1777 &runargs,
1778 vec!(("".to_owned(), "".to_owned())),
1779 Some("".to_owned()))
1780 .expect(&format!("failed to exec `{}`", config.adb_path));
1781
1782 // get stderr of result
1783 runargs = Vec::new();
1784 runargs.push("shell".to_owned());
1785 runargs.push("cat".to_owned());
1786 runargs.push(format!("{}/{}.stderr", config.adb_test_dir, prog_short));
1787
1788 let procsrv::Result{ out: stderr_out, err: _, status: _ } =
1789 procsrv::run("",
1790 &config.adb_path,
1791 None,
1792 &runargs,
1793 vec!(("".to_owned(), "".to_owned())),
1794 Some("".to_owned()))
1795 .expect(&format!("failed to exec `{}`", config.adb_path));
1796
1797 dump_output(config,
1798 testpaths,
1799 &stdout_out,
1800 &stderr_out);
1801
1802 ProcRes {
1803 status: Status::Parsed(exitcode),
1804 stdout: stdout_out,
1805 stderr: stderr_out,
1806 cmdline: cmdline
1807 }
1808 }
1809
1810 fn _arm_push_aux_shared_library(config: &Config, testpaths: &TestPaths) {
1811 let tdir = aux_output_dir_name(config, testpaths);
1812
1813 let dirs = fs::read_dir(&tdir).unwrap();
1814 for file in dirs {
1815 let file = file.unwrap().path();
1816 if file.extension().and_then(|s| s.to_str()) == Some("so") {
1817 // FIXME (#9639): This needs to handle non-utf8 paths
1818 let copy_result = procsrv::run("",
1819 &config.adb_path,
1820 None,
1821 &[
1822 "push".to_owned(),
1823 file.to_str()
1824 .unwrap()
1825 .to_owned(),
1826 config.adb_test_dir.to_owned(),
1827 ],
1828 vec!(("".to_owned(),
1829 "".to_owned())),
1830 Some("".to_owned()))
1831 .expect(&format!("failed to exec `{}`", config.adb_path));
1832
1833 if config.verbose {
1834 println!("push ({}) {:?} {} {}",
1835 config.target, file.display(),
1836 copy_result.out, copy_result.err);
1837 }
1838 }
1839 }
1840 }
1841
1842 // codegen tests (using FileCheck)
1843
1844 fn compile_test_and_save_ir(config: &Config, props: &TestProps,
1845 testpaths: &TestPaths) -> ProcRes {
1846 let aux_dir = aux_output_dir_name(config, testpaths);
1847 // FIXME (#9639): This needs to handle non-utf8 paths
1848 let mut link_args = vec!("-L".to_owned(),
1849 aux_dir.to_str().unwrap().to_owned());
1850 let llvm_args = vec!("--emit=llvm-ir".to_owned(),);
1851 link_args.extend(llvm_args);
1852 let args = make_compile_args(config,
1853 props,
1854 link_args,
1855 |a, b| TargetLocation::ThisDirectory(
1856 output_base_name(a, b).parent()
1857 .unwrap().to_path_buf()),
1858 testpaths);
1859 compose_and_run_compiler(config, props, testpaths, args, None)
1860 }
1861
1862 fn check_ir_with_filecheck(config: &Config, testpaths: &TestPaths) -> ProcRes {
1863 let irfile = output_base_name(config, testpaths).with_extension("ll");
1864 let prog = config.llvm_bin_path.as_ref().unwrap().join("FileCheck");
1865 let proc_args = ProcArgs {
1866 // FIXME (#9639): This needs to handle non-utf8 paths
1867 prog: prog.to_str().unwrap().to_owned(),
1868 args: vec!(format!("-input-file={}", irfile.to_str().unwrap()),
1869 testpaths.file.to_str().unwrap().to_owned())
1870 };
1871 compose_and_run(config, testpaths, proc_args, Vec::new(), "", None, None)
1872 }
1873
1874 fn run_codegen_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
1875 assert!(props.revisions.is_empty(), "revisions not relevant here");
1876
1877 if config.llvm_bin_path.is_none() {
1878 fatal(None, "missing --llvm-bin-path");
1879 }
1880
1881 let mut proc_res = compile_test_and_save_ir(config, props, testpaths);
1882 if !proc_res.status.success() {
1883 fatal_proc_rec(None, "compilation failed!", &proc_res);
1884 }
1885
1886 proc_res = check_ir_with_filecheck(config, testpaths);
1887 if !proc_res.status.success() {
1888 fatal_proc_rec(None,
1889 "verification with 'FileCheck' failed",
1890 &proc_res);
1891 }
1892 }
1893
1894 fn charset() -> &'static str {
1895 // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
1896 if cfg!(target_os = "bitrig") {
1897 "auto"
1898 } else if cfg!(target_os = "freebsd") {
1899 "ISO-8859-1"
1900 } else {
1901 "UTF-8"
1902 }
1903 }
1904
1905 fn run_rustdoc_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
1906 assert!(props.revisions.is_empty(), "revisions not relevant here");
1907
1908 let out_dir = output_base_name(config, testpaths);
1909 let _ = fs::remove_dir_all(&out_dir);
1910 ensure_dir(&out_dir);
1911
1912 let proc_res = document(config, props, testpaths, &out_dir);
1913 if !proc_res.status.success() {
1914 fatal_proc_rec(None, "rustdoc failed!", &proc_res);
1915 }
1916 let root = find_rust_src_root(config).unwrap();
1917
1918 let res = cmd2procres(config,
1919 testpaths,
1920 Command::new(&config.python)
1921 .arg(root.join("src/etc/htmldocck.py"))
1922 .arg(out_dir)
1923 .arg(&testpaths.file));
1924 if !res.status.success() {
1925 fatal_proc_rec(None, "htmldocck failed!", &res);
1926 }
1927 }
1928
1929 fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
1930 assert!(props.revisions.is_empty(), "revisions not relevant here");
1931
1932 let proc_res = compile_test(config, props, testpaths);
1933
1934 if !proc_res.status.success() {
1935 fatal_proc_rec(None, "compilation failed!", &proc_res);
1936 }
1937
1938 check_no_compiler_crash(None, &proc_res);
1939
1940 let prefix = "TRANS_ITEM ";
1941
1942 let actual: HashSet<String> = proc_res
1943 .stdout
1944 .lines()
1945 .filter(|line| line.starts_with(prefix))
1946 .map(|s| (&s[prefix.len()..]).to_string())
1947 .collect();
1948
1949 let expected: HashSet<String> = errors::load_errors(&testpaths.file, None)
1950 .iter()
1951 .map(|e| e.msg.trim().to_string())
1952 .collect();
1953
1954 if actual != expected {
1955 let mut missing: Vec<_> = expected.difference(&actual).collect();
1956 missing.sort();
1957
1958 let mut too_much: Vec<_> = actual.difference(&expected).collect();
1959 too_much.sort();
1960
1961 println!("Expected and actual sets of codegen-items differ.\n\
1962 These items should have been contained but were not:\n\n\
1963 {}\n\n\
1964 These items were contained but should not have been:\n\n\
1965 {}\n\n",
1966 missing.iter().fold("".to_string(), |s1, s2| s1 + "\n" + s2),
1967 too_much.iter().fold("".to_string(), |s1, s2| s1 + "\n" + s2));
1968 panic!();
1969 }
1970 }
1971
1972 fn run_incremental_test(config: &Config, props: &TestProps, testpaths: &TestPaths) {
1973 // Basic plan for a test incremental/foo/bar.rs:
1974 // - load list of revisions pass1, fail2, pass3
1975 // - each should begin with `rpass`, `rfail`, or `cfail`
1976 // - if `rpass`, expect compile and execution to succeed
1977 // - if `cfail`, expect compilation to fail
1978 // - if `rfail`, expect execution to fail
1979 // - create a directory build/foo/bar.incremental
1980 // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C pass1
1981 // - because name of revision starts with "pass", expect success
1982 // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C fail2
1983 // - because name of revision starts with "fail", expect an error
1984 // - load expected errors as usual, but filter for those that end in `[fail2]`
1985 // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C pass3
1986 // - because name of revision starts with "pass", expect success
1987 // - execute build/foo/bar.exe and save output
1988 //
1989 // FIXME -- use non-incremental mode as an oracle? That doesn't apply
1990 // to #[rustc_dirty] and clean tests I guess
1991
1992 assert!(!props.revisions.is_empty(), "incremental tests require a list of revisions");
1993
1994 let output_base_name = output_base_name(config, testpaths);
1995
1996 // Create the incremental workproduct directory.
1997 let incremental_dir = output_base_name.with_extension("incremental");
1998 if incremental_dir.exists() {
1999 fs::remove_dir_all(&incremental_dir).unwrap();
2000 }
2001 fs::create_dir_all(&incremental_dir).unwrap();
2002
2003 if config.verbose {
2004 print!("incremental_dir={}", incremental_dir.display());
2005 }
2006
2007 for revision in &props.revisions {
2008 let mut revision_props = props.clone();
2009 header::load_props_into(&mut revision_props, &testpaths.file, Some(&revision));
2010
2011 revision_props.compile_flags.extend(vec![
2012 format!("-Z"),
2013 format!("incremental={}", incremental_dir.display()),
2014 format!("--cfg"),
2015 format!("{}", revision),
2016 ]);
2017
2018 if config.verbose {
2019 print!("revision={:?} revision_props={:#?}", revision, revision_props);
2020 }
2021
2022 if revision.starts_with("rpass") {
2023 run_rpass_test_revision(config, &revision_props, testpaths, Some(&revision));
2024 } else if revision.starts_with("rfail") {
2025 run_rfail_test_revision(config, &revision_props, testpaths, Some(&revision));
2026 } else if revision.starts_with("cfail") {
2027 run_cfail_test_revision(config, &revision_props, testpaths, Some(&revision));
2028 } else {
2029 fatal(
2030 Some(revision),
2031 "revision name must begin with rpass, rfail, or cfail");
2032 }
2033 }
2034 }