1 #![crate_name = "compiletest"]
2 // The `test` crate is the only unstable feature
3 // allowed here, just to share similar code.
8 use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
9 use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, TestPaths};
10 use crate::util::logv;
12 use lazycell::LazyCell;
14 use std::ffi::OsString;
16 use std::io::{self, ErrorKind};
17 use std::path::{Path, PathBuf};
18 use std::process::{Command, Stdio};
19 use std::time::SystemTime;
20 use test::ColorConfig;
24 use self::header::{make_test_description, EarlyProps};
40 tracing_subscriber::fmt::init();
42 let config = parse_config(env::args().collect());
44 if config.valgrind_path.is_none() && config.force_valgrind {
45 panic!("Can't find Valgrind to run Valgrind tests");
48 if !config.has_tidy && config.mode == Mode::Rustdoc {
49 eprintln!("warning: `tidy` is not installed; diffs will not be generated");
56 pub fn parse_config(args: Vec<String>) -> Config {
57 let mut opts = Options::new();
58 opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
59 .reqopt("", "run-lib-path", "path to target shared libraries", "PATH")
60 .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH")
61 .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH")
62 .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH")
63 .reqopt("", "python", "path to python to use for doc tests", "PATH")
64 .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
65 .optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH")
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")
70 .reqopt("", "src-base", "directory to scan for test files", "PATH")
71 .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
72 .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
76 "which sort of compile tests to run",
77 "run-pass-valgrind | pretty | debug-info | codegen | rustdoc \
78 | rustdoc-json | codegen-units | incremental | run-make | ui | js-doc-test | mir-opt | assembly",
83 "which suite of compile tests to run. used for nicer error reporting.",
89 "force {check,build,run}-pass tests to this mode.",
90 "check | build | run",
92 .optopt("", "run", "whether to execute run-* tests", "auto | always | never")
93 .optflag("", "ignored", "run tests marked as ignored")
94 .optmulti("", "skip", "skip tests matching SUBSTRING. Can be passed multiple times", "SUBSTRING")
95 .optflag("", "exact", "filters match exactly")
99 "supervisor program to run tests under \
100 (eg. emulator, valgrind)",
103 .optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
104 .optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
105 .optflag("", "optimize-tests", "run tests with optimizations enabled")
106 .optflag("", "verbose", "run tests verbosely, showing all output")
110 "overwrite stderr/stdout files instead of complaining about a mismatch",
112 .optflag("", "quiet", "print one character per test instead of one line")
113 .optopt("", "color", "coloring: auto, always, never", "WHEN")
114 .optopt("", "logfile", "file to log test execution to", "FILE")
115 .optopt("", "target", "the target to build for", "TARGET")
116 .optopt("", "host", "the host to build for", "HOST")
117 .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
118 .optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH")
119 .optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING")
120 .optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING")
121 .optflag("", "system-llvm", "is LLVM the system LLVM")
122 .optopt("", "android-cross-path", "Android NDK standalone path", "PATH")
123 .optopt("", "adb-path", "path to the android debugger", "PATH")
124 .optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH")
125 .optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH")
126 .reqopt("", "cc", "path to a C compiler", "PATH")
127 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
128 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
129 .reqopt("", "cxxflags", "flags for the CXX compiler", "FLAGS")
130 .optopt("", "ar", "path to an archiver", "PATH")
131 .optopt("", "linker", "path to a linker", "PATH")
132 .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
133 .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
134 .optopt("", "nodejs", "the name of nodejs", "PATH")
135 .optopt("", "npm", "the name of npm", "PATH")
136 .optopt("", "remote-test-client", "path to the remote test client", "PATH")
140 "mode describing what file the actual ui output will be compared to",
146 "enable this to generate a Rustfix coverage file, which is saved in \
147 `./<build_base>/rustfix_missing_coverage.txt`",
149 .optflag("", "force-rerun", "rerun tests even if the inputs are unchanged")
150 .optflag("h", "help", "show this message")
151 .reqopt("", "channel", "current Rust channel", "CHANNEL")
152 .optopt("", "edition", "default Rust edition", "EDITION");
154 let (argv0, args_) = args.split_first().unwrap();
155 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
156 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
157 println!("{}", opts.usage(&message));
162 let matches = &match opts.parse(args_) {
164 Err(f) => panic!("{:?}", f),
167 if matches.opt_present("h") || matches.opt_present("help") {
168 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
169 println!("{}", opts.usage(&message));
174 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
175 match m.opt_str(nm) {
176 Some(s) => PathBuf::from(&s),
177 None => panic!("no option (=path) found for {}", nm),
181 fn make_absolute(path: PathBuf) -> PathBuf {
182 if path.is_relative() { env::current_dir().unwrap().join(path) } else { path }
185 let target = opt_str2(matches.opt_str("target"));
186 let android_cross_path = opt_path(matches, "android-cross-path");
187 let (cdb, cdb_version) = analyze_cdb(matches.opt_str("cdb"), &target);
188 let (gdb, gdb_version, gdb_native_rust) =
189 analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
190 let (lldb_version, lldb_native_rust) = matches
191 .opt_str("lldb-version")
193 .and_then(extract_lldb_version)
194 .map(|(v, b)| (Some(v), b))
195 .unwrap_or((None, false));
196 let color = match matches.opt_str("color").as_deref() {
197 Some("auto") | None => ColorConfig::AutoColor,
198 Some("always") => ColorConfig::AlwaysColor,
199 Some("never") => ColorConfig::NeverColor,
200 Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x),
203 matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version).or_else(
204 || header::extract_llvm_version_from_binary(&matches.opt_str("llvm-filecheck")?),
207 let src_base = opt_path(matches, "src-base");
208 let run_ignored = matches.opt_present("ignored");
209 let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
210 let has_tidy = if mode == Mode::Rustdoc {
213 .stdout(Stdio::null())
215 .map_or(false, |status| status.success())
217 // Avoid spawning an external command when we know tidy won't be used.
221 bless: matches.opt_present("bless"),
222 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
223 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
224 rustc_path: opt_path(matches, "rustc-path"),
225 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
226 rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
227 python: matches.opt_str("python").unwrap(),
228 jsondocck_path: matches.opt_str("jsondocck-path"),
229 jsondoclint_path: matches.opt_str("jsondoclint-path"),
230 valgrind_path: matches.opt_str("valgrind-path"),
231 force_valgrind: matches.opt_present("force-valgrind"),
232 run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
233 llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
234 llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
236 build_base: opt_path(matches, "build-base"),
237 stage_id: matches.opt_str("stage-id").unwrap(),
239 suite: matches.opt_str("suite").unwrap(),
242 filters: matches.free.clone(),
243 skip: matches.opt_strs("skip"),
244 filter_exact: matches.opt_present("exact"),
245 force_pass_mode: matches.opt_str("pass").map(|mode| {
246 mode.parse::<PassMode>()
247 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
249 run: matches.opt_str("run").and_then(|mode| match mode.as_str() {
251 "always" => Some(true),
252 "never" => Some(false),
253 _ => panic!("unknown `--run` option `{}` given", mode),
255 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
256 runtool: matches.opt_str("runtool"),
257 host_rustcflags: matches.opt_strs("host-rustcflags"),
258 target_rustcflags: matches.opt_strs("target-rustcflags"),
259 optimize_tests: matches.opt_present("optimize-tests"),
261 host: opt_str2(matches.opt_str("host")),
270 system_llvm: matches.opt_present("system-llvm"),
272 adb_path: opt_str2(matches.opt_str("adb-path")),
273 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
274 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
275 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
276 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
277 lldb_python_dir: matches.opt_str("lldb-python-dir"),
278 verbose: matches.opt_present("verbose"),
279 quiet: matches.opt_present("quiet"),
281 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
282 compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
283 rustfix_coverage: matches.opt_present("rustfix-coverage"),
285 channel: matches.opt_str("channel").unwrap(),
286 edition: matches.opt_str("edition"),
288 cc: matches.opt_str("cc").unwrap(),
289 cxx: matches.opt_str("cxx").unwrap(),
290 cflags: matches.opt_str("cflags").unwrap(),
291 cxxflags: matches.opt_str("cxxflags").unwrap(),
292 ar: matches.opt_str("ar").unwrap_or_else(|| String::from("ar")),
293 linker: matches.opt_str("linker"),
294 llvm_components: matches.opt_str("llvm-components").unwrap(),
295 nodejs: matches.opt_str("nodejs"),
296 npm: matches.opt_str("npm"),
298 force_rerun: matches.opt_present("force-rerun"),
300 target_cfg: LazyCell::new(),
304 pub fn log_config(config: &Config) {
306 logv(c, "configuration:".to_string());
307 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
308 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
309 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
310 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
311 logv(c, format!("rust_demangler_path: {:?}", config.rust_demangler_path));
312 logv(c, format!("src_base: {:?}", config.src_base.display()));
313 logv(c, format!("build_base: {:?}", config.build_base.display()));
314 logv(c, format!("stage_id: {}", config.stage_id));
315 logv(c, format!("mode: {}", config.mode));
316 logv(c, format!("run_ignored: {}", config.run_ignored));
317 logv(c, format!("filters: {:?}", config.filters));
318 logv(c, format!("skip: {:?}", config.skip));
319 logv(c, format!("filter_exact: {}", config.filter_exact));
322 format!("force_pass_mode: {}", opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),),
324 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
325 logv(c, format!("host-rustcflags: {:?}", config.host_rustcflags));
326 logv(c, format!("target-rustcflags: {:?}", config.target_rustcflags));
327 logv(c, format!("target: {}", config.target));
328 logv(c, format!("host: {}", config.host));
329 logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display()));
330 logv(c, format!("adb_path: {:?}", config.adb_path));
331 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
332 logv(c, format!("adb_device_status: {}", config.adb_device_status));
333 logv(c, format!("ar: {}", config.ar));
334 logv(c, format!("linker: {:?}", config.linker));
335 logv(c, format!("verbose: {}", config.verbose));
336 logv(c, format!("quiet: {}", config.quiet));
337 logv(c, "\n".to_string());
340 pub fn opt_str(maybestr: &Option<String>) -> &str {
347 pub fn opt_str2(maybestr: Option<String>) -> String {
349 None => "(none)".to_owned(),
354 pub fn run_tests(config: Config) {
355 // If we want to collect rustfix coverage information,
356 // we first make sure that the coverage file does not exist.
357 // It will be created later on.
358 if config.rustfix_coverage {
359 let mut coverage_file_path = config.build_base.clone();
360 coverage_file_path.push("rustfix_missing_coverage.txt");
361 if coverage_file_path.exists() {
362 if let Err(e) = fs::remove_file(&coverage_file_path) {
363 panic!("Could not delete {} due to {}", coverage_file_path.display(), e)
368 // sadly osx needs some file descriptor limits raised for running tests in
369 // parallel (especially when we have lots and lots of child processes).
370 // For context, see #8904
372 raise_fd_limit::raise_fd_limit();
374 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
375 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
376 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
378 // Let tests know which target they're running as
379 env::set_var("TARGET", &config.target);
381 let opts = test_opts(&config);
383 let mut configs = Vec::new();
384 if let Mode::DebugInfo = config.mode {
385 // Debugging emscripten code doesn't make sense today
386 if !config.target.contains("emscripten") {
387 configs.extend(configure_cdb(&config));
388 configs.extend(configure_gdb(&config));
389 configs.extend(configure_lldb(&config));
392 configs.push(config.clone());
395 let mut tests = Vec::new();
397 make_tests(c, &mut tests);
400 tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice()));
402 let res = test::run_tests_console(&opts, tests);
406 // We want to report that the tests failed, but we also want to give
407 // some indication of just what tests we were running. Especially on
408 // CI, where there can be cross-compiled tests for a lot of
409 // architectures, without this critical information it can be quite
410 // easy to miss which tests failed, and as such fail to reproduce
411 // the failure locally.
414 "Some tests failed in compiletest suite={}{} mode={} host={} target={}",
416 config.compare_mode.map(|c| format!(" compare_mode={:?}", c)).unwrap_or_default(),
422 std::process::exit(1);
425 // We don't know if tests passed or not, but if there was an error
426 // during testing we don't want to just succeed (we may not have
427 // tested something), so fail.
429 // This should realistically "never" happen, so don't try to make
430 // this a pretty error message.
431 panic!("I/O failure during tests: {:?}", e);
436 fn configure_cdb(config: &Config) -> Option<Config> {
437 config.cdb.as_ref()?;
439 Some(Config { debugger: Some(Debugger::Cdb), ..config.clone() })
442 fn configure_gdb(config: &Config) -> Option<Config> {
445 if config.matches_env("msvc") {
449 if config.remote_test_client.is_some() && !config.target.contains("android") {
451 "WARNING: debuginfo tests are not available when \
457 if config.target.contains("android") {
459 "{} debug-info test uses tcp 5039 port.\
464 // android debug-info test uses remote debugger so, we test 1 thread
465 // at once as they're all sharing the same TCP port to communicate
468 // we should figure out how to lift this restriction! (run them all
469 // on different ports allocated dynamically).
470 env::set_var("RUST_TEST_THREADS", "1");
473 Some(Config { debugger: Some(Debugger::Gdb), ..config.clone() })
476 fn configure_lldb(config: &Config) -> Option<Config> {
477 config.lldb_python_dir.as_ref()?;
479 if let Some(350) = config.lldb_version {
481 "WARNING: The used version of LLDB (350) has a \
482 known issue that breaks debuginfo tests. See \
483 issue #32520 for more information. Skipping all \
489 Some(Config { debugger: Some(Debugger::Lldb), ..config.clone() })
492 pub fn test_opts(config: &Config) -> test::TestOpts {
494 exclude_should_panic: false,
495 filters: config.filters.clone(),
496 filter_exact: config.filter_exact,
497 run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
498 format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
499 logfile: config.logfile.clone(),
501 bench_benchmarks: true,
502 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
503 Ok(val) => &val != "0",
510 skip: config.skip.clone(),
512 options: test::Options::new(),
514 force_run_in_process: false,
518 pub fn make_tests(config: &Config, tests: &mut Vec<test::TestDescAndFn>) {
519 debug!("making tests from {:?}", config.src_base.display());
520 let inputs = common_inputs_stamp(config);
521 collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests)
522 .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display()));
525 /// Returns a stamp constructed from input files common to all test cases.
526 fn common_inputs_stamp(config: &Config) -> Stamp {
527 let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
529 let mut stamp = Stamp::from_path(&config.rustc_path);
531 // Relevant pretty printer files
532 let pretty_printer_files = [
533 "src/etc/rust_types.py",
534 "src/etc/gdb_load_rust_pretty_printers.py",
535 "src/etc/gdb_lookup.py",
536 "src/etc/gdb_providers.py",
537 "src/etc/lldb_batchmode.py",
538 "src/etc/lldb_lookup.py",
539 "src/etc/lldb_providers.py",
541 for file in &pretty_printer_files {
542 let path = rust_src_dir.join(file);
543 stamp.add_path(&path);
546 stamp.add_dir(&rust_src_dir.join("src/etc/natvis"));
548 stamp.add_dir(&config.run_lib_path);
550 if let Some(ref rustdoc_path) = config.rustdoc_path {
551 stamp.add_path(&rustdoc_path);
552 stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
555 // Compiletest itself.
556 stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
561 fn collect_tests_from_dir(
564 relative_dir_path: &Path,
566 tests: &mut Vec<test::TestDescAndFn>,
567 ) -> io::Result<()> {
568 // Ignore directories that contain a file named `compiletest-ignore-dir`.
569 if dir.join("compiletest-ignore-dir").exists() {
573 if config.mode == Mode::RunMake && dir.join("Makefile").exists() {
574 let paths = TestPaths {
575 file: dir.to_path_buf(),
576 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
578 tests.extend(make_test(config, &paths, inputs));
582 // If we find a test foo/bar.rs, we have to build the
583 // output directory `$build/foo` so we can write
584 // `$build/foo/bar` into it. We do this *now* in this
585 // sequential loop because otherwise, if we do it in the
586 // tests themselves, they race for the privilege of
587 // creating the directories and sometimes fail randomly.
588 let build_dir = output_relative_path(config, relative_dir_path);
589 fs::create_dir_all(&build_dir).unwrap();
591 // Add each `.rs` file as a test, and recurse further on any
592 // subdirectories we find, except for `aux` directories.
593 for file in fs::read_dir(dir)? {
595 let file_path = file.path();
596 let file_name = file.file_name();
597 if is_test(&file_name) {
598 debug!("found test file: {:?}", file_path.display());
600 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
602 tests.extend(make_test(config, &paths, inputs))
603 } else if file_path.is_dir() {
604 let relative_file_path = relative_dir_path.join(file.file_name());
605 if &file_name != "auxiliary" {
606 debug!("found directory: {:?}", file_path.display());
607 collect_tests_from_dir(config, &file_path, &relative_file_path, inputs, tests)?;
610 debug!("found other file/directory: {:?}", file_path.display());
616 /// Returns true if `file_name` looks like a proper test file name.
617 pub fn is_test(file_name: &OsString) -> bool {
618 let file_name = file_name.to_str().unwrap();
620 if !file_name.ends_with(".rs") {
624 // `.`, `#`, and `~` are common temp-file prefixes.
625 let invalid_prefixes = &[".", "#", "~"];
626 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
629 fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec<test::TestDescAndFn> {
630 let test_path = if config.mode == Mode::RunMake {
631 // Parse directives in the Makefile
632 testpaths.file.join("Makefile")
634 PathBuf::from(&testpaths.file)
636 let early_props = EarlyProps::from_file(config, &test_path);
638 // Incremental tests are special, they inherently cannot be run in parallel.
639 // `runtest::run` will be responsible for iterating over revisions.
640 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
643 early_props.revisions.iter().map(Some).collect()
649 std::fs::File::open(&test_path).expect("open test file to parse ignores");
650 let cfg = revision.map(|v| &**v);
651 let test_name = crate::make_test_name(config, testpaths, revision);
652 let mut desc = make_test_description(config, test_name, &test_path, src_file, cfg);
653 // Ignore tests that already run and are up to date with respect to inputs.
654 if !config.force_rerun {
655 desc.ignore |= is_up_to_date(
659 revision.map(|s| s.as_str()),
663 test::TestDescAndFn { desc, testfn: make_test_closure(config, testpaths, revision) }
668 fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
669 output_base_dir(config, testpaths, revision).join("stamp")
672 fn files_related_to_test(
674 testpaths: &TestPaths,
676 revision: Option<&str>,
678 let mut related = vec![];
680 if testpaths.file.is_dir() {
681 // run-make tests use their individual directory
682 for entry in WalkDir::new(&testpaths.file) {
683 let path = entry.unwrap().into_path();
689 related.push(testpaths.file.clone());
692 for aux in &props.aux {
693 let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
698 for extension in UI_EXTENSIONS {
699 let path = expected_output_path(testpaths, revision, &config.compare_mode, extension);
708 testpaths: &TestPaths,
710 revision: Option<&str>,
713 let stamp_name = stamp(config, testpaths, revision);
715 let contents = match fs::read_to_string(&stamp_name) {
717 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
718 Err(_) => return false,
720 let expected_hash = runtest::compute_stamp_hash(config);
721 if contents != expected_hash {
726 let mut inputs = inputs.clone();
727 for path in files_related_to_test(config, testpaths, props, revision) {
728 inputs.add_path(&path);
731 inputs < Stamp::from_path(&stamp_name)
734 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
740 fn from_path(path: &Path) -> Self {
741 let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
742 stamp.add_path(path);
746 fn add_path(&mut self, path: &Path) {
747 let modified = fs::metadata(path)
748 .and_then(|metadata| metadata.modified())
749 .unwrap_or(SystemTime::UNIX_EPOCH);
750 self.time = self.time.max(modified);
753 fn add_dir(&mut self, path: &Path) {
754 for entry in WalkDir::new(path) {
755 let entry = entry.unwrap();
756 if entry.file_type().is_file() {
760 .and_then(|metadata| metadata.modified().ok())
761 .unwrap_or(SystemTime::UNIX_EPOCH);
762 self.time = self.time.max(modified);
770 testpaths: &TestPaths,
771 revision: Option<&String>,
772 ) -> test::TestName {
773 // Print the name of the file, relative to the repository root.
774 // `src_base` looks like `/path/to/rust/src/test/ui`
775 let root_directory = config.src_base.parent().unwrap().parent().unwrap().parent().unwrap();
776 let path = testpaths.file.strip_prefix(root_directory).unwrap();
777 let debugger = match config.debugger {
778 Some(d) => format!("-{}", d),
779 None => String::new(),
781 let mode_suffix = match config.compare_mode {
782 Some(ref mode) => format!(" ({})", mode.to_str()),
783 None => String::new(),
786 test::DynTestName(format!(
792 revision.map_or("".to_string(), |rev| format!("#{}", rev))
796 fn make_test_closure(
798 testpaths: &TestPaths,
799 revision: Option<&String>,
801 let config = config.clone();
802 let testpaths = testpaths.clone();
803 let revision = revision.cloned();
804 test::DynTestFn(Box::new(move || {
805 runtest::run(config, &testpaths, revision.as_deref());
810 /// Returns `true` if the given target is an Android target for the
811 /// purposes of GDB testing.
812 fn is_android_gdb_target(target: &str) -> bool {
815 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
819 /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
820 fn is_pc_windows_msvc_target(target: &str) -> bool {
821 target.ends_with("-pc-windows-msvc")
824 fn find_cdb(target: &str) -> Option<OsString> {
825 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
829 let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
830 let cdb_arch = if cfg!(target_arch = "x86") {
832 } else if cfg!(target_arch = "x86_64") {
834 } else if cfg!(target_arch = "aarch64") {
836 } else if cfg!(target_arch = "arm") {
839 return None; // No compatible CDB.exe in the Windows 10 SDK
842 let mut path = PathBuf::new();
844 path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
846 path.push(r"cdb.exe");
852 Some(path.into_os_string())
855 /// Returns Path to CDB
856 fn analyze_cdb(cdb: Option<String>, target: &str) -> (Option<OsString>, Option<[u16; 4]>) {
857 let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
859 let mut version = None;
860 if let Some(cdb) = cdb.as_ref() {
861 if let Ok(output) = Command::new(cdb).arg("/version").output() {
862 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
863 version = extract_cdb_version(&first_line);
871 fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
872 // Example full_version_line: "cdb version 10.0.18362.1"
873 let version = full_version_line.rsplit(' ').next()?;
874 let mut components = version.split('.');
875 let major: u16 = components.next().unwrap().parse().unwrap();
876 let minor: u16 = components.next().unwrap().parse().unwrap();
877 let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
878 let build: u16 = components.next().unwrap_or("0").parse().unwrap();
879 Some([major, minor, patch, build])
882 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
886 android_cross_path: &PathBuf,
887 ) -> (Option<String>, Option<u32>, bool) {
889 const GDB_FALLBACK: &str = "gdb";
891 const GDB_FALLBACK: &str = "gdb.exe";
893 const MIN_GDB_WITH_RUST: u32 = 7011010;
895 let fallback_gdb = || {
896 if is_android_gdb_target(target) {
897 let mut gdb_path = match android_cross_path.to_str() {
898 Some(x) => x.to_owned(),
899 None => panic!("cannot find android cross path"),
901 gdb_path.push_str("/bin/gdb");
904 GDB_FALLBACK.to_owned()
908 let gdb = match gdb {
909 None => fallback_gdb(),
910 Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
911 Some(ref s) => s.to_owned(),
914 let mut version_line = None;
915 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
916 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
917 version_line = Some(first_line.to_string());
921 let version = match version_line {
922 Some(line) => extract_gdb_version(&line),
923 None => return (None, None, false),
926 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
928 (Some(gdb), version, gdb_native_rust)
931 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
932 let full_version_line = full_version_line.trim();
934 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
935 // of the ? sections being optional
937 // We will parse up to 3 digits for each component, ignoring the date
939 // We skip text in parentheses. This avoids accidentally parsing
940 // the openSUSE version, which looks like:
941 // GNU gdb (GDB; openSUSE Leap 15.0) 8.1
942 // This particular form is documented in the GNU coding standards:
943 // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
945 let unbracketed_part = full_version_line.split('[').next().unwrap();
946 let mut splits = unbracketed_part.trim_end().rsplit(' ');
947 let version_string = splits.next().unwrap();
949 let mut splits = version_string.split('.');
950 let major = splits.next().unwrap();
951 let minor = splits.next().unwrap();
952 let patch = splits.next();
954 let major: u32 = major.parse().unwrap();
955 let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
957 let minor = minor.parse().unwrap();
958 let patch: u32 = match patch {
959 Some(patch) => match patch.find(not_a_digit) {
960 None => patch.parse().unwrap(),
961 Some(idx) if idx > 3 => 0,
962 Some(idx) => patch[..idx].parse().unwrap(),
968 // There is no patch version after minor-date (e.g. "4-2012").
970 let minor = minor[..idx].parse().unwrap();
975 Some(((major * 1000) + minor) * 1000 + patch)
978 /// Returns (LLDB version, LLDB is rust-enabled)
979 fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> {
980 // Extract the major LLDB version from the given version string.
981 // LLDB version strings are different for Apple and non-Apple platforms.
982 // The Apple variant looks like this:
984 // LLDB-179.5 (older versions)
985 // lldb-300.2.51 (new versions)
987 // We are only interested in the major version number, so this function
988 // will return `Some(179)` and `Some(300)` respectively.
990 // Upstream versions look like:
991 // lldb version 6.0.1
993 // There doesn't seem to be a way to correlate the Apple version
994 // with the upstream version, and since the tests were originally
995 // written against Apple versions, we make a fake Apple version by
996 // multiplying the first number by 100. This is a hack, but
997 // normally fine because the only non-Apple version we test is
1000 let full_version_line = full_version_line.trim();
1002 if let Some(apple_ver) =
1003 full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
1005 if let Some(idx) = apple_ver.find(not_a_digit) {
1006 let version: u32 = apple_ver[..idx].parse().unwrap();
1007 return Some((version, full_version_line.contains("rust-enabled")));
1009 } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
1010 if let Some(idx) = lldb_ver.find(not_a_digit) {
1011 let version: u32 = lldb_ver[..idx].parse().ok()?;
1012 return Some((version * 100, full_version_line.contains("rust-enabled")));
1018 fn not_a_digit(c: char) -> bool {