From efaa2d4f33aa5a52f8a5c142306c6bd8b3568832 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 10 Oct 2024 00:29:13 +0200 Subject: [PATCH] Convert OSVersion to a struct that implements traits Instead of being a type-alias to a tuple. This allows us to implement Deserialize for OSVersion, which will be useful when parsing SDK settings. --- Cargo.lock | 1 + compiler/rustc_codegen_ssa/src/apple.rs | 25 ++---- compiler/rustc_codegen_ssa/src/apple/tests.rs | 9 +- compiler/rustc_codegen_ssa/src/back/link.rs | 10 +-- .../rustc_codegen_ssa/src/back/metadata.rs | 5 +- compiler/rustc_codegen_ssa/src/lib.rs | 1 - compiler/rustc_driver_impl/src/lib.rs | 4 +- compiler/rustc_target/Cargo.toml | 1 + compiler/rustc_target/src/lib.rs | 1 + .../rustc_target/src/spec/base/apple/mod.rs | 89 +++++++++++++++---- .../rustc_target/src/spec/base/apple/tests.rs | 18 ++-- compiler/rustc_target/src/spec/mod.rs | 3 +- 12 files changed, 113 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 502920350d5c5..4648774b8b13f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4450,6 +4450,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", + "serde", "serde_json", "tracing", ] diff --git a/compiler/rustc_codegen_ssa/src/apple.rs b/compiler/rustc_codegen_ssa/src/apple.rs index c34a912366b75..33450bec328c8 100644 --- a/compiler/rustc_codegen_ssa/src/apple.rs +++ b/compiler/rustc_codegen_ssa/src/apple.rs @@ -1,14 +1,12 @@ use std::borrow::Cow; -use std::env; -use std::fs; use std::io::ErrorKind; -use std::fmt::{Display, from_fn}; use std::path::{Path, PathBuf}; +use std::{env, fs}; use rustc_session::Session; use rustc_target::spec::{ AppleOSVersion, apple_deployment_target_env_var, apple_minimum_deployment_target, - apple_os_minimum_deployment_target, apple_parse_version, + apple_os_minimum_deployment_target, }; use crate::errors::{AppleDeploymentTarget, AppleSdkError}; @@ -16,17 +14,6 @@ use crate::errors::{AppleDeploymentTarget, AppleSdkError}; #[cfg(test)] mod tests; -pub fn pretty_version(version: AppleOSVersion) -> impl Display { - let (major, minor, patch) = version; - from_fn(move |f| { - write!(f, "{major}.{minor}")?; - if patch != 0 { - write!(f, ".{patch}")?; - } - Ok(()) - }) -} - /// Get the deployment target based on the standard environment variables, or fall back to the /// minimum version supported by `rustc`. pub fn deployment_target(sess: &Session) -> AppleOSVersion { @@ -35,7 +22,7 @@ pub fn deployment_target(sess: &Session) -> AppleOSVersion { let env_var = apple_deployment_target_env_var(&sess.target.os); if let Ok(deployment_target) = env::var(env_var) { - match apple_parse_version(&deployment_target) { + match deployment_target.parse::() { Ok(version) => { // 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 @@ -44,8 +31,8 @@ pub fn deployment_target(sess: &Session) -> AppleOSVersion { if version < os_min { sess.dcx().emit_warn(AppleDeploymentTarget::TooLow { env_var, - version: pretty_version(version).to_string(), - os_min: pretty_version(os_min).to_string(), + version: version.pretty().to_string(), + os_min: os_min.pretty().to_string(), }); } @@ -71,7 +58,7 @@ fn add_version_to_llvm_target(llvm_target: &str, deployment_target: AppleOSVersi let environment = components.next(); assert_eq!(components.next(), None, "too many LLVM triple components"); - let (major, minor, patch) = deployment_target; + let AppleOSVersion { major, minor, patch } = deployment_target; assert!( !os.contains(|c: char| c.is_ascii_digit()), diff --git a/compiler/rustc_codegen_ssa/src/apple/tests.rs b/compiler/rustc_codegen_ssa/src/apple/tests.rs index cac186c4d2f1c..eeaf8776022ca 100644 --- a/compiler/rustc_codegen_ssa/src/apple/tests.rs +++ b/compiler/rustc_codegen_ssa/src/apple/tests.rs @@ -2,16 +2,21 @@ use std::io; use std::path::PathBuf; use std::process::Command; +use rustc_target::spec::AppleOSVersion; + use super::{add_version_to_llvm_target, find_sdk_root}; #[test] fn test_add_version_to_llvm_target() { + let version = AppleOSVersion { major: 10, minor: 14, patch: 1 }; assert_eq!( - add_version_to_llvm_target("aarch64-apple-macosx", (10, 14, 1)), + add_version_to_llvm_target("aarch64-apple-macosx", version), "aarch64-apple-macosx10.14.1" ); + + let version = AppleOSVersion { major: 16, minor: 1, patch: 0 }; assert_eq!( - add_version_to_llvm_target("aarch64-apple-ios-simulator", (16, 1, 0)), + add_version_to_llvm_target("aarch64-apple-ios-simulator", version), "aarch64-apple-ios16.1.0-simulator" ); } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index d218e78b31f1e..6f2edfc00e746 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -38,9 +38,9 @@ use rustc_session::{Session, filesearch}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ - Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures, - LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, - SplitDebuginfo, + AppleOSVersion, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, + LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, + SanitizerSet, SplitDebuginfo, }; use tempfile::Builder as TempFileBuilder; use tracing::{debug, info, warn}; @@ -3038,7 +3038,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}"), }; - let (major, minor, patch) = deployment_target(sess); + let AppleOSVersion { major, minor, patch } = deployment_target(sess); let min_version = format!("{major}.{minor}.{patch}"); // The SDK version is used at runtime when compiling with a newer SDK / version of Xcode: @@ -3108,7 +3108,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo // The presence of `-mmacosx-version-min` makes CC default to // macOS, and it sets the deployment target. - let (major, minor, patch) = deployment_target(sess); + let AppleOSVersion { major, minor, patch } = deployment_target(sess); // Intentionally pass this as a single argument, Clang doesn't // seem to like it otherwise. cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}")); diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 4cfda916ec24f..2fccf4f2c15a2 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -392,9 +392,12 @@ pub(crate) fn create_object_file(sess: &Session) -> Option object::write::MachOBuildVersion { + use rustc_target::spec::AppleOSVersion; + /// 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 - fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 { + fn pack_version(version: AppleOSVersion) -> u32 { + let AppleOSVersion { major, minor, patch } = version; let (major, minor, patch) = (major as u32, minor as u32, patch as u32); (major << 16) | (minor << 8) | patch } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 5310c2295ed0f..aeae825f30508 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -6,7 +6,6 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] -#![feature(debug_closure_helpers)] #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(let_chains)] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index d99633e1baa37..18422cb3a71f9 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -855,10 +855,10 @@ fn print_crate_info( } } DeploymentTarget => { - use rustc_codegen_ssa::apple::{deployment_target, pretty_version}; + use rustc_codegen_ssa::apple::deployment_target; if sess.target.is_like_osx { - println_info!("deployment_target={}", pretty_version(deployment_target(sess))) + println_info!("deployment_target={}", deployment_target(sess).pretty()) } else { #[allow(rustc::diagnostic_outside_of_impl)] sess.dcx().fatal("only Apple targets currently support deployment version info") diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml index c7d24154e8bd4..9f6556b58cd82 100644 --- a/compiler/rustc_target/Cargo.toml +++ b/compiler/rustc_target/Cargo.toml @@ -14,6 +14,7 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } +serde = "1" serde_json = "1.0.59" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 2121c4110dde6..d282cc183442e 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -12,6 +12,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] +#![feature(debug_closure_helpers)] #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 7eb5d626e34f1..8d408573fa289 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -1,6 +1,9 @@ use std::borrow::Cow; -use std::env; use std::num::ParseIntError; +use std::str::FromStr; +use std::{env, fmt}; + +use serde::de; use crate::spec::{ Cc, DebuginfoKind, FramePointer, LinkerFlavor, Lld, SplitDebuginfo, StackProbeType, StaticCow, @@ -201,14 +204,15 @@ pub fn os_minimum_deployment_target(os: &str) -> OSVersion { // ``` // $ rustc --print deployment-target // ``` - match os { + 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"), - } + }; + OSVersion { major, minor, patch } } /// The deployment target for the given target. @@ -219,7 +223,7 @@ pub fn os_minimum_deployment_target(os: &str) -> OSVersion { /// This matches what LLVM does, see in part: /// pub fn minimum_deployment_target(target: &Target) -> OSVersion { - match (&*target.os, &*target.arch, &*target.abi) { + 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), @@ -228,8 +232,9 @@ pub fn minimum_deployment_target(target: &Target) -> OSVersion { ("ios", _, "macabi") => (13, 1, 0), ("tvos", "aarch64", "sim") => (14, 0, 0), ("watchos", "aarch64", "sim") => (7, 0, 0), - (os, _, _) => os_minimum_deployment_target(os), - } + (os, _, _) => return os_minimum_deployment_target(os), + }; + OSVersion { major, minor, patch } } /// Name of the environment variable used to fetch the deployment target on the given OS. @@ -307,18 +312,68 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow]> { /// Deployment target or SDK version. /// /// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`. -pub type OSVersion = (u16, u8, u8); - -/// Parse an OS version triple (SDK version or deployment target). -pub fn parse_version(version: &str) -> Result { - 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()?)) +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct OSVersion { + pub major: u16, + pub minor: u8, + pub patch: u8, +} + +impl OSVersion { + pub const MIN: Self = Self { major: u16::MIN, minor: u8::MIN, patch: u8::MIN }; + + pub const MAX: Self = Self { major: u16::MAX, minor: u8::MAX, patch: u8::MAX }; + + pub fn pretty(self) -> impl fmt::Display { + let OSVersion { major, minor, patch } = self; + fmt::from_fn(move |f| { + write!(f, "{major}.{minor}")?; + if patch != 0 { + write!(f, ".{patch}")?; + } + Ok(()) + }) + } +} + +impl<'de> de::Deserialize<'de> for OSVersion { + fn deserialize>(deserializer: D) -> Result { + struct OSVersionVisitor; + + impl<'de> de::Visitor<'de> for OSVersionVisitor { + type Value = OSVersion; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("a valid `major.minor.patch` version") + } + + fn visit_str(self, value: &str) -> Result { + OSVersion::from_str(value).map_err(E::custom) + } + } + + deserializer.deserialize_str(OSVersionVisitor) + } +} + +impl FromStr for OSVersion { + type Err = ParseIntError; + + /// Parse an OS version triple (SDK version or deployment target). + fn from_str(version: &str) -> Result { + if let Some((major, minor)) = version.split_once('.') { + let major = major.parse()?; + if let Some((minor, patch)) = minor.split_once('.') { + let minor = minor.parse()?; + let patch = patch.parse()?; + Ok(Self { major, minor, patch }) + } else { + let minor = minor.parse()?; + Ok(Self { major, minor, patch: 0 }) + } } else { - Ok((major, minor.parse()?, 0)) + let major = version.parse()?; + Ok(Self { major, minor: 0, patch: 0 }) } - } else { - Ok((version.parse()?, 0, 0)) } } diff --git a/compiler/rustc_target/src/spec/base/apple/tests.rs b/compiler/rustc_target/src/spec/base/apple/tests.rs index 9435b9a5bad59..b23c04870e1ef 100644 --- a/compiler/rustc_target/src/spec/base/apple/tests.rs +++ b/compiler/rustc_target/src/spec/base/apple/tests.rs @@ -1,4 +1,6 @@ -use super::parse_version; +use std::str::FromStr; + +use super::OSVersion; use crate::spec::targets::{ 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, @@ -43,8 +45,14 @@ fn macos_link_environment_unmodified() { #[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))); + let cases = [ + ("10", 10, 0, 0), + ("10.12", 10, 12, 0), + ("10.12.6", 10, 12, 6), + ("9999.99.99", 9999, 99, 99), + ]; + for (str, major, minor, patch) in cases { + let expected = OSVersion { major, minor, patch }; + assert_eq!(Ok(expected), OSVersion::from_str(str)); + } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 93a7555c66554..b6dee7055f190 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -63,8 +63,7 @@ pub use base::apple::{ OSVersion as AppleOSVersion, deployment_target_env_var as apple_deployment_target_env_var, minimum_deployment_target as apple_minimum_deployment_target, os_minimum_deployment_target as apple_os_minimum_deployment_target, - parse_version as apple_parse_version, platform as current_apple_platform, - sdk_name as apple_sdk_name, + platform as current_apple_platform, sdk_name as apple_sdk_name, }; pub use base::avr_gnu::ef_avr_arch;