| 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 |  | 
|---|