From f685acf0d2aeb4bdc579fb71ac9729e1b47e127e Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Sat, 11 Apr 2026 16:08:12 +1000 Subject: [PATCH] Apple: visionOS and tvOS builds now supported; update to 26.4 SDKs --- Dockerfile.appleembedded | 9 + Dockerfile.osx | 9 +- build.sh | 4 +- files/appleembedded/patch-visionos-sdk.sh | 245 ++++++++++++++ files/patches/osxcross-fix-visionos.patch | 369 +++++++++++++++++----- 5 files changed, 559 insertions(+), 77 deletions(-) create mode 100644 files/appleembedded/patch-visionos-sdk.sh diff --git a/Dockerfile.appleembedded b/Dockerfile.appleembedded index e376195..f8430eb 100644 --- a/Dockerfile.appleembedded +++ b/Dockerfile.appleembedded @@ -37,4 +37,13 @@ ENV OSXCROSS_APPLEEMBEDDED=not_nothing ENV PATH="/root/ioscross/arm64/bin:/root/ioscross/x86_64/bin:${PATH}" +# Patch the visionOS SDK in place so it can be consumed by the swift.org +# 6.3 release compiler. See files/appleembedded/patch-visionos-sdk.sh for +# the per-category rationale and upstream-fix pointers. +COPY files/appleembedded/patch-visionos-sdk.sh /root/patch-visionos-sdk.sh +RUN chmod +x /root/patch-visionos-sdk.sh && \ + . "${SWIFTLY_HOME_DIR:-$HOME/.local/share/swiftly}/env.sh" && \ + /root/patch-visionos-sdk.sh && \ + rm /root/patch-visionos-sdk.sh + CMD /bin/bash diff --git a/Dockerfile.osx b/Dockerfile.osx index e35982f..21d626e 100644 --- a/Dockerfile.osx +++ b/Dockerfile.osx @@ -5,15 +5,16 @@ FROM godot-fedora:${img_version} # # sudo dnf install gnupg2 -ENV XCODE_SDKV=26.1.1 -ENV APPLE_SDKV=26.1 +ENV XCODE_SDKV=26.4 +ENV APPLE_SDKV=26.4 RUN dnf -y install --setopt=install_weak_deps=False \ automake autoconf bzip2-devel cmake gawk gcc gcc-c++ libdispatch libicu-devel libtool \ libxml2-devel openssl-devel uuid-devel yasm gpg && \ git clone --progress https://github.com/tpoechtrager/osxcross && \ cd /root/osxcross && \ - git checkout 121ce150c7857a9474dfff8a8e431482806b3e1b && \ + # latest commit from main branch + git checkout e6ab3fa7423f9235ce9ed6381d6d3af191b46b59 && \ # Patch to fix visionOS support. # See: https://github.com/llvm/llvm-project/issues/142502 patch -p1 < /root/files/patches/osxcross-fix-visionos.patch && \ @@ -31,7 +32,7 @@ RUN dnf -y install --setopt=install_weak_deps=False \ ENV OSXCROSS_ROOT=/root/osxcross ENV PATH="/root/osxcross/target/bin:${PATH}" -# Install Swift 6.2 toolchain +# Install latest Swift toolchain RUN mkdir -p /root/Xcode.app/Contents/Developer RUN cd /root/Xcode.app/Contents/Developer && tar xf /root/files/Xcode-Developer${XCODE_SDKV}.tar.xz --strip-components=1 diff --git a/build.sh b/build.sh index 46ca5ed..79642e3 100755 --- a/build.sh +++ b/build.sh @@ -58,8 +58,8 @@ podman_build windows podman_build web podman_build android -XCODE_SDK=26.1.1 -APPLE_SDKV=26.1 +XCODE_SDK=26.4 +APPLE_SDKV=26.4 if [ ! -e "${files_root}"/MacOSX${APPLE_SDKV}.sdk.tar.xz ] || [ ! -e "${files_root}"/Xcode-Developer${XCODE_SDK}.tar.xz ]; then if [ ! -r "${files_root}"/Xcode_${XCODE_SDK}.xip ]; then echo diff --git a/files/appleembedded/patch-visionos-sdk.sh b/files/appleembedded/patch-visionos-sdk.sh new file mode 100644 index 0000000..f648355 --- /dev/null +++ b/files/appleembedded/patch-visionos-sdk.sh @@ -0,0 +1,245 @@ +#!/bin/bash +# +# Patch Apple's Xcode visionOS SDK so it can be consumed by the open-source +# Swift toolchain from swift.org. Apple's compiler (swiftlang-6.3.0.123.x, +# shipped with Xcode 26) and the swift.org 6.3 release compiler diverge in +# several ways; until the relevant upstream fixes land on `swift/release/6.3` +# and are picked up by a swift.org release, we rewrite the affected header +# and `.swiftinterface` files in place. +# +# This script is idempotent and only touches the visionOS SDK (XROS.sdk). +# +# Remove patches as upstream fixes arrive: +# cat1 - swiftlang/llvm-project#11866 (visionOS availability inference for Obj-C) +# cat2 - no upstream fix yet; the OSS compiler does a strict string compare +# on `// swift-compiler-version:` in .swiftinterface files +# cat3 - Apple private-framework divergences (no upstream fix expected) +# +set -euo pipefail + +SDK="${1:-/root/Xcode.app/Contents/Developer/Platforms/XROS.platform/Developer/SDKs/XROS.sdk}" + +if [ ! -d "$SDK" ]; then + echo "patch-visionos-sdk: SDK not found at $SDK" >&2 + exit 1 +fi + +echo "patch-visionos-sdk: patching $SDK" + +############################################################################### +# cat1: visionOS availability inference for Obj-C headers. +# +# Apple's compiler infers `API_UNAVAILABLE(visionos)` whenever `ios` is in the +# unavailable list (visionOS derives from iOS). The OSS compiler doesn't do +# that inference yet, so we make the implicit explicit by adding `visionos` +# to every `API_UNAVAILABLE(...)` that lists `ios` but not `visionos`. +# +# Upstream fix: swiftlang/llvm-project#11866 (targets swift/release/6.3). +############################################################################### +echo "patch-visionos-sdk: cat1 - adding visionos to API_UNAVAILABLE in headers" +python3 - "$SDK/System/Library/Frameworks" <<'PY' +import os, re, sys +root = sys.argv[1] +pat = re.compile(r"API_UNAVAILABLE\(([^)]*)\)") +def repl(m): + inner = m.group(1) + toks = {t.strip() for t in inner.split(",")} + if "ios" in toks and "visionos" not in toks: + return f"API_UNAVAILABLE({inner}, visionos)" + return m.group(0) +ch_files = ch_calls = 0 +for dp, _, files in os.walk(root): + for fn in files: + if not fn.endswith(".h"): + continue + p = os.path.join(dp, fn) + try: + with open(p, "r", encoding="utf-8") as f: + src = f.read() + except (UnicodeDecodeError, PermissionError): + continue + new, _ = pat.subn(repl, src) + if new != src: + real = sum( + 1 for m in pat.finditer(src) + if "ios" in {t.strip() for t in m.group(1).split(",")} + and "visionos" not in {t.strip() for t in m.group(1).split(",")} + ) + if real: + with open(p, "w", encoding="utf-8") as f: + f.write(new) + ch_files += 1 + ch_calls += real +print(f" patched {ch_calls} calls in {ch_files} files") +PY + +############################################################################### +# cat2: swift-compiler-version stamp in .swiftinterface files. +# +# When the OSS compiler rebuilds a module from its textual interface, it does +# a strict string-compare on the `// swift-compiler-version:` header line. +# Apple's SDK is stamped `swiftlang-6.3.0.123.4` (or .5) and the OSS 6.3 +# release stamps `swift-6.3-RELEASE`, so the check fails. Rewrite the stamp +# to whatever the locally-installed OSS compiler reports. +# +# No upstream fix planned; this is fundamental to how Apple's release +# pipeline diverges from swift.org. +############################################################################### +echo "patch-visionos-sdk: cat2 - rewriting swift-compiler-version stamp in .swiftinterface" +# Try to resolve the OSS compiler's own stamp; fall back to the 6.3 release +# string if swift isn't in PATH at image-build time. +OSS_VERSION="$(swift --version 2>/dev/null | head -1 || true)" +if [ -z "$OSS_VERSION" ]; then + OSS_VERSION="Swift version 6.3 (swift-6.3-RELEASE)" +fi +echo " target stamp: $OSS_VERSION" +OSS_VERSION="$OSS_VERSION" python3 - "$SDK" <<'PY' +import os, re, sys +root = sys.argv[1] +oss = os.environ["OSS_VERSION"] +pat = re.compile(r"^// swift-compiler-version:.*$", re.MULTILINE) +ch = 0 +for dp, _, files in os.walk(root): + for fn in files: + if not fn.endswith(".swiftinterface"): + continue + p = os.path.join(dp, fn) + try: + with open(p, "r", encoding="utf-8") as f: + src = f.read() + except (UnicodeDecodeError, PermissionError): + continue + new, n = pat.subn(f"// swift-compiler-version: {oss}", src) + if n: + try: + with open(p, "w", encoding="utf-8") as f: + f.write(new) + ch += 1 + except PermissionError: + pass +print(f" rewrote stamp in {ch} .swiftinterface files") +PY + +############################################################################### +# cat3a: gut `@inlinable` bodies in RealityFoundation.swiftinterface that +# touch C++-imported types from `simd`/`AVFAudio`. +# +# With `-cxx-interoperability-mode=default` enabled, the OSS compiler treats +# the simd / AVFAudio C headers as C++ and refuses to compile `@inlinable` +# function bodies that use them ("C++ types ... do not support library +# evolution"). The affected declarations are internal helpers that Godot's +# visionOS code never calls, so we replace their bodies with a fatalError +# stub. The framework binary still provides the real implementation at run +# time on actual visionOS hardware. +# +# Gut every `@inlinable internal` unconditionally (they are implementation +# details), and `@inlinable public` only when its body references one of the +# poison tokens. +############################################################################### +RF_IFACE="$SDK/System/Library/Frameworks/RealityFoundation.framework/Modules/RealityFoundation.swiftmodule/arm64e-apple-xros.swiftinterface" +if [ -f "$RF_IFACE" ]; then + echo "patch-visionos-sdk: cat3a - gutting @inlinable bodies in RealityFoundation.swiftinterface" + python3 - "$RF_IFACE" <<'PY' +import sys, re +path = sys.argv[1] +with open(path, "r", encoding="utf-8") as f: + src = f.read() +poison = re.compile( + r"\b(simd_[A-Za-z_0-9]+|columns|AVAudioSourceNodeRenderBlock|AVAudioSinkNodeReceiverBlock)\b|\.vector\b" +) +lines = src.splitlines(keepends=True) +out = [] +i = 0 +gi = gp = 0 + +def find_ob(start): + j = start + while j < len(lines): + if lines[j].rstrip().endswith("{"): + return j, lines[j].rstrip().rfind("{") + j += 1 + return None, None + +def find_cb(ol, oc): + depth = 0 + for li in range(ol, len(lines)): + line = lines[li] + start = oc + 1 if li == ol else 0 + for ci in range(start, len(line)): + ch = line[ci] + if ch == "{": + depth += 1 + elif ch == "}": + if depth == 0: + return li + depth -= 1 + return None + +while i < len(lines): + line = lines[i] + if "@inlinable" in line: + is_internal = "internal" in line + ob, oc = find_ob(i) + if ob is None: + out.append(line); i += 1; continue + cb = find_cb(ob, oc) + if cb is None: + out.append(line); i += 1; continue + body_text = "".join(lines[ob:cb + 1]) + gut = is_internal or bool(poison.search(body_text)) + if gut: + out.extend(lines[i:ob]) + sig_line = lines[ob] + brace_pos = sig_line.rstrip().rfind("{") + out.append(sig_line.rstrip()[:brace_pos + 1] + " Swift.fatalError() }\n") + if is_internal: + gi += 1 + else: + gp += 1 + i = cb + 1 + continue + out.append(line) + i += 1 + +with open(path, "w", encoding="utf-8") as f: + f.write("".join(out)) +print(f" gutted {gi} @inlinable internal + {gp} @inlinable public bodies") +PY + + ########################################################################### + # cat3b: replace a public typealias whose target is an `AVFAudio` C++ block + # type. With C++ interop on, the compiler refuses the typealias itself. + # We swap it to a compatible function type so call sites that use + # `@escaping RealityFoundation.Audio.GeneratorRenderHandler` still parse. + ########################################################################### + echo "patch-visionos-sdk: cat3b - replacing GeneratorRenderHandler typealias in RealityFoundation.swiftinterface" + sed -i \ + "s|public typealias GeneratorRenderHandler = AVFAudio\.AVAudioSourceNodeRenderBlock|public typealias GeneratorRenderHandler = () -> Swift.Void|" \ + "$RF_IFACE" +else + echo "patch-visionos-sdk: cat3a/b - RealityFoundation.swiftinterface not found, skipping" +fi + +############################################################################### +# cat3c: `CTTextAlignment` / `CTLineBreakMode` default values in RealityKit. +# +# Apple's compiler strips the full `kCTTextAlignment` / `kCTLineBreak` prefix +# from these CoreFoundation enums and exposes them as `.left` / +# `.byTruncatingTail`. The OSS compiler strips a shorter prefix (the exact +# Swift name depends on version), so the default-argument expression in +# `MeshResource.generateText(...)` fails to resolve. Replace the defaults +# with raw-value initializers - they're valid regardless of how the compiler +# names the cases. +############################################################################### +RK_IFACE="$SDK/System/Library/Frameworks/RealityKit.framework/Modules/RealityKit.swiftmodule/arm64e-apple-xros.swiftinterface" +if [ -f "$RK_IFACE" ]; then + echo "patch-visionos-sdk: cat3c - replacing CT enum defaults in RealityKit.swiftinterface" + sed -i \ + -e "s|alignment: CoreText\.CTTextAlignment = \.left|alignment: CoreText.CTTextAlignment = CoreText.CTTextAlignment(rawValue: 0)!|g" \ + -e "s|lineBreakMode: CoreText\.CTLineBreakMode = \.byTruncatingTail|lineBreakMode: CoreText.CTLineBreakMode = CoreText.CTLineBreakMode(rawValue: 4)!|g" \ + "$RK_IFACE" +else + echo "patch-visionos-sdk: cat3c - RealityKit.swiftinterface not found, skipping" +fi + +echo "patch-visionos-sdk: done" diff --git a/files/patches/osxcross-fix-visionos.patch b/files/patches/osxcross-fix-visionos.patch index d410fce..9db6248 100644 --- a/files/patches/osxcross-fix-visionos.patch +++ b/files/patches/osxcross-fix-visionos.patch @@ -1,5 +1,24 @@ + build.sh | 2 + + build_clang.sh | 9 ++ + build_compiler_rt.sh | 10 ++ + patches/xros-availability-clang.patch | 279 ++++++++++++++++++++++++++++++++++ + 4 files changed, 300 insertions(+) + +diff --git a/build.sh b/build.sh +index 20f596d..38e4d06 100755 +--- a/build.sh ++++ b/build.sh +@@ -68,6 +68,8 @@ case $SDK_VERSION in + 26|26.0*) TARGET=darwin25; SUPPORTED_ARCHS="arm64 arm64e x86_64 x86_64h"; NEED_TAPI_SUPPORT=1; OSX_VERSION_MIN_INT=10.13 ;; + 26.1*) TARGET=darwin25.1; SUPPORTED_ARCHS="arm64 arm64e x86_64 x86_64h"; NEED_TAPI_SUPPORT=1; OSX_VERSION_MIN_INT=10.13 ;; + 26.2*) TARGET=darwin25.2; SUPPORTED_ARCHS="arm64 arm64e x86_64 x86_64h"; NEED_TAPI_SUPPORT=1; OSX_VERSION_MIN_INT=10.13 ;; ++ 26.3*) TARGET=darwin25.3; SUPPORTED_ARCHS="arm64 arm64e x86_64 x86_64h"; NEED_TAPI_SUPPORT=1; OSX_VERSION_MIN_INT=10.13 ;; ++ 26.4*) TARGET=darwin25.4; SUPPORTED_ARCHS="arm64 arm64e x86_64 x86_64h"; NEED_TAPI_SUPPORT=1; OSX_VERSION_MIN_INT=10.13 ;; + *) echo "Unsupported SDK"; exit 1 ;; + esac + diff --git a/build_clang.sh b/build_clang.sh -index 0d9c36b..57d967e 100755 +index 68c25d0..183789f 100755 --- a/build_clang.sh +++ b/build_clang.sh @@ -149,6 +149,15 @@ if [ $GITPROJECT == "apple" ]; then @@ -17,14 +36,14 @@ index 0d9c36b..57d967e 100755 + popd &>/dev/null fi - + diff --git a/build_compiler_rt.sh b/build_compiler_rt.sh -index da64909..feb9990 100755 +index 5ce8697..1611faf 100755 --- a/build_compiler_rt.sh +++ b/build_compiler_rt.sh -@@ -98,6 +98,16 @@ fi +@@ -99,6 +99,16 @@ fi get_sources https://github.com/llvm/llvm-project.git $BRANCH "compiler-rt" - + if [ $f_res -eq 1 ]; then + if [ $(osxcross-cmp $CLANG_VERSION ">=" 19.0) -eq 1 ]; then + pushd "$CURRENT_BUILD_PROJECT_NAME" @@ -37,82 +56,290 @@ index da64909..feb9990 100755 + fi + pushd "$CURRENT_BUILD_PROJECT_NAME/compiler-rt" &>/dev/null - + if [ $(osxcross-cmp $SDK_VERSION "<=" 10.11) -eq 1 ]; then diff --git a/patches/xros-availability-clang.patch b/patches/xros-availability-clang.patch new file mode 100644 -index 0000000..2666a18 +index 0000000..8638c7c --- /dev/null +++ b/patches/xros-availability-clang.patch -@@ -0,0 +1,71 @@ -+diff --git a/clang/include/clang/Basic/DarwinSDKInfo.h b/clang/include/clang/Basic/DarwinSDKInfo.h -+index db20b968a..33b36f53c 100644 -+--- a/clang/include/clang/Basic/DarwinSDKInfo.h -++++ b/clang/include/clang/Basic/DarwinSDKInfo.h -+@@ -72,6 +72,13 @@ public: -+ llvm::Triple::TvOS, llvm::Triple::UnknownEnvironment); -+ } -+ -++ /// Returns the os-environment mapping pair that's used to represent the -++ /// iOS -> visionOS version mapping. -++ static inline constexpr OSEnvPair iOStoXROSPair() { -++ return OSEnvPair(llvm::Triple::IOS, llvm::Triple::UnknownEnvironment, -++ llvm::Triple::XROS, llvm::Triple::UnknownEnvironment); -++ } -++ -+ private: -+ StorageType Value; -+ +@@ -0,0 +1,279 @@ +diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp -+index e2eada24f..26290b3ba 100644 ++index 79f3a0ca29e81..b881e43f83601 100644 +--- a/clang/lib/Sema/SemaDeclAttr.cpp ++++ b/clang/lib/Sema/SemaDeclAttr.cpp -+@@ -2415,6 +2415,48 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { -+ auto NewDeprecated = AdjustTvOSVersion(Deprecated.Version); -+ auto NewObsoleted = AdjustTvOSVersion(Obsoleted.Version); -+ ++@@ -2484,6 +2484,80 @@ static void handleFeatureAvailabilityAttr(Sema &S, Decl *D, ++ IsUnavailable, AL)); ++ } ++ +++/// Returns true if the given availability attribute should be inferred, and +++/// adjusts the value of the attribute as necessary to facilitate that. +++static bool shouldInferAvailabilityAttribute(const ParsedAttr &AL, +++ IdentifierInfo *&II, +++ bool &IsUnavailable, +++ VersionTuple &Introduced, +++ VersionTuple &Deprecated, +++ VersionTuple &Obsolete, Sema &S) { +++ const llvm::Triple &TT = S.Context.getTargetInfo().getTriple(); +++ const ASTContext &Context = S.Context; +++ if (TT.getOS() != llvm::Triple::XROS) +++ return false; +++ IdentifierInfo *NewII = nullptr; +++ if (II->getName() == "ios") +++ NewII = &Context.Idents.get("xros"); +++ else if (II->getName() == "ios_app_extension") +++ NewII = &Context.Idents.get("xros_app_extension"); +++ if (!NewII) +++ return false; +++ II = NewII; +++ +++ auto MakeUnavailable = [&]() { +++ IsUnavailable = true; +++ // Reset introduced, deprecated, obsoleted. +++ Introduced = VersionTuple(); +++ Deprecated = VersionTuple(); +++ Obsolete = VersionTuple(); +++ }; +++ +++ const DarwinSDKInfo *SDKInfo = S.getDarwinSDKInfoForAvailabilityChecking( +++ AL.getRange().getBegin(), "ios"); +++ +++ if (!SDKInfo) { +++ MakeUnavailable(); +++ return true; +++ } +++ // Map from the fallback platform availability to the current platform +++ // availability. +++ const auto *Mapping = SDKInfo->getVersionMapping(DarwinSDKInfo::OSEnvPair( +++ llvm::Triple::IOS, llvm::Triple::UnknownEnvironment, llvm::Triple::XROS, +++ llvm::Triple::UnknownEnvironment)); +++ if (!Mapping) { +++ MakeUnavailable(); +++ return true; +++ } +++ +++ if (!Introduced.empty()) { +++ auto NewIntroduced = Mapping->mapIntroducedAvailabilityVersion(Introduced); +++ if (!NewIntroduced) { +++ MakeUnavailable(); +++ return true; +++ } +++ Introduced = *NewIntroduced; +++ } +++ +++ if (!Obsolete.empty()) { +++ auto NewObsolete = +++ Mapping->mapDeprecatedObsoletedAvailabilityVersion(Obsolete); +++ if (!NewObsolete) { +++ MakeUnavailable(); +++ return true; +++ } +++ Obsolete = *NewObsolete; +++ } +++ +++ if (!Deprecated.empty()) { +++ auto NewDeprecated = +++ Mapping->mapDeprecatedObsoletedAvailabilityVersion(Deprecated); +++ Deprecated = NewDeprecated ? *NewDeprecated : VersionTuple(); +++ } +++ +++ return true; +++} +++ ++ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { ++ if (!AL.isAvailabilityAttribute()) { ++ handleFeatureAvailabilityAttr(S, D, AL); ++@@ -2601,6 +2675,25 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { ++ if (NewAttr) ++ D->addAttr(NewAttr); ++ +++ if (S.Context.getTargetInfo().getTriple().getOS() == llvm::Triple::XROS) { +++ IdentifierInfo *NewII = II; +++ bool NewIsUnavailable = IsUnavailable; +++ VersionTuple NewIntroduced = Introduced.Version; +++ VersionTuple NewDeprecated = Deprecated.Version; +++ VersionTuple NewObsoleted = Obsoleted.Version; +++ if (shouldInferAvailabilityAttribute(AL, NewII, NewIsUnavailable, +++ NewIntroduced, NewDeprecated, +++ NewObsoleted, S)) { ++ AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( ++ ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated, -++ NewObsoleted, IsUnavailable, Str, IsStrict, Replacement, -++ Sema::AMK_None, PriorityModifier + Sema::AP_InferredFromOtherPlatform, -++ IIEnvironment); +++ NewObsoleted, NewIsUnavailable, Str, IsStrict, Replacement, +++ Sema::AMK_None, +++ PriorityModifier + Sema::AP_InferredFromOtherPlatform, IIEnvironment); ++ if (NewAttr) ++ D->addAttr(NewAttr); ++ } -++ } else if (S.Context.getTargetInfo().getTriple().isXROS()) { -++ // Transcribe "ios" to "visionos" (and add a new attribute) if the versioning -++ // matches before the start of the visionOS platform. -++ IdentifierInfo *NewII = nullptr; -++ if (II->getName() == "ios") -++ NewII = &S.Context.Idents.get("xros"); -++ else if (II->getName() == "ios_app_extension") -++ NewII = &S.Context.Idents.get("xros_app_extension"); -++ -++ if (NewII) { -++ const auto *SDKInfo = S.getDarwinSDKInfoForAvailabilityChecking(); -++ const auto *IOSToXROSMapping = -++ SDKInfo ? SDKInfo->getVersionMapping( -++ DarwinSDKInfo::OSEnvPair::iOStoXROSPair()) -++ : nullptr; -++ -++ auto AdjustTvOSVersion = -++ [IOSToXROSMapping](VersionTuple Version) -> VersionTuple { -++ if (Version.empty()) -++ return Version; -++ -++ if (IOSToXROSMapping) { -++ if (auto MappedVersion = IOSToXROSMapping->map( -++ Version, VersionTuple(1, 0), std::nullopt)) { -++ return *MappedVersion; -++ } -++ } -++ return Version; -++ }; -++ -++ auto NewIntroduced = AdjustTvOSVersion(Introduced.Version); -++ auto NewDeprecated = AdjustTvOSVersion(Deprecated.Version); -++ auto NewObsoleted = AdjustTvOSVersion(Obsoleted.Version); -++ -+ AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr( -+ ND, AL, NewII, true /*Implicit*/, NewIntroduced, NewDeprecated, -+ NewObsoleted, IsUnavailable, Str, IsStrict, Replacement, +++ } +++ ++ // Transcribe "ios" to "watchos" (and add a new attribute) if the versioning ++ // matches before the start of the watchOS platform. ++ if (S.Context.getTargetInfo().getTriple().isWatchOS()) { ++diff --git a/clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json b/clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json ++new file mode 100644 ++index 0000000000000..e4f7c87519805 ++--- /dev/null +++++ b/clang/test/PlatformSpecific/xrOS/Sema/Inputs/XROS.sdk/SDKSettings.json ++@@ -0,0 +1,21 @@ +++{ +++ "DefaultVariant": "xrOS", "DisplayName": "xrOS", +++ "Version": "1.0", +++ "CanonicalName": "xros1.0", +++ "MaximumDeploymentTarget": "1.0.99", +++ "SupportedTargets": { +++ "xros": { +++ "Archs": ["arm64e", "arm64"], +++ "LLVMTargetTripleVendor": "apple", +++ "LLVMTargetTripleSys": "xros", +++ "LLVMTargetTripleEnvironment": "", +++ "SystemPrefix": "" +++ } +++ }, +++ "VersionMap": { +++ "iOS_visionOS": {"17.1": "1.0"}, +++ "iOS_xrOS": {"17.1": "1.0"}, +++ "visionOS_iOS": {"1.0": "17.1"}, +++ "xrOS_iOS": {"1.0": "17.1"} +++ } +++} ++diff --git a/clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c b/clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c ++new file mode 100644 ++index 0000000000000..76be32370e745 ++--- /dev/null +++++ b/clang/test/PlatformSpecific/xrOS/Sema/infer-ios-availability.c ++@@ -0,0 +1,117 @@ +++// RUN: %clang_cc1 -triple arm64-apple-xros1 -verify=ios -isysroot %S/Inputs/XROS.sdk %s 2>&1 +++// RUN: %clang_cc1 -triple arm64-apple-xros1 -fapplication-extension -verify=ios,ext -isysroot %S/Inputs/XROS.sdk %s 2>&1 +++ +++// RUN: %clang_cc1 -triple arm64-apple-xros2 -DXROS2 -verify=ios -isysroot %S/Inputs/XROS.sdk %s 2>&1 +++ +++__attribute__((availability(ios, unavailable))) +++void ios_unavail(); // ios-note {{}} +++ +++__attribute__((availability(ios_app_extension, unavailable))) +++void ios_ext_unavail(); // ext-note {{}} +++ +++void use() { +++ ios_unavail(); // ios-error {{'ios_unavail' is unavailable: not available on }} +++ ios_ext_unavail(); // ext-error {{'ios_ext_unavail' is unavailable: not available on }} +++} +++ +++__attribute__((availability(ios, introduced=10))) +++void ios_introduced_10(); +++ +++__attribute__((availability(ios_app_extension, introduced=10))) +++void ios_ext_introduced_10(); +++ +++__attribute__((availability(ios, introduced=17.1))) +++void ios_introduced_17(); +++ +++__attribute__((availability(ios_app_extension, introduced=17.1))) +++void ios_ext_introduced_17(); +++ +++__attribute__((availability(ios, introduced=18))) +++void ios_introduced_18(); // ios-note {{}} +++ +++__attribute__((availability(ios_app_extension, introduced=18))) +++void ios_ext_introduced_18(); // ext-note {{}} +++ +++void useIntroduced() { +++ // introduced iOS < 10 => introduced xrOS 1 +++ ios_introduced_10(); +++ ios_ext_introduced_10(); +++ // introduced iOS 17.1 => introduced xrOS 1 +++ ios_introduced_17(); +++ ios_ext_introduced_17(); +++ // introduced iOS 18 => xros unavailable (no mapping) +++ ios_introduced_18(); // ios-error {{is unavailable: not available on }} +++ ios_ext_introduced_18(); // ext-error {{is unavailable: not available on }} +++} +++ +++__attribute__((availability(ios, deprecated=10))) +++void ios_deprecated_10(); // ios-note {{}} +++ +++__attribute__((availability(ios_app_extension, deprecated=10))) +++void ios_ext_deprecated_10(); // ext-note {{}} +++ +++__attribute__((availability(ios, deprecated=17.1))) +++void ios_deprecated_17(); // ios-note {{}} +++ +++__attribute__((availability(ios_app_extension, deprecated=17.1))) +++void ios_ext_deprecated_17(); // ext-note {{}} +++ +++__attribute__((availability(ios, deprecated=18))) +++void ios_deprecated_18(); +++#ifdef XROS2 +++// ios-note@-2 {{}} +++#endif +++ +++__attribute__((availability(ios_app_extension, deprecated=18))) +++void ios_ext_deprecated_18(); +++ +++void useDeprecated() { +++ // deprecated iOS < 10 => deprecated xrOS 1 +++ ios_deprecated_10(); // ios-warning {{is deprecated: first deprecated in}} +++ ios_ext_deprecated_10(); // ext-warning {{is deprecated: first deprecated in}} +++ // deprecated iOS 17.1 => deprecated xrOS 1 +++ ios_deprecated_17(); // ios-warning {{is deprecated: first deprecated in}} +++ ios_ext_deprecated_17(); // ext-warning {{is deprecated: first deprecated in}} +++ // deprecated iOS 18 => deprecated xrOS 1.0.99 +++ ios_deprecated_18(); +++#ifdef XROS2 +++ // ios-warning@-2 {{is deprecated: first deprecated in}} +++#endif +++ ios_ext_deprecated_18(); +++} +++ +++__attribute__((availability(ios, obsoleted=10))) +++void ios_obsoleted_10(); // ios-note {{}} +++ +++__attribute__((availability(ios_app_extension, obsoleted=10))) +++void ios_ext_obsoleted_10(); // ext-note {{}} +++ +++__attribute__((availability(ios, obsoleted=17.1))) +++void ios_obsoleted_17(); // ios-note {{}} +++ +++__attribute__((availability(ios_app_extension, obsoleted=17.1))) +++void ios_ext_obsoleted_17(); // ext-note {{}} +++ +++__attribute__((availability(ios, obsoleted=18))) +++void ios_obsoleted_18(); +++#ifdef XROS2 +++// ios-note@-2 {{}} +++#endif +++ +++__attribute__((availability(ios_app_extension, obsoleted=18))) +++void ios_ext_obsoleted_18(); +++ +++void useObsoleted() { +++ // deprecated iOS < 10 => deprecated xrOS 1 +++ ios_obsoleted_10(); // ios-error {{is unavailable: obsoleted in}} +++ ios_ext_obsoleted_10(); // ext-error {{is unavailable: obsoleted in}} +++ // deprecated iOS 17.1 => deprecated xrOS 1 +++ ios_obsoleted_17(); // ios-error {{is unavailable: obsoleted in}} +++ ios_ext_obsoleted_17(); // ext-error {{is unavailable: obsoleted in}} +++ // obsoleted iOS 18 => obsoleted xrOS 1.0.99 +++ ios_obsoleted_18(); +++#ifdef XROS2 +++ // ios-error@-2 {{is unavailable: obsoleted in}} +++#endif +++ ios_ext_obsoleted_18(); +++} ++diff --git a/clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c b/clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c ++new file mode 100644 ++index 0000000000000..2645b487ffd72 ++--- /dev/null +++++ b/clang/test/PlatformSpecific/xrOS/Sema/infer-unavailable-from-missing-ios-availability-mapping.c ++@@ -0,0 +1,12 @@ +++// RUN: %clang_cc1 -triple arm64-apple-xros1 -verify=ios -DNOSDK %s 2>&1 +++// RUN: %clang_cc1 -triple arm64-apple-xros1 -verify=ios -isysroot %S/Inputs/XROS.sdk %s 2>&1 +++ +++#ifdef NOSDK +++// ios-warning@+2 {{ios availability is ignored without a valid 'SDKSettings.json' in the SDK}} +++#endif +++__attribute__((availability(ios, introduced=18))) // note the version introduced has to be higher than the versions in SDKSettings +++void ios_introduced_10(); // ios-note {{}} +++ +++void useIntroduced() { +++ ios_introduced_10(); // ios-error {{is unavailable: not available on }} +++}