Skip to content

Commit

Permalink
move glibc version detection logic into its own crate
Browse files Browse the repository at this point in the history
so we can write tests for it
  • Loading branch information
Qingping Hou authored and houqp committed Mar 23, 2021
1 parent 324a5e5 commit 7781719
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 36 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ members = [
"ruby",
"rust",
"python",
"glibc_version",
]
8 changes: 8 additions & 0 deletions glibc_version/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "glibc_version"
version = "0.1.0"
authors = ["Qingping Hou <qph@scribd.com>"]
edition = "2018"

[dependencies]
regex = "1"
88 changes: 88 additions & 0 deletions glibc_version/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
pub struct Version {
pub major: usize,
pub minor: usize,
}

#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod imp {
use super::Version;
use std::process::Command;

use regex::Regex;

// glibc version is taken from std/sys/unix/os.rs
pub fn get_version() -> Result<Version, String> {
let output = Command::new("ldd")
.args(&["--version"])
.output()
.expect("failed to execute ldd");
let output_str = std::str::from_utf8(&output.stdout).unwrap();
let version_str = ldd_output_to_version_str(output_str)?;

parse_glibc_version(version_str)
.ok_or_else(|| format!("Invalid version string from ldd output: {}", version_str,))
}

fn ldd_output_to_version_str(output_str: &str) -> Result<&str, String> {
let version_reg = Regex::new(r#"ldd \(.+\) ([0-9]+\.[0-9]+)"#).unwrap();
if let Some(captures) = version_reg.captures(output_str) {
Ok(captures.get(1).unwrap().as_str())
} else {
Err(format!(
"ERROR: failed to detect glibc version. ldd output: {}",
output_str,
))
}
}

// Returns Some((major, minor)) if the string is a valid "x.y" version,
// ignoring any extra dot-separated parts. Otherwise return None.
fn parse_glibc_version(version: &str) -> Option<Version> {
let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
match (parsed_ints.next(), parsed_ints.next()) {
(Some(Ok(major)), Some(Ok(minor))) => Some(Version { major, minor }),
_ => None,
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn parse_ldd_output() {
let ver_str = ldd_output_to_version_str(
r#"ldd (GNU libc) 2.12
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper."#,
)
.unwrap();
assert_eq!(ver_str, "2.12");

let ver_str = ldd_output_to_version_str(
r#"ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper."#,
)
.unwrap();
assert_eq!(ver_str, "2.31");
}
}
}

#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
mod imp {
use super::Version;
pub fn get_version() -> Result<Version, String> {
unimplemented!();
}
}

#[inline]
pub fn get_version() -> Result<Version, String> {
imp::get_version()
}
2 changes: 1 addition & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ azure = ["azure_core", "azure_storage", "reqwest"]
s3 = ["rusoto_core", "rusoto_credential", "rusoto_s3", "rusoto_sts"]

[build-dependencies]
regex = "1"
glibc_version = { path = "../glibc_version" }

[dev-dependencies]
utime = "0.3"
Expand Down
37 changes: 2 additions & 35 deletions rust/build.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,8 @@
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod platform_cfg {
// glibc version is taken from std/sys/unix/os.rs
pub fn glibc_version() -> (usize, usize) {
use regex::Regex;
use std::process::Command;

let output = Command::new("ldd")
.args(&["--version"])
.output()
.expect("failed to execute ldd");
let output_str = std::str::from_utf8(&output.stdout).unwrap();

let version_reg = Regex::new(r#"ldd \(.+\) ([0-9]+\.[0-9]+)"#).unwrap();
if let Some(captures) = version_reg.captures(output_str) {
let version_str = captures.get(1).unwrap().as_str();
parse_glibc_version(version_str).unwrap()
} else {
panic!(
"ERROR: failed to detect glibc version. ldd output: {}",
output_str
);
}
}

// Returns Some((major, minor)) if the string is a valid "x.y" version,
// ignoring any extra dot-separated parts. Otherwise return None.
fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
match (parsed_ints.next(), parsed_ints.next()) {
(Some(Ok(major)), Some(Ok(minor))) => Some((major, minor)),
_ => None,
}
}

fn detect_glibc_renameat2() {
let (major, minor) = glibc_version();
if major >= 2 && minor >= 28 {
let ver = glibc_version::get_version().unwrap();
if ver.major >= 2 && ver.minor >= 28 {
println!("cargo:rustc-cfg=glibc_renameat2");
}
}
Expand Down

0 comments on commit 7781719

Please sign in to comment.