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