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, PassMode};
12 use crate::common::{Config, TestPaths};
13 use crate::common::{DebugInfoCdb, DebugInfoGdb, DebugInfoGdbLldb, DebugInfoLldb, Mode, Pretty};
14 use crate::util::logv;
20 use std::ffi::OsString;
22 use std::io::{self, ErrorKind};
23 use std::path::{Path, PathBuf};
24 use std::process::Command;
25 use std::time::SystemTime;
26 use test::ColorConfig;
29 use self::header::{EarlyProps, Ignore};
46 let config = parse_config(env::args().collect());
48 if config.valgrind_path.is_none() && config.force_valgrind {
49 panic!("Can't find Valgrind to run Valgrind tests");
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 .reqopt("", "lldb-python", "path to python to use for doc tests", "PATH")
63 .reqopt("", "docck-python", "path to python 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 "(compile-fail|run-fail|run-pass-valgrind|pretty|debug-info|incremental|mir-opt)",
80 "force {check,build,run}-pass tests to this mode.",
81 "check | build | run",
83 .optflag("", "ignored", "run tests marked as ignored")
84 .optflag("", "exact", "filters match exactly")
88 "supervisor program to run tests under \
89 (eg. emulator, valgrind)",
92 .optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
93 .optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
94 .optflag("", "verbose", "run tests verbosely, showing all output")
98 "overwrite stderr/stdout files instead of complaining about a mismatch",
100 .optflag("", "quiet", "print one character per test instead of one line")
101 .optopt("", "color", "coloring: auto, always, never", "WHEN")
102 .optopt("", "logfile", "file to log test execution to", "FILE")
103 .optopt("", "target", "the target to build for", "TARGET")
104 .optopt("", "host", "the host to build for", "HOST")
105 .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
106 .optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH")
107 .optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING")
108 .optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING")
109 .optflag("", "system-llvm", "is LLVM the system LLVM")
110 .optopt("", "android-cross-path", "Android NDK standalone path", "PATH")
111 .optopt("", "adb-path", "path to the android debugger", "PATH")
112 .optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH")
113 .optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH")
114 .reqopt("", "cc", "path to a C compiler", "PATH")
115 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
116 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
117 .optopt("", "ar", "path to an archiver", "PATH")
118 .optopt("", "linker", "path to a linker", "PATH")
119 .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
120 .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS")
121 .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
122 .optopt("", "nodejs", "the name of nodejs", "PATH")
123 .optopt("", "remote-test-client", "path to the remote test client", "PATH")
127 "mode describing what file the actual ui output will be compared to",
133 "enable this to generate a Rustfix coverage file, which is saved in \
134 `./<build_base>/rustfix_missing_coverage.txt`",
136 .optflag("h", "help", "show this message");
138 let (argv0, args_) = args.split_first().unwrap();
139 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
140 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
141 println!("{}", opts.usage(&message));
146 let matches = &match opts.parse(args_) {
148 Err(f) => panic!("{:?}", f),
151 if matches.opt_present("h") || matches.opt_present("help") {
152 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
153 println!("{}", opts.usage(&message));
158 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
159 match m.opt_str(nm) {
160 Some(s) => PathBuf::from(&s),
161 None => panic!("no option (=path) found for {}", nm),
165 fn make_absolute(path: PathBuf) -> PathBuf {
166 if path.is_relative() { env::current_dir().unwrap().join(path) } else { path }
169 let target = opt_str2(matches.opt_str("target"));
170 let android_cross_path = opt_path(matches, "android-cross-path");
171 let cdb = analyze_cdb(matches.opt_str("cdb"), &target);
172 let (gdb, gdb_version, gdb_native_rust) =
173 analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
174 let (lldb_version, lldb_native_rust) = extract_lldb_version(matches.opt_str("lldb-version"));
176 let color = match matches.opt_str("color").as_ref().map(|x| &**x) {
177 Some("auto") | None => ColorConfig::AutoColor,
178 Some("always") => ColorConfig::AlwaysColor,
179 Some("never") => ColorConfig::NeverColor,
180 Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x),
183 let src_base = opt_path(matches, "src-base");
184 let run_ignored = matches.opt_present("ignored");
186 bless: matches.opt_present("bless"),
187 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
188 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
189 rustc_path: opt_path(matches, "rustc-path"),
190 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
191 lldb_python: matches.opt_str("lldb-python").unwrap(),
192 docck_python: matches.opt_str("docck-python").unwrap(),
193 valgrind_path: matches.opt_str("valgrind-path"),
194 force_valgrind: matches.opt_present("force-valgrind"),
195 run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
196 llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
197 llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
199 build_base: opt_path(matches, "build-base"),
200 stage_id: matches.opt_str("stage-id").unwrap(),
201 mode: matches.opt_str("mode").unwrap().parse().expect("invalid mode"),
203 filter: matches.free.first().cloned(),
204 filter_exact: matches.opt_present("exact"),
205 force_pass_mode: matches.opt_str("pass").map(|mode| {
206 mode.parse::<PassMode>()
207 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
209 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
210 runtool: matches.opt_str("runtool"),
211 host_rustcflags: matches.opt_str("host-rustcflags"),
212 target_rustcflags: matches.opt_str("target-rustcflags"),
214 host: opt_str2(matches.opt_str("host")),
221 llvm_version: matches.opt_str("llvm-version"),
222 system_llvm: matches.opt_present("system-llvm"),
224 adb_path: opt_str2(matches.opt_str("adb-path")),
225 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
226 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
227 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
228 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
229 lldb_python_dir: matches.opt_str("lldb-python-dir"),
230 verbose: matches.opt_present("verbose"),
231 quiet: matches.opt_present("quiet"),
233 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
234 compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
235 rustfix_coverage: matches.opt_present("rustfix-coverage"),
237 cc: matches.opt_str("cc").unwrap(),
238 cxx: matches.opt_str("cxx").unwrap(),
239 cflags: matches.opt_str("cflags").unwrap(),
240 ar: matches.opt_str("ar").unwrap_or("ar".into()),
241 linker: matches.opt_str("linker"),
242 llvm_components: matches.opt_str("llvm-components").unwrap(),
243 llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
244 nodejs: matches.opt_str("nodejs"),
248 pub fn log_config(config: &Config) {
250 logv(c, "configuration:".to_string());
251 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
252 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
253 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
254 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
255 logv(c, format!("src_base: {:?}", config.src_base.display()));
256 logv(c, format!("build_base: {:?}", config.build_base.display()));
257 logv(c, format!("stage_id: {}", config.stage_id));
258 logv(c, format!("mode: {}", config.mode));
259 logv(c, format!("run_ignored: {}", config.run_ignored));
260 logv(c, format!("filter: {}", opt_str(&config.filter.as_ref().map(|re| re.to_owned()))));
261 logv(c, format!("filter_exact: {}", config.filter_exact));
264 format!("force_pass_mode: {}", opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),),
266 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
267 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
268 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
269 logv(c, format!("target: {}", config.target));
270 logv(c, format!("host: {}", config.host));
271 logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display()));
272 logv(c, format!("adb_path: {:?}", config.adb_path));
273 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
274 logv(c, format!("adb_device_status: {}", config.adb_device_status));
275 logv(c, format!("ar: {}", config.ar));
276 logv(c, format!("linker: {:?}", config.linker));
277 logv(c, format!("verbose: {}", config.verbose));
278 logv(c, format!("quiet: {}", config.quiet));
279 logv(c, "\n".to_string());
282 pub fn opt_str(maybestr: &Option<String>) -> &str {
289 pub fn opt_str2(maybestr: Option<String>) -> String {
291 None => "(none)".to_owned(),
296 pub fn run_tests(config: &Config) {
297 if config.target.contains("android") {
298 if config.mode == DebugInfoGdb || config.mode == DebugInfoGdbLldb {
300 "{} debug-info test uses tcp 5039 port.\
305 // android debug-info test uses remote debugger so, we test 1 thread
306 // at once as they're all sharing the same TCP port to communicate
309 // we should figure out how to lift this restriction! (run them all
310 // on different ports allocated dynamically).
311 env::set_var("RUST_TEST_THREADS", "1");
316 // Note that we don't need to emit the gdb warning when
317 // DebugInfoGdbLldb, so it is ok to list that here.
318 DebugInfoGdbLldb | DebugInfoLldb => {
319 if let Some(lldb_version) = config.lldb_version.as_ref() {
320 if is_blacklisted_lldb_version(&lldb_version[..]) {
322 "WARNING: The used version of LLDB ({}) has a \
323 known issue that breaks debuginfo tests. See \
324 issue #32520 for more information. Skipping all \
332 // Some older versions of LLDB seem to have problems with multiple
333 // instances running in parallel, so only run one test thread at a
335 env::set_var("RUST_TEST_THREADS", "1");
339 if config.remote_test_client.is_some() && !config.target.contains("android") {
341 "WARNING: debuginfo tests are not available when \
348 DebugInfoCdb | _ => { /* proceed */ }
351 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
352 if let Mode::CodegenUnits = config.mode {
353 let _ = fs::remove_dir_all("tmp/partitioning-tests");
356 // If we want to collect rustfix coverage information,
357 // we first make sure that the coverage file does not exist.
358 // It will be created later on.
359 if config.rustfix_coverage {
360 let mut coverage_file_path = config.build_base.clone();
361 coverage_file_path.push("rustfix_missing_coverage.txt");
362 if coverage_file_path.exists() {
363 if let Err(e) = fs::remove_file(&coverage_file_path) {
364 panic!("Could not delete {} due to {}", coverage_file_path.display(), e)
369 let opts = test_opts(config);
370 let tests = make_tests(config);
371 // sadly osx needs some file descriptor limits raised for running tests in
372 // parallel (especially when we have lots and lots of child processes).
373 // For context, see #8904
375 raise_fd_limit::raise_fd_limit();
377 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
378 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
379 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
381 // Let tests know which target they're running as
382 env::set_var("TARGET", &config.target);
384 let res = test::run_tests_console(&opts, tests);
387 Ok(false) => panic!("Some tests failed"),
389 println!("I/O failure during tests: {:?}", e);
394 pub fn test_opts(config: &Config) -> test::TestOpts {
396 exclude_should_panic: false,
397 filter: config.filter.clone(),
398 filter_exact: config.filter_exact,
399 run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
400 format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
401 logfile: config.logfile.clone(),
403 bench_benchmarks: true,
404 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
405 Ok(val) => &val != "0",
412 options: test::Options::new(),
414 force_run_in_process: false,
418 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
419 debug!("making tests from {:?}", config.src_base.display());
420 let inputs = common_inputs_stamp(config);
421 let mut tests = Vec::new();
422 collect_tests_from_dir(
430 .expect(&format!("Could not read tests from {}", config.src_base.display()));
434 /// Returns a stamp constructed from input files common to all test cases.
435 fn common_inputs_stamp(config: &Config) -> Stamp {
436 let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
438 let mut stamp = Stamp::from_path(&config.rustc_path);
440 // Relevant pretty printer files
441 let pretty_printer_files = [
442 "src/etc/debugger_pretty_printers_common.py",
443 "src/etc/gdb_load_rust_pretty_printers.py",
444 "src/etc/gdb_rust_pretty_printing.py",
445 "src/etc/lldb_batchmode.py",
446 "src/etc/lldb_rust_formatters.py",
448 for file in &pretty_printer_files {
449 let path = rust_src_dir.join(file);
450 stamp.add_path(&path);
453 stamp.add_dir(&config.run_lib_path);
455 if let Some(ref rustdoc_path) = config.rustdoc_path {
456 stamp.add_path(&rustdoc_path);
457 stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
460 // Compiletest itself.
461 stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
466 fn collect_tests_from_dir(
470 relative_dir_path: &Path,
472 tests: &mut Vec<test::TestDescAndFn>,
473 ) -> io::Result<()> {
474 // Ignore directories that contain a file named `compiletest-ignore-dir`.
475 if dir.join("compiletest-ignore-dir").exists() {
479 if config.mode == Mode::RunMake && dir.join("Makefile").exists() {
480 let paths = TestPaths {
481 file: dir.to_path_buf(),
482 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
484 tests.extend(make_test(config, &paths, inputs));
488 // If we find a test foo/bar.rs, we have to build the
489 // output directory `$build/foo` so we can write
490 // `$build/foo/bar` into it. We do this *now* in this
491 // sequential loop because otherwise, if we do it in the
492 // tests themselves, they race for the privilege of
493 // creating the directories and sometimes fail randomly.
494 let build_dir = output_relative_path(config, relative_dir_path);
495 fs::create_dir_all(&build_dir).unwrap();
497 // Add each `.rs` file as a test, and recurse further on any
498 // subdirectories we find, except for `aux` directories.
499 for file in fs::read_dir(dir)? {
501 let file_path = file.path();
502 let file_name = file.file_name();
503 if is_test(&file_name) {
504 debug!("found test file: {:?}", file_path.display());
506 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
507 tests.extend(make_test(config, &paths, inputs))
508 } else if file_path.is_dir() {
509 let relative_file_path = relative_dir_path.join(file.file_name());
510 if &file_name != "auxiliary" {
511 debug!("found directory: {:?}", file_path.display());
512 collect_tests_from_dir(
522 debug!("found other file/directory: {:?}", file_path.display());
528 /// Returns true if `file_name` looks like a proper test file name.
529 pub fn is_test(file_name: &OsString) -> bool {
530 let file_name = file_name.to_str().unwrap();
532 if !file_name.ends_with(".rs") {
536 // `.`, `#`, and `~` are common temp-file prefixes.
537 let invalid_prefixes = &[".", "#", "~"];
538 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
541 fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec<test::TestDescAndFn> {
542 let early_props = if config.mode == Mode::RunMake {
543 // Allow `ignore` directives to be in the Makefile.
544 EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
546 EarlyProps::from_file(config, &testpaths.file)
549 // The `should-fail` annotation doesn't apply to pretty tests,
550 // since we run the pretty printer across all tests by default.
551 // If desired, we could add a `should-fail-pretty` annotation.
552 let should_panic = match config.mode {
553 Pretty => test::ShouldPanic::No,
555 if early_props.should_fail {
556 test::ShouldPanic::Yes
558 test::ShouldPanic::No
563 // Incremental tests are special, they inherently cannot be run in parallel.
564 // `runtest::run` will be responsible for iterating over revisions.
565 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
568 early_props.revisions.iter().map(|r| Some(r)).collect()
573 let ignore = early_props.ignore == Ignore::Ignore
574 // Debugging emscripten code doesn't make sense today
575 || ((config.mode == DebugInfoGdbLldb || config.mode == DebugInfoCdb ||
576 config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
577 && config.target.contains("emscripten"))
578 || (config.mode == DebugInfoGdb && !early_props.ignore.can_run_gdb())
579 || (config.mode == DebugInfoLldb && !early_props.ignore.can_run_lldb())
580 // Ignore tests that already run and are up to date with respect to inputs.
585 revision.map(|s| s.as_str()),
588 test::TestDescAndFn {
589 desc: test::TestDesc {
590 name: make_test_name(config, testpaths, revision),
594 test_type: test::TestType::Unknown,
596 testfn: make_test_closure(config, early_props.ignore, testpaths, revision),
602 fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
603 output_base_dir(config, testpaths, revision).join("stamp")
608 testpaths: &TestPaths,
610 revision: Option<&str>,
613 let stamp_name = stamp(config, testpaths, revision);
615 let contents = match fs::read_to_string(&stamp_name) {
617 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
618 Err(_) => return false,
620 let expected_hash = runtest::compute_stamp_hash(config);
621 if contents != expected_hash {
626 let mut inputs = inputs.clone();
627 inputs.add_path(&testpaths.file);
629 for aux in &props.aux {
630 let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
631 inputs.add_path(&path);
635 for extension in UI_EXTENSIONS {
636 let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
637 inputs.add_path(path);
640 inputs < Stamp::from_path(&stamp_name)
643 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
649 fn from_path(path: &Path) -> Self {
650 let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
651 stamp.add_path(path);
655 fn add_path(&mut self, path: &Path) {
656 let modified = fs::metadata(path)
657 .and_then(|metadata| metadata.modified())
658 .unwrap_or(SystemTime::UNIX_EPOCH);
659 self.time = self.time.max(modified);
662 fn add_dir(&mut self, path: &Path) {
663 for entry in WalkDir::new(path) {
664 let entry = entry.unwrap();
665 if entry.file_type().is_file() {
669 .and_then(|metadata| metadata.modified().ok())
670 .unwrap_or(SystemTime::UNIX_EPOCH);
671 self.time = self.time.max(modified);
679 testpaths: &TestPaths,
680 revision: Option<&String>,
681 ) -> test::TestName {
682 // Convert a complete path to something like
685 let path = PathBuf::from(config.src_base.file_name().unwrap())
686 .join(&testpaths.relative_dir)
687 .join(&testpaths.file.file_name().unwrap());
688 let mode_suffix = match config.compare_mode {
689 Some(ref mode) => format!(" ({})", mode.to_str()),
690 None => String::new(),
692 test::DynTestName(format!(
697 revision.map_or("".to_string(), |rev| format!("#{}", rev))
701 fn make_test_closure(
704 testpaths: &TestPaths,
705 revision: Option<&String>,
707 let mut config = config.clone();
708 if config.mode == DebugInfoGdbLldb {
709 // If both gdb and lldb were ignored, then the test as a whole
711 if !ignore.can_run_gdb() {
712 config.mode = DebugInfoLldb;
713 } else if !ignore.can_run_lldb() {
714 config.mode = DebugInfoGdb;
718 let testpaths = testpaths.clone();
719 let revision = revision.cloned();
720 test::DynTestFn(Box::new(move || {
721 runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str()))
725 /// Returns `true` if the given target is an Android target for the
726 /// purposes of GDB testing.
727 fn is_android_gdb_target(target: &String) -> bool {
729 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => true,
734 /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
735 fn is_pc_windows_msvc_target(target: &String) -> bool {
736 target.ends_with("-pc-windows-msvc")
739 fn find_cdb(target: &String) -> Option<OsString> {
740 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
744 let pf86 = env::var_os("ProgramFiles(x86)").or(env::var_os("ProgramFiles"))?;
745 let cdb_arch = if cfg!(target_arch = "x86") {
747 } else if cfg!(target_arch = "x86_64") {
749 } else if cfg!(target_arch = "aarch64") {
751 } else if cfg!(target_arch = "arm") {
754 return None; // No compatible CDB.exe in the Windows 10 SDK
757 let mut path = PathBuf::new();
759 path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
761 path.push(r"cdb.exe");
767 Some(path.into_os_string())
770 /// Returns Path to CDB
771 fn analyze_cdb(cdb: Option<String>, target: &String) -> Option<OsString> {
772 cdb.map(|s| OsString::from(s)).or(find_cdb(target))
775 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
779 android_cross_path: &PathBuf,
780 ) -> (Option<String>, Option<u32>, bool) {
782 const GDB_FALLBACK: &str = "gdb";
784 const GDB_FALLBACK: &str = "gdb.exe";
786 const MIN_GDB_WITH_RUST: u32 = 7011010;
788 let fallback_gdb = || {
789 if is_android_gdb_target(target) {
790 let mut gdb_path = match android_cross_path.to_str() {
791 Some(x) => x.to_owned(),
792 None => panic!("cannot find android cross path"),
794 gdb_path.push_str("/bin/gdb");
797 GDB_FALLBACK.to_owned()
801 let gdb = match gdb {
802 None => fallback_gdb(),
803 Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
804 Some(ref s) => s.to_owned(),
807 let mut version_line = None;
808 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
809 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
810 version_line = Some(first_line.to_string());
814 let version = match version_line {
815 Some(line) => extract_gdb_version(&line),
816 None => return (None, None, false),
819 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
821 (Some(gdb), version, gdb_native_rust)
824 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
825 let full_version_line = full_version_line.trim();
827 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
828 // of the ? sections being optional
830 // We will parse up to 3 digits for minor and patch, ignoring the date
831 // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
833 // don't start parsing in the middle of a number
834 let mut prev_was_digit = false;
835 for (pos, c) in full_version_line.char_indices() {
836 if prev_was_digit || !c.is_digit(10) {
837 prev_was_digit = c.is_digit(10);
841 prev_was_digit = true;
843 let line = &full_version_line[pos..];
845 let next_split = match line.find(|c: char| !c.is_digit(10)) {
847 None => continue, // no minor version
850 if line.as_bytes()[next_split] != b'.' {
851 continue; // no minor version
854 let major = &line[..next_split];
855 let line = &line[next_split + 1..];
857 let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
859 if line.as_bytes()[idx] == b'.' {
860 let patch = &line[idx + 1..];
863 patch.find(|c: char| !c.is_digit(10)).unwrap_or_else(|| patch.len());
864 let patch = &patch[..patch_len];
865 let patch = if patch_len > 3 || patch_len == 0 { None } else { Some(patch) };
867 (&line[..idx], patch)
872 None => (line, None),
875 if major.len() != 1 || minor.is_empty() {
879 let major: u32 = major.parse().unwrap();
880 let minor: u32 = minor.parse().unwrap();
881 let patch: u32 = patch.unwrap_or("0").parse().unwrap();
883 return Some(((major * 1000) + minor) * 1000 + patch);
889 /// Returns (LLDB version, LLDB is rust-enabled)
890 fn extract_lldb_version(full_version_line: Option<String>) -> (Option<String>, bool) {
891 // Extract the major LLDB version from the given version string.
892 // LLDB version strings are different for Apple and non-Apple platforms.
893 // The Apple variant looks like this:
895 // LLDB-179.5 (older versions)
896 // lldb-300.2.51 (new versions)
898 // We are only interested in the major version number, so this function
899 // will return `Some("179")` and `Some("300")` respectively.
901 // Upstream versions look like:
902 // lldb version 6.0.1
904 // There doesn't seem to be a way to correlate the Apple version
905 // with the upstream version, and since the tests were originally
906 // written against Apple versions, we make a fake Apple version by
907 // multiplying the first number by 100. This is a hack, but
908 // normally fine because the only non-Apple version we test is
911 if let Some(ref full_version_line) = full_version_line {
912 if !full_version_line.trim().is_empty() {
913 let full_version_line = full_version_line.trim();
915 for (pos, l) in full_version_line.char_indices() {
916 if l != 'l' && l != 'L' {
919 if pos + 5 >= full_version_line.len() {
922 let l = full_version_line[pos + 1..].chars().next().unwrap();
923 if l != 'l' && l != 'L' {
926 let d = full_version_line[pos + 2..].chars().next().unwrap();
927 if d != 'd' && d != 'D' {
930 let b = full_version_line[pos + 3..].chars().next().unwrap();
931 if b != 'b' && b != 'B' {
934 let dash = full_version_line[pos + 4..].chars().next().unwrap();
939 let vers = full_version_line[pos + 5..]
941 .take_while(|c| c.is_digit(10))
942 .collect::<String>();
943 if !vers.is_empty() {
944 return (Some(vers), full_version_line.contains("rust-enabled"));
948 if full_version_line.starts_with("lldb version ") {
949 let vers = full_version_line[13..]
951 .take_while(|c| c.is_digit(10))
952 .collect::<String>();
953 if !vers.is_empty() {
954 return (Some(vers + "00"), full_version_line.contains("rust-enabled"));
962 fn is_blacklisted_lldb_version(version: &str) -> bool {