/src/compiletest/runtest.rs
Rust | 1792 lines | 1611 code | 128 blank | 53 comment | 88 complexity | 6fb3ad5f3ec9617106629b4917f6805e MD5 | raw file
Possible License(s): AGPL-1.0, BSD-2-Clause, 0BSD, Apache-2.0, MIT, LGPL-2.0
Large files files are truncated, but you can click here to view the full file
- // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
- // file at the top-level directory of this distribution and at
- // http://rust-lang.org/COPYRIGHT.
- //
- // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
- // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
- // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
- // option. This file may not be copied, modified, or distributed
- // except according to those terms.
- use self::TargetLocation::*;
- use common::Config;
- use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
- use common::{Codegen, DebugInfoLldb, DebugInfoGdb};
- use errors;
- use header::TestProps;
- use header;
- use procsrv;
- use util::logv;
- use std::env;
- use std::fmt;
- use std::fs::{self, File};
- use std::io::BufReader;
- use std::io::prelude::*;
- use std::iter::repeat;
- use std::net::TcpStream;
- use std::path::{Path, PathBuf};
- use std::process::{Command, Output, ExitStatus};
- use std::str;
- use std::time::Duration;
- use test::MetricMap;
- pub fn run(config: Config, testfile: &Path) {
- match &*config.target {
- "arm-linux-androideabi" | "aarch64-linux-android" => {
- if !config.adb_device_status {
- panic!("android device not available");
- }
- }
- _=> { }
- }
- let mut _mm = MetricMap::new();
- run_metrics(config, testfile, &mut _mm);
- }
- pub fn run_metrics(config: Config, testfile: &Path, mm: &mut MetricMap) {
- if config.verbose {
- // We're going to be dumping a lot of info. Start on a new line.
- print!("\n\n");
- }
- debug!("running {:?}", testfile.display());
- let props = header::load_props(&testfile);
- debug!("loaded props");
- match config.mode {
- CompileFail => run_cfail_test(&config, &props, &testfile),
- ParseFail => run_cfail_test(&config, &props, &testfile),
- RunFail => run_rfail_test(&config, &props, &testfile),
- RunPass => run_rpass_test(&config, &props, &testfile),
- RunPassValgrind => run_valgrind_test(&config, &props, &testfile),
- Pretty => run_pretty_test(&config, &props, &testfile),
- DebugInfoGdb => run_debuginfo_gdb_test(&config, &props, &testfile),
- DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testfile),
- Codegen => run_codegen_test(&config, &props, &testfile, mm),
- }
- }
- fn get_output(props: &TestProps, proc_res: &ProcRes) -> String {
- if props.check_stdout {
- format!("{}{}", proc_res.stdout, proc_res.stderr)
- } else {
- proc_res.stderr.clone()
- }
- }
- fn run_cfail_test(config: &Config, props: &TestProps, testfile: &Path) {
- let proc_res = compile_test(config, props, testfile);
- if proc_res.status.success() {
- fatal_proc_rec(&format!("{} test compiled successfully!", config.mode)[..],
- &proc_res);
- }
- check_correct_failure_status(&proc_res);
- if proc_res.status.success() {
- fatal("process did not return an error status");
- }
- let output_to_check = get_output(props, &proc_res);
- let expected_errors = errors::load_errors(testfile);
- if !expected_errors.is_empty() {
- if !props.error_patterns.is_empty() {
- fatal("both error pattern and expected errors specified");
- }
- check_expected_errors(expected_errors, testfile, &proc_res);
- } else {
- check_error_patterns(props, testfile, &output_to_check, &proc_res);
- }
- check_no_compiler_crash(&proc_res);
- check_forbid_output(props, &output_to_check, &proc_res);
- }
- fn run_rfail_test(config: &Config, props: &TestProps, testfile: &Path) {
- let proc_res = if !config.jit {
- let proc_res = compile_test(config, props, testfile);
- if !proc_res.status.success() {
- fatal_proc_rec("compilation failed!", &proc_res);
- }
- exec_compiled_test(config, props, testfile)
- } else {
- jit_test(config, props, testfile)
- };
- // The value our Makefile configures valgrind to return on failure
- const VALGRIND_ERR: i32 = 100;
- if proc_res.status.code() == Some(VALGRIND_ERR) {
- fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
- }
- let output_to_check = get_output(props, &proc_res);
- check_correct_failure_status(&proc_res);
- check_error_patterns(props, testfile, &output_to_check, &proc_res);
- }
- fn check_correct_failure_status(proc_res: &ProcRes) {
- // The value the rust runtime returns on failure
- const RUST_ERR: i32 = 101;
- if proc_res.status.code() != Some(RUST_ERR) {
- fatal_proc_rec(
- &format!("failure produced the wrong error: {}",
- proc_res.status),
- proc_res);
- }
- }
- fn run_rpass_test(config: &Config, props: &TestProps, testfile: &Path) {
- if !config.jit {
- let mut proc_res = compile_test(config, props, testfile);
- if !proc_res.status.success() {
- fatal_proc_rec("compilation failed!", &proc_res);
- }
- proc_res = exec_compiled_test(config, props, testfile);
- if !proc_res.status.success() {
- fatal_proc_rec("test run failed!", &proc_res);
- }
- } else {
- let proc_res = jit_test(config, props, testfile);
- if !proc_res.status.success() {
- fatal_proc_rec("jit failed!", &proc_res);
- }
- }
- }
- fn run_valgrind_test(config: &Config, props: &TestProps, testfile: &Path) {
- if config.valgrind_path.is_none() {
- assert!(!config.force_valgrind);
- return run_rpass_test(config, props, testfile);
- }
- let mut proc_res = compile_test(config, props, testfile);
- if !proc_res.status.success() {
- fatal_proc_rec("compilation failed!", &proc_res);
- }
- let mut new_config = config.clone();
- new_config.runtool = new_config.valgrind_path.clone();
- proc_res = exec_compiled_test(&new_config, props, testfile);
- if !proc_res.status.success() {
- fatal_proc_rec("test run failed!", &proc_res);
- }
- }
- fn run_pretty_test(config: &Config, props: &TestProps, testfile: &Path) {
- if props.pp_exact.is_some() {
- logv(config, "testing for exact pretty-printing".to_string());
- } else {
- logv(config, "testing for converging pretty-printing".to_string());
- }
- let rounds =
- match props.pp_exact { Some(_) => 1, None => 2 };
- let mut src = String::new();
- File::open(testfile).unwrap().read_to_string(&mut src).unwrap();
- let mut srcs = vec!(src);
- let mut round = 0;
- while round < rounds {
- logv(config, format!("pretty-printing round {}", round));
- let proc_res = print_source(config,
- props,
- testfile,
- srcs[round].to_string(),
- &props.pretty_mode);
- if !proc_res.status.success() {
- fatal_proc_rec(&format!("pretty-printing failed in round {}", round),
- &proc_res);
- }
- let ProcRes{ stdout, .. } = proc_res;
- srcs.push(stdout);
- round += 1;
- }
- let mut expected = match props.pp_exact {
- Some(ref file) => {
- let filepath = testfile.parent().unwrap().join(file);
- let mut s = String::new();
- File::open(&filepath).unwrap().read_to_string(&mut s).unwrap();
- s
- }
- None => { srcs[srcs.len() - 2].clone() }
- };
- let mut actual = srcs[srcs.len() - 1].clone();
- if props.pp_exact.is_some() {
- // Now we have to care about line endings
- let cr = "\r".to_string();
- actual = actual.replace(&cr, "").to_string();
- expected = expected.replace(&cr, "").to_string();
- }
- compare_source(&expected, &actual);
- // If we're only making sure that the output matches then just stop here
- if props.pretty_compare_only { return; }
- // Finally, let's make sure it actually appears to remain valid code
- let proc_res = typecheck_source(config, props, testfile, actual);
- if !proc_res.status.success() {
- fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
- }
- if props.no_pretty_expanded { return }
- // additionally, run `--pretty expanded` and try to build it.
- let proc_res = print_source(config, props, testfile, srcs[round].clone(), "expanded");
- if !proc_res.status.success() {
- fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
- }
- let ProcRes{ stdout: expanded_src, .. } = proc_res;
- let proc_res = typecheck_source(config, props, testfile, expanded_src);
- if !proc_res.status.success() {
- fatal_proc_rec("pretty-printed source (expanded) does not typecheck",
- &proc_res);
- }
- return;
- fn print_source(config: &Config,
- props: &TestProps,
- testfile: &Path,
- src: String,
- pretty_type: &str) -> ProcRes {
- let aux_dir = aux_output_dir_name(config, testfile);
- compose_and_run(config,
- testfile,
- make_pp_args(config,
- props,
- testfile,
- pretty_type.to_string()),
- props.exec_env.clone(),
- &config.compile_lib_path,
- Some(aux_dir.to_str().unwrap()),
- Some(src))
- }
- fn make_pp_args(config: &Config,
- props: &TestProps,
- testfile: &Path,
- pretty_type: String) -> ProcArgs {
- let aux_dir = aux_output_dir_name(config, testfile);
- // FIXME (#9639): This needs to handle non-utf8 paths
- let mut args = vec!("-".to_string(),
- "-Zunstable-options".to_string(),
- "--pretty".to_string(),
- pretty_type,
- format!("--target={}", config.target),
- "-L".to_string(),
- aux_dir.to_str().unwrap().to_string());
- args.extend(split_maybe_args(&config.target_rustcflags).into_iter());
- args.extend(split_maybe_args(&props.compile_flags).into_iter());
- return ProcArgs {
- prog: config.rustc_path.to_str().unwrap().to_string(),
- args: args,
- };
- }
- fn compare_source(expected: &str, actual: &str) {
- if expected != actual {
- error("pretty-printed source does not match expected source");
- println!("\n\
- expected:\n\
- ------------------------------------------\n\
- {}\n\
- ------------------------------------------\n\
- actual:\n\
- ------------------------------------------\n\
- {}\n\
- ------------------------------------------\n\
- \n",
- expected, actual);
- panic!();
- }
- }
- fn typecheck_source(config: &Config, props: &TestProps,
- testfile: &Path, src: String) -> ProcRes {
- let args = make_typecheck_args(config, props, testfile);
- compose_and_run_compiler(config, props, testfile, args, Some(src))
- }
- fn make_typecheck_args(config: &Config, props: &TestProps, testfile: &Path) -> ProcArgs {
- let aux_dir = aux_output_dir_name(config, testfile);
- let target = if props.force_host {
- &*config.host
- } else {
- &*config.target
- };
- // FIXME (#9639): This needs to handle non-utf8 paths
- let mut args = vec!("-".to_string(),
- "-Zno-trans".to_string(),
- "--crate-type=lib".to_string(),
- format!("--target={}", target),
- "-L".to_string(),
- config.build_base.to_str().unwrap().to_string(),
- "-L".to_string(),
- aux_dir.to_str().unwrap().to_string());
- args.extend(split_maybe_args(&config.target_rustcflags).into_iter());
- args.extend(split_maybe_args(&props.compile_flags).into_iter());
- // FIXME (#9639): This needs to handle non-utf8 paths
- return ProcArgs {
- prog: config.rustc_path.to_str().unwrap().to_string(),
- args: args,
- };
- }
- }
- fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
- let mut config = Config {
- target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
- host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
- .. config.clone()
- };
- let config = &mut config;
- let DebuggerCommands {
- commands,
- check_lines,
- breakpoint_lines
- } = parse_debugger_commands(testfile, "gdb");
- let mut cmds = commands.connect("\n");
- // compile test file (it should have 'compile-flags:-g' in the header)
- let compiler_run_result = compile_test(config, props, testfile);
- if !compiler_run_result.status.success() {
- fatal_proc_rec("compilation failed!", &compiler_run_result);
- }
- let exe_file = make_exe_name(config, testfile);
- let debugger_run_result;
- match &*config.target {
- "arm-linux-androideabi" | "aarch64-linux-android" => {
- cmds = cmds.replace("run", "continue");
- // write debugger script
- let mut script_str = String::with_capacity(2048);
- script_str.push_str("set charset UTF-8\n");
- script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap()));
- script_str.push_str("target remote :5039\n");
- script_str.push_str(&format!("set solib-search-path \
- ./{}/stage2/lib/rustlib/{}/lib/\n",
- config.host, config.target));
- for line in breakpoint_lines.iter() {
- script_str.push_str(&format!("break {:?}:{}\n",
- testfile.file_name().unwrap()
- .to_string_lossy(),
- *line)[..]);
- }
- script_str.push_str(&cmds);
- script_str.push_str("\nquit\n");
- debug!("script_str = {}", script_str);
- dump_output_file(config,
- testfile,
- &script_str,
- "debugger.script");
- procsrv::run("",
- &config.adb_path,
- None,
- &[
- "push".to_string(),
- exe_file.to_str().unwrap().to_string(),
- config.adb_test_dir.clone()
- ],
- vec!(("".to_string(), "".to_string())),
- Some("".to_string()))
- .expect(&format!("failed to exec `{:?}`", config.adb_path));
- procsrv::run("",
- &config.adb_path,
- None,
- &[
- "forward".to_string(),
- "tcp:5039".to_string(),
- "tcp:5039".to_string()
- ],
- vec!(("".to_string(), "".to_string())),
- Some("".to_string()))
- .expect(&format!("failed to exec `{:?}`", config.adb_path));
- let adb_arg = format!("export LD_LIBRARY_PATH={}; \
- gdbserver{} :5039 {}/{}",
- config.adb_test_dir.clone(),
- if config.target.contains("aarch64")
- {"64"} else {""},
- config.adb_test_dir.clone(),
- exe_file.file_name().unwrap().to_str()
- .unwrap());
- let mut process = procsrv::run_background("",
- &config.adb_path
- ,
- None,
- &[
- "shell".to_string(),
- adb_arg.clone()
- ],
- vec!(("".to_string(),
- "".to_string())),
- Some("".to_string()))
- .expect(&format!("failed to exec `{:?}`", config.adb_path));
- loop {
- //waiting 1 second for gdbserver start
- #[allow(deprecated)]
- fn sleep() {
- ::std::old_io::timer::sleep(Duration::milliseconds(1000));
- }
- sleep();
- if TcpStream::connect("127.0.0.1:5039").is_ok() {
- break
- }
- }
- let tool_path = match config.android_cross_path.to_str() {
- Some(x) => x.to_string(),
- None => fatal("cannot find android cross path")
- };
- let debugger_script = make_out_name(config, testfile, "debugger.script");
- // FIXME (#9639): This needs to handle non-utf8 paths
- let debugger_opts =
- vec!("-quiet".to_string(),
- "-batch".to_string(),
- "-nx".to_string(),
- format!("-command={}", debugger_script.to_str().unwrap()));
- let mut gdb_path = tool_path;
- gdb_path.push_str(&format!("/bin/{}-gdb", config.target));
- let procsrv::Result {
- out,
- err,
- status
- } = procsrv::run("",
- &gdb_path,
- None,
- &debugger_opts,
- vec!(("".to_string(), "".to_string())),
- None)
- .expect(&format!("failed to exec `{:?}`", gdb_path));
- let cmdline = {
- let cmdline = make_cmdline("",
- &format!("{}-gdb", config.target),
- &debugger_opts);
- logv(config, format!("executing {}", cmdline));
- cmdline
- };
- debugger_run_result = ProcRes {
- status: Status::Normal(status),
- stdout: out,
- stderr: err,
- cmdline: cmdline
- };
- if process.kill().is_err() {
- println!("Adb process is already finished.");
- }
- }
- _=> {
- let rust_src_root = find_rust_src_root(config)
- .expect("Could not find Rust source root");
- let rust_pp_module_rel_path = Path::new("./src/etc");
- let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
- .to_str()
- .unwrap()
- .to_string();
- // write debugger script
- let mut script_str = String::with_capacity(2048);
- script_str.push_str("set charset UTF-8\n");
- script_str.push_str("show version\n");
- match config.gdb_version {
- Some(ref version) => {
- println!("NOTE: compiletest thinks it is using GDB version {}",
- version);
- if header::gdb_version_to_int(version) >
- header::gdb_version_to_int("7.4") {
- // Add the directory containing the pretty printers to
- // GDB's script auto loading safe path
- script_str.push_str(
- &format!("add-auto-load-safe-path {}\n",
- rust_pp_module_abs_path.replace(r"\", r"\\"))
- );
- }
- }
- _ => {
- println!("NOTE: compiletest does not know which version of \
- GDB it is using");
- }
- }
- // The following line actually doesn't have to do anything with
- // pretty printing, it just tells GDB to print values on one line:
- script_str.push_str("set print pretty off\n");
- // Add the pretty printer directory to GDB's source-file search path
- script_str.push_str(&format!("directory {}\n",
- rust_pp_module_abs_path));
- // Load the target executable
- script_str.push_str(&format!("file {}\n",
- exe_file.to_str().unwrap()
- .replace(r"\", r"\\")));
- // Add line breakpoints
- for line in &breakpoint_lines {
- script_str.push_str(&format!("break '{}':{}\n",
- testfile.file_name().unwrap()
- .to_string_lossy(),
- *line));
- }
- script_str.push_str(&cmds);
- script_str.push_str("\nquit\n");
- debug!("script_str = {}", script_str);
- dump_output_file(config,
- testfile,
- &script_str,
- "debugger.script");
- // run debugger script with gdb
- fn debugger() -> &'static str {
- if cfg!(windows) {"gdb.exe"} else {"gdb"}
- }
- let debugger_script = make_out_name(config, testfile, "debugger.script");
- // FIXME (#9639): This needs to handle non-utf8 paths
- let debugger_opts =
- vec!("-quiet".to_string(),
- "-batch".to_string(),
- "-nx".to_string(),
- format!("-command={}", debugger_script.to_str().unwrap()));
- let proc_args = ProcArgs {
- prog: debugger().to_string(),
- args: debugger_opts,
- };
- let environment = vec![("PYTHONPATH".to_string(), rust_pp_module_abs_path)];
- debugger_run_result = compose_and_run(config,
- testfile,
- proc_args,
- environment,
- &config.run_lib_path,
- None,
- None);
- }
- }
- if !debugger_run_result.status.success() {
- fatal("gdb failed to execute");
- }
- check_debugger_output(&debugger_run_result, &check_lines);
- }
- fn find_rust_src_root(config: &Config) -> Option<PathBuf> {
- let mut path = config.src_base.clone();
- let path_postfix = Path::new("src/etc/lldb_batchmode.py");
- while path.pop() {
- if path.join(&path_postfix).is_file() {
- return Some(path);
- }
- }
- return None;
- }
- fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) {
- if config.lldb_python_dir.is_none() {
- fatal("Can't run LLDB test because LLDB's python path is not set.");
- }
- let mut config = Config {
- target_rustcflags: cleanup_debug_info_options(&config.target_rustcflags),
- host_rustcflags: cleanup_debug_info_options(&config.host_rustcflags),
- .. config.clone()
- };
- let config = &mut config;
- // compile test file (it should have 'compile-flags:-g' in the header)
- let compile_result = compile_test(config, props, testfile);
- if !compile_result.status.success() {
- fatal_proc_rec("compilation failed!", &compile_result);
- }
- let exe_file = make_exe_name(config, testfile);
- match config.lldb_version {
- Some(ref version) => {
- println!("NOTE: compiletest thinks it is using LLDB version {}",
- version);
- }
- _ => {
- println!("NOTE: compiletest does not know which version of \
- LLDB it is using");
- }
- }
- // Parse debugger commands etc from test files
- let DebuggerCommands {
- commands,
- check_lines,
- breakpoint_lines,
- ..
- } = parse_debugger_commands(testfile, "lldb");
- // Write debugger script:
- // We don't want to hang when calling `quit` while the process is still running
- let mut script_str = String::from_str("settings set auto-confirm true\n");
- // Make LLDB emit its version, so we have it documented in the test output
- script_str.push_str("version\n");
- // Switch LLDB into "Rust mode"
- let rust_src_root = find_rust_src_root(config)
- .expect("Could not find Rust source root");
- let rust_pp_module_rel_path = Path::new("./src/etc/lldb_rust_formatters.py");
- let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
- .to_str()
- .unwrap()
- .to_string();
- script_str.push_str(&format!("command script import {}\n",
- &rust_pp_module_abs_path[..])[..]);
- script_str.push_str("type summary add --no-value ");
- script_str.push_str("--python-function lldb_rust_formatters.print_val ");
- script_str.push_str("-x \".*\" --category Rust\n");
- script_str.push_str("type category enable Rust\n");
- // Set breakpoints on every line that contains the string "#break"
- for line in &breakpoint_lines {
- script_str.push_str(&format!("breakpoint set --line {}\n", line));
- }
- // Append the other commands
- for line in &commands {
- script_str.push_str(line);
- script_str.push_str("\n");
- }
- // Finally, quit the debugger
- script_str.push_str("\nquit\n");
- // Write the script into a file
- debug!("script_str = {}", script_str);
- dump_output_file(config,
- testfile,
- &script_str,
- "debugger.script");
- let debugger_script = make_out_name(config, testfile, "debugger.script");
- // Let LLDB execute the script via lldb_batchmode.py
- let debugger_run_result = run_lldb(config,
- &exe_file,
- &debugger_script,
- &rust_src_root);
- if !debugger_run_result.status.success() {
- fatal_proc_rec("Error while running LLDB", &debugger_run_result);
- }
- check_debugger_output(&debugger_run_result, &check_lines);
- fn run_lldb(config: &Config,
- test_executable: &Path,
- debugger_script: &Path,
- rust_src_root: &Path)
- -> ProcRes {
- // Prepare the lldb_batchmode which executes the debugger script
- let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
- let mut cmd = Command::new("python");
- cmd.arg(&lldb_script_path)
- .arg(test_executable)
- .arg(debugger_script)
- .env("PYTHONPATH", config.lldb_python_dir.as_ref().unwrap());
- let (status, out, err) = match cmd.output() {
- Ok(Output { status, stdout, stderr }) => {
- (status,
- String::from_utf8(stdout).unwrap(),
- String::from_utf8(stderr).unwrap())
- },
- Err(e) => {
- fatal(&format!("Failed to setup Python process for \
- LLDB script: {}", e))
- }
- };
- dump_output(config, test_executable, &out, &err);
- return ProcRes {
- status: Status::Normal(status),
- stdout: out,
- stderr: err,
- cmdline: format!("{:?}", cmd)
- };
- }
- }
- struct DebuggerCommands {
- commands: Vec<String>,
- check_lines: Vec<String>,
- breakpoint_lines: Vec<uint>,
- }
- fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
- -> DebuggerCommands {
- let command_directive = format!("{}-command", debugger_prefix);
- let check_directive = format!("{}-check", debugger_prefix);
- let mut breakpoint_lines = vec!();
- let mut commands = vec!();
- let mut check_lines = vec!();
- let mut counter = 1;
- let reader = BufReader::new(File::open(file_path).unwrap());
- for line in reader.lines() {
- match line {
- Ok(line) => {
- if line.contains("#break") {
- breakpoint_lines.push(counter);
- }
- header::parse_name_value_directive(
- &line,
- &command_directive).map(|cmd| {
- commands.push(cmd)
- });
- header::parse_name_value_directive(
- &line,
- &check_directive).map(|cmd| {
- check_lines.push(cmd)
- });
- }
- Err(e) => {
- fatal(&format!("Error while parsing debugger commands: {}", e))
- }
- }
- counter += 1;
- }
- DebuggerCommands {
- commands: commands,
- check_lines: check_lines,
- breakpoint_lines: breakpoint_lines,
- }
- }
- fn cleanup_debug_info_options(options: &Option<String>) -> Option<String> {
- if options.is_none() {
- return None;
- }
- // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS.
- let options_to_remove = [
- "-O".to_string(),
- "-g".to_string(),
- "--debuginfo".to_string()
- ];
- let new_options =
- split_maybe_args(options).into_iter()
- .filter(|x| !options_to_remove.contains(x))
- .collect::<Vec<String>>()
- .connect(" ");
- Some(new_options)
- }
- fn check_debugger_output(debugger_run_result: &ProcRes, check_lines: &[String]) {
- let num_check_lines = check_lines.len();
- if num_check_lines > 0 {
- // Allow check lines to leave parts unspecified (e.g., uninitialized
- // bits in the wrong case of an enum) with the notation "[...]".
- let check_fragments: Vec<Vec<String>> =
- check_lines.iter().map(|s| {
- s
- .trim()
- .split("[...]")
- .map(|x| x.to_string())
- .collect()
- }).collect();
- // check if each line in props.check_lines appears in the
- // output (in order)
- let mut i = 0;
- for line in debugger_run_result.stdout.lines() {
- let mut rest = line.trim();
- let mut first = true;
- let mut failed = false;
- for frag in &check_fragments[i] {
- let found = if first {
- if rest.starts_with(frag) {
- Some(0)
- } else {
- None
- }
- } else {
- rest.find(frag)
- };
- match found {
- None => {
- failed = true;
- break;
- }
- Some(i) => {
- rest = &rest[(i + frag.len())..];
- }
- }
- first = false;
- }
- if !failed && rest.len() == 0 {
- i += 1;
- }
- if i == num_check_lines {
- // all lines checked
- break;
- }
- }
- if i != num_check_lines {
- fatal_proc_rec(&format!("line not found in debugger output: {}",
- check_lines.get(i).unwrap()),
- debugger_run_result);
- }
- }
- }
- fn check_error_patterns(props: &TestProps,
- testfile: &Path,
- output_to_check: &str,
- proc_res: &ProcRes) {
- if props.error_patterns.is_empty() {
- fatal(&format!("no error pattern specified in {:?}", testfile.display()));
- }
- let mut next_err_idx = 0;
- let mut next_err_pat = &props.error_patterns[next_err_idx];
- let mut done = false;
- for line in output_to_check.lines() {
- if line.contains(next_err_pat) {
- debug!("found error pattern {}", next_err_pat);
- next_err_idx += 1;
- if next_err_idx == props.error_patterns.len() {
- debug!("found all error patterns");
- done = true;
- break;
- }
- next_err_pat = &props.error_patterns[next_err_idx];
- }
- }
- if done { return; }
- let missing_patterns = &props.error_patterns[next_err_idx..];
- if missing_patterns.len() == 1 {
- fatal_proc_rec(&format!("error pattern '{}' not found!", missing_patterns[0]),
- proc_res);
- } else {
- for pattern in missing_patterns {
- error(&format!("error pattern '{}' not found!", *pattern));
- }
- fatal_proc_rec("multiple error patterns not found", proc_res);
- }
- }
- fn check_no_compiler_crash(proc_res: &ProcRes) {
- for line in proc_res.stderr.lines() {
- if line.starts_with("error: internal compiler error:") {
- fatal_proc_rec("compiler encountered internal error",
- proc_res);
- }
- }
- }
- fn check_forbid_output(props: &TestProps,
- output_to_check: &str,
- proc_res: &ProcRes) {
- for pat in &props.forbid_output {
- if output_to_check.contains(pat) {
- fatal_proc_rec("forbidden pattern found in compiler output", proc_res);
- }
- }
- }
- fn check_expected_errors(expected_errors: Vec<errors::ExpectedError> ,
- testfile: &Path,
- proc_res: &ProcRes) {
- // true if we found the error in question
- let mut found_flags: Vec<_> = repeat(false).take(expected_errors.len()).collect();
- if proc_res.status.success() {
- fatal("process did not return an error status");
- }
- let prefixes = expected_errors.iter().map(|ee| {
- format!("{}:{}:", testfile.display(), ee.line)
- }).collect::<Vec<String>>();
- fn prefix_matches(line: &str, prefix: &str) -> bool {
- use std::ascii::AsciiExt;
- // On windows just translate all '\' path separators to '/'
- let line = line.replace(r"\", "/");
- if cfg!(windows) {
- line.to_ascii_lowercase().starts_with(&prefix.to_ascii_lowercase())
- } else {
- line.starts_with(prefix)
- }
- }
- // A multi-line error will have followup lines which will always
- // start with one of these strings.
- fn continuation( line: &str) -> bool {
- line.starts_with(" expected") ||
- line.starts_with(" found") ||
- // 1234
- // Should have 4 spaces: see issue 18946
- line.starts_with("(")
- }
- // Scan and extract our error/warning messages,
- // which look like:
- // filename:line1:col1: line2:col2: *error:* msg
- // filename:line1:col1: line2:col2: *warning:* msg
- // where line1:col1: is the starting point, line2:col2:
- // is the ending point, and * represents ANSI color codes.
- for line in proc_res.stderr.lines() {
- let mut was_expected = false;
- for (i, ee) in expected_errors.iter().enumerate() {
- if !found_flags[i] {
- debug!("prefix={} ee.kind={} ee.msg={} line={}",
- prefixes[i],
- ee.kind,
- ee.msg,
- line);
- if (prefix_matches(line, &prefixes[i]) || continuation(line)) &&
- line.contains(&ee.kind) &&
- line.contains(&ee.msg) {
- found_flags[i] = true;
- was_expected = true;
- break;
- }
- }
- }
- // ignore this msg which gets printed at the end
- if line.contains("aborting due to") {
- was_expected = true;
- }
- if !was_expected && is_compiler_error_or_warning(line) {
- fatal_proc_rec(&format!("unexpected compiler error or warning: '{}'",
- line),
- proc_res);
- }
- }
- for (i, &flag) in found_flags.iter().enumerate() {
- if !flag {
- let ee = &expected_errors[i];
- fatal_proc_rec(&format!("expected {} on line {} not found: {}",
- ee.kind, ee.line, ee.msg),
- proc_res);
- }
- }
- }
- fn is_compiler_error_or_warning(line: &str) -> bool {
- let mut i = 0;
- return
- scan_until_char(line, ':', &mut i) &&
- scan_char(line, ':', &mut i) &&
- scan_integer(line, &mut i) &&
- scan_char(line, ':', &mut i) &&
- scan_integer(line, &mut i) &&
- scan_char(line, ':', &mut i) &&
- scan_char(line, ' ', &mut i) &&
- scan_integer(line, &mut i) &&
- scan_char(line, ':', &mut i) &&
- scan_integer(line, &mut i) &&
- scan_char(line, ' ', &mut i) &&
- (scan_string(line, "error", &mut i) ||
- scan_string(line, "warning", &mut i));
- }
- fn scan_until_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
- if *idx >= haystack.len() {
- return false;
- }
- let opt = haystack[(*idx)..].find(needle);
- if opt.is_none() {
- return false;
- }
- *idx = opt.unwrap();
- return true;
- }
- fn scan_char(haystack: &str, needle: char, idx: &mut uint) -> bool {
- if *idx >= haystack.len() {
- return false;
- }
- let range = haystack.char_range_at(*idx);
- if range.ch != needle {
- return false;
- }
- *idx = range.next;
- return true;
- }
- fn scan_integer(haystack: &str, idx: &mut uint) -> bool {
- let mut i = *idx;
- while i < haystack.len() {
- let range = haystack.char_range_at(i);
- if range.ch < '0' || '9' < range.ch {
- break;
- }
- i = range.next;
- }
- if i == *idx {
- return false;
- }
- *idx = i;
- return true;
- }
- fn scan_string(haystack: &str, needle: &str, idx: &mut uint) -> bool {
- let mut haystack_i = *idx;
- let mut needle_i = 0;
- while needle_i < needle.len() {
- if haystack_i >= haystack.len() {
- return false;
- }
- let range = haystack.char_range_at(haystack_i);
- haystack_i = range.next;
- if !scan_char(needle, range.ch, &mut needle_i) {
- return false;
- }
- }
- *idx = haystack_i;
- return true;
- }
- struct ProcArgs {
- prog: String,
- args: Vec<String>,
- }
- struct ProcRes {
- status: Status,
- stdout: String,
- stderr: String,
- cmdline: String,
- }
- enum Status {
- Parsed(i32),
- Normal(ExitStatus),
- }
- impl Status {
- fn code(&self) -> Option<i32> {
- match *self {
- Status::Parsed(i) => Some(i),
- Status::Normal(ref e) => e.code(),
- }
- }
- fn success(&self) -> bool {
- match *self {
- Status::Parsed(i) => i == 0,
- Status::Normal(ref e) => e.success(),
- }
- }
- }
- impl fmt::Display for Status {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Status::Parsed(i) => write!(f, "exit code: {}", i),
- Status::Normal(ref e) => e.fmt(f),
- }
- }
- }
- fn compile_test(config: &Config, props: &TestProps,
- testfile: &Path) -> ProcRes {
- compile_test_(config, props, testfile, &[])
- }
- fn jit_test(config: &Config, props: &TestProps, testfile: &Path) -> ProcRes {
- compile_test_(config, props, testfile, &["--jit".to_string()])
- }
- fn compile_test_(config: &Config, props: &TestProps,
- testfile: &Path, extra_args: &[String]) -> ProcRes {
- let aux_dir = aux_output_dir_name(config, testfile);
- // FIXME (#9639): This needs to handle non-utf8 paths
- let mut link_args = vec!("-L".to_string(),
- aux_dir.to_str().unwrap().to_string());
- link_args.extend(extra_args.iter().cloned());
- let args = make_compile_args(config,
- props,
- link_args,
- |a, b| TargetLocation::ThisFile(make_exe_name(a, b)), testfile);
- compose_and_run_compiler(config, props, testfile, args, None)
- }
- fn exec_compiled_test(config: &Config, props: &TestProps,
- testfile: &Path) -> ProcRes {
- let env = props.exec_env.clone();
- match &*config.target {
- "arm-linux-androideabi" | "aarch64-linux-android" => {
- _arm_exec_compiled_test(config, props, testfile, env)
- }
- _=> {
- let aux_dir = aux_output_dir_name(config, testfile);
- compose_and_run(config,
- testfile,
- make_run_args(config, props, testfile),
- env,
- &config.run_lib_path,
- Some(aux_dir.to_str().unwrap()),
- None)
- }
- }
- }
- fn compose_and_run_compiler(
- config: &Config,
- props: &TestProps,
- testfile: &Path,
- args: ProcArgs,
- input: Option<String>) -> ProcRes {
- if !props.aux_builds.is_empty() {
- ensure_dir(&aux_output_dir_name(config, testfile));
- }
- let aux_dir = aux_output_dir_name(config, testfile);
- // FIXME (#9639): This needs to handle non-utf8 paths
- let extra_link_args = vec!("-L".to_string(), aux_dir.to_str().unwrap().to_string());
- for rel_ab in &props.aux_builds {
- let abs_ab = config.aux_base.join(rel_ab);
- let aux_props = header::load_props(&abs_ab);
- let mut crate_type = if aux_props.no_prefer_dynamic {
- Vec::new()
- } else {
- vec!("--crate-type=dylib".to_string())
- };
- crate_type.extend(extra_link_args.clone().into_iter());
- let aux_args =
- make_compile_args(config,
- &aux_props,
- crate_type,
- |a,b| {
- let f = make_lib_name(a, b, testfile);
- let parent = f.parent().unwrap();
- TargetLocation::ThisDirectory(parent.to_path_buf())
- },
- &abs_ab);
- let auxres = compose_and_run(config,
- &abs_ab,
- aux_args,
- Vec::new(),
- &config.compile_lib_path,
- Some(aux_dir.to_str().unwrap()),
- None);
- if !auxres.status.success() {
- fatal_proc_rec(
- &format!("auxiliary build of {:?} failed to compile: ",
- abs_ab.display()),
- &auxres);
- }
- match &*config.target {
- "arm-linux-androideabi" | "aarch64-linux-android" => {
- _arm_push_aux_shared_library(config, testfile);
- }
- _ => {}
- }
- }
- compose_and_run(config,
- testfile,
- args,
- Vec::new(),
- &config.compile_lib_path,
- Some(aux_dir.to_str().unwrap()),
- input)
- }
- fn ensure_dir(path: &Path) {
- if path.is_dir() { return; }
- fs::create_dir(path).unwrap();
- }
- fn compose_and_run(config: &Config, testfile: &Path,
- ProcArgs{ args, prog }: ProcArgs,
- procenv: Vec<(String, String)> ,
- lib_path: &str,
- aux_path: Option<&str>,
- input: Option<String>) -> ProcRes {
- return program_output(config, testfile, lib_path,
- prog, aux_path, args, procenv, input);
- }
- enum TargetLocation {
- ThisFile(PathBuf),
- ThisDirectory(PathBuf),
- }
- fn make_compile_args<F>(config: &Config,
- props: &TestProps,
- extras: Vec<String> ,
- xform: F,
- testfile: &Path)
- -> ProcArgs where
- F: FnOnce(&Config, &Path) -> TargetLocation,
- {
- let xform_file = xform(config, testfile);
- let target = if props.force_host {
- &*config.host
- } else {
- &*config.target
- };
- // FIXME (#9639): This needs to handle non-utf8 paths
- let mut args = vec!(testfile.to_str().unwrap().to_string(),
- "-L".to_string(),
- config.build_base.to_str().unwrap().to_string(),
- format!("--target={}", target));
- args.push_all(&extras);
- if !props.no_prefer_dynamic {
- args.push("-C".to_string());
- args.push("prefer-dynamic".to_string());
- }
- let path = match xform_file {
- TargetLocation::ThisFile(path) => {
- args.push("-o".to_string());
- path
- }
- TargetLocation::ThisDirectory(path) => {
- args.push("--out-dir".to_string());
- path
- }
- };
- args.push(path.to_str().unwrap().to_string());
- if props.force_host {
- args.extend(split_maybe_args(&config.host_rustcflags).into_iter());
- } else {
- args.extend(split_maybe_args(&config.target_rustcflags).into_iter());
- }
- args.extend(split_maybe_args(&props.compile_flags).into_iter());
- return ProcArgs {
- prog: config.rustc_path.to_str().unwrap().to_string(),
- args: args,
- };
- }
- fn make_lib_name(config: &Config, auxfile: &Path, testfile: &Path) -> PathBuf {
- // what we return here is not particularly important, as it
- // happens; rustc ignores everything except for the directory.
- let auxname = output_testname(auxfile);
- aux_output_dir_name(config, testfile).join(&auxname)
- }
- fn make_exe_name(config: &Config, testfile: &Path) -> PathBuf {
- let mut f = output_base_name(config, testfile);
- if !env::consts::EXE_SUFFIX.is_empty() {
- let mut fname = f.file_name().unwrap().to_os_string();
- fname.push(env::consts::EXE_SUFFIX);
- f.set_file_name(&fname);
- }
- f
- }
- fn make_run_args(config: &Config, props: &TestProps, testfile: &Path) ->
- ProcArgs {
- // If we've got another tool to run under (valgrind),
- // then split apart its command
- let mut args = split_maybe_args(&config.runtool);
- let exe_file = make_exe_name(config, testfile);
- // FIXME (#9639): This needs to handle non-utf8 paths
- args.push(exe_file.to_str().unwrap().to_string());
- // Add the arguments in the run_flags directive
- args.extend(split_maybe_args(&props.run_flags).into_iter());
- let prog = args.remove(0);
- return ProcArgs {
- prog: prog,
- args: args,
- };
- }
- fn split_maybe_args(argstr: &Option<String>) -> Vec<String> {
- match *argstr {
- Some(ref s) => {
- s
- .split(' ')
- .filter_map(|s| {
- if s.chars().all(|c| c.is_whitespace()) {
- None
- } else {
- Some(s.to_string())
- }
- }).collect()
- }
- None => Vec::new()
- }
- }
- fn program_output(config: &Config, testfile: &Path, lib_path: &str, prog: String,
- aux_path: Option<&str>, args: Vec<String>,
- env: Vec<(String, String)>,
- input: Option<String>) -> ProcRes {
- let cmdline =
- {
- let cmdline = make_cmdline(lib_path,
- &prog,
- &args);
- logv(config, format!("executing {}", cmdline));
- cmdline
- };
- let procsrv::Result {
- out,
- err,
- status
- } = procsrv::run(lib_path,
- &prog,
- aux_path,
- &args,
- env,
- input).expect(&format!("failed to exec `{}`", prog));
- dump_output(config, testfile, &out, &err);
- return ProcRes {
- status: Status::Normal(status),
- stdout: out,
- stderr: err,
- cmdline: cmdline,
- };
- }
- fn make_cmdline(libpath: &str, prog: &str, args: &[String]) -> String {
- use util;
- // Linux and mac don't require adjusting the library search path
- if cfg!(unix) {
- format!("{} {}", prog, args.connect(" "))
- } else {
- // Build the LD_LIBRARY_PATH variable as it would be seen on the command line
- // for diagnostic purposes
- fn lib_path_cmd_prefix(path: &str) -> String {
- format!("{}=\"{}\"", util::lib_path_env_var(), util::make_new_path(path))
- }
- format!("{} {} {}", lib_path_cmd_prefix(libpath), prog, args.connect(" "))
- }
- }
- fn dump_output(config: &Config, testfile: &Path, out: &str, err: &str) {
- dump_output_file(config, testfile, out, "out");
- dump_output_file(config, testfile, err, "err");
- maybe_dump_to_stdout(config…
Large files files are truncated, but you can click here to view the full file