]> git.lizzy.rs Git - rust.git/blob - src/tools/compiletest/src/main.rs
Rollup merge of #36223 - abhiQmar:e0558-formatting, r=jonathandturner
[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
13 #![feature(box_syntax)]
14 #![feature(rustc_private)]
15 #![feature(test)]
16 #![feature(question_mark)]
17 #![feature(libc)]
18
19 #![deny(warnings)]
20
21 extern crate libc;
22 extern crate test;
23 extern crate getopts;
24 extern crate serialize as rustc_serialize;
25
26 #[macro_use]
27 extern crate log;
28
29 #[cfg(cargobuild)]
30 extern crate env_logger;
31
32 use std::env;
33 use std::ffi::OsString;
34 use std::fs;
35 use std::io;
36 use std::path::{Path, PathBuf};
37 use getopts::{optopt, optflag, reqopt};
38 use common::Config;
39 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Mode};
40 use test::TestPaths;
41 use util::logv;
42
43 use self::header::EarlyProps;
44
45 pub mod procsrv;
46 pub mod util;
47 mod json;
48 pub mod header;
49 pub mod runtest;
50 pub mod common;
51 pub mod errors;
52 mod raise_fd_limit;
53 mod uidiff;
54
55 fn main() {
56     #[cfg(cargobuild)]
57     fn log_init() { env_logger::init().unwrap(); }
58     #[cfg(not(cargobuild))]
59     fn log_init() {}
60     log_init();
61
62     let config = parse_config(env::args().collect());
63
64     if config.valgrind_path.is_none() && config.force_valgrind {
65         panic!("Can't find Valgrind to run Valgrind tests");
66     }
67
68     log_config(&config);
69     run_tests(&config);
70 }
71
72 pub fn parse_config(args: Vec<String> ) -> Config {
73
74     let groups : Vec<getopts::OptGroup> =
75         vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
76           reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
77           reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
78           reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"),
79           reqopt("", "lldb-python", "path to python to use for doc tests", "PATH"),
80           reqopt("", "docck-python", "path to python to use for doc tests", "PATH"),
81           optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"),
82           optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"),
83           optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR"),
84           reqopt("", "src-base", "directory to scan for test files", "PATH"),
85           reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
86           reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
87           reqopt("", "mode", "which sort of compile tests to run",
88                  "(compile-fail|parse-fail|run-fail|run-pass|\
89                   run-pass-valgrind|pretty|debug-info|incremental|mir-opt)"),
90           optflag("", "ignored", "run tests marked as ignored"),
91           optopt("", "runtool", "supervisor program to run tests under \
92                                  (eg. emulator, valgrind)", "PROGRAM"),
93           optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
94           optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
95           optflag("", "verbose", "run tests verbosely, showing all output"),
96           optflag("", "quiet", "print one character per test instead of one line"),
97           optopt("", "logfile", "file to log test execution to", "FILE"),
98           optopt("", "target", "the target to build for", "TARGET"),
99           optopt("", "host", "the host to build for", "HOST"),
100           optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"),
101           optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
102           optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING"),
103           optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
104           optopt("", "adb-path", "path to the android debugger", "PATH"),
105           optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
106           optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
107           reqopt("", "cc", "path to a C compiler", "PATH"),
108           reqopt("", "cxx", "path to a C++ compiler", "PATH"),
109           reqopt("", "cflags", "flags for the C compiler", "FLAGS"),
110           reqopt("", "llvm-components", "list of LLVM components built in", "LIST"),
111           reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS"),
112           optflag("h", "help", "show this message"));
113
114     let (argv0, args_) = args.split_first().unwrap();
115     if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
116         let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
117         println!("{}", getopts::usage(&message, &groups));
118         println!("");
119         panic!()
120     }
121
122     let matches =
123         &match getopts::getopts(args_, &groups) {
124           Ok(m) => m,
125           Err(f) => panic!("{:?}", f)
126         };
127
128     if matches.opt_present("h") || matches.opt_present("help") {
129         let message = format!("Usage: {} [OPTIONS]  [TESTNAME...]", argv0);
130         println!("{}", getopts::usage(&message, &groups));
131         println!("");
132         panic!()
133     }
134
135     fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
136         match m.opt_str(nm) {
137             Some(s) => PathBuf::from(&s),
138             None => panic!("no option (=path) found for {}", nm),
139         }
140     }
141
142     fn make_absolute(path: PathBuf) -> PathBuf {
143         if path.is_relative() {
144             env::current_dir().unwrap().join(path)
145         } else {
146             path
147         }
148     }
149
150     Config {
151         compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
152         run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
153         rustc_path: opt_path(matches, "rustc-path"),
154         rustdoc_path: opt_path(matches, "rustdoc-path"),
155         lldb_python: matches.opt_str("lldb-python").unwrap(),
156         docck_python: matches.opt_str("docck-python").unwrap(),
157         valgrind_path: matches.opt_str("valgrind-path"),
158         force_valgrind: matches.opt_present("force-valgrind"),
159         llvm_filecheck: matches.opt_str("llvm-filecheck").map(|s| PathBuf::from(&s)),
160         src_base: opt_path(matches, "src-base"),
161         build_base: opt_path(matches, "build-base"),
162         stage_id: matches.opt_str("stage-id").unwrap(),
163         mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
164         run_ignored: matches.opt_present("ignored"),
165         filter: matches.free.first().cloned(),
166         logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
167         runtool: matches.opt_str("runtool"),
168         host_rustcflags: matches.opt_str("host-rustcflags"),
169         target_rustcflags: matches.opt_str("target-rustcflags"),
170         target: opt_str2(matches.opt_str("target")),
171         host: opt_str2(matches.opt_str("host")),
172         gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
173         lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
174         llvm_version: matches.opt_str("llvm-version"),
175         android_cross_path: opt_path(matches, "android-cross-path"),
176         adb_path: opt_str2(matches.opt_str("adb-path")),
177         adb_test_dir: format!("{}/{}",
178             opt_str2(matches.opt_str("adb-test-dir")),
179             opt_str2(matches.opt_str("target"))),
180         adb_device_status:
181             opt_str2(matches.opt_str("target")).contains("android") &&
182             "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
183             !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
184         lldb_python_dir: matches.opt_str("lldb-python-dir"),
185         verbose: matches.opt_present("verbose"),
186         quiet: matches.opt_present("quiet"),
187
188         cc: matches.opt_str("cc").unwrap(),
189         cxx: matches.opt_str("cxx").unwrap(),
190         cflags: matches.opt_str("cflags").unwrap(),
191         llvm_components: matches.opt_str("llvm-components").unwrap(),
192         llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
193     }
194 }
195
196 pub fn log_config(config: &Config) {
197     let c = config;
198     logv(c, format!("configuration:"));
199     logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
200     logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
201     logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
202     logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
203     logv(c, format!("src_base: {:?}", config.src_base.display()));
204     logv(c, format!("build_base: {:?}", config.build_base.display()));
205     logv(c, format!("stage_id: {}", config.stage_id));
206     logv(c, format!("mode: {}", config.mode));
207     logv(c, format!("run_ignored: {}", config.run_ignored));
208     logv(c, format!("filter: {}",
209                     opt_str(&config.filter
210                                    .as_ref()
211                                    .map(|re| re.to_owned()))));
212     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
213     logv(c, format!("host-rustcflags: {}",
214                     opt_str(&config.host_rustcflags)));
215     logv(c, format!("target-rustcflags: {}",
216                     opt_str(&config.target_rustcflags)));
217     logv(c, format!("target: {}", config.target));
218     logv(c, format!("host: {}", config.host));
219     logv(c, format!("android-cross-path: {:?}",
220                     config.android_cross_path.display()));
221     logv(c, format!("adb_path: {:?}", config.adb_path));
222     logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
223     logv(c, format!("adb_device_status: {}",
224                     config.adb_device_status));
225     logv(c, format!("verbose: {}", config.verbose));
226     logv(c, format!("quiet: {}", config.quiet));
227     logv(c, format!("\n"));
228 }
229
230 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
231     match *maybestr {
232         None => "(none)",
233         Some(ref s) => s,
234     }
235 }
236
237 pub fn opt_str2(maybestr: Option<String>) -> String {
238     match maybestr {
239         None => "(none)".to_owned(),
240         Some(s) => s,
241     }
242 }
243
244 pub fn run_tests(config: &Config) {
245     if config.target.contains("android") {
246         if let DebugInfoGdb = config.mode {
247             println!("{} debug-info test uses tcp 5039 port.\
248                      please reserve it", config.target);
249         }
250
251         // android debug-info test uses remote debugger
252         // so, we test 1 thread at once.
253         // also trying to isolate problems with adb_run_wrapper.sh ilooping
254         env::set_var("RUST_TEST_THREADS","1");
255     }
256
257     match config.mode {
258         DebugInfoLldb => {
259             if let Some(lldb_version) = config.lldb_version.as_ref() {
260                 if is_blacklisted_lldb_version(&lldb_version[..]) {
261                     println!("WARNING: The used version of LLDB ({}) has a \
262                               known issue that breaks debuginfo tests. See \
263                               issue #32520 for more information. Skipping all \
264                               LLDB-based tests!",
265                              lldb_version);
266                     return
267                 }
268             }
269
270             // Some older versions of LLDB seem to have problems with multiple
271             // instances running in parallel, so only run one test thread at a
272             // time.
273             env::set_var("RUST_TEST_THREADS", "1");
274         }
275         _ => { /* proceed */ }
276     }
277
278     // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
279     if let Mode::CodegenUnits = config.mode {
280         let _ = fs::remove_dir_all("tmp/partitioning-tests");
281     }
282
283     let opts = test_opts(config);
284     let tests = make_tests(config);
285     // sadly osx needs some file descriptor limits raised for running tests in
286     // parallel (especially when we have lots and lots of child processes).
287     // For context, see #8904
288     unsafe { raise_fd_limit::raise_fd_limit(); }
289     // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
290     // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
291     env::set_var("__COMPAT_LAYER", "RunAsInvoker");
292     let res = test::run_tests_console(&opts, tests.into_iter().collect());
293     match res {
294         Ok(true) => {}
295         Ok(false) => panic!("Some tests failed"),
296         Err(e) => {
297             println!("I/O failure during tests: {:?}", e);
298         }
299     }
300 }
301
302 pub fn test_opts(config: &Config) -> test::TestOpts {
303     test::TestOpts {
304         filter: config.filter.clone(),
305         run_ignored: config.run_ignored,
306         quiet: config.quiet,
307         logfile: config.logfile.clone(),
308         run_tests: true,
309         bench_benchmarks: true,
310         nocapture: match env::var("RUST_TEST_NOCAPTURE") {
311             Ok(val) => &val != "0",
312             Err(_) => false
313         },
314         color: test::AutoColor,
315         test_threads: None,
316     }
317 }
318
319 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
320     debug!("making tests from {:?}",
321            config.src_base.display());
322     let mut tests = Vec::new();
323     collect_tests_from_dir(config,
324                            &config.src_base,
325                            &config.src_base,
326                            &PathBuf::new(),
327                            &mut tests)
328         .unwrap();
329     tests
330 }
331
332 fn collect_tests_from_dir(config: &Config,
333                           base: &Path,
334                           dir: &Path,
335                           relative_dir_path: &Path,
336                           tests: &mut Vec<test::TestDescAndFn>)
337                           -> io::Result<()> {
338     // Ignore directories that contain a file
339     // `compiletest-ignore-dir`.
340     for file in fs::read_dir(dir)? {
341         let file = file?;
342         let name = file.file_name();
343         if name == *"compiletest-ignore-dir" {
344             return Ok(());
345         }
346         if name == *"Makefile" && config.mode == Mode::RunMake {
347             let paths = TestPaths {
348                 file: dir.to_path_buf(),
349                 base: base.to_path_buf(),
350                 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
351             };
352             tests.push(make_test(config, &paths));
353             return Ok(())
354         }
355     }
356
357     // If we find a test foo/bar.rs, we have to build the
358     // output directory `$build/foo` so we can write
359     // `$build/foo/bar` into it. We do this *now* in this
360     // sequential loop because otherwise, if we do it in the
361     // tests themselves, they race for the privilege of
362     // creating the directories and sometimes fail randomly.
363     let build_dir = config.build_base.join(&relative_dir_path);
364     fs::create_dir_all(&build_dir).unwrap();
365
366     // Add each `.rs` file as a test, and recurse further on any
367     // subdirectories we find, except for `aux` directories.
368     let dirs = fs::read_dir(dir)?;
369     for file in dirs {
370         let file = file?;
371         let file_path = file.path();
372         let file_name = file.file_name();
373         if is_test(&file_name) {
374             debug!("found test file: {:?}", file_path.display());
375             let paths = TestPaths {
376                 file: file_path,
377                 base: base.to_path_buf(),
378                 relative_dir: relative_dir_path.to_path_buf(),
379             };
380             tests.push(make_test(config, &paths))
381         } else if file_path.is_dir() {
382             let relative_file_path = relative_dir_path.join(file.file_name());
383             if &file_name == "auxiliary" {
384                 // `aux` directories contain other crates used for
385                 // cross-crate tests. Don't search them for tests, but
386                 // do create a directory in the build dir for them,
387                 // since we will dump intermediate output in there
388                 // sometimes.
389                 let build_dir = config.build_base.join(&relative_file_path);
390                 fs::create_dir_all(&build_dir).unwrap();
391             } else {
392                 debug!("found directory: {:?}", file_path.display());
393                 collect_tests_from_dir(config,
394                                        base,
395                                        &file_path,
396                                        &relative_file_path,
397                                        tests)?;
398             }
399         } else {
400             debug!("found other file/directory: {:?}", file_path.display());
401         }
402     }
403     Ok(())
404 }
405
406 pub fn is_test(file_name: &OsString) -> bool {
407     let file_name = file_name.to_str().unwrap();
408
409     if !file_name.ends_with(".rs") {
410         return false;
411     }
412
413     // `.`, `#`, and `~` are common temp-file prefixes.
414     let invalid_prefixes = &[".", "#", "~"];
415     !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
416 }
417
418 pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
419     let early_props = EarlyProps::from_file(config, &testpaths.file);
420
421     // The `should-fail` annotation doesn't apply to pretty tests,
422     // since we run the pretty printer across all tests by default.
423     // If desired, we could add a `should-fail-pretty` annotation.
424     let should_panic = match config.mode {
425         Pretty => test::ShouldPanic::No,
426         _ => if early_props.should_fail {
427             test::ShouldPanic::Yes
428         } else {
429             test::ShouldPanic::No
430         }
431     };
432
433     test::TestDescAndFn {
434         desc: test::TestDesc {
435             name: make_test_name(config, testpaths),
436             ignore: early_props.ignore,
437             should_panic: should_panic,
438         },
439         testfn: make_test_closure(config, testpaths),
440     }
441 }
442
443 pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
444     // Convert a complete path to something like
445     //
446     //    run-pass/foo/bar/baz.rs
447     let path =
448         PathBuf::from(config.mode.to_string())
449         .join(&testpaths.relative_dir)
450         .join(&testpaths.file.file_name().unwrap());
451     test::DynTestName(format!("[{}] {}", config.mode, path.display()))
452 }
453
454 pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
455     let config = config.clone();
456     let testpaths = testpaths.clone();
457     test::DynTestFn(Box::new(move || {
458         runtest::run(config, &testpaths)
459     }))
460 }
461
462 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
463     match full_version_line {
464         Some(ref full_version_line)
465           if !full_version_line.trim().is_empty() => {
466             let full_version_line = full_version_line.trim();
467
468             // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
469             for (pos, c) in full_version_line.char_indices() {
470                 if !c.is_digit(10) {
471                     continue
472                 }
473                 if pos + 2 >= full_version_line.len() {
474                     continue
475                 }
476                 if full_version_line[pos + 1..].chars().next().unwrap() != '.' {
477                     continue
478                 }
479                 if !full_version_line[pos + 2..].chars().next().unwrap().is_digit(10) {
480                     continue
481                 }
482                 if pos > 0 && full_version_line[..pos].chars().next_back()
483                                                       .unwrap().is_digit(10) {
484                     continue
485                 }
486                 let mut end = pos + 3;
487                 while end < full_version_line.len() &&
488                       full_version_line[end..].chars().next()
489                                               .unwrap().is_digit(10) {
490                     end += 1;
491                 }
492                 return Some(full_version_line[pos..end].to_owned());
493             }
494             println!("Could not extract GDB version from line '{}'",
495                      full_version_line);
496             None
497         },
498         _ => None
499     }
500 }
501
502 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
503     // Extract the major LLDB version from the given version string.
504     // LLDB version strings are different for Apple and non-Apple platforms.
505     // At the moment, this function only supports the Apple variant, which looks
506     // like this:
507     //
508     // LLDB-179.5 (older versions)
509     // lldb-300.2.51 (new versions)
510     //
511     // We are only interested in the major version number, so this function
512     // will return `Some("179")` and `Some("300")` respectively.
513
514     if let Some(ref full_version_line) = full_version_line {
515         if !full_version_line.trim().is_empty() {
516             let full_version_line = full_version_line.trim();
517
518             for (pos, l) in full_version_line.char_indices() {
519                 if l != 'l' && l != 'L' { continue }
520                 if pos + 5 >= full_version_line.len() { continue }
521                 let l = full_version_line[pos + 1..].chars().next().unwrap();
522                 if l != 'l' && l != 'L' { continue }
523                 let d = full_version_line[pos + 2..].chars().next().unwrap();
524                 if d != 'd' && d != 'D' { continue }
525                 let b = full_version_line[pos + 3..].chars().next().unwrap();
526                 if b != 'b' && b != 'B' { continue }
527                 let dash = full_version_line[pos + 4..].chars().next().unwrap();
528                 if dash != '-' { continue }
529
530                 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
531                     c.is_digit(10)
532                 }).collect::<String>();
533                 if !vers.is_empty() { return Some(vers) }
534             }
535             println!("Could not extract LLDB version from line '{}'",
536                      full_version_line);
537         }
538     }
539     None
540 }
541
542 fn is_blacklisted_lldb_version(version: &str) -> bool {
543     version == "350"
544 }