Pass Apple SDK root to compiler driver via SDKROOT env var

This is more in-line with what Apple's tooling expects, and allows us to
better support custom compiler drivers (such as certain Homebrew and
Nixpkgs compilers) that prefer their own `-isysroot` flag.

Effectively, we now invoke the compiler driver as-if it was invoked as
`xcrun -sdk $sdk_name $tool`.
This commit is contained in:
Mads Marquart
2025-08-11 22:29:00 +02:00
parent 1dc37df514
commit 1cc44bfd22
3 changed files with 39 additions and 31 deletions

View File

@@ -3208,15 +3208,37 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) ->
let sdkroot = sess.time("get_apple_sdk_root", || get_apple_sdk_root(sess))?; let sdkroot = sess.time("get_apple_sdk_root", || get_apple_sdk_root(sess))?;
if cc == Cc::Yes { if cc == Cc::Yes {
// Use `-isysroot` instead of `--sysroot`, as only the former // There are a few options to pass the SDK root when linking with a C/C++ compiler:
// makes Clang treat it as a platform SDK. // - The `--sysroot` flag.
// - The `-isysroot` flag.
// - The `SDKROOT` environment variable.
// //
// This is admittedly a bit strange, as on most targets // `--sysroot` isn't actually enough to get Clang to treat it as a platform SDK, you need
// `-isysroot` only applies to include header files, but on Apple // to specify `-isysroot`. This is admittedly a bit strange, as on most targets `-isysroot`
// targets this also applies to libraries and frameworks. // only applies to include header files, but on Apple targets it also applies to libraries
cmd.cc_arg("-isysroot"); // and frameworks.
cmd.cc_arg(&sdk_root); //
// This leaves the choice between `-isysroot` and `SDKROOT`. Both are supported by Clang and
// GCC, though they may not be supported by all compiler drivers. We choose `SDKROOT`,
// primarily because that is the same interface that is used when invoking the tool under
// `xcrun -sdk macosx $tool`.
//
// In that sense, if a given compiler driver does not support `SDKROOT`, the blame is fairly
// clearly in the tool in question, since they also don't support being run under `xcrun`.
//
// Additionally, `SDKROOT` is an environment variable and thus optional. It also has lower
// precedence than `-isysroot`, so a custom compiler driver that does not support it and
// instead figures out the SDK on their own can easily do so by using `-isysroot`.
//
// (This in particular affects Clang built with the `DEFAULT_SYSROOT` CMake flag, such as
// the one provided by some versions of Homebrew's `llvm` package. Those will end up
// ignoring the value we set here, and instead use their built-in sysroot).
cmd.cmd().env("SDKROOT", &sdkroot);
} else { } else {
// When invoking the linker directly, we use the `-syslibroot` parameter. `SDKROOT` is not
// read by the linker, so it's really the only option.
//
// This is also what Clang does.
cmd.link_arg("-syslibroot"); cmd.link_arg("-syslibroot");
cmd.link_arg(&sdkroot); cmd.link_arg(&sdkroot);
} }
@@ -3250,7 +3272,13 @@ fn get_apple_sdk_root(sess: &Session) -> Option<PathBuf> {
} }
"macosx" "macosx"
if sdkroot.contains("iPhoneOS.platform") if sdkroot.contains("iPhoneOS.platform")
|| sdkroot.contains("iPhoneSimulator.platform") => {} || sdkroot.contains("iPhoneSimulator.platform")
|| sdkroot.contains("AppleTVOS.platform")
|| sdkroot.contains("AppleTVSimulator.platform")
|| sdkroot.contains("WatchOS.platform")
|| sdkroot.contains("WatchSimulator.platform")
|| sdkroot.contains("XROS.platform")
|| sdkroot.contains("XRSimulator.platform") => {}
"watchos" "watchos"
if sdkroot.contains("WatchSimulator.platform") if sdkroot.contains("WatchSimulator.platform")
|| sdkroot.contains("MacOSX.platform") => {} || sdkroot.contains("MacOSX.platform") => {}

View File

@@ -1,5 +1,4 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::env;
use std::fmt::{Display, from_fn}; use std::fmt::{Display, from_fn};
use std::num::ParseIntError; use std::num::ParseIntError;
use std::str::FromStr; use std::str::FromStr;
@@ -209,29 +208,10 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
// that's only applicable to cross-OS compilation. Always leave anything for the // that's only applicable to cross-OS compilation. Always leave anything for the
// host OS alone though. // host OS alone though.
if os == "macos" { if os == "macos" {
let mut env_remove = Vec::with_capacity(2); // `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
// Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
// may occur when we're linking a custom build script while targeting iOS for example.
if let Ok(sdkroot) = env::var("SDKROOT") {
if sdkroot.contains("iPhoneOS.platform")
|| sdkroot.contains("iPhoneSimulator.platform")
|| sdkroot.contains("AppleTVOS.platform")
|| sdkroot.contains("AppleTVSimulator.platform")
|| sdkroot.contains("WatchOS.platform")
|| sdkroot.contains("WatchSimulator.platform")
|| sdkroot.contains("XROS.platform")
|| sdkroot.contains("XRSimulator.platform")
{
env_remove.push("SDKROOT".into())
}
}
// Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
// "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld", // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
// although this is apparently ignored when using the linker at "/usr/bin/ld". // although this is apparently ignored when using the linker at "/usr/bin/ld".
env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into()); cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET", "XROS_DEPLOYMENT_TARGET"]
env_remove.push("TVOS_DEPLOYMENT_TARGET".into());
env_remove.push("XROS_DEPLOYMENT_TARGET".into());
env_remove.into()
} else { } else {
// Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part // Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part
// of the linking environment that's wrong and reversed. // of the linking environment that's wrong and reversed.

View File

@@ -1,6 +1,6 @@
# `SDKROOT` # `SDKROOT`
This environment variable is used on Apple targets. This environment variable is used on Apple targets.
It is passed through to the linker (currently either as `-isysroot` or `-syslibroot`). It is passed through to the linker (currently either directly or via the `-syslibroot` flag).
Note that this variable is not always respected. When the SDKROOT is clearly wrong (e.g. when the platform of the SDK does not match the `--target` used by rustc), this is ignored and rustc does its own detection. Note that this variable is not always respected. When the SDKROOT is clearly wrong (e.g. when the platform of the SDK does not match the `--target` used by rustc), this is ignored and rustc does its own detection.