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, TestPaths};
10 use crate::util::logv;
12 use lazycell::LazyCell;
14 use std::ffi::OsString;
16 use std::io::{self, ErrorKind};
17 use std::path::{Path, PathBuf};
18 use std::process::{Command, Stdio};
19 use std::time::SystemTime;
20 use test::ColorConfig;
24 use self::header::{make_test_description, EarlyProps};
40 tracing_subscriber::fmt::init();
42 let config = parse_config(env::args().collect());
44 if config.valgrind_path.is_none() && config.force_valgrind {
45 panic!("Can't find Valgrind to run Valgrind tests");
48 if !config.has_tidy && config.mode == Mode::Rustdoc {
49 eprintln!("warning: `tidy` is not installed; diffs will not be generated");
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 .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH")
63 .reqopt("", "python", "path to python to use for doc tests", "PATH")
64 .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
65 .optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH")
66 .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
67 .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
68 .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH")
69 .optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
70 .reqopt("", "src-base", "directory to scan for test files", "PATH")
71 .reqopt("", "build-base", "directory to deposit test outputs", "PATH")
72 .reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET")
76 "which sort of compile tests to run",
77 "run-pass-valgrind | pretty | debug-info | codegen | rustdoc \
78 | rustdoc-json | codegen-units | incremental | run-make | ui | js-doc-test | mir-opt | assembly",
83 "which suite of compile tests to run. used for nicer error reporting.",
89 "force {check,build,run}-pass tests to this mode.",
90 "check | build | run",
92 .optopt("", "run", "whether to execute run-* tests", "auto | always | never")
93 .optflag("", "ignored", "run tests marked as ignored")
94 .optmulti("", "skip", "skip tests matching SUBSTRING. Can be passed multiple times", "SUBSTRING")
95 .optflag("", "exact", "filters match exactly")
99 "supervisor program to run tests under \
100 (eg. emulator, valgrind)",
103 .optmulti("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS")
104 .optmulti("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS")
105 .optflag("", "optimize-tests", "run tests with optimizations enabled")
106 .optflag("", "verbose", "run tests verbosely, showing all output")
110 "overwrite stderr/stdout files instead of complaining about a mismatch",
112 .optflag("", "quiet", "print one character per test instead of one line")
113 .optopt("", "color", "coloring: auto, always, never", "WHEN")
114 .optopt("", "logfile", "file to log test execution to", "FILE")
115 .optopt("", "target", "the target to build for", "TARGET")
116 .optopt("", "host", "the host to build for", "HOST")
117 .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
118 .optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH")
119 .optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING")
120 .optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING")
121 .optflag("", "system-llvm", "is LLVM the system LLVM")
122 .optopt("", "android-cross-path", "Android NDK standalone path", "PATH")
123 .optopt("", "adb-path", "path to the android debugger", "PATH")
124 .optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH")
125 .optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH")
126 .reqopt("", "cc", "path to a C compiler", "PATH")
127 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
128 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
129 .reqopt("", "cxxflags", "flags for the CXX compiler", "FLAGS")
130 .optopt("", "ar", "path to an archiver", "PATH")
131 .optopt("", "linker", "path to a linker", "PATH")
132 .reqopt("", "llvm-components", "list of LLVM components built in", "LIST")
133 .optopt("", "llvm-bin-dir", "Path to LLVM's `bin` directory", "PATH")
134 .optopt("", "nodejs", "the name of nodejs", "PATH")
135 .optopt("", "npm", "the name of npm", "PATH")
136 .optopt("", "remote-test-client", "path to the remote test client", "PATH")
140 "mode describing what file the actual ui output will be compared to",
146 "enable this to generate a Rustfix coverage file, which is saved in \
147 `./<build_base>/rustfix_missing_coverage.txt`",
149 .optflag("", "force-rerun", "rerun tests even if the inputs are unchanged")
150 .optflag("h", "help", "show this message")
151 .reqopt("", "channel", "current Rust channel", "CHANNEL")
152 .optopt("", "edition", "default Rust edition", "EDITION");
154 let (argv0, args_) = args.split_first().unwrap();
155 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
156 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
157 println!("{}", opts.usage(&message));
162 let matches = &match opts.parse(args_) {
164 Err(f) => panic!("{:?}", f),
167 if matches.opt_present("h") || matches.opt_present("help") {
168 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
169 println!("{}", opts.usage(&message));
174 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
175 match m.opt_str(nm) {
176 Some(s) => PathBuf::from(&s),
177 None => panic!("no option (=path) found for {}", nm),
181 fn make_absolute(path: PathBuf) -> PathBuf {
182 if path.is_relative() { env::current_dir().unwrap().join(path) } else { path }
185 let target = opt_str2(matches.opt_str("target"));
186 let android_cross_path = opt_path(matches, "android-cross-path");
187 let (cdb, cdb_version) = analyze_cdb(matches.opt_str("cdb"), &target);
188 let (gdb, gdb_version, gdb_native_rust) =
189 analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
190 let (lldb_version, lldb_native_rust) = matches
191 .opt_str("lldb-version")
193 .and_then(extract_lldb_version)
194 .map(|(v, b)| (Some(v), b))
195 .unwrap_or((None, false));
196 let color = match matches.opt_str("color").as_deref() {
197 Some("auto") | None => ColorConfig::AutoColor,
198 Some("always") => ColorConfig::AlwaysColor,
199 Some("never") => ColorConfig::NeverColor,
200 Some(x) => panic!("argument for --color must be auto, always, or never, but found `{}`", x),
203 matches.opt_str("llvm-version").as_deref().and_then(header::extract_llvm_version);
205 let src_base = opt_path(matches, "src-base");
206 let run_ignored = matches.opt_present("ignored");
207 let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
208 let has_tidy = if mode == Mode::Rustdoc {
211 .stdout(Stdio::null())
213 .map_or(false, |status| status.success())
215 // Avoid spawning an external command when we know tidy won't be used.
219 bless: matches.opt_present("bless"),
220 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
221 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
222 rustc_path: opt_path(matches, "rustc-path"),
223 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
224 rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
225 python: matches.opt_str("python").unwrap(),
226 jsondocck_path: matches.opt_str("jsondocck-path"),
227 jsondoclint_path: matches.opt_str("jsondoclint-path"),
228 valgrind_path: matches.opt_str("valgrind-path"),
229 force_valgrind: matches.opt_present("force-valgrind"),
230 run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
231 llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
232 llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
234 build_base: opt_path(matches, "build-base"),
235 stage_id: matches.opt_str("stage-id").unwrap(),
237 suite: matches.opt_str("suite").unwrap(),
240 filters: matches.free.clone(),
241 skip: matches.opt_strs("skip"),
242 filter_exact: matches.opt_present("exact"),
243 force_pass_mode: matches.opt_str("pass").map(|mode| {
244 mode.parse::<PassMode>()
245 .unwrap_or_else(|_| panic!("unknown `--pass` option `{}` given", mode))
247 run: matches.opt_str("run").and_then(|mode| match mode.as_str() {
249 "always" => Some(true),
250 "never" => Some(false),
251 _ => panic!("unknown `--run` option `{}` given", mode),
253 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
254 runtool: matches.opt_str("runtool"),
255 host_rustcflags: Some(matches.opt_strs("host-rustcflags").join(" ")),
256 target_rustcflags: Some(matches.opt_strs("target-rustcflags").join(" ")),
257 optimize_tests: matches.opt_present("optimize-tests"),
259 host: opt_str2(matches.opt_str("host")),
268 system_llvm: matches.opt_present("system-llvm"),
270 adb_path: opt_str2(matches.opt_str("adb-path")),
271 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
272 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
273 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
274 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
275 lldb_python_dir: matches.opt_str("lldb-python-dir"),
276 verbose: matches.opt_present("verbose"),
277 quiet: matches.opt_present("quiet"),
279 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
280 compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
281 rustfix_coverage: matches.opt_present("rustfix-coverage"),
283 channel: matches.opt_str("channel").unwrap(),
284 edition: matches.opt_str("edition"),
286 cc: matches.opt_str("cc").unwrap(),
287 cxx: matches.opt_str("cxx").unwrap(),
288 cflags: matches.opt_str("cflags").unwrap(),
289 cxxflags: matches.opt_str("cxxflags").unwrap(),
290 ar: matches.opt_str("ar").unwrap_or_else(|| String::from("ar")),
291 linker: matches.opt_str("linker"),
292 llvm_components: matches.opt_str("llvm-components").unwrap(),
293 nodejs: matches.opt_str("nodejs"),
294 npm: matches.opt_str("npm"),
296 force_rerun: matches.opt_present("force-rerun"),
298 target_cfg: LazyCell::new(),
302 pub fn log_config(config: &Config) {
304 logv(c, "configuration:".to_string());
305 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
306 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
307 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
308 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
309 logv(c, format!("rust_demangler_path: {:?}", config.rust_demangler_path));
310 logv(c, format!("src_base: {:?}", config.src_base.display()));
311 logv(c, format!("build_base: {:?}", config.build_base.display()));
312 logv(c, format!("stage_id: {}", config.stage_id));
313 logv(c, format!("mode: {}", config.mode));
314 logv(c, format!("run_ignored: {}", config.run_ignored));
315 logv(c, format!("filters: {:?}", config.filters));
316 logv(c, format!("skip: {:?}", config.skip));
317 logv(c, format!("filter_exact: {}", config.filter_exact));
320 format!("force_pass_mode: {}", opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),),
322 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
323 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
324 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
325 logv(c, format!("target: {}", config.target));
326 logv(c, format!("host: {}", config.host));
327 logv(c, format!("android-cross-path: {:?}", config.android_cross_path.display()));
328 logv(c, format!("adb_path: {:?}", config.adb_path));
329 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
330 logv(c, format!("adb_device_status: {}", config.adb_device_status));
331 logv(c, format!("ar: {}", config.ar));
332 logv(c, format!("linker: {:?}", config.linker));
333 logv(c, format!("verbose: {}", config.verbose));
334 logv(c, format!("quiet: {}", config.quiet));
335 logv(c, "\n".to_string());
338 pub fn opt_str(maybestr: &Option<String>) -> &str {
345 pub fn opt_str2(maybestr: Option<String>) -> String {
347 None => "(none)".to_owned(),
352 pub fn run_tests(config: Config) {
353 // If we want to collect rustfix coverage information,
354 // we first make sure that the coverage file does not exist.
355 // It will be created later on.
356 if config.rustfix_coverage {
357 let mut coverage_file_path = config.build_base.clone();
358 coverage_file_path.push("rustfix_missing_coverage.txt");
359 if coverage_file_path.exists() {
360 if let Err(e) = fs::remove_file(&coverage_file_path) {
361 panic!("Could not delete {} due to {}", coverage_file_path.display(), e)
366 // sadly osx needs some file descriptor limits raised for running tests in
367 // parallel (especially when we have lots and lots of child processes).
368 // For context, see #8904
370 raise_fd_limit::raise_fd_limit();
372 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
373 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
374 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
376 // Let tests know which target they're running as
377 env::set_var("TARGET", &config.target);
379 let opts = test_opts(&config);
381 let mut configs = Vec::new();
382 if let Mode::DebugInfo = config.mode {
383 // Debugging emscripten code doesn't make sense today
384 if !config.target.contains("emscripten") {
385 configs.extend(configure_cdb(&config));
386 configs.extend(configure_gdb(&config));
387 configs.extend(configure_lldb(&config));
390 configs.push(config.clone());
393 let mut tests = Vec::new();
395 make_tests(c, &mut tests);
398 let res = test::run_tests_console(&opts, tests);
402 // We want to report that the tests failed, but we also want to give
403 // some indication of just what tests we were running. Especially on
404 // CI, where there can be cross-compiled tests for a lot of
405 // architectures, without this critical information it can be quite
406 // easy to miss which tests failed, and as such fail to reproduce
407 // the failure locally.
410 "Some tests failed in compiletest suite={}{} mode={} host={} target={}",
412 config.compare_mode.map(|c| format!(" compare_mode={:?}", c)).unwrap_or_default(),
418 std::process::exit(1);
421 // We don't know if tests passed or not, but if there was an error
422 // during testing we don't want to just succeed (we may not have
423 // tested something), so fail.
425 // This should realistically "never" happen, so don't try to make
426 // this a pretty error message.
427 panic!("I/O failure during tests: {:?}", e);
432 fn configure_cdb(config: &Config) -> Option<Config> {
433 config.cdb.as_ref()?;
435 Some(Config { debugger: Some(Debugger::Cdb), ..config.clone() })
438 fn configure_gdb(config: &Config) -> Option<Config> {
441 if config.matches_env("msvc") {
445 if config.remote_test_client.is_some() && !config.target.contains("android") {
447 "WARNING: debuginfo tests are not available when \
453 if config.target.contains("android") {
455 "{} debug-info test uses tcp 5039 port.\
460 // android debug-info test uses remote debugger so, we test 1 thread
461 // at once as they're all sharing the same TCP port to communicate
464 // we should figure out how to lift this restriction! (run them all
465 // on different ports allocated dynamically).
466 env::set_var("RUST_TEST_THREADS", "1");
469 Some(Config { debugger: Some(Debugger::Gdb), ..config.clone() })
472 fn configure_lldb(config: &Config) -> Option<Config> {
473 config.lldb_python_dir.as_ref()?;
475 if let Some(350) = config.lldb_version {
477 "WARNING: The used version of LLDB (350) has a \
478 known issue that breaks debuginfo tests. See \
479 issue #32520 for more information. Skipping all \
485 Some(Config { debugger: Some(Debugger::Lldb), ..config.clone() })
488 pub fn test_opts(config: &Config) -> test::TestOpts {
490 exclude_should_panic: false,
491 filters: config.filters.clone(),
492 filter_exact: config.filter_exact,
493 run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
494 format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
495 logfile: config.logfile.clone(),
497 bench_benchmarks: true,
498 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
499 Ok(val) => &val != "0",
506 skip: config.skip.clone(),
508 options: test::Options::new(),
510 force_run_in_process: false,
514 pub fn make_tests(config: &Config, tests: &mut Vec<test::TestDescAndFn>) {
515 debug!("making tests from {:?}", config.src_base.display());
516 let inputs = common_inputs_stamp(config);
517 collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests)
518 .unwrap_or_else(|_| panic!("Could not read tests from {}", config.src_base.display()));
521 /// Returns a stamp constructed from input files common to all test cases.
522 fn common_inputs_stamp(config: &Config) -> Stamp {
523 let rust_src_dir = config.find_rust_src_root().expect("Could not find Rust source root");
525 let mut stamp = Stamp::from_path(&config.rustc_path);
527 // Relevant pretty printer files
528 let pretty_printer_files = [
529 "src/etc/rust_types.py",
530 "src/etc/gdb_load_rust_pretty_printers.py",
531 "src/etc/gdb_lookup.py",
532 "src/etc/gdb_providers.py",
533 "src/etc/lldb_batchmode.py",
534 "src/etc/lldb_lookup.py",
535 "src/etc/lldb_providers.py",
537 for file in &pretty_printer_files {
538 let path = rust_src_dir.join(file);
539 stamp.add_path(&path);
542 stamp.add_dir(&rust_src_dir.join("src/etc/natvis"));
544 stamp.add_dir(&config.run_lib_path);
546 if let Some(ref rustdoc_path) = config.rustdoc_path {
547 stamp.add_path(&rustdoc_path);
548 stamp.add_path(&rust_src_dir.join("src/etc/htmldocck.py"));
551 // Compiletest itself.
552 stamp.add_dir(&rust_src_dir.join("src/tools/compiletest/"));
557 fn collect_tests_from_dir(
560 relative_dir_path: &Path,
562 tests: &mut Vec<test::TestDescAndFn>,
563 ) -> io::Result<()> {
564 // Ignore directories that contain a file named `compiletest-ignore-dir`.
565 if dir.join("compiletest-ignore-dir").exists() {
569 if config.mode == Mode::RunMake && dir.join("Makefile").exists() {
570 let paths = TestPaths {
571 file: dir.to_path_buf(),
572 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
574 tests.extend(make_test(config, &paths, inputs));
578 // If we find a test foo/bar.rs, we have to build the
579 // output directory `$build/foo` so we can write
580 // `$build/foo/bar` into it. We do this *now* in this
581 // sequential loop because otherwise, if we do it in the
582 // tests themselves, they race for the privilege of
583 // creating the directories and sometimes fail randomly.
584 let build_dir = output_relative_path(config, relative_dir_path);
585 fs::create_dir_all(&build_dir).unwrap();
587 // Add each `.rs` file as a test, and recurse further on any
588 // subdirectories we find, except for `aux` directories.
589 for file in fs::read_dir(dir)? {
591 let file_path = file.path();
592 let file_name = file.file_name();
593 if is_test(&file_name) {
594 debug!("found test file: {:?}", file_path.display());
596 TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() };
598 tests.extend(make_test(config, &paths, inputs))
599 } else if file_path.is_dir() {
600 let relative_file_path = relative_dir_path.join(file.file_name());
601 if &file_name != "auxiliary" {
602 debug!("found directory: {:?}", file_path.display());
603 collect_tests_from_dir(config, &file_path, &relative_file_path, inputs, tests)?;
606 debug!("found other file/directory: {:?}", file_path.display());
612 /// Returns true if `file_name` looks like a proper test file name.
613 pub fn is_test(file_name: &OsString) -> bool {
614 let file_name = file_name.to_str().unwrap();
616 if !file_name.ends_with(".rs") {
620 // `.`, `#`, and `~` are common temp-file prefixes.
621 let invalid_prefixes = &[".", "#", "~"];
622 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
625 fn make_test(config: &Config, testpaths: &TestPaths, inputs: &Stamp) -> Vec<test::TestDescAndFn> {
626 let test_path = if config.mode == Mode::RunMake {
627 // Parse directives in the Makefile
628 testpaths.file.join("Makefile")
630 PathBuf::from(&testpaths.file)
632 let early_props = EarlyProps::from_file(config, &test_path);
634 // Incremental tests are special, they inherently cannot be run in parallel.
635 // `runtest::run` will be responsible for iterating over revisions.
636 let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
639 early_props.revisions.iter().map(Some).collect()
645 std::fs::File::open(&test_path).expect("open test file to parse ignores");
646 let cfg = revision.map(|v| &**v);
647 let test_name = crate::make_test_name(config, testpaths, revision);
648 let mut desc = make_test_description(config, test_name, &test_path, src_file, cfg);
649 // Ignore tests that already run and are up to date with respect to inputs.
650 if !config.force_rerun {
651 desc.ignore |= is_up_to_date(
655 revision.map(|s| s.as_str()),
659 test::TestDescAndFn { desc, testfn: make_test_closure(config, testpaths, revision) }
664 fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
665 output_base_dir(config, testpaths, revision).join("stamp")
668 fn files_related_to_test(
670 testpaths: &TestPaths,
672 revision: Option<&str>,
674 let mut related = vec![];
676 if testpaths.file.is_dir() {
677 // run-make tests use their individual directory
678 for entry in WalkDir::new(&testpaths.file) {
679 let path = entry.unwrap().into_path();
685 related.push(testpaths.file.clone());
688 for aux in &props.aux {
689 let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
694 for extension in UI_EXTENSIONS {
695 let path = expected_output_path(testpaths, revision, &config.compare_mode, extension);
704 testpaths: &TestPaths,
706 revision: Option<&str>,
709 let stamp_name = stamp(config, testpaths, revision);
711 let contents = match fs::read_to_string(&stamp_name) {
713 Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
714 Err(_) => return false,
716 let expected_hash = runtest::compute_stamp_hash(config);
717 if contents != expected_hash {
722 let mut inputs = inputs.clone();
723 for path in files_related_to_test(config, testpaths, props, revision) {
724 inputs.add_path(&path);
727 inputs < Stamp::from_path(&stamp_name)
730 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
736 fn from_path(path: &Path) -> Self {
737 let mut stamp = Stamp { time: SystemTime::UNIX_EPOCH };
738 stamp.add_path(path);
742 fn add_path(&mut self, path: &Path) {
743 let modified = fs::metadata(path)
744 .and_then(|metadata| metadata.modified())
745 .unwrap_or(SystemTime::UNIX_EPOCH);
746 self.time = self.time.max(modified);
749 fn add_dir(&mut self, path: &Path) {
750 for entry in WalkDir::new(path) {
751 let entry = entry.unwrap();
752 if entry.file_type().is_file() {
756 .and_then(|metadata| metadata.modified().ok())
757 .unwrap_or(SystemTime::UNIX_EPOCH);
758 self.time = self.time.max(modified);
766 testpaths: &TestPaths,
767 revision: Option<&String>,
768 ) -> test::TestName {
769 // Print the name of the file, relative to the repository root.
770 // `src_base` looks like `/path/to/rust/src/test/ui`
771 let root_directory = config.src_base.parent().unwrap().parent().unwrap().parent().unwrap();
772 let path = testpaths.file.strip_prefix(root_directory).unwrap();
773 let debugger = match config.debugger {
774 Some(d) => format!("-{}", d),
775 None => String::new(),
777 let mode_suffix = match config.compare_mode {
778 Some(ref mode) => format!(" ({})", mode.to_str()),
779 None => String::new(),
782 test::DynTestName(format!(
788 revision.map_or("".to_string(), |rev| format!("#{}", rev))
792 fn make_test_closure(
794 testpaths: &TestPaths,
795 revision: Option<&String>,
797 let config = config.clone();
798 let testpaths = testpaths.clone();
799 let revision = revision.cloned();
800 test::DynTestFn(Box::new(move || {
801 runtest::run(config, &testpaths, revision.as_deref());
806 /// Returns `true` if the given target is an Android target for the
807 /// purposes of GDB testing.
808 fn is_android_gdb_target(target: &str) -> bool {
811 "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
815 /// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
816 fn is_pc_windows_msvc_target(target: &str) -> bool {
817 target.ends_with("-pc-windows-msvc")
820 fn find_cdb(target: &str) -> Option<OsString> {
821 if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
825 let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
826 let cdb_arch = if cfg!(target_arch = "x86") {
828 } else if cfg!(target_arch = "x86_64") {
830 } else if cfg!(target_arch = "aarch64") {
832 } else if cfg!(target_arch = "arm") {
835 return None; // No compatible CDB.exe in the Windows 10 SDK
838 let mut path = PathBuf::new();
840 path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
842 path.push(r"cdb.exe");
848 Some(path.into_os_string())
851 /// Returns Path to CDB
852 fn analyze_cdb(cdb: Option<String>, target: &str) -> (Option<OsString>, Option<[u16; 4]>) {
853 let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
855 let mut version = None;
856 if let Some(cdb) = cdb.as_ref() {
857 if let Ok(output) = Command::new(cdb).arg("/version").output() {
858 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
859 version = extract_cdb_version(&first_line);
867 fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
868 // Example full_version_line: "cdb version 10.0.18362.1"
869 let version = full_version_line.rsplit(' ').next()?;
870 let mut components = version.split('.');
871 let major: u16 = components.next().unwrap().parse().unwrap();
872 let minor: u16 = components.next().unwrap().parse().unwrap();
873 let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
874 let build: u16 = components.next().unwrap_or("0").parse().unwrap();
875 Some([major, minor, patch, build])
878 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
882 android_cross_path: &PathBuf,
883 ) -> (Option<String>, Option<u32>, bool) {
885 const GDB_FALLBACK: &str = "gdb";
887 const GDB_FALLBACK: &str = "gdb.exe";
889 const MIN_GDB_WITH_RUST: u32 = 7011010;
891 let fallback_gdb = || {
892 if is_android_gdb_target(target) {
893 let mut gdb_path = match android_cross_path.to_str() {
894 Some(x) => x.to_owned(),
895 None => panic!("cannot find android cross path"),
897 gdb_path.push_str("/bin/gdb");
900 GDB_FALLBACK.to_owned()
904 let gdb = match gdb {
905 None => fallback_gdb(),
906 Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
907 Some(ref s) => s.to_owned(),
910 let mut version_line = None;
911 if let Ok(output) = Command::new(&gdb).arg("--version").output() {
912 if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
913 version_line = Some(first_line.to_string());
917 let version = match version_line {
918 Some(line) => extract_gdb_version(&line),
919 None => return (None, None, false),
922 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
924 (Some(gdb), version, gdb_native_rust)
927 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
928 let full_version_line = full_version_line.trim();
930 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
931 // of the ? sections being optional
933 // We will parse up to 3 digits for each component, ignoring the date
935 // We skip text in parentheses. This avoids accidentally parsing
936 // the openSUSE version, which looks like:
937 // GNU gdb (GDB; openSUSE Leap 15.0) 8.1
938 // This particular form is documented in the GNU coding standards:
939 // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
941 let unbracketed_part = full_version_line.split('[').next().unwrap();
942 let mut splits = unbracketed_part.trim_end().rsplit(' ');
943 let version_string = splits.next().unwrap();
945 let mut splits = version_string.split('.');
946 let major = splits.next().unwrap();
947 let minor = splits.next().unwrap();
948 let patch = splits.next();
950 let major: u32 = major.parse().unwrap();
951 let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
953 let minor = minor.parse().unwrap();
954 let patch: u32 = match patch {
955 Some(patch) => match patch.find(not_a_digit) {
956 None => patch.parse().unwrap(),
957 Some(idx) if idx > 3 => 0,
958 Some(idx) => patch[..idx].parse().unwrap(),
964 // There is no patch version after minor-date (e.g. "4-2012").
966 let minor = minor[..idx].parse().unwrap();
971 Some(((major * 1000) + minor) * 1000 + patch)
974 /// Returns (LLDB version, LLDB is rust-enabled)
975 fn extract_lldb_version(full_version_line: &str) -> Option<(u32, bool)> {
976 // Extract the major LLDB version from the given version string.
977 // LLDB version strings are different for Apple and non-Apple platforms.
978 // The Apple variant looks like this:
980 // LLDB-179.5 (older versions)
981 // lldb-300.2.51 (new versions)
983 // We are only interested in the major version number, so this function
984 // will return `Some(179)` and `Some(300)` respectively.
986 // Upstream versions look like:
987 // lldb version 6.0.1
989 // There doesn't seem to be a way to correlate the Apple version
990 // with the upstream version, and since the tests were originally
991 // written against Apple versions, we make a fake Apple version by
992 // multiplying the first number by 100. This is a hack, but
993 // normally fine because the only non-Apple version we test is
996 let full_version_line = full_version_line.trim();
998 if let Some(apple_ver) =
999 full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
1001 if let Some(idx) = apple_ver.find(not_a_digit) {
1002 let version: u32 = apple_ver[..idx].parse().unwrap();
1003 return Some((version, full_version_line.contains("rust-enabled")));
1005 } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
1006 if let Some(idx) = lldb_ver.find(not_a_digit) {
1007 let version: u32 = lldb_ver[..idx].parse().ok()?;
1008 return Some((version * 100, full_version_line.contains("rust-enabled")));
1014 fn not_a_digit(c: char) -> bool {