mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-26 07:10:02 +00:00
feat detect-targets
: Improve support of non-std glibc/musl (#1343)
* feat `detect-targets`: Improve support of non-std glibc/musl Fixed #1329 - Refactor: Create `linux::detect_alternative_targets` to reuse code from other targets - Run `/lib/ld-linux-{cpu_arch}.so.1 --version` for checking glibc support instead of running `ldd --version` since it could be non-std glibc installation and does not provide `/lib/ld-linux-{cpu_arch}.so.1` - Check for non-std glibc and add fallback target `{cpu_arch}-{distro_name}-linux-gnu{abi}` - Add `{cpu_arch}-{distro_name}-linux-musl{abi}` fallback for musl libc, specially for Alpine since it has a `/lib/ld-musl-{cpu_arch}.so.1` - For unknown libc flavor, check for the target provided before fallback to musl Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * feat `detect-targets`: Support glibc on musl target Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * feat `detect-targets`: Unify `Libc::{Gnu, Musl}` checks since we can't really tell if we are on gnu or musl Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> --------- Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
3e67e3624a
commit
0fa315758b
2 changed files with 124 additions and 69 deletions
|
@ -32,43 +32,25 @@ cfg_if! {
|
||||||
/// Check [this issue](https://github.com/ryankurte/cargo-binstall/issues/155)
|
/// Check [this issue](https://github.com/ryankurte/cargo-binstall/issues/155)
|
||||||
/// for more information.
|
/// for more information.
|
||||||
pub async fn detect_targets() -> Vec<String> {
|
pub async fn detect_targets() -> Vec<String> {
|
||||||
#[cfg(target_os = "linux")]
|
let target = get_target_from_rustc().await.unwrap_or_else(|| {
|
||||||
{
|
guess_host_triple::guess_host_triple()
|
||||||
if let Some(target) = get_target_from_rustc().await {
|
.unwrap_or(crate::TARGET)
|
||||||
let mut targets = vec![target];
|
.to_string()
|
||||||
|
});
|
||||||
|
|
||||||
if targets[0].contains("gnu") {
|
let mut targets = vec![target];
|
||||||
targets.push(targets[0].replace("gnu", "musl"));
|
|
||||||
} else if targets[0].contains("android") {
|
|
||||||
targets.push(targets[0].replace("android", "musl"));
|
|
||||||
}
|
|
||||||
|
|
||||||
targets
|
cfg_if! {
|
||||||
} else {
|
if #[cfg(target_os = "macos")] {
|
||||||
linux::detect_targets_linux().await
|
targets.extend(macos::detect_alternative_targets(&targets[0]).await);
|
||||||
|
} else if #[cfg(target_os = "windows")] {
|
||||||
|
targets.extend(windows::detect_alternative_targets(&targets[0]));
|
||||||
|
} else if #[cfg(target_os = "linux")] {
|
||||||
|
targets.extend(linux::detect_alternative_targets(&targets[0]).await);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
targets
|
||||||
{
|
|
||||||
let target = get_target_from_rustc().await.unwrap_or_else(|| {
|
|
||||||
guess_host_triple::guess_host_triple()
|
|
||||||
.unwrap_or(crate::TARGET)
|
|
||||||
.to_string()
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut targets = vec![target];
|
|
||||||
|
|
||||||
cfg_if! {
|
|
||||||
if #[cfg(target_os = "macos")] {
|
|
||||||
targets.extend(macos::detect_alternative_targets(&targets[0]).await);
|
|
||||||
} else if #[cfg(target_os = "windows")] {
|
|
||||||
targets.extend(windows::detect_alternative_targets(&targets[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
targets
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Figure out what the host target is using `rustc`.
|
/// Figure out what the host target is using `rustc`.
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use crate::TARGET;
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::Path,
|
||||||
|
process::{Output, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
use std::process::{Output, Stdio};
|
use tokio::{process::Command, task};
|
||||||
|
|
||||||
use guess_host_triple::guess_host_triple;
|
|
||||||
use tokio::process::Command;
|
|
||||||
|
|
||||||
pub(super) async fn detect_targets_linux() -> Vec<String> {
|
|
||||||
let target = guess_host_triple().unwrap_or(TARGET);
|
|
||||||
|
|
||||||
|
pub(super) async fn detect_alternative_targets(target: &str) -> impl Iterator<Item = String> {
|
||||||
let (prefix, postfix) = target
|
let (prefix, postfix) = target
|
||||||
.rsplit_once('-')
|
.rsplit_once('-')
|
||||||
.expect("unwrap: target always has a -");
|
.expect("unwrap: target always has a -");
|
||||||
|
@ -25,51 +24,125 @@ pub(super) async fn detect_targets_linux() -> Vec<String> {
|
||||||
let musl_fallback_target = || format!("{prefix}-{}{abi}", "musl");
|
let musl_fallback_target = || format!("{prefix}-{}{abi}", "musl");
|
||||||
|
|
||||||
match libc {
|
match libc {
|
||||||
Libc::Gnu => {
|
// guess_host_triple cannot detect whether the system is using glibc,
|
||||||
// guess_host_triple cannot detect whether the system is using glibc,
|
// musl libc or other libc.
|
||||||
// musl libc or other libc.
|
//
|
||||||
//
|
// On Alpine, you can use `apk add gcompat` to install glibc
|
||||||
// As such, we need to launch the test ourselves.
|
// and run glibc programs.
|
||||||
if supports_gnu().await {
|
//
|
||||||
vec![target.to_string(), musl_fallback_target()]
|
// As such, we need to launch the test ourselves.
|
||||||
} else {
|
Libc::Gnu | Libc::Musl => {
|
||||||
vec![musl_fallback_target()]
|
let cpu_arch = target
|
||||||
}
|
.split_once('-')
|
||||||
}
|
.expect("unwrap: target always has a - for cpu_arch")
|
||||||
Libc::Android => vec![target.to_string(), musl_fallback_target()],
|
.0;
|
||||||
|
|
||||||
_ => vec![target.to_string()],
|
let has_glibc = task::spawn({
|
||||||
|
let glibc_path = format!("/lib/ld-linux-{cpu_arch}.so.1");
|
||||||
|
async move { is_gnu_ld(&glibc_path).await }
|
||||||
|
});
|
||||||
|
|
||||||
|
let distro_if_has_non_std_glibc = task::spawn(async {
|
||||||
|
if is_gnu_ld("/usr/bin/ldd").await {
|
||||||
|
get_distro_name().await
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let distro_if_has_musl_dynlib = if get_ld_flavor(&format!(
|
||||||
|
"/lib/ld-musl-{cpu_arch}.so.1"
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
== Some(Libc::Musl)
|
||||||
|
{
|
||||||
|
get_distro_name().await
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
[
|
||||||
|
has_glibc
|
||||||
|
.await
|
||||||
|
.unwrap_or(false)
|
||||||
|
.then(|| format!("{cpu_arch}-unknown-linux-gnu{abi}")),
|
||||||
|
distro_if_has_non_std_glibc
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.map(|distro_name| format!("{cpu_arch}-{distro_name}-linux-gnu{abi}")),
|
||||||
|
// Fallback for Linux flavors like Alpine, which has a musl dyn libc
|
||||||
|
distro_if_has_musl_dynlib
|
||||||
|
.map(|distro_name| format!("{cpu_arch}-{distro_name}-linux-musl{abi}")),
|
||||||
|
Some(musl_fallback_target()),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Libc::Android | Libc::Unknown => [
|
||||||
|
Some(target.to_string()),
|
||||||
|
Some(musl_fallback_target()),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn supports_gnu() -> bool {
|
async fn is_gnu_ld(cmd: &str) -> bool {
|
||||||
Command::new("ldd")
|
get_ld_flavor(cmd).await == Some(Libc::Gnu)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_ld_flavor(cmd: &str) -> Option<Libc> {
|
||||||
|
Command::new(cmd)
|
||||||
.arg("--version")
|
.arg("--version")
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.output()
|
.output()
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|Output { stdout, stderr, .. }| {
|
.and_then(|Output { stdout, stderr, .. }| {
|
||||||
parse_libc_version_from_ldd_output(&stdout)
|
Libc::parse(&stdout).or_else(|| Libc::parse(&stderr))
|
||||||
.or_else(|| parse_libc_version_from_ldd_output(&stderr))
|
|
||||||
})
|
})
|
||||||
== Some("gnu")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_libc_version_from_ldd_output(output: &[u8]) -> Option<&'static str> {
|
|
||||||
let s = String::from_utf8_lossy(output);
|
|
||||||
if s.contains("musl libc") {
|
|
||||||
Some("musl")
|
|
||||||
} else if s.contains("GLIBC") {
|
|
||||||
Some("gnu")
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
enum Libc {
|
enum Libc {
|
||||||
Gnu,
|
Gnu,
|
||||||
Musl,
|
Musl,
|
||||||
Android,
|
Android,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Libc {
|
||||||
|
fn parse(output: &[u8]) -> Option<Self> {
|
||||||
|
let s = String::from_utf8_lossy(output);
|
||||||
|
if s.contains("musl libc") {
|
||||||
|
Some(Self::Musl)
|
||||||
|
} else if s.contains("GLIBC") {
|
||||||
|
Some(Self::Gnu)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_distro_name() -> Option<String> {
|
||||||
|
task::spawn_blocking(get_distro_name_blocking)
|
||||||
|
.await
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_distro_name_blocking() -> Option<String> {
|
||||||
|
match fs::read_to_string("/etc/os-release") {
|
||||||
|
Ok(os_release) => os_release
|
||||||
|
.lines()
|
||||||
|
.find_map(|line| line.strip_prefix("ID=\"")?.strip_suffix('"'))
|
||||||
|
.map(ToString::to_string),
|
||||||
|
Err(_) => (Path::new("/etc/nix/nix.conf").is_file()
|
||||||
|
&& ["/nix/store", "/nix/var/profiles"]
|
||||||
|
.into_iter()
|
||||||
|
.map(Path::new)
|
||||||
|
.all(Path::is_dir))
|
||||||
|
.then_some("nixos".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue