From cbb3b03a945550bbaf2aaf7a76343be3340c4fa7 Mon Sep 17 00:00:00 2001 From: inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Date: Fri, 23 Jul 2021 00:23:27 -0700 Subject: [PATCH 1/5] Teach compiletest to parse arbitrary cfg options --- src/tools/compiletest/src/common.rs | 5 +++ src/tools/compiletest/src/header.rs | 36 +++++++++++++++++++- src/tools/compiletest/src/main.rs | 18 +++++++++- src/tools/compiletest/src/util.rs | 51 +++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 82fe790a576ab..a26537f9a8976 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -1,5 +1,6 @@ pub use self::Mode::*; +use std::collections::HashMap; use std::ffi::OsString; use std::fmt; use std::path::{Path, PathBuf}; @@ -275,6 +276,10 @@ pub struct Config { /// not vice versa. pub target_panic: PanicStrategy, + /// A map from target cfg keys to their values. If the key is present with no value, + /// the vector will contain the empty string. + pub target_cfg: HashMap>, + /// Target system to be tested pub target: String, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 5fcaa452ca30e..a2c2b77c85203 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -5,6 +5,8 @@ use std::io::prelude::*; use std::io::BufReader; use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; +use regex::Regex; use tracing::*; use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode}; @@ -669,6 +671,32 @@ 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 { + // This matches optional whitespace, followed by a group containing a series of word + // characters (including '_' and '-'), followed optionally by a sequence consisting + // of a colon, optional whitespace, and another group containing word characters. + // + // Matches in full: + // cfg-target-has-atomic: 128 + // cfg-target-has-atomic:128 + // cfg-target-has-atomic + // + // Matches up to the first space (exclusive): + // ignore-test - This test really shouldn't ever be run + // + // Matches up to the second space (exclusive): + // cfg-target-has-atomic: 128 - this requires fancy newfangled atomics + // + // Matches up to the second colon (exclusive) + // cfg-target-has-atomic:128: I put an extra colon here to confuse other programmers! + // + // Does not match: + // (a line consisting solely of whitespace) + // &*#$ cfg-target-has-atomic + // + lazy_static! { + static ref CFG_REGEX: Regex = Regex::new(r"^\s*([\w-]+)(?::\s*([\w-]+))?").unwrap(); + } + if !line.as_bytes().starts_with(prefix.as_bytes()) { return ParsedNameDirective::NoMatch; } @@ -676,7 +704,9 @@ impl Config { return ParsedNameDirective::NoMatch; } - let name = line[prefix.len() + 1..].split(&[':', ' '][..]).next().unwrap(); + let captures = CFG_REGEX.captures(&line[&prefix.len() + 1..]).unwrap(); + let name = captures.get(1).unwrap().as_str(); + let maybe_value = captures.get(2).map(|v| v.as_str().trim()); let is_match = name == "test" || self.target == name || // triple @@ -704,6 +734,10 @@ impl Config { Some(Debugger::Gdb) => name == "gdb", Some(Debugger::Lldb) => name == "lldb", None => false, + } || + match name.strip_prefix("cfg-") { + Some(rustc_cfg_name) => util::cfg_has(&self.target_cfg, rustc_cfg_name, maybe_value), + None => false }; if is_match { ParsedNameDirective::Match } else { ParsedNameDirective::NoMatch } diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 58cde108b3322..959e94aa85c93 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -11,6 +11,7 @@ use crate::common::{ use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, TestPaths}; use crate::util::logv; use getopts::Options; +use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::fs; @@ -201,6 +202,7 @@ pub fn parse_config(args: Vec) -> Config { let llvm_version = matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version); + let rustc_path = opt_path(matches, "rustc-path"); let src_base = opt_path(matches, "src-base"); let run_ignored = matches.opt_present("ignored"); let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode"); @@ -214,11 +216,24 @@ pub fn parse_config(args: Vec) -> Config { // Avoid spawning an external command when we know tidy won't be used. false }; + + let target_cfg = if cfg!(test) { + HashMap::new() + } else { + let rustc_cfg_data = Command::new(&rustc_path) + .args(&["--target", &target]) + .args(&["--print", "cfg"]) + .output() + .unwrap() + .stdout; + util::parse_rustc_cfg(String::from_utf8(rustc_cfg_data).unwrap()) + }; + Config { bless: matches.opt_present("bless"), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), - rustc_path: opt_path(matches, "rustc-path"), + rustc_path: rustc_path, rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from), rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from), lldb_python: matches.opt_str("lldb-python").unwrap(), @@ -257,6 +272,7 @@ pub fn parse_config(args: Vec) -> Config { Some("abort") => PanicStrategy::Abort, _ => panic!("unknown `--target-panic` option `{}` given", mode), }, + target_cfg, target, host: opt_str2(matches.opt_str("host")), cdb, diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 6ca145a58e92a..6b2dfc10afdee 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -1,4 +1,6 @@ use crate::common::Config; + +use std::collections::HashMap; use std::env; use std::ffi::OsStr; use std::path::PathBuf; @@ -215,6 +217,55 @@ pub fn logv(config: &Config, s: String) { } } +/// Parses the output of `rustc --print cfg` into a [`HashMap`] containing +/// the cfg values for each name. If the name is present with no value, +/// it is treated as having a value of "". +pub fn parse_rustc_cfg(rustc_output: String) -> HashMap> { + let mut map = HashMap::new(); + + for line in rustc_output.lines() { + if let Some((name, value)) = line.split_once('=') { + let normalized_value = value.trim_matches('"'); + cfg_add(&mut map, name, normalized_value); + } else { + cfg_add(&mut map, line, ""); + } + } + + return map; +} + +/// Adds the given name and value to the provided cfg [`HashMap`]. If the `name` already +/// points to a vector, this adds `value` to the vector. If `name` does not point +/// to a vector, this adds a new vector containing only `value` to the [`HashMap`]. +fn cfg_add(map: &mut HashMap>, name: &str, value: &str) { + let name = name.to_string(); + let value = value.to_string(); + + if let Some(values) = map.get_mut(&name) { + values.push(value.to_string()); + } else { + map.insert(name, vec![value.to_string()]); + } +} + +/// Checks if the cfg HashMap has the given `name`. If the `required_value` is +/// `Some(value)`, this will only return `true` if `name` is associated with `value`. +pub fn cfg_has( + map: &HashMap>, + name: &str, + required_value: Option<&str>, +) -> bool { + let name = name.replace("-", "_"); + let required_value = required_value.map(str::trim).map(str::to_string); + + match (map.get(&name), required_value) { + (None, _) => false, + (Some(_), None) => true, + (Some(values), Some(required_value)) => values.contains(&required_value), + } +} + pub trait PathBufExt { /// Append an extension to the path, even if it already has one. fn with_extra_extension>(&self, extension: S) -> PathBuf; From bc315aabf4558fd86f7710e1102757acf5ad11c4 Mon Sep 17 00:00:00 2001 From: inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Date: Fri, 17 Dec 2021 01:44:19 -0800 Subject: [PATCH 2/5] Evaulate cfg props for the real target in compiletest --- src/tools/compiletest/src/common.rs | 5 - src/tools/compiletest/src/header.rs | 148 ++++++++++++++++++++-------- src/tools/compiletest/src/main.rs | 14 --- src/tools/compiletest/src/util.rs | 36 ++++--- 4 files changed, 129 insertions(+), 74 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index a26537f9a8976..82fe790a576ab 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -1,6 +1,5 @@ pub use self::Mode::*; -use std::collections::HashMap; use std::ffi::OsString; use std::fmt; use std::path::{Path, PathBuf}; @@ -276,10 +275,6 @@ pub struct Config { /// not vice versa. pub target_panic: PanicStrategy, - /// A map from target cfg keys to their values. If the key is present with no value, - /// the vector will contain the empty string. - pub target_cfg: HashMap>, - /// Target system to be tested pub target: String, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index a2c2b77c85203..12c6366391fd1 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -16,9 +16,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. @@ -228,6 +231,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; @@ -340,11 +347,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) { @@ -372,6 +384,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 { @@ -650,14 +673,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 { @@ -670,7 +694,19 @@ 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 None; + } + if line.as_bytes().get(prefix.len()) != Some(&b'-') { + return None; + } + + let config = &line[&prefix.len() + 1..].to_string(); + Some(config.to_string()) + } + + fn evaluate_prop_for_target(&self, prop: &str, target: &str) -> EvaluatedProp { // This matches optional whitespace, followed by a group containing a series of word // characters (including '_' and '-'), followed optionally by a sequence consisting // of a colon, optional whitespace, and another group containing word characters. @@ -697,28 +733,21 @@ impl Config { static ref CFG_REGEX: Regex = Regex::new(r"^\s*([\w-]+)(?::\s*([\w-]+))?").unwrap(); } - if !line.as_bytes().starts_with(prefix.as_bytes()) { - return ParsedNameDirective::NoMatch; - } - if line.as_bytes().get(prefix.len()) != Some(&b'-') { - return ParsedNameDirective::NoMatch; - } - - let captures = CFG_REGEX.captures(&line[&prefix.len() + 1..]).unwrap(); + let captures = CFG_REGEX.captures(&prop).unwrap(); let name = captures.get(1).unwrap().as_str(); let maybe_value = captures.get(2).map(|v| v.as_str().trim()); 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", @@ -736,14 +765,17 @@ impl Config { None => false, } || match name.strip_prefix("cfg-") { - Some(rustc_cfg_name) => util::cfg_has(&self.target_cfg, rustc_cfg_name, maybe_value), + Some(rustc_cfg_name) => { + let cfg_data = util::fetch_cfg_from_rustc_for_target(&self.rustc_path, target); + util::cfg_has(&cfg_data, rustc_cfg_name, maybe_value) + }, None => false }; - if is_match { ParsedNameDirective::Match } else { ParsedNameDirective::NoMatch } + if is_match { EvaluatedProp::Match } else { EvaluatedProp::NoMatch } } - 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. @@ -768,6 +800,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"); @@ -900,6 +947,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); @@ -908,7 +959,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") @@ -921,15 +971,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 |= @@ -954,6 +1005,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. diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 959e94aa85c93..3a53d7120c843 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -11,7 +11,6 @@ use crate::common::{ use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, TestPaths}; use crate::util::logv; use getopts::Options; -use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::fs; @@ -217,18 +216,6 @@ pub fn parse_config(args: Vec) -> Config { false }; - let target_cfg = if cfg!(test) { - HashMap::new() - } else { - let rustc_cfg_data = Command::new(&rustc_path) - .args(&["--target", &target]) - .args(&["--print", "cfg"]) - .output() - .unwrap() - .stdout; - util::parse_rustc_cfg(String::from_utf8(rustc_cfg_data).unwrap()) - }; - Config { bless: matches.opt_present("bless"), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), @@ -272,7 +259,6 @@ pub fn parse_config(args: Vec) -> Config { Some("abort") => PanicStrategy::Abort, _ => panic!("unknown `--target-panic` option `{}` given", mode), }, - target_cfg, target, host: opt_str2(matches.opt_str("host")), cdb, diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 6b2dfc10afdee..49bce4ee032eb 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::env; use std::ffi::OsStr; use std::path::PathBuf; +use std::process::Command; use tracing::*; @@ -217,22 +218,31 @@ pub fn logv(config: &Config, s: String) { } } -/// Parses the output of `rustc --print cfg` into a [`HashMap`] containing -/// the cfg values for each name. If the name is present with no value, -/// it is treated as having a value of "". -pub fn parse_rustc_cfg(rustc_output: String) -> HashMap> { - let mut map = HashMap::new(); +pub fn fetch_cfg_from_rustc_for_target>( + rustc_path: &P, + target: &str, +) -> HashMap> { + let mut target_cfg = HashMap::new(); - for line in rustc_output.lines() { - if let Some((name, value)) = line.split_once('=') { - let normalized_value = value.trim_matches('"'); - cfg_add(&mut map, name, normalized_value); - } else { - cfg_add(&mut map, line, ""); + if !cfg!(test) { + let rustc_output = Command::new(&rustc_path) + .args(&["--target", &target]) + .args(&["--print", "cfg"]) + .output() + .unwrap() + .stdout; + let rustc_output = String::from_utf8(rustc_output).unwrap(); + + for line in rustc_output.lines() { + if let Some((name, value)) = line.split_once('=') { + let normalized_value = value.trim_matches('"'); + cfg_add(&mut target_cfg, name, normalized_value); + } else { + cfg_add(&mut target_cfg, line, ""); + } } } - - return map; + target_cfg } /// Adds the given name and value to the provided cfg [`HashMap`]. If the `name` already From e600e46b60f72d2b3e6d38421a077b38a06c5a90 Mon Sep 17 00:00:00 2001 From: inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Date: Sat, 18 Dec 2021 18:41:33 -0800 Subject: [PATCH 3/5] Change implementation per remaining review --- src/tools/compiletest/src/header.rs | 65 +++++++++++------------------ src/tools/compiletest/src/main.rs | 4 +- src/tools/compiletest/src/util.rs | 61 --------------------------- 3 files changed, 26 insertions(+), 104 deletions(-) diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 12c6366391fd1..6e74719f06a76 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -4,10 +4,9 @@ use std::fs::File; use std::io::prelude::*; use std::io::BufReader; use std::path::{Path, PathBuf}; +use std::process::Command; -use lazy_static::lazy_static; -use regex::Regex; -use tracing::*; +use tracing::{self, *}; use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode}; use crate::util; @@ -707,35 +706,9 @@ impl Config { } fn evaluate_prop_for_target(&self, prop: &str, target: &str) -> EvaluatedProp { - // This matches optional whitespace, followed by a group containing a series of word - // characters (including '_' and '-'), followed optionally by a sequence consisting - // of a colon, optional whitespace, and another group containing word characters. - // - // Matches in full: - // cfg-target-has-atomic: 128 - // cfg-target-has-atomic:128 - // cfg-target-has-atomic - // - // Matches up to the first space (exclusive): - // ignore-test - This test really shouldn't ever be run - // - // Matches up to the second space (exclusive): - // cfg-target-has-atomic: 128 - this requires fancy newfangled atomics - // - // Matches up to the second colon (exclusive) - // cfg-target-has-atomic:128: I put an extra colon here to confuse other programmers! - // - // Does not match: - // (a line consisting solely of whitespace) - // &*#$ cfg-target-has-atomic - // - lazy_static! { - static ref CFG_REGEX: Regex = Regex::new(r"^\s*([\w-]+)(?::\s*([\w-]+))?").unwrap(); - } - - let captures = CFG_REGEX.captures(&prop).unwrap(); - let name = captures.get(1).unwrap().as_str(); - let maybe_value = captures.get(2).map(|v| v.as_str().trim()); + let mut iter = prop.split(&[':', ' '][..]); + let name = iter.next().unwrap(); + let maybe_value = iter.find(|s| !s.is_empty()); let is_match = name == "test" || target == name || // triple @@ -763,18 +736,30 @@ impl Config { Some(Debugger::Gdb) => name == "gdb", Some(Debugger::Lldb) => name == "lldb", None => false, - } || - match name.strip_prefix("cfg-") { - Some(rustc_cfg_name) => { - let cfg_data = util::fetch_cfg_from_rustc_for_target(&self.rustc_path, target); - util::cfg_has(&cfg_data, rustc_cfg_name, maybe_value) - }, - 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, ""); + + 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_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", diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 3a53d7120c843..58cde108b3322 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -201,7 +201,6 @@ pub fn parse_config(args: Vec) -> Config { let llvm_version = matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version); - let rustc_path = opt_path(matches, "rustc-path"); let src_base = opt_path(matches, "src-base"); let run_ignored = matches.opt_present("ignored"); let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode"); @@ -215,12 +214,11 @@ pub fn parse_config(args: Vec) -> Config { // Avoid spawning an external command when we know tidy won't be used. false }; - Config { bless: matches.opt_present("bless"), compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), - rustc_path: rustc_path, + rustc_path: opt_path(matches, "rustc-path"), rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from), rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from), lldb_python: matches.opt_str("lldb-python").unwrap(), diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs index 49bce4ee032eb..6ca145a58e92a 100644 --- a/src/tools/compiletest/src/util.rs +++ b/src/tools/compiletest/src/util.rs @@ -1,10 +1,7 @@ use crate::common::Config; - -use std::collections::HashMap; use std::env; use std::ffi::OsStr; use std::path::PathBuf; -use std::process::Command; use tracing::*; @@ -218,64 +215,6 @@ pub fn logv(config: &Config, s: String) { } } -pub fn fetch_cfg_from_rustc_for_target>( - rustc_path: &P, - target: &str, -) -> HashMap> { - let mut target_cfg = HashMap::new(); - - if !cfg!(test) { - let rustc_output = Command::new(&rustc_path) - .args(&["--target", &target]) - .args(&["--print", "cfg"]) - .output() - .unwrap() - .stdout; - let rustc_output = String::from_utf8(rustc_output).unwrap(); - - for line in rustc_output.lines() { - if let Some((name, value)) = line.split_once('=') { - let normalized_value = value.trim_matches('"'); - cfg_add(&mut target_cfg, name, normalized_value); - } else { - cfg_add(&mut target_cfg, line, ""); - } - } - } - target_cfg -} - -/// Adds the given name and value to the provided cfg [`HashMap`]. If the `name` already -/// points to a vector, this adds `value` to the vector. If `name` does not point -/// to a vector, this adds a new vector containing only `value` to the [`HashMap`]. -fn cfg_add(map: &mut HashMap>, name: &str, value: &str) { - let name = name.to_string(); - let value = value.to_string(); - - if let Some(values) = map.get_mut(&name) { - values.push(value.to_string()); - } else { - map.insert(name, vec![value.to_string()]); - } -} - -/// Checks if the cfg HashMap has the given `name`. If the `required_value` is -/// `Some(value)`, this will only return `true` if `name` is associated with `value`. -pub fn cfg_has( - map: &HashMap>, - name: &str, - required_value: Option<&str>, -) -> bool { - let name = name.replace("-", "_"); - let required_value = required_value.map(str::trim).map(str::to_string); - - match (map.get(&name), required_value) { - (None, _) => false, - (Some(_), None) => true, - (Some(values), Some(required_value)) => values.contains(&required_value), - } -} - pub trait PathBufExt { /// Append an extension to the path, even if it already has one. fn with_extra_extension>(&self, extension: S) -> PathBuf; From 50656c8a60a67037688a60f2039b8d5ac401024d Mon Sep 17 00:00:00 2001 From: inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Date: Sun, 2 Jan 2022 23:19:24 -0800 Subject: [PATCH 4/5] Fix test --- src/test/assembly/asm/aarch64-outline-atomics.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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"] From ae9208344763c294b4b3617851a7946ad5e9ddf2 Mon Sep 17 00:00:00 2001 From: inquisitivecrystal <22333129+inquisitivecrystal@users.noreply.github.com> Date: Mon, 3 Jan 2022 18:17:03 -0800 Subject: [PATCH 5/5] Add tests --- src/test/ui/meta/cfg-ignore-with-target.rs | 12 ++++++++++++ src/test/ui/meta/cfg-ignore.rs | 10 ++++++++++ src/test/ui/meta/cfg-only.rs | 10 ++++++++++ 3 files changed, 32 insertions(+) create mode 100644 src/test/ui/meta/cfg-ignore-with-target.rs create mode 100644 src/test/ui/meta/cfg-ignore.rs create mode 100644 src/test/ui/meta/cfg-only.rs 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() {}