about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTim Neumann <mail@timnn.me>2016-10-29 20:11:53 +0200
committerTim Neumann <mail@timnn.me>2016-10-31 21:12:59 +0100
commitdce460028e9c21ebadb28f6744d4b10bf0e8d501 (patch)
tree6fa7314f67b10d5aa74d4666a448c92b04f08e1e
parent6554fb0d8d5d794db7a7532a5edd16eb28777a48 (diff)
downloadrust-dce460028e9c21ebadb28f6744d4b10bf0e8d501.tar.gz
rust-dce460028e9c21ebadb28f6744d4b10bf0e8d501.zip
detect gdb version & rust support in compiletest
-rwxr-xr-xconfigure7
-rw-r--r--mk/tests.mk2
-rw-r--r--src/bootstrap/check.rs4
-rw-r--r--src/bootstrap/config.rs6
-rw-r--r--src/bootstrap/config.toml.example3
-rw-r--r--src/bootstrap/lib.rs2
-rw-r--r--src/bootstrap/sanity.rs7
-rw-r--r--src/tools/compiletest/src/common.rs7
-rw-r--r--src/tools/compiletest/src/header.rs23
-rw-r--r--src/tools/compiletest/src/main.rs176
-rw-r--r--src/tools/compiletest/src/runtest.rs32
11 files changed, 180 insertions, 89 deletions
diff --git a/configure b/configure
index 2bc8c72e3ea..dab44c4a829 100755
--- a/configure
+++ b/configure
@@ -862,13 +862,6 @@ then
     fi
 fi
 
-if [ -n "$CFG_GDB" ]
-then
-    # Store GDB's version
-    CFG_GDB_VERSION=$($CFG_GDB --version 2>/dev/null | head -1)
-    putvar CFG_GDB_VERSION
-fi
-
 if [ -n "$CFG_LLDB" ]
 then
     # Store LLDB's version
diff --git a/mk/tests.mk b/mk/tests.mk
index 1957c989eb0..f3d8f0387bb 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -648,7 +648,7 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) = \
         --host $(3) \
 	--docck-python $$(CFG_PYTHON) \
 	--lldb-python $$(CFG_LLDB_PYTHON) \
-        --gdb-version="$(CFG_GDB_VERSION)" \
+        --gdb="$(CFG_GDB)" \
         --lldb-version="$(CFG_LLDB_VERSION)" \
         --llvm-version="$$(LLVM_VERSION_$(3))" \
         --android-cross-path=$(CFG_ARM_LINUX_ANDROIDEABI_NDK) \
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index af76a49fed0..50a2337b307 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -168,8 +168,8 @@ pub fn compiletest(build: &Build,
         cmd.arg("--lldb-python").arg(python_default);
     }
 
-    if let Some(ref vers) = build.gdb_version {
-        cmd.arg("--gdb-version").arg(vers);
+    if let Some(ref gdb) = build.config.gdb {
+        cmd.arg("--gdb").arg(gdb);
     }
     if let Some(ref vers) = build.lldb_version {
         cmd.arg("--lldb-version").arg(vers);
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 8c0ad1ccf82..0948a7c130e 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -85,6 +85,7 @@ pub struct Config {
     pub mandir: Option<String>,
     pub codegen_tests: bool,
     pub nodejs: Option<PathBuf>,
+    pub gdb: Option<PathBuf>,
 }
 
 /// Per-target configuration stored in the global configuration structure.
@@ -122,6 +123,7 @@ struct Build {
     compiler_docs: Option<bool>,
     docs: Option<bool>,
     submodules: Option<bool>,
+    gdb: Option<String>,
 }
 
 /// TOML representation of how the LLVM build is configured.
@@ -226,6 +228,7 @@ impl Config {
         }
         config.rustc = build.rustc.map(PathBuf::from);
         config.cargo = build.cargo.map(PathBuf::from);
+        config.gdb = build.gdb.map(PathBuf::from);
         set(&mut config.compiler_docs, build.compiler_docs);
         set(&mut config.docs, build.docs);
         set(&mut config.submodules, build.submodules);
@@ -392,6 +395,9 @@ impl Config {
                 "CFG_DEFAULT_LINKER" if value.len() > 0 => {
                     self.rustc_default_linker = Some(value.to_string());
                 }
+                "CFG_GDB" if value.len() > 0 => {
+                    self.gdb = Some(PathBuf::from(value));
+                }
                 "CFG_RELEASE_CHANNEL" => {
                     self.channel = value.to_string();
                 }
diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example
index b4730c003d6..1289cdba595 100644
--- a/src/bootstrap/config.toml.example
+++ b/src/bootstrap/config.toml.example
@@ -79,6 +79,9 @@
 # Indicate whether submodules are managed and updated automatically.
 #submodules = true
 
+# The path to (or name of) the GDB executable to use
+#gdb = "gdb"
+
 # =============================================================================
 # Options for compiling Rust code itself
 # =============================================================================
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 7c5a0c7373f..e99e088bffe 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -123,7 +123,6 @@ pub struct Build {
     bootstrap_key_stage0: String,
 
     // Probed tools at runtime
-    gdb_version: Option<String>,
     lldb_version: Option<String>,
     lldb_python_dir: Option<String>,
 
@@ -196,7 +195,6 @@ impl Build {
             package_vers: String::new(),
             cc: HashMap::new(),
             cxx: HashMap::new(),
-            gdb_version: None,
             lldb_version: None,
             lldb_python_dir: None,
         }
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 969cd70fd57..cc1b7136d47 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -92,6 +92,12 @@ pub fn check(build: &mut Build) {
         need_cmd(s.as_ref());
     }
 
+    if let Some(ref gdb) = build.config.gdb {
+        need_cmd(gdb.as_ref());
+    } else {
+        build.config.gdb = have_cmd("gdb".as_ref());
+    }
+
     // We're gonna build some custom C code here and there, host triples
     // also build some C++ shims for LLVM so we need a C++ compiler.
     for target in build.config.target.iter() {
@@ -198,7 +204,6 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
                    .to_string()
         })
     };
-    build.gdb_version = run(Command::new("gdb").arg("--version")).ok();
     build.lldb_version = run(Command::new("lldb").arg("--version")).ok();
     if build.lldb_version.is_some() {
         build.lldb_python_dir = run(Command::new("lldb").arg("-P")).ok();
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index b6b98d796e2..34f3837d8bb 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -146,8 +146,11 @@ pub struct Config {
     // Host triple for the compiler being invoked
     pub host: String,
 
-    // Version of GDB
-    pub gdb_version: Option<String>,
+    // Path to / name of the GDB executable
+    pub gdb: Option<String>,
+
+    // Version of GDB, encoded as ((major * 1000) + minor) * 1000 + patch
+    pub gdb_version: Option<u32>,
 
     // Whether GDB has native rust support
     pub gdb_native_rust: bool,
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 503a8516769..e57c9949b1c 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -18,6 +18,8 @@ use common::Config;
 use common;
 use util;
 
+use extract_gdb_version;
+
 /// Properties which must be known very early, before actually running
 /// the test.
 pub struct EarlyProps {
@@ -75,7 +77,7 @@ impl EarlyProps {
                 return true;
             }
 
-            if let Some(ref actual_version) = config.gdb_version {
+            if let Some(actual_version) = config.gdb_version {
                 if line.contains("min-gdb-version") {
                     let min_version = line.trim()
                         .split(' ')
@@ -83,7 +85,7 @@ impl EarlyProps {
                         .expect("Malformed GDB version directive");
                     // Ignore if actual version is smaller the minimum required
                     // version
-                    gdb_version_to_int(actual_version) < gdb_version_to_int(min_version)
+                    actual_version < extract_gdb_version(min_version).unwrap()
                 } else {
                     false
                 }
@@ -464,23 +466,6 @@ pub fn parse_name_value_directive(line: &str, directive: &str) -> Option<String>
     }
 }
 
-pub fn gdb_version_to_int(version_string: &str) -> isize {
-    let error_string = format!("Encountered GDB version string with unexpected format: {}",
-                               version_string);
-    let error_string = error_string;
-
-    let components: Vec<&str> = version_string.trim().split('.').collect();
-
-    if components.len() != 2 {
-        panic!("{}", error_string);
-    }
-
-    let major: isize = components[0].parse().ok().expect(&error_string);
-    let minor: isize = components[1].parse().ok().expect(&error_string);
-
-    return major * 1000 + minor;
-}
-
 pub fn lldb_version_to_int(version_string: &str) -> isize {
     let error_string = format!("Encountered LLDB version string with unexpected format: {}",
                                version_string);
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index c0557d8ff10..d6e64cc1752 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -12,6 +12,7 @@
 
 #![feature(box_syntax)]
 #![feature(rustc_private)]
+#![feature(static_in_const)]
 #![feature(test)]
 #![feature(libc)]
 
@@ -35,6 +36,7 @@ use std::ffi::OsString;
 use std::fs;
 use std::io;
 use std::path::{Path, PathBuf};
+use std::process::Command;
 use getopts::{optopt, optflag, reqopt};
 use common::Config;
 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Mode};
@@ -98,7 +100,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
           optopt("", "logfile", "file to log test execution to", "FILE"),
           optopt("", "target", "the target to build for", "TARGET"),
           optopt("", "host", "the host to build for", "HOST"),
-          optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"),
+          optopt("", "gdb", "path to GDB to use for GDB debuginfo tests", "PATH"),
           optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
           optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING"),
           optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
@@ -149,6 +151,8 @@ pub fn parse_config(args: Vec<String> ) -> Config {
         }
     }
 
+    let (gdb, gdb_version, gdb_native_rust) = analyze_gdb(matches.opt_str("gdb"));
+
     Config {
         compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
         run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
@@ -171,8 +175,9 @@ pub fn parse_config(args: Vec<String> ) -> Config {
         target_rustcflags: matches.opt_str("target-rustcflags"),
         target: opt_str2(matches.opt_str("target")),
         host: opt_str2(matches.opt_str("host")),
-        gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
-        gdb_native_rust: false,
+        gdb: gdb,
+        gdb_version: gdb_version,
+        gdb_native_rust: gdb_native_rust,
         lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
         llvm_version: matches.opt_str("llvm-version"),
         android_cross_path: opt_path(matches, "android-cross-path"),
@@ -471,44 +476,96 @@ pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn
     }))
 }
 
-fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
-    match full_version_line {
-        Some(ref full_version_line)
-          if !full_version_line.trim().is_empty() => {
-            let full_version_line = full_version_line.trim();
+/// Returns (Path to GDB, GDB Version, GDB has Rust Support)
+fn analyze_gdb(gdb: Option<String>) -> (Option<String>, Option<u32>, bool) {
+    #[cfg(not(windows))]
+    const GDB_FALLBACK: &str = "gdb";
+    #[cfg(windows)]
+    const GDB_FALLBACK: &str = "gdb.exe";
 
-            // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
-            for (pos, c) in full_version_line.char_indices() {
-                if !c.is_digit(10) {
-                    continue
-                }
-                if pos + 2 >= full_version_line.len() {
-                    continue
-                }
-                if full_version_line[pos + 1..].chars().next().unwrap() != '.' {
-                    continue
-                }
-                if !full_version_line[pos + 2..].chars().next().unwrap().is_digit(10) {
-                    continue
-                }
-                if pos > 0 && full_version_line[..pos].chars().next_back()
-                                                      .unwrap().is_digit(10) {
-                    continue
-                }
-                let mut end = pos + 3;
-                while end < full_version_line.len() &&
-                      full_version_line[end..].chars().next()
-                                              .unwrap().is_digit(10) {
-                    end += 1;
-                }
-                return Some(full_version_line[pos..end].to_owned());
-            }
-            println!("Could not extract GDB version from line '{}'",
-                     full_version_line);
-            None
-        },
-        _ => None
+    const MIN_GDB_WITH_RUST: u32 = 7011010;
+
+    let gdb = match gdb {
+        None => GDB_FALLBACK,
+        Some(ref s) if s.is_empty() => GDB_FALLBACK, // may be empty if configure found no gdb
+        Some(ref s) => s,
+    };
+
+    let version_line = Command::new(gdb).arg("--version").output().map(|output| {
+        String::from_utf8_lossy(&output.stdout).lines().next().unwrap().to_string()
+    }).ok();
+
+    let version = match version_line {
+        Some(line) => extract_gdb_version(&line),
+        None => return (None, None, false),
+    };
+
+    let gdb_native_rust = version.map_or(false, |v| v >= MIN_GDB_WITH_RUST);
+
+    return (Some(gdb.to_owned()), version, gdb_native_rust);
+}
+
+fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
+    let full_version_line = full_version_line.trim();
+
+    // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
+    // of the ? sections being optional
+
+    // We will parse up to 3 digits for minor and patch, ignoring the date
+    // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version
+
+    // don't start parsing in the middle of a number
+    let mut prev_was_digit = false;
+    for (pos, c) in full_version_line.char_indices() {
+        if prev_was_digit || !c.is_digit(10) {
+            prev_was_digit = c.is_digit(10);
+            continue
+        }
+
+        prev_was_digit = true;
+
+        let line = &full_version_line[pos..];
+
+        let next_split = match line.find(|c: char| !c.is_digit(10)) {
+            Some(idx) => idx,
+            None => continue, // no minor version
+        };
+
+        if line.as_bytes()[next_split] != b'.' {
+            continue; // no minor version
+        }
+
+        let major = &line[..next_split];
+        let line = &line[next_split + 1..];
+
+        let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
+            Some(idx) => if line.as_bytes()[idx] == b'.' {
+                let patch = &line[idx + 1..];
+
+                let patch_len = patch.find(|c: char| !c.is_digit(10)).unwrap_or(patch.len());
+                let patch = &patch[..patch_len];
+                let patch = if patch_len > 3 || patch_len == 0 { None } else { Some(patch) };
+
+                (&line[..idx], patch)
+            } else {
+                (&line[..idx], None)
+            },
+            None => (line, None),
+        };
+
+        if major.len() != 1 || minor.is_empty() {
+            continue;
+        }
+
+        let major: u32 = major.parse().unwrap();
+        let minor: u32 = minor.parse().unwrap();
+        let patch: u32 = patch.unwrap_or("0").parse().unwrap();
+
+        return Some(((major * 1000) + minor) * 1000 + patch);
     }
+
+    println!("Could not extract GDB version from line '{}'", full_version_line);
+    None
 }
 
 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
@@ -554,3 +611,44 @@ fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
 fn is_blacklisted_lldb_version(version: &str) -> bool {
     version == "350"
 }
+
+#[test]
+fn test_extract_gdb_version() {
+    macro_rules! test { ($($expectation:tt: $input:tt,)*) => {{$(
+        assert_eq!(extract_gdb_version($input), Some($expectation));
+    )*}}}
+
+    test! {
+        7000001: "GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)",
+
+        7002000: "GNU gdb (GDB) Red Hat Enterprise Linux (7.2-90.el6)",
+
+        7004000: "GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04",
+        7004001: "GNU gdb (GDB) 7.4.1-debian",
+
+        7006001: "GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7",
+
+        7007001: "GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1",
+        7007001: "GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1",
+        7007001: "GNU gdb (GDB) Fedora 7.7.1-21.fc20",
+
+        7008000: "GNU gdb (GDB; openSUSE 13.2) 7.8",
+        7009001: "GNU gdb (GDB) Fedora 7.9.1-20.fc22",
+        7010001: "GNU gdb (GDB) Fedora 7.10.1-31.fc23",
+
+        7011000: "GNU gdb (Ubuntu 7.11-0ubuntu1) 7.11",
+        7011001: "GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1",
+        7011001: "GNU gdb (Debian 7.11.1-2) 7.11.1",
+        7011001: "GNU gdb (GDB) Fedora 7.11.1-86.fc24",
+        7011001: "GNU gdb (GDB; openSUSE Leap 42.1) 7.11.1",
+        7011001: "GNU gdb (GDB; openSUSE Tumbleweed) 7.11.1",
+
+        7011090: "7.11.90",
+        7011090: "GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1) 7.11.90.20161005-git",
+
+        7012000: "7.12",
+        7012000: "GNU gdb (GDB) 7.12",
+        7012000: "GNU gdb (GDB) 7.12.20161027-git",
+        7012050: "GNU gdb (GDB) 7.12.50.20161027-git",
+    }
+}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index f6cd56423fb..3c6903ac21d 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -32,6 +32,8 @@ use std::path::{Path, PathBuf};
 use std::process::{Command, Output, ExitStatus};
 use std::str;
 
+use extract_gdb_version;
+
 pub fn run(config: Config, testpaths: &TestPaths) {
     match &*config.target {
 
@@ -44,6 +46,10 @@ pub fn run(config: Config, testpaths: &TestPaths) {
         _=> { }
     }
 
+    if config.mode == DebugInfoGdb && config.gdb.is_none() {
+        panic!("gdb not available but debuginfo gdb debuginfo test requested");
+    }
+
     if config.verbose {
         // We're going to be dumping a lot of info. Start on a new line.
         print!("\n\n");
@@ -598,19 +604,18 @@ actual:\n\
                 script_str.push_str("show version\n");
 
                 match self.config.gdb_version {
-                    Some(ref version) => {
+                    Some(version) => {
                         println!("NOTE: compiletest thinks it is using GDB version {}",
                                  version);
 
-                        if header::gdb_version_to_int(version) >
-                            header::gdb_version_to_int("7.4") {
-                                // Add the directory containing the pretty printers to
-                                // GDB's script auto loading safe path
-                                script_str.push_str(
-                                    &format!("add-auto-load-safe-path {}\n",
-                                             rust_pp_module_abs_path.replace(r"\", r"\\"))
-                                );
-                            }
+                        if version > extract_gdb_version("7.4").unwrap() {
+                            // Add the directory containing the pretty printers to
+                            // GDB's script auto loading safe path
+                            script_str.push_str(
+                                &format!("add-auto-load-safe-path {}\n",
+                                         rust_pp_module_abs_path.replace(r"\", r"\\"))
+                            );
+                        }
                     }
                     _ => {
                         println!("NOTE: compiletest does not know which version of \
@@ -645,11 +650,6 @@ actual:\n\
                 debug!("script_str = {}", script_str);
                 self.dump_output_file(&script_str, "debugger.script");
 
-                // run debugger script with gdb
-                fn debugger() -> &'static str {
-                    if cfg!(windows) {"gdb.exe"} else {"gdb"}
-                }
-
                 let debugger_script = self.make_out_name("debugger.script");
 
                 // FIXME (#9639): This needs to handle non-utf8 paths
@@ -660,7 +660,7 @@ actual:\n\
                          format!("-command={}", debugger_script.to_str().unwrap()));
 
                 let proc_args = ProcArgs {
-                    prog: debugger().to_owned(),
+                    prog: self.config.gdb.as_ref().unwrap().to_owned(),
                     args: debugger_opts,
                 };