Skip to content

Commit 1136a7d

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 973597d commit 1136a7d

File tree

4 files changed

+33
-59
lines changed

4 files changed

+33
-59
lines changed

src/lib.rs

+17-29
Original file line numberDiff line numberDiff line change
@@ -2182,17 +2182,14 @@ impl Build {
21822182
}
21832183
}
21842184

2185-
// Add version information to the target.
2186-
let llvm_target = if target.vendor == "apple" {
2187-
let deployment_target = self.apple_deployment_target(target);
2188-
target.versioned_llvm_target(Some(&deployment_target))
2189-
} else {
2190-
target.versioned_llvm_target(None)
2191-
};
2192-
2193-
// Pass `--target` with the LLVM target to properly
2194-
// configure Clang even when cross-compiling.
2195-
cmd.push_cc_arg(format!("--target={llvm_target}").into());
2185+
// Pass `--target` with the LLVM target to properly configure Clang even when
2186+
// cross-compiling.
2187+
//
2188+
// We intentionally don't put the deployment version in here on Apple targets,
2189+
// and instead pass that via `-mmacosx-version-min=` and similar flags, for
2190+
// better compatibility with older versions of Clang that has poor support for
2191+
// versioned target names (especially when it comes to configuration files).
2192+
cmd.push_cc_arg(format!("--target={}", target.unversioned_llvm_target).into());
21962193
}
21972194
}
21982195
ToolFamily::Msvc { clang_cl } => {
@@ -2208,8 +2205,9 @@ impl Build {
22082205
cmd.push_cc_arg("-m32".into());
22092206
cmd.push_cc_arg("-arch:IA32".into());
22102207
} else {
2211-
let llvm_target = target.versioned_llvm_target(None);
2212-
cmd.push_cc_arg(format!("--target={llvm_target}").into());
2208+
cmd.push_cc_arg(
2209+
format!("--target={}", target.unversioned_llvm_target).into(),
2210+
);
22132211
}
22142212
} else if target.full_arch == "i586" {
22152213
cmd.push_cc_arg("-arch:IA32".into());
@@ -2635,23 +2633,13 @@ impl Build {
26352633
fn apple_flags(&self, cmd: &mut Tool) -> Result<(), Error> {
26362634
let target = self.get_target()?;
26372635

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

26562644
// AppleClang sometimes requires sysroot even on macOS
26572645
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(

0 commit comments

Comments
 (0)