1use std::borrow::Cow;
2use std::env;
3use std::fmt::{Display, from_fn};
4use std::num::ParseIntError;
5use std::str::FromStr;
6
7use crate::spec::{
8 BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi,
9 SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs,
10};
11
12#[cfg(test)]
13mod tests;
14
15use Arch::*;
16#[allow(non_camel_case_types)]
17#[derive(Copy, Clone, PartialEq)]
18pub(crate) enum Arch {
19 Armv7k,
20 Armv7s,
21 Arm64,
22 Arm64e,
23 Arm64_32,
24 I386,
25 I686,
26 X86_64,
27 X86_64h,
28}
29
30impl Arch {
31 fn target_name(self) -> &'static str {
32 match self {
33 Armv7k => "armv7k",
34 Armv7s => "armv7s",
35 Arm64 => "arm64",
36 Arm64e => "arm64e",
37 Arm64_32 => "arm64_32",
38 I386 => "i386",
39 I686 => "i686",
40 X86_64 => "x86_64",
41 X86_64h => "x86_64h",
42 }
43 }
44
45 pub(crate) fn target_arch(self) -> Cow<'static, str> {
46 Cow::Borrowed(match self {
47 Armv7k | Armv7s => "arm",
48 Arm64 | Arm64e | Arm64_32 => "aarch64",
49 I386 | I686 => "x86",
50 X86_64 | X86_64h => "x86_64",
51 })
52 }
53
54 fn target_cpu(self, abi: TargetAbi) -> &'static str {
55 match self {
56 Armv7k => "cortex-a8",
57 Armv7s => "swift", Arm64 => match abi {
59 TargetAbi::Normal => "apple-a7",
60 TargetAbi::Simulator => "apple-a12",
61 TargetAbi::MacCatalyst => "apple-a12",
62 },
63 Arm64e => "apple-a12",
64 Arm64_32 => "apple-s4",
65 I386 | I686 => "penryn",
69 X86_64 => "penryn",
70 X86_64h => "core-avx2",
74 }
75 }
76
77 fn stack_probes(self) -> StackProbeType {
78 match self {
79 Armv7k | Armv7s => StackProbeType::None,
80 Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64h => StackProbeType::Inline,
81 }
82 }
83}
84
85#[derive(Copy, Clone, PartialEq)]
86pub(crate) enum TargetAbi {
87 Normal,
88 Simulator,
89 MacCatalyst,
90}
91
92impl TargetAbi {
93 fn target_abi(self) -> &'static str {
94 match self {
95 Self::Normal => "",
96 Self::MacCatalyst => "macabi",
97 Self::Simulator => "sim",
98 }
99 }
100}
101
102pub(crate) fn base(
105 os: &'static str,
106 arch: Arch,
107 abi: TargetAbi,
108) -> (TargetOptions, StaticCow<str>, StaticCow<str>) {
109 let mut opts = TargetOptions {
110 abi: abi.target_abi().into(),
111 llvm_floatabi: Some(FloatAbi::Hard),
112 os: os.into(),
113 cpu: arch.target_cpu(abi).into(),
114 link_env_remove: link_env_remove(os),
115 vendor: "apple".into(),
116 linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
117 function_sections: false,
119 dynamic_linking: true,
120 families: cvs!["unix"],
121 is_like_darwin: true,
122 binary_format: BinaryFormat::MachO,
123 default_dwarf_version: 4,
127 frame_pointer: FramePointer::Always,
128 has_rpath: true,
129 dll_suffix: ".dylib".into(),
130 archive_format: "darwin".into(),
131 has_thread_local: true,
134 abi_return_struct_as_int: true,
135 emit_debug_gdb_scripts: false,
136 eh_frame_header: false,
137 stack_probes: arch.stack_probes(),
138
139 debuginfo_kind: DebuginfoKind::DwarfDsym,
140 split_debuginfo: SplitDebuginfo::Packed,
143 supported_split_debuginfo: Cow::Borrowed(&[
144 SplitDebuginfo::Packed,
145 SplitDebuginfo::Unpacked,
146 SplitDebuginfo::Off,
147 ]),
148
149 link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
158
159 ..Default::default()
160 };
161 if matches!(arch, Arch::I386 | Arch::I686) {
162 opts.rustc_abi = Some(RustcAbi::X86Sse2);
164 }
165 (opts, unversioned_llvm_target(os, arch, abi), arch.target_arch())
166}
167
168fn unversioned_llvm_target(os: &str, arch: Arch, abi: TargetAbi) -> StaticCow<str> {
173 let arch = arch.target_name();
174 let os = match os {
177 "macos" => "macosx",
178 "ios" => "ios",
179 "watchos" => "watchos",
180 "tvos" => "tvos",
181 "visionos" => "xros",
182 _ => unreachable!("tried to get LLVM target OS for non-Apple platform"),
183 };
184 let environment = match abi {
185 TargetAbi::Normal => "",
186 TargetAbi::MacCatalyst => "-macabi",
187 TargetAbi::Simulator => "-simulator",
188 };
189 format!("{arch}-apple-{os}{environment}").into()
190}
191
192fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
193 if os == "macos" {
199 let mut env_remove = Vec::with_capacity(2);
200 if let Ok(sdkroot) = env::var("SDKROOT") {
203 if sdkroot.contains("iPhoneOS.platform")
204 || sdkroot.contains("iPhoneSimulator.platform")
205 || sdkroot.contains("AppleTVOS.platform")
206 || sdkroot.contains("AppleTVSimulator.platform")
207 || sdkroot.contains("WatchOS.platform")
208 || sdkroot.contains("WatchSimulator.platform")
209 || sdkroot.contains("XROS.platform")
210 || sdkroot.contains("XRSimulator.platform")
211 {
212 env_remove.push("SDKROOT".into())
213 }
214 }
215 env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
219 env_remove.push("TVOS_DEPLOYMENT_TARGET".into());
220 env_remove.push("XROS_DEPLOYMENT_TARGET".into());
221 env_remove.into()
222 } else {
223 cvs!["MACOSX_DEPLOYMENT_TARGET"]
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
233pub struct OSVersion {
234 pub major: u16,
235 pub minor: u8,
236 pub patch: u8,
237}
238
239impl FromStr for OSVersion {
240 type Err = ParseIntError;
241
242 fn from_str(version: &str) -> Result<Self, ParseIntError> {
244 if let Some((major, minor)) = version.split_once('.') {
245 let major = major.parse()?;
246 if let Some((minor, patch)) = minor.split_once('.') {
247 Ok(Self { major, minor: minor.parse()?, patch: patch.parse()? })
248 } else {
249 Ok(Self { major, minor: minor.parse()?, patch: 0 })
250 }
251 } else {
252 Ok(Self { major: version.parse()?, minor: 0, patch: 0 })
253 }
254 }
255}
256
257impl OSVersion {
258 pub fn new(major: u16, minor: u8, patch: u8) -> Self {
259 Self { major, minor, patch }
260 }
261
262 pub fn fmt_pretty(self) -> impl Display {
263 let Self { major, minor, patch } = self;
264 from_fn(move |f| {
265 write!(f, "{major}.{minor}")?;
266 if patch != 0 {
267 write!(f, ".{patch}")?;
268 }
269 Ok(())
270 })
271 }
272
273 pub fn fmt_full(self) -> impl Display {
274 let Self { major, minor, patch } = self;
275 from_fn(move |f| write!(f, "{major}.{minor}.{patch}"))
276 }
277
278 pub fn os_minimum_deployment_target(os: &str) -> Self {
280 let (major, minor, patch) = match os {
288 "macos" => (10, 12, 0),
289 "ios" => (10, 0, 0),
290 "tvos" => (10, 0, 0),
291 "watchos" => (5, 0, 0),
292 "visionos" => (1, 0, 0),
293 _ => unreachable!("tried to get deployment target for non-Apple platform"),
294 };
295 Self { major, minor, patch }
296 }
297
298 pub fn minimum_deployment_target(target: &Target) -> Self {
306 let (major, minor, patch) = match (&*target.os, &*target.arch, &*target.abi) {
307 ("macos", "aarch64", _) => (11, 0, 0),
308 ("ios", "aarch64", "macabi") => (14, 0, 0),
309 ("ios", "aarch64", "sim") => (14, 0, 0),
310 ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
311 ("ios", _, "macabi") => (13, 1, 0),
313 ("tvos", "aarch64", "sim") => (14, 0, 0),
314 ("watchos", "aarch64", "sim") => (7, 0, 0),
315 (os, _, _) => return Self::os_minimum_deployment_target(os),
316 };
317 Self { major, minor, patch }
318 }
319}
320
321pub fn deployment_target_env_var(os: &str) -> &'static str {
323 match os {
324 "macos" => "MACOSX_DEPLOYMENT_TARGET",
325 "ios" => "IPHONEOS_DEPLOYMENT_TARGET",
326 "watchos" => "WATCHOS_DEPLOYMENT_TARGET",
327 "tvos" => "TVOS_DEPLOYMENT_TARGET",
328 "visionos" => "XROS_DEPLOYMENT_TARGET",
329 _ => unreachable!("tried to get deployment target env var for non-Apple platform"),
330 }
331}