1 #![crate_name = "compiletest"]
3 #![feature(vec_remove_item)]
8 use crate::common
::{CompareMode, PassMode}
;
9 use crate::common
::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS}
;
10 use crate::common
::{Config, TestPaths}
;
11 use crate::common
::{DebugInfoCdb, DebugInfoGdbLldb, DebugInfoGdb, DebugInfoLldb, Mode, Pretty}
;
14 use std
::ffi
::OsString
;
16 use std
::io
::{self, ErrorKind}
;
17 use std
::path
::{Path, PathBuf}
;
18 use std
::process
::Command
;
19 use std
::time
::SystemTime
;
20 use test
::ColorConfig
;
21 use crate::util
::logv
;
27 use self::header
::{EarlyProps, Ignore}
;
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();
59 "path to host shared libraries",
64 "path to target shared libraries",
70 "path to rustc to use for compiling",
76 "path to rustdoc to use for compiling",
82 "path to python to use for doc tests",
88 "path to python to use for doc tests",
94 "path to Valgrind executable for Valgrind tests",
100 "fail if Valgrind tests cannot be run under Valgrind",
104 "run-clang-based-tests-with",
105 "path to Clang executable",
111 "path to LLVM's FileCheck binary",
114 .reqopt("", "src-base", "directory to scan for test files", "PATH")
118 "directory to deposit test outputs",
124 "the target-stage identifier",
130 "which sort of compile tests to run",
131 "(compile-fail|run-fail|run-pass-valgrind|pretty|debug-info|incremental|mir-opt)",
136 "force {check,build,run}-pass tests to this mode.",
137 "check | build | run"
139 .optflag("", "ignored", "run tests marked as ignored")
140 .optflag("", "exact", "filters match exactly")
144 "supervisor program to run tests under \
145 (eg. emulator, valgrind)",
151 "flags to pass to rustc for host",
157 "flags to pass to rustc for target",
160 .optflag("", "verbose", "run tests verbosely, showing all output")
164 "overwrite stderr/stdout files instead of complaining about a mismatch",
169 "print one character per test instead of one line",
171 .optopt("", "color", "coloring: auto, always, never", "WHEN")
172 .optopt("", "logfile", "file to log test execution to", "FILE")
173 .optopt("", "target", "the target to build for", "TARGET")
174 .optopt("", "host", "the host to build for", "HOST")
178 "path to CDB to use for CDB debuginfo tests",
184 "path to GDB to use for GDB debuginfo tests",
190 "the version of LLDB used",
196 "the version of LLVM used",
199 .optflag("", "system-llvm", "is LLVM the system LLVM")
202 "android-cross-path",
203 "Android NDK standalone path",
206 .optopt("", "adb-path", "path to the android debugger", "PATH")
210 "path to tests for the android debugger",
216 "directory containing LLDB's python module",
219 .reqopt("", "cc", "path to a C compiler", "PATH")
220 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
221 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
222 .optopt("", "ar", "path to an archiver", "PATH")
223 .optopt("", "linker", "path to a linker", "PATH")
227 "list of LLVM components built in",
230 .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS")
231 .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
232 .optopt("", "nodejs", "the name of nodejs", "PATH")
235 "remote-test-client",
236 "path to the remote test client",
242 "mode describing what file the actual ui output will be compared to",
248 "enable this to generate a Rustfix coverage file, which is saved in \
249 `./<build_base>/rustfix_missing_coverage.txt`",
251 .optflag("h", "help", "show this message");
253 let (argv0
, args_
) = args
.split_first().unwrap();
254 if args
.len() == 1 || args
[1] == "-h" || args
[1] == "--help" {
255 let message
= format
!("Usage: {} [OPTIONS] [TESTNAME...]", argv0
);
256 println
!("{}", opts
.usage(&message
));
261 let matches
= &match opts
.parse(args_
) {
263 Err(f
) => panic
!("{:?}", f
),
266 if matches
.opt_present("h") || matches
.opt_present("help") {
267 let message
= format
!("Usage: {} [OPTIONS] [TESTNAME...]", argv0
);
268 println
!("{}", opts
.usage(&message
));
273 fn opt_path(m
: &getopts
::Matches
, nm
: &str) -> PathBuf
{
274 match m
.opt_str(nm
) {
275 Some(s
) => PathBuf
::from(&s
),
276 None
=> panic
!("no option (=path) found for {}", nm
),
280 fn make_absolute(path
: PathBuf
) -> PathBuf
{
281 if path
.is_relative() {
282 env
::current_dir().unwrap().join(path
)
288 let target
= opt_str2(matches
.opt_str("target"));
289 let android_cross_path
= opt_path(matches
, "android-cross-path");
290 let cdb
= analyze_cdb(matches
.opt_str("cdb"), &target
);
291 let (gdb
, gdb_version
, gdb_native_rust
) = analyze_gdb(matches
.opt_str("gdb"), &target
,
292 &android_cross_path
);
293 let (lldb_version
, lldb_native_rust
) = extract_lldb_version(matches
.opt_str("lldb-version"));
295 let color
= match matches
.opt_str("color").as_ref().map(|x
| &**x
) {
296 Some("auto") | None
=> ColorConfig
::AutoColor
,
297 Some("always") => ColorConfig
::AlwaysColor
,
298 Some("never") => ColorConfig
::NeverColor
,
300 "argument for --color must be auto, always, or never, but found `{}`",
305 let src_base
= opt_path(matches
, "src-base");
306 let run_ignored
= matches
.opt_present("ignored");
308 bless
: matches
.opt_present("bless"),
309 compile_lib_path
: make_absolute(opt_path(matches
, "compile-lib-path")),
310 run_lib_path
: make_absolute(opt_path(matches
, "run-lib-path")),
311 rustc_path
: opt_path(matches
, "rustc-path"),
312 rustdoc_path
: matches
.opt_str("rustdoc-path").map(PathBuf
::from
),
313 lldb_python
: matches
.opt_str("lldb-python").unwrap(),
314 docck_python
: matches
.opt_str("docck-python").unwrap(),
315 valgrind_path
: matches
.opt_str("valgrind-path"),
316 force_valgrind
: matches
.opt_present("force-valgrind"),
317 run_clang_based_tests_with
: matches
.opt_str("run-clang-based-tests-with"),
318 llvm_filecheck
: matches
.opt_str("llvm-filecheck").map(PathBuf
::from
),
319 llvm_bin_dir
: matches
.opt_str("llvm-bin-dir").map(PathBuf
::from
),
321 build_base
: opt_path(matches
, "build-base"),
322 stage_id
: matches
.opt_str("stage-id").unwrap(),
327 .expect("invalid mode"),
329 filter
: matches
.free
.first().cloned(),
330 filter_exact
: matches
.opt_present("exact"),
331 force_pass_mode
: matches
.opt_str("pass").map(|mode
|
332 mode
.parse
::<PassMode
>()
333 .unwrap_or_else(|_
| panic
!("unknown `--pass` option `{}` given", mode
))
335 logfile
: matches
.opt_str("logfile").map(|s
| PathBuf
::from(&s
)),
336 runtool
: matches
.opt_str("runtool"),
337 host_rustcflags
: matches
.opt_str("host-rustcflags"),
338 target_rustcflags
: matches
.opt_str("target-rustcflags"),
340 host
: opt_str2(matches
.opt_str("host")),
347 llvm_version
: matches
.opt_str("llvm-version"),
348 system_llvm
: matches
.opt_present("system-llvm"),
350 adb_path
: opt_str2(matches
.opt_str("adb-path")),
351 adb_test_dir
: opt_str2(matches
.opt_str("adb-test-dir")),
352 adb_device_status
: opt_str2(matches
.opt_str("target")).contains("android")
353 && "(none)" != opt_str2(matches
.opt_str("adb-test-dir"))
354 && !opt_str2(matches
.opt_str("adb-test-dir")).is_empty(),
355 lldb_python_dir
: matches
.opt_str("lldb-python-dir"),
356 verbose
: matches
.opt_present("verbose"),
357 quiet
: matches
.opt_present("quiet"),
359 remote_test_client
: matches
.opt_str("remote-test-client").map(PathBuf
::from
),
360 compare_mode
: matches
.opt_str("compare-mode").map(CompareMode
::parse
),
361 rustfix_coverage
: matches
.opt_present("rustfix-coverage"),
363 cc
: matches
.opt_str("cc").unwrap(),
364 cxx
: matches
.opt_str("cxx").unwrap(),
365 cflags
: matches
.opt_str("cflags").unwrap(),
366 ar
: matches
.opt_str("ar").unwrap_or("ar".into()),
367 linker
: matches
.opt_str("linker"),
368 llvm_components
: matches
.opt_str("llvm-components").unwrap(),
369 llvm_cxxflags
: matches
.opt_str("llvm-cxxflags").unwrap(),
370 nodejs
: matches
.opt_str("nodejs"),
374 pub fn log_config(config
: &Config
) {
376 logv(c
, "configuration:".to_string());
379 format
!("compile_lib_path: {:?}", config
.compile_lib_path
),
381 logv(c
, format
!("run_lib_path: {:?}", config
.run_lib_path
));
382 logv(c
, format
!("rustc_path: {:?}", config
.rustc_path
.display()));
383 logv(c
, format
!("rustdoc_path: {:?}", config
.rustdoc_path
));
384 logv(c
, format
!("src_base: {:?}", config
.src_base
.display()));
385 logv(c
, format
!("build_base: {:?}", config
.build_base
.display()));
386 logv(c
, format
!("stage_id: {}", config
.stage_id
));
387 logv(c
, format
!("mode: {}", config
.mode
));
388 logv(c
, format
!("run_ignored: {}", config
.run_ignored
));
393 opt_str(&config
.filter
.as_ref().map(|re
| re
.to_owned()))
396 logv(c
, format
!("filter_exact: {}", config
.filter_exact
));
398 "force_pass_mode: {}",
399 opt_str(&config
.force_pass_mode
.map(|m
| format
!("{}", m
))),
401 logv(c
, format
!("runtool: {}", opt_str(&config
.runtool
)));
404 format
!("host-rustcflags: {}", opt_str(&config
.host_rustcflags
)),
408 format
!("target-rustcflags: {}", opt_str(&config
.target_rustcflags
)),
410 logv(c
, format
!("target: {}", config
.target
));
411 logv(c
, format
!("host: {}", config
.host
));
415 "android-cross-path: {:?}",
416 config
.android_cross_path
.display()
419 logv(c
, format
!("adb_path: {:?}", config
.adb_path
));
420 logv(c
, format
!("adb_test_dir: {:?}", config
.adb_test_dir
));
423 format
!("adb_device_status: {}", config
.adb_device_status
),
425 logv(c
, format
!("ar: {}", config
.ar
));
426 logv(c
, format
!("linker: {:?}", config
.linker
));
427 logv(c
, format
!("verbose: {}", config
.verbose
));
428 logv(c
, format
!("quiet: {}", config
.quiet
));
429 logv(c
, "\n".to_string());
432 pub fn opt_str(maybestr
: &Option
<String
>) -> &str {
439 pub fn opt_str2(maybestr
: Option
<String
>) -> String
{
441 None
=> "(none)".to_owned(),
446 pub fn run_tests(config
: &Config
) {
447 if config
.target
.contains("android") {
448 if config
.mode
== DebugInfoGdb
|| config
.mode
== DebugInfoGdbLldb
{
450 "{} debug-info test uses tcp 5039 port.\
455 // android debug-info test uses remote debugger so, we test 1 thread
456 // at once as they're all sharing the same TCP port to communicate
459 // we should figure out how to lift this restriction! (run them all
460 // on different ports allocated dynamically).
461 env
::set_var("RUST_TEST_THREADS", "1");
466 // Note that we don't need to emit the gdb warning when
467 // DebugInfoGdbLldb, so it is ok to list that here.
468 DebugInfoGdbLldb
| DebugInfoLldb
=> {
469 if let Some(lldb_version
) = config
.lldb_version
.as_ref() {
470 if is_blacklisted_lldb_version(&lldb_version
[..]) {
472 "WARNING: The used version of LLDB ({}) has a \
473 known issue that breaks debuginfo tests. See \
474 issue #32520 for more information. Skipping all \
482 // Some older versions of LLDB seem to have problems with multiple
483 // instances running in parallel, so only run one test thread at a
485 env
::set_var("RUST_TEST_THREADS", "1");
489 if config
.remote_test_client
.is_some() && !config
.target
.contains("android") {
491 "WARNING: debuginfo tests are not available when \
498 DebugInfoCdb
| _
=> { /* proceed */ }
501 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
502 if let Mode
::CodegenUnits
= config
.mode
{
503 let _
= fs
::remove_dir_all("tmp/partitioning-tests");
506 // If we want to collect rustfix coverage information,
507 // we first make sure that the coverage file does not exist.
508 // It will be created later on.
509 if config
.rustfix_coverage
{
510 let mut coverage_file_path
= config
.build_base
.clone();
511 coverage_file_path
.push("rustfix_missing_coverage.txt");
512 if coverage_file_path
.exists() {
513 if let Err(e
) = fs
::remove_file(&coverage_file_path
) {
514 panic
!("Could not delete {} due to {}", coverage_file_path
.display(), e
)
519 let opts
= test_opts(config
);
520 let tests
= make_tests(config
);
521 // sadly osx needs some file descriptor limits raised for running tests in
522 // parallel (especially when we have lots and lots of child processes).
523 // For context, see #8904
525 raise_fd_limit
::raise_fd_limit();
527 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
528 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
529 env
::set_var("__COMPAT_LAYER", "RunAsInvoker");
531 // Let tests know which target they're running as
532 env
::set_var("TARGET", &config
.target
);
534 let res
= test
::run_tests_console(&opts
, tests
);
537 Ok(false) => panic
!("Some tests failed"),
539 println
!("I/O failure during tests: {:?}", e
);
544 pub fn test_opts(config
: &Config
) -> test
::TestOpts
{
546 exclude_should_panic
: false,
547 filter
: config
.filter
.clone(),
548 filter_exact
: config
.filter_exact
,
549 run_ignored
: if config
.run_ignored
{
550 test
::RunIgnored
::Yes
554 format
: if config
.quiet
{
555 test
::OutputFormat
::Terse
557 test
::OutputFormat
::Pretty
559 logfile
: config
.logfile
.clone(),
561 bench_benchmarks
: true,
562 nocapture
: match env
::var("RUST_TEST_NOCAPTURE") {
563 Ok(val
) => &val
!= "0",
570 options
: test
::Options
::new(),
574 pub fn make_tests(config
: &Config
) -> Vec
<test
::TestDescAndFn
> {
575 debug
!("making tests from {:?}", config
.src_base
.display());
576 let mut tests
= Vec
::new();
577 collect_tests_from_dir(
587 fn collect_tests_from_dir(
591 relative_dir_path
: &Path
,
592 tests
: &mut Vec
<test
::TestDescAndFn
>,
593 ) -> io
::Result
<()> {
594 // Ignore directories that contain a file named `compiletest-ignore-dir`.
595 if dir
.join("compiletest-ignore-dir").exists() {
599 if config
.mode
== Mode
::RunMake
&& dir
.join("Makefile").exists() {
600 let paths
= TestPaths
{
601 file
: dir
.to_path_buf(),
602 relative_dir
: relative_dir_path
.parent().unwrap().to_path_buf(),
604 tests
.extend(make_test(config
, &paths
));
608 // If we find a test foo/bar.rs, we have to build the
609 // output directory `$build/foo` so we can write
610 // `$build/foo/bar` into it. We do this *now* in this
611 // sequential loop because otherwise, if we do it in the
612 // tests themselves, they race for the privilege of
613 // creating the directories and sometimes fail randomly.
614 let build_dir
= output_relative_path(config
, relative_dir_path
);
615 fs
::create_dir_all(&build_dir
).unwrap();
617 // Add each `.rs` file as a test, and recurse further on any
618 // subdirectories we find, except for `aux` directories.
619 for file
in fs
::read_dir(dir
)?
{
621 let file_path
= file
.path();
622 let file_name
= file
.file_name();
623 if is_test(&file_name
) {
624 debug
!("found test file: {:?}", file_path
.display());
625 let paths
= TestPaths
{
627 relative_dir
: relative_dir_path
.to_path_buf(),
629 tests
.extend(make_test(config
, &paths
))
630 } else if file_path
.is_dir() {
631 let relative_file_path
= relative_dir_path
.join(file
.file_name());
632 if &file_name
!= "auxiliary" {
633 debug
!("found directory: {:?}", file_path
.display());
634 collect_tests_from_dir(config
, base
, &file_path
, &relative_file_path
, tests
)?
;
637 debug
!("found other file/directory: {:?}", file_path
.display());
644 /// Returns true if `file_name` looks like a proper test file name.
645 pub fn is_test(file_name
: &OsString
) -> bool
{
646 let file_name
= file_name
.to_str().unwrap();
648 if !file_name
.ends_with(".rs") {
652 // `.`, `#`, and `~` are common temp-file prefixes.
653 let invalid_prefixes
= &[".", "#", "~"];
654 !invalid_prefixes
.iter().any(|p
| file_name
.starts_with(p
))
657 pub fn make_test(config
: &Config
, testpaths
: &TestPaths
) -> Vec
<test
::TestDescAndFn
> {
658 let early_props
= if config
.mode
== Mode
::RunMake
{
659 // Allow `ignore` directives to be in the Makefile.
660 EarlyProps
::from_file(config
, &testpaths
.file
.join("Makefile"))
662 EarlyProps
::from_file(config
, &testpaths
.file
)
665 // The `should-fail` annotation doesn't apply to pretty tests,
666 // since we run the pretty printer across all tests by default.
667 // If desired, we could add a `should-fail-pretty` annotation.
668 let should_panic
= match config
.mode
{
669 Pretty
=> test
::ShouldPanic
::No
,
670 _
=> if early_props
.should_fail
{
671 test
::ShouldPanic
::Yes
673 test
::ShouldPanic
::No
677 // Incremental tests are special, they inherently cannot be run in parallel.
678 // `runtest::run` will be responsible for iterating over revisions.
679 let revisions
= if early_props
.revisions
.is_empty() || config
.mode
== Mode
::Incremental
{
682 early_props
.revisions
.iter().map(|r
| Some(r
)).collect()
687 // Debugging emscripten code doesn't make sense today
688 let ignore
= early_props
.ignore
== Ignore
::Ignore
693 revision
.map(|s
| s
.as_str()),
695 || ((config
.mode
== DebugInfoGdbLldb
|| config
.mode
== DebugInfoCdb
||
696 config
.mode
== DebugInfoGdb
|| config
.mode
== DebugInfoLldb
)
697 && config
.target
.contains("emscripten"))
698 || (config
.mode
== DebugInfoGdb
&& !early_props
.ignore
.can_run_gdb())
699 || (config
.mode
== DebugInfoLldb
&& !early_props
.ignore
.can_run_lldb());
700 test
::TestDescAndFn
{
701 desc
: test
::TestDesc
{
702 name
: make_test_name(config
, testpaths
, revision
),
707 testfn
: make_test_closure(config
, early_props
.ignore
, testpaths
, revision
),
713 fn stamp(config
: &Config
, testpaths
: &TestPaths
, revision
: Option
<&str>) -> PathBuf
{
714 output_base_dir(config
, testpaths
, revision
).join("stamp")
719 testpaths
: &TestPaths
,
721 revision
: Option
<&str>,
723 let stamp_name
= stamp(config
, testpaths
, revision
);
725 let contents
= match fs
::read_to_string(&stamp_name
) {
727 Err(ref e
) if e
.kind() == ErrorKind
::InvalidData
=> panic
!("Can't read stamp contents"),
728 Err(_
) => return true,
730 let expected_hash
= runtest
::compute_stamp_hash(config
);
731 if contents
!= expected_hash
{
736 let rust_src_dir
= config
737 .find_rust_src_root()
738 .expect("Could not find Rust source root");
739 let stamp
= Stamp
::from_path(&stamp_name
);
740 let mut inputs
= vec
![Stamp
::from_path(&testpaths
.file
), Stamp
::from_path(&config
.rustc_path
)];
746 Stamp
::from_path(&testpaths
.file
.parent().unwrap().join("auxiliary").join(aux
))
749 // Relevant pretty printer files
750 let pretty_printer_files
= [
751 "src/etc/debugger_pretty_printers_common.py",
752 "src/etc/gdb_load_rust_pretty_printers.py",
753 "src/etc/gdb_rust_pretty_printing.py",
754 "src/etc/lldb_batchmode.py",
755 "src/etc/lldb_rust_formatters.py",
757 inputs
.extend(pretty_printer_files
.iter().map(|pretty_printer_file
| {
758 Stamp
::from_path(&rust_src_dir
.join(pretty_printer_file
))
760 inputs
.extend(Stamp
::from_dir(&config
.run_lib_path
));
761 if let Some(ref rustdoc_path
) = config
.rustdoc_path
{
762 inputs
.push(Stamp
::from_path(&rustdoc_path
));
763 inputs
.push(Stamp
::from_path(&rust_src_dir
.join("src/etc/htmldocck.py")));
767 inputs
.extend(UI_EXTENSIONS
.iter().map(|extension
| {
768 let path
= &expected_output_path(testpaths
, revision
, &config
.compare_mode
, extension
);
769 Stamp
::from_path(path
)
772 // Compiletest itself.
773 inputs
.extend(Stamp
::from_dir(&rust_src_dir
.join("src/tools/compiletest/")));
775 inputs
.iter().any(|input
| input
> &stamp
)
778 #[derive(Debug, PartialEq, PartialOrd, Ord, Eq)]
785 fn from_path(p
: &Path
) -> Self {
786 let time
= fs
::metadata(p
)
787 .and_then(|metadata
| metadata
.modified())
788 .unwrap_or(SystemTime
::UNIX_EPOCH
);
796 fn from_dir(path
: &Path
) -> impl Iterator
<Item
= Stamp
> {
799 .map(|entry
| entry
.unwrap())
800 .filter(|entry
| entry
.file_type().is_file())
802 let time
= (|| -> io
::Result
<_
> { entry.metadata()?.modified() }
)();
805 time
: time
.unwrap_or(SystemTime
::UNIX_EPOCH
),
806 file
: entry
.path().into(),
814 testpaths
: &TestPaths
,
815 revision
: Option
<&String
>,
816 ) -> test
::TestName
{
817 // Convert a complete path to something like
820 let path
= PathBuf
::from(config
.src_base
.file_name().unwrap())
821 .join(&testpaths
.relative_dir
)
822 .join(&testpaths
.file
.file_name().unwrap());
823 let mode_suffix
= match config
.compare_mode
{
824 Some(ref mode
) => format
!(" ({})", mode
.to_str()),
825 None
=> String
::new(),
827 test
::DynTestName(format
!(
832 revision
.map_or("".to_string(), |rev
| format
!("#{}", rev
))
836 fn make_test_closure(
839 testpaths
: &TestPaths
,
840 revision
: Option
<&String
>,
842 let mut config
= config
.clone();
843 if config
.mode
== DebugInfoGdbLldb
{
844 // If both gdb and lldb were ignored, then the test as a whole
846 if !ignore
.can_run_gdb() {
847 config
.mode
= DebugInfoLldb
;
848 } else if !ignore
.can_run_lldb() {
849 config
.mode
= DebugInfoGdb
;
853 let testpaths
= testpaths
.clone();
854 let revision
= revision
.cloned();
855 test
::DynTestFn(Box
::new(move || {
856 runtest
::run(config
, &testpaths
, revision
.as_ref().map(|s
| s
.as_str()))
860 /// Returns `true` if the given target is an Android target for the
861 /// purposes of GDB testing.
862 fn is_android_gdb_target(target
: &String
) -> bool
{
864 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => true,
869 /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
870 fn is_pc_windows_msvc_target(target
: &String
) -> bool
{
871 target
.ends_with("-pc-windows-msvc")
874 fn find_cdb(target
: &String
) -> Option
<OsString
> {
875 if !(cfg
!(windows
) && is_pc_windows_msvc_target(target
)) {
879 let pf86
= env
::var_os("ProgramFiles(x86)").or(env
::var_os("ProgramFiles"))?
;
880 let cdb_arch
= if cfg
!(target_arch
="x86") {
882 } else if cfg
!(target_arch
="x86_64") {
884 } else if cfg
!(target_arch
="aarch64") {
886 } else if cfg
!(target_arch
="arm") {
889 return None
; // No compatible CDB.exe in the Windows 10 SDK
892 let mut path
= PathBuf
::new();
894 path
.push(r
"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
896 path
.push(r
"cdb.exe");
902 Some(path
.into_os_string())
905 /// Returns Path to CDB
906 fn analyze_cdb(cdb
: Option
<String
>, target
: &String
) -> Option
<OsString
> {
907 cdb
.map(|s
| OsString
::from(s
)).or(find_cdb(target
))
910 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
911 fn analyze_gdb(gdb
: Option
<String
>, target
: &String
, android_cross_path
: &PathBuf
)
912 -> (Option
<String
>, Option
<u32>, bool
) {
914 const GDB_FALLBACK
: &str = "gdb";
916 const GDB_FALLBACK
: &str = "gdb.exe";
918 const MIN_GDB_WITH_RUST
: u32 = 7011010;
920 let fallback_gdb
= || {
921 if is_android_gdb_target(target
) {
922 let mut gdb_path
= match android_cross_path
.to_str() {
923 Some(x
) => x
.to_owned(),
924 None
=> panic
!("cannot find android cross path"),
926 gdb_path
.push_str("/bin/gdb");
929 GDB_FALLBACK
.to_owned()
933 let gdb
= match gdb
{
934 None
=> fallback_gdb(),
935 Some(ref s
) if s
.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
936 Some(ref s
) => s
.to_owned(),
939 let mut version_line
= None
;
940 if let Ok(output
) = Command
::new(&gdb
).arg("--version").output() {
941 if let Some(first_line
) = String
::from_utf8_lossy(&output
.stdout
).lines().next() {
942 version_line
= Some(first_line
.to_string());
946 let version
= match version_line
{
947 Some(line
) => extract_gdb_version(&line
),
948 None
=> return (None
, None
, false),
951 let gdb_native_rust
= version
.map_or(false, |v
| v
>= MIN_GDB_WITH_RUST
);
953 (Some(gdb
), version
, gdb_native_rust
)
956 fn extract_gdb_version(full_version_line
: &str) -> Option
<u32> {
957 let full_version_line
= full_version_line
.trim();
959 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
960 // of the ? sections being optional
962 // We will parse up to 3 digits for minor and patch, ignoring the date
963 // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
965 // don't start parsing in the middle of a number
966 let mut prev_was_digit
= false;
967 for (pos
, c
) in full_version_line
.char_indices() {
968 if prev_was_digit
|| !c
.is_digit(10) {
969 prev_was_digit
= c
.is_digit(10);
973 prev_was_digit
= true;
975 let line
= &full_version_line
[pos
..];
977 let next_split
= match line
.find(|c
: char| !c
.is_digit(10)) {
979 None
=> continue, // no minor version
982 if line
.as_bytes()[next_split
] != b'
.'
{
983 continue; // no minor version
986 let major
= &line
[..next_split
];
987 let line
= &line
[next_split
+ 1..];
989 let (minor
, patch
) = match line
.find(|c
: char| !c
.is_digit(10)) {
990 Some(idx
) => if line
.as_bytes()[idx
] == b'
.'
{
991 let patch
= &line
[idx
+ 1..];
993 let patch_len
= patch
994 .find(|c
: char| !c
.is_digit(10))
995 .unwrap_or_else(|| patch
.len());
996 let patch
= &patch
[..patch_len
];
997 let patch
= if patch_len
> 3 || patch_len
== 0 {
1003 (&line
[..idx
], patch
)
1005 (&line
[..idx
], None
)
1007 None
=> (line
, None
),
1010 if major
.len() != 1 || minor
.is_empty() {
1014 let major
: u32 = major
.parse().unwrap();
1015 let minor
: u32 = minor
.parse().unwrap();
1016 let patch
: u32 = patch
.unwrap_or("0").parse().unwrap();
1018 return Some(((major
* 1000) + minor
) * 1000 + patch
);
1024 /// Returns (LLDB version, LLDB is rust-enabled)
1025 fn extract_lldb_version(full_version_line
: Option
<String
>) -> (Option
<String
>, bool
) {
1026 // Extract the major LLDB version from the given version string.
1027 // LLDB version strings are different for Apple and non-Apple platforms.
1028 // The Apple variant looks like this:
1030 // LLDB-179.5 (older versions)
1031 // lldb-300.2.51 (new versions)
1033 // We are only interested in the major version number, so this function
1034 // will return `Some("179")` and `Some("300")` respectively.
1036 // Upstream versions look like:
1037 // lldb version 6.0.1
1039 // There doesn't seem to be a way to correlate the Apple version
1040 // with the upstream version, and since the tests were originally
1041 // written against Apple versions, we make a fake Apple version by
1042 // multiplying the first number by 100. This is a hack, but
1043 // normally fine because the only non-Apple version we test is
1046 if let Some(ref full_version_line
) = full_version_line
{
1047 if !full_version_line
.trim().is_empty() {
1048 let full_version_line
= full_version_line
.trim();
1050 for (pos
, l
) in full_version_line
.char_indices() {
1051 if l
!= 'l'
&& l
!= 'L'
{
1054 if pos
+ 5 >= full_version_line
.len() {
1057 let l
= full_version_line
[pos
+ 1..].chars().next().unwrap();
1058 if l
!= 'l'
&& l
!= 'L'
{
1061 let d
= full_version_line
[pos
+ 2..].chars().next().unwrap();
1062 if d
!= 'd'
&& d
!= 'D'
{
1065 let b
= full_version_line
[pos
+ 3..].chars().next().unwrap();
1066 if b
!= 'b'
&& b
!= 'B'
{
1069 let dash
= full_version_line
[pos
+ 4..].chars().next().unwrap();
1074 let vers
= full_version_line
[pos
+ 5..]
1076 .take_while(|c
| c
.is_digit(10))
1077 .collect
::<String
>();
1078 if !vers
.is_empty() {
1079 return (Some(vers
), full_version_line
.contains("rust-enabled"));
1083 if full_version_line
.starts_with("lldb version ") {
1084 let vers
= full_version_line
[13..]
1086 .take_while(|c
| c
.is_digit(10))
1087 .collect
::<String
>();
1088 if !vers
.is_empty() {
1089 return (Some(vers
+ "00"), full_version_line
.contains("rust-enabled"));
1097 fn is_blacklisted_lldb_version(version
: &str) -> bool
{