Skip to content

Commit

Permalink
Convert OSVersion to a struct that implements traits
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
madsmtm committed Oct 9, 2024
1 parent c70b075 commit efaa2d4
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 54 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4450,6 +4450,7 @@ dependencies = [
"rustc_macros",
"rustc_serialize",
"rustc_span",
"serde",
"serde_json",
"tracing",
]
Expand Down
25 changes: 6 additions & 19 deletions compiler/rustc_codegen_ssa/src/apple.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,19 @@
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};

#[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 {
Expand All @@ -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::<AppleOSVersion>() {
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
Expand All @@ -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(),
});
}

Expand All @@ -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()),
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_codegen_ssa/src/apple/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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}"));
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_ssa/src/back/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,9 +392,12 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
/// Since Xcode 15, Apple's LD apparently requires object files to use this load command, so this
/// returns the `MachOBuildVersion` for the target to do so.
fn macho_object_build_version_for_target(sess: &Session) -> 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
}
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_driver_impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_target/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
89 changes: 72 additions & 17 deletions compiler/rustc_target/src/spec/base/apple/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -219,7 +223,7 @@ pub fn os_minimum_deployment_target(os: &str) -> OSVersion {
/// 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) -> 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),
Expand All @@ -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.
Expand Down Expand Up @@ -307,18 +312,68 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
/// 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<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()?))
#[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<D: de::Deserializer<'de>>(deserializer: D) -> Result<OSVersion, D::Error> {
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<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
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<Self, Self::Err> {
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))
}
}
18 changes: 13 additions & 5 deletions compiler/rustc_target/src/spec/base/apple/tests.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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));
}
}
3 changes: 1 addition & 2 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit efaa2d4

Please sign in to comment.