1 use std::{borrow::Cow, env};
3 use crate::spec::{cvs, Cc, DebuginfoKind, FramePointer, LinkArgs};
4 use crate::spec::{LinkerFlavor, Lld, SplitDebuginfo, StaticCow, TargetOptions};
7 #[path = "apple/tests.rs"]
11 #[allow(non_camel_case_types)]
12 #[derive(Copy, Clone)]
29 pub fn target_name(self) -> &'static str {
34 Arm64 | Arm64_macabi | Arm64_sim => "arm64",
35 Arm64_32 => "arm64_32",
38 X86_64 | X86_64_sim | X86_64_macabi => "x86_64",
42 pub fn target_arch(self) -> Cow<'static, str> {
43 Cow::Borrowed(match self {
44 Armv7 | Armv7k | Armv7s => "arm",
45 Arm64 | Arm64_32 | Arm64_macabi | Arm64_sim => "aarch64",
47 X86_64 | X86_64_sim | X86_64_macabi => "x86_64",
51 fn target_abi(self) -> &'static str {
53 Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 => "",
54 X86_64_macabi | Arm64_macabi => "macabi",
55 // x86_64-apple-ios is a simulator target, even though it isn't
56 // declared that way in the target like the other ones...
57 Arm64_sim | X86_64_sim => "sim",
61 fn target_cpu(self) -> &'static str {
63 Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
64 Armv7k => "cortex-a8",
65 Armv7s => "cortex-a9",
67 Arm64_32 => "apple-s4",
68 I386 | I686 => "yonah",
69 X86_64 | X86_64_sim => "core2",
70 X86_64_macabi => "core2",
71 Arm64_macabi => "apple-a12",
72 Arm64_sim => "apple-a12",
76 fn link_env_remove(self) -> StaticCow<[StaticCow<str>]> {
78 Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim
80 cvs!["MACOSX_DEPLOYMENT_TARGET"]
82 X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],
87 fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs {
88 let platform_name: StaticCow<str> = match abi {
89 "sim" => format!("{}-simulator", os).into(),
90 "macabi" => "mac-catalyst".into(),
94 let platform_version: StaticCow<str> = match os.as_ref() {
95 "ios" => ios_lld_platform_version(),
96 "tvos" => tvos_lld_platform_version(),
97 "watchos" => watchos_lld_platform_version(),
98 "macos" => macos_lld_platform_version(arch),
103 let arch = arch.target_name();
105 let mut args = TargetOptions::link_args(
106 LinkerFlavor::Darwin(Cc::No, Lld::No),
107 &["-arch", arch, "-platform_version"],
109 super::add_link_args_iter(
111 LinkerFlavor::Darwin(Cc::No, Lld::No),
112 [platform_name, platform_version.clone(), platform_version].into_iter(),
115 super::add_link_args(&mut args, LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-arch", arch]);
121 pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
122 // Static TLS is only available in macOS 10.7+. If you try to compile for 10.6
123 // either the linker will complain if it is used or the binary will end up
124 // segfaulting at runtime when run on 10.6. Rust by default supports macOS
125 // 10.7+, but there is a standard environment variable,
126 // MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older
127 // versions of macOS. For example compiling on 10.10 with
128 // MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate
129 // warnings about the usage of static TLS.
131 // Here we detect what version is being requested, defaulting to 10.7. Static
132 // TLS is flagged as enabled if it looks to be supported. The architecture
133 // only matters for default deployment target which is 11.0 for ARM64 and
134 // 10.7 for everything else.
135 let has_thread_local = os == "macos" && macos_deployment_target(Arch::X86_64) >= (10, 7);
137 let abi = arch.target_abi();
142 cpu: arch.target_cpu().into(),
143 link_env_remove: arch.link_env_remove(),
144 vendor: "apple".into(),
145 linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
146 // macOS has -dead_strip, which doesn't rely on function_sections
147 function_sections: false,
148 dynamic_linking: true,
149 pre_link_args: pre_link_args(os, arch, abi),
150 families: cvs!["unix"],
152 default_dwarf_version: 2,
153 frame_pointer: FramePointer::Always,
155 dll_suffix: ".dylib".into(),
156 archive_format: "darwin".into(),
158 abi_return_struct_as_int: true,
159 emit_debug_gdb_scripts: false,
160 eh_frame_header: false,
162 debuginfo_kind: DebuginfoKind::DwarfDsym,
163 // The historical default for macOS targets is to run `dsymutil` which
164 // generates a packed version of debuginfo split from the main file.
165 split_debuginfo: SplitDebuginfo::Packed,
166 supported_split_debuginfo: Cow::Borrowed(&[
167 SplitDebuginfo::Packed,
168 SplitDebuginfo::Unpacked,
172 // This environment variable is pretty magical but is intended for
173 // producing deterministic builds. This was first discovered to be used
174 // by the `ar` tool as a way to control whether or not mtime entries in
175 // the archive headers were set to zero or not. It appears that
176 // eventually the linker got updated to do the same thing and now reads
177 // this environment variable too in recent versions.
179 // For some more info see the commentary on #47086
180 link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
186 fn deployment_target(var_name: &str) -> Option<(u32, u32)> {
187 let deployment_target = env::var(var_name).ok();
190 .and_then(|s| s.split_once('.'))
191 .and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok())
194 fn macos_default_deployment_target(arch: Arch) -> (u32, u32) {
195 // Note: Arm64_sim is not included since macOS has no simulator.
196 if matches!(arch, Arm64 | Arm64_macabi) { (11, 0) } else { (10, 7) }
199 fn macos_deployment_target(arch: Arch) -> (u32, u32) {
200 deployment_target("MACOSX_DEPLOYMENT_TARGET")
201 .unwrap_or_else(|| macos_default_deployment_target(arch))
204 fn macos_lld_platform_version(arch: Arch) -> String {
205 let (major, minor) = macos_deployment_target(arch);
206 format!("{}.{}", major, minor)
209 pub fn macos_llvm_target(arch: Arch) -> String {
210 let (major, minor) = macos_deployment_target(arch);
211 format!("{}-apple-macosx{}.{}.0", arch.target_name(), major, minor)
214 pub fn macos_link_env_remove() -> Vec<StaticCow<str>> {
215 let mut env_remove = Vec::with_capacity(2);
216 // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
217 // may occur when we're linking a custom build script while targeting iOS for example.
218 if let Ok(sdkroot) = env::var("SDKROOT") {
219 if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform") {
220 env_remove.push("SDKROOT".into())
223 // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
224 // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
225 // although this is apparently ignored when using the linker at "/usr/bin/ld".
226 env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
230 fn ios_deployment_target() -> (u32, u32) {
231 deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
234 pub fn ios_llvm_target(arch: Arch) -> String {
235 // Modern iOS tooling extracts information about deployment target
236 // from LC_BUILD_VERSION. This load command will only be emitted when
237 // we build with a version specific `llvm_target`, with the version
238 // set high enough. Luckily one LC_BUILD_VERSION is enough, for Xcode
239 // to pick it up (since std and core are still built with the fallback
240 // of version 7.0 and hence emit the old LC_IPHONE_MIN_VERSION).
241 let (major, minor) = ios_deployment_target();
242 format!("{}-apple-ios{}.{}.0", arch.target_name(), major, minor)
245 fn ios_lld_platform_version() -> String {
246 let (major, minor) = ios_deployment_target();
247 format!("{}.{}", major, minor)
250 pub fn ios_sim_llvm_target(arch: Arch) -> String {
251 let (major, minor) = ios_deployment_target();
252 format!("{}-apple-ios{}.{}.0-simulator", arch.target_name(), major, minor)
255 fn tvos_deployment_target() -> (u32, u32) {
256 deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
259 fn tvos_lld_platform_version() -> String {
260 let (major, minor) = tvos_deployment_target();
261 format!("{}.{}", major, minor)
264 fn watchos_deployment_target() -> (u32, u32) {
265 deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0))
268 fn watchos_lld_platform_version() -> String {
269 let (major, minor) = watchos_deployment_target();
270 format!("{}.{}", major, minor)
273 pub fn watchos_sim_llvm_target(arch: Arch) -> String {
274 let (major, minor) = watchos_deployment_target();
275 format!("{}-apple-watchos{}.{}.0-simulator", arch.target_name(), major, minor)