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