Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse target triples #1413

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2192,14 +2192,13 @@ impl Build {
// So instead, we pass the deployment target with `-m*-version-min=`, and only
// pass it here on visionOS and Mac Catalyst where that option does not exist:
// https://github.com/rust-lang/cc-rs/issues/1383
let clang_target = if target.os == "visionos" || target.abi == "macabi" {
Cow::Owned(
target.versioned_llvm_target(&self.apple_deployment_target(target)),
)
let version = if target.os == "visionos" || target.abi == "macabi" {
Some(self.apple_deployment_target(target))
} else {
Cow::Borrowed(target.llvm_target)
None
};

let clang_target = target.llvm_target(version.as_deref());
cmd.push_cc_arg(format!("--target={clang_target}").into());
}
}
Expand All @@ -2216,7 +2215,7 @@ impl Build {
cmd.push_cc_arg("-m32".into());
cmd.push_cc_arg("-arch:IA32".into());
} else {
cmd.push_cc_arg(format!("--target={}", target.llvm_target).into());
cmd.push_cc_arg(format!("--target={}", target.llvm_target(None)).into());
}
} else if target.full_arch == "i586" {
cmd.push_cc_arg("-arch:IA32".into());
Expand Down Expand Up @@ -3501,7 +3500,9 @@ impl Build {

fn get_target(&self) -> Result<TargetInfo<'_>, Error> {
match &self.target {
Some(t) if Some(&**t) != self.getenv_unwrap_str("TARGET").ok().as_deref() => t.parse(),
Some(t) if Some(&**t) != self.getenv_unwrap_str("TARGET").ok().as_deref() => {
TargetInfo::from_rustc_target(t)
}
// Fetch target information from environment if not set, or if the
// target was the same as the TARGET environment variable, in
// case the user did `build.target(&env::var("TARGET").unwrap())`.
Expand Down
137 changes: 91 additions & 46 deletions src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@
//!
//! See the `target-lexicon` crate for a more principled approach to this.

use std::str::FromStr;

use crate::{Error, ErrorKind};

mod apple;
mod generated;
mod llvm;
mod parser;

Expand Down Expand Up @@ -43,47 +38,11 @@ pub(crate) struct TargetInfo<'a> {
///
/// This is the same as the value of `cfg!(target_abi)`.
pub abi: &'a str,
/// The unversioned LLVM/Clang target triple.
///
/// NOTE: You should never need to match on this explicitly, use the other
/// fields on [`TargetInfo`] instead.
pub llvm_target: &'a str,
}

impl FromStr for TargetInfo<'_> {
type Err = Error;

/// This will fail when using a custom target triple unknown to `rustc`.
fn from_str(target_triple: &str) -> Result<Self, Error> {
if let Ok(index) =
generated::LIST.binary_search_by_key(&target_triple, |(target_triple, _)| target_triple)
{
let (_, info) = &generated::LIST[index];
Ok(info.clone())
} else {
Err(Error::new(
ErrorKind::UnknownTarget,
format!(
"unknown target `{target_triple}`.

NOTE: `cc-rs` only supports a fixed set of targets when not in a build script.
- If adding a new target, you will need to fork of `cc-rs` until the target
has landed on nightly and the auto-generated list has been updated. See also
the `rustc` dev guide on adding a new target:
https://rustc-dev-guide.rust-lang.org/building/new-target.html
- If using a custom target, prefer to upstream it to `rustc` if possible,
otherwise open an issue with `cc-rs`:
https://github.com/rust-lang/cc-rs/issues/new
"
),
))
}
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;
use std::process::Command;

use super::TargetInfo;

Expand All @@ -104,13 +63,92 @@ mod tests {

for target in targets {
// Check that it parses
let _ = TargetInfo::from_str(target).unwrap();
let _ = TargetInfo::from_rustc_target(target).unwrap();
}
}

fn target_from_rustc_cfgs<'a>(target: &'a str, cfgs: &'a str) -> TargetInfo<'a> {
// Cannot determine full architecture from cfgs.
let (full_arch, _rest) = target.split_once('-').expect("target to have arch");

let mut target = TargetInfo {
full_arch: full_arch.into(),
arch: "invalid-none-set".into(),
vendor: "invalid-none-set".into(),
os: "invalid-none-set".into(),
env: "invalid-none-set".into(),
// Not set in older Rust versions
abi: "".into(),
};

for cfg in cfgs.lines() {
if let Some((name, value)) = cfg.split_once('=') {
// Remove whitespace, if `rustc` decided to insert any
let name = name.trim();
let value = value.trim();

// Remove quotes around value
let value = value.strip_prefix('"').unwrap_or(value);
let value = value.strip_suffix('"').unwrap_or(value);

match name {
"target_arch" => target.arch = value,
"target_vendor" => target.vendor = value,
"target_os" => target.os = value,
"target_env" => target.env = value,
"target_abi" => target.abi = value,
_ => {}
}
} else {
// Skip cfgs like `debug_assertions` and `unix`.
}
}

target
}

#[test]
fn parse_rustc_targets() {
let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());

let target_list = Command::new(&rustc)
.arg("--print=target-list")
.output()
.unwrap()
.stdout;
let target_list = String::from_utf8(target_list).unwrap();

let mut has_failure = false;
for target in target_list.lines() {
let cfgs = Command::new(&rustc)
.arg("--target")
.arg(target)
.arg("--print=cfg")
.output()
.unwrap()
.stdout;
let cfgs = String::from_utf8(cfgs).unwrap();

let expected = target_from_rustc_cfgs(target, &cfgs);
let actual = TargetInfo::from_rustc_target(target);

if Some(&expected) != actual.as_ref().ok() {
eprintln!("failed comparing {target}:");
eprintln!(" expected: Ok({expected:?})");
eprintln!(" actual: {actual:?}");
eprintln!();
has_failure = true;
}
}

if has_failure {
panic!("failed comparing targets");
}
}

// Various custom target triples not (or no longer) known by `rustc`
#[test]
fn cannot_parse_extra() {
fn parse_extra() {
let targets = [
"aarch64-unknown-none-gnu",
"aarch64-uwp-windows-gnu",
Expand All @@ -120,13 +158,20 @@ mod tests {
"armv7neon-unknown-linux-musleabihf",
"thumbv7-unknown-linux-gnueabihf",
"thumbv7-unknown-linux-musleabihf",
"armv7-apple-ios",
"wasm32-wasi",
"x86_64-rumprun-netbsd",
"x86_64-unknown-linux",
"x86_64-alpine-linux-musl",
"x86_64-chimera-linux-musl",
"x86_64-foxkit-linux-musl",
"arm-poky-linux-gnueabi",
"x86_64-unknown-moturus",
];

for target in targets {
// Check that it does not parse
let _ = TargetInfo::from_str(target).unwrap_err();
// Check that it parses
let _ = TargetInfo::from_rustc_target(target).unwrap();
}
}
}
Loading
Loading