1 //===-- XcodeSDK.cpp ------------------------------------------------------===// 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 "lldb/Utility/XcodeSDK.h" 10 #include "lldb/Utility/FileSpec.h" 11 12 #include "lldb/lldb-types.h" 13 14 #include "llvm/TargetParser/Triple.h" 15 16 #include <string> 17 18 using namespace lldb; 19 using namespace lldb_private; 20 21 static llvm::StringRef GetName(XcodeSDK::Type type) { 22 switch (type) { 23 case XcodeSDK::MacOSX: 24 return "MacOSX"; 25 case XcodeSDK::iPhoneSimulator: 26 return "iPhoneSimulator"; 27 case XcodeSDK::iPhoneOS: 28 return "iPhoneOS"; 29 case XcodeSDK::AppleTVSimulator: 30 return "AppleTVSimulator"; 31 case XcodeSDK::AppleTVOS: 32 return "AppleTVOS"; 33 case XcodeSDK::WatchSimulator: 34 return "WatchSimulator"; 35 case XcodeSDK::watchOS: 36 return "WatchOS"; 37 case XcodeSDK::XRSimulator: 38 return "XRSimulator"; 39 case XcodeSDK::XROS: 40 return "XROS"; 41 case XcodeSDK::bridgeOS: 42 return "bridgeOS"; 43 case XcodeSDK::Linux: 44 return "Linux"; 45 case XcodeSDK::unknown: 46 return {}; 47 } 48 llvm_unreachable("Unhandled sdk type!"); 49 } 50 51 XcodeSDK::XcodeSDK(XcodeSDK::Info info) : m_name(GetName(info.type).str()) { 52 if (!m_name.empty()) { 53 if (!info.version.empty()) 54 m_name += info.version.getAsString(); 55 if (info.internal) 56 m_name += ".Internal"; 57 m_name += ".sdk"; 58 } 59 } 60 61 XcodeSDK &XcodeSDK::operator=(const XcodeSDK &other) = default; 62 63 bool XcodeSDK::operator==(const XcodeSDK &other) const { 64 return m_name == other.m_name; 65 } 66 67 static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) { 68 if (name.consume_front("MacOSX")) 69 return XcodeSDK::MacOSX; 70 if (name.consume_front("iPhoneSimulator")) 71 return XcodeSDK::iPhoneSimulator; 72 if (name.consume_front("iPhoneOS")) 73 return XcodeSDK::iPhoneOS; 74 if (name.consume_front("AppleTVSimulator")) 75 return XcodeSDK::AppleTVSimulator; 76 if (name.consume_front("AppleTVOS")) 77 return XcodeSDK::AppleTVOS; 78 if (name.consume_front("WatchSimulator")) 79 return XcodeSDK::WatchSimulator; 80 if (name.consume_front("WatchOS")) 81 return XcodeSDK::watchOS; 82 if (name.consume_front("XRSimulator")) 83 return XcodeSDK::XRSimulator; 84 if (name.consume_front("XROS")) 85 return XcodeSDK::XROS; 86 if (name.consume_front("bridgeOS")) 87 return XcodeSDK::bridgeOS; 88 if (name.consume_front("Linux")) 89 return XcodeSDK::Linux; 90 static_assert(XcodeSDK::Linux == XcodeSDK::numSDKTypes - 1, 91 "New SDK type was added, update this list!"); 92 return XcodeSDK::unknown; 93 } 94 95 static llvm::VersionTuple ParseSDKVersion(llvm::StringRef &name) { 96 unsigned i = 0; 97 while (i < name.size() && name[i] >= '0' && name[i] <= '9') 98 ++i; 99 if (i == name.size() || name[i++] != '.') 100 return {}; 101 while (i < name.size() && name[i] >= '0' && name[i] <= '9') 102 ++i; 103 if (i == name.size() || name[i++] != '.') 104 return {}; 105 106 llvm::VersionTuple version; 107 version.tryParse(name.slice(0, i - 1)); 108 name = name.drop_front(i); 109 return version; 110 } 111 112 static bool ParseAppleInternalSDK(llvm::StringRef &name) { 113 return name.consume_front("Internal.") || name.consume_front(".Internal."); 114 } 115 116 XcodeSDK::Info XcodeSDK::Parse() const { 117 XcodeSDK::Info info; 118 llvm::StringRef input(m_name); 119 info.type = ParseSDKName(input); 120 info.version = ParseSDKVersion(input); 121 info.internal = ParseAppleInternalSDK(input); 122 return info; 123 } 124 125 bool XcodeSDK::IsAppleInternalSDK() const { 126 llvm::StringRef input(m_name); 127 ParseSDKName(input); 128 ParseSDKVersion(input); 129 return ParseAppleInternalSDK(input); 130 } 131 132 llvm::VersionTuple XcodeSDK::GetVersion() const { 133 llvm::StringRef input(m_name); 134 ParseSDKName(input); 135 return ParseSDKVersion(input); 136 } 137 138 XcodeSDK::Type XcodeSDK::GetType() const { 139 llvm::StringRef input(m_name); 140 return ParseSDKName(input); 141 } 142 143 llvm::StringRef XcodeSDK::GetString() const { return m_name; } 144 145 bool XcodeSDK::Info::operator<(const Info &other) const { 146 return std::tie(type, version, internal) < 147 std::tie(other.type, other.version, other.internal); 148 } 149 150 bool XcodeSDK::Info::operator==(const Info &other) const { 151 return std::tie(type, version, internal) == 152 std::tie(other.type, other.version, other.internal); 153 } 154 155 void XcodeSDK::Merge(const XcodeSDK &other) { 156 // The "bigger" SDK always wins. 157 auto l = Parse(); 158 auto r = other.Parse(); 159 if (l < r) 160 *this = other; 161 else { 162 // The Internal flag always wins. 163 if (llvm::StringRef(m_name).ends_with(".sdk")) 164 if (!l.internal && r.internal) 165 m_name = 166 m_name.substr(0, m_name.size() - 3) + std::string("Internal.sdk"); 167 } 168 } 169 170 std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) { 171 std::string name; 172 switch (info.type) { 173 case MacOSX: 174 name = "macosx"; 175 break; 176 case iPhoneSimulator: 177 name = "iphonesimulator"; 178 break; 179 case iPhoneOS: 180 name = "iphoneos"; 181 break; 182 case AppleTVSimulator: 183 name = "appletvsimulator"; 184 break; 185 case AppleTVOS: 186 name = "appletvos"; 187 break; 188 case WatchSimulator: 189 name = "watchsimulator"; 190 break; 191 case watchOS: 192 name = "watchos"; 193 break; 194 case XRSimulator: 195 name = "xrsimulator"; 196 break; 197 case XROS: 198 name = "xros"; 199 break; 200 case bridgeOS: 201 name = "bridgeos"; 202 break; 203 case Linux: 204 name = "linux"; 205 break; 206 case unknown: 207 return {}; 208 } 209 if (!info.version.empty()) 210 name += info.version.getAsString(); 211 if (info.internal) 212 name += ".internal"; 213 return name; 214 } 215 216 bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type, 217 llvm::VersionTuple version) { 218 switch (sdk_type) { 219 case Type::MacOSX: 220 return version >= llvm::VersionTuple(10, 10); 221 case Type::iPhoneOS: 222 case Type::iPhoneSimulator: 223 case Type::AppleTVOS: 224 case Type::AppleTVSimulator: 225 return version >= llvm::VersionTuple(8); 226 case Type::watchOS: 227 case Type::WatchSimulator: 228 return version >= llvm::VersionTuple(6); 229 case Type::XROS: 230 case Type::XRSimulator: 231 return true; 232 default: 233 return false; 234 } 235 236 return false; 237 } 238 239 bool XcodeSDK::SupportsSwift() const { 240 XcodeSDK::Info info = Parse(); 241 switch (info.type) { 242 case Type::MacOSX: 243 return info.version.empty() || info.version >= llvm::VersionTuple(10, 10); 244 case Type::iPhoneOS: 245 case Type::iPhoneSimulator: 246 return info.version.empty() || info.version >= llvm::VersionTuple(8); 247 case Type::AppleTVSimulator: 248 case Type::AppleTVOS: 249 return info.version.empty() || info.version >= llvm::VersionTuple(9); 250 case Type::WatchSimulator: 251 case Type::watchOS: 252 return info.version.empty() || info.version >= llvm::VersionTuple(2); 253 case Type::XROS: 254 case Type::XRSimulator: 255 case Type::Linux: 256 return true; 257 default: 258 return false; 259 } 260 } 261 262 bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type, 263 const FileSpec &sdk_path) { 264 ConstString last_path_component = sdk_path.GetFilename(); 265 266 if (!last_path_component) 267 return false; 268 269 XcodeSDK sdk(last_path_component.GetStringRef().str()); 270 if (sdk.GetType() != desired_type) 271 return false; 272 return SDKSupportsModules(sdk.GetType(), sdk.GetVersion()); 273 } 274 275 XcodeSDK::Type XcodeSDK::GetSDKTypeForTriple(const llvm::Triple &triple) { 276 using namespace llvm; 277 switch (triple.getOS()) { 278 case Triple::MacOSX: 279 case Triple::Darwin: 280 return XcodeSDK::MacOSX; 281 case Triple::IOS: 282 switch (triple.getEnvironment()) { 283 case Triple::MacABI: 284 return XcodeSDK::MacOSX; 285 case Triple::Simulator: 286 return XcodeSDK::iPhoneSimulator; 287 default: 288 return XcodeSDK::iPhoneOS; 289 } 290 case Triple::TvOS: 291 if (triple.getEnvironment() == Triple::Simulator) 292 return XcodeSDK::AppleTVSimulator; 293 return XcodeSDK::AppleTVOS; 294 case Triple::WatchOS: 295 if (triple.getEnvironment() == Triple::Simulator) 296 return XcodeSDK::WatchSimulator; 297 return XcodeSDK::watchOS; 298 case Triple::XROS: 299 if (triple.getEnvironment() == Triple::Simulator) 300 return XcodeSDK::XRSimulator; 301 return XcodeSDK::XROS; 302 case Triple::Linux: 303 return XcodeSDK::Linux; 304 default: 305 return XcodeSDK::unknown; 306 } 307 } 308 309 std::string XcodeSDK::FindXcodeContentsDirectoryInPath(llvm::StringRef path) { 310 auto begin = llvm::sys::path::begin(path); 311 auto end = llvm::sys::path::end(path); 312 313 // Iterate over the path components until we find something that ends with 314 // .app. If the next component is Contents then we've found the Contents 315 // directory. 316 for (auto it = begin; it != end; ++it) { 317 if (it->ends_with(".app")) { 318 auto next = it; 319 if (++next != end && *next == "Contents") { 320 llvm::SmallString<128> buffer; 321 llvm::sys::path::append(buffer, begin, ++next, 322 llvm::sys::path::Style::posix); 323 return buffer.str().str(); 324 } 325 } 326 } 327 328 return {}; 329 } 330