]> git.proxmox.com Git - rustc.git/blame - src/tools/compiletest/src/main.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / src / tools / compiletest / src / main.rs
CommitLineData
a7813a04 1#![crate_name = "compiletest"]
dfeec247
XL
2// The `test` crate is the only unstable feature
3// allowed here, just to share similar code.
4#![feature(test)]
1a4d82fc 5
ff7c6d11 6extern crate test;
a7813a04 7
17df50a5
XL
8use crate::common::{
9 expected_output_path, output_base_dir, output_relative_path, PanicStrategy, UI_EXTENSIONS,
10};
136023e0 11use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, TestPaths};
dfeec247 12use crate::util::logv;
94b46f34 13use getopts::Options;
85aaf69f 14use std::env;
a7813a04 15use std::ffi::OsString;
c34b1796 16use std::fs;
0731742a 17use std::io::{self, ErrorKind};
c34b1796 18use std::path::{Path, PathBuf};
fc512014 19use std::process::{Command, Stdio};
48663c56 20use std::time::SystemTime;
2c00a5a8 21use test::ColorConfig;
3dfed10e 22use tracing::*;
0731742a 23use walkdir::WalkDir;
970d7e83 24
136023e0 25use self::header::{make_test_description, EarlyProps};
a7813a04 26
416331ca
XL
27#[cfg(test)]
28mod tests;
29
970d7e83 30pub mod common;
c295e0f8 31pub mod compute_diff;
970d7e83 32pub mod errors;
94b46f34
XL
33pub mod header;
34mod json;
9346a6ac 35mod raise_fd_limit;
abe05a73 36mod read2;
94b46f34
XL
37pub mod runtest;
38pub mod util;
a7813a04
XL
39
40fn main() {
29967ef6 41 tracing_subscriber::fmt::init();
970d7e83 42
85aaf69f 43 let config = parse_config(env::args().collect());
1a4d82fc
JJ
44
45 if config.valgrind_path.is_none() && config.force_valgrind {
46 panic!("Can't find Valgrind to run Valgrind tests");
47 }
48
fc512014 49 if !config.has_tidy && config.mode == Mode::Rustdoc {
6a06907d 50 eprintln!("warning: `tidy` is not installed; diffs will not be generated");
fc512014
XL
51 }
52
970d7e83 53 log_config(&config);
dfeec247 54 run_tests(config);
970d7e83
LB
55}
56
ff7c6d11 57pub fn parse_config(args: Vec<String>) -> Config {
041b39d2 58 let mut opts = Options::new();
dfeec247
XL
59 opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
60 .reqopt("", "run-lib-path", "path to target shared libraries", "PATH")
61 .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH")
62 .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH")
3dfed10e 63 .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH")
04454e1e 64 .reqopt("", "python", "path to python to use for doc tests", "PATH")
5869c6ff 65 .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
dfeec247
XL
66 .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
67 .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
68 .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH")
69 .optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
041b39d2 70 .reqopt("", "src-base", "directory to scan for test files", "PATH")
dfeec247
XL
71 .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
72 .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
ff7c6d11
XL
73 .reqopt(
74 "",
75 "mode",
76 "which sort of compile tests to run",
5869c6ff 77 "run-pass-valgrind | pretty | debug-info | codegen | rustdoc \
fc512014 78 | rustdoc-json | codegen-units | incremental | run-make | ui | js-doc-test | mir-opt | assembly",
ff7c6d11 79 )
29967ef6
XL
80 .reqopt(
81 "",
82 "suite",
83 "which suite of compile tests to run. used for nicer error reporting.",
84 "SUITE",
85 )
dc9dc135
XL
86 .optopt(
87 "",
88 "pass",
89 "force {check,build,run}-pass tests to this mode.",
dfeec247 90 "check | build | run",
dc9dc135 91 )
17df50a5 92 .optopt("", "run", "whether to execute run-* tests", "auto | always | never")
041b39d2 93 .optflag("", "ignored", "run tests marked as ignored")
04454e1e 94 .optmulti("", "skip", "skip tests matching SUBSTRING. Can be passed multiple times", "SUBSTRING")
041b39d2 95 .optflag("", "exact", "filters match exactly")
ff7c6d11
XL
96 .optopt(
97 "",
98 "runtool",
99 "supervisor program to run tests under \
100 (eg. emulator, valgrind)",
101 "PROGRAM",
102 )
17df50a5
XL
103 .optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
104 .optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
064997fb 105 .optflag("", "optimize-tests", "run tests with optimizations enabled")
17df50a5 106 .optopt("", "target-panic", "what panic strategy the target supports", "unwind | abort")
041b39d2 107 .optflag("", "verbose", "run tests verbosely, showing all output")
94b46f34
XL
108 .optflag(
109 "",
110 "bless",
111 "overwrite stderr/stdout files instead of complaining about a mismatch",
112 )
dfeec247 113 .optflag("", "quiet", "print one character per test instead of one line")
041b39d2
XL
114 .optopt("", "color", "coloring: auto, always, never", "WHEN")
115 .optopt("", "logfile", "file to log test execution to", "FILE")
116 .optopt("", "target", "the target to build for", "TARGET")
117 .optopt("", "host", "the host to build for", "HOST")
dfeec247
XL
118 .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
119 .optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH")
120 .optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING")
121 .optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING")
041b39d2 122 .optflag("", "system-llvm", "is LLVM the system LLVM")
dfeec247 123 .optopt("", "android-cross-path", "Android NDK standalone path", "PATH")
041b39d2 124 .optopt("", "adb-path", "path to the android debugger", "PATH")
dfeec247
XL
125 .optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH")
126 .optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH")
041b39d2
XL
127 .reqopt("", "cc", "path to a C compiler", "PATH")
128 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
129 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
5099ac24 130 .reqopt("", "cxxflags", "flags for the CXX compiler", "FLAGS")
abe05a73
XL
131 .optopt("", "ar", "path to an archiver", "PATH")
132 .optopt("", "linker", "path to a linker", "PATH")
dfeec247 133 .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
48663c56 134 .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
041b39d2 135 .optopt("", "nodejs", "the name of nodejs", "PATH")
6a06907d 136 .optopt("", "npm", "the name of npm", "PATH")
dfeec247 137 .optopt("", "remote-test-client", "path to the remote test client", "PATH")
83c7162d
XL
138 .optopt(
139 "",
140 "compare-mode",
141 "mode describing what file the actual ui output will be compared to",
94b46f34 142 "COMPARE MODE",
83c7162d 143 )
532ac7d7
XL
144 .optflag(
145 "",
146 "rustfix-coverage",
147 "enable this to generate a Rustfix coverage file, which is saved in \
148 `./<build_base>/rustfix_missing_coverage.txt`",
149 )
94222f64 150 .optflag("", "force-rerun", "rerun tests even if the inputs are unchanged")
17df50a5 151 .optflag("h", "help", "show this message")
3c0e092e
XL
152 .reqopt("", "channel", "current Rust channel", "CHANNEL")
153 .optopt("", "edition", "default Rust edition", "EDITION");
970d7e83 154
c1a9b12d 155 let (argv0, args_) = args.split_first().unwrap();
b039eaaf 156 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
1a4d82fc 157 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
041b39d2 158 println!("{}", opts.usage(&message));
e1599b0c 159 println!();
1a4d82fc
JJ
160 panic!()
161 }
162
ff7c6d11
XL
163 let matches = &match opts.parse(args_) {
164 Ok(m) => m,
165 Err(f) => panic!("{:?}", f),
166 };
970d7e83 167
1a4d82fc
JJ
168 if matches.opt_present("h") || matches.opt_present("help") {
169 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
041b39d2 170 println!("{}", opts.usage(&message));
e1599b0c 171 println!();
1a4d82fc
JJ
172 panic!()
173 }
174
c34b1796 175 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
85aaf69f 176 match m.opt_str(nm) {
c34b1796 177 Some(s) => PathBuf::from(&s),
85aaf69f
SL
178 None => panic!("no option (=path) found for {}", nm),
179 }
970d7e83
LB
180 }
181
54a0048b 182 fn make_absolute(path: PathBuf) -> PathBuf {
dfeec247 183 if path.is_relative() { env::current_dir().unwrap().join(path) } else { path }
54a0048b 184 }
1a4d82fc 185
0bf4aa26
XL
186 let target = opt_str2(matches.opt_str("target"));
187 let android_cross_path = opt_path(matches, "android-cross-path");
1b1a35ee 188 let (cdb, cdb_version) = analyze_cdb(matches.opt_str("cdb"), &target);
dfeec247
XL
189 let (gdb, gdb_version, gdb_native_rust) =
190 analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
3dfed10e
XL
191 let (lldb_version, lldb_native_rust) = matches
192 .opt_str("lldb-version")
193 .as_deref()
194 .and_then(extract_lldb_version)
195 .map(|(v, b)| (Some(v), b))
196 .unwrap_or((None, false));
197 let color = match matches.opt_str("color").as_deref() {
7cac9316
XL
198 Some("auto") | None => ColorConfig::AutoColor,
199 Some("always") => ColorConfig::AlwaysColor,
200 Some("never") => ColorConfig::NeverColor,
dfeec247 201 Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x),
7cac9316 202 };
3dfed10e
XL
203 let llvm_version =
204 matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version);
7cac9316 205
83c7162d
XL
206 let src_base = opt_path(matches, "src-base");
207 let run_ignored = matches.opt_present("ignored");
6a06907d
XL
208 let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
209 let has_tidy = if mode == Mode::Rustdoc {
210 Command::new("tidy")
211 .arg("--version")
212 .stdout(Stdio::null())
213 .status()
214 .map_or(false, |status| status.success())
215 } else {
216 // Avoid spawning an external command when we know tidy won't be used.
217 false
218 };
1a4d82fc 219 Config {
94b46f34 220 bless: matches.opt_present("bless"),
54a0048b
SL
221 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
222 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
970d7e83 223 rustc_path: opt_path(matches, "rustc-path"),
3b2f2976 224 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
3dfed10e 225 rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
04454e1e 226 python: matches.opt_str("python").unwrap(),
5869c6ff 227 jsondocck_path: matches.opt_str("jsondocck-path"),
1a4d82fc
JJ
228 valgrind_path: matches.opt_str("valgrind-path"),
229 force_valgrind: matches.opt_present("force-valgrind"),
9fa01778 230 run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
48663c56
XL
231 llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
232 llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
83c7162d 233 src_base,
970d7e83 234 build_base: opt_path(matches, "build-base"),
1a4d82fc 235 stage_id: matches.opt_str("stage-id").unwrap(),
6a06907d 236 mode,
29967ef6 237 suite: matches.opt_str("suite").unwrap(),
dfeec247 238 debugger: None,
83c7162d 239 run_ignored,
6a06907d 240 filters: matches.free.clone(),
04454e1e 241 skip: matches.opt_strs("skip"),
476ff2be 242 filter_exact: matches.opt_present("exact"),
dfeec247 243 force_pass_mode: matches.opt_str("pass").map(|mode| {
dc9dc135
XL
244 mode.parse::<PassMode>()
245 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
dfeec247 246 }),
17df50a5
XL
247 run: matches.opt_str("run").and_then(|mode| match mode.as_str() {
248 "auto" => None,
249 "always" => Some(true),
250 "never" => Some(false),
251 _ => panic!("unknown `--run` option `{}` given", mode),
252 }),
c34b1796 253 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
1a4d82fc 254 runtool: matches.opt_str("runtool"),
17df50a5
XL
255 host_rustcflags: Some(matches.opt_strs("host-rustcflags").join(" ")),
256 target_rustcflags: Some(matches.opt_strs("target-rustcflags").join(" ")),
064997fb 257 optimize_tests: matches.opt_present("optimize-tests"),
17df50a5
XL
258 target_panic: match matches.opt_str("target-panic").as_deref() {
259 Some("unwind") | None => PanicStrategy::Unwind,
260 Some("abort") => PanicStrategy::Abort,
261 _ => panic!("unknown `--target-panic` option `{}` given", mode),
262 },
e1599b0c 263 target,
1a4d82fc 264 host: opt_str2(matches.opt_str("host")),
dc9dc135 265 cdb,
1b1a35ee 266 cdb_version,
3b2f2976
XL
267 gdb,
268 gdb_version,
269 gdb_native_rust,
0bf4aa26
XL
270 lldb_version,
271 lldb_native_rust,
3dfed10e 272 llvm_version,
041b39d2 273 system_llvm: matches.opt_present("system-llvm"),
e1599b0c 274 android_cross_path,
1a4d82fc 275 adb_path: opt_str2(matches.opt_str("adb-path")),
7cac9316 276 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
ff7c6d11
XL
277 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
278 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
279 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
1a4d82fc 280 lldb_python_dir: matches.opt_str("lldb-python-dir"),
1a4d82fc 281 verbose: matches.opt_present("verbose"),
54a0048b 282 quiet: matches.opt_present("quiet"),
3b2f2976 283 color,
7cac9316 284 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
83c7162d 285 compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
532ac7d7 286 rustfix_coverage: matches.opt_present("rustfix-coverage"),
fc512014 287 has_tidy,
17df50a5 288 channel: matches.opt_str("channel").unwrap(),
3c0e092e 289 edition: matches.opt_str("edition"),
a7813a04
XL
290
291 cc: matches.opt_str("cc").unwrap(),
292 cxx: matches.opt_str("cxx").unwrap(),
293 cflags: matches.opt_str("cflags").unwrap(),
5099ac24 294 cxxflags: matches.opt_str("cxxflags").unwrap(),
3dfed10e 295 ar: matches.opt_str("ar").unwrap_or_else(|| String::from("ar")),
abe05a73 296 linker: matches.opt_str("linker"),
a7813a04 297 llvm_components: matches.opt_str("llvm-components").unwrap(),
c30ab7b3 298 nodejs: matches.opt_str("nodejs"),
6a06907d 299 npm: matches.opt_str("npm"),
94222f64
XL
300
301 force_rerun: matches.opt_present("force-rerun"),
970d7e83
LB
302 }
303}
304
1a4d82fc 305pub fn log_config(config: &Config) {
970d7e83 306 let c = config;
041b39d2 307 logv(c, "configuration:".to_string());
dfeec247 308 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
1a4d82fc
JJ
309 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
310 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
3b2f2976 311 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
3dfed10e 312 logv(c, format!("rust_demangler_path: {:?}", config.rust_demangler_path));
1a4d82fc
JJ
313 logv(c, format!("src_base: {:?}", config.src_base.display()));
314 logv(c, format!("build_base: {:?}", config.build_base.display()));
315 logv(c, format!("stage_id: {}", config.stage_id));
316 logv(c, format!("mode: {}", config.mode));
317 logv(c, format!("run_ignored: {}", config.run_ignored));
6a06907d 318 logv(c, format!("filters: {:?}", config.filters));
04454e1e 319 logv(c, format!("skip: {:?}", config.skip));
476ff2be 320 logv(c, format!("filter_exact: {}", config.filter_exact));
ff7c6d11
XL
321 logv(
322 c,
dfeec247 323 format!("force_pass_mode: {}", opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),),
ff7c6d11 324 );
dfeec247
XL
325 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
326 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
327 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
1a4d82fc
JJ
328 logv(c, format!("target: {}", config.target));
329 logv(c, format!("host: {}", config.host));
dfeec247 330 logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display()));
1a4d82fc
JJ
331 logv(c, format!("adb_path: {:?}", config.adb_path));
332 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
dfeec247 333 logv(c, format!("adb_device_status: {}", config.adb_device_status));
abe05a73
XL
334 logv(c, format!("ar: {}", config.ar));
335 logv(c, format!("linker: {:?}", config.linker));
1a4d82fc 336 logv(c, format!("verbose: {}", config.verbose));
54a0048b 337 logv(c, format!("quiet: {}", config.quiet));
041b39d2 338 logv(c, "\n".to_string());
970d7e83
LB
339}
340
041b39d2 341pub fn opt_str(maybestr: &Option<String>) -> &str {
970d7e83 342 match *maybestr {
1a4d82fc 343 None => "(none)",
85aaf69f 344 Some(ref s) => s,
970d7e83
LB
345 }
346}
347
1a4d82fc
JJ
348pub fn opt_str2(maybestr: Option<String>) -> String {
349 match maybestr {
e9174d1e 350 None => "(none)".to_owned(),
1a4d82fc
JJ
351 Some(s) => s,
352 }
970d7e83
LB
353}
354
dfeec247 355pub fn run_tests(config: Config) {
532ac7d7
XL
356 // If we want to collect rustfix coverage information,
357 // we first make sure that the coverage file does not exist.
358 // It will be created later on.
359 if config.rustfix_coverage {
360 let mut coverage_file_path = config.build_base.clone();
361 coverage_file_path.push("rustfix_missing_coverage.txt");
362 if coverage_file_path.exists() {
363 if let Err(e) = fs::remove_file(&coverage_file_path) {
364 panic!("Could not delete {} due to {}", coverage_file_path.display(), e)
365 }
366 }
367 }
368
1a4d82fc
JJ
369 // sadly osx needs some file descriptor limits raised for running tests in
370 // parallel (especially when we have lots and lots of child processes).
371 // For context, see #8904
ff7c6d11
XL
372 unsafe {
373 raise_fd_limit::raise_fd_limit();
374 }
85aaf69f
SL
375 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
376 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
377 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
32a655c1
SL
378
379 // Let tests know which target they're running as
380 env::set_var("TARGET", &config.target);
381
dfeec247
XL
382 let opts = test_opts(&config);
383
384 let mut configs = Vec::new();
385 if let Mode::DebugInfo = config.mode {
386 // Debugging emscripten code doesn't make sense today
387 if !config.target.contains("emscripten") {
388 configs.extend(configure_cdb(&config));
389 configs.extend(configure_gdb(&config));
390 configs.extend(configure_lldb(&config));
391 }
392 } else {
29967ef6 393 configs.push(config.clone());
dfeec247
XL
394 };
395
396 let mut tests = Vec::new();
397 for c in &configs {
398 make_tests(c, &mut tests);
399 }
400
0731742a 401 let res = test::run_tests_console(&opts, tests);
1a4d82fc
JJ
402 match res {
403 Ok(true) => {}
29967ef6
XL
404 Ok(false) => {
405 // We want to report that the tests failed, but we also want to give
406 // some indication of just what tests we were running. Especially on
407 // CI, where there can be cross-compiled tests for a lot of
408 // architectures, without this critical information it can be quite
409 // easy to miss which tests failed, and as such fail to reproduce
410 // the failure locally.
411
412 eprintln!(
413 "Some tests failed in compiletest suite={}{} mode={} host={} target={}",
414 config.suite,
415 config.compare_mode.map(|c| format!(" compare_mode={:?}", c)).unwrap_or_default(),
416 config.mode,
417 config.host,
418 config.target
419 );
420
421 std::process::exit(1);
422 }
1a4d82fc 423 Err(e) => {
ba9703b0 424 // We don't know if tests passed or not, but if there was an error
fc512014 425 // during testing we don't want to just succeed (we may not have
ba9703b0 426 // tested something), so fail.
29967ef6
XL
427 //
428 // This should realistically "never" happen, so don't try to make
429 // this a pretty error message.
ba9703b0 430 panic!("I/O failure during tests: {:?}", e);
1a4d82fc
JJ
431 }
432 }
970d7e83
LB
433}
434
dfeec247 435fn configure_cdb(config: &Config) -> Option<Config> {
3dfed10e 436 config.cdb.as_ref()?;
dfeec247
XL
437
438 Some(Config { debugger: Some(Debugger::Cdb), ..config.clone() })
439}
440
441fn configure_gdb(config: &Config) -> Option<Config> {
3dfed10e 442 config.gdb_version?;
dfeec247
XL
443
444 if util::matches_env(&config.target, "msvc") {
445 return None;
446 }
447
448 if config.remote_test_client.is_some() && !config.target.contains("android") {
449 println!(
450 "WARNING: debuginfo tests are not available when \
451 testing with remote"
452 );
453 return None;
454 }
455
456 if config.target.contains("android") {
457 println!(
458 "{} debug-info test uses tcp 5039 port.\
459 please reserve it",
460 config.target
461 );
462
463 // android debug-info test uses remote debugger so, we test 1 thread
464 // at once as they're all sharing the same TCP port to communicate
465 // over.
466 //
467 // we should figure out how to lift this restriction! (run them all
468 // on different ports allocated dynamically).
469 env::set_var("RUST_TEST_THREADS", "1");
470 }
471
472 Some(Config { debugger: Some(Debugger::Gdb), ..config.clone() })
473}
474
475fn configure_lldb(config: &Config) -> Option<Config> {
3dfed10e 476 config.lldb_python_dir.as_ref()?;
dfeec247 477
3dfed10e
XL
478 if let Some(350) = config.lldb_version {
479 println!(
480 "WARNING: The used version of LLDB (350) has a \
481 known issue that breaks debuginfo tests. See \
482 issue #32520 for more information. Skipping all \
483 LLDB-based tests!",
484 );
485 return None;
dfeec247
XL
486 }
487
dfeec247
XL
488 Some(Config { debugger: Some(Debugger::Lldb), ..config.clone() })
489}
490
1a4d82fc 491pub fn test_opts(config: &Config) -> test::TestOpts {
970d7e83 492 test::TestOpts {
48663c56 493 exclude_should_panic: false,
6a06907d 494 filters: config.filters.clone(),
476ff2be 495 filter_exact: config.filter_exact,
dfeec247
XL
496 run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
497 format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
1a4d82fc 498 logfile: config.logfile.clone(),
970d7e83 499 run_tests: true,
d9579d0f 500 bench_benchmarks: true,
54a0048b
SL
501 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
502 Ok(val) => &val != "0",
ff7c6d11 503 Err(_) => false,
54a0048b 504 },
7cac9316 505 color: config.color,
c295e0f8
XL
506 shuffle: false,
507 shuffle_seed: None,
5bcae85e 508 test_threads: None,
04454e1e 509 skip: config.skip.clone(),
476ff2be 510 list: false,
7cac9316 511 options: test::Options::new(),
60c5eb7d 512 time_options: None,
dfeec247 513 force_run_in_process: false,
970d7e83
LB
514 }
515}
516
dfeec247 517pub fn make_tests(config: &Config, tests: &mut Vec<test::TestDescAndFn>) {
ff7c6d11 518 debug!("making tests from {:?}", config.src_base.display());
60c5eb7d 519 let inputs = common_inputs_stamp(config);
ba9703b0 520 collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests)
3dfed10e 521 .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display()));
7453a54e
SL
522}
523
60c5eb7d
XL
524/// Returns a stamp constructed from input files common to all test cases.
525fn common_inputs_stamp(config: &Config) -> Stamp {
dfeec247 526 let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
60c5eb7d
XL
527
528 let mut stamp = Stamp::from_path(&config.rustc_path);
529
530 // Relevant pretty printer files
531 let pretty_printer_files = [
f035d41b 532 "src/etc/rust_types.py",
60c5eb7d 533 "src/etc/gdb_load_rust_pretty_printers.py",
f035d41b
XL
534 "src/etc/gdb_lookup.py",
535 "src/etc/gdb_providers.py",
60c5eb7d 536 "src/etc/lldb_batchmode.py",
f035d41b
XL
537 "src/etc/lldb_lookup.py",
538 "src/etc/lldb_providers.py",
60c5eb7d
XL
539 ];
540 for file in &pretty_printer_files {
541 let path = rust_src_dir.join(file);
542 stamp.add_path(&path);
543 }
544
545 stamp.add_dir(&config.run_lib_path);
546
547 if let Some(ref rustdoc_path) = config.rustdoc_path {
548 stamp.add_path(&rustdoc_path);
549 stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
550 }
551
552 // Compiletest itself.
553 stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
554
555 stamp
556}
557
ff7c6d11
XL
558fn collect_tests_from_dir(
559 config: &Config,
ff7c6d11
XL
560 dir: &Path,
561 relative_dir_path: &Path,
60c5eb7d 562 inputs: &Stamp,
ff7c6d11
XL
563 tests: &mut Vec<test::TestDescAndFn>,
564) -> io::Result<()> {
0731742a
XL
565 // Ignore directories that contain a file named `compiletest-ignore-dir`.
566 if dir.join("compiletest-ignore-dir").exists() {
567 return Ok(());
568 }
569
570 if config.mode == Mode::RunMake && dir.join("Makefile").exists() {
571 let paths = TestPaths {
572 file: dir.to_path_buf(),
573 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
574 };
60c5eb7d 575 tests.extend(make_test(config, &paths, inputs));
0731742a 576 return Ok(());
7453a54e
SL
577 }
578
a7813a04
XL
579 // If we find a test foo/bar.rs, we have to build the
580 // output directory `$build/foo` so we can write
581 // `$build/foo/bar` into it. We do this *now* in this
582 // sequential loop because otherwise, if we do it in the
583 // tests themselves, they race for the privilege of
584 // creating the directories and sometimes fail randomly.
94b46f34 585 let build_dir = output_relative_path(config, relative_dir_path);
a7813a04
XL
586 fs::create_dir_all(&build_dir).unwrap();
587
588 // Add each `.rs` file as a test, and recurse further on any
589 // subdirectories we find, except for `aux` directories.
0731742a 590 for file in fs::read_dir(dir)? {
54a0048b 591 let file = file?;
7453a54e 592 let file_path = file.path();
a7813a04
XL
593 let file_name = file.file_name();
594 if is_test(&file_name) {
595 debug!("found test file: {:?}", file_path.display());
dfeec247
XL
596 let paths =
597 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
04454e1e 598
60c5eb7d 599 tests.extend(make_test(config, &paths, inputs))
7453a54e
SL
600 } else if file_path.is_dir() {
601 let relative_file_path = relative_dir_path.join(file.file_name());
94b46f34 602 if &file_name != "auxiliary" {
a7813a04 603 debug!("found directory: {:?}", file_path.display());
ba9703b0 604 collect_tests_from_dir(config, &file_path, &relative_file_path, inputs, tests)?;
a7813a04
XL
605 }
606 } else {
607 debug!("found other file/directory: {:?}", file_path.display());
970d7e83
LB
608 }
609 }
7453a54e 610 Ok(())
970d7e83
LB
611}
612
532ac7d7 613/// Returns true if `file_name` looks like a proper test file name.
a7813a04
XL
614pub fn is_test(file_name: &OsString) -> bool {
615 let file_name = file_name.to_str().unwrap();
970d7e83 616
a7813a04
XL
617 if !file_name.ends_with(".rs") {
618 return false;
970d7e83
LB
619 }
620
a7813a04
XL
621 // `.`, `#`, and `~` are common temp-file prefixes.
622 let invalid_prefixes = &[".", "#", "~"];
623 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
970d7e83
LB
624}
625
60c5eb7d 626fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec<test::TestDescAndFn> {
136023e0
XL
627 let test_path = if config.mode == Mode::RunMake {
628 // Parse directives in the Makefile
629 testpaths.file.join("Makefile")
83c7162d 630 } else {
136023e0 631 PathBuf::from(&testpaths.file)
54a0048b 632 };
136023e0 633 let early_props = EarlyProps::from_file(config, &test_path);
54a0048b 634
94b46f34
XL
635 // Incremental tests are special, they inherently cannot be run in parallel.
636 // `runtest::run` will be responsible for iterating over revisions.
637 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
638 vec![None]
639 } else {
3dfed10e 640 early_props.revisions.iter().map(Some).collect()
94b46f34
XL
641 };
642 revisions
643 .into_iter()
644 .map(|revision| {
136023e0
XL
645 let src_file =
646 std::fs::File::open(&test_path).expect("open test file to parse ignores");
647 let cfg = revision.map(|v| &**v);
648 let test_name = crate::make_test_name(config, testpaths, revision);
649 let mut desc = make_test_description(config, test_name, &test_path, src_file, cfg);
650 // Ignore tests that already run and are up to date with respect to inputs.
94222f64
XL
651 if !config.force_rerun {
652 desc.ignore |= is_up_to_date(
653 config,
654 testpaths,
655 &early_props,
656 revision.map(|s| s.as_str()),
657 inputs,
658 );
659 }
136023e0 660 test::TestDescAndFn { desc, testfn: make_test_closure(config, testpaths, revision) }
94b46f34
XL
661 })
662 .collect()
970d7e83
LB
663}
664
94b46f34
XL
665fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
666 output_base_dir(config, testpaths, revision).join("stamp")
8bb4bdeb
XL
667}
668
04454e1e
FG
669fn files_related_to_test(
670 config: &Config,
671 testpaths: &TestPaths,
672 props: &EarlyProps,
673 revision: Option<&str>,
674) -> Vec<PathBuf> {
675 let mut related = vec![];
676
677 if testpaths.file.is_dir() {
678 // run-make tests use their individual directory
679 for entry in WalkDir::new(&testpaths.file) {
680 let path = entry.unwrap().into_path();
681 if path.is_file() {
682 related.push(path);
683 }
684 }
685 } else {
686 related.push(testpaths.file.clone());
687 }
688
689 for aux in &props.aux {
690 let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
691 related.push(path);
692 }
693
694 // UI test files.
695 for extension in UI_EXTENSIONS {
696 let path = expected_output_path(testpaths, revision, &config.compare_mode, extension);
697 related.push(path);
698 }
699
700 related
701}
702
60c5eb7d 703fn is_up_to_date(
94b46f34
XL
704 config: &Config,
705 testpaths: &TestPaths,
706 props: &EarlyProps,
707 revision: Option<&str>,
60c5eb7d 708 inputs: &Stamp,
94b46f34
XL
709) -> bool {
710 let stamp_name = stamp(config, testpaths, revision);
711 // Check hash.
0731742a 712 let contents = match fs::read_to_string(&stamp_name) {
94b46f34 713 Ok(f) => f,
0731742a 714 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
60c5eb7d 715 Err(_) => return false,
94b46f34 716 };
94b46f34
XL
717 let expected_hash = runtest::compute_stamp_hash(config);
718 if contents != expected_hash {
60c5eb7d 719 return false;
94b46f34
XL
720 }
721
722 // Check timestamps.
60c5eb7d 723 let mut inputs = inputs.clone();
04454e1e 724 for path in files_related_to_test(config, testpaths, props, revision) {
60c5eb7d 725 inputs.add_path(&path);
abe05a73 726 }
ff7c6d11 727
60c5eb7d 728 inputs < Stamp::from_path(&stamp_name)
9fa01778
XL
729}
730
60c5eb7d 731#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
9fa01778 732struct Stamp {
48663c56 733 time: SystemTime,
9fa01778
XL
734}
735
736impl Stamp {
60c5eb7d
XL
737 fn from_path(path: &Path) -> Self {
738 let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
739 stamp.add_path(path);
740 stamp
741 }
742
743 fn add_path(&mut self, path: &Path) {
744 let modified = fs::metadata(path)
48663c56
XL
745 .and_then(|metadata| metadata.modified())
746 .unwrap_or(SystemTime::UNIX_EPOCH);
60c5eb7d 747 self.time = self.time.max(modified);
9fa01778
XL
748 }
749
60c5eb7d
XL
750 fn add_dir(&mut self, path: &Path) {
751 for entry in WalkDir::new(path) {
752 let entry = entry.unwrap();
753 if entry.file_type().is_file() {
dfeec247
XL
754 let modified = entry
755 .metadata()
756 .ok()
60c5eb7d
XL
757 .and_then(|metadata| metadata.modified().ok())
758 .unwrap_or(SystemTime::UNIX_EPOCH);
759 self.time = self.time.max(modified);
760 }
761 }
48663c56 762 }
8bb4bdeb
XL
763}
764
94b46f34
XL
765fn make_test_name(
766 config: &Config,
767 testpaths: &TestPaths,
768 revision: Option<&String>,
769) -> test::TestName {
04454e1e
FG
770 // Print the name of the file, relative to the repository root.
771 // `src_base` looks like `/path/to/rust/src/test/ui`
772 let root_directory = config.src_base.parent().unwrap().parent().unwrap().parent().unwrap();
773 let path = testpaths.file.strip_prefix(root_directory).unwrap();
dfeec247
XL
774 let debugger = match config.debugger {
775 Some(d) => format!("-{}", d),
776 None => String::new(),
777 };
83c7162d
XL
778 let mode_suffix = match config.compare_mode {
779 Some(ref mode) => format!(" ({})", mode.to_str()),
a1dfa0c6 780 None => String::new(),
83c7162d 781 };
dfeec247 782
94b46f34 783 test::DynTestName(format!(
dfeec247 784 "[{}{}{}] {}{}",
94b46f34 785 config.mode,
dfeec247 786 debugger,
94b46f34
XL
787 mode_suffix,
788 path.display(),
789 revision.map_or("".to_string(), |rev| format!("#{}", rev))
790 ))
970d7e83
LB
791}
792
94b46f34
XL
793fn make_test_closure(
794 config: &Config,
795 testpaths: &TestPaths,
796 revision: Option<&String>,
797) -> test::TestFn {
dfeec247 798 let config = config.clone();
7453a54e 799 let testpaths = testpaths.clone();
94b46f34 800 let revision = revision.cloned();
3dfed10e 801 test::DynTestFn(Box::new(move || runtest::run(config, &testpaths, revision.as_deref())))
1a4d82fc
JJ
802}
803
9fa01778 804/// Returns `true` if the given target is an Android target for the
0bf4aa26 805/// purposes of GDB testing.
3dfed10e
XL
806fn is_android_gdb_target(target: &str) -> bool {
807 matches!(
808 &target[..],
809 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
810 )
0bf4aa26
XL
811}
812
dc9dc135 813/// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
3dfed10e 814fn is_pc_windows_msvc_target(target: &str) -> bool {
dc9dc135
XL
815 target.ends_with("-pc-windows-msvc")
816}
817
3dfed10e 818fn find_cdb(target: &str) -> Option<OsString> {
dc9dc135
XL
819 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
820 return None;
821 }
822
3dfed10e 823 let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
dfeec247 824 let cdb_arch = if cfg!(target_arch = "x86") {
dc9dc135 825 "x86"
dfeec247 826 } else if cfg!(target_arch = "x86_64") {
dc9dc135 827 "x64"
dfeec247 828 } else if cfg!(target_arch = "aarch64") {
dc9dc135 829 "arm64"
dfeec247 830 } else if cfg!(target_arch = "arm") {
dc9dc135
XL
831 "arm"
832 } else {
833 return None; // No compatible CDB.exe in the Windows 10 SDK
834 };
835
836 let mut path = PathBuf::new();
837 path.push(pf86);
838 path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
839 path.push(cdb_arch);
840 path.push(r"cdb.exe");
841
842 if !path.exists() {
843 return None;
844 }
845
846 Some(path.into_os_string())
847}
848
849/// Returns Path to CDB
1b1a35ee
XL
850fn analyze_cdb(cdb: Option<String>, target: &str) -> (Option<OsString>, Option<[u16; 4]>) {
851 let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
852
853 let mut version = None;
854 if let Some(cdb) = cdb.as_ref() {
855 if let Ok(output) = Command::new(cdb).arg("/version").output() {
856 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
857 version = extract_cdb_version(&first_line);
858 }
859 }
860 }
861
862 (cdb, version)
863}
864
865fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
866 // Example full_version_line: "cdb version 10.0.18362.1"
867 let version = full_version_line.rsplit(' ').next()?;
868 let mut components = version.split('.');
869 let major: u16 = components.next().unwrap().parse().unwrap();
870 let minor: u16 = components.next().unwrap().parse().unwrap();
871 let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
872 let build: u16 = components.next().unwrap_or("0").parse().unwrap();
873 Some([major, minor, patch, build])
dc9dc135
XL
874}
875
c30ab7b3 876/// Returns (Path to GDB, GDB Version, GDB has Rust Support)
dfeec247
XL
877fn analyze_gdb(
878 gdb: Option<String>,
3dfed10e 879 target: &str,
dfeec247
XL
880 android_cross_path: &PathBuf,
881) -> (Option<String>, Option<u32>, bool) {
c30ab7b3
SL
882 #[cfg(not(windows))]
883 const GDB_FALLBACK: &str = "gdb";
884 #[cfg(windows)]
885 const GDB_FALLBACK: &str = "gdb.exe";
85aaf69f 886
c30ab7b3
SL
887 const MIN_GDB_WITH_RUST: u32 = 7011010;
888
0bf4aa26
XL
889 let fallback_gdb = || {
890 if is_android_gdb_target(target) {
891 let mut gdb_path = match android_cross_path.to_str() {
892 Some(x) => x.to_owned(),
893 None => panic!("cannot find android cross path"),
894 };
895 gdb_path.push_str("/bin/gdb");
896 gdb_path
897 } else {
898 GDB_FALLBACK.to_owned()
899 }
900 };
901
c30ab7b3 902 let gdb = match gdb {
0bf4aa26
XL
903 None => fallback_gdb(),
904 Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
905 Some(ref s) => s.to_owned(),
c30ab7b3
SL
906 };
907
0531ce1d 908 let mut version_line = None;
0bf4aa26 909 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
0531ce1d
XL
910 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
911 version_line = Some(first_line.to_string());
912 }
913 }
c30ab7b3
SL
914
915 let version = match version_line {
916 Some(line) => extract_gdb_version(&line),
917 None => return (None, None, false),
918 };
919
920 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
921
0bf4aa26 922 (Some(gdb), version, gdb_native_rust)
c30ab7b3
SL
923}
924
925fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
926 let full_version_line = full_version_line.trim();
927
928 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
929 // of the ? sections being optional
930
f9f354fc
XL
931 // We will parse up to 3 digits for each component, ignoring the date
932
933 // We skip text in parentheses. This avoids accidentally parsing
934 // the openSUSE version, which looks like:
935 // GNU gdb (GDB; openSUSE Leap 15.0) 8.1
936 // This particular form is documented in the GNU coding standards:
937 // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
c30ab7b3 938
cdc7bbd5
XL
939 let unbracketed_part = full_version_line.split('[').next().unwrap();
940 let mut splits = unbracketed_part.trim_end().rsplit(' ');
3dfed10e
XL
941 let version_string = splits.next().unwrap();
942
943 let mut splits = version_string.split('.');
944 let major = splits.next().unwrap();
945 let minor = splits.next().unwrap();
946 let patch = splits.next();
947
948 let major: u32 = major.parse().unwrap();
949 let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
950 None => {
951 let minor = minor.parse().unwrap();
952 let patch: u32 = match patch {
953 Some(patch) => match patch.find(not_a_digit) {
954 None => patch.parse().unwrap(),
955 Some(idx) if idx > 3 => 0,
956 Some(idx) => patch[..idx].parse().unwrap(),
957 },
958 None => 0,
959 };
960 (minor, patch)
c30ab7b3 961 }
3dfed10e
XL
962 // There is no patch version after minor-date (e.g. "4-2012").
963 Some(idx) => {
964 let minor = minor[..idx].parse().unwrap();
965 (minor, 0)
c30ab7b3 966 }
3dfed10e 967 };
c30ab7b3 968
3dfed10e 969 Some(((major * 1000) + minor) * 1000 + patch)
1a4d82fc
JJ
970}
971
0bf4aa26 972/// Returns (LLDB version, LLDB is rust-enabled)
3dfed10e 973fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> {
1a4d82fc
JJ
974 // Extract the major LLDB version from the given version string.
975 // LLDB version strings are different for Apple and non-Apple platforms.
0bf4aa26 976 // The Apple variant looks like this:
1a4d82fc
JJ
977 //
978 // LLDB-179.5 (older versions)
979 // lldb-300.2.51 (new versions)
980 //
981 // We are only interested in the major version number, so this function
3dfed10e 982 // will return `Some(179)` and `Some(300)` respectively.
0bf4aa26
XL
983 //
984 // Upstream versions look like:
985 // lldb version 6.0.1
986 //
987 // There doesn't seem to be a way to correlate the Apple version
988 // with the upstream version, and since the tests were originally
989 // written against Apple versions, we make a fake Apple version by
990 // multiplying the first number by 100. This is a hack, but
991 // normally fine because the only non-Apple version we test is
992 // rust-enabled.
1a4d82fc 993
3dfed10e 994 let full_version_line = full_version_line.trim();
0bf4aa26 995
3dfed10e
XL
996 if let Some(apple_ver) =
997 full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
998 {
999 if let Some(idx) = apple_ver.find(not_a_digit) {
1000 let version: u32 = apple_ver[..idx].parse().unwrap();
1001 return Some((version, full_version_line.contains("rust-enabled")));
1002 }
1003 } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
1004 if let Some(idx) = lldb_ver.find(not_a_digit) {
cdc7bbd5 1005 let version: u32 = lldb_ver[..idx].parse().ok()?;
3dfed10e 1006 return Some((version * 100, full_version_line.contains("rust-enabled")));
e9174d1e 1007 }
1a4d82fc 1008 }
3dfed10e
XL
1009 None
1010}
1011
1012fn not_a_digit(c: char) -> bool {
1013 !c.is_digit(10)
970d7e83 1014}