Skip to content

Commit 0f40f29

Browse files
committed
Fix Apple deployment version floor when linking C++
1 parent 7e58690 commit 0f40f29

File tree

2 files changed

+109
-16
lines changed

2 files changed

+109
-16
lines changed

src/lib.rs

+70-16
Original file line numberDiff line numberDiff line change
@@ -3407,6 +3407,8 @@ impl Build {
34073407
target: &str,
34083408
arch_str: Option<&str>,
34093409
) -> String {
3410+
const OLD_IOS_MINIMUM_VERSION: &str = "7.0";
3411+
34103412
fn rustc_provided_target(rustc: Option<&str>, target: &str) -> Option<String> {
34113413
let rustc = rustc?;
34123414
let output = Command::new(rustc)
@@ -3427,6 +3429,62 @@ impl Build {
34273429
}
34283430
}
34293431

3432+
let deployment_from_env = |name: &str| {
3433+
// note this isn't hit in production codepaths, its mostly just for tests which don't
3434+
// set the real env
3435+
if let Some((_, v)) = self.env.iter().find(|(k, _)| &**k == OsStr::new(name)) {
3436+
Some(v.to_str().unwrap().to_string())
3437+
} else {
3438+
env::var(name).ok()
3439+
}
3440+
};
3441+
3442+
// Determines if the acquired deployment target is too low to support modern C++ on some Apple platform.
3443+
//
3444+
// A long time ago they used libstdc++, but since macOS 10.9 and iOS 7 libc++ has been the library the SDKs provide to link against.
3445+
// If a `cc`` config wants to use C++, we round up to these versions as the baseline.
3446+
let maybe_cpp_version_baseline = |deployment_target_ver: String| -> String {
3447+
if !self.cpp {
3448+
return deployment_target_ver;
3449+
}
3450+
3451+
let mut deployment_target = deployment_target_ver
3452+
.split('.')
3453+
.map(|v| v.parse::<u32>().expect("integer version"));
3454+
3455+
match os {
3456+
AppleOs::MacOs => {
3457+
let major = deployment_target.next().unwrap_or(0);
3458+
let minor = deployment_target.next().unwrap_or(0);
3459+
3460+
// If below 10.9, we round up.
3461+
if major == 10 && minor < 9 {
3462+
println!(
3463+
"cargo-warning: macOS deployment target ({}) too low, it will be increased",
3464+
deployment_target_ver
3465+
);
3466+
return String::from("10.9");
3467+
}
3468+
}
3469+
AppleOs::Ios => {
3470+
let major = deployment_target.next().unwrap_or(0);
3471+
3472+
if major < 7 {
3473+
println!(
3474+
"cargo-warning: iOS deployment target ({}) too low, it will be increased",
3475+
deployment_target_ver
3476+
);
3477+
return String::from(OLD_IOS_MINIMUM_VERSION);
3478+
}
3479+
}
3480+
// watchOS, tvOS, and others are all new enough that libc++ is their baseline.
3481+
_ => {}
3482+
}
3483+
3484+
// If the deployment target met or exceeded the C++ baseline
3485+
deployment_target_ver
3486+
};
3487+
34303488
let rustc = self.getenv("RUSTC");
34313489
let rustc = rustc.as_deref();
34323490
// note the hardcoded minimums here are subject to change in a future compiler release,
@@ -3436,31 +3494,27 @@ impl Build {
34363494
// the ordering of env -> rustc -> old defaults is intentional for performance when using
34373495
// an explicit target
34383496
match os {
3439-
AppleOs::MacOs => env::var("MACOSX_DEPLOYMENT_TARGET")
3440-
.ok()
3497+
AppleOs::MacOs => deployment_from_env("MACOSX_DEPLOYMENT_TARGET")
34413498
.or_else(|| rustc_provided_target(rustc, target))
3499+
.map(maybe_cpp_version_baseline)
34423500
.unwrap_or_else(|| {
34433501
if arch_str == Some("aarch64") {
3444-
"11.0"
3502+
"11.0".into()
34453503
} else {
3446-
if self.cpp {
3447-
"10.9"
3448-
} else {
3449-
"10.7"
3450-
}
3504+
maybe_cpp_version_baseline("10.7".into())
34513505
}
3452-
.into()
34533506
}),
3454-
AppleOs::Ios => env::var("IPHONEOS_DEPLOYMENT_TARGET")
3455-
.ok()
3507+
3508+
AppleOs::Ios => deployment_from_env("IPHONEOS_DEPLOYMENT_TARGET")
34563509
.or_else(|| rustc_provided_target(rustc, target))
3457-
.unwrap_or_else(|| "7.0".into()),
3458-
AppleOs::WatchOs => env::var("WATCHOS_DEPLOYMENT_TARGET")
3459-
.ok()
3510+
.map(maybe_cpp_version_baseline)
3511+
.unwrap_or_else(|| OLD_IOS_MINIMUM_VERSION.into()),
3512+
3513+
AppleOs::WatchOs => deployment_from_env("WATCHOS_DEPLOYMENT_TARGET")
34603514
.or_else(|| rustc_provided_target(rustc, target))
34613515
.unwrap_or_else(|| "5.0".into()),
3462-
AppleOs::TvOs => env::var("TVOS_DEPLOYMENT_TARGET")
3463-
.ok()
3516+
3517+
AppleOs::TvOs => deployment_from_env("TVOS_DEPLOYMENT_TARGET")
34643518
.or_else(|| rustc_provided_target(rustc, target))
34653519
.unwrap_or_else(|| "9.0".into()),
34663520
}

tests/test.rs

+39
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,45 @@ fn gnu_apple_darwin() {
508508
}
509509
}
510510

511+
#[cfg(target_os = "macos")]
512+
#[test]
513+
fn macos_cpp_minimums() {
514+
let versions = &[
515+
// Too low
516+
("10.7", "10.9"),
517+
// Minimum
518+
("10.9", "10.9"),
519+
// Higher
520+
("11.0", "11.0"),
521+
];
522+
523+
let target = "x86_64-apple-darwin";
524+
for (deployment_target, expected) in versions {
525+
let test = Test::gnu();
526+
test.gcc()
527+
.target(target)
528+
.host(target)
529+
.cpp(true)
530+
.__set_env("MACOSX_DEPLOYMENT_TARGET", deployment_target)
531+
.file("foo.c")
532+
.compile("foo");
533+
534+
test.cmd(0)
535+
.must_have(format!("-mmacosx-version-min={}", expected));
536+
}
537+
538+
let test = Test::gnu();
539+
test.gcc()
540+
.target(target)
541+
.host(target)
542+
.__set_env("MACOSX_DEPLOYMENT_TARGET", "10.7")
543+
.file("foo.c")
544+
.compile("foo");
545+
546+
// No C++ leaves it untouched
547+
test.cmd(0).must_have("-mmacosx-version-min=10.7");
548+
}
549+
511550
#[cfg(target_os = "macos")]
512551
#[test]
513552
fn clang_apple_tvos() {

0 commit comments

Comments
 (0)