]> git.proxmox.com Git - rustc.git/blob - src/tools/compiletest/src/main.rs
New upstream version 1.31.0~beta.4+dfsg1
[rustc.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|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 }