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;
18 use std::time::SystemTime;
19 use test::ColorConfig;
23 use self::header::EarlyProps;
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");
50 pub fn parse_config(args: Vec<String>) -> Config {
51 let mut opts = Options::new();
52 opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
53 .reqopt("", "run-lib-path", "path to target shared libraries", "PATH")
54 .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH")
55 .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH")
56 .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH")
57 .reqopt("", "lldb-python", "path to python to use for doc tests", "PATH")
58 .reqopt("", "docck-python", "path to python to use for doc tests", "PATH")
59 .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
60 .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
61 .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH")
62 .optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
63 .reqopt("", "src-base", "directory to scan for test files", "PATH")
64 .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
65 .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
69 "which sort of compile tests to run",
70 "compile-fail | run-fail | run-pass-valgrind | pretty | debug-info | codegen | rustdoc \
71 codegen-units | incremental | run-make | ui | js-doc-test | mir-opt | assembly",
76 "force {check,build,run}-pass tests to this mode.",
77 "check | build | run",
79 .optflag("", "ignored", "run tests marked as ignored")
80 .optflag("", "exact", "filters match exactly")
84 "supervisor program to run tests under \
85 (eg. emulator, valgrind)",
88 .optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
89 .optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
90 .optflag("", "verbose", "run tests verbosely, showing all output")
94 "overwrite stderr/stdout files instead of complaining about a mismatch",
96 .optflag("", "quiet", "print one character per test instead of one line")
97 .optopt("", "color", "coloring: auto, always, never", "WHEN")
98 .optopt("", "logfile", "file to log test execution to", "FILE")
99 .optopt("", "target", "the target to build for", "TARGET")
100 .optopt("", "host", "the host to build for", "HOST")
101 .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
102 .optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH")
103 .optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING")
104 .optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING")
105 .optflag("", "system-llvm", "is LLVM the system LLVM")
106 .optopt("", "android-cross-path", "Android NDK standalone path", "PATH")
107 .optopt("", "adb-path", "path to the android debugger", "PATH")
108 .optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH")
109 .optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH")
110 .reqopt("", "cc", "path to a C compiler", "PATH")
111 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
112 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
113 .optopt("", "ar", "path to an archiver", "PATH")
114 .optopt("", "linker", "path to a linker", "PATH")
115 .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
116 .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
117 .optopt("", "nodejs", "the name of nodejs", "PATH")
118 .optopt("", "remote-test-client", "path to the remote test client", "PATH")
122 "mode describing what file the actual ui output will be compared to",
128 "enable this to generate a Rustfix coverage file, which is saved in \
129 `./<build_base>/rustfix_missing_coverage.txt`",
131 .optflag("h", "help", "show this message");
133 let (argv0, args_) = args.split_first().unwrap();
134 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
135 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
136 println!("{}", opts.usage(&message));
141 let matches = &match opts.parse(args_) {
143 Err(f) => panic!("{:?}", f),
146 if matches.opt_present("h") || matches.opt_present("help") {
147 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
148 println!("{}", opts.usage(&message));
153 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
154 match m.opt_str(nm) {
155 Some(s) => PathBuf::from(&s),
156 None => panic!("no option (=path) found for {}", nm),
160 fn make_absolute(path: PathBuf) -> PathBuf {
161 if path.is_relative() { env::current_dir().unwrap().join(path) } else { path }
164 let target = opt_str2(matches.opt_str("target"));
165 let android_cross_path = opt_path(matches, "android-cross-path");
166 let cdb = analyze_cdb(matches.opt_str("cdb"), &target);
167 let (gdb, gdb_version, gdb_native_rust) =
168 analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
169 let (lldb_version, lldb_native_rust) = matches
170 .opt_str("lldb-version")
172 .and_then(extract_lldb_version)
173 .map(|(v, b)| (Some(v), b))
174 .unwrap_or((None, false));
175 let color = match matches.opt_str("color").as_deref() {
176 Some("auto") | None => ColorConfig::AutoColor,
177 Some("always") => ColorConfig::AlwaysColor,
178 Some("never") => ColorConfig::NeverColor,
179 Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x),
182 matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version);
184 let src_base = opt_path(matches, "src-base");
185 let run_ignored = matches.opt_present("ignored");
187 bless: matches.opt_present("bless"),
188 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
189 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
190 rustc_path: opt_path(matches, "rustc-path"),
191 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
192 rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
193 lldb_python: matches.opt_str("lldb-python").unwrap(),
194 docck_python: matches.opt_str("docck-python").unwrap(),
195 valgrind_path: matches.opt_str("valgrind-path"),
196 force_valgrind: matches.opt_present("force-valgrind"),
197 run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
198 llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
199 llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
201 build_base: opt_path(matches, "build-base"),
202 stage_id: matches.opt_str("stage-id").unwrap(),
203 mode: matches.opt_str("mode").unwrap().parse().expect("invalid mode"),
206 filter: matches.free.first().cloned(),
207 filter_exact: matches.opt_present("exact"),
208 force_pass_mode: matches.opt_str("pass").map(|mode| {
209 mode.parse::<PassMode>()
210 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
212 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
213 runtool: matches.opt_str("runtool"),
214 host_rustcflags: matches.opt_str("host-rustcflags"),
215 target_rustcflags: matches.opt_str("target-rustcflags"),
217 host: opt_str2(matches.opt_str("host")),
225 system_llvm: matches.opt_present("system-llvm"),
227 adb_path: opt_str2(matches.opt_str("adb-path")),
228 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
229 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
230 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
231 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
232 lldb_python_dir: matches.opt_str("lldb-python-dir"),
233 verbose: matches.opt_present("verbose"),
234 quiet: matches.opt_present("quiet"),
236 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
237 compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
238 rustfix_coverage: matches.opt_present("rustfix-coverage"),
240 cc: matches.opt_str("cc").unwrap(),
241 cxx: matches.opt_str("cxx").unwrap(),
242 cflags: matches.opt_str("cflags").unwrap(),
243 ar: matches.opt_str("ar").unwrap_or_else(|| String::from("ar")),
244 linker: matches.opt_str("linker"),
245 llvm_components: matches.opt_str("llvm-components").unwrap(),
246 nodejs: matches.opt_str("nodejs"),
250 pub fn log_config(config: &Config) {
252 logv(c, "configuration:".to_string());
253 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
254 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
255 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
256 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
257 logv(c, format!("rust_demangler_path: {:?}", config.rust_demangler_path));
258 logv(c, format!("src_base: {:?}", config.src_base.display()));
259 logv(c, format!("build_base: {:?}", config.build_base.display()));
260 logv(c, format!("stage_id: {}", config.stage_id));
261 logv(c, format!("mode: {}", config.mode));
262 logv(c, format!("run_ignored: {}", config.run_ignored));
263 logv(c, format!("filter: {}", opt_str(&config.filter)));
264 logv(c, format!("filter_exact: {}", config.filter_exact));
267 format!("force_pass_mode: {}", opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),),
269 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
270 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
271 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
272 logv(c, format!("target: {}", config.target));
273 logv(c, format!("host: {}", config.host));
274 logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display()));
275 logv(c, format!("adb_path: {:?}", config.adb_path));
276 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
277 logv(c, format!("adb_device_status: {}", config.adb_device_status));
278 logv(c, format!("ar: {}", config.ar));
279 logv(c, format!("linker: {:?}", config.linker));
280 logv(c, format!("verbose: {}", config.verbose));
281 logv(c, format!("quiet: {}", config.quiet));
282 logv(c, "\n".to_string());
285 pub fn opt_str(maybestr: &Option<String>) -> &str {
292 pub fn opt_str2(maybestr: Option<String>) -> String {
294 None => "(none)".to_owned(),
299 pub fn run_tests(config: Config) {
300 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
301 if let Mode::CodegenUnits = config.mode {
302 let _ = fs::remove_dir_all("tmp/partitioning-tests");
305 // If we want to collect rustfix coverage information,
306 // we first make sure that the coverage file does not exist.
307 // It will be created later on.
308 if config.rustfix_coverage {
309 let mut coverage_file_path = config.build_base.clone();
310 coverage_file_path.push("rustfix_missing_coverage.txt");
311 if coverage_file_path.exists() {
312 if let Err(e) = fs::remove_file(&coverage_file_path) {
313 panic!("Could not delete {} due to {}", coverage_file_path.display(), e)
318 // sadly osx needs some file descriptor limits raised for running tests in
319 // parallel (especially when we have lots and lots of child processes).
320 // For context, see #8904
322 raise_fd_limit::raise_fd_limit();
324 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
325 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
326 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
328 // Let tests know which target they're running as
329 env::set_var("TARGET", &config.target);
331 let opts = test_opts(&config);
333 let mut configs = Vec::new();
334 if let Mode::DebugInfo = config.mode {
335 // Debugging emscripten code doesn't make sense today
336 if !config.target.contains("emscripten") {
337 configs.extend(configure_cdb(&config));
338 configs.extend(configure_gdb(&config));
339 configs.extend(configure_lldb(&config));
342 configs.push(config);
345 let mut tests = Vec::new();
347 make_tests(c, &mut tests);
350 let res = test::run_tests_console(&opts, tests);
353 Ok(false) => panic!("Some tests failed"),
355 // We don't know if tests passed or not, but if there was an error
356 // during testing we don't want to just suceeed (we may not have
357 // tested something), so fail.
358 panic!("I/O failure during tests: {:?}", e);
363 fn configure_cdb(config: &Config) -> Option<Config> {
364 config.cdb.as_ref()?;
366 Some(Config { debugger: Some(Debugger::Cdb), ..config.clone() })
369 fn configure_gdb(config: &Config) -> Option<Config> {
372 if util::matches_env(&config.target, "msvc") {
376 if config.remote_test_client.is_some() && !config.target.contains("android") {
378 "WARNING: debuginfo tests are not available when \
384 if config.target.contains("android") {
386 "{} debug-info test uses tcp 5039 port.\
391 // android debug-info test uses remote debugger so, we test 1 thread
392 // at once as they're all sharing the same TCP port to communicate
395 // we should figure out how to lift this restriction! (run them all
396 // on different ports allocated dynamically).
397 env::set_var("RUST_TEST_THREADS", "1");
400 Some(Config { debugger: Some(Debugger::Gdb), ..config.clone() })
403 fn configure_lldb(config: &Config) -> Option<Config> {
404 config.lldb_python_dir.as_ref()?;
406 if let Some(350) = config.lldb_version {
408 "WARNING: The used version of LLDB (350) has a \
409 known issue that breaks debuginfo tests. See \
410 issue #32520 for more information. Skipping all \
416 // Some older versions of LLDB seem to have problems with multiple
417 // instances running in parallel, so only run one test thread at a
419 env::set_var("RUST_TEST_THREADS", "1");
421 Some(Config { debugger: Some(Debugger::Lldb), ..config.clone() })
424 pub fn test_opts(config: &Config) -> test::TestOpts {
426 exclude_should_panic: false,
427 filter: config.filter.clone(),
428 filter_exact: config.filter_exact,
429 run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
430 format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
431 logfile: config.logfile.clone(),
433 bench_benchmarks: true,
434 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
435 Ok(val) => &val != "0",
442 options: test::Options::new(),
444 force_run_in_process: false,
448 pub fn make_tests(config: &Config, tests: &mut Vec<test::TestDescAndFn>) {
449 debug!("making tests from {:?}", config.src_base.display());
450 let inputs = common_inputs_stamp(config);
451 collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests)
452 .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display()));
455 /// Returns a stamp constructed from input files common to all test cases.
456 fn common_inputs_stamp(config: &Config) -> Stamp {
457 let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
459 let mut stamp = Stamp::from_path(&config.rustc_path);
461 // Relevant pretty printer files
462 let pretty_printer_files = [
463 "src/etc/rust_types.py",
464 "src/etc/gdb_load_rust_pretty_printers.py",
465 "src/etc/gdb_lookup.py",
466 "src/etc/gdb_providers.py",
467 "src/etc/lldb_batchmode.py",
468 "src/etc/lldb_lookup.py",
469 "src/etc/lldb_providers.py",
471 for file in &pretty_printer_files {
472 let path = rust_src_dir.join(file);
473 stamp.add_path(&path);
476 stamp.add_dir(&config.run_lib_path);
478 if let Some(ref rustdoc_path) = config.rustdoc_path {
479 stamp.add_path(&rustdoc_path);
480 stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
482 // FIXME(richkadel): Do I need to add an `if let Some(rust_demangler_path) contribution to the
483 // stamp here as well?
485 // Compiletest itself.
486 stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
491 fn collect_tests_from_dir(
494 relative_dir_path: &Path,
496 tests: &mut Vec<test::TestDescAndFn>,
497 ) -> io::Result<()> {
498 // Ignore directories that contain a file named `compiletest-ignore-dir`.
499 if dir.join("compiletest-ignore-dir").exists() {
503 if config.mode == Mode::RunMake && dir.join("Makefile").exists() {
504 let paths = TestPaths {
505 file: dir.to_path_buf(),
506 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
508 tests.extend(make_test(config, &paths, inputs));
512 // If we find a test foo/bar.rs, we have to build the
513 // output directory `$build/foo` so we can write
514 // `$build/foo/bar` into it. We do this *now* in this
515 // sequential loop because otherwise, if we do it in the
516 // tests themselves, they race for the privilege of
517 // creating the directories and sometimes fail randomly.
518 let build_dir = output_relative_path(config, relative_dir_path);
519 fs::create_dir_all(&build_dir).unwrap();
521 // Add each `.rs` file as a test, and recurse further on any
522 // subdirectories we find, except for `aux` directories.
523 for file in fs::read_dir(dir)? {
525 let file_path = file.path();
526 let file_name = file.file_name();
527 if is_test(&file_name) {
528 debug!("found test file: {:?}", file_path.display());
530 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
531 tests.extend(make_test(config, &paths, inputs))
532 } else if file_path.is_dir() {
533 let relative_file_path = relative_dir_path.join(file.file_name());
534 if &file_name != "auxiliary" {
535 debug!("found directory: {:?}", file_path.display());
536 collect_tests_from_dir(config, &file_path, &relative_file_path, inputs, tests)?;
539 debug!("found other file/directory: {:?}", file_path.display());
545 /// Returns true if `file_name` looks like a proper test file name.
546 pub fn is_test(file_name: &OsString) -> bool {
547 let file_name = file_name.to_str().unwrap();
549 if !file_name.ends_with(".rs") {
553 // `.`, `#`, and `~` are common temp-file prefixes.
554 let invalid_prefixes = &[".", "#", "~"];
555 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
558 fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec<test::TestDescAndFn> {
559 let early_props = if config.mode == Mode::RunMake {
560 // Allow `ignore` directives to be in the Makefile.
561 EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
563 EarlyProps::from_file(config, &testpaths.file)
566 // The `should-fail` annotation doesn't apply to pretty tests,
567 // since we run the pretty printer across all tests by default.
568 // If desired, we could add a `should-fail-pretty` annotation.
569 let should_panic = match config.mode {
570 Pretty => test::ShouldPanic::No,
572 if early_props.should_fail {
573 test::ShouldPanic::Yes
575 test::ShouldPanic::No
580 // Incremental tests are special, they inherently cannot be run in parallel.
581 // `runtest::run` will be responsible for iterating over revisions.
582 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
585 early_props.revisions.iter().map(Some).collect()
590 let ignore = early_props.ignore
591 // Ignore tests that already run and are up to date with respect to inputs.
596 revision.map(|s| s.as_str()),
599 test::TestDescAndFn {
600 desc: test::TestDesc {
601 name: make_test_name(config, testpaths, revision),
605 test_type: test::TestType::Unknown,
607 testfn: make_test_closure(config, testpaths, revision),
613 fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
614 output_base_dir(config, testpaths, revision).join("stamp")
619 testpaths: &TestPaths,
621 revision: Option<&str>,
624 let stamp_name = stamp(config, testpaths, revision);
626 let contents = match fs::read_to_string(&stamp_name) {
628 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
629 Err(_) => return false,
631 let expected_hash = runtest::compute_stamp_hash(config);
632 if contents != expected_hash {
637 let mut inputs = inputs.clone();
638 // Use `add_dir` to account for run-make tests, which use their individual directory
639 inputs.add_dir(&testpaths.file);
641 for aux in &props.aux {
642 let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
643 inputs.add_path(&path);
647 for extension in UI_EXTENSIONS {
648 let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
649 inputs.add_path(path);
652 inputs < Stamp::from_path(&stamp_name)
655 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
661 fn from_path(path: &Path) -> Self {
662 let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
663 stamp.add_path(path);
667 fn add_path(&mut self, path: &Path) {
668 let modified = fs::metadata(path)
669 .and_then(|metadata| metadata.modified())
670 .unwrap_or(SystemTime::UNIX_EPOCH);
671 self.time = self.time.max(modified);
674 fn add_dir(&mut self, path: &Path) {
675 for entry in WalkDir::new(path) {
676 let entry = entry.unwrap();
677 if entry.file_type().is_file() {
681 .and_then(|metadata| metadata.modified().ok())
682 .unwrap_or(SystemTime::UNIX_EPOCH);
683 self.time = self.time.max(modified);
691 testpaths: &TestPaths,
692 revision: Option<&String>,
693 ) -> test::TestName {
694 // Convert a complete path to something like
697 let path = PathBuf::from(config.src_base.file_name().unwrap())
698 .join(&testpaths.relative_dir)
699 .join(&testpaths.file.file_name().unwrap());
700 let debugger = match config.debugger {
701 Some(d) => format!("-{}", d),
702 None => String::new(),
704 let mode_suffix = match config.compare_mode {
705 Some(ref mode) => format!(" ({})", mode.to_str()),
706 None => String::new(),
709 test::DynTestName(format!(
715 revision.map_or("".to_string(), |rev| format!("#{}", rev))
719 fn make_test_closure(
721 testpaths: &TestPaths,
722 revision: Option<&String>,
724 let config = config.clone();
725 let testpaths = testpaths.clone();
726 let revision = revision.cloned();
727 test::DynTestFn(Box::new(move || runtest::run(config, &testpaths, revision.as_deref())))
730 /// Returns `true` if the given target is an Android target for the
731 /// purposes of GDB testing.
732 fn is_android_gdb_target(target: &str) -> bool {
735 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
739 /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
740 fn is_pc_windows_msvc_target(target: &str) -> bool {
741 target.ends_with("-pc-windows-msvc")
744 fn find_cdb(target: &str) -> Option<OsString> {
745 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
749 let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
750 let cdb_arch = if cfg!(target_arch = "x86") {
752 } else if cfg!(target_arch = "x86_64") {
754 } else if cfg!(target_arch = "aarch64") {
756 } else if cfg!(target_arch = "arm") {
759 return None; // No compatible CDB.exe in the Windows 10 SDK
762 let mut path = PathBuf::new();
764 path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
766 path.push(r"cdb.exe");
772 Some(path.into_os_string())
775 /// Returns Path to CDB
776 fn analyze_cdb(cdb: Option<String>, target: &str) -> Option<OsString> {
777 cdb.map(OsString::from).or_else(|| find_cdb(target))
780 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
784 android_cross_path: &PathBuf,
785 ) -> (Option<String>, Option<u32>, bool) {
787 const GDB_FALLBACK: &str = "gdb";
789 const GDB_FALLBACK: &str = "gdb.exe";
791 const MIN_GDB_WITH_RUST: u32 = 7011010;
793 let fallback_gdb = || {
794 if is_android_gdb_target(target) {
795 let mut gdb_path = match android_cross_path.to_str() {
796 Some(x) => x.to_owned(),
797 None => panic!("cannot find android cross path"),
799 gdb_path.push_str("/bin/gdb");
802 GDB_FALLBACK.to_owned()
806 let gdb = match gdb {
807 None => fallback_gdb(),
808 Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
809 Some(ref s) => s.to_owned(),
812 let mut version_line = None;
813 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
814 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
815 version_line = Some(first_line.to_string());
819 let version = match version_line {
820 Some(line) => extract_gdb_version(&line),
821 None => return (None, None, false),
824 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
826 (Some(gdb), version, gdb_native_rust)
829 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
830 let full_version_line = full_version_line.trim();
832 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
833 // of the ? sections being optional
835 // We will parse up to 3 digits for each component, ignoring the date
837 // We skip text in parentheses. This avoids accidentally parsing
838 // the openSUSE version, which looks like:
839 // GNU gdb (GDB; openSUSE Leap 15.0) 8.1
840 // This particular form is documented in the GNU coding standards:
841 // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
843 let mut splits = full_version_line.rsplit(' ');
844 let version_string = splits.next().unwrap();
846 let mut splits = version_string.split('.');
847 let major = splits.next().unwrap();
848 let minor = splits.next().unwrap();
849 let patch = splits.next();
851 let major: u32 = major.parse().unwrap();
852 let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
854 let minor = minor.parse().unwrap();
855 let patch: u32 = match patch {
856 Some(patch) => match patch.find(not_a_digit) {
857 None => patch.parse().unwrap(),
858 Some(idx) if idx > 3 => 0,
859 Some(idx) => patch[..idx].parse().unwrap(),
865 // There is no patch version after minor-date (e.g. "4-2012").
867 let minor = minor[..idx].parse().unwrap();
872 Some(((major * 1000) + minor) * 1000 + patch)
875 /// Returns (LLDB version, LLDB is rust-enabled)
876 fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> {
877 // Extract the major LLDB version from the given version string.
878 // LLDB version strings are different for Apple and non-Apple platforms.
879 // The Apple variant looks like this:
881 // LLDB-179.5 (older versions)
882 // lldb-300.2.51 (new versions)
884 // We are only interested in the major version number, so this function
885 // will return `Some(179)` and `Some(300)` respectively.
887 // Upstream versions look like:
888 // lldb version 6.0.1
890 // There doesn't seem to be a way to correlate the Apple version
891 // with the upstream version, and since the tests were originally
892 // written against Apple versions, we make a fake Apple version by
893 // multiplying the first number by 100. This is a hack, but
894 // normally fine because the only non-Apple version we test is
897 let full_version_line = full_version_line.trim();
899 if let Some(apple_ver) =
900 full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
902 if let Some(idx) = apple_ver.find(not_a_digit) {
903 let version: u32 = apple_ver[..idx].parse().unwrap();
904 return Some((version, full_version_line.contains("rust-enabled")));
906 } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
907 if let Some(idx) = lldb_ver.find(not_a_digit) {
908 let version: u32 = lldb_ver[..idx].parse().unwrap();
909 return Some((version * 100, full_version_line.contains("rust-enabled")));
915 fn not_a_digit(c: char) -> bool {