1 #![crate_name = "compiletest"]
2 #![feature(vec_remove_item)]
4 // The `test` crate is the only unstable feature
5 // allowed here, just to share similar code.
10 use crate::common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
11 use crate::common::{CompareMode, Config, Debugger, Mode, PassMode, Pretty, TestPaths};
12 use crate::util::logv;
18 use std::ffi::OsString;
20 use std::io::{self, ErrorKind};
21 use std::path::{Path, PathBuf};
22 use std::process::Command;
23 use std::time::SystemTime;
24 use test::ColorConfig;
27 use self::header::EarlyProps;
44 let config = parse_config(env::args().collect());
46 if config.valgrind_path.is_none() && config.force_valgrind {
47 panic!("Can't find Valgrind to run Valgrind tests");
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 .reqopt("", "lldb-python", "path to python to use for doc tests", "PATH")
61 .reqopt("", "docck-python", "path to python to use for doc tests", "PATH")
62 .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
63 .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
64 .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH")
65 .optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
66 .reqopt("", "src-base", "directory to scan for test files", "PATH")
67 .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
68 .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
72 "which sort of compile tests to run",
73 "(compile-fail|run-fail|run-pass-valgrind|pretty|debug-info|incremental|mir-opt)",
78 "force {check,build,run}-pass tests to this mode.",
79 "check | build | run",
81 .optflag("", "ignored", "run tests marked as ignored")
82 .optflag("", "exact", "filters match exactly")
86 "supervisor program to run tests under \
87 (eg. emulator, valgrind)",
90 .optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
91 .optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
92 .optflag("", "verbose", "run tests verbosely, showing all output")
96 "overwrite stderr/stdout files instead of complaining about a mismatch",
98 .optflag("", "quiet", "print one character per test instead of one line")
99 .optopt("", "color", "coloring: auto, always, never", "WHEN")
100 .optopt("", "logfile", "file to log test execution to", "FILE")
101 .optopt("", "target", "the target to build for", "TARGET")
102 .optopt("", "host", "the host to build for", "HOST")
103 .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
104 .optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH")
105 .optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING")
106 .optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING")
107 .optflag("", "system-llvm", "is LLVM the system LLVM")
108 .optopt("", "android-cross-path", "Android NDK standalone path", "PATH")
109 .optopt("", "adb-path", "path to the android debugger", "PATH")
110 .optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH")
111 .optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH")
112 .reqopt("", "cc", "path to a C compiler", "PATH")
113 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
114 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
115 .optopt("", "ar", "path to an archiver", "PATH")
116 .optopt("", "linker", "path to a linker", "PATH")
117 .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
118 .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS")
119 .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
120 .optopt("", "nodejs", "the name of nodejs", "PATH")
121 .optopt("", "remote-test-client", "path to the remote test client", "PATH")
125 "mode describing what file the actual ui output will be compared to",
131 "enable this to generate a Rustfix coverage file, which is saved in \
132 `./<build_base>/rustfix_missing_coverage.txt`",
134 .optflag("h", "help", "show this message");
136 let (argv0, args_) = args.split_first().unwrap();
137 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
138 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
139 println!("{}", opts.usage(&message));
144 let matches = &match opts.parse(args_) {
146 Err(f) => panic!("{:?}", f),
149 if matches.opt_present("h") || matches.opt_present("help") {
150 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
151 println!("{}", opts.usage(&message));
156 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
157 match m.opt_str(nm) {
158 Some(s) => PathBuf::from(&s),
159 None => panic!("no option (=path) found for {}", nm),
163 fn make_absolute(path: PathBuf) -> PathBuf {
164 if path.is_relative() { env::current_dir().unwrap().join(path) } else { path }
167 let target = opt_str2(matches.opt_str("target"));
168 let android_cross_path = opt_path(matches, "android-cross-path");
169 let cdb = analyze_cdb(matches.opt_str("cdb"), &target);
170 let (gdb, gdb_version, gdb_native_rust) =
171 analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
172 let (lldb_version, lldb_native_rust) = extract_lldb_version(matches.opt_str("lldb-version"));
174 let color = match matches.opt_str("color").as_ref().map(|x| &**x) {
175 Some("auto") | None => ColorConfig::AutoColor,
176 Some("always") => ColorConfig::AlwaysColor,
177 Some("never") => ColorConfig::NeverColor,
178 Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x),
181 let src_base = opt_path(matches, "src-base");
182 let run_ignored = matches.opt_present("ignored");
184 bless: matches.opt_present("bless"),
185 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
186 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
187 rustc_path: opt_path(matches, "rustc-path"),
188 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
189 lldb_python: matches.opt_str("lldb-python").unwrap(),
190 docck_python: matches.opt_str("docck-python").unwrap(),
191 valgrind_path: matches.opt_str("valgrind-path"),
192 force_valgrind: matches.opt_present("force-valgrind"),
193 run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
194 llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
195 llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
197 build_base: opt_path(matches, "build-base"),
198 stage_id: matches.opt_str("stage-id").unwrap(),
199 mode: matches.opt_str("mode").unwrap().parse().expect("invalid mode"),
202 filter: matches.free.first().cloned(),
203 filter_exact: matches.opt_present("exact"),
204 force_pass_mode: matches.opt_str("pass").map(|mode| {
205 mode.parse::<PassMode>()
206 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
208 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
209 runtool: matches.opt_str("runtool"),
210 host_rustcflags: matches.opt_str("host-rustcflags"),
211 target_rustcflags: matches.opt_str("target-rustcflags"),
213 host: opt_str2(matches.opt_str("host")),
220 llvm_version: matches.opt_str("llvm-version"),
221 system_llvm: matches.opt_present("system-llvm"),
223 adb_path: opt_str2(matches.opt_str("adb-path")),
224 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
225 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
226 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
227 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
228 lldb_python_dir: matches.opt_str("lldb-python-dir"),
229 verbose: matches.opt_present("verbose"),
230 quiet: matches.opt_present("quiet"),
232 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
233 compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
234 rustfix_coverage: matches.opt_present("rustfix-coverage"),
236 cc: matches.opt_str("cc").unwrap(),
237 cxx: matches.opt_str("cxx").unwrap(),
238 cflags: matches.opt_str("cflags").unwrap(),
239 ar: matches.opt_str("ar").unwrap_or("ar".into()),
240 linker: matches.opt_str("linker"),
241 llvm_components: matches.opt_str("llvm-components").unwrap(),
242 llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
243 nodejs: matches.opt_str("nodejs"),
247 pub fn log_config(config: &Config) {
249 logv(c, "configuration:".to_string());
250 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
251 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
252 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
253 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
254 logv(c, format!("src_base: {:?}", config.src_base.display()));
255 logv(c, format!("build_base: {:?}", config.build_base.display()));
256 logv(c, format!("stage_id: {}", config.stage_id));
257 logv(c, format!("mode: {}", config.mode));
258 logv(c, format!("run_ignored: {}", config.run_ignored));
259 logv(c, format!("filter: {}", opt_str(&config.filter.as_ref().map(|re| re.to_owned()))));
260 logv(c, format!("filter_exact: {}", config.filter_exact));
263 format!("force_pass_mode: {}", opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),),
265 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
266 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
267 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
268 logv(c, format!("target: {}", config.target));
269 logv(c, format!("host: {}", config.host));
270 logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display()));
271 logv(c, format!("adb_path: {:?}", config.adb_path));
272 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
273 logv(c, format!("adb_device_status: {}", config.adb_device_status));
274 logv(c, format!("ar: {}", config.ar));
275 logv(c, format!("linker: {:?}", config.linker));
276 logv(c, format!("verbose: {}", config.verbose));
277 logv(c, format!("quiet: {}", config.quiet));
278 logv(c, "\n".to_string());
281 pub fn opt_str(maybestr: &Option<String>) -> &str {
288 pub fn opt_str2(maybestr: Option<String>) -> String {
290 None => "(none)".to_owned(),
295 pub fn run_tests(config: Config) {
296 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
297 if let Mode::CodegenUnits = config.mode {
298 let _ = fs::remove_dir_all("tmp/partitioning-tests");
301 // If we want to collect rustfix coverage information,
302 // we first make sure that the coverage file does not exist.
303 // It will be created later on.
304 if config.rustfix_coverage {
305 let mut coverage_file_path = config.build_base.clone();
306 coverage_file_path.push("rustfix_missing_coverage.txt");
307 if coverage_file_path.exists() {
308 if let Err(e) = fs::remove_file(&coverage_file_path) {
309 panic!("Could not delete {} due to {}", coverage_file_path.display(), e)
314 // sadly osx needs some file descriptor limits raised for running tests in
315 // parallel (especially when we have lots and lots of child processes).
316 // For context, see #8904
318 raise_fd_limit::raise_fd_limit();
320 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
321 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
322 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
324 // Let tests know which target they're running as
325 env::set_var("TARGET", &config.target);
327 let opts = test_opts(&config);
329 let mut configs = Vec::new();
330 if let Mode::DebugInfo = config.mode {
331 // Debugging emscripten code doesn't make sense today
332 if !config.target.contains("emscripten") {
333 configs.extend(configure_cdb(&config));
334 configs.extend(configure_gdb(&config));
335 configs.extend(configure_lldb(&config));
338 configs.push(config);
341 let mut tests = Vec::new();
343 make_tests(c, &mut tests);
346 let res = test::run_tests_console(&opts, tests);
349 Ok(false) => panic!("Some tests failed"),
351 println!("I/O failure during tests: {:?}", e);
356 fn configure_cdb(config: &Config) -> Option<Config> {
357 if config.cdb.is_none() {
361 Some(Config { debugger: Some(Debugger::Cdb), ..config.clone() })
364 fn configure_gdb(config: &Config) -> Option<Config> {
365 if config.gdb_version.is_none() {
369 if util::matches_env(&config.target, "msvc") {
373 if config.remote_test_client.is_some() && !config.target.contains("android") {
375 "WARNING: debuginfo tests are not available when \
381 if config.target.contains("android") {
383 "{} debug-info test uses tcp 5039 port.\
388 // android debug-info test uses remote debugger so, we test 1 thread
389 // at once as they're all sharing the same TCP port to communicate
392 // we should figure out how to lift this restriction! (run them all
393 // on different ports allocated dynamically).
394 env::set_var("RUST_TEST_THREADS", "1");
397 Some(Config { debugger: Some(Debugger::Gdb), ..config.clone() })
400 fn configure_lldb(config: &Config) -> Option<Config> {
401 if config.lldb_python_dir.is_none() {
405 if let Some(lldb_version) = config.lldb_version.as_ref() {
406 if is_blacklisted_lldb_version(&lldb_version) {
408 "WARNING: The used version of LLDB ({}) has a \
409 known issue that breaks debuginfo tests. See \
410 issue #32520 for more information. Skipping all \
418 // Some older versions of LLDB seem to have problems with multiple
419 // instances running in parallel, so only run one test thread at a
421 env::set_var("RUST_TEST_THREADS", "1");
423 Some(Config { debugger: Some(Debugger::Lldb), ..config.clone() })
426 pub fn test_opts(config: &Config) -> test::TestOpts {
428 exclude_should_panic: false,
429 filter: config.filter.clone(),
430 filter_exact: config.filter_exact,
431 run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
432 format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
433 logfile: config.logfile.clone(),
435 bench_benchmarks: true,
436 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
437 Ok(val) => &val != "0",
444 options: test::Options::new(),
446 force_run_in_process: false,
450 pub fn make_tests(config: &Config, tests: &mut Vec<test::TestDescAndFn>) {
451 debug!("making tests from {:?}", config.src_base.display());
452 let inputs = common_inputs_stamp(config);
453 collect_tests_from_dir(
461 .expect(&format!("Could not read tests from {}", config.src_base.display()));
464 /// Returns a stamp constructed from input files common to all test cases.
465 fn common_inputs_stamp(config: &Config) -> Stamp {
466 let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
468 let mut stamp = Stamp::from_path(&config.rustc_path);
470 // Relevant pretty printer files
471 let pretty_printer_files = [
472 "src/etc/debugger_pretty_printers_common.py",
473 "src/etc/gdb_load_rust_pretty_printers.py",
474 "src/etc/gdb_rust_pretty_printing.py",
475 "src/etc/lldb_batchmode.py",
476 "src/etc/lldb_rust_formatters.py",
478 for file in &pretty_printer_files {
479 let path = rust_src_dir.join(file);
480 stamp.add_path(&path);
483 stamp.add_dir(&config.run_lib_path);
485 if let Some(ref rustdoc_path) = config.rustdoc_path {
486 stamp.add_path(&rustdoc_path);
487 stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
490 // Compiletest itself.
491 stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
496 fn collect_tests_from_dir(
500 relative_dir_path: &Path,
502 tests: &mut Vec<test::TestDescAndFn>,
503 ) -> io::Result<()> {
504 // Ignore directories that contain a file named `compiletest-ignore-dir`.
505 if dir.join("compiletest-ignore-dir").exists() {
509 if config.mode == Mode::RunMake && dir.join("Makefile").exists() {
510 let paths = TestPaths {
511 file: dir.to_path_buf(),
512 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
514 tests.extend(make_test(config, &paths, inputs));
518 // If we find a test foo/bar.rs, we have to build the
519 // output directory `$build/foo` so we can write
520 // `$build/foo/bar` into it. We do this *now* in this
521 // sequential loop because otherwise, if we do it in the
522 // tests themselves, they race for the privilege of
523 // creating the directories and sometimes fail randomly.
524 let build_dir = output_relative_path(config, relative_dir_path);
525 fs::create_dir_all(&build_dir).unwrap();
527 // Add each `.rs` file as a test, and recurse further on any
528 // subdirectories we find, except for `aux` directories.
529 for file in fs::read_dir(dir)? {
531 let file_path = file.path();
532 let file_name = file.file_name();
533 if is_test(&file_name) {
534 debug!("found test file: {:?}", file_path.display());
536 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
537 tests.extend(make_test(config, &paths, inputs))
538 } else if file_path.is_dir() {
539 let relative_file_path = relative_dir_path.join(file.file_name());
540 if &file_name != "auxiliary" {
541 debug!("found directory: {:?}", file_path.display());
542 collect_tests_from_dir(
552 debug!("found other file/directory: {:?}", file_path.display());
558 /// Returns true if `file_name` looks like a proper test file name.
559 pub fn is_test(file_name: &OsString) -> bool {
560 let file_name = file_name.to_str().unwrap();
562 if !file_name.ends_with(".rs") {
566 // `.`, `#`, and `~` are common temp-file prefixes.
567 let invalid_prefixes = &[".", "#", "~"];
568 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
571 fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec<test::TestDescAndFn> {
572 let early_props = if config.mode == Mode::RunMake {
573 // Allow `ignore` directives to be in the Makefile.
574 EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
576 EarlyProps::from_file(config, &testpaths.file)
579 // The `should-fail` annotation doesn't apply to pretty tests,
580 // since we run the pretty printer across all tests by default.
581 // If desired, we could add a `should-fail-pretty` annotation.
582 let should_panic = match config.mode {
583 Pretty => test::ShouldPanic::No,
585 if early_props.should_fail {
586 test::ShouldPanic::Yes
588 test::ShouldPanic::No
593 // Incremental tests are special, they inherently cannot be run in parallel.
594 // `runtest::run` will be responsible for iterating over revisions.
595 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
598 early_props.revisions.iter().map(|r| Some(r)).collect()
603 let ignore = early_props.ignore
604 // Ignore tests that already run and are up to date with respect to inputs.
609 revision.map(|s| s.as_str()),
612 test::TestDescAndFn {
613 desc: test::TestDesc {
614 name: make_test_name(config, testpaths, revision),
618 test_type: test::TestType::Unknown,
620 testfn: make_test_closure(config, testpaths, revision),
626 fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
627 output_base_dir(config, testpaths, revision).join("stamp")
632 testpaths: &TestPaths,
634 revision: Option<&str>,
637 let stamp_name = stamp(config, testpaths, revision);
639 let contents = match fs::read_to_string(&stamp_name) {
641 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
642 Err(_) => return false,
644 let expected_hash = runtest::compute_stamp_hash(config);
645 if contents != expected_hash {
650 let mut inputs = inputs.clone();
651 // Use `add_dir` to account for run-make tests, which use their individual directory
652 inputs.add_dir(&testpaths.file);
654 for aux in &props.aux {
655 let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
656 inputs.add_path(&path);
660 for extension in UI_EXTENSIONS {
661 let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
662 inputs.add_path(path);
665 inputs < Stamp::from_path(&stamp_name)
668 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
674 fn from_path(path: &Path) -> Self {
675 let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
676 stamp.add_path(path);
680 fn add_path(&mut self, path: &Path) {
681 let modified = fs::metadata(path)
682 .and_then(|metadata| metadata.modified())
683 .unwrap_or(SystemTime::UNIX_EPOCH);
684 self.time = self.time.max(modified);
687 fn add_dir(&mut self, path: &Path) {
688 for entry in WalkDir::new(path) {
689 let entry = entry.unwrap();
690 if entry.file_type().is_file() {
694 .and_then(|metadata| metadata.modified().ok())
695 .unwrap_or(SystemTime::UNIX_EPOCH);
696 self.time = self.time.max(modified);
704 testpaths: &TestPaths,
705 revision: Option<&String>,
706 ) -> test::TestName {
707 // Convert a complete path to something like
710 let path = PathBuf::from(config.src_base.file_name().unwrap())
711 .join(&testpaths.relative_dir)
712 .join(&testpaths.file.file_name().unwrap());
713 let debugger = match config.debugger {
714 Some(d) => format!("-{}", d),
715 None => String::new(),
717 let mode_suffix = match config.compare_mode {
718 Some(ref mode) => format!(" ({})", mode.to_str()),
719 None => String::new(),
722 test::DynTestName(format!(
728 revision.map_or("".to_string(), |rev| format!("#{}", rev))
732 fn make_test_closure(
734 testpaths: &TestPaths,
735 revision: Option<&String>,
737 let config = config.clone();
738 let testpaths = testpaths.clone();
739 let revision = revision.cloned();
740 test::DynTestFn(Box::new(move || {
741 runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str()))
745 /// Returns `true` if the given target is an Android target for the
746 /// purposes of GDB testing.
747 fn is_android_gdb_target(target: &String) -> bool {
749 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => true,
754 /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
755 fn is_pc_windows_msvc_target(target: &String) -> bool {
756 target.ends_with("-pc-windows-msvc")
759 fn find_cdb(target: &String) -> Option<OsString> {
760 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
764 let pf86 = env::var_os("ProgramFiles(x86)").or(env::var_os("ProgramFiles"))?;
765 let cdb_arch = if cfg!(target_arch = "x86") {
767 } else if cfg!(target_arch = "x86_64") {
769 } else if cfg!(target_arch = "aarch64") {
771 } else if cfg!(target_arch = "arm") {
774 return None; // No compatible CDB.exe in the Windows 10 SDK
777 let mut path = PathBuf::new();
779 path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
781 path.push(r"cdb.exe");
787 Some(path.into_os_string())
790 /// Returns Path to CDB
791 fn analyze_cdb(cdb: Option<String>, target: &String) -> Option<OsString> {
792 cdb.map(|s| OsString::from(s)).or(find_cdb(target))
795 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
799 android_cross_path: &PathBuf,
800 ) -> (Option<String>, Option<u32>, bool) {
802 const GDB_FALLBACK: &str = "gdb";
804 const GDB_FALLBACK: &str = "gdb.exe";
806 const MIN_GDB_WITH_RUST: u32 = 7011010;
808 let fallback_gdb = || {
809 if is_android_gdb_target(target) {
810 let mut gdb_path = match android_cross_path.to_str() {
811 Some(x) => x.to_owned(),
812 None => panic!("cannot find android cross path"),
814 gdb_path.push_str("/bin/gdb");
817 GDB_FALLBACK.to_owned()
821 let gdb = match gdb {
822 None => fallback_gdb(),
823 Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
824 Some(ref s) => s.to_owned(),
827 let mut version_line = None;
828 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
829 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
830 version_line = Some(first_line.to_string());
834 let version = match version_line {
835 Some(line) => extract_gdb_version(&line),
836 None => return (None, None, false),
839 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
841 (Some(gdb), version, gdb_native_rust)
844 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
845 let full_version_line = full_version_line.trim();
847 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
848 // of the ? sections being optional
850 // We will parse up to 3 digits for minor and patch, ignoring the date
851 // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
853 // don't start parsing in the middle of a number
854 let mut prev_was_digit = false;
855 for (pos, c) in full_version_line.char_indices() {
856 if prev_was_digit || !c.is_digit(10) {
857 prev_was_digit = c.is_digit(10);
861 prev_was_digit = true;
863 let line = &full_version_line[pos..];
865 let next_split = match line.find(|c: char| !c.is_digit(10)) {
867 None => continue, // no minor version
870 if line.as_bytes()[next_split] != b'.' {
871 continue; // no minor version
874 let major = &line[..next_split];
875 let line = &line[next_split + 1..];
877 let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
879 if line.as_bytes()[idx] == b'.' {
880 let patch = &line[idx + 1..];
883 patch.find(|c: char| !c.is_digit(10)).unwrap_or_else(|| patch.len());
884 let patch = &patch[..patch_len];
885 let patch = if patch_len > 3 || patch_len == 0 { None } else { Some(patch) };
887 (&line[..idx], patch)
892 None => (line, None),
895 if major.len() != 1 || minor.is_empty() {
899 let major: u32 = major.parse().unwrap();
900 let minor: u32 = minor.parse().unwrap();
901 let patch: u32 = patch.unwrap_or("0").parse().unwrap();
903 return Some(((major * 1000) + minor) * 1000 + patch);
909 /// Returns (LLDB version, LLDB is rust-enabled)
910 fn extract_lldb_version(full_version_line: Option<String>) -> (Option<String>, bool) {
911 // Extract the major LLDB version from the given version string.
912 // LLDB version strings are different for Apple and non-Apple platforms.
913 // The Apple variant looks like this:
915 // LLDB-179.5 (older versions)
916 // lldb-300.2.51 (new versions)
918 // We are only interested in the major version number, so this function
919 // will return `Some("179")` and `Some("300")` respectively.
921 // Upstream versions look like:
922 // lldb version 6.0.1
924 // There doesn't seem to be a way to correlate the Apple version
925 // with the upstream version, and since the tests were originally
926 // written against Apple versions, we make a fake Apple version by
927 // multiplying the first number by 100. This is a hack, but
928 // normally fine because the only non-Apple version we test is
931 if let Some(ref full_version_line) = full_version_line {
932 if !full_version_line.trim().is_empty() {
933 let full_version_line = full_version_line.trim();
935 for (pos, l) in full_version_line.char_indices() {
936 if l != 'l' && l != 'L' {
939 if pos + 5 >= full_version_line.len() {
942 let l = full_version_line[pos + 1..].chars().next().unwrap();
943 if l != 'l' && l != 'L' {
946 let d = full_version_line[pos + 2..].chars().next().unwrap();
947 if d != 'd' && d != 'D' {
950 let b = full_version_line[pos + 3..].chars().next().unwrap();
951 if b != 'b' && b != 'B' {
954 let dash = full_version_line[pos + 4..].chars().next().unwrap();
959 let vers = full_version_line[pos + 5..]
961 .take_while(|c| c.is_digit(10))
962 .collect::<String>();
963 if !vers.is_empty() {
964 return (Some(vers), full_version_line.contains("rust-enabled"));
968 if full_version_line.starts_with("lldb version ") {
969 let vers = full_version_line[13..]
971 .take_while(|c| c.is_digit(10))
972 .collect::<String>();
973 if !vers.is_empty() {
974 return (Some(vers + "00"), full_version_line.contains("rust-enabled"));
982 fn is_blacklisted_lldb_version(version: &str) -> bool {