mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-06-14 22:56:37 +00:00

On ubuntu 24.02, glibc are installed in: ``` /usr/lib/aarch64-linux-gnu/libc.so.6 /usr/lib/aarch64-linux-gnu/libc.so ``` Signed-off-by: Jiahao XU <30436523+NobodyXu@users.noreply.github.com>
172 lines
5.2 KiB
Rust
172 lines
5.2 KiB
Rust
use std::{
|
|
process::{Output, Stdio},
|
|
str,
|
|
};
|
|
|
|
use tokio::{process::Command, task};
|
|
#[cfg(feature = "tracing")]
|
|
use tracing::debug;
|
|
|
|
pub(super) async fn detect_targets(target: String) -> Vec<String> {
|
|
let (_, postfix) = target
|
|
.rsplit_once('-')
|
|
.expect("unwrap: target always has a -");
|
|
|
|
let (abi, libc) = if let Some(abi) = postfix.strip_prefix("musl") {
|
|
(abi, Libc::Musl)
|
|
} else if let Some(abi) = postfix.strip_prefix("gnu") {
|
|
(abi, Libc::Gnu)
|
|
} else if let Some(abi) = postfix.strip_prefix("android") {
|
|
(abi, Libc::Android)
|
|
} else {
|
|
(postfix, Libc::Unknown)
|
|
};
|
|
|
|
let cpu_arch = target
|
|
.split_once('-')
|
|
.expect("unwrap: target always has a - for cpu_arch")
|
|
.0;
|
|
|
|
// For android the `-unknown-` is omitted, for alpine it has `-alpine-`
|
|
// instead of `-unknown-`.
|
|
let musl_fallback_target = || format!("{cpu_arch}-unknown-linux-musl{abi}");
|
|
|
|
match libc {
|
|
// guess_host_triple cannot detect whether the system is using glibc,
|
|
// musl libc or other libc.
|
|
//
|
|
// On Alpine, you can use `apk add gcompat` to install glibc
|
|
// and run glibc programs.
|
|
//
|
|
// As such, we need to launch the test ourselves.
|
|
Libc::Gnu | Libc::Musl => {
|
|
let handles: Vec<_> = {
|
|
let cpu_arch_suffix = cpu_arch.replace('_', "-");
|
|
let filename = format!("ld-linux-{cpu_arch_suffix}.so.2");
|
|
let dirname = format!("{cpu_arch}-linux-gnu");
|
|
|
|
[
|
|
format!("/lib/{filename}"),
|
|
format!("/lib64/{filename}"),
|
|
format!("/lib/{dirname}/{filename}"),
|
|
format!("/lib64/{dirname}/{filename}"),
|
|
format!("/usr/lib/{dirname}/{filename}"),
|
|
format!("/usr/lib64/{dirname}/{filename}"),
|
|
format!("/usr/lib/{dirname}/libc.so.6"),
|
|
format!("/usr/lib64/{dirname}/libc.so.6"),
|
|
format!("/usr/lib/{dirname}/libc.so"),
|
|
format!("/usr/lib64/{dirname}/libc.so"),
|
|
]
|
|
.into_iter()
|
|
.map(|p| AutoAbortHandle(tokio::spawn(is_gnu_ld(p))))
|
|
.collect()
|
|
};
|
|
|
|
let has_glibc = async move {
|
|
for mut handle in handles {
|
|
if let Ok(true) = (&mut handle.0).await {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
.await;
|
|
|
|
[
|
|
has_glibc.then(|| format!("{cpu_arch}-unknown-linux-gnu{abi}")),
|
|
Some(musl_fallback_target()),
|
|
]
|
|
}
|
|
Libc::Android | Libc::Unknown => [Some(target.clone()), Some(musl_fallback_target())],
|
|
}
|
|
.into_iter()
|
|
.flatten()
|
|
.collect()
|
|
}
|
|
|
|
async fn is_gnu_ld(cmd: String) -> bool {
|
|
get_ld_flavor(&cmd).await == Some(Libc::Gnu)
|
|
}
|
|
|
|
async fn get_ld_flavor(cmd: &str) -> Option<Libc> {
|
|
let Output {
|
|
status,
|
|
stdout,
|
|
stderr,
|
|
} = match Command::new(cmd)
|
|
.arg("--version")
|
|
.stdin(Stdio::null())
|
|
.output()
|
|
.await
|
|
{
|
|
Ok(output) => output,
|
|
Err(_err) => {
|
|
#[cfg(feature = "tracing")]
|
|
debug!("Running `{cmd} --version`: err={_err:?}");
|
|
return None;
|
|
}
|
|
};
|
|
|
|
let stdout = String::from_utf8_lossy(&stdout);
|
|
let stderr = String::from_utf8_lossy(&stderr);
|
|
|
|
#[cfg(feature = "tracing")]
|
|
debug!("`{cmd} --version`: status={status}, stdout='{stdout}', stderr='{stderr}'");
|
|
|
|
const ALPINE_GCOMPAT: &str = r#"This is the gcompat ELF interpreter stub.
|
|
You are not meant to run this directly.
|
|
"#;
|
|
|
|
if status.success() {
|
|
// Executing glibc ldd or /lib/ld-linux-{cpu_arch}.so.1 will always
|
|
// succeeds.
|
|
(stdout.contains("GLIBC") || stdout.contains("GNU libc")).then_some(Libc::Gnu)
|
|
} else if status.code() == Some(1) {
|
|
// On Alpine, executing both the gcompat glibc and the ldd and
|
|
// /lib/ld-musl-{cpu_arch}.so.1 will fail with exit status 1.
|
|
if stdout == ALPINE_GCOMPAT {
|
|
// Alpine's gcompat package will output ALPINE_GCOMPAT to stdout
|
|
Some(Libc::Gnu)
|
|
} else if stderr.contains("musl libc") {
|
|
// Alpine/s ldd and musl dynlib will output to stderr
|
|
Some(Libc::Musl)
|
|
} else {
|
|
None
|
|
}
|
|
} else if status.code() == Some(127) {
|
|
// On Ubuntu 20.04 (glibc 2.31), the `--version` flag is not supported
|
|
// and it will exit with status 127.
|
|
let status = Command::new(cmd)
|
|
.arg("/bin/true")
|
|
.stdin(Stdio::null())
|
|
.stdout(Stdio::null())
|
|
.stderr(Stdio::null())
|
|
.status()
|
|
.await
|
|
.ok()?;
|
|
|
|
#[cfg(feature = "tracing")]
|
|
debug!("`{cmd} --version`: status={status}");
|
|
|
|
status.success().then_some(Libc::Gnu)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[derive(Eq, PartialEq)]
|
|
enum Libc {
|
|
Gnu,
|
|
Musl,
|
|
Android,
|
|
Unknown,
|
|
}
|
|
|
|
struct AutoAbortHandle<T>(task::JoinHandle<T>);
|
|
|
|
impl<T> Drop for AutoAbortHandle<T> {
|
|
fn drop(&mut self) {
|
|
self.0.abort();
|
|
}
|
|
}
|