]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/main.rs
65f6bff7eaf55804249a795872f73026edc2a90c
[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, ErrorKind};
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|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 contents = match fs::read_to_string(&stamp_name) {
690         Ok(f) => f,
691         Err(ref e) if e.kind() == ErrorKind::InvalidData => panic!("Can't read stamp contents"),
692         Err(_) => return true,
693     };
694     let expected_hash = runtest::compute_stamp_hash(config);
695     if contents != expected_hash {
696         return true;
697     }
698
699     // Check timestamps.
700     let rust_src_dir = config
701         .find_rust_src_root()
702         .expect("Could not find Rust source root");
703     let stamp = mtime(&stamp_name);
704     let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)];
705     for aux in props.aux.iter() {
706         inputs.push(mtime(&testpaths
707             .file
708             .parent()
709             .unwrap()
710             .join("auxiliary")
711             .join(aux)));
712     }
713     // Relevant pretty printer files
714     let pretty_printer_files = [
715         "src/etc/debugger_pretty_printers_common.py",
716         "src/etc/gdb_load_rust_pretty_printers.py",
717         "src/etc/gdb_rust_pretty_printing.py",
718         "src/etc/lldb_batchmode.py",
719         "src/etc/lldb_rust_formatters.py",
720     ];
721     for pretty_printer_file in &pretty_printer_files {
722         inputs.push(mtime(&rust_src_dir.join(pretty_printer_file)));
723     }
724     let mut entries = config.run_lib_path.read_dir().unwrap().collect::<Vec<_>>();
725     while let Some(entry) = entries.pop() {
726         let entry = entry.unwrap();
727         let path = entry.path();
728         if entry.metadata().unwrap().is_file() {
729             inputs.push(mtime(&path));
730         } else {
731             entries.extend(path.read_dir().unwrap());
732         }
733     }
734     if let Some(ref rustdoc_path) = config.rustdoc_path {
735         inputs.push(mtime(&rustdoc_path));
736         inputs.push(mtime(&rust_src_dir.join("src/etc/htmldocck.py")));
737     }
738
739     // UI test files.
740     for extension in UI_EXTENSIONS {
741         let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
742         inputs.push(mtime(path));
743     }
744
745     inputs.iter().any(|input| *input > stamp)
746 }
747
748 fn mtime(path: &Path) -> FileTime {
749     fs::metadata(path)
750         .map(|f| FileTime::from_last_modification_time(&f))
751         .unwrap_or_else(|_| FileTime::zero())
752 }
753
754 fn make_test_name(
755     config: &Config,
756     testpaths: &TestPaths,
757     revision: Option<&String>,
758 ) -> test::TestName {
759     // Convert a complete path to something like
760     //
761     //    run-pass/foo/bar/baz.rs
762     let path = PathBuf::from(config.src_base.file_name().unwrap())
763         .join(&testpaths.relative_dir)
764         .join(&testpaths.file.file_name().unwrap());
765     let mode_suffix = match config.compare_mode {
766         Some(ref mode) => format!(" ({})", mode.to_str()),
767         None => String::new(),
768     };
769     test::DynTestName(format!(
770         "[{}{}] {}{}",
771         config.mode,
772         mode_suffix,
773         path.display(),
774         revision.map_or("".to_string(), |rev| format!("#{}", rev))
775     ))
776 }
777
778 fn make_test_closure(
779     config: &Config,
780     ignore: Ignore,
781     testpaths: &TestPaths,
782     revision: Option<&String>,
783 ) -> test::TestFn {
784     let mut config = config.clone();
785     if config.mode == DebugInfoBoth {
786         // If both gdb and lldb were ignored, then the test as a whole
787         // would be ignored.
788         if !ignore.can_run_gdb() {
789             config.mode = DebugInfoLldb;
790         } else if !ignore.can_run_lldb() {
791             config.mode = DebugInfoGdb;
792         }
793     }
794
795     let testpaths = testpaths.clone();
796     let revision = revision.cloned();
797     test::DynTestFn(Box::new(move || {
798         runtest::run(config, &testpaths, revision.as_ref().map(|s| s.as_str()))
799     }))
800 }
801
802 /// Returns true if the given target is an Android target for the
803 /// purposes of GDB testing.
804 fn is_android_gdb_target(target: &String) -> bool {
805     match &target[..] {
806         "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android" => true,
807         _ => false,
808     }
809 }
810
811 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
812 fn analyze_gdb(gdb: Option<String>, target: &String, android_cross_path: &PathBuf)
813                -> (Option<String>, Option<u32>, bool) {
814     #[cfg(not(windows))]
815     const GDB_FALLBACK: &str = "gdb";
816     #[cfg(windows)]
817     const GDB_FALLBACK: &str = "gdb.exe";
818
819     const MIN_GDB_WITH_RUST: u32 = 7011010;
820
821     let fallback_gdb = || {
822         if is_android_gdb_target(target) {
823             let mut gdb_path = match android_cross_path.to_str() {
824                 Some(x) => x.to_owned(),
825                 None => panic!("cannot find android cross path"),
826             };
827             gdb_path.push_str("/bin/gdb");
828             gdb_path
829         } else {
830             GDB_FALLBACK.to_owned()
831         }
832     };
833
834     let gdb = match gdb {
835         None => fallback_gdb(),
836         Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
837         Some(ref s) => s.to_owned(),
838     };
839
840     let mut version_line = None;
841     if let Ok(output) = Command::new(&gdb).arg("--version").output() {
842         if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
843             version_line = Some(first_line.to_string());
844         }
845     }
846
847     let version = match version_line {
848         Some(line) => extract_gdb_version(&line),
849         None => return (None, None, false),
850     };
851
852     let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
853
854     (Some(gdb), version, gdb_native_rust)
855 }
856
857 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
858     let full_version_line = full_version_line.trim();
859
860     // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
861     // of the ? sections being optional
862
863     // We will parse up to 3 digits for minor and patch, ignoring the date
864     // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
865
866     // don't start parsing in the middle of a number
867     let mut prev_was_digit = false;
868     for (pos, c) in full_version_line.char_indices() {
869         if prev_was_digit || !c.is_digit(10) {
870             prev_was_digit = c.is_digit(10);
871             continue;
872         }
873
874         prev_was_digit = true;
875
876         let line = &full_version_line[pos..];
877
878         let next_split = match line.find(|c: char| !c.is_digit(10)) {
879             Some(idx) => idx,
880             None => continue, // no minor version
881         };
882
883         if line.as_bytes()[next_split] != b'.' {
884             continue; // no minor version
885         }
886
887         let major = &line[..next_split];
888         let line = &line[next_split + 1..];
889
890         let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
891             Some(idx) => if line.as_bytes()[idx] == b'.' {
892                 let patch = &line[idx + 1..];
893
894                 let patch_len = patch
895                     .find(|c: char| !c.is_digit(10))
896                     .unwrap_or_else(|| patch.len());
897                 let patch = &patch[..patch_len];
898                 let patch = if patch_len > 3 || patch_len == 0 {
899                     None
900                 } else {
901                     Some(patch)
902                 };
903
904                 (&line[..idx], patch)
905             } else {
906                 (&line[..idx], None)
907             },
908             None => (line, None),
909         };
910
911         if major.len() != 1 || minor.is_empty() {
912             continue;
913         }
914
915         let major: u32 = major.parse().unwrap();
916         let minor: u32 = minor.parse().unwrap();
917         let patch: u32 = patch.unwrap_or("0").parse().unwrap();
918
919         return Some(((major * 1000) + minor) * 1000 + patch);
920     }
921
922     None
923 }
924
925 /// Returns (LLDB version, LLDB is rust-enabled)
926 fn extract_lldb_version(full_version_line: Option<String>) -> (Option<String>, bool) {
927     // Extract the major LLDB version from the given version string.
928     // LLDB version strings are different for Apple and non-Apple platforms.
929     // The Apple variant looks like this:
930     //
931     // LLDB-179.5 (older versions)
932     // lldb-300.2.51 (new versions)
933     //
934     // We are only interested in the major version number, so this function
935     // will return `Some("179")` and `Some("300")` respectively.
936     //
937     // Upstream versions look like:
938     // lldb version 6.0.1
939     //
940     // There doesn't seem to be a way to correlate the Apple version
941     // with the upstream version, and since the tests were originally
942     // written against Apple versions, we make a fake Apple version by
943     // multiplying the first number by 100.  This is a hack, but
944     // normally fine because the only non-Apple version we test is
945     // rust-enabled.
946
947     if let Some(ref full_version_line) = full_version_line {
948         if !full_version_line.trim().is_empty() {
949             let full_version_line = full_version_line.trim();
950
951             for (pos, l) in full_version_line.char_indices() {
952                 if l != 'l' && l != 'L' {
953                     continue;
954                 }
955                 if pos + 5 >= full_version_line.len() {
956                     continue;
957                 }
958                 let l = full_version_line[pos + 1..].chars().next().unwrap();
959                 if l != 'l' && l != 'L' {
960                     continue;
961                 }
962                 let d = full_version_line[pos + 2..].chars().next().unwrap();
963                 if d != 'd' && d != 'D' {
964                     continue;
965                 }
966                 let b = full_version_line[pos + 3..].chars().next().unwrap();
967                 if b != 'b' && b != 'B' {
968                     continue;
969                 }
970                 let dash = full_version_line[pos + 4..].chars().next().unwrap();
971                 if dash != '-' {
972                     continue;
973                 }
974
975                 let vers = full_version_line[pos + 5..]
976                     .chars()
977                     .take_while(|c| c.is_digit(10))
978                     .collect::<String>();
979                 if !vers.is_empty() {
980                     return (Some(vers), full_version_line.contains("rust-enabled"));
981                 }
982             }
983
984             if full_version_line.starts_with("lldb version ") {
985                 let vers = full_version_line[13..]
986                     .chars()
987                     .take_while(|c| c.is_digit(10))
988                     .collect::<String>();
989                 if !vers.is_empty() {
990                     return (Some(vers + "00"), full_version_line.contains("rust-enabled"));
991                 }
992             }
993         }
994     }
995     (None, false)
996 }
997
998 fn is_blacklisted_lldb_version(version: &str) -> bool {
999     version == "350"
1000 }
1001
1002 #[test]
1003 fn test_extract_gdb_version() {
1004     macro_rules! test { ($($expectation:tt: $input:tt,)*) => {{$(
1005         assert_eq!(extract_gdb_version($input), Some($expectation));
1006     )*}}}
1007
1008     test! {
1009         7000001: "GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)",
1010
1011         7002000: "GNU gdb (GDB) Red Hat Enterprise Linux (7.2-90.el6)",
1012
1013         7004000: "GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04",
1014         7004001: "GNU gdb (GDB) 7.4.1-debian",
1015
1016         7006001: "GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7",
1017
1018         7007001: "GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1",
1019         7007001: "GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1",
1020         7007001: "GNU gdb (GDB) Fedora 7.7.1-21.fc20",
1021
1022         7008000: "GNU gdb (GDB; openSUSE 13.2) 7.8",
1023         7009001: "GNU gdb (GDB) Fedora 7.9.1-20.fc22",
1024         7010001: "GNU gdb (GDB) Fedora 7.10.1-31.fc23",
1025
1026         7011000: "GNU gdb (Ubuntu 7.11-0ubuntu1) 7.11",
1027         7011001: "GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1",
1028         7011001: "GNU gdb (Debian 7.11.1-2) 7.11.1",
1029         7011001: "GNU gdb (GDB) Fedora 7.11.1-86.fc24",
1030         7011001: "GNU gdb (GDB; openSUSE Leap 42.1) 7.11.1",
1031         7011001: "GNU gdb (GDB; openSUSE Tumbleweed) 7.11.1",
1032
1033         7011090: "7.11.90",
1034         7011090: "GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1) 7.11.90.20161005-git",
1035
1036         7012000: "7.12",
1037         7012000: "GNU gdb (GDB) 7.12",
1038         7012000: "GNU gdb (GDB) 7.12.20161027-git",
1039         7012050: "GNU gdb (GDB) 7.12.50.20161027-git",
1040     }
1041 }