]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/main.rs
c931d3c0e30b7ab8146e960971ecd2627d5099ff
[rust.git] / src / tools / compiletest / src / main.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![crate_name = "compiletest"]
12 #![feature(test)]
13 #![deny(warnings)]
14
15 extern crate diff;
16 extern crate env_logger;
17 extern crate filetime;
18 extern crate getopts;
19 #[cfg(unix)]
20 extern crate libc;
21 #[macro_use]
22 extern crate log;
23 extern crate regex;
24 #[macro_use]
25 extern crate lazy_static;
26 #[macro_use]
27 extern crate serde_derive;
28 extern crate serde_json;
29 extern crate test;
30 extern crate rustfix;
31
32 use common::CompareMode;
33 use common::{expected_output_path, output_base_dir, output_relative_path, UI_EXTENSIONS};
34 use common::{Config, TestPaths};
35 use common::{DebugInfoBoth, DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
36 use filetime::FileTime;
37 use getopts::Options;
38 use std::env;
39 use std::ffi::OsString;
40 use std::fs;
41 use std::io::{self, Read};
42 use std::path::{Path, PathBuf};
43 use std::process::Command;
44 use test::ColorConfig;
45 use util::logv;
46
47 use self::header::{EarlyProps, Ignore};
48
49 pub mod common;
50 pub mod errors;
51 pub mod header;
52 mod json;
53 mod raise_fd_limit;
54 mod read2;
55 pub mod runtest;
56 pub mod util;
57
58 fn main() {
59     env_logger::init();
60
61     let config = parse_config(env::args().collect());
62
63     if config.valgrind_path.is_none() && config.force_valgrind {
64         panic!("Can't find Valgrind to run Valgrind tests");
65     }
66
67     log_config(&config);
68     run_tests(&config);
69 }
70
71 pub fn parse_config(args: Vec<String>) -> Config {
72     let mut opts = Options::new();
73     opts.reqopt(
74         "",
75         "compile-lib-path",
76         "path to host shared libraries",
77         "PATH",
78     ).reqopt(
79             "",
80             "run-lib-path",
81             "path to target shared libraries",
82             "PATH",
83         )
84         .reqopt(
85             "",
86             "rustc-path",
87             "path to rustc to use for compiling",
88             "PATH",
89         )
90         .optopt(
91             "",
92             "rustdoc-path",
93             "path to rustdoc to use for compiling",
94             "PATH",
95         )
96         .reqopt(
97             "",
98             "lldb-python",
99             "path to python to use for doc tests",
100             "PATH",
101         )
102         .reqopt(
103             "",
104             "docck-python",
105             "path to python to use for doc tests",
106             "PATH",
107         )
108         .optopt(
109             "",
110             "valgrind-path",
111             "path to Valgrind executable for Valgrind tests",
112             "PROGRAM",
113         )
114         .optflag(
115             "",
116             "force-valgrind",
117             "fail if Valgrind tests cannot be run under Valgrind",
118         )
119         .optopt(
120             "",
121             "llvm-filecheck",
122             "path to LLVM's FileCheck binary",
123             "DIR",
124         )
125         .reqopt("", "src-base", "directory to scan for test files", "PATH")
126         .reqopt(
127             "",
128             "build-base",
129             "directory to deposit test outputs",
130             "PATH",
131         )
132         .reqopt(
133             "",
134             "stage-id",
135             "the target-stage identifier",
136             "stageN-TARGET",
137         )
138         .reqopt(
139             "",
140             "mode",
141             "which sort of compile tests to run",
142             "(compile-fail|parse-fail|run-fail|run-pass|\
143              run-pass-valgrind|pretty|debug-info|incremental|mir-opt)",
144         )
145         .optflag("", "ignored", "run tests marked as ignored")
146         .optflag("", "exact", "filters match exactly")
147         .optopt(
148             "",
149             "runtool",
150             "supervisor program to run tests under \
151              (eg. emulator, valgrind)",
152             "PROGRAM",
153         )
154         .optopt(
155             "",
156             "host-rustcflags",
157             "flags to pass to rustc for host",
158             "FLAGS",
159         )
160         .optopt(
161             "",
162             "target-rustcflags",
163             "flags to pass to rustc for target",
164             "FLAGS",
165         )
166         .optflag("", "verbose", "run tests verbosely, showing all output")
167         .optflag(
168             "",
169             "bless",
170             "overwrite stderr/stdout files instead of complaining about a mismatch",
171         )
172         .optflag(
173             "",
174             "quiet",
175             "print one character per test instead of one line",
176         )
177         .optopt("", "color", "coloring: auto, always, never", "WHEN")
178         .optopt("", "logfile", "file to log test execution to", "FILE")
179         .optopt("", "target", "the target to build for", "TARGET")
180         .optopt("", "host", "the host to build for", "HOST")
181         .optopt(
182             "",
183             "gdb",
184             "path to GDB to use for GDB debuginfo tests",
185             "PATH",
186         )
187         .optopt(
188             "",
189             "lldb-version",
190             "the version of LLDB used",
191             "VERSION STRING",
192         )
193         .optopt(
194             "",
195             "llvm-version",
196             "the version of LLVM used",
197             "VERSION STRING",
198         )
199         .optflag("", "system-llvm", "is LLVM the system LLVM")
200         .optopt(
201             "",
202             "android-cross-path",
203             "Android NDK standalone path",
204             "PATH",
205         )
206         .optopt("", "adb-path", "path to the android debugger", "PATH")
207         .optopt(
208             "",
209             "adb-test-dir",
210             "path to tests for the android debugger",
211             "PATH",
212         )
213         .optopt(
214             "",
215             "lldb-python-dir",
216             "directory containing LLDB's python module",
217             "PATH",
218         )
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")
224         .reqopt(
225             "",
226             "llvm-components",
227             "list of LLVM components built in",
228             "LIST",
229         )
230         .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS")
231         .optopt("", "nodejs", "the name of nodejs", "PATH")
232         .optopt(
233             "",
234             "remote-test-client",
235             "path to the remote test client",
236             "PATH",
237         )
238         .optopt(
239             "",
240             "compare-mode",
241             "mode describing what file the actual ui output will be compared to",
242             "COMPARE MODE",
243         )
244         .optflag("h", "help", "show this message");
245
246     let (argv0, args_) = args.split_first().unwrap();
247     if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
248         let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
249         println!("{}", opts.usage(&message));
250         println!("");
251         panic!()
252     }
253
254     let matches = &match opts.parse(args_) {
255         Ok(m) => m,
256         Err(f) => panic!("{:?}", f),
257     };
258
259     if matches.opt_present("h") || matches.opt_present("help") {
260         let message = format!("Usage: {} [OPTIONS]  [TESTNAME...]", argv0);
261         println!("{}", opts.usage(&message));
262         println!("");
263         panic!()
264     }
265
266     fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
267         match m.opt_str(nm) {
268             Some(s) => PathBuf::from(&s),
269             None => panic!("no option (=path) found for {}", nm),
270         }
271     }
272
273     fn make_absolute(path: PathBuf) -> PathBuf {
274         if path.is_relative() {
275             env::current_dir().unwrap().join(path)
276         } else {
277             path
278         }
279     }
280
281     let target = opt_str2(matches.opt_str("target"));
282     let android_cross_path = opt_path(matches, "android-cross-path");
283     let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"), &target,
284                                                           &android_cross_path);
285     let (lldb_version, lldb_native_rust) = extract_lldb_version(matches.opt_str("lldb-version"));
286
287     let color = match matches.opt_str("color").as_ref().map(|x| &**x) {
288         Some("auto") | None => ColorConfig::AutoColor,
289         Some("always") => ColorConfig::AlwaysColor,
290         Some("never") => ColorConfig::NeverColor,
291         Some(x) => panic!(
292             "argument for --color must be auto, always, or never, but found `{}`",
293             x
294         ),
295     };
296
297     let src_base = opt_path(matches, "src-base");
298     let run_ignored = matches.opt_present("ignored");
299     Config {
300         bless: matches.opt_present("bless"),
301         compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
302         run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
303         rustc_path: opt_path(matches, "rustc-path"),
304         rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
305         lldb_python: matches.opt_str("lldb-python").unwrap(),
306         docck_python: matches.opt_str("docck-python").unwrap(),
307         valgrind_path: matches.opt_str("valgrind-path"),
308         force_valgrind: matches.opt_present("force-valgrind"),
309         llvm_filecheck: matches.opt_str("llvm-filecheck").map(|s| PathBuf::from(&s)),
310         src_base,
311         build_base: opt_path(matches, "build-base"),
312         stage_id: matches.opt_str("stage-id").unwrap(),
313         mode: matches
314             .opt_str("mode")
315             .unwrap()
316             .parse()
317             .expect("invalid mode"),
318         run_ignored,
319         filter: matches.free.first().cloned(),
320         filter_exact: matches.opt_present("exact"),
321         logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
322         runtool: matches.opt_str("runtool"),
323         host_rustcflags: matches.opt_str("host-rustcflags"),
324         target_rustcflags: matches.opt_str("target-rustcflags"),
325         target: target,
326         host: opt_str2(matches.opt_str("host")),
327         gdb,
328         gdb_version,
329         gdb_native_rust,
330         lldb_version,
331         lldb_native_rust,
332         llvm_version: matches.opt_str("llvm-version"),
333         system_llvm: matches.opt_present("system-llvm"),
334         android_cross_path: android_cross_path,
335         adb_path: opt_str2(matches.opt_str("adb-path")),
336         adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
337         adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
338             && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
339             && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
340         lldb_python_dir: matches.opt_str("lldb-python-dir"),
341         verbose: matches.opt_present("verbose"),
342         quiet: matches.opt_present("quiet"),
343         color,
344         remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
345         compare_mode: matches.opt_str("compare-mode").map(CompareMode::parse),
346
347         cc: matches.opt_str("cc").unwrap(),
348         cxx: matches.opt_str("cxx").unwrap(),
349         cflags: matches.opt_str("cflags").unwrap(),
350         ar: matches.opt_str("ar").unwrap_or("ar".into()),
351         linker: matches.opt_str("linker"),
352         llvm_components: matches.opt_str("llvm-components").unwrap(),
353         llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
354         nodejs: matches.opt_str("nodejs"),
355     }
356 }
357
358 pub fn log_config(config: &Config) {
359     let c = config;
360     logv(c, "configuration:".to_string());
361     logv(
362         c,
363         format!("compile_lib_path: {:?}", config.compile_lib_path),
364     );
365     logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
366     logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
367     logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
368     logv(c, format!("src_base: {:?}", config.src_base.display()));
369     logv(c, format!("build_base: {:?}", config.build_base.display()));
370     logv(c, format!("stage_id: {}", config.stage_id));
371     logv(c, format!("mode: {}", config.mode));
372     logv(c, format!("run_ignored: {}", config.run_ignored));
373     logv(
374         c,
375         format!(
376             "filter: {}",
377             opt_str(&config.filter.as_ref().map(|re| re.to_owned()))
378         ),
379     );
380     logv(c, format!("filter_exact: {}", config.filter_exact));
381     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
382     logv(
383         c,
384         format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)),
385     );
386     logv(
387         c,
388         format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)),
389     );
390     logv(c, format!("target: {}", config.target));
391     logv(c, format!("host: {}", config.host));
392     logv(
393         c,
394         format!(
395             "android-cross-path: {:?}",
396             config.android_cross_path.display()
397         ),
398     );
399     logv(c, format!("adb_path: {:?}", config.adb_path));
400     logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
401     logv(
402         c,
403         format!("adb_device_status: {}", config.adb_device_status),
404     );
405     logv(c, format!("ar: {}", config.ar));
406     logv(c, format!("linker: {:?}", config.linker));
407     logv(c, format!("verbose: {}", config.verbose));
408     logv(c, format!("quiet: {}", config.quiet));
409     logv(c, "\n".to_string());
410 }
411
412 pub fn opt_str(maybestr: &Option<String>) -> &str {
413     match *maybestr {
414         None => "(none)",
415         Some(ref s) => s,
416     }
417 }
418
419 pub fn opt_str2(maybestr: Option<String>) -> String {
420     match maybestr {
421         None => "(none)".to_owned(),
422         Some(s) => s,
423     }
424 }
425
426 pub fn run_tests(config: &Config) {
427     if config.target.contains("android") {
428         if config.mode == DebugInfoGdb || config.mode == DebugInfoBoth {
429             println!(
430                 "{} debug-info test uses tcp 5039 port.\
431                  please reserve it",
432                 config.target
433             );
434
435             // android debug-info test uses remote debugger so, we test 1 thread
436             // at once as they're all sharing the same TCP port to communicate
437             // over.
438             //
439             // we should figure out how to lift this restriction! (run them all
440             // on different ports allocated dynamically).
441             env::set_var("RUST_TEST_THREADS", "1");
442         }
443     }
444
445     match config.mode {
446         // Note that we don't need to emit the gdb warning when
447         // DebugInfoBoth, so it is ok to list that here.
448         DebugInfoBoth | DebugInfoLldb => {
449             if let Some(lldb_version) = config.lldb_version.as_ref() {
450                 if is_blacklisted_lldb_version(&lldb_version[..]) {
451                     println!(
452                         "WARNING: The used version of LLDB ({}) has a \
453                          known issue that breaks debuginfo tests. See \
454                          issue #32520 for more information. Skipping all \
455                          LLDB-based tests!",
456                         lldb_version
457                     );
458                     return;
459                 }
460             }
461
462             // Some older versions of LLDB seem to have problems with multiple
463             // instances running in parallel, so only run one test thread at a
464             // time.
465             env::set_var("RUST_TEST_THREADS", "1");
466         }
467
468         DebugInfoGdb => {
469             if config.remote_test_client.is_some() && !config.target.contains("android") {
470                 println!(
471                     "WARNING: debuginfo tests are not available when \
472                      testing with remote"
473                 );
474                 return;
475             }
476         }
477         _ => { /* proceed */ }
478     }
479
480     // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
481     if let Mode::CodegenUnits = config.mode {
482         let _ = fs::remove_dir_all("tmp/partitioning-tests");
483     }
484
485     let opts = test_opts(config);
486     let tests = make_tests(config);
487     // sadly osx needs some file descriptor limits raised for running tests in
488     // parallel (especially when we have lots and lots of child processes).
489     // For context, see #8904
490     unsafe {
491         raise_fd_limit::raise_fd_limit();
492     }
493     // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
494     // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
495     env::set_var("__COMPAT_LAYER", "RunAsInvoker");
496
497     // Let tests know which target they're running as
498     env::set_var("TARGET", &config.target);
499
500     let res = test::run_tests_console(&opts, tests.into_iter().collect());
501     match res {
502         Ok(true) => {}
503         Ok(false) => panic!("Some tests failed"),
504         Err(e) => {
505             println!("I/O failure during tests: {:?}", e);
506         }
507     }
508 }
509
510 pub fn test_opts(config: &Config) -> test::TestOpts {
511     test::TestOpts {
512         filter: config.filter.clone(),
513         filter_exact: config.filter_exact,
514         run_ignored: config.run_ignored,
515         format: if config.quiet {
516             test::OutputFormat::Terse
517         } else {
518             test::OutputFormat::Pretty
519         },
520         logfile: config.logfile.clone(),
521         run_tests: true,
522         bench_benchmarks: true,
523         nocapture: match env::var("RUST_TEST_NOCAPTURE") {
524             Ok(val) => &val != "0",
525             Err(_) => false,
526         },
527         color: config.color,
528         test_threads: None,
529         skip: vec![],
530         list: false,
531         options: test::Options::new(),
532     }
533 }
534
535 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
536     debug!("making tests from {:?}", config.src_base.display());
537     let mut tests = Vec::new();
538     collect_tests_from_dir(
539         config,
540         &config.src_base,
541         &config.src_base,
542         &PathBuf::new(),
543         &mut tests,
544     ).unwrap();
545     tests
546 }
547
548 fn collect_tests_from_dir(
549     config: &Config,
550     base: &Path,
551     dir: &Path,
552     relative_dir_path: &Path,
553     tests: &mut Vec<test::TestDescAndFn>,
554 ) -> io::Result<()> {
555     // Ignore directories that contain a file
556     // `compiletest-ignore-dir`.
557     for file in fs::read_dir(dir)? {
558         let file = file?;
559         let name = file.file_name();
560         if name == *"compiletest-ignore-dir" {
561             return Ok(());
562         }
563         if name == *"Makefile" && config.mode == Mode::RunMake {
564             let paths = TestPaths {
565                 file: dir.to_path_buf(),
566                 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
567             };
568             tests.extend(make_test(config, &paths));
569             return Ok(());
570         }
571     }
572
573     // If we find a test foo/bar.rs, we have to build the
574     // output directory `$build/foo` so we can write
575     // `$build/foo/bar` into it. We do this *now* in this
576     // sequential loop because otherwise, if we do it in the
577     // tests themselves, they race for the privilege of
578     // creating the directories and sometimes fail randomly.
579     let build_dir = output_relative_path(config, relative_dir_path);
580     fs::create_dir_all(&build_dir).unwrap();
581
582     // Add each `.rs` file as a test, and recurse further on any
583     // subdirectories we find, except for `aux` directories.
584     let dirs = fs::read_dir(dir)?;
585     for file in dirs {
586         let file = file?;
587         let file_path = file.path();
588         let file_name = file.file_name();
589         if is_test(&file_name) {
590             debug!("found test file: {:?}", file_path.display());
591             let paths = TestPaths {
592                 file: file_path,
593                 relative_dir: relative_dir_path.to_path_buf(),
594             };
595             tests.extend(make_test(config, &paths))
596         } else if file_path.is_dir() {
597             let relative_file_path = relative_dir_path.join(file.file_name());
598             if &file_name != "auxiliary" {
599                 debug!("found directory: {:?}", file_path.display());
600                 collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?;
601             }
602         } else {
603             debug!("found other file/directory: {:?}", file_path.display());
604         }
605     }
606     Ok(())
607 }
608
609 pub fn is_test(file_name: &OsString) -> bool {
610     let file_name = file_name.to_str().unwrap();
611
612     if !file_name.ends_with(".rs") {
613         return false;
614     }
615
616     // `.`, `#`, and `~` are common temp-file prefixes.
617     let invalid_prefixes = &[".", "#", "~"];
618     !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
619 }
620
621 pub fn make_test(config: &Config, testpaths: &TestPaths) -> Vec<test::TestDescAndFn> {
622     let early_props = if config.mode == Mode::RunMake {
623         // Allow `ignore` directives to be in the Makefile.
624         EarlyProps::from_file(config, &testpaths.file.join("Makefile"))
625     } else {
626         EarlyProps::from_file(config, &testpaths.file)
627     };
628
629     // The `should-fail` annotation doesn't apply to pretty tests,
630     // since we run the pretty printer across all tests by default.
631     // If desired, we could add a `should-fail-pretty` annotation.
632     let should_panic = match config.mode {
633         Pretty => test::ShouldPanic::No,
634         _ => if early_props.should_fail {
635             test::ShouldPanic::Yes
636         } else {
637             test::ShouldPanic::No
638         },
639     };
640
641     // Incremental tests are special, they inherently cannot be run in parallel.
642     // `runtest::run` will be responsible for iterating over revisions.
643     let revisions = if early_props.revisions.is_empty() || config.mode == Mode::Incremental {
644         vec![None]
645     } else {
646         early_props.revisions.iter().map(|r| Some(r)).collect()
647     };
648     revisions
649         .into_iter()
650         .map(|revision| {
651             // Debugging emscripten code doesn't make sense today
652             let ignore = early_props.ignore == Ignore::Ignore
653                 || !up_to_date(
654                     config,
655                     testpaths,
656                     &early_props,
657                     revision.map(|s| s.as_str()),
658                 )
659                 || ((config.mode == DebugInfoBoth ||
660                      config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
661                     && config.target.contains("emscripten"))
662                 || (config.mode == DebugInfoGdb && !early_props.ignore.can_run_gdb())
663                 || (config.mode == DebugInfoLldb && !early_props.ignore.can_run_lldb());
664             test::TestDescAndFn {
665                 desc: test::TestDesc {
666                     name: make_test_name(config, testpaths, revision),
667                     ignore,
668                     should_panic,
669                     allow_fail: false,
670                 },
671                 testfn: make_test_closure(config, early_props.ignore, testpaths, revision),
672             }
673         })
674         .collect()
675 }
676
677 fn stamp(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
678     output_base_dir(config, testpaths, revision).join("stamp")
679 }
680
681 fn up_to_date(
682     config: &Config,
683     testpaths: &TestPaths,
684     props: &EarlyProps,
685     revision: Option<&str>,
686 ) -> bool {
687     let stamp_name = stamp(config, testpaths, revision);
688     // Check hash.
689     let mut f = match fs::File::open(&stamp_name) {
690         Ok(f) => f,
691         Err(_) => return true,
692     };
693     let mut contents = String::new();
694     f.read_to_string(&mut contents)
695         .expect("Can't read stamp contents");
696     let expected_hash = runtest::compute_stamp_hash(config);
697     if contents != expected_hash {
698         return true;
699     }
700
701     // Check timestamps.
702     let rust_src_dir = config
703         .find_rust_src_root()
704         .expect("Could not find Rust source root");
705     let stamp = mtime(&stamp_name);
706     let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)];
707     for aux in props.aux.iter() {
708         inputs.push(mtime(&testpaths
709             .file
710             .parent()
711             .unwrap()
712             .join("auxiliary")
713             .join(aux)));
714     }
715     // Relevant pretty printer files
716     let pretty_printer_files = [
717         "src/etc/debugger_pretty_printers_common.py",
718         "src/etc/gdb_load_rust_pretty_printers.py",
719         "src/etc/gdb_rust_pretty_printing.py",
720         "src/etc/lldb_batchmode.py",
721         "src/etc/lldb_rust_formatters.py",
722     ];
723     for pretty_printer_file in &pretty_printer_files {
724         inputs.push(mtime(&rust_src_dir.join(pretty_printer_file)));
725     }
726     let mut entries = config.run_lib_path.read_dir().unwrap().collect::<Vec<_>>();
727     while let Some(entry) = entries.pop() {
728         let entry = entry.unwrap();
729         let path = entry.path();
730         if entry.metadata().unwrap().is_file() {
731             inputs.push(mtime(&path));
732         } else {
733             entries.extend(path.read_dir().unwrap());
734         }
735     }
736     if let Some(ref rustdoc_path) = config.rustdoc_path {
737         inputs.push(mtime(&rustdoc_path));
738         inputs.push(mtime(&rust_src_dir.join("src/etc/htmldocck.py")));
739     }
740
741     // UI test files.
742     for extension in UI_EXTENSIONS {
743         let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
744         inputs.push(mtime(path));
745     }
746
747     inputs.iter().any(|input| *input > stamp)
748 }
749
750 fn mtime(path: &Path) -> FileTime {
751     fs::metadata(path)
752         .map(|f| FileTime::from_last_modification_time(&f))
753         .unwrap_or_else(|_| FileTime::zero())
754 }
755
756 fn make_test_name(
757     config: &Config,
758     testpaths: &TestPaths,
759     revision: Option<&String>,
760 ) -> test::TestName {
761     // Convert a complete path to something like
762     //
763     //    run-pass/foo/bar/baz.rs
764     let path = PathBuf::from(config.src_base.file_name().unwrap())
765         .join(&testpaths.relative_dir)
766         .join(&testpaths.file.file_name().unwrap());
767     let mode_suffix = match config.compare_mode {
768         Some(ref mode) => format!(" ({})", mode.to_str()),
769         None => format!(""),
770     };
771     test::DynTestName(format!(
772         "[{}{}] {}{}",
773         config.mode,
774         mode_suffix,
775         path.display(),
776         revision.map_or("".to_string(), |rev| format!("#{}", rev))
777     ))
778 }
779
780 fn make_test_closure(
781     config: &Config,
782     ignore: Ignore,
783     testpaths: &TestPaths,
784     revision: Option<&String>,
785 ) -> test::TestFn {
786     let mut config = config.clone();
787     if config.mode == DebugInfoBoth {
788         // If both gdb and lldb were ignored, then the test as a whole
789         // would be ignored.
790         if !ignore.can_run_gdb() {
791             config.mode = DebugInfoLldb;
792         } else if !ignore.can_run_lldb() {
793             config.mode = DebugInfoGdb;
794         }
795     }
796
797     let testpaths = testpaths.clone();
798     let revision = revision.cloned();
799     test::DynTestFn(Box::new(move || {
800         runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str()))
801     }))
802 }
803
804 /// Returns true if the given target is an Android target for the
805 /// purposes of GDB testing.
806 fn is_android_gdb_target(target: &String) -> bool {
807     match &target[..] {
808         "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => true,
809         _ => false,
810     }
811 }
812
813 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
814 fn analyze_gdb(gdb: Option<String>, target: &String, android_cross_path: &PathBuf)
815                -> (Option<String>, Option<u32>, bool) {
816     #[cfg(not(windows))]
817     const GDB_FALLBACK: &str = "gdb";
818     #[cfg(windows)]
819     const GDB_FALLBACK: &str = "gdb.exe";
820
821     const MIN_GDB_WITH_RUST: u32 = 7011010;
822
823     let fallback_gdb = || {
824         if is_android_gdb_target(target) {
825             let mut gdb_path = match android_cross_path.to_str() {
826                 Some(x) => x.to_owned(),
827                 None => panic!("cannot find android cross path"),
828             };
829             gdb_path.push_str("/bin/gdb");
830             gdb_path
831         } else {
832             GDB_FALLBACK.to_owned()
833         }
834     };
835
836     let gdb = match gdb {
837         None => fallback_gdb(),
838         Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
839         Some(ref s) => s.to_owned(),
840     };
841
842     let mut version_line = None;
843     if let Ok(output) = Command::new(&gdb).arg("--version").output() {
844         if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
845             version_line = Some(first_line.to_string());
846         }
847     }
848
849     let version = match version_line {
850         Some(line) => extract_gdb_version(&line),
851         None => return (None, None, false),
852     };
853
854     let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
855
856     (Some(gdb), version, gdb_native_rust)
857 }
858
859 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
860     let full_version_line = full_version_line.trim();
861
862     // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
863     // of the ? sections being optional
864
865     // We will parse up to 3 digits for minor and patch, ignoring the date
866     // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
867
868     // don't start parsing in the middle of a number
869     let mut prev_was_digit = false;
870     for (pos, c) in full_version_line.char_indices() {
871         if prev_was_digit || !c.is_digit(10) {
872             prev_was_digit = c.is_digit(10);
873             continue;
874         }
875
876         prev_was_digit = true;
877
878         let line = &full_version_line[pos..];
879
880         let next_split = match line.find(|c: char| !c.is_digit(10)) {
881             Some(idx) => idx,
882             None => continue, // no minor version
883         };
884
885         if line.as_bytes()[next_split] != b'.' {
886             continue; // no minor version
887         }
888
889         let major = &line[..next_split];
890         let line = &line[next_split + 1..];
891
892         let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
893             Some(idx) => if line.as_bytes()[idx] == b'.' {
894                 let patch = &line[idx + 1..];
895
896                 let patch_len = patch
897                     .find(|c: char| !c.is_digit(10))
898                     .unwrap_or_else(|| patch.len());
899                 let patch = &patch[..patch_len];
900                 let patch = if patch_len > 3 || patch_len == 0 {
901                     None
902                 } else {
903                     Some(patch)
904                 };
905
906                 (&line[..idx], patch)
907             } else {
908                 (&line[..idx], None)
909             },
910             None => (line, None),
911         };
912
913         if major.len() != 1 || minor.is_empty() {
914             continue;
915         }
916
917         let major: u32 = major.parse().unwrap();
918         let minor: u32 = minor.parse().unwrap();
919         let patch: u32 = patch.unwrap_or("0").parse().unwrap();
920
921         return Some(((major * 1000) + minor) * 1000 + patch);
922     }
923
924     None
925 }
926
927 /// Returns (LLDB version, LLDB is rust-enabled)
928 fn extract_lldb_version(full_version_line: Option<String>) -> (Option<String>, bool) {
929     // Extract the major LLDB version from the given version string.
930     // LLDB version strings are different for Apple and non-Apple platforms.
931     // The Apple variant looks like this:
932     //
933     // LLDB-179.5 (older versions)
934     // lldb-300.2.51 (new versions)
935     //
936     // We are only interested in the major version number, so this function
937     // will return `Some("179")` and `Some("300")` respectively.
938     //
939     // Upstream versions look like:
940     // lldb version 6.0.1
941     //
942     // There doesn't seem to be a way to correlate the Apple version
943     // with the upstream version, and since the tests were originally
944     // written against Apple versions, we make a fake Apple version by
945     // multiplying the first number by 100.  This is a hack, but
946     // normally fine because the only non-Apple version we test is
947     // rust-enabled.
948
949     if let Some(ref full_version_line) = full_version_line {
950         if !full_version_line.trim().is_empty() {
951             let full_version_line = full_version_line.trim();
952
953             for (pos, l) in full_version_line.char_indices() {
954                 if l != 'l' && l != 'L' {
955                     continue;
956                 }
957                 if pos + 5 >= full_version_line.len() {
958                     continue;
959                 }
960                 let l = full_version_line[pos + 1..].chars().next().unwrap();
961                 if l != 'l' && l != 'L' {
962                     continue;
963                 }
964                 let d = full_version_line[pos + 2..].chars().next().unwrap();
965                 if d != 'd' && d != 'D' {
966                     continue;
967                 }
968                 let b = full_version_line[pos + 3..].chars().next().unwrap();
969                 if b != 'b' && b != 'B' {
970                     continue;
971                 }
972                 let dash = full_version_line[pos + 4..].chars().next().unwrap();
973                 if dash != '-' {
974                     continue;
975                 }
976
977                 let vers = full_version_line[pos + 5..]
978                     .chars()
979                     .take_while(|c| c.is_digit(10))
980                     .collect::<String>();
981                 if !vers.is_empty() {
982                     return (Some(vers), full_version_line.contains("rust-enabled"));
983                 }
984             }
985
986             if full_version_line.starts_with("lldb version ") {
987                 let vers = full_version_line[13..]
988                     .chars()
989                     .take_while(|c| c.is_digit(10))
990                     .collect::<String>();
991                 if !vers.is_empty() {
992                     return (Some(vers + "00"), full_version_line.contains("rust-enabled"));
993                 }
994             }
995         }
996     }
997     (None, false)
998 }
999
1000 fn is_blacklisted_lldb_version(version: &str) -> bool {
1001     version == "350"
1002 }
1003
1004 #[test]
1005 fn test_extract_gdb_version() {
1006     macro_rules! test { ($($expectation:tt: $input:tt,)*) => {{$(
1007         assert_eq!(extract_gdb_version($input), Some($expectation));
1008     )*}}}
1009
1010     test! {
1011         7000001: "GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)",
1012
1013         7002000: "GNU gdb (GDB) Red Hat Enterprise Linux (7.2-90.el6)",
1014
1015         7004000: "GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04",
1016         7004001: "GNU gdb (GDB) 7.4.1-debian",
1017
1018         7006001: "GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7",
1019
1020         7007001: "GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1",
1021         7007001: "GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1",
1022         7007001: "GNU gdb (GDB) Fedora 7.7.1-21.fc20",
1023
1024         7008000: "GNU gdb (GDB; openSUSE 13.2) 7.8",
1025         7009001: "GNU gdb (GDB) Fedora 7.9.1-20.fc22",
1026         7010001: "GNU gdb (GDB) Fedora 7.10.1-31.fc23",
1027
1028         7011000: "GNU gdb (Ubuntu 7.11-0ubuntu1) 7.11",
1029         7011001: "GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1",
1030         7011001: "GNU gdb (Debian 7.11.1-2) 7.11.1",
1031         7011001: "GNU gdb (GDB) Fedora 7.11.1-86.fc24",
1032         7011001: "GNU gdb (GDB; openSUSE Leap 42.1) 7.11.1",
1033         7011001: "GNU gdb (GDB; openSUSE Tumbleweed) 7.11.1",
1034
1035         7011090: "7.11.90",
1036         7011090: "GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1) 7.11.90.20161005-git",
1037
1038         7012000: "7.12",
1039         7012000: "GNU gdb (GDB) 7.12",
1040         7012000: "GNU gdb (GDB) 7.12.20161027-git",
1041         7012050: "GNU gdb (GDB) 7.12.50.20161027-git",
1042     }
1043 }