1 use std::{borrow::Cow, env};
3 use crate::spec::{cvs, Cc, DebuginfoKind, FramePointer, LinkArgs};
4 use crate::spec::{LinkerFlavor, Lld, SplitDebuginfo, StaticCow, TargetOptions};
6 fn pre_link_args(os: &'static str, arch: &'static str, abi: &'static str) -> LinkArgs {
7 let platform_name: StaticCow<str> = match abi {
8 "sim" => format!("{}-simulator", os).into(),
9 "macabi" => "mac-catalyst".into(),
13 let platform_version: StaticCow<str> = match os.as_ref() {
14 "ios" => ios_lld_platform_version(),
15 "tvos" => tvos_lld_platform_version(),
16 "watchos" => watchos_lld_platform_version(),
17 "macos" => macos_lld_platform_version(arch),
22 let mut args = TargetOptions::link_args(
23 LinkerFlavor::Darwin(Cc::No, Lld::No),
24 &["-arch", arch, "-platform_version"],
26 super::add_link_args_iter(
28 LinkerFlavor::Darwin(Cc::No, Lld::No),
29 [platform_name, platform_version.clone(), platform_version].into_iter(),
32 super::add_link_args(&mut args, LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-arch", arch]);
38 pub fn opts(os: &'static str, arch: &'static str, abi: &'static str) -> TargetOptions {
39 // ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6
40 // either the linker will complain if it is used or the binary will end up
41 // segfaulting at runtime when run on 10.6. Rust by default supports macOS
42 // 10.7+, but there is a standard environment variable,
43 // MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older
44 // versions of macOS. For example compiling on 10.10 with
45 // MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate
46 // warnings about the usage of ELF TLS.
48 // Here we detect what version is being requested, defaulting to 10.7. ELF
49 // TLS is flagged as enabled if it looks to be supported. The architecture
50 // only matters for default deployment target which is 11.0 for ARM64 and
51 // 10.7 for everything else.
52 let has_thread_local = macos_deployment_target("x86_64") >= (10, 7);
56 vendor: "apple".into(),
57 linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
58 // macOS has -dead_strip, which doesn't rely on function_sections
59 function_sections: false,
60 dynamic_linking: true,
61 pre_link_args: pre_link_args(os, arch, abi),
62 families: cvs!["unix"],
64 default_dwarf_version: 2,
65 frame_pointer: FramePointer::Always,
67 dll_suffix: ".dylib".into(),
68 archive_format: "darwin".into(),
70 abi_return_struct_as_int: true,
71 emit_debug_gdb_scripts: false,
72 eh_frame_header: false,
74 debuginfo_kind: DebuginfoKind::DwarfDsym,
75 // The historical default for macOS targets is to run `dsymutil` which
76 // generates a packed version of debuginfo split from the main file.
77 split_debuginfo: SplitDebuginfo::Packed,
78 supported_split_debuginfo: Cow::Borrowed(&[
79 SplitDebuginfo::Packed,
80 SplitDebuginfo::Unpacked,
84 // This environment variable is pretty magical but is intended for
85 // producing deterministic builds. This was first discovered to be used
86 // by the `ar` tool as a way to control whether or not mtime entries in
87 // the archive headers were set to zero or not. It appears that
88 // eventually the linker got updated to do the same thing and now reads
89 // this environment variable too in recent versions.
91 // For some more info see the commentary on #47086
92 link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
98 fn deployment_target(var_name: &str) -> Option<(u32, u32)> {
99 let deployment_target = env::var(var_name).ok();
102 .and_then(|s| s.split_once('.'))
103 .and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok())
106 fn macos_default_deployment_target(arch: &str) -> (u32, u32) {
107 if arch == "arm64" { (11, 0) } else { (10, 7) }
110 fn macos_deployment_target(arch: &str) -> (u32, u32) {
111 deployment_target("MACOSX_DEPLOYMENT_TARGET")
112 .unwrap_or_else(|| macos_default_deployment_target(arch))
115 fn macos_lld_platform_version(arch: &str) -> String {
116 let (major, minor) = macos_deployment_target(arch);
117 format!("{}.{}", major, minor)
120 pub fn macos_llvm_target(arch: &str) -> String {
121 let (major, minor) = macos_deployment_target(arch);
122 format!("{}-apple-macosx{}.{}.0", arch, major, minor)
125 pub fn macos_link_env_remove() -> Vec<StaticCow<str>> {
126 let mut env_remove = Vec::with_capacity(2);
127 // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
128 // may occur when we're linking a custom build script while targeting iOS for example.
129 if let Ok(sdkroot) = env::var("SDKROOT") {
130 if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform") {
131 env_remove.push("SDKROOT".into())
134 // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
135 // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
136 // although this is apparently ignored when using the linker at "/usr/bin/ld".
137 env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
141 fn ios_deployment_target() -> (u32, u32) {
142 deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
145 pub fn ios_llvm_target(arch: &str) -> String {
146 // Modern iOS tooling extracts information about deployment target
147 // from LC_BUILD_VERSION. This load command will only be emitted when
148 // we build with a version specific `llvm_target`, with the version
149 // set high enough. Luckily one LC_BUILD_VERSION is enough, for Xcode
150 // to pick it up (since std and core are still built with the fallback
151 // of version 7.0 and hence emit the old LC_IPHONE_MIN_VERSION).
152 let (major, minor) = ios_deployment_target();
153 format!("{}-apple-ios{}.{}.0", arch, major, minor)
156 fn ios_lld_platform_version() -> String {
157 let (major, minor) = ios_deployment_target();
158 format!("{}.{}", major, minor)
161 pub fn ios_sim_llvm_target(arch: &str) -> String {
162 let (major, minor) = ios_deployment_target();
163 format!("{}-apple-ios{}.{}.0-simulator", arch, major, minor)
166 fn tvos_deployment_target() -> (u32, u32) {
167 deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
170 fn tvos_lld_platform_version() -> String {
171 let (major, minor) = tvos_deployment_target();
172 format!("{}.{}", major, minor)
175 fn watchos_deployment_target() -> (u32, u32) {
176 deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0))
179 fn watchos_lld_platform_version() -> String {
180 let (major, minor) = watchos_deployment_target();
181 format!("{}.{}", major, minor)
184 pub fn watchos_sim_llvm_target(arch: &str) -> String {
185 let (major, minor) = watchos_deployment_target();
186 format!("{}-apple-watchos{}.{}.0-simulator", arch, major, minor)