From 9dd6dc2874bce63fd4f0106686a2406d19b9954d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 17 Nov 2022 11:16:30 -0800 Subject: [PATCH] cli: check glibc version more correctly on gnu (#166622) Fixes https://github.com/microsoft/vscode-remote-release/issues/7495 --- cli/src/util/prereqs.rs | 83 ++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs index 0070858ddeb..46bb0dc6dbf 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -17,6 +17,7 @@ use super::errors::AnyError; lazy_static! { static ref LDCONFIG_STDC_RE: Regex = Regex::new(r"libstdc\+\+.* => (.+)").unwrap(); static ref LDD_VERSION_RE: BinRegex = BinRegex::new(r"^ldd.*(.+)\.(.+)\s").unwrap(); + static ref GENERIC_VERSION_RE: Regex = Regex::new(r"^([0-9]+)\.([0-9]+)$").unwrap(); static ref LIBSTD_CXX_VERSION_RE: BinRegex = BinRegex::new(r"GLIBCXX_([0-9]+)\.([0-9]+)(?:\.([0-9]+))?").unwrap(); static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 18); @@ -116,13 +117,23 @@ async fn check_musl_interpreter() -> Result<(), String> { #[allow(dead_code)] async fn check_glibc_version() -> Result<(), String> { - let ldd_version = capture_command("ldd", ["--version"]) - .await - .ok() - .and_then(|o| extract_ldd_version(&o.stdout)); + #[cfg(target_env = "gnu")] + let version = { + let v = unsafe { libc::gnu_get_libc_version() }; + let v = unsafe { std::ffi::CStr::from_ptr(v) }; + let v = v.to_str().unwrap(); + extract_generic_version(v) + }; + #[cfg(not(target_env = "gnu"))] + let version = { + capture_command("ldd", ["--version"]) + .await + .ok() + .and_then(|o| extract_ldd_version(&o.stdout)) + }; - if let Some(v) = ldd_version { - return if v.gte(&MIN_LDD_VERSION) { + if let Some(v) = version { + return if v >= *MIN_LDD_VERSION { Ok(()) } else { Err(format!( @@ -181,7 +192,7 @@ fn check_for_sufficient_glibcxx_versions(contents: Vec) -> Result<(), String }) .collect(); - if !all_versions.iter().any(|v| MIN_CXX_VERSION.gte(v)) { + if !all_versions.iter().any(|v| &*MIN_CXX_VERSION >= v) { return Err(format!( "find GLIBCXX >= 3.4.18 (but found {} instead) for GNU environments", all_versions @@ -195,6 +206,7 @@ fn check_for_sufficient_glibcxx_versions(contents: Vec) -> Result<(), String Ok(()) } +#[allow(dead_code)] fn extract_ldd_version(output: &[u8]) -> Option { LDD_VERSION_RE.captures(output).map(|m| SimpleSemver { major: m.get(1).map_or(0, |s| u32_from_bytes(s.as_bytes())), @@ -203,6 +215,15 @@ fn extract_ldd_version(output: &[u8]) -> Option { }) } +#[allow(dead_code)] +fn extract_generic_version(output: &str) -> Option { + GENERIC_VERSION_RE.captures(output).map(|m| SimpleSemver { + major: m.get(1).map_or(0, |s| s.as_str().parse().unwrap()), + minor: m.get(2).map_or(0, |s| s.as_str().parse().unwrap()), + patch: 0, + }) +} + fn extract_libstd_from_ldconfig(output: &[u8]) -> Option { String::from_utf8_lossy(output) .lines() @@ -215,13 +236,35 @@ fn u32_from_bytes(b: &[u8]) -> u32 { String::from_utf8_lossy(b).parse::().unwrap_or(0) } -#[derive(Debug, PartialEq)] +#[derive(Debug, Default, PartialEq, Eq)] struct SimpleSemver { major: u32, minor: u32, patch: u32, } +impl PartialOrd for SimpleSemver { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for SimpleSemver { + fn cmp(&self, other: &Self) -> Ordering { + let major = self.major.cmp(&other.major); + if major != Ordering::Equal { + return major; + } + + let minor = self.minor.cmp(&other.minor); + if minor != Ordering::Equal { + return minor; + } + + self.patch.cmp(&other.patch) + } +} + impl From<&SimpleSemver> for String { fn from(s: &SimpleSemver) -> Self { format!("v{}.{}.{}", s.major, s.minor, s.patch) @@ -243,18 +286,6 @@ impl SimpleSemver { patch, } } - - fn gte(&self, other: &SimpleSemver) -> bool { - match self.major.cmp(&other.major) { - Ordering::Greater => true, - Ordering::Less => false, - Ordering::Equal => match self.minor.cmp(&other.minor) { - Ordering::Greater => true, - Ordering::Less => false, - Ordering::Equal => self.patch >= other.patch, - }, - } - } } #[cfg(test)] @@ -284,13 +315,13 @@ mod tests { #[test] fn test_gte() { - assert!(SimpleSemver::new(1, 2, 3).gte(&SimpleSemver::new(1, 2, 3))); - assert!(SimpleSemver::new(1, 2, 3).gte(&SimpleSemver::new(0, 10, 10))); - assert!(SimpleSemver::new(1, 2, 3).gte(&SimpleSemver::new(1, 1, 10))); + assert!(SimpleSemver::new(1, 2, 3) >= SimpleSemver::new(1, 2, 3)); + assert!(SimpleSemver::new(1, 2, 3) >= SimpleSemver::new(0, 10, 10)); + assert!(SimpleSemver::new(1, 2, 3) >= SimpleSemver::new(1, 1, 10)); - assert!(!SimpleSemver::new(1, 2, 3).gte(&SimpleSemver::new(1, 2, 10))); - assert!(!SimpleSemver::new(1, 2, 3).gte(&SimpleSemver::new(1, 3, 1))); - assert!(!SimpleSemver::new(1, 2, 3).gte(&SimpleSemver::new(2, 2, 1))); + assert!(SimpleSemver::new(1, 2, 3) < SimpleSemver::new(1, 2, 10)); + assert!(SimpleSemver::new(1, 2, 3) < SimpleSemver::new(1, 3, 1)); + assert!(SimpleSemver::new(1, 2, 3) < SimpleSemver::new(2, 2, 1)); } #[test]