diff --git a/src/test/assembly/asm/aarch64-outline-atomics.rs b/src/test/assembly/asm/aarch64-outline-atomics.rs index c2ec4e911b9d1..7a554683a240f 100644 --- a/src/test/assembly/asm/aarch64-outline-atomics.rs +++ b/src/test/assembly/asm/aarch64-outline-atomics.rs @@ -1,9 +1,6 @@ // assembly-output: emit-asm // compile-flags: -O -// compile-flags: --target aarch64-unknown-linux-gnu -// needs-llvm-components: aarch64 -// only-aarch64 -// only-linux +// only-aarch64-unknown-linux-gnu #![crate_type = "rlib"] diff --git a/src/test/ui/meta/cfg-ignore-with-target.rs b/src/test/ui/meta/cfg-ignore-with-target.rs new file mode 100644 index 0000000000000..58e7d4270d5cd --- /dev/null +++ b/src/test/ui/meta/cfg-ignore-with-target.rs @@ -0,0 +1,12 @@ +// This test checks that the "ignore-cfg" compiletest option is evaluated +// based on the "--target" option in "compile-flags", rather than +// the default platform. +// +// compile-flags: --target x86_64-unknown-linux-gnu +// needs-llvm-components: x86 +// ignore-cfg: target_os=linux +// check-pass + +compile_error!("this should never be compiled"); + +fn main() {} diff --git a/src/test/ui/meta/cfg-ignore.rs b/src/test/ui/meta/cfg-ignore.rs new file mode 100644 index 0000000000000..038ba125f088d --- /dev/null +++ b/src/test/ui/meta/cfg-ignore.rs @@ -0,0 +1,10 @@ +// This test checks that the "ignore-cfg" compiletest option matches +// up with rustc's builtin evaluation of cfgs. +// +// ignore-cfg: target_family=unix +// check-pass + +#[cfg(target_family = "unix")] +compile_error!("this shouldn't be compiled on unix"); + +fn main() {} diff --git a/src/test/ui/meta/cfg-only.rs b/src/test/ui/meta/cfg-only.rs new file mode 100644 index 0000000000000..16ecf10b510c3 --- /dev/null +++ b/src/test/ui/meta/cfg-only.rs @@ -0,0 +1,10 @@ +// This test checks that the "ignore-cfg" compiletest option matches +// up with rustc's builtin evaluation of cfgs. +// +// only-cfg: target_family=unix +// check-pass + +#[cfg(not(target_family = "unix"))] +compile_error!("this should only be compiled on unix"); + +fn main() {} diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 5fcaa452ca30e..6e74719f06a76 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -4,8 +4,9 @@ use std::fs::File; use std::io::prelude::*; use std::io::BufReader; use std::path::{Path, PathBuf}; +use std::process::Command; -use tracing::*; +use tracing::{self, *}; use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode}; use crate::util; @@ -14,9 +15,12 @@ use crate::{extract_cdb_version, extract_gdb_version}; #[cfg(test)] mod tests; -/// The result of parse_cfg_name_directive. +/// Whether a given property holds true of this test instance. Usually, +/// a property is something about the target (e.g. whether the target is Windows), +/// but it could also be something more general (e.g. whether this test is being +/// cross-compiled.) #[derive(Clone, Copy, PartialEq, Debug)] -enum ParsedNameDirective { +enum EvaluatedProp { /// No match. NoMatch, /// Match. @@ -226,6 +230,10 @@ impl TestProps { if !testfile.is_dir() { let file = File::open(testfile).unwrap(); + let mut target = config.target.clone(); + let mut per_prop_normalize_stdout: Vec<(String, (String, String))> = Vec::new(); + let mut per_prop_normalize_stderr: Vec<(String, (String, String))> = Vec::new(); + iter_header(testfile, file, &mut |revision, ln| { if revision.is_some() && revision != cfg { return; @@ -338,11 +346,16 @@ impl TestProps { self.ignore_pass = config.parse_ignore_pass(ln); } + if let Some(target_override) = config.parse_target_override(ln) { + target = target_override; + } + if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") { - self.normalize_stdout.push(rule); + per_prop_normalize_stdout.push(rule); } + if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") { - self.normalize_stderr.push(rule); + per_prop_normalize_stderr.push(rule); } if let Some(code) = config.parse_failure_status(ln) { @@ -370,6 +383,17 @@ impl TestProps { self.incremental = config.parse_incremental(ln); } }); + for (prop, normalization) in per_prop_normalize_stdout { + if config.evaluate_prop_for_target(&prop, &target) == EvaluatedProp::Match { + self.normalize_stdout.push(normalization); + } + } + + for (prop, normalization) in per_prop_normalize_stderr { + if config.evaluate_prop_for_target(&prop, &target) == EvaluatedProp::Match { + self.normalize_stderr.push(normalization); + } + } } if self.failure_status == -1 { @@ -648,14 +672,15 @@ impl Config { } } - fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> { - if self.parse_cfg_name_directive(line, prefix) == ParsedNameDirective::Match { - let from = parse_normalization_string(&mut line)?; - let to = parse_normalization_string(&mut line)?; - Some((from, to)) - } else { - None - } + fn parse_custom_normalization( + &self, + mut line: &str, + prefix: &str, + ) -> Option<(String, (String, String))> { + let prop = self.parse_prop_directive(line, prefix)?; + let from = parse_normalization_string(&mut line)?; + let to = parse_normalization_string(&mut line)?; + Some((prop, (from, to))) } fn parse_needs_matching_clang(&self, line: &str) -> bool { @@ -668,27 +693,34 @@ impl Config { /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86` /// or `normalize-stderr-32bit`. - fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective { + fn parse_prop_directive(&self, line: &str, prefix: &str) -> Option { if !line.as_bytes().starts_with(prefix.as_bytes()) { - return ParsedNameDirective::NoMatch; + return None; } if line.as_bytes().get(prefix.len()) != Some(&b'-') { - return ParsedNameDirective::NoMatch; + return None; } - let name = line[prefix.len() + 1..].split(&[':', ' '][..]).next().unwrap(); + let config = &line[&prefix.len() + 1..].to_string(); + Some(config.to_string()) + } + + fn evaluate_prop_for_target(&self, prop: &str, target: &str) -> EvaluatedProp { + let mut iter = prop.split(&[':', ' '][..]); + let name = iter.next().unwrap(); + let maybe_value = iter.find(|s| !s.is_empty()); let is_match = name == "test" || - self.target == name || // triple - util::matches_os(&self.target, name) || // target - util::matches_env(&self.target, name) || // env - self.target.ends_with(name) || // target and env - name == util::get_arch(&self.target) || // architecture - name == util::get_pointer_width(&self.target) || // pointer width + target == name || // triple + util::matches_os(target, name) || // target + util::matches_env(target, name) || // env + target.ends_with(name) || // target and env + name == util::get_arch(target) || // architecture + name == util::get_pointer_width(target) || // pointer width name == self.stage_id.split('-').next().unwrap() || // stage name == self.channel || // channel - (self.target != self.host && name == "cross-compile") || - (name == "endian-big" && util::is_big_endian(&self.target)) || + (target != self.host && name == "cross-compile") || + (name == "endian-big" && util::is_big_endian(target)) || (self.remote_test_client.is_some() && name == "remote") || match self.compare_mode { Some(CompareMode::Nll) => name == "compare-mode-nll", @@ -704,12 +736,31 @@ impl Config { Some(Debugger::Gdb) => name == "gdb", Some(Debugger::Lldb) => name == "lldb", None => false, - }; + } || name == "cfg" && self.cfg_matches_for_target(maybe_value.unwrap(), target); + + if is_match { EvaluatedProp::Match } else { EvaluatedProp::NoMatch } + } + + fn cfg_matches_for_target(&self, value: &str, target: &str) -> bool { + let whitespace_or_double_quote = |c: char| c.is_whitespace() || c == '"'; + + let value = value.replace(whitespace_or_double_quote, ""); - if is_match { ParsedNameDirective::Match } else { ParsedNameDirective::NoMatch } + let cfg_data = Command::new(&self.rustc_path) + .args(&["--target", &target]) + .args(&["--print", "cfg"]) + .output() + .unwrap() + .stdout; + let cfg_data = String::from_utf8(cfg_data).unwrap(); + let cfg_data = cfg_data.replace('"', ""); + + let matches_value = |line: &str| line == value || line.split('=').next().unwrap() == value; + + cfg_data.lines().any(matches_value) } - fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool { + fn has_prop_prefix(&self, line: &str, prefix: &str) -> bool { // returns whether this line contains this prefix or not. For prefix // "ignore", returns true if line says "ignore-x86_64", "ignore-arch", // "ignore-android" etc. @@ -734,6 +785,21 @@ impl Config { } } + pub fn parse_target_override(&self, line: &str) -> Option { + let flags = self.parse_name_value_directive(line, "compile-flags")?; + let (_, tail) = flags.split_once("--target")?; + if tail.starts_with(|c: char| c.is_whitespace() || c == '=') { + let tail = &tail[1..]; + let target = match tail.split_once(|c: char| c.is_whitespace()) { + Some((target, _)) => target, + None => &tail, + }; + Some(target.to_string()) + } else { + None + } + } + pub fn find_rust_src_root(&self) -> Option { let mut path = self.src_base.clone(); let path_postfix = Path::new("src/etc/lldb_batchmode.py"); @@ -866,6 +932,10 @@ pub fn make_test_description( let mut ignore = false; let mut should_fail = false; + let mut target = config.target.clone(); + let mut ignored_props: Vec = Vec::new(); + let mut only_test_on_props: Vec = Vec::new(); + let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some(); let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(); let has_asm_support = util::has_asm_support(&config.target); @@ -874,7 +944,6 @@ pub fn make_test_description( let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target); - // for `-Z gcc-ld=lld` let has_rust_lld = config .compile_lib_path .join("rustlib") @@ -887,15 +956,16 @@ pub fn make_test_description( if revision.is_some() && revision != cfg { return; } - ignore = match config.parse_cfg_name_directive(ln, "ignore") { - ParsedNameDirective::Match => true, - ParsedNameDirective::NoMatch => ignore, - }; - if config.has_cfg_prefix(ln, "only") { - ignore = match config.parse_cfg_name_directive(ln, "only") { - ParsedNameDirective::Match => ignore, - ParsedNameDirective::NoMatch => true, - }; + if let Some(target_override) = config.parse_target_override(ln) { + target = target_override; + } + if let Some(prop) = config.parse_prop_directive(ln, "ignore") { + ignored_props.push(prop); + } + if config.has_prop_prefix(ln, "only") { + if let Some(prop) = config.parse_prop_directive(ln, "only") { + only_test_on_props.push(prop); + } } ignore |= ignore_llvm(config, ln); ignore |= @@ -920,6 +990,19 @@ pub fn make_test_description( should_fail |= config.parse_name_directive(ln, "should-fail"); }); + for prop in ignored_props { + ignore = match config.evaluate_prop_for_target(&prop, &target) { + EvaluatedProp::Match => true, + EvaluatedProp::NoMatch => ignore, + }; + } + for prop in only_test_on_props { + ignore = match config.evaluate_prop_for_target(&prop, &target) { + EvaluatedProp::Match => ignore, + EvaluatedProp::NoMatch => true, + }; + } + // The `should-fail` annotation doesn't apply to pretty tests, // since we run the pretty printer across all tests by default. // If desired, we could add a `should-fail-pretty` annotation.