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.
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
::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}
;
13 use common
::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits}
;
14 use common
::{Incremental, RunMake, Ui, MirOpt}
;
15 use errors
::{self, ErrorKind, Error}
;
17 use header
::TestProps
;
25 use std
::collections
::HashSet
;
27 use std
::fs
::{self, File}
;
28 use std
::io
::{self, BufReader}
;
29 use std
::io
::prelude
::*;
30 use std
::net
::TcpStream
;
31 use std
::path
::{Path, PathBuf}
;
32 use std
::process
::{Command, Output, ExitStatus}
;
35 pub fn run(config
: Config
, testpaths
: &TestPaths
) {
36 match &*config
.target
{
38 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
39 if !config
.adb_device_status
{
40 panic
!("android device not available");
48 // We're going to be dumping a lot of info. Start on a new line.
51 debug
!("running {:?}", testpaths
.file
.display());
52 let base_props
= TestProps
::from_file(&testpaths
.file
);
54 let base_cx
= TestCx
{ config
: &config
,
60 if base_props
.revisions
.is_empty() {
61 base_cx
.run_revision()
63 for revision
in &base_props
.revisions
{
64 let mut revision_props
= base_props
.clone();
65 revision_props
.load_from(&testpaths
.file
, Some(&revision
));
68 props
: &revision_props
,
70 revision
: Some(revision
)
72 rev_cx
.run_revision();
76 base_cx
.complete_all();
79 struct TestCx
<'test
> {
80 config
: &'test Config
,
81 props
: &'test TestProps
,
82 testpaths
: &'test TestPaths
,
83 revision
: Option
<&'test
str>
86 struct DebuggerCommands
{
87 commands
: Vec
<String
>,
88 check_lines
: Vec
<String
>,
89 breakpoint_lines
: Vec
<usize>,
92 impl<'test
> TestCx
<'test
> {
93 /// invoked once before any revisions have been processed
95 assert
!(self.revision
.is_none(), "init_all invoked for a revision");
96 match self.config
.mode
{
97 Incremental
=> self.init_incremental_test(),
102 /// Code executed for each revision in turn (or, if there are no
103 /// revisions, exactly once, with revision == None).
104 fn run_revision(&self) {
105 match self.config
.mode
{
106 CompileFail
=> self.run_cfail_test(),
107 ParseFail
=> self.run_cfail_test(),
108 RunFail
=> self.run_rfail_test(),
109 RunPass
=> self.run_rpass_test(),
110 RunPassValgrind
=> self.run_valgrind_test(),
111 Pretty
=> self.run_pretty_test(),
112 DebugInfoGdb
=> self.run_debuginfo_gdb_test(),
113 DebugInfoLldb
=> self.run_debuginfo_lldb_test(),
114 Codegen
=> self.run_codegen_test(),
115 Rustdoc
=> self.run_rustdoc_test(),
116 CodegenUnits
=> self.run_codegen_units_test(),
117 Incremental
=> self.run_incremental_test(),
118 RunMake
=> self.run_rmake_test(),
119 Ui
=> self.run_ui_test(),
120 MirOpt
=> self.run_mir_opt_test(),
124 /// Invoked after all revisions have executed.
125 fn complete_all(&self) {
126 assert
!(self.revision
.is_none(), "init_all invoked for a revision");
129 fn run_cfail_test(&self) {
130 let proc_res
= self.compile_test();
132 if self.props
.must_compile_successfully
{
133 if !proc_res
.status
.success() {
135 "test compilation failed although it shouldn't!",
139 if proc_res
.status
.success() {
141 &format
!("{} test compiled successfully!", self.config
.mode
)[..],
145 self.check_correct_failure_status(&proc_res
);
148 let output_to_check
= self.get_output(&proc_res
);
149 let expected_errors
= errors
::load_errors(&self.testpaths
.file
, self.revision
);
150 if !expected_errors
.is_empty() {
151 if !self.props
.error_patterns
.is_empty() {
152 self.fatal("both error pattern and expected errors specified");
154 self.check_expected_errors(expected_errors
, &proc_res
);
156 self.check_error_patterns(&output_to_check
, &proc_res
);
159 self.check_no_compiler_crash(&proc_res
);
160 self.check_forbid_output(&output_to_check
, &proc_res
);
163 fn run_rfail_test(&self) {
164 let proc_res
= self.compile_test();
166 if !proc_res
.status
.success() {
167 self.fatal_proc_rec("compilation failed!", &proc_res
);
170 let proc_res
= self.exec_compiled_test();
172 // The value our Makefile configures valgrind to return on failure
173 const VALGRIND_ERR
: i32 = 100;
174 if proc_res
.status
.code() == Some(VALGRIND_ERR
) {
175 self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res
);
178 let output_to_check
= self.get_output(&proc_res
);
179 self.check_correct_failure_status(&proc_res
);
180 self.check_error_patterns(&output_to_check
, &proc_res
);
183 fn get_output(&self, proc_res
: &ProcRes
) -> String
{
184 if self.props
.check_stdout
{
185 format
!("{}{}", proc_res
.stdout
, proc_res
.stderr
)
187 proc_res
.stderr
.clone()
191 fn check_correct_failure_status(&self, proc_res
: &ProcRes
) {
192 // The value the rust runtime returns on failure
193 const RUST_ERR
: i32 = 101;
194 if proc_res
.status
.code() != Some(RUST_ERR
) {
196 &format
!("failure produced the wrong error: {}",
202 fn run_rpass_test(&self) {
203 let proc_res
= self.compile_test();
205 if !proc_res
.status
.success() {
206 self.fatal_proc_rec("compilation failed!", &proc_res
);
209 let expected_errors
= errors
::load_errors(&self.testpaths
.file
, self.revision
);
210 if !expected_errors
.is_empty() {
211 self.check_expected_errors(expected_errors
, &proc_res
);
214 let proc_res
= self.exec_compiled_test();
216 if !proc_res
.status
.success() {
217 self.fatal_proc_rec("test run failed!", &proc_res
);
221 fn run_valgrind_test(&self) {
222 assert
!(self.revision
.is_none(), "revisions not relevant here");
224 if self.config
.valgrind_path
.is_none() {
225 assert
!(!self.config
.force_valgrind
);
226 return self.run_rpass_test();
229 let mut proc_res
= self.compile_test();
231 if !proc_res
.status
.success() {
232 self.fatal_proc_rec("compilation failed!", &proc_res
);
235 let mut new_config
= self.config
.clone();
236 new_config
.runtool
= new_config
.valgrind_path
.clone();
237 let new_cx
= TestCx { config: &new_config, ..*self }
;
238 proc_res
= new_cx
.exec_compiled_test();
240 if !proc_res
.status
.success() {
241 self.fatal_proc_rec("test run failed!", &proc_res
);
245 fn run_pretty_test(&self) {
246 if self.props
.pp_exact
.is_some() {
247 logv(self.config
, "testing for exact pretty-printing".to_owned());
249 logv(self.config
, "testing for converging pretty-printing".to_owned());
252 let rounds
= match self.props
.pp_exact { Some(_) => 1, None => 2 }
;
254 let mut src
= String
::new();
255 File
::open(&self.testpaths
.file
).unwrap().read_to_string(&mut src
).unwrap();
256 let mut srcs
= vec
!(src
);
259 while round
< rounds
{
260 logv(self.config
, format
!("pretty-printing round {} revision {:?}",
261 round
, self.revision
));
262 let proc_res
= self.print_source(srcs
[round
].to_owned(), &self.props
.pretty_mode
);
264 if !proc_res
.status
.success() {
265 self.fatal_proc_rec(&format
!("pretty-printing failed in round {} revision {:?}",
266 round
, self.revision
),
270 let ProcRes{ stdout, .. }
= proc_res
;
275 let mut expected
= match self.props
.pp_exact
{
277 let filepath
= self.testpaths
.file
.parent().unwrap().join(file
);
278 let mut s
= String
::new();
279 File
::open(&filepath
).unwrap().read_to_string(&mut s
).unwrap();
282 None
=> { srcs[srcs.len() - 2].clone() }
284 let mut actual
= srcs
[srcs
.len() - 1].clone();
286 if self.props
.pp_exact
.is_some() {
287 // Now we have to care about line endings
288 let cr
= "\r".to_owned();
289 actual
= actual
.replace(&cr
, "").to_owned();
290 expected
= expected
.replace(&cr
, "").to_owned();
293 self.compare_source(&expected
, &actual
);
295 // If we're only making sure that the output matches then just stop here
296 if self.props
.pretty_compare_only { return; }
298 // Finally, let's make sure it actually appears to remain valid code
299 let proc_res
= self.typecheck_source(actual
);
300 if !proc_res
.status
.success() {
301 self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res
);
304 if !self.props
.pretty_expanded { return }
306 // additionally, run `--pretty expanded` and try to build it.
307 let proc_res
= self.print_source(srcs
[round
].clone(), "expanded");
308 if !proc_res
.status
.success() {
309 self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res
);
312 let ProcRes{ stdout: expanded_src, .. }
= proc_res
;
313 let proc_res
= self.typecheck_source(expanded_src
);
314 if !proc_res
.status
.success() {
316 "pretty-printed source (expanded) does not typecheck",
321 fn print_source(&self,
325 let aux_dir
= self.aux_output_dir_name();
326 self.compose_and_run(self.make_pp_args(pretty_type
.to_owned()),
327 self.props
.exec_env
.clone(),
328 self.config
.compile_lib_path
.to_str().unwrap(),
329 Some(aux_dir
.to_str().unwrap()),
333 fn make_pp_args(&self,
336 let aux_dir
= self.aux_output_dir_name();
337 // FIXME (#9639): This needs to handle non-utf8 paths
338 let mut args
= vec
!("-".to_owned(),
339 "-Zunstable-options".to_owned(),
340 "--unpretty".to_owned(),
342 format
!("--target={}", self.config
.target
),
344 aux_dir
.to_str().unwrap().to_owned());
345 args
.extend(self.split_maybe_args(&self.config
.target_rustcflags
));
346 args
.extend(self.props
.compile_flags
.iter().cloned());
348 prog
: self.config
.rustc_path
.to_str().unwrap().to_owned(),
353 fn compare_source(&self,
356 if expected
!= actual
{
357 self.error("pretty-printed source does not match expected source");
360 ------------------------------------------\n\
362 ------------------------------------------\n\
364 ------------------------------------------\n\
366 ------------------------------------------\n\
373 fn typecheck_source(&self, src
: String
) -> ProcRes
{
374 let args
= self.make_typecheck_args();
375 self.compose_and_run_compiler(args
, Some(src
))
378 fn make_typecheck_args(&self) -> ProcArgs
{
379 let aux_dir
= self.aux_output_dir_name();
380 let target
= if self.props
.force_host
{
386 let out_dir
= self.output_base_name().with_extension("pretty-out");
387 let _
= fs
::remove_dir_all(&out_dir
);
388 self.create_dir_racy(&out_dir
);
390 // FIXME (#9639): This needs to handle non-utf8 paths
391 let mut args
= vec
!("-".to_owned(),
392 "-Zno-trans".to_owned(),
393 "--out-dir".to_owned(),
394 out_dir
.to_str().unwrap().to_owned(),
395 format
!("--target={}", target
),
397 self.config
.build_base
.to_str().unwrap().to_owned(),
399 aux_dir
.to_str().unwrap().to_owned());
400 if let Some(revision
) = self.revision
{
403 format
!("{}", revision
),
406 args
.extend(self.split_maybe_args(&self.config
.target_rustcflags
));
407 args
.extend(self.props
.compile_flags
.iter().cloned());
408 // FIXME (#9639): This needs to handle non-utf8 paths
410 prog
: self.config
.rustc_path
.to_str().unwrap().to_owned(),
415 fn run_debuginfo_gdb_test(&self) {
416 assert
!(self.revision
.is_none(), "revisions not relevant here");
418 let config
= Config
{
419 target_rustcflags
: self.cleanup_debug_info_options(&self.config
.target_rustcflags
),
420 host_rustcflags
: self.cleanup_debug_info_options(&self.config
.host_rustcflags
),
421 .. self.config
.clone()
424 let test_cx
= TestCx
{
429 test_cx
.run_debuginfo_gdb_test_no_opt();
432 fn run_debuginfo_gdb_test_no_opt(&self) {
433 let DebuggerCommands
{
437 } = self.parse_debugger_commands("gdb");
438 let mut cmds
= commands
.join("\n");
440 // compile test file (it should have 'compile-flags:-g' in the header)
441 let compiler_run_result
= self.compile_test();
442 if !compiler_run_result
.status
.success() {
443 self.fatal_proc_rec("compilation failed!", &compiler_run_result
);
446 let exe_file
= self.make_exe_name();
448 let debugger_run_result
;
449 match &*self.config
.target
{
450 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
452 cmds
= cmds
.replace("run", "continue");
454 let tool_path
= match self.config
.android_cross_path
.to_str() {
455 Some(x
) => x
.to_owned(),
456 None
=> self.fatal("cannot find android cross path")
459 // write debugger script
460 let mut script_str
= String
::with_capacity(2048);
461 script_str
.push_str(&format
!("set charset {}\n", Self::charset()));
462 script_str
.push_str(&format
!("set sysroot {}\n", tool_path
));
463 script_str
.push_str(&format
!("file {}\n", exe_file
.to_str().unwrap()));
464 script_str
.push_str("target remote :5039\n");
465 script_str
.push_str(&format
!("set solib-search-path \
466 ./{}/stage2/lib/rustlib/{}/lib/\n",
467 self.config
.host
, self.config
.target
));
468 for line
in &breakpoint_lines
{
469 script_str
.push_str(&format
!("break {:?}:{}\n",
470 self.testpaths
.file
.file_name()
475 script_str
.push_str(&cmds
);
476 script_str
.push_str("\nquit\n");
478 debug
!("script_str = {}", script_str
);
479 self.dump_output_file(&script_str
, "debugger.script");
483 &self.config
.adb_path
,
487 exe_file
.to_str().unwrap().to_owned(),
488 self.config
.adb_test_dir
.clone()
490 vec
!(("".to_owned(), "".to_owned())),
492 .expect(&format
!("failed to exec `{:?}`", self.config
.adb_path
));
495 &self.config
.adb_path
,
498 "forward".to_owned(),
499 "tcp:5039".to_owned(),
500 "tcp:5039".to_owned()
502 vec
!(("".to_owned(), "".to_owned())),
504 .expect(&format
!("failed to exec `{:?}`", self.config
.adb_path
));
506 let adb_arg
= format
!("export LD_LIBRARY_PATH={}; \
507 gdbserver{} :5039 {}/{}",
508 self.config
.adb_test_dir
.clone(),
509 if self.config
.target
.contains("aarch64")
511 self.config
.adb_test_dir
.clone(),
512 exe_file
.file_name().unwrap().to_str()
515 let mut process
= procsrv
::run_background("",
516 &self.config
.adb_path
526 .expect(&format
!("failed to exec `{:?}`", self.config
.adb_path
));
528 //waiting 1 second for gdbserver start
529 ::std
::thread
::sleep(::std
::time
::Duration
::new(1,0));
530 if TcpStream
::connect("127.0.0.1:5039").is_ok() {
535 let debugger_script
= self.make_out_name("debugger.script");
536 // FIXME (#9639): This needs to handle non-utf8 paths
538 vec
!("-quiet".to_owned(),
541 format
!("-command={}", debugger_script
.to_str().unwrap()));
543 let mut gdb_path
= tool_path
;
544 gdb_path
.push_str(&format
!("/bin/{}-gdb", self.config
.target
));
545 let procsrv
::Result
{
553 vec
!(("".to_owned(), "".to_owned())),
555 .expect(&format
!("failed to exec `{:?}`", gdb_path
));
557 let cmdline
= self.make_cmdline("",
558 &format
!("{}-gdb", self.config
.target
),
560 logv(self.config
, format
!("executing {}", cmdline
));
564 debugger_run_result
= ProcRes
{
565 status
: Status
::Normal(status
),
570 if process
.kill().is_err() {
571 println
!("Adb process is already finished.");
576 let rust_src_root
= self.find_rust_src_root()
577 .expect("Could not find Rust source root");
578 let rust_pp_module_rel_path
= Path
::new("./src/etc");
579 let rust_pp_module_abs_path
= rust_src_root
.join(rust_pp_module_rel_path
)
583 // write debugger script
584 let mut script_str
= String
::with_capacity(2048);
585 script_str
.push_str(&format
!("set charset {}\n", Self::charset()));
586 script_str
.push_str("show version\n");
588 match self.config
.gdb_version
{
589 Some(ref version
) => {
590 println
!("NOTE: compiletest thinks it is using GDB version {}",
593 if header
::gdb_version_to_int(version
) >
594 header
::gdb_version_to_int("7.4") {
595 // Add the directory containing the pretty printers to
596 // GDB's script auto loading safe path
598 &format
!("add-auto-load-safe-path {}\n",
599 rust_pp_module_abs_path
.replace(r
"\", r
"\\"))
604 println
!("NOTE: compiletest does not know which version of \
609 // The following line actually doesn't have to do anything with
610 // pretty printing, it just tells GDB to print values on one line:
611 script_str
.push_str("set print pretty off\n");
613 // Add the pretty printer directory to GDB's source-file search path
614 script_str
.push_str(&format
!("directory {}\n",
615 rust_pp_module_abs_path
));
617 // Load the target executable
618 script_str
.push_str(&format
!("file {}\n",
619 exe_file
.to_str().unwrap()
620 .replace(r
"\", r
"\\")));
622 // Add line breakpoints
623 for line
in &breakpoint_lines
{
624 script_str
.push_str(&format
!("break '{}':{}\n",
625 self.testpaths
.file
.file_name().unwrap()
630 script_str
.push_str(&cmds
);
631 script_str
.push_str("\nquit\n");
633 debug
!("script_str = {}", script_str
);
634 self.dump_output_file(&script_str
, "debugger.script");
636 // run debugger script with gdb
637 fn debugger() -> &'
static str {
638 if cfg
!(windows
) {"gdb.exe"}
else {"gdb"}
641 let debugger_script
= self.make_out_name("debugger.script");
643 // FIXME (#9639): This needs to handle non-utf8 paths
645 vec
!("-quiet".to_owned(),
648 format
!("-command={}", debugger_script
.to_str().unwrap()));
650 let proc_args
= ProcArgs
{
651 prog
: debugger().to_owned(),
655 let environment
= vec
![("PYTHONPATH".to_owned(), rust_pp_module_abs_path
)];
657 debugger_run_result
=
658 self.compose_and_run(proc_args
,
660 self.config
.run_lib_path
.to_str().unwrap(),
666 if !debugger_run_result
.status
.success() {
667 self.fatal("gdb failed to execute");
670 self.check_debugger_output(&debugger_run_result
, &check_lines
);
673 fn find_rust_src_root(&self) -> Option
<PathBuf
> {
674 let mut path
= self.config
.src_base
.clone();
675 let path_postfix
= Path
::new("src/etc/lldb_batchmode.py");
678 if path
.join(&path_postfix
).is_file() {
686 fn run_debuginfo_lldb_test(&self) {
687 assert
!(self.revision
.is_none(), "revisions not relevant here");
689 if self.config
.lldb_python_dir
.is_none() {
690 self.fatal("Can't run LLDB test because LLDB's python path is not set.");
693 let config
= Config
{
694 target_rustcflags
: self.cleanup_debug_info_options(&self.config
.target_rustcflags
),
695 host_rustcflags
: self.cleanup_debug_info_options(&self.config
.host_rustcflags
),
696 .. self.config
.clone()
700 let test_cx
= TestCx
{
705 test_cx
.run_debuginfo_lldb_test_no_opt();
708 fn run_debuginfo_lldb_test_no_opt(&self) {
709 // compile test file (it should have 'compile-flags:-g' in the header)
710 let compile_result
= self.compile_test();
711 if !compile_result
.status
.success() {
712 self.fatal_proc_rec("compilation failed!", &compile_result
);
715 let exe_file
= self.make_exe_name();
717 match self.config
.lldb_version
{
718 Some(ref version
) => {
719 println
!("NOTE: compiletest thinks it is using LLDB version {}",
723 println
!("NOTE: compiletest does not know which version of \
728 // Parse debugger commands etc from test files
729 let DebuggerCommands
{
734 } = self.parse_debugger_commands("lldb");
736 // Write debugger script:
737 // We don't want to hang when calling `quit` while the process is still running
738 let mut script_str
= String
::from("settings set auto-confirm true\n");
740 // Make LLDB emit its version, so we have it documented in the test output
741 script_str
.push_str("version\n");
743 // Switch LLDB into "Rust mode"
744 let rust_src_root
= self.find_rust_src_root().expect("Could not find Rust source root");
745 let rust_pp_module_rel_path
= Path
::new("./src/etc/lldb_rust_formatters.py");
746 let rust_pp_module_abs_path
= rust_src_root
.join(rust_pp_module_rel_path
)
751 script_str
.push_str(&format
!("command script import {}\n",
752 &rust_pp_module_abs_path
[..])[..]);
753 script_str
.push_str("type summary add --no-value ");
754 script_str
.push_str("--python-function lldb_rust_formatters.print_val ");
755 script_str
.push_str("-x \".*\" --category Rust\n");
756 script_str
.push_str("type category enable Rust\n");
758 // Set breakpoints on every line that contains the string "#break"
759 let source_file_name
= self.testpaths
.file
.file_name().unwrap().to_string_lossy();
760 for line
in &breakpoint_lines
{
761 script_str
.push_str(&format
!("breakpoint set --file '{}' --line {}\n",
766 // Append the other commands
767 for line
in &commands
{
768 script_str
.push_str(line
);
769 script_str
.push_str("\n");
772 // Finally, quit the debugger
773 script_str
.push_str("\nquit\n");
775 // Write the script into a file
776 debug
!("script_str = {}", script_str
);
777 self.dump_output_file(&script_str
, "debugger.script");
778 let debugger_script
= self.make_out_name("debugger.script");
780 // Let LLDB execute the script via lldb_batchmode.py
781 let debugger_run_result
= self.run_lldb(&exe_file
,
785 if !debugger_run_result
.status
.success() {
786 self.fatal_proc_rec("Error while running LLDB", &debugger_run_result
);
789 self.check_debugger_output(&debugger_run_result
, &check_lines
);
793 test_executable
: &Path
,
794 debugger_script
: &Path
,
795 rust_src_root
: &Path
)
797 // Prepare the lldb_batchmode which executes the debugger script
798 let lldb_script_path
= rust_src_root
.join("src/etc/lldb_batchmode.py");
799 self.cmd2procres(Command
::new(&self.config
.lldb_python
)
800 .arg(&lldb_script_path
)
801 .arg(test_executable
)
802 .arg(debugger_script
)
804 self.config
.lldb_python_dir
.as_ref().unwrap()))
807 fn cmd2procres(&self, cmd
: &mut Command
) -> ProcRes
{
808 let (status
, out
, err
) = match cmd
.output() {
809 Ok(Output { status, stdout, stderr }
) => {
811 String
::from_utf8(stdout
).unwrap(),
812 String
::from_utf8(stderr
).unwrap())
815 self.fatal(&format
!("Failed to setup Python process for \
816 LLDB script: {}", e
))
820 self.dump_output(&out
, &err
);
822 status
: Status
::Normal(status
),
825 cmdline
: format
!("{:?}", cmd
)
829 fn parse_debugger_commands(&self, debugger_prefix
: &str) -> DebuggerCommands
{
830 let command_directive
= format
!("{}-command", debugger_prefix
);
831 let check_directive
= format
!("{}-check", debugger_prefix
);
833 let mut breakpoint_lines
= vec
!();
834 let mut commands
= vec
!();
835 let mut check_lines
= vec
!();
837 let reader
= BufReader
::new(File
::open(&self.testpaths
.file
).unwrap());
838 for line
in reader
.lines() {
841 if line
.contains("#break") {
842 breakpoint_lines
.push(counter
);
845 header
::parse_name_value_directive(
847 &command_directive
).map(|cmd
| {
851 header
::parse_name_value_directive(
853 &check_directive
).map(|cmd
| {
854 check_lines
.push(cmd
)
858 self.fatal(&format
!("Error while parsing debugger commands: {}", e
))
866 check_lines
: check_lines
,
867 breakpoint_lines
: breakpoint_lines
,
871 fn cleanup_debug_info_options(&self, options
: &Option
<String
>) -> Option
<String
> {
872 if options
.is_none() {
876 // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
877 let options_to_remove
= [
880 "--debuginfo".to_owned()
883 self.split_maybe_args(options
).into_iter()
884 .filter(|x
| !options_to_remove
.contains(x
))
885 .collect
::<Vec
<String
>>();
887 Some(new_options
.join(" "))
890 fn check_debugger_output(&self, debugger_run_result
: &ProcRes
, check_lines
: &[String
]) {
891 let num_check_lines
= check_lines
.len();
893 let mut check_line_index
= 0;
894 for line
in debugger_run_result
.stdout
.lines() {
895 if check_line_index
>= num_check_lines
{
899 if check_single_line(line
, &(check_lines
[check_line_index
])[..]) {
900 check_line_index
+= 1;
903 if check_line_index
!= num_check_lines
&& num_check_lines
> 0 {
904 self.fatal_proc_rec(&format
!("line not found in debugger output: {}",
905 check_lines
[check_line_index
]),
906 debugger_run_result
);
909 fn check_single_line(line
: &str, check_line
: &str) -> bool
{
910 // Allow check lines to leave parts unspecified (e.g., uninitialized
911 // bits in the wrong case of an enum) with the notation "[...]".
912 let line
= line
.trim();
913 let check_line
= check_line
.trim();
914 let can_start_anywhere
= check_line
.starts_with("[...]");
915 let can_end_anywhere
= check_line
.ends_with("[...]");
917 let check_fragments
: Vec
<&str> = check_line
.split("[...]")
918 .filter(|frag
| !frag
.is_empty())
920 if check_fragments
.is_empty() {
924 let (mut rest
, first_fragment
) = if can_start_anywhere
{
925 match line
.find(check_fragments
[0]) {
926 Some(pos
) => (&line
[pos
+ check_fragments
[0].len() ..], 1),
933 for fragment_index
in first_fragment
.. check_fragments
.len() {
934 let current_fragment
= check_fragments
[fragment_index
];
935 match rest
.find(current_fragment
) {
937 rest
= &rest
[pos
+ current_fragment
.len() .. ];
943 if !can_end_anywhere
&& !rest
.is_empty() {
951 fn check_error_patterns(&self,
952 output_to_check
: &str,
953 proc_res
: &ProcRes
) {
954 if self.props
.error_patterns
.is_empty() {
955 if self.props
.must_compile_successfully
{
958 self.fatal(&format
!("no error pattern specified in {:?}",
959 self.testpaths
.file
.display()));
962 let mut next_err_idx
= 0;
963 let mut next_err_pat
= self.props
.error_patterns
[next_err_idx
].trim();
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
);
969 if next_err_idx
== self.props
.error_patterns
.len() {
970 debug
!("found all error patterns");
974 next_err_pat
= self.props
.error_patterns
[next_err_idx
].trim();
979 let missing_patterns
= &self.props
.error_patterns
[next_err_idx
..];
980 if missing_patterns
.len() == 1 {
982 &format
!("error pattern '{}' not found!", missing_patterns
[0]),
985 for pattern
in missing_patterns
{
986 self.error(&format
!("error pattern '{}' not found!", *pattern
));
988 self.fatal_proc_rec("multiple error patterns not found", proc_res
);
992 fn check_no_compiler_crash(&self, proc_res
: &ProcRes
) {
993 for line
in proc_res
.stderr
.lines() {
994 if line
.contains("error: internal compiler error") {
995 self.fatal_proc_rec("compiler encountered internal error", proc_res
);
1000 fn check_forbid_output(&self,
1001 output_to_check
: &str,
1002 proc_res
: &ProcRes
) {
1003 for pat
in &self.props
.forbid_output
{
1004 if output_to_check
.contains(pat
) {
1005 self.fatal_proc_rec("forbidden pattern found in compiler output", proc_res
);
1010 fn check_expected_errors(&self,
1011 expected_errors
: Vec
<errors
::Error
>,
1012 proc_res
: &ProcRes
) {
1013 if proc_res
.status
.success() &&
1014 expected_errors
.iter().any(|x
| x
.kind
== Some(ErrorKind
::Error
)) {
1015 self.fatal_proc_rec("process did not return an error status", proc_res
);
1019 format
!("{}", self.testpaths
.file
.display())
1020 .replace(r
"\", "/"); // on windows, translate all '\' path separators to '/'
1022 // If the testcase being checked contains at least one expected "help"
1023 // message, then we'll ensure that all "help" messages are expected.
1024 // Otherwise, all "help" messages reported by the compiler will be ignored.
1025 // This logic also applies to "note" messages.
1026 let expect_help
= expected_errors
.iter().any(|ee
| ee
.kind
== Some(ErrorKind
::Help
));
1027 let expect_note
= expected_errors
.iter().any(|ee
| ee
.kind
== Some(ErrorKind
::Note
));
1029 // Parse the JSON output from the compiler and extract out the messages.
1030 let actual_errors
= json
::parse_output(&file_name
, &proc_res
.stderr
, &proc_res
);
1031 let mut unexpected
= Vec
::new();
1032 let mut found
= vec
![false; expected_errors
.len()];
1033 for actual_error
in &actual_errors
{
1038 .position(|(index
, expected_error
)| {
1040 actual_error
.line_num
== expected_error
.line_num
&&
1041 (expected_error
.kind
.is_none() ||
1042 actual_error
.kind
== expected_error
.kind
) &&
1043 actual_error
.msg
.contains(&expected_error
.msg
)
1048 // found a match, everybody is happy
1049 assert
!(!found
[index
]);
1050 found
[index
] = true;
1054 if self.is_unexpected_compiler_message(actual_error
, expect_help
, expect_note
) {
1056 &format
!("{}:{}: unexpected {:?}: '{}'",
1058 actual_error
.line_num
,
1059 actual_error
.kind
.as_ref()
1060 .map_or(String
::from("message"),
1063 unexpected
.push(actual_error
.clone());
1069 let mut not_found
= Vec
::new();
1070 // anything not yet found is a problem
1071 for (index
, expected_error
) in expected_errors
.iter().enumerate() {
1074 &format
!("{}:{}: expected {} not found: {}",
1076 expected_error
.line_num
,
1077 expected_error
.kind
.as_ref()
1078 .map_or("message".into(),
1080 expected_error
.msg
));
1081 not_found
.push(expected_error
.clone());
1085 if unexpected
.len() > 0 || not_found
.len() > 0 {
1087 &format
!("{} unexpected errors found, {} expected errors not found",
1088 unexpected
.len(), not_found
.len()));
1089 print
!("status: {}\ncommand: {}\n",
1090 proc_res
.status
, proc_res
.cmdline
);
1091 if unexpected
.len() > 0 {
1092 println
!("unexpected errors (from JSON output): {:#?}\n", unexpected
);
1094 if not_found
.len() > 0 {
1095 println
!("not found errors (from test file): {:#?}\n", not_found
);
1101 /// Returns true if we should report an error about `actual_error`,
1102 /// which did not match any of the expected error. We always require
1103 /// errors/warnings to be explicitly listed, but only require
1104 /// helps/notes if there are explicit helps/notes given.
1105 fn is_unexpected_compiler_message(&self,
1106 actual_error
: &Error
,
1110 match actual_error
.kind
{
1111 Some(ErrorKind
::Help
) => expect_help
,
1112 Some(ErrorKind
::Note
) => expect_note
,
1113 Some(ErrorKind
::Error
) => true,
1114 Some(ErrorKind
::Warning
) => true,
1115 Some(ErrorKind
::Suggestion
) => false,
1120 fn compile_test(&self) -> ProcRes
{
1121 let aux_dir
= self.aux_output_dir_name();
1122 // FIXME (#9639): This needs to handle non-utf8 paths
1123 let link_args
= vec
!("-L".to_owned(),
1124 aux_dir
.to_str().unwrap().to_owned());
1125 let args
= self.make_compile_args(link_args
,
1126 &self.testpaths
.file
,
1127 TargetLocation
::ThisFile(self.make_exe_name()));
1128 self.compose_and_run_compiler(args
, None
)
1131 fn document(&self, out_dir
: &Path
) -> ProcRes
{
1132 if self.props
.build_aux_docs
{
1133 for rel_ab
in &self.props
.aux_builds
{
1134 let aux_testpaths
= self.compute_aux_test_paths(rel_ab
);
1135 let aux_props
= self.props
.from_aux_file(&aux_testpaths
.file
, self.revision
);
1136 let aux_cx
= TestCx
{
1137 config
: self.config
,
1139 testpaths
: &aux_testpaths
,
1140 revision
: self.revision
1142 let auxres
= aux_cx
.document(out_dir
);
1143 if !auxres
.status
.success() {
1149 let aux_dir
= self.aux_output_dir_name();
1150 let mut args
= vec
!["-L".to_owned(),
1151 aux_dir
.to_str().unwrap().to_owned(),
1153 out_dir
.to_str().unwrap().to_owned(),
1154 self.testpaths
.file
.to_str().unwrap().to_owned()];
1155 args
.extend(self.props
.compile_flags
.iter().cloned());
1156 let args
= ProcArgs
{
1157 prog
: self.config
.rustdoc_path
.to_str().unwrap().to_owned(),
1160 self.compose_and_run_compiler(args
, None
)
1163 fn exec_compiled_test(&self) -> ProcRes
{
1164 let env
= self.props
.exec_env
.clone();
1166 match &*self.config
.target
{
1168 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
1169 self._arm_exec_compiled_test(env
)
1173 let aux_dir
= self.aux_output_dir_name();
1174 self.compose_and_run(self.make_run_args(),
1176 self.config
.run_lib_path
.to_str().unwrap(),
1177 Some(aux_dir
.to_str().unwrap()),
1183 /// For each `aux-build: foo/bar` annotation, we check to find the
1184 /// file in a `aux` directory relative to the test itself.
1185 fn compute_aux_test_paths(&self, rel_ab
: &str) -> TestPaths
{
1186 let test_ab
= self.testpaths
.file
1188 .expect("test file path has no parent")
1191 if !test_ab
.exists() {
1192 self.fatal(&format
!("aux-build `{}` source not found", test_ab
.display()))
1197 base
: self.testpaths
.base
.clone(),
1198 relative_dir
: self.testpaths
.relative_dir
1202 .expect("aux-build path has no parent")
1207 fn compose_and_run_compiler(&self, args
: ProcArgs
, input
: Option
<String
>) -> ProcRes
{
1208 if !self.props
.aux_builds
.is_empty() {
1209 self.create_dir_racy(&self.aux_output_dir_name());
1212 let aux_dir
= self.aux_output_dir_name();
1213 // FIXME (#9639): This needs to handle non-utf8 paths
1214 let extra_link_args
= vec
!["-L".to_owned(),
1215 aux_dir
.to_str().unwrap().to_owned()];
1217 for rel_ab
in &self.props
.aux_builds
{
1218 let aux_testpaths
= self.compute_aux_test_paths(rel_ab
);
1219 let aux_props
= self.props
.from_aux_file(&aux_testpaths
.file
, self.revision
);
1220 let mut crate_type
= if aux_props
.no_prefer_dynamic
{
1223 // We primarily compile all auxiliary libraries as dynamic libraries
1224 // to avoid code size bloat and large binaries as much as possible
1225 // for the test suite (otherwise including libstd statically in all
1226 // executables takes up quite a bit of space).
1228 // For targets like MUSL or Emscripten, however, there is no support for
1229 // dynamic libraries so we just go back to building a normal library. Note,
1230 // however, that for MUSL if the library is built with `force_host` then
1231 // it's ok to be a dylib as the host should always support dylibs.
1232 if (self.config
.target
.contains("musl") && !aux_props
.force_host
) ||
1233 self.config
.target
.contains("emscripten")
1235 vec
!("--crate-type=lib".to_owned())
1237 vec
!("--crate-type=dylib".to_owned())
1240 crate_type
.extend(extra_link_args
.clone());
1242 let f
= self.make_lib_name(&self.testpaths
.file
);
1243 let parent
= f
.parent().unwrap();
1244 TargetLocation
::ThisDirectory(parent
.to_path_buf())
1246 let aux_cx
= TestCx
{
1247 config
: self.config
,
1249 testpaths
: &aux_testpaths
,
1250 revision
: self.revision
1252 let aux_args
= aux_cx
.make_compile_args(crate_type
, &aux_testpaths
.file
, aux_output
);
1253 let auxres
= aux_cx
.compose_and_run(aux_args
,
1255 aux_cx
.config
.compile_lib_path
.to_str().unwrap(),
1256 Some(aux_dir
.to_str().unwrap()),
1258 if !auxres
.status
.success() {
1259 self.fatal_proc_rec(
1260 &format
!("auxiliary build of {:?} failed to compile: ",
1261 aux_testpaths
.file
.display()),
1265 match &*self.config
.target
{
1266 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => {
1267 self._arm_push_aux_shared_library();
1273 self.compose_and_run(args
,
1274 self.props
.rustc_env
.clone(),
1275 self.config
.compile_lib_path
.to_str().unwrap(),
1276 Some(aux_dir
.to_str().unwrap()),
1280 // Like std::fs::create_dir_all, except handles concurrent calls among multiple
1281 // threads or processes.
1282 fn create_dir_racy(&self, path
: &Path
) {
1283 match fs
::create_dir(path
) {
1285 Err(ref e
) if e
.kind() == io
::ErrorKind
::AlreadyExists
=> return,
1286 Err(ref e
) if e
.kind() == io
::ErrorKind
::NotFound
=> {}
1287 Err(e
) => panic
!("failed to create dir {:?}: {}", path
, e
),
1289 self.create_dir_racy(path
.parent().unwrap());
1290 match fs
::create_dir(path
) {
1292 Err(ref e
) if e
.kind() == io
::ErrorKind
::AlreadyExists
=> {}
1293 Err(e
) => panic
!("failed to create dir {:?}: {}", path
, e
),
1297 fn compose_and_run(&self,
1298 ProcArgs{ args, prog }
: ProcArgs
,
1299 procenv
: Vec
<(String
, String
)> ,
1301 aux_path
: Option
<&str>,
1302 input
: Option
<String
>) -> ProcRes
{
1303 return self.program_output(lib_path
, prog
, aux_path
, args
, procenv
, input
);
1306 fn make_compile_args(&self,
1307 extras
: Vec
<String
> ,
1309 output_file
: TargetLocation
)
1312 let target
= if self.props
.force_host
{
1315 &*self.config
.target
1318 // FIXME (#9639): This needs to handle non-utf8 paths
1319 let mut args
= vec
!(input_file
.to_str().unwrap().to_owned(),
1321 self.config
.build_base
.to_str().unwrap().to_owned(),
1322 format
!("--target={}", target
));
1324 if let Some(revision
) = self.revision
{
1327 format
!("{}", revision
),
1331 if let Some(ref incremental_dir
) = self.props
.incremental_dir
{
1334 format
!("incremental={}", incremental_dir
.display()),
1339 match self.config
.mode
{
1344 // If we are extracting and matching errors in the new
1345 // fashion, then you want JSON mode. Old-skool error
1346 // patterns still match the raw compiler output.
1347 if self.props
.error_patterns
.is_empty() {
1348 args
.extend(["--error-format",
1351 .map(|s
| s
.to_string()));
1361 .map(|s
| s
.to_string()));
1364 let mir_dump_dir
= self.get_mir_dump_dir();
1365 self.create_dir_racy(mir_dump_dir
.as_path());
1366 let mut dir_opt
= "dump-mir-dir=".to_string();
1367 dir_opt
.push_str(mir_dump_dir
.to_str().unwrap());
1368 debug
!("dir_opt: {:?}", dir_opt
);
1382 // do not use JSON output
1386 args
.extend_from_slice(&extras
);
1387 if !self.props
.no_prefer_dynamic
{
1388 args
.push("-C".to_owned());
1389 args
.push("prefer-dynamic".to_owned());
1391 let path
= match output_file
{
1392 TargetLocation
::ThisFile(path
) => {
1393 args
.push("-o".to_owned());
1396 TargetLocation
::ThisDirectory(path
) => {
1397 args
.push("--out-dir".to_owned());
1401 args
.push(path
.to_str().unwrap().to_owned());
1402 if self.props
.force_host
{
1403 args
.extend(self.split_maybe_args(&self.config
.host_rustcflags
));
1405 args
.extend(self.split_maybe_args(&self.config
.target_rustcflags
));
1407 args
.extend(self.props
.compile_flags
.iter().cloned());
1409 prog
: self.config
.rustc_path
.to_str().unwrap().to_owned(),
1414 fn make_lib_name(&self, auxfile
: &Path
) -> PathBuf
{
1415 // what we return here is not particularly important, as it
1416 // happens; rustc ignores everything except for the directory.
1417 let auxname
= self.output_testname(auxfile
);
1418 self.aux_output_dir_name().join(&auxname
)
1421 fn make_exe_name(&self) -> PathBuf
{
1422 let mut f
= self.output_base_name();
1423 // FIXME: This is using the host architecture exe suffix, not target!
1424 if self.config
.target
== "asmjs-unknown-emscripten" {
1425 let mut fname
= f
.file_name().unwrap().to_os_string();
1427 f
.set_file_name(&fname
);
1428 } else if !env
::consts
::EXE_SUFFIX
.is_empty() {
1429 let mut fname
= f
.file_name().unwrap().to_os_string();
1430 fname
.push(env
::consts
::EXE_SUFFIX
);
1431 f
.set_file_name(&fname
);
1436 fn make_run_args(&self) -> ProcArgs
{
1437 // If we've got another tool to run under (valgrind),
1438 // then split apart its command
1439 let mut args
= self.split_maybe_args(&self.config
.runtool
);
1441 // If this is emscripten, then run tests under nodejs
1442 if self.config
.target
== "asmjs-unknown-emscripten" {
1443 args
.push("nodejs".to_owned());
1446 let exe_file
= self.make_exe_name();
1448 // FIXME (#9639): This needs to handle non-utf8 paths
1449 args
.push(exe_file
.to_str().unwrap().to_owned());
1451 // Add the arguments in the run_flags directive
1452 args
.extend(self.split_maybe_args(&self.props
.run_flags
));
1454 let prog
= args
.remove(0);
1461 fn split_maybe_args(&self, argstr
: &Option
<String
>) -> Vec
<String
> {
1467 if s
.chars().all(|c
| c
.is_whitespace()) {
1478 fn program_output(&self,
1481 aux_path
: Option
<&str>,
1483 env
: Vec
<(String
, String
)>,
1484 input
: Option
<String
>)
1488 let cmdline
= self.make_cmdline(lib_path
,
1491 logv(self.config
, format
!("executing {}", cmdline
));
1494 let procsrv
::Result
{
1498 } = procsrv
::run(lib_path
,
1503 input
).expect(&format
!("failed to exec `{}`", prog
));
1504 self.dump_output(&out
, &err
);
1506 status
: Status
::Normal(status
),
1513 fn make_cmdline(&self, libpath
: &str, prog
: &str, args
: &[String
]) -> String
{
1516 // Linux and mac don't require adjusting the library search path
1518 format
!("{} {}", prog
, args
.join(" "))
1520 // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
1521 // for diagnostic purposes
1522 fn lib_path_cmd_prefix(path
: &str) -> String
{
1523 format
!("{}=\"{}\"", util
::lib_path_env_var(), util
::make_new_path(path
))
1526 format
!("{} {} {}", lib_path_cmd_prefix(libpath
), prog
, args
.join(" "))
1530 fn dump_output(&self, out
: &str, err
: &str) {
1531 self.dump_output_file(out
, "out");
1532 self.dump_output_file(err
, "err");
1533 self.maybe_dump_to_stdout(out
, err
);
1536 fn dump_output_file(&self,
1539 let outfile
= self.make_out_name(extension
);
1540 File
::create(&outfile
).unwrap().write_all(out
.as_bytes()).unwrap();
1543 fn make_out_name(&self, extension
: &str) -> PathBuf
{
1544 self.output_base_name().with_extension(extension
)
1547 fn aux_output_dir_name(&self) -> PathBuf
{
1548 let f
= self.output_base_name();
1549 let mut fname
= f
.file_name().unwrap().to_os_string();
1550 fname
.push(&format
!(".{}.libaux", self.config
.mode
));
1551 f
.with_file_name(&fname
)
1554 fn output_testname(&self, filepath
: &Path
) -> PathBuf
{
1555 PathBuf
::from(filepath
.file_stem().unwrap())
1558 /// Given a test path like `compile-fail/foo/bar.rs` Returns a name like
1560 /// <output>/foo/bar-stage1
1561 fn output_base_name(&self) -> PathBuf
{
1562 let dir
= self.config
.build_base
.join(&self.testpaths
.relative_dir
);
1564 // Note: The directory `dir` is created during `collect_tests_from_dir`
1566 .join(&self.output_testname(&self.testpaths
.file
))
1567 .with_extension(&self.config
.stage_id
)
1570 fn maybe_dump_to_stdout(&self, out
: &str, err
: &str) {
1571 if self.config
.verbose
{
1572 println
!("------{}------------------------------", "stdout");
1573 println
!("{}", out
);
1574 println
!("------{}------------------------------", "stderr");
1575 println
!("{}", err
);
1576 println
!("------------------------------------------");
1580 fn error(&self, err
: &str) {
1581 match self.revision
{
1582 Some(rev
) => println
!("\nerror in revision `{}`: {}", rev
, err
),
1583 None
=> println
!("\nerror: {}", err
)
1587 fn fatal(&self, err
: &str) -> ! {
1588 self.error(err
); panic
!();
1591 fn fatal_proc_rec(&self, err
: &str, proc_res
: &ProcRes
) -> ! {
1593 proc_res
.fatal(None
);
1596 fn _arm_exec_compiled_test(&self, env
: Vec
<(String
, String
)>) -> ProcRes
{
1597 let args
= self.make_run_args();
1598 let cmdline
= self.make_cmdline("", &args
.prog
, &args
.args
);
1600 // get bare program string
1601 let mut tvec
: Vec
<String
> = args
.prog
1605 let prog_short
= tvec
.pop().unwrap();
1608 let copy_result
= procsrv
::run("",
1609 &self.config
.adb_path
,
1614 self.config
.adb_test_dir
.clone()
1616 vec
!(("".to_owned(), "".to_owned())),
1617 Some("".to_owned()))
1618 .expect(&format
!("failed to exec `{}`", self.config
.adb_path
));
1620 if self.config
.verbose
{
1621 println
!("push ({}) {} {} {}",
1628 logv(self.config
, format
!("executing ({}) {}", self.config
.target
, cmdline
));
1630 let mut runargs
= Vec
::new();
1632 // run test via adb_run_wrapper
1633 runargs
.push("shell".to_owned());
1634 for (key
, val
) in env
{
1635 runargs
.push(format
!("{}={}", key
, val
));
1637 runargs
.push(format
!("{}/../adb_run_wrapper.sh", self.config
.adb_test_dir
));
1638 runargs
.push(format
!("{}", self.config
.adb_test_dir
));
1639 runargs
.push(format
!("{}", prog_short
));
1641 for tv
in &args
.args
{
1642 runargs
.push(tv
.to_owned());
1645 &self.config
.adb_path
,
1648 vec
!(("".to_owned(), "".to_owned())), Some("".to_owned()))
1649 .expect(&format
!("failed to exec `{}`", self.config
.adb_path
));
1651 // get exitcode of result
1652 runargs
= Vec
::new();
1653 runargs
.push("shell".to_owned());
1654 runargs
.push("cat".to_owned());
1655 runargs
.push(format
!("{}/{}.exitcode", self.config
.adb_test_dir
, prog_short
));
1657 let procsrv
::Result{ out: exitcode_out, err: _, status: _ }
=
1659 &self.config
.adb_path
,
1662 vec
!(("".to_owned(), "".to_owned())),
1663 Some("".to_owned()))
1664 .expect(&format
!("failed to exec `{}`", self.config
.adb_path
));
1666 let mut exitcode
: i32 = 0;
1667 for c
in exitcode_out
.chars() {
1668 if !c
.is_numeric() { break; }
1669 exitcode
= exitcode
* 10 + match c
{
1670 '
0'
... '
9'
=> c
as i32 - ('
0'
as i32),
1675 // get stdout of result
1676 runargs
= Vec
::new();
1677 runargs
.push("shell".to_owned());
1678 runargs
.push("cat".to_owned());
1679 runargs
.push(format
!("{}/{}.stdout", self.config
.adb_test_dir
, prog_short
));
1681 let procsrv
::Result{ out: stdout_out, err: _, status: _ }
=
1683 &self.config
.adb_path
,
1686 vec
!(("".to_owned(), "".to_owned())),
1687 Some("".to_owned()))
1688 .expect(&format
!("failed to exec `{}`", self.config
.adb_path
));
1690 // get stderr of result
1691 runargs
= Vec
::new();
1692 runargs
.push("shell".to_owned());
1693 runargs
.push("cat".to_owned());
1694 runargs
.push(format
!("{}/{}.stderr", self.config
.adb_test_dir
, prog_short
));
1696 let procsrv
::Result{ out: stderr_out, err: _, status: _ }
=
1698 &self.config
.adb_path
,
1701 vec
!(("".to_owned(), "".to_owned())),
1702 Some("".to_owned()))
1703 .expect(&format
!("failed to exec `{}`", self.config
.adb_path
));
1705 self.dump_output(&stdout_out
, &stderr_out
);
1708 status
: Status
::Parsed(exitcode
),
1715 fn _arm_push_aux_shared_library(&self) {
1716 let tdir
= self.aux_output_dir_name();
1718 let dirs
= fs
::read_dir(&tdir
).unwrap();
1720 let file
= file
.unwrap().path();
1721 if file
.extension().and_then(|s
| s
.to_str()) == Some("so") {
1722 // FIXME (#9639): This needs to handle non-utf8 paths
1723 let copy_result
= procsrv
::run("",
1724 &self.config
.adb_path
,
1731 self.config
.adb_test_dir
.to_owned(),
1733 vec
!(("".to_owned(),
1735 Some("".to_owned()))
1736 .expect(&format
!("failed to exec `{}`", self.config
.adb_path
));
1738 if self.config
.verbose
{
1739 println
!("push ({}) {:?} {} {}",
1740 self.config
.target
, file
.display(),
1741 copy_result
.out
, copy_result
.err
);
1747 // codegen tests (using FileCheck)
1749 fn compile_test_and_save_ir(&self) -> ProcRes
{
1750 let aux_dir
= self.aux_output_dir_name();
1751 // FIXME (#9639): This needs to handle non-utf8 paths
1752 let mut link_args
= vec
!("-L".to_owned(),
1753 aux_dir
.to_str().unwrap().to_owned());
1754 let llvm_args
= vec
!("--emit=llvm-ir".to_owned(),);
1755 link_args
.extend(llvm_args
);
1756 let args
= self.make_compile_args(link_args
,
1757 &self.testpaths
.file
,
1758 TargetLocation
::ThisDirectory(
1759 self.output_base_name().parent()
1762 self.compose_and_run_compiler(args
, None
)
1765 fn check_ir_with_filecheck(&self) -> ProcRes
{
1766 let irfile
= self.output_base_name().with_extension("ll");
1767 let prog
= self.config
.llvm_filecheck
.as_ref().unwrap();
1768 let proc_args
= ProcArgs
{
1769 // FIXME (#9639): This needs to handle non-utf8 paths
1770 prog
: prog
.to_str().unwrap().to_owned(),
1771 args
: vec
!(format
!("-input-file={}", irfile
.to_str().unwrap()),
1772 self.testpaths
.file
.to_str().unwrap().to_owned())
1774 self.compose_and_run(proc_args
, Vec
::new(), "", None
, None
)
1777 fn run_codegen_test(&self) {
1778 assert
!(self.revision
.is_none(), "revisions not relevant here");
1780 if self.config
.llvm_filecheck
.is_none() {
1781 self.fatal("missing --llvm-filecheck");
1784 let mut proc_res
= self.compile_test_and_save_ir();
1785 if !proc_res
.status
.success() {
1786 self.fatal_proc_rec("compilation failed!", &proc_res
);
1789 proc_res
= self.check_ir_with_filecheck();
1790 if !proc_res
.status
.success() {
1791 self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res
);
1795 fn charset() -> &'
static str {
1796 // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset
1797 if cfg
!(target_os
= "bitrig") {
1799 } else if cfg
!(target_os
= "freebsd") {
1806 fn run_rustdoc_test(&self) {
1807 assert
!(self.revision
.is_none(), "revisions not relevant here");
1809 let out_dir
= self.output_base_name();
1810 let _
= fs
::remove_dir_all(&out_dir
);
1811 self.create_dir_racy(&out_dir
);
1813 let proc_res
= self.document(&out_dir
);
1814 if !proc_res
.status
.success() {
1815 self.fatal_proc_rec("rustdoc failed!", &proc_res
);
1817 let root
= self.find_rust_src_root().unwrap();
1819 let res
= self.cmd2procres(Command
::new(&self.config
.docck_python
)
1820 .arg(root
.join("src/etc/htmldocck.py"))
1822 .arg(&self.testpaths
.file
));
1823 if !res
.status
.success() {
1824 self.fatal_proc_rec("htmldocck failed!", &res
);
1828 fn run_codegen_units_test(&self) {
1829 assert
!(self.revision
.is_none(), "revisions not relevant here");
1831 let proc_res
= self.compile_test();
1833 if !proc_res
.status
.success() {
1834 self.fatal_proc_rec("compilation failed!", &proc_res
);
1837 self.check_no_compiler_crash(&proc_res
);
1839 const PREFIX
: &'
static str = "TRANS_ITEM ";
1840 const CGU_MARKER
: &'
static str = "@@";
1842 let actual
: Vec
<TransItem
> = proc_res
1845 .filter(|line
| line
.starts_with(PREFIX
))
1846 .map(str_to_trans_item
)
1849 let expected
: Vec
<TransItem
> = errors
::load_errors(&self.testpaths
.file
, None
)
1851 .map(|e
| str_to_trans_item(&e
.msg
[..]))
1854 let mut missing
= Vec
::new();
1855 let mut wrong_cgus
= Vec
::new();
1857 for expected_item
in &expected
{
1858 let actual_item_with_same_name
= actual
.iter()
1859 .find(|ti
| ti
.name
== expected_item
.name
);
1861 if let Some(actual_item
) = actual_item_with_same_name
{
1862 if !expected_item
.codegen_units
.is_empty() {
1863 // Also check for codegen units
1864 if expected_item
.codegen_units
!= actual_item
.codegen_units
{
1865 wrong_cgus
.push((expected_item
.clone(), actual_item
.clone()));
1869 missing
.push(expected_item
.string
.clone());
1873 let unexpected
: Vec
<_
> =
1875 .filter(|acgu
| !expected
.iter().any(|ecgu
| acgu
.name
== ecgu
.name
))
1876 .map(|acgu
| acgu
.string
.clone())
1879 if !missing
.is_empty() {
1882 println
!("\nThese items should have been contained but were not:\n");
1884 for item
in &missing
{
1885 println
!("{}", item
);
1891 if !unexpected
.is_empty() {
1893 let mut sorted
= unexpected
.clone();
1898 println
!("\nThese items were contained but should not have been:\n");
1900 for item
in sorted
{
1901 println
!("{}", item
);
1907 if !wrong_cgus
.is_empty() {
1908 wrong_cgus
.sort_by_key(|pair
| pair
.0.name
.clone());
1909 println
!("\nThe following items were assigned to wrong codegen units:\n");
1911 for &(ref expected_item
, ref actual_item
) in &wrong_cgus
{
1912 println
!("{}", expected_item
.name
);
1913 println
!(" expected: {}", codegen_units_to_str(&expected_item
.codegen_units
));
1914 println
!(" actual: {}", codegen_units_to_str(&actual_item
.codegen_units
));
1919 if !(missing
.is_empty() && unexpected
.is_empty() && wrong_cgus
.is_empty())
1924 #[derive(Clone, Eq, PartialEq)]
1927 codegen_units
: HashSet
<String
>,
1931 // [TRANS_ITEM] name [@@ (cgu)+]
1932 fn str_to_trans_item(s
: &str) -> TransItem
{
1933 let s
= if s
.starts_with(PREFIX
) {
1934 (&s
[PREFIX
.len()..]).trim()
1939 let full_string
= format
!("{}{}", PREFIX
, s
.trim().to_owned());
1941 let parts
: Vec
<&str> = s
.split(CGU_MARKER
)
1943 .filter(|s
| !s
.is_empty())
1946 let name
= parts
[0].trim();
1948 let cgus
= if parts
.len() > 1 {
1949 let cgus_str
= parts
[1];
1953 .filter(|s
| !s
.is_empty())
1962 name
: name
.to_owned(),
1963 codegen_units
: cgus
,
1964 string
: full_string
,
1968 fn codegen_units_to_str(cgus
: &HashSet
<String
>) -> String
1970 let mut cgus
: Vec
<_
> = cgus
.iter().collect();
1973 let mut string
= String
::new();
1975 string
.push_str(&cgu
[..]);
1976 string
.push_str(" ");
1983 fn init_incremental_test(&self) {
1984 // (See `run_incremental_test` for an overview of how incremental tests work.)
1986 // Before any of the revisions have executed, create the
1987 // incremental workproduct directory. Delete any old
1988 // incremental work products that may be there from prior
1990 let incremental_dir
= self.incremental_dir();
1991 if incremental_dir
.exists() {
1992 // Canonicalizing the path will convert it to the //?/ format
1993 // on Windows, which enables paths longer than 260 character
1994 let canonicalized
= incremental_dir
.canonicalize().unwrap();
1995 fs
::remove_dir_all(canonicalized
).unwrap();
1997 fs
::create_dir_all(&incremental_dir
).unwrap();
1999 if self.config
.verbose
{
2000 print
!("init_incremental_test: incremental_dir={}", incremental_dir
.display());
2004 fn run_incremental_test(&self) {
2005 // Basic plan for a test incremental/foo/bar.rs:
2006 // - load list of revisions rpass1, cfail2, rpass3
2007 // - each should begin with `rpass`, `cfail`, or `cfail`
2008 // - if `rpass`, expect compile and execution to succeed
2009 // - if `cfail`, expect compilation to fail
2010 // - if `rfail`, expect execution to fail
2011 // - create a directory build/foo/bar.incremental
2012 // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass1
2013 // - because name of revision starts with "rpass", expect success
2014 // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C cfail2
2015 // - because name of revision starts with "cfail", expect an error
2016 // - load expected errors as usual, but filter for those that end in `[rfail2]`
2017 // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C rpass3
2018 // - because name of revision starts with "rpass", expect success
2019 // - execute build/foo/bar.exe and save output
2021 // FIXME -- use non-incremental mode as an oracle? That doesn't apply
2022 // to #[rustc_dirty] and clean tests I guess
2024 let revision
= self.revision
.expect("incremental tests require a list of revisions");
2026 // Incremental workproduct directory should have already been created.
2027 let incremental_dir
= self.incremental_dir();
2028 assert
!(incremental_dir
.exists(), "init_incremental_test failed to create incremental dir");
2030 // Add an extra flag pointing at the incremental directory.
2031 let mut revision_props
= self.props
.clone();
2032 revision_props
.incremental_dir
= Some(incremental_dir
);
2033 revision_props
.compile_flags
.push(String
::from("-Zincremental-info"));
2035 let revision_cx
= TestCx
{
2036 config
: self.config
,
2037 props
: &revision_props
,
2038 testpaths
: self.testpaths
,
2039 revision
: self.revision
,
2042 if self.config
.verbose
{
2043 print
!("revision={:?} revision_props={:#?}", revision
, revision_props
);
2046 if revision
.starts_with("rpass") {
2047 revision_cx
.run_rpass_test();
2048 } else if revision
.starts_with("rfail") {
2049 revision_cx
.run_rfail_test();
2050 } else if revision
.starts_with("cfail") {
2051 revision_cx
.run_cfail_test();
2054 "revision name must begin with rpass, rfail, or cfail");
2058 /// Directory where incremental work products are stored.
2059 fn incremental_dir(&self) -> PathBuf
{
2060 self.output_base_name().with_extension("inc")
2063 fn run_rmake_test(&self) {
2064 // FIXME(#11094): we should fix these tests
2065 if self.config
.host
!= self.config
.target
{
2069 let cwd
= env
::current_dir().unwrap();
2070 let src_root
= self.config
.src_base
.parent().unwrap()
2073 let src_root
= cwd
.join(&src_root
);
2075 let tmpdir
= cwd
.join(self.output_base_name());
2076 if tmpdir
.exists() {
2077 self.aggressive_rm_rf(&tmpdir
).unwrap();
2079 self.create_dir_racy(&tmpdir
);
2081 let mut cmd
= Command
::new("make");
2082 cmd
.current_dir(&self.testpaths
.file
)
2083 .env("TARGET", &self.config
.target
)
2084 .env("PYTHON", &self.config
.docck_python
)
2086 .env("RUST_BUILD_STAGE", &self.config
.stage_id
)
2087 .env("RUSTC", cwd
.join(&self.config
.rustc_path
))
2088 .env("RUSTDOC", cwd
.join(&self.config
.rustdoc_path
))
2089 .env("TMPDIR", &tmpdir
)
2090 .env("LD_LIB_PATH_ENVVAR", procsrv
::dylib_env_var())
2091 .env("HOST_RPATH_DIR", cwd
.join(&self.config
.compile_lib_path
))
2092 .env("TARGET_RPATH_DIR", cwd
.join(&self.config
.run_lib_path
))
2093 .env("LLVM_COMPONENTS", &self.config
.llvm_components
)
2094 .env("LLVM_CXXFLAGS", &self.config
.llvm_cxxflags
);
2096 if self.config
.target
.contains("msvc") {
2097 // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe`
2098 // and that `lib.exe` lives next to it.
2099 let lib
= Path
::new(&self.config
.cc
).parent().unwrap().join("lib.exe");
2101 // MSYS doesn't like passing flags of the form `/foo` as it thinks it's
2102 // a path and instead passes `C:\msys64\foo`, so convert all
2103 // `/`-arguments to MSVC here to `-` arguments.
2104 let cflags
= self.config
.cflags
.split(' '
).map(|s
| s
.replace("/", "-"))
2105 .collect
::<Vec
<_
>>().join(" ");
2107 cmd
.env("IS_MSVC", "1")
2108 .env("MSVC_LIB", format
!("'{}' -nologo", lib
.display()))
2109 .env("CC", format
!("'{}' {}", self.config
.cc
, cflags
))
2110 .env("CXX", &self.config
.cxx
);
2112 cmd
.env("CC", format
!("{} {}", self.config
.cc
, self.config
.cflags
))
2113 .env("CXX", format
!("{} {}", self.config
.cxx
, self.config
.cflags
));
2116 let output
= cmd
.output().expect("failed to spawn `make`");
2117 if !output
.status
.success() {
2119 status
: Status
::Normal(output
.status
),
2120 stdout
: String
::from_utf8_lossy(&output
.stdout
).into_owned(),
2121 stderr
: String
::from_utf8_lossy(&output
.stderr
).into_owned(),
2122 cmdline
: format
!("{:?}", cmd
),
2124 self.fatal_proc_rec("make failed", &res
);
2128 fn aggressive_rm_rf(&self, path
: &Path
) -> io
::Result
<()> {
2129 for e
in path
.read_dir()?
{
2131 let path
= entry
.path();
2132 if entry
.file_type()?
.is_dir() {
2133 self.aggressive_rm_rf(&path
)?
;
2135 // Remove readonly files as well on windows (by default we can't)
2136 fs
::remove_file(&path
).or_else(|e
| {
2137 if cfg
!(windows
) && e
.kind() == io
::ErrorKind
::PermissionDenied
{
2138 let mut meta
= entry
.metadata()?
.permissions();
2139 meta
.set_readonly(false);
2140 fs
::set_permissions(&path
, meta
)?
;
2141 fs
::remove_file(&path
)
2148 fs
::remove_dir(path
)
2151 fn run_ui_test(&self) {
2152 println
!("ui: {}", self.testpaths
.file
.display());
2154 let proc_res
= self.compile_test();
2156 let expected_stderr_path
= self.expected_output_path("stderr");
2157 let expected_stderr
= self.load_expected_output(&expected_stderr_path
);
2159 let expected_stdout_path
= self.expected_output_path("stdout");
2160 let expected_stdout
= self.load_expected_output(&expected_stdout_path
);
2162 let normalized_stdout
= self.normalize_output(&proc_res
.stdout
);
2163 let normalized_stderr
= self.normalize_output(&proc_res
.stderr
);
2166 errors
+= self.compare_output("stdout", &normalized_stdout
, &expected_stdout
);
2167 errors
+= self.compare_output("stderr", &normalized_stderr
, &expected_stderr
);
2170 println
!("To update references, run this command from build directory:");
2171 let relative_path_to_file
=
2172 self.testpaths
.relative_dir
2173 .join(self.testpaths
.file
.file_name().unwrap());
2174 println
!("{}/update-references.sh '{}' '{}'",
2175 self.config
.src_base
.display(),
2176 self.config
.build_base
.display(),
2177 relative_path_to_file
.display());
2178 self.fatal_proc_rec(&format
!("{} errors occurred comparing output.", errors
),
2183 fn run_mir_opt_test(&self) {
2184 let proc_res
= self.compile_test();
2186 if !proc_res
.status
.success() {
2187 self.fatal_proc_rec("compilation failed!", &proc_res
);
2190 let proc_res
= self.exec_compiled_test();
2192 if !proc_res
.status
.success() {
2193 self.fatal_proc_rec("test run failed!", &proc_res
);
2195 self.check_mir_dump();
2198 fn check_mir_dump(&self) {
2199 let mut test_file_contents
= String
::new();
2200 fs
::File
::open(self.testpaths
.file
.clone()).unwrap()
2201 .read_to_string(&mut test_file_contents
)
2203 if let Some(idx
) = test_file_contents
.find("// END RUST SOURCE") {
2204 let (_
, tests_text
) = test_file_contents
.split_at(idx
+ "// END_RUST SOURCE".len());
2205 let tests_text_str
= String
::from(tests_text
);
2206 let mut curr_test
: Option
<&str> = None
;
2207 let mut curr_test_contents
= Vec
::new();
2208 for l
in tests_text_str
.lines() {
2209 debug
!("line: {:?}", l
);
2210 if l
.starts_with("// START ") {
2211 let (_
, t
) = l
.split_at("// START ".len());
2212 curr_test
= Some(t
);
2213 } else if l
.starts_with("// END") {
2214 let (_
, t
) = l
.split_at("// END ".len());
2215 if Some(t
) != curr_test
{
2216 panic
!("mismatched START END test name");
2218 self.compare_mir_test_output(curr_test
.unwrap(), &curr_test_contents
);
2220 curr_test_contents
.clear();
2221 } else if l
.is_empty() {
2223 } else if l
.starts_with("// ") {
2224 let (_
, test_content
) = l
.split_at("// ".len());
2225 curr_test_contents
.push(test_content
);
2231 fn compare_mir_test_output(&self, test_name
: &str, expected_content
: &Vec
<&str>) {
2232 let mut output_file
= PathBuf
::new();
2233 output_file
.push(self.get_mir_dump_dir());
2234 output_file
.push(test_name
);
2235 debug
!("comparing the contests of: {:?}", output_file
);
2236 debug
!("with: {:?}", expected_content
);
2238 let mut dumped_file
= fs
::File
::open(output_file
.clone()).unwrap();
2239 let mut dumped_string
= String
::new();
2240 dumped_file
.read_to_string(&mut dumped_string
).unwrap();
2241 let mut dumped_lines
= dumped_string
.lines().filter(|l
| !l
.is_empty());
2242 let mut expected_lines
= expected_content
.iter().filter(|l
| !l
.is_empty());
2244 // We expect each non-empty line from expected_content to appear
2245 // in the dump in order, but there may be extra lines interleaved
2246 while let Some(expected_line
) = expected_lines
.next() {
2247 let e_norm
= normalize_mir_line(expected_line
);
2248 if e_norm
.is_empty() {
2251 let mut found
= false;
2252 while let Some(dumped_line
) = dumped_lines
.next() {
2253 let d_norm
= normalize_mir_line(dumped_line
);
2254 debug
!("found: {:?}", d_norm
);
2255 debug
!("expected: {:?}", e_norm
);
2256 if e_norm
== d_norm
{
2262 panic
!("ran out of mir dump output to match against");
2267 fn get_mir_dump_dir(&self) -> PathBuf
{
2268 let mut mir_dump_dir
= PathBuf
::from(self.config
.build_base
2272 debug
!("input_file: {:?}", self.testpaths
.file
);
2273 mir_dump_dir
.push(self.testpaths
.file
.file_stem().unwrap().to_str().unwrap());
2277 fn normalize_output(&self, output
: &str) -> String
{
2278 let parent_dir
= self.testpaths
.file
.parent().unwrap();
2279 let parent_dir_str
= parent_dir
.display().to_string();
2280 output
.replace(&parent_dir_str
, "$DIR")
2281 .replace("\\", "/") // normalize for paths on windows
2282 .replace("\r\n", "\n") // normalize for linebreaks on windows
2283 .replace("\t", "\\t") // makes tabs visible
2286 fn expected_output_path(&self, kind
: &str) -> PathBuf
{
2287 let extension
= match self.revision
{
2288 Some(r
) => format
!("{}.{}", r
, kind
),
2289 None
=> kind
.to_string(),
2291 self.testpaths
.file
.with_extension(extension
)
2294 fn load_expected_output(&self, path
: &Path
) -> String
{
2296 return String
::new();
2299 let mut result
= String
::new();
2300 match File
::open(path
).and_then(|mut f
| f
.read_to_string(&mut result
)) {
2303 self.fatal(&format
!("failed to load expected output from `{}`: {}",
2309 fn compare_output(&self, kind
: &str, actual
: &str, expected
: &str) -> usize {
2310 if actual
== expected
{
2314 println
!("normalized {}:\n{}\n", kind
, actual
);
2315 println
!("expected {}:\n{}\n", kind
, expected
);
2316 println
!("diff of {}:\n", kind
);
2317 for line
in uidiff
::diff_lines(actual
, expected
) {
2318 println
!("{}", line
);
2321 let output_file
= self.output_base_name().with_extension(kind
);
2322 match File
::create(&output_file
).and_then(|mut f
| f
.write_all(actual
.as_bytes())) {
2325 self.fatal(&format
!("failed to write {} to `{}`: {}",
2326 kind
, output_file
.display(), e
))
2330 println
!("\nThe actual {0} differed from the expected {0}.", kind
);
2331 println
!("Actual {} saved to {}", kind
, output_file
.display());
2341 pub struct ProcRes
{
2354 pub fn fatal(&self, err
: Option
<&str>) -> ! {
2355 if let Some(e
) = err
{
2356 println
!("\nerror: {}", e
);
2362 ------------------------------------------\n\
2364 ------------------------------------------\n\
2366 ------------------------------------------\n\
2368 ------------------------------------------\n\
2370 self.status
, self.cmdline
, self.stdout
,
2377 fn code(&self) -> Option
<i32> {
2379 Status
::Parsed(i
) => Some(i
),
2380 Status
::Normal(ref e
) => e
.code(),
2384 fn success(&self) -> bool
{
2386 Status
::Parsed(i
) => i
== 0,
2387 Status
::Normal(ref e
) => e
.success(),
2392 impl fmt
::Display
for Status
{
2393 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
2395 Status
::Parsed(i
) => write
!(f
, "exit code: {}", i
),
2396 Status
::Normal(ref e
) => e
.fmt(f
),
2401 enum TargetLocation
{
2403 ThisDirectory(PathBuf
),
2406 fn normalize_mir_line(line
: &str) -> String
{
2407 let no_comments
= if let Some(idx
) = line
.find("//") {
2408 let (l
, _
) = line
.split_at(idx
);
2413 no_comments
.replace(char::is_whitespace
, "")