Skip to main content

rustc_target/spec/base/apple/
mod.rs

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, CfgAbi, DebuginfoKind, Env, FloatAbi, FramePointer, LinkerFlavor, Lld, Os,
8    RustcAbi, SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs,
9};
10
11#[cfg(test)]
12mod tests;
13
14#[allow(non_camel_case_types)]
15#[derive(#[automatically_derived]
#[allow(non_camel_case_types)]
impl ::core::marker::Copy for Arch { }Copy, #[automatically_derived]
#[allow(non_camel_case_types)]
impl ::core::clone::Clone for Arch {
    #[inline]
    fn clone(&self) -> Arch { *self }
}Clone, #[automatically_derived]
#[allow(non_camel_case_types)]
impl ::core::cmp::PartialEq for Arch {
    #[inline]
    fn eq(&self, other: &Arch) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}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", // iOS 10 is only supported on iPhone 5 or higher.
56            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            // Only macOS 10.12+ is supported, which means
64            // all x86_64/x86 CPUs must be running at least penryn
65            // https://github.com/llvm/llvm-project/blob/01f924d0e37a5deae51df0d77e10a15b63aa0c0f/clang/lib/Driver/ToolChains/Arch/X86.cpp#L79-L82
66            Self::I386 | Self::I686 => "penryn",
67            Self::X86_64 => "penryn",
68            // Note: `core-avx2` is slightly more advanced than `x86_64h`, see
69            // comments (and disabled features) in `x86_64h_apple_darwin` for
70            // details. It is a higher baseline then `penryn` however.
71            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(#[automatically_derived]
impl ::core::marker::Copy for TargetEnv { }Copy, #[automatically_derived]
impl ::core::clone::Clone for TargetEnv {
    #[inline]
    fn clone(&self) -> TargetEnv { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for TargetEnv {
    #[inline]
    fn eq(&self, other: &TargetEnv) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq)]
90pub(crate) enum TargetEnv {
91    Normal,
92    Simulator,
93    MacCatalyst,
94}
95
96impl TargetEnv {
97    fn target_env(self) -> Env {
98        match self {
99            Self::Normal => Env::Unspecified,
100            Self::MacCatalyst => Env::MacAbi,
101            Self::Simulator => Env::Sim,
102        }
103    }
104
105    // NOTE: We originally set `cfg(target_abi = "macabi")` / `cfg(target_abi = "sim")`,
106    // before it was discovered that those are actually environments:
107    // https://github.com/rust-lang/rust/issues/133331
108    //
109    // But let's continue setting them for backwards compatibility.
110    // FIXME(madsmtm): Warn about using these in the future.
111    fn target_abi(self) -> CfgAbi {
112        match self {
113            Self::Normal => CfgAbi::Unspecified,
114            Self::MacCatalyst => CfgAbi::MacAbi,
115            Self::Simulator => CfgAbi::Sim,
116        }
117    }
118}
119
120/// Get the base target options, unversioned LLVM target and `target_arch` from the three
121/// things that uniquely identify Rust's Apple targets: The OS, the architecture, and the ABI.
122pub(crate) fn base(
123    os: Os,
124    arch: Arch,
125    env: TargetEnv,
126) -> (TargetOptions, StaticCow<str>, crate::spec::Arch) {
127    let link_env_remove = link_env_remove(&os);
128    let unversioned_llvm_target = unversioned_llvm_target(&os, arch, env);
129    let mut opts = TargetOptions {
130        llvm_floatabi: if arch.target_arch() == crate::spec::Arch::Arm {
131            Some(FloatAbi::Hard)
132        } else {
133            // `llvm_floatabi` makes no sense on x86 and aarch64.
134            None
135        },
136        os,
137        env: env.target_env(),
138        cfg_abi: env.target_abi(),
139        cpu: arch.target_cpu(env).into(),
140        link_env_remove,
141        vendor: "apple".into(),
142        linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
143        // macOS has -dead_strip, which doesn't rely on function_sections
144        function_sections: false,
145        dynamic_linking: true,
146        families: ::std::borrow::Cow::Borrowed(&[::std::borrow::Cow::Borrowed("unix")])cvs!["unix"],
147        is_like_darwin: true,
148        binary_format: BinaryFormat::MachO,
149        // LLVM notes that macOS 10.11+ and iOS 9+ default
150        // to v4, so we do the same.
151        // https://github.com/llvm/llvm-project/blob/378778a0d10c2f8d5df8ceff81f95b6002984a4b/clang/lib/Driver/ToolChains/Darwin.cpp#L1203
152        default_dwarf_version: 4,
153        frame_pointer: match arch {
154            // clang ignores `-fomit-frame-pointer` for Armv7, it only accepts `-momit-leaf-frame-pointer`
155            Arch::Armv7k | Arch::Armv7s => FramePointer::Always,
156            // clang supports omitting frame pointers for the rest, but... don't?
157            Arch::Arm64 | Arch::Arm64e | Arch::Arm64_32 => FramePointer::NonLeaf,
158            Arch::I386 | Arch::I686 | Arch::X86_64 | Arch::X86_64h => FramePointer::Always,
159        },
160        has_rpath: true,
161        dll_suffix: ".dylib".into(),
162        archive_format: "darwin".into(),
163        // Thread locals became available with iOS 8 and macOS 10.7,
164        // and both are far below our minimum.
165        has_thread_local: true,
166        abi_return_struct_as_int: true,
167        emit_debug_gdb_scripts: false,
168        eh_frame_header: false,
169        stack_probes: arch.stack_probes(),
170
171        debuginfo_kind: DebuginfoKind::DwarfDsym,
172        // The historical default for macOS targets is to run `dsymutil` which
173        // generates a packed version of debuginfo split from the main file.
174        split_debuginfo: SplitDebuginfo::Packed,
175        supported_split_debuginfo: Cow::Borrowed(&[
176            SplitDebuginfo::Packed,
177            SplitDebuginfo::Unpacked,
178            SplitDebuginfo::Off,
179        ]),
180
181        // Tell the linker that we would like it to avoid irreproducible binaries.
182        //
183        // This environment variable is pretty magical but is intended for
184        // producing deterministic builds. This was first discovered to be used
185        // by the `ar` tool as a way to control whether or not mtime entries in
186        // the archive headers were set to zero or not.
187        //
188        // In `ld64-351.8`, shipped with Xcode 9.3, the linker was updated to
189        // read this flag too. Linker versions that don't support this flag
190        // may embed modification timestamps in binaries (especially in debug
191        // information).
192        //
193        // A cleaner alternative would be to pass the `-reproducible` flag,
194        // though that is only supported since `ld64-819.6` shipped with Xcode
195        // 14, which is too new for our minimum supported version:
196        // https://doc.rust-lang.org/rustc/platform-support/apple-darwin.html#host-tooling
197        //
198        // For some more info see the commentary on #47086
199        link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
200
201        ..Default::default()
202    };
203    if #[allow(non_exhaustive_omitted_patterns)] match arch {
    Arch::I386 | Arch::I686 => true,
    _ => false,
}matches!(arch, Arch::I386 | Arch::I686) {
204        // All Apple x86-32 targets have SSE2.
205        opts.rustc_abi = Some(RustcAbi::X86Sse2);
206    }
207    (opts, unversioned_llvm_target, arch.target_arch())
208}
209
210/// Generate part of the LLVM target triple.
211///
212/// See `rustc_codegen_ssa::back::versioned_llvm_target` for the full triple passed to LLVM and
213/// Clang.
214fn unversioned_llvm_target(os: &Os, arch: Arch, env: TargetEnv) -> StaticCow<str> {
215    let arch = arch.target_name();
216    // Convert to the "canonical" OS name used by LLVM:
217    // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L236-L282
218    let os = match os {
219        Os::MacOs => "macosx",
220        Os::IOs => "ios",
221        Os::WatchOs => "watchos",
222        Os::TvOs => "tvos",
223        Os::VisionOs => "xros",
224        _ => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("tried to get LLVM target OS for non-Apple platform")));
}unreachable!("tried to get LLVM target OS for non-Apple platform"),
225    };
226    let environment = match env {
227        TargetEnv::Normal => "",
228        TargetEnv::MacCatalyst => "-macabi",
229        TargetEnv::Simulator => "-simulator",
230    };
231    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}-apple-{1}{2}", arch, os,
                environment))
    })format!("{arch}-apple-{os}{environment}").into()
232}
233
234fn link_env_remove(os: &Os) -> StaticCow<[StaticCow<str>]> {
235    // Apple platforms only officially support macOS as a host for any compilation.
236    //
237    // If building for macOS, we go ahead and remove any erroneous environment state
238    // that's only applicable to cross-OS compilation. Always leave anything for the
239    // host OS alone though.
240    if *os == Os::MacOs {
241        // `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
242        // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
243        // although this is apparently ignored when using the linker at "/usr/bin/ld".
244        ::std::borrow::Cow::Borrowed(&[::std::borrow::Cow::Borrowed("IPHONEOS_DEPLOYMENT_TARGET"),
                ::std::borrow::Cow::Borrowed("TVOS_DEPLOYMENT_TARGET"),
                ::std::borrow::Cow::Borrowed("XROS_DEPLOYMENT_TARGET")])cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET", "XROS_DEPLOYMENT_TARGET"]
245    } else {
246        // Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part
247        // of the linking environment that's wrong and reversed.
248        ::std::borrow::Cow::Borrowed(&[::std::borrow::Cow::Borrowed("MACOSX_DEPLOYMENT_TARGET")])cvs!["MACOSX_DEPLOYMENT_TARGET"]
249    }
250}
251
252/// Deployment target or SDK version.
253///
254/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
255#[derive(#[automatically_derived]
impl ::core::fmt::Debug for OSVersion {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "OSVersion",
            "major", &self.major, "minor", &self.minor, "patch", &&self.patch)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for OSVersion {
    #[inline]
    fn clone(&self) -> OSVersion {
        let _: ::core::clone::AssertParamIsClone<u16>;
        let _: ::core::clone::AssertParamIsClone<u8>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for OSVersion { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for OSVersion {
    #[inline]
    fn eq(&self, other: &OSVersion) -> bool {
        self.major == other.major && self.minor == other.minor &&
            self.patch == other.patch
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for OSVersion {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<u16>;
        let _: ::core::cmp::AssertParamIsEq<u8>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for OSVersion {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.major, state);
        ::core::hash::Hash::hash(&self.minor, state);
        ::core::hash::Hash::hash(&self.patch, state)
    }
}Hash, #[automatically_derived]
impl ::core::cmp::PartialOrd for OSVersion {
    #[inline]
    fn partial_cmp(&self, other: &OSVersion)
        -> ::core::option::Option<::core::cmp::Ordering> {
        match ::core::cmp::PartialOrd::partial_cmp(&self.major, &other.major)
            {
            ::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
                match ::core::cmp::PartialOrd::partial_cmp(&self.minor,
                        &other.minor) {
                    ::core::option::Option::Some(::core::cmp::Ordering::Equal)
                        =>
                        ::core::cmp::PartialOrd::partial_cmp(&self.patch,
                            &other.patch),
                    cmp => cmp,
                },
            cmp => cmp,
        }
    }
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for OSVersion {
    #[inline]
    fn cmp(&self, other: &OSVersion) -> ::core::cmp::Ordering {
        match ::core::cmp::Ord::cmp(&self.major, &other.major) {
            ::core::cmp::Ordering::Equal =>
                match ::core::cmp::Ord::cmp(&self.minor, &other.minor) {
                    ::core::cmp::Ordering::Equal =>
                        ::core::cmp::Ord::cmp(&self.patch, &other.patch),
                    cmp => cmp,
                },
            cmp => cmp,
        }
    }
}Ord)]
256pub struct OSVersion {
257    pub major: u16,
258    pub minor: u8,
259    pub patch: u8,
260}
261
262impl FromStr for OSVersion {
263    type Err = ParseIntError;
264
265    /// Parse an OS version triple (SDK version or deployment target).
266    fn from_str(version: &str) -> Result<Self, ParseIntError> {
267        if let Some((major, minor)) = version.split_once('.') {
268            let major = major.parse()?;
269            if let Some((minor, patch)) = minor.split_once('.') {
270                Ok(Self { major, minor: minor.parse()?, patch: patch.parse()? })
271            } else {
272                Ok(Self { major, minor: minor.parse()?, patch: 0 })
273            }
274        } else {
275            Ok(Self { major: version.parse()?, minor: 0, patch: 0 })
276        }
277    }
278}
279
280impl OSVersion {
281    pub fn new(major: u16, minor: u8, patch: u8) -> Self {
282        Self { major, minor, patch }
283    }
284
285    pub fn fmt_pretty(self) -> impl Display {
286        let Self { major, minor, patch } = self;
287        from_fn(move |f| {
288            f.write_fmt(format_args!("{0}.{1}", major, minor))write!(f, "{major}.{minor}")?;
289            if patch != 0 {
290                f.write_fmt(format_args!(".{0}", patch))write!(f, ".{patch}")?;
291            }
292            Ok(())
293        })
294    }
295
296    pub fn fmt_full(self) -> impl Display {
297        let Self { major, minor, patch } = self;
298        from_fn(move |f| f.write_fmt(format_args!("{0}.{1}.{2}", major, minor, patch))write!(f, "{major}.{minor}.{patch}"))
299    }
300
301    /// Minimum operating system versions currently supported by `rustc`.
302    pub fn os_minimum_deployment_target(os: &Os) -> Self {
303        // When bumping a version in here, remember to update the platform-support docs too.
304        //
305        // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
306        // default deployment target, prefer:
307        // ```
308        // $ rustc --print deployment-target
309        // ```
310        let (major, minor, patch) = match os {
311            Os::MacOs => (10, 12, 0),
312            Os::IOs => (10, 0, 0),
313            Os::TvOs => (10, 0, 0),
314            Os::WatchOs => (5, 0, 0),
315            Os::VisionOs => (1, 0, 0),
316            other => {
317                {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("tried to get deployment target for non-Apple platform: {0:?}",
                other)));
}unreachable!("tried to get deployment target for non-Apple platform: {:?}", other)
318            }
319        };
320        Self { major, minor, patch }
321    }
322
323    /// The deployment target for the given target.
324    ///
325    /// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
326    /// to raise the minimum OS version.
327    ///
328    /// This matches what LLVM does, see in part:
329    /// <https://github.com/llvm/llvm-project/blob/llvmorg-21.1.3/llvm/lib/TargetParser/Triple.cpp#L2140-L2175>
330    pub fn minimum_deployment_target(target: &Target) -> Self {
331        let (major, minor, patch) = match (&target.os, &target.arch, &target.env) {
332            (Os::MacOs, crate::spec::Arch::AArch64, _) => (11, 0, 0),
333            (Os::IOs, crate::spec::Arch::AArch64, Env::MacAbi) => (14, 0, 0),
334            (Os::IOs, crate::spec::Arch::AArch64, Env::Sim) => (14, 0, 0),
335            (Os::IOs, _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
336            // Mac Catalyst defaults to 13.1 in Clang.
337            (Os::IOs, _, Env::MacAbi) => (13, 1, 0),
338            (Os::TvOs, crate::spec::Arch::AArch64, Env::Sim) => (14, 0, 0),
339            (Os::WatchOs, crate::spec::Arch::AArch64, Env::Sim) => (7, 0, 0),
340            // True Aarch64 on watchOS (instead of their Aarch64 Ilp32 called `arm64_32`) has been
341            // available since Xcode 14, but it's only actually used more recently in watchOS 26.
342            (Os::WatchOs, crate::spec::Arch::AArch64, Env::Unspecified)
343                if !target.llvm_target.starts_with("arm64_32") =>
344            {
345                (26, 0, 0)
346            }
347            _ => return Self::os_minimum_deployment_target(&target.os),
348        };
349        Self { major, minor, patch }
350    }
351}
352
353/// Name of the environment variable used to fetch the deployment target on the given OS.
354pub fn deployment_target_env_var(os: &Os) -> &'static str {
355    match os {
356        Os::MacOs => "MACOSX_DEPLOYMENT_TARGET",
357        Os::IOs => "IPHONEOS_DEPLOYMENT_TARGET",
358        Os::WatchOs => "WATCHOS_DEPLOYMENT_TARGET",
359        Os::TvOs => "TVOS_DEPLOYMENT_TARGET",
360        Os::VisionOs => "XROS_DEPLOYMENT_TARGET",
361        _ => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("tried to get deployment target env var for non-Apple platform")));
}unreachable!("tried to get deployment target env var for non-Apple platform"),
362    }
363}