| 1 | use std::{env, str::FromStr}; |
| 2 | |
| 3 | use crate::{ |
| 4 | target::{llvm, TargetInfo}, |
| 5 | utilities::OnceLock, |
| 6 | Error, ErrorKind, |
| 7 | }; |
| 8 | |
| 9 | #[derive (Debug)] |
| 10 | struct 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 | |
| 20 | impl 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)] |
| 102 | pub(crate) struct TargetInfoParser(OnceLock<Result<TargetInfoParserInner, Error>>); |
| 103 | |
| 104 | impl 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 | |