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