Fix musl compilation (#234)

musl (which we already use in ruff) allows statically linked binaries on
linux. This PR switches to rustls and vendors and fixes the glibc
detection. Using static musl builds makes it easier to avoid glibc
errors in docker and we'll need it later for alpine users anyway.

An alternative is using vendored openssl.
This commit is contained in:
konsti 2023-10-30 18:10:17 +01:00 committed by GitHub
parent d47dc64974
commit 29bd0a4ed8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 279 additions and 264 deletions

214
Cargo.lock generated
View file

@ -784,21 +784,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
"foreign-types-shared",
]
[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.0" version = "1.2.0"
@ -1198,16 +1183,17 @@ dependencies = [
] ]
[[package]] [[package]]
name = "hyper-tls" name = "hyper-rustls"
version = "0.5.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97"
dependencies = [ dependencies = [
"bytes", "futures-util",
"http",
"hyper", "hyper",
"native-tls", "rustls",
"tokio", "tokio",
"tokio-native-tls", "tokio-rustls",
] ]
[[package]] [[package]]
@ -1594,24 +1580,6 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "native-tls"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
dependencies = [
"lazy_static",
"libc",
"log",
"openssl",
"openssl-probe",
"openssl-sys",
"schannel",
"security-framework",
"security-framework-sys",
"tempfile",
]
[[package]] [[package]]
name = "normalize-line-endings" name = "normalize-line-endings"
version = "0.3.0" version = "0.3.0"
@ -1668,50 +1636,6 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "openssl"
version = "0.10.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
dependencies = [
"bitflags 2.4.1",
"cfg-if 1.0.0",
"foreign-types",
"libc",
"once_cell",
"openssl-macros",
"openssl-sys",
]
[[package]]
name = "openssl-macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "option-ext" name = "option-ext"
version = "0.2.0" version = "0.2.0"
@ -1851,12 +1775,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]] [[package]]
name = "plain" name = "plain"
version = "0.2.3" version = "0.2.3"
@ -1868,7 +1786,6 @@ name = "platform-host"
version = "0.0.1" version = "0.0.1"
dependencies = [ dependencies = [
"fs-err", "fs-err",
"glibc_version",
"goblin", "goblin",
"platform-info", "platform-info",
"plist", "plist",
@ -2562,22 +2479,23 @@ dependencies = [
"http", "http",
"http-body", "http-body",
"hyper", "hyper",
"hyper-tls", "hyper-rustls",
"ipnet", "ipnet",
"js-sys", "js-sys",
"log", "log",
"mime", "mime",
"mime_guess", "mime_guess",
"native-tls",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
"pin-project-lite", "pin-project-lite",
"rustls",
"rustls-pemfile",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded", "serde_urlencoded",
"system-configuration", "system-configuration",
"tokio", "tokio",
"tokio-native-tls", "tokio-rustls",
"tokio-util", "tokio-util",
"tower-service", "tower-service",
"url", "url",
@ -2585,6 +2503,7 @@ dependencies = [
"wasm-bindgen-futures", "wasm-bindgen-futures",
"wasm-streams", "wasm-streams",
"web-sys", "web-sys",
"webpki-roots",
"winreg", "winreg",
] ]
@ -2651,6 +2570,20 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "ring"
version = "0.17.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
dependencies = [
"cc",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -2676,6 +2609,37 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "rustls"
version = "0.21.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c"
dependencies = [
"log",
"ring",
"rustls-webpki",
"sct",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
dependencies = [
"base64 0.21.4",
]
[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.15" version = "1.0.15"
@ -2697,15 +2661,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "schannel"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
dependencies = [
"windows-sys 0.48.0",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@ -2733,26 +2688,13 @@ dependencies = [
] ]
[[package]] [[package]]
name = "security-framework" name = "sct"
version = "2.9.2" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [ dependencies = [
"bitflags 1.3.2", "ring",
"core-foundation", "untrusted",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
dependencies = [
"core-foundation-sys",
"libc",
] ]
[[package]] [[package]]
@ -2896,6 +2838,12 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "ssri" name = "ssri"
version = "9.2.0" version = "9.2.0"
@ -3225,12 +3173,12 @@ dependencies = [
] ]
[[package]] [[package]]
name = "tokio-native-tls" name = "tokio-rustls"
version = "0.3.1" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [ dependencies = [
"native-tls", "rustls",
"tokio", "tokio",
] ]
@ -3477,6 +3425,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47" checksum = "e9df2af067a7953e9c3831320f35c1cc0600c30d44d9f7a12b01db1cd88d6b47"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "url" name = "url"
version = "2.4.1" version = "2.4.1"
@ -3501,12 +3455,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
@ -3694,6 +3642,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "webpki-roots"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]] [[package]]
name = "which" name = "which"
version = "4.4.2" version = "4.4.2"

View file

@ -46,7 +46,7 @@ pyproject-toml = { version = "0.7.0" }
rayon = { version = "1.8.0" } rayon = { version = "1.8.0" }
reflink-copy = { version = "0.1.10" } reflink-copy = { version = "0.1.10" }
regex = { version = "1.9.6" } regex = { version = "1.9.6" }
reqwest = { version = "0.11.22", features = ["json", "gzip", "brotli", "stream"] } reqwest = { version = "0.11.22", default-features = false, features = ["json", "gzip", "brotli", "stream", "rustls-tls"] }
reqwest-middleware = { version = "0.2.3" } reqwest-middleware = { version = "0.2.3" }
reqwest-retry = { version = "0.3.0" } reqwest-retry = { version = "0.3.0" }
rfc2047-decoder = { version = "1.0.1" } rfc2047-decoder = { version = "1.0.1" }

View file

@ -24,7 +24,7 @@ clap = { workspace = true }
configparser = { workspace = true } configparser = { workspace = true }
dirs = { workspace = true } dirs = { workspace = true }
fs-err = { workspace = true } fs-err = { workspace = true }
reqwest = { workspace = true, optional = true, features = ["blocking"] } reqwest = { workspace = true, optional = true, default-features = false, features = ["blocking"] }
rayon = { workspace = true, optional = true } rayon = { workspace = true, optional = true }
serde = { workspace = true } serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }

View file

@ -11,12 +11,11 @@ license = { workspace = true }
[dependencies] [dependencies]
fs-err = { workspace = true } fs-err = { workspace = true }
glibc_version = { workspace = true }
goblin = { workspace = true } goblin = { workspace = true }
platform-info = { workspace = true } platform-info = { workspace = true }
plist = { workspace = true } plist = { workspace = true }
regex = { workspace = true } regex = { workspace = true }
serde = { workspace = true } serde = { workspace = true, features = ["derive"] }
target-lexicon = { workspace = true } target-lexicon = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }

View file

@ -1,16 +1,15 @@
//! Abstractions for understanding the current platform (operating system and architecture). //! Abstractions for understanding the current platform (operating system and architecture).
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::{fmt, io}; use std::{fmt, io};
use fs_err as fs;
use goblin::elf::Elf;
use platform_info::{PlatformInfo, PlatformInfoAPI, UNameAPI}; use platform_info::{PlatformInfo, PlatformInfoAPI, UNameAPI};
use regex::Regex;
use serde::Deserialize;
use thiserror::Error; use thiserror::Error;
use tracing::trace;
use crate::linux::detect_linux_libc;
use crate::mac_os::get_mac_os_version;
mod linux;
mod mac_os;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum PlatformError { pub enum PlatformError {
@ -70,7 +69,7 @@ impl Os {
let target_triple = target_lexicon::HOST; let target_triple = target_lexicon::HOST;
let os = match target_triple.operating_system { let os = match target_triple.operating_system {
target_lexicon::OperatingSystem::Linux => Self::detect_linux_libc()?, target_lexicon::OperatingSystem::Linux => detect_linux_libc()?,
target_lexicon::OperatingSystem::Windows => Os::Windows, target_lexicon::OperatingSystem::Windows => Os::Windows,
target_lexicon::OperatingSystem::MacOSX { major, minor, .. } => { target_lexicon::OperatingSystem::MacOSX { major, minor, .. } => {
Os::Macos { major, minor } Os::Macos { major, minor }
@ -113,55 +112,6 @@ impl Os {
fn platform_info() -> Result<PlatformInfo, PlatformError> { fn platform_info() -> Result<PlatformInfo, PlatformError> {
PlatformInfo::new().map_err(|err| PlatformError::OsVersionDetectionError(err.to_string())) PlatformInfo::new().map_err(|err| PlatformError::OsVersionDetectionError(err.to_string()))
} }
fn detect_linux_libc() -> Result<Self, PlatformError> {
let libc = find_libc()?;
let linux = if let Ok(Some((major, minor))) = get_musl_version(&libc) {
Os::Musllinux { major, minor }
} else if let Ok(glibc_ld) = fs::read_link(&libc) {
// Try reading the link first as it's faster
let filename = glibc_ld
.file_name()
.ok_or_else(|| {
PlatformError::OsVersionDetectionError(
"Expected the glibc ld to be a file".to_string(),
)
})?
.to_string_lossy();
let expr = Regex::new(r"ld-(\d{1,3})\.(\d{1,3})\.so").unwrap();
if let Some(capture) = expr.captures(&filename) {
let major = capture.get(1).unwrap().as_str().parse::<u16>().unwrap();
let minor = capture.get(2).unwrap().as_str().parse::<u16>().unwrap();
Os::Manylinux { major, minor }
} else {
trace!("Couldn't use ld filename, using `ldd --version`");
// runs `ldd --version`
let version = glibc_version::get_version().map_err(|err| {
PlatformError::OsVersionDetectionError(format!(
"Failed to determine glibc version with `ldd --version`: {err}"
))
})?;
Os::Manylinux {
major: u16::try_from(version.major).map_err(|_| {
PlatformError::OsVersionDetectionError(format!(
"Invalid glibc major version {}",
version.major
))
})?,
minor: u16::try_from(version.minor).map_err(|_| {
PlatformError::OsVersionDetectionError(format!(
"Invalid glibc minor version {}",
version.minor
))
})?,
}
}
} else {
return Err(PlatformError::OsVersionDetectionError("Couldn't detect neither glibc version nor musl libc version, at least one of which is required".to_string()));
};
Ok(linux)
}
} }
impl fmt::Display for Os { impl fmt::Display for Os {
@ -237,76 +187,3 @@ impl Arch {
} }
} }
} }
/// Get the macOS version from the SystemVersion.plist file.
fn get_mac_os_version() -> Result<(u16, u16), PlatformError> {
// This is actually what python does
// https://github.com/python/cpython/blob/cb2b3c8d3566ae46b3b8d0718019e1c98484589e/Lib/platform.py#L409-L428
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct SystemVersion {
product_version: String,
}
let system_version: SystemVersion =
plist::from_file("/System/Library/CoreServices/SystemVersion.plist")
.map_err(|err| PlatformError::OsVersionDetectionError(err.to_string()))?;
let invalid_mac_os_version = || {
PlatformError::OsVersionDetectionError(format!(
"Invalid macOS version {}",
system_version.product_version
))
};
match system_version
.product_version
.split('.')
.collect::<Vec<&str>>()
.as_slice()
{
[major, minor] | [major, minor, _] => {
let major = major.parse::<u16>().map_err(|_| invalid_mac_os_version())?;
let minor = minor.parse::<u16>().map_err(|_| invalid_mac_os_version())?;
Ok((major, minor))
}
_ => Err(invalid_mac_os_version()),
}
}
/// Find musl libc path from executable's ELF header.
fn find_libc() -> Result<PathBuf, PlatformError> {
let buffer = fs::read("/bin/ls")?;
let error_str = "Couldn't parse /bin/ls for detecting the ld version";
let elf = Elf::parse(&buffer)
.map_err(|err| PlatformError::OsVersionDetectionError(format!("{error_str}: {err}")))?;
if let Some(elf_interpreter) = elf.interpreter {
Ok(PathBuf::from(elf_interpreter))
} else {
Err(PlatformError::OsVersionDetectionError(
error_str.to_string(),
))
}
}
/// Read the musl version from libc library's output. Taken from maturin.
///
/// The libc library should output something like this to `stderr`:
///
/// ```text
/// musl libc (`x86_64`)
/// Version 1.2.2
/// Dynamic Program Loader
/// ```
fn get_musl_version(ld_path: impl AsRef<Path>) -> std::io::Result<Option<(u16, u16)>> {
let output = Command::new(ld_path.as_ref())
.stdout(Stdio::null())
.stderr(Stdio::piped())
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
let expr = Regex::new(r"Version (\d{2,4})\.(\d{2,4})").unwrap();
if let Some(capture) = expr.captures(&stderr) {
let major = capture.get(1).unwrap().as_str().parse::<u16>().unwrap();
let minor = capture.get(2).unwrap().as_str().parse::<u16>().unwrap();
return Ok(Some((major, minor)));
}
Ok(None)
}

View file

@ -0,0 +1,149 @@
//! Taken from `glibc_version` (<https://github.com/delta-incubator/glibc-version-rs>),
//! which used the Apache 2.0 license (but not the MIT license)
use crate::{Os, PlatformError};
use fs_err as fs;
use goblin::elf::Elf;
use regex::Regex;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use tracing::trace;
// glibc version is taken from std/sys/unix/os.rs
fn get_version() -> Result<Os, PlatformError> {
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(|| {
PlatformError::OsVersionDetectionError(format!(
"Invalid version string from ldd output: {version_str}"
))
})
}
fn ldd_output_to_version_str(output_str: &str) -> Result<&str, PlatformError> {
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(PlatformError::OsVersionDetectionError(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<Os> {
let mut parsed_ints = version.split('.').map(str::parse).fuse();
match (parsed_ints.next(), parsed_ints.next()) {
(Some(Ok(major)), Some(Ok(minor))) => Some(Os::Manylinux { major, minor }),
_ => None,
}
}
pub(crate) fn detect_linux_libc() -> Result<Os, PlatformError> {
let libc = find_libc()?;
let linux = if let Ok(Some((major, minor))) = get_musl_version(&libc) {
Os::Musllinux { major, minor }
} else if let Ok(glibc_ld) = fs::read_link(&libc) {
// Try reading the link first as it's faster
let filename = glibc_ld
.file_name()
.ok_or_else(|| {
PlatformError::OsVersionDetectionError(
"Expected the glibc ld to be a file".to_string(),
)
})?
.to_string_lossy();
let expr = Regex::new(r"ld-(\d{1,3})\.(\d{1,3})\.so").unwrap();
if let Some(capture) = expr.captures(&filename) {
let major = capture.get(1).unwrap().as_str().parse::<u16>().unwrap();
let minor = capture.get(2).unwrap().as_str().parse::<u16>().unwrap();
Os::Manylinux { major, minor }
} else {
trace!("Couldn't use ld filename, using `ldd --version`");
// runs `ldd --version`
get_version().map_err(|err| {
PlatformError::OsVersionDetectionError(format!(
"Failed to determine glibc version with `ldd --version`: {err}"
))
})?
}
} else {
return Err(PlatformError::OsVersionDetectionError("Couldn't detect neither glibc version nor musl libc version, at least one of which is required".to_string()));
};
Ok(linux)
}
/// Read the musl version from libc library's output. Taken from maturin.
///
/// The libc library should output something like this to `stderr`:
///
/// ```text
/// musl libc (`x86_64`)
/// Version 1.2.2
/// Dynamic Program Loader
/// ```
fn get_musl_version(ld_path: impl AsRef<Path>) -> std::io::Result<Option<(u16, u16)>> {
let output = Command::new(ld_path.as_ref())
.stdout(Stdio::null())
.stderr(Stdio::piped())
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
let expr = Regex::new(r"Version (\d{2,4})\.(\d{2,4})").unwrap();
if let Some(capture) = expr.captures(&stderr) {
let major = capture.get(1).unwrap().as_str().parse::<u16>().unwrap();
let minor = capture.get(2).unwrap().as_str().parse::<u16>().unwrap();
return Ok(Some((major, minor)));
}
Ok(None)
}
/// Find musl libc path from executable's ELF header.
fn find_libc() -> Result<PathBuf, PlatformError> {
let buffer = fs::read("/bin/ls")?;
let error_str = "Couldn't parse /bin/ls for detecting the ld version";
let elf = Elf::parse(&buffer)
.map_err(|err| PlatformError::OsVersionDetectionError(format!("{error_str}: {err}")))?;
if let Some(elf_interpreter) = elf.interpreter {
Ok(PathBuf::from(elf_interpreter))
} else {
Err(PlatformError::OsVersionDetectionError(
error_str.to_string(),
))
}
}
#[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");
}
}

View file

@ -0,0 +1,36 @@
use crate::PlatformError;
use serde::Deserialize;
/// Get the macOS version from the SystemVersion.plist file.
pub(crate) fn get_mac_os_version() -> Result<(u16, u16), PlatformError> {
// This is actually what python does
// https://github.com/python/cpython/blob/cb2b3c8d3566ae46b3b8d0718019e1c98484589e/Lib/platform.py#L409-L428
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
struct SystemVersion {
product_version: String,
}
let system_version: SystemVersion =
plist::from_file("/System/Library/CoreServices/SystemVersion.plist")
.map_err(|err| PlatformError::OsVersionDetectionError(err.to_string()))?;
let invalid_mac_os_version = || {
PlatformError::OsVersionDetectionError(format!(
"Invalid macOS version {}",
system_version.product_version
))
};
match system_version
.product_version
.split('.')
.collect::<Vec<&str>>()
.as_slice()
{
[major, minor] | [major, minor, _] => {
let major = major.parse::<u16>().map_err(|_| invalid_mac_os_version())?;
let minor = minor.parse::<u16>().map_err(|_| invalid_mac_os_version())?;
Ok((major, minor))
}
_ => Err(invalid_mac_os_version()),
}
}