Skip to content

Commit 50b2510

Browse files
compiletest: Support opt-in Clang-based run-make tests.
Some cross-language run-make tests need a Clang compiler that matches the LLVM version of rustc. Since such a compiler usually isn't available these tests (marked with the "needs-matching-clang" directive) are ignored by default. For some CI jobs we do need these tests to run unconditionally though. In order to support this a --force-clang-based-tests flag is added to compiletest. If this flag is specified, compiletest will fail if it can't detect an appropriate version of Clang.
1 parent 75dcf9e commit 50b2510

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

src/tools/compiletest/src/common.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ pub struct Config {
144144
/// (or, alternatively, to silently run them like regular run-pass tests).
145145
pub force_valgrind: bool,
146146

147+
/// Whether to fail if we don't have a clang version available that matches
148+
/// rustc's LLVM version.
149+
pub force_clang_based_tests: bool,
150+
147151
/// The directory containing the tests to run
148152
pub src_base: PathBuf,
149153

@@ -205,6 +209,9 @@ pub struct Config {
205209
/// Is LLVM a system LLVM
206210
pub system_llvm: bool,
207211

212+
/// The version of Clang available to run-make tests (if any).
213+
pub clang_version: Option<String>,
214+
208215
/// Path to the android tools
209216
pub android_cross_path: PathBuf,
210217

src/tools/compiletest/src/header.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ impl EarlyProps {
111111
if ignore_llvm(config, ln) {
112112
props.ignore = Ignore::Ignore;
113113
}
114+
115+
if !config.force_clang_based_tests &&
116+
config.parse_needs_matching_clang(ln) {
117+
props.ignore = Ignore::Ignore;
118+
}
114119
}
115120

116121
if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoBoth) &&
@@ -705,6 +710,10 @@ impl Config {
705710
}
706711
}
707712

713+
fn parse_needs_matching_clang(&self, line: &str) -> bool {
714+
self.parse_name_directive(line, "needs-matching-clang")
715+
}
716+
708717
/// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
709718
/// or `normalize-stderr-32bit`.
710719
fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective {

src/tools/compiletest/src/main.rs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,32 @@ pub mod util;
5050
fn main() {
5151
env_logger::init();
5252

53-
let config = parse_config(env::args().collect());
53+
let mut config = parse_config(env::args().collect());
5454

5555
if config.valgrind_path.is_none() && config.force_valgrind {
5656
panic!("Can't find Valgrind to run Valgrind tests");
5757
}
5858

59+
// Some run-make tests need a version of Clang available that matches
60+
// rustc's LLVM version. Since this isn't always the case, these tests are
61+
// opt-in.
62+
let clang_based_tests_possible = check_clang_based_tests_possible(&config);
63+
match (clang_based_tests_possible, config.force_clang_based_tests) {
64+
(Ok(_), true) |
65+
(Err(_), false) => {
66+
// Nothing to do
67+
}
68+
(Ok(_), false) => {
69+
// If a valid clang version is available, run the tests even if
70+
// they are not forced.
71+
config.force_clang_based_tests = true;
72+
}
73+
(Err(msg), true) => {
74+
// Tests are forced but we don't have a valid version of Clang.
75+
panic!("{}", msg)
76+
}
77+
}
78+
5979
log_config(&config);
6080
run_tests(&config);
6181
}
@@ -108,6 +128,11 @@ pub fn parse_config(args: Vec<String>) -> Config {
108128
"force-valgrind",
109129
"fail if Valgrind tests cannot be run under Valgrind",
110130
)
131+
.optflag(
132+
"",
133+
"force-clang-based-tests",
134+
"fail if Clang-based run-make tests can't be run for some reason",
135+
)
111136
.optopt(
112137
"",
113138
"llvm-filecheck",
@@ -189,6 +214,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
189214
"VERSION STRING",
190215
)
191216
.optflag("", "system-llvm", "is LLVM the system LLVM")
217+
.optopt(
218+
"",
219+
"clang-version",
220+
"the version of Clang available to run-make tests",
221+
"VERSION STRING",
222+
)
192223
.optopt(
193224
"",
194225
"android-cross-path",
@@ -298,6 +329,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
298329
docck_python: matches.opt_str("docck-python").unwrap(),
299330
valgrind_path: matches.opt_str("valgrind-path"),
300331
force_valgrind: matches.opt_present("force-valgrind"),
332+
force_clang_based_tests: matches.opt_present("force-clang-based-tests"),
301333
llvm_filecheck: matches.opt_str("llvm-filecheck").map(|s| PathBuf::from(&s)),
302334
src_base,
303335
build_base: opt_path(matches, "build-base"),
@@ -323,6 +355,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
323355
lldb_native_rust,
324356
llvm_version: matches.opt_str("llvm-version"),
325357
system_llvm: matches.opt_present("system-llvm"),
358+
clang_version: matches.opt_str("clang-version"),
326359
android_cross_path: android_cross_path,
327360
adb_path: opt_str2(matches.opt_str("adb-path")),
328361
adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
@@ -1031,3 +1064,54 @@ fn test_extract_gdb_version() {
10311064
7012050: "GNU gdb (GDB) 7.12.50.20161027-git",
10321065
}
10331066
}
1067+
1068+
1069+
fn check_clang_based_tests_possible(config: &Config) -> Result<(), String> {
1070+
1071+
let llvm_version = if let Some(llvm_version) = config.llvm_version.as_ref() {
1072+
llvm_version
1073+
} else {
1074+
return Err(format!("Running `compiletest` with `--force-clang-based-tests` \
1075+
requires `--llvm-version` to be specified."));
1076+
};
1077+
1078+
let clang_major_version = if let Some(ref version_string) = config.clang_version {
1079+
major_version_from_clang_version_string(version_string)?
1080+
} else {
1081+
return Err(format!("Clang is required for running tests \
1082+
(because of --force-clang-based-tests) \
1083+
but it does not seem to be available."));
1084+
};
1085+
1086+
let rustc_llvm_major_version = major_version_from_llvm_version_string(&llvm_version)?;
1087+
1088+
return if clang_major_version != rustc_llvm_major_version {
1089+
Err(format!("`--force-clang-based-tests` needs the major version of Clang \
1090+
and rustc's LLVM to be the same. Clang version is: {}, \
1091+
Rustc LLVM is: {}",
1092+
config.clang_version.clone().unwrap(),
1093+
llvm_version))
1094+
} else {
1095+
Ok(())
1096+
};
1097+
1098+
fn major_version_from_clang_version_string(clang_version: &str) -> Result<&str, String> {
1099+
let re = regex::Regex::new(r"clang version (\d)\.\d").unwrap();
1100+
if let Some(captures) = re.captures(clang_version) {
1101+
Ok(captures.get(1).unwrap().as_str())
1102+
} else {
1103+
Err(format!("Failed to parse major version from Clang version \
1104+
string '{}'.", clang_version))
1105+
}
1106+
}
1107+
1108+
fn major_version_from_llvm_version_string(llvm_version: &str) -> Result<&str, String> {
1109+
let re = regex::Regex::new(r"(\d)\.\d").unwrap();
1110+
if let Some(captures) = re.captures(llvm_version) {
1111+
Ok(captures.get(1).unwrap().as_str())
1112+
} else {
1113+
Err(format!("Failed to parse major version from LLVM version \
1114+
string '{}'.", llvm_version))
1115+
}
1116+
}
1117+
}

0 commit comments

Comments
 (0)