Skip to content

Commit b536256

Browse files
committed
refactor: Move Apple OSVersion (back) to rustc_target
Also convert OSVersion into a proper struct for better type-safety.
1 parent 7d49ae9 commit b536256

File tree

11 files changed

+126
-101
lines changed

11 files changed

+126
-101
lines changed

compiler/rustc_codegen_ssa/src/back/apple.rs

+10-81
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use std::env;
2-
use std::fmt::{Display, from_fn};
3-
use std::num::ParseIntError;
2+
use std::str::FromStr;
43

54
use rustc_middle::middle::exported_symbols::SymbolExportKind;
65
use rustc_session::Session;
76
use rustc_target::spec::Target;
7+
pub(super) use rustc_target::spec::apple::OSVersion;
88

99
use crate::errors::AppleDeploymentTarget;
1010

@@ -110,76 +110,6 @@ pub(super) fn add_data_and_relocation(
110110
Ok(())
111111
}
112112

113-
/// Deployment target or SDK version.
114-
///
115-
/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
116-
type OSVersion = (u16, u8, u8);
117-
118-
/// Parse an OS version triple (SDK version or deployment target).
119-
fn parse_version(version: &str) -> Result<OSVersion, ParseIntError> {
120-
if let Some((major, minor)) = version.split_once('.') {
121-
let major = major.parse()?;
122-
if let Some((minor, patch)) = minor.split_once('.') {
123-
Ok((major, minor.parse()?, patch.parse()?))
124-
} else {
125-
Ok((major, minor.parse()?, 0))
126-
}
127-
} else {
128-
Ok((version.parse()?, 0, 0))
129-
}
130-
}
131-
132-
pub fn pretty_version(version: OSVersion) -> impl Display {
133-
let (major, minor, patch) = version;
134-
from_fn(move |f| {
135-
write!(f, "{major}.{minor}")?;
136-
if patch != 0 {
137-
write!(f, ".{patch}")?;
138-
}
139-
Ok(())
140-
})
141-
}
142-
143-
/// Minimum operating system versions currently supported by `rustc`.
144-
fn os_minimum_deployment_target(os: &str) -> OSVersion {
145-
// When bumping a version in here, remember to update the platform-support docs too.
146-
//
147-
// NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
148-
// default deployment target, prefer:
149-
// ```
150-
// $ rustc --print deployment-target
151-
// ```
152-
match os {
153-
"macos" => (10, 12, 0),
154-
"ios" => (10, 0, 0),
155-
"tvos" => (10, 0, 0),
156-
"watchos" => (5, 0, 0),
157-
"visionos" => (1, 0, 0),
158-
_ => unreachable!("tried to get deployment target for non-Apple platform"),
159-
}
160-
}
161-
162-
/// The deployment target for the given target.
163-
///
164-
/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
165-
/// to raise the minimum OS version.
166-
///
167-
/// This matches what LLVM does, see in part:
168-
/// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
169-
fn minimum_deployment_target(target: &Target) -> OSVersion {
170-
match (&*target.os, &*target.arch, &*target.abi) {
171-
("macos", "aarch64", _) => (11, 0, 0),
172-
("ios", "aarch64", "macabi") => (14, 0, 0),
173-
("ios", "aarch64", "sim") => (14, 0, 0),
174-
("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
175-
// Mac Catalyst defaults to 13.1 in Clang.
176-
("ios", _, "macabi") => (13, 1, 0),
177-
("tvos", "aarch64", "sim") => (14, 0, 0),
178-
("watchos", "aarch64", "sim") => (7, 0, 0),
179-
(os, _, _) => os_minimum_deployment_target(os),
180-
}
181-
}
182-
183113
/// Name of the environment variable used to fetch the deployment target on the given OS.
184114
pub fn deployment_target_env_var(os: &str) -> &'static str {
185115
match os {
@@ -195,22 +125,22 @@ pub fn deployment_target_env_var(os: &str) -> &'static str {
195125
/// Get the deployment target based on the standard environment variables, or fall back to the
196126
/// minimum version supported by `rustc`.
197127
pub fn deployment_target(sess: &Session) -> OSVersion {
198-
let min = minimum_deployment_target(&sess.target);
128+
let min = OSVersion::minimum_deployment_target(&sess.target);
199129
let env_var = deployment_target_env_var(&sess.target.os);
200130

201131
if let Ok(deployment_target) = env::var(env_var) {
202-
match parse_version(&deployment_target) {
132+
match OSVersion::from_str(&deployment_target) {
203133
Ok(version) => {
204-
let os_min = os_minimum_deployment_target(&sess.target.os);
134+
let os_min = OSVersion::os_minimum_deployment_target(&sess.target.os);
205135
// It is common that the deployment target is set a bit too low, for example on
206136
// macOS Aarch64 to also target older x86_64. So we only want to warn when variable
207137
// is lower than the minimum OS supported by rustc, not when the variable is lower
208138
// than the minimum for a specific target.
209139
if version < os_min {
210140
sess.dcx().emit_warn(AppleDeploymentTarget::TooLow {
211141
env_var,
212-
version: pretty_version(version).to_string(),
213-
os_min: pretty_version(os_min).to_string(),
142+
version: version.fmt_pretty().to_string(),
143+
os_min: os_min.fmt_pretty().to_string(),
214144
});
215145
}
216146

@@ -239,17 +169,16 @@ pub(super) fn add_version_to_llvm_target(
239169
let environment = components.next();
240170
assert_eq!(components.next(), None, "too many LLVM triple components");
241171

242-
let (major, minor, patch) = deployment_target;
243-
244172
assert!(
245173
!os.contains(|c: char| c.is_ascii_digit()),
246174
"LLVM target must not already be versioned"
247175
);
248176

177+
let version = deployment_target.fmt_full();
249178
if let Some(env) = environment {
250179
// Insert version into OS, before environment
251-
format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}-{env}")
180+
format!("{arch}-{vendor}-{os}{version}-{env}")
252181
} else {
253-
format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}")
182+
format!("{arch}-{vendor}-{os}{version}")
254183
}
255184
}
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,13 @@
1-
use super::{add_version_to_llvm_target, parse_version};
1+
use super::{OSVersion, add_version_to_llvm_target};
22

33
#[test]
44
fn test_add_version_to_llvm_target() {
55
assert_eq!(
6-
add_version_to_llvm_target("aarch64-apple-macosx", (10, 14, 1)),
6+
add_version_to_llvm_target("aarch64-apple-macosx", OSVersion::new(10, 14, 1)),
77
"aarch64-apple-macosx10.14.1"
88
);
99
assert_eq!(
10-
add_version_to_llvm_target("aarch64-apple-ios-simulator", (16, 1, 0)),
10+
add_version_to_llvm_target("aarch64-apple-ios-simulator", OSVersion::new(16, 1, 0)),
1111
"aarch64-apple-ios16.1.0-simulator"
1212
);
1313
}
14-
15-
#[test]
16-
fn test_parse_version() {
17-
assert_eq!(parse_version("10"), Ok((10, 0, 0)));
18-
assert_eq!(parse_version("10.12"), Ok((10, 12, 0)));
19-
assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6)));
20-
assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99)));
21-
}

compiler/rustc_codegen_ssa/src/back/link.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -3115,8 +3115,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
31153115
_ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"),
31163116
};
31173117

3118-
let (major, minor, patch) = apple::deployment_target(sess);
3119-
let min_version = format!("{major}.{minor}.{patch}");
3118+
let min_version = apple::deployment_target(sess).fmt_full().to_string();
31203119

31213120
// The SDK version is used at runtime when compiling with a newer SDK / version of Xcode:
31223121
// - By dyld to give extra warnings and errors, see e.g.:
@@ -3185,10 +3184,10 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
31853184

31863185
// The presence of `-mmacosx-version-min` makes CC default to
31873186
// macOS, and it sets the deployment target.
3188-
let (major, minor, patch) = apple::deployment_target(sess);
3187+
let version = apple::deployment_target(sess).fmt_full();
31893188
// Intentionally pass this as a single argument, Clang doesn't
31903189
// seem to like it otherwise.
3191-
cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}"));
3190+
cmd.cc_arg(&format!("-mmacosx-version-min={version}"));
31923191

31933192
// macOS has no environment, so with these two, we've told CC the
31943193
// four desired parameters.

compiler/rustc_codegen_ssa/src/back/metadata.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 {
388388
fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion {
389389
/// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz"
390390
/// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200
391-
fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 {
391+
fn pack_version(apple::OSVersion { major, minor, patch }: apple::OSVersion) -> u32 {
392392
let (major, minor, patch) = (major as u32, minor as u32, patch as u32);
393393
(major << 16) | (minor << 8) | patch
394394
}

compiler/rustc_codegen_ssa/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#![doc(rust_logo)]
88
#![feature(assert_matches)]
99
#![feature(box_patterns)]
10-
#![feature(debug_closure_helpers)]
1110
#![feature(file_buffered)]
1211
#![feature(if_let_guard)]
1312
#![feature(let_chains)]

compiler/rustc_driver_impl/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ fn print_crate_info(
783783
println_info!(
784784
"{}={}",
785785
apple::deployment_target_env_var(&sess.target.os),
786-
apple::pretty_version(apple::deployment_target(sess)),
786+
apple::deployment_target(sess).fmt_pretty(),
787787
)
788788
} else {
789789
#[allow(rustc::diagnostic_outside_of_impl)]

compiler/rustc_target/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
1313
#![doc(rust_logo)]
1414
#![feature(assert_matches)]
15+
#![feature(debug_closure_helpers)]
1516
#![feature(iter_intersperse)]
1617
#![feature(let_chains)]
1718
#![feature(rustc_attrs)]

compiler/rustc_target/src/spec/base/apple/mod.rs

+96-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use std::borrow::Cow;
22
use std::env;
3+
use std::fmt::{Display, from_fn};
4+
use std::num::ParseIntError;
5+
use std::str::FromStr;
36

47
use crate::spec::{
58
BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi,
6-
SplitDebuginfo, StackProbeType, StaticCow, TargetOptions, cvs,
9+
SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs,
710
};
811

912
#[cfg(test)]
@@ -222,3 +225,95 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
222225
cvs!["MACOSX_DEPLOYMENT_TARGET"]
223226
}
224227
}
228+
229+
/// Deployment target or SDK version.
230+
///
231+
/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
232+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
233+
pub struct OSVersion {
234+
pub major: u16,
235+
pub minor: u8,
236+
pub patch: u8,
237+
}
238+
239+
impl FromStr for OSVersion {
240+
type Err = ParseIntError;
241+
242+
/// Parse an OS version triple (SDK version or deployment target).
243+
fn from_str(version: &str) -> Result<Self, ParseIntError> {
244+
if let Some((major, minor)) = version.split_once('.') {
245+
let major = major.parse()?;
246+
if let Some((minor, patch)) = minor.split_once('.') {
247+
Ok(Self { major, minor: minor.parse()?, patch: patch.parse()? })
248+
} else {
249+
Ok(Self { major, minor: minor.parse()?, patch: 0 })
250+
}
251+
} else {
252+
Ok(Self { major: version.parse()?, minor: 0, patch: 0 })
253+
}
254+
}
255+
}
256+
257+
impl OSVersion {
258+
pub fn new(major: u16, minor: u8, patch: u8) -> Self {
259+
Self { major, minor, patch }
260+
}
261+
262+
pub fn fmt_pretty(self) -> impl Display {
263+
let Self { major, minor, patch } = self;
264+
from_fn(move |f| {
265+
write!(f, "{major}.{minor}")?;
266+
if patch != 0 {
267+
write!(f, ".{patch}")?;
268+
}
269+
Ok(())
270+
})
271+
}
272+
273+
pub fn fmt_full(self) -> impl Display {
274+
let Self { major, minor, patch } = self;
275+
from_fn(move |f| write!(f, "{major}.{minor}.{patch}"))
276+
}
277+
278+
/// Minimum operating system versions currently supported by `rustc`.
279+
pub fn os_minimum_deployment_target(os: &str) -> Self {
280+
// When bumping a version in here, remember to update the platform-support docs too.
281+
//
282+
// NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
283+
// default deployment target, prefer:
284+
// ```
285+
// $ rustc --print deployment-target
286+
// ```
287+
let (major, minor, patch) = match os {
288+
"macos" => (10, 12, 0),
289+
"ios" => (10, 0, 0),
290+
"tvos" => (10, 0, 0),
291+
"watchos" => (5, 0, 0),
292+
"visionos" => (1, 0, 0),
293+
_ => unreachable!("tried to get deployment target for non-Apple platform"),
294+
};
295+
Self { major, minor, patch }
296+
}
297+
298+
/// The deployment target for the given target.
299+
///
300+
/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
301+
/// to raise the minimum OS version.
302+
///
303+
/// This matches what LLVM does, see in part:
304+
/// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
305+
pub fn minimum_deployment_target(target: &Target) -> Self {
306+
let (major, minor, patch) = match (&*target.os, &*target.arch, &*target.abi) {
307+
("macos", "aarch64", _) => (11, 0, 0),
308+
("ios", "aarch64", "macabi") => (14, 0, 0),
309+
("ios", "aarch64", "sim") => (14, 0, 0),
310+
("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
311+
// Mac Catalyst defaults to 13.1 in Clang.
312+
("ios", _, "macabi") => (13, 1, 0),
313+
("tvos", "aarch64", "sim") => (14, 0, 0),
314+
("watchos", "aarch64", "sim") => (7, 0, 0),
315+
(os, _, _) => return Self::os_minimum_deployment_target(os),
316+
};
317+
Self { major, minor, patch }
318+
}
319+
}

compiler/rustc_target/src/spec/base/apple/tests.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::OSVersion;
12
use crate::spec::targets::{
23
aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_visionos_sim,
34
aarch64_apple_watchos_sim, i686_apple_darwin, x86_64_apple_darwin, x86_64_apple_ios,
@@ -42,3 +43,11 @@ fn macos_link_environment_unmodified() {
4243
);
4344
}
4445
}
46+
47+
#[test]
48+
fn test_parse_version() {
49+
assert_eq!("10".parse(), Ok(OSVersion::new(10, 0, 0)));
50+
assert_eq!("10.12".parse(), Ok(OSVersion::new(10, 12, 0)));
51+
assert_eq!("10.12.6".parse(), Ok(OSVersion::new(10, 12, 6)));
52+
assert_eq!("9999.99.99".parse(), Ok(OSVersion::new(9999, 99, 99)));
53+
}

compiler/rustc_target/src/spec/base/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
pub(crate) mod aix;
22
pub(crate) mod android;
3-
pub(crate) mod apple;
3+
pub mod apple;
44
pub(crate) mod avr;
55
pub(crate) mod bpf;
66
pub(crate) mod cygwin;

compiler/rustc_target/src/spec/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub mod crt_objects;
6060
mod base;
6161
mod json;
6262

63+
pub use base::apple;
6364
pub use base::avr::ef_avr_arch;
6465

6566
/// Linker is called through a C/C++ compiler.

0 commit comments

Comments
 (0)