cc/target/parser.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
use std::{env, str::FromStr};
use crate::{
target::{llvm, TargetInfo},
utilities::OnceLock,
Error, ErrorKind,
};
#[derive(Debug)]
struct TargetInfoParserInner {
full_arch: Box<str>,
arch: Box<str>,
vendor: Box<str>,
os: Box<str>,
env: Box<str>,
abi: Box<str>,
unversioned_llvm_target: Box<str>,
}
impl TargetInfoParserInner {
fn from_cargo_environment_variables() -> Result<Self, Error> {
// `TARGET` must be present.
//
// No need to emit `rerun-if-env-changed` for this,
// as it is controlled by Cargo itself.
#[allow(clippy::disallowed_methods)]
let target_triple = env::var("TARGET").map_err(|err| {
Error::new(
ErrorKind::EnvVarNotFound,
format!("failed reading TARGET: {err}"),
)
})?;
// Parse the full architecture name from the target triple.
let (full_arch, _rest) = target_triple.split_once('-').ok_or(Error::new(
ErrorKind::InvalidTarget,
format!("target `{target_triple}` had an unknown architecture"),
))?;
let cargo_env = |name, fallback: Option<&str>| -> Result<Box<str>, Error> {
// No need to emit `rerun-if-env-changed` for these,
// as they are controlled by Cargo itself.
#[allow(clippy::disallowed_methods)]
match env::var(name) {
Ok(var) => Ok(var.into_boxed_str()),
Err(err) => match fallback {
Some(fallback) => Ok(fallback.into()),
None => Err(Error::new(
ErrorKind::EnvVarNotFound,
format!("did not find fallback information for target `{target_triple}`, and failed reading {name}: {err}"),
)),
},
}
};
// Prefer to use `CARGO_ENV_*` if set, since these contain the most
// correct information relative to the current `rustc`, and makes it
// possible to support custom target JSON specs unknown to `rustc`.
//
// NOTE: If the user is using an older `rustc`, that data may be older
// than our pre-generated data, but we still prefer Cargo's view of
// the world, since at least `cc` won't differ from `rustc` in that
// case.
//
// These may not be set in case the user depended on being able to
// just set `TARGET` outside of build scripts; in those cases, fall
// back back to data from the known set of target triples instead.
//
// See discussion in #1225 for further details.
let fallback_target = TargetInfo::from_str(&target_triple).ok();
let ft = fallback_target.as_ref();
let arch = cargo_env("CARGO_CFG_TARGET_ARCH", ft.map(|t| t.arch))?;
let vendor = cargo_env("CARGO_CFG_TARGET_VENDOR", ft.map(|t| t.vendor))?;
let os = cargo_env("CARGO_CFG_TARGET_OS", ft.map(|t| t.os))?;
let env = cargo_env("CARGO_CFG_TARGET_ENV", ft.map(|t| t.env))?;
// `target_abi` was stabilized in Rust 1.78, which is higher than our
// MSRV, so it may not always be available; In that case, fall back to
// `""`, which is _probably_ correct for unknown target triples.
let abi = cargo_env("CARGO_CFG_TARGET_ABI", ft.map(|t| t.abi))
.unwrap_or_else(|_| String::default().into_boxed_str());
// Prefer `rustc`'s LLVM target triple information.
let unversioned_llvm_target = match fallback_target {
Some(ft) => ft.unversioned_llvm_target.to_string(),
None => llvm::guess_llvm_target_triple(full_arch, &vendor, &os, &env, &abi),
};
Ok(Self {
full_arch: full_arch.to_string().into_boxed_str(),
arch,
vendor,
os,
env,
abi,
unversioned_llvm_target: unversioned_llvm_target.into_boxed_str(),
})
}
}
/// Parser for [`TargetInfo`], contains cached information.
#[derive(Default, Debug)]
pub(crate) struct TargetInfoParser(OnceLock<Result<TargetInfoParserInner, Error>>);
impl TargetInfoParser {
pub fn parse_from_cargo_environment_variables(&self) -> Result<TargetInfo<'_>, Error> {
match self
.0
.get_or_init(TargetInfoParserInner::from_cargo_environment_variables)
{
Ok(TargetInfoParserInner {
full_arch,
arch,
vendor,
os,
env,
abi,
unversioned_llvm_target,
}) => Ok(TargetInfo {
full_arch,
arch,
vendor,
os,
env,
abi,
unversioned_llvm_target,
}),
Err(e) => Err(e.clone()),
}
}
}