]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_target/src/spec/apple_base.rs
Rollup merge of #105694 - ouz-a:issue_105689, r=estebank
[rust.git] / compiler / rustc_target / src / spec / apple_base.rs
1 use std::{borrow::Cow, env};
2
3 use crate::spec::{cvs, Cc, DebuginfoKind, FramePointer, LinkArgs};
4 use crate::spec::{LinkerFlavor, Lld, SplitDebuginfo, StaticCow, TargetOptions};
5
6 #[cfg(test)]
7 #[path = "apple/tests.rs"]
8 mod tests;
9
10 use Arch::*;
11 #[allow(non_camel_case_types)]
12 #[derive(Copy, Clone)]
13 pub enum Arch {
14     Armv7,
15     Armv7k,
16     Armv7s,
17     Arm64,
18     Arm64_32,
19     I386,
20     I686,
21     X86_64,
22     X86_64_sim,
23     X86_64_macabi,
24     Arm64_macabi,
25     Arm64_sim,
26 }
27
28 impl Arch {
29     pub fn target_name(self) -> &'static str {
30         match self {
31             Armv7 => "armv7",
32             Armv7k => "armv7k",
33             Armv7s => "armv7s",
34             Arm64 | Arm64_macabi | Arm64_sim => "arm64",
35             Arm64_32 => "arm64_32",
36             I386 => "i386",
37             I686 => "i686",
38             X86_64 | X86_64_sim | X86_64_macabi => "x86_64",
39         }
40     }
41
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",
46             I386 | I686 => "x86",
47             X86_64 | X86_64_sim | X86_64_macabi => "x86_64",
48         })
49     }
50
51     fn target_abi(self) -> &'static str {
52         match self {
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",
58         }
59     }
60
61     fn target_cpu(self) -> &'static str {
62         match self {
63             Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher
64             Armv7k => "cortex-a8",
65             Armv7s => "cortex-a9",
66             Arm64 => "apple-a7",
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",
73         }
74     }
75 }
76
77 fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs {
78     let platform_name: StaticCow<str> = match abi {
79         "sim" => format!("{}-simulator", os).into(),
80         "macabi" => "mac-catalyst".into(),
81         _ => os.into(),
82     };
83
84     let platform_version: StaticCow<str> = match os {
85         "ios" => ios_lld_platform_version(),
86         "tvos" => tvos_lld_platform_version(),
87         "watchos" => watchos_lld_platform_version(),
88         "macos" => macos_lld_platform_version(arch),
89         _ => unreachable!(),
90     }
91     .into();
92
93     let arch = arch.target_name();
94
95     let mut args = TargetOptions::link_args(
96         LinkerFlavor::Darwin(Cc::No, Lld::No),
97         &["-arch", arch, "-platform_version"],
98     );
99     super::add_link_args_iter(
100         &mut args,
101         LinkerFlavor::Darwin(Cc::No, Lld::No),
102         [platform_name, platform_version.clone(), platform_version].into_iter(),
103     );
104     if abi != "macabi" {
105         super::add_link_args(&mut args, LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-arch", arch]);
106     }
107
108     args
109 }
110
111 pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
112     // Static TLS is only available in macOS 10.7+. If you try to compile for 10.6
113     // either the linker will complain if it is used or the binary will end up
114     // segfaulting at runtime when run on 10.6. Rust by default supports macOS
115     // 10.7+, but there is a standard environment variable,
116     // MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older
117     // versions of macOS. For example compiling on 10.10 with
118     // MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate
119     // warnings about the usage of static TLS.
120     //
121     // Here we detect what version is being requested, defaulting to 10.7. Static
122     // TLS is flagged as enabled if it looks to be supported. The architecture
123     // only matters for default deployment target which is 11.0 for ARM64 and
124     // 10.7 for everything else.
125     let has_thread_local = os == "macos" && macos_deployment_target(Arch::X86_64) >= (10, 7);
126
127     let abi = arch.target_abi();
128
129     TargetOptions {
130         abi: abi.into(),
131         os: os.into(),
132         cpu: arch.target_cpu().into(),
133         link_env_remove: link_env_remove(arch, os),
134         vendor: "apple".into(),
135         linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
136         // macOS has -dead_strip, which doesn't rely on function_sections
137         function_sections: false,
138         dynamic_linking: true,
139         pre_link_args: pre_link_args(os, arch, abi),
140         families: cvs!["unix"],
141         is_like_osx: true,
142         default_dwarf_version: 2,
143         frame_pointer: FramePointer::Always,
144         has_rpath: true,
145         dll_suffix: ".dylib".into(),
146         archive_format: "darwin".into(),
147         has_thread_local,
148         abi_return_struct_as_int: true,
149         emit_debug_gdb_scripts: false,
150         eh_frame_header: false,
151
152         debuginfo_kind: DebuginfoKind::DwarfDsym,
153         // The historical default for macOS targets is to run `dsymutil` which
154         // generates a packed version of debuginfo split from the main file.
155         split_debuginfo: SplitDebuginfo::Packed,
156         supported_split_debuginfo: Cow::Borrowed(&[
157             SplitDebuginfo::Packed,
158             SplitDebuginfo::Unpacked,
159             SplitDebuginfo::Off,
160         ]),
161
162         // This environment variable is pretty magical but is intended for
163         // producing deterministic builds. This was first discovered to be used
164         // by the `ar` tool as a way to control whether or not mtime entries in
165         // the archive headers were set to zero or not. It appears that
166         // eventually the linker got updated to do the same thing and now reads
167         // this environment variable too in recent versions.
168         //
169         // For some more info see the commentary on #47086
170         link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
171
172         ..Default::default()
173     }
174 }
175
176 fn deployment_target(var_name: &str) -> Option<(u32, u32)> {
177     let deployment_target = env::var(var_name).ok();
178     deployment_target
179         .as_ref()
180         .and_then(|s| s.split_once('.'))
181         .and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok())
182 }
183
184 fn macos_default_deployment_target(arch: Arch) -> (u32, u32) {
185     // Note: Arm64_sim is not included since macOS has no simulator.
186     if matches!(arch, Arm64 | Arm64_macabi) { (11, 0) } else { (10, 7) }
187 }
188
189 fn macos_deployment_target(arch: Arch) -> (u32, u32) {
190     deployment_target("MACOSX_DEPLOYMENT_TARGET")
191         .unwrap_or_else(|| macos_default_deployment_target(arch))
192 }
193
194 fn macos_lld_platform_version(arch: Arch) -> String {
195     let (major, minor) = macos_deployment_target(arch);
196     format!("{}.{}", major, minor)
197 }
198
199 pub fn macos_llvm_target(arch: Arch) -> String {
200     let (major, minor) = macos_deployment_target(arch);
201     format!("{}-apple-macosx{}.{}.0", arch.target_name(), major, minor)
202 }
203
204 fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]> {
205     // Apple platforms only officially support macOS as a host for any compilation.
206     //
207     // If building for macOS, we go ahead and remove any erroneous environment state
208     // that's only applicable to cross-OS compilation. Always leave anything for the
209     // host OS alone though.
210     if os == "macos" {
211         let mut env_remove = Vec::with_capacity(2);
212         // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
213         // may occur when we're linking a custom build script while targeting iOS for example.
214         if let Ok(sdkroot) = env::var("SDKROOT") {
215             if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform")
216             {
217                 env_remove.push("SDKROOT".into())
218             }
219         }
220         // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
221         // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
222         // although this is apparently ignored when using the linker at "/usr/bin/ld".
223         env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
224         env_remove.into()
225     } else {
226         // Otherwise if cross-compiling for a different OS/SDK, remove any part
227         // of the linking environment that's wrong and reversed.
228         match arch {
229             Armv7 | Armv7k | Armv7s | Arm64 | Arm64_32 | I386 | I686 | X86_64 | X86_64_sim
230             | Arm64_sim => {
231                 cvs!["MACOSX_DEPLOYMENT_TARGET"]
232             }
233             X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],
234         }
235     }
236 }
237
238 fn ios_deployment_target() -> (u32, u32) {
239     deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
240 }
241
242 pub fn ios_llvm_target(arch: Arch) -> String {
243     // Modern iOS tooling extracts information about deployment target
244     // from LC_BUILD_VERSION. This load command will only be emitted when
245     // we build with a version specific `llvm_target`, with the version
246     // set high enough. Luckily one LC_BUILD_VERSION is enough, for Xcode
247     // to pick it up (since std and core are still built with the fallback
248     // of version 7.0 and hence emit the old LC_IPHONE_MIN_VERSION).
249     let (major, minor) = ios_deployment_target();
250     format!("{}-apple-ios{}.{}.0", arch.target_name(), major, minor)
251 }
252
253 fn ios_lld_platform_version() -> String {
254     let (major, minor) = ios_deployment_target();
255     format!("{}.{}", major, minor)
256 }
257
258 pub fn ios_sim_llvm_target(arch: Arch) -> String {
259     let (major, minor) = ios_deployment_target();
260     format!("{}-apple-ios{}.{}.0-simulator", arch.target_name(), major, minor)
261 }
262
263 fn tvos_deployment_target() -> (u32, u32) {
264     deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((7, 0))
265 }
266
267 fn tvos_lld_platform_version() -> String {
268     let (major, minor) = tvos_deployment_target();
269     format!("{}.{}", major, minor)
270 }
271
272 fn watchos_deployment_target() -> (u32, u32) {
273     deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0))
274 }
275
276 fn watchos_lld_platform_version() -> String {
277     let (major, minor) = watchos_deployment_target();
278     format!("{}.{}", major, minor)
279 }
280
281 pub fn watchos_sim_llvm_target(arch: Arch) -> String {
282     let (major, minor) = watchos_deployment_target();
283     format!("{}-apple-watchos{}.{}.0-simulator", arch.target_name(), major, minor)
284 }