Skip to content

Commit

Permalink
compiletest: use precise cfg matching instead of hard-coded tables
Browse files Browse the repository at this point in the history
  • Loading branch information
ehuss committed Aug 8, 2022
1 parent 9ee22ff commit fab8996
Show file tree
Hide file tree
Showing 9 changed files with 302 additions and 208 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,7 @@ dependencies = [
"getopts",
"glob",
"lazy_static",
"lazycell",
"libc",
"miow",
"regex",
Expand Down
1 change: 1 addition & 0 deletions src/tools/compiletest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ rustfix = "0.6.0"
lazy_static = "1.0"
walkdir = "2"
glob = "0.3.0"
lazycell = "1.3.0"

[target.'cfg(unix)'.dependencies]
libc = "0.2"
Expand Down
121 changes: 121 additions & 0 deletions src/tools/compiletest/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ pub use self::Mode::*;
use std::ffi::OsString;
use std::fmt;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;

use crate::util::PathBufExt;
use lazycell::LazyCell;
use test::ColorConfig;

#[derive(Clone, Copy, PartialEq, Debug)]
Expand Down Expand Up @@ -371,6 +373,8 @@ pub struct Config {

/// Whether to rerun tests even if the inputs are unchanged.
pub force_rerun: bool,

pub target_cfg: LazyCell<TargetCfg>,
}

impl Config {
Expand All @@ -380,6 +384,123 @@ impl Config {
!self.target.ends_with("-fuchsia")
})
}

fn target_cfg(&self) -> &TargetCfg {
self.target_cfg.borrow_with(|| TargetCfg::new(&self.rustc_path, &self.target))
}

pub fn matches_arch(&self, arch: &str) -> bool {
self.target_cfg().arch == arch ||
// Shorthand for convenience. The arch for
// asmjs-unknown-emscripten is actually wasm32.
(arch == "asmjs" && self.target.starts_with("asmjs")) ||
// Matching all the thumb variants as one can be convenient.
// (thumbv6m, thumbv7em, thumbv7m, etc.)
(arch == "thumb" && self.target.starts_with("thumb"))
}

pub fn matches_os(&self, os: &str) -> bool {
self.target_cfg().os == os
}

pub fn matches_env(&self, env: &str) -> bool {
self.target_cfg().env == env
}

pub fn matches_abi(&self, abi: &str) -> bool {
self.target_cfg().abi == abi
}

pub fn is_big_endian(&self) -> bool {
self.target_cfg().endian == Endian::Big
}

pub fn get_pointer_width(&self) -> u32 {
*&self.target_cfg().pointer_width
}

pub fn has_asm_support(&self) -> bool {
static ASM_SUPPORTED_ARCHS: &[&str] = &[
"x86", "x86_64", "arm", "aarch64", "riscv32",
"riscv64",
// These targets require an additional asm_experimental_arch feature.
// "nvptx64", "hexagon", "mips", "mips64", "spirv", "wasm32",
];
ASM_SUPPORTED_ARCHS.contains(&self.target_cfg().arch.as_str())
}
}

#[derive(Clone, Debug)]
pub struct TargetCfg {
arch: String,
os: String,
env: String,
abi: String,
pointer_width: u32,
endian: Endian,
}

#[derive(Eq, PartialEq, Clone, Debug)]
pub enum Endian {
Little,
Big,
}

impl TargetCfg {
fn new(rustc_path: &Path, target: &str) -> TargetCfg {
let output = match Command::new(rustc_path)
.arg("--print=cfg")
.arg("--target")
.arg(target)
.output()
{
Ok(output) => output,
Err(e) => panic!("error: failed to get cfg info from {:?}: {e}", rustc_path),
};
if !output.status.success() {
panic!(
"error: failed to get cfg info from {:?}\n--- stdout\n{}\n--- stderr\n{}",
rustc_path,
String::from_utf8(output.stdout).unwrap(),
String::from_utf8(output.stderr).unwrap(),
);
}
let print_cfg = String::from_utf8(output.stdout).unwrap();
let mut arch = None;
let mut os = None;
let mut env = None;
let mut abi = None;
let mut pointer_width = None;
let mut endian = None;
for line in print_cfg.lines() {
if let Some((name, value)) = line.split_once('=') {
let value = value.trim_matches('"');
match name {
"target_arch" => arch = Some(value),
"target_os" => os = Some(value),
"target_env" => env = Some(value),
"target_abi" => abi = Some(value),
"target_pointer_width" => pointer_width = Some(value.parse().unwrap()),
"target_endian" => {
endian = Some(match value {
"little" => Endian::Little,
"big" => Endian::Big,
s => panic!("unexpected {s}"),
})
}
_ => {}
}
}
}
TargetCfg {
arch: arch.unwrap().to_string(),
os: os.unwrap().to_string(),
env: env.unwrap().to_string(),
abi: abi.unwrap().to_string(),
pointer_width: pointer_width.unwrap(),
endian: endian.unwrap(),
}
}
}

#[derive(Debug, Clone)]
Expand Down
30 changes: 24 additions & 6 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,17 +661,35 @@ impl Config {

let name = line[prefix.len() + 1..].split(&[':', ' '][..]).next().unwrap();

let matches_pointer_width = || {
name.strip_suffix("bit")
.and_then(|width| width.parse::<u32>().ok())
.map(|width| self.get_pointer_width() == width)
.unwrap_or(false)
};

// 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).
let matches_wasm32_alias = || {
self.target == "wasm32-unknown-unknown" && matches!(name, "emscripten" | "wasm32-bare")
};

let is_match = name == "test" ||
self.target == name || // triple
util::matches_os(&self.target, name) || // target
util::matches_env(&self.target, name) || // env
self.matches_os(name) ||
self.matches_env(name) ||
self.matches_abi(name) ||
self.target.ends_with(name) || // target and env
name == util::get_arch(&self.target) || // architecture
name == util::get_pointer_width(&self.target) || // pointer width
self.matches_arch(name) ||
matches_wasm32_alias() ||
matches_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)) ||
(name == "endian-big" && self.is_big_endian()) ||
(self.remote_test_client.is_some() && name == "remote") ||
match self.compare_mode {
Some(CompareMode::Polonius) => name == "compare-mode-polonius",
Expand Down Expand Up @@ -869,7 +887,7 @@ pub fn make_test_description<R: Read>(

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);
let has_asm_support = config.has_asm_support();
let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target);
let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
Expand Down
158 changes: 149 additions & 9 deletions src/tools/compiletest/src/header/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ fn config() -> Config {
"--suite=ui",
"--compile-lib-path=",
"--run-lib-path=",
"--rustc-path=",
"--python=",
"--jsondocck-path=",
"--src-base=",
Expand All @@ -57,7 +56,9 @@ fn config() -> Config {
"--target=x86_64-unknown-linux-gnu",
"--channel=nightly",
];
let args = args.iter().map(ToString::to_string).collect();
let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
args.push("--rustc-path".to_string());
args.push(std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()));
crate::parse_config(args)
}

Expand Down Expand Up @@ -237,13 +238,20 @@ fn sanitizers() {

#[test]
fn asm_support() {
let mut config = config();

config.target = "avr-unknown-gnu-atmega328".to_owned();
assert!(check_ignore(&config, "// needs-asm-support"));

config.target = "i686-unknown-netbsd".to_owned();
assert!(!check_ignore(&config, "// needs-asm-support"));
let asms = [
("avr-unknown-gnu-atmega328", false),
("i686-unknown-netbsd", true),
("riscv32gc-unknown-linux-gnu", true),
("riscv64imac-unknown-none-elf", true),
("x86_64-unknown-linux-gnu", true),
("i686-unknown-netbsd", true),
];
for (target, has_asm) in asms {
let mut config = config();
config.target = target.to_string();
assert_eq!(config.has_asm_support(), has_asm);
assert_eq!(check_ignore(&config, "// needs-asm-support"), !has_asm)
}
}

#[test]
Expand Down Expand Up @@ -281,3 +289,135 @@ fn test_duplicate_revisions() {
let config = config();
parse_rs(&config, "// revisions: rpass1 rpass1");
}

#[test]
fn ignore_arch() {
let archs = [
("x86_64-unknown-linux-gnu", "x86_64"),
("i686-unknown-linux-gnu", "x86"),
("nvptx64-nvidia-cuda", "nvptx64"),
("asmjs-unknown-emscripten", "wasm32"),
("asmjs-unknown-emscripten", "asmjs"),
("thumbv7m-none-eabi", "thumb"),
];
for (target, arch) in archs {
let mut config = config();
config.target = target.to_string();
assert!(config.matches_arch(arch), "{target} {arch}");
assert!(check_ignore(&config, &format!("// ignore-{arch}")));
}
}

#[test]
fn matches_os() {
let oss = [
("x86_64-unknown-linux-gnu", "linux"),
("x86_64-fortanix-unknown-sgx", "unknown"),
("wasm32-unknown-unknown", "unknown"),
("x86_64-unknown-none", "none"),
];
for (target, os) in oss {
let mut config = config();
config.target = target.to_string();
assert!(config.matches_os(os), "{target} {os}");
assert!(check_ignore(&config, &format!("// ignore-{os}")));
}
}

#[test]
fn matches_env() {
let envs = [
("x86_64-unknown-linux-gnu", "gnu"),
("x86_64-fortanix-unknown-sgx", "sgx"),
("arm-unknown-linux-musleabi", "musl"),
];
for (target, env) in envs {
let mut config = config();
config.target = target.to_string();
assert!(config.matches_env(env), "{target} {env}");
assert!(check_ignore(&config, &format!("// ignore-{env}")));
}
}

#[test]
fn matches_abi() {
let abis = [
("aarch64-apple-ios-macabi", "macabi"),
("x86_64-unknown-linux-gnux32", "x32"),
("arm-unknown-linux-gnueabi", "eabi"),
];
for (target, abi) in abis {
let mut config = config();
config.target = target.to_string();
assert!(config.matches_abi(abi), "{target} {abi}");
assert!(check_ignore(&config, &format!("// ignore-{abi}")));
}
}

#[test]
fn is_big_endian() {
let endians = [
("x86_64-unknown-linux-gnu", false),
("bpfeb-unknown-none", true),
("m68k-unknown-linux-gnu", true),
("aarch64_be-unknown-linux-gnu", true),
("powerpc64-unknown-linux-gnu", true),
];
for (target, is_big) in endians {
let mut config = config();
config.target = target.to_string();
assert_eq!(config.is_big_endian(), is_big, "{target} {is_big}");
assert_eq!(check_ignore(&config, "// ignore-endian-big"), is_big);
}
}

#[test]
fn pointer_width() {
let widths = [
("x86_64-unknown-linux-gnu", 64),
("i686-unknown-linux-gnu", 32),
("arm64_32-apple-watchos", 32),
("msp430-none-elf", 16),
];
for (target, width) in widths {
let mut config = config();
config.target = target.to_string();
assert_eq!(config.get_pointer_width(), width, "{target} {width}");
assert_eq!(check_ignore(&config, "// ignore-16bit"), width == 16);
assert_eq!(check_ignore(&config, "// ignore-32bit"), width == 32);
assert_eq!(check_ignore(&config, "// ignore-64bit"), width == 64);
}
}

#[test]
fn wasm_special() {
let ignores = [
("wasm32-unknown-unknown", "emscripten", true),
("wasm32-unknown-unknown", "wasm32", true),
("wasm32-unknown-unknown", "wasm32-bare", true),
("wasm32-unknown-unknown", "wasm64", false),
("asmjs-unknown-emscripten", "emscripten", true),
("asmjs-unknown-emscripten", "wasm32", true),
("asmjs-unknown-emscripten", "wasm32-bare", false),
("wasm32-unknown-emscripten", "emscripten", true),
("wasm32-unknown-emscripten", "wasm32", true),
("wasm32-unknown-emscripten", "wasm32-bare", false),
("wasm32-wasi", "emscripten", false),
("wasm32-wasi", "wasm32", true),
("wasm32-wasi", "wasm32-bare", false),
("wasm32-wasi", "wasi", true),
("wasm64-unknown-unknown", "emscripten", false),
("wasm64-unknown-unknown", "wasm32", false),
("wasm64-unknown-unknown", "wasm32-bare", false),
("wasm64-unknown-unknown", "wasm64", true),
];
for (target, pattern, ignore) in ignores {
let mut config = config();
config.target = target.to_string();
assert_eq!(
check_ignore(&config, &format!("// ignore-{pattern}")),
ignore,
"{target} {pattern}"
);
}
}
Loading

0 comments on commit fab8996

Please # to comment.