Skip to content

Commit f5c5102

Browse files
committed
Auto merge of rust-lang#138947 - madsmtm:refactor-apple-versions, r=Noratrieb
Refactor Apple version handling in the compiler Move various Apple version handling code in the compiler out `rustc_codegen_ssa` and into a place where it can be accessed by `rustc_attr_parsing`, which I found to be necessary when doing rust-lang#136867. Thought I'd split it out to make it easier to land, and to make further changes like rust-lang#131477 have fewer conflicts / PR dependencies. There should be no functional changes in this PR. `@rustbot` label O-apple r? rust-lang/compiler
2 parents e7df5b0 + 7e4379c commit f5c5102

File tree

17 files changed

+190
-166
lines changed

17 files changed

+190
-166
lines changed

compiler/rustc_codegen_ssa/messages.ftl

-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@ codegen_ssa_add_native_library = failed to add native library {$library_path}: {
44
55
codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to AIX which is not guaranteed to work
66
7-
codegen_ssa_apple_deployment_target_invalid =
8-
failed to parse deployment target specified in {$env_var}: {$error}
9-
10-
codegen_ssa_apple_deployment_target_too_low =
11-
deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}
12-
137
codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error}
148
159
codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering

compiler/rustc_codegen_ssa/src/back/apple.rs

+5-126
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
use std::env;
21
use std::ffi::OsString;
3-
use std::fmt::{Display, from_fn};
4-
use std::num::ParseIntError;
52
use std::path::PathBuf;
63
use std::process::Command;
74

85
use itertools::Itertools;
96
use rustc_middle::middle::exported_symbols::SymbolExportKind;
107
use rustc_session::Session;
118
use rustc_target::spec::Target;
9+
pub(super) use rustc_target::spec::apple::OSVersion;
1210
use tracing::debug;
1311

14-
use crate::errors::{AppleDeploymentTarget, XcrunError, XcrunSdkPathWarning};
12+
use crate::errors::{XcrunError, XcrunSdkPathWarning};
1513
use crate::fluent_generated as fluent;
1614

1715
#[cfg(test)]
@@ -134,124 +132,6 @@ pub(super) fn add_data_and_relocation(
134132
Ok(())
135133
}
136134

137-
/// Deployment target or SDK version.
138-
///
139-
/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`.
140-
type OSVersion = (u16, u8, u8);
141-
142-
/// Parse an OS version triple (SDK version or deployment target).
143-
fn parse_version(version: &str) -> Result<OSVersion, ParseIntError> {
144-
if let Some((major, minor)) = version.split_once('.') {
145-
let major = major.parse()?;
146-
if let Some((minor, patch)) = minor.split_once('.') {
147-
Ok((major, minor.parse()?, patch.parse()?))
148-
} else {
149-
Ok((major, minor.parse()?, 0))
150-
}
151-
} else {
152-
Ok((version.parse()?, 0, 0))
153-
}
154-
}
155-
156-
pub fn pretty_version(version: OSVersion) -> impl Display {
157-
let (major, minor, patch) = version;
158-
from_fn(move |f| {
159-
write!(f, "{major}.{minor}")?;
160-
if patch != 0 {
161-
write!(f, ".{patch}")?;
162-
}
163-
Ok(())
164-
})
165-
}
166-
167-
/// Minimum operating system versions currently supported by `rustc`.
168-
fn os_minimum_deployment_target(os: &str) -> OSVersion {
169-
// When bumping a version in here, remember to update the platform-support docs too.
170-
//
171-
// NOTE: The defaults may change in future `rustc` versions, so if you are looking for the
172-
// default deployment target, prefer:
173-
// ```
174-
// $ rustc --print deployment-target
175-
// ```
176-
match os {
177-
"macos" => (10, 12, 0),
178-
"ios" => (10, 0, 0),
179-
"tvos" => (10, 0, 0),
180-
"watchos" => (5, 0, 0),
181-
"visionos" => (1, 0, 0),
182-
_ => unreachable!("tried to get deployment target for non-Apple platform"),
183-
}
184-
}
185-
186-
/// The deployment target for the given target.
187-
///
188-
/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense
189-
/// to raise the minimum OS version.
190-
///
191-
/// This matches what LLVM does, see in part:
192-
/// <https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L1900-L1932>
193-
fn minimum_deployment_target(target: &Target) -> OSVersion {
194-
match (&*target.os, &*target.arch, &*target.abi) {
195-
("macos", "aarch64", _) => (11, 0, 0),
196-
("ios", "aarch64", "macabi") => (14, 0, 0),
197-
("ios", "aarch64", "sim") => (14, 0, 0),
198-
("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0),
199-
// Mac Catalyst defaults to 13.1 in Clang.
200-
("ios", _, "macabi") => (13, 1, 0),
201-
("tvos", "aarch64", "sim") => (14, 0, 0),
202-
("watchos", "aarch64", "sim") => (7, 0, 0),
203-
(os, _, _) => os_minimum_deployment_target(os),
204-
}
205-
}
206-
207-
/// Name of the environment variable used to fetch the deployment target on the given OS.
208-
pub fn deployment_target_env_var(os: &str) -> &'static str {
209-
match os {
210-
"macos" => "MACOSX_DEPLOYMENT_TARGET",
211-
"ios" => "IPHONEOS_DEPLOYMENT_TARGET",
212-
"watchos" => "WATCHOS_DEPLOYMENT_TARGET",
213-
"tvos" => "TVOS_DEPLOYMENT_TARGET",
214-
"visionos" => "XROS_DEPLOYMENT_TARGET",
215-
_ => unreachable!("tried to get deployment target env var for non-Apple platform"),
216-
}
217-
}
218-
219-
/// Get the deployment target based on the standard environment variables, or fall back to the
220-
/// minimum version supported by `rustc`.
221-
pub fn deployment_target(sess: &Session) -> OSVersion {
222-
let min = minimum_deployment_target(&sess.target);
223-
let env_var = deployment_target_env_var(&sess.target.os);
224-
225-
if let Ok(deployment_target) = env::var(env_var) {
226-
match parse_version(&deployment_target) {
227-
Ok(version) => {
228-
let os_min = os_minimum_deployment_target(&sess.target.os);
229-
// It is common that the deployment target is set a bit too low, for example on
230-
// macOS Aarch64 to also target older x86_64. So we only want to warn when variable
231-
// is lower than the minimum OS supported by rustc, not when the variable is lower
232-
// than the minimum for a specific target.
233-
if version < os_min {
234-
sess.dcx().emit_warn(AppleDeploymentTarget::TooLow {
235-
env_var,
236-
version: pretty_version(version).to_string(),
237-
os_min: pretty_version(os_min).to_string(),
238-
});
239-
}
240-
241-
// Raise the deployment target to the minimum supported.
242-
version.max(min)
243-
}
244-
Err(error) => {
245-
sess.dcx().emit_err(AppleDeploymentTarget::Invalid { env_var, error });
246-
min
247-
}
248-
}
249-
} else {
250-
// If no deployment target variable is set, default to the minimum found above.
251-
min
252-
}
253-
}
254-
255135
pub(super) fn add_version_to_llvm_target(
256136
llvm_target: &str,
257137
deployment_target: OSVersion,
@@ -263,18 +143,17 @@ pub(super) fn add_version_to_llvm_target(
263143
let environment = components.next();
264144
assert_eq!(components.next(), None, "too many LLVM triple components");
265145

266-
let (major, minor, patch) = deployment_target;
267-
268146
assert!(
269147
!os.contains(|c: char| c.is_ascii_digit()),
270148
"LLVM target must not already be versioned"
271149
);
272150

151+
let version = deployment_target.fmt_full();
273152
if let Some(env) = environment {
274153
// Insert version into OS, before environment
275-
format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}-{env}")
154+
format!("{arch}-{vendor}-{os}{version}-{env}")
276155
} else {
277-
format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}")
156+
format!("{arch}-{vendor}-{os}{version}")
278157
}
279158
}
280159

compiler/rustc_codegen_ssa/src/back/apple/tests.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,15 @@ use super::*;
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
}
1414

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-
}
22-
2315
#[test]
2416
#[cfg_attr(not(target_os = "macos"), ignore = "xcode-select is only available on macOS")]
2517
fn lookup_developer_dir() {

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 = sess.apple_deployment_target().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 = sess.apple_deployment_target().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

+2-2
Original file line numberDiff line numberDiff line change
@@ -388,13 +388,13 @@ 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
}
395395

396396
let platform = apple::macho_platform(&sess.target);
397-
let min_os = apple::deployment_target(sess);
397+
let min_os = sess.apple_deployment_target();
398398

399399
let mut build_version = object::write::MachOBuildVersion::default();
400400
build_version.platform = platform;

compiler/rustc_codegen_ssa/src/back/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub mod write;
2020
/// Certain optimizations also depend on the deployment target.
2121
pub fn versioned_llvm_target(sess: &Session) -> Cow<'_, str> {
2222
if sess.target.is_like_darwin {
23-
apple::add_version_to_llvm_target(&sess.target.llvm_target, apple::deployment_target(sess))
23+
apple::add_version_to_llvm_target(&sess.target.llvm_target, sess.apple_deployment_target())
2424
.into()
2525
} else {
2626
// FIXME(madsmtm): Certain other targets also include a version,

compiler/rustc_codegen_ssa/src/errors.rs

-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
use std::borrow::Cow;
44
use std::ffi::OsString;
55
use std::io::Error;
6-
use std::num::ParseIntError;
76
use std::path::{Path, PathBuf};
87
use std::process::ExitStatus;
98

@@ -738,14 +737,6 @@ pub enum ExtractBundledLibsError<'a> {
738737
ExtractSection { rlib: &'a Path, error: Box<dyn std::error::Error> },
739738
}
740739

741-
#[derive(Diagnostic)]
742-
pub(crate) enum AppleDeploymentTarget {
743-
#[diag(codegen_ssa_apple_deployment_target_invalid)]
744-
Invalid { env_var: &'static str, error: ParseIntError },
745-
#[diag(codegen_ssa_apple_deployment_target_too_low)]
746-
TooLow { env_var: &'static str, version: String, os_min: String },
747-
}
748-
749740
#[derive(Diagnostic)]
750741
#[diag(codegen_ssa_read_file)]
751742
pub(crate) struct ReadFileError {

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ use std::time::{Instant, SystemTime};
3434
use std::{env, str};
3535

3636
use rustc_ast as ast;
37-
use rustc_codegen_ssa::back::apple;
3837
use rustc_codegen_ssa::traits::CodegenBackend;
3938
use rustc_codegen_ssa::{CodegenErrors, CodegenResults};
4039
use rustc_data_structures::profiling::{
@@ -807,8 +806,8 @@ fn print_crate_info(
807806
if sess.target.is_like_darwin {
808807
println_info!(
809808
"{}={}",
810-
apple::deployment_target_env_var(&sess.target.os),
811-
apple::pretty_version(apple::deployment_target(sess)),
809+
rustc_target::spec::apple::deployment_target_env_var(&sess.target.os),
810+
sess.apple_deployment_target().fmt_pretty(),
812811
)
813812
} else {
814813
#[allow(rustc::diagnostic_outside_of_impl)]

compiler/rustc_session/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
session_apple_deployment_target_invalid =
2+
failed to parse deployment target specified in {$env_var}: {$error}
3+
4+
session_apple_deployment_target_too_low =
5+
deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}
6+
17
session_binary_float_literal_not_supported = binary float literal is not supported
28
session_branch_protection_requires_aarch64 = `-Zbranch-protection` is only supported on aarch64
39

compiler/rustc_session/src/errors.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::num::NonZero;
1+
use std::num::{NonZero, ParseIntError};
22

33
use rustc_ast::token;
44
use rustc_ast::util::literal::LitError;
@@ -14,6 +14,14 @@ use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple};
1414
use crate::config::CrateType;
1515
use crate::parse::ParseSess;
1616

17+
#[derive(Diagnostic)]
18+
pub(crate) enum AppleDeploymentTarget {
19+
#[diag(session_apple_deployment_target_invalid)]
20+
Invalid { env_var: &'static str, error: ParseIntError },
21+
#[diag(session_apple_deployment_target_too_low)]
22+
TooLow { env_var: &'static str, version: String, os_min: String },
23+
}
24+
1725
pub(crate) struct FeatureGateError {
1826
pub(crate) span: MultiSpan,
1927
pub(crate) explain: DiagMessage,

compiler/rustc_session/src/session.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use rustc_target::asm::InlineAsmArch;
2929
use rustc_target::spec::{
3030
CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
3131
SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target,
32-
TargetTuple, TlsModel,
32+
TargetTuple, TlsModel, apple,
3333
};
3434

3535
use crate::code_stats::CodeStats;
@@ -895,6 +895,45 @@ impl Session {
895895
FileNameDisplayPreference::Local
896896
}
897897
}
898+
899+
/// Get the deployment target on Apple platforms based on the standard environment variables,
900+
/// or fall back to the minimum version supported by `rustc`.
901+
///
902+
/// This should be guarded behind `if sess.target.is_like_darwin`.
903+
pub fn apple_deployment_target(&self) -> apple::OSVersion {
904+
let min = apple::OSVersion::minimum_deployment_target(&self.target);
905+
let env_var = apple::deployment_target_env_var(&self.target.os);
906+
907+
// FIXME(madsmtm): Track changes to this.
908+
if let Ok(deployment_target) = env::var(env_var) {
909+
match apple::OSVersion::from_str(&deployment_target) {
910+
Ok(version) => {
911+
let os_min = apple::OSVersion::os_minimum_deployment_target(&self.target.os);
912+
// It is common that the deployment target is set a bit too low, for example on
913+
// macOS Aarch64 to also target older x86_64. So we only want to warn when variable
914+
// is lower than the minimum OS supported by rustc, not when the variable is lower
915+
// than the minimum for a specific target.
916+
if version < os_min {
917+
self.dcx().emit_warn(errors::AppleDeploymentTarget::TooLow {
918+
env_var,
919+
version: version.fmt_pretty().to_string(),
920+
os_min: os_min.fmt_pretty().to_string(),
921+
});
922+
}
923+
924+
// Raise the deployment target to the minimum supported.
925+
version.max(min)
926+
}
927+
Err(error) => {
928+
self.dcx().emit_err(errors::AppleDeploymentTarget::Invalid { env_var, error });
929+
min
930+
}
931+
}
932+
} else {
933+
// If no deployment target variable is set, default to the minimum found above.
934+
min
935+
}
936+
}
898937
}
899938

900939
// JUSTIFICATION: part of session construction

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)]

0 commit comments

Comments
 (0)