1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![crate_name = "compiletest"]
13 #![feature(slice_rotate)]
17 extern crate env_logger;
18 extern crate filetime;
24 extern crate rustc_serialize;
29 use std::ffi::OsString;
32 use std::path::{Path, PathBuf};
33 use std::process::Command;
34 use filetime::FileTime;
36 use common::{Config, TestPaths};
37 use common::{DebugInfoGdb, DebugInfoLldb, Mode, Pretty};
38 use common::{expected_output_path, UI_EXTENSIONS};
39 use test::ColorConfig;
42 use self::header::EarlyProps;
54 env_logger::init().unwrap();
56 let config = parse_config(env::args().collect());
58 if config.valgrind_path.is_none() && config.force_valgrind {
59 panic!("Can't find Valgrind to run Valgrind tests");
66 pub fn parse_config(args: Vec<String>) -> Config {
67 let mut opts = Options::new();
71 "path to host shared libraries",
76 "path to target shared libraries",
82 "path to rustc to use for compiling",
88 "path to rustdoc to use for compiling",
94 "path to python to use for doc tests",
100 "path to python to use for doc tests",
106 "path to Valgrind executable for Valgrind tests",
112 "fail if Valgrind tests cannot be run under Valgrind",
117 "path to LLVM's FileCheck binary",
120 .reqopt("", "src-base", "directory to scan for test files", "PATH")
124 "directory to deposit test outputs",
130 "the target-stage identifier",
136 "which sort of compile tests to run",
137 "(compile-fail|parse-fail|run-fail|run-pass|\
138 run-pass-valgrind|pretty|debug-info|incremental|mir-opt)",
140 .optflag("", "ignored", "run tests marked as ignored")
141 .optflag("", "exact", "filters match exactly")
145 "supervisor program to run tests under \
146 (eg. emulator, valgrind)",
152 "flags to pass to rustc for host",
158 "flags to pass to rustc for target",
161 .optflag("", "verbose", "run tests verbosely, showing all output")
165 "print one character per test instead of one line",
167 .optopt("", "color", "coloring: auto, always, never", "WHEN")
168 .optopt("", "logfile", "file to log test execution to", "FILE")
169 .optopt("", "target", "the target to build for", "TARGET")
170 .optopt("", "host", "the host to build for", "HOST")
174 "path to GDB to use for GDB debuginfo tests",
180 "the version of LLDB used",
186 "the version of LLVM used",
189 .optflag("", "system-llvm", "is LLVM the system LLVM")
192 "android-cross-path",
193 "Android NDK standalone path",
196 .optopt("", "adb-path", "path to the android debugger", "PATH")
200 "path to tests for the android debugger",
206 "directory containing LLDB's python module",
209 .reqopt("", "cc", "path to a C compiler", "PATH")
210 .reqopt("", "cxx", "path to a C++ compiler", "PATH")
211 .reqopt("", "cflags", "flags for the C compiler", "FLAGS")
212 .optopt("", "ar", "path to an archiver", "PATH")
213 .optopt("", "linker", "path to a linker", "PATH")
217 "list of LLVM components built in",
220 .reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS")
221 .optopt("", "nodejs", "the name of nodejs", "PATH")
224 "remote-test-client",
225 "path to the remote test client",
228 .optflag("h", "help", "show this message");
230 let (argv0, args_) = args.split_first().unwrap();
231 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
232 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
233 println!("{}", opts.usage(&message));
238 let matches = &match opts.parse(args_) {
240 Err(f) => panic!("{:?}", f),
243 if matches.opt_present("h") || matches.opt_present("help") {
244 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
245 println!("{}", opts.usage(&message));
250 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
251 match m.opt_str(nm) {
252 Some(s) => PathBuf::from(&s),
253 None => panic!("no option (=path) found for {}", nm),
257 fn make_absolute(path: PathBuf) -> PathBuf {
258 if path.is_relative() {
259 env::current_dir().unwrap().join(path)
265 let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"));
267 let color = match matches.opt_str("color").as_ref().map(|x| &**x) {
268 Some("auto") | None => ColorConfig::AutoColor,
269 Some("always") => ColorConfig::AlwaysColor,
270 Some("never") => ColorConfig::NeverColor,
272 "argument for --color must be auto, always, or never, but found `{}`",
278 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
279 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
280 rustc_path: opt_path(matches, "rustc-path"),
281 rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
282 lldb_python: matches.opt_str("lldb-python").unwrap(),
283 docck_python: matches.opt_str("docck-python").unwrap(),
284 valgrind_path: matches.opt_str("valgrind-path"),
285 force_valgrind: matches.opt_present("force-valgrind"),
286 llvm_filecheck: matches.opt_str("llvm-filecheck").map(|s| PathBuf::from(&s)),
287 src_base: opt_path(matches, "src-base"),
288 build_base: opt_path(matches, "build-base"),
289 stage_id: matches.opt_str("stage-id").unwrap(),
294 .expect("invalid mode"),
295 run_ignored: matches.opt_present("ignored"),
296 filter: matches.free.first().cloned(),
297 filter_exact: matches.opt_present("exact"),
298 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
299 runtool: matches.opt_str("runtool"),
300 host_rustcflags: matches.opt_str("host-rustcflags"),
301 target_rustcflags: matches.opt_str("target-rustcflags"),
302 target: opt_str2(matches.opt_str("target")),
303 host: opt_str2(matches.opt_str("host")),
307 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
308 llvm_version: matches.opt_str("llvm-version"),
309 system_llvm: matches.opt_present("system-llvm"),
310 android_cross_path: opt_path(matches, "android-cross-path"),
311 adb_path: opt_str2(matches.opt_str("adb-path")),
312 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
313 adb_device_status: opt_str2(matches.opt_str("target")).contains("android")
314 && "(none)" != opt_str2(matches.opt_str("adb-test-dir"))
315 && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
316 lldb_python_dir: matches.opt_str("lldb-python-dir"),
317 verbose: matches.opt_present("verbose"),
318 quiet: matches.opt_present("quiet"),
320 remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
322 cc: matches.opt_str("cc").unwrap(),
323 cxx: matches.opt_str("cxx").unwrap(),
324 cflags: matches.opt_str("cflags").unwrap(),
325 ar: matches.opt_str("ar").unwrap_or("ar".into()),
326 linker: matches.opt_str("linker"),
327 llvm_components: matches.opt_str("llvm-components").unwrap(),
328 llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
329 nodejs: matches.opt_str("nodejs"),
333 pub fn log_config(config: &Config) {
335 logv(c, "configuration:".to_string());
338 format!("compile_lib_path: {:?}", config.compile_lib_path),
340 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
341 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
342 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
343 logv(c, format!("src_base: {:?}", config.src_base.display()));
344 logv(c, format!("build_base: {:?}", config.build_base.display()));
345 logv(c, format!("stage_id: {}", config.stage_id));
346 logv(c, format!("mode: {}", config.mode));
347 logv(c, format!("run_ignored: {}", config.run_ignored));
352 opt_str(&config.filter.as_ref().map(|re| re.to_owned()))
355 logv(c, format!("filter_exact: {}", config.filter_exact));
356 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
359 format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)),
363 format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)),
365 logv(c, format!("target: {}", config.target));
366 logv(c, format!("host: {}", config.host));
370 "android-cross-path: {:?}",
371 config.android_cross_path.display()
374 logv(c, format!("adb_path: {:?}", config.adb_path));
375 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
378 format!("adb_device_status: {}", config.adb_device_status),
380 logv(c, format!("ar: {}", config.ar));
381 logv(c, format!("linker: {:?}", config.linker));
382 logv(c, format!("verbose: {}", config.verbose));
383 logv(c, format!("quiet: {}", config.quiet));
384 logv(c, "\n".to_string());
387 pub fn opt_str(maybestr: &Option<String>) -> &str {
394 pub fn opt_str2(maybestr: Option<String>) -> String {
396 None => "(none)".to_owned(),
401 pub fn run_tests(config: &Config) {
402 if config.target.contains("android") {
403 if let DebugInfoGdb = config.mode {
405 "{} debug-info test uses tcp 5039 port.\
410 // android debug-info test uses remote debugger so, we test 1 thread
411 // at once as they're all sharing the same TCP port to communicate
414 // we should figure out how to lift this restriction! (run them all
415 // on different ports allocated dynamically).
416 env::set_var("RUST_TEST_THREADS", "1");
422 if let Some(lldb_version) = config.lldb_version.as_ref() {
423 if is_blacklisted_lldb_version(&lldb_version[..]) {
425 "WARNING: The used version of LLDB ({}) has a \
426 known issue that breaks debuginfo tests. See \
427 issue #32520 for more information. Skipping all \
435 // Some older versions of LLDB seem to have problems with multiple
436 // instances running in parallel, so only run one test thread at a
438 env::set_var("RUST_TEST_THREADS", "1");
442 if config.remote_test_client.is_some() && !config.target.contains("android") {
444 "WARNING: debuginfo tests are not available when \
450 _ => { /* proceed */ }
453 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
454 if let Mode::CodegenUnits = config.mode {
455 let _ = fs::remove_dir_all("tmp/partitioning-tests");
458 let opts = test_opts(config);
459 let tests = make_tests(config);
460 // sadly osx needs some file descriptor limits raised for running tests in
461 // parallel (especially when we have lots and lots of child processes).
462 // For context, see #8904
464 raise_fd_limit::raise_fd_limit();
466 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
467 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
468 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
470 // Let tests know which target they're running as
471 env::set_var("TARGET", &config.target);
473 let res = test::run_tests_console(&opts, tests.into_iter().collect());
476 Ok(false) => panic!("Some tests failed"),
478 println!("I/O failure during tests: {:?}", e);
483 pub fn test_opts(config: &Config) -> test::TestOpts {
485 filter: config.filter.clone(),
486 filter_exact: config.filter_exact,
487 run_ignored: config.run_ignored,
489 logfile: config.logfile.clone(),
491 bench_benchmarks: true,
492 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
493 Ok(val) => &val != "0",
500 options: test::Options::new(),
504 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
505 debug!("making tests from {:?}", config.src_base.display());
506 let mut tests = Vec::new();
507 collect_tests_from_dir(
517 fn collect_tests_from_dir(
521 relative_dir_path: &Path,
522 tests: &mut Vec<test::TestDescAndFn>,
523 ) -> io::Result<()> {
524 // Ignore directories that contain a file
525 // `compiletest-ignore-dir`.
526 for file in fs::read_dir(dir)? {
528 let name = file.file_name();
529 if name == *"compiletest-ignore-dir" {
532 if name == *"Makefile" && config.mode == Mode::RunMake {
533 let paths = TestPaths {
534 file: dir.to_path_buf(),
535 base: base.to_path_buf(),
536 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
538 tests.push(make_test(config, &paths));
543 // If we find a test foo/bar.rs, we have to build the
544 // output directory `$build/foo` so we can write
545 // `$build/foo/bar` into it. We do this *now* in this
546 // sequential loop because otherwise, if we do it in the
547 // tests themselves, they race for the privilege of
548 // creating the directories and sometimes fail randomly.
549 let build_dir = config.build_base.join(&relative_dir_path);
550 fs::create_dir_all(&build_dir).unwrap();
552 // Add each `.rs` file as a test, and recurse further on any
553 // subdirectories we find, except for `aux` directories.
554 let dirs = fs::read_dir(dir)?;
557 let file_path = file.path();
558 let file_name = file.file_name();
559 if is_test(&file_name) {
560 debug!("found test file: {:?}", file_path.display());
561 let paths = TestPaths {
563 base: base.to_path_buf(),
564 relative_dir: relative_dir_path.to_path_buf(),
566 tests.push(make_test(config, &paths))
567 } else if file_path.is_dir() {
568 let relative_file_path = relative_dir_path.join(file.file_name());
569 if &file_name == "auxiliary" {
570 // `aux` directories contain other crates used for
571 // cross-crate tests. Don't search them for tests, but
572 // do create a directory in the build dir for them,
573 // since we will dump intermediate output in there
575 let build_dir = config.build_base.join(&relative_file_path);
576 fs::create_dir_all(&build_dir).unwrap();
578 debug!("found directory: {:?}", file_path.display());
579 collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?;
582 debug!("found other file/directory: {:?}", file_path.display());
588 pub fn is_test(file_name: &OsString) -> bool {
589 let file_name = file_name.to_str().unwrap();
591 if !file_name.ends_with(".rs") {
595 // `.`, `#`, and `~` are common temp-file prefixes.
596 let invalid_prefixes = &[".", "#", "~"];
597 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
600 pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
601 let early_props = EarlyProps::from_file(config, &testpaths.file);
603 // The `should-fail` annotation doesn't apply to pretty tests,
604 // since we run the pretty printer across all tests by default.
605 // If desired, we could add a `should-fail-pretty` annotation.
606 let should_panic = match config.mode {
607 Pretty => test::ShouldPanic::No,
608 _ => if early_props.should_fail {
609 test::ShouldPanic::Yes
611 test::ShouldPanic::No
615 // Debugging emscripten code doesn't make sense today
616 let ignore = early_props.ignore || !up_to_date(config, testpaths, &early_props)
617 || (config.mode == DebugInfoGdb || config.mode == DebugInfoLldb)
618 && config.target.contains("emscripten");
620 test::TestDescAndFn {
621 desc: test::TestDesc {
622 name: make_test_name(config, testpaths),
627 testfn: make_test_closure(config, testpaths),
631 fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf {
632 let stamp_name = format!(
634 testpaths.file.file_name().unwrap().to_str().unwrap(),
640 .unwrap_or_else(|_| config.build_base.clone())
641 .join(&testpaths.relative_dir)
645 fn up_to_date(config: &Config, testpaths: &TestPaths, props: &EarlyProps) -> bool {
646 let rust_src_dir = config
647 .find_rust_src_root()
648 .expect("Could not find Rust source root");
649 let stamp = mtime(&stamp(config, testpaths));
650 let mut inputs = vec![mtime(&testpaths.file), mtime(&config.rustc_path)];
651 for aux in props.aux.iter() {
652 inputs.push(mtime(&testpaths
659 // Relevant pretty printer files
660 let pretty_printer_files = [
661 "src/etc/debugger_pretty_printers_common.py",
662 "src/etc/gdb_load_rust_pretty_printers.py",
663 "src/etc/gdb_rust_pretty_printing.py",
664 "src/etc/lldb_batchmode.py",
665 "src/etc/lldb_rust_formatters.py",
667 for pretty_printer_file in &pretty_printer_files {
668 inputs.push(mtime(&rust_src_dir.join(pretty_printer_file)));
670 let mut entries = config.run_lib_path.read_dir().unwrap()
671 .collect::<Vec<_>>();
672 while let Some(entry) = entries.pop() {
673 let entry = entry.unwrap();
674 let path = entry.path();
675 if entry.metadata().unwrap().is_file() {
676 inputs.push(mtime(&path));
678 entries.extend(path.read_dir().unwrap());
681 if let Some(ref rustdoc_path) = config.rustdoc_path {
682 inputs.push(mtime(&rustdoc_path));
683 inputs.push(mtime(&rust_src_dir.join("src/etc/htmldocck.py")));
687 for extension in UI_EXTENSIONS {
688 for revision in &props.revisions {
689 let path = &expected_output_path(testpaths, Some(revision), extension);
690 inputs.push(mtime(path));
693 if props.revisions.is_empty() {
694 let path = &expected_output_path(testpaths, None, extension);
695 inputs.push(mtime(path));
699 inputs.iter().any(|input| *input > stamp)
702 fn mtime(path: &Path) -> FileTime {
704 .map(|f| FileTime::from_last_modification_time(&f))
705 .unwrap_or_else(|_| FileTime::zero())
708 pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
709 // Convert a complete path to something like
711 // run-pass/foo/bar/baz.rs
712 let path = PathBuf::from(config.src_base.file_name().unwrap())
713 .join(&testpaths.relative_dir)
714 .join(&testpaths.file.file_name().unwrap());
715 test::DynTestName(format!("[{}] {}", config.mode, path.display()))
718 pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
719 let config = config.clone();
720 let testpaths = testpaths.clone();
721 test::DynTestFn(Box::new(move || runtest::run(config, &testpaths)))
724 /// Returns (Path to GDB, GDB Version, GDB has Rust Support)
725 fn analyze_gdb(gdb: Option<String>) -> (Option<String>, Option<u32>, bool) {
727 const GDB_FALLBACK: &str = "gdb";
729 const GDB_FALLBACK: &str = "gdb.exe";
731 const MIN_GDB_WITH_RUST: u32 = 7011010;
733 let gdb = match gdb {
734 None => GDB_FALLBACK,
735 Some(ref s) if s.is_empty() => GDB_FALLBACK, // may be empty if configure found no gdb
739 let version_line = Command::new(gdb)
743 String::from_utf8_lossy(&output.stdout)
751 let version = match version_line {
752 Some(line) => extract_gdb_version(&line),
753 None => return (None, None, false),
756 let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
758 (Some(gdb.to_owned()), version, gdb_native_rust)
761 fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
762 let full_version_line = full_version_line.trim();
764 // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
765 // of the ? sections being optional
767 // We will parse up to 3 digits for minor and patch, ignoring the date
768 // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
770 // don't start parsing in the middle of a number
771 let mut prev_was_digit = false;
772 for (pos, c) in full_version_line.char_indices() {
773 if prev_was_digit || !c.is_digit(10) {
774 prev_was_digit = c.is_digit(10);
778 prev_was_digit = true;
780 let line = &full_version_line[pos..];
782 let next_split = match line.find(|c: char| !c.is_digit(10)) {
784 None => continue, // no minor version
787 if line.as_bytes()[next_split] != b'.' {
788 continue; // no minor version
791 let major = &line[..next_split];
792 let line = &line[next_split + 1..];
794 let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
795 Some(idx) => if line.as_bytes()[idx] == b'.' {
796 let patch = &line[idx + 1..];
798 let patch_len = patch
799 .find(|c: char| !c.is_digit(10))
800 .unwrap_or_else(|| patch.len());
801 let patch = &patch[..patch_len];
802 let patch = if patch_len > 3 || patch_len == 0 {
808 (&line[..idx], patch)
812 None => (line, None),
815 if major.len() != 1 || minor.is_empty() {
819 let major: u32 = major.parse().unwrap();
820 let minor: u32 = minor.parse().unwrap();
821 let patch: u32 = patch.unwrap_or("0").parse().unwrap();
823 return Some(((major * 1000) + minor) * 1000 + patch);
829 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
830 // Extract the major LLDB version from the given version string.
831 // LLDB version strings are different for Apple and non-Apple platforms.
832 // At the moment, this function only supports the Apple variant, which looks
835 // LLDB-179.5 (older versions)
836 // lldb-300.2.51 (new versions)
838 // We are only interested in the major version number, so this function
839 // will return `Some("179")` and `Some("300")` respectively.
841 if let Some(ref full_version_line) = full_version_line {
842 if !full_version_line.trim().is_empty() {
843 let full_version_line = full_version_line.trim();
845 for (pos, l) in full_version_line.char_indices() {
846 if l != 'l' && l != 'L' {
849 if pos + 5 >= full_version_line.len() {
852 let l = full_version_line[pos + 1..].chars().next().unwrap();
853 if l != 'l' && l != 'L' {
856 let d = full_version_line[pos + 2..].chars().next().unwrap();
857 if d != 'd' && d != 'D' {
860 let b = full_version_line[pos + 3..].chars().next().unwrap();
861 if b != 'b' && b != 'B' {
864 let dash = full_version_line[pos + 4..].chars().next().unwrap();
869 let vers = full_version_line[pos + 5..]
871 .take_while(|c| c.is_digit(10))
872 .collect::<String>();
873 if !vers.is_empty() {
882 fn is_blacklisted_lldb_version(version: &str) -> bool {
887 fn test_extract_gdb_version() {
888 macro_rules! test { ($($expectation:tt: $input:tt,)*) => {{$(
889 assert_eq!(extract_gdb_version($input), Some($expectation));
893 7000001: "GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)",
895 7002000: "GNU gdb (GDB) Red Hat Enterprise Linux (7.2-90.el6)",
897 7004000: "GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04",
898 7004001: "GNU gdb (GDB) 7.4.1-debian",
900 7006001: "GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7",
902 7007001: "GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1",
903 7007001: "GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1",
904 7007001: "GNU gdb (GDB) Fedora 7.7.1-21.fc20",
906 7008000: "GNU gdb (GDB; openSUSE 13.2) 7.8",
907 7009001: "GNU gdb (GDB) Fedora 7.9.1-20.fc22",
908 7010001: "GNU gdb (GDB) Fedora 7.10.1-31.fc23",
910 7011000: "GNU gdb (Ubuntu 7.11-0ubuntu1) 7.11",
911 7011001: "GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1",
912 7011001: "GNU gdb (Debian 7.11.1-2) 7.11.1",
913 7011001: "GNU gdb (GDB) Fedora 7.11.1-86.fc24",
914 7011001: "GNU gdb (GDB; openSUSE Leap 42.1) 7.11.1",
915 7011001: "GNU gdb (GDB; openSUSE Tumbleweed) 7.11.1",
918 7011090: "GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1) 7.11.90.20161005-git",
921 7012000: "GNU gdb (GDB) 7.12",
922 7012000: "GNU gdb (GDB) 7.12.20161027-git",
923 7012050: "GNU gdb (GDB) 7.12.50.20161027-git",