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, Pretty, TestPaths};
10 use crate::util::logv;
13 use std::ffi::OsString;
15 use std::io::{self, ErrorKind};
16 use std::path::{Path, PathBuf};
17 use std::process::{Command, Stdio};
18 use std::time::SystemTime;
19 use test::ColorConfig;
23 use self::header::EarlyProps;
38 tracing_subscriber::fmt::init();
40 let config = parse_config(env::args().collect());
42 if config.valgrind_path.is_none() && config.force_valgrind {
43 panic!("Can't find Valgrind to run Valgrind tests");
46 if !config.has_tidy && config.mode == Mode::Rustdoc {
47 eprintln!("warning: `tidy` is not installed; generated diffs will be harder to read");
54 pub fn parse_config(args: Vec<String>) -> Config {
55 let mut opts = Options::new();
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 .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH")
61 .reqopt("", "lldb-python", "path to python to use for doc tests", "PATH")
62 .reqopt("", "docck-python", "path to python to use for doc tests", "PATH")
63 .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
64 .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
65 .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
66 .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH")
67 .optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
68 .reqopt("", "src-base", "directory to scan for test files", "PATH")
69 .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
70 .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
74 "which sort of compile tests to run",
75 "run-pass-valgrind | pretty | debug-info | codegen | rustdoc \
76 | rustdoc-json | codegen-units | incremental | run-make | ui | js-doc-test | mir-opt | assembly",
81 "which suite of compile tests to run. used for nicer error reporting.",
87 "force {check,build,run}-pass tests to this mode.",
88 "check | build | run",
90 .optflag("", "ignored", "run tests marked as ignored")
91 .optflag("", "exact", "filters match exactly")
95 "supervisor program to run tests under \
96 (eg. emulator, valgrind)",
99 .optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
100 .optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
101 .optflag("", "verbose", "run tests verbosely, showing all output")
105 "overwrite stderr/stdout files instead of complaining about a mismatch",
107 .optflag("", "quiet", "print one character per test instead of one line")
108 .optopt("", "color", "coloring: auto, always, never", "WHEN")
109 .optopt("", "logfile", "file to log test execution to", "FILE")
110 .optopt("", "target", "the target to build for", "TARGET")
111 .optopt("", "host", "the host to build for", "HOST")
112 .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
113 .optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH")
114 .optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING")
115 .optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING")
116 .optflag("", "system-llvm", "is LLVM the system LLVM")
117 .optopt("", "android-cross-path", "Android NDK standalone path", "PATH")
118 .optopt("", "adb-path", "path to the android debugger", "PATH")
119 .optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH")
120 .optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH")
121 .reqopt("", "cc", "path to a C compiler", "PATH")
122 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
123 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
124 .optopt("", "ar", "path to an archiver", "PATH")
125 .optopt("", "linker", "path to a linker", "PATH")
126 .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
127 .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
128 .optopt("", "nodejs", "the name of nodejs", "PATH")
129 .optopt("", "npm", "the name of npm", "PATH")
130 .optopt("", "remote-test-client", "path to the remote test client", "PATH")
134 "mode describing what file the actual ui output will be compared to",
140 "enable this to generate a Rustfix coverage file, which is saved in \
141 `./<build_base>/rustfix_missing_coverage.txt`",
143 .optflag("h", "help", "show this message");
145 let (argv0, args_) = args.split_first().unwrap();
146 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
147 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
148 println!("{}", opts.usage(&message));
153 let matches = &match opts.parse(args_) {
155 Err(f) => panic!("{:?}", f),
158 if matches.opt_present("h") || matches.opt_present("help") {
159 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
160 println!("{}", opts.usage(&message));
165 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
166 match m.opt_str(nm) {
167 Some(s) => PathBuf::from(&s),
168 None => panic!("no option (=path) found for {}", nm),
172 fn make_absolute(path: PathBuf) -> PathBuf {
173 if path.is_relative() { env::current_dir().unwrap().join(path) } else { path }
176 let target = opt_str2(matches.opt_str("target"));
177 let android_cross_path = opt_path(matches, "android-cross-path");
178 let (cdb, cdb_version) = analyze_cdb(matches.opt_str("cdb"), &target);
179 let (gdb, gdb_version, gdb_native_rust) =
180 analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
181 let (lldb_version, lldb_native_rust) = matches
182 .opt_str("lldb-version")
184 .and_then(extract_lldb_version)
185 .map(|(v, b)| (Some(v), b))
186 .unwrap_or((None, false));
187 let color = match matches.opt_str("color").as_deref() {
188 Some("auto") | None => ColorConfig::AutoColor,
189 Some("always") => ColorConfig::AlwaysColor,
190 Some("never") => ColorConfig::NeverColor,
191 Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x),
194 matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version);
196 let src_base = opt_path(matches, "src-base");
197 let run_ignored = matches.opt_present("ignored");
198 let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
199 let has_tidy = if mode == Mode::Rustdoc {
202 .stdout(Stdio::null())
204 .map_or(false, |status| status.success())
206 // Avoid spawning an external command when we know tidy won't be used.
210 bless: matches.opt_present("bless"),
211 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
212 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
213 rustc_path: opt_path(matches, "rustc-path"),
214 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
215 rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
216 lldb_python: matches.opt_str("lldb-python").unwrap(),
217 docck_python: matches.opt_str("docck-python").unwrap(),
218 jsondocck_path: matches.opt_str("jsondocck-path"),
219 valgrind_path: matches.opt_str("valgrind-path"),
220 force_valgrind: matches.opt_present("force-valgrind"),
221 run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
222 llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
223 llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
225 build_base: opt_path(matches, "build-base"),
226 stage_id: matches.opt_str("stage-id").unwrap(),
228 suite: matches.opt_str("suite").unwrap(),
231 filters: matches.free.clone(),
232 filter_exact: matches.opt_present("exact"),
233 force_pass_mode: matches.opt_str("pass").map(|mode| {
234 mode.parse::<PassMode>()
235 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
237 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
238 runtool: matches.opt_str("runtool"),
239 host_rustcflags: matches.opt_str("host-rustcflags"),
240 target_rustcflags: matches.opt_str("target-rustcflags"),
242 host: opt_str2(matches.opt_str("host")),
251 system_llvm: matches.opt_present("system-llvm"),
253 adb_path: opt_str2(matches.opt_str("adb-path")),
254 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
255 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
256 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
257 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
258 lldb_python_dir: matches.opt_str("lldb-python-dir"),
259 verbose: matches.opt_present("verbose"),
260 quiet: matches.opt_present("quiet"),
262 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
263 compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
264 rustfix_coverage: matches.opt_present("rustfix-coverage"),
267 cc: matches.opt_str("cc").unwrap(),
268 cxx: matches.opt_str("cxx").unwrap(),
269 cflags: matches.opt_str("cflags").unwrap(),
270 ar: matches.opt_str("ar").unwrap_or_else(|| String::from("ar")),
271 linker: matches.opt_str("linker"),
272 llvm_components: matches.opt_str("llvm-components").unwrap(),
273 nodejs: matches.opt_str("nodejs"),
274 npm: matches.opt_str("npm"),
278 pub fn log_config(config: &Config) {
280 logv(c, "configuration:".to_string());
281 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
282 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
283 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
284 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
285 logv(c, format!("rust_demangler_path: {:?}", config.rust_demangler_path));
286 logv(c, format!("src_base: {:?}", config.src_base.display()));
287 logv(c, format!("build_base: {:?}", config.build_base.display()));
288 logv(c, format!("stage_id: {}", config.stage_id));
289 logv(c, format!("mode: {}", config.mode));
290 logv(c, format!("run_ignored: {}", config.run_ignored));
291 logv(c, format!("filters: {:?}", config.filters));
292 logv(c, format!("filter_exact: {}", config.filter_exact));
295 format!("force_pass_mode: {}", opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),),
297 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
298 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
299 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
300 logv(c, format!("target: {}", config.target));
301 logv(c, format!("host: {}", config.host));
302 logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display()));
303 logv(c, format!("adb_path: {:?}", config.adb_path));
304 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
305 logv(c, format!("adb_device_status: {}", config.adb_device_status));
306 logv(c, format!("ar: {}", config.ar));
307 logv(c, format!("linker: {:?}", config.linker));
308 logv(c, format!("verbose: {}", config.verbose));
309 logv(c, format!("quiet: {}", config.quiet));
310 logv(c, "\n".to_string());
313 pub fn opt_str(maybestr: &Option<String>) -> &str {
320 pub fn opt_str2(maybestr: Option<String>) -> String {
322 None => "(none)".to_owned(),
327 pub fn run_tests(config: Config) {
328 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
329 if let Mode::CodegenUnits = config.mode {
330 let _ = fs::remove_dir_all("tmp/partitioning-tests");
333 // If we want to collect rustfix coverage information,
334 // we first make sure that the coverage file does not exist.
335 // It will be created later on.
336 if config.rustfix_coverage {
337 let mut coverage_file_path = config.build_base.clone();
338 coverage_file_path.push("rustfix_missing_coverage.txt");
339 if coverage_file_path.exists() {
340 if let Err(e) = fs::remove_file(&coverage_file_path) {
341 panic!("Could not delete {} due to {}", coverage_file_path.display(), e)
346 // sadly osx needs some file descriptor limits raised for running tests in
347 // parallel (especially when we have lots and lots of child processes).
348 // For context, see #8904
350 raise_fd_limit::raise_fd_limit();
352 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
353 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
354 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
356 // Let tests know which target they're running as
357 env::set_var("TARGET", &config.target);
359 let opts = test_opts(&config);
361 let mut configs = Vec::new();
362 if let Mode::DebugInfo = config.mode {
363 // Debugging emscripten code doesn't make sense today
364 if !config.target.contains("emscripten") {
365 configs.extend(configure_cdb(&config));
366 configs.extend(configure_gdb(&config));
367 configs.extend(configure_lldb(&config));
370 configs.push(config.clone());
373 let mut tests = Vec::new();
375 make_tests(c, &mut tests);
378 let res = test::run_tests_console(&opts, tests);
382 // We want to report that the tests failed, but we also want to give
383 // some indication of just what tests we were running. Especially on
384 // CI, where there can be cross-compiled tests for a lot of
385 // architectures, without this critical information it can be quite
386 // easy to miss which tests failed, and as such fail to reproduce
387 // the failure locally.
390 "Some tests failed in compiletest suite={}{} mode={} host={} target={}",
392 config.compare_mode.map(|c| format!(" compare_mode={:?}", c)).unwrap_or_default(),
398 std::process::exit(1);
401 // We don't know if tests passed or not, but if there was an error
402 // during testing we don't want to just succeed (we may not have
403 // tested something), so fail.
405 // This should realistically "never" happen, so don't try to make
406 // this a pretty error message.
407 panic!("I/O failure during tests: {:?}", e);
412 fn configure_cdb(config: &Config) -> Option<Config> {
413 config.cdb.as_ref()?;
415 Some(Config { debugger: Some(Debugger::Cdb), ..config.clone() })
418 fn configure_gdb(config: &Config) -> Option<Config> {
421 if util::matches_env(&config.target, "msvc") {
425 if config.remote_test_client.is_some() && !config.target.contains("android") {
427 "WARNING: debuginfo tests are not available when \
433 if config.target.contains("android") {
435 "{} debug-info test uses tcp 5039 port.\
440 // android debug-info test uses remote debugger so, we test 1 thread
441 // at once as they're all sharing the same TCP port to communicate
444 // we should figure out how to lift this restriction! (run them all
445 // on different ports allocated dynamically).
446 env::set_var("RUST_TEST_THREADS", "1");
449 Some(Config { debugger: Some(Debugger::Gdb), ..config.clone() })
452 fn configure_lldb(config: &Config) -> Option<Config> {
453 config.lldb_python_dir.as_ref()?;
455 if let Some(350) = config.lldb_version {
457 "WARNING: The used version of LLDB (350) has a \
458 known issue that breaks debuginfo tests. See \
459 issue #32520 for more information. Skipping all \
465 // Some older versions of LLDB seem to have problems with multiple
466 // instances running in parallel, so only run one test thread at a
468 env::set_var("RUST_TEST_THREADS", "1");
470 Some(Config { debugger: Some(Debugger::Lldb), ..config.clone() })
473 pub fn test_opts(config: &Config) -> test::TestOpts {
475 exclude_should_panic: false,
476 filters: config.filters.clone(),
477 filter_exact: config.filter_exact,
478 run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
479 format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
480 logfile: config.logfile.clone(),
482 bench_benchmarks: true,
483 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
484 Ok(val) => &val != "0",
491 options: test::Options::new(),
493 force_run_in_process: false,
497 pub fn make_tests(config: &Config, tests: &mut Vec<test::TestDescAndFn>) {
498 debug!("making tests from {:?}", config.src_base.display());
499 let inputs = common_inputs_stamp(config);
500 collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests)
501 .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display()));
504 /// Returns a stamp constructed from input files common to all test cases.
505 fn common_inputs_stamp(config: &Config) -> Stamp {
506 let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
508 let mut stamp = Stamp::from_path(&config.rustc_path);
510 // Relevant pretty printer files
511 let pretty_printer_files = [
512 "src/etc/rust_types.py",
513 "src/etc/gdb_load_rust_pretty_printers.py",
514 "src/etc/gdb_lookup.py",
515 "src/etc/gdb_providers.py",
516 "src/etc/lldb_batchmode.py",
517 "src/etc/lldb_lookup.py",
518 "src/etc/lldb_providers.py",
520 for file in &pretty_printer_files {
521 let path = rust_src_dir.join(file);
522 stamp.add_path(&path);
525 stamp.add_dir(&config.run_lib_path);
527 if let Some(ref rustdoc_path) = config.rustdoc_path {
528 stamp.add_path(&rustdoc_path);
529 stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
532 // Compiletest itself.
533 stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
538 fn collect_tests_from_dir(
541 relative_dir_path: &Path,
543 tests: &mut Vec<test::TestDescAndFn>,
544 ) -> io::Result<()> {
545 // Ignore directories that contain a file named `compiletest-ignore-dir`.
546 if dir.join("compiletest-ignore-dir").exists() {
550 if config.mode == Mode::RunMake && dir.join("Makefile").exists() {
551 let paths = TestPaths {
552 file: dir.to_path_buf(),
553 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
555 tests.extend(make_test(config, &paths, inputs));
559 // If we find a test foo/bar.rs, we have to build the
560 // output directory `$build/foo` so we can write
561 // `$build/foo/bar` into it. We do this *now* in this
562 // sequential loop because otherwise, if we do it in the
563 // tests themselves, they race for the privilege of
564 // creating the directories and sometimes fail randomly.
565 let build_dir = output_relative_path(config, relative_dir_path);
566 fs::create_dir_all(&build_dir).unwrap();
568 // Add each `.rs` file as a test, and recurse further on any
569 // subdirectories we find, except for `aux` directories.
570 for file in fs::read_dir(dir)? {
572 let file_path = file.path();
573 let file_name = file.file_name();
574 if is_test(&file_name) {
575 debug!("found test file: {:?}", file_path.display());
577 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
578 tests.extend(make_test(config, &paths, inputs))
579 } else if file_path.is_dir() {
580 let relative_file_path = relative_dir_path.join(file.file_name());
581 if &file_name != "auxiliary" {
582 debug!("found directory: {:?}", file_path.display());
583 collect_tests_from_dir(config, &file_path, &relative_file_path, inputs, tests)?;
586 debug!("found other file/directory: {:?}", file_path.display());
592 /// Returns true if `file_name` looks like a proper test file name.
593 pub fn is_test(file_name: &OsString) -> bool {
594 let file_name = file_name.to_str().unwrap();
596 if !file_name.ends_with(".rs") {
600 // `.`, `#`, and `~` are common temp-file prefixes.
601 let invalid_prefixes = &[".", "#", "~"];
602 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
605 fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec<test::TestDescAndFn> {
606 let early_props = if config.mode == Mode::RunMake {
607 // Allow `ignore` directives to be in the Makefile.
608 EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
610 EarlyProps::from_file(config, &testpaths.file)
613 // The `should-fail` annotation doesn't apply to pretty tests,
614 // since we run the pretty printer across all tests by default.
615 // If desired, we could add a `should-fail-pretty` annotation.
616 let should_panic = match config.mode {
617 Pretty => test::ShouldPanic::No,
619 if early_props.should_fail {
620 test::ShouldPanic::Yes
622 test::ShouldPanic::No
627 // Incremental tests are special, they inherently cannot be run in parallel.
628 // `runtest::run` will be responsible for iterating over revisions.
629 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
632 early_props.revisions.iter().map(Some).collect()
637 let ignore = early_props.ignore
638 // Ignore tests that already run and are up to date with respect to inputs.
643 revision.map(|s| s.as_str()),
646 test::TestDescAndFn {
647 desc: test::TestDesc {
648 name: make_test_name(config, testpaths, revision),
652 test_type: test::TestType::Unknown,
654 testfn: make_test_closure(config, testpaths, revision),
660 fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
661 output_base_dir(config, testpaths, revision).join("stamp")
666 testpaths: &TestPaths,
668 revision: Option<&str>,
671 let stamp_name = stamp(config, testpaths, revision);
673 let contents = match fs::read_to_string(&stamp_name) {
675 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
676 Err(_) => return false,
678 let expected_hash = runtest::compute_stamp_hash(config);
679 if contents != expected_hash {
684 let mut inputs = inputs.clone();
685 // Use `add_dir` to account for run-make tests, which use their individual directory
686 inputs.add_dir(&testpaths.file);
688 for aux in &props.aux {
689 let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
690 inputs.add_path(&path);
694 for extension in UI_EXTENSIONS {
695 let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
696 inputs.add_path(path);
699 inputs < Stamp::from_path(&stamp_name)
702 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
708 fn from_path(path: &Path) -> Self {
709 let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
710 stamp.add_path(path);
714 fn add_path(&mut self, path: &Path) {
715 let modified = fs::metadata(path)
716 .and_then(|metadata| metadata.modified())
717 .unwrap_or(SystemTime::UNIX_EPOCH);
718 self.time = self.time.max(modified);
721 fn add_dir(&mut self, path: &Path) {
722 for entry in WalkDir::new(path) {
723 let entry = entry.unwrap();
724 if entry.file_type().is_file() {
728 .and_then(|metadata| metadata.modified().ok())
729 .unwrap_or(SystemTime::UNIX_EPOCH);
730 self.time = self.time.max(modified);
738 testpaths: &TestPaths,
739 revision: Option<&String>,
740 ) -> test::TestName {
741 // Convert a complete path to something like
744 let path = PathBuf::from(config.src_base.file_name().unwrap())
745 .join(&testpaths.relative_dir)
746 .join(&testpaths.file.file_name().unwrap());
747 let debugger = match config.debugger {
748 Some(d) => format!("-{}", d),
749 None => String::new(),
751 let mode_suffix = match config.compare_mode {
752 Some(ref mode) => format!(" ({})", mode.to_str()),
753 None => String::new(),
756 test::DynTestName(format!(
762 revision.map_or("".to_string(), |rev| format!("#{}", rev))
766 fn make_test_closure(
768 testpaths: &TestPaths,
769 revision: Option<&String>,
771 let config = config.clone();
772 let testpaths = testpaths.clone();
773 let revision = revision.cloned();
774 test::DynTestFn(Box::new(move || runtest::run(config, &testpaths, revision.as_deref())))
777 /// Returns `true` if the given target is an Android target for the
778 /// purposes of GDB testing.
779 fn is_android_gdb_target(target: &str) -> bool {
782 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
786 /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
787 fn is_pc_windows_msvc_target(target: &str) -> bool {
788 target.ends_with("-pc-windows-msvc")
791 fn find_cdb(target: &str) -> Option<OsString> {
792 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
796 let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
797 let cdb_arch = if cfg!(target_arch = "x86") {
799 } else if cfg!(target_arch = "x86_64") {
801 } else if cfg!(target_arch = "aarch64") {
803 } else if cfg!(target_arch = "arm") {
806 return None; // No compatible CDB.exe in the Windows 10 SDK
809 let mut path = PathBuf::new();
811 path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
813 path.push(r"cdb.exe");
819 Some(path.into_os_string())
822 /// Returns Path to CDB
823 fn analyze_cdb(cdb: Option<String>, target: &str) -> (Option<OsString>, Option<[u16; 4]>) {
824 let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
826 let mut version = None;
827 if let Some(cdb) = cdb.as_ref() {
828 if let Ok(output) = Command::new(cdb).arg("/version").output() {
829 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
830 version = extract_cdb_version(&first_line);
838 fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
839 // Example full_version_line: "cdb version 10.0.18362.1"
840 let version = full_version_line.rsplit(' ').next()?;
841 let mut components = version.split('.');
842 let major: u16 = components.next().unwrap().parse().unwrap();
843 let minor: u16 = components.next().unwrap().parse().unwrap();
844 let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
845 let build: u16 = components.next().unwrap_or("0").parse().unwrap();
846 Some([major, minor, patch, build])
849 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
853 android_cross_path: &PathBuf,
854 ) -> (Option<String>, Option<u32>, bool) {
856 const GDB_FALLBACK: &str = "gdb";
858 const GDB_FALLBACK: &str = "gdb.exe";
860 const MIN_GDB_WITH_RUST: u32 = 7011010;
862 let fallback_gdb = || {
863 if is_android_gdb_target(target) {
864 let mut gdb_path = match android_cross_path.to_str() {
865 Some(x) => x.to_owned(),
866 None => panic!("cannot find android cross path"),
868 gdb_path.push_str("/bin/gdb");
871 GDB_FALLBACK.to_owned()
875 let gdb = match gdb {
876 None => fallback_gdb(),
877 Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
878 Some(ref s) => s.to_owned(),
881 let mut version_line = None;
882 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
883 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
884 version_line = Some(first_line.to_string());
888 let version = match version_line {
889 Some(line) => extract_gdb_version(&line),
890 None => return (None, None, false),
893 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
895 (Some(gdb), version, gdb_native_rust)
898 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
899 let full_version_line = full_version_line.trim();
901 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
902 // of the ? sections being optional
904 // We will parse up to 3 digits for each component, ignoring the date
906 // We skip text in parentheses. This avoids accidentally parsing
907 // the openSUSE version, which looks like:
908 // GNU gdb (GDB; openSUSE Leap 15.0) 8.1
909 // This particular form is documented in the GNU coding standards:
910 // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
912 let mut splits = full_version_line.rsplit(' ');
913 let version_string = splits.next().unwrap();
915 let mut splits = version_string.split('.');
916 let major = splits.next().unwrap();
917 let minor = splits.next().unwrap();
918 let patch = splits.next();
920 let major: u32 = major.parse().unwrap();
921 let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
923 let minor = minor.parse().unwrap();
924 let patch: u32 = match patch {
925 Some(patch) => match patch.find(not_a_digit) {
926 None => patch.parse().unwrap(),
927 Some(idx) if idx > 3 => 0,
928 Some(idx) => patch[..idx].parse().unwrap(),
934 // There is no patch version after minor-date (e.g. "4-2012").
936 let minor = minor[..idx].parse().unwrap();
941 Some(((major * 1000) + minor) * 1000 + patch)
944 /// Returns (LLDB version, LLDB is rust-enabled)
945 fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> {
946 // Extract the major LLDB version from the given version string.
947 // LLDB version strings are different for Apple and non-Apple platforms.
948 // The Apple variant looks like this:
950 // LLDB-179.5 (older versions)
951 // lldb-300.2.51 (new versions)
953 // We are only interested in the major version number, so this function
954 // will return `Some(179)` and `Some(300)` respectively.
956 // Upstream versions look like:
957 // lldb version 6.0.1
959 // There doesn't seem to be a way to correlate the Apple version
960 // with the upstream version, and since the tests were originally
961 // written against Apple versions, we make a fake Apple version by
962 // multiplying the first number by 100. This is a hack, but
963 // normally fine because the only non-Apple version we test is
966 let full_version_line = full_version_line.trim();
968 if let Some(apple_ver) =
969 full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
971 if let Some(idx) = apple_ver.find(not_a_digit) {
972 let version: u32 = apple_ver[..idx].parse().unwrap();
973 return Some((version, full_version_line.contains("rust-enabled")));
975 } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
976 if let Some(idx) = lldb_ver.find(not_a_digit) {
977 let version: u32 = lldb_ver[..idx].parse().unwrap();
978 return Some((version * 100, full_version_line.contains("rust-enabled")));
984 fn not_a_digit(c: char) -> bool {