Skip to content

Commit cbb3b03

Browse files
Teach compiletest to parse arbitrary cfg options
1 parent a2d25b4 commit cbb3b03

File tree

4 files changed

+108
-2
lines changed

4 files changed

+108
-2
lines changed

src/tools/compiletest/src/common.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub use self::Mode::*;
22

3+
use std::collections::HashMap;
34
use std::ffi::OsString;
45
use std::fmt;
56
use std::path::{Path, PathBuf};
@@ -275,6 +276,10 @@ pub struct Config {
275276
/// not vice versa.
276277
pub target_panic: PanicStrategy,
277278

279+
/// A map from target cfg keys to their values. If the key is present with no value,
280+
/// the vector will contain the empty string.
281+
pub target_cfg: HashMap<String, Vec<String>>,
282+
278283
/// Target system to be tested
279284
pub target: String,
280285

src/tools/compiletest/src/header.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::io::prelude::*;
55
use std::io::BufReader;
66
use std::path::{Path, PathBuf};
77

8+
use lazy_static::lazy_static;
9+
use regex::Regex;
810
use tracing::*;
911

1012
use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode};
@@ -669,14 +671,42 @@ impl Config {
669671
/// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
670672
/// or `normalize-stderr-32bit`.
671673
fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective {
674+
// This matches optional whitespace, followed by a group containing a series of word
675+
// characters (including '_' and '-'), followed optionally by a sequence consisting
676+
// of a colon, optional whitespace, and another group containing word characters.
677+
//
678+
// Matches in full:
679+
// cfg-target-has-atomic: 128
680+
// cfg-target-has-atomic:128
681+
// cfg-target-has-atomic
682+
//
683+
// Matches up to the first space (exclusive):
684+
// ignore-test - This test really shouldn't ever be run
685+
//
686+
// Matches up to the second space (exclusive):
687+
// cfg-target-has-atomic: 128 - this requires fancy newfangled atomics
688+
//
689+
// Matches up to the second colon (exclusive)
690+
// cfg-target-has-atomic:128: I put an extra colon here to confuse other programmers!
691+
//
692+
// Does not match:
693+
// (a line consisting solely of whitespace)
694+
// &*#$ cfg-target-has-atomic
695+
//
696+
lazy_static! {
697+
static ref CFG_REGEX: Regex = Regex::new(r"^\s*([\w-]+)(?::\s*([\w-]+))?").unwrap();
698+
}
699+
672700
if !line.as_bytes().starts_with(prefix.as_bytes()) {
673701
return ParsedNameDirective::NoMatch;
674702
}
675703
if line.as_bytes().get(prefix.len()) != Some(&b'-') {
676704
return ParsedNameDirective::NoMatch;
677705
}
678706

679-
let name = line[prefix.len() + 1..].split(&[':', ' '][..]).next().unwrap();
707+
let captures = CFG_REGEX.captures(&line[&prefix.len() + 1..]).unwrap();
708+
let name = captures.get(1).unwrap().as_str();
709+
let maybe_value = captures.get(2).map(|v| v.as_str().trim());
680710

681711
let is_match = name == "test" ||
682712
self.target == name || // triple
@@ -704,6 +734,10 @@ impl Config {
704734
Some(Debugger::Gdb) => name == "gdb",
705735
Some(Debugger::Lldb) => name == "lldb",
706736
None => false,
737+
} ||
738+
match name.strip_prefix("cfg-") {
739+
Some(rustc_cfg_name) => util::cfg_has(&self.target_cfg, rustc_cfg_name, maybe_value),
740+
None => false
707741
};
708742

709743
if is_match { ParsedNameDirective::Match } else { ParsedNameDirective::NoMatch }

src/tools/compiletest/src/main.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::common::{
1111
use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, TestPaths};
1212
use crate::util::logv;
1313
use getopts::Options;
14+
use std::collections::HashMap;
1415
use std::env;
1516
use std::ffi::OsString;
1617
use std::fs;
@@ -201,6 +202,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
201202
let llvm_version =
202203
matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version);
203204

205+
let rustc_path = opt_path(matches, "rustc-path");
204206
let src_base = opt_path(matches, "src-base");
205207
let run_ignored = matches.opt_present("ignored");
206208
let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
@@ -214,11 +216,24 @@ pub fn parse_config(args: Vec<String>) -> Config {
214216
// Avoid spawning an external command when we know tidy won't be used.
215217
false
216218
};
219+
220+
let target_cfg = if cfg!(test) {
221+
HashMap::new()
222+
} else {
223+
let rustc_cfg_data = Command::new(&rustc_path)
224+
.args(&["--target", &target])
225+
.args(&["--print", "cfg"])
226+
.output()
227+
.unwrap()
228+
.stdout;
229+
util::parse_rustc_cfg(String::from_utf8(rustc_cfg_data).unwrap())
230+
};
231+
217232
Config {
218233
bless: matches.opt_present("bless"),
219234
compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
220235
run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
221-
rustc_path: opt_path(matches, "rustc-path"),
236+
rustc_path: rustc_path,
222237
rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
223238
rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
224239
lldb_python: matches.opt_str("lldb-python").unwrap(),
@@ -257,6 +272,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
257272
Some("abort") => PanicStrategy::Abort,
258273
_ => panic!("unknown `--target-panic` option `{}` given", mode),
259274
},
275+
target_cfg,
260276
target,
261277
host: opt_str2(matches.opt_str("host")),
262278
cdb,

src/tools/compiletest/src/util.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use crate::common::Config;
2+
3+
use std::collections::HashMap;
24
use std::env;
35
use std::ffi::OsStr;
46
use std::path::PathBuf;
@@ -215,6 +217,55 @@ pub fn logv(config: &Config, s: String) {
215217
}
216218
}
217219

220+
/// Parses the output of `rustc --print cfg` into a [`HashMap`] containing
221+
/// the cfg values for each name. If the name is present with no value,
222+
/// it is treated as having a value of "".
223+
pub fn parse_rustc_cfg(rustc_output: String) -> HashMap<String, Vec<String>> {
224+
let mut map = HashMap::new();
225+
226+
for line in rustc_output.lines() {
227+
if let Some((name, value)) = line.split_once('=') {
228+
let normalized_value = value.trim_matches('"');
229+
cfg_add(&mut map, name, normalized_value);
230+
} else {
231+
cfg_add(&mut map, line, "");
232+
}
233+
}
234+
235+
return map;
236+
}
237+
238+
/// Adds the given name and value to the provided cfg [`HashMap`]. If the `name` already
239+
/// points to a vector, this adds `value` to the vector. If `name` does not point
240+
/// to a vector, this adds a new vector containing only `value` to the [`HashMap`].
241+
fn cfg_add(map: &mut HashMap<String, Vec<String>>, name: &str, value: &str) {
242+
let name = name.to_string();
243+
let value = value.to_string();
244+
245+
if let Some(values) = map.get_mut(&name) {
246+
values.push(value.to_string());
247+
} else {
248+
map.insert(name, vec![value.to_string()]);
249+
}
250+
}
251+
252+
/// Checks if the cfg HashMap has the given `name`. If the `required_value` is
253+
/// `Some(value)`, this will only return `true` if `name` is associated with `value`.
254+
pub fn cfg_has(
255+
map: &HashMap<String, Vec<String>>,
256+
name: &str,
257+
required_value: Option<&str>,
258+
) -> bool {
259+
let name = name.replace("-", "_");
260+
let required_value = required_value.map(str::trim).map(str::to_string);
261+
262+
match (map.get(&name), required_value) {
263+
(None, _) => false,
264+
(Some(_), None) => true,
265+
(Some(values), Some(required_value)) => values.contains(&required_value),
266+
}
267+
}
268+
218269
pub trait PathBufExt {
219270
/// Append an extension to the path, even if it already has one.
220271
fn with_extra_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf;

0 commit comments

Comments
 (0)