]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use std::collections::HashSet; |
a7813a04 XL |
2 | use std::env; |
3 | use std::fs::File; | |
a7813a04 | 4 | use std::io::prelude::*; |
94b46f34 | 5 | use std::io::BufReader; |
a7813a04 XL |
6 | use std::path::{Path, PathBuf}; |
7 | ||
3dfed10e | 8 | use tracing::*; |
48663c56 | 9 | |
17df50a5 | 10 | use crate::common::{CompareMode, Config, Debugger, FailMode, Mode, PanicStrategy, PassMode}; |
dfeec247 | 11 | use crate::util; |
1b1a35ee | 12 | use crate::{extract_cdb_version, extract_gdb_version}; |
c30ab7b3 | 13 | |
416331ca XL |
14 | #[cfg(test)] |
15 | mod tests; | |
16 | ||
0bf4aa26 XL |
17 | /// The result of parse_cfg_name_directive. |
18 | #[derive(Clone, Copy, PartialEq, Debug)] | |
19 | enum ParsedNameDirective { | |
20 | /// No match. | |
21 | NoMatch, | |
22 | /// Match. | |
23 | Match, | |
0bf4aa26 XL |
24 | } |
25 | ||
a7813a04 XL |
26 | /// Properties which must be known very early, before actually running |
27 | /// the test. | |
74b04a01 | 28 | #[derive(Default)] |
a7813a04 | 29 | pub struct EarlyProps { |
8bb4bdeb | 30 | pub aux: Vec<String>, |
60c5eb7d | 31 | pub aux_crate: Vec<(String, String)>, |
ff7c6d11 | 32 | pub revisions: Vec<String>, |
a7813a04 XL |
33 | } |
34 | ||
35 | impl EarlyProps { | |
36 | pub fn from_file(config: &Config, testfile: &Path) -> Self { | |
136023e0 | 37 | let file = File::open(testfile).expect("open test file to parse earlyprops"); |
74b04a01 XL |
38 | Self::from_reader(config, testfile, file) |
39 | } | |
a7813a04 | 40 | |
74b04a01 XL |
41 | pub fn from_reader<R: Read>(config: &Config, testfile: &Path, rdr: R) -> Self { |
42 | let mut props = EarlyProps::default(); | |
136023e0 | 43 | iter_header(testfile, rdr, &mut |_, ln| { |
5099ac24 FG |
44 | config.push_name_value_directive(ln, directives::AUX_BUILD, &mut props.aux, |r| { |
45 | r.trim().to_string() | |
46 | }); | |
47 | config.push_name_value_directive( | |
48 | ln, | |
49 | directives::AUX_CRATE, | |
50 | &mut props.aux_crate, | |
51 | Config::parse_aux_crate, | |
52 | ); | |
cdc7bbd5 | 53 | config.parse_and_update_revisions(ln, &mut props.revisions); |
a7813a04 | 54 | }); |
a7813a04 | 55 | return props; |
a7813a04 XL |
56 | } |
57 | } | |
58 | ||
59 | #[derive(Clone, Debug)] | |
60 | pub struct TestProps { | |
61 | // Lines that should be expected, in order, on standard out | |
5bcae85e | 62 | pub error_patterns: Vec<String>, |
a7813a04 XL |
63 | // Extra flags to pass to the compiler |
64 | pub compile_flags: Vec<String>, | |
65 | // Extra flags to pass when the compiled code is run (such as --bench) | |
66 | pub run_flags: Option<String>, | |
67 | // If present, the name of a file that this test should match when | |
68 | // pretty-printed | |
69 | pub pp_exact: Option<PathBuf>, | |
70 | // Other crates that should be compiled (typically from the same | |
71 | // directory as the test, but for backwards compatibility reasons | |
72 | // we also check the auxiliary directory) | |
5bcae85e | 73 | pub aux_builds: Vec<String>, |
60c5eb7d XL |
74 | // Similar to `aux_builds`, but a list of NAME=somelib.rs of dependencies |
75 | // to build and pass with the `--extern` flag. | |
76 | pub aux_crates: Vec<(String, String)>, | |
a7813a04 | 77 | // Environment settings to use for compiling |
5bcae85e | 78 | pub rustc_env: Vec<(String, String)>, |
48663c56 XL |
79 | // Environment variables to unset prior to compiling. |
80 | // Variables are unset before applying 'rustc_env'. | |
81 | pub unset_rustc_env: Vec<String>, | |
a7813a04 | 82 | // Environment settings to use during execution |
5bcae85e | 83 | pub exec_env: Vec<(String, String)>, |
a7813a04 XL |
84 | // Build documentation for all specified aux-builds as well |
85 | pub build_aux_docs: bool, | |
86 | // Flag to force a crate to be built with the host architecture | |
87 | pub force_host: bool, | |
88 | // Check stdout for error-pattern output as well as stderr | |
89 | pub check_stdout: bool, | |
e1599b0c XL |
90 | // Check stdout & stderr for output of run-pass test |
91 | pub check_run_results: bool, | |
0bf4aa26 XL |
92 | // For UI tests, allows compiler to generate arbitrary output to stdout |
93 | pub dont_check_compiler_stdout: bool, | |
94 | // For UI tests, allows compiler to generate arbitrary output to stderr | |
95 | pub dont_check_compiler_stderr: bool, | |
a7813a04 | 96 | // Don't force a --crate-type=dylib flag on the command line |
48663c56 XL |
97 | // |
98 | // Set this for example if you have an auxiliary test file that contains | |
99 | // a proc-macro and needs `#![crate_type = "proc-macro"]`. This ensures | |
100 | // that the aux file is compiled as a `proc-macro` and not as a `dylib`. | |
a7813a04 | 101 | pub no_prefer_dynamic: bool, |
94222f64 | 102 | // Run -Zunpretty expanded when running pretty printing tests |
a7813a04 XL |
103 | pub pretty_expanded: bool, |
104 | // Which pretty mode are we testing with, default to 'normal' | |
105 | pub pretty_mode: String, | |
106 | // Only compare pretty output and don't try compiling | |
107 | pub pretty_compare_only: bool, | |
108 | // Patterns which must not appear in the output of a cfail test. | |
109 | pub forbid_output: Vec<String>, | |
110 | // Revisions to test for incremental compilation. | |
111 | pub revisions: Vec<String>, | |
112 | // Directory (if any) to use for incremental compilation. This is | |
113 | // not set by end-users; rather it is set by the incremental | |
114 | // testing harness and used when generating compilation | |
115 | // arguments. (In particular, it propagates to the aux-builds.) | |
116 | pub incremental_dir: Option<PathBuf>, | |
c295e0f8 XL |
117 | // If `true`, this test will use incremental compilation. |
118 | // | |
119 | // This can be set manually with the `incremental` header, or implicitly | |
120 | // by being a part of an incremental mode test. Using the `incremental` | |
121 | // header should be avoided if possible; using an incremental mode test is | |
122 | // preferred. Incremental mode tests support multiple passes, which can | |
123 | // verify that the incremental cache can be loaded properly after being | |
124 | // created. Just setting the header will only verify the behavior with | |
125 | // creating an incremental cache, but doesn't check that it is created | |
126 | // correctly. | |
127 | // | |
128 | // Compiletest will create the incremental directory, and ensure it is | |
129 | // empty before the test starts. Incremental mode tests will reuse the | |
130 | // incremental directory between passes in the same test. | |
131 | pub incremental: bool, | |
5099ac24 FG |
132 | // If `true`, this test is a known bug. |
133 | // | |
134 | // When set, some requirements are relaxed. Currently, this only means no | |
135 | // error annotations are needed, but this may be updated in the future to | |
136 | // include other relaxations. | |
137 | pub known_bug: bool, | |
dc9dc135 XL |
138 | // How far should the test proceed while still passing. |
139 | pass_mode: Option<PassMode>, | |
140 | // Ignore `--pass` overrides from the command line for this test. | |
141 | ignore_pass: bool, | |
dfeec247 XL |
142 | // How far this test should proceed to start failing. |
143 | pub fail_mode: Option<FailMode>, | |
8bb4bdeb XL |
144 | // rustdoc will test the output of the `--test` option |
145 | pub check_test_line_numbers_match: bool, | |
041b39d2 XL |
146 | // customized normalization rules |
147 | pub normalize_stdout: Vec<(String, String)>, | |
148 | pub normalize_stderr: Vec<(String, String)>, | |
0531ce1d | 149 | pub failure_status: i32, |
532ac7d7 XL |
150 | // Whether or not `rustfix` should apply the `CodeSuggestion`s of this test and compile the |
151 | // resulting Rust code. | |
83c7162d | 152 | pub run_rustfix: bool, |
532ac7d7 | 153 | // If true, `rustfix` will only apply `MachineApplicable` suggestions. |
b7449926 | 154 | pub rustfix_only_machine_applicable: bool, |
532ac7d7 | 155 | pub assembly_output: Option<String>, |
60c5eb7d XL |
156 | // If true, the test is expected to ICE |
157 | pub should_ice: bool, | |
6a06907d XL |
158 | // If true, the stderr is expected to be different across bit-widths. |
159 | pub stderr_per_bitwidth: bool, | |
a7813a04 XL |
160 | } |
161 | ||
5099ac24 FG |
162 | mod directives { |
163 | pub const ERROR_PATTERN: &'static str = "error-pattern"; | |
164 | pub const COMPILE_FLAGS: &'static str = "compile-flags"; | |
165 | pub const RUN_FLAGS: &'static str = "run-flags"; | |
166 | pub const SHOULD_ICE: &'static str = "should-ice"; | |
167 | pub const BUILD_AUX_DOCS: &'static str = "build-aux-docs"; | |
168 | pub const FORCE_HOST: &'static str = "force-host"; | |
169 | pub const CHECK_STDOUT: &'static str = "check-stdout"; | |
170 | pub const CHECK_RUN_RESULTS: &'static str = "check-run-results"; | |
171 | pub const DONT_CHECK_COMPILER_STDOUT: &'static str = "dont-check-compiler-stdout"; | |
172 | pub const DONT_CHECK_COMPILER_STDERR: &'static str = "dont-check-compiler-stderr"; | |
173 | pub const NO_PREFER_DYNAMIC: &'static str = "no-prefer-dynamic"; | |
174 | pub const PRETTY_EXPANDED: &'static str = "pretty-expanded"; | |
175 | pub const PRETTY_MODE: &'static str = "pretty-mode"; | |
176 | pub const PRETTY_COMPARE_ONLY: &'static str = "pretty-compare-only"; | |
177 | pub const AUX_BUILD: &'static str = "aux-build"; | |
178 | pub const AUX_CRATE: &'static str = "aux-crate"; | |
179 | pub const EXEC_ENV: &'static str = "exec-env"; | |
180 | pub const RUSTC_ENV: &'static str = "rustc-env"; | |
181 | pub const UNSET_RUSTC_ENV: &'static str = "unset-rustc-env"; | |
182 | pub const FORBID_OUTPUT: &'static str = "forbid-output"; | |
183 | pub const CHECK_TEST_LINE_NUMBERS_MATCH: &'static str = "check-test-line-numbers-match"; | |
184 | pub const IGNORE_PASS: &'static str = "ignore-pass"; | |
185 | pub const FAILURE_STATUS: &'static str = "failure-status"; | |
186 | pub const RUN_RUSTFIX: &'static str = "run-rustfix"; | |
187 | pub const RUSTFIX_ONLY_MACHINE_APPLICABLE: &'static str = "rustfix-only-machine-applicable"; | |
188 | pub const ASSEMBLY_OUTPUT: &'static str = "assembly-output"; | |
189 | pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth"; | |
190 | pub const INCREMENTAL: &'static str = "incremental"; | |
191 | pub const KNOWN_BUG: &'static str = "known-bug"; | |
192 | } | |
193 | ||
a7813a04 XL |
194 | impl TestProps { |
195 | pub fn new() -> Self { | |
a7813a04 | 196 | TestProps { |
9e0c209e | 197 | error_patterns: vec![], |
a7813a04 | 198 | compile_flags: vec![], |
9e0c209e SL |
199 | run_flags: None, |
200 | pp_exact: None, | |
201 | aux_builds: vec![], | |
60c5eb7d | 202 | aux_crates: vec![], |
a7813a04 XL |
203 | revisions: vec![], |
204 | rustc_env: vec![], | |
48663c56 | 205 | unset_rustc_env: vec![], |
9e0c209e | 206 | exec_env: vec![], |
9e0c209e SL |
207 | build_aux_docs: false, |
208 | force_host: false, | |
209 | check_stdout: false, | |
e1599b0c | 210 | check_run_results: false, |
0bf4aa26 XL |
211 | dont_check_compiler_stdout: false, |
212 | dont_check_compiler_stderr: false, | |
9e0c209e SL |
213 | no_prefer_dynamic: false, |
214 | pretty_expanded: false, | |
041b39d2 | 215 | pretty_mode: "normal".to_string(), |
9e0c209e SL |
216 | pretty_compare_only: false, |
217 | forbid_output: vec![], | |
a7813a04 | 218 | incremental_dir: None, |
c295e0f8 | 219 | incremental: false, |
5099ac24 | 220 | known_bug: false, |
dc9dc135 | 221 | pass_mode: None, |
dfeec247 | 222 | fail_mode: None, |
dc9dc135 | 223 | ignore_pass: false, |
8bb4bdeb | 224 | check_test_line_numbers_match: false, |
041b39d2 XL |
225 | normalize_stdout: vec![], |
226 | normalize_stderr: vec![], | |
8faf50e0 | 227 | failure_status: -1, |
83c7162d | 228 | run_rustfix: false, |
b7449926 | 229 | rustfix_only_machine_applicable: false, |
532ac7d7 | 230 | assembly_output: None, |
60c5eb7d | 231 | should_ice: false, |
6a06907d | 232 | stderr_per_bitwidth: false, |
a7813a04 XL |
233 | } |
234 | } | |
235 | ||
94b46f34 | 236 | pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self { |
a7813a04 XL |
237 | let mut props = TestProps::new(); |
238 | ||
239 | // copy over select properties to the aux build: | |
240 | props.incremental_dir = self.incremental_dir.clone(); | |
7cac9316 | 241 | props.load_from(testfile, cfg, config); |
a7813a04 XL |
242 | |
243 | props | |
244 | } | |
245 | ||
abe05a73 | 246 | pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self { |
a7813a04 | 247 | let mut props = TestProps::new(); |
abe05a73 | 248 | props.load_from(testfile, cfg, config); |
dfeec247 XL |
249 | |
250 | match (props.pass_mode, props.fail_mode) { | |
251 | (None, None) => props.fail_mode = Some(FailMode::Check), | |
252 | (Some(_), None) | (None, Some(_)) => {} | |
253 | (Some(_), Some(_)) => panic!("cannot use a *-fail and *-pass mode together"), | |
254 | } | |
255 | ||
a7813a04 XL |
256 | props |
257 | } | |
258 | ||
9fa01778 | 259 | /// Loads properties from `testfile` into `props`. If a property is |
a7813a04 XL |
260 | /// tied to a particular revision `foo` (indicated by writing |
261 | /// `//[foo]`), then the property is ignored unless `cfg` is | |
262 | /// `Some("foo")`. | |
94b46f34 | 263 | fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) { |
3c0e092e | 264 | let mut has_edition = false; |
74b04a01 XL |
265 | if !testfile.is_dir() { |
266 | let file = File::open(testfile).unwrap(); | |
a7813a04 | 267 | |
136023e0 XL |
268 | iter_header(testfile, file, &mut |revision, ln| { |
269 | if revision.is_some() && revision != cfg { | |
270 | return; | |
271 | } | |
272 | ||
5099ac24 | 273 | use directives::*; |
a7813a04 | 274 | |
5099ac24 FG |
275 | config.push_name_value_directive( |
276 | ln, | |
277 | ERROR_PATTERN, | |
278 | &mut self.error_patterns, | |
279 | |r| r, | |
280 | ); | |
281 | ||
282 | if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) { | |
74b04a01 XL |
283 | self.compile_flags.extend(flags.split_whitespace().map(|s| s.to_owned())); |
284 | } | |
8faf50e0 | 285 | |
74b04a01 XL |
286 | if let Some(edition) = config.parse_edition(ln) { |
287 | self.compile_flags.push(format!("--edition={}", edition)); | |
3c0e092e | 288 | has_edition = true; |
74b04a01 | 289 | } |
a7813a04 | 290 | |
cdc7bbd5 | 291 | config.parse_and_update_revisions(ln, &mut self.revisions); |
a7813a04 | 292 | |
5099ac24 | 293 | config.set_name_value_directive(ln, RUN_FLAGS, &mut self.run_flags, |r| r); |
a7813a04 | 294 | |
74b04a01 XL |
295 | if self.pp_exact.is_none() { |
296 | self.pp_exact = config.parse_pp_exact(ln, testfile); | |
297 | } | |
60c5eb7d | 298 | |
5099ac24 FG |
299 | config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice); |
300 | config.set_name_directive(ln, BUILD_AUX_DOCS, &mut self.build_aux_docs); | |
301 | config.set_name_directive(ln, FORCE_HOST, &mut self.force_host); | |
302 | config.set_name_directive(ln, CHECK_STDOUT, &mut self.check_stdout); | |
303 | config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut self.check_run_results); | |
304 | config.set_name_directive( | |
305 | ln, | |
306 | DONT_CHECK_COMPILER_STDOUT, | |
307 | &mut self.dont_check_compiler_stdout, | |
308 | ); | |
309 | config.set_name_directive( | |
310 | ln, | |
311 | DONT_CHECK_COMPILER_STDERR, | |
312 | &mut self.dont_check_compiler_stderr, | |
313 | ); | |
314 | config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic); | |
315 | config.set_name_directive(ln, PRETTY_EXPANDED, &mut self.pretty_expanded); | |
316 | ||
317 | if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE) { | |
74b04a01 XL |
318 | self.pretty_mode = m; |
319 | } | |
a7813a04 | 320 | |
5099ac24 FG |
321 | config.set_name_directive(ln, PRETTY_COMPARE_ONLY, &mut self.pretty_compare_only); |
322 | config.push_name_value_directive(ln, AUX_BUILD, &mut self.aux_builds, |r| { | |
323 | r.trim().to_string() | |
324 | }); | |
325 | config.push_name_value_directive( | |
326 | ln, | |
327 | AUX_CRATE, | |
328 | &mut self.aux_crates, | |
329 | Config::parse_aux_crate, | |
330 | ); | |
331 | config.push_name_value_directive( | |
332 | ln, | |
333 | EXEC_ENV, | |
334 | &mut self.exec_env, | |
335 | Config::parse_env, | |
336 | ); | |
337 | config.push_name_value_directive( | |
338 | ln, | |
339 | RUSTC_ENV, | |
340 | &mut self.rustc_env, | |
341 | Config::parse_env, | |
342 | ); | |
343 | config.push_name_value_directive( | |
344 | ln, | |
345 | UNSET_RUSTC_ENV, | |
346 | &mut self.unset_rustc_env, | |
347 | |r| r, | |
348 | ); | |
349 | config.push_name_value_directive(ln, FORBID_OUTPUT, &mut self.forbid_output, |r| r); | |
350 | config.set_name_directive( | |
351 | ln, | |
352 | CHECK_TEST_LINE_NUMBERS_MATCH, | |
353 | &mut self.check_test_line_numbers_match, | |
354 | ); | |
ff7c6d11 | 355 | |
74b04a01 XL |
356 | self.update_pass_mode(ln, cfg, config); |
357 | self.update_fail_mode(ln, config); | |
83c7162d | 358 | |
5099ac24 | 359 | config.set_name_directive(ln, IGNORE_PASS, &mut self.ignore_pass); |
0531ce1d | 360 | |
74b04a01 XL |
361 | if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") { |
362 | self.normalize_stdout.push(rule); | |
363 | } | |
364 | if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") { | |
365 | self.normalize_stderr.push(rule); | |
366 | } | |
83c7162d | 367 | |
5099ac24 FG |
368 | if let Some(code) = config |
369 | .parse_name_value_directive(ln, FAILURE_STATUS) | |
370 | .and_then(|code| code.trim().parse::<i32>().ok()) | |
371 | { | |
74b04a01 XL |
372 | self.failure_status = code; |
373 | } | |
b7449926 | 374 | |
5099ac24 FG |
375 | config.set_name_directive(ln, RUN_RUSTFIX, &mut self.run_rustfix); |
376 | config.set_name_directive( | |
377 | ln, | |
378 | RUSTFIX_ONLY_MACHINE_APPLICABLE, | |
379 | &mut self.rustfix_only_machine_applicable, | |
380 | ); | |
381 | config.set_name_value_directive( | |
382 | ln, | |
383 | ASSEMBLY_OUTPUT, | |
384 | &mut self.assembly_output, | |
385 | |r| r.trim().to_string(), | |
386 | ); | |
387 | config.set_name_directive(ln, STDERR_PER_BITWIDTH, &mut self.stderr_per_bitwidth); | |
388 | config.set_name_directive(ln, INCREMENTAL, &mut self.incremental); | |
389 | config.set_name_directive(ln, KNOWN_BUG, &mut self.known_bug); | |
74b04a01 XL |
390 | }); |
391 | } | |
a7813a04 | 392 | |
74d20737 | 393 | if self.failure_status == -1 { |
5869c6ff | 394 | self.failure_status = 1; |
74d20737 | 395 | } |
60c5eb7d XL |
396 | if self.should_ice { |
397 | self.failure_status = 101; | |
398 | } | |
74d20737 | 399 | |
c295e0f8 XL |
400 | if config.mode == Mode::Incremental { |
401 | self.incremental = true; | |
402 | } | |
403 | ||
041b39d2 XL |
404 | for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] { |
405 | if let Ok(val) = env::var(key) { | |
406 | if self.exec_env.iter().find(|&&(ref x, _)| x == key).is_none() { | |
407 | self.exec_env.push(((*key).to_owned(), val)) | |
5bcae85e | 408 | } |
a7813a04 XL |
409 | } |
410 | } | |
3c0e092e XL |
411 | |
412 | if let (Some(edition), false) = (&config.edition, has_edition) { | |
413 | self.compile_flags.push(format!("--edition={}", edition)); | |
414 | } | |
a7813a04 | 415 | } |
dc9dc135 | 416 | |
dfeec247 XL |
417 | fn update_fail_mode(&mut self, ln: &str, config: &Config) { |
418 | let check_ui = |mode: &str| { | |
419 | if config.mode != Mode::Ui { | |
420 | panic!("`{}-fail` header is only supported in UI tests", mode); | |
421 | } | |
422 | }; | |
74b04a01 XL |
423 | if config.mode == Mode::Ui && config.parse_name_directive(ln, "compile-fail") { |
424 | panic!("`compile-fail` header is useless in UI tests"); | |
425 | } | |
dfeec247 XL |
426 | let fail_mode = if config.parse_name_directive(ln, "check-fail") { |
427 | check_ui("check"); | |
428 | Some(FailMode::Check) | |
429 | } else if config.parse_name_directive(ln, "build-fail") { | |
430 | check_ui("build"); | |
431 | Some(FailMode::Build) | |
432 | } else if config.parse_name_directive(ln, "run-fail") { | |
433 | check_ui("run"); | |
434 | Some(FailMode::Run) | |
435 | } else { | |
436 | None | |
437 | }; | |
438 | match (self.fail_mode, fail_mode) { | |
439 | (None, Some(_)) => self.fail_mode = fail_mode, | |
440 | (Some(_), Some(_)) => panic!("multiple `*-fail` headers in a single test"), | |
441 | (_, None) => {} | |
442 | } | |
443 | } | |
444 | ||
dc9dc135 XL |
445 | fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) { |
446 | let check_no_run = |s| { | |
447 | if config.mode != Mode::Ui && config.mode != Mode::Incremental { | |
448 | panic!("`{}` header is only supported in UI and incremental tests", s); | |
449 | } | |
dfeec247 XL |
450 | if config.mode == Mode::Incremental |
451 | && !revision.map_or(false, |r| r.starts_with("cfail")) | |
452 | && !self.revisions.iter().all(|r| r.starts_with("cfail")) | |
453 | { | |
dc9dc135 XL |
454 | panic!("`{}` header is only supported in `cfail` incremental tests", s); |
455 | } | |
456 | }; | |
457 | let pass_mode = if config.parse_name_directive(ln, "check-pass") { | |
458 | check_no_run("check-pass"); | |
459 | Some(PassMode::Check) | |
460 | } else if config.parse_name_directive(ln, "build-pass") { | |
461 | check_no_run("build-pass"); | |
462 | Some(PassMode::Build) | |
dc9dc135 | 463 | } else if config.parse_name_directive(ln, "run-pass") { |
416331ca | 464 | if config.mode != Mode::Ui { |
dc9dc135 XL |
465 | panic!("`run-pass` header is only supported in UI tests") |
466 | } | |
467 | Some(PassMode::Run) | |
468 | } else { | |
469 | None | |
470 | }; | |
471 | match (self.pass_mode, pass_mode) { | |
472 | (None, Some(_)) => self.pass_mode = pass_mode, | |
473 | (Some(_), Some(_)) => panic!("multiple `*-pass` headers in a single test"), | |
474 | (_, None) => {} | |
475 | } | |
476 | } | |
477 | ||
478 | pub fn pass_mode(&self, config: &Config) -> Option<PassMode> { | |
dfeec247 | 479 | if !self.ignore_pass && self.fail_mode.is_none() && config.mode == Mode::Ui { |
dc9dc135 XL |
480 | if let (mode @ Some(_), Some(_)) = (config.force_pass_mode, self.pass_mode) { |
481 | return mode; | |
482 | } | |
483 | } | |
484 | self.pass_mode | |
485 | } | |
e1599b0c XL |
486 | |
487 | // does not consider CLI override for pass mode | |
488 | pub fn local_pass_mode(&self) -> Option<PassMode> { | |
489 | self.pass_mode | |
490 | } | |
a7813a04 XL |
491 | } |
492 | ||
136023e0 | 493 | fn iter_header<R: Read>(testfile: &Path, rdr: R, it: &mut dyn FnMut(Option<&str>, &str)) { |
a7813a04 | 494 | if testfile.is_dir() { |
5bcae85e | 495 | return; |
a7813a04 | 496 | } |
83c7162d | 497 | |
136023e0 | 498 | let comment = if testfile.extension().map(|e| e == "rs") == Some(true) { "//" } else { "#" }; |
83c7162d | 499 | |
74b04a01 XL |
500 | let mut rdr = BufReader::new(rdr); |
501 | let mut ln = String::new(); | |
502 | ||
503 | loop { | |
504 | ln.clear(); | |
505 | if rdr.read_line(&mut ln).unwrap() == 0 { | |
506 | break; | |
507 | } | |
83c7162d | 508 | |
a7813a04 XL |
509 | // Assume that any directives will be found before the first |
510 | // module or function. This doesn't seem to be an optimization | |
511 | // with a warm page cache. Maybe with a cold one. | |
a7813a04 XL |
512 | let ln = ln.trim(); |
513 | if ln.starts_with("fn") || ln.starts_with("mod") { | |
514 | return; | |
74b04a01 | 515 | } else if ln.starts_with(comment) && ln[comment.len()..].trim_start().starts_with('[') { |
a7813a04 | 516 | // A comment like `//[foo]` is specific to revision `foo` |
041b39d2 | 517 | if let Some(close_brace) = ln.find(']') { |
83c7162d | 518 | let open_brace = ln.find('[').unwrap(); |
dfeec247 | 519 | let lncfg = &ln[open_brace + 1..close_brace]; |
136023e0 | 520 | it(Some(lncfg), ln[(close_brace + 1)..].trim_start()); |
a7813a04 | 521 | } else { |
74b04a01 | 522 | panic!("malformed condition directive: expected `{}[foo]`, found `{}`", comment, ln) |
a7813a04 | 523 | } |
83c7162d | 524 | } else if ln.starts_with(comment) { |
136023e0 | 525 | it(None, ln[comment.len()..].trim_start()); |
a7813a04 XL |
526 | } |
527 | } | |
a7813a04 XL |
528 | } |
529 | ||
7cac9316 | 530 | impl Config { |
5099ac24 FG |
531 | fn parse_aux_crate(r: String) -> (String, String) { |
532 | let mut parts = r.trim().splitn(2, '='); | |
533 | ( | |
534 | parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(), | |
535 | parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(), | |
536 | ) | |
7cac9316 | 537 | } |
a7813a04 | 538 | |
cdc7bbd5 XL |
539 | fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) { |
540 | if let Some(raw) = self.parse_name_value_directive(line, "revisions") { | |
541 | let mut duplicates: HashSet<_> = existing.iter().cloned().collect(); | |
542 | for revision in raw.split_whitespace().map(|r| r.to_string()) { | |
543 | if !duplicates.insert(revision.clone()) { | |
544 | panic!("Duplicate revision: `{}` in line `{}`", revision, raw); | |
545 | } | |
546 | existing.push(revision); | |
547 | } | |
548 | } | |
7cac9316 | 549 | } |
a7813a04 | 550 | |
5099ac24 FG |
551 | fn parse_env(nv: String) -> (String, String) { |
552 | // nv is either FOO or FOO=BAR | |
553 | let mut strs: Vec<String> = nv.splitn(2, '=').map(str::to_owned).collect(); | |
a7813a04 | 554 | |
5099ac24 FG |
555 | match strs.len() { |
556 | 1 => (strs.pop().unwrap(), String::new()), | |
557 | 2 => { | |
558 | let end = strs.pop().unwrap(); | |
559 | (strs.pop().unwrap(), end) | |
5bcae85e | 560 | } |
5099ac24 FG |
561 | n => panic!("Expected 1 or 2 strings, not {}", n), |
562 | } | |
7cac9316 | 563 | } |
a7813a04 | 564 | |
7cac9316 XL |
565 | fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> { |
566 | if let Some(s) = self.parse_name_value_directive(line, "pp-exact") { | |
567 | Some(PathBuf::from(&s)) | |
041b39d2 XL |
568 | } else if self.parse_name_directive(line, "pp-exact") { |
569 | testfile.file_name().map(PathBuf::from) | |
a7813a04 | 570 | } else { |
041b39d2 XL |
571 | None |
572 | } | |
573 | } | |
574 | ||
575 | fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> { | |
0bf4aa26 | 576 | if self.parse_cfg_name_directive(line, prefix) == ParsedNameDirective::Match { |
0731742a XL |
577 | let from = parse_normalization_string(&mut line)?; |
578 | let to = parse_normalization_string(&mut line)?; | |
041b39d2 XL |
579 | Some((from, to)) |
580 | } else { | |
581 | None | |
582 | } | |
583 | } | |
584 | ||
9fa01778 XL |
585 | fn parse_needs_matching_clang(&self, line: &str) -> bool { |
586 | self.parse_name_directive(line, "needs-matching-clang") | |
587 | } | |
588 | ||
48663c56 XL |
589 | fn parse_needs_profiler_support(&self, line: &str) -> bool { |
590 | self.parse_name_directive(line, "needs-profiler-support") | |
591 | } | |
592 | ||
0731742a | 593 | /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86` |
0bf4aa26 XL |
594 | /// or `normalize-stderr-32bit`. |
595 | fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective { | |
dfeec247 XL |
596 | if !line.as_bytes().starts_with(prefix.as_bytes()) { |
597 | return ParsedNameDirective::NoMatch; | |
598 | } | |
599 | if line.as_bytes().get(prefix.len()) != Some(&b'-') { | |
600 | return ParsedNameDirective::NoMatch; | |
a7813a04 | 601 | } |
dfeec247 XL |
602 | |
603 | let name = line[prefix.len() + 1..].split(&[':', ' '][..]).next().unwrap(); | |
604 | ||
605 | let is_match = name == "test" || | |
3dfed10e | 606 | self.target == name || // triple |
dfeec247 XL |
607 | util::matches_os(&self.target, name) || // target |
608 | util::matches_env(&self.target, name) || // env | |
3dfed10e | 609 | self.target.ends_with(name) || // target and env |
dfeec247 XL |
610 | name == util::get_arch(&self.target) || // architecture |
611 | name == util::get_pointer_width(&self.target) || // pointer width | |
612 | name == self.stage_id.split('-').next().unwrap() || // stage | |
17df50a5 | 613 | name == self.channel || // channel |
dfeec247 | 614 | (self.target != self.host && name == "cross-compile") || |
3dfed10e | 615 | (name == "endian-big" && util::is_big_endian(&self.target)) || |
f035d41b | 616 | (self.remote_test_client.is_some() && name == "remote") || |
dfeec247 XL |
617 | match self.compare_mode { |
618 | Some(CompareMode::Nll) => name == "compare-mode-nll", | |
619 | Some(CompareMode::Polonius) => name == "compare-mode-polonius", | |
f035d41b | 620 | Some(CompareMode::Chalk) => name == "compare-mode-chalk", |
fc512014 XL |
621 | Some(CompareMode::SplitDwarf) => name == "compare-mode-split-dwarf", |
622 | Some(CompareMode::SplitDwarfSingle) => name == "compare-mode-split-dwarf-single", | |
dfeec247 XL |
623 | None => false, |
624 | } || | |
625 | (cfg!(debug_assertions) && name == "debug") || | |
626 | match self.debugger { | |
627 | Some(Debugger::Cdb) => name == "cdb", | |
628 | Some(Debugger::Gdb) => name == "gdb", | |
629 | Some(Debugger::Lldb) => name == "lldb", | |
630 | None => false, | |
631 | }; | |
632 | ||
633 | if is_match { ParsedNameDirective::Match } else { ParsedNameDirective::NoMatch } | |
a7813a04 | 634 | } |
a7813a04 | 635 | |
2c00a5a8 XL |
636 | fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool { |
637 | // returns whether this line contains this prefix or not. For prefix | |
638 | // "ignore", returns true if line says "ignore-x86_64", "ignore-arch", | |
83c7162d | 639 | // "ignore-android" etc. |
2c00a5a8 XL |
640 | line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') |
641 | } | |
642 | ||
7cac9316 XL |
643 | fn parse_name_directive(&self, line: &str, directive: &str) -> bool { |
644 | // Ensure the directive is a whole word. Do not match "ignore-x86" when | |
645 | // the line says "ignore-x86_64". | |
dfeec247 | 646 | line.starts_with(directive) |
3dfed10e | 647 | && matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':')) |
7cac9316 | 648 | } |
a7813a04 | 649 | |
7cac9316 XL |
650 | pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> { |
651 | let colon = directive.len(); | |
652 | if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { | |
94b46f34 | 653 | let value = line[(colon + 1)..].to_owned(); |
7cac9316 XL |
654 | debug!("{}: {}", directive, value); |
655 | Some(expand_variables(value, self)) | |
656 | } else { | |
657 | None | |
658 | } | |
a7813a04 | 659 | } |
abe05a73 XL |
660 | |
661 | pub fn find_rust_src_root(&self) -> Option<PathBuf> { | |
662 | let mut path = self.src_base.clone(); | |
663 | let path_postfix = Path::new("src/etc/lldb_batchmode.py"); | |
664 | ||
665 | while path.pop() { | |
666 | if path.join(&path_postfix).is_file() { | |
667 | return Some(path); | |
668 | } | |
669 | } | |
670 | ||
671 | None | |
672 | } | |
83c7162d | 673 | |
5099ac24 FG |
674 | fn parse_edition(&self, line: &str) -> Option<String> { |
675 | self.parse_name_value_directive(line, "edition") | |
83c7162d | 676 | } |
8faf50e0 | 677 | |
5099ac24 FG |
678 | fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) { |
679 | if !*value { | |
680 | *value = self.parse_name_directive(line, directive) | |
681 | } | |
b7449926 XL |
682 | } |
683 | ||
5099ac24 FG |
684 | fn set_name_value_directive<T>( |
685 | &self, | |
686 | line: &str, | |
687 | directive: &str, | |
688 | value: &mut Option<T>, | |
689 | parse: impl FnOnce(String) -> T, | |
690 | ) { | |
691 | if value.is_none() { | |
692 | *value = self.parse_name_value_directive(line, directive).map(parse); | |
693 | } | |
8faf50e0 | 694 | } |
c295e0f8 | 695 | |
5099ac24 FG |
696 | fn push_name_value_directive<T>( |
697 | &self, | |
698 | line: &str, | |
699 | directive: &str, | |
700 | values: &mut Vec<T>, | |
701 | parse: impl FnOnce(String) -> T, | |
702 | ) { | |
703 | if let Some(value) = self.parse_name_value_directive(line, directive).map(parse) { | |
704 | values.push(value); | |
705 | } | |
c295e0f8 | 706 | } |
a7813a04 XL |
707 | } |
708 | ||
7cac9316 | 709 | fn expand_variables(mut value: String, config: &Config) -> String { |
3dfed10e XL |
710 | const CWD: &str = "{{cwd}}"; |
711 | const SRC_BASE: &str = "{{src-base}}"; | |
712 | const BUILD_BASE: &str = "{{build-base}}"; | |
7cac9316 XL |
713 | |
714 | if value.contains(CWD) { | |
715 | let cwd = env::current_dir().unwrap(); | |
716 | value = value.replace(CWD, &cwd.to_string_lossy()); | |
717 | } | |
718 | ||
719 | if value.contains(SRC_BASE) { | |
720 | value = value.replace(SRC_BASE, &config.src_base.to_string_lossy()); | |
721 | } | |
722 | ||
723 | if value.contains(BUILD_BASE) { | |
724 | value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy()); | |
725 | } | |
726 | ||
727 | value | |
728 | } | |
041b39d2 XL |
729 | |
730 | /// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line` | |
731 | /// variable after the end of the quoted string. | |
732 | /// | |
733 | /// # Examples | |
734 | /// | |
735 | /// ``` | |
736 | /// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; | |
737 | /// let first = parse_normalization_string(&mut s); | |
738 | /// assert_eq!(first, Some("something (32 bits)".to_owned())); | |
739 | /// assert_eq!(s, " -> \"something ($WORD bits)\"."); | |
740 | /// ``` | |
741 | fn parse_normalization_string(line: &mut &str) -> Option<String> { | |
742 | // FIXME support escapes in strings. | |
0731742a XL |
743 | let begin = line.find('"')? + 1; |
744 | let end = line[begin..].find('"')? + begin; | |
041b39d2 | 745 | let result = line[begin..end].to_owned(); |
94b46f34 | 746 | *line = &line[end + 1..]; |
041b39d2 XL |
747 | Some(result) |
748 | } | |
3dfed10e XL |
749 | |
750 | pub fn extract_llvm_version(version: &str) -> Option<u32> { | |
cdc7bbd5 XL |
751 | let pat = |c: char| !c.is_ascii_digit() && c != '.'; |
752 | let version_without_suffix = match version.find(pat) { | |
753 | Some(pos) => &version[..pos], | |
754 | None => version, | |
755 | }; | |
3dfed10e XL |
756 | let components: Vec<u32> = version_without_suffix |
757 | .split('.') | |
758 | .map(|s| s.parse().expect("Malformed version component")) | |
759 | .collect(); | |
760 | let version = match *components { | |
761 | [a] => a * 10_000, | |
762 | [a, b] => a * 10_000 + b * 100, | |
763 | [a, b, c] => a * 10_000 + b * 100 + c, | |
764 | _ => panic!("Malformed version"), | |
765 | }; | |
766 | Some(version) | |
767 | } | |
768 | ||
136023e0 XL |
769 | /// Takes a directive of the form "<version1> [- <version2>]", |
770 | /// returns the numeric representation of <version1> and <version2> as | |
771 | /// tuple: (<version1> as u32, <version2> as u32) | |
772 | /// | |
773 | /// If the <version2> part is omitted, the second component of the tuple | |
774 | /// is the same as <version1>. | |
3dfed10e XL |
775 | fn extract_version_range<F>(line: &str, parse: F) -> Option<(u32, u32)> |
776 | where | |
777 | F: Fn(&str) -> Option<u32>, | |
778 | { | |
779 | let mut splits = line.splitn(2, "- ").map(str::trim); | |
780 | let min = splits.next().unwrap(); | |
781 | if min.ends_with('-') { | |
782 | return None; | |
783 | } | |
784 | ||
785 | let max = splits.next(); | |
786 | ||
787 | if min.is_empty() { | |
788 | return None; | |
789 | } | |
790 | ||
791 | let min = parse(min)?; | |
792 | let max = match max { | |
793 | Some(max) if max.is_empty() => return None, | |
794 | Some(max) => parse(max)?, | |
795 | _ => min, | |
796 | }; | |
797 | ||
798 | Some((min, max)) | |
799 | } | |
136023e0 XL |
800 | |
801 | pub fn make_test_description<R: Read>( | |
802 | config: &Config, | |
803 | name: test::TestName, | |
804 | path: &Path, | |
805 | src: R, | |
806 | cfg: Option<&str>, | |
807 | ) -> test::TestDesc { | |
808 | let mut ignore = false; | |
ee023bcb FG |
809 | #[cfg(not(bootstrap))] |
810 | let ignore_message: Option<String> = None; | |
136023e0 XL |
811 | let mut should_fail = false; |
812 | ||
813 | let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some(); | |
814 | let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(); | |
815 | let has_asm_support = util::has_asm_support(&config.target); | |
816 | let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target); | |
817 | let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target); | |
818 | let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target); | |
819 | let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target); | |
820 | let has_hwasan = util::HWASAN_SUPPORTED_TARGETS.contains(&&*config.target); | |
5099ac24 | 821 | let has_memtag = util::MEMTAG_SUPPORTED_TARGETS.contains(&&*config.target); |
136023e0 XL |
822 | // for `-Z gcc-ld=lld` |
823 | let has_rust_lld = config | |
824 | .compile_lib_path | |
825 | .join("rustlib") | |
826 | .join(&config.target) | |
827 | .join("bin") | |
828 | .join("gcc-ld") | |
829 | .join(if config.host.contains("windows") { "ld.exe" } else { "ld" }) | |
830 | .exists(); | |
831 | iter_header(path, src, &mut |revision, ln| { | |
832 | if revision.is_some() && revision != cfg { | |
833 | return; | |
834 | } | |
835 | ignore = match config.parse_cfg_name_directive(ln, "ignore") { | |
836 | ParsedNameDirective::Match => true, | |
837 | ParsedNameDirective::NoMatch => ignore, | |
838 | }; | |
839 | if config.has_cfg_prefix(ln, "only") { | |
840 | ignore = match config.parse_cfg_name_directive(ln, "only") { | |
841 | ParsedNameDirective::Match => ignore, | |
842 | ParsedNameDirective::NoMatch => true, | |
843 | }; | |
844 | } | |
845 | ignore |= ignore_llvm(config, ln); | |
846 | ignore |= | |
847 | config.run_clang_based_tests_with.is_none() && config.parse_needs_matching_clang(ln); | |
848 | ignore |= !has_asm_support && config.parse_name_directive(ln, "needs-asm-support"); | |
849 | ignore |= !rustc_has_profiler_support && config.parse_needs_profiler_support(ln); | |
850 | ignore |= !config.run_enabled() && config.parse_name_directive(ln, "needs-run-enabled"); | |
851 | ignore |= !rustc_has_sanitizer_support | |
852 | && config.parse_name_directive(ln, "needs-sanitizer-support"); | |
853 | ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address"); | |
854 | ignore |= !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak"); | |
855 | ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory"); | |
856 | ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread"); | |
857 | ignore |= !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress"); | |
5099ac24 | 858 | ignore |= !has_memtag && config.parse_name_directive(ln, "needs-sanitizer-memtag"); |
136023e0 XL |
859 | ignore |= config.target_panic == PanicStrategy::Abort |
860 | && config.parse_name_directive(ln, "needs-unwind"); | |
5099ac24 FG |
861 | ignore |= config.target == "wasm32-unknown-unknown" |
862 | && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS); | |
136023e0 XL |
863 | ignore |= config.debugger == Some(Debugger::Cdb) && ignore_cdb(config, ln); |
864 | ignore |= config.debugger == Some(Debugger::Gdb) && ignore_gdb(config, ln); | |
865 | ignore |= config.debugger == Some(Debugger::Lldb) && ignore_lldb(config, ln); | |
866 | ignore |= !has_rust_lld && config.parse_name_directive(ln, "needs-rust-lld"); | |
867 | should_fail |= config.parse_name_directive(ln, "should-fail"); | |
868 | }); | |
869 | ||
870 | // The `should-fail` annotation doesn't apply to pretty tests, | |
871 | // since we run the pretty printer across all tests by default. | |
872 | // If desired, we could add a `should-fail-pretty` annotation. | |
873 | let should_panic = match config.mode { | |
874 | crate::common::Pretty => test::ShouldPanic::No, | |
875 | _ if should_fail => test::ShouldPanic::Yes, | |
876 | _ => test::ShouldPanic::No, | |
877 | }; | |
878 | ||
879 | test::TestDesc { | |
880 | name, | |
881 | ignore, | |
ee023bcb FG |
882 | #[cfg(not(bootstrap))] |
883 | ignore_message, | |
136023e0 | 884 | should_panic, |
136023e0 XL |
885 | compile_fail: false, |
886 | no_run: false, | |
887 | test_type: test::TestType::Unknown, | |
888 | } | |
889 | } | |
890 | ||
891 | fn ignore_cdb(config: &Config, line: &str) -> bool { | |
892 | if let Some(actual_version) = config.cdb_version { | |
893 | if let Some(min_version) = line.strip_prefix("min-cdb-version:").map(str::trim) { | |
894 | let min_version = extract_cdb_version(min_version).unwrap_or_else(|| { | |
895 | panic!("couldn't parse version range: {:?}", min_version); | |
896 | }); | |
897 | ||
898 | // Ignore if actual version is smaller than the minimum | |
899 | // required version | |
900 | return actual_version < min_version; | |
901 | } | |
902 | } | |
903 | false | |
904 | } | |
905 | ||
906 | fn ignore_gdb(config: &Config, line: &str) -> bool { | |
907 | if let Some(actual_version) = config.gdb_version { | |
908 | if let Some(rest) = line.strip_prefix("min-gdb-version:").map(str::trim) { | |
909 | let (start_ver, end_ver) = extract_version_range(rest, extract_gdb_version) | |
910 | .unwrap_or_else(|| { | |
911 | panic!("couldn't parse version range: {:?}", rest); | |
912 | }); | |
913 | ||
914 | if start_ver != end_ver { | |
915 | panic!("Expected single GDB version") | |
916 | } | |
917 | // Ignore if actual version is smaller than the minimum | |
918 | // required version | |
919 | return actual_version < start_ver; | |
920 | } else if let Some(rest) = line.strip_prefix("ignore-gdb-version:").map(str::trim) { | |
921 | let (min_version, max_version) = extract_version_range(rest, extract_gdb_version) | |
922 | .unwrap_or_else(|| { | |
923 | panic!("couldn't parse version range: {:?}", rest); | |
924 | }); | |
925 | ||
926 | if max_version < min_version { | |
927 | panic!("Malformed GDB version range: max < min") | |
928 | } | |
929 | ||
930 | return actual_version >= min_version && actual_version <= max_version; | |
931 | } | |
932 | } | |
933 | false | |
934 | } | |
935 | ||
936 | fn ignore_lldb(config: &Config, line: &str) -> bool { | |
937 | if let Some(actual_version) = config.lldb_version { | |
938 | if let Some(min_version) = line.strip_prefix("min-lldb-version:").map(str::trim) { | |
939 | let min_version = min_version.parse().unwrap_or_else(|e| { | |
940 | panic!("Unexpected format of LLDB version string: {}\n{:?}", min_version, e); | |
941 | }); | |
942 | // Ignore if actual version is smaller the minimum required | |
943 | // version | |
944 | actual_version < min_version | |
945 | } else { | |
946 | line.starts_with("rust-lldb") && !config.lldb_native_rust | |
947 | } | |
948 | } else { | |
949 | false | |
950 | } | |
951 | } | |
952 | ||
953 | fn ignore_llvm(config: &Config, line: &str) -> bool { | |
954 | if config.system_llvm && line.starts_with("no-system-llvm") { | |
955 | return true; | |
956 | } | |
957 | if let Some(needed_components) = | |
958 | config.parse_name_value_directive(line, "needs-llvm-components") | |
959 | { | |
960 | let components: HashSet<_> = config.llvm_components.split_whitespace().collect(); | |
961 | if let Some(missing_component) = needed_components | |
962 | .split_whitespace() | |
963 | .find(|needed_component| !components.contains(needed_component)) | |
964 | { | |
965 | if env::var_os("COMPILETEST_NEEDS_ALL_LLVM_COMPONENTS").is_some() { | |
966 | panic!("missing LLVM component: {}", missing_component); | |
967 | } | |
968 | return true; | |
969 | } | |
970 | } | |
971 | if let Some(actual_version) = config.llvm_version { | |
972 | if let Some(rest) = line.strip_prefix("min-llvm-version:").map(str::trim) { | |
973 | let min_version = extract_llvm_version(rest).unwrap(); | |
974 | // Ignore if actual version is smaller the minimum required | |
975 | // version | |
976 | actual_version < min_version | |
977 | } else if let Some(rest) = line.strip_prefix("min-system-llvm-version:").map(str::trim) { | |
978 | let min_version = extract_llvm_version(rest).unwrap(); | |
979 | // Ignore if using system LLVM and actual version | |
980 | // is smaller the minimum required version | |
981 | config.system_llvm && actual_version < min_version | |
982 | } else if let Some(rest) = line.strip_prefix("ignore-llvm-version:").map(str::trim) { | |
983 | // Syntax is: "ignore-llvm-version: <version1> [- <version2>]" | |
984 | let (v_min, v_max) = | |
985 | extract_version_range(rest, extract_llvm_version).unwrap_or_else(|| { | |
986 | panic!("couldn't parse version range: {:?}", rest); | |
987 | }); | |
988 | if v_max < v_min { | |
989 | panic!("Malformed LLVM version range: max < min") | |
990 | } | |
991 | // Ignore if version lies inside of range. | |
992 | actual_version >= v_min && actual_version <= v_max | |
993 | } else { | |
994 | false | |
995 | } | |
996 | } else { | |
997 | false | |
998 | } | |
999 | } |