Skip to content

Commit

Permalink
Merge pull request rust-lang#160 from NobodyXu/feature/target-detection
Browse files Browse the repository at this point in the history
  • Loading branch information
passcod committed Jun 7, 2022
2 parents ee03e97 + 0819b65 commit 0c83d01
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 2 deletions.
34 changes: 34 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Expand Up @@ -62,6 +62,9 @@ zip = { version = "0.6.2", default-features = false, features = [ "deflate", "bz
# Enable feature zstdmt to enable multithreading in libzstd.
zstd = { version = "0.10.0", features = [ "bindgen", "zstdmt" ], default-features = false }

[target.'cfg(target_os = "macos")'.dependencies]
guess_host_triple = "0.1.3"

[dev-dependencies]
env_logger = "0.9.0"

Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Expand Up @@ -15,8 +15,8 @@ pub use helpers::*;
pub mod bins;
pub mod fetchers;

/// Compiled target triple, used as default for binary fetching
pub const TARGET: &str = env!("TARGET");
mod target;
pub use target::*;

/// Default package path template (may be overridden in package Cargo.toml)
pub const DEFAULT_PKG_URL: &str =
Expand Down
153 changes: 153 additions & 0 deletions src/target.rs
@@ -0,0 +1,153 @@
use std::io::{BufRead, Cursor};
use std::process::Output;
use tokio::process::Command;

/// 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<Box<str>> {
if let Some(target) = get_target_from_rustc().await {
let mut v = vec![target];

#[cfg(target_os = "linux")]
if v[0].contains("gnu") {
v.push(v[0].replace("gnu", "musl").into_boxed_str());
}

#[cfg(target_os = "macos")]
if &*v[0] == macos::AARCH64 {
v.push(macos::X86.into());
}

v
} else {
#[cfg(target_os = "linux")]
{
linux::detect_targets_linux().await
}
#[cfg(target_os = "macos")]
{
macos::detect_targets_macos()
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
vec![TARGET.into()]
}
}
}

/// Figure out what the host target is using `rustc`.
/// If `rustc` is absent, then it would return `None`.
async fn get_target_from_rustc() -> Option<Box<str>> {
let Output { status, stdout, .. } = Command::new("rustc").arg("-vV").output().await.ok()?;
if !status.success() {
return None;
}

Cursor::new(stdout)
.lines()
.filter_map(|line| line.ok())
.find_map(|line| {
line.strip_prefix("host: ")
.map(|host| host.to_owned().into_boxed_str())
})
}

#[cfg(target_os = "linux")]
mod linux {
use super::{Command, Output, TARGET};

pub(super) async fn detect_targets_linux() -> Vec<Box<str>> {
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_from_ldd_output(&stdout) {
libc_version
} else if let Some(libc_version) = parse_libc_version_from_ldd_output(&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_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
}
}

fn parse_abi() -> &'static str {
let last = TARGET.rsplit_once('-').unwrap().1;

if let Some(libc_version) = last.strip_prefix("musl") {
libc_version
} else if let Some(libc_version) = last.strip_prefix("gnu") {
libc_version
} else {
panic!("Unrecognized libc")
}
}

fn create_target_str(libc_version: &str, abi: &str) -> Box<str> {
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()
}
}

#[cfg(target_os = "macos")]
mod macos {
use guess_host_triple::guess_host_triple;

pub(super) const AARCH64: &str = "aarch64-apple-darwin";
pub(super) const X86: &str = "x86_64-apple-darwin";

pub(super) fn detect_targets_macos() -> Vec<Box<str>> {
if guess_host_triple() == Some(AARCH64) {
vec![AARCH64.into(), X86.into()]
} else {
vec![X86.into()]
}
}
}

0 comments on commit 0c83d01

Please sign in to comment.