3 use std::io::prelude::*;
4 use std::io::BufReader;
5 use std::path::{Path, PathBuf};
9 use crate::common::{self, CompareMode, Config, Mode};
12 use crate::extract_gdb_version;
14 /// Whether to ignore the test.
15 #[derive(Clone, Copy, PartialEq, Debug)]
19 /// Ignore it totally.
21 /// Ignore only the gdb test, but run the lldb test.
23 /// Ignore only the lldb test, but run the gdb test.
28 pub fn can_run_gdb(&self) -> bool {
29 *self == Ignore::Run || *self == Ignore::IgnoreLldb
32 pub fn can_run_lldb(&self) -> bool {
33 *self == Ignore::Run || *self == Ignore::IgnoreGdb
36 pub fn no_gdb(&self) -> Ignore {
38 Ignore::Run => Ignore::IgnoreGdb,
39 Ignore::IgnoreGdb => Ignore::IgnoreGdb,
44 pub fn no_lldb(&self) -> Ignore {
46 Ignore::Run => Ignore::IgnoreLldb,
47 Ignore::IgnoreLldb => Ignore::IgnoreLldb,
53 /// The result of parse_cfg_name_directive.
54 #[derive(Clone, Copy, PartialEq, Debug)]
55 enum ParsedNameDirective {
60 /// Mode was DebugInfoBoth and this matched gdb.
62 /// Mode was DebugInfoBoth and this matched lldb.
66 /// Properties which must be known very early, before actually running
68 pub struct EarlyProps {
70 pub should_fail: bool,
72 pub revisions: Vec<String>,
76 pub fn from_file(config: &Config, testfile: &Path) -> Self {
77 let mut props = EarlyProps {
84 if config.mode == common::DebugInfoBoth {
85 if config.lldb_python_dir.is_none() {
86 props.ignore = props.ignore.no_lldb();
88 if config.gdb_version.is_none() {
89 props.ignore = props.ignore.no_gdb();
93 let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
94 let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some();
96 iter_header(testfile, None, &mut |ln| {
97 // we should check if any only-<platform> exists and if it exists
98 // and does not matches the current platform, skip the test
99 if props.ignore != Ignore::Ignore {
100 props.ignore = match config.parse_cfg_name_directive(ln, "ignore") {
101 ParsedNameDirective::Match => Ignore::Ignore,
102 ParsedNameDirective::NoMatch => props.ignore,
103 ParsedNameDirective::MatchGdb => props.ignore.no_gdb(),
104 ParsedNameDirective::MatchLldb => props.ignore.no_lldb(),
107 if config.has_cfg_prefix(ln, "only") {
108 props.ignore = match config.parse_cfg_name_directive(ln, "only") {
109 ParsedNameDirective::Match => props.ignore,
110 ParsedNameDirective::NoMatch => Ignore::Ignore,
111 ParsedNameDirective::MatchLldb => props.ignore.no_gdb(),
112 ParsedNameDirective::MatchGdb => props.ignore.no_lldb(),
116 if ignore_llvm(config, ln) {
117 props.ignore = Ignore::Ignore;
120 if config.run_clang_based_tests_with.is_none() &&
121 config.parse_needs_matching_clang(ln) {
122 props.ignore = Ignore::Ignore;
125 if !rustc_has_profiler_support &&
126 config.parse_needs_profiler_support(ln) {
127 props.ignore = Ignore::Ignore;
130 if !rustc_has_sanitizer_support &&
131 config.parse_needs_sanitizer_support(ln) {
132 props.ignore = Ignore::Ignore;
136 if (config.mode == common::DebugInfoGdb || config.mode == common::DebugInfoBoth) &&
137 props.ignore.can_run_gdb() && ignore_gdb(config, ln) {
138 props.ignore = props.ignore.no_gdb();
141 if (config.mode == common::DebugInfoLldb || config.mode == common::DebugInfoBoth) &&
142 props.ignore.can_run_lldb() && ignore_lldb(config, ln) {
143 props.ignore = props.ignore.no_lldb();
146 if let Some(s) = config.parse_aux_build(ln) {
150 if let Some(r) = config.parse_revisions(ln) {
151 props.revisions.extend(r);
154 props.should_fail = props.should_fail || config.parse_name_directive(ln, "should-fail");
159 fn ignore_gdb(config: &Config, line: &str) -> bool {
160 if let Some(actual_version) = config.gdb_version {
161 if line.starts_with("min-gdb-version") {
162 let (start_ver, end_ver) = extract_gdb_version_range(line);
164 if start_ver != end_ver {
165 panic!("Expected single GDB version")
167 // Ignore if actual version is smaller the minimum required
169 actual_version < start_ver
170 } else if line.starts_with("ignore-gdb-version") {
171 let (min_version, max_version) = extract_gdb_version_range(line);
173 if max_version < min_version {
174 panic!("Malformed GDB version range: max < min")
177 actual_version >= min_version && actual_version <= max_version
186 // Takes a directive of the form "ignore-gdb-version <version1> [- <version2>]",
187 // returns the numeric representation of <version1> and <version2> as
188 // tuple: (<version1> as u32, <version2> as u32)
189 // If the <version2> part is omitted, the second component of the tuple
190 // is the same as <version1>.
191 fn extract_gdb_version_range(line: &str) -> (u32, u32) {
192 const ERROR_MESSAGE: &'static str = "Malformed GDB version directive";
194 let range_components = line.split(&[' ', '-'][..])
195 .filter(|word| !word.is_empty())
196 .map(extract_gdb_version)
197 .skip_while(Option::is_none)
198 .take(3) // 3 or more = invalid, so take at most 3.
199 .collect::<Vec<Option<u32>>>();
201 match range_components.len() {
203 let v = range_components[0].unwrap();
207 let v_min = range_components[0].unwrap();
208 let v_max = range_components[1].expect(ERROR_MESSAGE);
211 _ => panic!(ERROR_MESSAGE),
215 fn ignore_lldb(config: &Config, line: &str) -> bool {
216 if let Some(ref actual_version) = config.lldb_version {
217 if line.starts_with("min-lldb-version") {
218 let min_version = line.trim_end()
221 .expect("Malformed lldb version directive");
222 // Ignore if actual version is smaller the minimum required
224 lldb_version_to_int(actual_version) < lldb_version_to_int(min_version)
225 } else if line.starts_with("rust-lldb") && !config.lldb_native_rust {
235 fn ignore_llvm(config: &Config, line: &str) -> bool {
236 if config.system_llvm && line.starts_with("no-system-llvm") {
239 if let Some(ref actual_version) = config.llvm_version {
240 if line.starts_with("min-llvm-version") {
241 let min_version = line.trim_end()
244 .expect("Malformed llvm version directive");
245 // Ignore if actual version is smaller the minimum required
247 &actual_version[..] < min_version
248 } else if line.starts_with("min-system-llvm-version") {
249 let min_version = line.trim_end()
252 .expect("Malformed llvm version directive");
253 // Ignore if using system LLVM and actual version
254 // is smaller the minimum required version
255 config.system_llvm && &actual_version[..] < min_version
256 } else if line.starts_with("ignore-llvm-version") {
257 // Syntax is: "ignore-llvm-version <version1> [- <version2>]"
258 let range_components = line.split(' ')
259 .skip(1) // Skip the directive.
261 .filter(|word| !word.is_empty() && word != &"-")
262 .take(3) // 3 or more = invalid, so take at most 3.
263 .collect::<Vec<&str>>();
264 match range_components.len() {
266 &actual_version[..] == range_components[0]
269 let v_min = range_components[0];
270 let v_max = range_components[1];
272 panic!("Malformed LLVM version range: max < min")
274 // Ignore if version lies inside of range.
275 &actual_version[..] >= v_min && &actual_version[..] <= v_max
277 _ => panic!("Malformed LLVM version directive"),
289 #[derive(Clone, Debug)]
290 pub struct TestProps {
291 // Lines that should be expected, in order, on standard out
292 pub error_patterns: Vec<String>,
293 // Extra flags to pass to the compiler
294 pub compile_flags: Vec<String>,
295 // Extra flags to pass when the compiled code is run (such as --bench)
296 pub run_flags: Option<String>,
297 // If present, the name of a file that this test should match when
299 pub pp_exact: Option<PathBuf>,
300 // Other crates that should be compiled (typically from the same
301 // directory as the test, but for backwards compatibility reasons
302 // we also check the auxiliary directory)
303 pub aux_builds: Vec<String>,
304 // A list of crates to pass '--extern-private name:PATH' flags for
305 // This should be a subset of 'aux_build'
306 // FIXME: Replace this with a better solution: https://github.com/rust-lang/rust/pull/54020
307 pub extern_private: Vec<String>,
308 // Environment settings to use for compiling
309 pub rustc_env: Vec<(String, String)>,
310 // Environment variables to unset prior to compiling.
311 // Variables are unset before applying 'rustc_env'.
312 pub unset_rustc_env: Vec<String>,
313 // Environment settings to use during execution
314 pub exec_env: Vec<(String, String)>,
315 // Lines to check if they appear in the expected debugger output
316 pub check_lines: Vec<String>,
317 // Build documentation for all specified aux-builds as well
318 pub build_aux_docs: bool,
319 // Flag to force a crate to be built with the host architecture
320 pub force_host: bool,
321 // Check stdout for error-pattern output as well as stderr
322 pub check_stdout: bool,
323 // For UI tests, allows compiler to generate arbitrary output to stdout
324 pub dont_check_compiler_stdout: bool,
325 // For UI tests, allows compiler to generate arbitrary output to stderr
326 pub dont_check_compiler_stderr: bool,
327 // Don't force a --crate-type=dylib flag on the command line
329 // Set this for example if you have an auxiliary test file that contains
330 // a proc-macro and needs `#![crate_type = "proc-macro"]`. This ensures
331 // that the aux file is compiled as a `proc-macro` and not as a `dylib`.
332 pub no_prefer_dynamic: bool,
333 // Run --pretty expanded when running pretty printing tests
334 pub pretty_expanded: bool,
335 // Which pretty mode are we testing with, default to 'normal'
336 pub pretty_mode: String,
337 // Only compare pretty output and don't try compiling
338 pub pretty_compare_only: bool,
339 // Patterns which must not appear in the output of a cfail test.
340 pub forbid_output: Vec<String>,
341 // Revisions to test for incremental compilation.
342 pub revisions: Vec<String>,
343 // Directory (if any) to use for incremental compilation. This is
344 // not set by end-users; rather it is set by the incremental
345 // testing harness and used when generating compilation
346 // arguments. (In particular, it propagates to the aux-builds.)
347 pub incremental_dir: Option<PathBuf>,
348 // Specifies that a test must actually compile without errors.
349 pub compile_pass: bool,
350 // rustdoc will test the output of the `--test` option
351 pub check_test_line_numbers_match: bool,
352 // The test must be compiled and run successfully. Only used in UI tests for now.
354 // Skip any codegen step and running the executable. Only for run-pass.
355 pub skip_codegen: bool,
356 // Do not pass `-Z ui-testing` to UI tests
357 pub disable_ui_testing_normalization: bool,
358 // customized normalization rules
359 pub normalize_stdout: Vec<(String, String)>,
360 pub normalize_stderr: Vec<(String, String)>,
361 pub failure_status: i32,
362 // Whether or not `rustfix` should apply the `CodeSuggestion`s of this test and compile the
363 // resulting Rust code.
364 pub run_rustfix: bool,
365 // If true, `rustfix` will only apply `MachineApplicable` suggestions.
366 pub rustfix_only_machine_applicable: bool,
367 pub assembly_output: Option<String>,
371 pub fn new() -> Self {
373 error_patterns: vec![],
374 compile_flags: vec![],
378 extern_private: vec![],
381 unset_rustc_env: vec![],
384 build_aux_docs: false,
387 dont_check_compiler_stdout: false,
388 dont_check_compiler_stderr: false,
389 no_prefer_dynamic: false,
390 pretty_expanded: false,
391 pretty_mode: "normal".to_string(),
392 pretty_compare_only: false,
393 forbid_output: vec![],
394 incremental_dir: None,
396 check_test_line_numbers_match: false,
399 disable_ui_testing_normalization: false,
400 normalize_stdout: vec![],
401 normalize_stderr: vec![],
404 rustfix_only_machine_applicable: false,
405 assembly_output: None,
409 pub fn from_aux_file(&self, testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
410 let mut props = TestProps::new();
412 // copy over select properties to the aux build:
413 props.incremental_dir = self.incremental_dir.clone();
414 props.load_from(testfile, cfg, config);
419 pub fn from_file(testfile: &Path, cfg: Option<&str>, config: &Config) -> Self {
420 let mut props = TestProps::new();
421 props.load_from(testfile, cfg, config);
425 /// Loads properties from `testfile` into `props`. If a property is
426 /// tied to a particular revision `foo` (indicated by writing
427 /// `//[foo]`), then the property is ignored unless `cfg` is
429 fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
430 iter_header(testfile, cfg, &mut |ln| {
431 if let Some(ep) = config.parse_error_pattern(ln) {
432 self.error_patterns.push(ep);
435 if let Some(flags) = config.parse_compile_flags(ln) {
437 .extend(flags.split_whitespace().map(|s| s.to_owned()));
440 if let Some(edition) = config.parse_edition(ln) {
441 self.compile_flags.push(format!("--edition={}", edition));
444 if let Some(r) = config.parse_revisions(ln) {
445 self.revisions.extend(r);
448 if self.run_flags.is_none() {
449 self.run_flags = config.parse_run_flags(ln);
452 if self.pp_exact.is_none() {
453 self.pp_exact = config.parse_pp_exact(ln, testfile);
456 if !self.build_aux_docs {
457 self.build_aux_docs = config.parse_build_aux_docs(ln);
460 if !self.force_host {
461 self.force_host = config.parse_force_host(ln);
464 if !self.check_stdout {
465 self.check_stdout = config.parse_check_stdout(ln);
468 if !self.dont_check_compiler_stdout {
469 self.dont_check_compiler_stdout = config.parse_dont_check_compiler_stdout(ln);
472 if !self.dont_check_compiler_stderr {
473 self.dont_check_compiler_stderr = config.parse_dont_check_compiler_stderr(ln);
476 if !self.no_prefer_dynamic {
477 self.no_prefer_dynamic = config.parse_no_prefer_dynamic(ln);
480 if !self.pretty_expanded {
481 self.pretty_expanded = config.parse_pretty_expanded(ln);
484 if let Some(m) = config.parse_pretty_mode(ln) {
485 self.pretty_mode = m;
488 if !self.pretty_compare_only {
489 self.pretty_compare_only = config.parse_pretty_compare_only(ln);
492 if let Some(ab) = config.parse_aux_build(ln) {
493 self.aux_builds.push(ab);
496 if let Some(ep) = config.parse_extern_private(ln) {
497 self.extern_private.push(ep);
500 if let Some(ee) = config.parse_env(ln, "exec-env") {
501 self.exec_env.push(ee);
504 if let Some(ee) = config.parse_env(ln, "rustc-env") {
505 self.rustc_env.push(ee);
508 if let Some(ev) = config.parse_name_value_directive(ln, "unset-rustc-env") {
509 self.unset_rustc_env.push(ev);
512 if let Some(cl) = config.parse_check_line(ln) {
513 self.check_lines.push(cl);
516 if let Some(of) = config.parse_forbid_output(ln) {
517 self.forbid_output.push(of);
520 if !self.check_test_line_numbers_match {
521 self.check_test_line_numbers_match = config.parse_check_test_line_numbers_match(ln);
525 self.run_pass = config.parse_run_pass(ln);
528 if !self.compile_pass {
529 // run-pass implies compile_pass
530 self.compile_pass = config.parse_compile_pass(ln) || self.run_pass;
533 if !self.skip_codegen {
534 self.skip_codegen = config.parse_skip_codegen(ln);
537 if !self.disable_ui_testing_normalization {
538 self.disable_ui_testing_normalization =
539 config.parse_disable_ui_testing_normalization(ln);
542 if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stdout") {
543 self.normalize_stdout.push(rule);
545 if let Some(rule) = config.parse_custom_normalization(ln, "normalize-stderr") {
546 self.normalize_stderr.push(rule);
549 if let Some(code) = config.parse_failure_status(ln) {
550 self.failure_status = code;
553 if !self.run_rustfix {
554 self.run_rustfix = config.parse_run_rustfix(ln);
557 if !self.rustfix_only_machine_applicable {
558 self.rustfix_only_machine_applicable =
559 config.parse_rustfix_only_machine_applicable(ln);
562 if self.assembly_output.is_none() {
563 self.assembly_output = config.parse_assembly_output(ln);
567 if self.failure_status == -1 {
568 self.failure_status = match config.mode {
569 Mode::RunFail => 101,
574 for key in &["RUST_TEST_NOCAPTURE", "RUST_TEST_THREADS"] {
575 if let Ok(val) = env::var(key) {
576 if self.exec_env.iter().find(|&&(ref x, _)| x == key).is_none() {
577 self.exec_env.push(((*key).to_owned(), val))
584 fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut dyn FnMut(&str)) {
585 if testfile.is_dir() {
589 let comment = if testfile.to_string_lossy().ends_with(".rs") {
595 // FIXME: would be nice to allow some whitespace between comment and brace :)
596 // It took me like 2 days to debug why compile-flags weren’t taken into account for my test :)
597 let comment_with_brace = comment.to_string() + "[";
599 let rdr = BufReader::new(File::open(testfile).unwrap());
600 for ln in rdr.lines() {
601 // Assume that any directives will be found before the first
602 // module or function. This doesn't seem to be an optimization
603 // with a warm page cache. Maybe with a cold one.
604 let ln = ln.unwrap();
606 if ln.starts_with("fn") || ln.starts_with("mod") {
608 } else if ln.starts_with(&comment_with_brace) {
609 // A comment like `//[foo]` is specific to revision `foo`
610 if let Some(close_brace) = ln.find(']') {
611 let open_brace = ln.find('[').unwrap();
612 let lncfg = &ln[open_brace + 1 .. close_brace];
613 let matches = match cfg {
614 Some(s) => s == &lncfg[..],
618 it(ln[(close_brace + 1)..].trim_start());
621 panic!("malformed condition directive: expected `{}foo]`, found `{}`",
622 comment_with_brace, ln)
624 } else if ln.starts_with(comment) {
625 it(ln[comment.len() ..].trim_start());
632 fn parse_error_pattern(&self, line: &str) -> Option<String> {
633 self.parse_name_value_directive(line, "error-pattern")
636 fn parse_forbid_output(&self, line: &str) -> Option<String> {
637 self.parse_name_value_directive(line, "forbid-output")
640 fn parse_aux_build(&self, line: &str) -> Option<String> {
641 self.parse_name_value_directive(line, "aux-build")
642 .map(|r| r.trim().to_string())
645 fn parse_extern_private(&self, line: &str) -> Option<String> {
646 self.parse_name_value_directive(line, "extern-private")
649 fn parse_compile_flags(&self, line: &str) -> Option<String> {
650 self.parse_name_value_directive(line, "compile-flags")
653 fn parse_revisions(&self, line: &str) -> Option<Vec<String>> {
654 self.parse_name_value_directive(line, "revisions")
655 .map(|r| r.split_whitespace().map(|t| t.to_string()).collect())
658 fn parse_run_flags(&self, line: &str) -> Option<String> {
659 self.parse_name_value_directive(line, "run-flags")
662 fn parse_check_line(&self, line: &str) -> Option<String> {
663 self.parse_name_value_directive(line, "check")
666 fn parse_force_host(&self, line: &str) -> bool {
667 self.parse_name_directive(line, "force-host")
670 fn parse_build_aux_docs(&self, line: &str) -> bool {
671 self.parse_name_directive(line, "build-aux-docs")
674 fn parse_check_stdout(&self, line: &str) -> bool {
675 self.parse_name_directive(line, "check-stdout")
678 fn parse_dont_check_compiler_stdout(&self, line: &str) -> bool {
679 self.parse_name_directive(line, "dont-check-compiler-stdout")
682 fn parse_dont_check_compiler_stderr(&self, line: &str) -> bool {
683 self.parse_name_directive(line, "dont-check-compiler-stderr")
686 fn parse_no_prefer_dynamic(&self, line: &str) -> bool {
687 self.parse_name_directive(line, "no-prefer-dynamic")
690 fn parse_pretty_expanded(&self, line: &str) -> bool {
691 self.parse_name_directive(line, "pretty-expanded")
694 fn parse_pretty_mode(&self, line: &str) -> Option<String> {
695 self.parse_name_value_directive(line, "pretty-mode")
698 fn parse_pretty_compare_only(&self, line: &str) -> bool {
699 self.parse_name_directive(line, "pretty-compare-only")
702 fn parse_failure_status(&self, line: &str) -> Option<i32> {
703 match self.parse_name_value_directive(line, "failure-status") {
704 Some(code) => code.trim().parse::<i32>().ok(),
709 fn parse_compile_pass(&self, line: &str) -> bool {
710 self.parse_name_directive(line, "compile-pass")
713 fn parse_disable_ui_testing_normalization(&self, line: &str) -> bool {
714 self.parse_name_directive(line, "disable-ui-testing-normalization")
717 fn parse_check_test_line_numbers_match(&self, line: &str) -> bool {
718 self.parse_name_directive(line, "check-test-line-numbers-match")
721 fn parse_run_pass(&self, line: &str) -> bool {
722 self.parse_name_directive(line, "run-pass")
725 fn parse_skip_codegen(&self, line: &str) -> bool {
726 self.parse_name_directive(line, "skip-codegen")
729 fn parse_assembly_output(&self, line: &str) -> Option<String> {
730 self.parse_name_value_directive(line, "assembly-output")
731 .map(|r| r.trim().to_string())
734 fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> {
735 self.parse_name_value_directive(line, name).map(|nv| {
736 // nv is either FOO or FOO=BAR
737 let mut strs: Vec<String> = nv.splitn(2, '=').map(str::to_owned).collect();
740 1 => (strs.pop().unwrap(), String::new()),
742 let end = strs.pop().unwrap();
743 (strs.pop().unwrap(), end)
745 n => panic!("Expected 1 or 2 strings, not {}", n),
750 fn parse_pp_exact(&self, line: &str, testfile: &Path) -> Option<PathBuf> {
751 if let Some(s) = self.parse_name_value_directive(line, "pp-exact") {
752 Some(PathBuf::from(&s))
753 } else if self.parse_name_directive(line, "pp-exact") {
754 testfile.file_name().map(PathBuf::from)
760 fn parse_custom_normalization(&self, mut line: &str, prefix: &str) -> Option<(String, String)> {
761 if self.parse_cfg_name_directive(line, prefix) == ParsedNameDirective::Match {
762 let from = parse_normalization_string(&mut line)?;
763 let to = parse_normalization_string(&mut line)?;
770 fn parse_needs_matching_clang(&self, line: &str) -> bool {
771 self.parse_name_directive(line, "needs-matching-clang")
774 fn parse_needs_profiler_support(&self, line: &str) -> bool {
775 self.parse_name_directive(line, "needs-profiler-support")
778 fn parse_needs_sanitizer_support(&self, line: &str) -> bool {
779 self.parse_name_directive(line, "needs-sanitizer-support")
782 /// Parses a name-value directive which contains config-specific information, e.g., `ignore-x86`
783 /// or `normalize-stderr-32bit`.
784 fn parse_cfg_name_directive(&self, line: &str, prefix: &str) -> ParsedNameDirective {
785 if line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-') {
786 let name = line[prefix.len() + 1..]
787 .split(&[':', ' '][..])
792 util::matches_os(&self.target, name) || // target
793 name == util::get_arch(&self.target) || // architecture
794 name == util::get_pointer_width(&self.target) || // pointer width
795 name == self.stage_id.split('-').next().unwrap() || // stage
796 Some(name) == util::get_env(&self.target) || // env
797 (self.target != self.host && name == "cross-compile") ||
798 match self.compare_mode {
799 Some(CompareMode::Nll) => name == "compare-mode-nll",
800 Some(CompareMode::Polonius) => name == "compare-mode-polonius",
803 (cfg!(debug_assertions) && name == "debug") {
804 ParsedNameDirective::Match
807 common::DebugInfoBoth => {
809 ParsedNameDirective::MatchGdb
810 } else if name == "lldb" {
811 ParsedNameDirective::MatchLldb
813 ParsedNameDirective::NoMatch
816 common::DebugInfoGdb => if name == "gdb" {
817 ParsedNameDirective::Match
819 ParsedNameDirective::NoMatch
821 common::DebugInfoLldb => if name == "lldb" {
822 ParsedNameDirective::Match
824 ParsedNameDirective::NoMatch
826 common::Pretty => if name == "pretty" {
827 ParsedNameDirective::Match
829 ParsedNameDirective::NoMatch
831 _ => ParsedNameDirective::NoMatch,
835 ParsedNameDirective::NoMatch
839 fn has_cfg_prefix(&self, line: &str, prefix: &str) -> bool {
840 // returns whether this line contains this prefix or not. For prefix
841 // "ignore", returns true if line says "ignore-x86_64", "ignore-arch",
842 // "ignore-android" etc.
843 line.starts_with(prefix) && line.as_bytes().get(prefix.len()) == Some(&b'-')
846 fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
847 // Ensure the directive is a whole word. Do not match "ignore-x86" when
848 // the line says "ignore-x86_64".
849 line.starts_with(directive) && match line.as_bytes().get(directive.len()) {
850 None | Some(&b' ') | Some(&b':') => true,
855 pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
856 let colon = directive.len();
857 if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
858 let value = line[(colon + 1)..].to_owned();
859 debug!("{}: {}", directive, value);
860 Some(expand_variables(value, self))
866 pub fn find_rust_src_root(&self) -> Option<PathBuf> {
867 let mut path = self.src_base.clone();
868 let path_postfix = Path::new("src/etc/lldb_batchmode.py");
871 if path.join(&path_postfix).is_file() {
879 fn parse_run_rustfix(&self, line: &str) -> bool {
880 self.parse_name_directive(line, "run-rustfix")
883 fn parse_rustfix_only_machine_applicable(&self, line: &str) -> bool {
884 self.parse_name_directive(line, "rustfix-only-machine-applicable")
887 fn parse_edition(&self, line: &str) -> Option<String> {
888 self.parse_name_value_directive(line, "edition")
892 pub fn lldb_version_to_int(version_string: &str) -> isize {
893 let error_string = format!(
894 "Encountered LLDB version string with unexpected format: {}",
897 version_string.parse().expect(&error_string)
900 fn expand_variables(mut value: String, config: &Config) -> String {
901 const CWD: &'static str = "{{cwd}}";
902 const SRC_BASE: &'static str = "{{src-base}}";
903 const BUILD_BASE: &'static str = "{{build-base}}";
905 if value.contains(CWD) {
906 let cwd = env::current_dir().unwrap();
907 value = value.replace(CWD, &cwd.to_string_lossy());
910 if value.contains(SRC_BASE) {
911 value = value.replace(SRC_BASE, &config.src_base.to_string_lossy());
914 if value.contains(BUILD_BASE) {
915 value = value.replace(BUILD_BASE, &config.build_base.to_string_lossy());
921 /// Finds the next quoted string `"..."` in `line`, and extract the content from it. Move the `line`
922 /// variable after the end of the quoted string.
927 /// let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\".";
928 /// let first = parse_normalization_string(&mut s);
929 /// assert_eq!(first, Some("something (32 bits)".to_owned()));
930 /// assert_eq!(s, " -> \"something ($WORD bits)\".");
932 fn parse_normalization_string(line: &mut &str) -> Option<String> {
933 // FIXME support escapes in strings.
934 let begin = line.find('"')? + 1;
935 let end = line[begin..].find('"')? + begin;
936 let result = line[begin..end].to_owned();
937 *line = &line[end + 1..];
942 fn test_parse_normalization_string() {
943 let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\".";
944 let first = parse_normalization_string(&mut s);
945 assert_eq!(first, Some("something (32 bits)".to_owned()));
946 assert_eq!(s, " -> \"something ($WORD bits)\".");
948 // Nothing to normalize (No quotes)
949 let mut s = "normalize-stderr-32bit: something (32 bits) -> something ($WORD bits).";
950 let first = parse_normalization_string(&mut s);
951 assert_eq!(first, None);
952 assert_eq!(s, r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."#);
954 // Nothing to normalize (Only a single quote)
955 let mut s = "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits).";
956 let first = parse_normalization_string(&mut s);
957 assert_eq!(first, None);
958 assert_eq!(s, "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits).");
960 // Nothing to normalize (Three quotes)
961 let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits).";
962 let first = parse_normalization_string(&mut s);
963 assert_eq!(first, Some("something (32 bits)".to_owned()));
964 assert_eq!(s, " -> \"something ($WORD bits).");