1
Fork 0

refactor: Move Apple OSVersion (back) to rustc_target

Also convert OSVersion into a proper struct for better type-safety.
This commit is contained in:
Mads Marquart 2025-02-11 10:43:25 +01:00
parent a4166dabaa
commit d74ce25b65
11 changed files with 125 additions and 100 deletions

View file

@ -1,7 +1,6 @@
use std::env; use std::env;
use std::ffi::OsString; use std::ffi::OsString;
use std::fmt::{Display, from_fn}; use std::str::FromStr;
use std::num::ParseIntError;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
@ -9,6 +8,7 @@ use itertools::Itertools;
use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_session::Session; use rustc_session::Session;
use rustc_target::spec::Target; use rustc_target::spec::Target;
pub(super) use rustc_target::spec::apple::OSVersion;
use tracing::debug; use tracing::debug;
use crate::errors::{AppleDeploymentTarget, XcrunError, XcrunSdkPathWarning}; use crate::errors::{AppleDeploymentTarget, XcrunError, XcrunSdkPathWarning};
@ -134,76 +134,6 @@ pub(super) fn add_data_and_relocation(
Ok(()) Ok(())
} }
/// Deployment target or SDK version.
///
/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
type OSVersion = (u16, u8, u8);
/// Parse an OS version triple (SDK version or deployment target).
fn parse_version(version: &str) -> Result<OSVersion, ParseIntError> {
if let Some((major, minor)) = version.split_once('.') {
let major = major.parse()?;
if let Some((minor, patch)) = minor.split_once('.') {
Ok((major, minor.parse()?, patch.parse()?))
} else {
Ok((major, minor.parse()?, 0))
}
} else {
Ok((version.parse()?, 0, 0))
}
}
pub fn pretty_version(version: OSVersion) -> impl Display {
let (major, minor, patch) = version;
from_fn(move |f| {
write!(f, "{major}.{minor}")?;
if patch != 0 {
write!(f, ".{patch}")?;
}
Ok(())
})
}
/// Minimum operating system versions currently supported by `rustc`.
fn os_minimum_deployment_target(os: &str) -> OSVersion {
// When bumping a version in here, remember to update the platform-support docs too.
//
// NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
// default deployment target, prefer:
// ```
// $ rustc --print deployment-target
// ```
match os {
"macos" => (10, 12, 0),
"ios" => (10, 0, 0),
"tvos" => (10, 0, 0),
"watchos" => (5, 0, 0),
"visionos" => (1, 0, 0),
_ => unreachable!("tried to get deployment target for non-Apple platform"),
}
}
/// The deployment target for the given target.
///
/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
/// to raise the minimum OS version.
///
/// This matches what LLVM does, see in part:
/// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
fn minimum_deployment_target(target: &Target) -> OSVersion {
match (&*target.os, &*target.arch, &*target.abi) {
("macos", "aarch64", _) => (11, 0, 0),
("ios", "aarch64", "macabi") => (14, 0, 0),
("ios", "aarch64", "sim") => (14, 0, 0),
("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
// Mac Catalyst defaults to 13.1 in Clang.
("ios", _, "macabi") => (13, 1, 0),
("tvos", "aarch64", "sim") => (14, 0, 0),
("watchos", "aarch64", "sim") => (7, 0, 0),
(os, _, _) => os_minimum_deployment_target(os),
}
}
/// Name of the environment variable used to fetch the deployment target on the given OS. /// Name of the environment variable used to fetch the deployment target on the given OS.
pub fn deployment_target_env_var(os: &str) -> &'static str { pub fn deployment_target_env_var(os: &str) -> &'static str {
match os { match os {
@ -219,13 +149,13 @@ pub fn deployment_target_env_var(os: &str) -> &'static str {
/// Get the deployment target based on the standard environment variables, or fall back to the /// Get the deployment target based on the standard environment variables, or fall back to the
/// minimum version supported by `rustc`. /// minimum version supported by `rustc`.
pub fn deployment_target(sess: &Session) -> OSVersion { pub fn deployment_target(sess: &Session) -> OSVersion {
let min = minimum_deployment_target(&sess.target); let min = OSVersion::minimum_deployment_target(&sess.target);
let env_var = deployment_target_env_var(&sess.target.os); let env_var = deployment_target_env_var(&sess.target.os);
if let Ok(deployment_target) = env::var(env_var) { if let Ok(deployment_target) = env::var(env_var) {
match parse_version(&deployment_target) { match OSVersion::from_str(&deployment_target) {
Ok(version) => { Ok(version) => {
let os_min = os_minimum_deployment_target(&sess.target.os); let os_min = OSVersion::os_minimum_deployment_target(&sess.target.os);
// It is common that the deployment target is set a bit too low, for example on // It is common that the deployment target is set a bit too low, for example on
// macOS Aarch64 to also target older x86_64. So we only want to warn when variable // macOS Aarch64 to also target older x86_64. So we only want to warn when variable
// is lower than the minimum OS supported by rustc, not when the variable is lower // is lower than the minimum OS supported by rustc, not when the variable is lower
@ -233,8 +163,8 @@ pub fn deployment_target(sess: &Session) -> OSVersion {
if version < os_min { if version < os_min {
sess.dcx().emit_warn(AppleDeploymentTarget::TooLow { sess.dcx().emit_warn(AppleDeploymentTarget::TooLow {
env_var, env_var,
version: pretty_version(version).to_string(), version: version.fmt_pretty().to_string(),
os_min: pretty_version(os_min).to_string(), os_min: os_min.fmt_pretty().to_string(),
}); });
} }
@ -263,18 +193,17 @@ pub(super) fn add_version_to_llvm_target(
let environment = components.next(); let environment = components.next();
assert_eq!(components.next(), None, "too many LLVM triple components"); assert_eq!(components.next(), None, "too many LLVM triple components");
let (major, minor, patch) = deployment_target;
assert!( assert!(
!os.contains(|c: char| c.is_ascii_digit()), !os.contains(|c: char| c.is_ascii_digit()),
"LLVM target must not already be versioned" "LLVM target must not already be versioned"
); );
let version = deployment_target.fmt_full();
if let Some(env) = environment { if let Some(env) = environment {
// Insert version into OS, before environment // Insert version into OS, before environment
format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}-{env}") format!("{arch}-{vendor}-{os}{version}-{env}")
} else { } else {
format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}") format!("{arch}-{vendor}-{os}{version}")
} }
} }

View file

@ -3,23 +3,15 @@ use super::*;
#[test] #[test]
fn test_add_version_to_llvm_target() { fn test_add_version_to_llvm_target() {
assert_eq!( assert_eq!(
add_version_to_llvm_target("aarch64-apple-macosx", (10, 14, 1)), add_version_to_llvm_target("aarch64-apple-macosx", OSVersion::new(10, 14, 1)),
"aarch64-apple-macosx10.14.1" "aarch64-apple-macosx10.14.1"
); );
assert_eq!( assert_eq!(
add_version_to_llvm_target("aarch64-apple-ios-simulator", (16, 1, 0)), add_version_to_llvm_target("aarch64-apple-ios-simulator", OSVersion::new(16, 1, 0)),
"aarch64-apple-ios16.1.0-simulator" "aarch64-apple-ios16.1.0-simulator"
); );
} }
#[test]
fn test_parse_version() {
assert_eq!(parse_version("10"), Ok((10, 0, 0)));
assert_eq!(parse_version("10.12"), Ok((10, 12, 0)));
assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6)));
assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99)));
}
#[test] #[test]
#[cfg_attr(not(target_os = "macos"), ignore = "xcode-select is only available on macOS")] #[cfg_attr(not(target_os = "macos"), ignore = "xcode-select is only available on macOS")]
fn lookup_developer_dir() { fn lookup_developer_dir() {

View file

@ -3115,8 +3115,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
_ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"), _ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"),
}; };
let (major, minor, patch) = apple::deployment_target(sess); let min_version = apple::deployment_target(sess).fmt_full().to_string();
let min_version = format!("{major}.{minor}.{patch}");
// The SDK version is used at runtime when compiling with a newer SDK / version of Xcode: // The SDK version is used at runtime when compiling with a newer SDK / version of Xcode:
// - By dyld to give extra warnings and errors, see e.g.: // - 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
// The presence of `-mmacosx-version-min` makes CC default to // The presence of `-mmacosx-version-min` makes CC default to
// macOS, and it sets the deployment target. // macOS, and it sets the deployment target.
let (major, minor, patch) = apple::deployment_target(sess); let version = apple::deployment_target(sess).fmt_full();
// Intentionally pass this as a single argument, Clang doesn't // Intentionally pass this as a single argument, Clang doesn't
// seem to like it otherwise. // seem to like it otherwise.
cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}")); cmd.cc_arg(&format!("-mmacosx-version-min={version}"));
// macOS has no environment, so with these two, we've told CC the // macOS has no environment, so with these two, we've told CC the
// four desired parameters. // four desired parameters.

View file

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

View file

@ -7,7 +7,6 @@
#![doc(rust_logo)] #![doc(rust_logo)]
#![feature(assert_matches)] #![feature(assert_matches)]
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(debug_closure_helpers)]
#![feature(file_buffered)] #![feature(file_buffered)]
#![feature(if_let_guard)] #![feature(if_let_guard)]
#![feature(let_chains)] #![feature(let_chains)]

View file

@ -811,7 +811,7 @@ fn print_crate_info(
println_info!( println_info!(
"{}={}", "{}={}",
apple::deployment_target_env_var(&sess.target.os), apple::deployment_target_env_var(&sess.target.os),
apple::pretty_version(apple::deployment_target(sess)), apple::deployment_target(sess).fmt_pretty(),
) )
} else { } else {
#[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::diagnostic_outside_of_impl)]

View file

@ -12,6 +12,7 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)] #![doc(rust_logo)]
#![feature(assert_matches)] #![feature(assert_matches)]
#![feature(debug_closure_helpers)]
#![feature(iter_intersperse)] #![feature(iter_intersperse)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(rustc_attrs)] #![feature(rustc_attrs)]

View file

@ -1,9 +1,12 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::env; use std::env;
use std::fmt::{Display, from_fn};
use std::num::ParseIntError;
use std::str::FromStr;
use crate::spec::{ use crate::spec::{
BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi, BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi,
SplitDebuginfo, StackProbeType, StaticCow, TargetOptions, cvs, SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs,
}; };
#[cfg(test)] #[cfg(test)]
@ -222,3 +225,95 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
cvs!["MACOSX_DEPLOYMENT_TARGET"] cvs!["MACOSX_DEPLOYMENT_TARGET"]
} }
} }
/// Deployment target or SDK version.
///
/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct OSVersion {
pub major: u16,
pub minor: u8,
pub patch: u8,
}
impl FromStr for OSVersion {
type Err = ParseIntError;
/// Parse an OS version triple (SDK version or deployment target).
fn from_str(version: &str) -> Result<Self, ParseIntError> {
if let Some((major, minor)) = version.split_once('.') {
let major = major.parse()?;
if let Some((minor, patch)) = minor.split_once('.') {
Ok(Self { major, minor: minor.parse()?, patch: patch.parse()? })
} else {
Ok(Self { major, minor: minor.parse()?, patch: 0 })
}
} else {
Ok(Self { major: version.parse()?, minor: 0, patch: 0 })
}
}
}
impl OSVersion {
pub fn new(major: u16, minor: u8, patch: u8) -> Self {
Self { major, minor, patch }
}
pub fn fmt_pretty(self) -> impl Display {
let Self { major, minor, patch } = self;
from_fn(move |f| {
write!(f, "{major}.{minor}")?;
if patch != 0 {
write!(f, ".{patch}")?;
}
Ok(())
})
}
pub fn fmt_full(self) -> impl Display {
let Self { major, minor, patch } = self;
from_fn(move |f| write!(f, "{major}.{minor}.{patch}"))
}
/// Minimum operating system versions currently supported by `rustc`.
pub fn os_minimum_deployment_target(os: &str) -> Self {
// When bumping a version in here, remember to update the platform-support docs too.
//
// NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
// default deployment target, prefer:
// ```
// $ rustc --print deployment-target
// ```
let (major, minor, patch) = match os {
"macos" => (10, 12, 0),
"ios" => (10, 0, 0),
"tvos" => (10, 0, 0),
"watchos" => (5, 0, 0),
"visionos" => (1, 0, 0),
_ => unreachable!("tried to get deployment target for non-Apple platform"),
};
Self { major, minor, patch }
}
/// The deployment target for the given target.
///
/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
/// to raise the minimum OS version.
///
/// This matches what LLVM does, see in part:
/// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
pub fn minimum_deployment_target(target: &Target) -> Self {
let (major, minor, patch) = match (&*target.os, &*target.arch, &*target.abi) {
("macos", "aarch64", _) => (11, 0, 0),
("ios", "aarch64", "macabi") => (14, 0, 0),
("ios", "aarch64", "sim") => (14, 0, 0),
("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
// Mac Catalyst defaults to 13.1 in Clang.
("ios", _, "macabi") => (13, 1, 0),
("tvos", "aarch64", "sim") => (14, 0, 0),
("watchos", "aarch64", "sim") => (7, 0, 0),
(os, _, _) => return Self::os_minimum_deployment_target(os),
};
Self { major, minor, patch }
}
}

View file

@ -1,3 +1,4 @@
use super::OSVersion;
use crate::spec::targets::{ use crate::spec::targets::{
aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_visionos_sim, aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_visionos_sim,
aarch64_apple_watchos_sim, i686_apple_darwin, x86_64_apple_darwin, x86_64_apple_ios, aarch64_apple_watchos_sim, i686_apple_darwin, x86_64_apple_darwin, x86_64_apple_ios,
@ -42,3 +43,11 @@ fn macos_link_environment_unmodified() {
); );
} }
} }
#[test]
fn test_parse_version() {
assert_eq!("10".parse(), Ok(OSVersion::new(10, 0, 0)));
assert_eq!("10.12".parse(), Ok(OSVersion::new(10, 12, 0)));
assert_eq!("10.12.6".parse(), Ok(OSVersion::new(10, 12, 6)));
assert_eq!("9999.99.99".parse(), Ok(OSVersion::new(9999, 99, 99)));
}

View file

@ -1,6 +1,6 @@
pub(crate) mod aix; pub(crate) mod aix;
pub(crate) mod android; pub(crate) mod android;
pub(crate) mod apple; pub mod apple;
pub(crate) mod avr; pub(crate) mod avr;
pub(crate) mod bpf; pub(crate) mod bpf;
pub(crate) mod cygwin; pub(crate) mod cygwin;

View file

@ -60,6 +60,7 @@ pub mod crt_objects;
mod base; mod base;
mod json; mod json;
pub use base::apple;
pub use base::avr::ef_avr_arch; pub use base::avr::ef_avr_arch;
/// Linker is called through a C/C++ compiler. /// Linker is called through a C/C++ compiler.