From 8bd4b9b6a1362f53d48be1ee58bf751b1ac34e55 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Mon, 6 Jun 2022 22:32:15 +1000 Subject: [PATCH] Impl `detect_targets` for linux Signed-off-by: Jiahao XU --- src/lib.rs | 2 +- src/target.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index ef3cfbaf..0a6c1437 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ pub mod bins; pub mod fetchers; mod target; -pub use target::TARGET; +pub use target::{detect_targets, TARGET}; /// Default package path template (may be overridden in package Cargo.toml) pub const DEFAULT_PKG_URL: &str = diff --git a/src/target.rs b/src/target.rs index 94bfe775..4c6f8f57 100644 --- a/src/target.rs +++ b/src/target.rs @@ -1,2 +1,97 @@ /// Compiled target triple, used as default for binary fetching pub const TARGET: &str = env!("TARGET"); + +/// Detect the targets supported at runtime, +/// which might be different from `TARGET` which is detected +/// at compile-time. +/// +/// Return targets supported in the order of preference. +/// If target_os is linux and it support gnu, then it is preferred +/// to musl. +/// +/// If target_os is mac and it is aarch64, then aarch64 is preferred +/// to x86_64. +/// +/// Check [this issue](https://github.com/ryankurte/cargo-binstall/issues/155) +/// for more information. +pub async fn detect_targets() -> Vec> { + #[cfg(target_os = "linux")] + { + return linux::detect_targets_linux().await; + } + + todo!() +} + +#[cfg(target_os = "linux")] +mod linux { + use super::TARGET; + use std::process::Output; + use tokio::process::Command; + + async fn detect_targets_linux() -> Vec> { + let abi = parse_abi(); + + if let Ok(Output { + status: _, + stdout, + stderr, + }) = Command::new("ldd").arg("--version").output().await + { + let libc_version = if let Some(libc_version) = parse_libc_version(stdout) { + libc_version + } else if let Some(libc_version) = parse_libc_version(stderr) { + libc_version + } else { + return vec![create_target_str("musl", abi)]; + }; + + if libc_version == "gnu" { + return vec![ + create_target_str("gnu", abi), + create_target_str("musl", abi), + ]; + } + } + + // Fallback to using musl + vec![create_target_str("musl", abi)] + } + + fn parse_libc_version(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 + } + } + + const fn parse_abi() -> &'static str { + if TARGET.endswith("abi64") { + "abi64" + } else if TARGET.endswith("eabi") { + "eabi" + } else if TARGET.endswith("eabihf") { + "eabihf" + } else if TARGET.endswith("gnu") || TARGET.endswith("musl") { + "" + } else { + panic!("Unknown abi") + } + } + + fn create_target_str(libc_version: &str, abi: &str) -> Box { + let prefix = TARGET.rsplit_once('-').unwrap().0; + + let mut target = String::with_capacity(prefix.len() + 1 + libc_version.len() + abi.len()); + target.push_str(prefix); + target.push('-'); + target.push_str(libc_version); + target.push_str(abi); + + target.into_boxed_str() + } +}