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