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 rustc target names #1413

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
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
52 changes: 52 additions & 0 deletions .github/workflows/test-rustc-targets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Test nightly `rustc` targets and add issue comment if changed

on:
workflow_dispatch: # Allow running on-demand
schedule:
- cron: '0 3 * * 1,4' # Every Monday and Thursdag
push:
branches:
- 'main'
paths:
- 'src/target/**'

jobs:
regenerate:
if: github.repository_owner == 'rust-lang'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: true

- name: Install current nightly Rust
run: |
rustup toolchain install nightly --no-self-update --profile minimal
rustup default nightly
- run: cargo update
- uses: Swatinem/rust-cache@v2

- name: Test with `RUSTFLAGS=--cfg=rustc_target_test cargo test --lib`
id: test
continue-on-error: true # We want to open an issue if it fails
run: cargo test --lib 2>&1 | tee test_output.txt
env:
RUSTFLAGS: --cfg=rustc_target_test

# Added to https://github.com/rust-lang/cc-rs/issues/1426
- name: Add issue comment if test failed
if: steps.test.outcome == 'failure'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh issue comment 1426 --body "
Failed parsing `rustc` target on `$(rustc --version)`.

<details><summary>Test output</summary>
<p>
\`\`\`
$(cat test_output.txt)
\`\`\`
</p>
</details>
" --head $(git branch --show-current)
3 changes: 0 additions & 3 deletions dev-tools/gen-target-info/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,3 @@ pub use target_specs::*;

mod read;
pub use read::*;

mod write;
pub use write::*;
82 changes: 12 additions & 70 deletions dev-tools/gen-target-info/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,18 @@ use gen_target_info::{
get_target_spec_from_msrv, get_target_specs_from_json, get_targets_msrv, RustcTargetSpecs,
};

const PRELUDE: &str = r#"//! This file is generated code. Please edit the generator
//! in dev-tools/gen-target-info if you need to make changes.
const PRELUDE: &str = r#"//! This file is generated code. Please edit the generator in
//! dev-tools/gen-target-info if you need to make changes, or see
//! src/target/llvm.rs if you need to configure a specific LLVM triple.
"#;

fn generate_target_mapping(f: &mut File, target_specs: &RustcTargetSpecs) -> std::io::Result<()> {
writeln!(f, "use super::TargetInfo;")?;
writeln!(f)?;
writeln!(
f,
"pub(crate) const LIST: &[(&str, TargetInfo<'static>)] = &["
)?;
writeln!(f, "#[rustfmt::skip]")?;
writeln!(f, "pub(crate) const LLVM_TARGETS: &[(&str, &str)] = &[")?;

for (triple, spec) in &target_specs.0 {
let full_arch = triple.split_once('-').unwrap().0;
let arch = &spec.arch;
let vendor = spec.vendor.as_deref().unwrap_or("unknown");
let os = spec.os.as_deref().unwrap_or("none");
let env = spec.env.as_deref().unwrap_or("");
let abi = spec.abi.as_deref().unwrap_or("");

// FIXME(madsmtm): Unnecessary once we bump MSRV to Rust 1.74
let llvm_target = if spec.llvm_target == "armv7-apple-ios7.0.0" {
"armv7-apple-ios".to_string()
} else if os == "uefi" {
// Override the UEFI LLVM targets.
//
// The rustc mappings (as of 1.82) for the UEFI targets are:
// * i686-unknown-uefi -> i686-unknown-windows-gnu
// * x86_64-unknown-uefi -> x86_64-unknown-windows
// * aarch64-unknown-uefi -> aarch64-unknown-windows
//
// However, in cc-rs all the UEFI targets use
// -windows-gnu. This has been the case since 2021 [1].
// * i686-unknown-uefi -> i686-unknown-windows-gnu
// * x86_64-unknown-uefi -> x86_64-unknown-windows-gnu
// * aarch64-unknown-uefi -> aarch64-unknown-windows-gnu
//
// For now, override the UEFI mapping to keep the behavior
// of cc-rs unchanged.
//
// TODO: as discussed in [2], it may be possible to switch
// to new UEFI targets added to clang, and regardless it
// would be good to have consistency between rustc and
// cc-rs.
//
// [1]: https://github.com/rust-lang/cc-rs/pull/623
// [2]: https://github.com/rust-lang/cc-rs/pull/1264
let arch = if spec.arch == "x86" {
"i686"
} else {
&spec.arch
};
format!("{}-unknown-windows-gnu", arch)
} else {
spec.llvm_target.clone()
};

writeln!(f, " (")?;
writeln!(f, " {triple:?},")?;
writeln!(f, " TargetInfo {{")?;
writeln!(f, " full_arch: {full_arch:?},")?;
writeln!(f, " arch: {arch:?},")?;
writeln!(f, " vendor: {vendor:?},")?;
writeln!(f, " os: {os:?},")?;
writeln!(f, " env: {env:?},")?;
writeln!(f, " abi: {abi:?},")?;
writeln!(f, " llvm_target: {llvm_target:?},")?;
writeln!(f, " }},")?;
writeln!(f, " ),")?;
for (target_name, spec) in &target_specs.0 {
writeln!(f, " ({target_name:?}, {:?}),", spec.llvm_target)?;
}

writeln!(f, "];")?;
Expand All @@ -92,13 +34,13 @@ fn main() {
.unwrap()
!= 0
{
for target_triple in get_targets_msrv().lines() {
let target_triple = target_triple.unwrap();
let target_triple = target_triple.trim();
for target_name in get_targets_msrv().lines() {
let target_name = target_name.unwrap();
let target_name = target_name.trim();
target_specs
.0
.entry(target_triple.to_string())
.or_insert_with(|| get_target_spec_from_msrv(target_triple));
.entry(target_name.to_string())
.or_insert_with(|| get_target_spec_from_msrv(target_name));
}
}

Expand Down
18 changes: 0 additions & 18 deletions dev-tools/gen-target-info/src/write.rs

This file was deleted.

30 changes: 19 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@
//!
//! * Unix platforms require `cc` to be the C compiler. This can be found by
//! installing cc/clang on Linux distributions and Xcode on macOS, for example.
//! * Windows platforms targeting MSVC (e.g. your target triple ends in `-msvc`)
//! * Windows platforms targeting MSVC (e.g. your target name ends in `-msvc`)
//! require Visual Studio to be installed. `cc-rs` attempts to locate it, and
//! if it fails, `cl.exe` is expected to be available in `PATH`. This can be
//! set up by running the appropriate developer tools shell.
//! * Windows platforms targeting MinGW (e.g. your target triple ends in `-gnu`)
//! * Windows platforms targeting MinGW (e.g. your target name ends in `-gnu`)
//! require `cc` to be available in `PATH`. We recommend the
//! [MinGW-w64](https://www.mingw-w64.org/) distribution.
//! You may also acquire it via
Expand Down Expand Up @@ -2169,9 +2169,9 @@ impl Build {
// libstdc++ (this behavior was changed in llvm 14).
//
// This breaks C++11 (or greater) builds if targeting FreeBSD with the
// generic xxx-unknown-freebsd triple on clang 13 or below *without*
// generic xxx-unknown-freebsd target on clang 13 or below *without*
// explicitly specifying that libc++ should be used.
// When cross-compiling, we can't infer from the rust/cargo target triple
// When cross-compiling, we can't infer from the rust/cargo target name
// which major version of FreeBSD we are targeting, so we need to make sure
// that libc++ is used (unless the user has explicitly specified otherwise).
// There's no compelling reason to use a different approach when compiling
Expand Down Expand Up @@ -2202,14 +2202,14 @@ 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(&self.get_raw_target()?, version.as_deref());
cmd.push_cc_arg(format!("--target={clang_target}").into());
}
}
Expand All @@ -2235,7 +2235,13 @@ impl Build {
// <https://github.com/microsoft/STL/pull/4741>.
cmd.push_cc_arg("-arch:SSE2".into());
} else {
cmd.push_cc_arg(format!("--target={}", target.llvm_target).into());
cmd.push_cc_arg(
format!(
"--target={}",
target.llvm_target(&self.get_raw_target()?, None)
)
.into(),
);
}
} else if target.full_arch == "i586" {
cmd.push_cc_arg("-arch:IA32".into());
Expand Down Expand Up @@ -3520,7 +3526,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
95 changes: 2 additions & 93 deletions src/target.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
//! Very basic parsing of `rustc` target triples.
//!
//! See the `target-lexicon` crate for a more principled approach to this.

use std::str::FromStr;

use crate::{Error, ErrorKind};
//! Parsing of `rustc` target names to match the values exposed to Cargo
//! build scripts (`CARGO_CFG_*`).

mod apple;
mod generated;
Expand Down Expand Up @@ -43,90 +38,4 @@ 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 super::TargetInfo;

// Test tier 1 targets
#[test]
fn tier1() {
let targets = [
"aarch64-unknown-linux-gnu",
"aarch64-apple-darwin",
"i686-pc-windows-gnu",
"i686-pc-windows-msvc",
"i686-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-gnu",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
];

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

// Various custom target triples not (or no longer) known by `rustc`
#[test]
fn cannot_parse_extra() {
let targets = [
"aarch64-unknown-none-gnu",
"aarch64-uwp-windows-gnu",
"arm-frc-linux-gnueabi",
"arm-unknown-netbsd-eabi",
"armv7neon-unknown-linux-gnueabihf",
"armv7neon-unknown-linux-musleabihf",
"thumbv7-unknown-linux-gnueabihf",
"thumbv7-unknown-linux-musleabihf",
"x86_64-rumprun-netbsd",
"x86_64-unknown-linux",
];

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