diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 3b34eb063ec04..4924b41242fcd 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -8,7 +8,65 @@ codegen_ssa_apple_deployment_target_invalid = codegen_ssa_apple_deployment_target_too_low = deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} -codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {$error} +codegen_ssa_apple_sdk_error_failed_reading = + failed reading `{$path}` while looking for SDK root: {$error} + +codegen_ssa_apple_sdk_error_missing = + failed finding SDK for platform `{$sdk_name}`. It looks like you have not installed Xcode? + + { $sdk_name -> + [MacOSX] You should install Xcode via the App Store, or run `xcode-select --install` to install the Command Line Tools if you only intend on developing for macOS. + *[other] You should install Xcode via the App Store. + } + +codegen_ssa_apple_sdk_error_missing_commandline_tools = + failed finding SDK at `{$sdkroot}` in Command Line Tools installation. + + { $sdk_name -> + [MacOSX] Perhaps you need to reinstall it with `xcode-select --install`? + *[other] When compiling for iOS, tvOS, visionOS or watchOS, you will need a full installation of Xcode. + } + +codegen_ssa_apple_sdk_error_missing_cross_compile_non_macos = + failed finding Apple SDK with name `{$sdk_name}`. + + The SDK is needed by the linker to know where to find symbols in system libraries and for embedding the SDK version in the final object file. + + The SDK can be downloaded and extracted from https://developer.apple.com/download/all/?q=xcode (requires an Apple ID). + + The full Xcode bundle should contain the SDK in `Xcode.app/Contents/Developer/Platforms/{$sdk_name}.platform/Developer/SDKs/{$sdk_name}.sdk`{ $sdk_name -> + [MacOSX] , but downloading just the Command Line Tools for Xcode should also be sufficient to obtain the macOS SDK. + *[other] . + } + + You will then need to tell `rustc` about it using the `SDKROOT` environment variables. + + Furthermore, you might need to install a linker capable of linking Mach-O files, or at least ensure that `rustc` is configured to use the bundled `lld`. + + { $sdk_name -> + [MacOSX] {""} + *[other] Beware that cross-compilation to iOS, tvOS, visionOS or watchOS is generally ill supported on non-macOS hosts. + } + +codegen_ssa_apple_sdk_error_missing_developer_dir = + failed finding SDK inside active developer directory `{$dir}` set by the DEVELOPER_DIR environment variable. Looked in: + - `{$sdkroot}` + - `{$sdkroot_bare}` + +codegen_ssa_apple_sdk_error_missing_xcode = + failed finding SDK at `{$sdkroot}` in Xcode installation. + + { $sdk_name -> + [MacOSX] {""} + *[other] Perhaps you need a newer version of Xcode? + } + +codegen_ssa_apple_sdk_error_missing_xcode_select = + failed finding SDK inside active developer directory `{$dir}` set by `xcode-select`. Looked in: + - `{$sdkroot}` + - `{$sdkroot_bare}` + + Consider using `sudo xcode-select --switch path/to/Xcode.app` or `sudo xcode-select --reset` to select a valid path. codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} @@ -348,8 +406,6 @@ codegen_ssa_unknown_atomic_ordering = unknown ordering in atomic intrinsic codegen_ssa_unknown_reuse_kind = unknown cgu-reuse-kind `{$kind}` specified -codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}` - codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target codegen_ssa_use_cargo_directive = use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib) diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index 93d90cd16b24a..451ff6874e1ef 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -1,15 +1,34 @@ -use std::env; use std::fmt::{Display, from_fn}; +use std::io::ErrorKind; use std::num::ParseIntError; +use std::path::{Path, PathBuf}; +use std::{env, fs}; use rustc_session::Session; use rustc_target::spec::Target; -use crate::errors::AppleDeploymentTarget; +use crate::errors::{AppleDeploymentTarget, AppleSdkError}; #[cfg(test)] mod tests; +pub(super) fn sdk_name(target: &Target) -> &'static str { + match (&*target.os, &*target.abi) { + ("ios", "") => "iPhoneOS", + ("ios", "sim") => "iPhoneSimulator", + // Mac Catalyst uses the macOS SDK + ("ios", "macabi") => "MacOSX", + ("macos", "") => "MacOSX", + ("tvos", "") => "AppleTVOS", + ("tvos", "sim") => "AppleTVSimulator", + ("visionos", "") => "XROS", + ("visionos", "sim") => "XRSimulator", + ("watchos", "") => "WatchOS", + ("watchos", "sim") => "WatchSimulator", + (os, abi) => unreachable!("invalid os '{os}' / abi '{abi}' combination for Apple target"), + } +} + pub(super) fn macho_platform(target: &Target) -> u32 { match (&*target.os, &*target.abi) { ("macos", _) => object::macho::PLATFORM_MACOS, @@ -169,3 +188,132 @@ pub(super) fn add_version_to_llvm_target( format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}") } } + +// TOCTOU is not _really_ an issue with our use of `try_exists` in here, we mostly use it for +// diagnostics, and these directories are global state that the user can change anytime anyhow in +// ways that are going to interfere much more with the compilation process. +fn try_exists(path: &Path) -> Result { + path.try_exists().map_err(|error| AppleSdkError::FailedReading { path: path.to_owned(), error }) +} + +/// Get the SDK path for an SDK under `/Library/Developer/CommandLineTools`. +fn sdk_root_in_sdks_dir(sdks_dir: impl Into, sdk_name: &str) -> PathBuf { + let mut path = sdks_dir.into(); + path.push("SDKs"); + path.push(sdk_name); + path.set_extension("sdk"); + path +} + +/// Get the SDK path for an SDK under `/Applications/Xcode.app/Contents/Developer`. +fn sdk_root_in_developer_dir(developer_dir: impl Into, sdk_name: &str) -> PathBuf { + let mut path = developer_dir.into(); + path.push("Platforms"); + path.push(sdk_name); + path.set_extension("platform"); + path.push("Developer"); + path.push("SDKs"); + path.push(sdk_name); + path.set_extension("sdk"); + path +} + +/// Find a SDK root from the user's environment for the given SDK name. +/// +/// We do this by searching (purely by names in the filesystem, without reading SDKSettings.json) +/// for a matching SDK in the following places: +/// - `DEVELOPER_DIR` +/// - `/var/db/xcode_select_link` +/// - `/Applications/Xcode.app` +/// - `/Library/Developer/CommandLineTools` +/// +/// This does roughly the same thing as `xcrun -sdk $sdk_name -show-sdk-path` (see `man xcrun` for +/// a few details on the search algorithm). +/// +/// The reason why we implement this logic ourselves is: +/// - Reading these directly is faster than spawning a new process. +/// - `xcrun` can be fairly slow to start up after a reboot. +/// - In the future, we will be able to integrate this better with the compiler's change tracking +/// mechanisms, allowing rebuilds when the involved env vars and paths here change. See #118204. +/// - It's easier for us to emit better error messages. +/// +/// Though a downside is that `xcrun` might be expanded in the future to check more places, and then +/// `rustc` would have to be changed to keep up. Furthermore, `xcrun`'s exact algorithm is +/// undocumented, so it might be doing more things than we do here. +pub(crate) fn find_sdk_root(sdk_name: &'static str) -> Result { + // Only try this if the host OS is macOS. + if !cfg!(target_os = "macos") { + return Err(AppleSdkError::MissingCrossCompileNonMacOS { sdk_name }); + } + + // NOTE: We could consider walking upwards in `SDKROOT` assuming Xcode directory structure, but + // that isn't what `xcrun` does, and might still not yield the desired result (e.g. if using an + // old SDK to compile for an old ARM iOS arch, we don't want `rustc` to pick a macOS SDK from + // the old Xcode). + + // Try reading from `DEVELOPER_DIR`. + if let Some(dir) = std::env::var_os("DEVELOPER_DIR") { + let dir = PathBuf::from(dir); + let sdkroot = sdk_root_in_developer_dir(&dir, sdk_name); + + if try_exists(&sdkroot)? { + return Ok(sdkroot); + } else { + let sdkroot_bare = sdk_root_in_sdks_dir(&dir, sdk_name); + if try_exists(&sdkroot_bare)? { + return Ok(sdkroot_bare); + } else { + return Err(AppleSdkError::MissingDeveloperDir { dir, sdkroot, sdkroot_bare }); + } + } + } + + // Next, try to read the link that `xcode-select` sets. + // + // FIXME(madsmtm): Support cases where `/var/db/xcode_select_link` contains a relative path? + let path = Path::new("/var/db/xcode_select_link"); + match fs::read_link(path) { + Ok(dir) => { + let sdkroot = sdk_root_in_developer_dir(&dir, sdk_name); + if try_exists(&sdkroot)? { + return Ok(sdkroot); + } else { + let sdkroot_bare = sdk_root_in_sdks_dir(&dir, sdk_name); + if try_exists(&sdkroot_bare)? { + return Ok(sdkroot_bare); + } else { + return Err(AppleSdkError::MissingXcodeSelect { dir, sdkroot, sdkroot_bare }); + } + } + } + Err(err) if err.kind() == ErrorKind::NotFound => { + // Intentionally ignore not found errors, if `xcode-select --reset` is called the + // link will not exist. + } + Err(error) => return Err(AppleSdkError::FailedReading { path: path.into(), error }), + } + + // Next, fall back to reading from `/Applications/Xcode.app`. + let dir = Path::new("/Applications/Xcode.app/Contents/Developer"); + if try_exists(dir)? { + let sdkroot = sdk_root_in_developer_dir(dir, sdk_name); + if try_exists(&sdkroot)? { + return Ok(sdkroot); + } else { + return Err(AppleSdkError::MissingXcode { sdkroot, sdk_name }); + } + } + + // Finally, fall back to reading from `/Library/Developer/CommandLineTools`. + let dir = Path::new("/Library/Developer/CommandLineTools"); + if try_exists(dir)? { + let sdkroot = sdk_root_in_sdks_dir(dir, sdk_name); + if try_exists(&sdkroot)? { + return Ok(sdkroot); + } else { + return Err(AppleSdkError::MissingCommandlineTools { sdkroot, sdk_name }); + } + } + + Err(AppleSdkError::Missing { sdk_name }) +} diff --git a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs index 7ccda5a8190c7..6ebc01d376830 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs @@ -1,4 +1,8 @@ -use super::{add_version_to_llvm_target, parse_version}; +use std::io; +use std::path::PathBuf; +use std::process::Command; + +use super::{add_version_to_llvm_target, find_sdk_root, parse_version}; #[test] fn test_add_version_to_llvm_target() { @@ -19,3 +23,56 @@ fn test_parse_version() { assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6))); assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99))); } + +fn find_sdk_root_xcrun(sdk_name: &str) -> io::Result { + let output = Command::new("xcrun") + .arg("-sdk") + .arg(sdk_name.to_lowercase()) + .arg("-show-sdk-path") + .output()?; + if output.status.success() { + // FIXME(madsmtm): If using this for real, we should not error on non-UTF-8 paths. + let output = std::str::from_utf8(&output.stdout).unwrap(); + Ok(PathBuf::from(output.trim())) + } else { + let error = String::from_utf8(output.stderr); + let error = format!("process exit with error: {}", error.unwrap()); + Err(io::Error::new(io::ErrorKind::Other, error)) + } +} + +/// Ensure that our `find_sdk_root` matches `xcrun`'s behaviour. +/// +/// `xcrun` is quite slow the first time it's run after a reboot, so this test may take some time. +#[test] +#[cfg_attr(not(target_os = "macos"), ignore = "xcrun is only available on macOS")] +fn test_find_sdk_root() { + let sdks = [ + "MacOSX", + "AppleTVOS", + "AppleTVSimulator", + "iPhoneOS", + "iPhoneSimulator", + "WatchOS", + "WatchSimulator", + "XROS", + "XRSimulator", + ]; + for sdk_name in sdks { + if let Ok(expected) = find_sdk_root_xcrun(sdk_name) { + // `xcrun` prefers `MacOSX14.0.sdk` over `MacOSX.sdk`, so let's compare canonical paths. + let expected = std::fs::canonicalize(expected).unwrap(); + let actual = find_sdk_root(sdk_name).unwrap(); + let actual = std::fs::canonicalize(actual).unwrap(); + assert_eq!(expected, actual); + } else { + // The macOS SDK must always be findable in Rust's CI. + // + // The other SDKs are allowed to not be found in the current developer directory when + // running this test. + if sdk_name == "MacOSX" { + panic!("Could not find macOS SDK with `xcrun -sdk macosx -show-sdk-path`"); + } + } + } +} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 20920d16f3cea..867c050d06763 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3124,81 +3124,71 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo } fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option { - let arch = &sess.target.arch; - let os = &sess.target.os; - let llvm_target = &sess.target.llvm_target; - if sess.target.vendor != "apple" - || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "visionos" | "macos") - || !matches!(flavor, LinkerFlavor::Darwin(..)) - { + if !sess.target.is_like_osx { return None; } - - if os == "macos" && !matches!(flavor, LinkerFlavor::Darwin(Cc::No, _)) { + let LinkerFlavor::Darwin(cc, _) = flavor else { return None; - } - - let sdk_name = match (arch.as_ref(), os.as_ref()) { - ("aarch64", "tvos") if llvm_target.ends_with("-simulator") => "appletvsimulator", - ("aarch64", "tvos") => "appletvos", - ("x86_64", "tvos") => "appletvsimulator", - ("arm", "ios") => "iphoneos", - ("aarch64", "ios") if llvm_target.contains("macabi") => "macosx", - ("aarch64", "ios") if llvm_target.ends_with("-simulator") => "iphonesimulator", - ("aarch64", "ios") => "iphoneos", - ("x86", "ios") => "iphonesimulator", - ("x86_64", "ios") if llvm_target.contains("macabi") => "macosx", - ("x86_64", "ios") => "iphonesimulator", - ("x86_64", "watchos") => "watchsimulator", - ("arm64_32", "watchos") => "watchos", - ("aarch64", "watchos") if llvm_target.ends_with("-simulator") => "watchsimulator", - ("aarch64", "watchos") => "watchos", - ("aarch64", "visionos") if llvm_target.ends_with("-simulator") => "xrsimulator", - ("aarch64", "visionos") => "xros", - ("arm", "watchos") => "watchos", - (_, "macos") => "macosx", - _ => { - sess.dcx().emit_err(errors::UnsupportedArch { arch, os }); - return None; - } }; - let sdk_root = match get_apple_sdk_root(sdk_name) { + + let sdk_name = apple::sdk_name(&sess.target); + + let sdkroot = match get_apple_sdk_root(sdk_name) { Ok(s) => s, Err(e) => { - sess.dcx().emit_err(e); + // If cross compiling from non-macOS, the user might be using something like `zig cc`. + // + // In that case, we shouldn't error when the SDK is missing, though we still warn. + if cfg!(target_os = "macos") { + sess.dcx().emit_err(e); + } else { + sess.dcx().emit_warn(e); + } return None; } }; - match flavor { - LinkerFlavor::Darwin(Cc::Yes, _) => { - // Use `-isysroot` instead of `--sysroot`, as only the former - // makes Clang treat it as a platform SDK. - // - // This is admittedly a bit strange, as on most targets - // `-isysroot` only applies to include header files, but on Apple - // targets this also applies to libraries and frameworks. - cmd.cc_args(&["-isysroot", &sdk_root]); - } - LinkerFlavor::Darwin(Cc::No, _) => { - cmd.link_args(&["-syslibroot", &sdk_root]); - } - _ => unreachable!(), + if cc == Cc::Yes { + // To pass the SDK root to `cc`, we have a few options: + // 1. `--sysroot` flag. + // 2. `-isysroot` flag. + // 3. `SDKROOT` environment variable. + // + // `--sysroot` isn't actually enough to get Clang to treat it as a platform SDK, you need to + // specify `-isysroot` - this is admittedly a bit strange, as on most targets `-isysroot` + // only applies to include header files, but on Apple targets it also applies to libraries + // and frameworks. + // + // Now, while the `-isysroot` flag is pretty well supported (both Clang and GCC implements + // the desired behaviour), it may not be understood by any `cc`'s that the user might want + // to use. + // + // So to better support such use-cases, we pass the SDK root in the standard environment + // variable instead. This is also supported by GCC since 2019: + // + // + // This also works better with the trampoline `/usr/bin/cc` which calls `xcrun cc` + // internally, since the presence of `SDKROOT` means it won't have to look up the SDK root + // itself. + cmd.cmd().env("SDKROOT", &sdkroot); + } else { + // For `ld64`, we use the `-syslibroot` parameter (this is what Clang passes, and `SDKROOT` + // is not read by `ld64` so it's really the only option). + cmd.link_arg("-syslibroot"); + cmd.link_arg(&sdkroot); } - Some(sdk_root.into()) + Some(sdkroot) } -fn get_apple_sdk_root(sdk_name: &str) -> Result> { +fn get_apple_sdk_root(sdk_name: &'static str) -> Result { // Following what clang does // (https://github.com/llvm/llvm-project/blob/ // 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678) - // to allow the SDK path to be set. (For clang, xcrun sets - // SDKROOT; for rustc, the user or build system can set it, or we - // can fall back to checking for xcrun on PATH.) + // to allow the SDK path to be set. if let Ok(sdkroot) = env::var("SDKROOT") { - let p = Path::new(&sdkroot); - match sdk_name { + let p = PathBuf::from(&sdkroot); + match &*sdk_name.to_lowercase() { // Ignore `SDKROOT` if it's clearly set for the wrong platform. "appletvos" if sdkroot.contains("TVSimulator.platform") @@ -3213,7 +3203,13 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result {} + || 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" if sdkroot.contains("WatchSimulator.platform") || sdkroot.contains("MacOSX.platform") => {} @@ -3226,26 +3222,11 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result {} // Ignore `SDKROOT` if it's not a valid path. _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} - _ => return Ok(sdkroot), + _ => return Ok(p), } } - let res = - Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then( - |output| { - if output.status.success() { - Ok(String::from_utf8(output.stdout).unwrap()) - } else { - let error = String::from_utf8(output.stderr); - let error = format!("process exit with error: {}", error.unwrap()); - Err(io::Error::new(io::ErrorKind::Other, &error[..])) - } - }, - ); - match res { - Ok(output) => Ok(output.trim().to_string()), - Err(error) => Err(errors::AppleSdkRootError::SdkPath { sdk_name, error }), - } + apple::find_sdk_root(sdk_name) } /// When using the linker flavors opting in to `lld`, add the necessary paths and arguments to diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index cf8d1cfa0d10e..6c3eedbec91d4 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -533,13 +533,6 @@ pub enum ExtractBundledLibsError<'a> { ExtractSection { rlib: &'a Path, error: Box }, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_unsupported_arch)] -pub(crate) struct UnsupportedArch<'a> { - pub arch: &'a str, - pub os: &'a str, -} - #[derive(Diagnostic)] pub(crate) enum AppleDeploymentTarget { #[diag(codegen_ssa_apple_deployment_target_invalid)] @@ -548,10 +541,28 @@ pub(crate) enum AppleDeploymentTarget { TooLow { env_var: &'static str, version: String, os_min: String }, } -#[derive(Diagnostic)] -pub(crate) enum AppleSdkRootError<'a> { - #[diag(codegen_ssa_apple_sdk_error_sdk_path)] - SdkPath { sdk_name: &'a str, error: Error }, +#[derive(Diagnostic, Debug)] +pub(crate) enum AppleSdkError { + #[diag(codegen_ssa_apple_sdk_error_failed_reading)] + FailedReading { path: PathBuf, error: std::io::Error }, + + #[diag(codegen_ssa_apple_sdk_error_missing)] + Missing { sdk_name: &'static str }, + + #[diag(codegen_ssa_apple_sdk_error_missing_commandline_tools)] + MissingCommandlineTools { sdkroot: PathBuf, sdk_name: &'static str }, + + #[diag(codegen_ssa_apple_sdk_error_missing_cross_compile_non_macos)] + MissingCrossCompileNonMacOS { sdk_name: &'static str }, + + #[diag(codegen_ssa_apple_sdk_error_missing_developer_dir)] + MissingDeveloperDir { dir: PathBuf, sdkroot: PathBuf, sdkroot_bare: PathBuf }, + + #[diag(codegen_ssa_apple_sdk_error_missing_xcode)] + MissingXcode { sdkroot: PathBuf, sdk_name: &'static str }, + + #[diag(codegen_ssa_apple_sdk_error_missing_xcode_select)] + MissingXcodeSelect { dir: PathBuf, sdkroot: PathBuf, sdkroot_bare: PathBuf }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index f45c86640936f..8213770967100 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::env; use crate::spec::{ Cc, DebuginfoKind, FramePointer, LinkerFlavor, Lld, SplitDebuginfo, StackProbeType, StaticCow, @@ -187,29 +186,10 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow]> { // that's only applicable to cross-OS compilation. Always leave anything for the // host OS alone though. if os == "macos" { - let mut env_remove = Vec::with_capacity(2); - // 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 + // `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld", // although this is apparently ignored when using the linker at "/usr/bin/ld". - env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into()); - env_remove.push("TVOS_DEPLOYMENT_TARGET".into()); - env_remove.push("XROS_DEPLOYMENT_TARGET".into()); - env_remove.into() + cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET", "XROS_DEPLOYMENT_TARGET"] } else { // Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part // of the linking environment that's wrong and reversed. diff --git a/src/doc/rustc/src/platform-support/apple-darwin.md b/src/doc/rustc/src/platform-support/apple-darwin.md index 17ea225805b5c..31ae642fc93fd 100644 --- a/src/doc/rustc/src/platform-support/apple-darwin.md +++ b/src/doc/rustc/src/platform-support/apple-darwin.md @@ -58,4 +58,5 @@ the `-mmacosx-version-min=...`, `-miphoneos-version-min=...` or similar flags to disambiguate. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun -sdk macosx -show-sdk-path`. diff --git a/src/doc/rustc/src/platform-support/apple-ios-macabi.md b/src/doc/rustc/src/platform-support/apple-ios-macabi.md index a54656190d1f3..2d54e15f16314 100644 --- a/src/doc/rustc/src/platform-support/apple-ios-macabi.md +++ b/src/doc/rustc/src/platform-support/apple-ios-macabi.md @@ -20,7 +20,8 @@ These targets are cross-compiled, and require the corresponding macOS SDK iOS-specific headers, as provided by Xcode 11 or higher. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun -sdk macosx -show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-ios.md b/src/doc/rustc/src/platform-support/apple-ios.md index 5045f810400f5..c1c34118ad986 100644 --- a/src/doc/rustc/src/platform-support/apple-ios.md +++ b/src/doc/rustc/src/platform-support/apple-ios.md @@ -26,7 +26,8 @@ These targets are cross-compiled, and require the corresponding iOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun -sdk iphoneos -show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md index 7a3b601579a01..761f7a7ffb903 100644 --- a/src/doc/rustc/src/platform-support/apple-tvos.md +++ b/src/doc/rustc/src/platform-support/apple-tvos.md @@ -20,7 +20,8 @@ These targets are cross-compiled, and require the corresponding tvOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun -sdk appletvos -show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md index 56224d7e20d4e..df9a173b8b414 100644 --- a/src/doc/rustc/src/platform-support/apple-visionos.md +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -18,7 +18,8 @@ These targets are cross-compiled, and require the corresponding visionOS SDK (`XROS.sdk` or `XRSimulator.sdk`), as provided by Xcode 15 or newer. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun -sdk xros -show-sdk-path`. ### OS version diff --git a/src/doc/rustc/src/platform-support/apple-watchos.md b/src/doc/rustc/src/platform-support/apple-watchos.md index 8ba35f70b8578..f6f9bc5cac337 100644 --- a/src/doc/rustc/src/platform-support/apple-watchos.md +++ b/src/doc/rustc/src/platform-support/apple-watchos.md @@ -24,7 +24,8 @@ These targets are cross-compiled, and require the corresponding watchOS SDK ARM64 targets, Xcode 12 or higher is required. The path to the SDK can be passed to `rustc` using the common `SDKROOT` -environment variable. +environment variable, or will be inferred when compiling on host macOS using +roughly the same logic as `xcrun -sdk watchos -show-sdk-path`. ### OS version diff --git a/tests/run-make/link-under-xcode/foo.rs b/tests/run-make/link-under-xcode/foo.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/link-under-xcode/foo.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/link-under-xcode/rmake.rs b/tests/run-make/link-under-xcode/rmake.rs new file mode 100644 index 0000000000000..4f79f96532f8b --- /dev/null +++ b/tests/run-make/link-under-xcode/rmake.rs @@ -0,0 +1,32 @@ +//! Test that linking works under an environment similar to what Xcode sets up. +//! +//! Regression test for https://github.com/rust-lang/rust/issues/80817. + +//@ only-apple + +use run_make_support::{cmd, rustc, target}; + +fn main() { + // Fetch toolchain `/usr/bin` directory. Usually: + // /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin + let cc_bin = cmd("xcrun").arg("--find").arg("cc").run().stdout_utf8(); + let toolchain_bin = cc_bin.trim().strip_suffix("/cc").unwrap(); + + // Put toolchain directory at the front of PATH. + let path = format!("{}:{}", toolchain_bin, std::env::var("PATH").unwrap()); + + // Check that compiling and linking still works. + // + // Removing `SDKROOT` is necessary for the test to excercise what we want, since bootstrap runs + // under `/usr/bin/python3`, which will set SDKROOT for us. + rustc().target(target()).env_remove("SDKROOT").env("PATH", &path).input("foo.rs").run(); + + // Also check with ld64. + rustc() + .target(target()) + .env_remove("SDKROOT") + .env("PATH", &path) + .arg("-Clinker-flavor=ld") + .input("foo.rs") + .run(); +}