Skip to content

Commit

Permalink
Use Cargo's target information when possible (#1225)
Browse files Browse the repository at this point in the history
* Reduce the need for the host target triple

* Use Cargo's target information when possible

rustc's target triples generally only have a vague resemblance to each
other and to the information needed by `cc`. Let's instead prefer
`CARGO_CFG_*` variables when available, since these contain the
information directly from the compiler itself.

In the cases where it isn't available (i.e. when running outside of a
build script), we fall back to parsing the target triple, but instead of
doing it in an ad-hoc fashion with string manipulation, we do it in a
more structured fashion up front.

* Remove unnecessary derive

* Use pre-generated target information

* Use Cow<'static, str> in Target

* Remove redundant test

* Redo from_cargo_environment_variables to be more error resilient

* Fix after merge

* Appease clippy

* Add support for targets within MSRV

* Fix test

* Fix a few more tests

* Properly match previous behaviour

* Update to Rust 2024-10-25

* Use a `const` and binary search instead of a match

* Fix formatting
  • Loading branch information
madsmtm authored Nov 1, 2024
1 parent c07ab0e commit 290a629
Show file tree
Hide file tree
Showing 12 changed files with 3,655 additions and 450 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/regenerate-target-info.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ jobs:
git checkout -b regenerate-target-info-${{ github.run_id }}
- name: Install rust
# Install both MSRV and current nightly
run: |
rustup toolchain install stable nightly --no-self-update --profile minimal
rustup toolchain install 1.63 stable nightly --no-self-update --profile minimal
- name: Create lockfile
run: cargo update

- uses: Swatinem/rust-cache@v2
with:
cache-all-crates: 'true'
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
target
/target
Cargo.lock
.idea
*.iml
2 changes: 1 addition & 1 deletion dev-tools/gen-target-info/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod target_specs;
pub use target_specs::*;

mod read;
pub use read::get_target_specs_from_json;
pub use read::*;

mod write;
pub use write::*;
80 changes: 49 additions & 31 deletions dev-tools/gen-target-info/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,70 @@
use gen_target_info::{get_target_specs_from_json, write_target_tuple_mapping, RustcTargetSpecs};
use std::{collections::BTreeMap, fs::File, io::Write as _};
use std::io::Write as _;
use std::{fs::File, io::BufRead};

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.
"#;

fn generate_riscv_arch_mapping(f: &mut File, target_specs: &RustcTargetSpecs) {
let riscv_target_mapping = target_specs
.0
.iter()
.filter_map(|(target, target_spec)| {
let arch = target.split_once('-').unwrap().0;
(arch.contains("riscv") && arch != target_spec.arch)
.then_some((arch, &*target_spec.arch))
})
.collect::<BTreeMap<_, _>>();
write_target_tuple_mapping(f, "RISCV_ARCH_MAPPING", &riscv_target_mapping);
}
fn generate_target_mapping(f: &mut File, target_specs: &RustcTargetSpecs) -> std::io::Result<()> {
writeln!(f, "use super::Target;")?;
writeln!(f, "use std::borrow::Cow;")?;
writeln!(f)?;
writeln!(f, "pub(crate) const LIST: &[(&str, Target)] = &[")?;

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("");

writeln!(f, " (")?;
writeln!(f, " {triple:?},")?;
writeln!(f, " Target {{")?;
writeln!(f, " full_arch: Cow::Borrowed({full_arch:?}),")?;
writeln!(f, " arch: Cow::Borrowed({arch:?}),")?;
writeln!(f, " vendor: Cow::Borrowed({vendor:?}),")?;
writeln!(f, " os: Cow::Borrowed({os:?}),")?;
writeln!(f, " env: Cow::Borrowed({env:?}),")?;
writeln!(f, " abi: Cow::Borrowed({abi:?}),")?;
writeln!(f, " }},")?;
writeln!(f, " ),")?;
}

writeln!(f, "];")?;

fn generate_windows_triple_mapping(f: &mut File, target_specs: &RustcTargetSpecs) {
let windows_target_mapping = target_specs
.0
.iter()
.filter_map(|(target, target_spec)| {
let rust_target_parts = target.splitn(4, '-').collect::<Vec<_>>();
let os = *rust_target_parts.get(2)?;
(os.contains("windows") && target != &*target_spec.llvm_target)
.then_some((&**target, &*target_spec.llvm_target))
})
.collect::<BTreeMap<_, _>>();
write_target_tuple_mapping(f, "WINDOWS_TRIPLE_MAPPING", &windows_target_mapping);
Ok(())
}

fn main() {
let target_specs = get_target_specs_from_json();
// Primarily use information from nightly.
let mut target_specs = get_target_specs_from_json();
// Next, read from MSRV to support old, removed targets.
for target_triple in get_targets_msrv().lines() {
let target_triple = target_triple.unwrap();
let target_triple = target_triple.trim();
target_specs
.0
.entry(target_triple.to_string())
.or_insert_with(|| get_target_spec_from_msrv(target_triple));
}

// Open file to write to
let manifest_dir = env!("CARGO_MANIFEST_DIR");

let path = format!("{manifest_dir}/../../src/target_info.rs");
let mut f = File::create(path).expect("failed to create src/target_info.rs");
let path = format!("{manifest_dir}/../../src/target/generated.rs");
let mut f = File::create(path).expect("failed to create src/target/generated.rs");

f.write_all(PRELUDE.as_bytes()).unwrap();

// Start generating
generate_riscv_arch_mapping(&mut f, &target_specs);
generate_windows_triple_mapping(&mut f, &target_specs);
generate_target_mapping(&mut f, &target_specs).unwrap();

// Flush the data onto disk
f.flush().unwrap();
Expand Down
45 changes: 42 additions & 3 deletions dev-tools/gen-target-info/src/read.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,44 @@
use std::process;

use crate::RustcTargetSpecs;
use crate::{RustcTargetSpecs, TargetSpec};

pub fn get_targets_msrv() -> Vec<u8> {
let mut cmd = process::Command::new("rustc");
cmd.args(["+1.63", "--print", "target-list"]);
cmd.stdout(process::Stdio::piped());
cmd.stderr(process::Stdio::inherit());

let process::Output { status, stdout, .. } = cmd.output().unwrap();

if !status.success() {
panic!("{:?} failed with non-zero exit status: {}", cmd, status)
}

stdout
}

pub fn get_target_spec_from_msrv(target: &str) -> TargetSpec {
let mut cmd = process::Command::new("rustc");
cmd.args([
"+1.63",
"-Zunstable-options",
"--print",
"target-spec-json",
"--target",
target,
]);
cmd.env("RUSTC_BOOTSTRAP", "1");
cmd.stdout(process::Stdio::piped());
cmd.stderr(process::Stdio::inherit());

let process::Output { status, stdout, .. } = cmd.output().unwrap();

if !status.success() {
panic!("{:?} failed with non-zero exit status: {}", cmd, status)
}

serde_json::from_slice(&stdout).unwrap()
}

pub fn get_target_specs_from_json() -> RustcTargetSpecs {
let mut cmd = process::Command::new("rustc");
Expand All @@ -9,8 +47,9 @@ pub fn get_target_specs_from_json() -> RustcTargetSpecs {
"-Zunstable-options",
"--print",
"all-target-specs-json",
])
.stdout(process::Stdio::piped());
]);
cmd.stdout(process::Stdio::piped());
cmd.stderr(process::Stdio::inherit());

let process::Output { status, stdout, .. } = cmd.output().unwrap();

Expand Down
2 changes: 2 additions & 0 deletions dev-tools/gen-target-info/src/target_specs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub struct TargetSpec {
pub os: Option<String>,
/// `apple`, `pc`
pub vendor: Option<String>,
pub env: Option<String>,
pub abi: Option<String>,
pub pre_link_args: Option<PreLinkArgs>,
}

Expand Down
Loading

0 comments on commit 290a629

Please sign in to comment.