1use std::borrow::Cow;
2use std::fmt::{Display, from_fn};
3use std::num::ParseIntError;
4use std::str::FromStr;
5
6use crate::spec::{
7 BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi,
8 SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs,
9};
10
11#[cfg(test)]
12mod tests;
13
14#[allow(non_camel_case_types)]
15#[derive(Copy, Clone, PartialEq)]
16pub(crate) enum Arch {
17 Armv7k,
18 Armv7s,
19 Arm64,
20 Arm64e,
21 Arm64_32,
22 I386,
23 I686,
24 X86_64,
25 X86_64h,
26}
27
28impl Arch {
29 fn target_name(self) -> &'static str {
30 match self {
31 Self::Armv7k => "armv7k",
32 Self::Armv7s => "armv7s",
33 Self::Arm64 => "arm64",
34 Self::Arm64e => "arm64e",
35 Self::Arm64_32 => "arm64_32",
36 Self::I386 => "i386",
37 Self::I686 => "i686",
38 Self::X86_64 => "x86_64",
39 Self::X86_64h => "x86_64h",
40 }
41 }
42
43 pub(crate) fn target_arch(self) -> crate::spec::Arch {
44 match self {
45 Self::Armv7k | Self::Armv7s => crate::spec::Arch::Arm,
46 Self::Arm64 | Self::Arm64e | Self::Arm64_32 => crate::spec::Arch::AArch64,
47 Self::I386 | Self::I686 => crate::spec::Arch::X86,
48 Self::X86_64 | Self::X86_64h => crate::spec::Arch::X86_64,
49 }
50 }
51
52 fn target_cpu(self, env: TargetEnv) -> &'static str {
53 match self {
54 Self::Armv7k => "cortex-a8",
55 Self::Armv7s => "swift", Self::Arm64 => match env {
57 TargetEnv::Normal => "apple-a7",
58 TargetEnv::Simulator => "apple-a12",
59 TargetEnv::MacCatalyst => "apple-a12",
60 },
61 Self::Arm64e => "apple-a12",
62 Self::Arm64_32 => "apple-s4",
63 Self::I386 | Self::I686 => "penryn",
67 Self::X86_64 => "penryn",
68 Self::X86_64h => "core-avx2",
72 }
73 }
74
75 fn stack_probes(self) -> StackProbeType {
76 match self {
77 Self::Armv7k | Self::Armv7s => StackProbeType::None,
78 Self::Arm64
79 | Self::Arm64e
80 | Self::Arm64_32
81 | Self::I386
82 | Self::I686
83 | Self::X86_64
84 | Self::X86_64h => StackProbeType::Inline,
85 }
86 }
87}
88
89#[derive(Copy, Clone, PartialEq)]
90pub(crate) enum TargetEnv {
91 Normal,
92 Simulator,
93 MacCatalyst,
94}
95
96impl TargetEnv {
97 fn target_env(self) -> &'static str {
98 match self {
99 Self::Normal => "",
100 Self::MacCatalyst => "macabi",
101 Self::Simulator => "sim",
102 }
103 }
104}
105
106pub(crate) fn base(
109 os: &'static str,
110 arch: Arch,
111 env: TargetEnv,
112) -> (TargetOptions, StaticCow<str>, crate::spec::Arch) {
113 let mut opts = TargetOptions {
114 llvm_floatabi: Some(FloatAbi::Hard),
115 os: os.into(),
116 env: env.target_env().into(),
117 abi: env.target_env().into(),
124 cpu: arch.target_cpu(env).into(),
125 link_env_remove: link_env_remove(os),
126 vendor: "apple".into(),
127 linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
128 function_sections: false,
130 dynamic_linking: true,
131 families: cvs!["unix"],
132 is_like_darwin: true,
133 binary_format: BinaryFormat::MachO,
134 default_dwarf_version: 4,
138 frame_pointer: match arch {
139 Arch::Armv7k | Arch::Armv7s => FramePointer::Always,
141 Arch::Arm64 | Arch::Arm64e | Arch::Arm64_32 => FramePointer::NonLeaf,
143 Arch::I386 | Arch::I686 | Arch::X86_64 | Arch::X86_64h => FramePointer::Always,
144 },
145 has_rpath: true,
146 dll_suffix: ".dylib".into(),
147 archive_format: "darwin".into(),
148 has_thread_local: true,
151 abi_return_struct_as_int: true,
152 emit_debug_gdb_scripts: false,
153 eh_frame_header: false,
154 stack_probes: arch.stack_probes(),
155
156 debuginfo_kind: DebuginfoKind::DwarfDsym,
157 split_debuginfo: SplitDebuginfo::Packed,
160 supported_split_debuginfo: Cow::Borrowed(&[
161 SplitDebuginfo::Packed,
162 SplitDebuginfo::Unpacked,
163 SplitDebuginfo::Off,
164 ]),
165
166 link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
185
186 ..Default::default()
187 };
188 if matches!(arch, Arch::I386 | Arch::I686) {
189 opts.rustc_abi = Some(RustcAbi::X86Sse2);
191 }
192 (opts, unversioned_llvm_target(os, arch, env), arch.target_arch())
193}
194
195fn unversioned_llvm_target(os: &str, arch: Arch, env: TargetEnv) -> StaticCow<str> {
200 let arch = arch.target_name();
201 let os = match os {
204 "macos" => "macosx",
205 "ios" => "ios",
206 "watchos" => "watchos",
207 "tvos" => "tvos",
208 "visionos" => "xros",
209 _ => unreachable!("tried to get LLVM target OS for non-Apple platform"),
210 };
211 let environment = match env {
212 TargetEnv::Normal => "",
213 TargetEnv::MacCatalyst => "-macabi",
214 TargetEnv::Simulator => "-simulator",
215 };
216 format!("{arch}-apple-{os}{environment}").into()
217}
218
219fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
220 if os == "macos" {
226 cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET", "XROS_DEPLOYMENT_TARGET"]
230 } else {
231 cvs!["MACOSX_DEPLOYMENT_TARGET"]
234 }
235}
236
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
241pub struct OSVersion {
242 pub major: u16,
243 pub minor: u8,
244 pub patch: u8,
245}
246
247impl FromStr for OSVersion {
248 type Err = ParseIntError;
249
250 fn from_str(version: &str) -> Result<Self, ParseIntError> {
252 if let Some((major, minor)) = version.split_once('.') {
253 let major = major.parse()?;
254 if let Some((minor, patch)) = minor.split_once('.') {
255 Ok(Self { major, minor: minor.parse()?, patch: patch.parse()? })
256 } else {
257 Ok(Self { major, minor: minor.parse()?, patch: 0 })
258 }
259 } else {
260 Ok(Self { major: version.parse()?, minor: 0, patch: 0 })
261 }
262 }
263}
264
265impl OSVersion {
266 pub fn new(major: u16, minor: u8, patch: u8) -> Self {
267 Self { major, minor, patch }
268 }
269
270 pub fn fmt_pretty(self) -> impl Display {
271 let Self { major, minor, patch } = self;
272 from_fn(move |f| {
273 write!(f, "{major}.{minor}")?;
274 if patch != 0 {
275 write!(f, ".{patch}")?;
276 }
277 Ok(())
278 })
279 }
280
281 pub fn fmt_full(self) -> impl Display {
282 let Self { major, minor, patch } = self;
283 from_fn(move |f| write!(f, "{major}.{minor}.{patch}"))
284 }
285
286 pub fn os_minimum_deployment_target(os: &str) -> Self {
288 let (major, minor, patch) = match os {
296 "macos" => (10, 12, 0),
297 "ios" => (10, 0, 0),
298 "tvos" => (10, 0, 0),
299 "watchos" => (5, 0, 0),
300 "visionos" => (1, 0, 0),
301 _ => unreachable!("tried to get deployment target for non-Apple platform"),
302 };
303 Self { major, minor, patch }
304 }
305
306 pub fn minimum_deployment_target(target: &Target) -> Self {
314 let (major, minor, patch) = match (&*target.os, &target.arch, &*target.env) {
315 ("macos", crate::spec::Arch::AArch64, _) => (11, 0, 0),
316 ("ios", crate::spec::Arch::AArch64, "macabi") => (14, 0, 0),
317 ("ios", crate::spec::Arch::AArch64, "sim") => (14, 0, 0),
318 ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
319 ("ios", _, "macabi") => (13, 1, 0),
321 ("tvos", crate::spec::Arch::AArch64, "sim") => (14, 0, 0),
322 ("watchos", crate::spec::Arch::AArch64, "sim") => (7, 0, 0),
323 ("watchos", crate::spec::Arch::AArch64, "")
326 if !target.llvm_target.starts_with("arm64_32") =>
327 {
328 (26, 0, 0)
329 }
330 (os, _, _) => return Self::os_minimum_deployment_target(os),
331 };
332 Self { major, minor, patch }
333 }
334}
335
336pub fn deployment_target_env_var(os: &str) -> &'static str {
338 match os {
339 "macos" => "MACOSX_DEPLOYMENT_TARGET",
340 "ios" => "IPHONEOS_DEPLOYMENT_TARGET",
341 "watchos" => "WATCHOS_DEPLOYMENT_TARGET",
342 "tvos" => "TVOS_DEPLOYMENT_TARGET",
343 "visionos" => "XROS_DEPLOYMENT_TARGET",
344 _ => unreachable!("tried to get deployment target env var for non-Apple platform"),
345 }
346}