1use std::{env, str::FromStr};
2
3use crate::{
4 target::{llvm, TargetInfo},
5 utilities::OnceLock,
6 Error, ErrorKind,
7};
8
9#[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 llvm_target: Box<str>,
18}
19
20impl TargetInfoParserInner {
21 fn 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)]
27 let target_triple = env::var("TARGET").map_err(|err| {
28 Error::new(
29 ErrorKind::EnvVarNotFound,
30 format!("failed reading TARGET: {err}"),
31 )
32 })?;
33
34 // Parse the full architecture name from the target triple.
35 let (full_arch, _rest) = target_triple.split_once('-').ok_or(Error::new(
36 ErrorKind::InvalidTarget,
37 format!("target `{target_triple}` had an unknown architecture"),
38 ))?;
39
40 let 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)]
44 match env::var(name) {
45 Ok(var) => Ok(var.into_boxed_str()),
46 Err(err) => match fallback {
47 Some(fallback) => Ok(fallback.into()),
48 None => Err(Error::new(
49 ErrorKind::EnvVarNotFound,
50 format!("did not find fallback information for target `{target_triple}`, and failed reading {name}: {err}"),
51 )),
52 },
53 }
54 };
55
56 // 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.
70 let fallback_target = TargetInfo::from_str(&target_triple).ok();
71 let ft = fallback_target.as_ref();
72 let arch = cargo_env("CARGO_CFG_TARGET_ARCH", ft.map(|t| t.arch))?;
73 let vendor = cargo_env("CARGO_CFG_TARGET_VENDOR", ft.map(|t| t.vendor))?;
74 let os = cargo_env("CARGO_CFG_TARGET_OS", ft.map(|t| t.os))?;
75 let 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.
79 let abi = cargo_env("CARGO_CFG_TARGET_ABI", ft.map(|t| t.abi))
80 .unwrap_or_else(|_| String::default().into_boxed_str());
81
82 // Prefer `rustc`'s LLVM target triple information.
83 let llvm_target = match fallback_target {
84 Some(ft) => ft.llvm_target.to_string(),
85 None => llvm::guess_llvm_target_triple(full_arch, &vendor, &os, &env, &abi),
86 };
87
88 Ok(Self {
89 full_arch: full_arch.to_string().into_boxed_str(),
90 arch,
91 vendor,
92 os,
93 env,
94 abi,
95 llvm_target: llvm_target.into_boxed_str(),
96 })
97 }
98}
99
100/// Parser for [`TargetInfo`], contains cached information.
101#[derive(Default, Debug)]
102pub(crate) struct TargetInfoParser(OnceLock<Result<TargetInfoParserInner, Error>>);
103
104impl TargetInfoParser {
105 pub fn parse_from_cargo_environment_variables(&self) -> Result<TargetInfo<'_>, Error> {
106 match self
107 .0
108 .get_or_init(TargetInfoParserInner::from_cargo_environment_variables)
109 {
110 Ok(TargetInfoParserInner {
111 full_arch,
112 arch,
113 vendor,
114 os,
115 env,
116 abi,
117 llvm_target,
118 }) => Ok(TargetInfo {
119 full_arch,
120 arch,
121 vendor,
122 os,
123 env,
124 abi,
125 llvm_target,
126 }),
127 Err(e) => Err(e.clone()),
128 }
129 }
130}
131