Skip to content

Commit b6539dd

Browse files
committed
Pass deployment target with -m*-version-min=
Clang's supports many ways of passing the deployment target, and prefers that you use the `-target` flag for doing so. This works poorly work with configuration files, which Homebrew's LLVM uses, so we use the `-mmacosx-version-min=`, `-miphoneos-version-min=`, `-mtargetos=` etc. flags to pass the deployment target instead.
1 parent bf4dcf7 commit b6539dd

File tree

5 files changed

+42
-65
lines changed

5 files changed

+42
-65
lines changed

src/lib.rs

+17-29
Original file line numberDiff line numberDiff line change
@@ -2188,17 +2188,14 @@ impl Build {
21882188
}
21892189
}
21902190

2191-
// Add version information to the target.
2192-
let llvm_target = if target.vendor == "apple" {
2193-
let deployment_target = self.apple_deployment_target(target);
2194-
target.versioned_llvm_target(Some(&deployment_target))
2195-
} else {
2196-
target.versioned_llvm_target(None)
2197-
};
2198-
2199-
// Pass `--target` with the LLVM target to properly
2200-
// configure Clang even when cross-compiling.
2201-
cmd.push_cc_arg(format!("--target={llvm_target}").into());
2191+
// Pass `--target` with the LLVM target to properly configure Clang even when
2192+
// cross-compiling.
2193+
//
2194+
// We intentionally don't put the deployment version in here on Apple targets,
2195+
// and instead pass that via `-mmacosx-version-min=` and similar flags, for
2196+
// better compatibility with older versions of Clang that has poor support for
2197+
// versioned target names (especially when it comes to configuration files).
2198+
cmd.push_cc_arg(format!("--target={}", target.unversioned_llvm_target).into());
22022199
}
22032200
}
22042201
ToolFamily::Msvc { clang_cl } => {
@@ -2214,8 +2211,9 @@ impl Build {
22142211
cmd.push_cc_arg("-m32".into());
22152212
cmd.push_cc_arg("-arch:IA32".into());
22162213
} else {
2217-
let llvm_target = target.versioned_llvm_target(None);
2218-
cmd.push_cc_arg(format!("--target={llvm_target}").into());
2214+
cmd.push_cc_arg(
2215+
format!("--target={}", target.unversioned_llvm_target).into(),
2216+
);
22192217
}
22202218
} else if target.full_arch == "i586" {
22212219
cmd.push_cc_arg("-arch:IA32".into());
@@ -2645,23 +2643,13 @@ impl Build {
26452643
fn apple_flags(&self, cmd: &mut Tool) -> Result<(), Error> {
26462644
let target = self.get_target()?;
26472645

2648-
// If the compiler is Clang, then we've already specifed the target
2649-
// information (including the deployment target) with the `--target`
2650-
// option, so we don't need to do anything further here.
2646+
// Pass the deployment target via `-mmacosx-version-min=`, `-mtargetos=` and similar.
26512647
//
2652-
// If the compiler is GCC, then we need to specify
2653-
// `-mmacosx-version-min` to set the deployment target, as well
2654-
// as to say that the target OS is macOS.
2655-
//
2656-
// NOTE: GCC does not support `-miphoneos-version-min=` etc. (because
2657-
// it does not support iOS in general), but we specify them anyhow in
2658-
// case we actually have a Clang-like compiler disguised as a GNU-like
2659-
// compiler, or in case GCC adds support for these in the future.
2660-
if !cmd.is_like_clang() {
2661-
let min_version = self.apple_deployment_target(&target);
2662-
cmd.args
2663-
.push(target.apple_version_flag(&min_version).into());
2664-
}
2648+
// It is also necessary on GCC, as it forces a compilation error if the compiler is not
2649+
// configured for Darwin: https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html
2650+
let min_version = self.apple_deployment_target(&target);
2651+
cmd.args
2652+
.push(target.apple_version_flag(&min_version).into());
26652653

26662654
// AppleClang sometimes requires sysroot even on macOS
26672655
if cmd.is_xctoolchain_clang() || target.os != "macos" {

src/target.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ pub(crate) struct TargetInfo<'a> {
4444
/// This is the same as the value of `cfg!(target_abi)`.
4545
pub abi: &'a str,
4646
/// The unversioned LLVM/Clang target triple.
47-
unversioned_llvm_target: &'a str,
47+
///
48+
/// NOTE: You should never need to match on this explicitly, use the other
49+
/// fields on [`TargetInfo`] instead.
50+
pub unversioned_llvm_target: &'a str,
4851
}
4952

5053
impl FromStr for TargetInfo<'_> {

src/target/apple.rs

+12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ impl TargetInfo<'_> {
1818
}
1919

2020
pub(crate) fn apple_version_flag(&self, min_version: &str) -> String {
21+
// There are many aliases for these, and `-mtargetos=` is preferred on Clang nowadays, but
22+
// for compatibility with older Clang, we use the earliest supported name here.
23+
//
24+
// NOTE: GCC does not support `-miphoneos-version-min=` etc. (because it does not support
25+
// iOS in general), but we specify them anyhow in case we actually have a Clang-like
26+
// compiler disguised as a GNU-like compiler, or in case GCC adds support for these in the
27+
// future.
28+
//
29+
// See also:
30+
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mmacos-version-min
31+
// https://clang.llvm.org/docs/AttributeReference.html#availability
32+
// https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html#index-mmacosx-version-min
2133
match (self.os, self.abi) {
2234
("macos", "") => format!("-mmacosx-version-min={min_version}"),
2335
("ios", "") => format!("-miphoneos-version-min={min_version}"),

src/target/llvm.rs

-29
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,3 @@
1-
use std::borrow::Cow;
2-
3-
use super::TargetInfo;
4-
5-
impl<'a> TargetInfo<'a> {
6-
/// The versioned LLVM/Clang target triple.
7-
pub(crate) fn versioned_llvm_target(&self, version: Option<&str>) -> Cow<'a, str> {
8-
if let Some(version) = version {
9-
// Only support versioned Apple targets for now.
10-
assert_eq!(self.vendor, "apple");
11-
12-
let mut components = self.unversioned_llvm_target.split("-");
13-
let arch = components.next().expect("llvm_target should have arch");
14-
let vendor = components.next().expect("llvm_target should have vendor");
15-
let os = components.next().expect("LLVM target should have os");
16-
let environment = components.next();
17-
assert_eq!(components.next(), None, "too many LLVM target components");
18-
19-
Cow::Owned(if let Some(env) = environment {
20-
format!("{arch}-{vendor}-{os}{version}-{env}")
21-
} else {
22-
format!("{arch}-{vendor}-{os}{version}")
23-
})
24-
} else {
25-
Cow::Borrowed(self.unversioned_llvm_target)
26-
}
27-
}
28-
}
29-
301
/// Rust and Clang don't really agree on naming, so do a best-effort
312
/// conversion to support out-of-tree / custom target-spec targets.
323
pub(crate) fn guess_llvm_target_triple(

tests/test.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ fn macos_cpp_minimums() {
571571
let deployment_arg = exec
572572
.args
573573
.iter()
574-
.find_map(|arg| arg.strip_prefix("--target=x86_64-apple-macosx"))
574+
.find_map(|arg| arg.strip_prefix("-mmacosx-version-min="))
575575
.expect("no deployment target argument was set");
576576

577577
let mut deployment_parts = deployment_arg.split('.').map(|v| v.parse::<u32>().unwrap());
@@ -599,7 +599,7 @@ fn macos_cpp_minimums() {
599599
.compile("foo");
600600

601601
// No C++ leaves it untouched
602-
test.cmd(0).must_have("--target=x86_64-apple-macosx10.7");
602+
test.cmd(0).must_have("-mmacosx-version-min=10.7");
603603
}
604604

605605
#[cfg(target_os = "macos")]
@@ -614,7 +614,7 @@ fn clang_apple_tvos() {
614614
.file("foo.c")
615615
.compile("foo");
616616

617-
test.cmd(0).must_have("--target=arm64-apple-tvos9.0");
617+
test.cmd(0).must_have("-mappletvos-version-min=9.0");
618618
}
619619

620620
#[cfg(target_os = "macos")]
@@ -637,7 +637,8 @@ fn clang_apple_mac_catalyst() {
637637
.compile("foo");
638638
let execution = test.cmd(0);
639639

640-
execution.must_have("--target=arm64-apple-ios15.0-macabi");
640+
execution.must_have("--target=arm64-apple-ios-macabi");
641+
execution.must_have("-mtargetos=ios15.0-macabi");
641642
execution.must_have_in_order("-isysroot", sdkroot);
642643
execution.must_have_in_order(
643644
"-isystem",
@@ -667,7 +668,8 @@ fn clang_apple_tvsimulator() {
667668
.compile("foo");
668669

669670
test.cmd(0)
670-
.must_have("--target=x86_64-apple-tvos9.0-simulator");
671+
.must_have("--target=x86_64-apple-tvos-simulator");
672+
test.cmd(0).must_have("-mappletvsimulator-version-min=9.0");
671673
}
672674

673675
#[cfg(target_os = "macos")]
@@ -691,7 +693,8 @@ fn clang_apple_visionos() {
691693

692694
dbg!(test.cmd(0).args);
693695

694-
test.cmd(0).must_have("--target=arm64-apple-xros1.0");
696+
test.cmd(0).must_have("--target=arm64-apple-xros");
697+
test.cmd(0).must_have("-mtargetos=xros1.0");
695698
test.cmd(0).must_not_have("-mxros-version-min=1.0");
696699
test.cmd(0).must_not_have("-mxrsimulator-version-min=1.0");
697700
}

0 commit comments

Comments
 (0)