Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

compiletest: use target cfg instead of hard-coded tables #100260

Merged
merged 3 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
compiletest: use precise cfg matching instead of hard-coded tables
  • Loading branch information
ehuss committed Aug 8, 2022
commit fab899640002eb67d9b55156f447bd435f959568
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()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we confirm that this is picking up the right rustc as part of bootstrap? I'm a little worried by the or_else here -- when does that become necessary? It seems like an easy way for some obscure failure to sneak in.

I think given that libtest will eat stderr here anyway, we can probably just eprintln! in that path with a "warning: using global rustc" or something like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that is a little subtle, I should have left a comment here. The RUSTC here is from stage0. The fallback is only if you are running cargo test manually.

The exact version of rustc isn't too important here, as the tests that depend on running rustc aren't depending on anything too bleeding edge. If any of those targets get removed (or change properties), then test failures probably won't show up until the beta bump after those changes happen. I apologize ahead of time for when that happens since it usually falls on your shoulders. I tried to make sure the asserts included extra context information in case it does.

Printing a warning sounds good, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we've run into some problems around recently introduced targets getting linted by check-cfg in stage0 std; I think we'll be ok for tests though since we typically introduce at tier 3 so there's not any tests running in our CI.

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