move cfg handling into its own module
This commit is contained in:
parent
8eb3def446
commit
9cb4373a84
2 changed files with 239 additions and 231 deletions
|
|
@ -8,41 +8,16 @@ use std::process::Command;
|
|||
|
||||
use tracing::*;
|
||||
|
||||
use crate::common::CompareMode;
|
||||
use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
|
||||
use crate::header::cfg::parse_cfg_name_directive;
|
||||
use crate::header::cfg::MatchOutcome;
|
||||
use crate::util;
|
||||
use crate::{extract_cdb_version, extract_gdb_version};
|
||||
|
||||
mod cfg;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// The result of parse_cfg_name_directive.
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
struct ParsedNameDirective<'a> {
|
||||
name: Option<&'a str>,
|
||||
pretty_reason: Option<String>,
|
||||
comment: Option<&'a str>,
|
||||
outcome: MatchOutcome,
|
||||
}
|
||||
|
||||
impl ParsedNameDirective<'_> {
|
||||
fn invalid() -> Self {
|
||||
Self { name: None, pretty_reason: None, comment: None, outcome: MatchOutcome::NoMatch }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
enum MatchOutcome {
|
||||
/// No match.
|
||||
NoMatch,
|
||||
/// Match.
|
||||
Match,
|
||||
/// The directive was invalid.
|
||||
Invalid,
|
||||
/// The directive is handled by other parts of our tooling.
|
||||
External,
|
||||
}
|
||||
|
||||
/// Properties which must be known very early, before actually running
|
||||
/// the test.
|
||||
#[derive(Default)]
|
||||
|
|
@ -666,7 +641,7 @@ impl Config {
|
|||
}
|
||||
|
||||
fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> {
|
||||
if self.parse_cfg_name_directive(line, prefix).outcome == MatchOutcome::Match {
|
||||
if parse_cfg_name_directive(self, line, prefix).outcome == MatchOutcome::Match {
|
||||
let from = parse_normalization_string(&mut line)?;
|
||||
let to = parse_normalization_string(&mut line)?;
|
||||
Some((from, to))
|
||||
|
|
@ -683,206 +658,6 @@ impl Config {
|
|||
self.parse_name_directive(line, "needs-profiler-support")
|
||||
}
|
||||
|
||||
/// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
|
||||
/// or `normalize-stderr-32bit`.
|
||||
fn parse_cfg_name_directive<'a>(&self, line: &'a str, prefix: &str) -> ParsedNameDirective<'a> {
|
||||
if !line.as_bytes().starts_with(prefix.as_bytes()) {
|
||||
return ParsedNameDirective::invalid();
|
||||
}
|
||||
if line.as_bytes().get(prefix.len()) != Some(&b'-') {
|
||||
return ParsedNameDirective::invalid();
|
||||
}
|
||||
let line = &line[prefix.len() + 1..];
|
||||
|
||||
let (name, comment) =
|
||||
line.split_once(&[':', ' ']).map(|(l, c)| (l, Some(c))).unwrap_or((line, None));
|
||||
|
||||
// Some of the matchers might be "" depending on what the target information is. To avoid
|
||||
// problems we outright reject empty directives.
|
||||
if name == "" {
|
||||
return ParsedNameDirective::invalid();
|
||||
}
|
||||
|
||||
let mut outcome = MatchOutcome::Invalid;
|
||||
let mut message = None;
|
||||
|
||||
macro_rules! maybe_condition {
|
||||
(
|
||||
name: $name:expr,
|
||||
$(allowed_names: $allowed_names:expr,)?
|
||||
$(condition: $condition:expr,)?
|
||||
message: $($message:tt)*
|
||||
) => {{
|
||||
// This is not inlined to avoid problems with macro repetitions.
|
||||
let format_message = || format!($($message)*);
|
||||
|
||||
if outcome != MatchOutcome::Invalid {
|
||||
// Ignore all other matches if we already found one
|
||||
} else if $name.as_ref().map(|n| n == &name).unwrap_or(false) {
|
||||
message = Some(format_message());
|
||||
if true $(&& $condition)? {
|
||||
outcome = MatchOutcome::Match;
|
||||
} else {
|
||||
outcome = MatchOutcome::NoMatch;
|
||||
}
|
||||
}
|
||||
$(else if $allowed_names.contains(name) {
|
||||
message = Some(format_message());
|
||||
outcome = MatchOutcome::NoMatch;
|
||||
})?
|
||||
}};
|
||||
}
|
||||
macro_rules! condition {
|
||||
(
|
||||
name: $name:expr,
|
||||
$(allowed_names: $allowed_names:expr,)?
|
||||
$(condition: $condition:expr,)?
|
||||
message: $($message:tt)*
|
||||
) => {
|
||||
maybe_condition! {
|
||||
name: Some($name),
|
||||
$(allowed_names: $allowed_names,)*
|
||||
$(condition: $condition,)*
|
||||
message: $($message)*
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! hashset {
|
||||
($($value:expr),* $(,)?) => {{
|
||||
let mut set = HashSet::new();
|
||||
$(set.insert($value);)*
|
||||
set
|
||||
}}
|
||||
}
|
||||
|
||||
let target_cfgs = self.target_cfgs();
|
||||
let target_cfg = self.target_cfg();
|
||||
|
||||
condition! {
|
||||
name: "test",
|
||||
message: "always"
|
||||
}
|
||||
condition! {
|
||||
name: &self.target,
|
||||
allowed_names: &target_cfgs.all_targets,
|
||||
message: "when the target is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: &target_cfg.os,
|
||||
allowed_names: &target_cfgs.all_oses,
|
||||
message: "when the operative system is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: &target_cfg.env,
|
||||
allowed_names: &target_cfgs.all_envs,
|
||||
message: "when the target environment is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: &target_cfg.abi,
|
||||
allowed_names: &target_cfgs.all_abis,
|
||||
message: "when the ABI is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: &target_cfg.arch,
|
||||
allowed_names: &target_cfgs.all_archs,
|
||||
message: "when the architecture is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: format!("{}bit", target_cfg.pointer_width),
|
||||
allowed_names: &target_cfgs.all_pointer_widths,
|
||||
message: "when the pointer width is {name}"
|
||||
}
|
||||
for family in &target_cfg.families {
|
||||
condition! {
|
||||
name: family,
|
||||
allowed_names: &target_cfgs.all_families,
|
||||
message: "when the target family is {name}"
|
||||
}
|
||||
}
|
||||
|
||||
// If something is ignored for emscripten, it likely also needs to be
|
||||
// ignored for wasm32-unknown-unknown.
|
||||
// `wasm32-bare` is an alias to refer to just wasm32-unknown-unknown
|
||||
// (in contrast to `wasm32` which also matches non-bare targets like
|
||||
// asmjs-unknown-emscripten).
|
||||
condition! {
|
||||
name: "emscripten",
|
||||
condition: self.target == "wasm32-unknown-unknown",
|
||||
message: "when the target is WASM",
|
||||
}
|
||||
condition! {
|
||||
name: "wasm32-bare",
|
||||
condition: self.target == "wasm32-unknown-unknown",
|
||||
message: "when the target is WASM"
|
||||
}
|
||||
|
||||
condition! {
|
||||
name: &self.channel,
|
||||
allowed_names: hashset!["stable", "beta", "nightly"],
|
||||
message: "when the release channel is {name}",
|
||||
}
|
||||
condition! {
|
||||
name: "cross-compile",
|
||||
condition: self.target != self.host,
|
||||
message: "when cross-compiling"
|
||||
}
|
||||
condition! {
|
||||
name: "endian-big",
|
||||
condition: self.is_big_endian(),
|
||||
message: "on big-endian targets",
|
||||
}
|
||||
condition! {
|
||||
name: self.stage_id.split('-').next().unwrap(),
|
||||
allowed_names: hashset!["stable", "beta", "nightly"],
|
||||
message: "when the bootstrapping stage is {name}",
|
||||
}
|
||||
condition! {
|
||||
name: "remote",
|
||||
condition: self.remote_test_client.is_some(),
|
||||
message: "when running tests remotely",
|
||||
}
|
||||
condition! {
|
||||
name: "debug",
|
||||
condition: cfg!(debug_assertions),
|
||||
message: "when building with debug assertions",
|
||||
}
|
||||
maybe_condition! {
|
||||
name: self.debugger.as_ref().map(|d| d.to_str()),
|
||||
allowed_names: Debugger::VARIANTS
|
||||
.iter()
|
||||
.map(|v| v.to_str())
|
||||
.collect::<HashSet<_>>(),
|
||||
message: "when the debugger is {name}",
|
||||
}
|
||||
maybe_condition! {
|
||||
name: self.compare_mode
|
||||
.as_ref()
|
||||
.map(|d| format!("compare-mode-{}", d.to_str())),
|
||||
allowed_names: CompareMode::VARIANTS
|
||||
.iter()
|
||||
.map(|cm| format!("compare-mode-{}", cm.to_str()))
|
||||
.collect::<HashSet<_>>(),
|
||||
message: "when comparing with {name}",
|
||||
}
|
||||
|
||||
// Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest.
|
||||
if prefix == "ignore" && name.starts_with("tidy-") && outcome == MatchOutcome::Invalid {
|
||||
outcome = MatchOutcome::External;
|
||||
}
|
||||
|
||||
// Don't error out for ignore-pass, as that is handled elsewhere.
|
||||
if prefix == "ignore" && name == "pass" && outcome == MatchOutcome::Invalid {
|
||||
outcome = MatchOutcome::External;
|
||||
}
|
||||
|
||||
ParsedNameDirective {
|
||||
name: Some(name),
|
||||
comment: comment.map(|c| c.trim().trim_start_matches('-').trim()),
|
||||
outcome,
|
||||
pretty_reason: message,
|
||||
}
|
||||
}
|
||||
|
||||
fn has_cfg_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",
|
||||
|
|
@ -1151,7 +926,7 @@ pub fn make_test_description<R: Read>(
|
|||
}
|
||||
|
||||
{
|
||||
let parsed = config.parse_cfg_name_directive(ln, "ignore");
|
||||
let parsed = parse_cfg_name_directive(config, ln, "ignore");
|
||||
ignore = match parsed.outcome {
|
||||
MatchOutcome::Match => {
|
||||
let reason = parsed.pretty_reason.unwrap();
|
||||
|
|
@ -1171,7 +946,7 @@ pub fn make_test_description<R: Read>(
|
|||
}
|
||||
|
||||
if config.has_cfg_prefix(ln, "only") {
|
||||
let parsed = config.parse_cfg_name_directive(ln, "only");
|
||||
let parsed = parse_cfg_name_directive(config, ln, "only");
|
||||
ignore = match parsed.outcome {
|
||||
MatchOutcome::Match => ignore,
|
||||
MatchOutcome::NoMatch => {
|
||||
|
|
|
|||
233
src/tools/compiletest/src/header/cfg.rs
Normal file
233
src/tools/compiletest/src/header/cfg.rs
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
use crate::common::{Config, CompareMode, Debugger};
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
|
||||
/// or `normalize-stderr-32bit`.
|
||||
pub(super) fn parse_cfg_name_directive<'a>(
|
||||
config: &Config,
|
||||
line: &'a str,
|
||||
prefix: &str,
|
||||
) -> ParsedNameDirective<'a> {
|
||||
if !line.as_bytes().starts_with(prefix.as_bytes()) {
|
||||
return ParsedNameDirective::invalid();
|
||||
}
|
||||
if line.as_bytes().get(prefix.len()) != Some(&b'-') {
|
||||
return ParsedNameDirective::invalid();
|
||||
}
|
||||
let line = &line[prefix.len() + 1..];
|
||||
|
||||
let (name, comment) =
|
||||
line.split_once(&[':', ' ']).map(|(l, c)| (l, Some(c))).unwrap_or((line, None));
|
||||
|
||||
// Some of the matchers might be "" depending on what the target information is. To avoid
|
||||
// problems we outright reject empty directives.
|
||||
if name == "" {
|
||||
return ParsedNameDirective::invalid();
|
||||
}
|
||||
|
||||
let mut outcome = MatchOutcome::Invalid;
|
||||
let mut message = None;
|
||||
|
||||
macro_rules! maybe_condition {
|
||||
(
|
||||
name: $name:expr,
|
||||
$(allowed_names: $allowed_names:expr,)?
|
||||
$(condition: $condition:expr,)?
|
||||
message: $($message:tt)*
|
||||
) => {{
|
||||
// This is not inlined to avoid problems with macro repetitions.
|
||||
let format_message = || format!($($message)*);
|
||||
|
||||
if outcome != MatchOutcome::Invalid {
|
||||
// Ignore all other matches if we already found one
|
||||
} else if $name.as_ref().map(|n| n == &name).unwrap_or(false) {
|
||||
message = Some(format_message());
|
||||
if true $(&& $condition)? {
|
||||
outcome = MatchOutcome::Match;
|
||||
} else {
|
||||
outcome = MatchOutcome::NoMatch;
|
||||
}
|
||||
}
|
||||
$(else if $allowed_names.contains(name) {
|
||||
message = Some(format_message());
|
||||
outcome = MatchOutcome::NoMatch;
|
||||
})?
|
||||
}};
|
||||
}
|
||||
macro_rules! condition {
|
||||
(
|
||||
name: $name:expr,
|
||||
$(allowed_names: $allowed_names:expr,)?
|
||||
$(condition: $condition:expr,)?
|
||||
message: $($message:tt)*
|
||||
) => {
|
||||
maybe_condition! {
|
||||
name: Some($name),
|
||||
$(allowed_names: $allowed_names,)*
|
||||
$(condition: $condition,)*
|
||||
message: $($message)*
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! hashset {
|
||||
($($value:expr),* $(,)?) => {{
|
||||
let mut set = HashSet::new();
|
||||
$(set.insert($value);)*
|
||||
set
|
||||
}}
|
||||
}
|
||||
|
||||
let target_cfgs = config.target_cfgs();
|
||||
let target_cfg = config.target_cfg();
|
||||
|
||||
condition! {
|
||||
name: "test",
|
||||
message: "always"
|
||||
}
|
||||
condition! {
|
||||
name: &config.target,
|
||||
allowed_names: &target_cfgs.all_targets,
|
||||
message: "when the target is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: &target_cfg.os,
|
||||
allowed_names: &target_cfgs.all_oses,
|
||||
message: "when the operative system is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: &target_cfg.env,
|
||||
allowed_names: &target_cfgs.all_envs,
|
||||
message: "when the target environment is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: &target_cfg.abi,
|
||||
allowed_names: &target_cfgs.all_abis,
|
||||
message: "when the ABI is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: &target_cfg.arch,
|
||||
allowed_names: &target_cfgs.all_archs,
|
||||
message: "when the architecture is {name}"
|
||||
}
|
||||
condition! {
|
||||
name: format!("{}bit", target_cfg.pointer_width),
|
||||
allowed_names: &target_cfgs.all_pointer_widths,
|
||||
message: "when the pointer width is {name}"
|
||||
}
|
||||
for family in &target_cfg.families {
|
||||
condition! {
|
||||
name: family,
|
||||
allowed_names: &target_cfgs.all_families,
|
||||
message: "when the target family is {name}"
|
||||
}
|
||||
}
|
||||
|
||||
// If something is ignored for emscripten, it likely also needs to be
|
||||
// ignored for wasm32-unknown-unknown.
|
||||
// `wasm32-bare` is an alias to refer to just wasm32-unknown-unknown
|
||||
// (in contrast to `wasm32` which also matches non-bare targets like
|
||||
// asmjs-unknown-emscripten).
|
||||
condition! {
|
||||
name: "emscripten",
|
||||
condition: config.target == "wasm32-unknown-unknown",
|
||||
message: "when the target is WASM",
|
||||
}
|
||||
condition! {
|
||||
name: "wasm32-bare",
|
||||
condition: config.target == "wasm32-unknown-unknown",
|
||||
message: "when the target is WASM"
|
||||
}
|
||||
|
||||
condition! {
|
||||
name: &config.channel,
|
||||
allowed_names: hashset!["stable", "beta", "nightly"],
|
||||
message: "when the release channel is {name}",
|
||||
}
|
||||
condition! {
|
||||
name: "cross-compile",
|
||||
condition: config.target != config.host,
|
||||
message: "when cross-compiling"
|
||||
}
|
||||
condition! {
|
||||
name: "endian-big",
|
||||
condition: config.is_big_endian(),
|
||||
message: "on big-endian targets",
|
||||
}
|
||||
condition! {
|
||||
name: config.stage_id.split('-').next().unwrap(),
|
||||
allowed_names: hashset!["stable", "beta", "nightly"],
|
||||
message: "when the bootstrapping stage is {name}",
|
||||
}
|
||||
condition! {
|
||||
name: "remote",
|
||||
condition: config.remote_test_client.is_some(),
|
||||
message: "when running tests remotely",
|
||||
}
|
||||
condition! {
|
||||
name: "debug",
|
||||
condition: cfg!(debug_assertions),
|
||||
message: "when building with debug assertions",
|
||||
}
|
||||
maybe_condition! {
|
||||
name: config.debugger.as_ref().map(|d| d.to_str()),
|
||||
allowed_names: Debugger::VARIANTS
|
||||
.iter()
|
||||
.map(|v| v.to_str())
|
||||
.collect::<HashSet<_>>(),
|
||||
message: "when the debugger is {name}",
|
||||
}
|
||||
maybe_condition! {
|
||||
name: config.compare_mode
|
||||
.as_ref()
|
||||
.map(|d| format!("compare-mode-{}", d.to_str())),
|
||||
allowed_names: CompareMode::VARIANTS
|
||||
.iter()
|
||||
.map(|cm| format!("compare-mode-{}", cm.to_str()))
|
||||
.collect::<HashSet<_>>(),
|
||||
message: "when comparing with {name}",
|
||||
}
|
||||
|
||||
// Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest.
|
||||
if prefix == "ignore" && name.starts_with("tidy-") && outcome == MatchOutcome::Invalid {
|
||||
outcome = MatchOutcome::External;
|
||||
}
|
||||
|
||||
// Don't error out for ignore-pass, as that is handled elsewhere.
|
||||
if prefix == "ignore" && name == "pass" && outcome == MatchOutcome::Invalid {
|
||||
outcome = MatchOutcome::External;
|
||||
}
|
||||
|
||||
ParsedNameDirective {
|
||||
name: Some(name),
|
||||
comment: comment.map(|c| c.trim().trim_start_matches('-').trim()),
|
||||
outcome,
|
||||
pretty_reason: message,
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of parse_cfg_name_directive.
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub(super) struct ParsedNameDirective<'a> {
|
||||
pub(super) name: Option<&'a str>,
|
||||
pub(super) pretty_reason: Option<String>,
|
||||
pub(super) comment: Option<&'a str>,
|
||||
pub(super) outcome: MatchOutcome,
|
||||
}
|
||||
|
||||
impl ParsedNameDirective<'_> {
|
||||
fn invalid() -> Self {
|
||||
Self { name: None, pretty_reason: None, comment: None, outcome: MatchOutcome::NoMatch }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub(super) enum MatchOutcome {
|
||||
/// No match.
|
||||
NoMatch,
|
||||
/// Match.
|
||||
Match,
|
||||
/// The directive was invalid.
|
||||
Invalid,
|
||||
/// The directive is handled by other parts of our tooling.
|
||||
External,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue