1 //===--- DarwinSDKInfo.cpp - SDK Information parser for darwin - ----------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Basic/DarwinSDKInfo.h" 10 #include "llvm/ADT/StringSwitch.h" 11 #include "llvm/Support/ErrorOr.h" 12 #include "llvm/Support/JSON.h" 13 #include "llvm/Support/MemoryBuffer.h" 14 #include "llvm/Support/Path.h" 15 #include <optional> 16 17 using namespace clang; 18 19 std::optional<VersionTuple> DarwinSDKInfo::RelatedTargetVersionMapping::map( 20 const VersionTuple &Key, const VersionTuple &MinimumValue, 21 std::optional<VersionTuple> MaximumValue) const { 22 if (Key < MinimumKeyVersion) 23 return MinimumValue; 24 if (Key > MaximumKeyVersion) 25 return MaximumValue; 26 auto KV = Mapping.find(Key.normalize()); 27 if (KV != Mapping.end()) 28 return KV->getSecond(); 29 // If no exact entry found, try just the major key version. Only do so when 30 // a minor version number is present, to avoid recursing indefinitely into 31 // the major-only check. 32 if (Key.getMinor()) 33 return map(VersionTuple(Key.getMajor()), MinimumValue, MaximumValue); 34 // If this a major only key, return std::nullopt for a missing entry. 35 return std::nullopt; 36 } 37 38 std::optional<DarwinSDKInfo::RelatedTargetVersionMapping> 39 DarwinSDKInfo::RelatedTargetVersionMapping::parseJSON( 40 const llvm::json::Object &Obj, VersionTuple MaximumDeploymentTarget) { 41 VersionTuple Min = VersionTuple(std::numeric_limits<unsigned>::max()); 42 VersionTuple Max = VersionTuple(0); 43 VersionTuple MinValue = Min; 44 llvm::DenseMap<VersionTuple, VersionTuple> Mapping; 45 for (const auto &KV : Obj) { 46 if (auto Val = KV.getSecond().getAsString()) { 47 llvm::VersionTuple KeyVersion; 48 llvm::VersionTuple ValueVersion; 49 if (KeyVersion.tryParse(KV.getFirst()) || ValueVersion.tryParse(*Val)) 50 return std::nullopt; 51 Mapping[KeyVersion.normalize()] = ValueVersion; 52 if (KeyVersion < Min) 53 Min = KeyVersion; 54 if (KeyVersion > Max) 55 Max = KeyVersion; 56 if (ValueVersion < MinValue) 57 MinValue = ValueVersion; 58 } 59 } 60 if (Mapping.empty()) 61 return std::nullopt; 62 return RelatedTargetVersionMapping( 63 Min, Max, MinValue, MaximumDeploymentTarget, std::move(Mapping)); 64 } 65 66 static llvm::Triple::OSType parseOS(const llvm::json::Object &Obj) { 67 // The CanonicalName is the Xcode platform followed by a version, e.g. 68 // macosx16.0. 69 auto CanonicalName = Obj.getString("CanonicalName"); 70 if (!CanonicalName) 71 return llvm::Triple::UnknownOS; 72 size_t VersionStart = CanonicalName->find_first_of("0123456789"); 73 StringRef XcodePlatform = CanonicalName->slice(0, VersionStart); 74 return llvm::StringSwitch<llvm::Triple::OSType>(XcodePlatform) 75 .Case("macosx", llvm::Triple::MacOSX) 76 .Case("iphoneos", llvm::Triple::IOS) 77 .Case("iphonesimulator", llvm::Triple::IOS) 78 .Case("appletvos", llvm::Triple::TvOS) 79 .Case("appletvsimulator", llvm::Triple::TvOS) 80 .Case("watchos", llvm::Triple::WatchOS) 81 .Case("watchsimulator", llvm::Triple::WatchOS) 82 .Case("xros", llvm::Triple::XROS) 83 .Case("xrsimulator", llvm::Triple::XROS) 84 .Case("driverkit", llvm::Triple::DriverKit) 85 .Default(llvm::Triple::UnknownOS); 86 } 87 88 static std::optional<VersionTuple> getVersionKey(const llvm::json::Object &Obj, 89 StringRef Key) { 90 auto Value = Obj.getString(Key); 91 if (!Value) 92 return std::nullopt; 93 VersionTuple Version; 94 if (Version.tryParse(*Value)) 95 return std::nullopt; 96 return Version; 97 } 98 99 std::optional<DarwinSDKInfo> 100 DarwinSDKInfo::parseDarwinSDKSettingsJSON(const llvm::json::Object *Obj) { 101 auto Version = getVersionKey(*Obj, "Version"); 102 if (!Version) 103 return std::nullopt; 104 auto MaximumDeploymentVersion = 105 getVersionKey(*Obj, "MaximumDeploymentTarget"); 106 if (!MaximumDeploymentVersion) 107 return std::nullopt; 108 llvm::Triple::OSType OS = parseOS(*Obj); 109 llvm::DenseMap<OSEnvPair::StorageType, 110 std::optional<RelatedTargetVersionMapping>> 111 VersionMappings; 112 if (const auto *VM = Obj->getObject("VersionMap")) { 113 // FIXME: Generalize this out beyond iOS-deriving targets. 114 // Look for ios_<targetos> version mapping for targets that derive from ios. 115 for (const auto &KV : *VM) { 116 auto Pair = StringRef(KV.getFirst()).split("_"); 117 if (Pair.first.compare_insensitive("ios") == 0) { 118 llvm::Triple TT(llvm::Twine("--") + Pair.second.lower()); 119 if (TT.getOS() != llvm::Triple::UnknownOS) { 120 auto Mapping = RelatedTargetVersionMapping::parseJSON( 121 *KV.getSecond().getAsObject(), *MaximumDeploymentVersion); 122 if (Mapping) 123 VersionMappings[OSEnvPair(llvm::Triple::IOS, 124 llvm::Triple::UnknownEnvironment, 125 TT.getOS(), 126 llvm::Triple::UnknownEnvironment) 127 .Value] = std::move(Mapping); 128 } 129 } 130 } 131 132 if (const auto *Mapping = VM->getObject("macOS_iOSMac")) { 133 auto VersionMap = RelatedTargetVersionMapping::parseJSON( 134 *Mapping, *MaximumDeploymentVersion); 135 if (!VersionMap) 136 return std::nullopt; 137 VersionMappings[OSEnvPair::macOStoMacCatalystPair().Value] = 138 std::move(VersionMap); 139 } 140 if (const auto *Mapping = VM->getObject("iOSMac_macOS")) { 141 auto VersionMap = RelatedTargetVersionMapping::parseJSON( 142 *Mapping, *MaximumDeploymentVersion); 143 if (!VersionMap) 144 return std::nullopt; 145 VersionMappings[OSEnvPair::macCatalystToMacOSPair().Value] = 146 std::move(VersionMap); 147 } 148 } 149 150 return DarwinSDKInfo(std::move(*Version), 151 std::move(*MaximumDeploymentVersion), OS, 152 std::move(VersionMappings)); 153 } 154 155 Expected<std::optional<DarwinSDKInfo>> 156 clang::parseDarwinSDKInfo(llvm::vfs::FileSystem &VFS, StringRef SDKRootPath) { 157 llvm::SmallString<256> Filepath = SDKRootPath; 158 llvm::sys::path::append(Filepath, "SDKSettings.json"); 159 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File = 160 VFS.getBufferForFile(Filepath); 161 if (!File) { 162 // If the file couldn't be read, assume it just doesn't exist. 163 return std::nullopt; 164 } 165 Expected<llvm::json::Value> Result = 166 llvm::json::parse(File.get()->getBuffer()); 167 if (!Result) 168 return Result.takeError(); 169 170 if (const auto *Obj = Result->getAsObject()) { 171 if (auto SDKInfo = DarwinSDKInfo::parseDarwinSDKSettingsJSON(Obj)) 172 return std::move(SDKInfo); 173 } 174 return llvm::make_error<llvm::StringError>("invalid SDKSettings.json", 175 llvm::inconvertibleErrorCode()); 176 } 177