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
, cdb_version
) = 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")),
226 system_llvm
: matches
.opt_present("system-llvm"),
228 adb_path
: opt_str2(matches
.opt_str("adb-path")),
229 adb_test_dir
: opt_str2(matches
.opt_str("adb-test-dir")),
230 adb_device_status
: opt_str2(matches
.opt_str("target")).contains("android")
231 && "(none)" != opt_str2(matches
.opt_str("adb-test-dir"))
232 && !opt_str2(matches
.opt_str("adb-test-dir")).is_empty(),
233 lldb_python_dir
: matches
.opt_str("lldb-python-dir"),
234 verbose
: matches
.opt_present("verbose"),
235 quiet
: matches
.opt_present("quiet"),
237 remote_test_client
: matches
.opt_str("remote-test-client").map(PathBuf
::from
),
238 compare_mode
: matches
.opt_str("compare-mode").map(CompareMode
::parse
),
239 rustfix_coverage
: matches
.opt_present("rustfix-coverage"),
241 cc
: matches
.opt_str("cc").unwrap(),
242 cxx
: matches
.opt_str("cxx").unwrap(),
243 cflags
: matches
.opt_str("cflags").unwrap(),
244 ar
: matches
.opt_str("ar").unwrap_or_else(|| String
::from("ar")),
245 linker
: matches
.opt_str("linker"),
246 llvm_components
: matches
.opt_str("llvm-components").unwrap(),
247 nodejs
: matches
.opt_str("nodejs"),
251 pub fn log_config(config
: &Config
) {
253 logv(c
, "configuration:".to_string());
254 logv(c
, format
!("compile_lib_path: {:?}", config
.compile_lib_path
));
255 logv(c
, format
!("run_lib_path: {:?}", config
.run_lib_path
));
256 logv(c
, format
!("rustc_path: {:?}", config
.rustc_path
.display()));
257 logv(c
, format
!("rustdoc_path: {:?}", config
.rustdoc_path
));
258 logv(c
, format
!("rust_demangler_path: {:?}", config
.rust_demangler_path
));
259 logv(c
, format
!("src_base: {:?}", config
.src_base
.display()));
260 logv(c
, format
!("build_base: {:?}", config
.build_base
.display()));
261 logv(c
, format
!("stage_id: {}", config
.stage_id
));
262 logv(c
, format
!("mode: {}", config
.mode
));
263 logv(c
, format
!("run_ignored: {}", config
.run_ignored
));
264 logv(c
, format
!("filter: {}", opt_str(&config
.filter
)));
265 logv(c
, format
!("filter_exact: {}", config
.filter_exact
));
268 format
!("force_pass_mode: {}", opt_str(&config
.force_pass_mode
.map(|m
| format
!("{}", m
))),),
270 logv(c
, format
!("runtool: {}", opt_str(&config
.runtool
)));
271 logv(c
, format
!("host-rustcflags: {}", opt_str(&config
.host_rustcflags
)));
272 logv(c
, format
!("target-rustcflags: {}", opt_str(&config
.target_rustcflags
)));
273 logv(c
, format
!("target: {}", config
.target
));
274 logv(c
, format
!("host: {}", config
.host
));
275 logv(c
, format
!("android-cross-path: {:?}", config
.android_cross_path
.display()));
276 logv(c
, format
!("adb_path: {:?}", config
.adb_path
));
277 logv(c
, format
!("adb_test_dir: {:?}", config
.adb_test_dir
));
278 logv(c
, format
!("adb_device_status: {}", config
.adb_device_status
));
279 logv(c
, format
!("ar: {}", config
.ar
));
280 logv(c
, format
!("linker: {:?}", config
.linker
));
281 logv(c
, format
!("verbose: {}", config
.verbose
));
282 logv(c
, format
!("quiet: {}", config
.quiet
));
283 logv(c
, "\n".to_string());
286 pub fn opt_str(maybestr
: &Option
<String
>) -> &str {
293 pub fn opt_str2(maybestr
: Option
<String
>) -> String
{
295 None
=> "(none)".to_owned(),
300 pub fn run_tests(config
: Config
) {
301 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
302 if let Mode
::CodegenUnits
= config
.mode
{
303 let _
= fs
::remove_dir_all("tmp/partitioning-tests");
306 // If we want to collect rustfix coverage information,
307 // we first make sure that the coverage file does not exist.
308 // It will be created later on.
309 if config
.rustfix_coverage
{
310 let mut coverage_file_path
= config
.build_base
.clone();
311 coverage_file_path
.push("rustfix_missing_coverage.txt");
312 if coverage_file_path
.exists() {
313 if let Err(e
) = fs
::remove_file(&coverage_file_path
) {
314 panic
!("Could not delete {} due to {}", coverage_file_path
.display(), e
)
319 // sadly osx needs some file descriptor limits raised for running tests in
320 // parallel (especially when we have lots and lots of child processes).
321 // For context, see #8904
323 raise_fd_limit
::raise_fd_limit();
325 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
326 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
327 env
::set_var("__COMPAT_LAYER", "RunAsInvoker");
329 // Let tests know which target they're running as
330 env
::set_var("TARGET", &config
.target
);
332 let opts
= test_opts(&config
);
334 let mut configs
= Vec
::new();
335 if let Mode
::DebugInfo
= config
.mode
{
336 // Debugging emscripten code doesn't make sense today
337 if !config
.target
.contains("emscripten") {
338 configs
.extend(configure_cdb(&config
));
339 configs
.extend(configure_gdb(&config
));
340 configs
.extend(configure_lldb(&config
));
343 configs
.push(config
);
346 let mut tests
= Vec
::new();
348 make_tests(c
, &mut tests
);
351 let res
= test
::run_tests_console(&opts
, tests
);
354 Ok(false) => panic
!("Some tests failed"),
356 // We don't know if tests passed or not, but if there was an error
357 // during testing we don't want to just suceeed (we may not have
358 // tested something), so fail.
359 panic
!("I/O failure during tests: {:?}", e
);
364 fn configure_cdb(config
: &Config
) -> Option
<Config
> {
365 config
.cdb
.as_ref()?
;
367 Some(Config { debugger: Some(Debugger::Cdb), ..config.clone() }
)
370 fn configure_gdb(config
: &Config
) -> Option
<Config
> {
373 if util
::matches_env(&config
.target
, "msvc") {
377 if config
.remote_test_client
.is_some() && !config
.target
.contains("android") {
379 "WARNING: debuginfo tests are not available when \
385 if config
.target
.contains("android") {
387 "{} debug-info test uses tcp 5039 port.\
392 // android debug-info test uses remote debugger so, we test 1 thread
393 // at once as they're all sharing the same TCP port to communicate
396 // we should figure out how to lift this restriction! (run them all
397 // on different ports allocated dynamically).
398 env
::set_var("RUST_TEST_THREADS", "1");
401 Some(Config { debugger: Some(Debugger::Gdb), ..config.clone() }
)
404 fn configure_lldb(config
: &Config
) -> Option
<Config
> {
405 config
.lldb_python_dir
.as_ref()?
;
407 if let Some(350) = config
.lldb_version
{
409 "WARNING: The used version of LLDB (350) has a \
410 known issue that breaks debuginfo tests. See \
411 issue #32520 for more information. Skipping all \
417 // Some older versions of LLDB seem to have problems with multiple
418 // instances running in parallel, so only run one test thread at a
420 env
::set_var("RUST_TEST_THREADS", "1");
422 Some(Config { debugger: Some(Debugger::Lldb), ..config.clone() }
)
425 pub fn test_opts(config
: &Config
) -> test
::TestOpts
{
427 exclude_should_panic
: false,
428 filter
: config
.filter
.clone(),
429 filter_exact
: config
.filter_exact
,
430 run_ignored
: if config
.run_ignored { test::RunIgnored::Yes }
else { test::RunIgnored::No }
,
431 format
: if config
.quiet { test::OutputFormat::Terse }
else { test::OutputFormat::Pretty }
,
432 logfile
: config
.logfile
.clone(),
434 bench_benchmarks
: true,
435 nocapture
: match env
::var("RUST_TEST_NOCAPTURE") {
436 Ok(val
) => &val
!= "0",
443 options
: test
::Options
::new(),
445 force_run_in_process
: false,
449 pub fn make_tests(config
: &Config
, tests
: &mut Vec
<test
::TestDescAndFn
>) {
450 debug
!("making tests from {:?}", config
.src_base
.display());
451 let inputs
= common_inputs_stamp(config
);
452 collect_tests_from_dir(config
, &config
.src_base
, &PathBuf
::new(), &inputs
, tests
)
453 .unwrap_or_else(|_
| panic
!("Could not read tests from {}", config
.src_base
.display()));
456 /// Returns a stamp constructed from input files common to all test cases.
457 fn common_inputs_stamp(config
: &Config
) -> Stamp
{
458 let rust_src_dir
= config
.find_rust_src_root().expect("Could not find Rust source root");
460 let mut stamp
= Stamp
::from_path(&config
.rustc_path
);
462 // Relevant pretty printer files
463 let pretty_printer_files
= [
464 "src/etc/rust_types.py",
465 "src/etc/gdb_load_rust_pretty_printers.py",
466 "src/etc/gdb_lookup.py",
467 "src/etc/gdb_providers.py",
468 "src/etc/lldb_batchmode.py",
469 "src/etc/lldb_lookup.py",
470 "src/etc/lldb_providers.py",
472 for file
in &pretty_printer_files
{
473 let path
= rust_src_dir
.join(file
);
474 stamp
.add_path(&path
);
477 stamp
.add_dir(&config
.run_lib_path
);
479 if let Some(ref rustdoc_path
) = config
.rustdoc_path
{
480 stamp
.add_path(&rustdoc_path
);
481 stamp
.add_path(&rust_src_dir
.join("src/etc/htmldocck.py"));
483 // FIXME(richkadel): Do I need to add an `if let Some(rust_demangler_path) contribution to the
484 // stamp here as well?
486 // Compiletest itself.
487 stamp
.add_dir(&rust_src_dir
.join("src/tools/compiletest/"));
492 fn collect_tests_from_dir(
495 relative_dir_path
: &Path
,
497 tests
: &mut Vec
<test
::TestDescAndFn
>,
498 ) -> io
::Result
<()> {
499 // Ignore directories that contain a file named `compiletest-ignore-dir`.
500 if dir
.join("compiletest-ignore-dir").exists() {
504 if config
.mode
== Mode
::RunMake
&& dir
.join("Makefile").exists() {
505 let paths
= TestPaths
{
506 file
: dir
.to_path_buf(),
507 relative_dir
: relative_dir_path
.parent().unwrap().to_path_buf(),
509 tests
.extend(make_test(config
, &paths
, inputs
));
513 // If we find a test foo/bar.rs, we have to build the
514 // output directory `$build/foo` so we can write
515 // `$build/foo/bar` into it. We do this *now* in this
516 // sequential loop because otherwise, if we do it in the
517 // tests themselves, they race for the privilege of
518 // creating the directories and sometimes fail randomly.
519 let build_dir
= output_relative_path(config
, relative_dir_path
);
520 fs
::create_dir_all(&build_dir
).unwrap();
522 // Add each `.rs` file as a test, and recurse further on any
523 // subdirectories we find, except for `aux` directories.
524 for file
in fs
::read_dir(dir
)?
{
526 let file_path
= file
.path();
527 let file_name
= file
.file_name();
528 if is_test(&file_name
) {
529 debug
!("found test file: {:?}", file_path
.display());
531 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }
;
532 tests
.extend(make_test(config
, &paths
, inputs
))
533 } else if file_path
.is_dir() {
534 let relative_file_path
= relative_dir_path
.join(file
.file_name());
535 if &file_name
!= "auxiliary" {
536 debug
!("found directory: {:?}", file_path
.display());
537 collect_tests_from_dir(config
, &file_path
, &relative_file_path
, inputs
, tests
)?
;
540 debug
!("found other file/directory: {:?}", file_path
.display());
546 /// Returns true if `file_name` looks like a proper test file name.
547 pub fn is_test(file_name
: &OsString
) -> bool
{
548 let file_name
= file_name
.to_str().unwrap();
550 if !file_name
.ends_with(".rs") {
554 // `.`, `#`, and `~` are common temp-file prefixes.
555 let invalid_prefixes
= &[".", "#", "~"];
556 !invalid_prefixes
.iter().any(|p
| file_name
.starts_with(p
))
559 fn make_test(config
: &Config
, testpaths
: &TestPaths
, inputs
: &Stamp
) -> Vec
<test
::TestDescAndFn
> {
560 let early_props
= if config
.mode
== Mode
::RunMake
{
561 // Allow `ignore` directives to be in the Makefile.
562 EarlyProps
::from_file(config
, &testpaths
.file
.join("Makefile"))
564 EarlyProps
::from_file(config
, &testpaths
.file
)
567 // The `should-fail` annotation doesn't apply to pretty tests,
568 // since we run the pretty printer across all tests by default.
569 // If desired, we could add a `should-fail-pretty` annotation.
570 let should_panic
= match config
.mode
{
571 Pretty
=> test
::ShouldPanic
::No
,
573 if early_props
.should_fail
{
574 test
::ShouldPanic
::Yes
576 test
::ShouldPanic
::No
581 // Incremental tests are special, they inherently cannot be run in parallel.
582 // `runtest::run` will be responsible for iterating over revisions.
583 let revisions
= if early_props
.revisions
.is_empty() || config
.mode
== Mode
::Incremental
{
586 early_props
.revisions
.iter().map(Some
).collect()
591 let ignore
= early_props
.ignore
592 // Ignore tests that already run and are up to date with respect to inputs.
597 revision
.map(|s
| s
.as_str()),
600 test
::TestDescAndFn
{
601 desc
: test
::TestDesc
{
602 name
: make_test_name(config
, testpaths
, revision
),
606 test_type
: test
::TestType
::Unknown
,
608 testfn
: make_test_closure(config
, testpaths
, revision
),
614 fn stamp(config
: &Config
, testpaths
: &TestPaths
, revision
: Option
<&str>) -> PathBuf
{
615 output_base_dir(config
, testpaths
, revision
).join("stamp")
620 testpaths
: &TestPaths
,
622 revision
: Option
<&str>,
625 let stamp_name
= stamp(config
, testpaths
, revision
);
627 let contents
= match fs
::read_to_string(&stamp_name
) {
629 Err(ref e
) if e
.kind() == ErrorKind
::InvalidData
=> panic
!("Can't read stamp contents"),
630 Err(_
) => return false,
632 let expected_hash
= runtest
::compute_stamp_hash(config
);
633 if contents
!= expected_hash
{
638 let mut inputs
= inputs
.clone();
639 // Use `add_dir` to account for run-make tests, which use their individual directory
640 inputs
.add_dir(&testpaths
.file
);
642 for aux
in &props
.aux
{
643 let path
= testpaths
.file
.parent().unwrap().join("auxiliary").join(aux
);
644 inputs
.add_path(&path
);
648 for extension
in UI_EXTENSIONS
{
649 let path
= &expected_output_path(testpaths
, revision
, &config
.compare_mode
, extension
);
650 inputs
.add_path(path
);
653 inputs
< Stamp
::from_path(&stamp_name
)
656 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
662 fn from_path(path
: &Path
) -> Self {
663 let mut stamp
= Stamp { time: SystemTime::UNIX_EPOCH }
;
664 stamp
.add_path(path
);
668 fn add_path(&mut self, path
: &Path
) {
669 let modified
= fs
::metadata(path
)
670 .and_then(|metadata
| metadata
.modified())
671 .unwrap_or(SystemTime
::UNIX_EPOCH
);
672 self.time
= self.time
.max(modified
);
675 fn add_dir(&mut self, path
: &Path
) {
676 for entry
in WalkDir
::new(path
) {
677 let entry
= entry
.unwrap();
678 if entry
.file_type().is_file() {
682 .and_then(|metadata
| metadata
.modified().ok())
683 .unwrap_or(SystemTime
::UNIX_EPOCH
);
684 self.time
= self.time
.max(modified
);
692 testpaths
: &TestPaths
,
693 revision
: Option
<&String
>,
694 ) -> test
::TestName
{
695 // Convert a complete path to something like
698 let path
= PathBuf
::from(config
.src_base
.file_name().unwrap())
699 .join(&testpaths
.relative_dir
)
700 .join(&testpaths
.file
.file_name().unwrap());
701 let debugger
= match config
.debugger
{
702 Some(d
) => format
!("-{}", d
),
703 None
=> String
::new(),
705 let mode_suffix
= match config
.compare_mode
{
706 Some(ref mode
) => format
!(" ({})", mode
.to_str()),
707 None
=> String
::new(),
710 test
::DynTestName(format
!(
716 revision
.map_or("".to_string(), |rev
| format
!("#{}", rev
))
720 fn make_test_closure(
722 testpaths
: &TestPaths
,
723 revision
: Option
<&String
>,
725 let config
= config
.clone();
726 let testpaths
= testpaths
.clone();
727 let revision
= revision
.cloned();
728 test
::DynTestFn(Box
::new(move || runtest
::run(config
, &testpaths
, revision
.as_deref())))
731 /// Returns `true` if the given target is an Android target for the
732 /// purposes of GDB testing.
733 fn is_android_gdb_target(target
: &str) -> bool
{
736 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
740 /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
741 fn is_pc_windows_msvc_target(target
: &str) -> bool
{
742 target
.ends_with("-pc-windows-msvc")
745 fn find_cdb(target
: &str) -> Option
<OsString
> {
746 if !(cfg
!(windows
) && is_pc_windows_msvc_target(target
)) {
750 let pf86
= env
::var_os("ProgramFiles(x86)").or_else(|| env
::var_os("ProgramFiles"))?
;
751 let cdb_arch
= if cfg
!(target_arch
= "x86") {
753 } else if cfg
!(target_arch
= "x86_64") {
755 } else if cfg
!(target_arch
= "aarch64") {
757 } else if cfg
!(target_arch
= "arm") {
760 return None
; // No compatible CDB.exe in the Windows 10 SDK
763 let mut path
= PathBuf
::new();
765 path
.push(r
"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
767 path
.push(r
"cdb.exe");
773 Some(path
.into_os_string())
776 /// Returns Path to CDB
777 fn analyze_cdb(cdb
: Option
<String
>, target
: &str) -> (Option
<OsString
>, Option
<[u16; 4]>) {
778 let cdb
= cdb
.map(OsString
::from
).or_else(|| find_cdb(target
));
780 let mut version
= None
;
781 if let Some(cdb
) = cdb
.as_ref() {
782 if let Ok(output
) = Command
::new(cdb
).arg("/version").output() {
783 if let Some(first_line
) = String
::from_utf8_lossy(&output
.stdout
).lines().next() {
784 version
= extract_cdb_version(&first_line
);
792 fn extract_cdb_version(full_version_line
: &str) -> Option
<[u16; 4]> {
793 // Example full_version_line: "cdb version 10.0.18362.1"
794 let version
= full_version_line
.rsplit(' '
).next()?
;
795 let mut components
= version
.split('
.'
);
796 let major
: u16 = components
.next().unwrap().parse().unwrap();
797 let minor
: u16 = components
.next().unwrap().parse().unwrap();
798 let patch
: u16 = components
.next().unwrap_or("0").parse().unwrap();
799 let build
: u16 = components
.next().unwrap_or("0").parse().unwrap();
800 Some([major
, minor
, patch
, build
])
803 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
807 android_cross_path
: &PathBuf
,
808 ) -> (Option
<String
>, Option
<u32>, bool
) {
810 const GDB_FALLBACK
: &str = "gdb";
812 const GDB_FALLBACK
: &str = "gdb.exe";
814 const MIN_GDB_WITH_RUST
: u32 = 7011010;
816 let fallback_gdb
= || {
817 if is_android_gdb_target(target
) {
818 let mut gdb_path
= match android_cross_path
.to_str() {
819 Some(x
) => x
.to_owned(),
820 None
=> panic
!("cannot find android cross path"),
822 gdb_path
.push_str("/bin/gdb");
825 GDB_FALLBACK
.to_owned()
829 let gdb
= match gdb
{
830 None
=> fallback_gdb(),
831 Some(ref s
) if s
.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
832 Some(ref s
) => s
.to_owned(),
835 let mut version_line
= None
;
836 if let Ok(output
) = Command
::new(&gdb
).arg("--version").output() {
837 if let Some(first_line
) = String
::from_utf8_lossy(&output
.stdout
).lines().next() {
838 version_line
= Some(first_line
.to_string());
842 let version
= match version_line
{
843 Some(line
) => extract_gdb_version(&line
),
844 None
=> return (None
, None
, false),
847 let gdb_native_rust
= version
.map_or(false, |v
| v
>= MIN_GDB_WITH_RUST
);
849 (Some(gdb
), version
, gdb_native_rust
)
852 fn extract_gdb_version(full_version_line
: &str) -> Option
<u32> {
853 let full_version_line
= full_version_line
.trim();
855 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
856 // of the ? sections being optional
858 // We will parse up to 3 digits for each component, ignoring the date
860 // We skip text in parentheses. This avoids accidentally parsing
861 // the openSUSE version, which looks like:
862 // GNU gdb (GDB; openSUSE Leap 15.0) 8.1
863 // This particular form is documented in the GNU coding standards:
864 // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
866 let mut splits
= full_version_line
.rsplit(' '
);
867 let version_string
= splits
.next().unwrap();
869 let mut splits
= version_string
.split('
.'
);
870 let major
= splits
.next().unwrap();
871 let minor
= splits
.next().unwrap();
872 let patch
= splits
.next();
874 let major
: u32 = major
.parse().unwrap();
875 let (minor
, patch
): (u32, u32) = match minor
.find(not_a_digit
) {
877 let minor
= minor
.parse().unwrap();
878 let patch
: u32 = match patch
{
879 Some(patch
) => match patch
.find(not_a_digit
) {
880 None
=> patch
.parse().unwrap(),
881 Some(idx
) if idx
> 3 => 0,
882 Some(idx
) => patch
[..idx
].parse().unwrap(),
888 // There is no patch version after minor-date (e.g. "4-2012").
890 let minor
= minor
[..idx
].parse().unwrap();
895 Some(((major
* 1000) + minor
) * 1000 + patch
)
898 /// Returns (LLDB version, LLDB is rust-enabled)
899 fn extract_lldb_version(full_version_line
: &str) -> Option
<(u32, bool
)> {
900 // Extract the major LLDB version from the given version string.
901 // LLDB version strings are different for Apple and non-Apple platforms.
902 // The Apple variant looks like this:
904 // LLDB-179.5 (older versions)
905 // lldb-300.2.51 (new versions)
907 // We are only interested in the major version number, so this function
908 // will return `Some(179)` and `Some(300)` respectively.
910 // Upstream versions look like:
911 // lldb version 6.0.1
913 // There doesn't seem to be a way to correlate the Apple version
914 // with the upstream version, and since the tests were originally
915 // written against Apple versions, we make a fake Apple version by
916 // multiplying the first number by 100. This is a hack, but
917 // normally fine because the only non-Apple version we test is
920 let full_version_line
= full_version_line
.trim();
922 if let Some(apple_ver
) =
923 full_version_line
.strip_prefix("LLDB-").or_else(|| full_version_line
.strip_prefix("lldb-"))
925 if let Some(idx
) = apple_ver
.find(not_a_digit
) {
926 let version
: u32 = apple_ver
[..idx
].parse().unwrap();
927 return Some((version
, full_version_line
.contains("rust-enabled")));
929 } else if let Some(lldb_ver
) = full_version_line
.strip_prefix("lldb version ") {
930 if let Some(idx
) = lldb_ver
.find(not_a_digit
) {
931 let version
: u32 = lldb_ver
[..idx
].parse().unwrap();
932 return Some((version
* 100, full_version_line
.contains("rust-enabled")));
938 fn not_a_digit(c
: char) -> bool
{