pub use self::Mode::*;
use std::env;
use std::ffi::OsStr;
use std::fmt;
use std::fs::{read_dir, remove_file};
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use runtest::dylib_env_var;
use test::ColorConfig;
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Mode {
CompileFail,
ParseFail,
RunFail,
RunPass,
RunPassValgrind,
Pretty,
DebugInfoGdb,
DebugInfoLldb,
Codegen,
Rustdoc,
CodegenUnits,
Incremental,
RunMake,
Ui,
MirOpt,
Assembly,
}
impl Mode {
pub fn disambiguator(self) -> &'static str {
match self {
Pretty => ".pretty",
DebugInfoGdb => ".gdb",
DebugInfoLldb => ".lldb",
_ => "",
}
}
}
impl FromStr for Mode {
type Err = ();
fn from_str(s: &str) -> Result<Mode, ()> {
match s {
"compile-fail" => Ok(CompileFail),
"parse-fail" => Ok(ParseFail),
"run-fail" => Ok(RunFail),
"run-pass" => Ok(RunPass),
"run-pass-valgrind" => Ok(RunPassValgrind),
"pretty" => Ok(Pretty),
"debuginfo-lldb" => Ok(DebugInfoLldb),
"debuginfo-gdb" => Ok(DebugInfoGdb),
"codegen" => Ok(Codegen),
"rustdoc" => Ok(Rustdoc),
"codegen-units" => Ok(CodegenUnits),
"incremental" => Ok(Incremental),
"run-make" => Ok(RunMake),
"ui" => Ok(Ui),
"mir-opt" => Ok(MirOpt),
"assembly" => Ok(Assembly),
_ => Err(()),
}
}
}
impl fmt::Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(
match *self {
CompileFail => "compile-fail",
ParseFail => "parse-fail",
RunFail => "run-fail",
RunPass => "run-pass",
RunPassValgrind => "run-pass-valgrind",
Pretty => "pretty",
DebugInfoGdb => "debuginfo-gdb",
DebugInfoLldb => "debuginfo-lldb",
Codegen => "codegen",
Rustdoc => "rustdoc",
CodegenUnits => "codegen-units",
Incremental => "incremental",
RunMake => "run-make",
Ui => "ui",
MirOpt => "mir-opt",
Assembly => "assembly",
},
f,
)
}
}
#[derive(Clone)]
pub struct Config {
pub bless: bool,
pub compile_lib_path: PathBuf,
pub run_lib_path: PathBuf,
pub rustc_path: PathBuf,
pub rustdoc_path: Option<PathBuf>,
pub lldb_python: String,
pub docck_python: String,
pub llvm_filecheck_preprocess: Option<fn(&Path, &Path)>,
pub llvm_filecheck: Option<PathBuf>,
pub valgrind_path: Option<String>,
pub force_valgrind: bool,
pub src_base: PathBuf,
pub build_base: PathBuf,
pub stage_id: String,
pub mode: Mode,
pub run_ignored: bool,
pub filters: Vec<String>,
pub filter_exact: bool,
pub logfile: Option<PathBuf>,
pub runtool: Option<String>,
pub host_rustcflags: Option<String>,
pub target_rustcflags: Option<String>,
pub target: String,
pub host: String,
pub gdb: Option<String>,
pub gdb_version: Option<u32>,
pub gdb_native_rust: bool,
pub lldb_version: Option<String>,
pub llvm_version: Option<String>,
pub system_llvm: bool,
pub android_cross_path: PathBuf,
pub adb_path: String,
pub adb_test_dir: String,
pub adb_device_status: bool,
pub lldb_python_dir: Option<String>,
pub verbose: bool,
pub quiet: bool,
pub color: ColorConfig,
pub remote_test_client: Option<PathBuf>,
pub rustfix_coverage: bool,
pub edition: Option<String>,
pub strict_headers: bool,
pub cc: String,
pub cxx: String,
pub cflags: String,
pub ar: String,
pub linker: Option<String>,
pub llvm_components: String,
pub llvm_cxxflags: String,
pub nodejs: Option<String>,
}
#[derive(Clone)]
pub struct TestPaths {
pub file: PathBuf, pub base: PathBuf, pub relative_dir: PathBuf, }
pub fn expected_output_path(testpaths: &TestPaths, revision: Option<&str>, kind: &str) -> PathBuf {
assert!(UI_EXTENSIONS.contains(&kind));
let mut parts = Vec::new();
if let Some(x) = revision {
parts.push(x);
}
parts.push(kind);
let extension = parts.join(".");
testpaths.file.with_extension(extension)
}
pub const UI_EXTENSIONS: &[&str] = &[UI_STDERR, UI_STDOUT, UI_FIXED];
pub const UI_STDERR: &str = "stderr";
pub const UI_STDOUT: &str = "stdout";
pub const UI_FIXED: &str = "fixed";
fn lib_paths_flags<T>(lib_paths: T) -> String
where
T: AsRef<OsStr>,
{
let mut flags = String::new();
if !lib_paths.as_ref().is_empty() {
for p in env::split_paths(lib_paths.as_ref()) {
let p = p.to_str().unwrap();
assert!(!p.contains(' '), "spaces in paths not supported: {}", p);
flags += " -L ";
flags += p;
}
}
flags
}
impl Config {
pub fn link_deps(&mut self) {
let varname = dylib_env_var();
let lib_paths = env::var_os(varname).unwrap_or_default();
let mut flags = self.target_rustcflags.take().unwrap_or_default();
flags += lib_paths_flags(&lib_paths).as_str();
self.target_rustcflags = Some(flags);
}
fn find_deps_with_extension(&self, ext: &'static str) -> impl Iterator<Item = PathBuf> + '_ {
self.target_rustcflags
.iter()
.flat_map(|flags| flags.split_whitespace().filter(|s| s.ends_with("/deps")))
.filter_map(|directory| read_dir(directory).ok())
.flat_map(move |entries| {
entries.filter_map(Result::ok).filter_map(move |entry| {
let path = entry.path();
let extension = path.extension()?;
(extension == ext).then_some(path)
})
})
}
pub fn clean_rmeta(&self) {
self.find_deps_with_extension("rmeta").for_each(|path| {
let _: Result<(), _> = remove_file(path);
})
}
pub fn clean_rlib(&self) {
let mut rlibs = self.find_deps_with_extension("rlib").collect::<Vec<_>>();
let () = rlibs.sort();
let mut rlibs = rlibs.as_slice();
loop {
match rlibs {
[] => break,
[first, rest @ ..] => match rest {
[] => break,
[second, ..] => {
fn stem(path: &PathBuf) -> &str {
let stem = (|| {
let file_name = path.file_name()?;
let file_name = file_name.to_str()?;
let (stem, _) = file_name.split_once('-')?;
Some(stem)
})();
stem.unwrap_or_else(|| panic!("unable to parse {}", path.display()))
}
if stem(first) == stem(second) {
let _: Result<(), _> = remove_file(first);
}
rlibs = rest;
}
},
}
}
}
#[cfg(feature = "tmp")]
pub fn tempdir(mut self) -> ConfigWithTemp {
let tmp = tempfile::Builder::new()
.prefix("compiletest")
.tempdir()
.expect("failed to create temporary directory");
self.build_base = tmp.path().to_owned();
config_tempdir::ConfigWithTemp {
config: self,
tempdir: tmp,
}
}
}
#[cfg(feature = "tmp")]
mod config_tempdir {
use std::ops;
pub struct ConfigWithTemp {
pub config: super::Config,
pub tempdir: tempfile::TempDir,
}
impl ops::Deref for ConfigWithTemp {
type Target = super::Config;
fn deref(&self) -> &Self::Target {
&self.config
}
}
impl ops::DerefMut for ConfigWithTemp {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.config
}
}
}
#[cfg(feature = "tmp")]
pub use self::config_tempdir::ConfigWithTemp;
impl Default for Config {
fn default() -> Config {
#[cfg(feature = "rustc")]
let platform = rustc_session::config::host_triple().to_string();
Config {
bless: false,
compile_lib_path: PathBuf::from(""),
run_lib_path: PathBuf::from(""),
rustc_path: PathBuf::from("rustc"),
rustdoc_path: None,
lldb_python: "python".to_owned(),
docck_python: "docck-python".to_owned(),
valgrind_path: None,
force_valgrind: false,
llvm_filecheck_preprocess: None,
llvm_filecheck: None,
src_base: PathBuf::from("tests/run-pass"),
build_base: env::temp_dir(),
stage_id: "stage-id".to_owned(),
mode: Mode::RunPass,
run_ignored: false,
filters: vec![],
filter_exact: false,
logfile: None,
runtool: None,
host_rustcflags: None,
target_rustcflags: None,
#[cfg(feature = "rustc")]
target: platform.clone(),
#[cfg(not(feature = "rustc"))]
target: env!("COMPILETEST_TARGET").to_string(),
#[cfg(feature = "rustc")]
host: platform.clone(),
#[cfg(not(feature = "rustc"))]
host: env!("COMPILETEST_HOST").to_string(),
rustfix_coverage: false,
gdb: None,
gdb_version: None,
gdb_native_rust: false,
lldb_version: None,
llvm_version: None,
system_llvm: false,
android_cross_path: PathBuf::from("android-cross-path"),
adb_path: "adb-path".to_owned(),
adb_test_dir: "adb-test-dir/target".to_owned(),
adb_device_status: false,
lldb_python_dir: None,
verbose: false,
quiet: false,
color: ColorConfig::AutoColor,
remote_test_client: None,
cc: "cc".to_string(),
cxx: "cxx".to_string(),
cflags: "cflags".to_string(),
ar: "ar".to_string(),
linker: None,
llvm_components: "llvm-components".to_string(),
llvm_cxxflags: "llvm-cxxflags".to_string(),
nodejs: None,
edition: None,
strict_headers: false,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(not(target_os = "windows"))]
#[test]
fn test_lib_paths_flags() {
assert_eq!(lib_paths_flags(""), "");
assert_eq!(lib_paths_flags("/lib"), " -L /lib");
assert_eq!(lib_paths_flags("/lib:/usr/lib"), " -L /lib -L /usr/lib");
}
#[cfg(target_os = "windows")]
#[test]
fn test_lib_paths_flags() {
assert_eq!(lib_paths_flags(""), "");
assert_eq!(lib_paths_flags("C:\\lib"), " -L C:\\lib");
assert_eq!(
lib_paths_flags("C:\\lib;C:\\usr\\lib"),
" -L C:\\lib -L C:\\usr\\lib"
);
}
}